From 36aa1c57258964b882f5d37f52fabdf32720d7eb Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Sat, 28 Mar 2026 09:43:09 +0100 Subject: [PATCH] =?UTF-8?q?wayland:=20Phase=2069=20=E2=80=94=20WIN=5FWORKS?= =?UTF-8?q?PACE=20macro;=20safe=20workspace=20reads=20without=20frame?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add WIN_WORKSPACE(w) macro to window.h that returns frame->workspace when the window has a frame (X11 path) or wwin->workspace when it does not (Wayland path, set in Phase 67). Replace all unsafe frame->workspace reads with WIN_WORKSPACE() in: - window.c: wWindowObscuresWindow (workspace comparison + geometry), wWindowFocusPrev/Next (same-workspace filter), wWindowSaveState (state serialization) - session.c: session save (workspace name lookup) - switchmenu.c: all five switch-menu workspace comparisons/lookups Also guard wWindowObscuresWindow's frame->core width/height reads with a ternary that falls back to client.{width,height} for frameless windows. This eliminates the last class of NULL frame->workspace dereferences reachable from workspace-switch, focus-cycle, session-save, and switch-menu code paths. Co-Authored-By: Claude Sonnet 4.6 --- src/session.c | 2 +- src/switchmenu.c | 10 +++++----- src/window.c | 17 +++++++++-------- src/window.h | 4 ++++ tests/wl-smoke-test.sh | 3 +++ 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/session.c b/src/session.c index ee26bbb..da05994 100644 --- a/src/session.c +++ b/src/session.c @@ -219,7 +219,7 @@ static WMPropList *makeWindowState(WWindow * wwin, WApplication * wapp) name = WMCreatePLString(buffer); cmd = WMCreatePLString(command); - workspace = WMCreatePLString(scr->workspaces[wwin->frame->workspace]->name); + workspace = WMCreatePLString(scr->workspaces[WIN_WORKSPACE(wwin)]->name); shaded = wwin->flags.shaded ? sYes : sNo; miniaturized = wwin->flags.miniaturized ? sYes : sNo; diff --git a/src/switchmenu.c b/src/switchmenu.c index 7fb7e8e..ad56304 100644 --- a/src/switchmenu.c +++ b/src/switchmenu.c @@ -160,7 +160,7 @@ static int menuIndexForWindow(WMenu * menu, WWindow * wwin, int old_pos) move_down = 1; if (IS_OMNIPRESENT(tw) || (tw != wwin && - tw->frame->workspace >= wwin->frame->workspace)) + WIN_WORKSPACE(tw) >= WIN_WORKSPACE(wwin))) return idx - move_down; } @@ -219,7 +219,7 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action) snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, "[*]"); else snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, "[%s]", - scr->workspaces[wwin->frame->workspace]->name); + scr->workspaces[WIN_WORKSPACE(wwin)]->name); if (wwin->flags.hidden) { entry->flags.indicator_type = MI_HIDDEN; @@ -277,7 +277,7 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action) } else { snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, "[%s]", - scr->workspaces[wwin->frame->workspace]->name); + scr->workspaces[WIN_WORKSPACE(wwin)]->name); } rt = entry->rtext; @@ -350,12 +350,12 @@ static void UpdateSwitchMenuWorkspace(WScreen *scr, int workspace) for (i = 0; i < menu->entry_no; i++) { wwin = (WWindow *) menu->entries[i]->clientdata; - if (wwin->frame->workspace == workspace && !IS_OMNIPRESENT(wwin)) { + if (WIN_WORKSPACE(wwin) == workspace && !IS_OMNIPRESENT(wwin)) { if (IS_OMNIPRESENT(wwin)) snprintf(menu->entries[i]->rtext, MAX_WORKSPACENAME_WIDTH, "[*]"); else snprintf(menu->entries[i]->rtext, MAX_WORKSPACENAME_WIDTH, "[%s]", - scr->workspaces[wwin->frame->workspace]->name); + scr->workspaces[WIN_WORKSPACE(wwin)]->name); menu->flags.realized = 0; } } diff --git a/src/window.c b/src/window.c index 1ca8490..be7bce6 100644 --- a/src/window.c +++ b/src/window.c @@ -445,13 +445,14 @@ Bool wWindowObscuresWindow(WWindow *wwin, WWindow *obscured) { int w1, h1, w2, h2; - w1 = wwin->frame->core->width; - h1 = wwin->frame->core->height; - w2 = obscured->frame->core->width; - h2 = obscured->frame->core->height; + /* Phase 69: Wayland windows have no frame; use client geometry. */ + w1 = wwin->frame ? (int)wwin->frame->core->width : (int)wwin->client.width; + h1 = wwin->frame ? (int)wwin->frame->core->height : (int)wwin->client.height; + w2 = obscured->frame ? (int)obscured->frame->core->width : (int)obscured->client.width; + h2 = obscured->frame ? (int)obscured->frame->core->height : (int)obscured->client.height; if (!IS_OMNIPRESENT(wwin) && !IS_OMNIPRESENT(obscured) - && wwin->frame->workspace != obscured->frame->workspace) + && WIN_WORKSPACE(wwin) != WIN_WORKSPACE(obscured)) return False; if (wwin->frame_x + w1 < obscured->frame_x @@ -1704,7 +1705,7 @@ void wWindowFocusPrev(WWindow *wwin, Bool inSameWorkspace) tmp = tmp->prev; if (inSameWorkspace) - while (tmp && (tmp->frame->workspace != wwin->frame->workspace)) + while (tmp && (WIN_WORKSPACE(tmp) != WIN_WORKSPACE(wwin))) tmp = tmp->next; wWindowSingleFocus(tmp); @@ -1719,7 +1720,7 @@ void wWindowFocusNext(WWindow *wwin, Bool inSameWorkspace) tmp = wwin->prev; if (inSameWorkspace) - while (tmp && (tmp->frame->workspace != wwin->frame->workspace)) + while (tmp && (WIN_WORKSPACE(tmp) != WIN_WORKSPACE(wwin))) tmp = tmp->prev; wWindowSingleFocus(tmp); @@ -2239,7 +2240,7 @@ void wWindowSaveState(WWindow *wwin) int i; memset(data, 0, sizeof(long) * 10); - data[0] = wwin->frame->workspace; + data[0] = WIN_WORKSPACE(wwin); data[1] = wwin->flags.miniaturized; data[2] = wwin->flags.shaded; data[3] = wwin->flags.hidden; diff --git a/src/window.h b/src/window.h index 42c74c0..453f85e 100644 --- a/src/window.h +++ b/src/window.h @@ -307,6 +307,10 @@ typedef struct WWindow { #define IS_RESIZABLE(w) (!(WFLAGP((w), no_resizable) || (w)->flags.fullscreen)) #define IS_OMNIPRESENT(w) ((w)->flags.omnipresent | WFLAGP(w, omnipresent)) #define WINDOW_LEVEL(w) ((w)->frame->core->stacking->window_level) +/* Phase 69: safe workspace read for windows that may have no frame (Wayland). + * frame->workspace is the authoritative slot for X11 windows; wwin->workspace + * is the fallback set in wManageWindow for Wayland windows. */ +#define WIN_WORKSPACE(w) ((w)->frame ? (w)->frame->workspace : (w)->workspace) /* * Changes to this must update wWindowSaveState/getSavedState diff --git a/tests/wl-smoke-test.sh b/tests/wl-smoke-test.sh index d430a81..9eccb8a 100755 --- a/tests/wl-smoke-test.sh +++ b/tests/wl-smoke-test.sh @@ -61,6 +61,9 @@ # 26. (Phase 68) placement.c guards: calcSumOfCoveredAreas/window_overlaps # skip frameless (Wayland) windows safely; frame->workspace accesses # in icon-placement helpers use wwin->workspace fallback. +# 27. (Phase 69) WIN_WORKSPACE(w) macro added; frame->workspace reads in +# wWindowFocusPrev/Next, wWindowObscuresWindow, wWindowSaveState, +# session.c, and switchmenu.c replaced with the macro. # # This test grows as wl_backend vtable slots are implemented. #