9.5 KiB
WindowMaker Wayland Port — Roadmap
Current State (2026-05-28, final)
The backend abstraction is complete. The src/ policy layer has zero direct Xlib
calls and zero _x11_raw references. The Wayland backend (wlroots-based, 10K lines)
implements all core window management: stacking, maximize, shade, workspaces,
alt-tab, dock/clip, menus, move/resize, focus, and frame rendering.
WINGs X11 call isolation is complete. All 412+ direct Xlib calls that previously
lived in shared widget code have been isolated to per-backend _x11.c files with
Wayland stubs in wstubs_no_x11.c. The remaining work is feature implementation,
not leak cleanup.
What works on Wayland:
- Window management (stacking, maximize, shade, workspaces, alt-tab, move/resize)
- DND (compositor-internal with drag image overlay + cursor feedback; cross-client via wl_data_device with basic MIME routing)
- Clipboard + primary selection (copy/paste between Wayland native and XWayland)
- Session persistence (save/restore for native Wayland clients)
- Balloon tooltips (proper leave tracking + multi-line text)
- Window thumbnails (client_snapshot via buffer read)
- XWayland client support (DISPLAY=:1 via eager mode startup)
- WPrefs.app (all preference panels — crashes fixed by X11 call isolation)
The smoke test suite passes for core WM features.
What remains is feature completion and production hardening.
Priority 1: Daily-Driver Gaps
These block using the Wayland backend as a primary desktop.
1.1 Session Persistence
src/session.c (614 lines) — verify save/restore works on Wayland. Window
positions are tracked in WWindow (backend-neutral). The only concern is
matching restored sessions to native XDG clients via app_id instead of
WM_CLASS.
- Save/restore state for Wayland native clients:
PropGetWMClassalready returns the XDGapp_idon Wayland. Session save no longer bails whenGetCommandForWindowreturns NULL (external Wayland clients have no ICCCM command). Restore matches byapp_idvia instance+class without requiring a command. - Save/restore works for Wayland native clients (foot). XWayland (xterm) needs WM_COMMAND matching — lower priority.
- Verify dock state persistence (icon layout in
~/GNUstep/Defaults/)
1.2 Multi-Monitor
Currently single-output only. wlroots provides wlr_output_layout and
new_output signal.
- Populate
WScreen.heads[]fromwlr_output_layout_get_boxinscreen_open - Handle output hotplug (migrate windows on disconnect)
- Per-head usable area calculation (dock margins)
- Test with nested multi-output (
WMAKER_WAYLAND_NESTED=1+wlr-randr)
1.3 Clipboard
wlroots advertises wl_data_device_manager and zwp_primary_selection_v1.
XWayland bridge auto-syncs. Needs verification.
- Copy/paste between native Wayland clients — works
- Copy/paste between XWayland and native clients — works
- Primary selection (middle-click paste) — works (both directions)
1.4 Remaining Vtable Stubs
| Slot | Status |
|---|---|
overlay_draw_* |
✅ Done — real pixman overlay implementation |
client_snapshot |
✅ Done — buffer read for native, XGetImage for XWayland |
window_set_border |
✅ No-op (borders drawn into frame buffer) |
client_synth_configure |
✅ Done — XDG configure + XWayland configure |
pointer_change_grab |
✅ Done — sets cursor + establishes grab state during move/resize (fix: was not setting grab_type for opaque moves, causing cursor to reset to arrow on every motion event) |
Priority 2: WPrefs.app Full Port
Status: Direct X11 Calls Resolved
All 11 direct Xlib calls previously reported in WPrefs.app have been audited:
| File | Previously Reported | Actual Status |
|---|---|---|
TexturePanel.c |
7 calls | 0 active — all inside #if 0 dead code blocks |
Expert.c |
1 (XGetDefault) | 0 active — call already removed |
MouseSettings.c |
1 (XChangePointerControl) | 0 active — call already removed |
xmodifier.c |
1 (XGetModifierMapping) | 0 active — only in a comment |
WPrefs.app itself now has zero direct Xlib calls. The crash source is in WINGs widget library, which WPrefs links against.
Root Cause: WINGs X11 Leaks (412 calls)
WINGs still has 412 direct Xlib calls across 15 non-split widget files that
bypass the 5-vtable dispatch (W_ViewBackend, W_EventBackend, W_RenderBackend,
W_FontBackend, W_ColorBackend). These are the crash sites when WPrefs calls
WINGs APIs on the Wayland path.
| WINGs File | X11 Calls | What It Does |
|---|---|---|
wcolorpanel.c |
89 | Pixmap manipulation, XGetImage, pointer grabs, cursor recoloring |
dragsource.c |
45 | XDND protocol (XSendEvent, XInternAtom, grabs) |
wwindow.c |
41 | ICCCM window properties (XSetWMName, XSetWMHints, etc.) |
winputmethod.c |
40 | XIM/XIC lifecycle (XOpenIM, XCreateIC, XLookupString) |
dragdestination.c |
37 | XDND protocol (receive side) |
widgets.c |
35 | Screen init, GC/cursor creation, modifier mapping |
wballoon.c |
28 | Tooltip drawing, shape extension |
wtext.c |
23 | Clipboard, grabs |
selection.c |
21 | X selection protocol |
dragcommon.c |
16 | XDND shared atom/property helpers |
wappresource.c |
15 | App icon property reading |
wtextfield.c |
10 | Clipboard, grabs |
wpopupbutton.c |
7 | Pointer grabs |
wruler.c |
3 | XRectangle type usage |
wmisc.c |
2 | Coordinate translation |
These files need backend abstraction — either routing through the existing 5 vtables, or adding new vtables for DND, selection, and input methods.
WINGs X11 Call Isolation — COMPLETE
All 412+ direct Xlib calls in WINGs have been isolated to _x11.c backend files.
No active X11 calls remain in non-backend WINGs source files.
| File | LOC | X11 Calls | Status |
|---|---|---|---|
| DND (drag*source/destination/common) | 2437 | 98 | Isolated — internal DND works, cross-client |
| via wl_data_device with basic MIME routing | |||
wcolorpanel.c |
3556 | 89 | Split into wcolorpanel.c + _x11.c + _wl.c |
wwindow.c + widgets.c |
1849 | 76 | Already clean — all via vtable dispatch |
winputmethod_x11.c |
243 | 40 | Isolated — Wayland stub in wstubs_no_x11.c |
selection_x11.c |
392 | 21 | Push + fetch via clipboard hooks; synchronous |
| fetch path on Wayland | |||
wballoon_x11.c |
476 | 28 | Wayland balloon_draw in wview_wl.c renders |
| yellow tooltip with multi-line support | |||
wtext.c + wtextfield.c |
5577 | 33 | Already clean — all via vtable dispatch |
All other _x11.c files |
~2000 | ~27 | Isolated — stubs in wstubs_no_x11.c |
Priority 3: Visual Completeness
3.1 Iconify/Deiconify Zoom Animation
The X11 path uses XOR GC rubber-band rectangles (XGrabServer + XDrawRectangle).
On Wayland, implement as per-frame pixman overlay buffer.
- Implement
overlay_draw_rectin wl_backend.c using a transientwlr_scene_buffer - Wire
animateResize()in actions.c to use the vtable
3.2 Client Snapshot (Window Thumbnails)
Used by switch panel and icon preview. X11 uses XGetImage. Wayland options:
-
Render last committed
wlr_textureinto an RImage via pixman blit -
Use
wlr_screencopy_manager_v1 -
Implement
client_snapshotvtable slot using last-committed texture
3.3 Window Inspector
src/winspector.c — WINGs-based dialog. Should work on Wayland since WINGs
widgets are ported. Needs testing.
- Test window inspector opens and displays attributes
- Test attribute changes apply correctly
Priority 4: HiDPI & Scaling
- Set
wlr_output_set_scaleper output - Multiply pixman buffer allocations by scale factor
- Set PangoFT2 DPI from
output.scale * 96.0 - Ensure
WScreen.width/heightremain logical coordinates - Test at 2x scale
Priority 5: Protocol Wiring
wlroots already advertises these protocols. Wire them to policy:
| Protocol | Purpose | Effort |
|---|---|---|
ext-session-lock-v1 |
Screen lock | Medium |
xdg_activation_v1 |
Focus stealing prevention | Low |
zwp_idle_inhibit_manager_v1 |
Prevent blank during video | Low |
ext-foreign-toplevel-list-v1 |
Taskbar integration | Low |
zwp_input_method_v2 |
IME support | Medium |
wlr_gamma_control_v1 |
Night light | Low |
Priority 6: DRM/KMS Native Backend
For running without a host compositor:
- Use
wlr_backend_autocreate(replaces headless/nested) - libinput for real hardware input (wlroots handles this)
- VT switching via
wlr_session - Test on real hardware
Architecture Notes
- Rendering pattern: pixman_image_t → wlr_scene_buffer → scene graph position
- Event flow: wlroots signals → wl_backend.c handlers → WMEvent → DispatchEvent
- WINGs on Wayland: compositor-internal views (not wl_surfaces), pixman-rendered, events via ring buffer from compositor hit-testing
- wl_backend.c (420K) should be split into wl_frame.c, wl_menu.c, wl_input.c, wl_toplevel.c, wl_output.c as complexity grows
Reference Documents
| Document | Purpose |
|---|---|
ARCHITECTURE.md |
Codebase structure, data flow, key types |
BACKEND_ABSTRACTION.md |
Historical analysis of X11 leaks (all resolved) |
X11_IMPLEMENTATION.md |
How X11 path works (correctness reference) |
ACTION_PLAN_PROMPTS.md |
Task templates for mechanical porting work |
tools/wm-port/ |
Agent-assisted task dispatch tooling |