- wl_map: set mapped=1 before calling wl_redisplay (was bailing because
mapped flag wasn't set yet)
- wl_view_commit_backing_impl: enable scene node when content is committed
(frame_buf was created disabled, never enabled before frame was mapped)
- fake_leader_create: return a real frame_buf ID (was returning 0)
- client_reparent: handle frame_buf-to-frame_buf reparenting
- client_reparent: get WINGs view size from view table
- window_map: enable grandchildren (dialog content is grandchild of frame)
- window_move: move grandchildren when parent moves
- dialog.c: guard scr->w_visual for Wayland mode in Info panel
- wl_client_reparent: create child frame_buf for WINGs views, connect
backing pixman image so widgets paint into the scene graph
- View lookup table: map synthetic IDs to W_View* for event dispatch
- render_wl_pixmap_copy: detect view IDs and copy into backing image
- W_ViewCommitBacking callback: commit frame_buf after WINGs paint
- WMHandleEvent: use view table in Wayland mode (was always NULL)
- wtextfield.c paintCursor: use render backend draw_line instead of
XDrawLine (crash fix for Run dialog)
- wl_draw_string: remove ascent subtraction, y is TOP position (matches
X11 Pango behavior where pango_xft_render_layout takes top-left)
- Menu entry text: pass top position (y+3) not baseline (y+3+fh-1)
- Icon title: pass y=0 (top) not y=fh (baseline)
- frame_paint_titlebar: pass centered top not baseline
- frame_configure: compute absolute position from parent for children
(was setting absolute pos to relative coords, warping to 0,0)
- Titlebar buffer: always render full frame width, resize if needed
- Frame scene node: disable when no resizebar (transparent, not needed)
- Always call frame_paint after show/hide button (TS_NEW was skipping it)
frame_buf_at was returning the parent frame instead of child windows
(titlebar, content area) because the parent was later in the list.
Now prefers children (frame_bufs with parent_id set) over parents.
This fixes menu titlebar click detection — clicking the titlebar now
dispatches to titlebarMouseDown/menuTitleMouseDown instead of
menuMouseDown, enabling menu pinning and drag-to-move.
On SIGINT/SIGTERM:
- signalfd handler sets terminate flag and exits the event loop
- After loop exit, remove wlroots pointer listeners to prevent
assertion failure in wlr_pointer_finish
- Reset SIGTERM/SIGINT/SIGHUP to SIG_DFL after signalfd takes over
to prevent the old handleExitSig from competing
pointer_get_position was a noop returning (0,0), causing findMenu to
always return NULL. This made the menu close on every button release
regardless of pointer position. Now returns wl_state.pointer_x/y which
are updated on every pointer motion event.
With this fix, the root menu stays open when the pointer is over it on
release (matching X11 behavior), allowing entry selection or titlebar
pinning.
Add wl_frame_paint_button which renders button icons from XPM data
into the parent frame buffer. Called both from the vtable slot and
from wl_frame_paint during titlebar rendering. Uses PRED_CLOSE_XPM
and PRED_ICONIFY_XPM parsed lazily via RGetImageFromXPMData.
Also removes XEvent from all WFrameWindow callbacks (on_click_left,
on_click_right, on_dblclick_right, on_mousedown_resizebar) — they
now take WMEvent*.
wl_window_unmap now disables child scene nodes when the parent is
hidden. This fixes the menu entries staying visible after the menu
frame (titlebar) was unmapped. The entire menu now disappears
together, matching X11 behavior where the brother menu closes on
button release.
The menu frame was created at 1x1 and resized via wFrameWindowResize
which calls window_resize. But wl_window_resize didn't handle
frame_bufs — it only handled toplevel views. Now it reallocates the
pixman image when dimensions change, so the frame has the correct
size when wl_frame_paint renders the title bar.
- Entry separators: light line on top, dark line on bottom between entries
- Left/right bevel edges on each entry
- Cascade arrow (triangle) for submenu entries
- Paint menu frame title bar (wl_frame_paint) when menu is shown
- Matches X11 menu_draw_frame behavior
In X11, child windows are only visible when the parent is mapped.
In Wayland, all frame_bufs are independent scene nodes. The menu
content window was being mapped at creation time (wMenuCreate),
making it visible even though the parent frame was offscreen/hidden.
Fix: wl_window_map skips enabling a child node if its parent's
scene node is not yet enabled.
Child windows (menu content area, titlebar, resizebar) need their own
pixman buffers for painting. Previously frame_create_child returned a
bare synthetic ID with no buffer, so menu_paint_entry found nothing.
Also add parent-child tracking: when a parent frame moves or maps,
all children are repositioned/enabled automatically. Children store
their relative offset and parent ID.
The deadlock: frame_timer_cb called WMHandleEvent which entered
menuMouseDown's modal WMMaskEvent loop. That blocked inside
wl_event_loop_dispatch (reentrant), preventing new events.
Fix:
1. Replace wl_display_run() with custom loop that drains WINGs
events BETWEEN wl_event_loop_dispatch calls (not inside timer)
2. Make WINGs wl_flush() pump wlroots (call dispatch_fn) so
WMMaskEvent modal loops get new events without reentrancy
3. Fix wBalloonHide NULL dereference (scr->balloon not allocated)
4. Remove XEvent dependency from wSelectWindows (takes x,y now)
5. Remove debug traces
Right-click on background now opens the root menu.
- frame_buf_at(): hit-test finds which WM frame_buf is under pointer
- Button/motion events set window field to frame_buf ID or root_win
- Root window gets synthetic ID (0x0FFFFFFF) in Wayland mode
- Right-click on background → handleButtonPress → OpenRootMenu path
- wSelectWindows: remove XEvent dependency, take (x, y) coordinates
- Zero X11 symbols in event.o and moveres.o (verified with nm)
Text rendering uses the existing WINGs PangoFT2 backend — WMDrawString
renders directly into the frame buffer's pixman_image_t.
- Menu entries: real text via PangoFT2 (entry text + shortcut labels)
- Dock/clip icon titles: rendered with icon_title_font
- Frame titlebar: solid background + centered title text
- Frame resizebar: beveled grey bar
All text is anti-aliased (PangoFT2 alpha compositing onto pixman).
- Clip/drawer icons: reuse wl_appicon_paint (tile rendering)
- Menu entries: solid background fill (grey normal, blue selected)
with placeholder text line. Full text rendering TODO.
- Menu frame: beveled border (light top/left, dark bottom/right)
- All rendering commits to scene graph via wlr_scene_buffer_set_buffer
XWayland was started eagerly (at compositor init) which caused a
segfault when XWayland couldn't connect back to the Wayland socket
(race condition in nested mode). Since the WM UI is now pure Wayland
(no X11 rendering), XWayland is only needed for X11 client apps.
Switch to lazy mode: XWayland starts only when the first X11 client
connects. This eliminates the startup wait loop and the associated
crash. Also set xwayland_ready=true in the server destroy handler
as a safety net.
- wl_appicon_paint: renders dock icons by compositing the icon tile
(RImage) with the app image, then blitting RGBA→ARGB32 into the
frame buffer's pixman image and committing to the scene graph.
- Color parser: add grayNN/greyNN pattern support (gray0-gray100)
and rgb:RR/GG/BB X11 color format. All default textures now parse.
- Dock paint trigger: add explicit dockIconPaint calls at end of
wDockCreate and wDockRestoreState since Wayland has no Expose
events on window map.
The dock icon tile is now visible in the compositor window.
Three root causes fixed:
1. RCreateContext crash: wScreenInit called RCreateContext(dpy=NULL).
Fix: add RCreateContextWayland() to wrlib — creates a minimal 32-bit
ARGB context without X11. Wayland path in screen.c now always taken
when is_wayland (removed incorrect XWayland fallthrough).
2. Color parsing: parse_color and get_color_for_colormap were no-ops.
Fix: implement real color parser in wl_backend supporting #RGB,
#RRGGBB, #RRRRGGGGBBBB, rgb:RR/GG/BB, and common named colors.
Texture creation (solid, gradient) now works in Wayland mode.
3. RConvertImage crash: wrlib tried XCreateImage with NULL display.
Fix: RConvertImage/RConvertImageMask return success with pixmap=0
when context->dpy is NULL. Callers don't use pixmaps in Wayland
mode (rendering goes through vtable slots to pixman buffers).
4. Workspace crash: Wayland path skipped wWorkspaceNew(), so
workspace_count=0 when menu tried to access workspace entries.
Fix: add wWorkspaceNew + netwm_init to Wayland screen init path.
Result: compositor starts, event loop runs, window is visible.
WMAKER_USE_WAYLAND=1 wmaker now shows a compositor window.
- wmspec.c, xdnd.c, xmodifier.c: pure X11 mechanism code now compiled
as part of x11_backend.o (included via #include in x11_backend.c).
Removed from main link (Makefile modified).
- Policy file calls to NETWM/XDND functions replaced with vtable dispatch:
netwm_process_client_message, xdnd_process_client_message,
xdnd_process_selection, xdnd_make_awareness,
netwm_check_client_hint_change, netwm_position_splash,
netwm_check_initial_frame_state
- x11_prop_get_check_property now returns wfree-able memory (callers
use wfree instead of XFree)
RESULT: nm -u *.o | grep ' U X' returns ZERO for all non-backend .o files.
The backend abstraction is architecturally complete.
- motif.c: wGetMWMHints moved entirely to x11_backend.c (inlined into
x11_motif_get_hints)
- colormap.c: wColormapInstallForWindow inlined into x11_colormap_install;
policy file now contains only vtable dispatch stubs
- resources.c: wGetColorForColormap and wFreeColor now dispatch through
vtable; X11 implementation inlined into x11_backend.c
- xmodifier.c: wXModifierInitialize now called from x11_extensions_init
(removed from main.c)
motif.o, colormap.o, resources.o verified X11-free with nm.
xmodifier.o retains X11 symbols (mechanism code, only reached through
X11 backend — candidate for physical move to backend/ directory).
Add vtable slots: icon_select, icon_change_title, clip_icon_paint,
drawer_icon_paint, balloon_init, do_kaboom, menu_paint_entry,
menu_draw_frame.
Replace policy call sites with vtable dispatch. Make paintEntry and
drawFrame in menu.c non-static as wMenuPaintEntry/wMenuDrawFrame.
X11 backend calls the mechanism functions; Wayland backend is no-op.
wFrameWindowPaint now only reached through X11 backend's frame_paint.
- wScreenInit Wayland fast path now creates RContext on XWayland display
(needed for wTextureRenderImage and wraster image loading)
- Also creates WINGs screen (wmscreen) so WMDrawString/WMFontHeight work
- Add wl_get_x_display() accessor for screen.c to get XWayland Display*
- Remove if(dpy) guard around wScreenRestoreState in startup.c so dock,
clip, menus, and window management run in Wayland mode
Status: startup reaches menu creation but hangs in wMenuCreate due to
Xlib operations on XWayland during WINGs font/screen init. The pump
thread is running but something in the menu creation path is spinning.
Next step: identify and eliminate the remaining XWayland dependency in
the menu creation path.
The global 'dpy' variable was set to the XWayland Display*, causing
120+ direct Xlib calls in the WM core (XMapWindow, XClearWindow, etc.)
to pass synthetic frame buffer IDs to XWayland — hanging the startup.
Fix: wl_display_open returns NULL so global dpy=NULL. All existing
'if (dpy)' guards in the WM core now correctly skip Xlib calls.
The Wayland backend uses wl_state.x_display directly for the few
XWayland operations it needs (atom interning, screen info, GC alloc
for the no-focus window).
This unblocks startup — the event loop now runs successfully.
Architecture: frames are pixman images committed to wlr_scene_buffer
nodes — no XWayland dependency for WM UI rendering.
Key changes:
- Add wl_frame_buf struct (pixman_image_t + wlr_buffer + scene_buffer)
- Implement wlr_buffer_impl for frame buffers (begin_data_ptr_access)
- frame_create_toplevel: allocates pixman buffer + scene node
- frame_create_child: returns synthetic ID (sub-regions of parent)
- frame_configure: repositions scene node, reallocates on resize
- frame_destroy: destroys scene node and drops buffer
- frame_paint: renders titlebar texture (via wTextureRenderImage),
title text (via WMDrawString on pixman WMDrawable), and resizebar
into the pixman buffer, then commits to scene graph
- Add frame_paint vtable slot to WMBackend (backend.h)
- wFrameWindowPaint: calls wm_backend->frame_paint first; falls
through to Xlib code only if backend returns 0
- window_map/unmap/move: handle frame buffers via scene node ops
- stacking_raise/lower: handle frame buffers
- window_get_rect: return frame buffer geometry
- Remove all XWayland calls from frame/window lifecycle
The OR (override_redirect) surface handling remains for XWayland
client windows that use override_redirect (tooltips, menus from
X11 apps). The WM's own UI is now pure Wayland.
Key changes in wl_backend.c:
- wl_atom_intern: call XInternAtom on XWayland display (was returning 0)
- Add wl_or_view struct to track override_redirect XWayland surfaces
- handle_xwayland_new_surface: composite OR surfaces via scene graph
- frame_create_toplevel/child: create real X11 windows on XWayland
- frame_destroy/configure: delegate to Xlib
- window_map/unmap/move/resize: fall through to Xlib for frame windows
- stacking_raise/lower: update scene graph + Xlib fallback
- event_flush/sync: call XFlush/XSync on XWayland display
- window_set_border/border_width/clear: delegate to Xlib
- window_get_rect: query XWayland geometry for non-toplevel windows
Note: frame creation via XWayland is a temporary bridge. The correct
architectural approach is native scene-buffer rendering with pixman.
wl_display_open now pumps the Wayland event loop until XWayland fires its
"ready" signal, then calls XOpenDisplay() and returns the Display* to the
core WM. This makes dpy non-NULL so screen.c takes the full X11 code path:
RCreateContext, WINGs screen, workspaces, root menus, and EWMH hints all
initialise via the WM's own XWayland connection.
- wl_handle_x11_events: new fd watcher registered on ConnectionNumber(dpy)
so WINGs menus, dialogs, and keyboard events are dispatched during
wl_display_run() via XPending/XNextEvent/WMHandleEvent
- wl_screen_open: sets root_win/depth/colormap from DefaultRootWindow(dpy)
when bridge is active so RCreateContext gets valid X11 screen values
- wl_screen_create_internals: creates a 1x1 info_window on XWayland for
_NET_SUPPORTING_WM_CHECK and clipboard selection ownership
- handle_xwayland_map: guard against w_global.screen_count==0 (fires before
StartUp() in edge cases) to avoid assert in wScreenWithNumber
- wl_display_close: removes fd watcher and calls XCloseDisplay before
killing XWayland to avoid stale fd events
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs prevented nested Wayland mode (inside sway) from starting:
1. The wlr_wl_backend fires new_input during wlr_backend_start(), before
wl_state.seat exists (seat is created later in wl_display_open).
wlr_seat_set_keyboard(NULL, kb) → SIGSEGV. Guard with if (seat).
2. Sway's initial xdg-shell configure sends width=height=0 ("compositor
chooses"). Without an explicit mode, wlr_output_commit_state tries to
create a 0×0 GBM swapchain → crash in gbm_bo_create. Fall back to
1920×1080 when no preferred mode is available.
Also install XSetErrorHandler at the top of wl_display_open so the monitor
process's crash-dialog BadMatch (from sway's internal XWayland at :0) is
suppressed rather than printed in Xlib default format.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Wayland backend runs with a live XWayland Display* once XWayland
fires its ready signal. Without an error handler, benign X errors
(BadMatch on X_SetInputFocus, BadWindow during window teardown) reach
Xlib's default handler and produce noisy output.
Add wl_catch_xwayland_error() — same permissive policy as the X11
backend's x11_catch_error() — and install it via XSetErrorHandler in
wl_register_error_handlers(). Include <X11/Xproto.h> for the
X_SetInputFocus request code constant.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs prevented XWayland client windows from appearing:
1. flags.mapped never set for XWayland windows (window.c)
The condition `else if (!dpy)` is false when dpy is the XWayland
Display* (it is only true for pure native-Wayland mode with no X11).
Changed to `else if (!dpy || wm_backend->is_wayland)` so XWayland
windows also get flags.mapped=1 after wManageWindow, matching the
semantics of XDG toplevel windows.
2. scene node positioned at xw->x,xw->y before configure round-trip
handle_xwayland_map used xw->x,xw->y for the initial scene node
position. These values may not yet reflect the configure sent in
handle_new_xwayland_surface (the X11 round-trip is async). Changed
to use view->x,view->y which are set to output/4 in
handle_new_xwayland_surface and kept in sync by set_geometry listener.
Also added a log line when xw->surface is NULL at map time so the cause
is diagnosable from wmaker stderr.
Note: if xterm connects to the wrong DISPLAY (e.g. Sway's :1 instead of
wmaker's :2), no handle_xwayland_map fires at all. Check wmaker's
"XWayland ready on :X" log line for the correct display number.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>