276 lines
7.5 KiB
Bash
Executable file
276 lines
7.5 KiB
Bash
Executable file
#!/bin/sh
|
|
|
|
require() {
|
|
for cmd in "$@"; do
|
|
command -v "$cmd" >/dev/null 2>&1 || {
|
|
echo "hypr: missing dependency: $cmd" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
}
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: hypr <subcommand> [app] [args...]
|
|
|
|
Commands:
|
|
spawnfocus <app> [args...] Focus existing window or spawn app with args
|
|
pull [app] Pull window to current workspace (picker if no app)
|
|
windowrules Apply dynamic window rules
|
|
exit Safely exit hyprland
|
|
|
|
Options:
|
|
-h, --help Show this help message
|
|
EOF
|
|
}
|
|
|
|
[ -z "$1" ] && {
|
|
usage
|
|
exit 1
|
|
}
|
|
|
|
cmd="$1"
|
|
shift
|
|
|
|
case "$cmd" in
|
|
-h | --help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
exit)
|
|
require hyprctl
|
|
pkill hypridle
|
|
pkill hyprpaper
|
|
hyprctl dispatch exit
|
|
exit 0
|
|
;;
|
|
pull)
|
|
require hyprctl jq fuzzel
|
|
APP="$1"
|
|
case "$APP" in
|
|
google-chrome | google-chrome-stable) CLASS="google-chrome" ;;
|
|
chromium) CLASS="Chromium" ;;
|
|
zen | zen-browser) CLASS="zen" ;;
|
|
firefox) CLASS="firefox" ;;
|
|
alacritty) CLASS="Alacritty" ;;
|
|
code | vscodium) CLASS="Code" ;;
|
|
signal-desktop | signal) CLASS="signal" ;;
|
|
telegram-desktop | telegram) CLASS="TelegramDesktop" ;;
|
|
ghostty) CLASS="com.mitchellh.ghostty" ;;
|
|
bitwarden-desktop | bitwarden) CLASS="Bitwarden" ;;
|
|
slack) CLASS="Slack" ;;
|
|
discord) CLASS="discord" ;;
|
|
vesktop) CLASS="vesktop" ;;
|
|
element-desktop | element) CLASS="element" ;;
|
|
*) CLASS="$APP" ;;
|
|
esac
|
|
|
|
CUR_WS=$(hyprctl activeworkspace -j | jq -r '.id')
|
|
|
|
CUR_ADDR=$(hyprctl -j activewindow | jq -r '.address // empty')
|
|
|
|
WIN_ADDRS=$(hyprctl -j clients 2>/dev/null | jq -r --arg pat "$CLASS" '
|
|
.[]?
|
|
| select(
|
|
(.class? | ascii_downcase | contains($pat | ascii_downcase)) or
|
|
(.initialClass? | ascii_downcase | contains($pat | ascii_downcase)) or
|
|
(.xdgTag? // "" | ascii_downcase | contains($pat | ascii_downcase))
|
|
)
|
|
| "\(.class)\t\(.title // "no title")\t\(.address)"
|
|
')
|
|
|
|
WIN_COUNT=$(echo "$WIN_ADDRS" | grep -c '^' || :)
|
|
|
|
if [ "$WIN_COUNT" -eq 0 ]; then
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$WIN_COUNT" -eq 1 ]; then
|
|
WIN_ADDR=$(echo "$WIN_ADDRS" | awk -F'\t' '{print $3}')
|
|
else
|
|
ROFI_LINES=$(
|
|
echo "$WIN_ADDRS" |
|
|
awk -F'\t' -v cur="$CUR_ADDR" '
|
|
$3 != cur { printf "%s : %s\t%s\n", $1, $2, $3 }
|
|
'
|
|
)
|
|
|
|
[ -z "$ROFI_LINES" ] && exit 0
|
|
|
|
SELECTED=$(echo "$ROFI_LINES" | fuzzel --dmenu --prompt="pull: ")
|
|
|
|
[ -z "$SELECTED" ] && exit 0
|
|
|
|
WIN_ADDR=$(echo "$SELECTED" | awk -F'\t' '{print $2}')
|
|
fi
|
|
|
|
[ -z "$WIN_ADDR" ] && exit 0
|
|
|
|
hyprctl dispatch movetoworkspace "${CUR_WS},address:${WIN_ADDR}"
|
|
hyprctl dispatch focuswindow "address:${WIN_ADDR}"
|
|
;;
|
|
spawnfocus)
|
|
require hyprctl jq fuzzel socat
|
|
WS=""
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--ws)
|
|
if [ $# -lt 2 ]; then
|
|
echo "Missing workspace number after --ws"
|
|
exit 1
|
|
fi
|
|
WS="$2"
|
|
shift 2
|
|
;;
|
|
-*)
|
|
echo "Unknown option $1"
|
|
exit 1
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
APP="$1"
|
|
[ -z "$APP" ] && {
|
|
echo 'Specify an app'
|
|
exit 1
|
|
}
|
|
shift
|
|
|
|
case "$APP" in
|
|
google-chrome | google-chrome-stable) CLASS="google-chrome" ;;
|
|
chromium) CLASS="Chromium" ;;
|
|
zen | zen-browser) CLASS="zen" ;;
|
|
firefox) CLASS="firefox" ;;
|
|
alacritty) CLASS="Alacritty" ;;
|
|
code | vscodium) CLASS="Code" ;;
|
|
signal-desktop | signal) CLASS="signal" ;;
|
|
telegram-desktop | telegram) CLASS="TelegramDesktop" ;;
|
|
ghostty) CLASS="com.mitchellh.ghostty" ;;
|
|
bitwarden-desktop | bitwarden) CLASS="Bitwarden" ;;
|
|
slack) CLASS="Slack" ;;
|
|
discord) CLASS="discord" ;;
|
|
vesktop) CLASS="vesktop" ;;
|
|
element-desktop | element) CLASS="element" ;;
|
|
*) CLASS="$APP" ;;
|
|
esac
|
|
|
|
WIN_ADDRS=$(hyprctl -j clients 2>/dev/null | jq -r --arg class "$CLASS" '
|
|
.[]? | select(
|
|
((.xdgTag // "") | ascii_downcase | contains($class | ascii_downcase)) or
|
|
((.initialClass // "") | ascii_downcase | contains($class | ascii_downcase)) or
|
|
((.class // "") | ascii_downcase | contains($class | ascii_downcase))
|
|
) | "\(.class)\t\(.title)\t\(.address)"
|
|
')
|
|
|
|
WIN_COUNT=$(echo "$WIN_ADDRS" | grep -c .)
|
|
|
|
if [ "$WIN_COUNT" -eq 0 ]; then
|
|
WIN_ADDR=""
|
|
elif [ "$WIN_COUNT" -eq 1 ]; then
|
|
WIN_ADDR=$(echo "$WIN_ADDRS" | awk -F'\t' '{print $3}')
|
|
elif [ "$WIN_COUNT" -gt 1 ]; then
|
|
SELECTED=$(echo "$WIN_ADDRS" | awk -F'\t' '{print $1 ": " $2 "\t" $3}' | fuzzel --dmenu --prompt="window: ")
|
|
|
|
if [ -z "$SELECTED" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
WIN_ADDR=$(echo "$SELECTED" | awk -F'\t' '{print $2}')
|
|
fi
|
|
if [ -n "$WIN_ADDR" ]; then
|
|
if [ -n "$WS" ]; then
|
|
WIN_WS=$(hyprctl clients -j | jq -r --arg addr "$WIN_ADDR" '.[] | select(.address == $addr) | .workspace.id')
|
|
|
|
if [ "$WIN_WS" != "$WS" ]; then
|
|
hyprctl dispatch movetoworkspacesilent "$WS,address:$WIN_ADDR"
|
|
fi
|
|
|
|
hyprctl dispatch workspace "$WS"
|
|
else
|
|
WIN_WS=$(hyprctl clients -j | jq -r --arg addr "$WIN_ADDR" '.[] | select(.address == $addr) | .workspace.id')
|
|
|
|
hyprctl dispatch workspace "$WIN_WS"
|
|
fi
|
|
|
|
hyprctl dispatch focuswindow "address:$WIN_ADDR"
|
|
else
|
|
EXISTING_HEX=$(hyprctl -j clients 2>/dev/null | jq -r --arg class "$CLASS" '
|
|
.[]? | select(
|
|
((.xdgTag // "") | ascii_downcase | contains($class | ascii_downcase)) or
|
|
((.initialClass // "") | ascii_downcase | contains($class | ascii_downcase)) or
|
|
((.class // "") | ascii_downcase | contains($class | ascii_downcase))
|
|
) | .address | ltrimstr("0x")
|
|
' | tr '\n' ' ')
|
|
|
|
if [ -n "$WS" ]; then
|
|
hyprctl dispatch workspace "$WS"
|
|
fi
|
|
|
|
if [ $# -eq 0 ]; then
|
|
"$APP" &
|
|
else
|
|
"$APP" "$@" &
|
|
fi
|
|
|
|
socat -u UNIX-CONNECT:"$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock" - | while read -r line; do
|
|
case "$line" in
|
|
openwindow*)
|
|
window_id=$(echo "$line" | cut -d'>' -f3 | cut -d',' -f1)
|
|
event_class=$(echo "$line" | cut -d'>' -f3 | cut -d',' -f3)
|
|
class_lower=$(echo "$event_class" | tr '[:upper:]' '[:lower:]')
|
|
target_lower=$(echo "$CLASS" | tr '[:upper:]' '[:lower:]')
|
|
case "$class_lower" in
|
|
*"$target_lower"*)
|
|
case " $EXISTING_HEX " in
|
|
*" $window_id "*) ;;
|
|
*)
|
|
addr="0x$window_id"
|
|
if [ -n "$WS" ]; then
|
|
hyprctl dispatch movetoworkspacesilent "$WS,address:$addr"
|
|
fi
|
|
hyprctl dispatch focuswindow "address:$addr"
|
|
break
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
done
|
|
fi
|
|
;;
|
|
windowrules)
|
|
require hyprctl socat
|
|
socat -u UNIX-CONNECT:"$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock" - | while IFS= read -r line; do
|
|
event="${line%%>>*}"
|
|
data="${line#*>>}"
|
|
case "$event" in
|
|
windowtitlev2)
|
|
window_id="${data%%,*}"
|
|
window_title="${data#*,}"
|
|
window_title=$(echo "$window_title" | tr '[:upper:]' '[:lower:]')
|
|
# NOTE: has been buggy recently
|
|
# case "$window_title" in
|
|
# *'extension: (bitwarden password manager) - bitwarden'*)
|
|
# hyprctl --batch "dispatch setfloating address:0x$window_id; dispatch centerwindow address:0x$window_id"
|
|
# ;;
|
|
# *'sign in - google accounts '*)
|
|
# hyprctl --batch "dispatch setfloating address:0x$window_id; \
|
|
# dispatch resizewindowpixel exact 30% 70%,address:0x$window_id; \
|
|
# dispatch centerwindow address:0x$window_id"
|
|
# ;;
|
|
# esac
|
|
;;
|
|
esac
|
|
done
|
|
;;
|
|
|
|
*)
|
|
echo "Unknown subcommand: $cmd"
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|