chore(gdb): add event subsystem wrappers with LVEvent, LVEventDsc, LVEventList and gen_event_consts

This commit is contained in:
Benign X
2026-03-05 23:45:06 +08:00
committed by VIFEX
parent 87aed2985c
commit ded2b49dbd
7 changed files with 378 additions and 0 deletions
+10
View File
@@ -29,6 +29,11 @@ from .lvgl import (
create_cache_iterator,
LVRedBlackTree,
LVArray,
LVEvent,
LVEventDsc,
LVEventList,
event_code_name,
EVENT_CODE_NAMES,
)
from . import cmds as cmds
@@ -61,4 +66,9 @@ __all__ = [
"LVImageHeaderCache",
"create_cache_iterator",
"LVArray",
"LVEvent",
"LVEventDsc",
"LVEventList",
"event_code_name",
"EVENT_CODE_NAMES",
]
+10
View File
@@ -30,6 +30,11 @@ from .misc import (
LVImageHeaderCache,
create_cache_iterator,
LVArray,
LVEvent,
LVEventDsc,
LVEventList,
event_code_name,
EVENT_CODE_NAMES,
)
__all__ = [
@@ -66,4 +71,9 @@ __all__ = [
"LVImageHeaderCache",
"create_cache_iterator",
"LVArray",
"LVEvent",
"LVEventDsc",
"LVEventList",
"event_code_name",
"EVENT_CODE_NAMES",
]
+10
View File
@@ -59,6 +59,16 @@ class LVObject(Value):
def child_count(self):
return self.spec_attr.child_cnt if self.spec_attr else 0
@property
def event_list(self):
"""Get event list from obj->spec_attr->event_list."""
from ..misc.lv_event import LVEventList
spec = self.spec_attr
if not spec or not int(spec):
return None
return LVEventList(spec.event_list)
@property
def children(self):
if not self.spec_attr:
+12
View File
@@ -16,6 +16,13 @@ from .lv_cache_iter_factory import create_cache_iterator
from .lv_image_cache import LVImageCache
from .lv_image_header_cache import LVImageHeaderCache
from .lv_array import LVArray
from .lv_event import (
LVEvent,
LVEventDsc,
LVEventList,
event_code_name,
EVENT_CODE_NAMES,
)
__all__ = [
"LVList",
@@ -39,4 +46,9 @@ __all__ = [
"LVImageHeaderCache",
"create_cache_iterator",
"LVArray",
"LVEvent",
"LVEventDsc",
"LVEventList",
"event_code_name",
"EVENT_CODE_NAMES",
]
+158
View File
@@ -0,0 +1,158 @@
import gdb
from lvglgdb.value import Value, ValueInput
from .lv_event_consts import EVENT_CODE_NAMES
from .lv_array import LVArray
def event_code_name(code: int) -> str:
"""Map event code integer to human-readable name."""
return EVENT_CODE_NAMES.get(code, f"UNKNOWN({code})")
class LVEvent(Value):
"""LVGL event wrapper"""
def __init__(self, event: ValueInput):
super().__init__(Value.normalize(event, "lv_event_t"))
@property
def code(self) -> int:
return int(self.super_value("code"))
@property
def code_name(self) -> str:
return event_code_name(self.code)
@property
def current_target(self) -> Value:
return self.super_value("current_target")
@property
def original_target(self) -> Value:
return self.super_value("original_target")
@property
def param(self) -> Value:
return self.super_value("param")
@property
def prev(self):
p = self.super_value("prev")
return LVEvent(p) if int(p) else None
@property
def deleted(self) -> bool:
return bool(int(self.super_value("deleted")))
@property
def stop_processing(self) -> bool:
return bool(int(self.super_value("stop_processing")))
def print_info(self):
print(f"Event: code={self.code_name}({self.code})")
print(f" current_target={self.current_target}")
print(f" original_target={self.original_target}")
print(f" deleted={self.deleted} stop_processing={self.stop_processing}")
class LVEventDsc(Value):
"""LVGL event descriptor wrapper"""
def __init__(self, dsc: ValueInput):
super().__init__(Value.normalize(dsc, "lv_event_dsc_t"))
@property
def cb(self) -> Value:
return self.super_value("cb")
@property
def user_data(self) -> Value:
return self.super_value("user_data")
@property
def filter(self) -> int:
return int(self.super_value("filter"))
@property
def filter_code(self) -> int:
"""Event code with flag bits masked off."""
return self.filter & ~(0x8000 | 0x10000)
@property
def filter_name(self) -> str:
return event_code_name(self.filter_code)
@property
def is_preprocess(self) -> bool:
return bool(self.filter & 0x8000)
@property
def is_marked_deleting(self) -> bool:
return bool(self.filter & 0x10000)
def print_info(self):
cb_str = self.cb.format_string(symbols=True, address=True)
flags = []
if self.is_preprocess:
flags.append("PRE")
if self.is_marked_deleting:
flags.append("DEL")
flag_str = f" [{','.join(flags)}]" if flags else ""
print(f" cb={cb_str} filter={self.filter_name}({self.filter_code}){flag_str}")
class LVEventList(Value):
"""LVGL event list wrapper (lv_event_list_t contains lv_array_t)"""
def __init__(self, event_list: ValueInput):
super().__init__(Value.normalize(event_list, "lv_event_list_t"))
@property
def array(self) -> LVArray:
"""Array stores lv_event_dsc_t* pointers (not inline structs)."""
dsc_ptr_type = gdb.lookup_type("lv_event_dsc_t").pointer()
return LVArray(self.super_value("array"), dsc_ptr_type)
@property
def is_traversing(self) -> bool:
return bool(int(self.super_value("is_traversing")))
@property
def has_marked_deleting(self) -> bool:
return bool(int(self.super_value("has_marked_deleting")))
def __iter__(self):
"""Yield LVEventDsc for each pointer stored in the array."""
for dsc_ptr in self.array:
yield LVEventDsc(dsc_ptr)
def __len__(self) -> int:
return len(self.array)
@staticmethod
def print_entries(event_dscs):
"""Print event descriptors as a PrettyTable."""
from prettytable import PrettyTable
table = PrettyTable()
table.field_names = ["#", "callback", "filter", "flags", "user_data"]
table.align = "l"
count = 0
for i, dsc in enumerate(event_dscs):
cb_str = dsc.cb.format_string(symbols=True, address=True)
flags = []
if dsc.is_preprocess:
flags.append("PRE")
if dsc.is_marked_deleting:
flags.append("DEL")
table.add_row(
[i, cb_str, dsc.filter_name, ",".join(flags) or "-", dsc.user_data]
)
count += 1
if count == 0:
print("No event descriptors.")
else:
print(table)
@@ -0,0 +1,83 @@
"""
Auto-generated event constants from LVGL headers.
Do not edit manually. Regenerate with:
python3 scripts/gen_event_consts.py
"""
EVENT_CODE_NAMES = {
0: "ALL",
1: "PRESSED",
2: "PRESSING",
3: "PRESS_LOST",
4: "SHORT_CLICKED",
5: "SINGLE_CLICKED",
6: "DOUBLE_CLICKED",
7: "TRIPLE_CLICKED",
8: "LONG_PRESSED",
9: "LONG_PRESSED_REPEAT",
10: "CLICKED",
11: "RELEASED",
12: "SCROLL_BEGIN",
13: "SCROLL_THROW_BEGIN",
14: "SCROLL_END",
15: "SCROLL",
16: "GESTURE",
17: "KEY",
18: "ROTARY",
19: "FOCUSED",
20: "DEFOCUSED",
21: "LEAVE",
22: "HIT_TEST",
23: "INDEV_RESET",
24: "HOVER_OVER",
25: "HOVER_LEAVE",
26: "COVER_CHECK",
27: "REFR_EXT_DRAW_SIZE",
28: "DRAW_MAIN_BEGIN",
29: "DRAW_MAIN",
30: "DRAW_MAIN_END",
31: "DRAW_POST_BEGIN",
32: "DRAW_POST",
33: "DRAW_POST_END",
34: "DRAW_TASK_ADDED",
35: "VALUE_CHANGED",
36: "INSERT",
37: "REFRESH",
38: "READY",
39: "CANCEL",
40: "STATE_CHANGED",
41: "CREATE",
42: "DELETE",
43: "CHILD_CHANGED",
44: "CHILD_CREATED",
45: "CHILD_DELETED",
46: "SCREEN_UNLOAD_START",
47: "SCREEN_LOAD_START",
48: "SCREEN_LOADED",
49: "SCREEN_UNLOADED",
50: "SIZE_CHANGED",
51: "STYLE_CHANGED",
52: "LAYOUT_CHANGED",
53: "GET_SELF_SIZE",
54: "INVALIDATE_AREA",
55: "RESOLUTION_CHANGED",
56: "COLOR_FORMAT_CHANGED",
57: "REFR_REQUEST",
58: "REFR_START",
59: "REFR_READY",
60: "RENDER_START",
61: "RENDER_READY",
62: "FLUSH_START",
63: "FLUSH_FINISH",
64: "FLUSH_WAIT_START",
65: "FLUSH_WAIT_FINISH",
66: "SYNC_START",
67: "SYNC_FINISH",
68: "SYNC_WAIT_START",
69: "SYNC_WAIT_FINISH",
70: "UPDATE_LAYOUT_COMPLETED",
71: "VSYNC",
72: "VSYNC_REQUEST",
73: "TRANSLATION_LANGUAGE_CHANGED",
}
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""
Generate event constant tables from LVGL header files.
Parses lv_event.h for the lv_event_code_t enum.
Usage:
python3 scripts/gen_event_consts.py
"""
import re
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" / "misc" / "lv_event_consts.py"
EVENT_H = LVGL_SRC / "misc" / "lv_event.h"
def parse_event_codes(path: Path) -> dict[int, str]:
"""Parse lv_event_code_t enum from lv_event.h."""
text = path.read_text()
m = re.search(r"typedef\s+enum\s*\{(.*?)\}\s*lv_event_code_t", text, re.DOTALL)
if not m:
raise RuntimeError("Cannot find lv_event_code_t enum")
codes = {}
current_val = 0
for line in m.group(1).splitlines():
line = line.strip().rstrip(",")
if (
not line
or line.startswith("/*")
or line.startswith("//")
or line.startswith("*")
or line.startswith("#")
):
continue
# Match: LV_EVENT_XXX = value
match = re.match(r"(LV_EVENT_\w+)\s*=\s*(0x[\da-fA-F]+|\d+)", line)
if match:
name = match.group(1)
current_val = int(match.group(2), 0)
else:
match = re.match(r"(LV_EVENT_\w+)", line)
if not match:
continue
name = match.group(1)
# Skip meta entries
if name in ("LV_EVENT_LAST", "LV_EVENT_PREPROCESS", "LV_EVENT_MARKED_DELETING"):
current_val += 1
continue
short = name.removeprefix("LV_EVENT_")
codes[current_val] = short
current_val += 1
return codes
def generate(codes: dict[int, str]) -> str:
"""Generate Python source for the event constants module."""
lines = [
'"""',
"Auto-generated event constants from LVGL headers.",
"",
"Do not edit manually. Regenerate with:",
" python3 scripts/gen_event_consts.py",
'"""',
"",
]
lines.append("EVENT_CODE_NAMES = {")
for k in sorted(codes):
lines.append(f' {k}: "{codes[k]}",')
lines.append("}")
lines.append("")
return "\n".join(lines)
def main():
codes = parse_event_codes(EVENT_H)
src = generate(codes)
OUTPUT.write_text(src)
print(f"Generated {OUTPUT} ({len(codes)} event codes)")
if __name__ == "__main__":
main()