IPC
The compositor listens on a Unix socket, $XDG_RUNTIME_DIR/atrium.<DISPLAY>.<PID>.sock, exported as $ATRIUM_SOCKET. The protocol is JSON, one message per line: send a Request, get a Reply; request EventStream to receive Event messages for as long as the connection lives. It is niri’s protocol with Atrium’s workspace and layout state added.
atrium msg
atrium msg workspaces # every workspace with its layout state
atrium msg windows
atrium msg outputs
atrium msg focused-window
atrium msg pick-window # click a window, print its details
atrium msg event-stream # follow events
atrium msg action focus-workspace 3
atrium msg action close-window
atrium msg action --help # every bindable action
atrium msg -j workspaces # JSON
Subcommands: outputs, workspaces, windows, layers, keyboard-layouts, focused-output, focused-window, pick-window, pick-color, action, output, event-stream, version, overview-state, casts.
Workspace fields
{
"id": 3, "idx": 3, "name": "3", "output": "HDMI-A-1",
"label": "Terminal", "icon": "/usr/share/atrium/icons/heroicons/o-command-line.svg",
"show_label": true, "show_icon": true,
"is_active": true, "is_focused": true, "is_urgent": false,
"active_window_id": 42,
"layout": "master-stack", "master_ratio": 0.55, "master_count": 1, "flipped": false
}
Windows carry is_iconified alongside the usual id, title, app id, workspace, and focus fields.
Events
A consumer connects, receives a full snapshot (workspaces, windows, layout), then a stream of changes: WorkspacesChanged, WorkspaceActivated, WindowOpenedOrChanged, WindowClosed, WindowFocusChanged, WindowUrgencyChanged, KeyboardLayoutsChanged, CastStartedOrChanged, and the rest. Consumers filter client-side. A layout change (algorithm, ratio, flip) emits WorkspacesChanged.
The consumer pattern
atrium-shell’s Services/AtriumIpc.qml is the reference consumer: it runs atrium msg -j event-stream as a child process, restarts it if it exits, and on any event debounces into one authoritative re-pull of atrium msg -j workspaces rather than modelling each event. Bars written for other compositors adapt the same way: a process reading the stream, a full re-query on change.
Screencast requests
Request::StartCast creates a PipeWire stream for an output or window and answers with session and stream ids; CastStartedOrChanged then carries the pw_node_id. This is how atrium-portal hands a node to xdg-desktop-portal without any Mutter-compatible D-Bus surface.