From f289ad7a8de763f0ae76b6066179e9ec2597a216 Mon Sep 17 00:00:00 2001 From: Window Maker Date: Sat, 16 May 2026 12:28:44 +0200 Subject: [PATCH] wl impl planning --- ACTION_PLAN.md | 506 +++++++++++++++ ACTION_PLAN_PROMPTS.md | 666 ++++++++++++++++++++ ARCHITECTURE.md | 245 ++++++++ BACKEND_ABSTRACTION.md | 1326 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 2743 insertions(+) create mode 100644 ACTION_PLAN.md create mode 100644 ACTION_PLAN_PROMPTS.md create mode 100644 ARCHITECTURE.md create mode 100644 BACKEND_ABSTRACTION.md diff --git a/ACTION_PLAN.md b/ACTION_PLAN.md new file mode 100644 index 0000000..a029517 --- /dev/null +++ b/ACTION_PLAN.md @@ -0,0 +1,506 @@ +# 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 + +--- + +## 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 +- **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` +- **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 +- **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 +- **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 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` +- **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` +- **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 + +*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: + ```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 + 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 (already split) | F.2-F.5 | +| `wmlib/` | 9 | Not addressed (too small; keep for X11 or drop) | +| **Total** | **~700** | **~45 tasks** | diff --git a/ACTION_PLAN_PROMPTS.md b/ACTION_PLAN_PROMPTS.md new file mode 100644 index 0000000..97a88af --- /dev/null +++ b/ACTION_PLAN_PROMPTS.md @@ -0,0 +1,666 @@ +# Prompt Templates for Mechanical Wayland Port Tasks + +## Reference Tables (embed in every prompt) + +### X11 Event Type → WMEvent Type + +``` +KeyPress/KeyRelease → WME_KEY_PRESS/WME_KEY_RELEASE → event->u.key.* +ButtonPress/ButtonRelease → WME_BUTTON_PRESS/WME_BUTTON_RELEASE → event->u.button.* +MotionNotify → WME_MOTION → event->u.motion.* +EnterNotify/LeaveNotify → WME_ENTER/WME_LEAVE → event->u.crossing.* +Expose → WME_EXPOSE → event->u.expose.* +ConfigureNotify → WME_CONFIGURE → event->u.configure.* +ConfigureRequest → WME_CONFIGURE_REQUEST → event->u.configureRequest.* +MapNotify → WME_MAP → event->u.lifecycle.* +MapRequest → WME_MAP_REQUEST → event->u.mapRequest.* +UnmapNotify → WME_UNMAP → event->u.lifecycle.* +DestroyNotify → WME_DESTROY → event->u.lifecycle.* +FocusIn/FocusOut → WME_FOCUS_IN/WME_FOCUS_OUT → event->u.focus.* +PropertyNotify → WME_PROPERTY → event->u.property.* +ClientMessage → WME_CLIENT_MESSAGE → event->u.clientMessage.* +SelectionNotify → WME_SELECTION → event->u.selection.* +SelectionRequest → WME_SELECTION_REQUEST → event->u.selection.* (check) +SelectionClear → WME_SELECTION_CLEAR → event->u.selection.* (check) +VisibilityNotify → WME_VISIBILITY → event->u.visibility.* +ColormapNotify → WME_COLORMAP → event->u.colormap.* +MappingNotify → WME_MAPPING → event->u.mapping.* +ReparentNotify → WME_REPARENT → event->u.reparent.* +``` + +### X11 Event Field → WMEvent Field + +``` +event->xkey.keycode → event->u.key.keycode +event->xkey.state → event->u.key.state +event->xkey.time → event->u.key.time +event->xkey.window → event->u.key.window +event->xkey.x, y → event->u.key.x, y +event->xkey.x_root → event->u.key.x_root + +event->xbutton.button → event->u.button.button +event->xbutton.state → event->u.button.state +event->xbutton.time → event->u.button.time +event->xbutton.window → event->u.button.window +event->xbutton.x, y → event->u.button.x, y +event->xbutton.x_root → event->u.button.x_root + +event->xmotion.state → event->u.motion.state +event->xmotion.time → event->u.motion.time +event->xmotion.window → event->u.motion.window +event->xmotion.x, y → event->u.motion.x, y +event->xmotion.x_root → event->u.motion.x_root + +event->xcrossing.window → event->u.crossing.window +event->xcrossing.state → event->u.crossing.state +event->xcrossing.time → event->u.crossing.time +event->xcrossing.mode → event->u.crossing.mode +event->xcrossing.detail → event->u.crossing.detail + +event->xconfigure.window → event->u.configure.window +event->xconfigure.x, y → event->u.configure.x, y +event->xconfigure.width → event->u.configure.width +event->xconfigure.height → event->u.configure.height + +event->xclient.data.l[N] → event->u.clientMessage.data.l[N] +event->xclient.message_type → event->u.clientMessage.atom +event->xclient.window → event->u.clientMessage.window + +event->xproperty.atom → event->u.property.atom +event->xproperty.time → event->u.property.time +event->xproperty.window → event->u.property.window +``` + +--- + +## Template 1: Change Function Signature from `XEvent*` to `WMEvent*` + +**Use for**: A5.1, B.3, E.3, E.4 (and any function that takes an X11 event type as parameter) + +``` +TASK: Change the function {{FUNCTION_NAME}} in {{FILE}} from taking an X11 event +type to taking the backend-neutral WMEvent type. + +GOVERNING PRINCIPLE: Policy code in src/ must never reference X11 types. The +backend-neutral event type is `WMEvent` (defined in WINGs/WINGs/WMEvent.h). + +STEPS: + +1. Read {{FILE}} and {{HEADER_FILE}} to find the function signature. + +2. Change the parameter type: + OLD: {{OLD_PARAM_TYPE}} *{{PARAM_NAME}} + NEW: WMEvent *{{PARAM_NAME}} + +3. Update the header file ({{HEADER_FILE}}) with the same signature change. + +4. Inside the function body, replace every X11 event field access: + - event->xkey.* → event->u.key.* + - event->xbutton.* → event->u.button.* + - event->xmotion.* → event->u.motion.* + - event->xcrossing.* → event->u.crossing.* + - event->xconfigure.* → event->u.configure.* + - event->xclient.* → event->u.clientMessage.* + - event->xproperty.* → event->u.property.* + - event->type == MotionNotify → event->type == WME_MOTION + - event->type == ButtonPress → event->type == WME_BUTTON_PRESS + - event->type == ButtonRelease → event->type == WME_BUTTON_RELEASE + - event->type == KeyPress → event->type == WME_KEY_PRESS + (Use the X11→WMEvent mapping table for the complete list.) + +5. Find ALL call sites by grepping for {{FUNCTION_NAME}} across src/ and WINGs/. + At each call site: + - If the caller has a `WMEvent`, pass it directly. + - If the caller has an `XEvent*` via `_x11_raw`, change to pass the `WMEvent` + directly (this eliminates the `_x11_raw` access). + +6. If the function used `XWindowChanges` or other X11 structs internally to call + Xlib functions, replace those Xlib calls with wm_backend->*() vtable calls + using the WMEvent fields. + +VERIFICATION: +- `grep -rn '{{OLD_PARAM_TYPE}}' {{FILE}} {{HEADER_FILE}}` returns zero matches. +- `grep -rn '_x11_raw.*{{FUNCTION_NAME}}\|{{FUNCTION_NAME}}.*_x11_raw' src/` returns + zero matches. +- The code compiles. + +CONSTRAINTS: +- Do NOT add `#ifdef` guards. Do NOT add `if (dpy)` checks. +- Do NOT change the function's logic — only its interface types. +- Do NOT modify files in src/backend/. +``` + +--- + +## Template 2: Eliminate `_x11_raw` Access + +**Use for**: R.1, R.2, R.3, E.1, and individual sites in E.2 + +``` +TASK: Eliminate the `_x11_raw` escape hatch at {{FILE}}:{{LINE}}. + +GOVERNING PRINCIPLE: Policy code must never reach through the WMEvent abstraction +to access the raw X11 event. The `_x11_raw` field exists only for backend code. + +CONTEXT: At line {{LINE}}, the code does: + XEvent *xev = (XEvent *)event->_x11_raw; +and then accesses xev->{{XEVENT_FIELD}} to get {{WHAT_IT_NEEDS}}. + +STEPS: + +1. Read {{FILE}} around line {{LINE}} (±30 lines) to understand what information + is extracted from the raw XEvent. + +2. Identify the equivalent field in the WMEvent struct: + {{XEVENT_FIELD}} → {{WMEVENT_FIELD}} + (Use the X11→WMEvent field mapping table.) + +3. Replace the `_x11_raw` cast and XEvent field access with direct WMEvent access: + OLD: XEvent *xev = (XEvent *)event->_x11_raw; + ... xev->{{XEVENT_FIELD}} ... + NEW: ... event->{{WMEVENT_FIELD}} ... + +4. Delete the `XEvent *xev` declaration and the cast. + +5. If the raw XEvent was passed to another function (e.g., `someFunc(xev)`), + that function's signature must also be changed (use Template 1). + +6. If the raw XEvent was passed to a `wm_backend->*()` vtable call, change the + vtable call to accept `WMEvent*` instead of `void*`. Update the vtable + signature in backend.h, and update both x11_backend.c and wl_backend.c + implementations. The X11 backend extracts `_x11_raw` internally; the Wayland + backend uses WMEvent fields directly. + +VERIFICATION: +- `grep -n '_x11_raw' {{FILE}}` shows one fewer match than before. +- The code compiles. + +CONSTRAINTS: +- Do NOT add NULL checks on `_x11_raw`. Do NOT add fallback paths. +- If the information needed is NOT available in WMEvent, STOP and report what + field is missing. Do not invent a workaround. +``` + +--- + +## Template 3: Replace Direct Xlib Call with `wm_backend->*()` Vtable Call + +**Use for**: C.1, R.4, R.5, B.1, and any `src/*.c` file with direct Xlib calls + +``` +TASK: Replace the direct Xlib call {{XLIB_FUNCTION}} at {{FILE}}:{{LINE}} with +the backend vtable call {{VTABLE_SLOT}}. + +GOVERNING PRINCIPLE: Policy code in src/ calls display operations exclusively +through `wm_backend->*()`. Direct Xlib calls are forbidden. + +CONTEXT: The global `wm_backend` pointer is declared in src/backend/backend.h: + extern const WMBackend *wm_backend; + +STEPS: + +1. Read {{FILE}} around line {{LINE}} (±10 lines) to understand the Xlib call + and its arguments. + +2. Find the matching vtable slot in src/backend/backend.h: + `grep -n '{{VTABLE_SLOT}}' src/backend/backend.h` + Read the slot's signature and documentation comment. + +3. Replace the Xlib call: + OLD: {{XLIB_FUNCTION}}(dpy, {{ARGS}}); + NEW: wm_backend->{{VTABLE_SLOT}}({{TRANSLATED_ARGS}}); + + Argument translation rules: + - Drop `dpy` (the backend knows its own display connection). + - Drop `GC` arguments (the backend manages its own GCs). + - `scr->root_win` → `scr` (the backend knows the root window). + - X11 `Window` → `WNativeWindow` (same underlying type, no cast needed). + - X11 `Pixmap` → `Pixmap` (same type in both backends). + +4. If the vtable slot does NOT exist, STOP and report: + "Need new vtable slot: {{PROPOSED_SIGNATURE}}" + Do not add the slot yourself. + +5. Remove any now-unused `#include` of X11 headers if this was the last Xlib + call in the file. + +VERIFICATION: +- `grep -n '{{XLIB_FUNCTION}}' {{FILE}}` shows one fewer match. +- The code compiles. + +CONSTRAINTS: +- Do NOT add `#ifdef` guards or `if (dpy)` checks. +- Do NOT modify files in src/backend/. +- One Xlib call per task. Do not batch. +``` + +--- + +## Template 4: Guard X11-Only Code Block in WINGs + +**Use for**: D.1, D.2, D.11, and WINGs files where X11 code must be skipped on Wayland + +``` +TASK: Guard the X11-only code block at {{FILE}}:{{START_LINE}}-{{END_LINE}} so it +only executes when running on the X11 backend. + +GOVERNING PRINCIPLE: WINGs code uses `scr->display != NULL` to detect the X11 +backend. When `scr->display` is NULL, the Wayland backend is active and X11 +calls must be skipped. + +STEPS: + +1. Read {{FILE}} from line {{START_LINE}} to {{END_LINE}}. + +2. Confirm every line in this range calls Xlib functions or uses X11 types that + have no meaning on Wayland (GC, XCreateGC, XInternAtom, etc.). + +3. Wrap the block: + ```c + if (scr->display) { + // ... existing X11 code unchanged ... + } + ``` + Where `scr` is the `W_Screen*` in scope. Find it via: + - Direct variable `scr` or `scrPtr` + - `view->screen` + - `WMWidgetScreen(widget)` + +4. If the Wayland backend needs equivalent behavior (e.g., the code creates + cursors or interns atoms), add an `else` block that calls the appropriate + backend vtable: + ```c + if (scr->display) { + // X11 path + } else { + // Wayland path — use scr->viewBackend->*() or scr->renderBackend->*() + } + ``` + If no Wayland equivalent is needed (e.g., GC creation), the `else` is omitted. + +VERIFICATION: +- The code compiles. +- Running with `WMAKER_USE_WAYLAND=1` does not crash in this code path (the + X11 block is skipped). + +CONSTRAINTS: +- Do NOT move the code to a separate file. This template is for cases where + the X11 code is small (< 50 lines) and inline guarding is cleaner than a split. +- Do NOT delete the X11 code. It must continue to work for the X11 backend. +- The guard condition is always `if (scr->display)`, never `#ifdef WAYLAND`. +``` + +--- + +## Template 5: Split File into `_x11.c` / `_wl.c` Backend Pair + +**Use for**: D.3, D.5, D.6, and any WINGs file that is entirely X11-specific + +``` +TASK: Split {{FILE}} into an X11 backend file, a Wayland backend file, and a +thin dispatch layer. + +GOVERNING PRINCIPLE: WINGs uses five backend vtables (view, event, render, font, +color). Files that are entirely X11-specific should be split into _x11/_wl pairs +following the existing pattern (e.g., wcolor.c / wcolor_x11.c / wcolor_wl.c). + +EXISTING PATTERN (use as reference): +- WINGs/wcolor.c — dispatch layer, calls scr->colorBackend->*() +- WINGs/wcolor_x11.c — X11 implementation, exports W_ColorBackendX11 +- WINGs/wcolor_wl.c — Wayland implementation, exports W_ColorBackendWL + +STEPS: + +1. Read {{FILE}} completely. Identify all public functions (declared in headers). + +2. Rename {{FILE}} → {{FILE_BASE}}_x11.c. Keep all code unchanged. + +3. Create {{FILE_BASE}}_wl.c with stub implementations of every public function. + Each stub should either: + - Implement the Wayland equivalent if straightforward + - Call `wwarning("{{FUNCTION}}: not yet implemented for Wayland")` and return + a safe default (NULL, 0, False) + +4. Create {{FILE_BASE}}.c as a dispatch layer. For each public function: + ```c + ReturnType PublicFunction(args...) { + if (scr->display) + return x11_PublicFunction(args...); + else + return wl_PublicFunction(args...); + } + ``` + OR, if a vtable is more appropriate, define a new backend struct in WINGsP.h.in + following the existing pattern (W_ColorBackend, W_FontBackend, etc.). + +5. Update WINGs/Makefile.am: + - Remove {{FILE_BASE}}.c from the source list (it was renamed) + - Add {{FILE_BASE}}.c, {{FILE_BASE}}_x11.c, {{FILE_BASE}}_wl.c + +6. Update any header files that declare the public functions — no signature + changes needed. + +VERIFICATION: +- `make -C WINGs` compiles successfully. +- The X11 backend path is unchanged (existing behavior preserved). +- `grep -rn 'X[A-Z][a-z]' WINGs/{{FILE_BASE}}.c` returns zero (dispatch layer + has no Xlib calls). + +CONSTRAINTS: +- The _wl.c stubs may be incomplete. That is acceptable. Mark incomplete stubs + with `/* TODO: Wayland implementation */`. +- Do NOT change any function signatures. +- Do NOT modify {{FILE_BASE}}_x11.c beyond the rename. All existing code stays. +``` + +--- + +## Template 6: Replace Xlib Drawing Call with WINGs Render Backend + +**Use for**: D.4, D.7, D.8, D.9, D.10, and WPrefs.app drawing (E.5a-c) + +``` +TASK: Replace the Xlib drawing call {{XLIB_CALL}} at {{FILE}}:{{LINE}} with a +WINGs render backend call. + +GOVERNING PRINCIPLE: Widget code draws through `scr->renderBackend->*()`. Direct +Xlib drawing calls (XFillRectangle, XDrawLine, XCopyArea, etc.) are forbidden +in non-_x11.c files. + +RENDER BACKEND VTABLE (from WINGsP.h.in): + scr->renderBackend->pixmap_create(rctx, width, height, depth) + scr->renderBackend->pixmap_free(rctx, pm) + scr->renderBackend->pixmap_copy(rctx, src, dst, sx, sy, w, h, dx, dy) + scr->renderBackend->draw_line(rctx, d, color, x1, y1, x2, y2) + scr->renderBackend->draw_rect(rctx, d, color, x, y, w, h) + scr->renderBackend->fill_rect(rctx, d, color, x, y, w, h) + scr->renderBackend->fill_polygon(rctx, d, color, points, npoints) + scr->renderBackend->fill_arc(rctx, d, color, x, y, w, h, angle1, angle2) + scr->renderBackend->composite_pixmap(rctx, dst, src, sx, sy, w, h, dx, dy) + +Where: + rctx = scr->renderBackendCtx + d = the target WMDrawable (Pixmap or window's drawable) + color = a WMColor* (obtain via WMColorGC pattern → WMColor*) + +STEPS: + +1. Read {{FILE}} around line {{LINE}} (±5 lines). + +2. Identify the Xlib call and its arguments. + +3. Map to the render backend equivalent: + XFillRectangle(dpy, d, gc, x, y, w, h) + → scr->renderBackend->fill_rect(scr->renderBackendCtx, d, color, x, y, w, h) + + XDrawLine(dpy, d, gc, x1, y1, x2, y2) + → scr->renderBackend->draw_line(scr->renderBackendCtx, d, color, x1, y1, x2, y2) + + XDrawRectangle(dpy, d, gc, x, y, w, h) + → scr->renderBackend->draw_rect(scr->renderBackendCtx, d, color, x, y, w, h) + + XCopyArea(dpy, src, dst, gc, sx, sy, w, h, dx, dy) + → scr->renderBackend->pixmap_copy(scr->renderBackendCtx, src, dst, sx, sy, w, h, dx, dy) + + XCreatePixmap(dpy, drawable, w, h, depth) + → scr->renderBackend->pixmap_create(scr->renderBackendCtx, w, h, depth) + + XFreePixmap(dpy, pm) + → scr->renderBackend->pixmap_free(scr->renderBackendCtx, pm) + + XFillPolygon(dpy, d, gc, points, npoints, shape, mode) + → scr->renderBackend->fill_polygon(scr->renderBackendCtx, d, color, points, npoints) + + XFillArc(dpy, d, gc, x, y, w, h, angle1, angle2) + → scr->renderBackend->fill_arc(scr->renderBackendCtx, d, color, x, y, w, h, angle1, angle2) + +4. For the `color` argument: the Xlib code uses a GC with a foreground color. + Find where the GC's foreground was set (look for `XSetForeground` or + `WMColorGC(someColor)`). Use that `WMColor*` directly. + +5. For `scr`: find the WMScreen in scope. Common patterns: + - `WMWidgetScreen(widget)` + - `view->screen` + - local variable `scr` + +VERIFICATION: +- `grep -n '{{XLIB_CALL}}' {{FILE}}` shows one fewer match. +- The code compiles. + +CONSTRAINTS: +- One call replacement per task. +- If the GC was created with `XCreateGC` just for this drawing sequence, and + this is the last use, also delete the `XCreateGC` and `XFreeGC` calls. +- Do NOT change drawing logic (coordinates, colors, order). +``` + +--- + +## Template 7: Remove Compile-Time `#ifdef` Guard Using Backend-Neutral API + +**Use for**: A4.1, B.2, H.1, R.7, and any `#ifdef USE_XINERAMA` / `#ifdef USE_XSHAPE` / `#ifdef USE_DOCK_XDND` site + +``` +TASK: Remove the compile-time guard `#ifdef {{GUARD_NAME}}` at {{FILE}}:{{LINE}} +and replace the guarded code with a backend-neutral API call. + +GOVERNING PRINCIPLE: No `#ifdef` guards for backend-specific features in policy +code. Feature availability is determined at runtime by the backend vtable. + +STEPS: + +1. Read {{FILE}} around line {{LINE}}. Identify the `#ifdef {{GUARD_NAME}}` block + and the corresponding `#else` / `#endif`. + +2. Understand what the guarded code does: + - `USE_XINERAMA`: queries per-monitor geometry + - `USE_XSHAPE`: queries window shape + - `USE_DOCK_XDND`: sets up XDND awareness + - `USE_MWM_HINTS`: reads Motif hints + - `USE_RANDR`: handles RandR events + +3. Identify the backend-neutral replacement: + - Xinerama monitor queries → `wGetHeadForPoint(scr, point)`, + `wGetUsableAreaForHead(scr, head)`, `wGetRectForHead(scr, head)`. + These functions already exist and are backend-abstracted. + - XShape queries → `wm_backend->window_get_shape()` or equivalent vtable slot. + - XDND setup → move into the backend's `appicon_show` implementation. + - MWM hints → `wm_backend->client_get_mwm_hints()` or equivalent vtable slot. + +4. Replace the `#ifdef` / `#else` / `#endif` with the backend-neutral code. + Delete both the guarded block and the else block. Write one unified path. + +5. If the `#ifdef` block called Xlib functions that have no vtable equivalent, + STOP and report: "Need vtable slot for {{OPERATION}}". + +VERIFICATION: +- `grep -n '{{GUARD_NAME}}' {{FILE}}` shows one fewer match. +- The code compiles with and without the feature flag defined. + +CONSTRAINTS: +- Do NOT add a different `#ifdef`. Do NOT add runtime `if (dpy)` checks. +- The replacement must work on both X11 and Wayland backends. +- If the `#else` block was empty (feature simply absent), the replacement is + to call the vtable slot unconditionally — the Wayland backend returns a + sensible default (e.g., "no shape", "single head"). +``` + +--- + +## Template 8: Replace Xlib Call in WPrefs.app with WINGs API + +**Use for**: E.5 sub-tasks (all WPrefs.app Xlib elimination) + +``` +TASK: Replace the Xlib call {{XLIB_CALL}} at WPrefs.app/{{FILE}}:{{LINE}} with +the equivalent WINGs public API call. + +GOVERNING PRINCIPLE: WPrefs.app is a WINGs client application. It must use only +WINGs public APIs (declared in WINGs/WINGs/WINGs.h), never Xlib directly. WINGs +routes all operations through its backend vtables internally. + +WINGS API EQUIVALENTS: + XFillRectangle(dpy, d, WMColorGC(c), x, y, w, h) + → WMFillRectangle(scr, d, c, x, y, w, h) + (if WMFillRectangle does not exist, use: + scr->renderBackend->fill_rect(scr->renderBackendCtx, d, c, x, y, w, h)) + + XDrawLine(dpy, d, WMColorGC(c), x1, y1, x2, y2) + → WMDrawLine(scr, d, c, x1, y1, x2, y2) + (if WMDrawLine does not exist, use: + scr->renderBackend->draw_line(scr->renderBackendCtx, d, c, x1, y1, x2, y2)) + + XCopyArea(dpy, src, dst, gc, sx, sy, w, h, dx, dy) + → WMCopyArea(scr, src, dst, sx, sy, w, h, dx, dy) + (if WMCopyArea does not exist, use: + scr->renderBackend->pixmap_copy(scr->renderBackendCtx, src, dst, sx, sy, w, h, dx, dy)) + + XCreatePixmap(dpy, parent, w, h, depth) + → WMCreatePixmap(scr, w, h, depth, False) [returns WMPixmap*] + then use WMGetPixmapXID(pixmap) to get the drawable + + XFreePixmap(dpy, pm) + → WMReleasePixmap(pixmap) [if using WMPixmap*] + or scr->renderBackend->pixmap_free(scr->renderBackendCtx, pm) + + XCreateGC / XFreeGC + → DELETE. WINGs manages GCs internally. Use WMColor* for color selection. + + XParseColor(dpy, cmap, name, &xcolor) + → WMCreateNamedColor(scr, name, True) [returns WMColor*] + + XGrabKeyboard / XUngrabKeyboard + → No direct WINGs equivalent. Use: + scr->viewBackend->keyboard_grab(scr->viewBackendCtx, view) + (if slot exists; otherwise STOP and report) + + XGrabPointer / XUngrabPointer + → No direct WINGs equivalent. Use: + scr->viewBackend->pointer_grab(scr->viewBackendCtx, view) + (if slot exists; otherwise STOP and report) + + XOpenDisplay + → WMOpenScreen(display_name) [in main.c initialization] + + XSendEvent (for WM commands) + → Write to defaults database + send SIGHUP, or use a WINGs notification. + STOP and report if unclear. + +STEPS: + +1. Read WPrefs.app/{{FILE}} around line {{LINE}} (±10 lines). +2. Identify the Xlib call and its purpose. +3. Find the WINGs equivalent from the table above. +4. Replace the call. Adjust variable types if needed (e.g., `GC gc` → delete, + `XColor xcolor` → `WMColor *color`). +5. If the WINGs API function does not exist, use the renderBackend/viewBackend + vtable call directly (WPrefs.app includes WINGsP.h for internal access). + +VERIFICATION: +- `grep -n '{{XLIB_CALL}}' WPrefs.app/{{FILE}}` shows one fewer match. +- The code compiles. + +CONSTRAINTS: +- One call replacement per task. +- Prefer WINGs public API over direct vtable access. +- Do NOT add `#ifdef` guards or `if (dpy)` checks. +``` + +--- + +## Template 9: Verify File is X11-Free + +**Use for**: F.2, F.3, F.4, G.1, I.1, I.2, C.3, H.5, and any verification task + +``` +TASK: Verify that {{FILE}} has no X11 dependencies that would break on the +Wayland backend. + +STEPS: + +1. Run: `grep -nE '\bX[A-Z][a-z]' {{FILE}}` + This finds direct Xlib function calls. + EXPECTED: {{EXPECTED_XLIB_COUNT}} matches. + +2. Run: `grep -nE '\bDisplay\b|\bXEvent\b|\bXImage\b|\bXColor\b' {{FILE}}` + This finds X11 type usage. + EXPECTED: {{EXPECTED_TYPE_COUNT}} matches. + +3. Run: `grep -n '_x11_raw' {{FILE}}` + This finds raw event escape hatches. + EXPECTED: 0 matches. + +4. Run: `grep -n '#ifdef.*USE_X\|#ifdef.*HAVE_X\|#ifdef.*XINERAMA\|#ifdef.*XSHAPE\|#ifdef.*XDND' {{FILE}}` + This finds X11 feature guards. + EXPECTED: {{EXPECTED_IFDEF_COUNT}} matches. + +5. If any count exceeds expected: + - For each unexpected match, report the line number and the X11 reference. + - Classify as: (a) actual dependency, (b) comment/string, (c) type used + but never passed to Xlib. + +REPORT FORMAT: +``` +FILE: {{FILE}} +Xlib calls: N (expected {{EXPECTED_XLIB_COUNT}}) +X11 types: N (expected {{EXPECTED_TYPE_COUNT}}) +_x11_raw: N (expected 0) +#ifdef guards: N (expected {{EXPECTED_IFDEF_COUNT}}) +STATUS: CLEAN / NEEDS WORK +ISSUES: (list any unexpected findings) +``` + +CONSTRAINTS: +- Do NOT modify the file. This is a read-only verification task. +- Report findings exactly. Do not interpret or fix. +``` + +--- + +## Template 10: Batch Instantiation Helper + +**Use for**: Generating concrete prompts from templates + the action plan + +``` +Given the action plan task {{TASK_ID}}, instantiate the appropriate template. + +ACTION PLAN TASK: + ID: {{TASK_ID}} + File(s): {{FILES}} + Operation: {{OPERATION_SUMMARY}} + Xlib calls to eliminate: {{COUNT}} + Blocked by: {{DEPENDENCIES}} + +Select the template: +- Function signature change (XEvent* → WMEvent*) → Template 1 +- _x11_raw elimination → Template 2 +- Direct Xlib → wm_backend vtable → Template 3 +- Guard X11 block in WINGs → Template 4 +- Split into _x11/_wl pair → Template 5 +- Xlib drawing → WINGs render backend → Template 6 +- Remove #ifdef guard → Template 7 +- WPrefs.app Xlib → WINGs API → Template 8 +- Verification only → Template 9 + +Fill in ALL template variables from the action plan data and the codebase. +Include exact line numbers (use `grep -n` to find current line numbers, as they +may have shifted from the original analysis). + +Output the fully instantiated prompt, ready to hand to an agent. +``` + +--- + +## Usage Notes + +1. **One task per prompt.** Never batch multiple Xlib call replacements into a single prompt. The agent should make one surgical change, verify it compiles, and stop. + +2. **Always include the mapping tables.** The X11→WMEvent type and field mapping tables should be included verbatim in every Template 1 and Template 2 instantiation. The agent needs them as reference, not from memory. + +3. **Line numbers drift.** Before instantiating a template, re-grep for the exact current line number. The analysis line numbers are from the initial read and will shift as changes accumulate. + +4. **Compilation is the gate.** Every task ends with "the code compiles." If it doesn't, the task is not done. The agent should fix compilation errors before reporting completion. + +5. **STOP conditions are real.** Templates 3, 6, 7, and 8 include "STOP and report" conditions for missing vtable slots or APIs. The agent must actually stop — not invent a workaround. Missing infrastructure is a signal to create a new task, not to hack around it. + +6. **Wave ordering matters.** Wave 1 tasks are independent and can be parallelized. Wave 2 tasks depend on Wave 1. Wave 3 (WPrefs.app) depends on Wave 2. Respect this ordering when dispatching to agents. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..4c0e2c1 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,245 @@ +# WindowMaker 0.96.0 — Architecture Overview + +## Top-Level Directory Map + +| Directory | Role | LOC (`.c`) | +|---|---|---| +| **`src/`** | **Window manager core** — policy, layout, event dispatch | ~46k | +| **`src/backend/`** | Display-system abstraction (X11 + Wayland vtable) | ~13k | +| **`WINGs/`** | Widget library (buttons, text fields, panels, etc.) + utility library (WUtil) | ~40k | +| **`wrlib/`** | **Raster graphics library** — image loading, scaling, gradients, format codecs | ~9k | +| **`wmlib/`** | Tiny helper library for external apps to talk to the WM (menus, events) | ~500 | +| **`WPrefs.app/`** | Preferences GUI application (uses WINGs) | ~15k | +| **`util/`** | CLI utilities: `wmsetbg`, `wmagnify`, `wmiv`, `getstyle`, `setstyle`, etc. | ~7k | +| **`WindowMaker/`** | Runtime data: default menus, themes, icon sets, pixmaps (no code) | — | +| **`doc/`** | Documentation | — | +| **`po/`** | Translations (gettext) | — | + +## 1. The Core — `src/` + +This is the window manager itself. The biggest files reveal the hotspots: + +| File | LOC | Responsibility | +|---|---|---| +| `dock.c` | 4913 | Dock & clip (app launcher bar) | +| `defaults.c` | 3450 | Reading/writing the defaults database (user prefs) | +| `window.c` | 2899 | `WWindow` lifecycle — manage, unmanage, configure | +| `moveres.c` | 2458 | Interactive move/resize (rubber-banding, snap) | +| `menu.c` | 2407 | Menu data structure, drawing, keyboard navigation | +| `actions.c` | 2340 | High-level window actions (maximize, shade, close…) | +| `event.c` | 2076 | **Main event loop + dispatch** | +| `wmspec.c` | 2029 | EWMH / `_NET_WM_*` compliance | +| `framewin.c` | 1243 | Frame window (titlebar, buttons, resizebar) drawing | +| `icon.c` | 964 | Miniwindow / app icon rendering | +| `workspace.c` | 936 | Virtual desktop management | +| `screen.c` | 640 | Per-screen state (`WScreen`) init/teardown | +| `startup.c` | 651 | `StartUp()` — full WM initialization sequence | +| `main.c` | 792 | `main()` — arg parsing, backend selection, enters `EventLoop()` | + +### Key Data Structures + +- **`WScreen`** (`screen.h`, 352 lines) — god object for a physical screen. Holds the root window, stacking lists, colormap, default pixmaps, the dock, the clip, workspace list, and the backend-specific `backend_screen` pointer. + +- **`WWindow`** (`window.h`) — one per managed client. Contains the `WFrameWindow`, client hints, geometry, state flags. + +- **`WFrameWindow`** (`framewin.h`) — the decoration frame. Has child `WCoreWindow`s for the titlebar, left/right buttons, and resizebar. + +- **`WCoreWindow`** (`wcore.h`) — lowest-level wrapper around a native window. Every WM-owned window (frames, icons, menus) is a `WCoreWindow`. Contains a `WObjDescriptor`. + +- **`WObjDescriptor`** (`WindowMaker.h`) — the **event dispatch target**. Every `WCoreWindow` carries one. It has four function pointers: + ```c + void (*handle_expose)(WObjDescriptor *sender, WBackendEvent *event); + void (*handle_mousedown)(...); + void (*handle_enternotify)(...); + void (*handle_leavenotify)(...); + ``` + Plus a `parent_type` / `parent` back-pointer so dispatch can reach the owning `WWindow` or `WMenu`. + +## 2. Event Dispatch — `src/event.c` + +The flow is: + +``` +main() + → StartUp() // init screens, load defaults, manage existing windows + → EventLoop() // never returns + → wm_backend->event_loop_run() + // X11: WINGs event pump (WMNextEvent + WMHandleEvent) + // Wayland: wl_display_run() +``` + +Inside the X11 path, each event ends up at: + +``` +DispatchEvent(WMEvent *event) + → switch (event->type) + WME_MAP_REQUEST → handleMapRequest() + WME_KEY_PRESS → handleKeyPress() + WME_BUTTON_PRESS → handleButtonPress() + WME_EXPOSE → handleExpose() + WME_PROPERTY → handlePropertyNotify() + WME_CONFIGURE_REQUEST → handleConfigureRequest() + ... +``` + +Most handlers look up the target window via `wm_backend->context_find(win, WM_CTX_CLIENT_WIN)` to get the `WObjDescriptor`, then call its function pointers (e.g., `desc->handle_mousedown(desc, event)`). + +**Signal handling** is also routed through `DispatchEvent` — `SIGTERM` sets `WSTATE_NEED_EXIT`, `SIGHUP` sets `WSTATE_NEED_RESTART`, and the top of `DispatchEvent` checks these flags before processing the event. + +## 3. Backend Abstraction — `src/backend/` + +A **vtable pattern** (`WMBackend` struct, 1821-line header) with a single global pointer: + +```c +extern const WMBackend *wm_backend; // set in main() before anything else +``` + +`main.c` picks the backend: +```c +wm_backend = &wl_backend; // or +wm_backend = &x11_backend; +``` + +The vtable covers **everything** the display system does: + +| Category | Example slots | +|---|---| +| Display lifecycle | `display_open`, `display_close`, `display_post_open` | +| Screen lifecycle | `screen_open`, `screen_init_display`, `screen_close` | +| Frame windows | `frame_create_toplevel`, `frame_create_child`, `frame_destroy`, `frame_configure`, `frame_paint` | +| Context table | `context_save`, `context_find`, `context_delete` | +| Event loop | `event_loop_run`, `event_loop_terminate`, `event_sync`, `event_pending` | +| Focus & grabs | `focus_set`, pointer/keyboard grab | +| Atoms & properties | `atom_intern`, property get/set | + +Implementations: +- **`backend/x11/x11_backend.c`** (4465 lines) — wraps Xlib calls +- **`backend/wayland/wl_backend.c`** (8426 lines) — wlroots compositor +- **`backend/x11/x11_event.c`** — X11-specific event translation +- **`backend/x11/x11_props.c`** — X11 property helpers +- **`backend/x11/x11_monitor.c`** — Xinerama/XRandR monitor detection + +The event type itself is backend-neutral: `WMEvent` / `WBackendEvent` defined in `WINGs/WINGs/WMEvent.h` with an enum (`WME_KEY_PRESS`, `WME_BUTTON_PRESS`, `WME_MAP_REQUEST`, etc.). + +## 4. WINGs — Widget Library (`WINGs/`) + +Two sub-libraries in one directory: + +### WINGs widgets (`WINGs/WINGs/WINGs.h`, 1933 lines) +Full GUI toolkit with an NeXTSTEP flavor: + +| Widget file | What | +|---|---| +| `wwindow.c` | Top-level windows | +| `wbutton.c` | Buttons | +| `wtextfield.c` | Single-line text input | +| `wtext.c` | Multi-line rich text (3998 lines — the biggest widget) | +| `wlist.c` | List boxes | +| `wbrowser.c` | Column browser (NeXT-style) | +| `wscroller.c` / `wscrollview.c` | Scrollbars and scroll containers | +| `wslider.c` | Sliders | +| `wpopupbutton.c` | Popup/dropdown buttons | +| `wcolorpanel.c` | Color picker panel | +| `wcolorwell.c` | Color swatch | +| `wfontpanel.c` | Font chooser | +| `wfilepanel.c` | Open/save file dialogs | +| `wtabview.c` | Tab views | +| `wsplitview.c` | Split panes | +| `wruler.c` | Text ruler | +| `wframe.c` | Group boxes | +| `wlabel.c` | Labels | +| `wballoon.c` | Tooltip balloons | +| `wpanel.c` | Alert/input panels | + +Core plumbing: +- **`wview.c`** / `wview_wl.c` / `wview_x11.c` — the view hierarchy (WINGs' equivalent of a "widget base class") +- **`wevent.c`** / `wevent_wl.c` / `wevent_x11.c` — WINGs event handling +- **`wrender_wl.c`** / `wrender_x11.c` — rendering backend split +- **`wfont.c`** / `wfont_wl.c` / `wfont_x11.c` — font rendering split +- **`wcolor.c`** / `wcolor_wl.c` / `wcolor_x11.c` — color allocation split + +### WUtil (`WINGs/WINGs/WUtil.h`, 959 lines) +Utility library used everywhere: +- `array.c`, `bagtree.c`, `hashtable.c`, `tree.c` — data structures +- `proplist.c` — NeXTSTEP-style property list parser (used for defaults database) +- `notification.c` — notification center +- `userdefaults.c` — user defaults system +- `findfile.c`, `string.c`, `memory.c`, `misc.c` — general utilities +- `handlers.c` — idle/timer handlers +- `selection.c` — X selection (clipboard) +- `menuparser.c` — menu file parser + +## 5. Rendering — `wrlib/` + +**wrlib** (Window Maker Raster Library) is a standalone image manipulation library: + +- **Image formats**: PNG, JPEG, TIFF, GIF, XPM, PPM, WebP, ImageMagick (each in `load_*.c` / `save_*.c`) +- **Operations**: `scale.c`, `rotate.c`, `flip.c`, `gradient.c`, `convolve.c`, `draw.c`, `alpha_combine.c` +- **Display bridge**: `convert.c` / `convert_x11.c` / `convert_wl.c` — converts `RImage` to display-system pixmaps +- **Context**: `context.c` — `RContext` wraps the display connection + visual for rendering + +The WM core uses wrlib for all texture/icon rendering (via `texture.c` and `icon.c` in `src/`). + +## 6. Rendering in the WM Core — `src/texture.c`, `src/framewin.c` + +- **`WTexture`** (`texture.h`) is a union of solid color, gradients, and pixmap-based textures. `wTextureRender()` / `wTextureRenderImage()` produce `Pixmap`s or `RImage`s from these. +- **`framewin.c`** paints the actual window decorations — titlebar, buttons, resizebar — using textures. The backend vtable's `frame_paint()` slot allows the Wayland backend to override the entire paint path. +- **`WPixmap`** (`pixmap.h`) is a simple image+mask pair used for icons and button pixmaps. + +## 7. Stacking + +`stacking.c` / `stacking.h` — manages the window stacking order using **window levels** (defined in `WindowMaker.h`): + +``` +WMBackLevel → WMDesktopLevel → WMNormalLevel → WMFloatingLevel → WMDockLevel + → WMSubmenuLevel → WMMainMenuLevel → WMFullscreenLevel → WMPopUpLevel → ... +``` + +## 8. Supporting Programs + +- **`WPrefs.app/`** — full preferences editor. Each `*.c` file is a preference panel (Appearance, Focus, Docks, Icons, Keyboard, Mouse, etc.). Uses WINGs widgets. +- **`util/`** — CLI tools: `wmsetbg` (set background), `wmagnify` (screen magnifier), `wmiv` (image viewer), `getstyle`/`setstyle` (theme import/export), `wmgenmenu`/`wmmenugen` (auto-generate menus from `.desktop` files). + +## Dependency Graph + +``` +┌─────────────────────────────────────────────────────┐ +│ WPrefs.app │ +│ util/ │ +└──────────────┬──────────────────┬───────────────────┘ + │ │ + ▼ ▼ +┌──────────────────┐ ┌─────────────────┐ +│ WINGs widgets │ │ src/ (WM core)│ +│ WINGs/w*.c │ │ policy + layout│ +└───────┬──────────┘ └──┬──────────────┘ + │ │ + │ ┌─────────────┘ + ▼ ▼ +┌──────────────────┐ ┌─────────────────────────┐ +│ WUtil │ │ src/backend/ │ +│ (data structs, │ │ backend.h vtable │ +│ proplist, etc) │ │ ┌─────────┬───────────┐ │ +└───────┬──────────┘ │ │ x11/ │ wayland/ │ │ + │ │ └─────────┴───────────┘ │ + ▼ └────────────┬──────────────┘ +┌──────────────────┐ │ +│ wrlib │◄────────────────┘ +│ (image loading, │ +│ rendering) │ +└──────────────────┘ +``` + +## Key Architectural Patterns + +1. **Vtable backend abstraction** — all display-system calls go through `wm_backend->*()`. Policy code never calls Xlib directly. + +2. **Context-table dispatch** — native window → `WObjDescriptor` lookup via `context_find()`. This is how events reach the right object. + +3. **`WObjDescriptor` function pointers** — per-object event handlers (expose, mousedown, enter, leave). This is the closest thing to polymorphic dispatch in the codebase. + +4. **Flat C module structure** — no deep directory nesting. Each `src/*.c` file is a module with a matching `.h`. Dependencies are explicit `#include`s. + +5. **Three rendering layers**: wrlib (`RImage` manipulation) → `WTexture` (WM-level texture descriptions) → `framewin.c` / `icon.c` (actual decoration painting). + +6. **Wayland as a parallel backend** — not a port, but a second implementation behind the same vtable. The `_wl.c` / `_x11.c` file splits in WINGs show this pattern extending into the widget library too. diff --git a/BACKEND_ABSTRACTION.md b/BACKEND_ABSTRACTION.md new file mode 100644 index 0000000..5bdabbc --- /dev/null +++ b/BACKEND_ABSTRACTION.md @@ -0,0 +1,1326 @@ +# Backend-Specific Branches Remaining in `src/` Policy Code + +The backend vtable (`wm_backend->*()`) handles most display-system dispatch, but significant X11-specific code still lives directly in the policy files. Here's every category. + +--- + +## 1. Compile-Time `#ifdef` Branches + +### `USE_WAYLAND_BACKEND` (2 sites) + +| File | Line | Purpose | +|---|---|---| +| `main.c` | 60 | `#include` wayland backend header | +| `main.c` | 771 | Select `wl_backend` vs `x11_backend` vtable | + +This is clean — it's the backend selection switch. + +### `XKB_BUTTON_HINT` (38 sites — the biggest offender) + +Sprayed across the frame window and window management code. Adds a "language indicator" button to the titlebar. + +| File | Sites | What it guards | +|---|---|---| +| `framewin.c` | **22** | Language button creation, layout, drawing, click handling, destroy | +| `framewin.h` | **10** | Language button fields in `WFrameWindow` struct, function prototypes | +| `window.c` | **10** | Language button setup during window manage/unmanage, attribute updates | +| `wdefaults.c` | 3 | Language button in per-window attribute defaults | +| `WindowMaker.h` | 1 | `WBUT_XKBGROUP*` button constants, `PRED_BPIXMAPS` count | +| `window.h` | 1 | `language_button` field in `WWindow` | +| `def_pixmaps.h` | 1 | Language button pixmap data | +| `dialog.c` | 2 | Feature reporting in info dialog | +| `winspector.c` | 1 | Window inspector language option | + +### `KEEP_XKB_LOCK_STATUS` (6 sites) + +| File | Line(s) | What it guards | +|---|---|---| +| `framewin.c` | 23, 86 | XKB include, language state tracking in frame | +| `framewin.h` | 93 | `languagemode` / `last_languagemode` fields | +| `event.c` | 1853 | XKB state-change event handling | +| `window.c` | 1727 | XKB lock status update on focus | +| `WindowMaker.h` | 359, 622 | XKB state in preferences, XKB event base in globals | +| `keybind.h` | 161 | `WKBD_TOGGLE` keybinding | + +### `USE_DOCK_XDND` (7 sites) + +| File | Line(s) | What it guards | +|---|---|---| +| `appicon.c` | 52, 131, 315, 338 | XDND include, DND-aware setup, DND event handling on app icons | +| `dock.c` | 76 | XDND include | +| `event.c` | (via backend) | XDND client message dispatch | +| `dialog.c` | 1433 | Feature reporting | + +Plus `xdnd.c` itself (312 lines) is **entirely X11-specific** — raw `XInternAtom`, `XGetWindowProperty`, `XSendEvent`, `XGetSelectionOwner` throughout. + +### `USE_XSHAPE` (4 sites + headers) + +| File | Line(s) | What it guards | +|---|---|---| +| `window.c` | 654, 2012, 2236, 2301 | Shape extension queries on client windows | +| `window.h` | 263, 345 | `net_has_shape` flag, shape update prototype | +| `WindowMaker.h` | 615 | Shape event base in globals | +| `balloon.h` | 26 | `SHAPED_BALLOON` for shaped tooltip windows | + +### `USE_XINERAMA` (10 sites) + +| File | Line(s) | What it guards | +|---|---|---| +| `workspace.c` | 252, 280, 295, 303, 328 | Per-monitor workspace geometry calculations | +| `wmspec.c` | 78, 165, 294, 1043, 1087, 1820 | EWMH multi-monitor strut/workarea handling | +| `actions.c` | 975 | Maximize to monitor bounds | +| `dialog.c` | 1445, 1447 | Feature reporting, Solaris Xinerama variant | + +### `USE_RANDR` (1 site + header) + +| File | Line(s) | What it guards | +|---|---|---| +| `dialog.c` | 1458 | Feature reporting | +| `WindowMaker.h` | 629 | RandR event base in globals | + +### `USE_MWM_HINTS` (2 sites) + +| File | Line(s) | What it guards | +|---|---|---| +| `window.c` | 63, 360 | Motif WM hints include and processing | +| `dialog.c` | 1429 | Feature reporting | + +### `HAVE_INOTIFY` (6 sites) + +| File | Line(s) | What it guards | +|---|---|---| +| `main.c` | 23, 487, 787 | Inotify includes, setup, watch creation | +| `event.c` | 25 | Inotify includes | +| `shutdown.c` | 61, 91 | Inotify fd cleanup | +| `WindowMaker.h` | 553 | Inotify fd in globals | + +### Minor `HAVE_*` guards + +| Guard | File | Purpose | +|---|---|---| +| `HAVE_SETSID` | `main.c:326,378`, `dock.c:3000` | `setsid()` availability | +| `HAVE_MALLOC_H` | `dialog.c:39` | Include path | +| `HAVE_XINTERNATOMS` | `wmspec.c:662` | Batch atom intern optimization | +| `HAVE_STDNORETURN` | `event.h:28`, `main.h:27` | C11 `_Noreturn` | + +--- + +## 2. Runtime `_x11_raw` Escape Hatch (16 sites) + +The `WMEvent` struct carries a `void *_x11_raw` field — a pointer to the original `XEvent`. Policy code reaches through it when the abstracted event fields aren't sufficient: + +| File | Line(s) | What it does | +|---|---|---| +| `event.c` | 249, 264, 269, 276 | `handleConfigureRequest` — passes raw XEvent to backend for RandR, Shape, XKB extension events | +| `event.c` | 431 | `handleMapRequest` — raw XEvent for `wManageWindow` | +| `event.c` | 446–447 | `handleExtensions` — passes raw event to backend | +| `event.c` | 782 | `handleButtonPress` — raw XEvent for `wDockReceiveDNDDrop` | +| `event.c` | 831, 834, 852, 857 | `handleClientMessage` — raw XEvent for EWMH, XDND, selection | +| `event.c` | 971, 974 | `handleClientMessage` — raw XEvent to `netwm_process_client_message`, `xdnd_process_client_message` | +| `event.c` | 993 | `handleClientMessage` — raw XEvent for colormap install | +| `event.c` | 1354 | `handleKeyPress` — raw XEvent for root menu shortcuts | +| `framewin.c` | 1165 | Frame titlebar click — raw XEvent | +| `icon.c` | 861 | Miniwindow double-click — raw XEvent | +| `cycling.c` | 227–234 | Window cycling — synthesizes XEvent for switch panel, with Wayland fallback | +| `menu.c` | 738 | Menu key handling — raw XEvent | +| `winspector.c` | 1038 | Window inspector — raw XEvent | + +--- + +## 3. X11 Types in Function Signatures + +Functions in policy code that take raw X11 types as parameters — these are API boundaries that can't work on Wayland without the `_x11_raw` escape hatch or a cast: + +| File | Function | X11 parameter type | +|---|---|---| +| `client.c:119` | `wClientConfigure()` | `XConfigureRequestEvent *` | +| `client.c:247` | `wClientCheckProperty()` | `XPropertyEvent *` | +| `rootmenu.c:320` | `wRootMenuPerformShortcut()` | `XEvent *` | +| `switchpanel.c:684` | `wSwitchPanelHandleEvent()` | `XEvent *` | +| `dock.c:1978` | `findDock()` | `XEvent *` | +| `dock.c:2009` | `wDockReceiveDNDDrop()` | `XEvent *` | +| `icon.c:57` | `miniwindowDblClick()` | `XEvent *` | +| `menu.c:677` | `check_key()` | `XKeyEvent *` | +| `window.c:90` | `windowLanguageClick()` | `XEvent *` | +| `wmspec.c:1701` | `wNETWMProcessClientMessage()` | `XClientMessageEvent *` | +| `event.c:94–95` | `handle_selection_request/clear()` | `XSelectionRequestEvent/ClearEvent *` | + +--- + +## 4. Direct Xlib Calls in Policy Code (by file) + +Files that call Xlib functions directly instead of going through `wm_backend`: + +| File | Call count | What's leaking | +|---|---|---| +| **`wmspec.c`** | **65** | `XGetWindowProperty`, `XChangeProperty`, `XDeleteProperty`, `XInternAtom(s)`, `XFlush` — the entire EWMH implementation is raw Xlib | +| **`xdnd.c`** | **33** | `XInternAtom`, `XGetWindowProperty`, `XSendEvent`, `XGetSelectionOwner`, `XFree` — 100% X11 | +| **`event.c`** | **26** | `XInternAtom`, `XChangeProperty`, selection handling, `DefaultRootWindow` | +| **`defaults.c`** | **20** | `XColor` parsing via `XParseColor`/`XAllocColor` for color preferences | +| **`xmodifier.c`** | **11** | `XGetModifierMapping`, `XFreeModifiermap` — keyboard modifier detection | +| **`misc.c`** | **8** | `XKeysymToKeycode`, `XLookupString`, `DefaultRootWindow` | +| **`moveres.c`** | **6** | `XSetForeground`, `XDrawLine` — XOR rubber-band lines on root window | +| **`menu.c`** | **5** | Debug logging with window IDs | +| **`icon.c`** | **5** | `XWindowAttributes`, `XGetWindowAttributes` | +| **`cycling.c`** | **5** | `XEvent` synthesis for switch panel | +| **`usermenu.c`** | **4** | `XSendEvent`, `XStringToKeysym` — synthetic key events to client | +| **`dock.c`** | **3** | `XEvent` in DND drop handling | +| **`client.c`** | **3** | `XWindowChanges`, `XConfigureRequestEvent` | +| **`appicon.c`** | **1** | `XClearArea` for dock icon label | + +--- + +## 5. X11 Types Embedded in Data Structures + +These X11 types are used as field types in core structs, meaning the data model itself is X11-flavored: + +| Type | Usage count in `src/*.h` | Where | +|---|---|---| +| `Pixmap` | ~46 | `screen.h`, `texture.h`, `pixmap.h`, `framewin.h` — everywhere pixmaps are stored | +| `GC` | ~10 | `screen.h` (7 GC fields), `texture.h` (6 GC fields), `balloon.h` | +| `XColor` | ~39 (in `.c`) | `defaults.c`, `texture.c`, `resources.c` | +| `Visual *` | ~5 | `framewin.h`, `screen.h` | +| `Colormap` | ~5 | `framewin.h`, `screen.h`, `resources.h` | +| `Atom` | ~96 (in `.c`) | `wmspec.c` (95), `xdnd.c` (19), scattered elsewhere | +| `Cursor` | in `screen.h` | Cursor array in `WScreen` | + +--- + +## Summary: Heat Map + +``` + Fully Partially Entirely + Abstracted Abstracted X11-Raw + ────────── ────────── ──────── +wcore.c ██████████ +stacking.c ██████████ +placement.c ██████████ +workspace.c ████████░░ (XINERAMA) +actions.c ████████░░ (XINERAMA) +window.c ██████░░░░ (XKB, SHAPE, MWM) +framewin.c █████░░░░░ (XKB 22 sites, _x11_raw) +event.c ████░░░░░░ (_x11_raw 16 sites, Xlib) +defaults.c ████░░░░░░ (XColor) +cycling.c ███░░░░░░░ (XEvent synthesis) +moveres.c ███░░░░░░░ (XDrawLine) +dock.c ███░░░░░░░ (XEvent, XDND) +icon.c ███░░░░░░░ (XWindowAttributes, _x11_raw) +menu.c ███░░░░░░░ (XKeyEvent, _x11_raw) +client.c ██░░░░░░░░ (X11 event types in API) +wmspec.c ░░░░░░░░░░ ██████████ (65 Xlib calls) +xdnd.c ░░░░░░░░░░ ██████████ (33 Xlib calls) +xmodifier.c ░░░░░░░░░░ ██████████ (11 Xlib calls) +usermenu.c ░░░░░░░░░░ ████████░░ (XSendEvent) +``` + +The **three biggest remaining problems** for Wayland parity are: +1. **`wmspec.c`** — the entire EWMH implementation is raw Xlib (65 calls). On Wayland, EWMH doesn't exist, so this needs to become a no-op or be moved behind the backend. +2. **`xdnd.c`** — entirely X11. Wayland has its own DND protocol. +3. **`event.c`'s `_x11_raw` escape hatch** — 16 sites where policy code reaches through the abstraction to touch the raw `XEvent`. + +--- + +# wrlib/ — Backend-Specific Branch Inventory + +## File Classification + +### Entirely X11-Free (pure image manipulation) + +These files have **zero** Xlib calls and no X11 type usage. Some `#include ` transitively through `wraster.h` but don't use it: + +| File | LOC | Purpose | +|---|---|---| +| `alpha_combine.c` | 71 | Alpha compositing | +| `draw.c` | 504 | Line/pixel drawing on RImage | +| `gradient.c` | 501 | Gradient rendering | +| `load_gif.c` | 232 | GIF loader | +| `load_jpeg.c` | 196 | JPEG loader | +| `load_magick.c` | 130 | ImageMagick loader | +| `load_png.c` | 220 | PNG loader | +| `load_tiff.c` | 153 | TIFF loader | +| `load_webp.c` | 172 | WebP loader | +| `load_xpm_normalized.c` | 459 | Pure-C XPM parser (no libXpm) | +| `save_jpeg.c` | 104 | JPEG saver | +| `save_png.c` | 127 | PNG saver | +| `save_xpm.c` | 283 | XPM saver | +| **Total** | **3152** | **36% of wrlib — fully portable** | + +### Gratuitous X11 Header Includes (no actual Xlib calls) + +These files `#include ` (pulled in transitively via `wraster.h`) but make **zero** Xlib function calls. The include is unnecessary: + +| File | LOC | Actual purpose | +|---|---|---| +| `color.c` | 130 | RColor ↔ pixel conversion (uses `RContext` fields only) | +| `convolve.c` | 145 | Image convolution/blur | +| `flip.c` | 163 | Image flip | +| `load.c` | 395 | Format dispatch (calls `RLoadPNG`, etc.) | +| `load_ppm.c` | 394 | PPM loader | +| `misc.c` | 279 | Error strings, format support queries | +| `raster.c` | 640 | `RCreateImage`, `RCloneImage`, `RGetSubImage`, etc. | +| `rotate.c` | 434 | Image rotation | +| `save.c` | 61 | Save format dispatch | +| `scale.c` | 499 | Image scaling | +| **Total** | **3140** | **36% of wrlib — portable code with unnecessary X11 header dep** | + +### Genuinely X11-Dependent + +| File | LOC | Xlib calls | What it does | +|---|---|---|---| +| `context.c` | 772 | **44** | `RCreateContext()` — the big one. Visual selection, colormap allocation, GC creation, XShm probing. Also contains `RCreateContextWayland()` (the Wayland alternative). | +| `xutil.c` | 263 | **32** | `RCreateXImage`, `RDestroyXImage`, `RGetXImage`, `RPutXImage` — XImage/XShm lifecycle | +| `convert.c` | 985 | **14** | `image2TrueColor`, `image2PseudoColor`, `image2GrayScale`, `image2Bitmap` — RImage→XImage pixel conversion | +| `convert_x11.c` | 96 | **6** | `x11_convert_image`, `x11_convert_image_mask` — XImage→Pixmap via `XCreatePixmap`/`XCopyArea` | +| `xpixmap.c` | 183 | **13** | `RCreateImageFromXImage`, `RCreateImageFromDrawable` — Pixmap/Drawable→RImage extraction | +| `load_xpm.c` | 180 | **2** | `RLoadXPM`, `RGetImageFromXPMData` — uses `libXpm` + `XParseColor` for color resolution | +| **Total** | **2479** | **111** | **28% of wrlib — hard X11 dependency** | + +### Wayland Backend + +| File | LOC | What it does | +|---|---|---| +| `convert_wl.c` | 58 | `wl_convert_image`, `wl_convert_image_mask` — RImage→`pixman_image_t` (cast to `Pixmap`) | + +--- + +## 1. Compile-Time `#ifdef` Branches + +### `USE_XSHM` (10 sites — 2 files) + +The XShm (shared memory) extension for fast image transfer to the X server: + +| File | Line(s) | What it guards | +|---|---|---| +| `context.c` | 639 | XShm version check, shared pixmap capability probe | +| `xutil.c` | 34, 44, 71, 158, 195, 238, 251 | XShm include, `XShmCreateImage`, `XShmAttach`, `XShmDetach`, `XShmGetImage`, `XShmPutImage`, shared pixmap creation. The entire `RCreateXImage`/`RDestroyXImage` have dual paths (shm vs non-shm). | +| `convert_x11.c` | 16, 43 | Shared pixmap fast path in `x11_convert_image` | +| `xutil.h` | 24 | `R_CreateXImageMappedPixmap` prototype | + +### `HAVE_LIBXMU` (2 sites) + +| File | Line(s) | What it guards | +|---|---|---| +| `context.c` | 29, 145 | `XmuLookupStandardColormap` — optional shortcut for standard colormap lookup | + +### `USE_*` Image Format Guards (16 sites) + +All in `load.c`, `save.c`, and `imgformat.h` — these are feature toggles, not backend-specific: + +| Guard | Sites | Files | +|---|---|---| +| `USE_TIFF` | 3 | `load.c`, `imgformat.h` | +| `USE_PNG` | 3 | `load.c`, `save.c`, `imgformat.h` | +| `USE_JPEG` | 3 | `load.c`, `save.c`, `imgformat.h` | +| `USE_GIF` | 5 | `load.c`, `load_gif.c`, `imgformat.h` | +| `USE_WEBP` | 2 | `load.c`, `imgformat.h` | +| `USE_MAGICK` | 3 | `load.c`, `load_magick.c`, `misc.c`, `imgformat.h` | + +### Minor Guards + +| Guard | File | Purpose | +|---|---|---| +| `WRLIB_DEBUG` | `context.c` (4), `convert.c` (6) | Debug logging | +| `HAVE_FLOAT_MATHFUNC` | `context.c:46` | `floorf()` availability | +| `HAVE_STDNORETURN` | `load_jpeg.c:34` | C11 `_Noreturn` | +| `I18N` | `misc.c:248` | Internationalization | + +--- + +## 2. Runtime Backend Dispatch + +There is exactly **one** runtime dispatch point — the `convert_image` function pointer in `RContext`: + +```c +// wraster.h.in:210-211 +int (*convert_image)(struct RContext *ctx, RImage *image, Pixmap *pixmap); +int (*convert_image_mask)(struct RContext *ctx, RImage *image, Pixmap *pixmap, Pixmap *mask, int threshold); +``` + +Set at context creation time: +- **X11 path** (`context.c:660-661`): `context->convert_image = x11_convert_image` +- **Wayland path** (`context.c:768-769`): `context->convert_image = wl_convert_image` + +Called from `convert.c:869,882`: +```c +return context->convert_image(context, image, pixmap); +``` + +This is the **only** abstraction seam in wrlib. Everything else is either pure image manipulation or hardcoded X11. + +--- + +## 3. X11 Types in the Public API (`wraster.h.in`) + +The public header exposes X11 types directly: + +| API function | X11 types in signature | +|---|---| +| `RCreateContext()` | `Display *dpy` | +| `RGetClosestXColor()` | `XColor *retColor` | +| `RCreateImageFromXImage()` | `XImage *image, XImage *mask` | +| `RCreateImageFromDrawable()` | `Drawable drawable, Pixmap mask` | +| `RConvertImage()` | `Pixmap *pixmap` (return) | +| `RConvertImageMask()` | `Pixmap *pixmap, Pixmap *mask` (return) | +| `RCreateXImage()` | returns `RXImage *` (wraps `XImage`) | +| `RGetXImage()` | `Drawable d` | +| `RPutXImage()` | `Drawable d, GC gc` | + +### `RContext` struct fields that are X11 types + +| Field | Type | Used by | +|---|---|---| +| `dpy` | `Display *` | Everything in context.c, xutil.c, xpixmap.c, convert_x11.c, load_xpm.c | +| `cmap` | `Colormap` | context.c (colormap allocation), load_xpm.c (color parsing) | +| `copy_gc` | `GC` | convert_x11.c (pixmap copy) | +| `visual` | `Visual *` | context.c (visual selection), xutil.c (XImage creation) | +| `drawable` | `Window` | convert_x11.c (XCreatePixmap root window) | +| `colors` | `XColor *` | context.c (PseudoColor palette) | + +### `RXImage` struct — entirely X11 + +```c +// wraster.h.in:265-268 +XImage *image; +// USE_XSHM: +XShmSegmentInfo info; +``` + +--- + +## 4. Direct Xlib Calls by File + +| File | Calls | Functions used | +|---|---|---| +| **`context.c`** | **44** | `XAllocColor`, `XQueryColors`, `XInternAtom`, `XGetRGBColormaps`, `XAllocStandardColormap`, `XFree`, `XCreateColormap`, `XGetVisualInfo`, `XCreateWindow`, `XCreateGC`, `XFreeGC`, `XDestroyWindow`, `XShmQueryVersion`, `XShmPixmapFormat`, `XSetWindowAttributes` | +| **`xutil.c`** | **32** | `XCreateImage`, `XDestroyImage`, `XShmCreateImage`, `XShmAttach`, `XShmDetach`, `XShmGetImage`, `XShmPutImage`, `XSync`, `XSetErrorHandler`, `XGetGeometry`, `XGetImage`, `XPutImage`, `XCreatePixmap`, `XFreePixmap` | +| **`convert.c`** | **14** | `RCreateXImage`, `RDestroyXImage` (wrlib wrappers, but they call Xlib internally) | +| **`xpixmap.c`** | **13** | `XGetPixel`, `XGetGeometry`, `XGetImage`, `XDestroyImage` | +| **`convert_x11.c`** | **6** | `XCreatePixmap`, `XCopyArea`, `XFreePixmap`, `XCreateGC`, `XFreeGC`, `RPutXImage` | +| **`load_xpm.c`** | **2** | `XParseColor` (color name→RGB), `XpmCreateXpmImageFromData`, `XpmReadFileToXpmImage`, `XpmFreeXpmImage` (libXpm, not Xlib proper) | + +--- + +## 5. Summary: Heat Map + +``` + Pure Gratuitous Genuinely + Portable X11 Header X11-Dependent + ────────── ────────── ──────── +alpha_combine.c ██████████ +draw.c ██████████ +gradient.c ██████████ +load_gif.c ██████████ +load_jpeg.c ██████████ +load_magick.c ██████████ +load_png.c ██████████ +load_tiff.c ██████████ +load_webp.c ██████████ +load_xpm_normalized.c ██████████ +save_jpeg.c ██████████ +save_png.c ██████████ +save_xpm.c ██████████ +convert_wl.c ██████████ (Wayland backend) +color.c ██████████ +convolve.c ██████████ +flip.c ██████████ +load.c ██████████ +load_ppm.c ██████████ +misc.c ██████████ +raster.c ██████████ +rotate.c ██████████ +save.c ██████████ +scale.c ██████████ +load_xpm.c ████████░░ (libXpm + XParseColor) +convert.c ██████████ (RXImage converters) +convert_x11.c ██████████ (X11 convert backend) +xpixmap.c ██████████ (Drawable→RImage) +xutil.c ██████████ (XImage/XShm lifecycle) +context.c ██████████ (RContext creation, 44 calls) +``` + +**By the numbers:** + +| Category | Files | LOC | % of wrlib | +|---|---|---|---| +| Fully portable | 14 | 3210 | 36% | +| Portable but needlessly includes `X11/Xlib.h` | 10 | 3140 | 36% | +| Genuinely X11-dependent | 6 | 2479 | 28% | + +**The Wayland story in wrlib is already partially told.** The `convert_image` function pointer dispatch and `RCreateContextWayland()` exist, and `convert_wl.c` provides the pixman-based conversion path. What remains X11-bound: + +1. **`context.c`** — `RCreateContext()` (525 lines of X11 visual/colormap setup) vs `RCreateContextWayland()` (45 lines). The X11 path is 10× larger but they're already separate functions. +2. **`xutil.c` + `xpixmap.c`** — the entire `RXImage` abstraction is X11-only. On Wayland, these are dead code (the `wl_convert_image` path never touches `RXImage`). +3. **`convert.c`** — the `image2TrueColor`/`image2PseudoColor`/etc. converters produce `RXImage` (X11). On Wayland, they're bypassed entirely by the function pointer dispatch. +4. **`load_xpm.c`** — uses `libXpm` and `XParseColor`. The alternative `load_xpm_normalized.c` is a pure-C XPM parser that doesn't need X11 (already exists as the Wayland-compatible path). +5. **The public API** — `wraster.h` exposes `Display *`, `Pixmap`, `Drawable`, `GC`, `XImage`, `XColor` in function signatures. On Wayland, `Pixmap` is reused as a `uintptr_t` cast of `pixman_image_t *` — a type pun, not a real abstraction. + +--- + +# wmlib/ — Backend-Specific Branch Inventory + +wmlib is tiny — 481 lines of C across 4 files, plus 2 headers (238 lines). It's a client-side helper library that lets external applications communicate with the WindowMaker WM via X11 properties and client messages. **It is 100% X11.** + +## File Map + +| File | LOC | Purpose | Xlib calls | +|---|---|---|---| +| `command.c` | 75 | Send WM commands (hide app, hide others, set GNUstep attributes) via `XSendEvent` + `XChangeProperty` | **5** | +| `event.c` | 87 | Process incoming `_WINDOWMAKER_MENU` client messages from the WM | **1** | +| `menu.c` | 239 | Build menu tree in memory, serialize to X text property via `XSetTextProperty` | **3** | +| `app.c` | 80 | Application context (`WMAppContext`) — stores `Display *`, main window, window list | **0** (stores X11 types, doesn't call Xlib) | +| `WMaker.h` | 162 | Public API header | — | +| `app.h` | 41 | Private `wmAppContext` struct | — | +| `menu.h` | 76 | Private `wmMenu`/`wmMenuEntry` structs | — | + +## 1. Compile-Time `#ifdef` Branches + +**None.** Zero `#ifdef` guards of any kind (beyond header include guards and `__cplusplus`). No conditional compilation, no feature toggles, no backend switches. + +## 2. Runtime Backend Dispatch + +**None.** No function pointers, no vtables, no `is_wayland` checks. + +## 3. X11 Types in the Public API (`WMaker.h`) + +Every public function takes X11 types: + +| Function | X11 types | +|---|---| +| `WMAppCreateWithMain()` | `Display *display`, `Window main_window` | +| `WMAppCreate()` | `Display *display` | +| `WMAppAddWindow()` | `Window window` | +| `WMProcessEvent()` | `XEvent *event` | +| `WMSetWindowAttributes()` | `Display *dpy`, `Window window` | +| `WMMenuAction` callback | `Time timestamp` | + +The `GNUstepWMAttributes` struct contains `Pixmap` fields. + +## 4. X11 Types in Private Structs + +### `wmAppContext` (`app.h`) + +```c +Display *dpy; +Window main_window; +Window *windows; +``` + +### `wmMenuEntry` / `wmMenu` (`menu.h`) + +No X11 types — pure C data structures (strings, tags, linked list pointers). The X11 dependency comes only at serialization time. + +## 5. Direct Xlib Calls — Complete List + +There are only **9** Xlib calls in the entire library: + +| File | Line | Call | Purpose | +|---|---|---|---| +| `command.c:31` | `XInternAtom(dpy, "_WINDOWMAKER_WM_FUNCTION", False)` | Get the WM command atom | +| `command.c:47` | `XSendEvent(...)` | Send "hide application" command to WM | +| `command.c:62` | `XSendEvent(...)` | Send "hide others" command to WM | +| `command.c:70` | `XInternAtom(dpy, "_GNUSTEP_WM_ATTR", False)` | Get the GNUstep attributes atom | +| `command.c:71` | `XChangeProperty(...)` | Set GNUstep WM attributes on a window | +| `event.c:76` | `XInternAtom(app->dpy, "_WINDOWMAKER_MENU", False)` | Get the menu event atom | +| `menu.c:199` | `XInternAtom(dpy, WMMENU_PROPNAME, False)` | Get the menu property atom | +| `menu.c:226` | `XStringListToTextProperty(...)` | Serialize menu string list | +| `menu.c:232` | `XSetTextProperty(...)` | Write serialized menu to window property | + +Plus one `XFree()` call (`menu.c:234`). + +## 6. Communication Protocol + +The library implements a simple IPC protocol built entirely on X11 mechanisms: + +- **App → WM**: `XSendEvent` with `ClientMessage` type `_WINDOWMAKER_WM_FUNCTION` to the root window +- **App → WM (menus)**: `XSetTextProperty` writes a serialized menu tree to `_WINDOWMAKER_MENU` on the app's main window +- **WM → App**: `ClientMessage` events with type `_WINDOWMAKER_MENU` carrying a menu item tag + +## Summary + +``` + Pure X11-Dependent + Portable + ────────── ────────── +app.c (data only) ████░░░░░░ (stores Display*/Window) +menu.c (data + serial) ░░░░░░░░░░ ██████████ (XSetTextProperty) +command.c ░░░░░░░░░░ ██████████ (XSendEvent, XChangeProperty) +event.c ░░░░░░░░░░ ██████████ (XInternAtom, ClientMessage) +WMaker.h ░░░░░░░░░░ ██████████ (Display, Window, Pixmap, XEvent, Time) +``` + +**wmlib is entirely X11.** There is no abstraction layer, no backend dispatch, no Wayland path, and no compile-time guards. The entire library is a thin wrapper around X11 client messages and properties for the GNUstep/WindowMaker protocol. + +For a Wayland port, wmlib would either need to be: +1. **Replaced** with a Wayland-native protocol (e.g., a custom `wl_protocol` extension), or +2. **Kept as-is** for XWayland clients only, with a parallel Wayland-native equivalent, or +3. **Dropped** — its functionality (app hide/show, WM-managed menus) could be subsumed into the compositor's own protocol. + +Given its size (481 lines of C, 9 Xlib calls), any of these options is straightforward. + +--- + +# util/ — Backend-Specific Branch Inventory + +util/ contains **15 standalone CLI programs** (7336 LOC). They range from fully X11-dependent GUI apps to pure file-manipulation tools with zero display-system dependency. + +## File Classification + +### Entirely X11-Free (pure file/text manipulation) + +| File | LOC | Purpose | X11 calls | +|---|---|---|---| +| `convertfonts.c` | 167 | Convert old WM font config to new format | **0** | +| `fontconv.c` | 200 | XLFD font name conversion helpers | **0** | +| `wdread.c` | 110 | Read a key from the WM defaults database | **0** | +| `wdwrite.c` | 114 | Write a key to the WM defaults database | **0** | +| `getstyle.c` | 418 | Export style/theme from WM defaults to file | **0** | +| `geticonset.c` | 124 | Export icon set from WM defaults to file | **0** | +| `seticons.c` | 129 | Import icon set into WM defaults | **0** | +| `wmmenugen.c` | 393 | Generate WM menu from `.desktop` files (PropList output) | **0** | +| `wmmenugen_misc.c` | 172 | Menu generator helpers (terminal detection) | **0** | +| `wmmenugen_parse_xdg.c` | 737 | XDG `.desktop` file parser | **0** | +| `wmmenugen_parse_wmconfig.c` | 297 | wmconfig file parser | **0** | +| `wmgenmenu.c` | 519 | Another menu generator (hardcoded menu templates) | **0**¹ | +| **Total** | **3380** | | | + +¹ `wmgenmenu.c` has the string "XTerm" as a menu item label — no actual Xlib call. + +### X11-Dependent GUI Applications + +| File | LOC | Xlib calls | Purpose | +|---|---|---|---| +| `wmiv.c` + `wmiv.h` | 1020 + 1279 | **60** | Image viewer — full X11 window, event loop, keyboard handling | +| `wmsetbg.c` | 1444 | **53** | Set desktop background — pixmap creation, root window manipulation | +| `wmagnify.c` | 491 | **20** | Screen magnifier — `XGetImage` from root, WINGs GUI | +| `wxpaste.c` | 232 | **18** | Paste from X selection/clipboard | +| `wxcopy.c` | 246 | **12** | Copy to X cut buffers/selection | +| `setstyle.c` | 523 | **10** | Import style into WM defaults + send reload command via `XSendEvent` | +| **Total** | **3956** (excl. `wmiv.h`) | **173** | | + +--- + +## 1. Compile-Time `#ifdef` Branches + +### Backend/Extension Guards + +| Guard | File | Sites | What it guards | +|---|---|---|---| +| `USE_XINERAMA` | `wmsetbg.c` | **7** | Xinerama include, `XineramaQueryScreens`, per-head background stretching, `--xinerama` CLI flag | +| `USE_XPM` | `wmiv.c` | **4** | XPM icon loading for the image viewer window | +| `HAVE_EXIF` | `wmiv.c` | **3** | EXIF orientation auto-rotation | +| `HAVE_PTHREAD` | `wmiv.c` | **5** | Background image preloading thread | + +### Portability Guards (not backend-specific) + +| Guard | Files | Sites | What it guards | +|---|---|---|---| +| `__GLIBC__` | 8 files | 8 | `#define _GNU_SOURCE` | +| `HAVE_STDNORETURN` | 7 files | 7 | C11 `_Noreturn` | +| `_GNU_SOURCE` | `wmiv.c` | 1 | GNU extensions | +| `HAVE_SYS_SELECT_H` | `wxpaste.c` | 1 | `` include | +| `HAVE_LIBINTL_H` + `I18N` | `wmgenmenu.c` | 1 | Gettext | +| `PATH_MAX` | `getstyle.c` | 1 | Fallback definition | +| `DEBUG` | `wmsetbg.c` (6), `wmmenugen.c` (2), `wmmenugen_parse_*.c` (3) | 11 | Debug logging | + +--- + +## 2. Runtime Backend Dispatch + +### `_x11_raw` Escape Hatch (1 site) + +| File | Line | What it does | +|---|---|---| +| `wmagnify.c` | 314 | `XEvent *xev = (XEvent *)event->_x11_raw` — casts the WINGs event to get the raw `XKeyEvent` for `XLookupString` | + +No other runtime backend checks (`is_wayland`, `dpy == NULL`, etc.) in any util file. + +--- + +## 3. X11 Types in Function Signatures / Structs + +### `wmsetbg.c` + +```c +Display *dpy; // global +Pixmap texture->pixmap; // background storage +XColor color; // color parsing +XRectangle head[]; // Xinerama head geometry +``` + +### `wmiv.c` + +```c +Display *dpy; // global +Window win; // viewer window +XTextProperty title_property; // window title +XClassHint, XSizeHints, XWMHints // window manager hints +XEvent e; // event loop +XKeyEvent event; // synthetic key events +``` + +### `wmagnify.c` + +```c +Display *dpy, *vdpy; // two display connections (magnify from one, draw to other) +XImage *image; // captured screen region +XRectangle rects[]; // magnified pixel rectangles +GC gc; // drawing context +``` + +### `wxcopy.c` / `wxpaste.c` + +```c +Display *dpy; // global +Atom selatom, clipatom; // selection atoms +XEvent ev; // selection events +Window win; // hidden window for selection protocol +``` + +### `setstyle.c` + +```c +Display *dpy; // opened only to send reload command +XEvent ev; // client message +XColor color1, color2; // color validation +``` + +--- + +## 4. Direct Xlib Calls — Per File + +### `wmiv.c` (60 calls) — Image Viewer + +Full X11 application: window creation, event loop, pixmap management. + +| Category | Calls | +|---|---| +| Window lifecycle | `XOpenDisplay`, `XCreateSimpleWindow`, `XMapWindow`, `XCloseDisplay`, `XSelectInput` | +| Drawing | `XCopyArea` (×6), `XClearWindow` (×3), `XResizeWindow` (×4), `XFreePixmap` (×3) | +| Window properties | `XSetWMProperties`, `XSetWMName`, `XSetWMProtocols`, `XStringListToTextProperty`, `XAllocClassHint`, `XAllocSizeHints`, `XAllocWMHints` | +| Event loop | `XNextEvent`, `XSendEvent`, `XFlush`, `XSync` | +| EWMH | `XInternAtom` (×2 — `_NET_WM_STATE`, `_NET_WM_STATE_FULLSCREEN`) | +| Input | `XKeysymToKeycode`, `XGetWindowAttributes` | + +### `wmsetbg.c` (53 calls) — Background Setter + +Root window pixmap manipulation, color allocation, property management. + +| Category | Calls | +|---|---| +| Color | `XParseColor` (×7), `XAllocColor` (×2), `XFreeColors` | +| Pixmap | `XCreatePixmap` (×3), `XFreePixmap` (×2), `XCopyArea` (×3), `XSetForeground` (×2), `XFillRectangle` (×2) | +| Root window | `XSetWindowBackground`, `XSetWindowBackgroundPixmap`, `XClearWindow` | +| Properties | `XInternAtom`, `XGetWindowProperty`, `XChangeProperty`, `XDeleteProperty` — `_XROOTPMAP_ID` management | +| Connection | `XOpenDisplay` (×2 — main + `RetainPermanent` trick), `XCloseDisplay`, `XSetCloseDownMode`, `XSync`, `XFlush`, `XSynchronize` | +| Server grab | `XGrabServer`, `XUngrabServer` | +| Error handling | `XSetErrorHandler`, `XKillClient` | +| Xinerama | `XineramaIsActive`, `XineramaQueryScreens`, `XFree` | + +### `wmagnify.c` (20 calls) — Screen Magnifier + +Captures screen pixels and renders magnified view via WINGs. + +| Category | Calls | +|---|---| +| Capture | `XGetImage`, `XDestroyImage`, `XGetPixel`, `XQueryPointer` | +| Drawing | `XCreateGC`, `XSetForeground`, `XFillRectangles` (×3) | +| Connection | `XOpenDisplay` (×2 — main + virtual display), `XMaxRequestSize` | +| Input | `XLookupString` (via `_x11_raw`) | + +### `wxpaste.c` (18 calls) — Clipboard Paste + +X11 selection protocol client. + +| Category | Calls | +|---|---| +| Selection | `XConvertSelection`, `XGetWindowProperty`, `XFetchBuffer` | +| Connection | `XOpenDisplay`, `XCloseDisplay`, `XDisplayName` | +| Window | `XCreateSimpleWindow`, `XSelectInput`, `XStoreName` | +| Events | `XNextEvent`, `XPending`, `XInternAtom` (×2) | + +### `wxcopy.c` (12 calls) — Clipboard Copy + +X11 cut buffer / selection protocol. + +| Category | Calls | +|---|---| +| Buffers | `XStoreBuffer`, `XRotateBuffers`, `XListProperties`, `XSetSelectionOwner` | +| Connection | `XOpenDisplay`, `XCloseDisplay`, `XDisplayName`, `XSetErrorHandler`, `XFlush` | +| Properties | `XFree` | + +### `setstyle.c` (10 calls) — Style Importer + +Mostly file manipulation; X11 used only for color validation and sending a reload command. + +| Category | Calls | +|---|---| +| Color validation | `XParseColor` (×2) — validates color names in the style | +| WM command | `XOpenDisplay`, `XInternAtom` (`_WINDOWMAKER_COMMAND`), `XSendEvent`, `XFlush` | + +--- + +## 5. Summary: Heat Map + +``` + Pure X11-Dependent + Portable + ────────── ────────── +convertfonts.c ██████████ +fontconv.c ██████████ +wdread.c ██████████ +wdwrite.c ██████████ +getstyle.c ██████████ +geticonset.c ██████████ +seticons.c ██████████ +wmmenugen.c ██████████ +wmmenugen_misc.c ██████████ +wmmenugen_parse_xdg.c ██████████ +wmmenugen_parse_wmc.. ██████████ +wmgenmenu.c ██████████ +setstyle.c ██████░░░░ (XParseColor, XSendEvent for reload) +wxcopy.c ░░░░░░░░░░ ██████████ (X cut buffers/selection) +wxpaste.c ░░░░░░░░░░ ██████████ (X selection protocol) +wmagnify.c ░░░░░░░░░░ ██████████ (XGetImage, WINGs GUI) +wmsetbg.c ░░░░░░░░░░ ██████████ (root pixmap, 53 Xlib calls) +wmiv.c ░░░░░░░░░░ ██████████ (full X11 app, 60 Xlib calls) +``` + +**By the numbers:** + +| Category | Files | LOC | % of util | +|---|---|---|---| +| Fully portable (no X11) | 12 | 3380 | 46% | +| Mostly portable (minor X11 for WM command) | 1 (`setstyle.c`) | 523 | 7% | +| Entirely X11-dependent | 5 | 3433 | 47% | + +**The five X11-dependent utilities are all standalone GUI programs.** For a Wayland port, each would need either: + +1. **`wmsetbg.c`** — Wayland compositors typically own the background; this tool would become a protocol message to the compositor rather than root window pixmap manipulation. +2. **`wmiv.c`** — Full rewrite against Wayland client APIs (or just use WINGs, which already has a Wayland view backend). +3. **`wmagnify.c`** — Already uses WINGs widgets; the X11 dependency is `XGetImage` for screen capture (no Wayland equivalent without a screencopy protocol). +4. **`wxcopy.c` / `wxpaste.c`** — X11 selection protocol → Wayland `wl_data_device` / `zwp_primary_selection_v1`. Completely different protocol, complete rewrite. + +The 12 portable utilities (defaults DB tools, menu generators, font converters) work on any platform unchanged. + +--- + +# WINGs/ — Backend-Specific Branch Inventory + +WINGs is the largest component at ~40k LOC across 62 `.c` files. It has a well-developed **five-vtable backend abstraction** but significant X11 leakage remains in widget code. + +## Architecture: The Five Backend Vtables + +WINGs uses runtime vtable dispatch set in `widgets.c:777-812` based on `WMAKER_USE_WAYLAND` env var: + +```c +scrPtr->viewBackend = wayland ? &W_ViewBackendWL : &W_ViewBackendX11; +scrPtr->eventBackend = wayland ? &W_EventBackendWL : &W_EventBackendX11; +scrPtr->renderBackend = wayland ? &W_RenderBackendWL : &W_RenderBackendX11; +scrPtr->fontBackend = wayland ? &W_FontBackendWL : &W_FontBackendX11; +scrPtr->colorBackend = wayland ? &W_ColorBackendWL : &W_ColorBackendX11; +``` + +| Vtable | Slots | X11 impl | Wayland impl | +|---|---|---|---| +| `W_ViewBackend` | 19 (realize, map, unmap, move, resize, raise, lower, reparent, destroy, set_background_color, set_cursor, redisplay, selection, flush…) | `wview_x11.c` (238 LOC) | `wview_wl.c` (368 LOC) | +| `W_EventBackend` | 5 (screen_init, screen_fini, fetch, check_mask, post_dispatch) | `wevent_x11.c` (354 LOC) | `wevent_wl.c` (235 LOC) | +| `W_RenderBackend` | 12 (pixmap_create, pixmap_free, pixmap_copy, draw_line, draw_rect, fill_rect, fill_stippled, composite_pixmap, fill_polygon, fill_arc, create_disabled_mask) | `wrender_x11.c` (175 LOC) | `wrender_wl.c` (279 LOC) | +| `W_FontBackend` | 8 (screen_init, screen_fini, create, destroy, measure, draw_string, draw_image_string, height) | `wfont_x11.c` (212 LOC) | `wfont_wl.c` (235 LOC) | +| `W_ColorBackend` | 5 (screen_init, screen_fini, alloc_color, find_close_color, free_color, parse_named) | `wcolor_x11.c` (130 LOC) | `wcolor_wl.c` (106 LOC) | + +**Backend split files total**: X11 = 1109 LOC, Wayland = 1223 LOC. + +--- + +## File Classification + +### Fully Portable (zero Xlib calls, zero X11 types) + +| File | LOC | Purpose | +|---|---|---| +| `proplist.c` | 1878 | NeXTSTEP property list parser/serializer | +| `wfilepanel.c` | 896 | File open/save panel | +| `wpanel.c` | 847 | Alert/input panels | +| `wscroller.c` | 830 | Scrollbar widget | +| `wfontpanel.c` | 825 | Font chooser panel | +| `wbutton.c` | 755 | Button widget | +| `bagtree.c` | 745 | Balanced tree data structure | +| `userdefaults.c` | 641 | User defaults system | +| `handlers.c` | 566 | Timer/idle handler dispatch | +| `wscrollview.c` | 561 | Scroll container widget | +| `notification.c` | 482 | Notification center | +| `string.c` | 425 | String utilities | +| `hashtable.c` | 422 | Hash table | +| `array.c` | 363 | Dynamic array | +| `wfont.c` | 329 | Font dispatch (delegates to fontBackend) | +| `data.c` | 289 | WMData wrapper | +| `tree.c` | 255 | Tree data structure | +| `wbox.c` | 247 | Box layout widget | +| `wprogressindicator.c` | 243 | Progress bar widget | +| `wframe.c` | 230 | Group box widget | +| `wmenuitem.c` | 226 | Menu item data | +| `memory.c` | 223 | Memory allocation wrappers | +| `wapplication.c` | 216 | Application context | +| `wlabel.c` | 214 | Label widget | +| `wcolor.c` | 187 | Color dispatch (delegates to colorBackend) | +| `configuration.c` | 132 | Configuration file handling | +| `usleep.c` | 26 | Microsecond sleep | +| `wutil.c` | 24 | Misc utilities | +| **+ Wayland backends** | | | +| `wview_wl.c` | 368 | Wayland view backend | +| `wevent_wl.c` | 235 | Wayland event backend | +| `wfont_wl.c` | 235 | Wayland font backend | +| `wrender_wl.c` | 279 | Wayland render backend | +| `wcolor_wl.c` | 106 | Wayland color backend | +| **Total** | **12,474** | **31% of WINGs** | + +### X11 Backend Split Files (properly abstracted) + +| File | LOC | Xlib calls | Purpose | +|---|---|---|---| +| `wview_x11.c` | 238 | 29 | X11 view lifecycle (XCreateWindow, XMapWindow, etc.) | +| `wrender_x11.c` | 175 | 24 | X11 drawing (XCreatePixmap, XFillRectangle, etc.) | +| `wevent_x11.c` | 354 | 20 | X11 event fetching (XNextEvent, XPending, etc.) | +| `wcolor_x11.c` | 130 | 11 | X11 color allocation (XAllocColor, etc.) | +| `wfont_x11.c` | 212 | 2 | X11 font rendering (Xft) | +| **Total** | **1109** | **86** | **3% of WINGs — properly isolated** | + +### Widget Files with Xlib Leakage (should be backend-neutral but aren't) + +| File | LOC | Xlib calls | What's leaking | +|---|---|---|---| +| **`wcolorpanel.c`** | 3556 | **89** | `XCreatePixmap`, `XCopyArea`, `XFillRectangle`, `XFreePixmap`, `XGetImage`, `XGetPixel`, `XPutPixel`, `XPutImage`, `XSubImage`, `XDestroyImage`, `XImage`, `XColor`, `XGrabPointer`, `XUngrabPointer`, `XQueryPointer`, `XSetWindowBackgroundPixmap`, `XRecolorCursor`, `XSetForeground`, `XSetClipMask`, `XSetClipOrigin`, `XCreatePixmapCursor`, `XFreeCursor`, `XCreatePixmapFromBitmapData`, `XFlush`, `XPending`, `XPeekEvent`, `XNextEvent`, `XCheckTypedEvent` | +| **`dragsource.c`** | 1179 | **45** | XDND protocol: `XSendEvent`, `XGetWindowProperty`, `XChangeProperty`, `XSetSelectionOwner`, `XConvertSelection`, `XGrabPointer`, `XUngrabPointer`, `XInternAtom`, `XGetInputFocus`, `XQueryTree`, `XTranslateCoordinates`, shape extension | +| **`wwindow.c`** | 712 | **41** | Window properties: `XSetWMName`, `XSetWMProtocols`, `XGetWMProtocols`, `XSetWMHints`, `XGetWMHints`, `XSetWMNormalHints`, `XSetClassHint`, `XSetTransientForHint`, `XChangeProperty`, `XWithdrawWindow`, `XAllocSizeHints`, `XAllocWMHints`, `XAllocClassHint`, `XmbTextListToTextProperty`, `XStoreName`, `XSetIconName`, `XFree` | +| **`winputmethod.c`** | 243 | **40** | XIM/XIC: `XOpenIM`, `XCreateIC`, `XDestroyIC`, `XSetICValues`, `XGetICValues`, `XSetICFocus`, `XUnsetICFocus`, `XSetIMValues`, `XGetIMValues`, `XRegisterIMInstantiateCallback`, `XUnregisterIMInstantiateCallback`, `XVaCreateNestedList`, `XSetInputFocus`, `XLookupString`, `XSelectInput`, `XDisplayKeycodes`, `XGetKeyboardMapping`, `XFree` | +| **`dragdestination.c`** | 999 | **37** | XDND protocol: `XSendEvent`, `XGetWindowProperty`, `XChangeProperty`, `XConvertSelection`, `XInternAtom`, `XTranslateCoordinates` | +| **`widgets.c`** | 1137 | **35** | Screen init: `XOpenDisplay`, `XCreateGC` (×8), `XCreateBitmapFromData`, `XCreatePixmap`, `XCreateFontCursor`, `XCreatePixmapCursor`, `XSetForeground`, `XFillRectangle`, `XFreePixmap`, `XInternAtom(s)`, `XListProperties`, `XStoreBuffer`, `XGetModifierMapping`, `XFreeModifiermap`, `XKeysymToKeycode`, `XDisplayName`, `XColor` | +| **`wballoon.c`** | 476 | **28** | Tooltip drawing: `XFillArc`, `XFillRectangle`, `XFillPolygon`, `XSetForeground`, `XCreateGC`, `XTranslateCoordinates`, shape extension | +| **`wtext.c`** | 3998 | **23** | Rich text: `XBell`, `XFetchBuffer`, `XStoreBuffer`, `XFree`, `XUngrabPointer`, `XGrabPointer`, `XFlush`, `XInternAtom`, `XCreateGC`, `XCreateBitmapFromData`, `XChangeWindowAttributes`, `XSetWindowAttributes`, `XMapWindow`, `XRectangle`, `XEvent` | +| **`selection.c`** | 392 | **21** | X selection protocol: `XConvertSelection`, `XGetWindowProperty`, `XChangeProperty`, `XSendEvent`, `XSetSelectionOwner`, `XGetSelectionOwner`, `XDeleteProperty`, `XInternAtom`, `XFree` | +| **`dragcommon.c`** | 259 | **16** | XDND shared: `XInternAtom`, `XGetWindowProperty`, `XChangeProperty`, `XFree` | +| **`wappresource.c`** | 148 | **15** | App icon: `XGetWindowProperty`, `XFree`, `XInternAtom` | +| **`wtextfield.c`** | 1579 | **10** | Text input: `XBell`, `XFetchBuffer`, `XStoreBuffer`, `XFree`, `XUngrabPointer`, `XGrabPointer`, `XFlush`, `XInternAtom`, `XDrawRectangle` | +| **`wpopupbutton.c`** | 697 | **7** | Popup: `XGrabPointer`, `XUngrabPointer`, `XFlush` | +| **`wruler.c`** | 526 | **3** | Ruler: `XRectangle` type usage | +| **`wmisc.c`** | 351 | **2** | Misc: `XTranslateCoordinates` | +| **Total** | **16,252** | **412** | **41% of WINGs** | + +### Utility Files (WUtil sub-library, no X11) + +| File | LOC | Purpose | +|---|---|---| +| `menuparser.c` | 652 | Menu file parser | +| `menuparser_macros.c` | 723 | Menu parser macro expansion | +| `findfile.c` | 523 | File path search | +| `error.c` | 147 | Error/warning output | +| `misc.c` | 45 | Misc WUtil helpers | +| **Total** | **2090** | **5% of WINGs** | + +--- + +## 1. Compile-Time `#ifdef` Branches + +### Backend/Extension Guards + +| Guard | File(s) | Sites | What it guards | +|---|---|---|---| +| `USE_XSHAPE` | `dragsource.c` (2), `wballoon.c` (2), `wcolorpanel.c` (6) | **10** | Shape extension for DND drag icons, balloon tooltips, color picker magnifier | +| `USE_PANGO` | `wfont_x11.c` | **9** | Pango text rendering in X11 font backend | +| `XDND_DEBUG` | `dragsource.c` (4), `dragdestination.c` (3), `dragcommon.c` (3) | **10** | DND protocol debug logging | +| `HAVE_XINTERNATOMS` | `widgets.c` | **1** | Batch atom intern optimization | +| `X_HAVE_UTF8_STRING` | `winputmethod.c` | **1** | `Xutf8LookupString` availability | +| `DOUBLE_BUFFER` | `wmisc.c` (3), `wscroller.c` (8) | **11** | Double-buffered drawing | + +### Portability Guards (not backend-specific) + +| Guard | File(s) | Sites | What it guards | +|---|---|---|---| +| `HAVE_SYSLOG` / `HAVE_SYSLOG_H` | `error.c`, `misc.c` | 6 | Syslog logging | +| `HAVE_INOTIFY` | `userdefaults.c` | 4 | File change monitoring | +| `USE_BOEHM_GC` | `memory.c` | 6 | Boehm garbage collector | +| `HAVE_SYS_SELECT_H` | `handlers.c` | 1 | `` | +| `HAVE_POLL` | `handlers.c` | 1 | `poll()` vs `select()` | +| `HAVE_MKSTEMP` | `proplist.c` | 2 | Secure temp file creation | +| `HAVE_STDNORETURN` | `memory.c` | 1 | C11 `_Noreturn` | +| `HAVE_STRLCAT` / `HAVE_STRLCPY` | `string.c` | 2 | BSD string functions | +| `HAVE_BSD_STRING_H` | `string.c` | 1 | BSD string header | +| `HAVE_SYSCONF` | `error.c` | 1 | `sysconf()` | +| `HAVE_SELECT` | `handlers.c` | 1 | `select()` availability | +| `STRICT_NEXT_BEHAVIOUR` | `wscroller.c` (3), `wslider.c` (1) | 4 | NeXTSTEP-strict scrollbar behavior | +| `I18N` | `wapplication.c` | 1 | Internationalization | +| `DEBUG` | `menuparser_macros.c` (4), various | ~6 | Debug logging | +| `VERBOSE` | `wfilepanel.c` (2), `memory.c` (3) | 5 | Verbose logging | + +--- + +## 2. X11 Types in Core Structs (`WINGsP.h.in`) + +### `W_Screen` (the WINGs screen object) + +| Field | Type | Count | +|---|---|---| +| `display` | `Display *` | 1 | +| `rootWin` | `Window` | 1 | +| `applicationIconWindow`, `groupLeader` | `Window` | 2 | +| `stipple` | `Pixmap` | 1 | +| GCs | `GC` | **8** (`stippleGC`, `copyGC`, `clipGC`, `monoGC`, `xorGC`, `ixorGC`, `drawStringGC`, `drawImStringGC`) | +| `colormap` | `Colormap` | 1 | +| `visual` | `Visual *` | 1 | +| Cursors | `Cursor` | (in `defaultCursor`, `textCursor`, `invisibleCursor`) | +| Atoms | `Atom` | (multiple protocol atoms) | + +### `W_DragSourceInfo` / `W_DragDestinationInfo` + +| Field | Type | +|---|---| +| `sourceAction`, `destinationAction` | `Atom` | + +--- + +## 3. Direct Xlib Calls — Summary by Category + +### In properly-split `_x11` files (86 calls — correctly isolated) + +| File | Calls | Category | +|---|---|---| +| `wview_x11.c` | 29 | Window create/map/unmap/move/resize/destroy | +| `wrender_x11.c` | 24 | Pixmap create/free/copy, drawing primitives | +| `wevent_x11.c` | 20 | Event fetch, pending check, XSync | +| `wcolor_x11.c` | 11 | Color allocation | +| `wfont_x11.c` | 2 | Xft font rendering | + +### In non-split widget files (412 calls — leaking) + +| Category | Calls | Files | +|---|---|---| +| **DND (XDND protocol)** | **98** | `dragsource.c` (45), `dragdestination.c` (37), `dragcommon.c` (16) | +| **Color picker** | **89** | `wcolorpanel.c` — pixmap manipulation, image capture, pointer grabs | +| **Window properties** | **41** | `wwindow.c` — ICCCM hints, WM protocols | +| **Input methods** | **40** | `winputmethod.c` — XIM/XIC lifecycle | +| **Screen init** | **35** | `widgets.c` — GC creation, cursor creation, modifier mapping | +| **Tooltips** | **28** | `wballoon.c` — shaped balloon drawing | +| **Rich text** | **23** | `wtext.c` — clipboard, grabs | +| **Selection/clipboard** | **21** | `selection.c` — X selection protocol | +| **App resources** | **15** | `wappresource.c` — icon property reading | +| **Text field** | **10** | `wtextfield.c` — clipboard, grabs | +| **Popup button** | **7** | `wpopupbutton.c` — pointer grabs | +| **Ruler** | **3** | `wruler.c` — `XRectangle` type | +| **Misc** | **2** | `wmisc.c` — coordinate translation | + +--- + +## 4. Summary: Heat Map + +``` + Fully Backend-Split Xlib + Portable (vtable) Leaking + ────────── ────────── ──────── +WUtil layer: + proplist.c ██████████ + bagtree/array/hash/.. ██████████ + handlers.c ██████████ + menuparser*.c ██████████ + findfile/string/etc ██████████ + +Backend split files: + wview_x11/wl.c ██████████ + wevent_x11/wl.c ██████████ + wrender_x11/wl.c ██████████ + wfont_x11/wl.c ██████████ + wcolor_x11/wl.c ██████████ + +Clean widgets (0 Xlib): + wbutton.c ██████████ + wscroller.c ██████████ + wscrollview.c ██████████ + wlist.c ██████████ + wbrowser.c ██████████ + wslider.c ██████████ + wsplitview.c ██████████ + wtabview.c ██████████ + wfilepanel.c ██████████ + wfontpanel.c ██████████ + wpanel.c ██████████ + wframe.c ██████████ + wlabel.c ██████████ + wbox.c ██████████ + wprogressindicator.c ██████████ + wcolorwell.c ██████████ + wmenuitem.c ██████████ + +Leaking widgets: + wcolorpanel.c ██████████ (89 calls) + dragsource.c ██████████ (45 calls) + wwindow.c ██████████ (41 calls) + winputmethod.c ██████████ (40 calls) + dragdestination.c ██████████ (37 calls) + widgets.c ██████████ (35 calls) + wballoon.c ██████████ (28 calls) + wtext.c ████████░░ (23 calls) + selection.c ████████░░ (21 calls) + dragcommon.c ██████░░░░ (16 calls) + wappresource.c ████░░░░░░ (15 calls) + wtextfield.c ███░░░░░░░ (10 calls) + wpopupbutton.c ██░░░░░░░░ (7 calls) + wruler.c █░░░░░░░░░ (3 calls) + wmisc.c █░░░░░░░░░ (2 calls) +``` + +**By the numbers:** + +| Category | Files | LOC | Xlib calls | % of WINGs | +|---|---|---|---|---| +| Fully portable (WUtil + clean widgets + Wayland backends) | 34 | 14,564 | 0 | 36% | +| Backend-split `_x11` files (properly isolated) | 5 | 1,109 | 86 | 3% | +| Leaking widget files (X11 in non-split code) | 15 | 16,252 | 412 | 41% | +| WUtil utility code (no X11) | 8 | 2,090 | 0 | 5% | +| Remaining clean widgets (0 calls but use X11 types transitively) | ~6k | | | 15% | + +**The three biggest remaining problems for Wayland parity:** + +1. **DND subsystem** (`dragsource.c` + `dragdestination.c` + `dragcommon.c` = 2437 LOC, 98 Xlib calls) — the entire XDND protocol implementation. Wayland has `wl_data_device`. Needs a sixth vtable or full replacement. + +2. **`wcolorpanel.c`** (3556 LOC, 89 Xlib calls) — the color picker does raw pixmap manipulation, screen capture (`XGetImage`), pointer grabs, and cursor recoloring. Needs to route through `renderBackend` and `viewBackend`. + +3. **`wwindow.c`** + **`winputmethod.c`** + **`widgets.c`** (2092 LOC, 116 Xlib calls combined) — ICCCM window properties, XIM/XIC input method lifecycle, and screen initialization. These are protocol-level concerns that need backend abstraction (ICCCM doesn't exist on Wayland; input methods use `zwp_text_input_v3`). + +--- + +# WPrefs.app/ — Backend-Specific Branch Inventory + +WPrefs.app is the WindowMaker preferences editor — a WINGs-based GUI application with 15,540 LOC across 21 `.c` files. Each file is a preference panel. + +## File Map + +| File | LOC | Purpose | +|---|---|---| +| `Appearance.c` | 2356 | Titlebar/menu texture and color configuration | +| `Menu.c` | 1825 | Menu structure editor | +| `TexturePanel.c` | 1549 | Texture creation/editing dialog | +| `editmenu.c` | 1361 | Drag-and-drop menu editor widget | +| `WPrefs.c` | 927 | Main window, panel switching, save/revert | +| `MouseSettings.c` | 798 | Mouse speed, button mapping, modifier keys | +| `FontSimple.c` | 751 | Font configuration for titlebars, menus, etc. | +| `KeyboardShortcuts.c` | 695 | Keyboard shortcut binding editor | +| `WindowHandling.c` | 612 | Window placement, focus, maximize behavior | +| `Icons.c` | 485 | Icon size, position, animation | +| `HotCornerShortcuts.c` | 465 | Screen corner actions | +| `Configurations.c` | 445 | Misc settings (icon size, edge resistance, etc.) | +| `Expert.c` | 363 | Expert-level toggle options | +| `Focus.c` | 362 | Focus mode, auto-raise delay | +| `Preferences.c` | 359 | Size/position display, dialog behavior | +| `Docks.c` | 335 | Dock/clip behavior | +| `Paths.c` | 328 | Pixmap/icon search paths | +| `xmodifier.c` | 311 | Keyboard modifier detection (from XEmacs) | +| `Workspace.c` | 240 | Workspace naming | +| `MenuPreferences.c` | 234 | Menu behavior options | +| `Themes.c` | 221 | Theme import/export | +| `main.c` | 180 | Entry point, display open, WINGs init | +| `double.c` | 163 | Double-click speed test widget | +| `WPrefs.h` | 175 | Shared header | +| `TexturePanel.h` | 56 | Texture panel header | +| `editmenu.h` | 113 | Menu editor header | + +--- + +## File Classification + +### Fully Portable (zero Xlib calls, zero meaningful X11 types) + +These files use only WINGs widget APIs and WMPropList for defaults manipulation: + +| File | LOC | Purpose | +|---|---|---| +| `FontSimple.c` | 751 | Font configuration | +| `WindowHandling.c` | 612 | Window placement/behavior | +| `Icons.c` | 485 | Icon preferences | +| `Configurations.c` | 445 | Misc configuration | +| `Expert.c` | 363 | Expert toggles | +| `Focus.c` | 362 | Focus mode | +| `Preferences.c` | 359 | Size/position display | +| `Docks.c` | 335 | Dock behavior | +| `Workspace.c` | 240 | Workspace naming | +| `MenuPreferences.c` | 234 | Menu behavior | +| `Themes.c` | 221 | Theme management | +| `KeyboardSettings.c` | 175 | Keyboard repeat/delay | +| **Total** | **4582** | **29% of WPrefs.app** | + +Note: several of these files reference `Display *` or `Window` in variable declarations (obtained via `WMScreenDisplay()`) but only pass them to WINGs functions — they never call Xlib directly. The X11 types flow through transitively from WINGs. + +### X11-Dependent Files + +| File | LOC | Xlib calls | What's leaking | +|---|---|---|---| +| **`Appearance.c`** | 2356 | **67** | Texture preview rendering: `XCreatePixmap`, `XCopyArea` (×20), `XDrawLine` (×15), `XDrawRectangle` (×6), `XFillRectangle` (×4), `XFillPolygon` (×2), `XFreePixmap` (×6), `XCreateGC`/`XFreeGC`, `XParseColor` | +| **`editmenu.c`** | 1361 | **37** | Drag-and-drop menu editing: `XGrabPointer`/`XUngrabPointer`, `XTranslateCoordinates` (×4), `XRecolorCursor` (×4), `XQueryTree`, `XQueryPointer`, `XGetWindowAttributes`, `XGetGeometry`, `XMoveWindow`, `XClearWindow`, `XFlush`, `XChangeProperty`, `XSetWMNormalHints`, `XAllocSizeHints`, `XSetErrorHandler` | +| **`Menu.c`** | 1825 | **24** | Menu editor: `XGrabKeyboard`/`XUngrabKeyboard`, `XDrawLine` (×7), `XSetForeground` (×2), `XFillRectangle`, `XCreateGC`/`XFreeGC` — mostly drawing crosshair markers and key capture | +| **`TexturePanel.c`** | 1549 | **14** | Texture editor: `XFillRectangle` (×3), `XCopyArea` (×2), `XFreePixmap`, `XParseColor`, `XCreateSimpleWindow`, `XMapRaised`, `XFlush`, `XOpenDisplay`, `XColor` | +| **`xmodifier.c`** | 311 | **12** | Modifier key detection: `XGetModifierMapping`, `XFreeModifiermap`, `XKeysymToKeycode` — entirely X11 | +| **`KeyboardShortcuts.c`** | 695 | **12** | Shortcut capture: `XGrabKeyboard`/`XUngrabKeyboard`, `XKeysymToKeycode`, `XKeysymToString`, `XAllowEvents`, `XGetModifierMapping`, `XFreeModifiermap`, `XConvertCase`, `XFillRectangle` | +| **`HotCornerShortcuts.c`** | 465 | **11** | Hot corner icon rendering: `XCreatePixmap` (×5), `XCopyArea` (×5), `XFreePixmap` (×5), `XCreateGC`/`XFreeGC` — icon quadrant assembly | +| **`WPrefs.c`** | 927 | **7** | WM command: `XInternAtom`, `XSendEvent`, `XFlush` (×2) — sends `_WINDOWMAKER_COMMAND` to reload | +| **`MouseSettings.c`** | 798 | **6** | Mouse config: `XChangePointerControl`, `XGetPointerControl`, `XGetModifierMapping`, `XFreeModifiermap`, `XKeysymToString` | +| **`main.c`** | 180 | **4** | Startup: `XSupportsLocale`, `XSetLocaleModifiers`, `XOpenDisplay`, `XDisplayName` | +| **`double.c`** | 163 | **2** | Double-click widget: `XFillRectangle`, `XClearWindow` | +| **`Paths.c`** | 328 | **1** | List drawing: `XFillRectangle` (in custom list cell renderer) | +| **Total** | **8958** | **197** | | + +--- + +## 1. Compile-Time `#ifdef` Branches + +### Backend/Feature Guards + +| Guard | File | Sites | What it guards | +|---|---|---|---| +| `XKB_MODELOCK` | `Expert.c:87`, `KeyboardShortcuts.c:166` | 2 | XKB mode lock toggle option, extra keybinding | +| `HAVE_XCONVERTCASE` | `KeyboardShortcuts.c:171` | 1 | Whether to use system `XConvertCase` or local implementation | + +### Portability Guards + +| Guard | File | Sites | What it guards | +|---|---|---|---| +| `HAVE_STDNORETURN` | `WPrefs.c:27`, `main.c:33` | 2 | C11 `_Noreturn` | +| `I18N` | `main.c:131` | 1 | Gettext initialization | +| `FC_WEIGHT_NORMAL` | `FontSimple.c:28` | 1 | Fontconfig constant fallback | +| `DEBUG` | `Expert.c:286` | 1 | Debug panel option | + +**Total: 8 `#ifdef` sites.** Remarkably few for a 15k LOC application. + +--- + +## 2. Runtime Backend Dispatch + +**None.** No vtable calls, no `is_wayland` checks, no `_x11_raw` access, no `dpy == NULL` guards. WPrefs.app is a pure WINGs client — it gets its X11 dependency entirely through WINGs APIs and direct Xlib calls. + +--- + +## 3. X11 Types in Shared Headers + +### `WPrefs.h` + +```c +int ModifierFromKey(Display *dpy, const char *key); // line 144 +``` + +One function prototype with `Display *`. The rest of the header is WINGs types (`WMScreen *`, `WMWindow *`, etc.). + +--- + +## 4. Direct Xlib Calls — By Category + +### Drawing / Pixmap Manipulation (120 calls — 61%) + +The dominant category. WPrefs renders texture previews, menu editor decorations, and icon composites directly via Xlib: + +| Operation | Count | Files | +|---|---|---| +| `XCopyArea` | 27 | `Appearance.c` (20), `HotCornerShortcuts.c` (5), `TexturePanel.c` (2) | +| `XDrawLine` | 22 | `Appearance.c` (15), `Menu.c` (7) | +| `XFillRectangle` | 13 | `Appearance.c` (4), `Menu.c` (1), `TexturePanel.c` (3), `Paths.c` (1), `double.c` (1), `KeyboardShortcuts.c` (1), `HotCornerShortcuts.c` (1), `editmenu.c` (1) | +| `XCreatePixmap` | 8 | `Appearance.c` (3), `HotCornerShortcuts.c` (5) | +| `XFreePixmap` | 8 | `Appearance.c` (4), `HotCornerShortcuts.c` (3), `TexturePanel.c` (1) | +| `XDrawRectangle` | 6 | `Appearance.c` | +| `XCreateGC` / `XFreeGC` | 6 | `Appearance.c` (4), `Menu.c` (2) | +| `XFillPolygon` / `XDrawLines` | 4 | `Appearance.c` | +| `XSetForeground` | 2 | `Menu.c` | + +**These should all route through WINGs' `W_RenderBackend` vtable** but don't. + +### Keyboard / Input (24 calls — 12%) + +| Operation | Count | Files | +|---|---|---| +| `XGrabKeyboard` / `XUngrabKeyboard` | 4 | `KeyboardShortcuts.c`, `Menu.c` | +| `XKeysymToKeycode` / `XKeysymToString` | 5 | `KeyboardShortcuts.c`, `MouseSettings.c` | +| `XGetModifierMapping` / `XFreeModifiermap` | 6 | `KeyboardShortcuts.c`, `MouseSettings.c`, `xmodifier.c` | +| `XConvertCase` | 1 | `KeyboardShortcuts.c` | +| `XAllowEvents` | 1 | `KeyboardShortcuts.c` | + +### Window / Pointer Manipulation (22 calls — 11%) + +| Operation | Count | Files | +|---|---|---| +| `XGrabPointer` / `XUngrabPointer` | 4 | `editmenu.c` | +| `XTranslateCoordinates` | 5 | `editmenu.c` | +| `XRecolorCursor` | 4 | `editmenu.c` | +| `XQueryTree` / `XQueryPointer` | 2 | `editmenu.c` | +| `XGetWindowAttributes` / `XGetGeometry` | 2 | `editmenu.c` | +| `XMoveWindow` / `XClearWindow` | 2 | `editmenu.c`, `double.c` | +| `XChangePointerControl` / `XGetPointerControl` | 2 | `MouseSettings.c` | + +### WM Communication (7 calls — 4%) + +| Operation | Count | Files | +|---|---|---| +| `XInternAtom` | 1 | `WPrefs.c` | +| `XSendEvent` | 1 | `WPrefs.c` | +| `XFlush` | 3 | `WPrefs.c` (2), `TexturePanel.c` (1) | +| `XChangeProperty` / `XSetWMNormalHints` | 2 | `editmenu.c` | + +### Color Parsing (3 calls — 2%) + +| Operation | Count | Files | +|---|---|---| +| `XParseColor` | 2 | `Appearance.c`, `TexturePanel.c` | +| `XColor` type | 2 | `Appearance.c`, `TexturePanel.c` | + +### Display Connection (4 calls — 2%) + +| Operation | Count | Files | +|---|---|---| +| `XOpenDisplay` | 2 | `main.c`, `TexturePanel.c` | +| `XSupportsLocale` / `XSetLocaleModifiers` | 2 | `main.c` | + +--- + +## 5. Summary: Heat Map + +``` + Fully X11 + Portable Leaking + ────────── ──────── +FontSimple.c ██████████ +WindowHandling.c ██████████ +Icons.c ██████████ +Configurations.c ██████████ +Expert.c ██████████ +Focus.c ██████████ +Preferences.c ██████████ +Docks.c ██████████ +Workspace.c ██████████ +MenuPreferences.c ██████████ +Themes.c ██████████ +KeyboardSettings.c ██████████ +Paths.c █████████░ (1 XFillRectangle) +double.c ████████░░ (2 calls) +WPrefs.c ███████░░░ (7 calls - WM reload command) +main.c ███████░░░ (4 calls - display open) +MouseSettings.c ██████░░░░ (6 calls - pointer control) +HotCornerShortcuts.c █████░░░░░ (11 calls - icon pixmap assembly) +KeyboardShortcuts.c █████░░░░░ (12 calls - key grab/detect) +TexturePanel.c ████░░░░░░ (14 calls - texture preview) +Menu.c ███░░░░░░░ (24 calls - menu editor drawing) +editmenu.c ██░░░░░░░░ (37 calls - DnD menu editing) +xmodifier.c ░░░░░░░░░░ ██████████ (12 calls - entirely X11) +Appearance.c ░░░░░░░░░░ ██████████ (67 calls - texture preview) +``` + +**By the numbers:** + +| Category | Files | LOC | Xlib calls | % of WPrefs.app | +|---|---|---|---|---| +| Fully portable | 12 | 4582 | 0 | 29% | +| Minor X11 leakage (≤7 calls) | 5 | 1876 | 20 | 12% | +| Moderate X11 leakage (8–37 calls) | 5 | 3834 | 86 | 25% | +| Heavy X11 dependency (>37 calls) | 2 | 3717 | 91 | 24% | +| Entirely X11 (xmodifier.c) | 1 | 311 | 12 | 2% | + +**The three biggest problems for Wayland parity:** + +1. **`Appearance.c`** (67 Xlib calls) — all texture preview rendering. Uses `XCopyArea`, `XDrawLine`, `XDrawRectangle`, `XFillPolygon` directly on pixmaps. Should use WINGs' `W_RenderBackend` instead. + +2. **`editmenu.c`** (37 Xlib calls) — the drag-and-drop menu editor. Pointer grabs, coordinate translation, window tree queries, cursor recoloring. These are interaction-model operations that need backend abstraction. + +3. **Keyboard/modifier detection** (`KeyboardShortcuts.c` + `xmodifier.c` = 24 Xlib calls) — `XGrabKeyboard`, `XGetModifierMapping`, `XKeysymToKeycode`. On Wayland, keyboard handling uses `xkbcommon` directly rather than X11 modifier mapping.