wl_unmap: send NULL to wl_surface when W_ViewSceneNodeSetEnabledFn unavailable

This commit is contained in:
lkn 2026-06-07 15:48:38 +02:00
parent f95cd3d6d3
commit d923d9fb83
3 changed files with 25 additions and 96 deletions

View File

@ -561,89 +561,24 @@ wl_client_toplevel_destroy(struct W_View *view)
}
void
wl_client_toplevel_recycle(WNativeWindow id)
wl_client_view_unmap(WNativeWindow id)
{
WLClientToplevel *top = wlc_top_find(id);
if (!top) return;
/* Destroy the Wayland protocol objects but keep the
* WLClientToplevel struct and its view->window mapping,
* so the surface can be recreated cleanly on remap. */
if (top->wmaker_popup) {
wmaker_popup_v1_destroy(top->wmaker_popup);
top->wmaker_popup = NULL;
}
if (top->xdg_toplevel) {
xdg_toplevel_destroy(top->xdg_toplevel);
top->xdg_toplevel = NULL;
}
if (top->xdg_surface) {
xdg_surface_destroy(top->xdg_surface);
top->xdg_surface = NULL;
}
if (top->surface) {
wl_surface_destroy(top->surface);
top->surface = NULL;
}
if (top->buf) {
wlclient_destroy_shm_buffer(top->buf);
top->buf = NULL;
}
top->configured = 0;
top->mapped = 0;
top->want_close = 0;
}
static void
wl_client_toplevel_remap_locked(WLClientToplevel *top)
{
if (!top || top->surface) return; /* already mapped */
WLClientCtx *ctx = top->ctx;
if (!ctx || !ctx->compositor || !ctx->wm_base) return;
top->buf = wlclient_create_shm_buffer(ctx,
top->pending_w, top->pending_h);
if (!top->buf) return;
top->surface = wl_compositor_create_surface(ctx->compositor);
if (!top->surface) return;
top->xdg_surface = xdg_wm_base_get_xdg_surface(
ctx->wm_base, top->surface);
if (!top->xdg_surface) return;
xdg_surface_add_listener(top->xdg_surface,
&xdg_surface_listener, top);
top->xdg_toplevel = xdg_surface_get_toplevel(
top->xdg_surface);
if (!top->xdg_toplevel) return;
xdg_toplevel_add_listener(top->xdg_toplevel,
&xdg_toplevel_listener, top);
if (top->view->flags.popup) {
xdg_toplevel_set_app_id(top->xdg_toplevel, "wings-popup");
xdg_toplevel_set_title(top->xdg_toplevel, "");
if (ctx->wmaker_popup_mgr) {
top->wmaker_popup =
wmaker_popup_manager_v1_get_popup(
ctx->wmaker_popup_mgr,
top->xdg_toplevel);
if (top->wmaker_popup && ctx->main_toplevel)
wmaker_popup_v1_set_parent(
top->wmaker_popup,
ctx->main_toplevel);
}
}
if (!top || !top->surface) return;
/* Attach NULL + commit: the standard Wayland way
* for a client to hide a surface from the compositor.
* The xdg_toplevel object stays alive for remap. */
wl_surface_attach(top->surface, NULL, 0, 0);
wl_surface_commit(top->surface);
W_SetViewBacking(top->view->window, top->buf->image);
if (top->ctx && top->ctx->display)
wl_display_flush(top->ctx->display);
}
void
wl_client_toplevel_remap(WNativeWindow id)
{
WLClientToplevel *top = wlc_top_find(id);
wl_client_toplevel_remap_locked(top);
}
void
W_WLClientSetMinMaxSize(W_View *view)

View File

@ -18,8 +18,6 @@ static unsigned int wl_view_next_id = WL_VIEW_ID_BASE;
void (*W_ViewCommitBackingFn)(unsigned long id) = NULL;
/* Optional hook: destroy wl_surface + xdg_toplevel on popup unmap
* so the compositor has no stale state. Set by the WM backend. */
void (*W_WLClientToplevelRecycleFn)(unsigned long id) = NULL;
void (*W_WLClientToplevelRemapFn)(unsigned long id) = NULL;
void (*W_ViewDestroyBackingFn)(unsigned long id) = NULL;
void (*W_ViewSceneNodeSetEnabledFn)(unsigned long id, int enabled) = NULL;
@ -224,12 +222,16 @@ static void wl_unmap(void *vctx, W_View *view)
} else
wwarning("wl_unmap: W_ViewSceneNodeSetEnabledFn is NULL");
/* Scene node disabled in the diagnostic block above. */
/* Destroy the WINGs client wl_surface + xdg_toplevel on unmap so
* the compositor has no stale state for this surface. The recycle
* function preserves the WLClientToplevel struct and ID mapping
* so the surface can be recreated on the next map. */
if (view->flags.topLevel && view->flags.popup && W_WLClientToplevelRecycleFn)
W_WLClientToplevelRecycleFn((unsigned long)view->window);
/* In client mode (e.g. WPrefs), W_ViewSceneNodeSetEnabledFn may be
* NULL because the compositor-side function is not available in the
* client process. Attach NULL to the wl_surface, which is the
* standard Wayland way for a client to hide a surface. */
if (view->flags.topLevel && view->flags.popup
&& !W_ViewSceneNodeSetEnabledFn) {
extern void wl_client_view_unmap(WNativeWindow id);
wl_client_view_unmap((WNativeWindow)view->window);
}
if (view->parent && view->parent->flags.mapped)
wl_redisplay(vctx, view->parent);
}
@ -324,10 +326,7 @@ static void wl_map(void *vctx, W_View *view)
child = child->nextSister;
}
}
/* Re-enable scene node after unmap so the surface becomes visible again.
* For recycled popups, recreate the wl_surface + xdg_toplevel first. */
if (view->flags.topLevel && view->flags.popup && W_WLClientToplevelRemapFn)
W_WLClientToplevelRemapFn((unsigned long)view->window);
if (W_ViewSceneNodeSetEnabledFn)
W_ViewSceneNodeSetEnabledFn((unsigned long)view->window, 1);

View File

@ -470,12 +470,7 @@ wl_display_open(const char *name)
unsigned int, int, int, unsigned long);
extern void wl_pointer_ungrab(unsigned long);
W_WLPointerGrabFn = wl_pointer_grab;
extern void (*W_WLClientToplevelRecycleFn)(unsigned long);
extern void wl_client_toplevel_recycle(WNativeWindow);
W_WLClientToplevelRecycleFn = wl_client_toplevel_recycle;
extern void (*W_WLClientToplevelRemapFn)(unsigned long);
extern void wl_client_toplevel_remap(WNativeWindow);
W_WLClientToplevelRemapFn = wl_client_toplevel_remap;
W_WLPointerUngrabFn = wl_pointer_ungrab;
extern void (*W_ClipboardStoreFn)(const char *text, int len);