diff --git a/WINGs/wclient_wl.c b/WINGs/wclient_wl.c index 6d877c5..37a0424 100644 --- a/WINGs/wclient_wl.c +++ b/WINGs/wclient_wl.c @@ -560,6 +560,91 @@ wl_client_toplevel_destroy(struct W_View *view) wl_client_view_destroy_backing((unsigned long)view->window); } +void +wl_client_toplevel_recycle(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); + } + } + + wl_surface_commit(top->surface); + W_SetViewBacking(top->view->window, top->buf->image); +} + +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) { diff --git a/WINGs/wview_wl.c b/WINGs/wview_wl.c index 5bd87ac..4449d4f 100644 --- a/WINGs/wview_wl.c +++ b/WINGs/wview_wl.c @@ -16,6 +16,10 @@ 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; +/* 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; @@ -211,6 +215,12 @@ static void wl_unmap(void *vctx, W_View *view) /* Disable the scene buffer so the view disappears from screen. */ if (W_ViewSceneNodeSetEnabledFn) W_ViewSceneNodeSetEnabledFn((unsigned long)view->window, 0); + /* 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); if (view->parent && view->parent->flags.mapped) wl_redisplay(vctx, view->parent); } @@ -305,7 +315,10 @@ static void wl_map(void *vctx, W_View *view) child = child->nextSister; } } - /* Re-enable scene node after unmap so the surface becomes visible again. */ + /* 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); diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index f23a6ea..a425438 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -470,6 +470,12 @@ 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);