From c1f89c3d98826c69284e5d9c8125c035ad80ece4 Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 19 Aug 2026 14:59:35 +0530 Subject: [PATCH 01/15] Revert "feat: GNOME 50 support" This reverts commit 7898b65c20735057faf0797f8ed056704ca55f0d. GNOME 49+ support is a breaking change for older versions. Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- .gitignore | 3 +-- Makefile | 2 +- metadata.json | 3 +-- src/color_dialog/tsconfig.json | 2 +- src/floating_exceptions/src/main.ts | 2 +- src/floating_exceptions/tsconfig.json | 2 +- src/utils.ts | 6 +----- tsconfig.json | 2 +- 8 files changed, 8 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index e67238b4..8a8458bc 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,4 @@ debian/* !debian/*install .confirm_shortcut_change .vscode -node_modules -tsconfig.tsbuildinfo \ No newline at end of file +node_modules \ No newline at end of file diff --git a/Makefile b/Makefile index bffe3752..d83c5946 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ sources = src/*.ts *.css all: depcheck compile clean: - rm -rf _build target tsconfig.tsbuildinfo + rm -rf _build target # Configure local settings on system configure: diff --git a/metadata.json b/metadata.json index 84a0c5ed..41e860ff 100644 --- a/metadata.json +++ b/metadata.json @@ -9,7 +9,6 @@ "46", "47", "48", - "49", - "50" + "49" ] } diff --git a/src/color_dialog/tsconfig.json b/src/color_dialog/tsconfig.json index 6e61c341..b08b2ac7 100644 --- a/src/color_dialog/tsconfig.json +++ b/src/color_dialog/tsconfig.json @@ -4,8 +4,8 @@ "target": "es2015", "strict": true, "outDir": "../../target/color_dialog", - "rootDir": "./src", "forceConsistentCasingInFileNames": true, + "downlevelIteration": true, "lib": ["es2015"], "pretty": true, "removeComments": true, diff --git a/src/floating_exceptions/src/main.ts b/src/floating_exceptions/src/main.ts index de8d3126..9da3b700 100644 --- a/src/floating_exceptions/src/main.ts +++ b/src/floating_exceptions/src/main.ts @@ -6,7 +6,7 @@ import Gtk from 'gi://Gtk?version=3.0'; import Pango from 'gi://Pango'; /** The directory that this script is executed from. */ -const SCRIPT_DIR = GLib.path_get_dirname(new Error().stack!.split(':')[0].slice(1)); +const SCRIPT_DIR = GLib.path_get_dirname(new Error().stack.split(':')[0].slice(1)); /** Add our directory so we can import modules from it. */ imports.searchPath.push(SCRIPT_DIR); diff --git a/src/floating_exceptions/tsconfig.json b/src/floating_exceptions/tsconfig.json index fb275f36..c3a76870 100644 --- a/src/floating_exceptions/tsconfig.json +++ b/src/floating_exceptions/tsconfig.json @@ -4,8 +4,8 @@ "target": "es2015", // "strict": true, "outDir": "../../target/floating_exceptions", - "rootDir": "./src", "forceConsistentCasingInFileNames": true, + "downlevelIteration": true, "lib": ["es2015"], "pretty": true, "removeComments": true, diff --git a/src/utils.ts b/src/utils.ts index 8f335c09..e89a15c6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -10,11 +10,7 @@ const { Ok, Err } = result; const { Error } = error; export function is_wayland(): boolean { - if (typeof Meta.is_wayland_compositor === 'function') { - return Meta.is_wayland_compositor(); - } - // GNOME 50+ removed X11 support; always Wayland - return true; + return Meta.is_wayland_compositor(); } export function block_signal(object: GObject.Object, signal: SignalID) { diff --git a/tsconfig.json b/tsconfig.json index 16972668..82fa7c6c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,8 +5,8 @@ "target": "es2020", "strict": true, "outDir": "./target", - "rootDir": "./src", "forceConsistentCasingInFileNames": true, + "downlevelIteration": true, "lib": [ "es2020" ], From a57d7e2e59c6710783bb75173220693800c56597 Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 19 Aug 2026 14:59:42 +0530 Subject: [PATCH 02/15] Revert "fix: using Mtk instead of Meta in extension.ts" This reverts commit 3cb093b8e6a36c48dd5e84533dc874ea74cd8a9e. GNOME 49+ support is a breaking change for older versions. Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- src/extension.ts | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 503407f3..99f02088 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -44,13 +44,6 @@ import Gio from 'gi://Gio'; import St from 'gi://St'; import Shell from 'gi://Shell'; import Meta from 'gi://Meta'; -// Try to import Mtk for newer GNOME versions, fallback to Meta for older versions -let Mtk: any; -try { - Mtk = imports.gi.Mtk; -} catch (e) { - Mtk = null; -} const { GlobalEvent, WindowEvent } = Events; const { cursor_rect, is_keyboard_op, is_resize_op, is_move_op } = Lib; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; @@ -487,7 +480,7 @@ export class Ext extends Ecs.System { if (old) { try { GLib.source_remove(old); - } catch (_) { } + } catch (_) {} } const new_s = GLib.timeout_add(GLib.PRIORITY_LOW, 500, () => { @@ -1265,7 +1258,7 @@ export class Ext extends Ecs.System { this.register(Events.window_move(this, win, rect)); } else { - win.move(this, rect, () => { }); + 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); @@ -1488,7 +1481,7 @@ export class Ext extends Ecs.System { [area, monitor_attachment] = (win.stack === null && attach_to.stack === null && is_sibling) || - (win.stack === null && is_sibling) + (win.stack === null && is_sibling) ? [fork.area, false] : [attach_to.meta.get_frame_rect(), false]; } else { @@ -1519,8 +1512,8 @@ export class Ext extends Ecs.System { ? [area.x, area.y, half_width, area.height] : [area.x + half_width, area.y, half_width, area.height] : swap - ? [area.x, area.y, area.width, half_height] - : [area.x, area.y + half_height, area.width, half_height]; + ? [area.x, area.y, area.width, half_height] + : [area.x, area.y + half_height, area.width, half_height]; this.overlay.x = new_area[0]; this.overlay.y = new_area[1]; @@ -1549,8 +1542,8 @@ export class Ext extends Ecs.System { this.current_style = this.settings.is_dark() ? Style.Dark : this.settings.is_high_contrast() - ? Style.HighContrast - : Style.Light; + ? Style.HighContrast + : Style.Light; } /** Handle window maximization notifications */ @@ -2299,7 +2292,7 @@ export class Ext extends Ecs.System { this.moved_by_mouse = false; } - update_display_configuration_before() { } + update_display_configuration_before() {} update_display_configuration(workareas_only: boolean) { if (!this.auto_tiler || sessionMode.isLocked) return; @@ -2630,10 +2623,7 @@ export class Ext extends Ecs.System { cursor_status(): [Rectangle, number] { const cursor = cursor_rect(); - // Use Mtk.Rectangle if available (newer GNOME), otherwise fallback to Meta.Rectangle - const rect = Mtk ? - new Mtk.Rectangle({ x: cursor.x, y: cursor.y, width: 1, height: 1 }) : - new Meta.Rectangle({ x: cursor.x, y: cursor.y, width: 1, height: 1 }); + const rect = new Meta.Rectangle({ x: cursor.x, y: cursor.y, width: 1, height: 1 }); const monitor = display.get_monitor_index_for_rect(rect); return [cursor, monitor]; } @@ -2675,12 +2665,12 @@ let ext: Ext | null = null; let indicator: Indicator | null = null; declare global { - var popShellExtension: any; + var popShellExtension: any; } export default class PopShellExtension extends Extension { enable() { - globalThis.popShellExtension = this; + globalThis.popShellExtension = this; log.info('enable'); if (!ext) { From 506d7990d84b3a770087dbce9f8fe0c53c640362 Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 19 Aug 2026 14:59:47 +0530 Subject: [PATCH 03/15] Revert "fix: update window maximization logic to use new properties and added gnome 49 shell version in metadata" This reverts commit d22d2ff059898394eb737c7dc819c7624a970644. GNOME 49+ support is a breaking change for older versions. Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- metadata.json | 3 +-- src/auto_tiler.ts | 2 +- src/mod.d.ts | 3 +-- src/window.ts | 10 +++++----- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/metadata.json b/metadata.json index 41e860ff..705da42e 100644 --- a/metadata.json +++ b/metadata.json @@ -8,7 +8,6 @@ "45", "46", "47", - "48", - "49" + "48" ] } diff --git a/src/auto_tiler.ts b/src/auto_tiler.ts index 2a5cc51f..ccaafffe 100644 --- a/src/auto_tiler.ts +++ b/src/auto_tiler.ts @@ -700,7 +700,7 @@ export class AutoTiler { } private toggle_orientation_(ext: Ext, focused: ShellWindow): Result { - if (focused.is_maximized()) { + if (focused.meta.get_maximized()) { return Err('cannot toggle maximized window'); } diff --git a/src/mod.d.ts b/src/mod.d.ts index e1ac012f..bb840e58 100644 --- a/src/mod.d.ts +++ b/src/mod.d.ts @@ -275,8 +275,6 @@ declare namespace Meta { minimized: Readonly; window_type: Readonly; decorated: Readonly; - maximized_horizontally: Readonly; - maximized_vertically: Readonly; activate(time: number): void; change_workspace_by_index(workspace: number, append: boolean): void; @@ -286,6 +284,7 @@ declare namespace Meta { get_display(): Meta.Display | null; get_description(): string; get_frame_rect(): Rectangular; + get_maximized(): number; get_monitor(): number; get_pid(): number; get_role(): null | string; diff --git a/src/window.ts b/src/window.ts index 2fbd2928..5be8ae5e 100644 --- a/src/window.ts +++ b/src/window.ts @@ -276,7 +276,7 @@ export class ShellWindow { } is_maximized(): boolean { - return this.meta.maximized_horizontally || this.meta.maximized_vertically; + return this.meta.get_maximized() !== 0; } /** @@ -300,7 +300,7 @@ export class ShellWindow { } is_snap_edge(): boolean { - return this.meta.maximized_vertically && !this.meta.maximized_horizontally; + return this.meta.get_maximized() == Meta.MaximizeFlags.VERTICAL; } is_tilable(ext: Ext): boolean { @@ -372,9 +372,9 @@ export class ShellWindow { const actor = meta.get_compositor_private(); if (actor) { - if (this.is_maximized()) { - meta.unmaximize(Meta.MaximizeFlags.BOTH); - } + meta.unmaximize(Meta.MaximizeFlags.HORIZONTAL); + meta.unmaximize(Meta.MaximizeFlags.VERTICAL); + meta.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL); actor.remove_all_transitions(); ext.movements.insert(this.entity, clone); From 8ed1e8f0958f162801d6fd92fde7512926d18227 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 04/15] 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 ccaafffe..b9ffa33b 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 c4f56acef8dcaa6df4019004d9534414584de937 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 05/15] 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 ae5de88219ffffe0953104e6c72516d6fd5a2542 Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Fri, 3 Jul 2026 16:09:48 +0530 Subject: [PATCH 06/15] src/config: Add exceptions for wl-clipboard. wl-clipboard invisible window (literally just a dot shows up) can cause the tiler to make it take an entire window worth of space, and it is supposed to be immediately closed so we see a tiling flicker. And also sometimes the window dangles (there is an open bug for it), so it takes up unnecessary space. This window is not supposed to be tiled, in fact it has skip-taskbar flag set. Let's add exceptions for it. Closes: #1819 Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- src/config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config.ts b/src/config.ts index 440d4ec0..9db10cf5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -59,6 +59,7 @@ export const DEFAULT_FLOAT_RULES: Array = [ { class: 'zoom' }, { class: '^.*action=join.*$' }, { class: 'gjs' }, + { class: 'io.github.bugaevc.wl-clipboard' }, ]; export interface WindowRule { @@ -77,6 +78,7 @@ export const SKIPTASKBAR_EXCEPTIONS: Array = [ { class: 'Guake' }, { class: 'Com.github.amezin.ddterm' }, { class: 'plank' }, + { class: 'io.github.bugaevc.wl-clipboard' }, ]; export interface FloatRule { From 955257f403efba0452b75f272314203a10a51ce8 Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Sat, 4 Jul 2026 16:48:11 +0530 Subject: [PATCH 07/15] src/config: Add exceptions for nautilus file preview. Nautilus file previews, like pressing space on a PDF file, is a temporary window and it can't zoom in, it is supposed to be overlaid. Let's add exception for it. Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- src/config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config.ts b/src/config.ts index 9db10cf5..29ba761c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -60,6 +60,7 @@ export const DEFAULT_FLOAT_RULES: Array = [ { class: '^.*action=join.*$' }, { class: 'gjs' }, { class: 'io.github.bugaevc.wl-clipboard' }, + { class: 'org.gnome.NautilusPreviewer' }, ]; export interface WindowRule { @@ -79,6 +80,7 @@ export const SKIPTASKBAR_EXCEPTIONS: Array = [ { class: 'Com.github.amezin.ddterm' }, { class: 'plank' }, { class: 'io.github.bugaevc.wl-clipboard' }, + { class: 'org.gnome.NautilusPreviewer' }, ]; export interface FloatRule { From f9576b8c7cfcf13bdcc4b5a52626bec1639145f6 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 08/15] 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 99f02088..442ee8c9 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2705,6 +2705,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(); @@ -2730,6 +2731,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 7f1fec0eb5f38e14d3a507110eb6377de6e7e573 Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 19 Aug 2026 15:12:07 +0530 Subject: [PATCH 09/15] metadata: Remove GNOME 45. This branch supports GNOME 46+ only according to README. Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- metadata.json | 1 - 1 file changed, 1 deletion(-) diff --git a/metadata.json b/metadata.json index 705da42e..51d2b7ac 100644 --- a/metadata.json +++ b/metadata.json @@ -5,7 +5,6 @@ "uuid": "pop-shell@system76.com", "settings-schema": "org.gnome.shell.extensions.pop-shell", "shell-version": [ - "45", "46", "47", "48" From 4b0a81620fe196f65254e8d7672d179b4acfda90 Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 19 Aug 2026 15:09:30 +0530 Subject: [PATCH 10/15] README, metadata: Add new branch for GNOME 49+. Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- README.md | 3 ++- metadata.json | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a8d76ebd..4f2c6ef8 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,8 @@ Use the branch corresponding to your GNOME Shell version (`git checkout branch_n - **GNOME 3.36 through 41:** Use the `master_focal` branch. - **GNOME 42 through 44:** Use the `master_jammy` branch. - **GNOME 45:** Use the `master_mantic` branch. -- **GNOME 46+:** Use the `master_noble` branch. +- **GNOME 46 through 48:** Use the `master_noble` branch. +- **GNOME 49+:** Use the `master_resolute` branch. GNU Make and TypeScript are also required to build the project. diff --git a/metadata.json b/metadata.json index 51d2b7ac..32718183 100644 --- a/metadata.json +++ b/metadata.json @@ -5,8 +5,6 @@ "uuid": "pop-shell@system76.com", "settings-schema": "org.gnome.shell.extensions.pop-shell", "shell-version": [ - "46", - "47", - "48" + "49" ] } From 8ccc7e8455a4d5d42e9eeaf6e68f944e938ac9f8 Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 19 Aug 2026 15:04:16 +0530 Subject: [PATCH 11/15] Reapply "fix: update window maximization logic to use new properties and added gnome 49 shell version in metadata" This reverts commit 506d7990d84b3a770087dbce9f8fe0c53c640362. Add back the fix for GNOME 49+ branch. Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- src/auto_tiler.ts | 2 +- src/mod.d.ts | 3 ++- src/window.ts | 10 +++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/auto_tiler.ts b/src/auto_tiler.ts index b9ffa33b..848847fa 100644 --- a/src/auto_tiler.ts +++ b/src/auto_tiler.ts @@ -730,7 +730,7 @@ export class AutoTiler { } private toggle_orientation_(ext: Ext, focused: ShellWindow): Result { - if (focused.meta.get_maximized()) { + if (focused.is_maximized()) { return Err('cannot toggle maximized window'); } diff --git a/src/mod.d.ts b/src/mod.d.ts index bb840e58..e1ac012f 100644 --- a/src/mod.d.ts +++ b/src/mod.d.ts @@ -275,6 +275,8 @@ declare namespace Meta { minimized: Readonly; window_type: Readonly; decorated: Readonly; + maximized_horizontally: Readonly; + maximized_vertically: Readonly; activate(time: number): void; change_workspace_by_index(workspace: number, append: boolean): void; @@ -284,7 +286,6 @@ declare namespace Meta { get_display(): Meta.Display | null; get_description(): string; get_frame_rect(): Rectangular; - get_maximized(): number; get_monitor(): number; get_pid(): number; get_role(): null | string; diff --git a/src/window.ts b/src/window.ts index 5be8ae5e..2fbd2928 100644 --- a/src/window.ts +++ b/src/window.ts @@ -276,7 +276,7 @@ export class ShellWindow { } is_maximized(): boolean { - return this.meta.get_maximized() !== 0; + return this.meta.maximized_horizontally || this.meta.maximized_vertically; } /** @@ -300,7 +300,7 @@ export class ShellWindow { } is_snap_edge(): boolean { - return this.meta.get_maximized() == Meta.MaximizeFlags.VERTICAL; + return this.meta.maximized_vertically && !this.meta.maximized_horizontally; } is_tilable(ext: Ext): boolean { @@ -372,9 +372,9 @@ export class ShellWindow { const actor = meta.get_compositor_private(); if (actor) { - meta.unmaximize(Meta.MaximizeFlags.HORIZONTAL); - meta.unmaximize(Meta.MaximizeFlags.VERTICAL); - meta.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL); + if (this.is_maximized()) { + meta.unmaximize(Meta.MaximizeFlags.BOTH); + } actor.remove_all_transitions(); ext.movements.insert(this.entity, clone); From 21f3fe1823d4a9b44173d9c7f235130b2ff90d0d Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 19 Aug 2026 15:04:45 +0530 Subject: [PATCH 12/15] Reapply "fix: using Mtk instead of Meta in extension.ts" This reverts commit a57d7e2e59c6710783bb75173220693800c56597. Add back the fix for GNOME 49+ branch. Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- src/extension.ts | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 442ee8c9..c7599b7d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -44,6 +44,13 @@ import Gio from 'gi://Gio'; import St from 'gi://St'; import Shell from 'gi://Shell'; import Meta from 'gi://Meta'; +// Try to import Mtk for newer GNOME versions, fallback to Meta for older versions +let Mtk: any; +try { + Mtk = imports.gi.Mtk; +} catch (e) { + Mtk = null; +} const { GlobalEvent, WindowEvent } = Events; const { cursor_rect, is_keyboard_op, is_resize_op, is_move_op } = Lib; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; @@ -480,7 +487,7 @@ export class Ext extends Ecs.System { if (old) { try { GLib.source_remove(old); - } catch (_) {} + } catch (_) { } } const new_s = GLib.timeout_add(GLib.PRIORITY_LOW, 500, () => { @@ -1258,7 +1265,7 @@ export class Ext extends Ecs.System { this.register(Events.window_move(this, win, rect)); } else { - win.move(this, rect, () => {}); + 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); @@ -1481,7 +1488,7 @@ export class Ext extends Ecs.System { [area, monitor_attachment] = (win.stack === null && attach_to.stack === null && is_sibling) || - (win.stack === null && is_sibling) + (win.stack === null && is_sibling) ? [fork.area, false] : [attach_to.meta.get_frame_rect(), false]; } else { @@ -1512,8 +1519,8 @@ export class Ext extends Ecs.System { ? [area.x, area.y, half_width, area.height] : [area.x + half_width, area.y, half_width, area.height] : swap - ? [area.x, area.y, area.width, half_height] - : [area.x, area.y + half_height, area.width, half_height]; + ? [area.x, area.y, area.width, half_height] + : [area.x, area.y + half_height, area.width, half_height]; this.overlay.x = new_area[0]; this.overlay.y = new_area[1]; @@ -1542,8 +1549,8 @@ export class Ext extends Ecs.System { this.current_style = this.settings.is_dark() ? Style.Dark : this.settings.is_high_contrast() - ? Style.HighContrast - : Style.Light; + ? Style.HighContrast + : Style.Light; } /** Handle window maximization notifications */ @@ -2292,7 +2299,7 @@ export class Ext extends Ecs.System { this.moved_by_mouse = false; } - update_display_configuration_before() {} + update_display_configuration_before() { } update_display_configuration(workareas_only: boolean) { if (!this.auto_tiler || sessionMode.isLocked) return; @@ -2623,7 +2630,10 @@ export class Ext extends Ecs.System { cursor_status(): [Rectangle, number] { const cursor = cursor_rect(); - const rect = new Meta.Rectangle({ x: cursor.x, y: cursor.y, width: 1, height: 1 }); + // Use Mtk.Rectangle if available (newer GNOME), otherwise fallback to Meta.Rectangle + const rect = Mtk ? + new Mtk.Rectangle({ x: cursor.x, y: cursor.y, width: 1, height: 1 }) : + new Meta.Rectangle({ x: cursor.x, y: cursor.y, width: 1, height: 1 }); const monitor = display.get_monitor_index_for_rect(rect); return [cursor, monitor]; } @@ -2665,12 +2675,12 @@ let ext: Ext | null = null; let indicator: Indicator | null = null; declare global { - var popShellExtension: any; + var popShellExtension: any; } export default class PopShellExtension extends Extension { enable() { - globalThis.popShellExtension = this; + globalThis.popShellExtension = this; log.info('enable'); if (!ext) { From 7dbe46432ddc2d3650af547046b4e63e2a68b4ee Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 19 Aug 2026 15:08:18 +0530 Subject: [PATCH 13/15] Reapply "feat: GNOME 50 support" This reverts commit c1f89c3d98826c69284e5d9c8125c035ad80ece4. Add back the fix for GNOME 49+ branch. Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- .gitignore | 3 ++- Makefile | 2 +- metadata.json | 3 ++- src/color_dialog/tsconfig.json | 2 +- src/floating_exceptions/src/main.ts | 2 +- src/floating_exceptions/tsconfig.json | 2 +- src/utils.ts | 6 +++++- tsconfig.json | 2 +- 8 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 8a8458bc..e67238b4 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ debian/* !debian/*install .confirm_shortcut_change .vscode -node_modules \ No newline at end of file +node_modules +tsconfig.tsbuildinfo \ No newline at end of file diff --git a/Makefile b/Makefile index d83c5946..bffe3752 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ sources = src/*.ts *.css all: depcheck compile clean: - rm -rf _build target + rm -rf _build target tsconfig.tsbuildinfo # Configure local settings on system configure: diff --git a/metadata.json b/metadata.json index 32718183..1fd28bbf 100644 --- a/metadata.json +++ b/metadata.json @@ -5,6 +5,7 @@ "uuid": "pop-shell@system76.com", "settings-schema": "org.gnome.shell.extensions.pop-shell", "shell-version": [ - "49" + "49", + "50" ] } diff --git a/src/color_dialog/tsconfig.json b/src/color_dialog/tsconfig.json index b08b2ac7..6e61c341 100644 --- a/src/color_dialog/tsconfig.json +++ b/src/color_dialog/tsconfig.json @@ -4,8 +4,8 @@ "target": "es2015", "strict": true, "outDir": "../../target/color_dialog", + "rootDir": "./src", "forceConsistentCasingInFileNames": true, - "downlevelIteration": true, "lib": ["es2015"], "pretty": true, "removeComments": true, diff --git a/src/floating_exceptions/src/main.ts b/src/floating_exceptions/src/main.ts index 9da3b700..de8d3126 100644 --- a/src/floating_exceptions/src/main.ts +++ b/src/floating_exceptions/src/main.ts @@ -6,7 +6,7 @@ import Gtk from 'gi://Gtk?version=3.0'; import Pango from 'gi://Pango'; /** The directory that this script is executed from. */ -const SCRIPT_DIR = GLib.path_get_dirname(new Error().stack.split(':')[0].slice(1)); +const SCRIPT_DIR = GLib.path_get_dirname(new Error().stack!.split(':')[0].slice(1)); /** Add our directory so we can import modules from it. */ imports.searchPath.push(SCRIPT_DIR); diff --git a/src/floating_exceptions/tsconfig.json b/src/floating_exceptions/tsconfig.json index c3a76870..fb275f36 100644 --- a/src/floating_exceptions/tsconfig.json +++ b/src/floating_exceptions/tsconfig.json @@ -4,8 +4,8 @@ "target": "es2015", // "strict": true, "outDir": "../../target/floating_exceptions", + "rootDir": "./src", "forceConsistentCasingInFileNames": true, - "downlevelIteration": true, "lib": ["es2015"], "pretty": true, "removeComments": true, diff --git a/src/utils.ts b/src/utils.ts index e89a15c6..8f335c09 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -10,7 +10,11 @@ const { Ok, Err } = result; const { Error } = error; export function is_wayland(): boolean { - return Meta.is_wayland_compositor(); + if (typeof Meta.is_wayland_compositor === 'function') { + return Meta.is_wayland_compositor(); + } + // GNOME 50+ removed X11 support; always Wayland + return true; } export function block_signal(object: GObject.Object, signal: SignalID) { diff --git a/tsconfig.json b/tsconfig.json index 82fa7c6c..16972668 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,8 +5,8 @@ "target": "es2020", "strict": true, "outDir": "./target", + "rootDir": "./src", "forceConsistentCasingInFileNames": true, - "downlevelIteration": true, "lib": [ "es2020" ], From 0b1bc03146807f1da8dddbc1209bd051f8a1ba68 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 14/15] 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 3cd8daaae6abfe72fdb8ab53dba8508bcea9f1b6 Mon Sep 17 00:00:00 2001 From: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> Date: Wed, 19 Aug 2026 15:15:12 +0530 Subject: [PATCH 15/15] src/extension: Remove futile fallback case for Mtk. This branch is GNOME 49+ only, and the fix is for GNOME 49+. We have reverted the commit orignally to restore old compatibility and re-applied it in this new GNOME-49+-only branch. So we no longer need the fallback. Signed-off-by: Siddh Raman Pant <25429745+siddhpant@users.noreply.github.com> --- src/extension.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index ed3c9664..f152d72c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -44,13 +44,7 @@ import Gio from 'gi://Gio'; import St from 'gi://St'; import Shell from 'gi://Shell'; import Meta from 'gi://Meta'; -// Try to import Mtk for newer GNOME versions, fallback to Meta for older versions -let Mtk: any; -try { - Mtk = imports.gi.Mtk; -} catch (e) { - Mtk = null; -} +import Mtk from 'git://Mtk'; const { GlobalEvent, WindowEvent } = Events; const { cursor_rect, is_keyboard_op, is_resize_op, is_move_op } = Lib; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; @@ -2628,10 +2622,7 @@ export class Ext extends Ecs.System { cursor_status(): [Rectangle, number] { const cursor = cursor_rect(); - // Use Mtk.Rectangle if available (newer GNOME), otherwise fallback to Meta.Rectangle - const rect = Mtk ? - new Mtk.Rectangle({ x: cursor.x, y: cursor.y, width: 1, height: 1 }) : - new Meta.Rectangle({ x: cursor.x, y: cursor.y, width: 1, height: 1 }); + const rect = new Mtk.Rectangle({ x: cursor.x, y: cursor.y, width: 1, height: 1 }); const monitor = display.get_monitor_index_for_rect(rect); return [cursor, monitor]; }