#!/usr/bin/env bash set -euo pipefail # Script to launch an application or focus it if already running in sway # Usage: launch-or-focus [args...] if [ $# -eq 0 ]; then echo "Usage: $0 [args...]" >&2 exit 1 fi COMMAND="$1" shift ARGS=("$@") APP_NAME=$(basename "$COMMAND") # Extract --class and --title values from args if present CLASS_NAME="" TITLE_NAME="" for i in "${!ARGS[@]}"; do if [ "${ARGS[$i]}" = "--class" ] && [ $((i+1)) -lt ${#ARGS[@]} ]; then CLASS_NAME="${ARGS[$((i+1))]}" elif [ "${ARGS[$i]}" = "--title" ] && [ $((i+1)) -lt ${#ARGS[@]} ]; then TITLE_NAME="${ARGS[$((i+1))]}" fi done find_window_by_class() { local class="$1" swaymsg -t get_tree | jq -r --arg class "$class" ' recurse(.nodes[]?, .floating_nodes[]?) | select( ((.window_properties.class | type) == "string" and .window_properties.class == $class) or ((.app_id | type) == "string" and .app_id == $class) ) | .id ' | head -n 1 } find_window_by_title() { local title="$1" local app="$2" local result # Search for windows matching both app and title (most specific) result=$(swaymsg -t get_tree | jq -r --arg title "$title" --arg app "$app" ' recurse(.nodes[]?, .floating_nodes[]?) | select( ( ((.app_id | type) == "string" and (.app_id == $app or (.app_id | test($app; "i")))) or ((.window_properties.class | type) == "string" and (.window_properties.class == $app or (.window_properties.class | test($app; "i")))) ) and ((.name | type) == "string" and .name == $title) ) | .id ' | head -n 1) [ -n "$result" ] && [ "$result" != "null" ] && echo "$result" && return # Fall back to title-only exact match result=$(swaymsg -t get_tree | jq -r --arg title "$title" ' recurse(.nodes[]?, .floating_nodes[]?) | select((.name | type) == "string" and .name == $title) | .id ' | head -n 1) [ -n "$result" ] && [ "$result" != "null" ] && echo "$result" && return # Final fallback: title-only case-insensitive match swaymsg -t get_tree | jq -r --arg title "$title" ' recurse(.nodes[]?, .floating_nodes[]?) | select((.name | type) == "string" and (.name | test($title; "i"))) | .id ' | head -n 1 } find_window() { local app_name="$1" swaymsg -t get_tree | jq -r --arg app "$app_name" ' recurse(.nodes[]?, .floating_nodes[]?) | select( ((.app_id | type) == "string" and (.app_id == $app or (.app_id | test($app; "i")))) or ((.window_properties.class | type) == "string" and (.window_properties.class == $app or (.window_properties.class | test($app; "i")))) or ((.name | type) == "string" and (.name | test($app; "i"))) ) | .id ' | head -n 1 } focus_window() { local window_id="$1" swaymsg "[con_id=$window_id]" focus } WINDOW_ID="" [ -n "$CLASS_NAME" ] && WINDOW_ID=$(find_window_by_class "$CLASS_NAME") ([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && [ -n "$TITLE_NAME" ] && WINDOW_ID=$(find_window_by_title "$TITLE_NAME" "$APP_NAME") ([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && WINDOW_ID=$(find_window "$APP_NAME") ([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && WINDOW_ID=$(find_window "${APP_NAME,,}") [ -n "$WINDOW_ID" ] && [ "$WINDOW_ID" != "null" ] && [ "$WINDOW_ID" -eq "$WINDOW_ID" ] 2>/dev/null && focus_window "$WINDOW_ID" && exit 0 "$COMMAND" "${ARGS[@]}" >/dev/null 2>&1 & disown