From f0d579a03af9e3ba4a7cf64da96c23f485faac7a Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sat, 21 Feb 2026 00:22:44 -0500 Subject: added script to mute active window from Ivy and s1lang On branch development Your branch is up to date with 'origin/development'. Changes to be committed: modified: CHANGELOG.md modified: config/hypr/configs/Keybinds.conf renamed: config/hypr/scripts/SinkIntToggle.sh -> config/hypr/scripts/Toggle-Active-Window-Audio.sh --- config/hypr/scripts/Toggle-Active-Window-Audio.sh | 97 +++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100755 config/hypr/scripts/Toggle-Active-Window-Audio.sh (limited to 'config/hypr/scripts/Toggle-Active-Window-Audio.sh') diff --git a/config/hypr/scripts/Toggle-Active-Window-Audio.sh b/config/hypr/scripts/Toggle-Active-Window-Audio.sh new file mode 100755 index 00000000..44370d10 --- /dev/null +++ b/config/hypr/scripts/Toggle-Active-Window-Audio.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +set -euo pipefail + +XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" +swayIconDir="${XDG_CONFIG_HOME}/swaync/icons" + +#// Credits to sl1ng for the orginal script. Rewritten by Vyle. +ctlcheck=("pactl" "jq" "notify-send" "awk" "pgrep" "hyprctl" "iconv") +missing=() + +for ctl in "${ctlcheck[@]}"; do + command -v "${ctl}" >/dev/null || missing+=("${ctl}") +done + +if (( ${#missing[@]} )) 2>/dev/null; then + echo "Missing required dependencies: \"${missing[*]}\"" + exit 1 +fi + +#// Parse .pid, .class, .title to __pid, __class, __title. +active_json="$(hyprctl -j activewindow 2>/dev/null || { echo -e "Did hyprctl fail to run? [EXIT-CODE:-1]"; exit 1; } )" +PID="$(jq -r '"\(.pid)\t\(.class)\t\(.title)"' <<< "${active_json}" || { echo -e "Did jq fail to run? [EXIT-CODE:-1]"; exit 1; } )" + +IFS=$'\t' read -r __pid __class __title <<< "${PID}" + +[[ -z "${__pid}" ]] && { echo -e "Could not resolve PID for focused window."; exit 1; } +sink_json="$(pactl -f json list sink-inputs 2>/dev/null | iconv -f utf-8 -t utf-8 -c || { echo -e "Did pactl or iconv fail to run? Required manual intervention."; exit 1; } )" + +#// Check if the __pid matches application.process.id or else verify other statements. +mapfile -t sink_ids < <(jq -r --arg pid "${__pid}" --arg class "${__class}" --arg title "${__title}" ' +.[] | + def lc(x): (x // "" | ascii_downcase); + def normalize(x): x | gsub("[-_~.]+";" ") ; + select( + (.properties["application.process.id"] // "") == $pid + or + (lc(.properties["application.name"]) | contains(lc($class))) + or + (lc(.properties["application.id"]) | contains(lc($class))) + or + (lc(.properties["application.process.binary"]) | contains(lc($class))) + or + (normalize(lc(.properties["media.name"])) | contains(normalize(lc($title)))) + ) | .index' <<< "${sink_json}" +) + +if [[ "${#sink_ids[@]}" -eq 0 ]]; then + fallback_pid="$(pgrep -x "${__class}" | head -n 1 || true)" + if [[ -n "${fallback_pid}" ]]; then + mapfile -t sink_ids < <( jq -r --arg pid "${fallback_pid}" '.[] | + select(.properties["application.process.id"] == $pid) | .index' <<< "${sink_json}" ) + fi +fi + +#// Auto-Detect if the environment is on Hyprland or $HYPRLAND_INSTANCE_SIGNATURE. +if [[ ${#sink_ids[@]} -eq 0 ]]; then + if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE}" ]]; then + # Even if the fallback_pid remains empty, we will dispatch exit code based on $HYPRLAND_INSTANCE_SIGNATURE. + notify-send -a "t1" -r 91190 -t 1200 -i "${swayIconDir}/volume-low.png" "No sink input for the active_window: ${__class}" + echo "No sink input for focused window: ${__class}" + exit 1 + else + echo "No sink input for focused active_window ${__class}" + exit 1 + fi +fi + +idsJson=$(printf '%s\n' "${sink_ids[@]}" | jq -s 'map(tonumber)') + +#// Get the available option from pactl. +want_mute=$(jq -r --argjson ids "$idsJson" ' + [ .[] | select(.index as $i | $ids | index($i)) | .mute ] as $m | + if all($m[]; . == true) then "no" + else "yes" + end' <<< "${sink_json}" +) + +if [[ "${want_mute}" == "no" ]]; then + state_msg="Unmuted" + swayIcon="${swayIconDir}/volume-high.png" +else + state_msg="Muted" + swayIcon="${swayIconDir}/volume-mute.png" +fi + +[[ -f "${swayIcon}" ]] || echo -e "Missing swaync icons." + +for id in "${sink_ids[@]}"; do + pactl set-sink-input-mute "$id" "$want_mute" +done + +#// Append pamixer to get a nice result. Pamixer is complete optional here. +if command -v pamixer >/dev/null; then + notify-send -a "t2" -r 91190 -t 800 -i "${swayIcon}" "${state_msg} ${__class}" "$(pamixer --get-default-sink | awk -F '"' 'END{print $(NF - 1)}')" +else + notify-send -a "t2" -r 91190 -t 800 -i "${swayIcon}" "${state_msg} ${__class}" +fi -- cgit v1.2.3 From 888b3fad081d04d624547a09be52afe5d594c0dc Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sat, 21 Feb 2026 16:56:31 -0500 Subject: Fixed muting script to work more consitently and send notifications On branch development Your branch is up to date with 'origin/development'. Changes to be committed: modified: config/hypr/scripts/Toggle-Active-Window-Audio.sh --- config/hypr/scripts/Toggle-Active-Window-Audio.sh | 65 +++++++++++++++++++---- 1 file changed, 56 insertions(+), 9 deletions(-) (limited to 'config/hypr/scripts/Toggle-Active-Window-Audio.sh') diff --git a/config/hypr/scripts/Toggle-Active-Window-Audio.sh b/config/hypr/scripts/Toggle-Active-Window-Audio.sh index 44370d10..6d434c13 100755 --- a/config/hypr/scripts/Toggle-Active-Window-Audio.sh +++ b/config/hypr/scripts/Toggle-Active-Window-Audio.sh @@ -25,14 +25,30 @@ IFS=$'\t' read -r __pid __class __title <<< "${PID}" [[ -z "${__pid}" ]] && { echo -e "Could not resolve PID for focused window."; exit 1; } sink_json="$(pactl -f json list sink-inputs 2>/dev/null | iconv -f utf-8 -t utf-8 -c || { echo -e "Did pactl or iconv fail to run? Required manual intervention."; exit 1; } )" +#// Collect all descendant PIDs for the active window (Chrome/Wayland audio often runs in child processes). +declare -A seen_pids=() +queue=("${__pid}") +all_pids=() +while ((${#queue[@]})); do + pid="${queue[0]}" + queue=("${queue[@]:1}") + [[ -n "${seen_pids[$pid]:-}" ]] && continue + seen_pids["$pid"]=1 + all_pids+=("$pid") + mapfile -t children < <(pgrep -P "$pid" || true) + for child in "${children[@]}"; do + [[ -n "${seen_pids[$child]:-}" ]] || queue+=("$child") + done +done +pidsJson="$(printf '%s\n' "${all_pids[@]}" | jq -s 'map(tonumber)')" -#// Check if the __pid matches application.process.id or else verify other statements. -mapfile -t sink_ids < <(jq -r --arg pid "${__pid}" --arg class "${__class}" --arg title "${__title}" ' +#// Check if any descendant PID matches application.process.id or else verify other statements. +mapfile -t sink_ids < <(jq -r --argjson pids "${pidsJson}" --arg class "${__class}" --arg title "${__title}" ' .[] | def lc(x): (x // "" | ascii_downcase); def normalize(x): x | gsub("[-_~.]+";" ") ; select( - (.properties["application.process.id"] // "") == $pid + (.properties["application.process.id"] | tostring | tonumber? as $p | $p != null and ($pids | index($p))) or (lc(.properties["application.name"]) | contains(lc($class))) or @@ -45,10 +61,25 @@ mapfile -t sink_ids < <(jq -r --arg pid "${__pid}" --arg class "${__class}" --ar ) if [[ "${#sink_ids[@]}" -eq 0 ]]; then - fallback_pid="$(pgrep -x "${__class}" | head -n 1 || true)" - if [[ -n "${fallback_pid}" ]]; then - mapfile -t sink_ids < <( jq -r --arg pid "${fallback_pid}" '.[] | - select(.properties["application.process.id"] == $pid) | .index' <<< "${sink_json}" ) + mapfile -t fallback_pids < <(pgrep -x "${__class}" || true) + if [[ "${#fallback_pids[@]}" -gt 0 ]]; then + declare -A seen_fallback=() + queue=("${fallback_pids[@]}") + all_fallback=() + while ((${#queue[@]})); do + pid="${queue[0]}" + queue=("${queue[@]:1}") + [[ -n "${seen_fallback[$pid]:-}" ]] && continue + seen_fallback["$pid"]=1 + all_fallback+=("$pid") + mapfile -t children < <(pgrep -P "$pid" || true) + for child in "${children[@]}"; do + [[ -n "${seen_fallback[$child]:-}" ]] || queue+=("$child") + done + done + fallbackJson="$(printf '%s\n' "${all_fallback[@]}" | jq -s 'map(tonumber)')" + mapfile -t sink_ids < <( jq -r --argjson pids "${fallbackJson}" '.[] | + select((.properties["application.process.id"] | tostring | tonumber? as $p | $p != null and ($pids | index($p)))) | .index' <<< "${sink_json}" ) fi fi @@ -85,13 +116,29 @@ fi [[ -f "${swayIcon}" ]] || echo -e "Missing swaync icons." +changed=0 +failed_ids=() for id in "${sink_ids[@]}"; do - pactl set-sink-input-mute "$id" "$want_mute" + if pactl set-sink-input-mute "$id" "$want_mute"; then + changed=1 + else + failed_ids+=("$id") + fi done +if [[ "$changed" -eq 0 ]]; then + notify-send -a "t2" -r 91190 -t 1200 -i "${swayIconDir}/volume-low.png" "Failed to change sink input(s)" "${failed_ids[*]:-unknown}" + exit 1 +fi + #// Append pamixer to get a nice result. Pamixer is complete optional here. if command -v pamixer >/dev/null; then - notify-send -a "t2" -r 91190 -t 800 -i "${swayIcon}" "${state_msg} ${__class}" "$(pamixer --get-default-sink | awk -F '"' 'END{print $(NF - 1)}')" + sink_name="$(pamixer --get-default-sink 2>/dev/null | awk -F '"' 'END{print $(NF - 1)}' 2>/dev/null || true)" + if [[ -n "${sink_name}" ]]; then + notify-send -a "t2" -r 91190 -t 800 -i "${swayIcon}" "${state_msg} ${__class}" "${sink_name}" + else + notify-send -a "t2" -r 91190 -t 800 -i "${swayIcon}" "${state_msg} ${__class}" + fi else notify-send -a "t2" -r 91190 -t 800 -i "${swayIcon}" "${state_msg} ${__class}" fi -- cgit v1.2.3 From 023bc847111881bf19adcfbe92792d66f0cf7513 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sat, 21 Feb 2026 17:05:06 -0500 Subject: Added check for pactl in Toggle-Active-Window-Audio.sh On branch development Your branch is up to date with 'origin/development'. Changes to be committed: modified: config/hypr/scripts/Toggle-Active-Window-Audio.sh --- config/hypr/scripts/Toggle-Active-Window-Audio.sh | 3 +++ 1 file changed, 3 insertions(+) (limited to 'config/hypr/scripts/Toggle-Active-Window-Audio.sh') diff --git a/config/hypr/scripts/Toggle-Active-Window-Audio.sh b/config/hypr/scripts/Toggle-Active-Window-Audio.sh index 6d434c13..4d9bcd33 100755 --- a/config/hypr/scripts/Toggle-Active-Window-Audio.sh +++ b/config/hypr/scripts/Toggle-Active-Window-Audio.sh @@ -13,6 +13,9 @@ for ctl in "${ctlcheck[@]}"; do done if (( ${#missing[@]} )) 2>/dev/null; then + if printf '%s\n' "${missing[@]}" | grep -qx "pactl"; then + notify-send -a "t1" -r 91190 -t 2000 -i "${swayIconDir}/volume-low.png" "ERROR: pactl not installed" "Install 'pactl' (pulseaudio-utils or pipewire-pulse)." + fi echo "Missing required dependencies: \"${missing[*]}\"" exit 1 fi -- cgit v1.2.3