wayland: Phase 30 — native Wayland dock panel via wm_shm_surface

Replace the Phase 29 test square with a proper dock panel anchored to the
right edge of the primary output.  The panel is rendered entirely via
pixman into a wm_shm_surface and uploaded to the wlroots scene graph — no
X11 drawing resources needed.

Visual design:
  - Dark charcoal background (0xFF1C1C1C)
  - 1-px left-edge highlight (0xFF404040) separating dock from desktop
  - 1-px top separator (0xFF303030)
  - Column of bevelled icon-slot placeholders (top/left highlight +
    bottom/right shadow) at icon_size × icon_size spacing

Layout and sizing:
  - Width = icon_size + 4 px (default 68 px for 64-px icons)
  - Height = full output height
  - X position = output_width - dock_width (flush to right edge)
  - n_slots = (height - 4) / (icon_size + 2)  (16 slots @ 1080p)

Usable area:
  - head-0 x2 shrunk by dock_width so window placement never overlaps

The WDock X11 code path remains disabled (nodock=1) — icon population
and click handling come in later phases.

Smoke test: PASS ("dock panel created at 1852,0 (68x1080, 16 slots)")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Levi Neely 2026-03-31 13:55:24 +02:00
parent 4f0273e5e6
commit 8fe783e304
2 changed files with 137 additions and 16 deletions

View File

@ -393,6 +393,9 @@ struct wl_compositor_state {
/* Phase 29 axis listeners (headless pointer + real nested pointer). */
struct wl_listener pointer_axis_listener;
struct wl_listener real_pointer_axis_listener;
/* Phase 30: WM dock panel (native Wayland rendering via wm_shm_surface). */
struct wm_shm_surface *dock_surface; /* NULL until screen_create_internals */
int dock_width; /* pixels reserved on right edge */
};
static struct wl_compositor_state wl_state;
@ -2556,6 +2559,127 @@ wm_shm_surface_destroy(struct wm_shm_surface *s)
wlr_buffer_drop(&s->wlr_buf); /* producer done; shm_buf_destroy frees s */
}
/* ================================================================== */
/* Phase 30: WM dock panel (layer-anchored, pixman-rendered) */
/* ================================================================== */
/*
* The dock is a vertical strip anchored to the right edge of the primary
* output. It is rendered into a wm_shm_surface whose pixels flow through
* the wlroots scene graph, exactly like any other WM UI element.
*
* Layout (right edge, full screen height):
*
* ┌──────────────────────────────────────────────────┬────────┐
* │ │ ██████ │
* │ desktop / client windows │ ██████ │
* │ (constrained to usable area) │ ██████ │
* │ │ dock │
* └──────────────────────────────────────────────────┴────────┘
* ← dw →
*
* The usable area (where windows are placed) is shrunk by dock_width so
* clients never overlap the panel.
*/
/* Default width when wPreferences.icon_size is not yet set. */
#define WM_DOCK_DEFAULT_WIDTH 68 /* 64 px icon + 4 px border */
/* Colours (ARGB8888, fully opaque) */
#define WM_DOCK_BG 0xFF1C1C1C /* dark charcoal background */
#define WM_DOCK_HIGHLIGHT 0xFF404040 /* 1-px highlight on left edge */
#define WM_DOCK_TOPSEP 0xFF303030 /* 1-px separator at top */
#define WM_DOCK_TILE_BG 0xFF252525 /* icon-slot background */
#define WM_DOCK_TILE_HI 0xFF353535 /* icon-slot top/left highlight */
#define WM_DOCK_TILE_SH 0xFF151515 /* icon-slot bottom/right shadow */
/* wm_dock_render_tile — paint one icon slot starting at (x, y).
* Slots are icon_size × icon_size with a 1-px inset bevel. */
static void
wm_dock_render_tile(struct wm_shm_surface *s,
int x, int y, int tile_sz)
{
/* Background */
wm_shm_surface_fill(s, x, y, tile_sz, tile_sz, WM_DOCK_TILE_BG);
/* Top highlight */
wm_shm_surface_fill(s, x, y, tile_sz, 1, WM_DOCK_TILE_HI);
/* Left highlight */
wm_shm_surface_fill(s, x, y, 1, tile_sz, WM_DOCK_TILE_HI);
/* Bottom shadow */
wm_shm_surface_fill(s, x, y + tile_sz - 1, tile_sz, 1, WM_DOCK_TILE_SH);
/* Right shadow */
wm_shm_surface_fill(s, x + tile_sz - 1, y, 1, tile_sz, WM_DOCK_TILE_SH);
}
/* wm_dock_render — paint the full dock panel.
* Draws the background, left-edge highlight, top separator, and a column
* of empty icon slots to give visual structure before real icons arrive. */
static void
wm_dock_render(struct wm_shm_surface *s, int icon_sz, int n_slots)
{
int w = s->width;
int h = s->height;
int tile_sz = icon_sz;
int pad = (w - tile_sz) / 2; /* horizontal centering margin */
/* Background */
wm_shm_surface_fill(s, 0, 0, w, h, WM_DOCK_BG);
/* Left-edge highlight (the dock's inner border with the desktop) */
wm_shm_surface_fill(s, 0, 0, 1, h, WM_DOCK_HIGHLIGHT);
/* Top separator */
wm_shm_surface_fill(s, 0, 0, w, 1, WM_DOCK_TOPSEP);
/* Icon-slot placeholders */
for (int i = 0; i < n_slots; i++) {
int ty = 2 + i * (tile_sz + 2);
if (ty + tile_sz > h)
break;
wm_dock_render_tile(s, pad, ty, tile_sz);
}
}
/* wm_dock_create — allocate the dock surface, render it, attach to scene,
* and shrink the head-0 usable area by dock_width pixels on the right. */
static void
wm_dock_create(WScreen *scr)
{
if (!wl_state.scene || !wl_state.output)
return;
int sw = (int)wl_state.output->width;
int sh = (int)wl_state.output->height;
int icon_sz = (wPreferences.icon_size > 0)
? wPreferences.icon_size : 64;
int dw = icon_sz + 4; /* dock width = icon + 2px pad each side */
int n_slots = (sh - 4) / (icon_sz + 2); /* how many slots fit */
struct wm_shm_surface *s = wm_shm_surface_create(dw, sh);
if (!s) {
wwarning("wl_backend: wm_dock_create: failed to allocate surface");
return;
}
wm_dock_render(s, icon_sz, n_slots);
wm_shm_surface_commit(s, &wl_state.scene->node, sw - dw, 0);
wl_state.dock_surface = s;
wl_state.dock_width = dw;
/* Shrink head-0 usable area so window placement avoids the dock. */
if (scr->usableArea) {
int right = scr->usableArea[0].x2 - dw;
if (right < scr->usableArea[0].x1)
right = scr->usableArea[0].x1;
scr->usableArea[0].x2 = right;
scr->totalUsableArea[0].x2 = right;
}
wwarning("wl_backend: dock panel created at %d,0 (%dx%d, %d slots)",
sw - dw, dw, sh, n_slots);
}
/* ------------------------------------------------------------------ */
/* Screen open/close */
/* ------------------------------------------------------------------ */
@ -4056,22 +4180,15 @@ wl_screen_create_internals(WScreen *scr)
wSetModifierMasks(Mod1Mask, Mod1Mask, Mod4Mask, 0);
/* Dock/clip require X11 rendering resources not yet available in Wayland
* mode. Disable until native Wayland dock rendering is implemented. */
/* Phase 30: the WDock X11 code path is still disabled (it needs X11 GCs /
* pixmaps we don't have in pure Wayland mode). The visual dock panel is
* created below via wm_dock_create() which uses wm_shm_surface directly. */
wPreferences.flags.noclip = 1;
wPreferences.flags.nodock = 1;
wwarning("wl_backend: dock/clip disabled pending native Wayland rendering");
/* Phase 29 smoke test: create a 64×64 steel-blue surface at (16,16) to
* verify the pixman → wlr_scene_buffer pipeline produces visible pixels. */
if (wl_state.scene) {
struct wm_shm_surface *test_surf = wm_shm_surface_create(64, 64);
if (test_surf) {
wm_shm_surface_fill(test_surf, 0, 0, 64, 64, 0xFF4080C0); /* steel-blue */
wm_shm_surface_commit(test_surf, &wl_state.scene->node, 16, 16);
wwarning("wl_backend: Phase 29 test surface committed (64x64 steel-blue at 16,16)");
}
}
/* Phase 30: create the native Wayland dock panel. */
wm_dock_create(scr);
}
static void wl_screen_create_pixmaps(WScreen *scr) { (void)scr; }
static void wl_screen_load_tech_font(WScreen *scr) { (void)scr; }

