From fbf6c92961822dc0a5254e2d37b53fb044efb4be Mon Sep 17 00:00:00 2001 From: Philippe Date: Thu, 10 Sep 2026 16:49:50 +0200 Subject: [PATCH 1/2] 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 | 9 ++++++++- 1 file changed, 8 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 7b0c524..e7dcd20 100644 --- a/tiling-assistant@leleat-on-github/src/extension/moveHandler.js +++ b/tiling-assistant@leleat-on-github/src/extension/moveHandler.js @@ -293,6 +293,12 @@ export default class TilingMoveHandler { this._dragSprite = null; } } finally { + // Leaving it pending would re-enter _edgeTilingPreview() on a destroyed window + if (this._latestMonitorLockTimerId) { + GLib.Source.remove(this._latestMonitorLockTimerId); + this._latestMonitorLockTimerId = null; + } + if (this._posChangedId) { window.disconnect(this._posChangedId); this._posChangedId = 0; @@ -497,7 +503,8 @@ 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()) + // check that the window still exists, and a grab is still active + if (global.display.is_grabbed() && window.get_compositor_private()) this._edgeTilingPreview(window, grabOp); } From 730fdd9015714f9a58941c1af702f10a50235245 Mon Sep 17 00:00:00 2001 From: Philippe Date: Thu, 10 Sep 2026 16:49:58 +0200 Subject: [PATCH 2/2] moveHandler: Don't log an error when a dragged window is destroyed _onMoveFinished() is a try/finally with no catch, so the Error thrown by window.assertExistence() escapes the grab-op-end handler and GJS dumps a stack trace, even though the finally block already handles the case. Catch it around the assertion only, so errors from the tiling code below are still reported, like _onWindowWorkspaceChanged() does. --- .../src/extension/moveHandler.js | 7 ++++++- 1 file changed, 6 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 e7dcd20..d49f5cb 100644 --- a/tiling-assistant@leleat-on-github/src/extension/moveHandler.js +++ b/tiling-assistant@leleat-on-github/src/extension/moveHandler.js @@ -257,7 +257,12 @@ export default class TilingMoveHandler { _onMoveFinished(window) { try { - window.assertExistence(); + // Ignore the expected error when the window was destroyed during the grab. + try { + window.assertExistence(); + } catch { + return; + } if (this._tileRect) { // Ctrl-drag to replace some windows in a tile group / create a new tile group