launch-or-focus 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. # Script to launch an application or focus it if already running in sway
  4. # Usage: launch-or-focus <command> [args...]
  5. if [ $# -eq 0 ]; then
  6. echo "Usage: $0 <command> [args...]" >&2
  7. exit 1
  8. fi
  9. COMMAND="$1"
  10. shift
  11. ARGS=("$@")
  12. APP_NAME=$(basename "$COMMAND")
  13. # Extract --class and --title values from args if present
  14. # Handle both --class VALUE and --class=VALUE formats
  15. CLASS_NAME=""
  16. TITLE_NAME=""
  17. for i in "${!ARGS[@]}"; do
  18. if [[ "${ARGS[$i]}" =~ ^--class=(.+)$ ]]; then
  19. CLASS_NAME="${BASH_REMATCH[1]}"
  20. elif [ "${ARGS[$i]}" = "--class" ] && [ $((i+1)) -lt ${#ARGS[@]} ]; then
  21. CLASS_NAME="${ARGS[$((i+1))]}"
  22. elif [[ "${ARGS[$i]}" =~ ^--title=(.+)$ ]]; then
  23. TITLE_NAME="${BASH_REMATCH[1]}"
  24. elif [ "${ARGS[$i]}" = "--title" ] && [ $((i+1)) -lt ${#ARGS[@]} ]; then
  25. TITLE_NAME="${ARGS[$((i+1))]}"
  26. fi
  27. done
  28. find_window_by_class() {
  29. local class="$1"
  30. swaymsg -t get_tree | jq -r --arg class "$class" '
  31. recurse(.nodes[]?, .floating_nodes[]?) |
  32. select(
  33. ((.window_properties.class | type) == "string" and .window_properties.class == $class) or
  34. ((.app_id | type) == "string" and .app_id == $class)
  35. ) |
  36. .id
  37. ' | head -n 1
  38. }
  39. find_window_by_title() {
  40. local title="$1"
  41. local app="$2"
  42. local result
  43. # Search for windows matching both app and title (most specific)
  44. result=$(swaymsg -t get_tree | jq -r --arg title "$title" --arg app "$app" '
  45. recurse(.nodes[]?, .floating_nodes[]?) |
  46. select(
  47. (
  48. ((.app_id | type) == "string" and (.app_id == $app or (.app_id | test($app; "i")))) or
  49. ((.window_properties.class | type) == "string" and (.window_properties.class == $app or (.window_properties.class | test($app; "i"))))
  50. ) and
  51. ((.name | type) == "string" and .name == $title)
  52. ) |
  53. .id
  54. ' | head -n 1)
  55. [ -n "$result" ] && [ "$result" != "null" ] && echo "$result" && return
  56. # Fall back to title-only exact match
  57. result=$(swaymsg -t get_tree | jq -r --arg title "$title" '
  58. recurse(.nodes[]?, .floating_nodes[]?) |
  59. select((.name | type) == "string" and .name == $title) |
  60. .id
  61. ' | head -n 1)
  62. [ -n "$result" ] && [ "$result" != "null" ] && echo "$result" && return
  63. # Final fallback: title-only case-insensitive match
  64. swaymsg -t get_tree | jq -r --arg title "$title" '
  65. recurse(.nodes[]?, .floating_nodes[]?) |
  66. select((.name | type) == "string" and (.name | test($title; "i"))) |
  67. .id
  68. ' | head -n 1
  69. }
  70. find_window() {
  71. local app_name="$1"
  72. swaymsg -t get_tree | jq -r --arg app "$app_name" '
  73. recurse(.nodes[]?, .floating_nodes[]?) |
  74. select(
  75. ((.app_id | type) == "string" and (.app_id == $app or (.app_id | test($app; "i")))) or
  76. ((.window_properties.class | type) == "string" and (.window_properties.class == $app or (.window_properties.class | test($app; "i")))) or
  77. ((.name | type) == "string" and (.name | test($app; "i")))
  78. ) |
  79. .id
  80. ' | head -n 1
  81. }
  82. focus_window() {
  83. local window_id="$1"
  84. swaymsg "[con_id=$window_id]" focus
  85. }
  86. WINDOW_ID=""
  87. [ -n "$CLASS_NAME" ] && WINDOW_ID=$(find_window_by_class "$CLASS_NAME")
  88. ([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && [ -n "$TITLE_NAME" ] && WINDOW_ID=$(find_window_by_title "$TITLE_NAME" "$APP_NAME")
  89. ([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && WINDOW_ID=$(find_window "$APP_NAME")
  90. ([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && WINDOW_ID=$(find_window "${APP_NAME,,}")
  91. [ -n "$WINDOW_ID" ] && [ "$WINDOW_ID" != "null" ] && [ "$WINDOW_ID" -eq "$WINDOW_ID" ] 2>/dev/null && focus_window "$WINDOW_ID" && exit 0
  92. "$COMMAND" "${ARGS[@]}" >/dev/null 2>&1 &
  93. disown