fix background image rendering on Wayland: defer bg until output ready, disable wmsetbg helper, add pixmap path search, fix output destroy crash, update background.menu to use wdwrite

This commit is contained in:
Window Maker 2026-06-04 09:05:22 +02:00
parent 550d7828a8
commit 8c65d3fa88
5 changed files with 86 additions and 9 deletions

View File

@ -23,10 +23,10 @@
"Wine Shades" WS_BACK '(vgradient, "#600040", "#180010")'
"Gradient" END
"Images" MENU
"Tiled" OPEN_MENU BACKGROUNDS_DIR USER_BACKGROUNDS_DIR WITH wmsetbg -u -t
"Scaled" OPEN_MENU BACKGROUNDS_DIR USER_BACKGROUNDS_DIR WITH wmsetbg -u -s
"Centered" OPEN_MENU BACKGROUNDS_DIR USER_BACKGROUNDS_DIR WITH wmsetbg -u -e
"Maximized" OPEN_MENU BACKGROUNDS_DIR USER_BACKGROUNDS_DIR WITH wmsetbg -u -a
"Filled" OPEN_MENU BACKGROUNDS_DIR USER_BACKGROUNDS_DIR WITH wmsetbg -u -f
"Tiled" OPEN_MENU BACKGROUNDS_DIR USER_BACKGROUNDS_DIR WITH sh -c 'wdwrite WindowMaker WorkspaceBack "(tpixmap, $1, black)"' placeholder
"Scaled" OPEN_MENU BACKGROUNDS_DIR USER_BACKGROUNDS_DIR WITH sh -c 'wdwrite WindowMaker WorkspaceBack "(spixmap, $1, black)"' placeholder
"Centered" OPEN_MENU BACKGROUNDS_DIR USER_BACKGROUNDS_DIR WITH sh -c 'wdwrite WindowMaker WorkspaceBack "(cpixmap, $1, black)"' placeholder
"Maximized" OPEN_MENU BACKGROUNDS_DIR USER_BACKGROUNDS_DIR WITH sh -c 'wdwrite WindowMaker WorkspaceBack "(mpixmap, $1, black)"' placeholder
"Filled" OPEN_MENU BACKGROUNDS_DIR USER_BACKGROUNDS_DIR WITH sh -c 'wdwrite WindowMaker WorkspaceBack "(fpixmap, $1, black)"' placeholder
"Images" END
"Background" END

View File

