From f288d205cbdb098e607001ca1572865e228626ff Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 17 Jun 2026 16:19:04 +0530 Subject: [PATCH 1/5] src/auto_tiler: Sync smart_gapped on each tiling attempt. The first window is fully maximized, and then later when a second window is opened, we don't get border for the first window when it is in focus because after tiling the first window has stale value for smart_gapped, and thus is_single_max_screen() returns true, causing permitted in show_border() to be false. Let's set smart_gapped for each window in a tree each time we tile. Closes: #1758 Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- src/auto_tiler.ts | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/auto_tiler.ts b/src/auto_tiler.ts index 2a5cc51f..848847fa 100644 --- a/src/auto_tiler.ts +++ b/src/auto_tiler.ts @@ -29,6 +29,43 @@ export class AutoTiler { this.attached = attached; } + private sync_smart_gapped(ext: Ext, fork: Fork) { + let toplevel = fork; + let parent = this.forest.parents.get(toplevel.entity); + + /* Walk upwards to get the actual top-level root from the fork. */ + while (parent) { + const next = this.forest.forks.get(parent); + if (!next) + break; + + toplevel = next; + parent = this.forest.parents.get(toplevel.entity); + } + + const smart_gapped = ( + toplevel.is_toplevel && + toplevel.smart_gapped && + toplevel.right === null + ); + + /* Set smart_gapped for each window. */ + for (const branch of this.forest.iter(toplevel.entity)) { + let entities: Entity[] = []; + + if (branch.inner.kind === NodeKind.WINDOW) + entities = [branch.inner.entity]; + else if (branch.inner.kind === NodeKind.STACK) + entities = branch.inner.entities; + + for (const entity of entities) { + const win = ext.windows.get(entity); + if (win) + win.smart_gapped = smart_gapped; + } + } + } + /** Swap window associations in the auto-tiler * * Call this when a window has swapped positions with another, so that we @@ -106,13 +143,6 @@ export class AutoTiler { rect.height -= ext.gap_outer * 2; } - if (fork.left.inner.kind === 2) { - const win = ext.windows.get(fork.left.inner.entity); - if (win) { - win.smart_gapped = fork.smart_gapped; - } - } - fork.area = fork.set_area(rect.clone()); fork.length_left = Math.round(fork.prev_ratio * fork.length()); this.tile(ext, fork, fork.area); @@ -132,7 +162,6 @@ export class AutoTiler { const [entity, fork] = this.forest.create_toplevel(win.entity, rect.clone(), workspace_id); this.forest.on_attach(entity, win.entity); fork.smart_gapped = smart_gaps; - win.smart_gapped = smart_gaps; this.tile(ext, fork, rect); } @@ -490,6 +519,7 @@ export class AutoTiler { } tile(ext: Ext, fork: Fork, area: Rectangle) { + this.sync_smart_gapped(ext, fork); this.forest.tile(ext, fork, area); } From 4688638cf2b638434a85c9aba57d74d91286f883 Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:25:43 +0530 Subject: [PATCH 2/5] src/stack: Prevent duplicate signals on close button spam. It caused GNOME crash due to bug in mutter, which was fixed in v49. But regardless, we should not spam the close signal. Also people might be using versions before 49. Closes: #1794 Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- src/stack.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/stack.ts b/src/stack.ts index 60d553bb..5667d3e3 100644 --- a/src/stack.ts +++ b/src/stack.ts @@ -91,8 +91,14 @@ const TabButton = GObject.registerClass( }), ); - close_button.connect('clicked', () => { - window.meta.delete(global.get_current_time()); + this._running_close = false; + + close_button.connect('clicked', () => { + if (!this._running_close) { + this._running_close = true; + window.meta.delete(global.get_current_time()); + this._running_close = false; + } }); close_button.set_x_align(Clutter.ActorAlign.END); From 2defe4352643af1c2d773c14e5475da3168d61a0 Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 17 Jun 2026 16:57:25 +0530 Subject: [PATCH 3/5] src/keybindings: Avoid re-registering window-management mode bindings. Entering window-management mode removed the normal bindings and re-added the management bindings, then did the inverse on exit. This leads to journalctl spam since we overwrite a keybinding, and mutter logs overwrite warnings. Register the window-management mode bindings once with it being inactive initially (Shell.ActionMode.NONE), and later enable it (Shell.ActionMode.NORMAL) when entering the management mode, and then disabling on exit. Closes: #1820 Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- src/extension.ts | 2 ++ src/keybindings.ts | 26 ++++++++++++++++++++++++-- src/tiling.ts | 17 +++++++++++++++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 503407f3..c7599b7d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2715,6 +2715,7 @@ export default class PopShellExtension extends Extension { } ext.keybindings.enable(ext.keybindings.global).enable(ext.keybindings.window_focus); + ext.tiler.enable_keybindings(ext); if (ext.settings.tile_by_default()) { ext.auto_tile_on(); @@ -2740,6 +2741,7 @@ export default class PopShellExtension extends Extension { layoutManager.removeChrome(ext.overlay); ext.keybindings.disable(ext.keybindings.global).disable(ext.keybindings.window_focus); + ext.tiler.disable_keybindings(ext); if (ext.auto_tiler) { ext.auto_tiler.destroy(ext); diff --git a/src/keybindings.ts b/src/keybindings.ts index 394ea4e6..69cb9752 100644 --- a/src/keybindings.ts +++ b/src/keybindings.ts @@ -8,6 +8,8 @@ export class Keybindings { global: Object; window_focus: Object; + private active: Set = new Set(); + private ext: Ext; constructor(ext: Ext) { @@ -62,15 +64,22 @@ export class Keybindings { }; } - enable(keybindings: any) { + enable(keybindings: any, modes: number = Shell.ActionMode.NORMAL) { for (const name in keybindings) { + if (this.active.has(name)) { + wm.allowKeybinding(name, modes); + continue; + } + wm.addKeybinding( name, this.ext.settings.ext, Meta.KeyBindingFlags.NONE, - Shell.ActionMode.NORMAL, + modes, keybindings[name], ); + + this.active.add(name); } return this; @@ -78,7 +87,20 @@ export class Keybindings { disable(keybindings: Object) { for (const name in keybindings) { + if (!this.active.has(name)) + continue; + wm.removeKeybinding(name); + this.active.delete(name); + } + + return this; + } + + allow(keybindings: Object, modes: number) { + for (const name in keybindings) { + if (this.active.has(name)) + wm.allowKeybinding(name, modes); } return this; diff --git a/src/tiling.ts b/src/tiling.ts index 8ea72562..7577f435 100644 --- a/src/tiling.ts +++ b/src/tiling.ts @@ -17,6 +17,7 @@ import { AutoTiler } from './auto_tiler.js'; import { Fork } from './fork.js'; import Meta from 'gi://Meta'; +import Shell from 'gi://Shell'; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; const { ShellWindow } = window; @@ -60,6 +61,14 @@ export class Tiler { }; } + enable_keybindings(ext: Ext) { + ext.keybindings.enable(this.keybindings, Shell.ActionMode.NONE); + } + + disable_keybindings(ext: Ext) { + ext.keybindings.disable(this.keybindings); + } + toggle_orientation(ext: Ext) { const window = ext.focus_window(); if (window && ext.auto_tiler) { @@ -763,7 +772,9 @@ export class Tiler { }); } - ext.keybindings.disable(ext.keybindings.window_focus).enable(this.keybindings); + ext.keybindings + .allow(ext.keybindings.window_focus, Shell.ActionMode.NONE) + .allow(this.keybindings, Shell.ActionMode.NORMAL); } } @@ -817,7 +828,9 @@ export class Tiler { ext.overlay.visible = false; // Disable tiling keybindings - ext.keybindings.disable(this.keybindings).enable(ext.keybindings.window_focus); + ext.keybindings + .allow(this.keybindings, Shell.ActionMode.NONE) + .allow(ext.keybindings.window_focus, Shell.ActionMode.NORMAL); } } From f96f09f1d025c3bb3b6ab360843be7aa76328421 Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:06:46 +0530 Subject: [PATCH 4/5] src: Fix (un)maximize usage for GNOME 49. The API had changed here: https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4415 Thus we had warnings being logged when in stacked mode. The commit which added support for GNOME 49 forgot to change that. Also, to be in line with the fixes commit, we remove the unmaxize(HORIZONTAL); unmaximize(VERTICAL); unmaximize(BOTH); code to checking for whether the window is maximized and then using BOTH (i.e. its replacement) directly. Closes: #1801 Fixes: d22d2ff05989 ("fix: update window maximization logic to use new properties and added gnome 49 shell version in metadata") Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- src/extension.ts | 14 ++++++-------- src/mod.d.ts | 8 +++++--- src/tiling.ts | 2 +- src/window.ts | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index c7599b7d..ed3c9664 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -909,10 +909,10 @@ export class Ext extends Ecs.System { ) { if (prev.rect().contains(win.rect())) { if (prev.is_maximized()) { - prev.meta.unmaximize(Meta.MaximizeFlags.BOTH); + prev.meta.unmaximize(); } } else if (prev.stack) { - prev.meta.unmaximize(Meta.MaximizeFlags.BOTH); + prev.meta.unmaximize(); this.auto_tiler.forest.stacks.get(prev.stack)?.restack(); } } @@ -1034,7 +1034,7 @@ export class Ext extends Ecs.System { compare.is_maximized() && win.entity[0] !== compare.entity[0] ) { - compare.meta.unmaximize(Meta.MaximizeFlags.BOTH); + compare.meta.unmaximize(); } } } @@ -1257,10 +1257,8 @@ export class Ext extends Ecs.System { } if (this.auto_tiler) { - if (this.is_floating(win)) { - win.meta.unmaximize(Meta.MaximizeFlags.HORIZONTAL); - win.meta.unmaximize(Meta.MaximizeFlags.VERTICAL); - win.meta.unmaximize(Meta.MaximizeFlags.BOTH); + if (this.is_floating(win) && win.is_maximized()) { + win.meta.unmaximize(); } this.register(Events.window_move(this, win, rect)); @@ -1268,7 +1266,7 @@ export class Ext extends Ecs.System { win.move(this, rect, () => { }); // if the resulting dimensions of rect == next if (rect.width == next_area.width && rect.height == next_area.height) { - win.meta.maximize(Meta.MaximizeFlags.BOTH); + win.meta.maximize(); } } } diff --git a/src/mod.d.ts b/src/mod.d.ts index e1ac012f..9b039dad 100644 --- a/src/mod.d.ts +++ b/src/mod.d.ts @@ -306,13 +306,15 @@ declare namespace Meta { is_skip_taskbar(): boolean; make_above(): void; make_fullscreen(): void; - maximize(flags: MaximizeFlags): void; + maximize(): void; + unmaximize(): void; + unminimize(): void; + set_maximize_flags(flags: MaximizeFlags): void; + set_unmaximize_flags(flags: MaximizeFlags): void; move_frame(user_op: boolean, x: number, y: number): void; move_resize_frame(user_op: boolean, x: number, y: number, w: number, h: number): boolean; raise(): void; skip_taskbar: boolean; - unmaximize(flags: any): void; - unminimize(): void; } interface WindowActor extends Clutter.Actor { diff --git a/src/tiling.ts b/src/tiling.ts index 7577f435..58f1dd0d 100644 --- a/src/tiling.ts +++ b/src/tiling.ts @@ -758,7 +758,7 @@ export class Tiler { this.window = win.entity; if (win.is_maximized()) { - win.meta.unmaximize(Meta.MaximizeFlags.BOTH); + win.meta.unmaximize(); } // Set overlay to match window diff --git a/src/window.ts b/src/window.ts index 2fbd2928..0adf2d5b 100644 --- a/src/window.ts +++ b/src/window.ts @@ -373,7 +373,7 @@ export class ShellWindow { if (actor) { if (this.is_maximized()) { - meta.unmaximize(Meta.MaximizeFlags.BOTH); + meta.unmaximize(); } actor.remove_all_transitions(); From 4038ef0de15d0b197693bec15b85664e04c040fc Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:14:29 +0530 Subject: [PATCH 5/5] README: Add new branch. Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a8d76ebd..8242b201 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ Use the branch corresponding to your GNOME Shell version (`git checkout branch_n - **GNOME 42 through 44:** Use the `master_jammy` branch. - **GNOME 45:** Use the `master_mantic` branch. - **GNOME 46+:** Use the `master_noble` branch. +- **GNOME 49+:** Use the `master_resolute` branch. GNU Make and TypeScript are also required to build the project.