wayland: Phase 40 — wire toplevel unmap to wUnmanageWindow
Guard all dpy==NULL / frame==NULL crash sites in the unmanage path:
- wUnmanageWindow (window.c): frame ptr set to NULL-safe expression;
window_unmap, raise_delay timer, and owner border change all
guarded on frame != NULL
- wSelectWindow (actions.c): early return if !wwin->frame
- wNETCleanupFrameExtents (wmspec.c): early return if !dpy
- wClientRestore (client.c): early return if !dpy
Wire handle_toplevel_unmap → context_find → wUnmanageWindow(destroyed=True)
so XDG toplevels are removed from scr->focused_window on client disconnect.
Smoke test: add Phase 40 check — grep "toplevel unmapped".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ca09635129
commit
490ecb34ff
|
|
@ -2159,6 +2159,9 @@ void wSelectWindow(WWindow *wwin, Bool flag)
|
|||
{
|
||||
WScreen *scr = wwin->screen_ptr;
|
||||
|
||||
if (!wwin->frame)
|
||||
return; /* Wayland: no frame window to set border on */
|
||||
|
||||
if (flag) {
|
||||
wwin->flags.selected = 1;
|
||||
if (wwin->frame->selected_border_pixel)
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ handle_toplevel_map(struct wl_listener *listener, void *data)
|
|||
}
|
||||
|
||||
/*
|
||||
* Phase 38: toplevel unmap — surface hidden (minimised or gone).
|
||||
* Phase 40: toplevel unmap — surface hidden or destroyed; unmanage the window.
|
||||
*/
|
||||
static void
|
||||
handle_toplevel_unmap(struct wl_listener *listener, void *data)
|
||||
|
|
@ -166,7 +166,16 @@ handle_toplevel_unmap(struct wl_listener *listener, void *data)
|
|||
(void)data;
|
||||
|
||||
wwarning("wl_backend: toplevel unmapped id=0x%lx", view->id);
|
||||
/* TODO Phase 39: call wUnmanageWindow */
|
||||
|
||||
/* Phase 40: remove the WWindow from the WM's managed list. */
|
||||
WScreen *scr = wScreenWithNumber(0);
|
||||
if (scr) {
|
||||
WObjDescriptor *desc = wm_backend->context_find(view->id, WM_CTX_CLIENT_WIN);
|
||||
if (desc && desc->parent_type == WCLASS_WINDOW) {
|
||||
WWindow *wwin = (WWindow *)desc->parent;
|
||||
wUnmanageWindow(wwin, False, True);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@
|
|||
*/
|
||||
void wClientRestore(WWindow * wwin)
|
||||
{
|
||||
if (!dpy)
|
||||
return; /* Wayland: no X11 reparent/border-width */
|
||||
#if 0
|
||||
int gx, gy;
|
||||
|
||||
|
|
|
|||
10
src/window.c
10
src/window.c
|
|
@ -1472,7 +1472,8 @@ WWindow *wManageInternalWindow(WScreen *scr, WNativeWindow window, WNativeWindow
|
|||
*/
|
||||
void wUnmanageWindow(WWindow *wwin, Bool restore, Bool destroyed)
|
||||
{
|
||||
WCoreWindow *frame = wwin->frame->core;
|
||||
/* Wayland: frame may be NULL when managing frameless surfaces */
|
||||
WCoreWindow *frame = wwin->frame ? wwin->frame->core : NULL;
|
||||
WWindow *owner = NULL;
|
||||
WWindow *newFocusedWindow = NULL;
|
||||
int wasFocused;
|
||||
|
|
@ -1507,7 +1508,8 @@ void wUnmanageWindow(WWindow *wwin, Bool restore, Bool destroyed)
|
|||
wm_backend->key_ungrab_all(wwin->client_win);
|
||||
}
|
||||
|
||||
wm_backend->window_unmap(frame->window);
|
||||
if (frame)
|
||||
wm_backend->window_unmap(frame->window);
|
||||
|
||||
wm_backend->window_unmap(wwin->client_win);
|
||||
|
||||
|
|
@ -1516,7 +1518,7 @@ void wUnmanageWindow(WWindow *wwin, Bool restore, Bool destroyed)
|
|||
|
||||
/* remove all pending events on window */
|
||||
/* I think this only matters for autoraise */
|
||||
if (wPreferences.raise_delay)
|
||||
if (wPreferences.raise_delay && wwin->frame)
|
||||
WMDeleteTimerWithClientData(wwin->frame->core);
|
||||
|
||||
wm_backend->event_flush();
|
||||
|
|
@ -1613,7 +1615,7 @@ void wUnmanageWindow(WWindow *wwin, Bool restore, Bool destroyed)
|
|||
WMPostNotificationName(WMNUnmanaged, wwin, NULL);
|
||||
if (wasFocused) {
|
||||
if (newFocusedWindow != owner && owner) {
|
||||
if (wwin->flags.is_gnustep == 0)
|
||||
if (wwin->flags.is_gnustep == 0 && owner->frame)
|
||||
wFrameWindowChangeState(owner->frame, WS_UNFOCUSED);
|
||||
}
|
||||
wSetFocusTo(scr, newFocusedWindow);
|
||||
|
|
|
|||
|
|
@ -2021,5 +2021,7 @@ void wNETFrameExtents(WWindow *wwin)
|
|||
|
||||
void wNETCleanupFrameExtents(WWindow *wwin)
|
||||
{
|
||||
if (!dpy)
|
||||
return; /* Wayland: no X11 _NET_FRAME_EXTENTS property */
|
||||
XDeleteProperty(dpy, wwin->client_win, net_frame_extents);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
# mapped" when weston-simple-shm creates its XDG toplevel surface
|
||||
# 9. (Phase 39) wManageWindow called for mapped XDG toplevel: compositor
|
||||
# logs "wManageWindow succeeded" for the synthetic surface ID
|
||||
# 10. (Phase 40) wUnmanageWindow called when client disconnects: compositor
|
||||
# logs "toplevel unmapped" and exits cleanly after cleanup
|
||||
#
|
||||
# This test grows as wl_backend vtable slots are implemented.
|
||||
#
|
||||
|
|
@ -155,6 +157,11 @@ if command -v weston-simple-shm >/dev/null 2>&1; then
|
|||
grep -q 'wManageWindow succeeded' /tmp/wl-smoke-stderr \
|
||||
|| die "compositor did not log 'wManageWindow succeeded' (wManageWindow call failed)"
|
||||
echo " manage check: wManageWindow succeeded for weston-simple-shm toplevel"
|
||||
|
||||
# -- 10. Phase 40: wUnmanageWindow fired when client disconnects ------
|
||||
grep -q 'toplevel unmapped' /tmp/wl-smoke-stderr \
|
||||
|| die "compositor did not log 'toplevel unmapped' (wUnmanageWindow not triggered)"
|
||||
echo " unmanage check: toplevel unmapped and wUnmanageWindow called"
|
||||
else
|
||||
echo " client check: skipped (weston-simple-shm not installed)"
|
||||
fi
|
||||
|
|
|
|||
Loading…
Reference in New Issue