Skip to content

Commit 90585cb

Browse files
authored
fix(tiling): Improve display update behavior
1 parent c97b9b2 commit 90585cb

File tree

5 files changed

+190
-85
lines changed

5 files changed

+190
-85
lines changed

src/extension.ts

Lines changed: 169 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export class Ext extends Ecs.System<ExtEvent> {
101101
row_size: number = 32;
102102

103103
/** The known display configuration, for tracking monitor removals and changes */
104-
displays: Map<number, Display> = new Map();
104+
displays: [number, Map<number, Display>] = [global.display.get_primary_monitor(), new Map()];
105105

106106
/** The current scaling factor in GNOME Shell */
107107
dpi: number = St.ThemeContext.get_for_stage(global.stage).scale_factor;
@@ -145,6 +145,8 @@ export class Ext extends Ecs.System<ExtEvent> {
145145

146146
was_locked: boolean = false;
147147

148+
private workareas_update: null | SignalID = null
149+
148150
/** Record of misc. global objects and their attached signals */
149151
private signals: Map<GObject.Object, Array<SignalID>> = new Map();
150152

@@ -542,30 +544,48 @@ export class Ext extends Ecs.System<ExtEvent> {
542544

543545
find_monitor_to_retach(width: number, height: number): [number, Display] {
544546
if (!this.settings.workspaces_only_on_primary()) {
545-
for (const [index, display] of this.displays) {
547+
for (const [index, display] of this.displays[1]) {
546548
if (display.area.width == width && display.area.height == height) {
547549
return [index, display];
548550
}
549551
}
550552
}
551553

552554
const primary = display.get_primary_monitor();
553-
return [primary, this.displays.get(primary) as Display];
555+
return [primary, this.displays[1].get(primary) as Display];
554556
}
555557

556558
find_unused_workspace(monitor: number): [number, any] {
557559
if (!this.auto_tiler) return [0, wom.get_workspace_by_index(0)]
558560

559561
let id = 0
560562

561-
for (const fork of this.auto_tiler.forest.forks.values()) {
562-
if (fork.monitor === monitor && id < fork.workspace) id = fork.workspace
563+
const tiled_windows = new Array<Window.ShellWindow>()
564+
565+
for (const [window] of this.auto_tiler.attached.iter()) {
566+
if (!this.auto_tiler.attached.contains(window)) continue
567+
568+
const win = this.windows.get(window)
569+
570+
if (win && !win.reassignment && win.meta.get_monitor() === monitor) tiled_windows.push(win)
571+
}
572+
573+
cancel:
574+
while (true) {
575+
for (const window of tiled_windows) {
576+
if (window.workspace_id() === id) {
577+
id += 1
578+
continue cancel
579+
}
580+
}
581+
582+
break
563583
}
564584

565-
id += 1
566585
let new_work
567586

568-
if (id === wom.get_n_workspaces()) {
587+
if (id + 1 === wom.get_n_workspaces()) {
588+
id += 1
569589
new_work = wom.append_new_workspace(true, global.get_current_time())
570590
} else {
571591
new_work = wom.get_workspace_by_index(id)
@@ -599,7 +619,7 @@ export class Ext extends Ecs.System<ExtEvent> {
599619
}
600620

601621
monitor_work_area(monitor: number): Rectangle {
602-
const meta = display.get_workspace_manager()
622+
const meta = wom
603623
.get_active_workspace()
604624
.get_work_area_for_monitor(monitor);
605625

@@ -1458,7 +1478,7 @@ export class Ext extends Ecs.System<ExtEvent> {
14581478
return true;
14591479
});
14601480

1461-
const workspace_manager = display.get_workspace_manager();
1481+
const workspace_manager = wom;
14621482

14631483
for (const [, ws] of iter_workspaces(workspace_manager)) {
14641484
let index = ws.index();
@@ -1794,7 +1814,7 @@ export class Ext extends Ecs.System<ExtEvent> {
17941814
}
17951815
}
17961816

1797-
update_display_configuration(_workareas_only: boolean) {
1817+
update_display_configuration(workareas_only: boolean) {
17981818
if (!this.auto_tiler || sessionMode.isLocked) return
17991819

18001820
if (this.ignore_display_update) {
@@ -1805,7 +1825,49 @@ export class Ext extends Ecs.System<ExtEvent> {
18051825
// Ignore the update if there are no monitors to assign to
18061826
if (layoutManager.monitors.length === 0) return
18071827

1808-
if (this.displays_updating !== null) GLib.source_remove(this.displays_updating)
1828+
const primary_display = global.display.get_primary_monitor()
1829+
1830+
const primary_display_ready = (ext: Ext): boolean => {
1831+
const area = global.display.get_monitor_geometry(primary_display)
1832+
const work_area = ext.monitor_work_area(primary_display)
1833+
1834+
if (!area || !work_area) return false
1835+
1836+
return !(area.width === work_area.width && area.height === work_area.height)
1837+
}
1838+
1839+
function displays_ready(): boolean {
1840+
const monitors = global.display.get_n_monitors()
1841+
1842+
if (monitors === 0) return false
1843+
1844+
for (let i = 0; i < monitors; i += 1) {
1845+
const display = global.display.get_monitor_geometry(i)
1846+
1847+
if (!display) return false
1848+
1849+
if (display.width < 1 || display.height < 1) return false
1850+
}
1851+
1852+
return true
1853+
}
1854+
1855+
if (!displays_ready() || !primary_display_ready(this)) {
1856+
if (this.displays_updating !== null) return
1857+
if (this.workareas_update !== null) GLib.source_remove(this.workareas_update)
1858+
1859+
this.workareas_update = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
1860+
this.register_fn(() => {
1861+
this.update_display_configuration(workareas_only)
1862+
})
1863+
1864+
this.workareas_update = null
1865+
1866+
return false
1867+
})
1868+
1869+
return
1870+
}
18091871

18101872
// Update every tree on each display with the new dimensions
18111873
const update_tiling = () => {
@@ -1814,28 +1876,82 @@ export class Ext extends Ecs.System<ExtEvent> {
18141876
for (const f of this.auto_tiler.forest.forks.values()) {
18151877
if (!f.is_toplevel) continue
18161878

1817-
const display = this.displays.get(f.monitor);
1879+
const display = this.monitor_work_area(f.monitor)
18181880

18191881
if (display) {
1820-
f.set_area(display.ws)
1882+
const area = new Rect.Rectangle([display.x, display.y, display.width, display.height])
1883+
1884+
f.smart_gapped = false
1885+
f.set_area(area.clone());
18211886
this.auto_tiler.update_toplevel(this, f, f.monitor, this.settings.smart_gaps());
18221887
}
18231888
}
18241889
}
18251890

1826-
let updated = new Map()
1827-
let changes = new Map()
1891+
let migrations: Array<[Fork, number, Rectangle, boolean]> = new Array()
1892+
1893+
const apply_migrations = (assigned_monitors: Set<number>) => {
1894+
if (!migrations) return
1895+
1896+
const iterator = migrations[Symbol.iterator]()
1897+
1898+
GLib.timeout_add(GLib.PRIORITY_LOW, 500, () => {
1899+
let next: null | [Fork, number, Rectangle, boolean] = iterator.next().value;
1900+
1901+
if (next) {
1902+
const [fork, new_monitor, workspace, find_workspace] = next
1903+
let new_workspace
1904+
1905+
if (find_workspace) {
1906+
if (assigned_monitors.has(new_monitor)) {
1907+
[new_workspace] = this.find_unused_workspace(new_monitor)
1908+
} else {
1909+
assigned_monitors.add(new_monitor)
1910+
new_workspace = 0
1911+
}
1912+
} else {
1913+
new_workspace = fork.workspace
1914+
}
1915+
1916+
fork.migrate(this, forest, workspace, new_monitor, new_workspace);
1917+
fork.set_ratio(fork.length() / 2)
1918+
1919+
return true
1920+
}
1921+
1922+
update_tiling()
1923+
1924+
return false
1925+
})
1926+
}
1927+
1928+
function mark_for_reassignment(ext: Ext, fork: Ecs.Entity) {
1929+
for (const win of forest.iter(fork, node.NodeKind.WINDOW)) {
1930+
if (win.inner.kind === 2) {
1931+
const entity = win.inner.entity
1932+
const window = ext.windows.get(entity)
1933+
if (window) window.reassignment = true
1934+
}
1935+
}
1936+
}
1937+
1938+
const [ old_primary, old_displays ] = this.displays
1939+
1940+
const changes = new Map<number, number>()
18281941

18291942
// Records which display's windows were moved to what new display's ID
18301943
for (const [entity, w] of this.windows.iter()) {
18311944
if (!w.actor_exists()) continue
18321945

18331946
this.monitors.with(entity, ([mon,]) => {
1834-
changes.set(mon, w.meta.get_monitor())
1947+
const assignment = mon === old_primary ? primary_display : w.meta.get_monitor()
1948+
changes.set(mon, assignment)
18351949
})
18361950
}
18371951

18381952
// Fetch a new list of monitors
1953+
const updated = new Map()
1954+
18391955
for (const monitor of layoutManager.monitors) {
18401956
const mon = monitor as Monitor
18411957

@@ -1845,44 +1961,39 @@ export class Ext extends Ecs.System<ExtEvent> {
18451961
updated.set(mon.index, { area, ws })
18461962
}
18471963

1848-
function compare_maps<K, V>(map1: Map<K, V>, map2: Map<K, V>) {
1849-
if (map1.size !== map2.size) {
1850-
return false
1851-
}
1964+
const forest = this.auto_tiler.forest
18521965

1853-
let cmp
1966+
if (old_displays.size === updated.size) {
1967+
update_tiling()
18541968

1855-
for (let [key, val] of map1) {
1856-
cmp = map2.get(key)
1857-
if (cmp !== val || (cmp === undefined && !map2.has(key))) {
1858-
return false
1859-
}
1860-
}
1969+
this.displays = [primary_display, updated]
1970+
1971+
return
1972+
}
18611973

1862-
return true
1974+
this.displays = [primary_display, updated]
1975+
1976+
if (utils.map_eq(old_displays, updated)) {
1977+
return
1978+
}
1979+
1980+
if (this.displays_updating !== null) GLib.source_remove(this.displays_updating)
1981+
1982+
if (this.workareas_update !== null) {
1983+
GLib.source_remove(this.workareas_update)
1984+
this.workareas_update = null
18631985
}
18641986

1865-
// Delay actions until 3 seconds later, in case of temporary connection loss
1987+
// Delay actions in case of temporary connection loss
18661988
this.displays_updating = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 2000, () => {
18671989
(() => {
18681990
if (!this.auto_tiler) return
18691991

1870-
if (compare_maps(this.displays, updated)) {
1871-
return
1872-
}
1873-
1874-
this.displays = updated
1875-
1876-
const forest = this.auto_tiler.forest
1877-
1878-
let migrations: Array<[Fork, number, Rectangle, boolean]> = new Array()
1879-
let toplevels = new Array()
1880-
let assigned_monitors = new Set()
1992+
const toplevels = new Array()
1993+
const assigned_monitors = new Set<number>()
18811994

18821995
for (const [old_mon, new_mon] of changes) {
1883-
if (old_mon === new_mon) {
1884-
assigned_monitors.add(new_mon)
1885-
}
1996+
if (old_mon === new_mon) assigned_monitors.add(new_mon)
18861997
}
18871998

18881999
for (const f of forest.forks.values()) {
@@ -1891,61 +2002,35 @@ export class Ext extends Ecs.System<ExtEvent> {
18912002

18922003
let migration: null | [Fork, number, Rectangle, boolean] = null;
18932004

1894-
for (const [old_monitor, new_monitor] of changes) {
1895-
if (old_monitor === new_monitor) continue
2005+
const displays = this.displays[1]
18962006

1897-
if (f.monitor === old_monitor) {
1898-
const display = this.displays.get(new_monitor)
2007+
for (const [old_monitor, new_monitor] of changes) {
2008+
const display = displays.get(new_monitor)
18992009

1900-
if (display) {
1901-
f.monitor = new_monitor
1902-
f.workspace = 0
1903-
migration = [f, new_monitor, display.ws, true]
1904-
}
2010+
if (!display) continue
19052011

1906-
break
2012+
if (f.monitor === old_monitor) {
2013+
f.monitor = new_monitor
2014+
f.workspace = 0
2015+
migration = [f, new_monitor, display.ws, true]
19072016
}
19082017
}
19092018

19102019
if (!migration) {
1911-
const display = this.displays.get(f.monitor)
2020+
const display = displays.get(f.monitor)
19122021
if (display) {
19132022
migration = [f, f.monitor, display.ws, false]
19142023
}
19152024
}
19162025

1917-
if (migration) migrations.push(migration)
1918-
}
1919-
}
1920-
1921-
let iterator = migrations[Symbol.iterator]()
1922-
1923-
GLib.timeout_add(GLib.PRIORITY_LOW, 500, () => {
1924-
let next: null | [Fork, number, Rectangle, boolean] = iterator.next().value;
1925-
1926-
if (next) {
1927-
const [fork, new_monitor, workspace, find_workspace] = next
1928-
let new_workspace
1929-
1930-
if (find_workspace) {
1931-
if (assigned_monitors.has(new_monitor)) {
1932-
[new_workspace] = this.find_unused_workspace(new_monitor)
1933-
} else {
1934-
assigned_monitors.add(new_monitor)
1935-
new_workspace = 0
1936-
}
1937-
} else {
1938-
new_workspace = fork.workspace
2026+
if (migration) {
2027+
mark_for_reassignment(this, migration[0].entity)
2028+
migrations.push(migration)
19392029
}
1940-
1941-
fork.migrate(this, forest, workspace, new_monitor, new_workspace);
1942-
return true
19432030
}
2031+
}
19442032

1945-
update_tiling()
1946-
1947-
return false
1948-
})
2033+
apply_migrations(assigned_monitors)
19492034

19502035
return
19512036
})()
@@ -2053,7 +2138,7 @@ export class Ext extends Ecs.System<ExtEvent> {
20532138

20542139
/** Fetch a workspace by its index */
20552140
workspace_by_id(id: number): Meta.Workspace | null {
2056-
return display.get_workspace_manager().get_workspace_by_index(id);
2141+
return wom.get_workspace_by_index(id);
20572142
}
20582143

20592144
workspace_id(window: Window.ShellWindow | null = null): [number, number] {

src/fork.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ export class Fork {
285285
let window = ext.windows.get(child.inner.entity);
286286
if (window) {
287287
ext.size_signals_block(window);
288+
window.reassignment = false
288289
window.known_workspace = workspace
289290
window.meta.change_workspace_by_index(workspace, true)
290291
ext.monitors.insert(window.entity, [monitor, workspace])

0 commit comments

Comments
 (0)