From ded2b49dbd8193d7e11e0348810abd5931e66185 Mon Sep 17 00:00:00 2001 From: Benign X <1341398182@qq.com> Date: Thu, 5 Mar 2026 23:45:06 +0800 Subject: [PATCH] chore(gdb): add event subsystem wrappers with LVEvent, LVEventDsc, LVEventList and gen_event_consts --- scripts/gdb/lvglgdb/__init__.py | 10 ++ scripts/gdb/lvglgdb/lvgl/__init__.py | 10 ++ scripts/gdb/lvglgdb/lvgl/core/lv_obj.py | 10 ++ scripts/gdb/lvglgdb/lvgl/misc/__init__.py | 12 ++ scripts/gdb/lvglgdb/lvgl/misc/lv_event.py | 158 ++++++++++++++++++ .../gdb/lvglgdb/lvgl/misc/lv_event_consts.py | 83 +++++++++ scripts/gdb/scripts/gen_event_consts.py | 95 +++++++++++ 7 files changed, 378 insertions(+) create mode 100644 scripts/gdb/lvglgdb/lvgl/misc/lv_event.py create mode 100644 scripts/gdb/lvglgdb/lvgl/misc/lv_event_consts.py create mode 100644 scripts/gdb/scripts/gen_event_consts.py diff --git a/scripts/gdb/lvglgdb/__init__.py b/scripts/gdb/lvglgdb/__init__.py index ac6c485392..bd6ca58776 100644 --- a/scripts/gdb/lvglgdb/__init__.py +++ b/scripts/gdb/lvglgdb/__init__.py @@ -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", ] diff --git a/scripts/gdb/lvglgdb/lvgl/__init__.py b/scripts/gdb/lvglgdb/lvgl/__init__.py index d7c6f2a58a..0706aafbe9 100644 --- a/scripts/gdb/lvglgdb/lvgl/__init__.py +++ b/scripts/gdb/lvglgdb/lvgl/__init__.py @@ -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", ] diff --git a/scripts/gdb/lvglgdb/lvgl/core/lv_obj.py b/scripts/gdb/lvglgdb/lvgl/core/lv_obj.py index 362fdfb738..c08304a796 100644 --- a/scripts/gdb/lvglgdb/lvgl/core/lv_obj.py +++ b/scripts/gdb/lvglgdb/lvgl/core/lv_obj.py @@ -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: diff --git a/scripts/gdb/lvglgdb/lvgl/misc/__init__.py b/scripts/gdb/lvglgdb/lvgl/misc/__init__.py index b850e0dc79..663e18aa8c 100644 --- a/scripts/gdb/lvglgdb/lvgl/misc/__init__.py +++ b/scripts/gdb/lvglgdb/lvgl/misc/__init__.py @@ -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", ] diff --git a/scripts/gdb/lvglgdb/lvgl/misc/lv_event.py b/scripts/gdb/lvglgdb/lvgl/misc/lv_event.py new file mode 100644 index 0000000000..7eddf4cc0f --- /dev/null +++ b/scripts/gdb/lvglgdb/lvgl/misc/lv_event.py @@ -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) diff --git a/scripts/gdb/lvglgdb/lvgl/misc/lv_event_consts.py b/scripts/gdb/lvglgdb/lvgl/misc/lv_event_consts.py new file mode 100644 index 0000000000..b1f3f6a9fc --- /dev/null +++ b/scripts/gdb/lvglgdb/lvgl/misc/lv_event_consts.py @@ -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", +} diff --git a/scripts/gdb/scripts/gen_event_consts.py b/scripts/gdb/scripts/gen_event_consts.py new file mode 100644 index 0000000000..0f7b6d14cd --- /dev/null +++ b/scripts/gdb/scripts/gen_event_consts.py @@ -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()