refactor: reorder functions in KWinDriver

This commit is contained in:
Mikhail Zolotukhin 2021-09-08 20:00:42 +03:00
parent 285a750e85
commit 77c01cd214

View File

@ -37,7 +37,6 @@ export default class KWinDriver implements IDriverContext {
public static backendName: string = "kwin";
// TODO: split context implementation
//#region implement properties of IDriverContext (except `setTimeout`)
public get backend(): string {
return KWinDriver.backendName;
}
@ -62,8 +61,9 @@ export default class KWinDriver implements IDriverContext {
// TODO: fousing window on other screen?
// TODO: find a way to change activity
if (this.kwinApi.workspace.currentDesktop !== ksrf.desktop)
if (this.kwinApi.workspace.currentDesktop !== ksrf.desktop) {
this.kwinApi.workspace.currentDesktop = ksrf.desktop;
}
}
public get currentWindow(): Window | null {
@ -72,15 +72,16 @@ export default class KWinDriver implements IDriverContext {
}
public set currentWindow(window: Window | null) {
if (window !== null)
if (window !== null) {
this.kwinApi.workspace.activeClient = (
window.window as KWinWindow
).client;
}
}
public get screens(): ISurface[] {
const screens = [];
for (let screen = 0; screen < this.kwinApi.workspace.numScreens; screen++)
for (let screen = 0; screen < this.kwinApi.workspace.numScreens; screen++) {
screens.push(
new KWinSurface(
screen,
@ -91,6 +92,7 @@ export default class KWinDriver implements IDriverContext {
this.config
)
);
}
return screens;
}
@ -98,13 +100,12 @@ export default class KWinDriver implements IDriverContext {
return this.mousePoller.mousePosition;
}
//#endregion
private engine: TilingEngine;
private control: TilingController;
private controller: TilingController;
private windowMap: WrapperMap<KWin.Client, Window>;
private entered: boolean;
private mousePoller: KWinMousePoller;
private qml: Bismuth.Qml.Main;
private kwinApi: KWin.Api;
@ -128,6 +129,7 @@ export default class KWinDriver implements IDriverContext {
}
this.debug = new Debug(this.config);
// TODO: find a better way to to this
if (this.config.preventMinimize && this.config.monocleMinimizeRest) {
this.debug.debug(
() => "preventMinimize is disabled because of monocleMinimizeRest."
@ -136,7 +138,11 @@ export default class KWinDriver implements IDriverContext {
}
this.engine = new TilingEngine(this.config, this.debug);
this.control = new TilingController(this.engine, this.config, this.debug);
this.controller = new TilingController(
this.engine,
this.config,
this.debug
);
this.windowMap = new WrapperMap(
(client: KWin.Client) => KWinWindow.generateID(client),
(client: KWin.Client) =>
@ -167,7 +173,7 @@ export default class KWinDriver implements IDriverContext {
this.debug.debug(() => "Config: " + this.config);
this.bindEvents();
this.bindShortcut();
this.bindShortcuts();
this.manageWindows();
@ -175,6 +181,129 @@ export default class KWinDriver implements IDriverContext {
this.engine.arrange(this);
}
/**
* Bind script to the various KWin events
*/
private bindEvents() {
this.connect(this.kwinApi.workspace.numberScreensChanged, (count: number) =>
this.controller.onSurfaceUpdate(this, "screens=" + count)
);
this.connect(this.kwinApi.workspace.screenResized, (screen: number) => {
const srf = new KWinSurface(
screen,
this.kwinApi.workspace.currentActivity,
this.kwinApi.workspace.currentDesktop,
this.qml.activityInfo,
this.kwinApi,
this.config
);
this.controller.onSurfaceUpdate(this, "resized " + srf.toString());
});
this.connect(
this.kwinApi.workspace.currentActivityChanged,
(activity: string) => this.controller.onCurrentSurfaceChanged(this)
);
this.connect(
this.kwinApi.workspace.currentDesktopChanged,
(desktop: number, client: KWin.Client) =>
this.controller.onCurrentSurfaceChanged(this)
);
this.connect(this.kwinApi.workspace.clientAdded, (client: KWin.Client) => {
// NOTE: windowShown can be fired in various situations.
// We need only the first one - when window is created.
let handled = false;
const handler = () => {
if (handled) return;
handled = true;
const window = this.windowMap.add(client);
this.controller.onWindowAdded(this, window);
if (window.state !== WindowState.Unmanaged)
this.bindWindowEvents(window, client);
else this.windowMap.remove(client);
client.windowShown.disconnect(wrapper);
};
const wrapper = this.connect(client.windowShown, handler);
this.setTimeout(handler, 50);
});
this.connect(
this.kwinApi.workspace.clientRemoved,
(client: KWin.Client) => {
const window = this.windowMap.get(client);
if (window) {
this.controller.onWindowRemoved(this, window);
this.windowMap.remove(client);
}
}
);
this.connect(
this.kwinApi.workspace.clientMaximizeSet,
(client: KWin.Client, h: boolean, v: boolean) => {
const maximized = h === true && v === true;
const window = this.windowMap.get(client);
if (window) {
(window.window as KWinWindow).maximized = maximized;
this.controller.onWindowMaximizeChanged(this, window, maximized);
}
}
);
this.connect(
this.kwinApi.workspace.clientFullScreenSet,
(client: KWin.Client, fullScreen: boolean, user: boolean) =>
this.controller.onWindowChanged(
this,
this.windowMap.get(client),
"fullscreen=" + fullScreen + " user=" + user
)
);
this.connect(
this.kwinApi.workspace.clientMinimized,
(client: KWin.Client) => {
if (this.config.preventMinimize) {
client.minimized = false;
this.kwinApi.workspace.activeClient = client;
} else
this.controller.onWindowChanged(
this,
this.windowMap.get(client),
"minimized"
);
}
);
this.connect(
this.kwinApi.workspace.clientUnminimized,
(client: KWin.Client) =>
this.controller.onWindowChanged(
this,
this.windowMap.get(client),
"unminimized"
)
);
// TODO: options.configChanged.connect(this.onConfigChanged);
/* NOTE: How disappointing. This doesn't work at all. Even an official kwin script tries this.
* https://github.com/KDE/kwin/blob/master/scripts/minimizeall/contents/code/main.js */
}
/**
* Rigister Bismuth shortcuts
*/
private bindShortcuts() {
this.bindMainShortcuts();
this.bindLayoutShortcuts();
}
/**
* Manage the windows
*/
@ -204,7 +333,6 @@ export default class KWinDriver implements IDriverContext {
}
}
//#region implement methods of IDriverContext`
public setTimeout(func: () => void, timeout: number) {
KWinSetTimeout(
() => this.enter(func),
@ -217,21 +345,13 @@ export default class KWinDriver implements IDriverContext {
public showNotification(text: string) {
this.qml.popupDialog.show(text);
}
//#endregion
private bindShortcut() {
if (!this.kwinApi.KWin.registerShortcut) {
this.debug.debug(
() => "KWin.registerShortcut doesn't exist. Omitting shortcut binding."
);
return;
}
private bindMainShortcuts() {
const bind = (seq: string, title: string, input: Shortcut) => {
title = "Krohnkite: " + title;
title = "Bismuth: " + title;
seq = "Meta+" + seq;
this.kwinApi.KWin.registerShortcut(title, "", seq, () => {
this.enter(() => this.control.onShortcut(this, input));
this.enter(() => this.controller.onShortcut(this, input));
});
};
@ -263,31 +383,28 @@ export default class KWinDriver implements IDriverContext {
bind("Shift+R", "Rotate Part", Shortcut.RotatePart);
bind("Return", "Set master", Shortcut.SetMaster);
}
const bindLayout = (
seq: string,
title: string,
layoutClass: ILayoutClass
) => {
title = "Krohnkite: " + title + " Layout";
private bindLayoutShortcuts() {
const bind = (seq: string, title: string, layoutClass: ILayoutClass) => {
title = "Bismuth: " + title + " Layout";
seq = seq !== "" ? "Meta+" + seq : "";
this.kwinApi.KWin.registerShortcut(title, "", seq, () => {
this.enter(() =>
this.control.onShortcut(this, Shortcut.SetLayout, layoutClass.id)
this.controller.onShortcut(this, Shortcut.SetLayout, layoutClass.id)
);
});
};
bindLayout("T", "Tile", TileLayout);
bindLayout("M", "Monocle", MonocleLayout);
bindLayout("", "Three Column", ThreeColumnLayout);
bindLayout("", "Spread", SpreadLayout);
bindLayout("", "Stair", StairLayout);
bindLayout("", "Floating", FloatingLayout);
bindLayout("", "Quarter", QuarterLayout);
bind("T", "Tile", TileLayout);
bind("M", "Monocle", MonocleLayout);
bind("", "Three Column", ThreeColumnLayout);
bind("", "Spread", SpreadLayout);
bind("", "Stair", StairLayout);
bind("", "Floating", FloatingLayout);
bind("", "Quarter", QuarterLayout);
}
//#region Helper functions
/**
* Binds callback to the signal w/ extra fail-safe measures, like re-entry
* prevention and auto-disconnect on termination.
@ -327,119 +444,6 @@ export default class KWinDriver implements IDriverContext {
this.entered = false;
}
}
//#endregion
private bindEvents() {
this.connect(this.kwinApi.workspace.numberScreensChanged, (count: number) =>
this.control.onSurfaceUpdate(this, "screens=" + count)
);
this.connect(this.kwinApi.workspace.screenResized, (screen: number) => {
const srf = new KWinSurface(
screen,
this.kwinApi.workspace.currentActivity,
this.kwinApi.workspace.currentDesktop,
this.qml.activityInfo,
this.kwinApi,
this.config
);
this.control.onSurfaceUpdate(this, "resized " + srf.toString());
});
this.connect(
this.kwinApi.workspace.currentActivityChanged,
(activity: string) => this.control.onCurrentSurfaceChanged(this)
);
this.connect(
this.kwinApi.workspace.currentDesktopChanged,
(desktop: number, client: KWin.Client) =>
this.control.onCurrentSurfaceChanged(this)
);
this.connect(this.kwinApi.workspace.clientAdded, (client: KWin.Client) => {
/* NOTE: windowShown can be fired in various situations.
* We need only the first one - when window is created. */
let handled = false;
const handler = () => {
if (handled) return;
handled = true;
const window = this.windowMap.add(client);
this.control.onWindowAdded(this, window);
if (window.state !== WindowState.Unmanaged)
this.bindWindowEvents(window, client);
else this.windowMap.remove(client);
client.windowShown.disconnect(wrapper);
};
const wrapper = this.connect(client.windowShown, handler);
this.setTimeout(handler, 50);
});
this.connect(
this.kwinApi.workspace.clientRemoved,
(client: KWin.Client) => {
const window = this.windowMap.get(client);
if (window) {
this.control.onWindowRemoved(this, window);
this.windowMap.remove(client);
}
}
);
this.connect(
this.kwinApi.workspace.clientMaximizeSet,
(client: KWin.Client, h: boolean, v: boolean) => {
const maximized = h === true && v === true;
const window = this.windowMap.get(client);
if (window) {
(window.window as KWinWindow).maximized = maximized;
this.control.onWindowMaximizeChanged(this, window, maximized);
}
}
);
this.connect(
this.kwinApi.workspace.clientFullScreenSet,
(client: KWin.Client, fullScreen: boolean, user: boolean) =>
this.control.onWindowChanged(
this,
this.windowMap.get(client),
"fullscreen=" + fullScreen + " user=" + user
)
);
this.connect(
this.kwinApi.workspace.clientMinimized,
(client: KWin.Client) => {
if (this.config.preventMinimize) {
client.minimized = false;
this.kwinApi.workspace.activeClient = client;
} else
this.control.onWindowChanged(
this,
this.windowMap.get(client),
"minimized"
);
}
);
this.connect(
this.kwinApi.workspace.clientUnminimized,
(client: KWin.Client) =>
this.control.onWindowChanged(
this,
this.windowMap.get(client),
"unminimized"
)
);
// TODO: options.configChanged.connect(this.onConfigChanged);
/* NOTE: How disappointing. This doesn't work at all. Even an official kwin script tries this.
* https://github.com/KDE/kwin/blob/master/scripts/minimizeall/contents/code/main.js */
}
private bindWindowEvents(window: Window, client: KWin.Client) {
let moving = false;
@ -454,38 +458,38 @@ export default class KWinDriver implements IDriverContext {
moving = client.move;
if (moving) {
this.mousePoller.start();
this.control.onWindowMoveStart(window);
this.controller.onWindowMoveStart(window);
} else {
this.control.onWindowMoveOver(this, window);
this.controller.onWindowMoveOver(this, window);
this.mousePoller.stop();
}
}
if (resizing !== client.resize) {
resizing = client.resize;
if (resizing) this.control.onWindowResizeStart(window);
else this.control.onWindowResizeOver(this, window);
if (resizing) this.controller.onWindowResizeStart(window);
else this.controller.onWindowResizeOver(this, window);
}
});
this.connect(client.geometryChanged, () => {
if (moving) this.control.onWindowMove(window);
else if (resizing) this.control.onWindowResize(this, window);
if (moving) this.controller.onWindowMove(window);
else if (resizing) this.controller.onWindowResize(this, window);
else {
if (!window.actualGeometry.equals(window.geometry))
this.control.onWindowGeometryChanged(this, window);
this.controller.onWindowGeometryChanged(this, window);
}
});
this.connect(client.activeChanged, () => {
if (client.active) this.control.onWindowFocused(this, window);
if (client.active) this.controller.onWindowFocused(this, window);
});
this.connect(client.screenChanged, () =>
this.control.onWindowChanged(this, window, "screen=" + client.screen)
this.controller.onWindowChanged(this, window, "screen=" + client.screen)
);
this.connect(client.activitiesChanged, () =>
this.control.onWindowChanged(
this.controller.onWindowChanged(
this,
window,
"activity=" + client.activities.join(",")
@ -493,7 +497,7 @@ export default class KWinDriver implements IDriverContext {
);
this.connect(client.desktopChanged, () =>
this.control.onWindowChanged(this, window, "desktop=" + client.desktop)
this.controller.onWindowChanged(this, window, "desktop=" + client.desktop)
);
}
@ -505,7 +509,7 @@ export default class KWinDriver implements IDriverContext {
}
/**
* Window wrapper map
* Wrapper map type.
*/
class WrapperMap<F, T> {
private items: { [key: string]: T };