Feat/wayland driver binding - #413
Conversation
MicroPython commit e00daa3a ("py/binary,objint: Add overflow checks and
int.to_bytes(signed=True)") renamed mp_obj_int_to_bytes_impl() to
mp_obj_int_to_bytes() with a new signature (adding is_signed and
overflow_check parameters), for the 1.29 release. The generated binding
called the old name unconditionally in mp_obj_get_ull(), so building against
1.29 or later failed to link with an undefined reference to
mp_obj_int_to_bytes_impl.
Guard the call on MICROPY_VERSION so the binding builds against both APIs.
beb5693 to
8455c89
Compare
|
Note on the #411 dependency: it's not just a nice-to-have. CI here now covers both desktop drivers the same way: |
There was a problem hiding this comment.
3 issues found across 14 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/micropython_lvgl_ci.yml">
<violation number="1" location=".github/workflows/micropython_lvgl_ci.yml:117">
P2: The `sleep 1` before running Wayland tests creates a race condition. On a loaded CI runner, Weston may take more than 1 second to initialize its socket, causing the test command to fail with a connection error. Replace the fixed sleep with a polling loop that waits for the socket file `$XDG_RUNTIME_DIR/wayland-ci` to appear (with a timeout).</violation>
</file>
<file name="micropython.mk">
<violation number="1" location="micropython.mk:41">
P3: Incremental Wayland builds recompile generated protocol glue on every `make`, because this parse-time scanner invocation rewrites a listed C source unconditionally. Model the XML/header/source generation as Make targets with XML/scanner prerequisites so unchanged outputs are retained and compilation is ordered normally.</violation>
</file>
<file name="README.md">
<violation number="1" location="README.md:195">
P2: The Wayland example creates a default event_loop (which calls lv.task_handler = lv_timer_handler() periodically) AND also calls lv.wayland_timer_handler() in the manual while loop. The event_loop source explicitly says lv.wayland_timer_handler() must be used *instead of* the plain handler since it wraps the same inner call with Wayland event processing. Remove the event_loop line — the manual while loop with lv.wayland_timer_handler() is the complete replacement.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| export XDG_RUNTIME_DIR=/tmp/xdg-runtime | ||
| weston --backend=headless --socket=wayland-ci --idle-time=0 & | ||
| WESTON_PID=$! | ||
| sleep 1 |
There was a problem hiding this comment.
P2: The sleep 1 before running Wayland tests creates a race condition. On a loaded CI runner, Weston may take more than 1 second to initialize its socket, causing the test command to fail with a connection error. Replace the fixed sleep with a polling loop that waits for the socket file $XDG_RUNTIME_DIR/wayland-ci to appear (with a timeout).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/micropython_lvgl_ci.yml, line 117:
<comment>The `sleep 1` before running Wayland tests creates a race condition. On a loaded CI runner, Weston may take more than 1 second to initialize its socket, causing the test command to fail with a connection error. Replace the fixed sleep with a polling loop that waits for the socket file `$XDG_RUNTIME_DIR/wayland-ci` to appear (with a timeout).</comment>
<file context>
@@ -102,7 +103,24 @@ jobs:
+ export XDG_RUNTIME_DIR=/tmp/xdg-runtime
+ weston --backend=headless --socket=wayland-ci --idle-time=0 &
+ WESTON_PID=$!
+ sleep 1
+ WAYLAND_DISPLAY=wayland-ci LV_MP_TEST_DRIVER=wayland \
+ MICROPY_MICROPYTHON=ports/unix/build-lvgl/micropython \
</file context>
| WIDTH = 480 | ||
| HEIGHT = 320 | ||
|
|
||
| event_loop = event_loop() |
There was a problem hiding this comment.
P2: The Wayland example creates a default event_loop (which calls lv.task_handler = lv_timer_handler() periodically) AND also calls lv.wayland_timer_handler() in the manual while loop. The event_loop source explicitly says lv.wayland_timer_handler() must be used instead of the plain handler since it wraps the same inner call with Wayland event processing. Remove the event_loop line — the manual while loop with lv.wayland_timer_handler() is the complete replacement.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 195:
<comment>The Wayland example creates a default event_loop (which calls lv.task_handler = lv_timer_handler() periodically) AND also calls lv.wayland_timer_handler() in the manual while loop. The event_loop source explicitly says lv.wayland_timer_handler() must be used *instead of* the plain handler since it wraps the same inner call with Wayland event processing. Remove the event_loop line — the manual while loop with lv.wayland_timer_handler() is the complete replacement.</comment>
<file context>
@@ -178,6 +178,57 @@ Driver code is under `/driver` directory.
+WIDTH = 480
+HEIGHT = 320
+
+event_loop = event_loop()
+disp_drv = lv.wayland_window_create_simple(WIDTH, HEIGHT, "MicroPython-LVGL")
+pointer = lv.wayland_pointer_create()
</file context>
| LVGL_WAYLAND_XDG_SHELL_XML := $(WAYLAND_PROTOCOLS_DIR)/stable/xdg-shell/xdg-shell.xml | ||
| $(shell mkdir -p $(LVGL_WAYLAND_PROTOCOLS_DIR)) | ||
| $(shell $(WAYLAND_SCANNER) client-header $(LVGL_WAYLAND_XDG_SHELL_XML) $(LVGL_WAYLAND_PROTOCOLS_DIR)/wayland_xdg_shell.h) | ||
| $(shell $(WAYLAND_SCANNER) private-code $(LVGL_WAYLAND_XDG_SHELL_XML) $(LVGL_WAYLAND_PROTOCOLS_DIR)/wayland_xdg_shell.c) |
There was a problem hiding this comment.
P3: Incremental Wayland builds recompile generated protocol glue on every make, because this parse-time scanner invocation rewrites a listed C source unconditionally. Model the XML/header/source generation as Make targets with XML/scanner prerequisites so unchanged outputs are retained and compilation is ordered normally.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At micropython.mk, line 41:
<comment>Incremental Wayland builds recompile generated protocol glue on every `make`, because this parse-time scanner invocation rewrites a listed C source unconditionally. Model the XML/header/source generation as Make targets with XML/scanner prerequisites so unchanged outputs are retained and compilation is ordered normally.</comment>
<file context>
@@ -20,6 +20,31 @@ ifeq ($(UNAME_S),Darwin)
+LVGL_WAYLAND_XDG_SHELL_XML := $(WAYLAND_PROTOCOLS_DIR)/stable/xdg-shell/xdg-shell.xml
+$(shell mkdir -p $(LVGL_WAYLAND_PROTOCOLS_DIR))
+$(shell $(WAYLAND_SCANNER) client-header $(LVGL_WAYLAND_XDG_SHELL_XML) $(LVGL_WAYLAND_PROTOCOLS_DIR)/wayland_xdg_shell.h)
+$(shell $(WAYLAND_SCANNER) private-code $(LVGL_WAYLAND_XDG_SHELL_XML) $(LVGL_WAYLAND_PROTOCOLS_DIR)/wayland_xdg_shell.c)
+
+CFLAGS_USERMOD += $(WAYLAND_CFLAGS_USERMOD) -DMICROPY_WAYLAND=1 -I$(LVGL_WAYLAND_PROTOCOLS_DIR) -I$(USERMOD_DIR)/lvgl
</file context>
Summary
Adds LVGL's native Wayland driver as a working alternative to the SDL
driver, and fixes a regression that's been silently keeping every LVGL
desktop window driver (SDL included) out of the Python bindings entirely.
lv_sdl_window_create()can segfault deep inside the proprietary NVIDIAdriver as soon as a real window is presented, on Wayland sessions with an
NVIDIA GPU. This is #46, filed in 2019 and auto-closed stale in 2020
without a fix - I hit it firsthand and confirmed it's still present today,
5+ years later. The maintainers' own diagnosis at the time: this binding's
SDL driver deliberately runs SDL on MicroPython's own thread (for LVGL
binding thread-safety), unlike the standalone
lv_driversSDL simulator,which refreshes on its own dedicated thread and doesn't hit the bug.
LVGL's native Wayland driver (
lvgl/src/drivers/wayland) sidesteps thewhole crash class: it presents via
wl_shmand never creates a GL/EGLcontext at all. It's already upstream in LVGL, just never wired into this
binding's build or exposed to Python - both are fixed here.
While wiring Wayland up, I found gen_mpy.py hasn't been able to see any
desktop window driver - SDL included - since 3f32386 (2026-05-02) switched
its input from
lvgl.h(which pulls insrc/drivers/lv_drivers.h) tolvgl_private.h(which only pulls in_private.hcounterparts for thelibinput/evdevdrivers).lv.sdl_window_createetc. have simply notexisted as Python attributes since then, regardless of
LV_USE_SDL- thisalso silently broke this repo's own
tests/testdisplay.py's interactivemode. Fixed by
gen/lv_mpy_drivers.h, which gen_mpy now parses instead: itpulls in
lvgl_private.handsrc/drivers/lv_drivers.h, so any driverenabled in
lv_conf.hgets bound the same way widgets already are.gen/lv_mpy_drivers.cadds a binding-friendlylv_wayland_window_create_simple()wrapper, since gen_mpy can't bindlv_wayland_window_create()'s close-callback parameter (nouser_data-carrying struct argument for it to hook into).
micropython.mkautodetectswayland-client/wayland-cursor/xkbcommonvia
pkg-config, the same pattern SDL already uses, and generates thexdg-shell protocol glue at build time via
wayland-scannerinto$(BUILD)- nothing checked into thelvglsubmodule.LV_COLOR_DEPTHisforced to 32 only when Wayland is actually compiled in, since
lv_wayland_window_create()hangs rather than failing fast on acompositor that doesn't advertise
wl_shmRGB565 support (optionalper-spec, unlike XRGB8888/ARGB8888).
This is based on #411, which this build needed to even link against a
current MicroPython. Expecting that one to land first.
Testing
Rather than duplicating
tests/display/tests/indevper driver,testdisplay.py/testrunner.py/lv_utils.pygain aDRIVERselector(
"sdl"/"wayland", or theLV_MP_TEST_DRIVERenv var), so the existingsuites run against either driver unchanged. Verified
tests/display/basic.pyagainst Wayland via a headlesswestoncompositor - output matches the existing
.expbyte for byte, no newexpected-output file needed. CI installs the Wayland build deps and
weston, and runstests/displayagainst Wayland in a new job step.Doesn't attempt to fix the underlying NVIDIA/SDL bug itself (#46) - a
proprietary-driver issue outside this repo's control - or the window
decorations abort (
lv_wl_window_decorations.c, unrelated, left atupstream's own default of off).
Generative AI
I used generative AI tools when creating this PR, but a human has checked
the code and is responsible for the code and the description above.
Summary by cubic
Adds LVGL’s Wayland window driver as a safe alternative to SDL on Linux, restores desktop driver APIs to the Python bindings, and ensures compatibility with MicroPython 1.29.
New Features
lv.wayland_window_create_simple(...),lv.wayland_pointer_create(),lv.wayland_keyboard_create(),lv.wayland_timer_handler(),lv.wayland_window_is_open().wayland-client,wayland-cursor,xkbcommon,wayland-scanner, andwayland-protocolsare found viapkg-config; generatesxdg-shellglue at build time.handler_cbfor Wayland.waylandviaLV_MP_TEST_DRIVER; CI uses headlessweston.LV_COLOR_DEPTH=32when Wayland is compiled to ensure a supportedwl_shmformat.Bug Fixes
gen_mpygen/lv_mpy_drivers.h(includessrc/drivers/lv_drivers.h) and adds alv_wayland_window_create_simple()wrapper to avoid an unbindable close-callback.mp_obj_int_to_bytes()with version guards.Written for commit 8455c89. Summary will update on new commits.