decompose wl_backend.c: extract wl_dock.c
This commit is contained in:
parent
698d4dfba7
commit
5a2faa765a
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,322 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_scene.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <pixman.h>
|
||||
#include "../../screen.h"
|
||||
#include "../x11/wmspec.h"
|
||||
#include "wl_types.h"
|
||||
#include <WINGs/WINGsP.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue