wl_backend: fix W_ViewSceneNodeSetEnabledFn never wired — gview scene buffer stayed on after unmap

The blue position indicator (gview — geometry view with blue-gray background,
white coordinate text) was visible during window move, but never disappeared
when the move ended.

Root cause: W_ViewSceneNodeSetEnabledFn was declared in wview_wl.c and
called by wl_unmap() to disable the view's scene buffer node, but
wl_backend.c never wired it up — the pointer was NULL.

Also: add auto-create of frame_buf for WINGs toplevel views not explicitly
reparented (switch panel, menus, gview), sync position from WINGs view on
commit, auto-realize children in wl_map/wl_map_subviews, and guard scene
node enablement against unmapped views.
This commit is contained in:
Window Maker 2026-05-30 11:17:10 +02:00
parent 2797e1dcfc
commit fd6e972bbf
3 changed files with 108 additions and 3 deletions

View File

@ -17,6 +17,7 @@ static unsigned int wl_view_next_id = WL_VIEW_ID_BASE;
/* Callback set by WM backend to commit frame_buf after paint */
void (*W_ViewCommitBackingFn)(unsigned long id) = NULL;
void (*W_ViewDestroyBackingFn)(unsigned long id) = NULL;
void (*W_ViewSceneNodeSetEnabledFn)(unsigned long id, int enabled) = NULL;
/* Callback for wclient_wl.c to trigger a full redisplay after resize */
static void wl_redisplay(void *vctx, W_View *view);
@ -207,6 +208,9 @@ static void wl_unmap(void *vctx, W_View *view)
{
(void)vctx;
view->flags.mapped = 0;
/* Disable the scene buffer so the view disappears from screen. */
if (W_ViewSceneNodeSetEnabledFn)
W_ViewSceneNodeSetEnabledFn((unsigned long)view->window, 0);
if (view->parent && view->parent->flags.mapped)
wl_redisplay(vctx, view->parent);
}
@ -290,6 +294,17 @@ static void wl_map(void *vctx, W_View *view)
view->flags.mapped = 1;
if (!view->flags.realized)
return;
/* Auto-realize children recursively before redisplay.
* W_RealizeView recurses into the full subtree, so calling it
* on each direct child handles arbitrary nesting depth. */
{
W_View *child = view->childrenList;
while (child) {
if (!child->flags.realized)
W_RealizeView(child);
child = child->nextSister;
}
}
if (view->flags.topLevel) {
wl_redisplay(vctx, view);
return;
@ -312,6 +327,13 @@ static void wl_map_subviews(void *vctx, W_View *view)
{
W_View *child = view->childrenList;
while (child) {
if (!child->flags.realized) {
/* Auto-realize children that were created after their
* parent was already realized. This is needed for e.g.
* switch panel icon frames added via addIconForWindow
* after WMRealizeWidget(panel->win). */
W_RealizeView(child);
}
if (child->flags.realized) {
child->flags.mapped = 1;
}

View File

@ -2720,6 +2720,10 @@ wl_display_open(const char *name)
extern void (*W_ViewDestroyBackingFn)(unsigned long id);
W_ViewDestroyBackingFn = wl_view_destroy_backing_impl;
extern void (*W_ViewSceneNodeSetEnabledFn)(unsigned long id, int enabled);
extern void wl_view_scene_node_set_enabled(unsigned long id, int enabled);
W_ViewSceneNodeSetEnabledFn = wl_view_scene_node_set_enabled;
extern void (*W_ClipboardStoreFn)(const char *text, int len);
extern char *(*W_ClipboardFetchFn)(int *len_out);
W_ClipboardStoreFn = wl_clipboard_store_to_seat;
@ -7815,12 +7819,91 @@ wl_client_reparent(WNativeWindow client, WNativeWindow new_parent, int x, int y)
void wl_view_commit_backing_impl(unsigned long id)
{
struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)id);
/* If the view was resized, the old frame_buf's image is stale. */
if (fb) {
extern void *W_GetViewBacking(unsigned long id);
void *current = W_GetViewBacking((unsigned long)id);
if (current != (void *)fb->image) {
wlr_scene_node_destroy(&fb->scene_buf->node);
wl_list_remove(&fb->link);
wlr_buffer_drop(&fb->base);
wfree(fb);
fb = NULL;
}
}
if (!fb) {
/* Auto-create a frame_buf for WINGs toplevel views that were
* not explicitly reparented (e.g. switch panel, menus).
* Without a frame_buf/scene_buffer the view content is painted
* into its pixman backing but never appears on screen. */
extern W_View *W_GetViewForWindow(unsigned long id);
extern void *W_GetViewBacking(unsigned long id);
W_View *view = W_GetViewForWindow((unsigned long)id);
if (view && view->flags.topLevel) {
fb = wmalloc(sizeof(*fb));
memset(fb, 0, sizeof(*fb));
fb->id = id;
fb->x = view->pos.x;
fb->y = view->pos.y;
fb->width = view->size.width;
fb->height = view->size.height;
fb->image = (pixman_image_t *)W_GetViewBacking(id);
if (fb->image) {
pixman_image_ref(fb->image);
wlr_buffer_init(&fb->base, &frame_buf_impl,
pixman_image_get_width(fb->image),
pixman_image_get_height(fb->image));
if (wl_state.scene) {
struct wlr_scene_tree *parent =
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(parent, &fb->base);
if (fb->scene_buf) {
fb_attach_scene_buf_listener(fb);
wlr_scene_node_set_position(&fb->scene_buf->node,
fb->x, fb->y);
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
}
}
wl_list_insert(&wl_state.frame_list, &fb->link);
} else {
wfree(fb);
fb = NULL;
}
}
}
if (fb && fb->scene_buf) {
wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL);
/* Enable the node — it has content now */
wlr_scene_node_set_enabled(&fb->scene_buf->node, true);
/* Sync position from the WINGs view — it may have moved since
* the frame_buf was created (e.g. switch panel is positioned
* after widget creation via WMMoveWidget). */
extern W_View *W_GetViewForWindow(unsigned long id);
W_View *v = W_GetViewForWindow((unsigned long)id);
if (v) {
if (fb->x != v->pos.x || fb->y != v->pos.y) {
fb->x = v->pos.x;
fb->y = v->pos.y;
wlr_scene_node_set_position(&fb->scene_buf->node,
fb->x, fb->y);
}
}
/* Only enable the scene node if the view is still mapped.
* If the view was unmapped (e.g. move ended), wl_unmap disables
* it via W_ViewSceneNodeSetEnabledFn — don't re-enable here. */
if (!v || v->flags.mapped) {
wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL);
wlr_scene_node_set_enabled(&fb->scene_buf->node, true);
}
}
}
void
wl_view_scene_node_set_enabled(unsigned long id, int enabled)
{
struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)id);
if (fb && fb->scene_buf)
wlr_scene_node_set_enabled(&fb->scene_buf->node, !!enabled);
}
void wl_view_destroy_backing_impl(unsigned long id)
{