# 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 ` 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: ```c 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 ` 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: ```c 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`~~ ✅ (commit 3d10085) **Status**: DONE — dock_shadow frame_buf created for icon drag ghost overlay. All vtable ops (window_move, map, unmap, set_background_pixmap) already handle frame_bufs. - **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 B Wayland Implementation — COMPLETE (2026-05-23) All Wayland-specific Phase B tasks from WAYLAND_ROADMAP.md are done: | Task | Description | Status | |------|-------------|--------| | B2 | Dock icon drag & drop — dock_shadow frame_buf created | ✅ commit 3d10085 | | B3 | Dock auto-launch on login — pure policy (fork/exec), works as-is | ✅ no code needed | | B4 | Clip workspace indicator — number + name rendered on clip icon | ✅ commit 3687ff0 | | B5 | Drawer icon — directional arrow rendered | ✅ commit 9ed1c19 | | B6 | Icon bounce on launch — `doAppBounce` uses vtable `window_move`, already works | ✅ no code needed | --- ## 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`): ```c // 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 — COMPLETE (2026-05-23) *All WINGs shared widget code now has zero X11 executing on the Wayland path.* ### Phase D Summary | Commit | Files | Description | |--------|-------|-------------| | 264679a | wwindow.c | ICCCM properties routed through viewBackend vtable | | 9eecb25 | wballoon.c, wpopupbutton.c, wtext.c | Drawing routed through render/view backends | | 2dce454 | wsplitview.c, wtextfield.c | XOR drawing via xor_fill_rect vtable, removed XRotateBuffers | | 9ccfd33 | selection.c | selection_release + selection_request vtable dispatch | | 0e26aa8 | wcolorpanel.c | 60 pixmap/copy/fill calls → render backend | | 8ec3380 | wcolorpanel.c | Magnifier gated via magnify_callback vtable (X11 provides impl, Wayland NULL) | | 7077e00 | wtext.c, wmisc.c | Removed dead X11 code (unused XEvent, dead #ifndef DOUBLE_BUFFER) | ### Verified Safe (no changes needed) | File | Reason | |------|--------| | winputmethod.c | W_InitIM called from W_X11PlatformInit only; W_CreateIC/W_DestroyIC guarded by scr->imctx (NULL on Wayland) | | wappresource.c | W_InitApplication called from W_X11PlatformInit only | | wballoon.c (drawing) | drawBalloon/makePixmap/W_BalloonShowText only reachable via x11_balloon_draw vtable | | DnD (dragcommon/dragsource/dragdestination) | drag_begin vtable intercepts WMDragImageFromView; all other DnD code only called from X11 event handlers | | wtextfield.c convertSelection | X11 selection handler, only triggered by SelectionRequest X events | | wcolorpanel.c magnifier | 40 X11 calls gated behind magnify_callback (NULL on Wayland) | ### Remaining X11 References (compile-time only, no runtime impact) - `XPoint` struct type in wruler.c, wtabview.c (just 2 ints) - `XSetWindowAttributes` in wview.c (struct type for view init) - `#include ` for key constants (xkbcommon uses same values) - `WMCreatePixmapFromXPixmaps` API name in wbutton.c, wcolorwell.c --- ### 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: ```c 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** |