windowmaker-wl/ACTION_PLAN.md

36 KiB

Wayland Port — Concrete Action Plan

Correlated with WAYLAND_ROADMAP.md phases and the abstraction leak analysis.


How to Read This Plan

Each task is mechanical: it names the exact file(s), the exact Xlib calls or X11 types to eliminate, and the exact replacement mechanism. Tasks are grouped by roadmap phase and ordered by dependency.

Task format:

  • ID: {Phase}.{Number}
  • File(s): exact source files
  • Operation: what to do
  • Xlib calls eliminated: count
  • Blocked by: prerequisite tasks

Wave 1 Complete (2026-05-18)

All 8 signature change tasks completed (changes since commit 9943c05):

Task Description Status
A5.1 wSwitchPanelHandleEvent: XEvent* → WMEvent* ✅
A5.2 Remove XEvent synthesis in cycling.c ✅
B.3 wDockReceiveDNDDrop: XEvent* → WMEvent* ✅
E.3 wRootMenuPerformShortcut: XEvent* → WMEvent* ✅
E.4 wClientConfigure/wClientCheckProperty: X11 types → WMEvent* ✅
R.1 framewin.c: _x11_raw → WMEvent ✅
R.2 icon.c: _x11_raw → WMEvent ✅
R.3 menu.c: _x11_raw → WMEvent ✅

Results:

  • _x11_raw sites in src/: 16 → 9 (7 eliminated in policy code)
  • Build verified clean (no new warnings, no regressions)
  • Crash regression (use-after-free on modal dialog close) fixed:
    • W_IsValidView() validates stale _view_target against view_table at fetch boundary in wevent_wl.c, plus defense-in-depth in wview.c and wevent.c
    • Scene graph cleanup: wl_destroy() calls W_ViewDestroyBackingFn → wl_view_destroy_backing_impl() to destroy wlr_scene_node and wlr_buffer_drop when WINGs toplevels close

Wave 2 Complete (2026-05-18)

All 4 mechanical elimination tasks completed (changes since commit 0950b22):

Task Description Status
A4.1 workspace.c: remove 5 USE_XINERAMA guards ✅
B.1 appicon.c: XClearArea → wm_backend->window_clear ✅
R.5 usermenu.c: XStringToKeysym → xkb_keysym_from_name, XSendEvent → dpy guard ✅
R.7 window.c: remove 4 USE_XSHAPE guards ✅

Results:

  • _x11_raw sites in src/: 9 → 9 (no new _x11_raw; these tasks eliminated X11 calls, not _x11_raw)
  • Build verified clean (no new warnings, no regressions)
  • C.1 skipped: XOR rubber-band drawing in moveres.c is inside #if 0 — dead code, no action needed

Bug Fixes

Native XDG client rendering fix (commit 8d963e7)

Problem: Native XDG clients (e.g. foot terminal, gedit) would render once on initial map but never re-render on buffer commits. The compositor received wl_surface.commit events but did not schedule output frame refreshes.

Root cause: surface_commit_listener in wl_backend.c was registered but did not call wlr_output_schedule_frame(). Without this call, the compositor never redrew the output after the initial frame.

Fix: The surface_commit_listener callback now calls wlr_output_schedule_frame() on every buffer commit, ensuring the compositor redraws the output and picks up the new buffer content.


Phase F: wrlib Wayland Integration

Do this first — other phases depend on wrlib working without X11.

