wl impl planning

This commit is contained in:
Window Maker 2026-05-16 12:28:44 +02:00
parent 10c90ff6ce
commit f289ad7a8d
4 changed files with 2743 additions and 0 deletions

506
ACTION_PLAN.md Normal file
View File

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

666
ACTION_PLAN_PROMPTS.md Normal file
View File

@ -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.

245
ARCHITECTURE.md Normal file
View File

@ -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.

1326
BACKEND_ABSTRACTION.md Normal file

File diff suppressed because it is too large Load Diff