Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean this branch is not backwards compatible back to 46+? We'll need to set a new branch up before merging if that's the case. (And the previous line should be updated to say GNOME 46 through 48.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's due to the maximise API change at https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4415.

I think the current branch is also broken for same reason, but I didn't test building on old GNOME.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, the other bug fixes does pertain to older versions too.

If we want a linear split in history, I can reorder commits from the current master_noble branch.

@siddhpant siddhpant Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Updated the README as requested)

@siddhpant siddhpant Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If reordering doesn't sound good, new branch can start from d22d2f (=> master_noble at d22d2f~1). Only 3 commits.

And then the fixes can be back-ported (cherry-picked).


GNU Make and TypeScript are also required to build the project.

Expand Down
46 changes: 38 additions & 8 deletions src/auto_tiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
}

Expand Down
4 changes: 4 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export const DEFAULT_FLOAT_RULES: Array<FloatRule> = [
{ class: 'zoom' },
{ class: '^.*action=join.*$' },
{ class: 'gjs' },
{ class: 'io.github.bugaevc.wl-clipboard' },
{ class: 'org.gnome.NautilusPreviewer' },
];

export interface WindowRule {
Expand All @@ -77,6 +79,8 @@ export const SKIPTASKBAR_EXCEPTIONS: Array<WindowRule> = [
{ class: 'Guake' },
{ class: 'Com.github.amezin.ddterm' },
{ class: 'plank' },
{ class: 'io.github.bugaevc.wl-clipboard' },
{ class: 'org.gnome.NautilusPreviewer' },
];

export interface FloatRule {
Expand Down
16 changes: 8 additions & 8 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -909,10 +909,10 @@ export class Ext extends Ecs.System<ExtEvent> {
) {
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();
}
}
Expand Down Expand Up @@ -1034,7 +1034,7 @@ export class Ext extends Ecs.System<ExtEvent> {
compare.is_maximized() &&
win.entity[0] !== compare.entity[0]
) {
compare.meta.unmaximize(Meta.MaximizeFlags.BOTH);
compare.meta.unmaximize();
}
}
}
Expand Down Expand Up @@ -1257,18 +1257,16 @@ export class Ext extends Ecs.System<ExtEvent> {
}

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));
} else {
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();
}
}
}
Expand Down Expand Up @@ -2715,6 +2713,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();
Expand All @@ -2740,6 +2739,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);
Expand Down
26 changes: 24 additions & 2 deletions src/keybindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export class Keybindings {
global: Object;
window_focus: Object;

private active: Set<string> = new Set();

private ext: Ext;

constructor(ext: Ext) {
Expand Down Expand Up @@ -62,23 +64,43 @@ 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;
}

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;
Expand Down
8 changes: 5 additions & 3 deletions src/mod.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 8 additions & 2 deletions src/stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
19 changes: 16 additions & 3 deletions src/tiling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -749,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
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ export class ShellWindow {

if (actor) {
if (this.is_maximized()) {
meta.unmaximize(Meta.MaximizeFlags.BOTH);
meta.unmaximize();
}
actor.remove_all_transitions();

Expand Down