ratvol.bash 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. #!/bin/bash
  2. #------------------------------------------------------------------------------
  3. # Modifies the volume according to the passed argument and uses the ratpoison
  4. # window manager to draw a volume volume bar. By default increases volume when
  5. # called.
  6. #
  7. # Dependencies:
  8. # - amixer and/or pulsemixer (used to fetch and set audio value)
  9. # - ratpoison
  10. #
  11. # Notes:
  12. # - So that ratpoison can output unicode while we use unicode sequences, we
  13. # need to assign potential unicode characters using printf -v
  14. # - When the script is executed multiple times in a row (say, when the user is
  15. # quickly changing the volume), ratpoison would not properly restore the
  16. # bar gravity to what it was. To avoid this, we prefer to exit early if a
  17. # specific lockfile (/tmp/ratvol.lock) exists.
  18. # - Reasoning for disabling set -u:
  19. # Seting a variable declared as an integer (declare -i) to a string,
  20. # causes it to have value 0. This script (ab)uses this behaviour for
  21. # error handling, since it saves us the use of two regexes calls to
  22. # validate user input for the barsize and step arguments. Since set -u
  23. # conflicts with this behaviour (it raises variable unbound error on string
  24. # assigments to integer variables), I have disabled it.
  25. #------------------------------------------------------------------------------
  26. set -e -o pipefail
  27. # Desc:
  28. # Stop script execution and output a foramtted error message.
  29. # Args:
  30. # $@: string
  31. # contents of the error message
  32. die() {
  33. local err_msg="ratvol: ${@}"
  34. ratpoison -c "echo ${err_msg}" # Also print error message in ratpoison
  35. printf '%s\n' "${err_msg}"
  36. printf "Try ratvol -h for more information\n"
  37. exit 1;
  38. }
  39. usage() {
  40. cat <<-USAGE_END
  41. Usage: ratvol [OPTIONS] [+]
  42. Modifies the master volume, by default decreasing it, and draws a volume
  43. bar using ratpoison.
  44. -c, --character [character] Character used to fill volume bar, it must be only one
  45. -g, --gravity [gravity] Where the volume bar should be drawn Valid positions are:
  46. nw, w, sw, n, c, s, ne, e, se
  47. -s, --step [step] How much the volume the volume step should be
  48. -p, --percentage Show the percentage level
  49. -t, --msgtime Duration that the volume bar will stay on screen
  50. -h, --help Prints this message
  51. USAGE_END
  52. exit ${1}
  53. }
  54. # Desc:
  55. # Draws a volume bar using ratpoison.
  56. # Args:
  57. # $1: int volume
  58. # The percentage of the current volume level
  59. # $2: enum(nw|w|sw|n|c|s|ne|e|se) gravity
  60. # Where should ratpoison draw the bar (the gravity)
  61. # $3: bool show_percentage
  62. # Whether or not to show the percentage value in the bar
  63. # $4: char fill_char
  64. # The character used to fill the volume bar
  65. # $5: int barsize
  66. # The size of the volume bar
  67. # $6: int max_vol
  68. # The maximum volume that audio backend allows
  69. # $7: int msgtime
  70. # The amount of time the volume bar stays on the screen
  71. # $8: int icon
  72. # The icon, if any, that should appear in the beginning of the bar
  73. drawbar() {
  74. local -r volume=${1} gravity=${2} show_percentage=${3}
  75. local -r fill_char=${4} barsize=${5} max_vol=${6} msgtime=${7}
  76. local -r old_gravity=$(ratpoison -c 'set bargravity')
  77. local -r old_msgtime=$(ratpoison -c 'set msgwait')
  78. local -r filled=$((barsize * volume / max_vol))
  79. # Format volbar according to icon
  80. local volbar
  81. # Escape the '%' so printf can output it
  82. [[ ${8} == '%' ]] && local -r icon="%${8}"
  83. if [[ -z ${icon} ]] ; then
  84. # Empty if there is no icon
  85. local volbar=""
  86. else
  87. # Use printf to handle unicode
  88. printf -v volbar "${icon} "
  89. fi
  90. for ((i=0 ; i < barsize; i++)) ; do
  91. # Draw the percentage
  92. if ${show_percentage} && ((i == barsize / 2 - 2 )); then
  93. volbar+="[${volume}%]"
  94. fi
  95. # Draw the filled part of the volume bar
  96. if ((i < filled)) ; then
  97. volbar+=${fill_char}
  98. continue
  99. fi
  100. # Fill the remainder with empty space
  101. volbar+=' '
  102. done
  103. # Prepare ratpoison for drawing
  104. ratpoison -c "set bargravity ${gravity}"
  105. ratpoison -c "set msgwait ${msgtime}"
  106. # Draw the bar
  107. ratpoison -c "echo ${volbar}"
  108. # Restore changed settings
  109. ratpoison -c "set bargravity ${old_gravity}"
  110. ratpoison -c "set msgwait ${old_msgtime}"
  111. }
  112. main() {
  113. # Handles race issues when various calls are made in quick sucession
  114. local -r lockfile='/tmp/ratvol.lock'
  115. [[ -e ${lockfile} ]] && exit
  116. trap "rm ${lockfile}" EXIT
  117. touch ${lockfile}
  118. local -r volregex='[[:digit:]]*(?=%)'
  119. local -r block_char='\u2588'
  120. local -i barsize=34
  121. local -i step=5
  122. local -i max_vol=100
  123. printf -v icon '\u266a'
  124. printf -v character "${block_char}"
  125. msgtime=1
  126. show_percentage=false
  127. gravity='nw'
  128. op='-'
  129. short_opts='hpg:s:S:t:i:c:'
  130. long_opts='help,show-percentage,no-icon,gravity:,step:,barsize:,msgtime:,character:'
  131. parsed=$(getopt --options ${short_opts} --longoptions ${long_opts} -n 'compiled' -- "${@}")
  132. [[ $? != 0 ]] && die 'Try ratvol -h for more information'
  133. eval set -- "${parsed}"
  134. while true; do
  135. case "${1}" in
  136. -g|--gravity)
  137. gravity="${2}"
  138. shift 2
  139. ;;
  140. -p|--percentage)
  141. show_percentage=true
  142. shift
  143. ;;
  144. -s|--step)
  145. step="${2}"
  146. shift 2
  147. ;;
  148. -i|--icon)
  149. icon="${2}"
  150. shift 2
  151. ;;
  152. -t|--msgtime)
  153. msgtime="${2}"
  154. shift 2
  155. ;;
  156. -t|--step)
  157. step="${2}"
  158. shift 2
  159. ;;
  160. -S|--barsize)
  161. barsize="${2}"
  162. shift 2
  163. ;;
  164. -c|--character)
  165. printf -v character "${2}"
  166. shift 2
  167. ;;
  168. --)
  169. shift
  170. break
  171. ;;
  172. -h|--help) usage 0 ;;
  173. *)
  174. echo 'Internal error.' >&2
  175. exit 1
  176. ;;
  177. esac
  178. done
  179. # Input validation
  180. [[ ${gravity} != @(nw|w|sw|n|c|s|ne|e|se) ]] && die "Bad value for gravity: ${gravity}"
  181. ((${#character} != 1)) && die "Character must have a length of 1: ${character}"
  182. ((${step} <= 0)) && die "Step can't be less than 0: ${step}"
  183. ((${barsize} <= 0)) && die "Barsize can't be less than 0: ${barsize}"
  184. ((${msgtime} <= 0)) && die "Msgtime can't be less than 0: ${msgtime}"
  185. # Handle invalid volume operation and extra invalid arguments
  186. if ((${#} >= 1)) ; then
  187. ((${#} > 1)) && die "Unrecognized arguments: ${@}"
  188. if [[ ${1} == '+' ]] ; then
  189. op='+'
  190. elif [[ ${1} == '-' ]] ; then
  191. : # nop
  192. else
  193. die "Unrecognized argument: ${1}"
  194. fi
  195. fi
  196. # Check if pulsemixer exists
  197. if [[ -n $(type -P pulsemixer) ]] ; then
  198. volume="$(pulsemixer --change-volume ${op}5 --get-volume)"
  199. # pulsemixer gives two audio levels, we only need one, so we cut the second
  200. volume=${volume% *}
  201. max_vol=150
  202. else
  203. volume="$(amixer set Master ${step}%${op} | grep -Po ${volregex})"
  204. fi
  205. drawbar\
  206. ${volume} ${gravity} ${show_percentage}\
  207. ${character} ${barsize} ${max_vol}\
  208. ${msgtime} ${icon}
  209. }
  210. main $@