diff options
Diffstat (limited to 'config/hypr/scripts/Tak0-Per-Window-Switch.sh')
| -rwxr-xr-x | config/hypr/scripts/Tak0-Per-Window-Switch.sh | 173 |
1 files changed, 114 insertions, 59 deletions
diff --git a/config/hypr/scripts/Tak0-Per-Window-Switch.sh b/config/hypr/scripts/Tak0-Per-Window-Switch.sh index cc878615..a058a136 100755 --- a/config/hypr/scripts/Tak0-Per-Window-Switch.sh +++ b/config/hypr/scripts/Tak0-Per-Window-Switch.sh @@ -7,24 +7,24 @@ # ================================================== ################################################################## # # -# # # TAK_0'S Per-Window-Switch # # # -# # -# # # Just a little script that I made to switch keyboard layouts # # per-window instead of global switching for the more # # smooth and comfortable workflow. # # # ################################################################## -MAP_FILE="$HOME/.cache/kb_layout_per_window" -ICON="${XDG_CONFIG_HOME:-$HOME/.config}/swaync/images/ja.png" +set -uo pipefail + +MAP_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/kb_layout_per_window" +notif="${XDG_CONFIG_HOME:-$HOME/.config}/swaync/images" +icons_dir="${XDG_CONFIG_HOME:-$HOME/.config}/swaync/icons" SCRIPT_NAME="$(basename "$0")" SCRIPT_PATH="$(readlink -f "$0")" # Detect active Hyprland config mode -config_home="${XDG_CONFIG_HOME:-${XDG_CONFIG_HOME:-$HOME/.config}}" +config_home="${XDG_CONFIG_HOME:-$HOME/.config}" hypr_dir="$config_home/hypr" lua_entry="$hypr_dir/hyprland.lua" legacy_lua_entry="$config_home/hyprland.lua" @@ -35,70 +35,93 @@ else hypr_config_mode="conf" fi -# Ensure map file exists +# Pick notification icon +if [[ -f "$icons_dir/keyboard.png" ]]; then + ICON="$icons_dir/keyboard.png" +elif [[ -f "$notif/ja.png" ]]; then + ICON="$notif/ja.png" +else + ICON="$notif/note.png" +fi + +# Ensure cache directory and map file exist +mkdir -p "$(dirname "$MAP_FILE")" touch "$MAP_FILE" -# Function to get layouts from config files +# Function to get layouts (queries live Hyprland option first, then config files) get_layouts() { local layouts="" - if [[ "$hypr_config_mode" == "lua" ]]; then - local lua_user="$hypr_dir/UserConfigs/user_settings.lua" - local lua_sys="$hypr_dir/configs/system_settings.lua" - local lua_legacy_sys="$hypr_dir/UserConfigs/system_settings.lua" - local lua_pristine_sys="$hypr_dir/lua/settings.lua" - if [[ -f "$lua_user" ]] && grep -q 'kb_layout' "$lua_user" 2>/dev/null; then - layouts=$(grep 'kb_layout' "$lua_user" | sed -n 's/.*kb_layout\s*=\s*"\([^"]*\)".*/\1/p' | head -n1) - elif [[ -f "$lua_sys" ]] && grep -q 'kb_layout' "$lua_sys" 2>/dev/null; then - layouts=$(grep 'kb_layout' "$lua_sys" | sed -n 's/.*kb_layout\s*=\s*"\([^"]*\)".*/\1/p' | head -n1) - elif [[ -f "$lua_legacy_sys" ]] && grep -q 'kb_layout' "$lua_legacy_sys" 2>/dev/null; then - layouts=$(grep 'kb_layout' "$lua_legacy_sys" | sed -n 's/.*kb_layout\s*=\s*"\([^"]*\)".*/\1/p' | head -n1) - elif [[ -f "$lua_pristine_sys" ]] && grep -q 'kb_layout' "$lua_pristine_sys" 2>/dev/null; then - layouts=$(grep 'kb_layout' "$lua_pristine_sys" | sed -n 's/.*kb_layout\s*=\s*"\([^"]*\)".*/\1/p' | head -n1) - fi - else - local conf_user="$hypr_dir/UserConfigs/UserSettings.conf" - local conf_sys="$hypr_dir/configs/SystemSettings.conf" - if [[ -f "$conf_user" ]] && grep -q 'kb_layout' "$conf_user" 2>/dev/null; then - layouts=$(grep 'kb_layout' "$conf_user" | cut -d '=' -f2 | tr -d '[:space:]' | head -n1) - elif [[ -f "$conf_sys" ]] && grep -q 'kb_layout' "$conf_sys" 2>/dev/null; then - layouts=$(grep 'kb_layout' "$conf_sys" | cut -d '=' -f2 | tr -d '[:space:]' | head -n1) + + # 1. Primary: Live option from active Hyprland session (fast, authoritative in both Lua & conf modes) + layouts=$(hyprctl -j getoption input:kb_layout 2>/dev/null | jq -r '.str // empty' || true) + + # 2. Secondary: Query keyboards directly from devices JSON + if [[ -z "$layouts" || "$layouts" == "null" ]]; then + layouts=$(hyprctl -j devices 2>/dev/null | jq -r '[.keyboards[].layout | select(. != null and . != "")] | first // empty' || true) + fi + + # 3. Fallback: Parse configuration files + if [[ -z "$layouts" || "$layouts" == "null" ]]; then + if [[ "$hypr_config_mode" == "lua" ]]; then + local lua_user="$hypr_dir/UserConfigs/user_settings.lua" + local lua_sys="$hypr_dir/configs/system_settings.lua" + local lua_legacy_sys="$hypr_dir/UserConfigs/system_settings.lua" + local lua_pristine_sys="$hypr_dir/lua/settings.lua" + if [[ -f "$lua_user" ]] && grep -q 'kb_layout' "$lua_user" 2>/dev/null; then + layouts=$(grep 'kb_layout' "$lua_user" | sed -n "s/.*kb_layout[[:space:]]*=[[:space:]]*[\"']\([^\"']*\)[\"'].*/\1/p" | head -n1) + elif [[ -f "$lua_sys" ]] && grep -q 'kb_layout' "$lua_sys" 2>/dev/null; then + layouts=$(grep 'kb_layout' "$lua_sys" | sed -n "s/.*kb_layout[[:space:]]*=[[:space:]]*[\"']\([^\"']*\)[\"'].*/\1/p" | head -n1) + elif [[ -f "$lua_legacy_sys" ]] && grep -q 'kb_layout' "$lua_legacy_sys" 2>/dev/null; then + layouts=$(grep 'kb_layout' "$lua_legacy_sys" | sed -n "s/.*kb_layout[[:space:]]*=[[:space:]]*[\"']\([^\"']*\)[\"'].*/\1/p" | head -n1) + elif [[ -f "$lua_pristine_sys" ]] && grep -q 'kb_layout' "$lua_pristine_sys" 2>/dev/null; then + layouts=$(grep 'kb_layout' "$lua_pristine_sys" | sed -n "s/.*kb_layout[[:space:]]*=[[:space:]]*[\"']\([^\"']*\)[\"'].*/\1/p" | head -n1) + fi + else + local conf_user="$hypr_dir/UserConfigs/UserSettings.conf" + local conf_sys="$hypr_dir/configs/SystemSettings.conf" + if [[ -f "$conf_user" ]] && grep -q 'kb_layout' "$conf_user" 2>/dev/null; then + layouts=$(grep 'kb_layout' "$conf_user" | cut -d '=' -f2 | tr -d '[:space:]' | head -n1) + elif [[ -f "$conf_sys" ]] && grep -q 'kb_layout' "$conf_sys" 2>/dev/null; then + layouts=$(grep 'kb_layout' "$conf_sys" | cut -d '=' -f2 | tr -d '[:space:]' | head -n1) + fi fi fi - echo "$layouts" | tr ',' ' ' + + echo "${layouts:-us}" | tr ',' ' ' } raw_layouts=$(get_layouts) if [[ -z "$raw_layouts" ]]; then - echo "Error: cannot find kb_layout in configuration files." >&2 + echo "Error: cannot find kb_layout in configuration files or active session." >&2 exit 1 fi kb_layouts=($raw_layouts) count=${#kb_layouts[@]} -# Get current active window ID +# Get current active window ID/address get_win() { - hyprctl activewindow -j | jq -r '.address // .id' + hyprctl activewindow -j 2>/dev/null | jq -r '.address // .id // empty' } -# Get available keyboards +# Get available keyboard names get_keyboards() { - hyprctl devices -j | jq -r '.keyboards[].name' + hyprctl devices -j 2>/dev/null | jq -r '.keyboards[].name // empty' } # Save window-specific layout save_map() { local W=$1 L=$2 - grep -v "^${W}:" "$MAP_FILE" > "$MAP_FILE.tmp" 2>/dev/null + grep -v "^${W}:" "$MAP_FILE" > "$MAP_FILE.tmp" 2>/dev/null || true echo "${W}:${L}" >> "$MAP_FILE.tmp" mv "$MAP_FILE.tmp" "$MAP_FILE" } -# Load layout for window (fallback to default) +# Load layout for window (fallback to default layout) load_map() { local W=$1 local E - E=$(grep "^${W}:" "$MAP_FILE" 2>/dev/null) + E=$(grep "^${W}:" "$MAP_FILE" 2>/dev/null || true) [[ -n "$E" ]] && echo "${E#*:}" || echo "${kb_layouts[0]}" } @@ -106,34 +129,50 @@ load_map() { do_switch() { local IDX=$1 for kb in $(get_keyboards); do - hyprctl switchxkblayout "$kb" "$IDX" >/dev/null 2>&1 + hyprctl switchxkblayout "$kb" "$IDX" >/dev/null 2>&1 || true done } # Toggle layout for current window only cmd_toggle() { - local W=$(get_win) + local W + W=$(get_win) [[ -z "$W" || "$W" == "null" ]] && return - local CUR=$(load_map "$W") + + local CUR + CUR=$(load_map "$W") local i=0 local NEXT + for idx in "${!kb_layouts[@]}"; do if [[ "${kb_layouts[idx]}" == "$CUR" ]]; then i=$idx break fi done - NEXT=$(( (i+1) % count )) + + NEXT=$(( (i + 1) % count )) do_switch "$NEXT" save_map "$W" "${kb_layouts[NEXT]}" - notify-send -u low -i "$ICON" "kb_layout: ${kb_layouts[NEXT]}" + + if command -v notify-send >/dev/null 2>&1 && [[ -n "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ]]; then + notify-send -e \ + -h string:x-canonical-private-synchronous:kb_layout_notif \ + -h boolean:SWAYNC_BYPASS_DND:true \ + -u low \ + -i "$ICON" \ + " Keyboard Layout (Per-Window)" " ${kb_layouts[NEXT]}" 2>/dev/null || true + fi } # Restore layout on focus cmd_restore() { - local W=$(get_win) + local W + W=$(get_win) [[ -z "$W" || "$W" == "null" ]] && return - local LAY=$(load_map "$W") + + local LAY + LAY=$(load_map "$W") for idx in "${!kb_layouts[@]}"; do if [[ "${kb_layouts[idx]}" == "$LAY" ]]; then do_switch "$idx" @@ -144,38 +183,54 @@ cmd_restore() { # Listen to focus events and restore window-specific layouts subscribe() { - local SOCKET2="$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock" + local runtime_dir="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" + local SOCKET2="" + + if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then + SOCKET2="$runtime_dir/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock" + fi + if [[ ! -S "$SOCKET2" ]]; then - # Fallback if HYPRLAND_INSTANCE_SIGNATURE is not set correctly in this subshell - local sig=$(hyprctl instances -j | jq -r '.[0].instance' 2>/dev/null) - SOCKET2="$XDG_RUNTIME_DIR/hypr/$sig/.socket2.sock" + local sig + sig=$(hyprctl instances -j 2>/dev/null | jq -r '.[0].instance // empty') + [[ -n "$sig" ]] && SOCKET2="$runtime_dir/hypr/$sig/.socket2.sock" fi - [[ -S "$SOCKET2" ]] || { + if [[ ! -S "$SOCKET2" ]]; then echo "Error: Hyprland socket not found." >&2 exit 1 - } + fi - socat -u UNIX-CONNECT:"$SOCKET2" - | while read -r line; do + if ! command -v socat >/dev/null 2>&1; then + echo "Error: socat is required for socket listening." >&2 + exit 1 + fi + + socat -u UNIX-CONNECT:"$SOCKET2" - 2>/dev/null | while read -r line; do if [[ "$line" =~ ^activewindow ]]; then - cmd_restore + cmd_restore fi done } # CLI -case "$1" in +case "${1:-toggle}" in --listener) subscribe ;; + status) + W=$(get_win) + load_map "$W" + ;; toggle|"") - # Ensure only one listener - if ! pgrep -f "$SCRIPT_NAME.*--listener" >/dev/null; then + # Ensure only one listener is active + if ! pgrep -f "$SCRIPT_NAME.*--listener" >/dev/null 2>&1; then "$SCRIPT_PATH" --listener & fi - cmd_toggle + cmd_toggle ;; - *) - echo "Usage: $SCRIPT_NAME [toggle]" >&2; exit 1 + *) + echo "Usage: $SCRIPT_NAME [toggle|status]" >&2 + exit 1 ;; esac |
