mirror of
https://github.com/lvgl/lvgl.git
synced 2026-03-23 14:03:13 +08:00
chore(gdb): add LVObjClass wrapper with class hierarchy and format_coord utility
This commit is contained in:
@@ -38,9 +38,11 @@ from .lvgl import (
|
||||
LVTimer,
|
||||
LVImageDecoder,
|
||||
LVFsDrv,
|
||||
format_coord,
|
||||
LVIndev,
|
||||
INDEV_TYPE_NAMES,
|
||||
LVGroup,
|
||||
LVObjClass,
|
||||
)
|
||||
from . import cmds as cmds
|
||||
|
||||
@@ -82,7 +84,9 @@ __all__ = [
|
||||
"LVTimer",
|
||||
"LVImageDecoder",
|
||||
"LVFsDrv",
|
||||
"format_coord",
|
||||
"LVIndev",
|
||||
"INDEV_TYPE_NAMES",
|
||||
"LVGroup",
|
||||
"LVObjClass",
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import gdb
|
||||
|
||||
from .core import DumpObj, DumpIndev, DumpGroup
|
||||
from .core import DumpObj, DumpIndev, DumpGroup, InfoObjClass
|
||||
from .display import DumpDisplayBuf
|
||||
from .draw import InfoDrawUnit, DumpDrawTask
|
||||
from .misc import (
|
||||
@@ -45,6 +45,7 @@ DumpDrawTask()
|
||||
# Infos
|
||||
InfoStyle()
|
||||
InfoDrawUnit()
|
||||
InfoObjClass()
|
||||
|
||||
# Drivers
|
||||
Lvglobal()
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
from .lv_obj import DumpObj
|
||||
from .lv_indev import DumpIndev
|
||||
from .lv_group import DumpGroup
|
||||
from .lv_obj_class import InfoObjClass
|
||||
|
||||
__all__ = [
|
||||
"DumpObj",
|
||||
"DumpIndev",
|
||||
"DumpGroup",
|
||||
"InfoObjClass",
|
||||
]
|
||||
|
||||
46
scripts/gdb/lvglgdb/cmds/core/lv_obj_class.py
Normal file
46
scripts/gdb/lvglgdb/cmds/core/lv_obj_class.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import argparse
|
||||
|
||||
import gdb
|
||||
|
||||
from lvglgdb.lvgl.core.lv_obj_class import LVObjClass
|
||||
|
||||
|
||||
class InfoObjClass(gdb.Command):
|
||||
"""show object class hierarchy or list all classes"""
|
||||
|
||||
def __init__(self):
|
||||
super(InfoObjClass, self).__init__(
|
||||
"info obj_class", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
||||
)
|
||||
|
||||
def invoke(self, args, from_tty):
|
||||
parser = argparse.ArgumentParser(description="Show object class info.")
|
||||
parser.add_argument(
|
||||
"--all",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="List all registered object classes.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"expr",
|
||||
type=str,
|
||||
nargs="?",
|
||||
default=None,
|
||||
help="Expression evaluating to an lv_obj_class_t.",
|
||||
)
|
||||
try:
|
||||
args = parser.parse_args(gdb.string_to_argv(args))
|
||||
except SystemExit:
|
||||
return
|
||||
|
||||
if args.all or not args.expr:
|
||||
classes = LVObjClass.collect_all()
|
||||
LVObjClass.print_entries(classes)
|
||||
return
|
||||
|
||||
try:
|
||||
cls = LVObjClass(args.expr)
|
||||
except gdb.error as e:
|
||||
print(f"Error: {e}")
|
||||
return
|
||||
cls.print_info()
|
||||
@@ -7,6 +7,7 @@ from .core import (
|
||||
LVIndev,
|
||||
INDEV_TYPE_NAMES,
|
||||
LVGroup,
|
||||
LVObjClass,
|
||||
)
|
||||
from .display import LVDisplay
|
||||
from .draw import (
|
||||
@@ -48,6 +49,7 @@ from .misc import (
|
||||
LVTimer,
|
||||
LVImageDecoder,
|
||||
LVFsDrv,
|
||||
format_coord,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
@@ -93,7 +95,9 @@ __all__ = [
|
||||
"LVTimer",
|
||||
"LVImageDecoder",
|
||||
"LVFsDrv",
|
||||
"format_coord",
|
||||
"LVIndev",
|
||||
"INDEV_TYPE_NAMES",
|
||||
"LVGroup",
|
||||
"LVObjClass",
|
||||
]
|
||||
|
||||
@@ -2,6 +2,7 @@ from .lv_obj import LVObject, ObjStyle, dump_obj_info, dump_obj_styles
|
||||
from .lv_global import curr_inst
|
||||
from .lv_indev import LVIndev, INDEV_TYPE_NAMES
|
||||
from .lv_group import LVGroup
|
||||
from .lv_obj_class import LVObjClass
|
||||
|
||||
__all__ = [
|
||||
"LVObject",
|
||||
@@ -12,4 +13,5 @@ __all__ = [
|
||||
"LVIndev",
|
||||
"INDEV_TYPE_NAMES",
|
||||
"LVGroup",
|
||||
"LVObjClass",
|
||||
]
|
||||
|
||||
149
scripts/gdb/lvglgdb/lvgl/core/lv_obj_class.py
Normal file
149
scripts/gdb/lvglgdb/lvgl/core/lv_obj_class.py
Normal file
@@ -0,0 +1,149 @@
|
||||
import gdb
|
||||
from prettytable import PrettyTable
|
||||
|
||||
from lvglgdb.value import Value, ValueInput
|
||||
from ..misc.lv_utils import format_coord
|
||||
|
||||
|
||||
class LVObjClass(Value):
|
||||
"""LVGL object class wrapper"""
|
||||
|
||||
def __init__(self, cls: ValueInput):
|
||||
super().__init__(Value.normalize(cls, "lv_obj_class_t"))
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
n = self.super_value("name")
|
||||
return n.string() if int(n) else "(unnamed)"
|
||||
|
||||
@property
|
||||
def base_class(self):
|
||||
base = self.super_value("base_class")
|
||||
return LVObjClass(base) if int(base) else None
|
||||
|
||||
@property
|
||||
def width_def(self) -> int:
|
||||
return int(self.super_value("width_def"))
|
||||
|
||||
@property
|
||||
def height_def(self) -> int:
|
||||
return int(self.super_value("height_def"))
|
||||
|
||||
@property
|
||||
def instance_size(self) -> int:
|
||||
return int(self.super_value("instance_size"))
|
||||
|
||||
@property
|
||||
def editable(self) -> int:
|
||||
return int(self.super_value("editable"))
|
||||
|
||||
@property
|
||||
def group_def(self) -> int:
|
||||
return int(self.super_value("group_def"))
|
||||
|
||||
@property
|
||||
def theme_inheritable(self) -> bool:
|
||||
return bool(int(self.super_value("theme_inheritable")))
|
||||
|
||||
@property
|
||||
def constructor_cb(self) -> Value:
|
||||
return self.super_value("constructor_cb")
|
||||
|
||||
@property
|
||||
def destructor_cb(self) -> Value:
|
||||
return self.super_value("destructor_cb")
|
||||
|
||||
@property
|
||||
def event_cb(self) -> Value:
|
||||
return self.super_value("event_cb")
|
||||
|
||||
@property
|
||||
def user_data(self) -> Value:
|
||||
return self.super_value("user_data")
|
||||
|
||||
def __iter__(self):
|
||||
cls = self
|
||||
while cls:
|
||||
yield cls
|
||||
cls = cls.base_class
|
||||
|
||||
@staticmethod
|
||||
def collect_all():
|
||||
"""Collect all lv_obj_class_t globals from the symbol table."""
|
||||
import re
|
||||
|
||||
# Search by symbol name suffix; filter by type to exclude non-class matches
|
||||
output = gdb.execute("info variables _class$", to_string=True)
|
||||
classes = []
|
||||
for line in output.splitlines():
|
||||
if "lv_obj_class_t" not in line:
|
||||
continue
|
||||
m = re.search(r"\b(lv_\w+_class)\s*;", line)
|
||||
if m:
|
||||
try:
|
||||
cls = LVObjClass(m.group(1))
|
||||
classes.append(cls)
|
||||
except gdb.error:
|
||||
pass
|
||||
return classes
|
||||
|
||||
@staticmethod
|
||||
def print_entries(classes):
|
||||
"""Print object classes as a PrettyTable."""
|
||||
table = PrettyTable()
|
||||
table.field_names = [
|
||||
"#",
|
||||
"name",
|
||||
"base",
|
||||
"size",
|
||||
"editable",
|
||||
"group_def",
|
||||
"default_size",
|
||||
"theme_inh",
|
||||
]
|
||||
table.align = "l"
|
||||
|
||||
for i, cls in enumerate(classes):
|
||||
base = cls.base_class
|
||||
base_name = base.name if base else "-"
|
||||
table.add_row(
|
||||
[
|
||||
i,
|
||||
cls.name,
|
||||
base_name,
|
||||
cls.instance_size,
|
||||
cls.editable,
|
||||
cls.group_def,
|
||||
f"({format_coord(cls.width_def)}, {format_coord(cls.height_def)})",
|
||||
cls.theme_inheritable,
|
||||
]
|
||||
)
|
||||
|
||||
if not table.rows:
|
||||
print("No object classes found.")
|
||||
else:
|
||||
print(table)
|
||||
|
||||
def print_info(self):
|
||||
chain = list(self.__iter__())
|
||||
names = [c.name for c in chain]
|
||||
print(f"ObjClass: {' -> '.join(names)}")
|
||||
print(
|
||||
f" size={self.instance_size} editable={self.editable} group_def={self.group_def}"
|
||||
)
|
||||
w = format_coord(self.width_def)
|
||||
h = format_coord(self.height_def)
|
||||
print(f" default_size=({w}, {h}) theme_inheritable={self.theme_inheritable}")
|
||||
ctor = int(self.constructor_cb)
|
||||
dtor = int(self.destructor_cb)
|
||||
evt = int(self.event_cb)
|
||||
if ctor:
|
||||
print(
|
||||
f" constructor_cb = {self.constructor_cb.format_string(symbols=True)}"
|
||||
)
|
||||
if dtor:
|
||||
print(
|
||||
f" destructor_cb = {self.destructor_cb.format_string(symbols=True)}"
|
||||
)
|
||||
if evt:
|
||||
print(f" event_cb = {self.event_cb.format_string(symbols=True)}")
|
||||
@@ -27,6 +27,7 @@ from .lv_anim import LVAnim
|
||||
from .lv_timer import LVTimer
|
||||
from .lv_image_decoder import LVImageDecoder
|
||||
from .lv_fs import LVFsDrv
|
||||
from .lv_utils import format_coord
|
||||
|
||||
__all__ = [
|
||||
"LVList",
|
||||
@@ -59,4 +60,5 @@ __all__ = [
|
||||
"LVTimer",
|
||||
"LVImageDecoder",
|
||||
"LVFsDrv",
|
||||
"format_coord",
|
||||
]
|
||||
|
||||
@@ -65,3 +65,30 @@ def build_global_field_map(field_type_name):
|
||||
return result
|
||||
except gdb.error:
|
||||
return {}
|
||||
|
||||
|
||||
# LVGL coordinate type constants (from lv_area.h)
|
||||
_COORD_TYPE_SHIFT = 29
|
||||
_COORD_TYPE_SPEC = 1 << _COORD_TYPE_SHIFT
|
||||
_COORD_MAX = (1 << _COORD_TYPE_SHIFT) - 1
|
||||
_SIZE_CONTENT = _COORD_MAX | _COORD_TYPE_SPEC
|
||||
_PCT_POS_MAX = (_COORD_MAX - 1) // 2
|
||||
|
||||
|
||||
def format_coord(val):
|
||||
"""Format an lv_coord_t value into a human-readable string.
|
||||
|
||||
Decodes special LVGL coordinate encodings:
|
||||
- LV_SIZE_CONTENT -> "CONTENT"
|
||||
- LV_PCT(x) -> "x%"
|
||||
- plain pixel -> "123"
|
||||
"""
|
||||
val = int(val)
|
||||
if val == _SIZE_CONTENT:
|
||||
return "CONTENT"
|
||||
if val & _COORD_TYPE_SPEC:
|
||||
plain = val & ~_COORD_TYPE_SPEC
|
||||
if plain <= _PCT_POS_MAX:
|
||||
return f"{plain}%"
|
||||
return f"{_PCT_POS_MAX - plain}%"
|
||||
return str(val)
|
||||
|
||||
Reference in New Issue
Block a user