diff --git a/res/main.qml b/res/main.qml index f23eee35..1f3a6063 100644 --- a/res/main.qml +++ b/res/main.qml @@ -61,6 +61,7 @@ Item { const kwinScriptingAPI = { workspace: workspace, options: options, + KWin: KWin }; Bismuth.init(qmlObjects, kwinScriptingAPI); diff --git a/src/config.ts b/src/config.ts index 6de717d0..fff56d7f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -31,7 +31,6 @@ import CascadeLayout from "./layouts/cascade_layout"; import { ILayout } from "./ilayout"; import { ILayoutClass } from "./ilayout"; -import { debug } from "./util/debug"; export default interface IConfig { //#region Layout @@ -83,6 +82,8 @@ export default interface IConfig { ignoreActivity: string[]; ignoreScreen: number[]; //#endregion + + debugEnabled: boolean; } export class Config implements IConfig { @@ -136,13 +137,19 @@ export class Config implements IConfig { public ignoreScreen: number[]; //#endregion - constructor() { + public debugEnabled: boolean; + + private kwinApi: KWin.Api; + + constructor(kwinApi: KWin.Api) { function commaSeparate(str: string): string[] { if (!str || typeof str !== "string") return []; return str.split(",").map((part) => part.trim()); } - DEBUG.enabled = DEBUG.enabled || KWin.readConfig("debug", false); + this.kwinApi = kwinApi; + + this.debugEnabled = this.kwinApi.KWin.readConfig("debug", false); this.layoutOrder = []; this.layoutFactories = {}; @@ -159,62 +166,56 @@ export class Config implements IConfig { ["enableCascadeLayout", false, CascadeLayout], // TODO: add config ] as Array<[string, boolean, ILayoutClass]> ).forEach(([configKey, defaultValue, layoutClass]) => { - if (KWin.readConfig(configKey, defaultValue)) + if (this.kwinApi.KWin.readConfig(configKey, defaultValue)) this.layoutOrder.push(layoutClass.id); // TODO: Refactor this: config should not create factories. It is not its responsibility this.layoutFactories[layoutClass.id] = () => new layoutClass(this); }); - this.maximizeSoleTile = KWin.readConfig("maximizeSoleTile", false); - this.monocleMaximize = KWin.readConfig("monocleMaximize", true); - this.monocleMinimizeRest = KWin.readConfig("monocleMinimizeRest", false); + this.maximizeSoleTile = this.kwinApi.KWin.readConfig("maximizeSoleTile", false); + this.monocleMaximize = this.kwinApi.KWin.readConfig("monocleMaximize", true); + this.monocleMinimizeRest = this.kwinApi.KWin.readConfig("monocleMinimizeRest", false); - this.adjustLayout = KWin.readConfig("adjustLayout", true); - this.adjustLayoutLive = KWin.readConfig("adjustLayoutLive", true); - this.keepFloatAbove = KWin.readConfig("keepFloatAbove", true); - this.noTileBorder = KWin.readConfig("noTileBorder", false); + this.adjustLayout = this.kwinApi.KWin.readConfig("adjustLayout", true); + this.adjustLayoutLive = this.kwinApi.KWin.readConfig("adjustLayoutLive", true); + this.keepFloatAbove = this.kwinApi.KWin.readConfig("keepFloatAbove", true); + this.noTileBorder = this.kwinApi.KWin.readConfig("noTileBorder", false); this.limitTileWidthRatio = 0; - if (KWin.readConfig("limitTileWidth", false)) - this.limitTileWidthRatio = KWin.readConfig("limitTileWidthRatio", 1.6); + if (this.kwinApi.KWin.readConfig("limitTileWidth", false)) + this.limitTileWidthRatio = this.kwinApi.KWin.readConfig("limitTileWidthRatio", 1.6); - this.screenGapBottom = KWin.readConfig("screenGapBottom", 0); - this.screenGapLeft = KWin.readConfig("screenGapLeft", 0); - this.screenGapRight = KWin.readConfig("screenGapRight", 0); - this.screenGapTop = KWin.readConfig("screenGapTop", 0); - this.tileLayoutGap = KWin.readConfig("tileLayoutGap", 0); + this.screenGapBottom = this.kwinApi.KWin.readConfig("screenGapBottom", 0); + this.screenGapLeft = this.kwinApi.KWin.readConfig("screenGapLeft", 0); + this.screenGapRight = this.kwinApi.KWin.readConfig("screenGapRight", 0); + this.screenGapTop = this.kwinApi.KWin.readConfig("screenGapTop", 0); + this.tileLayoutGap = this.kwinApi.KWin.readConfig("tileLayoutGap", 0); - const directionalKeyDwm = KWin.readConfig("directionalKeyDwm", true); - const directionalKeyFocus = KWin.readConfig("directionalKeyFocus", false); + const directionalKeyDwm = this.kwinApi.KWin.readConfig("directionalKeyDwm", true); + const directionalKeyFocus = this.kwinApi.KWin.readConfig("directionalKeyFocus", false); this.directionalKeyMode = directionalKeyDwm ? "dwm" : "focus"; - this.newWindowAsMaster = KWin.readConfig("newWindowAsMaster", false); + this.newWindowAsMaster = this.kwinApi.KWin.readConfig("newWindowAsMaster", false); - this.layoutPerActivity = KWin.readConfig("layoutPerActivity", true); - this.layoutPerDesktop = KWin.readConfig("layoutPerDesktop", true); - this.floatUtility = KWin.readConfig("floatUtility", true); - this.preventMinimize = KWin.readConfig("preventMinimize", false); - this.preventProtrusion = KWin.readConfig("preventProtrusion", true); - this.pollMouseXdotool = KWin.readConfig("pollMouseXdotool", false); + this.layoutPerActivity = this.kwinApi.KWin.readConfig("layoutPerActivity", true); + this.layoutPerDesktop = this.kwinApi.KWin.readConfig("layoutPerDesktop", true); + this.floatUtility = this.kwinApi.KWin.readConfig("floatUtility", true); + this.preventMinimize = this.kwinApi.KWin.readConfig("preventMinimize", false); + this.preventProtrusion = this.kwinApi.KWin.readConfig("preventProtrusion", true); + this.pollMouseXdotool = this.kwinApi.KWin.readConfig("pollMouseXdotool", false); - this.floatingClass = commaSeparate(KWin.readConfig("floatingClass", "")); - this.floatingTitle = commaSeparate(KWin.readConfig("floatingTitle", "")); - this.ignoreActivity = commaSeparate(KWin.readConfig("ignoreActivity", "")); + this.floatingClass = commaSeparate(this.kwinApi.KWin.readConfig("floatingClass", "")); + this.floatingTitle = commaSeparate(this.kwinApi.KWin.readConfig("floatingTitle", "")); + this.ignoreActivity = commaSeparate(this.kwinApi.KWin.readConfig("ignoreActivity", "")); this.ignoreClass = commaSeparate( - KWin.readConfig("ignoreClass", "krunner,yakuake,spectacle,kded5") + this.kwinApi.KWin.readConfig("ignoreClass", "krunner,yakuake,spectacle,kded5") ); - this.ignoreRole = commaSeparate(KWin.readConfig("ignoreRole", "quake")); + this.ignoreRole = commaSeparate(this.kwinApi.KWin.readConfig("ignoreRole", "quake")); - this.ignoreScreen = commaSeparate(KWin.readConfig("ignoreScreen", "")).map( + this.ignoreScreen = commaSeparate(this.kwinApi.KWin.readConfig("ignoreScreen", "")).map( (str) => parseInt(str, 10) ); - this.ignoreTitle = commaSeparate(KWin.readConfig("ignoreTitle", "")); + this.ignoreTitle = commaSeparate(this.kwinApi.KWin.readConfig("ignoreTitle", "")); - if (this.preventMinimize && this.monocleMinimizeRest) { - debug( - () => "preventMinimize is disabled because of monocleMinimizeRest." - ); - this.preventMinimize = false; - } } public toString(): string { diff --git a/src/driver/kwin/kwin_config.ts b/src/driver/kwin/kwin_config.ts deleted file mode 100644 index 7c3bca7d..00000000 --- a/src/driver/kwin/kwin_config.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2018-2019 Eon S. Jeon -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. diff --git a/src/driver/kwin/kwin_driver.ts b/src/driver/kwin/kwin_driver.ts index 32f6dd3d..e48ed0d7 100644 --- a/src/driver/kwin/kwin_driver.ts +++ b/src/driver/kwin/kwin_driver.ts @@ -40,9 +40,8 @@ import { KWinSetTimeout } from "./kwin_set_timeout"; import { ILayoutClass } from "../../ilayout"; import { WindowState } from "../../engine/window"; import WrapperMap from "../../util/wrappermap"; -import { debug, debugObj } from "../../util/debug"; import IConfig, { Config } from "../../config"; -// import { workspace } from "../../extern/global"; +import Debug from "../../util/debug"; /** * Abstracts KDE implementation specific details. @@ -123,24 +122,38 @@ export default class KWinDriver implements IDriverContext { private kwinApi: KWin.Api; private config: IConfig; + private debug: Debug; - constructor(qmlObjects: Bismuth.Qml.Main, kwinScriptingApi: KWin.Api, config?: IConfig) { + /** + * @param qmlObjects objects from QML gui. Required for the interaction with QML, as we cannot access globals. + * @param kwinApi KWin scripting API. Required for interaction with KWin, as we cannot access globals. + * @param config Bismuth configuration. If none is provided, the configuration is read from KConfig (in most cases from config file). + */ + constructor(qmlObjects: Bismuth.Qml.Main, kwinApi: KWin.Api, config?: IConfig) { if (config) { this.config = config; } else { - this.config = new Config(); + this.config = new Config(kwinApi); + } + this.debug = new Debug(this.config); + + if (this.config.preventMinimize && this.config.monocleMinimizeRest) { + this.debug.debug( + () => "preventMinimize is disabled because of monocleMinimizeRest." + ); + this.config.preventMinimize = false; } - this.engine = new TilingEngine(this.config); - this.control = new TilingController(this.engine, this.config); + this.engine = new TilingEngine(this.config, this.debug); + this.control = new TilingController(this.engine, this.config, this.debug); this.windowMap = new WrapperMap( (client: KWin.Client) => KWinWindow.generateID(client), - (client: KWin.Client) => new Window(new KWinWindow(client, this.qml, this.kwinApi, this.config), this.config) + (client: KWin.Client) => new Window(new KWinWindow(client, this.qml, this.kwinApi, this.config, this.debug), this.config, this.debug) ); this.entered = false; - this.mousePoller = new KWinMousePoller(qmlObjects, this.config); + this.mousePoller = new KWinMousePoller(qmlObjects, this.config, this.debug); this.qml = qmlObjects; - this.kwinApi = kwinScriptingApi; + this.kwinApi = kwinApi; } /** @@ -149,12 +162,7 @@ export default class KWinDriver implements IDriverContext { public main() { console.log("Initiating systems!"); - // DEBUG = { - // enabled: false, - // started: new Date().getTime(), - // }; - - // debug(() => "Config: " + this.config); + this.debug.debug(() => "Config: " + this.config); this.bindEvents(); this.bindShortcut(); @@ -172,7 +180,7 @@ export default class KWinDriver implements IDriverContext { //#region implement methods of IDriverContext` public setTimeout(func: () => void, timeout: number) { - KWinSetTimeout(() => this.enter(func), timeout, this.qml.scriptRoot); + KWinSetTimeout(() => this.enter(func), timeout, this.qml.scriptRoot, this.debug); } public showNotification(text: string) { @@ -181,8 +189,8 @@ export default class KWinDriver implements IDriverContext { //#endregion private bindShortcut() { - if (!KWin.registerShortcut) { - debug( + if (!this.kwinApi.KWin.registerShortcut) { + this.debug.debug( () => "KWin.registerShortcut doesn't exist. Omitting shortcut binding." ); return; @@ -191,7 +199,7 @@ export default class KWinDriver implements IDriverContext { const bind = (seq: string, title: string, input: Shortcut) => { title = "Krohnkite: " + title; seq = "Meta+" + seq; - KWin.registerShortcut(title, "", seq, () => { + this.kwinApi.KWin.registerShortcut(title, "", seq, () => { this.enter(() => this.control.onShortcut(this, input)); }); }; @@ -232,7 +240,7 @@ export default class KWinDriver implements IDriverContext { ) => { title = "Krohnkite: " + title + " Layout"; seq = seq !== "" ? "Meta+" + seq : ""; - KWin.registerShortcut(title, "", seq, () => { + this.kwinApi.KWin.registerShortcut(title, "", seq, () => { this.enter(() => this.control.onShortcut(this, Shortcut.SetLayout, layoutClass.id) ); @@ -268,7 +276,7 @@ export default class KWinDriver implements IDriverContext { * Run the given function in a protected(?) context to prevent nested event * handling. * - * KWin emits signals as soons as window states are changed, even when + * KWin emits signals as soon as window states are changed, even when * those states are modified by the script. This causes multiple re-entry * during event handling, resulting in performance degradation and harder * debugging. @@ -282,7 +290,7 @@ export default class KWinDriver implements IDriverContext { } catch (e) { // TODO: investigate why this line prevents compiling // debug(() => "Error raised from line " + e.lineNumber); - debug(() => e); + this.debug.debug(() => e); } finally { this.entered = false; } @@ -397,7 +405,7 @@ export default class KWinDriver implements IDriverContext { let resizing = false; this.connect(client.moveResizedChanged, () => { - debugObj(() => [ + this.debug.debugObj(() => [ "moveResizedChanged", { window, move: client.move, resize: client.resize }, ]); diff --git a/src/driver/kwin/kwin_mouse_poller.ts b/src/driver/kwin/kwin_mouse_poller.ts index 26c0efcd..3769284b 100644 --- a/src/driver/kwin/kwin_mouse_poller.ts +++ b/src/driver/kwin/kwin_mouse_poller.ts @@ -19,6 +19,7 @@ // DEALINGS IN THE SOFTWARE. import IConfig from "../../config"; +import Debug from "../../util/debug"; import { KWinSetTimeout } from "./kwin_set_timeout"; export default class KWinMousePoller { @@ -39,7 +40,7 @@ export default class KWinMousePoller { private qml: Bismuth.Qml.Main; private config: IConfig; - constructor(qml: Bismuth.Qml.Main, config: IConfig) { + constructor(qml: Bismuth.Qml.Main, config: IConfig, debug: Debug) { this.startCount = 0; this.cmdResult = null; this.qml = qml; @@ -56,7 +57,7 @@ export default class KWinMousePoller { KWinSetTimeout(() => { if (this.started) qml.mousePoller.connectSource(KWinMousePoller.COMMAND); - }, KWinMousePoller.INTERVAL, this.qml.scriptRoot); + }, KWinMousePoller.INTERVAL, this.qml.scriptRoot, debug); }); } diff --git a/src/driver/kwin/kwin_set_timeout.ts b/src/driver/kwin/kwin_set_timeout.ts index a793a256..bf4b07f8 100644 --- a/src/driver/kwin/kwin_set_timeout.ts +++ b/src/driver/kwin/kwin_set_timeout.ts @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -import { debugObj } from "../../util/debug"; +import Debug from "../../util/debug"; export default class KWinTimerPool { public static readonly instance = new KWinTimerPool(); @@ -31,10 +31,10 @@ export default class KWinTimerPool { this.numTimers = 0; } - public setTimeout(func: () => void, timeout: number, scriptRoot: object) { + public setTimeout(func: () => void, timeout: number, scriptRoot: object, debug: Debug) { if (this.timers.length === 0) { this.numTimers++; - debugObj(() => ["setTimeout/newTimer", { numTimers: this.numTimers }]); + debug.debugObj(() => ["setTimeout/newTimer", { numTimers: this.numTimers }]); } const timer: QQmlTimer = @@ -62,6 +62,6 @@ export default class KWinTimerPool { } } -export function KWinSetTimeout(func: () => void, timeout: number, scriptRoot: object) { - KWinTimerPool.instance.setTimeout(func, timeout, scriptRoot); +export function KWinSetTimeout(func: () => void, timeout: number, scriptRoot: object, debug: Debug) { + KWinTimerPool.instance.setTimeout(func, timeout, scriptRoot, debug); } diff --git a/src/driver/kwin/kwin_surface.ts b/src/driver/kwin/kwin_surface.ts index f8d72502..42590a4b 100644 --- a/src/driver/kwin/kwin_surface.ts +++ b/src/driver/kwin/kwin_surface.ts @@ -55,7 +55,7 @@ export default class KWinSurface implements ISurface { this.config.ignoreActivity.indexOf(activityName) >= 0 || this.config.ignoreScreen.indexOf(screen) >= 0; this.workingArea = toRect( - this.kwinApi.workspace.clientArea(KWin.PlacementArea, screen, desktop) + this.kwinApi.workspace.clientArea(this.kwinApi.KWin.PlacementArea, screen, desktop) ); this.screen = screen; diff --git a/src/driver/kwin/kwin_window.ts b/src/driver/kwin/kwin_window.ts index 38fe169a..fbc4f5fb 100644 --- a/src/driver/kwin/kwin_window.ts +++ b/src/driver/kwin/kwin_window.ts @@ -24,8 +24,8 @@ import KWinSurface from "./kwin_surface"; import Rect from "../../util/rect"; import { toQRect, toRect } from "../../util/kwinutil"; import { clip, matchWords } from "../../util/func"; -import { debugObj } from "../../util/debug"; import IConfig from "../../config"; +import Debug from "../../util/debug"; export default class KWinWindow implements IDriverWindow { public static generateID(client: KWin.Client) { @@ -101,8 +101,9 @@ export default class KWinWindow implements IDriverWindow { private qml: Bismuth.Qml.Main; private kwinApi: KWin.Api; private config: IConfig; + private debug: Debug; - constructor(client: KWin.Client, qml: Bismuth.Qml.Main, kwinApi: KWin.Api, config: IConfig) { + constructor(client: KWin.Client, qml: Bismuth.Qml.Main, kwinApi: KWin.Api, config: IConfig, debug: Debug) { this.client = client; this.id = KWinWindow.generateID(client); this.maximized = false; @@ -111,10 +112,11 @@ export default class KWinWindow implements IDriverWindow { this.qml = qml; this.kwinApi = kwinApi; this.config = config; + this.debug = debug; } public commit(geometry?: Rect, noBorder?: boolean, keepAbove?: boolean) { - debugObj(() => ["KWinWindow#commit", { geometry, noBorder, keepAbove }]); + this.debug.debugObj(() => ["KWinWindow#commit", { geometry, noBorder, keepAbove }]); if (this.client.move || this.client.resize) return; @@ -145,7 +147,7 @@ export default class KWinWindow implements IDriverWindow { if (this.config.preventProtrusion) { const area = toRect( this.kwinApi.workspace.clientArea( - KWin.PlacementArea, + this.kwinApi.KWin.PlacementArea, this.client.screen, this.kwinApi.workspace.currentDesktop ) diff --git a/src/engine/tiling_controler.ts b/src/engine/tiling_controler.ts index c68a4183..1e3bd9ae 100644 --- a/src/engine/tiling_controler.ts +++ b/src/engine/tiling_controler.ts @@ -23,8 +23,8 @@ import IDriverContext from "../idriver_context"; import Window from "./window"; import { WindowState } from "./window"; import { Shortcut } from "../shortcut"; -import { debugObj } from "../util/debug"; import IConfig from "../config"; +import Debug from "../util/debug"; /** * TilingController translates events to actions, implementing high-level @@ -35,24 +35,26 @@ import IConfig from "../config"; export default class TilingController { private engine: TilingEngine; private config: IConfig; + private debug: Debug; - public constructor(engine: TilingEngine, config: IConfig) { + public constructor(engine: TilingEngine, config: IConfig, debug: Debug) { this.engine = engine; this.config = config; + this.debug = debug; } public onSurfaceUpdate(ctx: IDriverContext, comment: string): void { - debugObj(() => ["onSurfaceUpdate", { comment }]); + this.debug.debugObj(() => ["onSurfaceUpdate", { comment }]); this.engine.arrange(ctx); } public onCurrentSurfaceChanged(ctx: IDriverContext): void { - debugObj(() => ["onCurrentSurfaceChanged", { srf: ctx.currentSurface }]); + this.debug.debugObj(() => ["onCurrentSurfaceChanged", { srf: ctx.currentSurface }]); this.engine.arrange(ctx); } public onWindowAdded(ctx: IDriverContext, window: Window): void { - debugObj(() => ["onWindowAdded", { window }]); + this.debug.debugObj(() => ["onWindowAdded", { window }]); this.engine.manage(window); /* move window to next surface if the current surface is "full" */ @@ -74,7 +76,7 @@ export default class TilingController { } public onWindowRemoved(ctx: IDriverContext, window: Window): void { - debugObj(() => ["onWindowRemoved", { window }]); + this.debug.debugObj(() => ["onWindowRemoved", { window }]); this.engine.unmanage(window); this.engine.arrange(ctx); } @@ -88,7 +90,7 @@ export default class TilingController { } public onWindowMoveOver(ctx: IDriverContext, window: Window): void { - debugObj(() => ["onWindowMoveOver", { window }]); + this.debug.debugObj(() => ["onWindowMoveOver", { window }]); /* swap window by dragging */ if (window.state === WindowState.Tiled) { @@ -129,7 +131,7 @@ export default class TilingController { } public onWindowResize(ctx: IDriverContext, window: Window): void { - debugObj(() => ["onWindowResize", { window }]); + this.debug.debugObj(() => ["onWindowResize", { window }]); if (this.config.adjustLayout && this.config.adjustLayoutLive) { if (window.state === WindowState.Tiled) { this.engine.adjustLayout(window); @@ -139,7 +141,7 @@ export default class TilingController { } public onWindowResizeOver(ctx: IDriverContext, window: Window): void { - debugObj(() => ["onWindowResizeOver", { window }]); + this.debug.debugObj(() => ["onWindowResizeOver", { window }]); if (this.config.adjustLayout && window.tiled) { this.engine.adjustLayout(window); this.engine.arrange(ctx); @@ -155,7 +157,7 @@ export default class TilingController { } public onWindowGeometryChanged(ctx: IDriverContext, window: Window): void { - debugObj(() => ["onWindowGeometryChanged", { window }]); + this.debug.debugObj(() => ["onWindowGeometryChanged", { window }]); this.engine.enforceSize(ctx, window); } @@ -167,7 +169,7 @@ export default class TilingController { comment?: string ): void { if (window) { - debugObj(() => ["onWindowChanged", { window, comment }]); + this.debug.debugObj(() => ["onWindowChanged", { window, comment }]); if (comment === "unminimized") ctx.currentWindow = window; diff --git a/src/engine/tiling_engine.ts b/src/engine/tiling_engine.ts index b642be66..a93dafcc 100644 --- a/src/engine/tiling_engine.ts +++ b/src/engine/tiling_engine.ts @@ -30,9 +30,9 @@ import { Shortcut } from "../shortcut"; import { WindowState } from "./window"; import Rect from "../util/rect"; import RectDelta from "../util/rectdelta"; -import { debug, debugObj } from "../util/debug"; import { overlap, wrapIndex } from "../util/func"; import IConfig from "../config"; +import Debug from "../util/debug"; export type Direction = "up" | "down" | "left" | "right"; @@ -44,9 +44,11 @@ export default class TilingEngine { public windows: WindowStore; private config: IConfig; + private debug: Debug; - constructor(config: IConfig) { + constructor(config: IConfig, debug: Debug) { this.config = config; + this.debug = debug; this.layouts = new LayoutStore(this.config); this.windows = new WindowStore(); } @@ -199,7 +201,7 @@ export default class TilingEngine { * Arrange tiles on all screens. */ public arrange(ctx: IDriverContext) { - debug(() => "arrange"); + this.debug.debug(() => "arrange"); ctx.screens.forEach((srf: ISurface) => { this.arrangeScreen(ctx, srf); }); @@ -225,7 +227,7 @@ export default class TilingEngine { ); const visibles = this.windows.getVisibleWindows(srf); - debugObj(() => [ + this.debug.debugObj(() => [ "arrangeScreen", { layout, @@ -266,7 +268,7 @@ export default class TilingEngine { } visibles.forEach((window) => window.commit()); - debugObj(() => ["arrangeScreen/finished", { srf }]); + this.debug.debugObj(() => ["arrangeScreen/finished", { srf }]); } /** diff --git a/src/engine/window.ts b/src/engine/window.ts index eead0a24..b87696b0 100644 --- a/src/engine/window.ts +++ b/src/engine/window.ts @@ -21,7 +21,7 @@ import IConfig from "../config"; import IDriverWindow from "../idriver_window"; import ISurface from "../isurface"; -import { debugObj } from "../util/debug"; +import Debug from "../util/debug"; import Rect from "../util/rect"; import RectDelta from "../util/rectdelta"; @@ -154,9 +154,11 @@ export default class Window { private weightMap: { [key: string]: number }; private config: IConfig; + private debug: Debug; - constructor(window: IDriverWindow, config: IConfig) { + constructor(window: IDriverWindow, config: IConfig, debug: Debug) { this.config = config; + this.debug = debug; this.id = window.id; this.window = window; @@ -172,7 +174,7 @@ export default class Window { public commit() { const state = this.state; - debugObj(() => ["Window#commit", { state: WindowState[state] }]); + this.debug.debugObj(() => ["Window#commit", { state: WindowState[state] }]); switch (state) { case WindowState.NativeMaximized: this.window.commit(undefined, undefined, false); diff --git a/src/extern/kwin.d.ts b/src/extern/kwin.d.ts index 8a7b8a82..bf592065 100644 --- a/src/extern/kwin.d.ts +++ b/src/extern/kwin.d.ts @@ -1,4 +1,5 @@ // Copyright (c) 2018-2019 Eon S. Jeon +// Copyright (c) 2021 Mikhail Zolotukhin // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -20,23 +21,19 @@ // API Reference: https://develop.kde.org/docs/plasma/kwin/api/ +// TODO: Register the rest of the API declare namespace KWin { interface Api { workspace: KWin.WorkspaceWrapper; options: KWin.Options; + KWin: KWin.KWin; } - /* enum ClientAreaOption */ - var PlacementArea: number; - - function readConfig(key: string, defaultValue?: any): any; - - function registerShortcut( - title: string, - text: string, - keySequence: string, - callback: any - ): boolean; + interface KWin { + readConfig(key: string, defaultValue?: any): any; + registerShortcut(title: string, text: string, keySequence: string, callback: any): boolean; + PlacementArea: number; + } interface WorkspaceWrapper { /* read-only */ diff --git a/src/index.ts b/src/index.ts index b3efb0eb..72b18db4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,9 +18,13 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. - import KWinDriver from "./driver/kwin/kwin_driver" +/** + * Script entry-point from QML side. + * @param qmlObjects objects from QML gui. Required for the interaction with QML, as we cannot access globals. + * @param kwinApi KWin scripting API. Required for interaction with KWin, as we cannot access globals. + */ export function init(qmlObjects: Bismuth.Qml.Main, kwinScriptingApi: KWin.Api) { const driver = new KWinDriver(qmlObjects, kwinScriptingApi) driver.main() diff --git a/src/util/debug.ts b/src/util/debug.ts index e7099c9d..5940e703 100644 --- a/src/util/debug.ts +++ b/src/util/debug.ts @@ -1,4 +1,5 @@ // Copyright (c) 2018-2019 Eon S. Jeon +// Copyright (c) 2021 Mikhail Zolotukhin // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -18,29 +19,32 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -declare global { - var DEBUG: any -} +import IConfig from "../config"; -var DEBUG = { - enabled: false, - started: new Date().getTime(), -}; +export default class Debug { + private enabled: boolean; + private started: number; -export function debug(f: () => any) { - if (DEBUG.enabled) { - const timestamp = (new Date().getTime() - DEBUG.started) / 1000; - console.log("[" + timestamp + "]", f()); // tslint:disable-line:no-console - } -} - -export function debugObj(f: () => [string, any]) { - if (DEBUG.enabled) { - const timestamp = (new Date().getTime() - DEBUG.started) / 1000; - const [name, obj] = f(); - const buf = []; - for (const i in obj) buf.push(i + "=" + obj[i]); - - console.log("[" + timestamp + "]", name + ": " + buf.join(" ")); // tslint:disable-line:no-console + constructor(config: IConfig) { + this.enabled = config.debugEnabled; + this.started = new Date().getTime(); + } + + public debug(f: () => any): void { + if (this.enabled) { + const timestamp = (new Date().getTime() - this.started) / 1000; + console.log("[" + timestamp + "]", f()); // tslint:disable-line:no-console + } + } + + public debugObj(f: () => [string, any]): void { + if (this.enabled) { + const timestamp = (new Date().getTime() - this.started) / 1000; + const [name, obj] = f(); + const buf = []; + for (const i in obj) buf.push(i + "=" + obj[i]); + + console.log("[" + timestamp + "]", name + ": " + buf.join(" ")); // tslint:disable-line:no-console + } } }