Press the browser key twice, get one browser
The application keys ran their command unconditionally, so pressing the browser key twice gave you two browsers. That is the single most common "Linux feels wrong" moment and it is a twenty-line fix: match the window class, focus it if it is there, launch if it is not. Two details the obvious version gets wrong. Patterns are anchored, because an unanchored "mail" matches gmail-notifier and the mail key would raise somebody's notifier. And class alone is not enough here: the terminal and the editor are both kitty, so the editor bind narrows by title or it raises whatever terminal happened to be open. The contract's fixture contains both traps. Found and fixed a bug this shipped with earlier today. Hyprland 0.56 dispatches through Lua, so `hyprctl dispatch focuswindow address:0x...` is parsed as Lua source and fails with a syntax error -- which is what the launcher's Switch Window command had been doing since it landed. Its own contract stubbed hyprctl and recorded the arguments, so the call looked correct and never ran. Both now use the selector form hl.focus actually accepts, and the contract asserts the shape rather than only that something was dispatched.
This commit is contained in:
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Focus the window if it is already open; start it if it is not.
|
||||
#
|
||||
# panama-launch --class '^helium$' -- helium-browser-bin
|
||||
# panama-launch --class '^kitty$' --title 'nvim' -- kitty nvim .
|
||||
#
|
||||
# This is what the application keys do on every other desktop. Pressing the
|
||||
# browser key twice on macOS or Windows raises the browser; here it used to
|
||||
# open a second one, which is the single most common "Linux feels wrong"
|
||||
# moment and a twenty-line fix.
|
||||
#
|
||||
# Matching is a regular expression against the window class, optionally
|
||||
# narrowed by title. Both halves matter: the terminal and the editor are both
|
||||
# kitty on this desktop, and only the title tells them apart -- so a class-only
|
||||
# match would make the editor key raise whatever terminal happened to be open.
|
||||
#
|
||||
# Anchor your patterns. `--class mail` would match `gmail-notifier`, and the
|
||||
# key that should open Thunderbird would raise somebody's notifier instead.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
class_pattern=""
|
||||
title_pattern=""
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--class) class_pattern="${2:-}"; shift 2 ;;
|
||||
--title) title_pattern="${2:-}"; shift 2 ;;
|
||||
--) shift; break ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$class_pattern" || $# -eq 0 ]]; then
|
||||
echo 'usage: panama-launch --class <regex> [--title <regex>] -- command [args...]' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
launch() {
|
||||
# setsid so the application outlives this script and is not a child of the
|
||||
# compositor's exec, which would tie its lifetime to a shell that exits.
|
||||
setsid "$@" >/dev/null 2>&1 &
|
||||
exit 0
|
||||
}
|
||||
|
||||
# No compositor, no window list: just start the thing.
|
||||
command -v hyprctl >/dev/null 2>&1 || launch "$@"
|
||||
|
||||
address="$(hyprctl clients -j 2>/dev/null | jq -r --arg class "$class_pattern" --arg title "$title_pattern" '
|
||||
[ .[]
|
||||
| select(.mapped)
|
||||
| select(.class | test($class))
|
||||
| select($title == "" or (.title | test($title)))
|
||||
]
|
||||
# Most recently focused first: with several matches, raise the one the
|
||||
# user was last in rather than whichever the compositor lists first.
|
||||
| sort_by(-.focusHistoryID)
|
||||
| .[0].address // empty
|
||||
' 2>/dev/null)"
|
||||
|
||||
if [[ -n "$address" ]]; then
|
||||
# Hyprland 0.56 dispatches through Lua: `hyprctl dispatch focuswindow
|
||||
# address:0x...` is parsed as Lua source and fails. The selector string is
|
||||
# what hl.focus accepts; a table of the same fields is refused.
|
||||
exec hyprctl dispatch "hl.dsp.focus({ window = \"address:$address\" })"
|
||||
fi
|
||||
|
||||
launch "$@"
|
||||
Reference in New Issue
Block a user