step 2: extract wl_background.c from wl_backend.c (desktop background rendering, 304 lines)
This commit is contained in:
parent
8cca118517
commit
9178f8a903
|
|
@ -165,6 +165,7 @@ if USE_WAYLAND_BACKEND
|
|||
wmaker_SOURCES += \
|
||||
backend/wayland/wl_backend.h \
|
||||
backend/wayland/wl_backend.c \
|
||||
backend/wayland/wl_background.c \
|
||||
backend/wayland/wl_context.c \
|
||||
backend/wayland/wl_event.c \
|
||||
backend/wayland/wl_stacking.c \
|
||||
|
|
|
|||
|
|
@ -1799,310 +1799,6 @@ wl_screen_load_tech_font(WScreen *scr)
|
|||
RImage *wl_screen_capture_window(WScreen *scr, WWindow *wwin) { (void)scr; (void)wwin; return NULL; }
|
||||
void wl_refresh_desktop(WScreen *scr) { (void)scr; }
|
||||
|
||||
/* --- 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 int parse_color_to_rcolor(WScreen *scr, const char *name, RColor *out)
|
||||
{
|
||||
WMColorRGB wmc;
|
||||
if (!name || !wGetColor(scr, name, &wmc))
|
||||
return 0;
|
||||
out->red = wmc.red >> 8;
|
||||
out->green = wmc.green >> 8;
|
||||
out->blue = wmc.blue >> 8;
|
||||
out->alpha = 255;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void wl_set_desktop_background(WScreen *scr, const char *texture_desc)
|
||||
{
|
||||
if (!scr || !texture_desc)
|
||||
return;
|
||||
if (!wl_state.output) {
|
||||
/* Output not ready yet — cache the background for later */
|
||||
wfree(wl_state.pending_background);
|
||||
wl_state.pending_background = wstrdup(texture_desc);
|
||||
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};
|
||||
if (colorstr)
|
||||
parse_color_to_rcolor(scr, colorstr, &color);
|
||||
/* For solid colors, just update the scene rect directly */
|
||||
if (wl_state.bg_rect) {
|
||||
float c[4] = { color.red/255.0f, color.green/255.0f, color.blue/255.0f, 1.0f };
|
||||
wlr_scene_rect_set_color(wl_state.bg_rect, c);
|
||||
wlr_scene_node_set_enabled(&wl_state.bg_rect->node, true);
|
||||
if (wl_state.bg_image)
|
||||
wlr_scene_node_set_enabled(&wl_state.bg_image->node, false);
|
||||
}
|
||||
if (wl_state.output)
|
||||
wlr_output_schedule_frame(wl_state.output);
|
||||
WMReleasePropList(texpl);
|
||||
return;
|
||||
} 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};
|
||||
if (c1str) parse_color_to_rcolor(scr, c1str, &color1);
|
||||
if (c2str) parse_color_to_rcolor(scr, c2str, &color2);
|
||||
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, "mvgradient") == 0 ||
|
||||
strcasecmp(type, "mhgradient") == 0 ||
|
||||
strcasecmp(type, "mdgradient") == 0) &&
|
||||
WMGetPropListItemCount(texpl) >= 3) {
|
||||
int ncolors = WMGetPropListItemCount(texpl) - 1;
|
||||
RColor **colors = wmalloc((ncolors + 1) * sizeof(RColor *));
|
||||
RColor *cbuf = wmalloc(ncolors * sizeof(RColor));
|
||||
int ci;
|
||||
for (ci = 0; ci < ncolors; ci++) {
|
||||
const char *cs = WMGetFromPLString(WMGetFromPLArray(texpl, ci + 1));
|
||||
cbuf[ci] = (RColor){0, 0, 0, 255};
|
||||
if (cs) parse_color_to_rcolor(scr, cs, &cbuf[ci]);
|
||||
colors[ci] = &cbuf[ci];
|
||||
}
|
||||
colors[ncolors] = NULL;
|
||||
int style = (type[1] == 'v' || type[1] == 'V') ? RVerticalGradient :
|
||||
(type[1] == 'h' || type[1] == 'H') ? RHorizontalGradient :
|
||||
RDiagonalGradient;
|
||||
bg = RRenderMultiGradient(width, height, colors, style);
|
||||
wfree(cbuf);
|
||||
wfree(colors);
|
||||
} else if ((strcasecmp(type, "pixmap") == 0 ||
|
||||
strcasecmp(type, "spixmap") == 0 ||
|
||||
strcasecmp(type, "mpixmap") == 0 ||
|
||||
strcasecmp(type, "cpixmap") == 0 ||
|
||||
strcasecmp(type, "fpixmap") == 0 ||
|
||||
strcasecmp(type, "tpixmap") == 0) &&
|
||||
WMGetPropListItemCount(texpl) >= 2) {
|
||||
const char *path = WMGetFromPLString(WMGetFromPLArray(texpl, 1));
|
||||
/* Background color (3rd argument, optional) */
|
||||
RColor bgcolor = {0, 0, 0, 255};
|
||||
if (WMGetPropListItemCount(texpl) >= 3) {
|
||||
const char *cstr = WMGetFromPLString(WMGetFromPLArray(texpl, 2));
|
||||
if (cstr) parse_color_to_rcolor(scr, cstr, &bgcolor);
|
||||
}
|
||||
if (path) {
|
||||
/* Try the path as-is first, then search pixmap_path */
|
||||
RImage *img = RLoadImage(rc, path, 0);
|
||||
if (!img && path[0] != '/') {
|
||||
/* Relative path — search pixmap_path */
|
||||
char *search_paths[] = { wPreferences.pixmap_path, PKGDATADIR "/Backgrounds", NULL };
|
||||
for (int _pi = 0; search_paths[_pi]; _pi++) {
|
||||
char _full[PATH_MAX];
|
||||
snprintf(_full, sizeof(_full), "%s/%s", search_paths[_pi], path);
|
||||
img = RLoadImage(rc, _full, 0);
|
||||
if (img) break;
|
||||
}
|
||||
}
|
||||
if (!img) {
|
||||
} else {
|
||||
|
||||
/* Combine with background color if image has alpha */
|
||||
if (img->format == RRGBAFormat)
|
||||
RCombineImageWithColor(img, &bgcolor);
|
||||
|
||||
char mode = (type[0] >= 'a' && type[0] <= 'z') ? type[0] - 32 : type[0];
|
||||
if (mode == 'S') {
|
||||
/* Scaled: stretch to exact screen size */
|
||||
bg = RSmoothScaleImage(img, width, height);
|
||||
} else if (mode == 'F') {
|
||||
/* Filled: scale preserving aspect, fill screen (crop) */
|
||||
int w, h;
|
||||
if (img->width * height > img->height * width) {
|
||||
w = (height * img->width) / img->height;
|
||||
h = height;
|
||||
} else {
|
||||
w = width;
|
||||
h = (width * img->height) / img->width;
|
||||
}
|
||||
RImage *scaled = RSmoothScaleImage(img, w, h);
|
||||
if (scaled) {
|
||||
bg = RCreateImage(width, height, 0);
|
||||
if (bg) {
|
||||
RClearImage(bg, &bgcolor);
|
||||
int sx = (scaled->width - width) / 2;
|
||||
int sy = (scaled->height - height) / 2;
|
||||
RCopyArea(bg, scaled, sx, sy, width, height, 0, 0);
|
||||
}
|
||||
RReleaseImage(scaled);
|
||||
}
|
||||
} else if (mode == 'M') {
|
||||
/* Maximized: scale preserving aspect, fit inside (letterbox) */
|
||||
int w, h;
|
||||
if (img->width * height > img->height * width) {
|
||||
w = width;
|
||||
h = (width * img->height) / img->width;
|
||||
} else {
|
||||
w = (height * img->width) / img->height;
|
||||
h = height;
|
||||
}
|
||||
RImage *scaled = RSmoothScaleImage(img, w, h);
|
||||
if (scaled) {
|
||||
bg = RCreateImage(width, height, 0);
|
||||
if (bg) {
|
||||
RClearImage(bg, &bgcolor);
|
||||
int dx = (width - scaled->width) / 2;
|
||||
int dy = (height - scaled->height) / 2;
|
||||
RCombineArea(bg, scaled, 0, 0, scaled->width, scaled->height, dx, dy);
|
||||
}
|
||||
RReleaseImage(scaled);
|
||||
}
|
||||
} else if (mode == 'C') {
|
||||
/* Centered: place at center, no scaling */
|
||||
bg = RCreateImage(width, height, 0);
|
||||
if (bg) {
|
||||
RClearImage(bg, &bgcolor);
|
||||
int dx = (width - img->width) / 2;
|
||||
int dy = (height - img->height) / 2;
|
||||
/* Handle images larger than screen */
|
||||
int sx = 0, sy = 0, cw = img->width, ch = img->height;
|
||||
if (dx < 0) { sx = -dx; dx = 0; cw = width; }
|
||||
if (dy < 0) { sy = -dy; dy = 0; ch = height; }
|
||||
RCombineArea(bg, img, sx, sy, cw, ch, dx, dy);
|
||||
}
|
||||
} else if (mode == 'T') {
|
||||
/* Tiled: repeat the image */
|
||||
bg = RCreateImage(width, height, 0);
|
||||
if (bg) {
|
||||
for (int ty = 0; ty < height; ty += img->height) {
|
||||
for (int tx = 0; tx < width; tx += img->width) {
|
||||
int cw = img->width;
|
||||
int ch = img->height;
|
||||
if (tx + cw > width) cw = width - tx;
|
||||
if (ty + ch > height) ch = height - ty;
|
||||
RCombineArea(bg, img, 0, 0, cw, ch, tx, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Default (plain "pixmap"): scale to fill */
|
||||
bg = RSmoothScaleImage(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);
|
||||
wlr_scene_node_set_enabled(&wl_state.bg_image->node, true);
|
||||
} 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;
|
||||
|
||||
/* Schedule a frame to display the new background immediately */
|
||||
if (wl_state.output)
|
||||
wlr_output_schedule_frame(wl_state.output);
|
||||
}
|
||||
|
||||
void
|
||||
wl_monitors_query(WScreen *scr, WHeadGeometry **heads_out, int *count_out)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* wl_background.c -- Desktop background rendering (wlr_buffer + RImage backend)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_scene.h>
|
||||
#include <wlr/types/wlr_buffer.h>
|
||||
#include <wlr/interfaces/wlr_buffer.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <pixman.h>
|
||||
#include "../../screen.h"
|
||||
#include "../../resources.h"
|
||||
#include "wl_types.h"
|
||||
|
||||
/* --- 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 int parse_color_to_rcolor(WScreen *scr, const char *name, RColor *out)
|
||||
{
|
||||
WMColorRGB wmc;
|
||||
if (!name || !wGetColor(scr, name, &wmc))
|
||||
return 0;
|
||||
out->red = wmc.red >> 8;
|
||||
out->green = wmc.green >> 8;
|
||||
out->blue = wmc.blue >> 8;
|
||||
out->alpha = 255;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void wl_set_desktop_background(WScreen *scr, const char *texture_desc)
|
||||
{
|
||||
if (!scr || !texture_desc)
|
||||
return;
|
||||
if (!wl_state.output) {
|
||||
/* Output not ready yet — cache the background for later */
|
||||
wfree(wl_state.pending_background);
|
||||
wl_state.pending_background = wstrdup(texture_desc);
|
||||
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};
|
||||
if (colorstr)
|
||||
parse_color_to_rcolor(scr, colorstr, &color);
|
||||
/* For solid colors, just update the scene rect directly */
|
||||
if (wl_state.bg_rect) {
|
||||
float c[4] = { color.red/255.0f, color.green/255.0f, color.blue/255.0f, 1.0f };
|
||||
wlr_scene_rect_set_color(wl_state.bg_rect, c);
|
||||
wlr_scene_node_set_enabled(&wl_state.bg_rect->node, true);
|
||||
if (wl_state.bg_image)
|
||||
wlr_scene_node_set_enabled(&wl_state.bg_image->node, false);
|
||||
}
|
||||
if (wl_state.output)
|
||||
wlr_output_schedule_frame(wl_state.output);
|
||||
WMReleasePropList(texpl);
|
||||
return;
|
||||
} 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};
|
||||
if (c1str) parse_color_to_rcolor(scr, c1str, &color1);
|
||||
if (c2str) parse_color_to_rcolor(scr, c2str, &color2);
|
||||
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, "mvgradient") == 0 ||
|
||||
strcasecmp(type, "mhgradient") == 0 ||
|
||||
strcasecmp(type, "mdgradient") == 0) &&
|
||||
WMGetPropListItemCount(texpl) >= 3) {
|
||||
int ncolors = WMGetPropListItemCount(texpl) - 1;
|
||||
RColor **colors = wmalloc((ncolors + 1) * sizeof(RColor *));
|
||||
RColor *cbuf = wmalloc(ncolors * sizeof(RColor));
|
||||
int ci;
|
||||
for (ci = 0; ci < ncolors; ci++) {
|
||||
const char *cs = WMGetFromPLString(WMGetFromPLArray(texpl, ci + 1));
|
||||
cbuf[ci] = (RColor){0, 0, 0, 255};
|
||||
if (cs) parse_color_to_rcolor(scr, cs, &cbuf[ci]);
|
||||
colors[ci] = &cbuf[ci];
|
||||
}
|
||||
colors[ncolors] = NULL;
|
||||
int style = (type[1] == 'v' || type[1] == 'V') ? RVerticalGradient :
|
||||
(type[1] == 'h' || type[1] == 'H') ? RHorizontalGradient :
|
||||
RDiagonalGradient;
|
||||
bg = RRenderMultiGradient(width, height, colors, style);
|
||||
wfree(cbuf);
|
||||
wfree(colors);
|
||||
} else if ((strcasecmp(type, "pixmap") == 0 ||
|
||||
strcasecmp(type, "spixmap") == 0 ||
|
||||
strcasecmp(type, "mpixmap") == 0 ||
|
||||
strcasecmp(type, "cpixmap") == 0 ||
|
||||
strcasecmp(type, "fpixmap") == 0 ||
|
||||
strcasecmp(type, "tpixmap") == 0) &&
|
||||
WMGetPropListItemCount(texpl) >= 2) {
|
||||
const char *path = WMGetFromPLString(WMGetFromPLArray(texpl, 1));
|
||||
/* Background color (3rd argument, optional) */
|
||||
RColor bgcolor = {0, 0, 0, 255};
|
||||
if (WMGetPropListItemCount(texpl) >= 3) {
|
||||
const char *cstr = WMGetFromPLString(WMGetFromPLArray(texpl, 2));
|
||||
if (cstr) parse_color_to_rcolor(scr, cstr, &bgcolor);
|
||||
}
|
||||
if (path) {
|
||||
/* Try the path as-is first, then search pixmap_path */
|
||||
RImage *img = RLoadImage(rc, path, 0);
|
||||
if (!img && path[0] != '/') {
|
||||
/* Relative path — search pixmap_path */
|
||||
char *search_paths[] = { wPreferences.pixmap_path, PKGDATADIR "/Backgrounds", NULL };
|
||||
for (int _pi = 0; search_paths[_pi]; _pi++) {
|
||||
char _full[PATH_MAX];
|
||||
snprintf(_full, sizeof(_full), "%s/%s", search_paths[_pi], path);
|
||||
img = RLoadImage(rc, _full, 0);
|
||||
if (img) break;
|
||||
}
|
||||
}
|
||||
if (!img) {
|
||||
} else {
|
||||
|
||||
/* Combine with background color if image has alpha */
|
||||
if (img->format == RRGBAFormat)
|
||||
RCombineImageWithColor(img, &bgcolor);
|
||||
|
||||
char mode = (type[0] >= 'a' && type[0] <= 'z') ? type[0] - 32 : type[0];
|
||||
if (mode == 'S') {
|
||||
/* Scaled: stretch to exact screen size */
|
||||
bg = RSmoothScaleImage(img, width, height);
|
||||
} else if (mode == 'F') {
|
||||
/* Filled: scale preserving aspect, fill screen (crop) */
|
||||
int w, h;
|
||||
if (img->width * height > img->height * width) {
|
||||
w = (height * img->width) / img->height;
|
||||
h = height;
|
||||
} else {
|
||||
w = width;
|
||||
h = (width * img->height) / img->width;
|
||||
}
|
||||
RImage *scaled = RSmoothScaleImage(img, w, h);
|
||||
if (scaled) {
|
||||
bg = RCreateImage(width, height, 0);
|
||||
if (bg) {
|
||||
RClearImage(bg, &bgcolor);
|
||||
int sx = (scaled->width - width) / 2;
|
||||
int sy = (scaled->height - height) / 2;
|
||||
RCopyArea(bg, scaled, sx, sy, width, height, 0, 0);
|
||||
}
|
||||
RReleaseImage(scaled);
|
||||
}
|
||||
} else if (mode == 'M') {
|
||||
/* Maximized: scale preserving aspect, fit inside (letterbox) */
|
||||
int w, h;
|
||||
if (img->width * height > img->height * width) {
|
||||
w = width;
|
||||
h = (width * img->height) / img->width;
|
||||
} else {
|
||||
w = (height * img->width) / img->height;
|
||||
h = height;
|
||||
}
|
||||
RImage *scaled = RSmoothScaleImage(img, w, h);
|
||||
if (scaled) {
|
||||
bg = RCreateImage(width, height, 0);
|
||||
if (bg) {
|
||||
RClearImage(bg, &bgcolor);
|
||||
int dx = (width - scaled->width) / 2;
|
||||
int dy = (height - scaled->height) / 2;
|
||||
RCombineArea(bg, scaled, 0, 0, scaled->width, scaled->height, dx, dy);
|
||||
}
|
||||
RReleaseImage(scaled);
|
||||
}
|
||||
} else if (mode == 'C') {
|
||||
/* Centered: place at center, no scaling */
|
||||
bg = RCreateImage(width, height, 0);
|
||||
if (bg) {
|
||||
RClearImage(bg, &bgcolor);
|
||||
int dx = (width - img->width) / 2;
|
||||
int dy = (height - img->height) / 2;
|
||||
/* Handle images larger than screen */
|
||||
int sx = 0, sy = 0, cw = img->width, ch = img->height;
|
||||
if (dx < 0) { sx = -dx; dx = 0; cw = width; }
|
||||
if (dy < 0) { sy = -dy; dy = 0; ch = height; }
|
||||
RCombineArea(bg, img, sx, sy, cw, ch, dx, dy);
|
||||
}
|
||||
} else if (mode == 'T') {
|
||||
/* Tiled: repeat the image */
|
||||
bg = RCreateImage(width, height, 0);
|
||||
if (bg) {
|
||||
for (int ty = 0; ty < height; ty += img->height) {
|
||||
for (int tx = 0; tx < width; tx += img->width) {
|
||||
int cw = img->width;
|
||||
int ch = img->height;
|
||||
if (tx + cw > width) cw = width - tx;
|
||||
if (ty + ch > height) ch = height - ty;
|
||||
RCombineArea(bg, img, 0, 0, cw, ch, tx, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Default (plain "pixmap"): scale to fill */
|
||||
bg = RSmoothScaleImage(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);
|
||||
wlr_scene_node_set_enabled(&wl_state.bg_image->node, true);
|
||||
} 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;
|
||||
|
||||
/* Schedule a frame to display the new background immediately */
|
||||
if (wl_state.output)
|
||||
wlr_output_schedule_frame(wl_state.output);
|
||||
}
|
||||
Loading…
Reference in New Issue