@ -21,6 +21,10 @@
#include <time.h>
#include <pthread.h>
/* debug helper for __func__ */
#define DBG() wwarning("DEBUG %s:%d: %s", __FILE__, __LINE__, __func__)
#define DBG1(fmt, ...) wwarning("DEBUG %s:%d: " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/headless.h>
@ -1828,6 +1832,11 @@ wl_screen_alloc_gcs(WScreen *scr)
void
wl_screen_create_internals(WScreen *scr)
{
wl_state.scr = scr;
{
FILE *fp = fopen("/tmp/wl_backend_trace.log", "a");
if (fp) { fprintf(fp, "wl_screen_create_internals: scr=%p\n", (void*)scr); fclose(fp); }
}
/* Create a real off-screen InputOnly X window that absorbs keyboard focus
* when no application window holds it. This mirrors x11_screen_create_internals
* exactly: without a mapped, viewable focus-sink window, any XSetInputFocus
@ -2021,18 +2030,30 @@ static int parse_color_to_rcolor(WScreen *scr, const char *name, RColor *out)
void wl_set_desktop_background(WScreen *scr, const char *texture_desc)
{
if (!scr || !texture_desc || !wl_state.output)
DBG1("texture_desc=%s output=%p", texture_desc, wl_state.output);
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);
DBG1("output not ready, cached pending_background=%s", texture_desc);
return;
}
int width = wl_state.output->width;
int height = wl_state.output->height;
/* Parse the texture proplist */
DBG();
WMPropList *texpl = WMCreatePropListFromDescription(texture_desc);
if (!texpl)
if (!texpl) {
DBG1("WMCreatePropListFromDescription returned NULL");
return;
}
DBG();
/* Render the texture to an RImage using wrlib */
RContext *rc = scr->rcontext;
RImage *bg = NULL;
@ -2041,6 +2062,7 @@ static int parse_color_to_rcolor(WScreen *scr, const char *name, RColor *out)
WMPropList *type_pl = WMGetFromPLArray(texpl, 0);
const char *type = type_pl ? WMGetFromPLString(type_pl) : "";
DBG1("type=%s", type);
if (strcasecmp(type, "solid") == 0 && WMGetPropListItemCount(texpl) >= 2) {
const char *colorstr = WMGetFromPLString(WMGetFromPLArray(texpl, 1));
RColor color = {0x50, 0x50, 0x75, 0xFF};
@ -2107,8 +2129,25 @@ static int parse_color_to_rcolor(WScreen *scr, const char *name, RColor *out)
if (cstr) parse_color_to_rcolor(scr, cstr, &bgcolor);
}
if (path) {
DBG1("loading image: %s", path);
/* Try the path as-is first, then search pixmap_path */
RImage *img = RLoadImage(rc, path, 0);
if (img) {
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);
DBG1("trying: %s", _full);
img = RLoadImage(rc, _full, 0);
if (img) break;
}
}
if (!img) {
DBG1("RLoadImage failed: %s", RMessageForError(RErrorCode));
} else {
DBG1("image loaded: %dx%d fmt=%d", img->width, img->height, img->format);
/* Combine with background color if image has alpha */
if (img->format == RRGBAFormat)
RCombineImageWithColor(img, &bgcolor);
@ -2199,10 +2238,13 @@ static int parse_color_to_rcolor(WScreen *scr, const char *name, RColor *out)
WMReleasePropList(texpl);
if (!bg) {
DBG1("bg is NULL after processing");
return;
}
DBG1("bg created: %dx%d fmt=%d", bg->width, bg->height, bg->format);
DBG1("converting RImage to pixman buffer (width=%d height=%d)", width, height);
/* Convert RImage to pixman_image and set as scene buffer */
struct wl_bg_buffer *buf = calloc(1, sizeof(*buf));
if (!buf) { RReleaseImage(bg); return; }
@ -2235,13 +2277,16 @@ static int parse_color_to_rcolor(WScreen *scr, const char *name, RColor *out)
if (wl_state.bg_rect)
wlr_scene_node_set_enabled(&wl_state.bg_rect->node, false);
DBG1("bg_image=%p bg_rect=%p level_tree=%p", wl_state.bg_image, wl_state.bg_rect, wl_state.level_trees[WL_LAYER_BACKGROUND]);
/* 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);
DBG1("updated existing bg_image");
} else {
wl_state.bg_image = wlr_scene_buffer_create(
wl_state.level_trees[WL_LAYER_BACKGROUND], &buf->base);
DBG1("created new bg_image=%p", wl_state.bg_image);
}
/* Release old buffer, keep new one */

View File

@ -38,6 +38,16 @@ handle_output_frame(struct wl_listener *listener, void *data)
}
}
#include <wlr/util/log.h>
static void handle_output_destroy(struct wl_listener *listener, void *data)
{
(void)listener; (void)data;
wl_list_remove(&wl_state.output_frame_listener.link);
wl_state.scene_output = NULL;
wl_state.output = NULL;
}
void
handle_backend_new_output(struct wl_listener *listener, void *data)
{
@ -88,6 +98,8 @@ handle_backend_new_output(struct wl_listener *listener, void *data)
wl_state.scene_output = wlr_scene_output_create(wl_state.scene, output);
wl_state.output_frame_listener.notify = handle_output_frame;
wl_signal_add(&output->events.frame, &wl_state.output_frame_listener);
wl_state.output_destroy_listener.notify = handle_output_destroy;
wl_signal_add(&output->events.destroy, &wl_state.output_destroy_listener);
wwarning("wl_backend: scene output created for %s (%dx%d)",
output->name, output->width, output->height);
@ -99,6 +111,14 @@ handle_backend_new_output(struct wl_listener *listener, void *data)
wwarning("wl_backend: root background created (%dx%d)",
(int)output->width, (int)output->height);
}
/* Apply any background that was cached before output was ready */
if (wl_state.pending_background && wl_state.scr) {
wwarning("wl_backend: applying pending background");
wl_set_desktop_background(wl_state.scr, wl_state.pending_background);
wfree(wl_state.pending_background);
wl_state.pending_background = NULL;
}
}
wwarning("wl_backend: new output %s (%dx%d)",

View File

@ -213,6 +213,7 @@ struct wl_compositor_state {
struct wlr_scene_output *scene_output;
struct wl_listener backend_new_output_listener;
struct wl_listener output_frame_listener;
struct wl_listener output_destroy_listener;
char socket_name[64]; /* Phase 34: e.g. "wayland-0" */
int started; /* non-zero after successful display_open */
/* Self-pipe for async-signal-safe shutdown: signal handler writes to [1],
@ -350,6 +351,7 @@ struct wl_compositor_state {
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 */
char *pending_background; /* cached texture desc, applied when output is ready */
/* Phase 81: full-screen overlay surface for XOR animation drawing */
struct wm_shm_surface *overlay_surf; /* NULL until first draw */
@ -364,6 +366,7 @@ struct wl_compositor_state {
/* Deferred dock creation: set in screen_create_internals, consumed in
* wl_event_loop_run() after the startup pump thread stops (to avoid racing
* with the pump thread's frame-timer callback on the scene graph). */
WScreen *scr; /* first screen, set during screen_create_internals */
WScreen *dock_pending_scr;
/* Button icon RImages (parsed from XPM on first use) */
RImage *btn_close_img;

View File

@ -2926,6 +2926,12 @@ static int setWorkspaceSpecificBack(WScreen * scr, WDefaultEntry * entry, void *
(void) entry;
(void) bar;
/* If the backend handles backgrounds directly (Wayland), skip helper */
if (wm_backend->set_desktop_background) {
WMReleasePropList(value);
return 0;
}
if (scr->flags.backimage_helper_launched) {
if (WMGetPropListItemCount(value) == 0) {
SendHelperMessage(scr, 'C', 0, NULL);
@ -2972,6 +2978,7 @@ static int setWorkspaceBack(WScreen * scr, WDefaultEntry * entry, void *tdata, v
(void) entry;
(void) bar;
fprintf(stderr, "TRACE setWorkspaceBack: backend=%p valcount=%d\n", (void*)wm_backend->set_desktop_background, WMGetPropListItemCount(value));
/* If the backend can render backgrounds directly (Wayland), use it */
if (wm_backend->set_desktop_background && WMGetPropListItemCount(value) > 0) {
char *str = WMGetPropListDescription(value, False);
@ -2979,6 +2986,8 @@ static int setWorkspaceBack(WScreen * scr, WDefaultEntry * entry, void *tdata, v
wm_backend->set_desktop_background(scr, str);
wfree(str);
}
WMReleasePropList(value);
return 0;
}
if (scr->flags.backimage_helper_launched) {