wayland: fix click delay after shade and modal dialog parenting

After shading a window, clicks on the lower titlebar would be routed to
the resizebar handler, requiring multiple clicks before one landed in
a region where frame_buf_button_at didn't return the (disabled) resizebar.

Two related bugs:

1. frame_buf_button_at iterated all child frame_bufs without checking
   enabled state. The shaded resizebar's bounds still recorded in
   fb->x/y/width/height but its scene_buf was disabled. Since the
   shaded resizebar was repositioned ON TOP of the titlebar, clicks
   in the overlapping region returned the (smaller, disabled)
   resizebar instead of the titlebar. Added an enabled-state check.

2. wlroots scene graph caches buffer dimensions for hit-testing.
   Mutating fb->base.width/height directly leaves stale bounds. Use
   wlr_scene_buffer_set_dest_size() whenever the buffer is resized.

Also fixes:
- WINGs dialog content was placed in WL_LAYER_FLOATING instead of
  under the parent frame's tree, so the content didn't follow the
  WM-managed decorations on move. Now parented under the frame tree
  and positioned at relative offset.
- wl_window_map called wlr_scene_node_raise_to_top on the client
  scene_node, which raised it above the titlebar within the frame
  tree. After unshade this hid the titlebar. Removed.
- xdg_activation focus path: raise the frame tree, not the client
  scene_node, for the same reason.
This commit is contained in:
Window Maker 2026-05-23 21:08:19 +02:00
parent 13101c53c3
commit bc25aa4b8a
1 changed files with 45 additions and 12 deletions

View File

@ -4076,6 +4076,7 @@ frame_buf_button_at(int px, int py)
wl_list_for_each(fb, &wl_state.frame_list, link) {
if (!fb->parent_id) continue; /* top-level root */
if (!fb->scene_buf) continue; /* no scene node */
if (!fb->scene_buf->node.enabled) continue; /* hidden */
/* Buttons are small children of the titlebar with enabled scene_bufs.
* Skip large nodes (titlebars, resizebars) by size threshold. */
if (px < fb->x || px >= fb->x + fb->width) continue;
@ -4270,9 +4271,11 @@ wl_frame_configure(WNativeWindow win, int x, int y, int width, int height)
fb->height = height;
fb->base.width = width;
fb->base.height = height;
/* Detach old buffer from scene so it picks up new dimensions on next commit */
/* Notify scene graph of new size — required for hit-testing.
* wlroots caches buffer dimensions; without this, clicks beyond
* the old size would miss this scene_buf. */
if (fb->scene_buf)
wlr_scene_buffer_set_buffer(fb->scene_buf, NULL);
wlr_scene_buffer_set_dest_size(fb->scene_buf, width, height);
}
/* Update absolute position tracking for children (used by hit-testing) */
@ -4347,6 +4350,9 @@ wl_frame_paint(struct WFrameWindow *fwin)
tb_fb->base.width = tb_w;
tb_fb->base.height = tb_h;
tb_img = tb_fb->image;
/* Notify scene graph so hit-testing covers the new size. */
if (tb_fb->scene_buf)
wlr_scene_buffer_set_dest_size(tb_fb->scene_buf, tb_w, tb_h);
}
WTexture *tex = fwin->title_texture[state];
@ -5147,8 +5153,16 @@ handle_xdg_activation_request(struct wl_listener *listener, void *data)
if (desc && desc->parent_type == WCLASS_WINDOW) {
WWindow *wwin = (WWindow *)desc->parent;
wSetFocusTo(wScreenWithNumber(0), wwin);
if (view->scene_node)
/* Raise the entire window assembly (frame tree), not just the
* client scene_node — raising the client alone would push it
* above the titlebar and hide decorations. */
if (view->frame_id) {
struct wl_frame_buf *fb = frame_buf_find(view->frame_id);
if (fb && fb->tree)
wlr_scene_node_raise_to_top(&fb->tree->node);
} else if (view->scene_node) {
wlr_scene_node_raise_to_top(&view->scene_node->node);
}
/* Move to front of toplevel list (Z-order). */
wl_list_remove(&view->link);
wl_list_insert(wl_state.toplevel_list.prev, &view->link);
@ -6395,7 +6409,6 @@ wl_window_map(WNativeWindow win)
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);
wlr_scene_node_raise_to_top(&v->scene_node->node);
return;
}
if (v)
@ -6581,8 +6594,9 @@ static void wl_window_resize(WNativeWindow win, int w, int h)
fb->height = h;
fb->base.width = w;
fb->base.height = h;
/* Update scene graph's cached buffer size for hit-testing. */
if (fb->scene_buf)
wlr_scene_buffer_set_buffer(fb->scene_buf, NULL);
wlr_scene_buffer_set_dest_size(fb->scene_buf, w, h);
}
}
static void wl_window_set_border(WNativeWindow win, unsigned long pixel)
@ -6856,8 +6870,20 @@ wl_client_reparent(WNativeWindow client, WNativeWindow new_parent, int x, int y)
existing->rel_y = y;
existing->x = parent_fb->x + x;
existing->y = parent_fb->y + y;
if (existing->scene_buf)
wlr_scene_node_set_position(&existing->scene_buf->node, x, y);
if (existing->scene_buf) {
/* Ensure it's under the parent frame's tree, then position
* at relative offset within the tree. */
if (parent_fb->tree &&
existing->scene_buf->node.parent != parent_fb->tree) {
wlr_scene_node_reparent(&existing->scene_buf->node,
parent_fb->tree);
}
if (parent_fb->tree)
wlr_scene_node_set_position(&existing->scene_buf->node, x, y);
else
wlr_scene_node_set_position(&existing->scene_buf->node,
existing->x, existing->y);
}
return;
}
@ -6888,13 +6914,20 @@ wl_client_reparent(WNativeWindow client, WNativeWindow new_parent, int x, int y)
fb->height = h;
if (wl_state.scene) {
/* WINGs dialog box: place in WL_LAYER_FLOATING so it renders
* above normal windows but below menus and dock. */
struct wlr_scene_tree *_wp = wl_state.level_trees[WL_LAYER_FLOATING]
? wl_state.level_trees[WL_LAYER_FLOATING] : &wl_state.scene->tree;
/* Place the WINGs content under the parent frame's tree so it
* moves with the WM-managed dialog decorations. */
struct wlr_scene_tree *_wp = parent_fb->tree
? parent_fb->tree
: (wl_state.level_trees[WL_LAYER_FLOATING]
? wl_state.level_trees[WL_LAYER_FLOATING]
: &wl_state.scene->tree);
fb->scene_buf = wlr_scene_buffer_create(_wp, &fb->base);
if (fb->scene_buf) {
wlr_scene_node_set_position(&fb->scene_buf->node, fb->x, fb->y);
/* If under the frame tree, use relative coords; otherwise absolute. */
if (parent_fb->tree)
wlr_scene_node_set_position(&fb->scene_buf->node, x, y);
else
wlr_scene_node_set_position(&fb->scene_buf->node, fb->x, fb->y);
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
}
}