Skip to content

Commit ad72dee

Browse files
committed
feat: Add some streaming executors which support delays
1 parent fb9f2ed commit ad72dee

File tree

3 files changed

+97
-62
lines changed

3 files changed

+97
-62
lines changed

src/executor.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,65 @@ export class GLibExecutor<T> implements Executor<T> {
3838
});
3939
}
4040
}
41+
42+
export class OnceExecutor<X, T extends Iterable<X>> {
43+
#iterable: T
44+
#signal: SignalID | null = null
45+
46+
constructor(iterable: T) {
47+
this.#iterable = iterable
48+
}
49+
50+
start(delay: number, apply: (v: X) => boolean, then?: () => void) {
51+
this.stop()
52+
53+
const iterator = this.#iterable[Symbol.iterator]()
54+
55+
this.#signal = GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, () => {
56+
const next: X = iterator.next().value
57+
58+
if (typeof next === 'undefined') {
59+
if (then) GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, () => {
60+
then()
61+
return false
62+
})
63+
64+
return false
65+
}
66+
67+
return apply(next)
68+
})
69+
}
70+
71+
stop() {
72+
if (this.#signal !== null) GLib.source_remove(this.#signal)
73+
}
74+
}
75+
76+
export class ChannelExecutor<X> {
77+
#channel: Array<X> = new Array()
78+
79+
#signal: null | number = null
80+
81+
clear() { this.#channel.splice(0) }
82+
83+
get length(): number { return this.#channel.length }
84+
85+
send(v: X) {
86+
this.#channel.push(v)
87+
}
88+
89+
start(delay: number, apply: (v: X) => boolean) {
90+
this.stop()
91+
92+
this.#signal = GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, () => {
93+
const e = this.#channel.shift();
94+
95+
return typeof e === 'undefined' ? true : apply(e)
96+
});
97+
}
98+
99+
stop() {
100+
if (this.#signal !== null) GLib.source_remove(this.#signal)
101+
}
102+
}

src/extension.ts

Lines changed: 29 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import * as Executor from 'executor';
2222
import * as movement from 'movement';
2323
import * as stack from 'stack';
2424
import * as add_exception from 'dialog_add_exception';
25+
import * as exec from 'executor';
2526

2627
import type { Entity } from 'ecs';
2728
import type { ExtEvent } from 'events';
@@ -195,10 +196,6 @@ export class Ext extends Ecs.System<ExtEvent> {
195196
/** Calculates window placements when tiling and focus-switching */
196197
tiler: Tiling.Tiler = new Tiling.Tiler(this);
197198

198-
tiler_active: boolean = false;
199-
200-
tiler_queue: null | SignalID = null;
201-
202199
constructor() {
203200
super(new Executor.GLibExecutor());
204201

@@ -1454,29 +1451,10 @@ export class Ext extends Ecs.System<ExtEvent> {
14541451
signals_attach() {
14551452
this.conf_watch = this.attach_config();
14561453

1457-
this.tiler_queue = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => {
1458-
if (this.tiler_active) return true;
1459-
1460-
const m = this.tiler.movements.shift();
1461-
1462-
if (m) {
1463-
this.tiler_active = true;
1464-
1465-
const callback = () => {
1466-
m();
1467-
this.tiler_active = false;
1468-
};
1469-
1470-
if (!this.schedule_idle(() => {
1471-
callback();
1472-
return false;
1473-
})) {
1474-
callback();
1475-
}
1476-
}
1477-
1478-
return true;
1479-
});
1454+
this.tiler.queue.start(100, (movement) => {
1455+
movement()
1456+
return true
1457+
})
14801458

14811459
const workspace_manager = wom;
14821460

@@ -1661,9 +1639,7 @@ export class Ext extends Ecs.System<ExtEvent> {
16611639
this.conf_watch = null;
16621640
}
16631641

1664-
if (this.tiler_queue !== null) {
1665-
GLib.source_remove(this.tiler_queue)
1666-
}
1642+
this.tiler.queue.stop()
16671643

16681644
this.signals.clear();
16691645
}
@@ -1888,41 +1864,37 @@ export class Ext extends Ecs.System<ExtEvent> {
18881864
}
18891865
}
18901866

1891-
let migrations: Array<[Fork, number, Rectangle, boolean]> = new Array()
1867+
type Migration = [Fork, number, Rectangle, boolean]
1868+
1869+
let migrations: Array<Migration> = new Array()
18921870

18931871
const apply_migrations = (assigned_monitors: Set<number>) => {
18941872
if (!migrations) return
18951873

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)
1874+
new exec.OnceExecutor<Migration, Migration[]>(migrations)
1875+
.start(
1876+
500,
1877+
([fork, new_monitor, workspace, find_workspace]) => {
1878+
let new_workspace
1879+
1880+
if (find_workspace) {
1881+
if (assigned_monitors.has(new_monitor)) {
1882+
[new_workspace] = this.find_unused_workspace(new_monitor)
1883+
} else {
1884+
assigned_monitors.add(new_monitor)
1885+
new_workspace = 0
1886+
}
19081887
} else {
1909-
assigned_monitors.add(new_monitor)
1910-
new_workspace = 0
1888+
new_workspace = fork.workspace
19111889
}
1912-
} else {
1913-
new_workspace = fork.workspace
1914-
}
19151890

1916-
fork.migrate(this, forest, workspace, new_monitor, new_workspace);
1917-
fork.set_ratio(fork.length() / 2)
1891+
fork.migrate(this, forest, workspace, new_monitor, new_workspace);
1892+
fork.set_ratio(fork.length() / 2)
19181893

1919-
return true
1920-
}
1921-
1922-
update_tiling()
1923-
1924-
return false
1925-
})
1894+
return true
1895+
},
1896+
() => update_tiling()
1897+
)
19261898
}
19271899

19281900
function mark_for_reassignment(ext: Ext, fork: Ecs.Entity) {

src/tiling.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import * as Tags from 'tags';
1111
import * as Tweener from 'tweener';
1212
import * as window from 'window';
1313
import * as geom from 'geom';
14+
import * as exec from 'executor';
1415

1516
import type { Entity } from './ecs';
1617
import type { Rectangle } from './rectangle';
@@ -35,12 +36,12 @@ export class Tiler {
3536

3637
window: Entity | null = null;
3738

38-
movements: Array<() => void> = new Array();
39-
4039
moving: boolean = false;
4140

4241
private swap_window: Entity | null = null;
4342

43+
queue: exec.ChannelExecutor<() => void> = new exec.ChannelExecutor()
44+
4445
constructor(ext: Ext) {
4546
this.keybindings = {
4647
"management-orientation": () => {
@@ -187,8 +188,8 @@ export class Tiler {
187188
move(ext: Ext, x: number, y: number, w: number, h: number, direction: Direction, focus: () => window.ShellWindow | number | null) {
188189
if (!this.window) return;
189190
if (ext.auto_tiler && !ext.contains_tag(this.window, Tags.Floating)) {
190-
if (this.movements.length === 2) return;
191-
this.movements.push(() => {
191+
if (this.queue.length === 2) return;
192+
this.queue.send(() => {
192193
const focused = ext.focus_window();
193194
if (focused) {
194195
// The window that the focused window is being moved onto
@@ -662,7 +663,7 @@ export class Tiler {
662663
}
663664

664665
exit(ext: Ext) {
665-
this.movements.splice(0);
666+
this.queue.clear()
666667

667668
if (this.window) {
668669
this.window = null;

0 commit comments

Comments
 (0)