Switch to running quickshell as a systemd service that is auto-restarted if it crashes
This commit is contained in:
@@ -63,6 +63,7 @@ GROUP_DESCRIPTIONS[reminder]="Desktop notification reminders"
|
||||
GROUP_DESCRIPTIONS[remove]="Removal workflows"
|
||||
GROUP_DESCRIPTIONS[restart]="Restart Omarchy components"
|
||||
GROUP_DESCRIPTIONS[setup]="Interactive setup wizards"
|
||||
GROUP_DESCRIPTIONS[shell]="Omarchy shell IPC helpers"
|
||||
GROUP_DESCRIPTIONS[screensaver]="Screensaver branding and animation"
|
||||
GROUP_DESCRIPTIONS[snapshot]="System snapshots"
|
||||
GROUP_DESCRIPTIONS[style]="Global UI style controls"
|
||||
|
||||
@@ -255,7 +255,7 @@ stop_screenrecording() {
|
||||
}
|
||||
|
||||
toggle_screenrecording_indicator() {
|
||||
omarchy-shell-ipc --if-running bar refreshScreenRecording >/dev/null 2>&1 || true
|
||||
omarchy-shell bar refreshScreenRecording >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
screenrecording_active() {
|
||||
|
||||
@@ -12,6 +12,7 @@ if [[ -f $FIRST_RUN_MODE ]]; then
|
||||
|
||||
bash "$OMARCHY_PATH/install/first-run/battery-monitor.sh"
|
||||
bash "$OMARCHY_PATH/install/first-run/recover-internal-monitor.sh"
|
||||
bash "$OMARCHY_PATH/install/first-run/omarchy-shell.sh"
|
||||
bash "$OMARCHY_PATH/install/first-run/cleanup-reboot-sudoers.sh"
|
||||
bash "$OMARCHY_PATH/install/first-run/firewall.sh"
|
||||
bash "$OMARCHY_PATH/install/first-run/dns-resolver.sh"
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
# omarchy:summary=Launch the Omarchy bar settings panel
|
||||
|
||||
exec omarchy-shell-ipc shell summon omarchy.settings "{}"
|
||||
exec omarchy-shell shell summon omarchy.settings "{}"
|
||||
|
||||
+4
-4
@@ -4,8 +4,8 @@
|
||||
# omarchy:args=[toggle|summon|close|refresh|ping] [route]
|
||||
# omarchy:examples=omarchy menu | omarchy menu toggle system | omarchy menu summon style.theme | omarchy menu refresh
|
||||
|
||||
# Thin wrapper around `omarchy-shell-ipc menu`. Keybinds and the bar icon
|
||||
# call omarchy-shell-ipc directly to skip the omarchy-CLI dispatch hop;
|
||||
# Thin wrapper around `omarchy-shell menu`. Keybinds and the bar icon
|
||||
# call omarchy-shell directly to skip the omarchy-CLI dispatch hop;
|
||||
# this exists so humans get `omarchy menu toggle X` etc.
|
||||
|
||||
verb="${1-toggle}"
|
||||
@@ -13,10 +13,10 @@ route="${2-root}"
|
||||
|
||||
case "$verb" in
|
||||
toggle | summon)
|
||||
exec omarchy-shell-ipc menu "$verb" "$route"
|
||||
exec omarchy-shell menu "$verb" "$route"
|
||||
;;
|
||||
close | refresh | ping)
|
||||
exec omarchy-shell-ipc menu "$verb"
|
||||
exec omarchy-shell menu "$verb"
|
||||
;;
|
||||
-h | --help | help)
|
||||
cat <<USAGE
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# omarchy:group=menu
|
||||
# omarchy:examples=omarchy menu clipboard
|
||||
|
||||
omarchy-shell-ipc shell toggle omarchy.clipboard-picker "{}"
|
||||
omarchy-shell shell toggle omarchy.clipboard-picker "{}"
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# omarchy:group=menu
|
||||
# omarchy:examples=omarchy menu emoji
|
||||
|
||||
omarchy-shell-ipc shell toggle omarchy.emoji-picker "{}"
|
||||
omarchy-shell shell toggle omarchy.emoji-picker "{}"
|
||||
|
||||
+5
-55
@@ -223,67 +223,17 @@ if [[ $cache_only == true || $prepare_only == true ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Image rows can contain newlines and tabs, which don't survive a positional
|
||||
# bash argv into `quickshell ipc call`. Base64-encode for transit; the
|
||||
# ImagePicker plugin Qt.atob()s on the other side.
|
||||
# Image rows can contain newlines and tabs, which don't survive positional
|
||||
# shell IPC arguments. Base64-encode for transit; the ImagePicker plugin
|
||||
# Qt.atob()s on the other side.
|
||||
rows_b64=$(printf '%s' "$rows" | base64 -w 0)
|
||||
|
||||
if [[ $preload == true ]]; then
|
||||
omarchy-shell-ipc image-selector preload "$rows_b64" "$selected_list_image" "$show_labels" "$filterable" >/dev/null || true
|
||||
omarchy-shell image-selector preload "$rows_b64" "$selected_list_image" "$show_labels" "$filterable" >/dev/null || true
|
||||
exit 0
|
||||
fi
|
||||
|
||||
send_builtin_ipc_request() {
|
||||
perl -MCwd=abs_path -MEncode=encode,decode -MSocket \
|
||||
-e '
|
||||
sub read_qstring {
|
||||
my ($data, $offset) = @_;
|
||||
return ("", $offset) if $offset + 4 > length($data);
|
||||
my $length = unpack("N", substr($data, $offset, 4));
|
||||
$offset += 4;
|
||||
return ("", $offset) if $length == 0xffffffff || $offset + $length > length($data);
|
||||
return (decode("UTF-16BE", substr($data, $offset, $length)), $offset + $length);
|
||||
}
|
||||
|
||||
sub qstring {
|
||||
my $encoded = encode("UTF-16BE", $_[0] // "");
|
||||
return pack("N", length($encoded)) . $encoded;
|
||||
}
|
||||
|
||||
my $shell_qml = abs_path(shift @ARGV);
|
||||
my @open_args = @ARGV;
|
||||
my $runtime_dir = $ENV{"XDG_RUNTIME_DIR"} || "/run/user/$<";
|
||||
my $payload = chr(3) . qstring("image-selector") . qstring("open") . pack("N", scalar @open_args) . join("", map { qstring($_) } @open_args);
|
||||
my @candidates;
|
||||
|
||||
for my $lock_path (glob("$runtime_dir/quickshell/by-id/*/instance.lock")) {
|
||||
my $data;
|
||||
next unless open(my $lock, "<:raw", $lock_path);
|
||||
{ local $/; $data = <$lock>; }
|
||||
close($lock);
|
||||
my (undef, $offset) = read_qstring($data, 0);
|
||||
my ($path) = read_qstring($data, $offset);
|
||||
next unless $path && abs_path($path) eq $shell_qml;
|
||||
(my $socket_path = $lock_path) =~ s{/instance\.lock$}{/ipc.sock};
|
||||
push @candidates, [(stat($lock_path))[9] || 0, $socket_path];
|
||||
}
|
||||
|
||||
for my $candidate (sort { $b->[0] <=> $a->[0] } @candidates) {
|
||||
socket(my $client, AF_UNIX, SOCK_STREAM, 0) || next;
|
||||
if (connect($client, sockaddr_un($candidate->[1]))) {
|
||||
syswrite($client, $payload);
|
||||
close($client);
|
||||
exit 0;
|
||||
}
|
||||
close($client);
|
||||
}
|
||||
|
||||
exit 1;
|
||||
' "$OMARCHY_PATH/default/quickshell/omarchy-shell/shell.qml" \
|
||||
"" "$rows_b64" "$selected_list_image" "$selection_file" "$done_file" "$show_labels" "$filterable"
|
||||
}
|
||||
|
||||
if ! send_builtin_ipc_request && ! omarchy-shell-ipc image-selector open \
|
||||
if ! omarchy-shell image-selector open \
|
||||
"" \
|
||||
"$rows_b64" \
|
||||
"$selected_list_image" \
|
||||
|
||||
@@ -8,4 +8,4 @@ if (($# == 0)); then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
omarchy-shell-ipc notifications dismiss "$1" >/dev/null 2>&1 || true
|
||||
omarchy-shell notifications dismiss "$1" >/dev/null 2>&1 || true
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
# omarchy:summary=Show the current weather notification
|
||||
|
||||
omarchy-shell-ipc weatherFlyout show
|
||||
omarchy-shell weatherFlyout show
|
||||
|
||||
+1
-1
@@ -34,4 +34,4 @@ payload=$(jq -cn \
|
||||
--arg max "$max" \
|
||||
'{icon:$icon,message:$message,value:$value,progressText:$progressText,max:$max}')
|
||||
|
||||
omarchy-shell-ipc osd show "$payload" >/dev/null 2>&1 || true
|
||||
omarchy-shell osd show "$payload" >/dev/null 2>&1 || true
|
||||
|
||||
@@ -3,13 +3,5 @@
|
||||
# omarchy:summary=Restart the Omarchy shell
|
||||
# omarchy:examples=omarchy restart shell
|
||||
|
||||
CONFIG_DIR="$OMARCHY_PATH/default/quickshell/omarchy-shell"
|
||||
|
||||
# Skip `quickshell kill` (graceful IPC exit) — it currently trips an upstream
|
||||
# crash in IpcKillCommand::exec (Quickshell #539), which leaves the Wayland
|
||||
# surface lingering and produces a duplicate bar when the new instance starts.
|
||||
# A SIGKILL drops the client connection and the compositor cleans up surfaces
|
||||
# immediately, with no state to flush since the shell holds nothing persistent.
|
||||
pkill -KILL -f "quickshell -p $CONFIG_DIR" 2>/dev/null
|
||||
|
||||
setsid uwsm-app -- quickshell -p "$CONFIG_DIR" >/dev/null 2>&1 &
|
||||
systemctl --user enable omarchy-shell.service >/dev/null
|
||||
systemctl --user restart omarchy-shell.service
|
||||
|
||||
Executable
+141
@@ -0,0 +1,141 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Send an IPC call to the running Omarchy shell
|
||||
# omarchy:args=<target> <method> [args...]
|
||||
# omarchy:examples=omarchy shell shell ping | omarchy-shell menu toggle root
|
||||
|
||||
if [[ $# -eq 0 || $1 == "-h" || $1 == "--help" ]]; then
|
||||
cat <<USAGE
|
||||
Usage: omarchy-shell <target> <method> [args...]
|
||||
|
||||
Forwards an IPC call to the supervised omarchy-shell service. The shell is
|
||||
expected to already be running; this command does not start it.
|
||||
|
||||
Examples:
|
||||
omarchy-shell shell ping
|
||||
omarchy-shell shell summon omarchy.settings "{}"
|
||||
omarchy-shell shell listPlugins
|
||||
omarchy-shell menu toggle root
|
||||
USAGE
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if (( $# < 2 )); then
|
||||
echo "Usage: omarchy-shell <target> <method> [args...]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z ${OMARCHY_PATH:-} ]]; then
|
||||
echo "OMARCHY_PATH is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SHELL_QML="$OMARCHY_PATH/default/quickshell/omarchy-shell/shell.qml"
|
||||
|
||||
perl -MCwd=abs_path -MEncode=encode,decode -MSocket -MIO::Handle \
|
||||
-e '
|
||||
binmode STDOUT, ":encoding(UTF-8)";
|
||||
binmode STDERR, ":encoding(UTF-8)";
|
||||
$SIG{PIPE} = "IGNORE";
|
||||
|
||||
sub read_qstring {
|
||||
my ($data, $offset) = @_;
|
||||
return (undef, $offset) if $offset + 4 > length($data);
|
||||
my $length = unpack("N", substr($data, $offset, 4));
|
||||
$offset += 4;
|
||||
return (undef, $offset) if $length == 0xffffffff || $offset + $length > length($data);
|
||||
return (decode("UTF-16BE", substr($data, $offset, $length)), $offset + $length);
|
||||
}
|
||||
|
||||
sub qstring {
|
||||
my $encoded = encode("UTF-16BE", $_[0] // "");
|
||||
return pack("N", length($encoded)) . $encoded;
|
||||
}
|
||||
|
||||
sub write_all {
|
||||
my ($client, $payload) = @_;
|
||||
my $written = 0;
|
||||
while ($written < length($payload)) {
|
||||
my $n = syswrite($client, $payload, length($payload) - $written, $written);
|
||||
return 0 unless defined $n;
|
||||
$written += $n;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub print_success {
|
||||
my ($response) = @_;
|
||||
my ($message) = read_qstring($response, 2);
|
||||
return 0 unless defined $message && length($message);
|
||||
print $message;
|
||||
print "\n" unless substr($message, -1) eq "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
my $shell_qml_arg = shift @ARGV;
|
||||
my $shell_qml = abs_path($shell_qml_arg);
|
||||
my $target = shift @ARGV;
|
||||
my $function = shift @ARGV;
|
||||
my @args = @ARGV;
|
||||
my $runtime_dir = $ENV{"XDG_RUNTIME_DIR"} || "/run/user/$<";
|
||||
my $payload = chr(3) . qstring($target) . qstring($function) . pack("N", scalar @args) . join("", map { qstring($_) } @args);
|
||||
my @candidates;
|
||||
|
||||
if (!defined $shell_qml) {
|
||||
print STDERR "omarchy-shell config not found: $shell_qml_arg\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
for my $lock_path (glob("$runtime_dir/quickshell/by-id/*/instance.lock")) {
|
||||
my $data;
|
||||
next unless open(my $lock, "<:raw", $lock_path);
|
||||
{ local $/; $data = <$lock>; }
|
||||
close($lock);
|
||||
my (undef, $offset) = read_qstring($data, 0);
|
||||
my ($path) = read_qstring($data, $offset);
|
||||
next unless $path;
|
||||
my $abs_path = abs_path($path);
|
||||
next unless defined $abs_path && $abs_path eq $shell_qml;
|
||||
(my $socket_path = $lock_path) =~ s{/instance\.lock$}{/ipc.sock};
|
||||
push @candidates, [(stat($lock_path))[9] || 0, $socket_path];
|
||||
}
|
||||
|
||||
for my $candidate (sort { $b->[0] <=> $a->[0] } @candidates) {
|
||||
socket(my $client, AF_UNIX, SOCK_STREAM, 0) || next;
|
||||
if (connect($client, sockaddr_un($candidate->[1]))) {
|
||||
unless (write_all($client, $payload)) {
|
||||
close($client);
|
||||
next;
|
||||
}
|
||||
|
||||
my $response = "";
|
||||
while (1) {
|
||||
my $buffer = "";
|
||||
my $n = sysread($client, $buffer, 65536);
|
||||
last unless defined $n && $n > 0;
|
||||
$response .= $buffer;
|
||||
}
|
||||
close($client);
|
||||
|
||||
next unless length($response);
|
||||
my $code = ord(substr($response, 0, 1));
|
||||
if ($code == 5) {
|
||||
print_success($response);
|
||||
exit 0;
|
||||
} elsif ($code == 2) {
|
||||
print STDERR "Target not found.\n";
|
||||
} elsif ($code == 3) {
|
||||
print STDERR "Function not found.\n";
|
||||
} elsif ($code == 4) {
|
||||
print STDERR "Invalid arguments.\n";
|
||||
} else {
|
||||
print STDERR "Unexpected IPC response: $code\n";
|
||||
}
|
||||
exit 1;
|
||||
}
|
||||
close($client);
|
||||
}
|
||||
|
||||
print STDERR "omarchy-shell is not running\n";
|
||||
exit 1;
|
||||
' "$SHELL_QML" "$@"
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Send an IPC call to omarchy-shell, starting it if not running
|
||||
# omarchy:hidden=true
|
||||
|
||||
if [[ $# -eq 0 || $1 == "-h" || $1 == "--help" ]]; then
|
||||
cat <<USAGE
|
||||
Usage: omarchy-shell-ipc [--if-running] <target> <method> [args...]
|
||||
|
||||
Forwards a quickshell ipc call to omarchy-shell. Starts the shell if it
|
||||
isn't already running, unless --if-running is passed (in which case the
|
||||
call is silently skipped when no shell instance exists).
|
||||
|
||||
Examples:
|
||||
omarchy-shell-ipc shell ping
|
||||
omarchy-shell-ipc shell summon omarchy.settings "{}"
|
||||
omarchy-shell-ipc shell hide omarchy.image-picker
|
||||
omarchy-shell-ipc shell listPlugins
|
||||
omarchy-shell-ipc shell listShellConfig
|
||||
omarchy-shell-ipc shell rescanPlugins
|
||||
omarchy-shell-ipc --if-running bar refreshIndicators
|
||||
omarchy-shell-ipc image-selector ping
|
||||
omarchy-shell-ipc image-selector cancel ""
|
||||
USAGE
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if_running=0
|
||||
if [[ $1 == "--if-running" ]]; then
|
||||
if_running=1
|
||||
shift
|
||||
fi
|
||||
|
||||
SHELL_DIR="$OMARCHY_PATH/default/quickshell/omarchy-shell"
|
||||
|
||||
shell_running() {
|
||||
quickshell list -p "$SHELL_DIR" 2>/dev/null | grep -q '^Instance '
|
||||
}
|
||||
|
||||
if (( if_running )); then
|
||||
shell_running || exit 0
|
||||
else
|
||||
# Serialize concurrent invocations so two CLI callers don't both spawn the shell
|
||||
# when no instance is running yet.
|
||||
lockfile="${XDG_RUNTIME_DIR:-/tmp}/omarchy-shell-ipc.lock"
|
||||
{
|
||||
flock 9
|
||||
if ! shell_running; then
|
||||
# 9<&- so the spawned shell does not inherit (and keep) the lock fd; the
|
||||
# spawned process is long-lived and would otherwise hold the lock for the
|
||||
# remainder of the session, deadlocking every subsequent helper invocation.
|
||||
setsid uwsm-app -- quickshell -p "$SHELL_DIR" >/dev/null 2>&1 9<&- &
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||
sleep 0.2
|
||||
shell_running && break
|
||||
done
|
||||
fi
|
||||
} 9>"$lockfile"
|
||||
fi
|
||||
|
||||
exec quickshell ipc -p "$SHELL_DIR" call -- "$@"
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Send a fire-and-forget IPC call to a running omarchy-shell
|
||||
# omarchy:hidden=true
|
||||
|
||||
SHELL_QML="$OMARCHY_PATH/default/quickshell/omarchy-shell/shell.qml"
|
||||
|
||||
if (( $# < 2 )); then
|
||||
echo "Usage: omarchy-shell-ipc-fast <target> <function> [args...]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
perl -MCwd=abs_path -MEncode=encode,decode -MSocket \
|
||||
-e '
|
||||
sub read_qstring {
|
||||
my ($data, $offset) = @_;
|
||||
return ("", $offset) if $offset + 4 > length($data);
|
||||
my $length = unpack("N", substr($data, $offset, 4));
|
||||
$offset += 4;
|
||||
return ("", $offset) if $length == 0xffffffff || $offset + $length > length($data);
|
||||
return (decode("UTF-16BE", substr($data, $offset, $length)), $offset + $length);
|
||||
}
|
||||
|
||||
sub qstring {
|
||||
my $encoded = encode("UTF-16BE", $_[0] // "");
|
||||
return pack("N", length($encoded)) . $encoded;
|
||||
}
|
||||
|
||||
my $shell_qml = abs_path(shift @ARGV);
|
||||
my $target = shift @ARGV;
|
||||
my $function = shift @ARGV;
|
||||
my @args = @ARGV;
|
||||
my $runtime_dir = $ENV{"XDG_RUNTIME_DIR"} || "/run/user/$<";
|
||||
my $payload = chr(3) . qstring($target) . qstring($function) . pack("N", scalar @args) . join("", map { qstring($_) } @args);
|
||||
my @candidates;
|
||||
|
||||
for my $lock_path (glob("$runtime_dir/quickshell/by-id/*/instance.lock")) {
|
||||
my $data;
|
||||
next unless open(my $lock, "<:raw", $lock_path);
|
||||
{ local $/; $data = <$lock>; }
|
||||
close($lock);
|
||||
my (undef, $offset) = read_qstring($data, 0);
|
||||
my ($path) = read_qstring($data, $offset);
|
||||
next unless $path && abs_path($path) eq $shell_qml;
|
||||
(my $socket_path = $lock_path) =~ s{/instance\.lock$}{/ipc.sock};
|
||||
push @candidates, [(stat($lock_path))[9] || 0, $socket_path];
|
||||
}
|
||||
|
||||
for my $candidate (sort { $b->[0] <=> $a->[0] } @candidates) {
|
||||
socket(my $client, AF_UNIX, SOCK_STREAM, 0) || next;
|
||||
if (connect($client, sockaddr_un($candidate->[1]))) {
|
||||
syswrite($client, $payload);
|
||||
close($client);
|
||||
exit 0;
|
||||
}
|
||||
close($client);
|
||||
}
|
||||
|
||||
exit 1;
|
||||
' "$SHELL_QML" "$@"
|
||||
@@ -22,4 +22,4 @@ ln -nsf "$BACKGROUND" "$CURRENT_BACKGROUND_LINK"
|
||||
|
||||
# Update the live shell background immediately when it is running. The
|
||||
# background plugin also polls this symlink, but IPC avoids the visible delay.
|
||||
omarchy-shell-ipc-fast background set "$BACKGROUND" >/dev/null 2>&1 || true
|
||||
omarchy-shell background set "$BACKGROUND" >/dev/null 2>&1 || true
|
||||
|
||||
@@ -32,7 +32,7 @@ run_parallel() {
|
||||
}
|
||||
|
||||
shell_ipc() {
|
||||
timeout 2 omarchy-shell-ipc --if-running "$@" >/dev/null 2>&1
|
||||
timeout 2 omarchy-shell "$@" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
snapshot_background_path() {
|
||||
|
||||
@@ -10,4 +10,4 @@ else
|
||||
omarchy-notification-send -g "" "Idle behavior on" "System can now sleep / lock on idle"
|
||||
fi
|
||||
|
||||
omarchy-shell-ipc --if-running bar refreshIndicators >/dev/null 2>&1 || true
|
||||
omarchy-shell bar refreshIndicators >/dev/null 2>&1 || true
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# omarchy:summary=Toggle notification do-not-disturb mode
|
||||
|
||||
state=$(omarchy-shell notifications toggleDnd 2>/dev/null || echo "")
|
||||
omarchy-shell --if-running bar refreshIndicators >/dev/null 2>&1 || true
|
||||
omarchy-shell bar refreshIndicators >/dev/null 2>&1 || true
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
|
||||
# omarchy:summary=Ensure the status bar icon offering the available update is removed
|
||||
|
||||
omarchy-shell-ipc --if-running bar refreshUpdate >/dev/null 2>&1 || true
|
||||
omarchy-shell bar refreshUpdate >/dev/null 2>&1 || true
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user