Remove completed planning documents (WAYLAND_ROADMAP.md, ACTION_PLAN.md,
ACTION_PLAN_PROMPTS.md). Update ARCHITECTURE.md and BACKEND_ABSTRACTION.md
with current file sizes and completion status. Add ROADMAP.md covering
remaining work prioritized for daily-driver use.
Three issues prevented WPrefs from being managed by wmaker in X11:
1. W_X11OpenDisplay: XOpenDisplay("") fails; treat empty string as NULL
so it falls back to DISPLAY env var.
2. wevent_x11.c/wevent.c: MapRequest and ConfigureRequest events must
bypass WINGs view lookup (both _view_target in x11_fetch and
W_GetViewForWindow in WMHandleEvent) to reach DispatchEvent via
extraEventHandler.
3. wview_x11.c: x11_create_root must use RootWindow() not
scr->rootWin (which may be an intermediate window created by
RCreateContext for non-default visuals). Otherwise toplevel
WINGs windows are created as children of the wrong parent and
their MapRequest never reaches the WM.
WPrefs.app/main.c defaults display_name to "" (empty string).
W_X11OpenDisplay passed this directly to XOpenDisplay, but
XOpenDisplay("") is not equivalent to XOpenDisplay(NULL) — the
former fails while the latter uses the DISPLAY environment variable.
Treat empty display string as NULL so XOpenDisplay falls back to
the environment variable.
Also remove debug traces from event.c and x11_backend.c.
SubstructureRedirect events (MapRequest, ConfigureRequest) are delivered
to the root window. Since the root window has a WINGs view registered
(the WMScreen root view), W_GetViewForXWindow resolves it and
WMHandleEvent dispatches to the view handlers instead of falling through
to extraEventHandler (DispatchEvent).
This caused WINGs-based apps like WPrefs to never have their windows
managed by the WM - the MapRequest was silently consumed.
Fix: skip the view lookup for MapRequest and ConfigureRequest events so
they always reach DispatchEvent via extraEventHandler.
Menu entry painting:
- Add drawFrame() calls for 3D beveled entries (WTEX_SOLID)
- Add menu_text_clearance to text Y positioning
- Add left-side indicator drawing (radio/check/snap pixmaps)
- Add indicator X offset (MENU_INDICATOR_SPACE)
- Fix disabled+selected text color
- Fix texture-aware background clearing
- Add cascade arrow for submenu entries
Balloon tooltips:
- Implement x11_balloon_show() with text and mini-preview rendering
- Previously was a no-op stub
Clip arrows:
- Draw workspace-switching triangles on clip icon
- Previously stubbed out
Event handling:
- Add MapRequest, ConfigureRequest, ReparentNotify translation to
xev_to_wme() in WINGs/wevent_x11.c
- These WM-critical events were silently dropped, preventing new X
clients (e.g. xterm) from being managed
Selection and mapping events have mask 0 in the type table (matching
X11 semantics where they bypass event masks). Change the dispatch
check to deliver events with mask==0 to all registered handlers,
so these events are not silently dropped.
The wmeMaskForType table had WME_CLIENT_MESSAGE mapped to 0, so
WMHandleEvent never dispatched client messages to registered handlers.
The window close handler (registered with ClientMessageMask) was never
called. Set the correct mask so the deleteWindow protocol works.
The poll-based wlclient_dispatch with timeout 0 missed events that
were already buffered. Use prepare_read/read_events/dispatch_pending
which reads whatever is available on the socket without polling.
In-process WINGs clients have their own wl_display connection that
needs to be dispatched for xdg_toplevel.close to be received. Add
W_WLClientDispatchPending() and call it each main loop iteration
so protocol events reach the client-side handlers promptly.
During W_RealizeView, child views with mapWhenRealized triggered
wl_map which called wl_redisplay on the toplevel. This ran expose
handlers on a partially-realized widget tree, causing backing image
corruption from resizes during redisplay.
Fix: track realization depth with W_WL_Realizing counter. wl_map
skips redisplay when realization is in progress. After the outermost
W_RealizeView completes, trigger a single redisplay of the toplevel
to paint everything correctly.
The fill_rectangles call used view->size which can exceed the backing
image dimensions when a view is resized during realization without its
backing being reallocated. This wrote past the allocated buffer,
corrupting the heap and causing later pixman_image_composite32 crashes.
Use pixman_image_get_width/height of the actual backing instead.
Remove the verbose COMPOSITE fprintf and BAD composite warning that
were used for debugging. Keep the source/destination bounds clipping
as it correctly handles the case where view->size diverges from the
backing image dimensions during realization.
The font prefs panel crashed because wl_resize called wl_redisplay
while already inside a redisplay. This caused expose handlers to
trigger more resizes, creating unbounded recursion and memory growth
(backing images growing to 1024+ pixels wide). Skip the redisplay
from wl_resize when wl_redisplay_depth > 0 — the ongoing parent
redisplay will composite the resized child at its new size.
The previous source-only clamp was insufficient. Also check that both
src and dst have non-NULL pixel data, and clip the composite region to
the destination image dimensions to prevent out-of-bounds writes.
During W_RealizeView the WME_EXPOSE handler can trigger child resizes
that make view->size exceed the backing image dimensions. Clamp the
composite width/height to the source image actual size to prevent
out-of-bounds reads in pixman_image_composite32.
Font prefs panel crashed in pixman_image_composite32 during W_MapView
because a child view had zero width/height. Skip compositing for
zero-sized views.
In Wayland mode the X11 display is NULL. WMCreateColorWell calls
WMRegisterViewForDraggedTypes which calls XInternAtom, crashing.
Bail early when display is NULL since X11 DnD is not available.
The xdg_toplevel.close protocol message requires the client to dispatch
its wl_display, but in-process WINGs clients may not do so promptly.
Directly enqueue the WME_CLIENT_MESSAGE/deleteWindowAtom event from
wl_client_close so the closeAction fires on the next WINGs event drain.
The close button press from the compositor sends xdg_toplevel.close,
but the handler only set a flag without notifying the WINGs event
system. Synthesize a WME_CLIENT_MESSAGE with deleteWindowAtom so
the window closeAction callback fires and the app shuts down
gracefully.
Menu background colors were hardcoded. Now uses:
- scr->select_color for selected item background
- scr->menu_item_texture->normal for unselected background
- scr->menu_item_texture->dim/light/dark for cascade arrow
Text colors already used scr->select_text_color, mtext_color, dtext_color.
Replace the filled triangle with three lines matching the upstream X11
implementation: a dim top edge, light bottom edge, and dark left edge,
forming a 3D right-pointing triangle indicator for submenu entries.
The triangle was drawn with increasing width from left to right,
making it point left. Reverse the vertical span so the widest part
is on the left and it tapers to a point on the right.
WINGs timers (used by workspace name fade animation) were never firing
because the compositor event loop never called W_CheckTimerHandlers.
This function is normally invoked by WMNextEvent, but the compositor
uses a custom loop. Add the call so timers fire each iteration.
The workspace name display was rendering with an opaque black background
and never fading out. Fix by:
- Using transparent (alpha=0) background instead of black
- Returning RGBA images from badge_render and badge_get_background
- Preserving alpha channel in badge_update instead of hardcoding 0xFF
This allows the fade animation (RCombineImagesWithOpaqueness) to work
correctly, blending text with decreasing opacity over a transparent
background until the badge disappears.
WINGs button callbacks trigger wl_redisplay which commits the client
surface, but the compositor only processes that commit on the next
wl_event_loop_dispatch. Add a zero-timeout dispatch after handling
WINGs events so client commits are processed immediately, making
the next scheduled frame render the updated content.
Also revert posF redisplay back to posVF (posF caused gray flash).
Redisplaying just posVF was insufficient; redisplay the entire posF
parent frame to ensure the icon position indicator renders at its
new location immediately after a button click.
The extra wlr_scene_output_commit broke frame pacing in nested mode by
consuming the host frame callback. Instead, move wlr_output_schedule_frame
to after wl_event_loop_dispatch so client commits processed during
dispatch are rendered on the next frame event.
Client surface commits arrive during wl_event_loop_dispatch but may be
processed after the frame event has already rendered the old state. Add
an additional wlr_scene_output_commit after dispatch to immediately
pick up any new client content, fixing the one-interaction-behind
visual update issue in WINGs clients like WPrefs.
Without an explicit wl_display_flush after wl_surface_commit, protocol
requests were buffered until the next event loop iteration. This caused
visual updates (like the icon position preview in WPrefs) to appear one
interaction behind.
The posV widget was moved/resized but the parent frame (posVF) was
never redrawn, so the visual update only appeared on the next event
that triggered a redraw. Add WMRedisplayWidget after the move.
SIGUSR2 is fundamentally unsafe in a multi-threaded compositor: library
threads (pango fontconfig) created after pthread_sigmask receive the
signal and crash. The Wayland event loop already has inotify watching
the defaults directory, so file changes are detected automatically
without any explicit signaling.
The early pthread_sigmask(SIG_BLOCK, SIGUSR2) is retained as a safety
measure to prevent stray SIGUSR2 from terminating the compositor.
SIGUSR2 was being delivered to pango fontconfig threads instead of the
signalfd because those threads were created before the signal mask was
set in wl_event_loop_run. Block SIGUSR2 at the very start of real_main
so all threads (including library-spawned ones) inherit the blocked
mask and the signalfd is the sole recipient.
SIGUSR2 was not in the signalfd set, so it was delivered asynchronously
to the old signal handler which set WSTATE_NEED_REREAD. But the Wayland
event loop never checked that state flag, and the signal delivery itself
could crash the compositor.
Add SIGUSR2 to the signalfd set and handle it synchronously within the
event loop by calling wDefaultsCheckDomains directly.
The previous getppid() approach sent SIGUSR2 to the terminal process
(foot) rather than wmaker, killing the shell. Now wmaker exports
WMAKER_PID at startup and wl_send_wm_command reads it to signal the
correct process.
The Wayland event backend stub for send_wm_command was a no-op, so
WPrefs settings changes never triggered a reread in the running wmaker.
Now sends SIGUSR2 to the parent process (wmaker) on "Reconfigure",
causing wDefaultsCheckDomains to run and apply changes immediately.