UNPKG

bond-wm

Version:

An X Window Manager built on web technologies.

1,569 lines (1,550 loc) 116 kB
import { createRequire } from 'module'; const require = createRequire(import.meta.url); var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // args.ts import yargs from "yargs"; import { hideBin } from "yargs/helpers"; function getArgs() { return argv; } function loggingEnabled() { return argv.consoleLogging || !!argv.fileLogging; } var argv; var init_args = __esm({ "args.ts"() { "use strict"; argv = yargs(hideBin(process.argv)).nargs("config", 1).option("config", { describe: "Config package specifier to load" }).boolean("console-logging").default("console-logging", false).option("console-logging", { describe: "Enable console log output" }).nargs("file-logging", 1).option("file-logging", { describe: "Enable logging output to a file" }).usage( `bond-wm window manager Usage: $0 [options]` ).help().argv; console.log(argv); } }); // log.ts import * as fs from "fs"; import * as util from "util"; function log(...args) { if (logFile || consoleLogging) { const logText = formatLogText(args); logFile?.write(logText); if (consoleLogging) { stdout.write(logText); } } } function logDir(obj, options) { if (logFile || consoleLogging) { const logText = util.inspect(obj, { showHidden: false, depth: 3, colors: false, ...options }) + "\n"; logFile?.write(logText); if (consoleLogging) { stdout.write(logText); } } } function logError(...args) { if (logFile || consoleLogging) { const logText = formatLogText(args); logFile?.write(logText); if (consoleLogging) { stderr.write(logText); } } } function formatLogText(args) { return util.format.apply(null, args) + "\n"; } var consoleLogging, fileLogging, logFile, stdout, stderr; var init_log = __esm({ "log.ts"() { "use strict"; init_args(); ({ consoleLogging, fileLogging } = getArgs()); logFile = fileLogging ? fs.createWriteStream(fileLogging, { flags: "w" }) : null; stdout = process.stdout; stderr = process.stderr; } }); // configureStore.ts import { applyMiddleware } from "redux"; import { configureStore } from "@reduxjs/toolkit"; import { composeWithStateSync } from "@wnayes/electron-redux/main"; import { configReducer, desktopReducer, pluginStateReducer, screenReducer, trayReducer, windowReducer } from "@bond-wm/shared"; function configureWMStore(middleware) { const enhancer = composeWithStateSync(applyMiddleware(...middleware)); const store = configureStore({ reducer: { config: configReducer, desktop: desktopReducer, pluginState: pluginStateReducer, screens: screenReducer, tray: trayReducer, windows: windowReducer }, // Could try to tune this, but for now just disable it. middleware: (getDefaultMiddleware) => getDefaultMiddleware({ immutableCheck: false, serializableCheck: false }), enhancers: (getDefaultEnhancers) => { return getDefaultEnhancers().concat(enhancer); } }); return store; } var init_configureStore = __esm({ "configureStore.ts"() { "use strict"; } }); // xutils.ts function internAtomAsync(X, name) { return new Promise((resolve3, reject) => { X.InternAtom(false, name, (err, atom) => { if (err) { reject(err); } else { resolve3(atom); } }); }); } async function getPropertyValue(X, wid, nameAtom, typeAtom) { const prop = await getRawPropertyValue(X, wid, nameAtom, typeAtom); switch (prop.type) { case X.atoms.STRING: return prop.data.toString(); case ExtraAtoms.UTF8_STRING: return prop.data.toString(); case X.atoms.WINDOW: if (prop.data && prop.data.length >= 4) { return prop.data.readInt32LE(0); } return void 0; default: log("Unhandled atom property type", prop); return void 0; } } function getRawPropertyValue(X, wid, nameAtom, typeAtom) { return new Promise((resolve3, reject) => { X.GetProperty(0, wid, nameAtom, typeAtom, 0, 1e7, function(err, prop) { if (err) { reject(err); return; } log("Got property value response", prop); resolve3(prop); }); }); } function changeWindowEventMask(X, wid, eventMask) { let failed; log("Changing event mask for", wid, eventMask); X.ChangeWindowAttributes(wid, { eventMask }, (err) => { if (err && err.error === 10) { logError( `Error while changing event mask for for ${wid} to ${eventMask}: Another window manager already running.`, err ); failed = true; return; } logError(`Error while changing event mask for for ${wid} to ${eventMask}`, err); failed = true; }); return !failed; } function numsToBuffer(nums) { const buffer = Buffer.alloc(nums.length * 4); for (let i = 0; i < nums.length; i++) { buffer.writeInt32LE(nums[i], i * 4); } return buffer; } var init_xutils = __esm({ "xutils.ts"() { "use strict"; init_log(); init_wm(); } }); // ewmh.ts import { WindowType, XWMWindowType, setWindowAlwaysOnTopAction, setWindowFullscreenAction, setWindowUrgentAction } from "@bond-wm/shared"; import { XCB_COPY_FROM_PARENT, XPropMode } from "@bond-wm/shared"; import { pid } from "process"; import { ResizeDirection } from "@bond-wm/shared"; function netWMMoveResizeTypeToInternal(newWmMoveResizeType) { switch (newWmMoveResizeType) { case 0 /* _NET_WM_MOVERESIZE_SIZE_TOPLEFT */: return ResizeDirection.TopLeft; case 1 /* _NET_WM_MOVERESIZE_SIZE_TOP */: return ResizeDirection.Top; case 2 /* _NET_WM_MOVERESIZE_SIZE_TOPRIGHT */: return ResizeDirection.TopRight; case 3 /* _NET_WM_MOVERESIZE_SIZE_RIGHT */: return ResizeDirection.Right; case 4 /* _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT */: return ResizeDirection.BottomRight; case 5 /* _NET_WM_MOVERESIZE_SIZE_BOTTOM */: return ResizeDirection.Bottom; case 6 /* _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT */: return ResizeDirection.BottomLeft; case 7 /* _NET_WM_MOVERESIZE_SIZE_LEFT */: return ResizeDirection.Left; default: throw new Error("Unexpected resize type"); } } async function createEWMHEventConsumer({ X, store, getWindowIdFromFrameId }, dragModule) { const atoms = { _NET_SUPPORTED: await internAtomAsync(X, "_NET_SUPPORTED"), _NET_SUPPORTING_WM_CHECK: await internAtomAsync(X, "_NET_SUPPORTING_WM_CHECK"), _NET_WM_NAME: await internAtomAsync(X, "_NET_WM_NAME"), _NET_WM_ICON: await internAtomAsync(X, "_NET_WM_ICON"), _NET_WM_STATE: await internAtomAsync(X, "_NET_WM_STATE"), _NET_WM_STATE_ABOVE: await internAtomAsync(X, "_NET_WM_STATE_ABOVE"), _NET_WM_STATE_FULLSCREEN: await internAtomAsync(X, "_NET_WM_STATE_FULLSCREEN"), _NET_WM_STATE_DEMANDS_ATTENTION: await internAtomAsync(X, "_NET_WM_STATE_DEMANDS_ATTENTION"), _NET_WM_WINDOW_TYPE: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE"), _NET_WM_WINDOW_TYPE_DESKTOP: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_DESKTOP"), _NET_WM_WINDOW_TYPE_DOCK: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_DOCK"), _NET_WM_WINDOW_TYPE_TOOLBAR: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_TOOLBAR"), _NET_WM_WINDOW_TYPE_MENU: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_MENU"), _NET_WM_WINDOW_TYPE_UTILITY: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_UTILITY"), _NET_WM_WINDOW_TYPE_SPLASH: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_SPLASH"), _NET_WM_WINDOW_TYPE_DIALOG: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_DIALOG"), _NET_WM_WINDOW_TYPE_DROPDOWN_MENU: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"), _NET_WM_WINDOW_TYPE_POPUP_MENU: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_POPUP_MENU"), _NET_WM_WINDOW_TYPE_TOOLTIP: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_TOOLTIP"), _NET_WM_WINDOW_TYPE_NOTIFICATION: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_NOTIFICATION"), _NET_WM_WINDOW_TYPE_COMBO: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_COMBO"), _NET_WM_WINDOW_TYPE_DND: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_DND"), _NET_WM_WINDOW_TYPE_NORMAL: await internAtomAsync(X, "_NET_WM_WINDOW_TYPE_NORMAL"), _NET_FRAME_EXTENTS: await internAtomAsync(X, "_NET_FRAME_EXTENTS"), _NET_WM_PID: await internAtomAsync(X, "_NET_WM_PID"), _NET_WM_MOVERESIZE: await internAtomAsync(X, "_NET_WM_MOVERESIZE"), UTF8_STRING: await internAtomAsync(X, "UTF8_STRING") }; function updateWindowStateHints(wid) { const win = store.getState().windows[wid]; if (!win) { return; } const hintAtoms = []; if (win.alwaysOnTop) { hintAtoms.push(atoms._NET_WM_STATE_ABOVE); } if (win.fullscreen) { hintAtoms.push(atoms._NET_WM_STATE_FULLSCREEN); } if (win.urgent) { hintAtoms.push(atoms._NET_WM_STATE_DEMANDS_ATTENTION); } X.ChangeProperty(XPropMode.Replace, wid, atoms._NET_WM_STATE, X.atoms.ATOM, 32, numsToBuffer(hintAtoms)); } function removeWindowStateHints(wid) { X.DeleteProperty(wid, atoms._NET_WM_STATE, (err) => { if (err) { log("Could not delete _NET_WM_STATE"); } }); } function processWindowStateChange(wid, action, atom) { let handled = true; switch (atom) { case atoms._NET_WM_STATE_ABOVE: processWindowAboveChange(wid, action); break; case atoms._NET_WM_STATE_DEMANDS_ATTENTION: processWindowUrgentChange(wid, action); break; case atoms._NET_WM_STATE_FULLSCREEN: processWindowFullscreenChange(wid, action); break; default: handled = false; break; } if (handled) { updateWindowStateHints(wid); } } function processWindowAboveChange(wid, action) { const win = store.getState().windows[wid]; if (!win) { return; } switch (action) { case 1 /* _NET_WM_STATE_ADD */: if (!win.alwaysOnTop) { store.dispatch(setWindowAlwaysOnTopAction({ wid, alwaysOnTop: true })); } break; case 0 /* _NET_WM_STATE_REMOVE */: if (win.alwaysOnTop) { store.dispatch(setWindowAlwaysOnTopAction({ wid, alwaysOnTop: false })); } break; case 2 /* _NET_WM_STATE_TOGGLE */: store.dispatch(setWindowAlwaysOnTopAction({ wid, alwaysOnTop: !win.alwaysOnTop })); break; } } function processWindowFullscreenChange(wid, action) { const win = store.getState().windows[wid]; if (!win) { return; } switch (action) { case 1 /* _NET_WM_STATE_ADD */: if (!win.fullscreen) { store.dispatch(setWindowFullscreenAction({ wid, fullscreen: true })); } break; case 0 /* _NET_WM_STATE_REMOVE */: if (win.fullscreen) { store.dispatch(setWindowFullscreenAction({ wid, fullscreen: false })); } break; case 2 /* _NET_WM_STATE_TOGGLE */: store.dispatch(setWindowFullscreenAction({ wid, fullscreen: !win.fullscreen })); break; } } function processWindowUrgentChange(wid, action) { const win = store.getState().windows[wid]; if (!win) { return; } switch (action) { case 1 /* _NET_WM_STATE_ADD */: if (!win.urgent) { store.dispatch(setWindowUrgentAction({ wid, urgent: true })); } break; case 0 /* _NET_WM_STATE_REMOVE */: if (win.urgent) { store.dispatch(setWindowUrgentAction({ wid, urgent: false })); } break; case 2 /* _NET_WM_STATE_TOGGLE */: store.dispatch(setWindowUrgentAction({ wid, urgent: !win.urgent })); break; } } function getWindowTypeFromAtom(typeAtom) { switch (typeAtom) { case atoms._NET_WM_WINDOW_TYPE_DESKTOP: return WindowType.Desktop; case atoms._NET_WM_WINDOW_TYPE_DOCK: return WindowType.Dock; case atoms._NET_WM_WINDOW_TYPE_TOOLBAR: return WindowType.Toolbar; case atoms._NET_WM_WINDOW_TYPE_MENU: return WindowType.Menu; case atoms._NET_WM_WINDOW_TYPE_UTILITY: return WindowType.Utility; case atoms._NET_WM_WINDOW_TYPE_SPLASH: return WindowType.Splash; case atoms._NET_WM_WINDOW_TYPE_DIALOG: return WindowType.Dialog; case atoms._NET_WM_WINDOW_TYPE_DROPDOWN_MENU: return WindowType.DropdownMenu; case atoms._NET_WM_WINDOW_TYPE_POPUP_MENU: return WindowType.PopupMenu; case atoms._NET_WM_WINDOW_TYPE_TOOLTIP: return WindowType.Tooltip; case atoms._NET_WM_WINDOW_TYPE_NOTIFICATION: return WindowType.Notification; case atoms._NET_WM_WINDOW_TYPE_COMBO: return WindowType.Combo; case atoms._NET_WM_WINDOW_TYPE_DND: return WindowType.DragDrop; case atoms._NET_WM_WINDOW_TYPE_NORMAL: return WindowType.Normal; default: return null; } } return { onScreenCreated({ root }) { X.ChangeProperty( XPropMode.Replace, root, atoms._NET_SUPPORTED, X.atoms.ATOM, 32, numsToBuffer([ atoms._NET_SUPPORTED, atoms._NET_SUPPORTING_WM_CHECK, atoms._NET_WM_NAME, atoms._NET_WM_ICON, atoms._NET_WM_STATE, atoms._NET_WM_STATE_ABOVE, atoms._NET_WM_STATE_FULLSCREEN, atoms._NET_FRAME_EXTENTS, atoms._NET_WM_PID, atoms._NET_WM_MOVERESIZE ]) ); const wid = X.AllocID(); X.CreateWindow(wid, root, -1, -1, 1, 1, 0, XCB_COPY_FROM_PARENT, 0, 0); const widBuffer = numsToBuffer([wid]); X.ChangeProperty(XPropMode.Replace, root, atoms._NET_SUPPORTING_WM_CHECK, X.atoms.WINDOW, 32, widBuffer); X.ChangeProperty(XPropMode.Replace, wid, atoms._NET_SUPPORTING_WM_CHECK, X.atoms.WINDOW, 32, widBuffer); X.ChangeProperty(XPropMode.Replace, wid, atoms._NET_WM_NAME, atoms.UTF8_STRING, 8, "bond-wm"); X.ChangeProperty(XPropMode.Replace, wid, atoms._NET_WM_PID, X.atoms.CARDINAL, 32, numsToBuffer([pid])); }, onClientMessage({ wid, windowType, messageType, data }) { switch (messageType) { case atoms._NET_WM_STATE: { if (windowType === XWMWindowType.Client) { const stateData = data; processWindowStateChange(wid, stateData[0], stateData[1]); if (stateData[2] !== 0) { processWindowStateChange(wid, stateData[0], stateData[2]); } } } break; case atoms._NET_WM_MOVERESIZE: { if (windowType === XWMWindowType.Frame) { const trueWid = getWindowIdFromFrameId(wid); if (typeof trueWid === "number") { wid = trueWid; } } const moveResizeData = data; if (moveResizeData[2] === 11 /* _NET_WM_MOVERESIZE_CANCEL */) { dragModule.endMoveResize(wid); break; } const coords = [moveResizeData[0], moveResizeData[1]]; switch (moveResizeData[2]) { case 8 /* _NET_WM_MOVERESIZE_MOVE */: dragModule.startMove(wid, coords); break; case 0 /* _NET_WM_MOVERESIZE_SIZE_TOPLEFT */: case 1 /* _NET_WM_MOVERESIZE_SIZE_TOP */: case 2 /* _NET_WM_MOVERESIZE_SIZE_TOPRIGHT */: case 3 /* _NET_WM_MOVERESIZE_SIZE_RIGHT */: case 4 /* _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT */: case 5 /* _NET_WM_MOVERESIZE_SIZE_BOTTOM */: case 6 /* _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT */: case 7 /* _NET_WM_MOVERESIZE_SIZE_LEFT */: dragModule.startResize(wid, coords, netWMMoveResizeTypeToInternal(moveResizeData[2])); break; } } break; } }, onMapNotify({ wid, windowType }) { if (windowType === XWMWindowType.Client) { updateWindowStateHints(wid); } }, onUnmapNotify({ wid, windowType }) { if (windowType === XWMWindowType.Client) { removeWindowStateHints(wid); } }, onSetFrameExtents({ wid, frameExtents }) { const extentsInts = Buffer.alloc(16); extentsInts.writeInt32LE(frameExtents.left, 0); extentsInts.writeInt32LE(frameExtents.right, 4); extentsInts.writeInt32LE(frameExtents.top, 8); extentsInts.writeInt32LE(frameExtents.bottom, 12); X.ChangeProperty(XPropMode.Replace, wid, atoms._NET_FRAME_EXTENTS, X.atoms.CARDINAL, 32, extentsInts); }, async getNetWmType(wid) { const { data } = await getRawPropertyValue(X, wid, atoms._NET_WM_WINDOW_TYPE, X.atoms.ATOM); if (!data) { return null; } const types = []; let i = 0; while (i < data.byteLength) { const typeAtom = data.readInt32LE(i); const type = getWindowTypeFromAtom(typeAtom); if (type !== null) { types.push(type); } i += 4; } if (types.length > 1) { log(`Window ${wid} has more than one type: ${types.join(",")}`); } return types[0] ?? null; }, async getNetWmIcons(wid) { const { data } = await getRawPropertyValue(X, wid, atoms._NET_WM_ICON, X.atoms.CARDINAL); if (!data) { return []; } const icons = []; const dataLength = data.byteLength; let i = 0; while (i < dataLength) { const info = { width: data.readInt32LE(i), height: data.readInt32LE(i + 4), data: [] }; i += 8; for (let j = 0; j < info.width * info.height; j++) { if (i >= dataLength) { logError("Icon data truncated for " + wid); break; } info.data.push(data.readUint32LE(i)); i += 4; } icons.push(info); } return icons; } }; } var init_ewmh = __esm({ "ewmh.ts"() { "use strict"; init_xutils(); init_log(); init_xutils(); } }); // pointer.ts import { geometryContains } from "@bond-wm/shared"; function queryPointer(X, relativeWid) { return new Promise((resolve3, reject) => { X.QueryPointer(relativeWid, (err, result) => { if (err) { reject(err); } else { resolve3(result); } }); }); } async function getScreenIndexWithCursor(context, relativeWid) { const pointerInfo = await queryPointer(context.X, relativeWid); if (!pointerInfo) { return -1; } const screens = context.store.getState().screens.filter((s) => s.root === pointerInfo.root); if (!screens.length) { return -1; } if (screens.length === 1) { return screens[0].index; } for (const screen of screens) { if (geometryContains(screen, pointerInfo.rootX, pointerInfo.rootY)) { return screen.index; } } return screens[0].index; } var init_pointer = __esm({ "pointer.ts"() { "use strict"; } }); // icccm.ts import { XPropMode as XPropMode2, XWMWindowType as XWMWindowType2 } from "@bond-wm/shared"; async function createICCCMEventConsumer({ X }) { const atoms = { WM_STATE: await internAtomAsync(X, "WM_STATE") }; function updateWindowState(wid) { const wmStateBuffer = Buffer.alloc(8); wmStateBuffer.writeUInt32LE(1 /* NormalState */, 0); wmStateBuffer.writeUInt32LE(0, 4); X.ChangeProperty(XPropMode2.Replace, wid, atoms.WM_STATE, atoms.WM_STATE, 32, wmStateBuffer); } function removeWindowState(wid) { X.DeleteProperty(wid, atoms.WM_STATE, (err) => { if (err) { log("Could not delete WM_STATE"); } }); } return { onMapNotify({ wid, windowType }) { if (windowType === XWMWindowType2.Client) { updateWindowState(wid); } }, onUnmapNotify({ wid, windowType }) { if (windowType === XWMWindowType2.Client) { removeWindowState(wid); } } }; } async function getWMTransientFor(X, wid) { return await getPropertyValue(X, wid, X.atoms.WM_TRANSIENT_FOR, X.atoms.WINDOW); } async function getWMClass(X, wid) { const { data } = await getRawPropertyValue(X, wid, X.atoms.WM_CLASS, X.atoms.STRING); if (!data) { return void 0; } const wmClass = ["", ""]; const firstNullByteIndex = data.indexOf(0); if (firstNullByteIndex > 0) { wmClass[0] = data.toString("utf8", 0, firstNullByteIndex); } if (firstNullByteIndex + 1 < data.length - 1) { wmClass[1] = data.toString("utf8", firstNullByteIndex + 1, data.length - 1); } return wmClass; } async function getWMHints(X, wid) { const { data } = await getRawPropertyValue(X, wid, X.atoms.WM_HINTS, X.atoms.WM_HINTS); if (!data || data.length < SIZEOF_WMHints) { return; } const hints = { flags: data.readInt32LE(0), input: data.readInt32LE(4), initialState: data.readInt32LE(8), iconPixmap: data.readInt32LE(12), iconWindow: data.readInt32LE(16), iconX: data.readInt32LE(20), iconY: data.readInt32LE(24), iconMask: data.readInt32LE(28) }; return hints; } async function getNormalHints(X, wid) { const { data } = await getRawPropertyValue(X, wid, X.atoms.WM_NORMAL_HINTS, X.atoms.WM_SIZE_HINTS); if (!data || data.length < SIZEOF_WMSizeHints) { return; } const hints = { flags: data.readInt32LE(0), minWidth: data.readInt32LE(20), minHeight: data.readInt32LE(24), maxWidth: data.readInt32LE(28), maxHeight: data.readInt32LE(32), widthIncrement: data.readInt32LE(36), heightIncrement: data.readInt32LE(40), minAspect: [data.readInt32LE(44), data.readInt32LE(48)], maxAspect: [data.readInt32LE(52), data.readInt32LE(56)], baseWidth: data.readInt32LE(60), baseHeight: data.readInt32LE(64), gravity: data.readInt32LE(68) }; return hints; } var SIZEOF_WMHints, SIZEOF_WMSizeHints; var init_icccm = __esm({ "icccm.ts"() { "use strict"; init_log(); init_xutils(); SIZEOF_WMHints = 32; SIZEOF_WMSizeHints = 72; } }); // motif.ts async function createMotifModule({ X }) { const atoms = { _MOTIF_WM_HINTS: await internAtomAsync(X, "_MOTIF_WM_HINTS") }; return { async getMotifHints(wid) { const { data } = await getRawPropertyValue(X, wid, atoms._MOTIF_WM_HINTS, atoms._MOTIF_WM_HINTS); if (!data || data.length < SIZEOF_MotifHints) { return; } const hints = { flags: data.readInt32LE(0), functions: data.readInt32LE(4), decorations: !!data.readInt32LE(8), inputMode: data.readInt32LE(12), status: data.readInt32LE(16) }; return hints; } }; } function hasMotifDecorations(motifHints) { if (!motifHints) { return true; } if (motifHints.flags & 2 /* MWM_HINTS_DECORATIONS */) { return motifHints.decorations; } return true; } var SIZEOF_MotifHints; var init_motif = __esm({ "motif.ts"() { "use strict"; init_xutils(); SIZEOF_MotifHints = 20; } }); // menus.ts import { app, BrowserWindow, Menu } from "electron"; import { ContextMenuKind } from "@bond-wm/shared"; function showContextMenu(event, kind, version) { log("Showing context menu (kind=" + ContextMenuKind[kind]); switch (kind) { case ContextMenuKind.Desktop: showDesktopMenu(event, version); break; case ContextMenuKind.Frame: showFrameMenu(event); break; } } function showDesktopMenu(event, version) { const browserWindow = BrowserWindow.fromWebContents(event.sender); if (!browserWindow) { return; } const desktopMenu = Menu.buildFromTemplate([ { label: "bond-wm" + (version ? ` \u2014 ${version}` : ""), enabled: false }, { type: "separator" }, { label: "Reload Desktop", click: () => { browserWindow.reload(); } }, { label: "Desktop Developer Tools", click: () => { browserWindow.webContents.openDevTools(); } }, { label: "Quit", click: () => { app.quit(); } } ]); desktopMenu.popup({ window: browserWindow }); } function showFrameMenu(event) { const browserWindow = BrowserWindow.fromWebContents(event.sender); if (!browserWindow) { return; } const frameMenu = Menu.buildFromTemplate([ { label: "Reload Frame", click: () => { browserWindow.reload(); } }, { label: "Frame Developer Tools", click: () => { browserWindow.webContents.openDevTools({ mode: "detach" }); } } ]); frameMenu.popup({ window: browserWindow }); } var init_menus = __esm({ "menus.ts"() { "use strict"; init_log(); } }); // exec.ts import { exec } from "child_process"; function execCommand(command, callback) { exec(command, (_error, stdout2) => { callback(stdout2); }); } var init_exec = __esm({ "exec.ts"() { "use strict"; } }); // autocomplete.ts import { ipcMain } from "electron"; function setupAutocompleteListener() { ipcMain.on("completion-options-get", (event) => { getCompletionOptions().then((options) => { event.sender.send("completion-options-result", options); }); }); } function getCompletionOptions() { return new Promise((resolve3) => { try { execCommand("/usr/bin/env bash -c 'compgen -c'", (commands) => { resolve3( commands.split("\n").map((c) => c.trim()).filter((c) => !!c) ); }); } catch { resolve3([]); } }); } var init_autocomplete = __esm({ "autocomplete.ts"() { "use strict"; init_exec(); } }); // window.ts import { arraysEqual, intersect, setWindowTagsAction } from "@bond-wm/shared"; function updateWindowTagsForNextScreen(store, win, nextScreen) { const nextScreenTags = nextScreen.currentTags; const tagIntersect = intersect(win.tags, nextScreenTags); if (tagIntersect.length > 0) { if (!arraysEqual(tagIntersect, win.tags)) { store.dispatch(setWindowTagsAction({ wid: win.id, tags: tagIntersect })); } } else if (nextScreenTags.length > 0) { store.dispatch(setWindowTagsAction({ wid: win.id, tags: [nextScreenTags[0]] })); } } var init_window = __esm({ "window.ts"() { "use strict"; } }); // drag.ts import { configureWindowAction, endDragAction, setWindowIntoScreenAction, startDragAction, XWMWindowType as XWMWindowType3 } from "@bond-wm/shared"; import { selectWindowMaximizeCanTakeEffect } from "@bond-wm/shared"; import { geometryArea, geometryIntersect } from "@bond-wm/shared"; import { getAbsoluteWindowGeometry, newHeightForWindow, newWidthForWindow, ResizeDirection as ResizeDirection2 } from "@bond-wm/shared"; import { XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XEventMask } from "@bond-wm/shared"; async function createDragModule({ X, store, getFrameIdFromWindowId, getWindowIdFromFrameId }, getLayoutPlugins) { function endMoveResize(wid) { const state = store.getState(); const win = state.windows[wid]; if (!win || !win._dragState) { return; } log("Ending drag for " + wid); X.UngrabPointer(XCB_CURRENT_TIME); X.UngrabKeyboard(XCB_CURRENT_TIME); store.dispatch(endDragAction({ wid })); setWindowIntoBestScreen(state.screens, win); } function doGrabsForDrag(wid) { const fid = getFrameIdFromWindowId(wid) ?? wid; X.GrabPointer( fid, false, XEventMask.PointerMotion | XEventMask.ButtonRelease, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, 0, // None 0, // None XCB_CURRENT_TIME, (err) => { if (err) { logError(err); } } ); X.GrabKeyboard(fid, false, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); } function setWindowIntoBestScreen(screens, win) { const prevWinScreen = screens[win.screenIndex]; const bestWinScreen = getBestScreenForWindow(screens, win); if (bestWinScreen && bestWinScreen !== prevWinScreen) { updateWindowTagsForNextScreen(store, win, bestWinScreen); store.dispatch(setWindowIntoScreenAction({ wid: win.id, screenIndex: screens.indexOf(bestWinScreen) })); store.dispatch( configureWindowAction({ wid: win.id, ...win.outer, x: prevWinScreen.x + win.outer.x - bestWinScreen.x, y: prevWinScreen.y + win.outer.y - bestWinScreen.y }) ); } } function getBestScreenForWindow(screens, win) { let bestScreen = null; let bestIntersectArea = Number.MIN_SAFE_INTEGER; const winAbsCoords = getAbsoluteWindowGeometry(screens[win.screenIndex], win); for (const screen of screens) { const intersect2 = geometryIntersect(screen, winAbsCoords); if (!intersect2) { continue; } const intersectArea = geometryArea(intersect2); if (intersectArea > bestIntersectArea) { bestIntersectArea = intersectArea; bestScreen = screen; } } return bestScreen; } return { startMove(wid, coords) { const state = store.getState(); const win = store.getState().windows[wid]; if (!win || win._dragState || win.maximized && selectWindowMaximizeCanTakeEffect(state, getLayoutPlugins(win.screenIndex), wid) || win.fullscreen) { return; } log("Starting drag for " + wid, coords); store.dispatch(startDragAction({ wid, coords, moving: true })); doGrabsForDrag(wid); }, startResize(wid, coords, direction) { const state = store.getState(); const win = store.getState().windows[wid]; if (!win || win._dragState || win.maximized && selectWindowMaximizeCanTakeEffect(state, getLayoutPlugins(win.screenIndex), wid) || win.fullscreen) { log("Choosing to not start resize for " + wid, coords, ResizeDirection2[direction]); return; } log("Starting resize for " + wid, coords, ResizeDirection2[direction]); store.dispatch(startDragAction({ wid, coords, resize: direction })); doGrabsForDrag(wid); }, endMoveResize, onPointerMotion({ wid, windowType, rootx, rooty }) { let fid; if (windowType === XWMWindowType3.Frame) { fid = wid; wid = getWindowIdFromFrameId(fid); } else if (windowType === XWMWindowType3.Client) { fid = getFrameIdFromWindowId(wid); } const win = store.getState().windows[wid]; if (!win || !win._dragState || !win._dragState.startOuterSize || !win._dragState.startCoordinates) { return; } const { startOuterSize, startCoordinates } = win._dragState; const xDiff = rootx - startCoordinates[0]; const yDiff = rooty - startCoordinates[1]; function configureWindow(win2, newConfig) { store.dispatch( configureWindowAction({ wid: win2.id, ...startOuterSize, x: typeof newConfig.x === "number" ? newConfig.x : void 0, y: typeof newConfig.y === "number" ? newConfig.y : void 0, width: typeof newConfig.width === "number" ? newWidthForWindow(win2, newConfig.width) : void 0, height: typeof newConfig.height === "number" ? newHeightForWindow(win2, newConfig.height) : void 0 }) ); } if (win._dragState.moving) { configureWindow(win, { x: startOuterSize.x + xDiff, y: startOuterSize.y + yDiff }); return; } if (typeof win._dragState.resize === "number") { switch (win._dragState.resize) { case ResizeDirection2.TopLeft: configureWindow(win, { x: startOuterSize.x + xDiff, y: startOuterSize.y + yDiff, width: startOuterSize.width - xDiff, height: startOuterSize.height - yDiff }); break; case ResizeDirection2.Top: configureWindow(win, { y: startOuterSize.y + yDiff, height: startOuterSize.height - yDiff }); break; case ResizeDirection2.TopRight: configureWindow(win, { y: startOuterSize.y + yDiff, width: startOuterSize.width + xDiff, height: startOuterSize.height - yDiff }); break; case ResizeDirection2.Right: configureWindow(win, { width: startOuterSize.width + xDiff }); break; case ResizeDirection2.BottomRight: configureWindow(win, { width: startOuterSize.width + xDiff, height: startOuterSize.height + yDiff }); break; case ResizeDirection2.Bottom: configureWindow(win, { height: startOuterSize.height + yDiff }); break; case ResizeDirection2.BottomLeft: configureWindow(win, { x: startOuterSize.x + xDiff, width: startOuterSize.width - xDiff, height: startOuterSize.height + yDiff }); break; case ResizeDirection2.Left: configureWindow(win, { x: startOuterSize.x + xDiff, width: startOuterSize.width - xDiff }); break; } } }, onButtonRelease({ wid, windowType }) { let fid; if (windowType === XWMWindowType3.Frame) { fid = wid; wid = getWindowIdFromFrameId(fid); } else if (windowType === XWMWindowType3.Client) { fid = getFrameIdFromWindowId(wid); } endMoveResize(wid); }, onKeyPress({ wid, windowType }) { let fid; if (windowType === XWMWindowType3.Frame) { fid = wid; wid = getWindowIdFromFrameId(fid); } else if (windowType === XWMWindowType3.Client) { fid = getFrameIdFromWindowId(wid); } endMoveResize(wid); return false; } }; } var init_drag = __esm({ "drag.ts"() { "use strict"; init_log(); init_window(); } }); // shortcuts.ts import { X11_KEY_MODIFIER } from "@bond-wm/shared"; import * as nodeKeySym from "@bond-wm/keysym"; async function createShortcutsModule({ X, XDisplay }) { const mapping = await getKeyboardMapping(XDisplay); const keycodeToKeysyms = []; const keysymsToKeycode = []; const keysymsToKeycodeShift = []; for (let i = 0; i < mapping.length; i++) { const keycode = XDisplay.min_keycode + i; const keysyms = mapping[i]; keycodeToKeysyms[keycode] = keysyms; if (keysyms[0] > 0) { keysymsToKeycode[keysyms[0]] = keycode; } if (keysyms[1] > 0) { keysymsToKeycodeShift[keysyms[1]] = keycode; } } const processedRegisteredKeys = {}; function getXModifierForShortcutPiece(piece) { switch (piece.toLowerCase()) { case "shift": return X11_KEY_MODIFIER.ShiftMask; case "ctrl": case "ctl": case "control": return X11_KEY_MODIFIER.ControlMask; case "mod4": case "win": return X11_KEY_MODIFIER.Mod4Mask; default: return null; } } function registerShortcut(rootWid, keyString, callback) { const pieces = keyString.split("+").map((s) => s.trim()).filter((s) => !!s); if (pieces.length === 0) { return; } let xModifiers = 0; for (let i = 0; i < pieces.length - 1; i++) { const xModifier = getXModifierForShortcutPiece(pieces[i]); if (typeof xModifier === "number") { xModifiers |= xModifier; } else { logError("Unrecognized key modifier: " + pieces[i]); } } const lastPiece = pieces[pieces.length - 1]; if (!lastPiece) { return; } const hasShift = !!(xModifiers & X11_KEY_MODIFIER.ShiftMask); let keySym = nodeKeySym.fromName(hasShift ? toUpper(lastPiece) : toLower(lastPiece)); if (!keySym) { keySym = nodeKeySym.fromName(lastPiece); } const keySymMap = hasShift ? keysymsToKeycodeShift : keysymsToKeycode; const keySymMapFallback = hasShift ? keysymsToKeycode : keysymsToKeycodeShift; const keycode = keySymMap[keySym?.keysym ?? -1] ?? keySymMapFallback[keySym?.keysym ?? -1]; if (keycode > 0) { processedRegisteredKeys[xModifiers] ||= {}; if (!processedRegisteredKeys[xModifiers][keycode]) { processedRegisteredKeys[xModifiers][keycode] = { originalKeyString: keyString, callback }; X.GrabKey( rootWid, true, xModifiers, keycode, 1, 1 /* Async */ ); log(`Registered modifiers: ${xModifiers}, keycode: ${keycode} for ${keyString}`); } } else { logError("Could not register " + keyString); } } return { registerShortcuts(rootWid, registeredKeys) { for (const keyString in registeredKeys) { registerShortcut(rootWid, keyString, registeredKeys[keyString]); } }, registerShortcut, onKeyPress(args) { const { keycode, modifiers } = args; const keysyms = keycodeToKeysyms[keycode]; log("keysyms", keysyms); if (keysyms) { const keysym = keysyms[modifiers & X11_KEY_MODIFIER.ShiftMask ? 1 : 0]; if (keysym) { log("keysym", keysym); log("fromKeysym", nodeKeySym.fromKeysym(keysym)); } } if (processedRegisteredKeys[args.modifiers]) { const info = processedRegisteredKeys[args.modifiers][args.keycode]; if (typeof info === "object" && typeof info.callback === "function") { log(`Running ${info.originalKeyString} shortcut handler`); args.originalKeyString = info.originalKeyString; info.callback(args); return true; } } return false; } }; } async function getKeyboardMapping(XDisplay) { return new Promise((resolve3, reject) => { const { min_keycode, max_keycode } = XDisplay; XDisplay.client.GetKeyboardMapping(min_keycode, max_keycode - min_keycode, (err, list) => { if (err) { reject(err); return; } resolve3(list); }); }); } function toUpper(value) { if (value in _toUpperMap) { return _toUpperMap[value]; } return value.toUpperCase(); } function toLower(value) { if (value in _toLowerMap) { return _toLowerMap[value]; } return value.toLowerCase(); } var _toUpperMap, _toLowerMap; var init_shortcuts = __esm({ "shortcuts.ts"() { "use strict"; init_log(); _toUpperMap = Object.assign(/* @__PURE__ */ Object.create(null), { "0": ")", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", "8": "*", "9": "(", "`": "~" }); _toLowerMap = /* @__PURE__ */ Object.create(null); for (const lower in _toUpperMap) { _toLowerMap[_toUpperMap[lower]] = lower; } } }); // assert.ts import { strict } from "assert"; var init_assert = __esm({ "assert.ts"() { "use strict"; } }); // xdg.ts import { env } from "node:process"; import { join } from "node:path"; import { existsSync } from "node:fs"; function getXDGConfigHome() { let XDG_CONFIG_HOME = env["XDG_CONFIG_HOME"]; if (!XDG_CONFIG_HOME) { const HOME = env["HOME"] || "~"; XDG_CONFIG_HOME = join(HOME, ".config"); } return XDG_CONFIG_HOME; } async function getXDGUserDirectory(kind) { if (!_userDirs) { _userDirs = await getXdgUserDirs(); } let dir; switch (kind) { case 0 /* Desktop */: dir = _userDirs?.XDG_DESKTOP_DIR ?? null; break; case 1 /* Documents */: dir = _userDirs?.XDG_DOCUMENTS_DIR ?? null; break; case 2 /* Download */: dir = _userDirs?.XDG_DOWNLOAD_DIR ?? null; break; case 3 /* Music */: dir = _userDirs?.XDG_MUSIC_DIR ?? null; break; case 4 /* Pictures */: dir = _userDirs?.XDG_PICTURES_DIR ?? null; break; case 5 /* PublicShare */: dir = _userDirs?.XDG_PUBLICSHARE_DIR ?? null; break; case 6 /* Templates */: dir = _userDirs?.XDG_TEMPLATES_DIR ?? null; break; case 7 /* Videos */: dir = _userDirs?.XDG_VIDEOS_DIR ?? null; break; default: throw new Error("Unknown XDG user directory kind"); } if (dir && !existsSync(dir)) { dir = null; } return dir; } var getXdgUserDirs, _userDirs; var init_xdg = __esm({ "xdg.ts"() { "use strict"; getXdgUserDirs = __require("xdg-user-dir"); _userDirs = null; } }); // config.ts import { existsSync as existsSync2 } from "node:fs"; import { dirname, join as join2, resolve, sep } from "node:path"; import { setConfigPath, setConfigPathAction } from "@bond-wm/shared"; async function determineConfigPath(store) { let configPath = getArgs().config; if (configPath) { if (configPath.startsWith(".")) { configPath = resolve(configPath); } else if (!configPath.startsWith(sep)) { configPath = dirname(__require.resolve(`${configPath}/package.json`)); } if (!existsSync2(configPath)) { throw new Error(`The --config path ${configPath} failed to resolve or does not exist.`); } } else { const XDG_CONFIG_HOME = getXDGConfigHome(); log("XDG_CONFIG_HOME", XDG_CONFIG_HOME); configPath = join2(XDG_CONFIG_HOME, "bond-wm-config"); if (!existsSync2(configPath)) { throw new Error("No --config path was specified, and no default config locations existed."); } } setConfigPath(configPath); store.dispatch(setConfigPathAction(configPath)); return configPath; } var init_config = __esm({ "config.ts"() { "use strict"; init_log(); init_args(); init_xdg(); } }); // systray.ts import { addTrayWindowAction, configureTrayWindowAction, removeTrayWindowAction, setTrayBackgroundColorAction } from "@bond-wm/shared"; import { X11_EVENT_TYPE, XCB_COPY_FROM_PARENT as XCB_COPY_FROM_PARENT2, XPropMode as XPropMode3 } from "@bond-wm/shared"; async function createTrayEventConsumer({ X, store, XDisplay }) { const TraySelectionAtom = `_NET_SYSTEM_TRAY_S${X.screenNum}`; const atoms = { MANAGER: await internAtomAsync(X, "MANAGER"), [TraySelectionAtom]: await internAtomAsync(X, TraySelectionAtom), _NET_SYSTEM_TRAY_OPCODE: await internAtomAsync(X, "_NET_SYSTEM_TRAY_OPCODE"), _NET_SYSTEM_TRAY_ORIENTATION: await internAtomAsync(X, "_NET_SYSTEM_TRAY_ORIENTATION"), _NET_SYSTEM_TRAY_MESSAGE_DATA: await internAtomAsync(X, "_NET_SYSTEM_TRAY_MESSAGE_DATA") }; let _registered = false; let _trayOwnerWid = 0; let _currentColorPixel; const frameBrowserWinIdToFrameId = /* @__PURE__ */ new Map(); const _notificationState = {}; function isTrayWin(win) { return win in store.getState().tray.windows; } function dockTrayWindow(trayWid) { if (isTrayWin(trayWid)) { return; } store.dispatch(addTrayWindowAction({ wid: trayWid })); changeWindowEventMask(X, trayWid, TRAY_WIN_EVENT_MASK); X.ChangeWindowAttributes(trayWid, { backgroundPixel: _currentColorPixel }); X.ConfigureWindow(trayWid, { width: 16, height: 16 }); } return { onScreenCreated(args) { if (_registered) { return; } _registered = true; if (typeof _currentColorPixel !== "number") { _currentColorPixel = XDisplay.screen[0].black_pixel; } _trayOwnerWid = X.AllocID(); X.CreateWindow( _trayOwnerWid, args.root, -1, -1, 1, 1, 0, XCB_COPY_FROM_PARENT2, 1, XDisplay.screen[0].root_visual, { colormap: XDisplay.screen[0].default_colormap, backgroundPixel: _currentColorPixel, borderPixel: 0 } ); changeWindowEventMask(X, _trayOwnerWid, TRAY_OWNER_EVENT_MASK); X.ChangeProperty( XPropMode3.Replace, _trayOwnerWid, atoms._NET_SYSTEM_TRAY_ORIENTATION, X.atoms.INTEGER, 32, numsToBuffer([0 /* _NET_SYSTEM_TRAY_ORIENTATION_HORZ */]) ); const selection = atoms[TraySelectionAtom]; const eventData = Buffer.alloc(32); eventData.writeUInt8(X11_EVENT_TYPE.ClientMessage, 0); eventData.writeUInt8(32, 1); eventData.writeUInt32LE(args.root, 4); eventData.writeUInt32LE(atoms.MANAGER, 8); eventData.writeUInt32LE(0, 12); eventData.writeUInt32LE(selection, 16); eventData.writeUInt32LE(_trayOwnerWid, 20); eventData.writeUInt32LE(0, 24); eventData.writeUInt32LE(0, 28); X.SetSelectionOwner(_trayOwnerWid, selection); X.SendEvent(args.root, false, 16777215, eventData); log(`Registered ${_trayOwnerWid} as tray selection owner for ${TraySelectionAtom}.`); }, onUnmapNotify(args) { if (isTrayWin(args.wid)) { store.dispatch(removeTrayWindowAction(args.wid)); const frameId = frameBrowserWinIdToFrameId.get(args.wid); if (typeof frameId === "number") { X.DestroyWindow(frameId); } } }, onClientMessage(args) { if (args.messageType === atoms._NET_SYSTEM_TRAY_OPCODE) { switch (args.data[1]) { case 0 /* SYSTEM_TRAY_REQUEST_DOCK */: { const widToDock = args.data[2]; log(`SYSTEM_TRAY_REQUEST_DOCK, widToDock=${widToDock}`); dockTrayWindow(widToDock); } break; case 1 /* SYSTEM_TRAY_BEGIN_MESSAGE */: { const trayWid = args.wid; const timeout = args.data[2]; const messageLength = args.data[3]; const messageId = args.data[4]; log( `SYSTEM_TRAY_BEGIN_MESSAGE, trayWid=${trayWid}, id=${messageId}, len=${messageLength}, timeout=${timeout}` ); _notificationState[trayWid][messageId] = { text: "", totalSize: messageLength, receivedSize: 0 }; } break; case 2 /* SYSTEM_TRAY_CANCEL_MESSAGE */: { const trayWid = args.wid; const messageId = args.data[2]; log(`SYSTEM_TRAY_CANCEL_MESSAGE, trayWid=${trayWid}, id=${messageId}`); delete _notificationState[trayWid][messageId]; } break; default: log("Unhandled system tray op", args.data[1], SystemTrayOps[args.data[1]]); break; } } else if (args.messageType === atoms._NET_SYSTEM_TRAY_MESSAGE_DATA) { const trayWid = args.wid; let stateEntry; for (const messageId in _notificationState[trayWid]) { if (stateEntry) { logError(`_NET_SYSTEM_TRAY_MESSAGE_DATA: Unexpected: multiple notification entries`); } stateEntry = _notificationState[trayWid][messageId]; } if (!stateEntry) { logError(`_NET_SYSTEM_TRAY_MESSAGE_DATA: Unexpected: message data for non-existent notification`); return; } const sizeToRead = Math.min(20, stateEntry.totalSize - stateEntry.receivedSize); const textBuffer = numsToBuffer(args.data); const partialText = textBuffer.toString("utf8", 0, sizeToRead); log(`_NET_SYSTEM_TRAY_MESSAGE_DATA, trayWid=${trayWid}, partial=${partialText}`); stateEntry.text += partialText; stateEntry.receivedSize += sizeToRead; if (stateEntry.receivedSize === stateEntry.totalSize) { log(`_NET_SYSTEM_TRAY_MESSAGE_DATA, trayWid=${trayWid}, message complete=${stateEntry.text}`); } } }, onReduxAction(args) { if (configureTrayWindowAction.match(args.action)) { const state = args.getState(); const payload = args.action.payload; const wid = payload.wid; const win = state.tray.windows[wid]; if (!win) { return; } const screen = state.screens[payload.screenIndex]; const trayConfig = { x: screen.x + payload.x, y: screen.y + payload.y, width: payload.width, height: payload.height }; log(`Configuring tray window ${wid}`, trayConfig); const frameWid = frameBrowserWinIdToFrameId.get(wid); if (typeof frameWid === "number") { X.ConfigureWindow(frameWid, trayConfig); X.ConfigureWindow(wid, { x: 0, y: 0, width: payload.width, height: payload.height }); } else { X.ConfigureWindow(wid, trayConfig); } X.MapWindow(wid); if (typeof frameWid === "number") { X.RaiseWindow(frameWid); } X.RaiseWindow(wid); } else if (setTrayBackgroundColorAction.ma