Answer omarchy-shell calls over the shell's own socket (#13435)

* Answer omarchy-shell calls over the shell's own socket

Every omarchy-shell call started a qs ipc client, ~45ms of startup for one
IPC call: a theme switch makes two, and every script-driven OSD, toggle
refresh and lock query paid it too.

The shell now serves a socket in XDG_RUNTIME_DIR, named from its config
path and Wayland display as qs ipc selects its instance, and omarchy-shell
tries it first through socat, which starts in ~5ms. First-party handlers
register as ShellIpc, an IpcHandler that qs ipc still reaches, and the
socket calls only the functions a handler declares with their exact
argument count, allowed by name so QObject methods such as destroy() stay
out of reach.

When the shell ran nothing it answers SKIP, and omarchy-shell asks qs ipc
for its exact answer, so errors, third-party plugins and an unreachable
socket behave as before. A call that may have run is never retried: a
timeout or a connection closed without an answer reports the shell as not
responding.

omarchy-shell shell ping takes ~13-18ms instead of ~61ms, and omarchy-osd
reaches the screen in ~36ms instead of ~77ms. Output and exit status match
the qs ipc path across 26 calls, errors and quiet mode included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Only accept whole socket replies and retry only unmade connections

A reply cut off after its OK prefix passed for the whole answer, and an
empty reply with socat failing was retried through qs ipc although the
request might already have been delivered.

An answer now counts only once its record separator arrived. socat's own
errors join the reply, so only its connect error, a socket nothing
listens on, falls back to qs ipc beside an explicit SKIP; anything else
is reported as not responding rather than retried.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
David Heinemeier HanssonandClaude Opus 5.5 authored and GitHub committed 2026-09-27 20:46:00 +02:00
1 parent 616feda103
commit c231097df7
27 files changed
+356 -23

No files matched your search

+1 -1
View File
@@ -324,7 +324,7 @@ Panel {
onTriggered: root.nowMs = Date.now()
}
IpcHandler {
ShellIpc {
target: root.ipcTarget
function open(): void { root.open() }
function close(): void { root.close() }
+1 -1
View File
@@ -208,7 +208,7 @@ Item {
}
}
IpcHandler {
ShellIpc {
target: "background"
function refresh(): void {
+1 -1
View File
@@ -1182,7 +1182,7 @@ Item {
// changes land in quick succession, stranding the bar off screen until the
// shell restarts. `omarchy-toggle-bar` nudges this after flipping the flag
// so the probe re-reads it even when the watch has gone quiet.
IpcHandler {
ShellIpc {
target: "omarchy.bar"
// Start rather than restart: a probe already in flight was launched by the
+1 -1
View File
@@ -165,7 +165,7 @@ BarWidget {
? activeVerticalBlock.implicitHeight + inactiveVerticalArea.implicitHeight
: Math.max(activeHorizontalBlock.implicitHeight, inactiveHorizontalArea.implicitHeight)
IpcHandler {
ShellIpc {
target: "omarchy.indicators"
function refresh(): void {
+1 -1
View File
@@ -24,7 +24,7 @@ BarWidget {
implicitWidth: button.implicitWidth
implicitHeight: button.implicitHeight
IpcHandler {
ShellIpc {
target: "omarchy.system-update"
function refresh(): void {
+1 -1
View File
@@ -641,7 +641,7 @@ Item {
checkStrandedLock()
}
IpcHandler {
ShellIpc {
target: "lock"
function lock(): string {
+1 -1
View File
@@ -862,7 +862,7 @@ Item {
return true
}
IpcHandler {
ShellIpc {
id: ipcHandler
target: "notifications"
+1 -1
View File
@@ -112,7 +112,7 @@ Item {
text: OsdModel.widestIcon
}
IpcHandler {
ShellIpc {
target: "osd"
function show(payloadJson: string): string {
root.open(payloadJson)
+1 -1
View File
@@ -637,7 +637,7 @@ Panel {
Quickshell.execDetached(["omarchy-bluetooth-power", adapter.enabled ? "off" : "on"])
}
IpcHandler {
ShellIpc {
target: "omarchy.bluetooth"
function open() { root.open() }
+1 -1
View File
@@ -130,7 +130,7 @@ BarWidget {
}
}
IpcHandler {
ShellIpc {
target: "omarchy.clock"
function refresh(): void { root.broadcast("refresh") }
+1 -1
View File
@@ -155,7 +155,7 @@ Panel {
function onFilesChanged() { root.ensureCursor() }
}
IpcHandler {
ShellIpc {
target: root.ipcTarget
function open(): void { root.open() }
function close(): void { root.close() }
+1 -1
View File
@@ -217,7 +217,7 @@ Panel {
})
}
IpcHandler {
ShellIpc {
target: "omarchy.monitor"
function brightness(percent: string): string { return root.brightnessIpc(percent) }
+1 -1
View File
@@ -207,7 +207,7 @@ Panel {
Qt.callLater(function() { root.refresh(true) })
}
IpcHandler {
ShellIpc {
target: "omarchy.network"
function open() { root.open() }
+1 -1
View File
@@ -174,7 +174,7 @@ Panel {
if (root.bar && root.bar.shell) root.bar.shell.updateEntryInline(root.moduleName, root.settings)
}
IpcHandler {
ShellIpc {
target: "omarchy.power"
function open() { root.open() }
+1 -1
View File
@@ -363,7 +363,7 @@ Panel {
function onAccountsAccessDeniedChanged() { root.ensureCursor() }
}
IpcHandler {
ShellIpc {
target: root.ipcTarget
function open(): void { root.open() }
function close(): void { root.close() }
+1 -1
View File
@@ -475,7 +475,7 @@ Panel {
onTriggered: root.refresh()
}
IpcHandler {
ShellIpc {
target: root.ipcTarget
function open(): void { root.openFromHotkey() }
+2 -1
View File
@@ -4,6 +4,7 @@ import Quickshell.Hyprland
import Quickshell.Io
import Quickshell.Wayland
import "IdleModel.js" as IdleModel
import qs.Commons
Item {
id: root
@@ -335,7 +336,7 @@ Item {
refreshStayAwakeState()
}
IpcHandler {
ShellIpc {
target: "idle"
function status(): string {
+2 -1
View File
@@ -4,6 +4,7 @@ import Quickshell.Io
import Quickshell.Services.Mpris
import Quickshell.Services.Pipewire
import "MediaModel.js" as MediaModel
import qs.Commons
Item {
id: root
@@ -530,7 +531,7 @@ Item {
return true
}
IpcHandler {
ShellIpc {
id: ipcHandler
target: "media"
@@ -1,6 +1,7 @@
import QtQuick
import Quickshell.Io
import "NightlightModel.js" as NightlightModel
import qs.Commons
Item {
id: root
@@ -85,7 +86,7 @@ Item {
Component.onCompleted: refresh()
IpcHandler {
ShellIpc {
target: "nightlight"
function status(): string {