diff options
| author | --global <ik.boikov.public@protonmail.com> | 2021-12-19 12:21:17 +0100 |
|---|---|---|
| committer | --global <ik.boikov.public@protonmail.com> | 2021-12-19 12:21:17 +0100 |
| commit | 628e19315b1849d7eb8c46c3ea88701ff71514b3 (patch) | |
| tree | fa453e4790225fc2b2dcdbb18456317212b426c2 /scripts | |
| parent | 04440b45c6c8d60863312740a05b04bc9b6b6290 (diff) | |
movving scripts around
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/.local/bin/tablet/osu (renamed from scripts/.local/bin/tabletosu) | 0 | ||||
| -rwxr-xr-x | scripts/.local/bin/tablet/pad (renamed from scripts/.local/bin/tabletpad) | 0 | ||||
| -rwxr-xr-x | scripts/.local/bin/tablet/reset (renamed from scripts/.local/bin/tabletreset) | 0 | ||||
| -rwxr-xr-x | scripts/.local/bin/tablet/scale (renamed from scripts/.local/bin/tabletscale) | 0 | ||||
| -rwxr-xr-x | scripts/.local/bin/termcolors | 9 | ||||
| -rwxr-xr-x | scripts/.local/bin/volume | 1273 |
6 files changed, 0 insertions, 1282 deletions
diff --git a/scripts/.local/bin/tabletosu b/scripts/.local/bin/tablet/osu index ef9e319..ef9e319 100755 --- a/scripts/.local/bin/tabletosu +++ b/scripts/.local/bin/tablet/osu diff --git a/scripts/.local/bin/tabletpad b/scripts/.local/bin/tablet/pad index a056d19..a056d19 100755 --- a/scripts/.local/bin/tabletpad +++ b/scripts/.local/bin/tablet/pad diff --git a/scripts/.local/bin/tabletreset b/scripts/.local/bin/tablet/reset index 3f68711..3f68711 100755 --- a/scripts/.local/bin/tabletreset +++ b/scripts/.local/bin/tablet/reset diff --git a/scripts/.local/bin/tabletscale b/scripts/.local/bin/tablet/scale index c405048..c405048 100755 --- a/scripts/.local/bin/tabletscale +++ b/scripts/.local/bin/tablet/scale diff --git a/scripts/.local/bin/termcolors b/scripts/.local/bin/termcolors deleted file mode 100755 index f72e18d..0000000 --- a/scripts/.local/bin/termcolors +++ /dev/null @@ -1,9 +0,0 @@ -for x in {0..8}; do - for i in {30..37}; do - for a in {40..47}; do - echo -ne "\e[$x;$i;$a""m\\\e[$x;$i;$a""m\e[0;37;40m " - done - echo - done -done -echo "" diff --git a/scripts/.local/bin/volume b/scripts/.local/bin/volume deleted file mode 100755 index 4593733..0000000 --- a/scripts/.local/bin/volume +++ /dev/null @@ -1,1273 +0,0 @@ -#!/bin/bash -# -# i3-volume -# -# Volume control and volume notifications. -# -# Dependencies: -# awk (POSIX compatible) -# pulseaudio-utils - if using PulseAudio -# alsa-utils - if using amixer -# -# Copyright (c) 2016 Beau Hastings. All rights reserved. -# License: GNU General Public License v2 -# -# Author: Beau Hastings <beau@saweet.net> -# URL: https://github.com/hastinbe/i3-volume -# Wiki: https://github.com/hastinbe/i3-volume/wiki/ - -define_helpers() { - empty() { - [[ -z $1 ]] - } - - not_empty() { - [[ -n $1 ]] - } - - isset() { - [[ -v $1 ]] - } - - command_exists() { - command -v "$1" >/dev/null 2>&1; - } - - error() { - echo "$COLOR_RED$*$COLOR_RESET" - } - - has_color() { - (( $(tput colors 2>/dev/null || echo 0) >= 8 )) && [ -t 1 ] - } - - # Converts milliseconds to seconds with rounding up - # - # Arguments: - # milliseconds (integer) An integer in milliseconds - ms_to_secs() { - echo $(( ($1 + (1000 - 1)) / 1000 )) - } - - is_command_hookable() { - ! [[ ${POST_HOOK_EXEMPT_COMMANDS[*]} =~ $1 ]] - } - - has_capability() { - [[ "${NOTIFY_CAPS[*]}" =~ $1 ]] - } - - max() { - echo $(( $1 > $2 ? $1 : $2 )) - } -} - -define_notify() { - # Display a notification indicating muted or current volume. - notify_volume() { - local -r vol=$(get_volume) - local icon - - if is_muted; then - text="Volume muted" - - if $USE_FULLCOLOR_ICONS; then - icon=${ICONS[0]} - else - icon=${ICONS_SYMBOLIC[0]} - fi - else - printf -v text "Volume %3s%%" "$vol" - - icon=$(get_volume_icon "$vol") - - if $SHOW_VOLUME_PROGRESS; then - local -r progress=$(progress_bar "$vol") - text="$text $progress" - fi - fi - - case "$NOTIFICATION_METHOD" in - xosd ) notify_volume_xosd "$vol" "$text" ;; - herbe ) notify_volume_herbe "$text" ;; - volnoti ) notify_volume_volnoti "$vol" ;; - kosd ) notify_volume_kosd "$vol" ;; - dunst ) notify_volume_libnotify "$vol" "$icon" "$text" ;; - notify-osd ) notify_volume_libnotify "$vol" "$icon" "$text" ;; - libnotify ) notify_volume_libnotify "$vol" "$icon" "$text" ;; - haskell-notification-daemon) notify_volume_libnotify "$vol" "$icon" "$text" ;; - * ) notify_volume_libnotify "$vol" "$icon" "$text" ;; - esac - } - - list_notification_methods() { - awk -W posix 'match($0,/ notify_volume_([[:alnum:]]+)/) {print substr($0, 19, RLENGTH-18)}' "${BASH_SOURCE[0]}" || exit "$EX_USAGE" - exit "$EX_OK" - } - - setup_notification_icons() { - if not_empty "$SYMBOLIC_ICON_SUFFIX"; then - apply_symbolic_icon_suffix - fi - } - - show_volume_notification() { - $DISPLAY_NOTIFICATIONS || return - - if empty "$NOTIFICATION_METHOD"; then - load_notify_server_info - NOTIFICATION_METHOD=$NOTIFY_SERVER - fi - - setup_notification_icons - notify_volume - } - - # Loads notification system information via DBus - load_notify_server_info() { - command_exists dbus-send || return - IFS=$'\t' read -r NOTIFY_SERVER _ _ _ < <(dbus-send --print-reply --dest=org.freedesktop.Notifications /org/freedesktop/Notifications org.freedesktop.Notifications.GetServerInformation | awk 'BEGIN { ORS="\t" }; match($0, /^ string ".*"/) {print substr($0, RSTART+11, RLENGTH-12)}') - } - - # Load notification system capabilities via DBus - load_notify_server_caps() { - command_exists dbus-send || return - IFS= read -r -d '' -a NOTIFY_CAPS < <(dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_FDN}.GetCapabilities" | awk 'RS=" " { if (NR > 2) print $1 }') - } - - # Send notifcation for libnotify-compatible notification daemons. - # - # Arguments: - # Volume (integer) An integer indicating the volume. - # Icon (string) Icon to display. - # Text (string) Notification text. - notify_volume_libnotify() { - local -r vol=$1 - local -r icon=$2 - local -r text=${*:3} - local -a args=( - -t "$EXPIRES" - ) - local -a hints=( - # Replaces previous notification in some notification servers - string:synchronous:volume - # Replaces previous notification in NotifyOSD - string:x-canonical-private-synchronous:volume - ) - - # If we're not drawing our own progress bar, allow the notification daemon to draw its own (if supported) - if ! $SHOW_VOLUME_PROGRESS; then - hints+=(int:value:"$vol") - fi - - (( ${#NOTIFY_CAPS[@]} < 1 )) && load_notify_server_caps - - if has_capability icon-static; then - args+=(-i "$icon") - - # haskell-notification-daemon (aka deadd-notification-center) does not support -i|--icon - hints+=(string:image-path:"$icon") - fi - - if $PLAY_SOUND && has_capability sound; then - hints+=(string:sound-name:audio-volume-change) - fi - - if ! isset NO_NOTIFY_COLOR && [[ $NOTIFICATION_METHOD == "dunst" ]]; then - if is_muted; then - hints+=(string:fgcolor:"$COLOR_MUTED") - else - hints+=(string:fgcolor:"$(volume_color "$vol")") - fi - fi - - if $USE_DUNSTIFY; then - args+=(-r 1000) - - # Transient notifications will bypass the idle_threshold setting. - # Should be boolean, but Notify-OSD doesn't support boolean yet. Dunst checks - # for int and bool with transient so lets play nice with both servers. - hints+=(int:transient:1) - - read -ra hints <<< "${hints[@]/#/-h }" - "${DUNSTIFY_PATH:+${DUNSTIFY_PATH%/}/}dunstify" "${hints[@]}" "${args[@]}" "$text" - elif isset USE_NOTIFY_SEND_PY; then - # Replaces previous notification, but leaves itself running in the bg to work - args+=(--replaces-process volume) - - # By-pass the server's persistence capability, if it should exist - hints+=(boolean:transient:true) - - "${NOTIFY_SEND_PATH:+${NOTIFY_SEND_PATH%/}/}notify-send.py" --hint "${hints[@]}" "${args[@]}" "$text" & - else - read -ra hints <<< "${hints[@]/#/-h }" - "${NOTIFY_SEND_PATH:+${NOTIFY_SEND_PATH%/}/}notify-send" "${hints[@]}" "${args[@]}" "$text" - fi - } - - # Send notification to XOSD. - # - # Arguments: - # Volume (integer) An integer indicating the volume. - # Text (string) Notification text. - notify_volume_xosd() { - local -r vol=$1 - local -r text=${*:2} - local -r delay=$(ms_to_secs "$EXPIRES") - local percentage - - if is_muted; then - color=$COLOR_MUTED - percentage=0 - else - color=$(volume_color "$vol") - percentage=$vol - fi - - "${XOSD_PATH:+${XOSD_PATH%/}/}osd_cat" --align center -b percentage -P "$percentage" -d "$delay" -p top -A center -c "$color" -T "$text" -O 2 -u "$COLOR_XOSD_OUTLINE" & disown - } - - # Send notification to herbe. - # - # Arguments: - # Text (string) Notification text. - # - # Note: a patch with a notify-send script for herbe, not in the current version at this - # time but would make this irrelevant. See https://github.com/dudik/herbe/pull/10 - notify_volume_herbe() { - local -r text=$* - - # Dismiss existing/pending notifications to prevent queuing - pkill -SIGUSR1 herbe - - "${HERBE_PATH:+${HERBE_PATH%/}/}herbe" "$text" & disown - } - - # Send notification to volnoti. - # - # Arguments: - # Volume (integer) An integer indicating the volume. - notify_volume_volnoti() { - local -r vol=$1 - - if is_muted; then - "${VOLNOTI_PATH:+${VOLNOTI_PATH%/}/}volnoti-show" -m "$vol" - else - "${VOLNOTI_PATH:+${VOLNOTI_PATH%/}/}volnoti-show" "$vol" - fi - } - - # Send notification to KOSD. - # - # Arguments: - # Volume (integer) An integer indicating the volume. - notify_volume_kosd() { - local -r vol=$1 - - if is_muted; then - qdbus org.kde.kded /modules/kosd showVolume "$vol" 1 - else - qdbus org.kde.kded /modules/kosd showVolume "$vol" 0 - fi - } -} - -define_output_formats() { - # Outputs the current volume in the default format. - output_volume_default() { - if is_muted; then - echo MUTE - else - echo "$(get_volume)%" - fi - } - - # Outputs the current volume using a custom format string. - # - # Format options: - # %v = volume percentage or "MUTE" when muted - # %s = sink name (PulseAudio only) - # %c = card (alsamixer only) - # %m = mixer (alsamixer only) - # %p = volume progress bar - # %i = volume icon - output_volume_custom() { - local -r format=$* - local -r vol=$(get_volume) - local string - - if is_muted; then - string=${format//\%v/MUTE} - else - string=${format//\%v/$vol%} - fi - - string=${string//\%s/$SINK} - string=${string//\%c/$CARD} - string=${string//\%m/$MIXER} - string=${string//\%p/$(progress_bar "$vol")} - string=${string//\%i/$(get_volume_emoji "$vol")} - - echo -ne "$string" - } - - # Outputs the current volume for i3blocks. - output_volume_i3blocks() { - local short_text - local full_text - - if is_muted; then - short_text="<span color=\"$COLOR_MUTED\">MUTE</span>\n" - full_text="<span color=\"$COLOR_MUTED\">MUTE</span>\n" - else - local -r vol=$(get_volume) - local -r color=$(volume_color "$vol") - - short_text="<span color=\"$color\">${vol}%</span>\n" - full_text="<span color=\"$color\">${vol}%</span>\n" - - if isset MAX_VOLUME && (( vol > MAX_VOLUME )); then - EXITCODE=$EX_URGENT - fi - fi - - echo -ne "$full_text$short_text" - } - - # Outputs the current volume for xob. - output_volume_xob() { - local -ir vol=$(get_volume) - - if is_muted; then - echo "${vol}!" - else - echo "$vol" - fi - } -} - -define_commands() { - # Increase volume relative to current volume. - # - # Arguments: - # Step (integer) Percentage to increase by. - # Max Volume (optional) (integer|percentage) Maximum volume limit. - increase_volume() { - local step=${1:?$(error 'Step is required')} - local -r max_volume=$2 - - if not_empty "$max_volume"; then - local -r vol=$(get_volume) - - if (( vol + step > max_volume )); then - # Instead of doing nothing, step to max_volume - step=$( max "0" "$(( max_volume - vol ))" ) - fi - fi - - if $USE_AMIXER; then - amixer_increase_volume "$CARD" "$step" - else - pa_increase_volume "$SINK" "$step" - fi - } - - # Decrease volume relative to current volume. - # - # Arguments: - # Step (integer) Percentage to decrease by. - decrease_volume() { - local -r step=${1:?$(error 'Step is required')} - - if $USE_AMIXER; then - amixer_decrease_volume "$CARD" "$step" - else - pa_decrease_volume "$SINK" "$step" - fi - } - - # Set volume. - # - # Arguments: - # Volume (integer|linear factor|percentage|decibel) - # Max Volume (optional) (integer|percentage) Maximum volume limit. - set_volume() { - local -r vol=${1:?$(error 'Volume is required')} - local -r max_volume=$2 - - if not_empty "$max_volume" && (( vol > max_volume )); then - return - fi - - if $USE_AMIXER; then - amixer_set_volume "${vol}%" "$CARD" - else - pa_set_volume "$SINK" "${vol}%" - fi - } - - toggle_mute() { - if $USE_AMIXER; then - amixer_toggle_mute "$CARD" - else - pa_toggle_mute "$SINK" - fi - } - - # Outputs the current volume. - # - # Arguments - # Output method (string) Method to use to output volume. - output_volume() { - local -r for=${1:?$(error 'Output method is required')} - - case "$for" in - i3blocks ) output_volume_i3blocks ;; - xob ) output_volume_xob ;; - default ) output_volume_default ;; - * ) output_volume_custom "$*" ;; - esac - } - - list_output_formats() { - awk -W posix 'match($0,/ output_volume_([[:alnum:]]+)/) {print substr($0, 19, RLENGTH-18)}' "${BASH_SOURCE[0]}" || exit "$EX_USAGE" - exit "$EX_OK" - } - - usage() { - cat <<- EOF 1>&2 -${COLOR_YELLOW}Usage:${COLOR_RESET} $0 [<options>] <command> [<args>] -Control volume and related notifications. - -${COLOR_YELLOW}Commands:${COLOR_RESET} - ${COLOR_GREEN}up <value>${COLOR_RESET} increase volume - ${COLOR_GREEN}down <value>${COLOR_RESET} decrease volume - ${COLOR_GREEN}set <value>${COLOR_RESET} set volume - ${COLOR_GREEN}mute${COLOR_RESET} toggle mute - ${COLOR_GREEN}listen${COLOR_RESET} listen for changes to a PulseAudio sink - ${COLOR_GREEN}output <format>${COLOR_RESET} output volume in a supported format - custom format substitutions: - %v = volume - %s = sink name (PulseAudio only) - %c = card (alsamixer only) - %m = mixer (alsamixer only) - %p = volume progress bar - %i = volume icon/emoji - - examples: - "Volume is %v" = Volume is 50% - "%i %v %p \n" = 奔 50% ██████████ - ${COLOR_GREEN}outputs${COLOR_RESET} show available output formats - ${COLOR_GREEN}notifications${COLOR_RESET} show available notification methods - ${COLOR_GREEN}help${COLOR_RESET} display help - -${COLOR_YELLOW}Options:${COLOR_RESET} - ${COLOR_GREEN}-a${COLOR_RESET} use amixer - ${COLOR_GREEN}-n${COLOR_RESET} enable notifications - ${COLOR_GREEN}-C${COLOR_RESET} use libcanberra for playing event sounds - ${COLOR_GREEN}-P${COLOR_RESET} play sound for volume changes - ${COLOR_GREEN}-j <muted,high,low,medium>${COLOR_RESET} specify custom volume emojis as a comma separated list - ${COLOR_GREEN}-t <process_name>${COLOR_RESET} process name of status bar (${COLOR_MAGENTA}requires -u${COLOR_RESET}) - ${COLOR_GREEN}-u <signal>${COLOR_RESET} signal to update status bar (${COLOR_MAGENTA}requires -t${COLOR_RESET}) - ${COLOR_GREEN}-x <value>${COLOR_RESET} maximum volume - ${COLOR_GREEN}-X <value>${COLOR_RESET} maximum amplification; if supported (${COLOR_MAGENTA}default: 2${COLOR_RESET}) - ${COLOR_GREEN}-h${COLOR_RESET} display help - -${COLOR_YELLOW}amixer Options:${COLOR_RESET} - ${COLOR_GREEN}-c <card>${COLOR_RESET} card number to control - ${COLOR_GREEN}-m <mixer>${COLOR_RESET} set mixer (${COLOR_MAGENTA}default: Master${COLOR_RESET}) - -${COLOR_YELLOW}PulseAudio Options:${COLOR_RESET} - ${COLOR_GREEN}-s <sink>${COLOR_RESET} symbolic name of sink - -${COLOR_YELLOW}Notification Options:${COLOR_RESET} - ${COLOR_GREEN}-N <method>${COLOR_RESET} notification method (${COLOR_MAGENTA}default: libnotify${COLOR_RESET}) - ${COLOR_GREEN}-p${COLOR_RESET} enable progress bar - ${COLOR_GREEN}-e <expires>${COLOR_RESET} expiration time of notifications in ms - ${COLOR_GREEN}-l${COLOR_RESET} use fullcolor instead of symbolic icons - ${COLOR_GREEN}-S <suffix>${COLOR_RESET} append suffix to symbolic icon names - ${COLOR_GREEN}-y${COLOR_RESET} use dunstify (${COLOR_MAGENTA}default: notify-send${COLOR_RESET}) - -${COLOR_YELLOW}Environment Variables:${COLOR_RESET} - ${COLOR_CYAN}XOSD_PATH${COLOR_RESET} path to osd_cat - ${COLOR_CYAN}HERBE_PATH${COLOR_RESET} path to herbe - ${COLOR_CYAN}VOLNOTI_PATH${COLOR_RESET} path to volnoti-show - ${COLOR_CYAN}DUNSTIFY_PATH${COLOR_RESET} path to dunstify - ${COLOR_CYAN}CANBERRA_PATH${COLOR_RESET} path to canberra-gtk-play - ${COLOR_CYAN}NOTIFY_SEND_PATH${COLOR_RESET} path to notify-send or notify-send.py - ${COLOR_CYAN}USE_NOTIFY_SEND_PY${COLOR_RESET} flag to use notify-send.py instead of notify-send - ${COLOR_CYAN}NO_NOTIFY_COLOR${COLOR_RESET} flag to disable colors in notifications -EOF - exit "$EX_USAGE" - } -} - -# Get the volume as a percentage. -get_volume() { - if $USE_AMIXER; then - amixer_get_volume "$CARD" "$MIXER" - else - pa_get_volume "$SINK" - fi -} - -is_muted() { - if $USE_AMIXER; then - amixer_is_muted "$CARD" - return $? - else - pa_is_muted "$SINK" - return $? - fi -} - -# Gets an icon for the provided volume. -# -# Arguments: -# Volume (integer) An integer indicating the volume. -# -# Returns: -# The volume icon name. -get_volume_icon() { - local -r vol=${1:?$(error 'Volume is required')} - local icon - - if $USE_FULLCOLOR_ICONS; then - if (( vol >= 70 )); then icon=${ICONS[1]} - elif (( vol >= 40 )); then icon=${ICONS[3]} - elif (( vol > 0 )); then icon=${ICONS[2]} - else icon=${ICONS[2]} - fi - else - # Get overamplified icon if available, otherwise default to high volume icon - if (( vol > 100 )); then icon=${ICONS_SYMBOLIC[4]:-${ICONS_SYMBOLIC[1]}} - elif (( vol >= 70 )); then icon=${ICONS_SYMBOLIC[1]} - elif (( vol >= 40 )); then icon=${ICONS_SYMBOLIC[3]} - elif (( vol > 0 )); then icon=${ICONS_SYMBOLIC[2]} - else icon=${ICONS_SYMBOLIC[2]} - fi - fi - - echo "$icon" -} - -# Gets an emoji for the provided volume. -# -# Arguments: -# Volume (integer) An integer indicating the volume. -# -# Returns: -# The volume emoji. -get_volume_emoji() { - local -r vol=${1:?$(error 'Volume is required')} - local icon - - if is_muted; then - icon=${ICONS_EMOJI[0]} - else - if (( vol >= 70 )); then icon=${ICONS_EMOJI[1]} - elif (( vol >= 40 )); then icon=${ICONS_EMOJI[3]} - elif (( vol > 0 )); then icon=${ICONS_EMOJI[2]} - else icon=${ICONS_EMOJI[2]} - fi - fi - - echo "$icon" -} - -# Updates the status line. -# -# Arguments: -# signal (string) The signal used to update the status line. -# proc (string) The name of the status line process. -update_statusline() { - local -r signal=${1:?$(error 'Signal is required')} - local -r proc=${2:?$(error 'Process name is required')} - - pkill "-$signal" "$proc" -} - -# Generates a progress bar for the provided value. -# -# Arguments: -# Percentage (integer) Percentage of progress. -# Maximum (integer) Maximum percentage. (default: 100) -# Divisor (integer) For calculating the ratio of blocks to progress (default: 5) -# -# Returns: -# The progress bar. -progress_bar() { - local -r percent=${1:?$(error 'Percentage is required')} - local -r max_percent=${2:-100} - local -r divisor=${3:-5} - local -r progress=$(( (percent > max_percent ? max_percent : percent) / divisor )) - - printf -v bar "%*s" $progress "" - echo "${bar// /█}" -} - -apply_symbolic_icon_suffix() { - for i in "${!ICONS_SYMBOLIC[@]}"; do - ICONS_SYMBOLIC[$i]="${ICONS_SYMBOLIC[$i]}${SYMBOLIC_ICON_SUFFIX}" - done -} - -# Get color for the given volume -# -# Arguments: -# $1 - The volume -volume_color() { - local -ir vol=${1:?$(error 'A volume is required')} - - if $USE_AMIXER; then - amixer_volume_color "$vol" - else - pa_volume_color "$vol" - fi -} - -# Updates the status bars -# -# Returns -# 0 when no problem occurred -# 1 when one $of signal or $statusline are set but not both -update_statusbar() { - if not_empty "$SIGNAL"; then - if empty "$STATUSLINE"; then - return 1 - fi - update_statusline "$SIGNAL" "$STATUSLINE" - else - if not_empty "$STATUSLINE"; then - return 1 - fi - fi - - return 0 -} - -setup_audio() { - if $USE_AMIXER; then - setup_amixer - else - setup_pulseaudio - fi -} - -# All PulseAudio functions are defined here -define_pulseaudio_functions() { - # Executes `pactl list sinks` or return its output if called previously - pa_list_sinks() { - if $OPT_LISTEN || empty "$PA_LIST_SINKS"; then - PA_LIST_SINKS=$(pactl list sinks) - fi - echo "$PA_LIST_SINKS" - } - - pa_invalidate_cache() { - unset PA_LIST_SINKS - } - - pa_default_sink_name() { - pactl info | awk -F': ' '/^Default Sink: /{print $2}' - } - - # Get the index of a sink name. - # - # Arguments - # Sink name (string) Symbolic name of sink. - pa_get_sink_index() { - local -r sink=${1:?$(error 'Sink name is required')} - - pa_list_sinks | \ - awk -W posix '/^Sink #/{gsub("#", ""); idx = $2} - /^[ \t]+Name: / {insink = $2 == "'"$sink"'"; if (insink) { print idx }; exit}' - } - - # Get the volume as a percentage. - # - # Arguments - # Sink name (string) Symbolic name of sink. - pa_get_volume() { - local -r sink=${1:?$(error 'Sink name is required')} - - pa_list_sinks | \ - awk -W posix '/^[ \t]+Name: / {insink = $2 == "'"$sink"'"} - /^[ \t]+Volume: / && insink {gsub("%,?", ""); print $5; exit}' - } - - # Get the max volume as a percentage. - # - # Arguments - # Sink name (string) Symbolic name of sink. - pa_get_base_volume() { - local -r sink=${1:?$(error 'Sink name is required')} - - pa_list_sinks | \ - awk -W posix '/^[ \t]+Name: / {insink = $2 == "'"$sink"'"} - /^[ \t]+Base Volume: / && insink {gsub("%", ""); print $5; exit}' - } - - # Increase volume relative to current volume using pulseaudio. - # - # Arguments: - # Sink name (string) Symbolic name of sink. - # Step (integer) Percentage to increase by. - pa_increase_volume() { - local -r sink=$1 - local -r step=${2:=-5} - - pa_set_volume "$sink" "+${step}%" - } - - # Decrease volume relative to current volume using pulseaudio. - # - # Arguments: - # Sink name (string) Symbolic name of sink. - # Step (integer|percentage) Percentage to decrease by. - pa_decrease_volume() { - local -r sink=$1 - local -r step=${2:=-5} - - pa_set_volume "$sink" "-${step}%" - } - - # Set volume using pulseaudio. - # - # Arguments: - # Sink name (string) Symbolic name of sink. - # Volume (integer|linear factor|percentage|decibel) - pa_set_volume() { - local -r sink=${1:?$(error 'Sink name is required')} - local -r vol=${2:?$(error 'Volume is required')} - - pa_invalidate_cache - - pactl set-sink-volume "$sink" "$vol" - } - - # Toggle mute using pulseaudio. - # - # Arguments: - # Sink name (string) Symbolic name of sink. - pa_toggle_mute() { - local -r sink=${1:?$(error 'Sink name is required')} - - pa_invalidate_cache - - pactl set-sink-mute "$sink" toggle - } - - # Check if sink is muted. - # - # Arguments: - # Sink name (string) Symbolic name of sink. - # - # Returns: - # 0 when true, 1 when false. - pa_is_muted() { - local -r sink=${1:?$(error 'Sink name is required')} - - pa_list_sinks | \ - awk -W posix '/^[ \t]+Name: / {insink = $2 == "'"$sink"'"} - /^[ \t]+Mute: / && insink && $2 ~ /^yes$/ { exitcode=1 }; END { exit !exitcode }' - } - - # Get the flags of the PulseAudio sink. - # - # Arguments - # Sink name (string) Symbolic name of sink. - pa_get_sink_flags() { - local -r sink=${1:?$(error 'Sink name is required')} - - pa_list_sinks | \ - awk -W posix '/^[ \t]+Name: / {insink = $2 == "'"$sink"'"} - /^[ \t]+Flags: / && insink { for(i = 2; i <= NF; ++i) printf $i FS; exit}' - } - - # Get color for the given volume for PulseAudio - # - # Arguments: - # $1 - The volume - pa_volume_color() { - local -ir vol=${1:?$(error 'A volume is required')} - - if (( vol >= PA_VOLUME_MUTED && vol < PA_BASE_VOLUME )); then - echo "$COLOR_MUTED_TO_BASE" - elif (( vol >= PA_BASE_VOLUME && vol <= PA_VOLUME_NORM )); then - echo "$COLOR_BASE_TO_NORM" - elif (( vol > PA_VOLUME_NORM && vol <= MAX_VOLUME )); then - echo "$COLOR_NORM_TO_MAX" - else - echo "$COLOR_OTHER" - fi - } - - # Listens for PulseAudio events - # - # Arguments: - # Output (optional) (string) An output mode. When set, outputs volume in the output mode format. - listen() { - local -r output=$* - local -r index=$(pa_get_sink_index "$SINK") - - # Output volume so statusbars have something to display before any event occurs - not_empty "$output" && output_volume "$output" - - while IFS= read -r; do - show_volume_notification - update_statusbar - play_volume_changed - not_empty "$output" && output_volume "$output" - done < <(pactl subscribe | stdbuf -oL grep -e "Event 'change' on sink #$index") - } - - # Play a sound file. - # - # Arguments: - # Sound file (string) - pa_play() { - local -r file=$1 - - paplay -d "$SINK" "$file" & - } -} - -# Register PulseAudio related functions and settings -setup_pulseaudio() { - define_pulseaudio_functions - - PA_LIST_SINKS=$(pactl list sinks) || exit 1 - - if empty "$SINK"; then - SINK="$(pa_default_sink_name)" - fi - - # Determine a max volume when it's not specified - if ! isset MAX_VOLUME; then - read -ra SINK_FLAGS < <(pa_get_sink_flags "$SINK") - PA_BASE_VOLUME=$(pa_get_base_volume "$SINK") - - # Does the sink support digital (software) amplification? - if [[ "${SINK_FLAGS[*]}" =~ "DECIBEL_VOLUME" ]]; then - MAX_VOLUME=$((PA_VOLUME_NORM * MAX_AMPLIFICATION)) - else - MAX_VOLUME=$PA_VOLUME_NORM - fi - fi -} - -# All amixer functions are defined here -define_amixer_functions() { - # Get the volume as a percentage. - # - # Arguments - # Card (integer) Card number to control. - # Mixer (string) Name of the mixer. - amixer_get_volume() { - local -r card=$1 - local -r mixer=${2:-Master} - - amixer ${card:+-c "$card" --} sget "$mixer" | \ - awk -W posix -F'[][]' '/dB/ { gsub("%", ""); print $2 }' - } - - # Increase volume relative to current volume using amixer. - # - # Arguments: - # Card (integer) Card number to control. - # Step (integer) Percentage to increase by. - amixer_increase_volume() { - local -r card=$1 - local -r step=${2:=-5} - - amixer_set_volume "${step}%+" "$card" - } - - # Decrease volume relative to current volume using amixer. - # - # Arguments: - # Card (integer) Card number to control. - # Step (integer) Percentage to decrease by. - amixer_decrease_volume() { - local -r card=$1 - local -r step=${2:=-5} - - amixer_set_volume "${step}%-" "$card" - } - - # Set volume using amixer. - # - # Arguments: - # Volume (integer|linear factor|percentage|decibel) - # Card (optional) (integer) Card number to control. - amixer_set_volume() { - local -r vol=${1:?$(error 'Volume is required')} - local -r card=$2 - - amixer -q ${card:+-c "$card" --} set "$MIXER" "$vol" - } - - # Toggle mute using amixer. - # - # Arguments: - # Card (integer) Card number to control. - amixer_toggle_mute() { - local -r card=$1 - - amixer -q ${card:+-c "$card" --} set "$MIXER" toggle - } - - # Check if card is muted. - # - # Arguments: - # Card (optional) (integer) Card number to control. - # - # Returns: - # 0 when true, 1 when false. - amixer_is_muted() { - local -r card=$1 - - amixer ${card:+-c "$card" --} sget "$MIXER" | \ - awk -W posix -F'[][]' '/dB/ && $6 ~ /^off$/ { exitcode=1 }; END { exit !exitcode }' - } - - # Get color for the given volume for amixer - # - # Arguments: - # $1 - The volume - amixer_volume_color() { - local -ir vol=${1:?$(error 'A volume is required')} - - if (( vol >= 0 && vol < 100 )); then - echo "$COLOR_MUTED_TO_BASE" - elif (( vol == 100 )); then - echo "$COLOR_BASE_TO_NORM" - elif (( vol > 100 && vol <= MAX_VOLUME )); then - echo "$COLOR_NORM_TO_MAX" - else - echo "$COLOR_OTHER" - fi - } - - # Play a sound file. - # - # Arguments: - # Sound file (string) - amixer_play() { - local -r file=$1 - - aplay -q "$file" & - } -} - -# Register amixer related functions and settings -setup_amixer() { - define_amixer_functions -} - -setup_color() { - if has_color; then - COLOR_RESET=$'\033[0m' - COLOR_RED=$'\033[0;31m' - COLOR_GREEN=$'\033[0;32m' - COLOR_YELLOW=$'\033[0;33m' - COLOR_MAGENTA=$'\033[0;35m' - COLOR_CYAN=$'\033[0;36m' - fi -} - -# Rearrange all options to place flags first -# Author: greycat -# URL: https://mywiki.wooledge.org/ComplexOptionParsing -arrange_opts() { - local flags args optstr=$1 - shift - - while (($#)); do - case $1 in - --) - args+=("$@") - break; - ;; - -*) - flags+=("$1") - if [[ $optstr == *"${1: -1}:"* ]]; then - flags+=("$2") - shift - fi - ;; - *) - args+=("$1") - ;; - esac - shift - done - OPTARR=("${flags[@]}" "${args[@]}") -} - -parse_opts() { - local optstring=:ac:Ce:hj:lm:nN:pPs:S:t:u:x:X:y - - arrange_opts "$optstring" "$@" - set -- "${OPTARR[@]}" - - OPTIND=1 - - while getopts "$optstring" opt; do - case "$opt" in - a ) USE_AMIXER=true ;; - c ) CARD=$OPTARG ;; - C ) USE_CANBERRA=true ;; - e ) EXPIRES=$OPTARG ;; - j ) IFS=, read -ra ICONS_EMOJI <<< "$OPTARG" ;; - l ) USE_FULLCOLOR_ICONS=true ;; - m ) MIXER=${OPTARG@Q} ;; - n ) DISPLAY_NOTIFICATIONS=true ;; - N ) NOTIFICATION_METHOD=$OPTARG ;; - p ) SHOW_VOLUME_PROGRESS=true ;; - P ) PLAY_SOUND=true ;; - s ) SINK=$OPTARG ;; - S ) SYMBOLIC_ICON_SUFFIX=$OPTARG ;; - t ) STATUSLINE=$OPTARG ;; - u ) SIGNAL=$OPTARG ;; - x ) MAX_VOLUME=$OPTARG ;; - X ) MAX_AMPLIFICATION=$OPTARG ;; - y ) USE_DUNSTIFY=true ;; - h | *) usage ;; - esac - done - - read -ra CMDARGS <<< "${OPTARR[@]:$((OPTIND-1))}" -} - -exec_command() { - IFS=' ' read -ra ARGS <<< "$1" - set -- "${ARGS[@]}" - - COMMAND=${1:?$(error 'A command is required')} - shift - - case "$COMMAND" in - up|raise|increase) - case "$#" in 1) ;; *) usage ;; esac - increase_volume "$1" "$MAX_VOLUME" - ;; - down|lower|decrease) - case "$#" in 1) ;; *) usage ;; esac - decrease_volume "$1" - ;; - set) - case "$#" in 1) ;; *) usage ;; esac - case "$1" in - +*) increase_volume "${1:1}" "$MAX_VOLUME" ;; - -*) decrease_volume "${1:1}" ;; - *) set_volume "$1" "$MAX_VOLUME" ;; - esac - ;; - mute) - toggle_mute - ;; - listen) - listen "$*" - ;; - output) - case "$#" in 0) usage ;; esac - output_volume "$*" - exit "${EXITCODE:-$EX_OK}" - ;; - outputs) - list_output_formats - ;; - notifications) - list_notification_methods - ;; - *) - usage - ;; - esac -} - -play_volume_changed() { - $PLAY_SOUND || return - - # Sound can be handled by the notification method - if $DISPLAY_NOTIFICATIONS && has_capability sound; then - return - fi - - if $USE_CANBERRA; then - ca_play "$SOUND_VOLUME_CHANGED" "Volume Changed" - else - if $USE_AMIXER; then - amixer_play "$SOUND_VOLUME_CHANGED" - else - pa_play "$SOUND_VOLUME_CHANGED" - fi - fi -} - -ca_play() { - local -r file=$1 desc=$2 - - if [[ -f $file ]]; then - "${CANBERRA_PATH:+${CANBERRA_PATH%/}/}canberra-gtk-play" -f "$file" -d "$desc" - else - "${CANBERRA_PATH:+${CANBERRA_PATH%/}/}canberra-gtk-play" -i "audio-volume-change" -d "$desc" - fi -} - -post_command_hook() { - if is_command_hookable "$COMMAND"; then - show_volume_notification - play_volume_changed - update_statusbar || usage - fi -} - -main() { - # Getopt parsing variables - declare OPTIND - declare -a OPTARR CMDARGS - - ########################################################### - # Non-command line option variables - ########################################################### - - # Commands which will not use post_command_hook(), usually because - # they handle notifications and/or statusbar updates manually - declare -a POST_HOOK_EXEMPT_COMMANDS=( - listen - ) - - # Exit codes - declare -ir \ - EX_OK=0 \ - EX_URGENT=33 \ - EX_USAGE=64 - - # Main program exit code - declare -i EXITCODE=$EX_OK - - # Standard notification icons. Usually full color - # Note: order matters; muted, high, low, medium, and optionally overamplified - declare -a ICONS=( - audio-volume-muted - audio-volume-high - audio-volume-low - audio-volume-medium - ) - - # Symbolic notification icons. Usually low color or monochrome - # Note: order matters; muted, high, low, medium, and optionally overamplified - declare -a ICONS_SYMBOLIC=( - audio-volume-muted-symbolic - audio-volume-high-symbolic - audio-volume-low-symbolic - audio-volume-medium-symbolic - ## Only exists in some icon sets - # audio-volume-overamplified-symbolic - ) - - # Emoji-based icons. - declare -a ICONS_EMOJI=( - ﱝ - 墳 - 奄 - 奔 - ) - - # Volume changed sound. - declare SOUND_VOLUME_CHANGED=${SOUND_VOLUME_CHANGED:-/usr/share/sounds/freedesktop/stereo/audio-volume-change.oga} - - # DBUS constants - declare -r \ - DBUS_NAME=org.freedesktop.Notifications \ - DBUS_PATH=/org/freedesktop/Notifications \ - DBUS_IFAC_FDN=org.freedesktop.Notifications - - # Notification server information - declare \ - NOTIFY_SERVER - # NOTIFY_VENDOR \ - # NOTIFY_VERSION \ - # NOTIFY_SPEC_VERSION - - # Notification capabilities - declare -a NOTIFY_CAPS=() - - # PulseAudio sink flags - declare -a SINK_FLAGS=() - - # PulseAudio volume variables and constants. - # Note: unlike in PA, PA_VOLUME_* here are percentages instead of integers - declare -i PA_BASE_VOLUME=100 - declare -ir \ - PA_VOLUME_NORM=100 \ - PA_VOLUME_MUTED=0 - - # Cached output of `pactl list sinks`; so we don't have to call it each time we need it - declare PA_LIST_SINKS - - # Output volume colors - declare -r \ - COLOR_MUTED=${COLOR_MUTED:-#FFFF00} \ - COLOR_MUTED_TO_BASE=${COLOR_MUTED_TO_BASE:-#00FF00} \ - COLOR_BASE_TO_NORM=${COLOR_BASE_TO_NORM:-#FFFF00} \ - COLOR_NORM_TO_MAX=${COLOR_NORM_TO_MAX:-#FF0000} \ - COLOR_OTHER=${COLOR_OTHER:-#FFFFFF} \ - COLOR_XOSD_OUTLINE=${COLOR_XOSD_OUTLINE:-#222222} - - declare \ - COLOR_RESET \ - COLOR_RED \ - COLOR_GREEN \ - COLOR_YELLOW \ - COLOR_MAGENTA \ - COLOR_CYAN - - ########################################################### - # Command line option variables - ########################################################### - declare -l NOTIFICATION_METHOD - - declare \ - COMMAND \ - DISPLAY_NOTIFICATIONS=false \ - SHOW_VOLUME_PROGRESS=false \ - USE_AMIXER=false \ - USE_DUNSTIFY=false \ - USE_FULLCOLOR_ICONS=false \ - CARD \ - MIXER=Master \ - SIGNAL \ - SINK \ - STATUSLINE \ - SYMBOLIC_ICON_SUFFIX \ - NOTIFICATION_METHOD \ - PLAY_SOUND=false \ - USE_CANBERRA=false - - declare -i \ - EXPIRES=1500 \ - MAX_VOLUME \ - MAX_AMPLIFICATION=2 - - define_helpers - define_notify - define_output_formats - define_commands - - setup_color - - parse_opts "$@" - - # Requires options to be parsed first - setup_audio - - exec_command "${CMDARGS[*]}" && post_command_hook - - exit ${EXITCODE:-$EX_OK} -} - -main "$@" |
