From 2ae1f712e64044b093ea1302e84815042101bab9 Mon Sep 17 00:00:00 2001 From: PhilMeyr <16798563+PhilMeyr@users.noreply.github.com> Date: Fri, 31 Jul 2026 11:43:38 +0200 Subject: [PATCH] moveHandler: Remove the monitor grace period timer when a grab ends When a drag crosses a monitor boundary, _edgeTilingPreview() arms a 150 ms timer that captures the dragged window and re-enters _edgeTilingPreview() when it fires. Nothing removes that source when the grab ends, so a window destroyed within those 150 ms leaves it pending. It then reaches window.get_work_area_for_monitor(), which asserts in meta_window_get_workspaces() on an unmanaging window and aborts the shell. Remove the pending source in the finally block of _onMoveFinished(), and guard the callback with get_compositor_private(), since is_grabbed() is a property of the display rather than of the window. Closes #435 --- .../src/extension/moveHandler.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tiling-assistant@leleat-on-github/src/extension/moveHandler.js b/tiling-assistant@leleat-on-github/src/extension/moveHandler.js index 3e80664..7f7fa87 100644 --- a/tiling-assistant@leleat-on-github/src/extension/moveHandler.js +++ b/tiling-assistant@leleat-on-github/src/extension/moveHandler.js @@ -284,6 +284,14 @@ export default class TilingMoveHandler { this._dragSprite = null; } } finally { + // The grace period timer captures `window`, so leaving it pending + // lets it re-enter _edgeTilingPreview() after the window was + // unmanaged, which aborts mutter. + if (this._latestMonitorLockTimerId) { + GLib.Source.remove(this._latestMonitorLockTimerId); + this._latestMonitorLockTimerId = null; + } + if (this._posChangedId) { window.disconnect(this._posChangedId); this._posChangedId = 0; @@ -488,7 +496,9 @@ export default class TilingMoveHandler { // Only update the monitorNr, if the latest timer timed out. if (timerId === this._latestMonitorLockTimerId) { this._monitorNr = global.display.get_current_monitor(); - if (global.display.is_grabbed()) + // is_grabbed() is about the display, not about `window` + // still being managed. + if (global.display.is_grabbed() && window.get_compositor_private()) this._edgeTilingPreview(window, grabOp); }