mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-09 20:27:41 +08:00
chore(gdb): add draw task/unit wrappers with gen_draw_consts and PrettyTable output
This commit is contained in:
@@ -17,6 +17,7 @@ py import lvglgdb
|
||||
dump obj
|
||||
dump cache image
|
||||
dump cache image_header
|
||||
dump draw_task <layer_expr>
|
||||
|
||||
# Inspect a single lv_style_t variable
|
||||
info style my_style
|
||||
|
||||
@@ -3,6 +3,11 @@ from .lvgl import (
|
||||
curr_inst,
|
||||
LVDisplay,
|
||||
LVDrawBuf,
|
||||
LVDrawTask,
|
||||
DRAW_TASK_TYPE_NAMES,
|
||||
DRAW_TASK_STATE_NAMES,
|
||||
DRAW_UNIT_TYPE_NAMES,
|
||||
LVDrawUnit,
|
||||
LVList,
|
||||
LVObject,
|
||||
ObjStyle,
|
||||
@@ -30,6 +35,8 @@ __all__ = [
|
||||
"curr_inst",
|
||||
"LVDisplay",
|
||||
"LVDrawBuf",
|
||||
"LVDrawTask",
|
||||
"LVDrawUnit",
|
||||
"LVList",
|
||||
"LVCache",
|
||||
"LVRedBlackTree",
|
||||
|
||||
@@ -2,8 +2,8 @@ import gdb
|
||||
|
||||
from .core import DumpObj
|
||||
from .display import DumpDisplayBuf
|
||||
from .draw import InfoDrawUnit
|
||||
from .misc import InfoStyle, DumpCache, CheckPrefix, CheckCache
|
||||
from .draw import InfoDrawUnit, DumpDrawTask
|
||||
from .debugger import Debugger
|
||||
from .drivers import Lvglobal
|
||||
|
||||
@@ -25,6 +25,7 @@ DumpDisplayBuf()
|
||||
DumpCache()
|
||||
CheckPrefix()
|
||||
CheckCache()
|
||||
DumpDrawTask()
|
||||
|
||||
# Infos
|
||||
InfoStyle()
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from .lv_draw import InfoDrawUnit
|
||||
from .lv_draw_task import DumpDrawTask
|
||||
|
||||
__all__ = [
|
||||
"InfoDrawUnit",
|
||||
"DumpDrawTask",
|
||||
]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import gdb
|
||||
|
||||
from lvglgdb.value import Value
|
||||
from lvglgdb.lvgl import curr_inst
|
||||
from lvglgdb.lvgl.draw.lv_draw_unit import LVDrawUnit
|
||||
from lvglgdb.lvgl.draw.lv_draw_consts import DRAW_UNIT_TYPE_NAMES
|
||||
|
||||
|
||||
class InfoDrawUnit(gdb.Command):
|
||||
@@ -12,39 +13,22 @@ class InfoDrawUnit(gdb.Command):
|
||||
"info draw_unit", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
||||
)
|
||||
|
||||
def dump_draw_unit(self, draw_unit: Value):
|
||||
# Dereference to get the string content of the name from draw_unit
|
||||
name = draw_unit.name.string()
|
||||
def invoke(self, args, from_tty):
|
||||
for unit in curr_inst().draw_units():
|
||||
self._dump_unit(unit)
|
||||
|
||||
# Print draw_unit information and the name
|
||||
print(f"Draw Unit: {draw_unit}, Name: {name}")
|
||||
def _dump_unit(self, unit: LVDrawUnit):
|
||||
name = unit.name
|
||||
print(f"Draw Unit: {unit}, Name: {name}")
|
||||
|
||||
# Handle different draw_units based on the name
|
||||
def lookup_type(name):
|
||||
try:
|
||||
return gdb.lookup_type(name)
|
||||
except gdb.error:
|
||||
return None
|
||||
type_name = DRAW_UNIT_TYPE_NAMES.get(name, "lv_draw_unit_t")
|
||||
try:
|
||||
target_type = gdb.lookup_type(type_name)
|
||||
except gdb.error:
|
||||
target_type = gdb.lookup_type("lv_draw_unit_t")
|
||||
|
||||
types = {
|
||||
"DMA2D": lookup_type("lv_draw_dma2d_unit_t"),
|
||||
"NEMA_GFX": lookup_type("lv_draw_nema_gfx_unit_t"),
|
||||
"NXP_PXP": lookup_type("lv_draw_pxp_unit_t"),
|
||||
"NXP_VGLITE": lookup_type("lv_draw_vglite_unit_t"),
|
||||
"OPENGLES": lookup_type("lv_draw_opengles_unit_t"),
|
||||
"DAVE2D": lookup_type("lv_draw_dave2d_unit_t"),
|
||||
"SDL": lookup_type("lv_draw_sdl_unit_t"),
|
||||
"SW": lookup_type("lv_draw_sw_unit_t"),
|
||||
"VG_LITE": lookup_type("lv_draw_vg_lite_unit_t"),
|
||||
}
|
||||
|
||||
type = types.get(name, lookup_type("lv_draw_unit_t"))
|
||||
print(
|
||||
draw_unit.cast(type, ptr=True)
|
||||
unit.cast(target_type, ptr=True)
|
||||
.dereference()
|
||||
.format_string(pretty_structs=True, symbols=True)
|
||||
)
|
||||
|
||||
def invoke(self, args, from_tty):
|
||||
for unit in curr_inst().draw_units():
|
||||
self.dump_draw_unit(unit)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import gdb
|
||||
|
||||
from lvglgdb.value import Value
|
||||
from lvglgdb.lvgl.draw.lv_draw_task import LVDrawTask
|
||||
|
||||
|
||||
class DumpDrawTask(gdb.Command):
|
||||
"""dump draw tasks from a layer"""
|
||||
|
||||
def __init__(self):
|
||||
super(DumpDrawTask, self).__init__(
|
||||
"dump draw_task", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION
|
||||
)
|
||||
|
||||
def invoke(self, args, from_tty):
|
||||
if not args.strip():
|
||||
print("Usage: dump draw_task <layer_expression>")
|
||||
return
|
||||
try:
|
||||
val = gdb.parse_and_eval(args.strip())
|
||||
except gdb.error as e:
|
||||
print(f"Error: {e}")
|
||||
return
|
||||
|
||||
LVDrawTask.print_entries(LVDrawTask(Value(val)))
|
||||
@@ -1,6 +1,13 @@
|
||||
from .core import LVObject, ObjStyle, curr_inst, dump_obj_info, dump_obj_styles
|
||||
from .display import LVDisplay
|
||||
from .draw import LVDrawBuf
|
||||
from .draw import (
|
||||
LVDrawBuf,
|
||||
LVDrawTask,
|
||||
LVDrawUnit,
|
||||
DRAW_TASK_TYPE_NAMES,
|
||||
DRAW_TASK_STATE_NAMES,
|
||||
DRAW_UNIT_TYPE_NAMES,
|
||||
)
|
||||
from .misc import (
|
||||
LVList,
|
||||
LVStyle,
|
||||
@@ -29,6 +36,11 @@ __all__ = [
|
||||
"ObjStyle",
|
||||
"LVDisplay",
|
||||
"LVDrawBuf",
|
||||
"LVDrawTask",
|
||||
"DRAW_TASK_TYPE_NAMES",
|
||||
"DRAW_TASK_STATE_NAMES",
|
||||
"DRAW_UNIT_TYPE_NAMES",
|
||||
"LVDrawUnit",
|
||||
"curr_inst",
|
||||
"LVList",
|
||||
"LVStyle",
|
||||
|
||||
@@ -50,12 +50,11 @@ class LVGL:
|
||||
return disp.act_scr if disp else None
|
||||
|
||||
def draw_units(self):
|
||||
unit = self.lv_global.draw_info.unit_head
|
||||
from ..draw.lv_draw_unit import LVDrawUnit
|
||||
|
||||
# Iterate through all draw units
|
||||
while unit:
|
||||
yield unit
|
||||
unit = unit.next
|
||||
head = self.lv_global.draw_info.unit_head
|
||||
if int(head):
|
||||
yield from LVDrawUnit(head)
|
||||
|
||||
def image_cache(self):
|
||||
from ..misc.lv_image_cache import LVImageCache
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
from .lv_draw_buf import LVDrawBuf
|
||||
from .lv_draw_task import LVDrawTask
|
||||
from .lv_draw_unit import LVDrawUnit
|
||||
from .lv_draw_consts import (
|
||||
DRAW_TASK_TYPE_NAMES,
|
||||
DRAW_TASK_STATE_NAMES,
|
||||
DRAW_UNIT_TYPE_NAMES,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"LVDrawBuf",
|
||||
"LVDrawTask",
|
||||
"DRAW_TASK_TYPE_NAMES",
|
||||
"DRAW_TASK_STATE_NAMES",
|
||||
"DRAW_UNIT_TYPE_NAMES",
|
||||
"LVDrawUnit",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
"""
|
||||
Auto-generated draw constants from LVGL headers.
|
||||
|
||||
Do not edit manually. Regenerate with:
|
||||
python3 scripts/gen_draw_consts.py
|
||||
"""
|
||||
|
||||
DRAW_TASK_TYPE_NAMES = {
|
||||
0: "NONE",
|
||||
1: "FILL",
|
||||
2: "BORDER",
|
||||
3: "BOX_SHADOW",
|
||||
4: "LETTER",
|
||||
5: "LABEL",
|
||||
6: "IMAGE",
|
||||
7: "LAYER",
|
||||
8: "LINE",
|
||||
9: "ARC",
|
||||
10: "TRIANGLE",
|
||||
11: "MASK_RECTANGLE",
|
||||
12: "MASK_BITMAP",
|
||||
13: "BLUR",
|
||||
14: "VECTOR",
|
||||
15: "3D",
|
||||
}
|
||||
|
||||
DRAW_TASK_STATE_NAMES = {
|
||||
0: "BLOCKED",
|
||||
1: "WAITING",
|
||||
2: "QUEUED",
|
||||
3: "IN_PROGRESS",
|
||||
4: "FINISHED",
|
||||
}
|
||||
|
||||
DRAW_UNIT_TYPE_NAMES = {
|
||||
"DAVE2D": "lv_draw_dave2d_unit_t",
|
||||
"DMA2D": "lv_draw_dma2d_unit_t",
|
||||
"ESP_PPA": "lv_draw_ppa_unit_t",
|
||||
"G2D": "lv_draw_g2d_unit_t",
|
||||
"NANOVG": "lv_draw_nanovg_unit_t",
|
||||
"NEMA_GFX": "lv_draw_nema_gfx_unit_t",
|
||||
"NXP_PXP": "lv_draw_pxp_unit_t",
|
||||
"OPENGLES": "lv_draw_opengles_unit_t",
|
||||
"SDL": "lv_draw_sdl_unit_t",
|
||||
"SW": "lv_draw_sw_unit_t",
|
||||
"SW_ARM2D": "lv_draw_sw_unit_t",
|
||||
"VG_LITE": "lv_draw_vg_lite_unit_t",
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
from prettytable import PrettyTable
|
||||
|
||||
from lvglgdb.value import Value, ValueInput
|
||||
from .lv_draw_consts import DRAW_TASK_TYPE_NAMES, DRAW_TASK_STATE_NAMES
|
||||
|
||||
|
||||
class LVDrawTask(Value):
|
||||
"""LVGL draw task wrapper"""
|
||||
|
||||
def __init__(self, task: ValueInput):
|
||||
super().__init__(Value.normalize(task, "lv_draw_task_t"))
|
||||
|
||||
@property
|
||||
def type(self) -> int:
|
||||
return int(self.super_value("type"))
|
||||
|
||||
@property
|
||||
def type_name(self) -> str:
|
||||
return DRAW_TASK_TYPE_NAMES.get(self.type, f"UNKNOWN({self.type})")
|
||||
|
||||
@property
|
||||
def state(self) -> int:
|
||||
return int(self.super_value("state"))
|
||||
|
||||
@property
|
||||
def state_name(self) -> str:
|
||||
return DRAW_TASK_STATE_NAMES.get(self.state, f"UNKNOWN({self.state})")
|
||||
|
||||
@property
|
||||
def area(self) -> tuple:
|
||||
a = self.super_value("area")
|
||||
return (int(a["x1"]), int(a["y1"]), int(a["x2"]), int(a["y2"]))
|
||||
|
||||
@property
|
||||
def opa(self) -> int:
|
||||
return int(self.super_value("opa"))
|
||||
|
||||
@property
|
||||
def next(self):
|
||||
n = self.super_value("next")
|
||||
return LVDrawTask(n) if int(n) else None
|
||||
|
||||
def __iter__(self):
|
||||
node = self
|
||||
while node:
|
||||
yield node
|
||||
node = node.next
|
||||
|
||||
@property
|
||||
def preferred_draw_unit_id(self) -> int:
|
||||
return int(self.super_value("preferred_draw_unit_id"))
|
||||
|
||||
@staticmethod
|
||||
def print_entries(tasks):
|
||||
"""Print draw tasks as a PrettyTable."""
|
||||
table = PrettyTable()
|
||||
table.field_names = ["#", "type", "state", "area", "opa", "unit_id"]
|
||||
table.align = "l"
|
||||
|
||||
count = 0
|
||||
for i, t in enumerate(tasks):
|
||||
table.add_row(
|
||||
[
|
||||
i,
|
||||
t.type_name,
|
||||
t.state_name,
|
||||
t.area,
|
||||
t.opa,
|
||||
t.preferred_draw_unit_id,
|
||||
]
|
||||
)
|
||||
count += 1
|
||||
|
||||
if count == 0:
|
||||
print("No draw tasks.")
|
||||
else:
|
||||
print(table)
|
||||
@@ -0,0 +1,47 @@
|
||||
from prettytable import PrettyTable
|
||||
|
||||
from lvglgdb.value import Value, ValueInput
|
||||
|
||||
|
||||
class LVDrawUnit(Value):
|
||||
"""LVGL draw unit wrapper"""
|
||||
|
||||
def __init__(self, unit: ValueInput):
|
||||
super().__init__(Value.normalize(unit, "lv_draw_unit_t"))
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
n = self.super_value("name")
|
||||
return n.string() if int(n) else "(unnamed)"
|
||||
|
||||
@property
|
||||
def idx(self) -> int:
|
||||
return int(self.super_value("idx"))
|
||||
|
||||
@property
|
||||
def next(self):
|
||||
n = self.super_value("next")
|
||||
return LVDrawUnit(n) if int(n) else None
|
||||
|
||||
def __iter__(self):
|
||||
node = self
|
||||
while node:
|
||||
yield node
|
||||
node = node.next
|
||||
|
||||
@staticmethod
|
||||
def print_entries(units):
|
||||
"""Print draw units as a PrettyTable."""
|
||||
table = PrettyTable()
|
||||
table.field_names = ["#", "name", "idx"]
|
||||
table.align = "l"
|
||||
|
||||
count = 0
|
||||
for i, unit in enumerate(units):
|
||||
table.add_row([i, unit.name, unit.idx])
|
||||
count += 1
|
||||
|
||||
if count == 0:
|
||||
print("No draw units.")
|
||||
else:
|
||||
print(table)
|
||||
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate draw constant tables from LVGL header and source files.
|
||||
|
||||
Parses lv_draw.h for task type/state enums, and scans draw unit source
|
||||
files for name-to-struct-type mappings.
|
||||
|
||||
Usage:
|
||||
python3 scripts/gen_draw_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" / "draw" / "lv_draw_consts.py"
|
||||
|
||||
DRAW_H = LVGL_SRC / "draw" / "lv_draw.h"
|
||||
DRAW_DIR = LVGL_SRC / "draw"
|
||||
|
||||
|
||||
def parse_enum(path: Path, enum_type: str, prefix: str) -> dict[int, str]:
|
||||
"""Parse a C enum from a header file."""
|
||||
text = path.read_text()
|
||||
|
||||
pattern = rf"\}}\s*{re.escape(enum_type)}\s*;"
|
||||
m = re.search(rf"typedef\s+enum\s*\{{(.*?){pattern}", text, re.DOTALL)
|
||||
if not m:
|
||||
raise RuntimeError(f"Cannot find {enum_type} enum in {path}")
|
||||
|
||||
entries = {}
|
||||
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 = re.match(rf"({re.escape(prefix)}\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(rf"({re.escape(prefix)}\w+)", line)
|
||||
if not match:
|
||||
continue
|
||||
name = match.group(1)
|
||||
|
||||
short = name.removeprefix(prefix)
|
||||
entries[current_val] = short
|
||||
current_val += 1
|
||||
|
||||
return entries
|
||||
|
||||
|
||||
def parse_draw_unit_types(draw_dir: Path) -> dict[str, str]:
|
||||
"""Scan draw unit .c files for name-to-struct-type mappings.
|
||||
|
||||
Looks for patterns like: unit->base_unit.name = "SW";
|
||||
Then finds the corresponding struct type from the variable declaration.
|
||||
"""
|
||||
mappings = {}
|
||||
|
||||
for c_file in draw_dir.rglob("*.c"):
|
||||
text = c_file.read_text()
|
||||
|
||||
for m in re.finditer(r'(\w+)->base_unit\.name\s*=\s*"(\w+)"', text):
|
||||
var_name = m.group(1)
|
||||
unit_name = m.group(2)
|
||||
|
||||
decl = re.search(
|
||||
rf"(lv_draw_\w+_unit_t)\s*\*\s*{re.escape(var_name)}\b", text
|
||||
)
|
||||
if decl:
|
||||
mappings[unit_name] = decl.group(1)
|
||||
|
||||
return mappings
|
||||
|
||||
|
||||
def generate(
|
||||
task_types: dict[int, str],
|
||||
task_states: dict[int, str],
|
||||
unit_types: dict[str, str],
|
||||
) -> str:
|
||||
"""Generate Python source for the draw constants module."""
|
||||
lines = [
|
||||
'"""',
|
||||
"Auto-generated draw constants from LVGL headers.",
|
||||
"",
|
||||
"Do not edit manually. Regenerate with:",
|
||||
" python3 scripts/gen_draw_consts.py",
|
||||
'"""',
|
||||
"",
|
||||
]
|
||||
|
||||
lines.append("DRAW_TASK_TYPE_NAMES = {")
|
||||
for k in sorted(task_types):
|
||||
lines.append(f' {k}: "{task_types[k]}",')
|
||||
lines.append("}")
|
||||
lines.append("")
|
||||
|
||||
lines.append("DRAW_TASK_STATE_NAMES = {")
|
||||
for k in sorted(task_states):
|
||||
lines.append(f' {k}: "{task_states[k]}",')
|
||||
lines.append("}")
|
||||
lines.append("")
|
||||
|
||||
lines.append("DRAW_UNIT_TYPE_NAMES = {")
|
||||
for name in sorted(unit_types):
|
||||
lines.append(f' "{name}": "{unit_types[name]}",')
|
||||
lines.append("}")
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def main():
|
||||
task_types = parse_enum(DRAW_H, "lv_draw_task_type_t", "LV_DRAW_TASK_TYPE_")
|
||||
task_states = parse_enum(DRAW_H, "lv_draw_task_state_t", "LV_DRAW_TASK_STATE_")
|
||||
unit_types = parse_draw_unit_types(DRAW_DIR)
|
||||
|
||||
src = generate(task_types, task_states, unit_types)
|
||||
OUTPUT.write_text(src)
|
||||
print(
|
||||
f"Generated {OUTPUT} ({len(task_types)} task types, "
|
||||
f"{len(task_states)} task states, {len(unit_types)} unit types)"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user