diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 3ba7560..4e70cfe 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -3981,6 +3981,34 @@ frame_buf_find(WNativeWindow id) return NULL; } +/* Recursively update fb->x, fb->y for all descendants of `parent`, used + * to keep absolute-position tracking in sync after the parent moves. + * Hit-testing (frame_buf_at, frame_buf_button_at) and click coordinate + * translation depend on these fields being current. */ +static void +frame_buf_update_children_abs(WNativeWindow parent_id) +{ + struct wl_frame_buf *parent = frame_buf_find(parent_id); + if (!parent) return; + struct wl_frame_buf *child; + wl_list_for_each(child, &wl_state.frame_list, link) { + if (child->parent_id == parent_id) { + child->x = parent->x + child->rel_x; + child->y = parent->y + child->rel_y; + /* Recurse into grandchildren */ + frame_buf_update_children_abs(child->id); + } + } + /* Also update toplevel views parented to this frame */ + struct wl_toplevel_view *cv; + wl_list_for_each(cv, &wl_state.toplevel_list, link) { + if (cv->frame_id == parent_id) { + cv->x = parent->x + cv->frame_rel_x; + cv->y = parent->y + cv->frame_rel_y; + } + } +} + /* Hit-test: find the frame_buf under (px, py) that should receive the event. * * Returns the most-specific (innermost) frame_buf whose rendered pixels @@ -4077,6 +4105,17 @@ frame_buf_button_at(int px, int py) if (!fb->parent_id) continue; /* top-level root */ if (!fb->scene_buf) continue; /* no scene node */ if (!fb->scene_buf->node.enabled) continue; /* hidden */ + /* Walk up the scene tree — if any ancestor is disabled, this + * frame_buf is not actually visible (just its node enabled flag + * is set, but a parent tree was disabled e.g. by window_unmap). */ + struct wlr_scene_node *n = fb->scene_buf->node.parent + ? &fb->scene_buf->node.parent->node : NULL; + int ancestor_disabled = 0; + while (n) { + if (!n->enabled) { ancestor_disabled = 1; break; } + n = n->parent ? &n->parent->node : NULL; + } + if (ancestor_disabled) continue; /* 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; @@ -4278,22 +4317,8 @@ wl_frame_configure(WNativeWindow win, int x, int y, int width, int height) wlr_scene_buffer_set_dest_size(fb->scene_buf, width, height); } - /* Update absolute position tracking for children (used by hit-testing) */ - struct wl_toplevel_view *cv; - wl_list_for_each(cv, &wl_state.toplevel_list, link) { - if (cv->frame_id == win) { - cv->x = fb->x + cv->frame_rel_x; - cv->y = fb->y + cv->frame_rel_y; - break; - } - } - struct wl_frame_buf *child; - wl_list_for_each(child, &wl_state.frame_list, link) { - if (child->parent_id == win) { - child->x = fb->x + child->rel_x; - child->y = fb->y + child->rel_y; - } - } + /* Update absolute position tracking — recursively for all descendants. */ + frame_buf_update_children_abs(win); } static void wl_frame_paint_button(WCoreWindow *button, WTexture *texture, @@ -6551,22 +6576,10 @@ static void wl_window_move(WNativeWindow win, int x, int y) if (fb->tree) wlr_scene_node_set_position(&fb->tree->node, x, y); } - /* Update absolute position tracking for hit-testing */ - struct wl_toplevel_view *cv; - wl_list_for_each(cv, &wl_state.toplevel_list, link) { - if (cv->frame_id == win) { - cv->x = fb->x + cv->frame_rel_x; - cv->y = fb->y + cv->frame_rel_y; - break; - } - } - struct wl_frame_buf *child; - wl_list_for_each(child, &wl_state.frame_list, link) { - if (child->parent_id == win) { - child->x = fb->x + child->rel_x; - child->y = fb->y + child->rel_y; - } - } + /* Update absolute position tracking for hit-testing — recursively + * for all descendants so grandchildren (e.g. WINGs panel inside + * a fake_leader inside a WMaker frame) stay in sync. */ + frame_buf_update_children_abs(win); return; } } @@ -6948,20 +6961,20 @@ wl_client_reparent(WNativeWindow client, WNativeWindow new_parent, int x, int y) fb->rel_y = y; fb->x = parent_fb->x + x; fb->y = parent_fb->y + y; - if (fb->scene_buf) { - /* Reparent under parent's tree and use relative position */ + if (fb->tree) { + /* This frame_buf has a tree (e.g. fake_leader): reparent + * the whole tree so all children move with it. */ + if (parent_fb->tree) + wlr_scene_node_reparent(&fb->tree->node, parent_fb->tree); + wlr_scene_node_set_position(&fb->tree->node, x, y); + } else if (fb->scene_buf) { + /* Plain frame_buf (no tree): reparent scene_buf directly. */ if (parent_fb->tree) wlr_scene_node_reparent(&fb->scene_buf->node, parent_fb->tree); wlr_scene_node_set_position(&fb->scene_buf->node, x, y); } - /* Update absolute tracking for children */ - struct wl_frame_buf *child; - wl_list_for_each(child, &wl_state.frame_list, link) { - if (child->parent_id == client) { - child->x = fb->x + child->rel_x; - child->y = fb->y + child->rel_y; - } - } + /* Update absolute tracking recursively for all descendants */ + frame_buf_update_children_abs(client); } } } @@ -7140,7 +7153,11 @@ wl_icccm_free_command(char **argv) static WNativeWindow wl_fake_leader_create(WScreen *scr) { (void)scr; - /* Create a frame_buf to act as the dialog's content container */ + /* Create a frame_buf with a tree to act as the dialog's content + * container. The tree is essential — content reparented into the + * fake_leader (e.g. WINGs panel views) becomes a child of the tree + * and follows it when the fake_leader itself is reparented into + * a WMaker frame. */ struct wl_frame_buf *fb = wmalloc(sizeof(*fb)); memset(fb, 0, sizeof(*fb)); @@ -7154,9 +7171,16 @@ static WNativeWindow wl_fake_leader_create(WScreen *scr) if (wl_state.scene) { struct wlr_scene_tree *_pp = wl_state.level_trees[WL_LAYER_NORMAL] ? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree; - fb->scene_buf = wlr_scene_buffer_create(_pp, &fb->base); - if (fb->scene_buf) - wlr_scene_node_set_enabled(&fb->scene_buf->node, false); + fb->tree = wlr_scene_tree_create(_pp); + if (fb->tree) { + wlr_scene_node_set_position(&fb->tree->node, 0, 0); + fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base); + if (fb->scene_buf) { + wlr_scene_node_set_position(&fb->scene_buf->node, 0, 0); + wlr_scene_node_set_enabled(&fb->scene_buf->node, false); + } + wlr_scene_node_set_enabled(&fb->tree->node, true); + } } wl_list_insert(&wl_state.frame_list, &fb->link);