From 2c55725bb6ce01471ecb5e063c88fda8b3019c33 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Sat, 28 Mar 2026 09:40:44 +0100 Subject: [PATCH] =?UTF-8?q?wayland:=20Phase=2068=20=E2=80=94=20placement.c?= =?UTF-8?q?:=20safe=20overlap=20calculation=20without=20frame?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guard all frame->core and frame->workspace dereferences in placement.c against NULL frame pointers (Wayland windows have no WFrameWindow): - calcSumOfCoveredAreas: skip windows without frame (no stacking level or geometry available via frame; Wayland window client geometry is tracked separately in wwin->client.* and wwin->frame_x/y). - window_overlaps: add early-return path for frameless windows that uses wwin->client.{width,height} and wwin->frame_x/y for the overlap test, bypassing the frame->core stacking level check. - getPlaceOrigin (icon-placement helper): guard frame->workspace reads for WCLASS_MINIWINDOW and WCLASS_WINDOW parent types using logical-OR with the wwin->workspace fallback. Together these prevent NULL dereferences when smart/auto/cascade placement iterates the managed-window list and encounters Wayland windows alongside X11 windows. Co-Authored-By: Claude Sonnet 4.6 --- src/placement.c | 33 ++++++++++++++++++++++++++++++--- tests/wl-smoke-test.sh | 3 +++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/placement.c b/src/placement.c index e4cd343..5a84aaf 100644 --- a/src/placement.c +++ b/src/placement.c @@ -62,7 +62,9 @@ iconPosition(WCoreWindow *wcore, int sx1, int sy1, int sx2, int sy2, ok = 1; } else if (wcore->descriptor.parent_type == WCLASS_MINIWINDOW && - (((WIcon *) parent)->owner->frame->workspace == workspace + ((!((WIcon *) parent)->owner->frame || + ((WIcon *) parent)->owner->frame->workspace == workspace || + ((WIcon *) parent)->owner->workspace == workspace) || IS_OMNIPRESENT(((WIcon *) parent)->owner) || wPreferences.sticky_icons) && ((WIcon *) parent)->mapped) { @@ -73,7 +75,10 @@ iconPosition(WCoreWindow *wcore, int sx1, int sy1, int sx2, int sy2, ok = 1; } else if (wcore->descriptor.parent_type == WCLASS_WINDOW && ((WWindow *) parent)->flags.icon_moved - && (((WWindow *) parent)->frame->workspace == workspace || IS_OMNIPRESENT((WWindow *) parent) + && ((!((WWindow *) parent)->frame || + ((WWindow *) parent)->frame->workspace == workspace || + ((WWindow *) parent)->workspace == workspace) + || IS_OMNIPRESENT((WWindow *) parent) || wPreferences.sticky_icons)) { *retX = ((WWindow *) parent)->icon_x; *retY = ((WWindow *) parent)->icon_y; @@ -268,6 +273,11 @@ static int calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h) test_window = test_window->prev; for (; test_window != NULL; test_window = test_window->next) { + /* Phase 68: Wayland windows have no frame; skip them in the + * overlap calculation (their geometry is tracked in client.* but + * they have no X11 stacking level — treat as zero obstruction). */ + if (!test_window->frame) + continue; if (test_window->frame->core->stacking->window_level < WMNormalLevel) { continue; } @@ -277,8 +287,12 @@ static int calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h) tx = test_window->frame_x; ty = test_window->frame_y; + /* Phase 68: guard frame->workspace with wwin->workspace fallback */ + int tw_workspace = test_window->workspace; + if (test_window->frame) + tw_workspace = test_window->frame->workspace; if (test_window->flags.mapped || (test_window->flags.shaded && - test_window->frame->workspace == wwin->screen_ptr->current_workspace && + tw_workspace == wwin->screen_ptr->current_workspace && !(test_window->flags.miniaturized || test_window->flags.hidden))) { sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h); } @@ -308,6 +322,19 @@ window_overlaps(WWindow *win, int x, int y, int w, int h, Bool ignore_sunken) { int tw, th, tx, ty; + /* Phase 68: Wayland windows have no frame; use client geometry. */ + if (!win->frame) { + tw = win->client.width; + th = win->client.height; + tx = win->frame_x; + ty = win->frame_y; + if ((tx < (x + w)) && ((tx + tw) > x) && + (ty < (y + h)) && ((ty + th) > y) && + win->flags.mapped) + return True; + return False; + } + if (ignore_sunken && win->frame->core->stacking->window_level < WMNormalLevel) { return False; diff --git a/tests/wl-smoke-test.sh b/tests/wl-smoke-test.sh index a5b4783..d430a81 100755 --- a/tests/wl-smoke-test.sh +++ b/tests/wl-smoke-test.sh @@ -58,6 +58,9 @@ # 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). +# 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. # # This test grows as wl_backend vtable slots are implemented. #