wayland: Phase 67 — WWindow.workspace; safe workspace switching without frame

Add int workspace to WWindow so Wayland-managed windows (which have no
WFrameWindow and therefore no frame->workspace slot) can track their
workspace assignment.  Set wwin->workspace = workspace in the Wayland
branch of wManageWindow alongside the existing geometry recording.

Guard wWorkspaceForceChange against NULL frame dereferences: introduce a
tmp_workspace local that falls back to tmp->workspace when frame is NULL,
and protect the omnipresent workspace assignment the same way.  Also add
a NULL guard for tmp->icon->core in the miniwindow-unmap path.

This prevents NULL dereferences if workspace switching is triggered
(e.g., via a root-menu action) while Wayland windows are managed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Levi Neely 2026-03-28 09:38:07 +01:00
parent a2571be53e
commit e60dff8ffd
4 changed files with 21 additions and 4 deletions

View File

@ -1171,13 +1171,17 @@ WWindow *wManageWindow(WScreen *scr, WNativeWindow window)
/* to make sure the window receives it's new position after reparenting */
wWindowSynthConfigureNotify(wwin);
} else {
/* Wayland: record geometry from normal hints; frame stays NULL */
/* Wayland: record geometry from normal hints; frame stays NULL.
* Phase 67: store workspace in wwin->workspace since there is no
* frame->workspace slot; wWorkspaceForceChange uses this field
* when frame is NULL to avoid NULL dereferences on WS switch. */
wwin->client.x = x;
wwin->client.y = y;
wwin->client.width = width;
wwin->client.height = height;
wwin->frame_x = x;
wwin->frame_y = y;
wwin->workspace = workspace;
}
/* Setup descriptors and save window to internal lists */

View File

@ -294,6 +294,10 @@ typedef struct WWindow {
int icon_w, icon_h;
RImage *net_icon_image; /* Window Image */
WNETWindowType window_type; /* _NET_WM_WINDOW_TYPE (WNW_TYPE_*) */
/* Phase 67: workspace slot for Wayland path (frame is NULL, so
* frame->workspace is unavailable; workspace operations use this
* field instead when !frame). */
int workspace;
} WWindow;
#define HAS_TITLEBAR(w) (!(WFLAGP((w), no_titlebar) || (w)->flags.fullscreen))

View File

@ -467,7 +467,10 @@ void wWorkspaceForceChange(WScreen * scr, int workspace)
* but will create annoyance for every other application
*/
while (tmp) {
if (tmp->frame->workspace != workspace && !tmp->flags.selected) {
/* Phase 67: Wayland windows have no frame; use wwin->workspace
* as the workspace slot when frame is NULL. */
int tmp_workspace = tmp->frame ? tmp->frame->workspace : tmp->workspace;
if (tmp_workspace != workspace && !tmp->flags.selected) {
/* unmap windows not on this workspace */
if ((tmp->flags.mapped || tmp->flags.shaded) &&
!IS_OMNIPRESENT(tmp) && !tmp->flags.changing_workspace) {
@ -480,7 +483,7 @@ void wWorkspaceForceChange(WScreen * scr, int workspace)
}
/* also unmap miniwindows not on this workspace */
if (!wPreferences.sticky_icons && tmp->flags.miniaturized &&
tmp->icon && !IS_OMNIPRESENT(tmp)) {
tmp->icon && !IS_OMNIPRESENT(tmp) && tmp->icon->core) {
wm_backend->window_unmap(tmp->icon->core->window);
tmp->icon->mapped = 0;
}
@ -488,7 +491,10 @@ void wWorkspaceForceChange(WScreen * scr, int workspace)
if (IS_OMNIPRESENT(tmp)) {
WApplication *wapp = wApplicationOf(tmp->main_window);
tmp->frame->workspace = workspace;
if (tmp->frame)
tmp->frame->workspace = workspace;
else
tmp->workspace = workspace;
if (wapp) {
wapp->last_workspace = workspace;

View File

@ -55,6 +55,9 @@
# 24. (Phase 66) XDG popup initial configure scheduled so context menus and
# autocomplete dropdowns can commit first buffer without stalling
# (build-only check — weston-simple-shm creates no popups).
# 25. (Phase 67) WWindow.workspace added; wWorkspaceForceChange uses it as
# fallback when frame==NULL so workspace switching is safe in Wayland
# (build-only check — workspace switch requires keyboard shortcuts).
#
# This test grows as wl_backend vtable slots are implemented.
#