Comparing version 1.2.2 to 1.3.0
@@ -1,45 +0,46 @@ | ||
import { MessageEvent, Event, CloseEvent, ErrorEvent, WebSocket } from 'ws'; | ||
import { MessageEvent, Event, CloseEvent, ErrorEvent } from 'ws'; | ||
interface BorderDelta { | ||
left: number; | ||
top: number; | ||
right: number; | ||
bottom: number; | ||
interface KeybindingConfig { | ||
bindings: string[]; | ||
command: unknown; | ||
} | ||
interface BindingModeConfig { | ||
name: string; | ||
displayName: string; | ||
keybindings: KeybindingConfig; | ||
} | ||
declare enum ContainerType { | ||
RootContainer = "root_container", | ||
Monitor = "monitor", | ||
Workspace = "workspace", | ||
SplitContainer = "split_container", | ||
FloatingWindow = "floating_window", | ||
TilingWindow = "tiling_window", | ||
MinimizedWindow = "minimized_window", | ||
MaximizedWindow = "maximized_window", | ||
FullscreenWindow = "fullscreen_window" | ||
ROOT = "root", | ||
MONITOR = "monitor", | ||
WORKSPACE = "workspace", | ||
SPLIT = "split", | ||
WINDOW = "window" | ||
} | ||
interface Container { | ||
id: string; | ||
x: number; | ||
y: number; | ||
width: number; | ||
height: number; | ||
type: ContainerType; | ||
focusIndex: number; | ||
children: Container[]; | ||
interface AppMetadata { | ||
version: string; | ||
} | ||
declare enum Direction { | ||
Left = "left", | ||
Right = "right", | ||
Up = "up", | ||
Down = "down" | ||
declare enum DisplayState { | ||
SHOWN = "shown", | ||
SHOWING = "showing", | ||
HIDDEN = "hidden", | ||
HIDING = "hiding" | ||
} | ||
interface EventSubscription { | ||
subscriptionId: string; | ||
interface LengthUnit { | ||
amount: number; | ||
unit: 'pixel' | 'percentage'; | ||
} | ||
interface FloatingPlacement { | ||
interface RectDelta { | ||
left: LengthUnit; | ||
top: LengthUnit; | ||
right: LengthUnit; | ||
bottom: LengthUnit; | ||
} | ||
interface Rect { | ||
left: number; | ||
@@ -49,120 +50,172 @@ top: number; | ||
bottom: number; | ||
x: number; | ||
y: number; | ||
width: number; | ||
height: number; | ||
} | ||
interface Window extends Container { | ||
floatingPlacement: FloatingPlacement; | ||
borderDelta: BorderDelta; | ||
handle: number; | ||
children: []; | ||
declare enum TilingDirection { | ||
VERTICAL = "vertical", | ||
HORIZONTAL = "horizontal" | ||
} | ||
interface FloatingWindow extends Window { | ||
type: ContainerType.FloatingWindow; | ||
declare enum WindowType { | ||
TILING = "tiling", | ||
FLOATING = "floating", | ||
MINIMIZED = "minimized", | ||
FULLSCREEN = "fullscreen" | ||
} | ||
interface FullscreenWindow extends Window { | ||
type: ContainerType.FullscreenWindow; | ||
} | ||
type WindowState = { | ||
type: WindowType.FLOATING; | ||
centered: boolean; | ||
shownOnTop: boolean; | ||
} | { | ||
type: WindowType.FULLSCREEN; | ||
maximized: boolean; | ||
shownOnTop: boolean; | ||
} | { | ||
type: WindowType.MINIMIZED; | ||
} | { | ||
type: WindowType.TILING; | ||
}; | ||
interface MaximizedWindow extends Window { | ||
type: ContainerType.MaximizedWindow; | ||
interface Window { | ||
id: string; | ||
type: ContainerType.WINDOW; | ||
parent: string; | ||
hasFocus: boolean; | ||
floatingPlacement: Rect; | ||
borderDelta: RectDelta; | ||
handle: number; | ||
tilingSize: number | null; | ||
state: WindowState; | ||
prevState: WindowState | null; | ||
displayState: DisplayState; | ||
title: string; | ||
processName: string; | ||
className: string; | ||
width: number; | ||
height: number; | ||
x: number; | ||
y: number; | ||
} | ||
declare enum WindowState { | ||
Tiling = "tiling", | ||
Floating = "floating", | ||
Minimized = "minimized", | ||
Maximized = "maximized", | ||
Fullscreen = "fullscreen" | ||
interface SplitContainer { | ||
id: string; | ||
type: ContainerType.SPLIT; | ||
parent: string; | ||
childFocusOrder: string[]; | ||
children: (SplitContainer | Window)[]; | ||
hasFocus: boolean; | ||
tilingDirection: TilingDirection; | ||
tilingSize: number; | ||
width: number; | ||
height: number; | ||
x: number; | ||
y: number; | ||
} | ||
interface MinimizedWindow extends Window { | ||
type: ContainerType.MinimizedWindow; | ||
previousState: WindowState; | ||
} | ||
declare enum TilingDirection { | ||
Vertical = "vertical", | ||
Horizontal = "horizontal" | ||
} | ||
interface TilingWindow extends Window { | ||
type: ContainerType.TilingWindow; | ||
sizePercentage: number; | ||
} | ||
interface SplitContainer extends Container { | ||
type: ContainerType.SplitContainer; | ||
layout: TilingDirection; | ||
sizePercentage: number; | ||
children: (SplitContainer | TilingWindow | FloatingWindow | MinimizedWindow | MaximizedWindow | FullscreenWindow)[]; | ||
} | ||
interface Workspace extends Container { | ||
type: ContainerType.Workspace; | ||
layout: TilingDirection; | ||
sizePercentage: number; | ||
interface Workspace { | ||
id: string; | ||
type: ContainerType.WORKSPACE; | ||
parent: string; | ||
childFocusOrder: string[]; | ||
children: (SplitContainer | Window)[]; | ||
hasFocus: boolean; | ||
isDisplayed: boolean; | ||
name: string; | ||
children: (SplitContainer | TilingWindow | FloatingWindow | MinimizedWindow | MaximizedWindow | FullscreenWindow)[]; | ||
tilingDirection: TilingDirection; | ||
width: number; | ||
height: number; | ||
x: number; | ||
y: number; | ||
} | ||
interface Monitor extends Container { | ||
type: ContainerType.Monitor; | ||
deviceName: string; | ||
interface Monitor { | ||
id: string; | ||
type: ContainerType.MONITOR; | ||
parent: string; | ||
childFocusOrder: string[]; | ||
children: Workspace[]; | ||
hasFocus: boolean; | ||
dpi: number; | ||
width: number; | ||
height: number; | ||
x: number; | ||
y: number; | ||
handle: number; | ||
device_name: string; | ||
device_path: string | null; | ||
hardware_id: string | null; | ||
working_rect: Rect; | ||
} | ||
declare enum ResizeDimension { | ||
Height = "height", | ||
Width = "width" | ||
} | ||
interface RootContainer extends Container { | ||
type: ContainerType.RootContainer; | ||
interface RootContainer { | ||
id: string; | ||
type: ContainerType.ROOT; | ||
parent: null; | ||
childFocusOrder: string[]; | ||
children: Monitor[]; | ||
} | ||
type FocusInDirectionCommand = `focus ${Direction}`; | ||
type FocusWorkspaceCommand = `focus workspace ${string}`; | ||
type MoveWindowCommand = `move ${Direction}`; | ||
type MoveWindowToWorkspaceCommand = `move to workspace ${string}`; | ||
type SetWindowSizeCommand = `set ${ResizeDimension} ${string}`; | ||
type ResizeWindowCommand = `resize ${ResizeDimension} ${string}`; | ||
type ResizeWindowBordersCommand = `resize borders ${string}`; | ||
type SetWindowStateCommand = `set ${WindowState}`; | ||
type ToggleWindowStateCommand = `toggle ${Exclude<WindowState, 'minimized' | 'maximized'>}`; | ||
type ChangeFocusModeCommand = 'focus mode toggle'; | ||
type ChangeTilingDirectionCommand = `tiling direction ${TilingDirection | 'toggle'}`; | ||
type ExitWmCommand = 'exit wm'; | ||
type ReloadConfigCommand = 'reload config'; | ||
type CloseWindowCommand = 'close'; | ||
type ExecProcessCommand = `exec ${string}`; | ||
type IgnoreCommand = 'ignore'; | ||
type WmCommand = FocusInDirectionCommand | FocusWorkspaceCommand | MoveWindowCommand | MoveWindowToWorkspaceCommand | SetWindowSizeCommand | ResizeWindowCommand | ResizeWindowBordersCommand | SetWindowStateCommand | ToggleWindowStateCommand | ChangeFocusModeCommand | ChangeTilingDirectionCommand | ExitWmCommand | ReloadConfigCommand | CloseWindowCommand | ExecProcessCommand | IgnoreCommand; | ||
type Container = RootContainer | Monitor | Workspace | SplitContainer | Window; | ||
type SubscribeMessage = 'subscribe' | `subscribe --events ${string}` | `subscribe -e ${string}`; | ||
type UnsubscribeMessage = `unsubscribe ${string}`; | ||
type InvokeCommandMessage = `command "${WmCommand}"` | `command "${WmCommand}" --context-container-id ${string}` | `command "${WmCommand}" -c ${string}`; | ||
type GetMonitorsMessage = 'monitors'; | ||
type GetWorkspacesMessage = 'workspaces'; | ||
type GetWindowsMessage = 'windows'; | ||
type GetFocusedContainerMessage = 'focused_container'; | ||
type GetBindingModeMessage = 'binding_mode'; | ||
type ClientMessage = SubscribeMessage | UnsubscribeMessage | InvokeCommandMessage | GetMonitorsMessage | GetWorkspacesMessage | GetWindowsMessage | GetFocusedContainerMessage | GetBindingModeMessage; | ||
/** | ||
* All possible GlazeWM event types (e.g. `'focus_changed'`). | ||
*/ | ||
declare enum WmEventType { | ||
ALL = "all", | ||
APPLICATION_EXITING = "application_exiting", | ||
BINDING_MODES_CHANGED = "binding_modes_changed", | ||
FOCUS_CHANGED = "focus_changed", | ||
FOCUSED_CONTAINER_MOVED = "focused_container_moved", | ||
MONITOR_ADDED = "monitor_added", | ||
MONITOR_UPDATED = "monitor_updated", | ||
MONITOR_REMOVED = "monitor_removed", | ||
TILING_DIRECTION_CHANGED = "tiling_direction_changed", | ||
USER_CONFIG_CHANGED = "user_config_changed", | ||
WINDOW_MANAGED = "window_managed", | ||
WINDOW_UNMANAGED = "window_unmanaged", | ||
WORKSPACE_ACTIVATED = "workspace_activated", | ||
WORKSPACE_DEACTIVATED = "workspace_deactivated", | ||
WORKSPACE_MOVED = "workspace_moved" | ||
} | ||
/** | ||
* All possible GlazeWM event interfaces. | ||
*/ | ||
type WmEvent = ApplicationExitingEvent | BindingModesChangedEvent | FocusChangedEvent | FocusedContainerMovedEvent | MonitorAddedEvent | MonitorUpdatedEvent | MonitorRemovedEvent | TilingDirectionChangedEvent | UserConfigChangedEvent | WindowManagedEvent | WindowUnmanagedEvent | WorkspaceActivatedEvent | WorkspaceDeactivatedEvent | WorkspaceMovedEvent; | ||
/** | ||
* Utility type for getting event interface for given {@link WmEventType}. | ||
* | ||
* @example | ||
* ```typescript | ||
* type Example = WmEventData<WmEventType.MONITOR_ADDED> // -> MonitorAddedEvent | ||
* ``` | ||
*/ | ||
type WmEventData<T extends WmEventType = WmEventType.ALL> = { | ||
[WmEventType.ALL]: WmEvent; | ||
[WmEventType.APPLICATION_EXITING]: ApplicationExitingEvent; | ||
[WmEventType.BINDING_MODES_CHANGED]: BindingModesChangedEvent; | ||
[WmEventType.FOCUS_CHANGED]: FocusChangedEvent; | ||
[WmEventType.FOCUSED_CONTAINER_MOVED]: FocusedContainerMovedEvent; | ||
[WmEventType.MONITOR_ADDED]: MonitorAddedEvent; | ||
[WmEventType.MONITOR_REMOVED]: MonitorRemovedEvent; | ||
[WmEventType.MONITOR_UPDATED]: MonitorUpdatedEvent; | ||
[WmEventType.TILING_DIRECTION_CHANGED]: TilingDirectionChangedEvent; | ||
[WmEventType.WINDOW_MANAGED]: WindowManagedEvent; | ||
[WmEventType.WINDOW_UNMANAGED]: WindowUnmanagedEvent; | ||
[WmEventType.USER_CONFIG_CHANGED]: UserConfigChangedEvent; | ||
[WmEventType.WORKSPACE_ACTIVATED]: WorkspaceActivatedEvent; | ||
[WmEventType.WORKSPACE_DEACTIVATED]: WorkspaceDeactivatedEvent; | ||
[WmEventType.WORKSPACE_MOVED]: WorkspaceMovedEvent; | ||
}[T]; | ||
interface ApplicationExitingEvent { | ||
type: WmEventType.ApplicationExiting; | ||
type: WmEventType.APPLICATION_EXITING; | ||
} | ||
interface BindingModeChangedEvent { | ||
type: WmEventType.BindingModeChanged; | ||
bindingMode: string; | ||
interface BindingModesChangedEvent { | ||
type: WmEventType.BINDING_MODES_CHANGED; | ||
activeBindingModes: BindingModeConfig[]; | ||
} | ||
interface FocusChangedEvent { | ||
type: WmEventType.FocusChanged; | ||
type: WmEventType.FOCUS_CHANGED; | ||
focusedContainer: Container; | ||
@@ -172,13 +225,8 @@ } | ||
interface FocusedContainerMovedEvent { | ||
type: WmEventType.FocusedContainerMoved; | ||
type: WmEventType.FOCUSED_CONTAINER_MOVED; | ||
focusedContainer: Container; | ||
} | ||
interface TilingDirectionChangedEvent { | ||
type: WmEventType.TilingDirectionChanged; | ||
newTilingDirection: TilingDirection; | ||
} | ||
interface MonitorAddedEvent { | ||
type: WmEventType.MonitorAdded; | ||
type: WmEventType.MONITOR_ADDED; | ||
addedMonitor: Monitor; | ||
@@ -188,3 +236,3 @@ } | ||
interface MonitorRemovedEvent { | ||
type: WmEventType.MonitorRemoved; | ||
type: WmEventType.MONITOR_REMOVED; | ||
removedId: string; | ||
@@ -194,8 +242,21 @@ removedDeviceName: string; | ||
interface UserConfigReloadedEvent { | ||
type: WmEventType.UserConfigReloaded; | ||
interface MonitorUpdatedEvent { | ||
type: WmEventType.MONITOR_UPDATED; | ||
updatedMonitor: Monitor; | ||
} | ||
interface TilingDirectionChangedEvent { | ||
type: WmEventType.TILING_DIRECTION_CHANGED; | ||
newTilingDirection: TilingDirection; | ||
} | ||
interface UserConfigChangedEvent { | ||
type: WmEventType.USER_CONFIG_CHANGED; | ||
configPath: string; | ||
configString: string; | ||
parsedConfig: any; | ||
} | ||
interface WindowManagedEvent { | ||
type: WmEventType.WindowManaged; | ||
type: WmEventType.WINDOW_MANAGED; | ||
managedWindow: Window; | ||
@@ -205,3 +266,3 @@ } | ||
interface WindowUnmanagedEvent { | ||
type: WmEventType.WindowUnmanaged; | ||
type: WmEventType.WINDOW_UNMANAGED; | ||
unmanagedId: string; | ||
@@ -211,9 +272,4 @@ unmanagedHandle: number; | ||
interface WorkingAreaResizedEvent { | ||
type: WmEventType.WorkingAreaResized; | ||
affectedMonitor: Monitor; | ||
} | ||
interface WorkspaceActivatedEvent { | ||
type: WmEventType.WorkspaceActivated; | ||
type: WmEventType.WORKSPACE_ACTIVATED; | ||
activatedWorkspace: Workspace; | ||
@@ -223,63 +279,25 @@ } | ||
interface WorkspaceDeactivatedEvent { | ||
type: WmEventType.WorkspaceDeactivated; | ||
removedId: string; | ||
removedName: string; | ||
type: WmEventType.WORKSPACE_DEACTIVATED; | ||
deactivatedId: string; | ||
deactivatedName: string; | ||
} | ||
/** | ||
* All possible GlazeWM event types (eg. `'focus_changed'`). | ||
*/ | ||
declare enum WmEventType { | ||
All = "all", | ||
BindingModeChanged = "binding_mode_changed", | ||
FocusChanged = "focus_changed", | ||
FocusedContainerMoved = "focused_container_moved", | ||
MonitorAdded = "monitor_added", | ||
MonitorRemoved = "monitor_removed", | ||
TilingDirectionChanged = "tiling_direction_changed", | ||
UserConfigReloaded = "user_config_reloaded", | ||
WindowManaged = "window_managed", | ||
WindowUnmanaged = "window_unmanaged", | ||
WorkspaceActivated = "workspace_activated", | ||
WorkspaceDeactivated = "workspace_deactivated", | ||
WorkingAreaResized = "working_area_resized", | ||
ApplicationExiting = "application_exiting" | ||
interface WorkspaceMovedEvent { | ||
type: WmEventType.WORKSPACE_MOVED; | ||
movedWorkspace: Workspace; | ||
newMonitor: Monitor; | ||
} | ||
/** | ||
* All possible GlazeWM event interfaces. | ||
*/ | ||
type WmEvent = ApplicationExitingEvent | BindingModeChangedEvent | FocusChangedEvent | FocusedContainerMovedEvent | MonitorAddedEvent | MonitorRemovedEvent | TilingDirectionChangedEvent | UserConfigReloadedEvent | WindowManagedEvent | WindowUnmanagedEvent | WorkspaceActivatedEvent | WorkspaceDeactivatedEvent | WorkingAreaResizedEvent; | ||
/** | ||
* Utility type for getting event interface for given {@link WmEventType}. | ||
* | ||
* @example | ||
* ```typescript | ||
* type Example = WmEventData<WmEventType.MONITOR_ADDED> // -> MonitorAddedEvent | ||
* ``` | ||
*/ | ||
type WmEventData<T extends WmEventType = WmEventType.All> = { | ||
[WmEventType.All]: WmEvent; | ||
[WmEventType.ApplicationExiting]: ApplicationExitingEvent; | ||
[WmEventType.BindingModeChanged]: BindingModeChangedEvent; | ||
[WmEventType.FocusChanged]: FocusChangedEvent; | ||
[WmEventType.FocusedContainerMoved]: FocusedContainerMovedEvent; | ||
[WmEventType.MonitorAdded]: MonitorAddedEvent; | ||
[WmEventType.MonitorRemoved]: MonitorRemovedEvent; | ||
[WmEventType.TilingDirectionChanged]: TilingDirectionChangedEvent; | ||
[WmEventType.WindowManaged]: WindowManagedEvent; | ||
[WmEventType.WindowUnmanaged]: WindowUnmanagedEvent; | ||
[WmEventType.UserConfigReloaded]: UserConfigReloadedEvent; | ||
[WmEventType.WorkspaceActivated]: WorkspaceActivatedEvent; | ||
[WmEventType.WorkspaceDeactivated]: WorkspaceDeactivatedEvent; | ||
[WmEventType.WorkingAreaResized]: WorkingAreaResizedEvent; | ||
}[T]; | ||
declare enum ServerMessageType { | ||
ClientResponse = "client_response", | ||
EventSubscription = "event_subscription" | ||
CLIENT_RESPONSE = "client_response", | ||
EVENT_SUBSCRIPTION = "event_subscription" | ||
} | ||
interface BaseServerMessage<T> { | ||
/** Whether the server message has an associated error. */ | ||
/** | ||
* Whether the server message has an associated error. | ||
*/ | ||
success: boolean; | ||
/** The type of server message. */ | ||
/** | ||
* The type of server message. | ||
*/ | ||
messageType: ServerMessageType; | ||
@@ -290,3 +308,3 @@ /** | ||
*/ | ||
data?: T; | ||
data: T | null; | ||
/** | ||
@@ -296,6 +314,6 @@ * The error message. This property is only present for messages where | ||
*/ | ||
error?: string; | ||
error: string | null; | ||
} | ||
interface ClientResponseMessage<T> extends BaseServerMessage<T> { | ||
messageType: ServerMessageType.ClientResponse; | ||
messageType: ServerMessageType.CLIENT_RESPONSE; | ||
/** | ||
@@ -307,3 +325,3 @@ * The client message that this is in response to. | ||
interface EventSubscriptionMessage<T> extends BaseServerMessage<T> { | ||
messageType: ServerMessageType.EventSubscription; | ||
messageType: ServerMessageType.EVENT_SUBSCRIPTION; | ||
/** | ||
@@ -320,3 +338,3 @@ * A unique identifier for the subscription. | ||
} | ||
/** Unregister a callback. */ | ||
/** Unregisters a callback. */ | ||
type UnlistenFn = () => void; | ||
@@ -340,44 +358,55 @@ type MessageCallback = (e: MessageEvent) => void; | ||
/** | ||
* Instantiate client. Connection to IPC server is established when sending | ||
* the first message or by explicitly calling {@link connect}. | ||
* Instantiates client. | ||
* | ||
* Connection to the IPC server is established when sending the first | ||
* message or by explicitly calling {@link connect}. | ||
*/ | ||
constructor(_options?: WmClientOptions | undefined); | ||
/** | ||
* Send an IPC message and wait for a reply. | ||
* | ||
* @throws If message is invalid or IPC server is unable to handle the message. | ||
* Gets all monitors. {@link Monitor} | ||
*/ | ||
sendAndWaitReply<T>(message: ClientMessage): Promise<T>; | ||
queryMonitors(): Promise<{ | ||
monitors: Monitor[]; | ||
}>; | ||
/** | ||
* Get all monitors. {@link Monitor} | ||
* Gets all active workspaces. {@link Workspace} | ||
*/ | ||
getMonitors(): Promise<Monitor[]>; | ||
queryWorkspaces(): Promise<{ | ||
workspaces: Workspace[]; | ||
}>; | ||
/** | ||
* Get all active workspaces. {@link Workspace} | ||
* Gets all managed windows. {@link Window} | ||
*/ | ||
getWorkspaces(): Promise<Workspace[]>; | ||
queryWindows(): Promise<{ | ||
windows: Window[]; | ||
}>; | ||
/** | ||
* Get all windows. {@link Window} | ||
* Gets the currently focused container. This can either be a | ||
* {@link Window} or a {@link Workspace} without any descendant windows. | ||
*/ | ||
getWindows(): Promise<Window[]>; | ||
queryFocused(): Promise<{ | ||
focused: Window | Workspace; | ||
}>; | ||
/** | ||
* Get the currently focused container. This can either be a | ||
* {@link Window} or a {@link Workspace} without any descendant windows. | ||
* Gets the active binding modes. {@link BindingModeConfig} | ||
*/ | ||
getFocusedContainer(): Promise<Container>; | ||
queryBindingModes(): Promise<{ | ||
bindingModes: BindingModeConfig[]; | ||
}>; | ||
/** | ||
* Get the name of the active binding mode (if one is active). | ||
* Gets metadata about the running GlazeWM application. | ||
* {@link AppMetadata} | ||
*/ | ||
getBindingMode(): Promise<string | null>; | ||
queryAppMetadata(): Promise<AppMetadata>; | ||
/** | ||
* Invoke a WM command (eg. "focus workspace 1"). | ||
* Invokes a WM command (e.g. `"focus --workspace 1"`). | ||
* | ||
* @param command WM command to run (eg. "focus workspace 1"). | ||
* @param contextContainer (optional) Container or ID of container to use as | ||
* context. If not provided, this defaults to the currently focused container. | ||
* @param command WM command to run (e.g. `"focus --workspace 1"`). | ||
* @param subjectContainerId (optional) ID of container to use as subject. | ||
* If not provided, this defaults to the currently focused container. | ||
* @throws If command fails. | ||
*/ | ||
runCommand(command: WmCommand, contextContainer?: Container | string): Promise<void>; | ||
runCommand(command: string, subjectContainerId?: string): Promise<void>; | ||
/** | ||
* Establish websocket connection. | ||
* Establishes websocket connection. | ||
* | ||
@@ -388,7 +417,7 @@ * @throws If connection attempt fails. | ||
/** | ||
* Close the websocket connection. | ||
* Closes the websocket connection. | ||
*/ | ||
close(): void; | ||
closeConnection(): void; | ||
/** | ||
* Register a callback for one GlazeWM event. | ||
* Registers a callback for a GlazeWM event type. | ||
* | ||
@@ -405,3 +434,3 @@ * @example | ||
/** | ||
* Register a callback for multiple GlazeWM events. | ||
* Registers a callback for multiple GlazeWM event types. | ||
* | ||
@@ -418,3 +447,3 @@ * @example | ||
/** | ||
* Register a callback for when websocket messages are received. | ||
* Registers a callback for when websocket messages are received. | ||
* | ||
@@ -428,3 +457,3 @@ * @example | ||
/** | ||
* Register a callback for when the websocket connects. | ||
* Registers a callback for when the websocket connects. | ||
* | ||
@@ -438,3 +467,3 @@ * @example | ||
/** | ||
* Register a callback for when the websocket disconnects. | ||
* Registers a callback for when the websocket disconnects. | ||
* | ||
@@ -448,4 +477,4 @@ * @example | ||
/** | ||
* Register a callback for when the websocket connection has been closed due | ||
* to an error. | ||
* Registers a callback for when the websocket connection has been closed | ||
* due to an error. | ||
* | ||
@@ -458,7 +487,30 @@ * @example | ||
onError(callback: ErrorCallback): UnlistenFn; | ||
/** | ||
* Sends an IPC message and waits for a reply. | ||
* | ||
* @private | ||
* @throws If message is invalid or IPC server is unable to handle the | ||
* message. | ||
*/ | ||
private _sendAndWaitReply; | ||
/** | ||
* Utility function for registering a callback. | ||
* | ||
* @private | ||
*/ | ||
private _registerCallback; | ||
/** | ||
* Instantiates `WebSocket` and adds event listeners for socket events. | ||
* | ||
* @private | ||
*/ | ||
private _createSocket; | ||
_waitForConnection(): Promise<WebSocket>; | ||
/** | ||
* Waits for the websocket connection to be established. | ||
* | ||
* @private | ||
*/ | ||
private _waitForConnection; | ||
} | ||
export { BorderDelta, ChangeFocusModeCommand, ChangeTilingDirectionCommand, ClientMessage, ClientResponseMessage, CloseWindowCommand, ConnectCallback, Container, Direction, DisconnectCallback, ErrorCallback, EventSubscription, EventSubscriptionMessage, ExecProcessCommand, ExitWmCommand, FloatingPlacement, FloatingWindow, FocusInDirectionCommand, FocusWorkspaceCommand, FullscreenWindow, GetBindingModeMessage, GetFocusedContainerMessage, GetMonitorsMessage, GetWindowsMessage, GetWorkspacesMessage, IgnoreCommand, InvokeCommandMessage, MaximizedWindow, MessageCallback, MinimizedWindow, Monitor, MoveWindowCommand, MoveWindowToWorkspaceCommand, ReloadConfigCommand, ResizeDimension, ResizeWindowBordersCommand, ResizeWindowCommand, RootContainer, ServerMessage, ServerMessageType, SetWindowSizeCommand, SetWindowStateCommand, SplitContainer, SubscribeCallback, SubscribeMessage, TilingDirection, TilingWindow, ToggleWindowStateCommand, UnlistenFn, UnsubscribeMessage, Window, WindowState, WmClient, WmClientOptions, WmCommand, WmEvent, WmEventData, WmEventType, Workspace }; | ||
export { AppMetadata, ApplicationExitingEvent, BindingModeConfig, BindingModesChangedEvent, ClientResponseMessage, ConnectCallback, Container, ContainerType, DisconnectCallback, DisplayState, ErrorCallback, EventSubscriptionMessage, FocusChangedEvent, FocusedContainerMovedEvent, KeybindingConfig, LengthUnit, MessageCallback, Monitor, MonitorAddedEvent, MonitorRemovedEvent, MonitorUpdatedEvent, Rect, RectDelta, RootContainer, ServerMessage, ServerMessageType, SplitContainer, SubscribeCallback, TilingDirection, TilingDirectionChangedEvent, UnlistenFn, UserConfigChangedEvent, Window, WindowManagedEvent, WindowState, WindowType, WindowUnmanagedEvent, WmClient, WmClientOptions, WmEvent, WmEventData, WmEventType, Workspace, WorkspaceActivatedEvent, WorkspaceDeactivatedEvent, WorkspaceMovedEvent }; |
@@ -33,7 +33,7 @@ "use strict"; | ||
__export(src_exports, { | ||
Direction: () => Direction, | ||
ResizeDimension: () => ResizeDimension, | ||
ContainerType: () => ContainerType, | ||
DisplayState: () => DisplayState, | ||
ServerMessageType: () => ServerMessageType, | ||
TilingDirection: () => TilingDirection, | ||
WindowState: () => WindowState, | ||
WindowType: () => WindowType, | ||
WmClient: () => WmClient, | ||
@@ -47,4 +47,6 @@ WmEventType: () => WmEventType | ||
/** | ||
* Instantiate client. Connection to IPC server is established when sending | ||
* the first message or by explicitly calling {@link connect}. | ||
* Instantiates client. | ||
* | ||
* Connection to the IPC server is established when sending the first | ||
* message or by explicitly calling {@link connect}. | ||
*/ | ||
@@ -64,78 +66,62 @@ constructor(_options) { | ||
/** | ||
* Send an IPC message and wait for a reply. | ||
* | ||
* @throws If message is invalid or IPC server is unable to handle the message. | ||
* Gets all monitors. {@link Monitor} | ||
*/ | ||
async sendAndWaitReply(message) { | ||
let unlisten; | ||
return new Promise(async (resolve, reject) => { | ||
await this.connect(); | ||
this._socket.send(message); | ||
unlisten = this.onMessage((e) => { | ||
const serverMessage = JSON.parse( | ||
e.data | ||
); | ||
const isReplyMessage = serverMessage.messageType === "client_response" && serverMessage.clientMessage === message; | ||
if (isReplyMessage && serverMessage.error) { | ||
reject( | ||
`Server reply to message '${message}' has error: ${serverMessage.error}` | ||
); | ||
} | ||
if (isReplyMessage) { | ||
resolve(serverMessage.data); | ||
} | ||
}); | ||
}).finally(() => unlisten()); | ||
async queryMonitors() { | ||
return this._sendAndWaitReply( | ||
"query monitors" | ||
); | ||
} | ||
/** | ||
* Get all monitors. {@link Monitor} | ||
* Gets all active workspaces. {@link Workspace} | ||
*/ | ||
async getMonitors() { | ||
return this.sendAndWaitReply("monitors"); | ||
async queryWorkspaces() { | ||
return this._sendAndWaitReply( | ||
"query workspaces" | ||
); | ||
} | ||
/** | ||
* Get all active workspaces. {@link Workspace} | ||
* Gets all managed windows. {@link Window} | ||
*/ | ||
async getWorkspaces() { | ||
return this.sendAndWaitReply("workspaces"); | ||
async queryWindows() { | ||
return this._sendAndWaitReply("query windows"); | ||
} | ||
/** | ||
* Get all windows. {@link Window} | ||
* Gets the currently focused container. This can either be a | ||
* {@link Window} or a {@link Workspace} without any descendant windows. | ||
*/ | ||
async getWindows() { | ||
return this.sendAndWaitReply("windows"); | ||
async queryFocused() { | ||
return this._sendAndWaitReply( | ||
"query focused" | ||
); | ||
} | ||
/** | ||
* Get the currently focused container. This can either be a | ||
* {@link Window} or a {@link Workspace} without any descendant windows. | ||
* Gets the active binding modes. {@link BindingModeConfig} | ||
*/ | ||
async getFocusedContainer() { | ||
return this.sendAndWaitReply("focused_container"); | ||
async queryBindingModes() { | ||
return this._sendAndWaitReply( | ||
"query binding-modes" | ||
); | ||
} | ||
/** | ||
* Get the name of the active binding mode (if one is active). | ||
* Gets metadata about the running GlazeWM application. | ||
* {@link AppMetadata} | ||
*/ | ||
async getBindingMode() { | ||
return this.sendAndWaitReply("binding_mode"); | ||
async queryAppMetadata() { | ||
return this._sendAndWaitReply("query app-metadata"); | ||
} | ||
/** | ||
* Invoke a WM command (eg. "focus workspace 1"). | ||
* Invokes a WM command (e.g. `"focus --workspace 1"`). | ||
* | ||
* @param command WM command to run (eg. "focus workspace 1"). | ||
* @param contextContainer (optional) Container or ID of container to use as | ||
* context. If not provided, this defaults to the currently focused container. | ||
* @param command WM command to run (e.g. `"focus --workspace 1"`). | ||
* @param subjectContainerId (optional) ID of container to use as subject. | ||
* If not provided, this defaults to the currently focused container. | ||
* @throws If command fails. | ||
*/ | ||
async runCommand(command, contextContainer) { | ||
if (!contextContainer) { | ||
await this.sendAndWaitReply(`command "${command}"`); | ||
return; | ||
} | ||
const contextContainerId = typeof contextContainer === "string" ? contextContainer : contextContainer.id; | ||
await this.sendAndWaitReply( | ||
`command "${command}" -c ${contextContainerId}` | ||
async runCommand(command, subjectContainerId) { | ||
await this._sendAndWaitReply( | ||
subjectContainerId ? `command --id ${subjectContainerId} ${command}` : `command ${command}` | ||
); | ||
} | ||
/** | ||
* Establish websocket connection. | ||
* Establishes websocket connection. | ||
* | ||
@@ -152,9 +138,9 @@ * @throws If connection attempt fails. | ||
/** | ||
* Close the websocket connection. | ||
* Closes the websocket connection. | ||
*/ | ||
close() { | ||
closeConnection() { | ||
this._socket?.close(); | ||
} | ||
/** | ||
* Register a callback for one GlazeWM event. | ||
* Registers a callback for a GlazeWM event type. | ||
* | ||
@@ -173,3 +159,3 @@ * @example | ||
/** | ||
* Register a callback for multiple GlazeWM events. | ||
* Registers a callback for multiple GlazeWM event types. | ||
* | ||
@@ -185,5 +171,3 @@ * @example | ||
async subscribeMany(events, callback) { | ||
const response = await this.sendAndWaitReply( | ||
`subscribe -e ${events.join(",")}` | ||
); | ||
const response = await this._sendAndWaitReply(`sub --events ${events.join(" ")}`); | ||
const unlisten = this.onMessage((e) => { | ||
@@ -200,4 +184,4 @@ const serverMessage = JSON.parse( | ||
unlisten(); | ||
await this.sendAndWaitReply( | ||
`unsubscribe ${response.subscriptionId}` | ||
await this._sendAndWaitReply( | ||
`unsub --id ${response.subscriptionId}` | ||
); | ||
@@ -207,3 +191,3 @@ }; | ||
/** | ||
* Register a callback for when websocket messages are received. | ||
* Registers a callback for when websocket messages are received. | ||
* | ||
@@ -219,3 +203,3 @@ * @example | ||
/** | ||
* Register a callback for when the websocket connects. | ||
* Registers a callback for when the websocket connects. | ||
* | ||
@@ -231,3 +215,3 @@ * @example | ||
/** | ||
* Register a callback for when the websocket disconnects. | ||
* Registers a callback for when the websocket disconnects. | ||
* | ||
@@ -243,4 +227,4 @@ * @example | ||
/** | ||
* Register a callback for when the websocket connection has been closed due | ||
* to an error. | ||
* Registers a callback for when the websocket connection has been closed | ||
* due to an error. | ||
* | ||
@@ -255,2 +239,35 @@ * @example | ||
} | ||
/** | ||
* Sends an IPC message and waits for a reply. | ||
* | ||
* @private | ||
* @throws If message is invalid or IPC server is unable to handle the | ||
* message. | ||
*/ | ||
async _sendAndWaitReply(message) { | ||
let unlisten; | ||
return new Promise(async (resolve, reject) => { | ||
await this.connect(); | ||
this._socket.send(message); | ||
unlisten = this.onMessage((e) => { | ||
const serverMessage = JSON.parse( | ||
e.data | ||
); | ||
const isReplyMessage = serverMessage.messageType === "client_response" && serverMessage.clientMessage === message; | ||
if (isReplyMessage && serverMessage.error) { | ||
reject( | ||
`Server reply to message '${message}' has error: ${serverMessage.error}` | ||
); | ||
} | ||
if (isReplyMessage) { | ||
resolve(serverMessage.data); | ||
} | ||
}); | ||
}).finally(() => unlisten()); | ||
} | ||
/** | ||
* Utility function for registering a callback. | ||
* | ||
* @private | ||
*/ | ||
_registerCallback(callbacks, newCallback) { | ||
@@ -266,6 +283,11 @@ callbacks.push(newCallback); | ||
} | ||
/** | ||
* Instantiates `WebSocket` and adds event listeners for socket events. | ||
* | ||
* @private | ||
*/ | ||
async _createSocket() { | ||
const WebSocketApi = await (globalThis.WebSocket ?? import("ws").then((ws) => ws.default).catch(() => { | ||
throw new Error( | ||
"The dependency 'ws' is required for environments without a built-in WebSocket API. \nRun `npm i ws` to resolve this error." | ||
"The dependency 'ws' is required for environments without abuilt-in WebSocket API. \nRun `npm i ws` to resolve thiserror." | ||
); | ||
@@ -282,2 +304,7 @@ })); | ||
} | ||
/** | ||
* Waits for the websocket connection to be established. | ||
* | ||
* @private | ||
*/ | ||
async _waitForConnection() { | ||
@@ -294,69 +321,72 @@ if (this._socket && this._socket.readyState === this._socket.OPEN) { | ||
// src/types/shared/direction.ts | ||
var Direction = /* @__PURE__ */ ((Direction2) => { | ||
Direction2["Left"] = "left"; | ||
Direction2["Right"] = "right"; | ||
Direction2["Up"] = "up"; | ||
Direction2["Down"] = "down"; | ||
return Direction2; | ||
})(Direction || {}); | ||
// src/types/containers/container-type.ts | ||
var ContainerType = /* @__PURE__ */ ((ContainerType2) => { | ||
ContainerType2["ROOT"] = "root"; | ||
ContainerType2["MONITOR"] = "monitor"; | ||
ContainerType2["WORKSPACE"] = "workspace"; | ||
ContainerType2["SPLIT"] = "split"; | ||
ContainerType2["WINDOW"] = "window"; | ||
return ContainerType2; | ||
})(ContainerType || {}); | ||
// src/types/shared/resize-dimension.ts | ||
var ResizeDimension = /* @__PURE__ */ ((ResizeDimension2) => { | ||
ResizeDimension2["Height"] = "height"; | ||
ResizeDimension2["Width"] = "width"; | ||
return ResizeDimension2; | ||
})(ResizeDimension || {}); | ||
// src/types/server-message.ts | ||
var ServerMessageType = /* @__PURE__ */ ((ServerMessageType2) => { | ||
ServerMessageType2["CLIENT_RESPONSE"] = "client_response"; | ||
ServerMessageType2["EVENT_SUBSCRIPTION"] = "event_subscription"; | ||
return ServerMessageType2; | ||
})(ServerMessageType || {}); | ||
// src/types/shared/tiling-direction.ts | ||
// src/types/common/display-state.ts | ||
var DisplayState = /* @__PURE__ */ ((DisplayState2) => { | ||
DisplayState2["SHOWN"] = "shown"; | ||
DisplayState2["SHOWING"] = "showing"; | ||
DisplayState2["HIDDEN"] = "hidden"; | ||
DisplayState2["HIDING"] = "hiding"; | ||
return DisplayState2; | ||
})(DisplayState || {}); | ||
// src/types/common/tiling-direction.ts | ||
var TilingDirection = /* @__PURE__ */ ((TilingDirection2) => { | ||
TilingDirection2["Vertical"] = "vertical"; | ||
TilingDirection2["Horizontal"] = "horizontal"; | ||
TilingDirection2["VERTICAL"] = "vertical"; | ||
TilingDirection2["HORIZONTAL"] = "horizontal"; | ||
return TilingDirection2; | ||
})(TilingDirection || {}); | ||
// src/types/shared/window-state.ts | ||
var WindowState = /* @__PURE__ */ ((WindowState2) => { | ||
WindowState2["Tiling"] = "tiling"; | ||
WindowState2["Floating"] = "floating"; | ||
WindowState2["Minimized"] = "minimized"; | ||
WindowState2["Maximized"] = "maximized"; | ||
WindowState2["Fullscreen"] = "fullscreen"; | ||
return WindowState2; | ||
})(WindowState || {}); | ||
// src/types/common/window-type.ts | ||
var WindowType = /* @__PURE__ */ ((WindowType2) => { | ||
WindowType2["TILING"] = "tiling"; | ||
WindowType2["FLOATING"] = "floating"; | ||
WindowType2["MINIMIZED"] = "minimized"; | ||
WindowType2["FULLSCREEN"] = "fullscreen"; | ||
return WindowType2; | ||
})(WindowType || {}); | ||
// src/types/wm-events.ts | ||
var WmEventType = /* @__PURE__ */ ((WmEventType2) => { | ||
WmEventType2["All"] = "all"; | ||
WmEventType2["BindingModeChanged"] = "binding_mode_changed"; | ||
WmEventType2["FocusChanged"] = "focus_changed"; | ||
WmEventType2["FocusedContainerMoved"] = "focused_container_moved"; | ||
WmEventType2["MonitorAdded"] = "monitor_added"; | ||
WmEventType2["MonitorRemoved"] = "monitor_removed"; | ||
WmEventType2["TilingDirectionChanged"] = "tiling_direction_changed"; | ||
WmEventType2["UserConfigReloaded"] = "user_config_reloaded"; | ||
WmEventType2["WindowManaged"] = "window_managed"; | ||
WmEventType2["WindowUnmanaged"] = "window_unmanaged"; | ||
WmEventType2["WorkspaceActivated"] = "workspace_activated"; | ||
WmEventType2["WorkspaceDeactivated"] = "workspace_deactivated"; | ||
WmEventType2["WorkingAreaResized"] = "working_area_resized"; | ||
WmEventType2["ApplicationExiting"] = "application_exiting"; | ||
WmEventType2["ALL"] = "all"; | ||
WmEventType2["APPLICATION_EXITING"] = "application_exiting"; | ||
WmEventType2["BINDING_MODES_CHANGED"] = "binding_modes_changed"; | ||
WmEventType2["FOCUS_CHANGED"] = "focus_changed"; | ||
WmEventType2["FOCUSED_CONTAINER_MOVED"] = "focused_container_moved"; | ||
WmEventType2["MONITOR_ADDED"] = "monitor_added"; | ||
WmEventType2["MONITOR_UPDATED"] = "monitor_updated"; | ||
WmEventType2["MONITOR_REMOVED"] = "monitor_removed"; | ||
WmEventType2["TILING_DIRECTION_CHANGED"] = "tiling_direction_changed"; | ||
WmEventType2["USER_CONFIG_CHANGED"] = "user_config_changed"; | ||
WmEventType2["WINDOW_MANAGED"] = "window_managed"; | ||
WmEventType2["WINDOW_UNMANAGED"] = "window_unmanaged"; | ||
WmEventType2["WORKSPACE_ACTIVATED"] = "workspace_activated"; | ||
WmEventType2["WORKSPACE_DEACTIVATED"] = "workspace_deactivated"; | ||
WmEventType2["WORKSPACE_MOVED"] = "workspace_moved"; | ||
return WmEventType2; | ||
})(WmEventType || {}); | ||
// src/types/server-message.ts | ||
var ServerMessageType = /* @__PURE__ */ ((ServerMessageType2) => { | ||
ServerMessageType2["ClientResponse"] = "client_response"; | ||
ServerMessageType2["EventSubscription"] = "event_subscription"; | ||
return ServerMessageType2; | ||
})(ServerMessageType || {}); | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = { | ||
Direction, | ||
ResizeDimension, | ||
ContainerType, | ||
DisplayState, | ||
ServerMessageType, | ||
TilingDirection, | ||
WindowState, | ||
WindowType, | ||
WmClient, | ||
WmEventType | ||
}); |
{ | ||
"name": "glazewm", | ||
"version": "1.2.2", | ||
"version": "1.3.0", | ||
"description": "Library for inter-process communication (IPC) with GlazeWM.", | ||
@@ -30,3 +30,3 @@ "keywords": [ | ||
"lint": "prettier --check .", | ||
"prepublishOnly": "npm run build" | ||
"test": "vitest" | ||
}, | ||
@@ -44,2 +44,3 @@ "prettier": "@glzr/style-guide/prettier", | ||
"typescript": "5.1.6", | ||
"vitest": "2.0.4", | ||
"ws": "8.13.0" | ||
@@ -46,0 +47,0 @@ }, |
@@ -37,18 +37,19 @@ # GlazeWM-js · [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/glzr-io/GlazeWM-js/pulls) [![License](https://img.shields.io/github/license/glzr-io/glazewm-js)](https://github.com/glzr-io/glazewm-js/blob/main/LICENSE.md) [![Discord invite](https://img.shields.io/discord/1041662798196908052.svg?logo=discord&colorB=7289DA)](https://discord.gg/ud6z3qjRvM) | ||
// Get monitors, active workspaces, and windows. | ||
const monitors = await client.getMonitors(); | ||
const workspaces = await client.getWorkspaces(); | ||
const windows = await client.getWindows(); | ||
// Get monitors, workspaces, windows, and the currently focused container. | ||
const { monitors } = await client.queryMonitors(); | ||
const { workspaces } = await client.queryWorkspaces(); | ||
const { windows } = await client.queryWindows(); | ||
const { focused } = await client.queryFocused(); | ||
// Run a WM command. | ||
await client.runCommand('focus workspace 1'); | ||
await client.runCommand('focus --workspace 1'); | ||
// Run a WM command with a given context container. This way we can target the | ||
// container to operate on. If no context container is specified, it defaults to | ||
// the currently focused container. | ||
await client.runCommand('move left', windows[0]); | ||
// Run a WM command with a given subject container. This way we can target | ||
// the container to operate on. If no subject container is specified, it | ||
// defaults to the currently focused container. | ||
await client.runCommand('move --direction left', windows[0].id); | ||
// Listen to a WM event (e.g. whenever the focused container changes). | ||
await client.subscribe( | ||
WmEventType.FocusChanged, | ||
WmEventType.FOCUS_CHANGED, | ||
(event: FocusChangedEvent) => console.log(event), | ||
@@ -59,3 +60,3 @@ ); | ||
await client.subscribeMany( | ||
[WmEventType.WorkspaceActivated, WmEventType.WorkspaceDeactivated], | ||
[WmEventType.WORKSPACE_ACTIVATED, WmEventType.WORKSPACE_DEACTIVATED], | ||
(event: WorkspaceActivatedEvent | WorkspaceDeactivatedEvent) => | ||
@@ -62,0 +63,0 @@ console.log(event), |
@@ -10,3 +10,3 @@ import { | ||
import { | ||
type ClientMessage, | ||
type BindingModeConfig, | ||
WmEventType, | ||
@@ -17,6 +17,4 @@ type WmEventData, | ||
type Workspace, | ||
type WmCommand, | ||
type Container, | ||
type EventSubscription, | ||
type Window, | ||
type AppMetadata, | ||
} from './types'; | ||
@@ -29,3 +27,3 @@ | ||
/** Unregister a callback. */ | ||
/** Unregisters a callback. */ | ||
export type UnlistenFn = () => void; | ||
@@ -56,4 +54,6 @@ | ||
/** | ||
* Instantiate client. Connection to IPC server is established when sending | ||
* the first message or by explicitly calling {@link connect}. | ||
* Instantiates client. | ||
* | ||
* Connection to the IPC server is established when sending the first | ||
* message or by explicitly calling {@link connect}. | ||
*/ | ||
@@ -63,97 +63,71 @@ constructor(private _options?: WmClientOptions) {} | ||
/** | ||
* Send an IPC message and wait for a reply. | ||
* | ||
* @throws If message is invalid or IPC server is unable to handle the message. | ||
* Gets all monitors. {@link Monitor} | ||
*/ | ||
async sendAndWaitReply<T>(message: ClientMessage): Promise<T> { | ||
let unlisten: UnlistenFn; | ||
// Resolve when a reply comes in for the client message. | ||
return new Promise<T>(async (resolve, reject) => { | ||
await this.connect(); | ||
this._socket!.send(message); | ||
unlisten = this.onMessage(e => { | ||
const serverMessage: ServerMessage<T> = JSON.parse( | ||
e.data as string, | ||
); | ||
// Whether the incoming message is a reply to the client message. | ||
const isReplyMessage = | ||
serverMessage.messageType === 'client_response' && | ||
serverMessage.clientMessage === message; | ||
if (isReplyMessage && serverMessage.error) { | ||
reject( | ||
`Server reply to message '${message}' has error: ${serverMessage.error}`, | ||
); | ||
} | ||
if (isReplyMessage) { | ||
resolve(serverMessage.data as T); | ||
} | ||
}); | ||
}).finally(() => unlisten()); | ||
async queryMonitors(): Promise<{ monitors: Monitor[] }> { | ||
return this._sendAndWaitReply<{ monitors: Monitor[] }>( | ||
'query monitors', | ||
); | ||
} | ||
/** | ||
* Get all monitors. {@link Monitor} | ||
* Gets all active workspaces. {@link Workspace} | ||
*/ | ||
async getMonitors(): Promise<Monitor[]> { | ||
return this.sendAndWaitReply<Monitor[]>('monitors'); | ||
async queryWorkspaces(): Promise<{ workspaces: Workspace[] }> { | ||
return this._sendAndWaitReply<{ workspaces: Workspace[] }>( | ||
'query workspaces', | ||
); | ||
} | ||
/** | ||
* Get all active workspaces. {@link Workspace} | ||
* Gets all managed windows. {@link Window} | ||
*/ | ||
async getWorkspaces(): Promise<Workspace[]> { | ||
return this.sendAndWaitReply<Workspace[]>('workspaces'); | ||
async queryWindows(): Promise<{ windows: Window[] }> { | ||
return this._sendAndWaitReply<{ windows: Window[] }>('query windows'); | ||
} | ||
/** | ||
* Get all windows. {@link Window} | ||
* Gets the currently focused container. This can either be a | ||
* {@link Window} or a {@link Workspace} without any descendant windows. | ||
*/ | ||
async getWindows(): Promise<Window[]> { | ||
return this.sendAndWaitReply<Window[]>('windows'); | ||
async queryFocused(): Promise<{ focused: Window | Workspace }> { | ||
return this._sendAndWaitReply<{ focused: Window | Workspace }>( | ||
'query focused', | ||
); | ||
} | ||
/** | ||
* Get the currently focused container. This can either be a | ||
* {@link Window} or a {@link Workspace} without any descendant windows. | ||
* Gets the active binding modes. {@link BindingModeConfig} | ||
*/ | ||
async getFocusedContainer(): Promise<Container> { | ||
return this.sendAndWaitReply<Container>('focused_container'); | ||
async queryBindingModes(): Promise<{ | ||
bindingModes: BindingModeConfig[]; | ||
}> { | ||
return this._sendAndWaitReply<{ bindingModes: BindingModeConfig[] }>( | ||
'query binding-modes', | ||
); | ||
} | ||
/** | ||
* Get the name of the active binding mode (if one is active). | ||
* Gets metadata about the running GlazeWM application. | ||
* {@link AppMetadata} | ||
*/ | ||
async getBindingMode(): Promise<string | null> { | ||
return this.sendAndWaitReply<string | null>('binding_mode'); | ||
async queryAppMetadata(): Promise<AppMetadata> { | ||
return this._sendAndWaitReply<AppMetadata>('query app-metadata'); | ||
} | ||
/** | ||
* Invoke a WM command (eg. "focus workspace 1"). | ||
* Invokes a WM command (e.g. `"focus --workspace 1"`). | ||
* | ||
* @param command WM command to run (eg. "focus workspace 1"). | ||
* @param contextContainer (optional) Container or ID of container to use as | ||
* context. If not provided, this defaults to the currently focused container. | ||
* @param command WM command to run (e.g. `"focus --workspace 1"`). | ||
* @param subjectContainerId (optional) ID of container to use as subject. | ||
* If not provided, this defaults to the currently focused container. | ||
* @throws If command fails. | ||
*/ | ||
async runCommand( | ||
command: WmCommand, | ||
contextContainer?: Container | string, | ||
command: string, | ||
subjectContainerId?: string, | ||
): Promise<void> { | ||
if (!contextContainer) { | ||
await this.sendAndWaitReply<null>(`command "${command}"`); | ||
return; | ||
} | ||
const contextContainerId = | ||
typeof contextContainer === 'string' | ||
? contextContainer | ||
: contextContainer!.id; | ||
await this.sendAndWaitReply<null>( | ||
`command "${command}" -c ${contextContainerId}`, | ||
await this._sendAndWaitReply<{ subjectContainerId: string }>( | ||
subjectContainerId | ||
? `command --id ${subjectContainerId} ${command}` | ||
: `command ${command}`, | ||
); | ||
@@ -163,3 +137,3 @@ } | ||
/** | ||
* Establish websocket connection. | ||
* Establishes websocket connection. | ||
* | ||
@@ -181,5 +155,5 @@ * @throws If connection attempt fails. | ||
/** | ||
* Close the websocket connection. | ||
* Closes the websocket connection. | ||
*/ | ||
close(): void { | ||
closeConnection(): void { | ||
this._socket?.close(); | ||
@@ -189,3 +163,3 @@ } | ||
/** | ||
* Register a callback for one GlazeWM event. | ||
* Registers a callback for a GlazeWM event type. | ||
* | ||
@@ -208,3 +182,3 @@ * @example | ||
/** | ||
* Register a callback for multiple GlazeWM events. | ||
* Registers a callback for multiple GlazeWM event types. | ||
* | ||
@@ -223,5 +197,5 @@ * @example | ||
): Promise<UnlistenFn> { | ||
const response = await this.sendAndWaitReply<EventSubscription>( | ||
`subscribe -e ${events.join(',')}`, | ||
); | ||
const response = await this._sendAndWaitReply<{ | ||
subscriptionId: string; | ||
}>(`sub --events ${events.join(' ')}`); | ||
@@ -245,4 +219,4 @@ const unlisten = this.onMessage(e => { | ||
await this.sendAndWaitReply<EventSubscription>( | ||
`unsubscribe ${response.subscriptionId}`, | ||
await this._sendAndWaitReply<{ subscriptionId: string }>( | ||
`unsub --id ${response.subscriptionId}`, | ||
); | ||
@@ -253,3 +227,3 @@ }; | ||
/** | ||
* Register a callback for when websocket messages are received. | ||
* Registers a callback for when websocket messages are received. | ||
* | ||
@@ -266,3 +240,3 @@ * @example | ||
/** | ||
* Register a callback for when the websocket connects. | ||
* Registers a callback for when the websocket connects. | ||
* | ||
@@ -279,3 +253,3 @@ * @example | ||
/** | ||
* Register a callback for when the websocket disconnects. | ||
* Registers a callback for when the websocket disconnects. | ||
* | ||
@@ -292,4 +266,4 @@ * @example | ||
/** | ||
* Register a callback for when the websocket connection has been closed due | ||
* to an error. | ||
* Registers a callback for when the websocket connection has been closed | ||
* due to an error. | ||
* | ||
@@ -305,2 +279,45 @@ * @example | ||
/** | ||
* Sends an IPC message and waits for a reply. | ||
* | ||
* @private | ||
* @throws If message is invalid or IPC server is unable to handle the | ||
* message. | ||
*/ | ||
private async _sendAndWaitReply<T>(message: string): Promise<T> { | ||
let unlisten: UnlistenFn; | ||
// Resolve when a reply comes in for the client message. | ||
return new Promise<T>(async (resolve, reject) => { | ||
await this.connect(); | ||
this._socket!.send(message); | ||
unlisten = this.onMessage(e => { | ||
const serverMessage: ServerMessage<T> = JSON.parse( | ||
e.data as string, | ||
); | ||
// Whether the incoming message is a reply to the client message. | ||
const isReplyMessage = | ||
serverMessage.messageType === 'client_response' && | ||
serverMessage.clientMessage === message; | ||
if (isReplyMessage && serverMessage.error) { | ||
reject( | ||
`Server reply to message '${message}' has error: ${serverMessage.error}`, | ||
); | ||
} | ||
if (isReplyMessage) { | ||
resolve(serverMessage.data as T); | ||
} | ||
}); | ||
}).finally(() => unlisten()); | ||
} | ||
/** | ||
* Utility function for registering a callback. | ||
* | ||
* @private | ||
*/ | ||
private _registerCallback<T>( | ||
@@ -322,5 +339,10 @@ callbacks: T[], | ||
/** | ||
* Instantiates `WebSocket` and adds event listeners for socket events. | ||
* | ||
* @private | ||
*/ | ||
private async _createSocket(): Promise<WebSocket> { | ||
// Get instance of `Websocket` to use. Uses the `Websocket` web API when | ||
// running in the browser, otherwise uses `ws` when running Node. | ||
// Get instance of `WebSocket` to use. Uses the `WebSocket` web API | ||
// when running in the browser, otherwise uses `ws` when running Node. | ||
const WebSocketApi = await (globalThis.WebSocket ?? | ||
@@ -331,4 +353,5 @@ import('ws') | ||
throw new Error( | ||
"The dependency 'ws' is required for environments without a built-in" + | ||
' WebSocket API. \nRun `npm i ws` to resolve this error.', | ||
"The dependency 'ws' is required for environments without a" + | ||
'built-in WebSocket API. \nRun `npm i ws` to resolve this' + | ||
'error.', | ||
); | ||
@@ -356,3 +379,8 @@ })); | ||
async _waitForConnection(): Promise<WebSocket> { | ||
/** | ||
* Waits for the websocket connection to be established. | ||
* | ||
* @private | ||
*/ | ||
private async _waitForConnection(): Promise<WebSocket> { | ||
if (this._socket && this._socket.readyState === this._socket.OPEN) { | ||
@@ -359,0 +387,0 @@ return this._socket; |
import { WmEventType } from '../wm-events'; | ||
export interface ApplicationExitingEvent { | ||
type: WmEventType.ApplicationExiting; | ||
type: WmEventType.APPLICATION_EXITING; | ||
} |
import { WmEventType } from '../wm-events'; | ||
import type { Container } from '../shared'; | ||
import type { Container } from '../containers'; | ||
export interface FocusChangedEvent { | ||
type: WmEventType.FocusChanged; | ||
type: WmEventType.FOCUS_CHANGED; | ||
focusedContainer: Container; | ||
} |
import { WmEventType } from '../wm-events'; | ||
import type { Container } from '../shared'; | ||
import type { Container } from '../containers'; | ||
export interface FocusedContainerMovedEvent { | ||
type: WmEventType.FocusedContainerMoved; | ||
type: WmEventType.FOCUSED_CONTAINER_MOVED; | ||
focusedContainer: Container; | ||
} |
export * from './application-exiting-event'; | ||
export * from './binding-mode-changed-event'; | ||
export * from './binding-modes-changed-event'; | ||
export * from './focus-changed-event'; | ||
export * from './focused-container-moved-event'; | ||
export * from './tiling-direction-changed-event'; | ||
export * from './monitor-added-event'; | ||
export * from './monitor-removed-event'; | ||
export * from './user-config-reloaded-event'; | ||
export * from './monitor-updated-event'; | ||
export * from './tiling-direction-changed-event'; | ||
export * from './user-config-changed-event'; | ||
export * from './window-managed-event'; | ||
export * from './window-unmanaged-event'; | ||
export * from './working-area-resized-event'; | ||
export * from './workspace-activated-event'; | ||
export * from './workspace-deactivated-event'; | ||
export * from './workspace-moved-event'; |
import { WmEventType } from '../wm-events'; | ||
import type { Monitor } from '../shared'; | ||
import type { Monitor } from '../containers'; | ||
export interface MonitorAddedEvent { | ||
type: WmEventType.MonitorAdded; | ||
type: WmEventType.MONITOR_ADDED; | ||
addedMonitor: Monitor; | ||
} |
import { WmEventType } from '../wm-events'; | ||
export interface MonitorRemovedEvent { | ||
type: WmEventType.MonitorRemoved; | ||
type: WmEventType.MONITOR_REMOVED; | ||
removedId: string; | ||
removedDeviceName: string; | ||
} |
import { WmEventType } from '../wm-events'; | ||
import { TilingDirection } from '../shared'; | ||
import { TilingDirection } from '../common'; | ||
export interface TilingDirectionChangedEvent { | ||
type: WmEventType.TilingDirectionChanged; | ||
type: WmEventType.TILING_DIRECTION_CHANGED; | ||
newTilingDirection: TilingDirection; | ||
} |
@@ -1,7 +0,7 @@ | ||
import type { Window } from '../shared'; | ||
import type { Window } from '../containers'; | ||
import { WmEventType } from '../wm-events'; | ||
export interface WindowManagedEvent { | ||
type: WmEventType.WindowManaged; | ||
type: WmEventType.WINDOW_MANAGED; | ||
managedWindow: Window; | ||
} |
import { WmEventType } from '../wm-events'; | ||
export interface WindowUnmanagedEvent { | ||
type: WmEventType.WindowUnmanaged; | ||
type: WmEventType.WINDOW_UNMANAGED; | ||
unmanagedId: string; | ||
unmanagedHandle: number; | ||
} |
import { WmEventType } from '../wm-events'; | ||
import type { Workspace } from '../shared'; | ||
import type { Workspace } from '../containers'; | ||
export interface WorkspaceActivatedEvent { | ||
type: WmEventType.WorkspaceActivated; | ||
type: WmEventType.WORKSPACE_ACTIVATED; | ||
activatedWorkspace: Workspace; | ||
} |
import { WmEventType } from '../wm-events'; | ||
export interface WorkspaceDeactivatedEvent { | ||
type: WmEventType.WorkspaceDeactivated; | ||
removedId: string; | ||
removedName: string; | ||
type: WmEventType.WORKSPACE_DEACTIVATED; | ||
deactivatedId: string; | ||
deactivatedName: string; | ||
} |
@@ -1,5 +0,6 @@ | ||
export * from './shared'; | ||
export * from './client-messages'; | ||
export * from './config'; | ||
export * from './containers'; | ||
export * from './events'; | ||
export * from './server-message'; | ||
export * from './common'; | ||
export * from './wm-events'; | ||
export * from './wm-commands'; | ||
export * from './server-message'; |
export enum ServerMessageType { | ||
ClientResponse = 'client_response', | ||
EventSubscription = 'event_subscription', | ||
CLIENT_RESPONSE = 'client_response', | ||
EVENT_SUBSCRIPTION = 'event_subscription', | ||
} | ||
interface BaseServerMessage<T> { | ||
/** Whether the server message has an associated error. */ | ||
/** | ||
* Whether the server message has an associated error. | ||
*/ | ||
success: boolean; | ||
/** The type of server message. */ | ||
/** | ||
* The type of server message. | ||
*/ | ||
messageType: ServerMessageType; | ||
@@ -17,3 +21,3 @@ | ||
*/ | ||
data?: T; | ||
data: T | null; | ||
@@ -24,7 +28,7 @@ /** | ||
*/ | ||
error?: string; | ||
error: string | null; | ||
} | ||
export interface ClientResponseMessage<T> extends BaseServerMessage<T> { | ||
messageType: ServerMessageType.ClientResponse; | ||
messageType: ServerMessageType.CLIENT_RESPONSE; | ||
@@ -38,3 +42,3 @@ /** | ||
export interface EventSubscriptionMessage<T> extends BaseServerMessage<T> { | ||
messageType: ServerMessageType.EventSubscription; | ||
messageType: ServerMessageType.EVENT_SUBSCRIPTION; | ||
@@ -41,0 +45,0 @@ /** |
import type { | ||
ApplicationExitingEvent, | ||
BindingModeChangedEvent, | ||
BindingModesChangedEvent, | ||
FocusChangedEvent, | ||
@@ -8,4 +8,5 @@ FocusedContainerMovedEvent, | ||
MonitorRemovedEvent, | ||
MonitorUpdatedEvent, | ||
TilingDirectionChangedEvent, | ||
UserConfigReloadedEvent, | ||
UserConfigChangedEvent, | ||
WindowManagedEvent, | ||
@@ -15,23 +16,24 @@ WindowUnmanagedEvent, | ||
WorkspaceDeactivatedEvent, | ||
WorkingAreaResizedEvent, | ||
WorkspaceMovedEvent, | ||
} from './events'; | ||
/** | ||
* All possible GlazeWM event types (eg. `'focus_changed'`). | ||
* All possible GlazeWM event types (e.g. `'focus_changed'`). | ||
*/ | ||
export enum WmEventType { | ||
All = 'all', | ||
BindingModeChanged = 'binding_mode_changed', | ||
FocusChanged = 'focus_changed', | ||
FocusedContainerMoved = 'focused_container_moved', | ||
MonitorAdded = 'monitor_added', | ||
MonitorRemoved = 'monitor_removed', | ||
TilingDirectionChanged = 'tiling_direction_changed', | ||
UserConfigReloaded = 'user_config_reloaded', | ||
WindowManaged = 'window_managed', | ||
WindowUnmanaged = 'window_unmanaged', | ||
WorkspaceActivated = 'workspace_activated', | ||
WorkspaceDeactivated = 'workspace_deactivated', | ||
WorkingAreaResized = 'working_area_resized', | ||
ApplicationExiting = 'application_exiting', | ||
ALL = 'all', | ||
APPLICATION_EXITING = 'application_exiting', | ||
BINDING_MODES_CHANGED = 'binding_modes_changed', | ||
FOCUS_CHANGED = 'focus_changed', | ||
FOCUSED_CONTAINER_MOVED = 'focused_container_moved', | ||
MONITOR_ADDED = 'monitor_added', | ||
MONITOR_UPDATED = 'monitor_updated', | ||
MONITOR_REMOVED = 'monitor_removed', | ||
TILING_DIRECTION_CHANGED = 'tiling_direction_changed', | ||
USER_CONFIG_CHANGED = 'user_config_changed', | ||
WINDOW_MANAGED = 'window_managed', | ||
WINDOW_UNMANAGED = 'window_unmanaged', | ||
WORKSPACE_ACTIVATED = 'workspace_activated', | ||
WORKSPACE_DEACTIVATED = 'workspace_deactivated', | ||
WORKSPACE_MOVED = 'workspace_moved', | ||
} | ||
@@ -44,9 +46,10 @@ | ||
| ApplicationExitingEvent | ||
| BindingModeChangedEvent | ||
| BindingModesChangedEvent | ||
| FocusChangedEvent | ||
| FocusedContainerMovedEvent | ||
| MonitorAddedEvent | ||
| MonitorUpdatedEvent | ||
| MonitorRemovedEvent | ||
| TilingDirectionChangedEvent | ||
| UserConfigReloadedEvent | ||
| UserConfigChangedEvent | ||
| WindowManagedEvent | ||
@@ -56,3 +59,3 @@ | WindowUnmanagedEvent | ||
| WorkspaceDeactivatedEvent | ||
| WorkingAreaResizedEvent; | ||
| WorkspaceMovedEvent; | ||
@@ -67,17 +70,18 @@ /** | ||
*/ | ||
export type WmEventData<T extends WmEventType = WmEventType.All> = { | ||
[WmEventType.All]: WmEvent; | ||
[WmEventType.ApplicationExiting]: ApplicationExitingEvent; | ||
[WmEventType.BindingModeChanged]: BindingModeChangedEvent; | ||
[WmEventType.FocusChanged]: FocusChangedEvent; | ||
[WmEventType.FocusedContainerMoved]: FocusedContainerMovedEvent; | ||
[WmEventType.MonitorAdded]: MonitorAddedEvent; | ||
[WmEventType.MonitorRemoved]: MonitorRemovedEvent; | ||
[WmEventType.TilingDirectionChanged]: TilingDirectionChangedEvent; | ||
[WmEventType.WindowManaged]: WindowManagedEvent; | ||
[WmEventType.WindowUnmanaged]: WindowUnmanagedEvent; | ||
[WmEventType.UserConfigReloaded]: UserConfigReloadedEvent; | ||
[WmEventType.WorkspaceActivated]: WorkspaceActivatedEvent; | ||
[WmEventType.WorkspaceDeactivated]: WorkspaceDeactivatedEvent; | ||
[WmEventType.WorkingAreaResized]: WorkingAreaResizedEvent; | ||
export type WmEventData<T extends WmEventType = WmEventType.ALL> = { | ||
[WmEventType.ALL]: WmEvent; | ||
[WmEventType.APPLICATION_EXITING]: ApplicationExitingEvent; | ||
[WmEventType.BINDING_MODES_CHANGED]: BindingModesChangedEvent; | ||
[WmEventType.FOCUS_CHANGED]: FocusChangedEvent; | ||
[WmEventType.FOCUSED_CONTAINER_MOVED]: FocusedContainerMovedEvent; | ||
[WmEventType.MONITOR_ADDED]: MonitorAddedEvent; | ||
[WmEventType.MONITOR_REMOVED]: MonitorRemovedEvent; | ||
[WmEventType.MONITOR_UPDATED]: MonitorUpdatedEvent; | ||
[WmEventType.TILING_DIRECTION_CHANGED]: TilingDirectionChangedEvent; | ||
[WmEventType.WINDOW_MANAGED]: WindowManagedEvent; | ||
[WmEventType.WINDOW_UNMANAGED]: WindowUnmanagedEvent; | ||
[WmEventType.USER_CONFIG_CHANGED]: UserConfigChangedEvent; | ||
[WmEventType.WORKSPACE_ACTIVATED]: WorkspaceActivatedEvent; | ||
[WmEventType.WORKSPACE_DEACTIVATED]: WorkspaceDeactivatedEvent; | ||
[WmEventType.WORKSPACE_MOVED]: WorkspaceMovedEvent; | ||
}[T]; |
{ | ||
"extends": "@glzr/style-guide/tsconfig/vanilla-bundler", | ||
"compilerOptions": { | ||
"outDir": "dist" | ||
"outDir": "dist", | ||
"types": ["vitest/globals"] | ||
}, | ||
"include": ["src"] | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
65591
53
1971
64
8