From 5a2faa765ac970b2d97617d0f840e03337705a31 Mon Sep 17 00:00:00 2001 From: Window Maker Date: Tue, 2 Jun 2026 15:49:44 +0200 Subject: [PATCH] decompose wl_backend.c: extract wl_dock.c --- src/Makefile.am | 1 + src/backend/wayland/wl_backend.c | 308 +---------------------------- src/backend/wayland/wl_dock.c | 322 +++++++++++++++++++++++++++++++ 3 files changed, 326 insertions(+), 305 deletions(-) create mode 100644 src/backend/wayland/wl_dock.c diff --git a/src/Makefile.am b/src/Makefile.am index 5612f67..f95c8bc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -189,6 +189,7 @@ wmaker_SOURCES += \ backend/wayland/wl_pointer_focus.c \ backend/wayland/wl_grab.c \ backend/wayland/wl_keyboard.c \ + backend/wayland/wl_dock.c \ backend/wayland/wl_input_fwd.c endif diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index b8596c7..41ddf00 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -232,8 +232,8 @@ void W_UnregisterBacking(unsigned long id); /* ------------------------------------------------------------------ */ #include "wl_types.h" -static void wm_dock_app_add(struct wl_toplevel_view *v); -static void wm_dock_app_remove(WNativeWindow id); +extern void wm_dock_app_add(struct wl_toplevel_view *v); +extern void wm_dock_app_remove(WNativeWindow id); struct wl_compositor_state wl_state; @@ -341,7 +341,7 @@ static void handle_layer_shell_new_surface(struct wl_listener *listener, void *d static void handle_virtual_pointer_new(struct wl_listener *listener, void *data); /* Phase 17: XWayland new surface forward declaration. */ static void handle_xwayland_new_surface(struct wl_listener *listener, void *data); -static void wm_dock_create(WScreen *scr); +extern void wm_dock_create(WScreen *scr); /* Phase 20: nested backend real input device forward declaration. */ extern void handle_backend_new_input(struct wl_listener *listener, void *data); /* Phase 18: output lifecycle and frame rendering forward declarations. */ @@ -2869,308 +2869,6 @@ wl_event_loop_terminate(void) } /* ================================================================== */ -/* 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); - /* Dock panel belongs in WL_LAYER_DOCK so it renders at the same - * depth as the dock icon frame_bufs (which wl_stacking_set_level - * places there for WMStatusWindowLevel=21). */ - { - struct wlr_scene_tree *_dp = wl_state.level_trees[WL_LAYER_DOCK] - ? wl_state.level_trees[WL_LAYER_DOCK] : &wl_state.scene->tree; - wm_shm_surface_commit(s, _dp, sw - dw, 0); - } - - wl_state.dock_surface = s; - wl_state.dock_width = dw; - wl_state.dock_icon_sz = icon_sz; - wl_state.dock_n_slots = n_slots; - wl_state.dock_app_count = 0; - - /* 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); -} - -/* ------------------------------------------------------------------ */ -/* Phase 30+: dock icon management */ -/* ------------------------------------------------------------------ */ - -/* - * wm_shm_surface_repaint — after updating the pixman pixel data, - * force wlroots to re-upload the buffer to the GPU. - * - * wlroots 0.15 caches a texture per wlr_buffer pointer. Since our - * buffer is the same C object the cache entry stays valid even when - * pixel data changes. The reliable solution is to destroy the - * scene_buffer node and recreate it; wlroots then creates a fresh - * texture from the updated pixel data on the next frame. - */ -static void -wm_shm_surface_repaint(struct wm_shm_surface *s) -{ - if (!s || !s->scene_buf) return; - int x = s->scene_buf->node.x; - int y = s->scene_buf->node.y; - struct wlr_scene_tree *parent = s->scene_buf->node.parent; - wlr_scene_node_destroy(&s->scene_buf->node); - s->scene_buf = NULL; - if (parent) { - s->scene_buf = wlr_scene_buffer_create(parent, &s->wlr_buf); - if (s->scene_buf) - wlr_scene_node_set_position(&s->scene_buf->node, x, y); - } -} - -/* - * wm_dock_load_icon — read _NET_WM_ICON from an XWayland window via - * wrlib and return a heap-allocated ARGB8888 pixel array. - * Returns NULL for XDG native clients (no X11 icon available) or on - * failure; caller must free() the returned pointer when done. - */ -static uint32_t * -wm_dock_load_icon(struct wl_toplevel_view *v, int *out_w, int *out_h) -{ - *out_w = *out_h = 0; - if (!v->xw_surface || !wl_state.x_display) - return NULL; /* XDG native: no X11 icon path */ - - RImage *ri = get_window_image_from_x11(wl_state.x_display, (WNativeWindow)v->id); - if (!ri) - return NULL; - - int w = ri->width, h = ri->height; - int bpp = (ri->format == RRGBAFormat) ? 4 : 3; - uint32_t *argb = malloc((size_t)(w * h) * 4); - if (!argb) { RReleaseImage(ri); return NULL; } - - unsigned char *p = ri->data; - for (int i = 0; i < w * h; i++, p += bpp) { - uint8_t r = p[0], g = p[1], b = p[2]; - uint8_t a = (bpp == 4) ? p[3] : 255; - argb[i] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | - ((uint32_t)g << 8) | (uint32_t)b; - } - RReleaseImage(ri); - *out_w = w; - *out_h = h; - return argb; -} - -/* - * wm_dock_refresh — repaint the dock surface with empty tiles, then - * overlay each managed app's icon into its slot, and re-upload to GPU. - */ -static void -wm_dock_refresh(void) -{ - struct wm_shm_surface *s = wl_state.dock_surface; - if (!s) return; - - int icon_sz = wl_state.dock_icon_sz; - int n_slots = wl_state.dock_n_slots; - int w = s->width; - int tile_sz = icon_sz; - int pad = (w - tile_sz) / 2; - - /* Repaint background and all placeholder tiles */ - wm_dock_render(s, icon_sz, n_slots); - - /* Composite each app icon into its slot */ - for (int i = 0; i < wl_state.dock_app_count && i < n_slots; i++) { - int iw = wl_state.dock_apps[i].icon_w; - int ih = wl_state.dock_apps[i].icon_h; - uint32_t *px = wl_state.dock_apps[i].icon_argb; - - if (!px || iw <= 0 || ih <= 0) - continue; - - pixman_image_t *src = pixman_image_create_bits( - PIXMAN_a8r8g8b8, iw, ih, px, iw * 4); - if (!src) continue; - - /* Scale into the inset area (1px bevel border) */ - int dst_sz = tile_sz - 2; - if (dst_sz < 1) dst_sz = 1; - pixman_transform_t xf; - pixman_transform_init_scale(&xf, - pixman_double_to_fixed((double)iw / dst_sz), - pixman_double_to_fixed((double)ih / dst_sz)); - pixman_image_set_transform(src, &xf); - pixman_image_set_filter(src, PIXMAN_FILTER_BILINEAR, NULL, 0); - - int ty = 2 + i * (tile_sz + 2); - if (ty + tile_sz > s->height) { pixman_image_unref(src); break; } - - pixman_image_composite32(PIXMAN_OP_OVER, - src, NULL, s->image, - 0, 0, 0, 0, - pad + 1, ty + 1, - dst_sz, dst_sz); - pixman_image_unref(src); - } - - /* Re-upload to GPU */ - wm_shm_surface_repaint(s); -} - -/* - * wm_dock_app_add — called when a window is managed; adds it to the - * dock list, attempts to load its icon, and refreshes the dock panel. - */ -static void -wm_dock_app_add(struct wl_toplevel_view *v) -{ - if (!wl_state.dock_surface) return; - if (wl_state.dock_app_count >= WM_DOCK_MAX_APPS) return; - - int slot = wl_state.dock_app_count; - wl_state.dock_apps[slot].id = v->id; - wl_state.dock_apps[slot].icon_argb = - wm_dock_load_icon(v, &wl_state.dock_apps[slot].icon_w, - &wl_state.dock_apps[slot].icon_h); - wl_state.dock_app_count++; - - wm_dock_refresh(); - - wwarning("wl_backend: dock: add id=0x%lx icon=%s slot=%d", - v->id, - wl_state.dock_apps[slot].icon_argb ? "loaded" : "none", - slot); -} - -/* - * wm_dock_app_remove — called when a window is unmanaged; removes it - * from the dock list and refreshes the panel. - */ -static void -wm_dock_app_remove(WNativeWindow id) -{ - if (!wl_state.dock_surface) return; - - for (int i = 0; i < wl_state.dock_app_count; i++) { - if (wl_state.dock_apps[i].id != id) - continue; - free(wl_state.dock_apps[i].icon_argb); - int tail = wl_state.dock_app_count - i - 1; - if (tail > 0) - memmove(&wl_state.dock_apps[i], - &wl_state.dock_apps[i + 1], - (size_t)tail * sizeof(wl_state.dock_apps[0])); - wl_state.dock_app_count--; - wm_dock_refresh(); - wwarning("wl_backend: dock: remove id=0x%lx total=%d", - id, wl_state.dock_app_count); - return; - } -} /* ------------------------------------------------------------------ */ diff --git a/src/backend/wayland/wl_dock.c b/src/backend/wayland/wl_dock.c new file mode 100644 index 0000000..7e87aad --- /dev/null +++ b/src/backend/wayland/wl_dock.c @@ -0,0 +1,322 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include "../../screen.h" +#include "../x11/wmspec.h" +#include "wl_types.h" +#include + +/* Forward declarations */ +struct wm_shm_surface *wm_shm_surface_create(int width, int height); +void wm_shm_surface_fill(struct wm_shm_surface *s, int x, int y, int w, int h, uint32_t color); +void wm_shm_surface_commit(struct wm_shm_surface *s, struct wlr_scene_tree *parent, int x, int y); + +/* 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. */ +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); + /* Dock panel belongs in WL_LAYER_DOCK so it renders at the same + * depth as the dock icon frame_bufs (which wl_stacking_set_level + * places there for WMStatusWindowLevel=21). */ + { + struct wlr_scene_tree *_dp = wl_state.level_trees[WL_LAYER_DOCK] + ? wl_state.level_trees[WL_LAYER_DOCK] : &wl_state.scene->tree; + wm_shm_surface_commit(s, _dp, sw - dw, 0); + } + + wl_state.dock_surface = s; + wl_state.dock_width = dw; + wl_state.dock_icon_sz = icon_sz; + wl_state.dock_n_slots = n_slots; + wl_state.dock_app_count = 0; + + /* 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); +} + +/* ------------------------------------------------------------------ */ +/* Phase 30+: dock icon management */ +/* ------------------------------------------------------------------ */ + +/* + * wm_shm_surface_repaint — after updating the pixman pixel data, + * force wlroots to re-upload the buffer to the GPU. + * + * wlroots 0.15 caches a texture per wlr_buffer pointer. Since our + * buffer is the same C object the cache entry stays valid even when + * pixel data changes. The reliable solution is to destroy the + * scene_buffer node and recreate it; wlroots then creates a fresh + * texture from the updated pixel data on the next frame. + */ +static void +wm_shm_surface_repaint(struct wm_shm_surface *s) +{ + if (!s || !s->scene_buf) return; + int x = s->scene_buf->node.x; + int y = s->scene_buf->node.y; + struct wlr_scene_tree *parent = s->scene_buf->node.parent; + wlr_scene_node_destroy(&s->scene_buf->node); + s->scene_buf = NULL; + if (parent) { + s->scene_buf = wlr_scene_buffer_create(parent, &s->wlr_buf); + if (s->scene_buf) + wlr_scene_node_set_position(&s->scene_buf->node, x, y); + } +} + +/* + * wm_dock_load_icon — read _NET_WM_ICON from an XWayland window via + * wrlib and return a heap-allocated ARGB8888 pixel array. + * Returns NULL for XDG native clients (no X11 icon available) or on + * failure; caller must free() the returned pointer when done. + */ +static uint32_t * +wm_dock_load_icon(struct wl_toplevel_view *v, int *out_w, int *out_h) +{ + *out_w = *out_h = 0; + if (!v->xw_surface || !wl_state.x_display) + return NULL; /* XDG native: no X11 icon path */ + + RImage *ri = get_window_image_from_x11(wl_state.x_display, (WNativeWindow)v->id); + if (!ri) + return NULL; + + int w = ri->width, h = ri->height; + int bpp = (ri->format == RRGBAFormat) ? 4 : 3; + uint32_t *argb = malloc((size_t)(w * h) * 4); + if (!argb) { RReleaseImage(ri); return NULL; } + + unsigned char *p = ri->data; + for (int i = 0; i < w * h; i++, p += bpp) { + uint8_t r = p[0], g = p[1], b = p[2]; + uint8_t a = (bpp == 4) ? p[3] : 255; + argb[i] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | + ((uint32_t)g << 8) | (uint32_t)b; + } + RReleaseImage(ri); + *out_w = w; + *out_h = h; + return argb; +} + +/* + * wm_dock_refresh — repaint the dock surface with empty tiles, then + * overlay each managed app's icon into its slot, and re-upload to GPU. + */ +static void +wm_dock_refresh(void) +{ + struct wm_shm_surface *s = wl_state.dock_surface; + if (!s) return; + + int icon_sz = wl_state.dock_icon_sz; + int n_slots = wl_state.dock_n_slots; + int w = s->width; + int tile_sz = icon_sz; + int pad = (w - tile_sz) / 2; + + /* Repaint background and all placeholder tiles */ + wm_dock_render(s, icon_sz, n_slots); + + /* Composite each app icon into its slot */ + for (int i = 0; i < wl_state.dock_app_count && i < n_slots; i++) { + int iw = wl_state.dock_apps[i].icon_w; + int ih = wl_state.dock_apps[i].icon_h; + uint32_t *px = wl_state.dock_apps[i].icon_argb; + + if (!px || iw <= 0 || ih <= 0) + continue; + + pixman_image_t *src = pixman_image_create_bits( + PIXMAN_a8r8g8b8, iw, ih, px, iw * 4); + if (!src) continue; + + /* Scale into the inset area (1px bevel border) */ + int dst_sz = tile_sz - 2; + if (dst_sz < 1) dst_sz = 1; + pixman_transform_t xf; + pixman_transform_init_scale(&xf, + pixman_double_to_fixed((double)iw / dst_sz), + pixman_double_to_fixed((double)ih / dst_sz)); + pixman_image_set_transform(src, &xf); + pixman_image_set_filter(src, PIXMAN_FILTER_BILINEAR, NULL, 0); + + int ty = 2 + i * (tile_sz + 2); + if (ty + tile_sz > s->height) { pixman_image_unref(src); break; } + + pixman_image_composite32(PIXMAN_OP_OVER, + src, NULL, s->image, + 0, 0, 0, 0, + pad + 1, ty + 1, + dst_sz, dst_sz); + pixman_image_unref(src); + } + + /* Re-upload to GPU */ + wm_shm_surface_repaint(s); +} + +/* + * wm_dock_app_add — called when a window is managed; adds it to the + * dock list, attempts to load its icon, and refreshes the dock panel. + */ +void +wm_dock_app_add(struct wl_toplevel_view *v) +{ + if (!wl_state.dock_surface) return; + if (wl_state.dock_app_count >= WM_DOCK_MAX_APPS) return; + + int slot = wl_state.dock_app_count; + wl_state.dock_apps[slot].id = v->id; + wl_state.dock_apps[slot].icon_argb = + wm_dock_load_icon(v, &wl_state.dock_apps[slot].icon_w, + &wl_state.dock_apps[slot].icon_h); + wl_state.dock_app_count++; + + wm_dock_refresh(); + + wwarning("wl_backend: dock: add id=0x%lx icon=%s slot=%d", + v->id, + wl_state.dock_apps[slot].icon_argb ? "loaded" : "none", + slot); +} + +/* + * wm_dock_app_remove — called when a window is unmanaged; removes it + * from the dock list and refreshes the panel. + */ +void +wm_dock_app_remove(WNativeWindow id) +{ + if (!wl_state.dock_surface) return; + + for (int i = 0; i < wl_state.dock_app_count; i++) { + if (wl_state.dock_apps[i].id != id) + continue; + free(wl_state.dock_apps[i].icon_argb); + int tail = wl_state.dock_app_count - i - 1; + if (tail > 0) + memmove(&wl_state.dock_apps[i], + &wl_state.dock_apps[i + 1], + (size_t)tail * sizeof(wl_state.dock_apps[0])); + wl_state.dock_app_count--; + wm_dock_refresh(); + wwarning("wl_backend: dock: remove id=0x%lx total=%d", + id, wl_state.dock_app_count); + return; + } +}