windowmaker-wl/ROADMAP.md

231 lines
9.5 KiB
Markdown

# 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`.
- [x] Save/restore state for Wayland native clients: `PropGetWMClass` already
returns the XDG `app_id` on Wayland. Session save no longer bails when
`GetCommandForWindow` returns NULL (external Wayland clients have no ICCCM
command). Restore matches by `app_id` via instance+class without requiring
a command.
- [x] 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[]` from `wlr_output_layout_get_box` in `screen_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.
- [x] Copy/paste between native Wayland clients — works
- [x] Copy/paste between XWayland and native clients — works
- [x] 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_rect` in wl_backend.c using a transient `wlr_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_texture` into an RImage via pixman blit
- Use `wlr_screencopy_manager_v1`
- [ ] Implement `client_snapshot` vtable 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_scale` per output
- [ ] Multiply pixman buffer allocations by scale factor
- [ ] Set PangoFT2 DPI from `output.scale * 96.0`
- [ ] Ensure `WScreen.width/height` remain 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 |