#!/usr/bin/env bash set -euo pipefail # Simple script to launch an application or focus it if already running in sway # Usage: launch-or-focus [--class CLASS] [--title TITLE] [args...] # # Examples: # launch-or-focus firefox # launch-or-focus code --class Code --title "My Project" # launch-or-focus kitty --class kitty CLASS="" TITLE="" ARGS=() # Parse arguments while [[ $# -gt 0 ]]; do case $1 in --class) CLASS="$2" shift 2 ;; --title) TITLE="$2" shift 2 ;; *) ARGS+=("$1") shift ;; esac done if [ ${#ARGS[@]} -eq 0 ]; then echo "Usage: $0 [--class CLASS] [--title TITLE] [args...]" >&2 exit 1 fi COMMAND="${ARGS[0]}" APP_NAME=$(basename "$COMMAND") unset 'ARGS[0]' ARGS=("${ARGS[@]}") # If class/title were provided as flags, add them to command args (for apps that support them) if [ -n "$CLASS" ]; then ARGS=("--class=$CLASS" "${ARGS[@]}") fi if [ -n "$TITLE" ]; then ARGS=("--title=$TITLE" "${ARGS[@]}") fi # Find window by app_id (sway uses app_id), title, or app name find_window() { local class="$1" local title="$2" local app="$3" # If class provided, search by app_id first (sway uses app_id) if [ -n "$class" ]; then swaymsg -t get_tree | jq -r --arg class "$class" ' recurse(.nodes[]?, .floating_nodes[]?) | select((.app_id | type) == "string" and .app_id == $class) | .id ' | head -n 1 return fi # If only title provided, search by title if [ -n "$title" ]; then swaymsg -t get_tree | jq -r --arg title "$title" ' recurse(.nodes[]?, .floating_nodes[]?) | select((.name | type) == "string" and .name == $title) | .id ' | head -n 1 return fi # Fallback to app name (search by app_id) swaymsg -t get_tree | jq -r --arg app "$app" ' recurse(.nodes[]?, .floating_nodes[]?) | select((.app_id | type) == "string" and .app_id == $app) | .id ' | head -n 1 } # Try to find existing window WINDOW_ID=$(find_window "$CLASS" "$TITLE" "$APP_NAME") # Focus if found, otherwise launch if [ -n "$WINDOW_ID" ] && [ "$WINDOW_ID" != "null" ]; then swaymsg "[con_id=$WINDOW_ID]" focus else "$COMMAND" "${ARGS[@]}" >/dev/null 2>&1 & disown fi