launch-or-focus 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. # Simple script to launch an application or focus it if already running in sway
  4. # Usage: launch-or-focus <command> [--class CLASS] [--title TITLE] [args...]
  5. #
  6. # Examples:
  7. # launch-or-focus firefox
  8. # launch-or-focus code --class Code --title "My Project"
  9. # launch-or-focus kitty --class kitty
  10. CLASS=""
  11. TITLE=""
  12. ARGS=()
  13. # Parse arguments
  14. while [[ $# -gt 0 ]]; do
  15. case $1 in
  16. --class)
  17. CLASS="$2"
  18. shift 2
  19. ;;
  20. --title)
  21. TITLE="$2"
  22. shift 2
  23. ;;
  24. *)
  25. ARGS+=("$1")
  26. shift
  27. ;;
  28. esac
  29. done
  30. if [ ${#ARGS[@]} -eq 0 ]; then
  31. echo "Usage: $0 <command> [--class CLASS] [--title TITLE] [args...]" >&2
  32. exit 1
  33. fi
  34. COMMAND="${ARGS[0]}"
  35. APP_NAME=$(basename "$COMMAND")
  36. unset 'ARGS[0]'
  37. ARGS=("${ARGS[@]}")
  38. # If class/title were provided as flags, add them to command args (for apps that support them)
  39. if [ -n "$CLASS" ]; then
  40. ARGS=("--class=$CLASS" "${ARGS[@]}")
  41. fi
  42. if [ -n "$TITLE" ]; then
  43. ARGS=("--title=$TITLE" "${ARGS[@]}")
  44. fi
  45. # Find window by app_id (sway uses app_id), title, or app name
  46. find_window() {
  47. local class="$1"
  48. local title="$2"
  49. local app="$3"
  50. # If class provided, search by app_id first (sway uses app_id)
  51. if [ -n "$class" ]; then
  52. swaymsg -t get_tree | jq -r --arg class "$class" '
  53. recurse(.nodes[]?, .floating_nodes[]?) |
  54. select((.app_id | type) == "string" and .app_id == $class) |
  55. .id
  56. ' | head -n 1
  57. return
  58. fi
  59. # If only title provided, search by title
  60. if [ -n "$title" ]; then
  61. swaymsg -t get_tree | jq -r --arg title "$title" '
  62. recurse(.nodes[]?, .floating_nodes[]?) |
  63. select((.name | type) == "string" and .name == $title) |
  64. .id
  65. ' | head -n 1
  66. return
  67. fi
  68. # Fallback to app name (search by app_id)
  69. swaymsg -t get_tree | jq -r --arg app "$app" '
  70. recurse(.nodes[]?, .floating_nodes[]?) |
  71. select((.app_id | type) == "string" and .app_id == $app) |
  72. .id
  73. ' | head -n 1
  74. }
  75. # Try to find existing window
  76. WINDOW_ID=$(find_window "$CLASS" "$TITLE" "$APP_NAME")
  77. # Focus if found, otherwise launch
  78. if [ -n "$WINDOW_ID" ] && [ "$WINDOW_ID" != "null" ]; then
  79. swaymsg "[con_id=$WINDOW_ID]" focus
  80. else
  81. "$COMMAND" "${ARGS[@]}" >/dev/null 2>&1 &
  82. disown
  83. fi