#!/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 < [app] [args...] Commands: volume {up,down,toggle} Adjust volume accordingly and notify brightness {up,down} Adjust brightness accordingly and notify spawnfocus [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 ;; brightness) require brightnessctl notify-send BRIGHT_STEP=5 case "$1" in up) brightnessctl set "$BRIGHT_STEP"%+ ;; down) brightnessctl set "$BRIGHT_STEP"%- ;; *) echo "Invalid subcommand: $1" >&2; exit 1 ;; esac pct=$(awk -v cur="$(brightnessctl get)" -v max="$(brightnessctl max)" 'BEGIN { printf "%d", cur * 100 / max }') filled=$((pct / 5)) empty=$((20 - filled)) bar="" i=0; while [ "$i" -lt "$filled" ]; do bar="${bar}━"; i=$((i + 1)); done i=0; while [ "$i" -lt "$empty" ]; do bar="${bar}─"; i=$((i + 1)); done notify-send -a hypr -t 2500 -r 5555 "☼ $bar" ;; volume) require wpctl notify-send SINK="@DEFAULT_AUDIO_SINK@" VOL_STEP=5 get_vol() { wpctl get-volume "$SINK" | awk '{printf "%d", $2 * 100}'; } case "$1" in up) wpctl set-volume "$SINK" "${VOL_STEP}%+" --limit 1.0 ;; down) wpctl set-volume "$SINK" "${VOL_STEP}%-" ;; toggle) wpctl set-mute "$SINK" toggle ;; *) echo "Invalid subcommand: $1" >&2 exit 1 ;; esac vol=$(get_vol) filled=$((vol / 5)) empty=$((20 - filled)) bar="" i=0; while [ "$i" -lt "$filled" ]; do bar="${bar}━"; i=$((i + 1)); done i=0; while [ "$i" -lt "$empty" ]; do bar="${bar}─"; i=$((i + 1)); done if wpctl get-volume "$SINK" | grep -q MUTED; then icon="󰖁" elif [ "$vol" -le 33 ]; then icon="󰕿" elif [ "$vol" -le 66 ]; then icon="󰖀" else icon="󰕾" fi notify-send -a hypr -t 2500 -r 5556 "$icon $bar" ;; pull) require hyprctl jq fuzzel APP="$1" case "$APP" in google-chrome | google-chrome-stable) CLASS="google-chrome" ;; zen | zen-browser) CLASS="zen" ;; chromium | ungoogled-chromium) CLASS="Chromium" ;; 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 | ungoogled-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