#!/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 [--title ] -- 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 "$@"