DONE F.1 Remove gratuitous #include <X11/Xlib.h> from pure files

  • Files: wrlib/color.c, wrlib/convolve.c, wrlib/flip.c, wrlib/load.c, wrlib/load_ppm.c, wrlib/misc.c, wrlib/raster.c, wrlib/rotate.c, wrlib/save.c, wrlib/scale.c
  • Operation: Each file includes X11/Xlib.h but makes zero Xlib calls. They get it transitively via #include "wraster.h". The include is in wraster.h.in itself (line 47), so these files can't avoid it without changing the public header. Two options:
    • (a) Guard the X11 includes in wraster.h.in with #ifndef WRLIB_NO_X11 and define that in Wayland builds, OR
    • (b) Accept the transitive include (it's a type definition header, not a link dependency) and move on.
  • Recommendation: Option (b) — this is cosmetic. Skip and move to substantive tasks.
  • Xlib calls eliminated: 0 (cosmetic only)

F.2 Verify RCreateContextWayland() completeness ✅

Status: DONE — all fields verified correct, no issues.

  • File: wrlib/context.c:725-775
  • Operation: Read RCreateContextWayland(). Verify it sets all fields that RConvertImage and image loaders need: depth, vclass, red_offset, green_offset, blue_offset, black, white, convert_image, convert_image_mask. Verify dpy, cmap, copy_gc, visual, drawable are NULL/0.
  • Test: Call RCreateContextWayland(1920, 1080, NULL), then RLoadImage(ctx, "test.png", 0), then RConvertImage(ctx, img, &pixmap). Confirm the pixmap is a pixman_image_t* cast to Pixmap.
  • Blocked by: nothing

F.3 Verify wl_convert_image / wl_convert_image_mask ✅

Status: DONE — RGBA→ARGB32 conversion correct, alpha handling correct, no bugs.

  • File: wrlib/convert_wl.c (58 lines)
  • Operation: Read the implementation. Verify RGBA→ARGB32 byte order conversion is correct. Verify alpha channel handling for RRGBFormat (no alpha) vs RRGBAFormat. Verify the returned Pixmap is (Pixmap)(uintptr_t)pixman_image_t*.
  • Test: Load a PNG with alpha, convert, verify pixel values in the pixman image.
  • Blocked by: F.2

F.4 Verify image format loaders are X11-independent ✅

Status: DONE — all 7 loaders verified X11-independent, zero dpy/cmap/visual references.

  • Files: wrlib/load_png.c, wrlib/load_jpeg.c, wrlib/load_gif.c, wrlib/load_tiff.c, wrlib/load_webp.c, wrlib/load_ppm.c, wrlib/load_xpm_normalized.c
  • Operation: Confirm each loader takes RContext* only for error reporting (or not at all) and returns RImage* without touching ctx->dpy. Grep each file for ->dpy, ->cmap, ->visual.
  • Expected result: All clean except load_xpm.c (uses XParseColor).
  • Blocked by: nothing

F.5 Provide Wayland-safe XPM loading path ✅

Status: DONE — already wired up at load.c:199-204 — ctx->dpy==NULL check dispatches to RLoadXPMNormalized.

  • File: wrlib/load_xpm.c (180 lines) — uses XParseColor for color name→RGB
  • Operation: load_xpm_normalized.c (459 lines) already exists as a pure-C XPM parser that doesn't need X11. Verify that RLoadImage() in wrlib/load.c dispatches to load_xpm_normalized.c when ctx->dpy == NULL. If not, add a check:
    if (ctx->dpy == NULL)
        return RLoadXPMNormalized(ctx, file);
    else
        return RLoadXPM(ctx, file);
    
  • Xlib calls eliminated: 2 (in the Wayland path)
  • Blocked by: F.2

Phase F Summary

All five Phase F tasks verified — the wrlib Wayland integration path is complete with zero code changes required:

Task Result
F.1 Gratuitous #include <X11/Xlib.h> in 10 pure files — cosmetic only, accepted per option (b).
F.2 RCreateContextWayland() — all fields verified correct. depth=32, TrueColor, RGBA offsets, black/white, convert function pointers all set. dpy/cmap/visual/drawable NULL'd.
F.3 wl_convert_image / wl_convert_image_mask — RGBA→ARGB32 byte order correct, alpha handling correct for both RRGBFormat and RRGBAFormat, mask thresholding with stride-aware indexing, Pixmap cast idiomatic. No bugs.
F.4 All 7 image format loaders verified X11-independent — zero references to ->dpy, ->cmap, ->visual.
F.5 Wayland-safe XPM loading already wired at load.c:199-204 — dpy == NULL check dispatches to RLoadXPMNormalized (pure-C parser) vs RLoadXPM (X11/XParseColor).

Key architecture point: wrlib's convert_image function pointer (context->convert_image) is the single abstraction seam — set to wl_convert_image by RCreateContextWayland() and x11_convert_image by RCreateContext(). Everything else is either pure image manipulation (portable) or hardcoded X11 (xutil.c, xpixmap.c, convert_x11.c) that is dead code on the Wayland path.


Phase A: Core Window Management Gaps

A4: Workspace Switching

A4.1 Eliminate USE_XINERAMA guards in workspace.c ✅

  • File: src/workspace.c — 5 #ifdef USE_XINERAMA sites (lines 252, 280, 295, 303, 328)
  • Operation: Each guard selects between per-monitor and whole-screen geometry for workspace name display. The monitor geometry should come from WScreen fields (already populated by the backend), not from Xinerama calls. For each site:
    1. Read the #ifdef USE_XINERAMA block and the #else block
    2. Replace both with code that reads wGetHeadForPointerLocation(scr) or equivalent backend-neutral monitor query (these functions already exist and are backend-abstracted)
    3. Delete the #ifdef
  • Xlib calls eliminated: 0 (these are compile-time guards, not direct Xlib calls; the Xinerama calls are in the backend)
  • Blocked by: nothing

A5: Alt-Tab Window Cycling

A5.1 Change wSwitchPanelHandleEvent signature from XEvent* to WMEvent* ✅

  • Files: src/switchpanel.c:684, src/switchpanel.h:33
  • Operation:
    1. Change function signature from WWindow *wSwitchPanelHandleEvent(WSwitchPanel *panel, XEvent *event) to WWindow *wSwitchPanelHandleEvent(WSwitchPanel *panel, WMEvent *event)
    2. Inside the function, replace all event->xmotion.x field accesses with event->u.motion.x (the backend-neutral WMEvent fields)
    3. Replace all event->xbutton.button with event->u.button.button, etc.
    4. Replace event->type == MotionNotify with event->type == WME_MOTION, etc.
  • Blocked by: nothing

A5.2 Remove XEvent synthesis in cycling.c ✅

  • File: src/cycling.c:227-259
  • Operation: After A5.1, the wSwitchPanelHandleEvent takes WMEvent*. Delete the entire XEvent synthesis block (lines 227-259) and replace with:
    tmp = wSwitchPanelHandleEvent(swpanel, &ev);
    
    Pass the WMEvent ev directly instead of synthesizing an XEvent.
  • Xlib calls eliminated: 5 (XEvent type references)
  • Blocked by: A5.1

Phase B: Dock & App Icons

B.1 Eliminate XClearArea in appicon.c ✅

  • File: src/appicon.c:371
  • Operation: Replace XClearArea(dpy, dicon->icon->core->window, 2, 2, 50, ...) with wm_backend->window_clear_area(dicon->icon->core->window, 2, 2, 50, ...) or wm_backend->frame_paint(...) to trigger a repaint. Check if a window_clear_area vtable slot exists; if not, use frame_paint on the icon's frame.
  • Xlib calls eliminated: 1
  • Blocked by: nothing

B.2 Eliminate USE_DOCK_XDND guards in appicon.c

  • File: src/appicon.c — 4 sites (lines 52, 131, 315, 338)
  • Operation: The XDND setup and handling code is X11-specific. It should be moved behind a vtable slot:
    1. Lines 52, 131: Move #include "xdnd.h" and wXDNDMakeAwareness() call into the X11 backend's appicon_show implementation
    2. Lines 315, 338: The DND event handling in iconDblClick / iconMouseDown should dispatch through wm_backend->xdnd_process_client_message (already exists)
    3. Remove all #ifdef USE_DOCK_XDND from appicon.c
  • Xlib calls eliminated: 0 (guards only; actual Xlib calls are in xdnd.c)
  • Blocked by: nothing

B.3 Change wDockReceiveDNDDrop signature from XEvent* to WMEvent* ✅

  • Files: src/dock.c:2009, src/dock.h:97
  • Operation: Same pattern as A5.1. Change int wDockReceiveDNDDrop(WScreen *scr, XEvent *event) to take WMEvent*. Update the single call site in event.c:782 to pass the WMEvent instead of casting _x11_raw.
  • Xlib calls eliminated: 1 _x11_raw access in event.c
  • Blocked by: nothing

Phase C: Visual Polish & Animations

C.1 Eliminate XOR rubber-band drawing in moveres.c ⏭️ (inside #if 0 — dead code, skip)

  • File: src/moveres.c — 6 Xlib calls (lines 145-151)
  • Operation: Replace XSetForeground + XDrawLine calls with wm_backend->overlay_draw_line() vtable calls (slot exists at backend.h:797):
    // Before:
    XSetForeground(dpy, lgc, scr->line_pixel);
    XDrawLine(dpy, scr->root_win, lgc, 0, y-1, scr->scr_width, y-1);
    // After:
    wm_backend->overlay_draw_line(scr, 0, y-1, scr->scr_width, y-1);
    
    Do this for all 4 XDrawLine calls and the XSetForeground.
  • Xlib calls eliminated: 6
  • Blocked by: nothing (vtable slots exist, X11 backend implements them as XDrawLine, Wayland backend implements as pixman overlay)

C.3 Verify balloon tooltip vtable slots

  • Files: src/backend/backend.h (line 1772: balloon_show), src/balloon.c
  • Operation: Confirm balloon_show / balloon_hide vtable slots exist and are called from balloon.c. Confirm balloon.c has zero direct Xlib calls (our analysis showed 0). No action needed if clean.
  • Blocked by: nothing

Phase D: WINGs Widget Porting

Ordered by dependency and impact.

D.1 Route widgets.c screen init through backends

  • File: WINGs/widgets.c — 35 Xlib calls
  • Operation: The screen initialization code (lines 568-739) creates GCs, cursors, stipple pixmaps, and interns atoms using raw Xlib. This code runs on both backends but should only run on X11. Split into two parts:
    1. Lines 568-571: XOpenDisplay — already handled (WINGs init takes a Display* or NULL)
    2. Lines 637-739: GC creation, cursor creation, modifier mapping, atom interning. Wrap this entire block in:
      if (scrPtr->display != NULL) {  // X11 path
          // existing code
      }
      
      The Wayland path skips GC/cursor creation (handled by wrender_wl.c and wview_wl.c). The modifier mapping should use xkb_keymap on Wayland (already in wevent_wl.c).
    3. Lines 735-739: XInternAtoms — move to a viewBackend->platform_init() call (slot exists at backend.h line 621, already called at widgets.c:832)
  • Xlib calls eliminated: 35
  • Blocked by: nothing

D.2 Route wwindow.c ICCCM properties through vtable

  • File: WINGs/wwindow.c — 41 Xlib calls
  • Operation: Every Xlib call sets ICCCM window properties (WM_NAME, WM_ICON_NAME, WM_PROTOCOLS, WM_NORMAL_HINTS, WM_CLASS, WM_HINTS, _NET_WM_ICON, transient-for). These are X11-only concepts. For each function:
    1. WMSetWindowTitle (line 155): Wrap the XmbTextListToTextProperty + XSetWMName + XChangeProperty block in if (scr->display). On Wayland, set xdg_toplevel_set_title via a new viewBackend->set_title(view, title) slot.
    2. WMSetWindowMiniwindowTitle (line 176): Same pattern, viewBackend->set_icon_title(view, title).
    3. WMSetWindowMiniwindowImage (line 230): XChangeProperty for _NET_WM_ICON. Wrap in if (scr->display). No Wayland equivalent needed (compositor owns icons).
    4. WMSetWindowCloseAction / WMSetWindowMaximized (lines 260-285): XGetWMProtocols / XSetWMProtocols. Wrap in if (scr->display).
    5. setWindowGeometry (line 318): XAllocSizeHints / XSetWMNormalHints. Wrap in if (scr->display).
    6. WMRealizeWindow (lines 415-464): XAllocClassHint / XSetClassHint / XAllocWMHints / XSetWMHints / XSetWMProtocols / XSetTransientForHint. Wrap in if (scr->display).
    7. WMSetWindowMiniwindowPixmap (line 572): XGetWMHints / XSetWMHints. Wrap in if (scr->display).
    8. WMCloseWindow (line 618): XWithdrawWindow. Replace with viewBackend->unmap(vctx, view).
  • Xlib calls eliminated: 41
  • Blocked by: nothing

D.3 Move winputmethod.c behind a backend vtable

  • File: WINGs/winputmethod.c — 40 Xlib calls
  • Operation: The entire file is XIM/XIC lifecycle management — purely X11. On Wayland, input methods use zwp_text_input_v3. Create two files:
    1. Rename winputmethod.c → winputmethod_x11.c
    2. Create winputmethod_wl.c with stub implementations (or zwp_text_input_v3 if needed now)
    3. Create winputmethod.c as a thin dispatch layer that calls scr->imBackend->*() or uses if (scr->display) guards
    4. Update Makefile.am to compile all three
  • Xlib calls eliminated: 40 (moved to _x11.c)
  • Blocked by: nothing

D.4 Move wballoon.c drawing through render backend

  • File: WINGs/wballoon.c — 28 Xlib calls
  • Operation: The balloon drawing code uses XFillArc, XFillRectangle, XFillPolygon, XSetForeground, XCreateGC to draw shaped tooltips. Replace each with renderBackend->fill_arc(), renderBackend->fill_rect(), renderBackend->fill_polygon() calls. The render backend vtable already has these slots.
    • XTranslateCoordinates (line 182): Replace with viewBackend->get_screen_position() (slot exists).
    • USE_XSHAPE guards (2 sites): Move shape extension calls into viewBackend or wrap in if (scr->display).
  • Xlib calls eliminated: 28
  • Blocked by: nothing

D.5 Move selection.c behind a backend vtable

  • File: WINGs/selection.c — 21 Xlib calls
  • Operation: The entire file implements X11 selection protocol (XConvertSelection, XGetWindowProperty, XChangeProperty, XSendEvent, XSetSelectionOwner). On Wayland, clipboard uses wl_data_device. Create:
    1. Rename selection.c → selection_x11.c
    2. Create selection_wl.c with wl_data_device implementation (or stubs)
    3. Create selection.c dispatch layer
    4. Update Makefile.am
  • Xlib calls eliminated: 21 (moved to _x11.c)
  • Blocked by: nothing

D.6 Move DND subsystem behind a backend vtable

  • Files: WINGs/dragsource.c (45 calls), WINGs/dragdestination.c (37 calls), WINGs/dragcommon.c (16 calls) — 98 total
  • Operation: The entire XDND protocol implementation is X11-only. On Wayland, DnD uses wl_data_device for cross-client and compositor-internal mechanisms for WINGs↔WINGs.
    1. Rename all three to *_x11.c
    2. Create dragsource_wl.c, dragdestination_wl.c, dragcommon_wl.c (stubs initially)
    3. Create thin dispatch files or add a W_DnDBackend vtable to WINGsP.h.in
    4. Update Makefile.am
  • Xlib calls eliminated: 98 (moved to _x11.c)
  • Blocked by: nothing

D.7 Route wcolorpanel.c through render/view backends

  • File: WINGs/wcolorpanel.c — 89 Xlib calls
  • Operation: The color panel does raw pixmap manipulation (XCreatePixmap, XCopyArea, XGetImage, XGetPixel, XPutPixel, XPutImage), pointer grabs (XGrabPointer, XUngrabPointer, XQueryPointer), and cursor manipulation (XRecolorCursor, XCreatePixmapCursor). Replace systematically:
    1. Pixmap ops (XCreatePixmap, XCopyArea, XFreePixmap, XFillRectangle — ~44 calls): Replace with renderBackend->pixmap_create(), renderBackend->pixmap_copy(), renderBackend->pixmap_free(), renderBackend->fill_rect().
    2. Image capture (XGetImage, XGetPixel, XPutPixel, XSubImage, XPutImage, XDestroyImage — ~15 calls): Replace with renderBackend image access functions or wrlib RImage operations.
    3. Pointer grabs (XGrabPointer, XUngrabPointer, XQueryPointer — ~6 calls): Replace with viewBackend pointer grab functions.
    4. Cursor (XRecolorCursor, XCreatePixmapCursor, XFreeCursor — ~4 calls): Replace with viewBackend->set_cursor().
    5. Event polling (XPending, XPeekEvent, XNextEvent, XCheckTypedEvent, XFlush — ~6 calls): Replace with eventBackend functions.
    6. USE_XSHAPE guards (6 sites): Move shape calls into viewBackend or guard with if (scr->display).
  • Xlib calls eliminated: 89
  • Blocked by: D.1 (needs render backend working)

D.8 Route wtext.c clipboard and grabs through backends

  • File: WINGs/wtext.c — 23 Xlib calls
  • Operation:
    1. Clipboard (XFetchBuffer, XStoreBuffer, XFree — 6 calls): Replace with WINGs selection API (which will be backend-split after D.5).
    2. Pointer grabs (XGrabPointer, XUngrabPointer — 4 calls): Replace with viewBackend pointer grab.
    3. Misc (XBell — 2 calls): Replace with viewBackend->bell() or no-op.
    4. Drawing (XCreateGC, XCreateBitmapFromData, XChangeWindowAttributes, XSetWindowAttributes, XMapWindow, XInternAtom, XFlush — 9 calls): Route through render/view backends.
    5. XRectangle type (2 uses): Replace with WMRect.
  • Xlib calls eliminated: 23
  • Blocked by: D.5 (clipboard)

D.9 Route wtextfield.c clipboard and grabs through backends

  • File: WINGs/wtextfield.c — 10 Xlib calls
  • Operation: Same categories as D.8 but smaller:
    1. Clipboard (XFetchBuffer, XStoreBuffer, XFree): Use WINGs selection API
    2. Grabs (XGrabPointer, XUngrabPointer): Use viewBackend
    3. Bell (XBell): Use viewBackend->bell() or no-op
    4. Drawing (XDrawRectangle, XInternAtom, XFlush): Use renderBackend
  • Xlib calls eliminated: 10
  • Blocked by: D.5

D.10 Route wpopupbutton.c grabs through view backend

  • File: WINGs/wpopupbutton.c — 7 Xlib calls
  • Operation: Replace XGrabPointer / XUngrabPointer / XFlush with viewBackend pointer grab calls.
  • Xlib calls eliminated: 7
  • Blocked by: nothing

D.11 Route wappresource.c property reads through backend

  • File: WINGs/wappresource.c — 15 Xlib calls
  • Operation: Replace XGetWindowProperty / XFree / XInternAtom calls with viewBackend property functions or guard with if (scr->display). These read app icon properties — on Wayland, icons come from xdg_toplevel or desktop files.
  • Xlib calls eliminated: 15
  • Blocked by: nothing

D.12 Route wmisc.c coordinate translation through view backend

  • File: WINGs/wmisc.c — 2 Xlib calls
  • Operation: Replace XTranslateCoordinates with viewBackend->get_screen_position().
  • Xlib calls eliminated: 2
  • Blocked by: nothing

Phase E: Dialogs & Panels

E.1 Eliminate _x11_raw in winspector.c

  • File: src/winspector.c:1038
  • Operation: The _x11_raw cast is used to get an XEvent* for some event handling. Replace with WMEvent field access (same pattern as A5.1).
  • Xlib calls eliminated: 1 _x11_raw access
  • Blocked by: nothing

E.2 Eliminate _x11_raw accesses in event.c

  • File: src/event.c — 16 _x11_raw sites

  • Operation: Systematic elimination, one site at a time:

    Line(s) Context Replacement
    249, 264, 269, 276 handleConfigureRequest — passes raw XEvent to backend for RandR/Shape/XKB Already dispatches through wm_backend->event_randr_update() and wm_backend->event_handle_extensions(). The _x11_raw is the argument to these vtable calls. Change vtable signatures to accept WMEvent* instead of void*, and have the X11 backend extract _x11_raw internally.
    431 handleMapRequest — raw XEvent for wManageWindow Check if wManageWindow actually needs the raw event or just the window ID (from event->u.mapRequest.window). If just the ID, pass that.
    446-447 handleExtensions — passes raw event to backend Already a vtable call. Change vtable to accept WMEvent*.
    782 handleButtonPress — raw XEvent for wDockReceiveDNDDrop Eliminated by task B.3.
    831, 834, 852, 857 handleClientMessage — raw XEvent for EWMH, XDND, selection Already dispatched through wm_backend->netwm_process_client_message() and wm_backend->xdnd_process_client_message(). Change vtable signatures to accept WMEvent*.
    971, 974 handleClientMessage — same Same as above.
    993 handleClientMessage — colormap install Change vtable call to accept WMEvent*.
    1354 handleKeyPress — raw XEvent for root menu shortcuts Task E.3 below.
  • Xlib calls eliminated: 16 _x11_raw accesses

  • Blocked by: B.3, E.3

E.3 Change wRootMenuPerformShortcut from XEvent* to WMEvent* ✅

  • Files: src/rootmenu.c:320, src/rootmenu.h:25
  • Operation: Change signature from Bool wRootMenuPerformShortcut(XEvent *event) to Bool wRootMenuPerformShortcut(WMEvent *event). Update internals to use event->u.key.* fields instead of event->xkey.*.
  • Blocked by: nothing

E.4 Eliminate X11 types in client.c function signatures ✅

  • Files: src/client.c:119,247, src/client.h:27,31
  • Operation:
    1. wClientConfigure(WWindow *wwin, XConfigureRequestEvent *xcre) → change parameter to WMEvent *event and extract fields from event->u.configureRequest.*
    2. wClientCheckProperty(WWindow *wwin, XPropertyEvent *event) → change parameter to WMEvent *event and extract from event->u.property.*
    3. Update all call sites (in event.c)
  • Xlib calls eliminated: 3 (XWindowChanges usage inside, plus signature cleanup)
  • Blocked by: nothing

E.5 Port WPrefs.app drawing through WINGs render backend

  • Files: WPrefs.app/Appearance.c (67 calls), WPrefs.app/editmenu.c (37), WPrefs.app/Menu.c (24), WPrefs.app/TexturePanel.c (14), WPrefs.app/HotCornerShortcuts.c (11), WPrefs.app/KeyboardShortcuts.c (12), WPrefs.app/MouseSettings.c (6), WPrefs.app/WPrefs.c (7), WPrefs.app/main.c (4), WPrefs.app/double.c (2), WPrefs.app/Paths.c (1), WPrefs.app/xmodifier.c (12)

  • Operation: WPrefs.app is a WINGs client. All its Xlib calls should go through WINGs APIs:

    Sub-task Files Calls Replacement
    E.5a: Replace XCopyArea/XFillRectangle/XDrawLine/XDrawRectangle/XFillPolygon/XDrawLines with WMDrawLine, WMFillRectangle, WMCopyArea (WINGs drawing API, routed through renderBackend) Appearance.c, Menu.c, TexturePanel.c, HotCornerShortcuts.c, double.c, Paths.c, KeyboardShortcuts.c 120 WINGs render API
    E.5b: Replace XCreatePixmap/XFreePixmap with WMCreatePixmap/WMReleasePixmap Appearance.c, HotCornerShortcuts.c, TexturePanel.c 16 WINGs pixmap API
    E.5c: Replace XCreateGC/XFreeGC with WINGs managed GCs or remove (render backend handles GCs internally) Appearance.c, Menu.c 6 Remove; use WINGs color GCs
    E.5d: Replace XGrabKeyboard/XUngrabKeyboard with WINGs keyboard grab API (needs creation or backend call) KeyboardShortcuts.c, Menu.c 4 New WINGs API or viewBackend
    E.5e: Replace XGrabPointer/XUngrabPointer/XQueryPointer/XTranslateCoordinates with WINGs pointer API editmenu.c 12 viewBackend pointer ops
    E.5f: Replace XGetModifierMapping/XKeysymToKeycode/XKeysymToString with xkbcommon equivalents or WINGs API KeyboardShortcuts.c, MouseSettings.c, xmodifier.c 18 xkbcommon or WINGs key API
    E.5g: Replace XParseColor with WINGs WMCreateNamedColor Appearance.c, TexturePanel.c 3 WINGs color API
    E.5h: Replace XSendEvent/XInternAtom (WM reload command) with WINGs or backend-neutral mechanism WPrefs.c 3 Write to defaults file + signal
    E.5i: Replace XOpenDisplay in main.c with WINGs init (already takes display) main.c 4 WINGs WMOpenScreen
    E.5j: Replace XChangePointerControl/XGetPointerControl with backend call or libinput MouseSettings.c 2 Backend-specific or defer
    E.5k: Replace XRecolorCursor/XMoveWindow/XClearWindow/XSetWMNormalHints etc. in editmenu.c editmenu.c 15 WINGs view/window API
  • Xlib calls eliminated: 197 total

  • Blocked by: D.1 through D.10 (WINGs backends must work first)


Phase G: Session & Persistence

G.1 Verify session.c is X11-free

  • File: src/session.c
  • Operation: Our analysis showed 0 Xlib calls. Confirm by grep. No action needed.
  • Blocked by: nothing

G.2 Verify defaults.c XColor usage

  • File: src/defaults.c — 20 Xlib references (all XColor type for color parsing)
  • Operation: The XColor references are for parsing color names from the defaults database. These should use wm_backend->color_parse_name() or equivalent vtable slot. Check if a vtable slot exists for color name→RGB conversion. If so, replace the XColor + XParseColor calls. If not, add one.
  • Xlib calls eliminated: 20
  • Blocked by: nothing

Phase I: EWMH / Foreign Toplevel

I.1 Assess wmspec.c — no action needed for Wayland

  • File: src/wmspec.c — 65 Xlib calls
  • Operation: EWMH (_NET_WM_*) is an X11-only protocol. On Wayland, the equivalent is wlr_foreign_toplevel_management_v1 (already advertised by wlroots). The 65 Xlib calls in wmspec.c are correct — they implement EWMH for X11 clients. They should NOT be eliminated; they should be guarded so they only execute for X11/XWayland clients.
  • Check: Verify that wmspec.c functions are only called from code paths that have an X11 client (XWayland surface). The if (!dpy || !wwin->frame) guard at line 2000 suggests some guarding exists. Verify completeness.
  • Xlib calls eliminated: 0 (these are correct for X11 path)
  • Blocked by: nothing

I.2 Assess xdnd.c — no action needed for Wayland

  • File: src/xdnd.c — 33 Xlib calls
  • Operation: Same as I.1. XDND is X11-only. On Wayland, DnD uses wl_data_device (handled by wlroots). The xdnd.c code is correct for XWayland clients. Verify it's only called from X11 code paths.
  • Blocked by: nothing

Phase H: Advanced Compositor Features

H.1 Eliminate USE_XINERAMA guards in src/ policy files

  • Files: src/actions.c:975, src/wmspec.c (6 sites), src/workspace.c (5 sites — covered in A4.1)
  • Operation: Same pattern as A4.1. Replace Xinerama-specific monitor queries with backend-neutral wGetHeadForPoint() / wGetUsableAreaForHead() calls. These functions already exist and are backend-abstracted (the backend populates WScreen head geometry).
  • Xlib calls eliminated: 0 (compile-time guards)
  • Blocked by: nothing

H.5 Verify clipboard works via wlroots

  • Operation: wlroots implements wl_data_device and zwp_primary_selection_v1. Verify copy/paste works between native Wayland clients and between XWayland clients. No code changes expected.
  • Blocked by: nothing

Remaining src/ Cleanup (not phase-specific)

R.1 Eliminate _x11_raw in framewin.c ✅

  • File: src/framewin.c:1165
  • Operation: The _x11_raw access is in a titlebar click handler. Replace with WMEvent field access.
  • Blocked by: nothing

R.2 Eliminate _x11_raw in icon.c ✅

  • File: src/icon.c:861
  • Operation: The _x11_raw access is in miniwindowDblClick. Replace with WMEvent field access.
  • Blocked by: nothing

R.3 Eliminate _x11_raw in menu.c ✅

  • File: src/menu.c:738
  • Operation: The _x11_raw access is in check_key. Replace with WMEvent field access for key handling.
  • Blocked by: nothing

R.4 Eliminate X11 types in misc.c

  • File: src/misc.c — 8 Xlib calls (XKeysymToKeycode, XLookupString, DefaultRootWindow)
  • Operation: Replace with wm_backend->keysym_to_keycode() (vtable slot exists) and wm_backend->key_lookup_string() (may need new slot).
  • Blocked by: nothing

R.5 Eliminate X11 in usermenu.c ✅

  • File: src/usermenu.c — 4 Xlib calls (XSendEvent, XStringToKeysym)
  • Operation: Replace XSendEvent with wm_backend->client_send_key_event() or equivalent vtable slot. Replace XStringToKeysym with xkb_keysym_from_name() (libxkbcommon, already linked).
  • Blocked by: nothing

R.6 Move xmodifier.c behind backend

  • File: src/xmodifier.c — 11 Xlib calls
  • Operation: This file detects keyboard modifier mappings. On Wayland, modifiers come from xkb_keymap. The X11 path should remain for X11 backend. Guard the entire file with if (dpy) or move to backend/x11/.
  • Blocked by: nothing

R.7 Eliminate USE_XSHAPE guards in window.c ✅

  • File: src/window.c — 4 #ifdef USE_XSHAPE sites
  • Operation: Shape extension queries should be behind the backend. Move XShapeQueryExtents calls into wm_backend->window_get_shape() or similar vtable slot. The X11 backend implements it; the Wayland backend returns "no shape" (Wayland surfaces handle their own shape via alpha).
  • Blocked by: nothing

R.8 Eliminate XKB_BUTTON_HINT guards

  • Files: src/framewin.c (22 sites), src/window.c (10), src/wdefaults.c (3), src/framewin.h (10), src/window.h (1), src/WindowMaker.h (1), src/def_pixmaps.h (1), src/dialog.c (2), src/winspector.c (1) — 51 sites total
  • Operation: The language button feature is controlled by a compile-time #ifdef. This is not a backend concern — it's a feature toggle. Two options:
    • (a) Make it always-on (remove #ifdef, keep the code)
    • (b) Make it a runtime preference (remove #ifdef, add wPreferences.show_language_button check)
    • (c) Leave as-is (it's not blocking Wayland)
  • Recommendation: Option (c) — this is orthogonal to Wayland porting. Skip.

R.9 Eliminate KEEP_XKB_LOCK_STATUS guards

  • Files: src/framewin.c (2), src/event.c (1), src/window.c (1), src/WindowMaker.h (2), src/framewin.h (1), src/keybind.h (1) — 8 sites
  • Operation: Same as R.8 — feature toggle, not backend concern. Skip.

R.10 Eliminate HAVE_INOTIFY guards

  • Files: src/main.c (3), src/event.c (1), src/shutdown.c (2), src/WindowMaker.h (1) — 7 sites
  • Operation: Inotify is Linux-specific, not X11-specific. Works on Wayland too. No action needed.

Execution Order Summary

IMMEDIATE (unblocks everything else):
  ~~F.2  Verify RCreateContextWayland~~ ✅
  ~~F.3  Verify wl_convert_image~~ ✅
  ~~F.4  Verify image loaders~~ ✅
  ~~F.5  Wayland-safe XPM loading~~ ✅

WAVE 1 (independent, no prerequisites):
  ~~A4.1  workspace.c USE_XINERAMA → backend-neutral~~ ✅
  A5.1  ~~switchpanel.c XEvent* → WMEvent*~~ ✅
  ~~B.1   appicon.c XClearArea → vtable~~ ✅
  B.2   appicon.c USE_DOCK_XDND → backend
  B.3   ~~dock.c/h XEvent* → WMEvent*~~ ✅
  ~~C.1   moveres.c XDrawLine → overlay vtable~~ ⏭️ (inside `#if 0` — dead code, skip)
  D.1   widgets.c screen init → backend guard
  D.3   winputmethod.c → _x11/_wl split
  D.5   selection.c → _x11/_wl split
  D.6   DND subsystem → _x11/_wl split
  D.10  wpopupbutton.c grabs → viewBackend
  D.11  wappresource.c props → backend guard
  D.12  wmisc.c coords → viewBackend
  E.3   ~~rootmenu.c XEvent* → WMEvent*~~ ✅
  E.4   ~~client.c X11 types → WMEvent*~~ ✅
  R.1   ~~framewin.c _x11_raw → WMEvent~~ ✅
  R.2   ~~icon.c _x11_raw → WMEvent~~ ✅
  R.3   ~~menu.c _x11_raw → WMEvent~~ ✅
  R.4   misc.c Xlib → vtable
  ~~R.5   usermenu.c Xlib → vtable~~ ✅
  R.6   xmodifier.c → backend guard
  ~~R.7   window.c USE_XSHAPE → vtable~~ ✅
  G.2   defaults.c XColor → vtable
  I.1   wmspec.c — verify X11-only paths
  I.2   xdnd.c — verify X11-only paths

WAVE 2 (depends on Wave 1):
  A5.2  ~~cycling.c XEvent synthesis → delete~~ ✅
  D.2   wwindow.c ICCCM → viewBackend
  D.4   wballoon.c drawing → renderBackend
  D.7   wcolorpanel.c → renderBackend/viewBackend
  D.8   wtext.c clipboard/grabs → backends
  D.9   wtextfield.c clipboard/grabs → backends
  E.1   winspector.c _x11_raw → WMEvent
  E.2   event.c _x11_raw elimination (all 16 sites)

WAVE 3 (depends on Wave 2):
  E.5   WPrefs.app full port (197 calls)

VERIFY (can happen anytime):
  C.3   balloon vtable slots
  ~~F.2-F.4~~  wrlib Wayland path ✅
  G.1   session.c clean
  H.1   USE_XINERAMA policy cleanup
  H.5   clipboard via wlroots

Scorecard

Category Xlib calls to eliminate Tasks
src/ _x11_raw escapes 16 E.2, R.1-R.3, E.1
src/ X11 function signatures ~15 A5.1, B.3, E.3, E.4
src/ direct Xlib calls ~50 C.1, R.4, R.5, R.6, G.2, B.1
src/ compile-time guards ~25 sites A4.1, B.2, R.7, H.1
WINGs/ non-split Xlib calls 412 D.1-D.12
WPrefs.app/ Xlib calls 197 E.5
wrlib/ verification 0 ✅ (all done) F.2-F.5 ✅
wmlib/ 9 Not addressed (too small; keep for X11 or drop)
Total ~700 ~45 tasks