View File

@ -320,13 +320,17 @@ grep -q 'pointer motion/button/frame listeners wired' /tmp/wl-smoke-stderr \
|| die "compositor did not log pointer listeners wired"
echo " ptr listener check: pointer motion/button/frame listeners wired"
# -- 4f. Phase 11: dock/clip disabled in Wayland mode --------------------
# -- 4f. Phase 30: dock panel rendered via wm_shm_surface ----------------
#
# wl_screen_create_internals always sets noclip=1 nodock=1 until native
# Wayland dock rendering is implemented.
# wl_screen_create_internals calls wm_dock_create() which logs
# "dock panel created at X,0 (WxH, N slots)".
# WDock X11 code path is still disabled (noclip/nodock=1).
grep -q 'dock/clip disabled' /tmp/wl-smoke-stderr \
|| die "compositor did not log dock/clip disabled (wl_screen_create_internals broken?)"
echo " nodock check: dock/clip disabled pending native Wayland rendering"
grep -q 'dock panel created' /tmp/wl-smoke-stderr \
|| die "compositor did not log dock panel creation (wm_dock_create failed?)"
DOCK_INFO=$(grep -o 'dock panel created at [0-9]*,0 ([^)]*)' /tmp/wl-smoke-stderr | head -1)
echo " nodock check: dock/clip disabled; ${DOCK_INFO:-dock panel created}"
# -- 4f2b. Phase 25: clipboard selection wired ----------------------------
grep -q 'clipboard selection wired' /tmp/wl-smoke-stderr \