- 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.
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>
wWindowConfigure: add early-return Wayland path that dispatches
client_synth_configure via vtable and updates client geometry instead
of trying to configure a non-existent X11 frame window.
wWindowSingleFocus: use client dimensions when frame is NULL for the
wScreenBringInside call.
wWindowSaveState: fall back to client width/height when frame is NULL
(non-maximized path).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add WIN_WORKSPACE(w) macro to window.h that returns frame->workspace
when the window has a frame (X11 path) or wwin->workspace when it does
not (Wayland path, set in Phase 67).
Replace all unsafe frame->workspace reads with WIN_WORKSPACE() in:
- window.c: wWindowObscuresWindow (workspace comparison + geometry),
wWindowFocusPrev/Next (same-workspace filter),
wWindowSaveState (state serialization)
- session.c: session save (workspace name lookup)
- switchmenu.c: all five switch-menu workspace comparisons/lookups
Also guard wWindowObscuresWindow's frame->core width/height reads with
a ternary that falls back to client.{width,height} for frameless windows.
This eliminates the last class of NULL frame->workspace dereferences
reachable from workspace-switch, focus-cycle, session-save, and
switch-menu code paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add int workspace to WWindow so Wayland-managed windows (which have no
WFrameWindow and therefore no frame->workspace slot) can track their
workspace assignment. Set wwin->workspace = workspace in the Wayland
branch of wManageWindow alongside the existing geometry recording.
Guard wWorkspaceForceChange against NULL frame dereferences: introduce a
tmp_workspace local that falls back to tmp->workspace when frame is NULL,
and protect the omnipresent workspace assignment the same way. Also add
a NULL guard for tmp->icon->core in the miniwindow-unmap path.
This prevents NULL dereferences if workspace switching is triggered
(e.g., via a root-menu action) while Wayland windows are managed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three related fixes to make the WM focus lifecycle work end-to-end in
the Wayland path:
1. window.c: set wwin->flags.mapped=1 for Wayland windows (the
wWindowMap call in wManageWindow is guarded by if(wwin->frame)
which is NULL in Wayland, leaving mapped=0 and causing wSetFocusTo
to take the no-focus branch immediately).
2. screen.c: call screen_create_internals in the Wayland goto path;
this was skipped by goto screen_done but is needed to set
auto_focus=1 and clear initial keyboard focus via wlr_seat.
3. wl_backend.c: implement wl_screen_create_internals (sets
no_focus_win=WNATIVE_NONE, auto_focus=1, clears seat focus);
wl_focus_set now calls wlr_seat_keyboard_clear_focus and logs
"focus_clear" when win==WNATIVE_NONE; remove the redundant direct
wl_focus_set call from handle_toplevel_map since wManageWindow now
handles focus via wSetFocusTo.
Smoke test: add check 20 verifying "focus_clear" is logged after the
last managed window unmaps.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add wwin->frame NULL check before accessing wwin->frame->titlebar in
wWindowSynthConfigureNotify so frameless Wayland windows do not
dereference a NULL frame pointer when a synthetic configure notify
is sent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add if (wwin->frame) guards to wWindowMap and wWindowUnmap so that
frameless Wayland windows do not dereference wwin->frame->core->window.
Guard getCommandForWindow in misc.c against NULL dpy to prevent
XGetCommand crash when the PID-based haveCommand path fires.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implement wl_client_get_pid using wl_resource_get_client +
wl_client_get_credentials so the WM can retrieve the OS PID of
Wayland clients. Guard getCommandForWindow in misc.c against NULL
dpy to prevent XGetCommand crash when the real PID path fires.
Guard wWindowChangeWorkspace against NULL frame for frameless
Wayland windows. Log pid= in the wManageWindow-succeeded line;
add Phase 50 pid check to smoke test.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pointer_query / pointer_query_full: return 1 when compositor is running
(wl_state.started) so the same_screen check in wManageWindow passes and
wSetFocusTo is called for newly mapped XDG toplevels.
Guard frame-dependent calls in the focus update path:
- wWindowFocus (window.c): guard wFrameWindowChangeState on frame!=NULL
- wWindowUnfocus (window.c): same; also guard wFrameWindowPaint on
owner->frame when clearing semi-focus on transient owner
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Guard all dpy==NULL / frame==NULL crash sites in the unmanage path:
- wUnmanageWindow (window.c): frame ptr set to NULL-safe expression;
window_unmap, raise_delay timer, and owner border change all
guarded on frame != NULL
- wSelectWindow (actions.c): early return if !wwin->frame
- wNETCleanupFrameExtents (wmspec.c): early return if !dpy
- wClientRestore (client.c): early return if !dpy
Wire handle_toplevel_unmap → context_find → wUnmanageWindow(destroyed=True)
so XDG toplevels are removed from scr->focused_window on client disconnect.
Smoke test: add Phase 40 check — grep "toplevel unmapped".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>