diff --git a/src/backend/backend.h b/src/backend/backend.h index c96684a..8b7e87b 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -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) */ /* ------------------------------------------------------------------ */ diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 005481e..e450508 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -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, diff --git a/src/backend/x11/x11_backend.c b/src/backend/x11/x11_backend.c index c743fd2..23f3e18 100644 --- a/src/backend/x11/x11_backend.c +++ b/src/backend/x11/x11_backend.c @@ -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, diff --git a/src/defaults.c b/src/defaults.c index f4d53e0..033c3a4 100644 --- a/src/defaults.c +++ b/src/defaults.c @@ -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;