The XDND-on-dock feature was a configure-time toggle (--disable-xdnd)
gating ~25 #ifdef sites across shared src/. This was a pre-Wayland
feature flag, not a backend distinction, but it polluted the policy
layer with build-time conditionals.
Make it unconditional:
configure.ac Remove --disable-xdnd and AM_CONDITIONAL. XDND on
the dock is always built.
src/Makefile.am Move xdnd.c from the if-USE_DOCK_XDND block into
the unconditional wmaker_SOURCES list.
src/dock.h, dock.c, dockedapp.c, appicon.h, appicon.c, event.c,
misc.c, startup.c, dialog.c, screen.h
Strip all #ifdef USE_DOCK_XDND/#endif pairs. In
dockedapp.c the #else branch (rendering an 'XDnD
support was not compiled in' label) is dead and
removed.
Backend routing: the X11 backend continues to drive the XDND protocol
through xdnd.c via the existing xdnd_process_client_message /
xdnd_process_selection / xdnd_make_awareness vtable slots. Wayland's
slots are no-ops (XWayland clients drop into the X11 path naturally
via wl_state.x_display).
src/ is now free of feature-flag guards related to DnD.
Three more vtable slots that took void* (intended for an XEvent*) widened
to take WMEvent*; the X11 impls extract _x11_raw internally:
event_handle_extensions(WMEvent*)
netwm_process_client_message(WMEvent*)
xdnd_process_client_message(WMEvent*)
Also added a new client_message_forward(WMEvent*, WNativeWindow) slot for
the OffiX DND ClientMessage redirect (icon frame -> real client_win).
The X11 backend rewrites xclient.window and XSendEvent's it; the Wayland
backend does the same on wl_state.x_display for XWayland clients.
After this change, src/event.c contains zero _x11_raw references — the
last X11 escape from the shared event dispatch layer is closed.
Five _x11_raw accesses in shared event.c (the worst architectural leak)
removed by widening vtable signatures and moving X11-only logic into
the X11 backend:
WME_SELECTION_REQUEST / WME_SELECTION_CLEAR
* handle_selection_request/clear functions deleted from event.c
* Reimplemented in x11_backend.c as x11_handle_selection_request/clear
where they read _x11_raw internally and call XChangeProperty etc.
* New vtable slots handle_selection_request / handle_selection_clear
(WMEvent*). Wayland: no-ops (wmaker UI does not own a WM_Sn
selection on the X server).
WME_CONFIGURE
* event_randr_update vtable widened to (WMEvent*).
* Root-window check (DefaultRootWindow) moved into x11 impl.
WME_MAPPING
* Dropped the if (xev) guard — keyboard_mapping_refresh already
took WMEvent*.
WME_SELECTION
* xdnd_process_selection vtable widened to (WMEvent*).
Net: event.c is X11-free except for DefaultRootWindow uses elsewhere
that will be cleaned up in subsequent tasks.
Replace broken xev->u.unmap.send_event check (WMEvent has no unmap
member) with unconditional withdraw=True when event comes from root.
Also remove unused XEvent variable in handleExpose.
WMHandleEvent is the middle layer — it must not contain any
backend-specific code. Move view lookup to _view_target field
set by each event backend during fetch. Move XRefreshKeyboardMapping
and XFilterEvent into x11_fetch. Selection and DND handling removed
from middle layer (handled by extraEventHandler in the WM layer).
The _x11_raw field remains in WMEvent for now (used by src/event.c
and other WM-layer code that still needs the raw XEvent), but the
middle layer no longer touches it.
Remove every is_wayland / !is_wayland guard from WM policy files.
All backend-specific logic now lives exclusively in the backends:
- main.c: display_post_open vtable slot handles fatal check, visual ID,
display name, and multiHead parsing (X11) or no-ops (Wayland)
- event.c: EventLoop() unconditionally calls event_loop_run(); the X11
backend's implementation contains the WMNextEvent+inotify loop
- screen.c: screen_init_display vtable slot handles RContext/WMScreen/
color creation (each backend owns its full init path)
- monitor.c: show_crash_dialog vtable slot (X11 shows panel, Wayland
returns abort)
- startup.c: InitializeSwitchMenu called unconditionally (it's
backend-agnostic); defaults polling timer no longer guarded
- shutdown.c: wScreenSaveState called unconditionally
- window.c: client_reparent called unconditionally (Wayland no-ops);
dead frameless-window code removed
Policy code is now completely backend-agnostic. The only references to
is_wayland are the vtable field definitions in the two backend files.
handleKeyPress passed event->_x11_raw (NULL in Wayland mode) to
wRootMenuPerformShortcut which dereferences it immediately. Skip
the call when _x11_raw is NULL.
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)
- 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.
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.
Add wWaylandKeyPress(keycode, modifiers) to event.c/h: scans
wKeyBindings and dispatches a subset of WM shortcuts (miniaturize,
hide, maximize, raise/lower, close, focus-next/prev, workspace switch)
using raw XKB keycode + modifier values instead of an XEvent.
In wl_backend.c, wire handle_keyboard_key and handle_keyboard_modifiers
listeners on the headless keyboard device so that:
- key events are checked against WM shortcuts first
- non-shortcut keys are forwarded to the focused client via
wlr_seat_keyboard_notify_key
- modifier changes are always notified via wlr_seat_keyboard_notify_modifiers
The headless backend doesn't generate real key events so the listener
is a no-op in the smoke test, but the infrastructure is ready for a
real DRM+libinput backend.
Smoke test: check for "keyboard key+modifiers listeners wired" log.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Guard all keyboard shortcut handlers in event.c that dereference
wwin->frame->core for stacking operations (WKBD_KEEP_ON_TOP,
WKBD_KEEP_AT_BOTTOM, WKBD_RAISE, WKBD_LOWER, WKBD_RAISELOWER,
WKBD_WINDOWMENU, WKBD_XKB_TOGGLE) so they skip cleanly when a
Wayland window is focused.
Add early-return guards to wKeyboardMoveResizeWindow,
wMouseMoveWindow, and wMouseResizeWindow in moveres.c so that
Wayland windows (frame==NULL) do not crash if those entry points
are reached.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add display_default_screen, atom_intern, and event_loop_run vtable slots.
Guard all X11-only paths in startup.c/screen.c/shutdown.c/event.c/xdnd.c
with if (!dpy) so the Wayland path reaches wl_display_run() and exits
cleanly (exit 0) on SIGTERM. wl-smoke-test.sh now PASSES.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>