wl_backend: implement desktop background rendering
On X11, wmsetbg sets the root window pixmap. On Wayland there's no root window, so the compositor must render the background directly. Added wm_backend->set_desktop_background() vtable method: - X11: NULL (wmsetbg continues to handle it) - Wayland: parses the texture proplist description and renders: - solid colors - vertical/horizontal/diagonal gradients (via RRenderGradient) - pixmap/spixmap/cpixmap images (via RLoadImage + RScaleImage) The rendered image is displayed as a wlr_scene_buffer in the WL_LAYER_BACKGROUND layer, replacing the static solid-color rect. setWorkspaceBack() in defaults.c now calls the backend method when available, so background changes from WPrefs take effect immediately.
This commit is contained in:
parent
aab89ab11a
commit
93587378d9
|
|
@ -656,6 +656,17 @@ typedef struct WMBackend {
|
|||
*/
|
||||
void (*refresh_desktop)(WScreen *scr);
|
||||
|
||||
/*
|
||||
* Set the desktop background from a texture specification (proplist).
|
||||
* On X11 this is a no-op (wmsetbg handles it).
|
||||
* On Wayland the compositor renders the background directly.
|
||||
* `texture_desc` is the proplist description string, e.g.:
|
||||
* "(solid, \"rgb:50/50/75\")"
|
||||
* "(pixmap, \"/path/to/image.jpg\")"
|
||||
* "(vgradient, \"rgb:00/00/80\", \"rgb:00/00/00\")"
|
||||
*/
|
||||
void (*set_desktop_background)(WScreen *scr, const char *texture_desc);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* WNativeWindow event subscription (Phase 11) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
|
|||
|
|
@ -553,6 +553,9 @@ struct wl_compositor_state {
|
|||
#define WL_LAYER_OVERLAY 8
|
||||
#define WL_NUM_LEVEL_BUCKETS 9
|
||||
struct wlr_scene_tree *level_trees[WL_NUM_LEVEL_BUCKETS];
|
||||
struct wlr_scene_rect *bg_rect; /* default solid background */
|
||||
struct wlr_scene_buffer *bg_image; /* wallpaper image (if set) */
|
||||
struct wlr_buffer *bg_wlr_buf; /* backing buffer for bg_image */
|
||||
|
||||
/* Phase 81: full-screen overlay surface for XOR animation drawing */
|
||||
struct wm_shm_surface *overlay_surf; /* NULL until first draw */
|
||||
|
|
@ -2209,8 +2212,9 @@ handle_backend_new_output(struct wl_listener *listener, void *data)
|
|||
* surfaces. Default WindowMaker desktop colour: rgb:50/50/75. */
|
||||
{
|
||||
float bg[4] = { 0x50/255.0f, 0x50/255.0f, 0x75/255.0f, 1.0f };
|
||||
wlr_scene_rect_create(wl_state.level_trees[WL_LAYER_BACKGROUND],
|
||||
(int)output->width, (int)output->height, bg);
|
||||
wl_state.bg_rect = wlr_scene_rect_create(
|
||||
wl_state.level_trees[WL_LAYER_BACKGROUND],
|
||||
(int)output->width, (int)output->height, bg);
|
||||
wwarning("wl_backend: root background created (%dx%d)",
|
||||
(int)output->width, (int)output->height);
|
||||
}
|
||||
|
|
@ -7645,8 +7649,179 @@ static RImage *wl_screen_capture_area(WScreen *scr) { (void)scr; return NULL; }
|
|||
static RImage *wl_screen_capture_window(WScreen *scr, WWindow *wwin) { (void)scr; (void)wwin; return NULL; }
|
||||
static void wl_refresh_desktop(WScreen *scr) { (void)scr; }
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Monitors */
|
||||
/* --- Desktop background rendering --- */
|
||||
|
||||
/* Simple wlr_buffer implementation backed by a pixman_image_t */
|
||||
struct wl_bg_buffer {
|
||||
struct wlr_buffer base;
|
||||
pixman_image_t *image;
|
||||
};
|
||||
|
||||
static void bg_buffer_destroy(struct wlr_buffer *wlr_buf)
|
||||
{
|
||||
struct wl_bg_buffer *buf = wl_container_of(wlr_buf, buf, base);
|
||||
if (buf->image)
|
||||
pixman_image_unref(buf->image);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static bool bg_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buf,
|
||||
uint32_t flags, void **data, uint32_t *format, size_t *stride)
|
||||
{
|
||||
(void)flags;
|
||||
struct wl_bg_buffer *buf = wl_container_of(wlr_buf, buf, base);
|
||||
*data = pixman_image_get_data(buf->image);
|
||||
*format = DRM_FORMAT_ARGB8888;
|
||||
*stride = pixman_image_get_stride(buf->image);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bg_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buf)
|
||||
{
|
||||
(void)wlr_buf;
|
||||
}
|
||||
|
||||
static const struct wlr_buffer_impl bg_buffer_impl = {
|
||||
.destroy = bg_buffer_destroy,
|
||||
.begin_data_ptr_access = bg_buffer_begin_data_ptr_access,
|
||||
.end_data_ptr_access = bg_buffer_end_data_ptr_access,
|
||||
};
|
||||
|
||||
static void wl_set_desktop_background(WScreen *scr, const char *texture_desc)
|
||||
{
|
||||
if (!scr || !texture_desc || !wl_state.output)
|
||||
return;
|
||||
|
||||
int width = wl_state.output->width;
|
||||
int height = wl_state.output->height;
|
||||
|
||||
/* Parse the texture proplist */
|
||||
WMPropList *texpl = WMCreatePropListFromDescription(texture_desc);
|
||||
if (!texpl)
|
||||
return;
|
||||
|
||||
/* Render the texture to an RImage using wrlib */
|
||||
RContext *rc = scr->rcontext;
|
||||
RImage *bg = NULL;
|
||||
|
||||
if (WMIsPLArray(texpl) && WMGetPropListItemCount(texpl) > 0) {
|
||||
WMPropList *type_pl = WMGetFromPLArray(texpl, 0);
|
||||
const char *type = type_pl ? WMGetFromPLString(type_pl) : "";
|
||||
|
||||
if (strcasecmp(type, "solid") == 0 && WMGetPropListItemCount(texpl) >= 2) {
|
||||
const char *colorstr = WMGetFromPLString(WMGetFromPLArray(texpl, 1));
|
||||
RColor color = {0x50, 0x50, 0x75, 0xFF};
|
||||
unsigned int r, g, b;
|
||||
if (colorstr) {
|
||||
if (sscanf(colorstr, "rgb:%x/%x/%x", &r, &g, &b) == 3) {
|
||||
color.red = r; color.green = g; color.blue = b;
|
||||
} else if (sscanf(colorstr, "#%02x%02x%02x", &r, &g, &b) == 3) {
|
||||
color.red = r; color.green = g; color.blue = b;
|
||||
}
|
||||
}
|
||||
bg = RCreateImage(width, height, 0);
|
||||
if (bg) RClearImage(bg, &color);
|
||||
} else if ((strcasecmp(type, "vgradient") == 0 ||
|
||||
strcasecmp(type, "hgradient") == 0 ||
|
||||
strcasecmp(type, "dgradient") == 0) &&
|
||||
WMGetPropListItemCount(texpl) >= 3) {
|
||||
const char *c1str = WMGetFromPLString(WMGetFromPLArray(texpl, 1));
|
||||
const char *c2str = WMGetFromPLString(WMGetFromPLArray(texpl, 2));
|
||||
RColor color1 = {0, 0, 0, 255}, color2 = {0, 0, 0, 255};
|
||||
unsigned int r, g, b;
|
||||
if (c1str && sscanf(c1str, "rgb:%x/%x/%x", &r, &g, &b) == 3) {
|
||||
color1.red = r; color1.green = g; color1.blue = b;
|
||||
}
|
||||
if (c2str && sscanf(c2str, "rgb:%x/%x/%x", &r, &g, &b) == 3) {
|
||||
color2.red = r; color2.green = g; color2.blue = b;
|
||||
}
|
||||
int style = (type[0] == 'v' || type[0] == 'V') ? RVerticalGradient :
|
||||
(type[0] == 'h' || type[0] == 'H') ? RHorizontalGradient :
|
||||
RDiagonalGradient;
|
||||
bg = RRenderGradient(width, height, &color1, &color2, style);
|
||||
} else if ((strcasecmp(type, "pixmap") == 0 ||
|
||||
strcasecmp(type, "spixmap") == 0 ||
|
||||
strcasecmp(type, "mpixmap") == 0 ||
|
||||
strcasecmp(type, "cpixmap") == 0 ||
|
||||
strcasecmp(type, "tpixmap") == 0) &&
|
||||
WMGetPropListItemCount(texpl) >= 2) {
|
||||
const char *path = WMGetFromPLString(WMGetFromPLArray(texpl, 1));
|
||||
if (path) {
|
||||
RImage *img = RLoadImage(rc, path, 0);
|
||||
if (img) {
|
||||
if (type[0] == 's' || type[0] == 'S') {
|
||||
/* Scale to fill */
|
||||
bg = RScaleImage(img, width, height);
|
||||
} else if (type[0] == 'c' || type[0] == 'C') {
|
||||
/* Center */
|
||||
bg = RCreateImage(width, height, 0);
|
||||
if (bg) {
|
||||
RColor black = {0, 0, 0, 255};
|
||||
RClearImage(bg, &black);
|
||||
int x = (width - img->width) / 2;
|
||||
int y = (height - img->height) / 2;
|
||||
RCombineArea(bg, img, 0, 0, img->width, img->height, x, y);
|
||||
}
|
||||
} else {
|
||||
/* Default: scale to fill */
|
||||
bg = RScaleImage(img, width, height);
|
||||
}
|
||||
RReleaseImage(img);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WMReleasePropList(texpl);
|
||||
|
||||
if (!bg)
|
||||
return;
|
||||
|
||||
/* Convert RImage to pixman_image and set as scene buffer */
|
||||
struct wl_bg_buffer *buf = calloc(1, sizeof(*buf));
|
||||
if (!buf) { RReleaseImage(bg); return; }
|
||||
|
||||
buf->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, NULL, width * 4);
|
||||
if (!buf->image) { free(buf); RReleaseImage(bg); return; }
|
||||
|
||||
/* Copy RImage (RGBA or RGB) into ARGB32 pixman buffer */
|
||||
uint32_t *dst = (uint32_t *)pixman_image_get_data(buf->image);
|
||||
unsigned char *src = bg->data;
|
||||
int npixels = width * height;
|
||||
if (bg->format == RRGBAFormat) {
|
||||
for (int i = 0; i < npixels; i++) {
|
||||
dst[i] = ((uint32_t)src[3] << 24) | ((uint32_t)src[0] << 16) |
|
||||
((uint32_t)src[1] << 8) | (uint32_t)src[2];
|
||||
src += 4;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < npixels; i++) {
|
||||
dst[i] = 0xFF000000 | ((uint32_t)src[0] << 16) |
|
||||
((uint32_t)src[1] << 8) | (uint32_t)src[2];
|
||||
src += 3;
|
||||
}
|
||||
}
|
||||
RReleaseImage(bg);
|
||||
|
||||
wlr_buffer_init(&buf->base, &bg_buffer_impl, width, height);
|
||||
|
||||
/* Hide the solid-color rect */
|
||||
if (wl_state.bg_rect)
|
||||
wlr_scene_node_set_enabled(&wl_state.bg_rect->node, false);
|
||||
|
||||
/* Replace or create the background scene buffer */
|
||||
if (wl_state.bg_image) {
|
||||
wlr_scene_buffer_set_buffer(wl_state.bg_image, &buf->base);
|
||||
} else {
|
||||
wl_state.bg_image = wlr_scene_buffer_create(
|
||||
wl_state.level_trees[WL_LAYER_BACKGROUND], &buf->base);
|
||||
}
|
||||
|
||||
/* Release old buffer, keep new one */
|
||||
if (wl_state.bg_wlr_buf)
|
||||
wlr_buffer_drop(wl_state.bg_wlr_buf);
|
||||
wl_state.bg_wlr_buf = &buf->base;
|
||||
}
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void
|
||||
|
|
@ -9321,6 +9496,7 @@ const WMBackend wl_backend = {
|
|||
.screen_capture_area = wl_screen_capture_area,
|
||||
.screen_capture_window = wl_screen_capture_window,
|
||||
.refresh_desktop = wl_refresh_desktop,
|
||||
.set_desktop_background = wl_set_desktop_background,
|
||||
|
||||
.monitors_query = wl_monitors_query,
|
||||
.monitors_select_events = wl_monitors_select_events,
|
||||
|
|
|
|||
|
|
@ -4574,6 +4574,7 @@ const WMBackend x11_backend = {
|
|||
.window_get_rect = x11_window_get_rect,
|
||||
.client_snapshot = x11_client_snapshot,
|
||||
.refresh_desktop = x11_refresh_desktop,
|
||||
.set_desktop_background = NULL, /* X11: wmsetbg handles this */
|
||||
.window_select_events = x11_window_select_events,
|
||||
.window_set_border = x11_window_set_border,
|
||||
.window_set_border_width = x11_window_set_border_width,
|
||||
|
|
|
|||
|
|
@ -2961,6 +2961,15 @@ static int setWorkspaceBack(WScreen * scr, WDefaultEntry * entry, void *tdata, v
|
|||
(void) entry;
|
||||
(void) bar;
|
||||
|
||||
/* If the backend can render backgrounds directly (Wayland), use it */
|
||||
if (wm_backend->set_desktop_background && WMGetPropListItemCount(value) > 0) {
|
||||
char *str = WMGetPropListDescription(value, False);
|
||||
if (str) {
|
||||
wm_backend->set_desktop_background(scr, str);
|
||||
wfree(str);
|
||||
}
|
||||
}
|
||||
|
||||
if (scr->flags.backimage_helper_launched) {
|
||||
char *str;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue