chore(gdb): add LVSubject/LVObserver wrappers with gen_subject_consts and info subject command

This commit is contained in:
Benign X
2026-03-06 12:36:23 +08:00
committed by VIFEX
parent d803a26b43
commit e27bba80fe
9 changed files with 194 additions and 1 deletions

View File

@@ -43,6 +43,9 @@ from .lvgl import (
INDEV_TYPE_NAMES,
LVGroup,
LVObjClass,
LVSubject,
LVObserver,
SUBJECT_TYPE_NAMES,
)
from . import cmds as cmds
@@ -89,4 +92,7 @@ __all__ = [
"INDEV_TYPE_NAMES",
"LVGroup",
"LVObjClass",
"LVSubject",
"LVObserver",
"SUBJECT_TYPE_NAMES",
]

View File

@@ -1,6 +1,6 @@
import gdb
from .core import DumpObj, DumpIndev, DumpGroup, InfoObjClass
from .core import DumpObj, DumpIndev, DumpGroup, InfoObjClass, InfoSubject
from .display import DumpDisplayBuf
from .draw import InfoDrawUnit, DumpDrawTask
from .misc import (
@@ -46,6 +46,7 @@ DumpDrawTask()
InfoStyle()
InfoDrawUnit()
InfoObjClass()
InfoSubject()
# Drivers
Lvglobal()

View File

@@ -2,10 +2,12 @@ from .lv_obj import DumpObj
from .lv_indev import DumpIndev
from .lv_group import DumpGroup
from .lv_obj_class import InfoObjClass
from .lv_observer import InfoSubject
__all__ = [
"DumpObj",
"DumpIndev",
"DumpGroup",
"InfoObjClass",
"InfoSubject",
]

View File

@@ -0,0 +1,23 @@
import gdb
from lvglgdb.lvgl.core.lv_observer import LVSubject
class InfoSubject(gdb.Command):
"""show subject and its observers"""
def __init__(self):
super(InfoSubject, self).__init__(
"info subject", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
)
def invoke(self, args, from_tty):
if not args.strip():
print("Usage: info subject <expression>")
return
try:
subject = LVSubject(args.strip())
except gdb.error as e:
print(f"Error: {e}")
return
subject.print_info()

View File

@@ -8,6 +8,9 @@ from .core import (
INDEV_TYPE_NAMES,
LVGroup,
LVObjClass,
LVSubject,
LVObserver,
SUBJECT_TYPE_NAMES,
)
from .display import LVDisplay
from .draw import (
@@ -100,4 +103,7 @@ __all__ = [
"INDEV_TYPE_NAMES",
"LVGroup",
"LVObjClass",
"LVSubject",
"LVObserver",
"SUBJECT_TYPE_NAMES",
]

View File

@@ -3,6 +3,8 @@ 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
from .lv_observer import LVSubject, LVObserver
from .lv_observer_consts import SUBJECT_TYPE_NAMES
__all__ = [
"LVObject",
@@ -14,4 +16,7 @@ __all__ = [
"INDEV_TYPE_NAMES",
"LVGroup",
"LVObjClass",
"LVSubject",
"LVObserver",
"SUBJECT_TYPE_NAMES",
]

View File

@@ -0,0 +1,77 @@
from lvglgdb.value import Value, ValueInput
from ..misc.lv_ll import LVList
from .lv_observer_consts import SUBJECT_TYPE_NAMES
class LVObserver(Value):
"""LVGL observer wrapper"""
def __init__(self, obs: ValueInput):
super().__init__(Value.normalize(obs, "lv_observer_t"))
@property
def subject(self) -> Value:
return self.super_value("subject")
@property
def cb(self) -> Value:
return self.super_value("cb")
@property
def target(self) -> Value:
return self.super_value("target")
@property
def user_data(self) -> Value:
return self.super_value("user_data")
@property
def auto_free_user_data(self) -> bool:
return bool(int(self.super_value("auto_free_user_data")))
@property
def notified(self) -> bool:
return bool(int(self.super_value("notified")))
@property
def for_obj(self) -> bool:
return bool(int(self.super_value("for_obj")))
def print_info(self):
cb_str = self.cb.format_string(symbols=True, address=True)
print(
f" Observer: cb={cb_str} target={self.target}" f" for_obj={self.for_obj}"
)
class LVSubject(Value):
"""LVGL subject wrapper"""
def __init__(self, subject: ValueInput):
super().__init__(Value.normalize(subject, "lv_subject_t"))
@property
def type(self) -> int:
return int(self.super_value("type"))
@property
def type_name(self) -> str:
return SUBJECT_TYPE_NAMES.get(self.type, f"UNKNOWN({self.type})")
@property
def size(self) -> int:
return int(self.super_value("size"))
@property
def user_data(self) -> Value:
return self.super_value("user_data")
def __iter__(self):
for obs in LVList(self.subs_ll, "lv_observer_t"):
yield LVObserver(obs)
def print_info(self):
ll = LVList(self.subs_ll, "lv_observer_t")
print(f"Subject: type={self.type_name} subscribers={ll.len}")
for obs in self.__iter__():
obs.print_info()

View File

@@ -0,0 +1,17 @@
"""
Auto-generated observer constants from LVGL headers.
Do not edit manually. Regenerate with:
python3 scripts/gen_subject_consts.py
"""
SUBJECT_TYPE_NAMES = {
0: "INVALID",
1: "NONE",
2: "INT",
3: "FLOAT",
4: "POINTER",
5: "COLOR",
6: "GROUP",
7: "STRING",
}

View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Generate subject type constant table from LVGL header files.
Parses lv_observer.h for subject type enum.
Usage:
python3 scripts/gen_subject_consts.py
"""
import re
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
GDB_ROOT = SCRIPT_DIR.parent
LVGL_SRC = GDB_ROOT.parent.parent / "src"
OUTPUT = GDB_ROOT / "lvglgdb" / "lvgl" / "core" / "lv_observer_consts.py"
OBSERVER_H = LVGL_SRC / "core" / "lv_observer.h"
sys.path.insert(0, str(SCRIPT_DIR))
from gen_indev_consts import parse_enum
def generate(subject_types: dict[int, str]) -> str:
"""Generate Python source for the observer constants module."""
lines = [
'"""',
"Auto-generated observer constants from LVGL headers.",
"",
"Do not edit manually. Regenerate with:",
" python3 scripts/gen_subject_consts.py",
'"""',
"",
"SUBJECT_TYPE_NAMES = {",
]
for k in sorted(subject_types):
lines.append(f' {k}: "{subject_types[k]}",')
lines.append("}")
lines.append("")
return "\n".join(lines)
def main():
subject_types = parse_enum(
OBSERVER_H, "lv_subject_type_t", "LV_SUBJECT_TYPE_"
)
src = generate(subject_types)
OUTPUT.write_text(src)
print(f"Generated {OUTPUT} ({len(subject_types)} subject types)")
if __name__ == "__main__":
main()