Replace hand-rolled shell IPC with qs ipc call
The embedded Perl program parsed instance.lock QString framing and spoke
the Quickshell IPC socket protocol by hand. Quickshell ships this
natively: qs -p <config> ipc call does instance discovery, the call, and
response printing, byte-identical for every in-repo call pattern.
The bash wrapper keeps what qs doesn't do: the -q best-effort mode, the
{} default argument for summon/toggle, and the familiar error wordings.
Two qs quirks are handled: function names like "show" shadow qs's own
CLI subcommands, so arguments are always passed after --, and IPC-level
failures (unknown target/function, bad arguments) are reported on stdout
with exit 0, so those are matched and mapped to stderr with exit 1.
165 lines to 62.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
798d6af8b0
commit
4a2502cb63
+22
-124
@@ -10,6 +10,12 @@ if [[ ${1:-} == "-q" ]]; then
|
||||
shift
|
||||
fi
|
||||
|
||||
fail() {
|
||||
(( QUIET )) && exit 0
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (( $# == 0 )) || [[ $1 == "-h" || $1 == "--help" ]]; then
|
||||
cat <<USAGE
|
||||
Usage: omarchy-shell [-q] <target> <method> [args...]
|
||||
@@ -31,134 +37,26 @@ USAGE
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if (( $# < 2 )); then
|
||||
(( QUIET )) && exit 0
|
||||
echo "Usage: omarchy-shell <target> <method> [args...]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z ${OMARCHY_PATH:-} ]]; then
|
||||
(( QUIET )) && exit 0
|
||||
echo "OMARCHY_PATH is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SHELL_QML="$OMARCHY_PATH/shell/shell.qml"
|
||||
(( $# >= 2 )) || fail "Usage: omarchy-shell <target> <method> [args...]"
|
||||
[[ -n ${OMARCHY_PATH:-} ]] || fail "OMARCHY_PATH is not set"
|
||||
[[ -f $OMARCHY_PATH/shell/shell.qml ]] || fail "omarchy-shell config not found: $OMARCHY_PATH/shell/shell.qml"
|
||||
|
||||
if [[ $1 == "shell" && ( $2 == "summon" || $2 == "toggle" ) ]] && (( $# == 3 )); then
|
||||
set -- "$1" "$2" "$3" "{}"
|
||||
fi
|
||||
|
||||
export OMARCHY_SHELL_QUIET="$QUIET"
|
||||
# The -- keeps function names that shadow qs subcommands (e.g. show) as
|
||||
# positionals. qs reports connection failures with a nonzero exit, but IPC-level
|
||||
# failures (unknown target/function, bad arguments) go to stdout with exit 0.
|
||||
output=$(qs -p "$OMARCHY_PATH/shell" ipc call -- "$@" 2>/dev/null) || fail "omarchy-shell is not running"
|
||||
|
||||
perl -MCwd=abs_path -MEncode=encode,decode -MSocket -MIO::Handle \
|
||||
-e '
|
||||
binmode STDOUT, ":encoding(UTF-8)";
|
||||
binmode STDERR, ":encoding(UTF-8)";
|
||||
$SIG{PIPE} = "IGNORE";
|
||||
case $output in
|
||||
"Target not found." | "Function not found." | "Too few arguments provided"* | "Too many arguments provided"*)
|
||||
fail "$output"
|
||||
;;
|
||||
esac
|
||||
|
||||
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 = decode("UTF-8", shift @ARGV);
|
||||
my $function = decode("UTF-8", shift @ARGV);
|
||||
my @args = map { decode("UTF-8", $_) } @ARGV;
|
||||
my $runtime_dir = $ENV{"XDG_RUNTIME_DIR"} || "/run/user/$<";
|
||||
my $quiet = ($ENV{"OMARCHY_SHELL_QUIET"} || "") eq "1";
|
||||
my $payload = chr(3) . qstring($target) . qstring($function) . pack("N", scalar @args) . join("", map { qstring($_) } @args);
|
||||
my @candidates;
|
||||
|
||||
if (!defined $shell_qml) {
|
||||
exit 0 if $quiet;
|
||||
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) {
|
||||
exit 0 if $quiet;
|
||||
print_success($response);
|
||||
exit 0;
|
||||
} elsif ($code == 2) {
|
||||
print STDERR "Target not found.\n" unless $quiet;
|
||||
} elsif ($code == 3) {
|
||||
print STDERR "Function not found.\n" unless $quiet;
|
||||
} elsif ($code == 4) {
|
||||
print STDERR "Invalid arguments.\n" unless $quiet;
|
||||
} else {
|
||||
print STDERR "Unexpected IPC response: $code\n" unless $quiet;
|
||||
}
|
||||
exit($quiet ? 0 : 1);
|
||||
}
|
||||
close($client);
|
||||
}
|
||||
|
||||
exit 0 if $quiet;
|
||||
print STDERR "omarchy-shell is not running\n";
|
||||
exit 1;
|
||||
' "$SHELL_QML" "$@"
|
||||
if (( !QUIET )) && [[ -n $output ]]; then
|
||||
echo "$output"
|
||||
fi
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user