From 499d4fac7e6066ae28eb5065fb5e41e2a20508a9 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Wed, 13 May 2026 07:15:57 +0200 Subject: [PATCH] Wayland backend: native frame rendering via pixman + wlr_scene_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architecture: frames are pixman images committed to wlr_scene_buffer nodes — no XWayland dependency for WM UI rendering. Key changes: - Add wl_frame_buf struct (pixman_image_t + wlr_buffer + scene_buffer) - Implement wlr_buffer_impl for frame buffers (begin_data_ptr_access) - frame_create_toplevel: allocates pixman buffer + scene node - frame_create_child: returns synthetic ID (sub-regions of parent) - frame_configure: repositions scene node, reallocates on resize - frame_destroy: destroys scene node and drops buffer - frame_paint: renders titlebar texture (via wTextureRenderImage), title text (via WMDrawString on pixman WMDrawable), and resizebar into the pixman buffer, then commits to scene graph - Add frame_paint vtable slot to WMBackend (backend.h) - wFrameWindowPaint: calls wm_backend->frame_paint first; falls through to Xlib code only if backend returns 0 - window_map/unmap/move: handle frame buffers via scene node ops - stacking_raise/lower: handle frame buffers - window_get_rect: return frame buffer geometry - Remove all XWayland calls from frame/window lifecycle The OR (override_redirect) surface handling remains for XWayland client windows that use override_redirect (tooltips, menus from X11 apps). The WM's own UI is now pure Wayland. --- src/backend/backend.h | 11 + src/backend/wayland/wl_backend.c | 431 +++++++++++++++++++++++-------- src/backend/x11/x11_backend.c | 1 + src/framewin.c | 4 + 4 files changed, 343 insertions(+), 104 deletions(-) diff --git a/src/backend/backend.h b/src/backend/backend.h index 4bd528e..28fca27 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -37,6 +37,7 @@ typedef struct _WScreen WScreen; typedef struct WWindow WWindow; typedef struct GNUstepWMAttributes GNUstepWMAttributes; struct WObjDescriptor; +struct WFrameWindow; /* * WMBackend -- vtable for all display-system operations. @@ -168,6 +169,16 @@ typedef struct WMBackend { */ void (*frame_configure)(WNativeWindow win, int x, int y, int width, int height); + /* + * Render the complete frame decoration (titlebar, buttons, resizebar) + * into the frame's backing store and make it visible. + * X11: uses Xlib drawing on the frame sub-windows (the traditional path). + * Wayland: renders into a pixman buffer and commits to the scene graph. + * Returns 1 if the backend handled painting, 0 if the caller should + * fall through to legacy Xlib code (X11 backend returns 0). + */ + int (*frame_paint)(struct WFrameWindow *fwin); + /* ------------------------------------------------------------------ */ /* Window context table (Phase 2) */ /* */ diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 732932e..fc305d1 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -182,6 +182,9 @@ #include "../../WindowMaker.h" #include "../../screen.h" #include "../../window.h" +#include "../../framewin.h" /* WFrameWindow for frame_paint */ +#include "../../texture.h" /* WTexture, wTextureRenderImage */ +#include "../../misc.h" /* ShrinkString */ #include "../../actions.h" /* Phase 16: wMaximizeWindow, wIconifyWindow, etc. */ #include "../../stacking.h" /* Phase 24: wRaiseFrame for click-to-raise */ #include "../../xinerama.h" /* Phase 16: wGetHeadForWindow */ @@ -265,6 +268,21 @@ struct wl_or_view { struct wl_list link; /* wl_state.or_list */ }; +/* + * wl_frame_buf — a pixman-backed scene buffer for WM frame windows. + * Each frame (titlebar + buttons + resizebar) is rendered into a single + * ARGB pixman image and displayed via a wlr_scene_buffer node. + */ +struct wl_frame_buf { + struct wlr_buffer base; /* wlr_buffer (must be first) */ + pixman_image_t *image; /* ARGB32 pixel data */ + struct wlr_scene_buffer *scene_buf; /* scene graph node */ + WNativeWindow id; /* synthetic ID (>= 0x10000000) */ + int x, y; /* position in output coords */ + int width, height; + struct wl_list link; /* wl_state.frame_list */ +}; + /* * Phase 36: wl_layer_view — one entry per mapped zwlr_layer_shell_v1 surface. * Tracks the wlroots layer surface, its scene node, and lifecycle listeners. @@ -315,6 +333,7 @@ struct wl_compositor_state { /* Phase 38: per-toplevel view list and context store */ struct wl_list toplevel_list; /* wl_toplevel_view.link */ struct wl_list or_list; /* wl_or_view.link */ + struct wl_list frame_list; /* wl_frame_buf.link */ struct wl_list layer_list; /* wl_layer_view.link */ struct wl_list ctx_lists[3]; /* one per WContextKey */ unsigned long next_synthetic_id; /* >= 0x10000000 */ @@ -2353,6 +2372,7 @@ wl_display_open(const char *name) /* Phase 38: initialise per-toplevel tracking and context store. */ wl_list_init(&wl_state.toplevel_list); wl_list_init(&wl_state.or_list); + wl_list_init(&wl_state.frame_list); wl_list_init(&wl_state.layer_list); { int ki; @@ -3429,6 +3449,50 @@ wl_screen_open(WScreen *scr) static void wl_screen_close(WScreen *scr) { (void)scr; } +/* ------------------------------------------------------------------ */ +/* Frame buffer (pixman + wlr_scene_buffer) */ +/* ------------------------------------------------------------------ */ + +static void frame_buf_destroy(struct wlr_buffer *wlr_buf) +{ + struct wl_frame_buf *fb = wl_container_of(wlr_buf, fb, base); + if (fb->image) + pixman_image_unref(fb->image); + wfree(fb); +} + +static bool frame_buf_begin_data_ptr_access(struct wlr_buffer *wlr_buf, + uint32_t flags, void **data, uint32_t *format, size_t *stride) +{ + struct wl_frame_buf *fb = wl_container_of(wlr_buf, fb, base); + (void)flags; + *data = pixman_image_get_data(fb->image); + *format = DRM_FORMAT_ARGB8888; + *stride = (size_t)pixman_image_get_stride(fb->image); + return true; +} + +static void frame_buf_end_data_ptr_access(struct wlr_buffer *wlr_buf) +{ + (void)wlr_buf; +} + +static const struct wlr_buffer_impl frame_buf_impl = { + .destroy = frame_buf_destroy, + .begin_data_ptr_access = frame_buf_begin_data_ptr_access, + .end_data_ptr_access = frame_buf_end_data_ptr_access, +}; + +static struct wl_frame_buf * +frame_buf_find(WNativeWindow id) +{ + struct wl_frame_buf *fb; + wl_list_for_each(fb, &wl_state.frame_list, link) { + if (fb->id == id) return fb; + } + return NULL; +} + /* ------------------------------------------------------------------ */ /* Frame / window lifecycle */ /* ------------------------------------------------------------------ */ @@ -3441,82 +3505,239 @@ wl_frame_create_toplevel(WScreen *scr, unsigned long colormap, WMPixel border_pixel) { - if (!dpy) + (void)border_width; (void)depth; (void)visual; + (void)colormap; (void)border_pixel; (void)scr; + + if (width <= 0) width = 1; + if (height <= 0) height = 1; + + struct wl_frame_buf *fb = wmalloc(sizeof(*fb)); + memset(fb, 0, sizeof(*fb)); + + fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, + width, height, NULL, 0); + if (!fb->image) { + wfree(fb); return WNATIVE_NONE; - - int vmask; - XSetWindowAttributes attribs; - Window win; - - vmask = CWBorderPixel | CWCursor | CWEventMask | CWOverrideRedirect | CWColormap; - attribs.override_redirect = True; - attribs.cursor = wPreferences.cursor[WCUR_NORMAL]; - attribs.background_pixmap = None; - attribs.background_pixel = scr->black_pixel; - attribs.border_pixel = border_pixel; - attribs.event_mask = SubstructureRedirectMask | ButtonPressMask | - ButtonReleaseMask | ButtonMotionMask | - ExposureMask | EnterWindowMask | LeaveWindowMask; - attribs.colormap = (Colormap)colormap; - - if (wPreferences.use_saveunders) { - vmask |= CWSaveUnder; - attribs.save_under = True; } - win = XCreateWindow(dpy, (Window)scr->root_win, - x, y, width, height, border_width, - depth, CopyFromParent, (Visual *)visual, vmask, &attribs); - XClearWindow(dpy, win); - return (WNativeWindow)win; + wlr_buffer_init(&fb->base, &frame_buf_impl, width, height); + + fb->id = wl_state.next_synthetic_id++; + fb->x = x; + fb->y = y; + fb->width = width; + fb->height = height; + + /* Create scene buffer node. */ + if (wl_state.scene) { + fb->scene_buf = wlr_scene_buffer_create(&wl_state.scene->tree, + &fb->base); + if (fb->scene_buf) { + wlr_scene_node_set_position(&fb->scene_buf->node, x, y); + wlr_scene_node_set_enabled(&fb->scene_buf->node, false); + } + } + + wl_list_insert(&wl_state.frame_list, &fb->link); + return fb->id; } static WNativeWindow wl_frame_create_child(WNativeWindow parent_win, WScreen *scr, int x, int y, int width, int height) { - if (!dpy) - return WNATIVE_NONE; - - int vmask; - XSetWindowAttributes attribs; - Window win; - - vmask = CWBorderPixel | CWCursor | CWEventMask | CWColormap; - attribs.cursor = wPreferences.cursor[WCUR_NORMAL]; - attribs.background_pixmap = None; - attribs.background_pixel = scr->black_pixel; - attribs.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | - ButtonReleaseMask | ButtonMotionMask | - ExposureMask | EnterWindowMask | LeaveWindowMask; - attribs.colormap = scr->w_colormap; - - win = XCreateWindow(dpy, (Window)parent_win, - x, y, width, height, 0, - scr->w_depth, CopyFromParent, - scr->w_visual, vmask, &attribs); - return (WNativeWindow)win; + (void)parent_win; (void)scr; + (void)x; (void)y; (void)width; (void)height; + /* Child windows (titlebar, buttons, resizebar) are sub-regions of the + * parent frame buffer. Return a synthetic ID so context_save works, + * but no separate buffer is needed — frame_paint renders everything. */ + return wl_state.next_synthetic_id++; } static void wl_frame_destroy(WScreen *scr, WNativeWindow win) { (void)scr; - if (dpy && win != WNATIVE_NONE) - XDestroyWindow(dpy, (Window)win); + struct wl_frame_buf *fb = frame_buf_find(win); + if (!fb) return; + if (fb->scene_buf) + wlr_scene_node_destroy(&fb->scene_buf->node); + wl_list_remove(&fb->link); + wlr_buffer_drop(&fb->base); } static void wl_frame_configure(WNativeWindow win, int x, int y, int width, int height) { - if (!dpy || win == WNATIVE_NONE) - return; - XWindowChanges xwc; - xwc.x = x; - xwc.y = y; - xwc.width = width; - xwc.height = height; - XConfigureWindow(dpy, (Window)win, CWX | CWY | CWWidth | CWHeight, &xwc); + struct wl_frame_buf *fb = frame_buf_find(win); + if (!fb) return; + + fb->x = x; + fb->y = y; + + if (fb->scene_buf) + wlr_scene_node_set_position(&fb->scene_buf->node, x, y); + + /* Resize: reallocate the pixman image if dimensions changed. */ + if (width > 0 && height > 0 && (width != fb->width || height != fb->height)) { + pixman_image_unref(fb->image); + fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, + width, height, NULL, 0); + fb->width = width; + fb->height = height; + fb->base.width = width; + fb->base.height = height; + } +} + +/* + * wl_frame_paint — render the entire frame decoration into the pixman buffer. + * + * Uses wTextureRenderImage() (pure CPU) for titlebar/resizebar backgrounds, + * pixman for bevel lines, and the WINGs font backend for title text. + * Returns 1 (handled) so wFrameWindowPaint skips the Xlib code path. + */ +static int +wl_frame_paint(struct WFrameWindow *fwin) +{ + struct wl_frame_buf *fb = frame_buf_find(fwin->core->window); + if (!fb || !fb->image) + return 0; /* not our frame — shouldn't happen */ + + int state = fwin->flags.state; + int total_w = fb->width; + int total_h = fb->height; + + /* Clear the buffer to transparent black. */ + pixman_color_t clear = {0, 0, 0, 0xFFFF}; + pixman_image_fill_rectangles(PIXMAN_OP_SRC, fb->image, &clear, + 1, &(pixman_rectangle16_t){0, 0, total_w, total_h}); + + /* --- Titlebar --- */ + if (fwin->titlebar && fwin->top_width > 0) { + int tb_w = total_w; + int tb_h = fwin->top_width; + WTexture *tex = fwin->title_texture[state]; + + /* Render texture to RImage. */ + RImage *img = wTextureRenderImage(tex, tb_w, tb_h, WREL_RAISED); + if (img) { + /* Blit RImage into the pixman buffer at (0, 0). + * RImage is RGBA (4 bytes/pixel) or RGB (3 bytes/pixel). */ + uint32_t *dst = pixman_image_get_data(fb->image); + int dst_stride = pixman_image_get_stride(fb->image) / 4; + int x, y; + if (img->format == RRGBAFormat) { + for (y = 0; y < tb_h && y < img->height; y++) { + unsigned char *src_row = img->data + y * img->width * 4; + uint32_t *dst_row = dst + y * dst_stride; + for (x = 0; x < tb_w && x < img->width; x++) { + unsigned char r = src_row[x*4+0]; + unsigned char g = src_row[x*4+1]; + unsigned char b = src_row[x*4+2]; + unsigned char a = src_row[x*4+3]; + dst_row[x] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | + ((uint32_t)g << 8) | (uint32_t)b; + } + } + } else { + /* RGB format */ + for (y = 0; y < tb_h && y < img->height; y++) { + unsigned char *src_row = img->data + y * img->width * 3; + uint32_t *dst_row = dst + y * dst_stride; + for (x = 0; x < tb_w && x < img->width; x++) { + unsigned char r = src_row[x*3+0]; + unsigned char g = src_row[x*3+1]; + unsigned char b = src_row[x*3+2]; + dst_row[x] = 0xFF000000U | ((uint32_t)r << 16) | + ((uint32_t)g << 8) | (uint32_t)b; + } + } + } + RReleaseImage(img); + } + + /* Draw title text. */ + if (fwin->title && *fwin->font && fwin->title_color[state]) { + int lofs = 6, rofs = 6; + int titlelen, tw, tx, ty, th; + char *title; + + if (fwin->left_button && !fwin->flags.hide_left_button && + !fwin->flags.lbutton_dont_fit) + lofs += fwin->left_button->width + 3; + if (fwin->right_button && !fwin->flags.hide_right_button && + !fwin->flags.rbutton_dont_fit) + rofs += fwin->right_button->width + 3; + + title = ShrinkString(*fwin->font, fwin->title, tb_w - lofs - rofs); + titlelen = strlen(title); + tw = WMWidthOfString(*fwin->font, title, titlelen); + th = WMFontHeight(*fwin->font); + + switch (fwin->flags.justification) { + case WTJ_LEFT: tx = lofs; break; + case WTJ_RIGHT: tx = tb_w - tw - rofs; break; + default: tx = (tb_w - tw) / 2; break; + } + ty = (tb_h - th) / 2; + + /* Draw text into the frame buffer using WMDrawable = pixman_image_t* */ + WMDrawString(fwin->screen_ptr->wmscreen, (WMDrawable)fb->image, + fwin->title_color[state], *fwin->font, + tx, ty, title, titlelen); + wfree(title); + } + } + + /* --- Resizebar --- */ + if (fwin->resizebar && fwin->bottom_width > 0) { + int rb_y = total_h - fwin->bottom_width; + int rb_w = total_w; + int rb_h = fwin->bottom_width; + WTexture *tex = fwin->resizebar_texture[0]; + + RImage *img = wTextureRenderImage(tex, rb_w, rb_h, WREL_RAISED); + if (img) { + uint32_t *dst = pixman_image_get_data(fb->image); + int dst_stride = pixman_image_get_stride(fb->image) / 4; + int x, y; + if (img->format == RRGBAFormat) { + for (y = 0; y < rb_h && y < img->height; y++) { + unsigned char *src_row = img->data + y * img->width * 4; + uint32_t *dst_row = dst + (rb_y + y) * dst_stride; + for (x = 0; x < rb_w && x < img->width; x++) { + unsigned char r = src_row[x*4+0]; + unsigned char g = src_row[x*4+1]; + unsigned char b = src_row[x*4+2]; + unsigned char a = src_row[x*4+3]; + dst_row[x] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | + ((uint32_t)g << 8) | (uint32_t)b; + } + } + } else { + for (y = 0; y < rb_h && y < img->height; y++) { + unsigned char *src_row = img->data + y * img->width * 3; + uint32_t *dst_row = dst + (rb_y + y) * dst_stride; + for (x = 0; x < rb_w && x < img->width; x++) { + unsigned char r = src_row[x*3+0]; + unsigned char g = src_row[x*3+1]; + unsigned char b = src_row[x*3+2]; + dst_row[x] = 0xFF000000U | ((uint32_t)r << 16) | + ((uint32_t)g << 8) | (uint32_t)b; + } + } + } + RReleaseImage(img); + } + } + + /* Commit the buffer to the scene graph. */ + if (fb->scene_buf) + wlr_scene_buffer_set_buffer(fb->scene_buf, &fb->base); + + return 1; } /* ------------------------------------------------------------------ */ @@ -4702,7 +4923,6 @@ static void wl_server_ungrab(void) { overlay_flush(); } static void wl_stacking_raise(WNativeWindow win) { - /* Phase 58: move view to head of list (top of Z-order). */ struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v) { wl_list_remove(&v->link); @@ -4711,15 +4931,16 @@ wl_stacking_raise(WNativeWindow win) wlr_scene_node_raise_to_top(&v->scene_node->node); return; } - /* Frame/icon windows on XWayland. */ - if (dpy && win != WNATIVE_NONE) - XRaiseWindow(dpy, (Window)win); + struct wl_frame_buf *fb = frame_buf_find(win); + if (fb && fb->scene_buf) { + wlr_scene_node_raise_to_top(&fb->scene_buf->node); + return; + } } static void wl_stacking_lower(WNativeWindow win) { - /* Phase 58: move view to tail of list (bottom of Z-order). */ struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v) { wl_list_remove(&v->link); @@ -4728,8 +4949,11 @@ wl_stacking_lower(WNativeWindow win) wlr_scene_node_lower_to_bottom(&v->scene_node->node); return; } - if (dpy && win != WNATIVE_NONE) - XLowerWindow(dpy, (Window)win); + struct wl_frame_buf *fb = frame_buf_find(win); + if (fb && fb->scene_buf) { + wlr_scene_node_lower_to_bottom(&fb->scene_buf->node); + return; + } } static void @@ -4978,16 +5202,17 @@ wl_prop_get_wm_state(WNativeWindow win, long **data_out) static void wl_window_map(WNativeWindow win) { - /* Show the scene node for this window (workspace switch arriving on workspace). */ struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v && v->scene_node) { wlr_scene_node_set_enabled(&v->scene_node->node, true); return; } - /* Frame/icon/menu windows live on XWayland — map them via Xlib. - * XWayland will fire associate → handle_or_map adds the scene node. */ - if (dpy && win != WNATIVE_NONE) - XMapWindow(dpy, (Window)win); + /* Frame buffer: enable its scene node. */ + struct wl_frame_buf *fb = frame_buf_find(win); + if (fb && fb->scene_buf) { + wlr_scene_node_set_enabled(&fb->scene_buf->node, true); + return; + } } static void @@ -4999,36 +5224,38 @@ wl_window_map_raised(WNativeWindow win) wlr_scene_node_raise_to_top(&v->scene_node->node); return; } - if (dpy && win != WNATIVE_NONE) - XMapRaised(dpy, (Window)win); + struct wl_frame_buf *fb = frame_buf_find(win); + if (fb && fb->scene_buf) { + wlr_scene_node_set_enabled(&fb->scene_buf->node, true); + wlr_scene_node_raise_to_top(&fb->scene_buf->node); + return; + } } static void wl_window_map_subwindows(WNativeWindow parent) { - if (dpy && parent != WNATIVE_NONE) - XMapSubwindows(dpy, (Window)parent); + (void)parent; + /* Child windows are sub-regions of the frame buffer — no-op. */ } static void wl_window_unmap(WNativeWindow win) { - /* Hide the scene node for this window (workspace switch leaving workspace). */ struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v && v->scene_node) { wlr_scene_node_set_enabled(&v->scene_node->node, false); return; } - /* Frame/icon/menu windows on XWayland. */ - if (dpy && win != WNATIVE_NONE) - XUnmapWindow(dpy, (Window)win); + struct wl_frame_buf *fb = frame_buf_find(win); + if (fb && fb->scene_buf) { + wlr_scene_node_set_enabled(&fb->scene_buf->node, false); + return; + } } static void wl_window_move(WNativeWindow win, int x, int y) { - /* Phase 55: record the WM-assigned position in the view struct. - * Phase 17: also inform XWayland of the new position. */ struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v) { v->x = x; v->y = y; - /* Phase 18: keep scene node in sync with WM position. */ if (v->scene_node) wlr_scene_node_set_position(&v->scene_node->node, x, y); if (v->xw_surface) @@ -5036,15 +5263,17 @@ static void wl_window_move(WNativeWindow win, int x, int y) (int16_t)x, (int16_t)y, v->xw_surface->width, v->xw_surface->height); return; } - /* Frame/icon windows on XWayland. */ - if (dpy && win != WNATIVE_NONE) - XMoveWindow(dpy, (Window)win, x, y); + struct wl_frame_buf *fb = frame_buf_find(win); + if (fb) { + fb->x = x; fb->y = y; + if (fb->scene_buf) + wlr_scene_node_set_position(&fb->scene_buf->node, x, y); + return; + } } static void wl_window_resize(WNativeWindow win, int w, int h) { - /* Phase 44: send a configure with the new size. - * Phase 17: XWayland path uses wlr_xwayland_surface_configure. */ struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v) { if (w <= 0 || h <= 0) return; @@ -5058,25 +5287,20 @@ static void wl_window_resize(WNativeWindow win, int w, int h) wlr_xdg_surface_schedule_configure(v->xdg_surface); return; } - /* Frame/icon windows on XWayland. */ - if (dpy && win != WNATIVE_NONE && w > 0 && h > 0) - XResizeWindow(dpy, (Window)win, (unsigned)w, (unsigned)h); + /* Frame buffer resize is handled by frame_configure. */ } static void wl_window_set_border(WNativeWindow win, unsigned long pixel) { - if (dpy && win != WNATIVE_NONE) - XSetWindowBorder(dpy, (Window)win, pixel); + (void)win; (void)pixel; /* borders are drawn into the frame buffer */ } static void wl_window_set_border_width(WNativeWindow win, int width) { - if (dpy && win != WNATIVE_NONE) - XSetWindowBorderWidth(dpy, (Window)win, (unsigned)width); + (void)win; (void)width; /* no-op for native frames */ } static void wl_window_set_save_under(WNativeWindow win, int save_under) { (void)win; (void)save_under; } static void wl_window_clear(WNativeWindow win) { - if (dpy && win != WNATIVE_NONE) - XClearWindow(dpy, (Window)win); + (void)win; /* frame buffer is cleared in frame_paint */ } static int @@ -5107,18 +5331,16 @@ wl_window_get_rect(WNativeWindow win, if (win_h) { *win_h = h > 0 ? h : 1; } return 1; } - /* Non-synthetic: query XWayland for frame/icon windows. */ - if (dpy) { - Window root_ret; - int rx, ry; - unsigned int rw, rh, rbw, rdepth; - if (XGetGeometry(dpy, (Window)win, &root_ret, &rx, &ry, &rw, &rh, &rbw, &rdepth)) { - if (win_x) { *win_x = rx; } if (win_y) { *win_y = ry; } - if (win_w) { *win_w = (int)rw > 0 ? (int)rw : 1; } - if (win_h) { *win_h = (int)rh > 0 ? (int)rh : 1; } - return 1; - } + /* Frame buffer geometry. */ + struct wl_frame_buf *fb = frame_buf_find(win); + if (fb) { + if (win_x) { *win_x = fb->x; } + if (win_y) { *win_y = fb->y; } + if (win_w) { *win_w = fb->width; } + if (win_h) { *win_h = fb->height; } + return 1; } + /* Synthetic child IDs (titlebar, buttons) — return minimal geometry. */ if (win_x) { *win_x = 0; } if (win_y) { *win_y = 0; } if (win_w) { *win_w = 1; } if (win_h) { *win_h = 1; } return 0; @@ -5804,6 +6026,7 @@ const WMBackend wl_backend = { .frame_create_child = wl_frame_create_child, .frame_destroy = wl_frame_destroy, .frame_configure = wl_frame_configure, + .frame_paint = wl_frame_paint, .context_save = wl_context_save, .context_find = wl_context_find, diff --git a/src/backend/x11/x11_backend.c b/src/backend/x11/x11_backend.c index e39a2ce..f77ca17 100644 --- a/src/backend/x11/x11_backend.c +++ b/src/backend/x11/x11_backend.c @@ -2413,6 +2413,7 @@ const WMBackend x11_backend = { .frame_create_child = x11_frame_create_child, .frame_destroy = x11_frame_destroy, .frame_configure = x11_frame_configure, + .frame_paint = NULL, /* X11: use legacy Xlib code in framewin.c */ .context_save = x11_context_save, .context_find = x11_context_find, .context_delete = x11_context_delete, diff --git a/src/framewin.c b/src/framewin.c index ad12e73..8ae0201 100644 --- a/src/framewin.c +++ b/src/framewin.c @@ -952,6 +952,10 @@ void wFrameWindowPaint(WFrameWindow * fwin) WScreen *scr = fwin->screen_ptr; int state; + /* Let the backend handle painting if it can (Wayland: pixman rendering). */ + if (wm_backend->frame_paint && wm_backend->frame_paint(fwin)) + return; + state = fwin->flags.state; if (fwin->flags.is_client_window_frame)