wayland: use xdg geometry instead of surface size for CSD clients

Firefox and other CSD clients include invisible shadow borders in their
surface buffer.  Use xdg_surface->geometry (the client-declared content
rect) for:
- reporting client width/height to the WM frame
- sizing the frame on commit
- offsetting the scene node so shadows are outside the frame bounds

Falls back to surface->current.width/height when geometry is unset.
This commit is contained in:
lkn 2026-06-21 19:17:06 +02:00
parent e0edd782d8
commit 1ee10d37f5
2 changed files with 23 additions and 9 deletions

View File

@ -40,8 +40,14 @@ wl_window_get_client_attrs(WNativeWindow win, WClientAttributes *out)
}
} else if (v && v->xdg_surface && v->xdg_surface->surface) {
out->map_state = WCLIENT_VIEWABLE;
out->width = v->xdg_surface->surface->current.width;
out->height = v->xdg_surface->surface->current.height;
struct wlr_box geo = v->xdg_surface->geometry;
if (geo.width > 0 && geo.height > 0) {
out->width = geo.width;
out->height = geo.height;
} else {
out->width = v->xdg_surface->surface->current.width;
out->height = v->xdg_surface->surface->current.height;
}
} else {
out->map_state = WCLIENT_UNMAPPED;
}

View File

@ -319,17 +319,21 @@ handle_toplevel_map(struct wl_listener *listener, void *data)
view_attach_scene_destroy_listener(view);
/* Deferred reparent: move client surface under its
* frame grouping tree now that the node exists. */
struct wlr_box geo = view->xdg_surface->geometry;
if (view->frame_id) {
struct wl_frame_buf *_pfb = frame_buf_find(view->frame_id);
if (_pfb && _pfb->tree)
wlr_scene_node_reparent(&view->scene_node->node,
_pfb->tree);
/* Position at relative offset within frame tree */
/* Position at relative offset within frame tree,
* adjusted for CSD geometry offset (shadows). */
wlr_scene_node_set_position(&view->scene_node->node,
view->frame_rel_x, view->frame_rel_y);
view->frame_rel_x - geo.x,
view->frame_rel_y - geo.y);
} else {
wlr_scene_node_set_position(&view->scene_node->node,
view->x, view->y);
view->x - geo.x,
view->y - geo.y);
}
}
}
@ -558,17 +562,21 @@ handle_toplevel_commit(struct wl_listener *listener, void *data)
if (view->scene_node && view->frame_id) {
struct wl_frame_buf *ffb = frame_buf_find(view->frame_id);
if (ffb && ffb->tree && view->scene_node->node.parent != ffb->tree) {
struct wlr_box geo = view->xdg_surface->geometry;
wlr_scene_node_reparent(&view->scene_node->node, ffb->tree);
wlr_scene_node_set_position(&view->scene_node->node,
view->frame_rel_x, view->frame_rel_y);
view->frame_rel_x - geo.x,
view->frame_rel_y - geo.y);
}
}
/* When the client commits a buffer at a new size (e.g. after interactive
* resize), update the WM frame to match. */
* resize), update the WM frame to match. Use xdg geometry (excludes
* CSD shadows) when available, fall back to surface size. */
if (view->xdg_surface && view->xdg_surface->surface && view->frame_id) {
int cw = view->xdg_surface->surface->current.width;
int ch = view->xdg_surface->surface->current.height;
struct wlr_box geo = view->xdg_surface->geometry;
int cw = (geo.width > 0) ? geo.width : view->xdg_surface->surface->current.width;
int ch = (geo.height > 0) ? geo.height : view->xdg_surface->surface->current.height;
if (cw > 0 && ch > 0) {
WObjDescriptor *desc = wm_backend->context_find(view->id, WM_CTX_CLIENT_WIN);
if (desc && desc->parent_type == WCLASS_WINDOW) {