bond-wm
Version:
An X Window Manager built on web technologies.
1,493 lines (1,478 loc) • 140 kB
JavaScript
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;
}
});
// notifications.ts
import { app, ipcMain } from "electron";
import { execSync } from "node:child_process";
import { EventEmitter } from "node:events";
import * as dbus from "@particle/dbus-next";
import { interface as dbusInterface, RequestNameReply, Message } from "@particle/dbus-next";
var NotificationIPCMessages, NotificationServer, NotificationInterface;
var init_notifications = __esm({
"notifications.ts"() {
"use strict";
init_log();
NotificationIPCMessages = {
NewNotification: "notification:new",
CloseNotification: "notification:close",
ClearAllNotifications: "notification:clear-all",
NotificationAction: "notification:action",
NotificationClosed: "notification:user-closed",
RequestNotifications: "notification:request-all"
};
NotificationServer = class {
bus = dbus.sessionBus();
activeNotifications = /* @__PURE__ */ new Map();
notificationInterface;
broadcastCallback;
constructor(broadcastCallback) {
this.broadcastCallback = broadcastCallback;
this.notificationInterface = new NotificationInterface(
this.handleNotification.bind(this),
this.parseActions.bind(this),
this.bus
// Pass bus to interface
);
this.setupIPCHandlers();
}
setupIPCHandlers() {
ipcMain.on(NotificationIPCMessages.RequestNotifications, (event) => {
const notifications = Array.from(this.activeNotifications.values());
notifications.forEach((notification) => {
event.reply(NotificationIPCMessages.NewNotification, notification);
});
});
ipcMain.on(NotificationIPCMessages.NotificationClosed, (event, id) => {
if (this.activeNotifications.has(id)) {
this.activeNotifications.delete(id);
this.emitNotificationClosed(id, 2);
this.broadcastToAllDesktops(NotificationIPCMessages.CloseNotification, id);
}
});
ipcMain.on(
NotificationIPCMessages.NotificationAction,
(event, data) => {
const notification = this.activeNotifications.get(data.notificationId);
if (notification) {
try {
this.emitActionInvoked(data.notificationId, data.actionId);
this.activeNotifications.delete(data.notificationId);
this.broadcastToAllDesktops(NotificationIPCMessages.CloseNotification, data.notificationId);
} catch (error) {
logError(`Error processing action:`, error);
}
} else {
logError(`Warning: Notification ${data.notificationId} not found for action ${data.actionId}`);
}
}
);
ipcMain.on(NotificationIPCMessages.ClearAllNotifications, () => {
this.activeNotifications.clear();
this.broadcastToAllDesktops(NotificationIPCMessages.ClearAllNotifications);
});
}
broadcastToAllDesktops(channel, ...args) {
if (this.broadcastCallback) {
this.broadcastCallback(channel, ...args);
}
}
emitActionInvoked(notificationId, actionId) {
try {
if (!this.notificationInterface) {
throw new Error("Notification interface is not initialized");
}
this.notificationInterface.emitActionInvoked(notificationId, actionId);
try {
const cmd = `dbus-send --session --type=signal /org/freedesktop/Notifications org.freedesktop.Notifications.ActionInvoked uint32:${notificationId} string:"${actionId}"`;
execSync(cmd);
} catch (err) {
logError(`Error sending signal via dbus-send:`, err);
}
} catch (error) {
logError("Error emitting ActionInvoked signal:", error);
}
}
emitNotificationClosed(notificationId, reason = 3) {
try {
if (!this.notificationInterface) {
throw new Error("Notification interface is not initialized");
}
this.notificationInterface.emitNotificationClosed(notificationId, reason);
} catch (error) {
logError("Warning: Error emitting NotificationClosed signal:", error);
}
}
async start() {
try {
log("Starting notification server...");
const dbusObj = await this.bus.getProxyObject("org.freedesktop.DBus", "/org/freedesktop/DBus");
const dbusInterface2 = dbusObj.getInterface("org.freedesktop.DBus");
const names = await dbusInterface2.ListNames();
if (names.includes("org.freedesktop.Notifications")) {
log("Notification service already running");
return;
}
this.bus.export("/org/freedesktop/Notifications", this.notificationInterface);
const result = await this.bus.requestName("org.freedesktop.Notifications", 4);
if (result === RequestNameReply.PRIMARY_OWNER) {
log("Notification service registered successfully");
setTimeout(() => {
log(" Testing D-Bus signal emission...");
try {
this.emitActionInvoked(999, "test-action");
this.emitNotificationClosed(999, 1);
} catch (error) {
logError("Error in signal test:", error);
}
}, 2e3);
} else {
logError("Warning: Could not register service. Code:", result);
}
} catch (error) {
logError("Error starting DBus server:", error);
}
}
handleNotification(notification) {
this.activeNotifications.set(notification.id, notification);
this.broadcastToAllDesktops(NotificationIPCMessages.NewNotification, notification);
}
parseActions(actions) {
const parsedActions = [];
for (let i = 0; i < actions.length; i += 2) {
if (actions[i + 1]) {
parsedActions.push({
id: actions[i],
label: actions[i + 1]
});
}
}
return parsedActions;
}
};
NotificationInterface = class extends dbusInterface.Interface {
// Store the bus instance for signal emission
constructor(notifyCallback, parseActions, bus) {
super("org.freedesktop.Notifications");
this.notifyCallback = notifyCallback;
this.parseActions = parseActions;
this.bus = bus;
this.emitter = new EventEmitter();
}
notificationCounter = 1;
emitter;
bus;
// Methods to emit signals directly
emitActionInvoked(notificationId, actionId) {
const signalMessage = new Message({
type: dbus.MessageType.SIGNAL,
path: "/org/freedesktop/Notifications",
interface: "org.freedesktop.Notifications",
member: "ActionInvoked",
signature: "us",
body: [notificationId, actionId]
});
if (this.bus && this.bus.send) {
this.bus.send(signalMessage);
} else {
throw new Error("Bus not available in interface instance");
}
this.emitter.emit("ActionInvoked", notificationId, actionId);
}
emitNotificationClosed(notificationId, reason) {
const signalMessage = new Message({
type: dbus.MessageType.SIGNAL,
path: "/org/freedesktop/Notifications",
interface: "org.freedesktop.Notifications",
member: "NotificationClosed",
signature: "uu",
body: [notificationId, reason]
});
if (this.bus && this.bus.send) {
this.bus.send(signalMessage);
} else {
throw new Error("Bus not available in interface instance");
}
this.emitter.emit("NotificationClosed", notificationId, reason);
}
Notify(appName, replacesId, appIcon, summary, body, actions, hints, expireTimeout) {
const id = replacesId > 0 ? replacesId : this.notificationCounter++;
const notification = {
id,
appName: String(appName),
summary: String(summary),
body: String(body),
appIcon: appIcon ? String(appIcon) : void 0,
expireTimeout,
actions: this.parseActions(actions),
timestamp: Date.now()
};
this.notifyCallback(notification);
return id;
}
CloseNotification(id) {
log(`D-Bus call to CloseNotification for ID: ${id}`);
}
GetCapabilities() {
return ["body", "actions", "persistence", "action-icons", "body-markup", "body-hyperlinks"];
}
GetServerInformation() {
return ["Bond WM Notifications", "Bond WM", app.getVersion(), "1.2"];
}
};
NotificationInterface.configureMembers({
methods: {
Notify: {
inSignature: "susssasa{sv}i",
outSignature: "u"
},
CloseNotification: {
inSignature: "u",
outSignature: ""
},
GetCapabilities: {
inSignature: "",
outSignature: "as"
},
GetServerInformation: {
inSignature: "",
outSignature: "ssss"
}
},
signals: {
ActionInvoked: {
signature: "us"
},
NotificationClosed: {
signature: "uu"
}
}
});
}
});
// 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,
selectWindowMaximizeCanTakeEffect,
selectWindowsFromTag,
setTagCurrentLayoutAction,
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, wmServer, getWindowIdFromFrameId, getLayoutPlugins }, 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_STATE_MAXIMIZED_VERT: await internAtomAsync(X, "_NET_WM_STATE_MAXIMIZED_VERT"),
_NET_WM_STATE_MAXIMIZED_HORZ: await internAtomAsync(X, "_NET_WM_STATE_MAXIMIZED_HORZ"),
_NET_WM_STATE_HIDDEN: await internAtomAsync(X, "_NET_WM_STATE_HIDDEN"),
_NET_WM_ALLOWED_ACTIONS: await internAtomAsync(X, "_NET_WM_ALLOWED_ACTIONS"),
_NET_WM_ACTION_MOVE: await internAtomAsync(X, "_NET_WM_ACTION_MOVE"),
_NET_WM_ACTION_RESIZE: await internAtomAsync(X, "_NET_WM_ACTION_RESIZE"),
_NET_WM_ACTION_MINIMIZE: await internAtomAsync(X, "_NET_WM_ACTION_MINIMIZE"),
_NET_WM_ACTION_SHADE: await internAtomAsync(X, "_NET_WM_ACTION_SHADE"),
_NET_WM_ACTION_STICK: await internAtomAsync(X, "_NET_WM_ACTION_STICK"),
_NET_WM_ACTION_MAXIMIZE_HORZ: await internAtomAsync(X, "_NET_WM_ACTION_MAXIMIZE_HORZ"),
_NET_WM_ACTION_MAXIMIZE_VERT: await internAtomAsync(X, "_NET_WM_ACTION_MAXIMIZE_VERT"),
_NET_WM_ACTION_FULLSCREEN: await internAtomAsync(X, "_NET_WM_ACTION_FULLSCREEN"),
_NET_WM_ACTION_CHANGE_DESKTOP: await internAtomAsync(X, "_NET_WM_ACTION_CHANGE_DESKTOP"),
_NET_WM_ACTION_CLOSE: await internAtomAsync(X, "_NET_WM_ACTION_CLOSE"),
_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);
}
if (win.maximized) {
hintAtoms.push(atoms._NET_WM_STATE_MAXIMIZED_VERT);
hintAtoms.push(atoms._NET_WM_STATE_MAXIMIZED_HORZ);
}
if (win.minimized) {
hintAtoms.push(atoms._NET_WM_STATE_HIDDEN);
}
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 updateWindowAllowedActions(wid) {
const state = store.getState();
const win = state.windows[wid];
if (!win) {
return;
}
const actionAtoms = [
atoms._NET_WM_ACTION_MOVE,
atoms._NET_WM_ACTION_RESIZE,
atoms._NET_WM_ACTION_MINIMIZE,
atoms._NET_WM_ACTION_FULLSCREEN,
atoms._NET_WM_ACTION_CHANGE_DESKTOP,
atoms._NET_WM_ACTION_CLOSE
];
const canMaximize = selectWindowMaximizeCanTakeEffect(state, getLayoutPlugins(win.screenIndex), wid);
if (canMaximize) {
actionAtoms.push(atoms._NET_WM_ACTION_MAXIMIZE_HORZ);
actionAtoms.push(atoms._NET_WM_ACTION_MAXIMIZE_VERT);
}
X.ChangeProperty(
XPropMode.Replace,
wid,
atoms._NET_WM_ALLOWED_ACTIONS,
X.atoms.ATOM,
32,
numsToBuffer(actionAtoms)
);
}
function removeWindowAllowedActions(wid) {
X.DeleteProperty(wid, atoms._NET_WM_ALLOWED_ACTIONS, (err) => {
if (err) {
log("Could not delete _NET_WM_ALLOWED_ACTIONS");
}
});
}
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;
case atoms._NET_WM_STATE_MAXIMIZED_VERT:
case atoms._NET_WM_STATE_MAXIMIZED_HORZ:
processWindowMaximizeChange(wid, action);
break;
case atoms._NET_WM_STATE_HIDDEN:
processWindowMinimizeChange(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 processWindowMaximizeChange(wid, action) {
switch (action) {
case 1 /* _NET_WM_STATE_ADD */:
wmServer.maximizeWindow(wid);
break;
case 0 /* _NET_WM_STATE_REMOVE */:
wmServer.restoreWindow(wid);
break;
case 2 /* _NET_WM_STATE_TOGGLE */:
{
const win = store.getState().windows[wid];
if (!win) {
return;
}
const newMaximized = !win.maximized;
if (newMaximized) {
wmServer.maximizeWindow(wid);
} else {
wmServer.restoreWindow(wid);
}
}
break;
}
}
function processWindowMinimizeChange(wid, action) {
switch (action) {
case 1 /* _NET_WM_STATE_ADD */:
wmServer.minimizeWindow(wid);
break;
case 0 /* _NET_WM_STATE_REMOVE */:
wmServer.restoreWindow(wid);
break;
case 2 /* _NET_WM_STATE_TOGGLE */:
{
const win = store.getState().windows[wid];
if (!win) {
return;
}
const newMinimized = !win.minimized;
if (newMinimized) {
wmServer.minimizeWindow(wid);
} else {
wmServer.restoreWindow(wid);
}
}
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_WM_STATE_MAXIMIZED_VERT,
atoms._NET_WM_STATE_MAXIMIZED_HORZ,
atoms._NET_WM_STATE_HIDDEN,
atoms._NET_WM_ALLOWED_ACTIONS,
atoms._NET_WM_ACTION_MOVE,
atoms._NET_WM_ACTION_RESIZE,
atoms._NET_WM_ACTION_MINIMIZE,
atoms._NET_WM_ACTION_SHADE,
atoms._NET_WM_ACTION_STICK,
atoms._NET_WM_ACTION_MAXIMIZE_HORZ,
atoms._NET_WM_ACTION_MAXIMIZE_VERT,
atoms._NET_WM_ACTION_FULLSCREEN,
atoms._NET_WM_ACTION_CHANGE_DESKTOP,
atoms._NET_WM_ACTION_CLOSE,
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);
updateWindowAllowedActions(wid);
}
},
onUnmapNotify({ wid, windowType }) {
if (windowType === XWMWindowType.Client) {
removeWindowStateHints(wid);
removeWindowAllowedActions(wid);
}
},
onMinimize({ wid }) {
updateWindowStateHints(wid);
},
onMaximize({ wid }) {
updateWindowStateHints(wid);
},
onRestore({ wid }) {
updateWindowStateHints(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);
},
onReduxAction({ action, getState }) {
if (setTagCurrentLayoutAction.match(action)) {
for (const win of selectWindowsFromTag(getState(), action.payload.screenIndex, action.payload.tag)) {
updateWindowAllowedActions(win.id);
}
}
},
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, wmServer }) {
const atoms = {
WM_STATE: await internAtomAsync(X, "WM_STATE"),
WM_CHANGE_STATE: await internAtomAsync(X, "WM_CHANGE_STATE")
};
function updateWindowState(wid, state = 1 /* NormalState */) {
const wmStateBuffer = Buffer.alloc(8);
wmStateBuffer.writeUInt32LE(state, 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);
}
},
onMinimize({ wid }) {
updateWindowState(wid, 3 /* IconicState */);
},
onRestore({ wid }) {
updateWindowState(wid, 1 /* NormalState */);
},
onClientMessage({ wid, data, messageType, windowType }) {
if (windowType !== XWMWindowType2.Client) {
return;
}
if (messageType === atoms.WM_CHANGE_STATE) {
const stateValue = data[0];
switch (stateValue) {
case 3 /* IconicState */:
wmServer.minimizeWindow(wid);
break;
case 1 /* NormalState */:
wmServer.restoreWindow(wid);
break;
}
}
}
};
}
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 as app2, 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: () => {
app2.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 as ipcMain2 } from "electron";
function setupAutocompleteListener() {
ipcMain2.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 as selectWindowMaximizeCanTakeEffect2 } 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 && selectWindowMaximizeCanTakeEffect2(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 && selectWindowMaximizeCanTakeEffect2(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:
{
const desiredWidth = startOuterSize.width - xDiff;
const desiredHeight = startOuterSize.height - yDiff;
const snappedWidth = newWidthForWindow(win, desiredWidth);
const snappedHeight = newHeightForWindow(win, desiredHeight);
configureWindow(win, {
x: startOuterSize.x + (startOuterSize.width - snappedWidth),
y: startOuterSize.y + (startOuterSize.height - snappedHeight),
width: snappedWidth,
height: snappedHeight
});
}
break;
case ResizeDirection2.Top:
{
const desiredHeight = startOuterSize.height - yDiff;
const snappedHeight = newHeightForWindow(win, desiredHeight);
configureWindow(win, {
y: startOuterSize.y + (startOuterSize.height - snappedHeight),
height: snappedHeight
});
}
break;
case ResizeDirection2.TopRight:
{
const desiredWidth = startOuterSize.width + xDiff;
const desiredHeight = startOuterSize.height - yDiff;
const snappedWidth = newWidthForWindow(win, desiredWidth);
const snappedHeight = newHeightForWindow(win, desiredHeight);
configureWindow(win, {
y: startOuterSize.y + (startOuterSize.height - snappedHeight),
width: snappedWidth,
height: snappedHeight
});
}
break;
case ResizeDirection2.Right:
configureWindow(win, {
width: startOuterSize.width + xDiff
});
break;
case ResizeDirection2.BottomRight: