panel.sh 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. #!/usr/bin/env bash
  2. set -euo pipefail # bash strict mode
  3. quote() {
  4. local q="$(printf '%q ' "$@")"
  5. printf '%s' "${q% }"
  6. }
  7. hc_quoted="$(quote "${herbstclient_command[@]:-herbstclient}")"
  8. hc() { "${herbstclient_command[@]:-herbstclient}" "$@" ;}
  9. monitor=${1:-0}
  10. geometry=( $(hc monitor_rect "$monitor") )
  11. if [ -z "$geometry" ] ;then
  12. echo "Invalid monitor $monitor"
  13. exit 1
  14. fi
  15. # geometry has the format W H X Y
  16. x=${geometry[0]}
  17. y=${geometry[1]}
  18. panel_width=${geometry[2]}
  19. panel_height=18
  20. font="-*-fixed-medium-*-*-*-*-*-*-*-*-*-*-*"
  21. # font="-*-roboto mono-*-*-*-*-16-*-*-*-*-*-*-*"
  22. # font="-*-terminus-*-*-*-*-16-*-*-*-*-*-*-*"
  23. bgcolor=$(hc get frame_border_normal_color)
  24. selbg=$(hc get window_border_active_color)
  25. selfg='#101010'
  26. ####
  27. # Try to find textwidth binary.
  28. # In e.g. Ubuntu, this is named dzen2-textwidth.
  29. if which textwidth &> /dev/null ; then
  30. textwidth="textwidth";
  31. elif which dzen2-textwidth &> /dev/null ; then
  32. textwidth="dzen2-textwidth";
  33. elif which xftwidth &> /dev/null ; then # For guix
  34. textwidth="xtfwidth";
  35. else
  36. echo "This script requires the textwidth tool of the dzen2 project."
  37. exit 1
  38. fi
  39. ####
  40. # true if we are using the svn version of dzen2
  41. # depending on version/distribution, this seems to have version strings like
  42. # "dzen-" or "dzen-x.x.x-svn"
  43. if dzen2 -v 2>&1 | head -n 1 | grep -q '^dzen-\([^,]*-svn\|\),'; then
  44. dzen2_svn="true"
  45. else
  46. dzen2_svn=""
  47. fi
  48. if awk -Wv 2>/dev/null | head -1 | grep -q '^mawk'; then
  49. # mawk needs "-W interactive" to line-buffer stdout correctly
  50. # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=593504
  51. uniq_linebuffered() {
  52. awk -W interactive '$0 != l { print ; l=$0 ; fflush(); }' "$@"
  53. }
  54. else
  55. # other awk versions (e.g. gawk) issue a warning with "-W interactive", so
  56. # we don't want to use it there.
  57. uniq_linebuffered() {
  58. awk '$0 != l { print ; l=$0 ; fflush(); }' "$@"
  59. }
  60. fi
  61. hc pad $monitor $panel_height
  62. {
  63. ### Event generator ###
  64. # based on different input data (mpc, date, hlwm hooks, ...) this generates events, formed like this:
  65. # <eventname>\t<data> [...]
  66. # e.g.
  67. # date ^fg(#efefef)18:33^fg(#909090), 2013-10-^fg(#efefef)29
  68. #mpc idleloop player &
  69. while true ; do
  70. # "date" output is checked once a second, but an event is only
  71. # generated if the output changed compared to the previous run.
  72. date +$'date\t^fg(#efefef)%H:%M^fg(#909090), %Y-%m-^fg(#efefef)%d '
  73. sleep 1 || break
  74. done > >(uniq_linebuffered) &
  75. childpid=$!
  76. hc --idle
  77. kill $childpid
  78. } 2> /dev/null | {
  79. IFS=$'\t' read -ra tags <<< "$(hc tag_status $monitor)"
  80. visible=true
  81. date=""
  82. # player=""
  83. windowtitle=""
  84. while true ; do
  85. ### Output ###
  86. # This part prints dzen data based on the _previous_ data handling run,
  87. # and then waits for the next event to happen.
  88. separator="^bg()^fg($selbg)|"
  89. # draw tags
  90. for i in "${tags[@]}" ; do
  91. case ${i:0:1} in
  92. '#')
  93. echo -n "^bg($selbg)^fg($selfg)"
  94. ;;
  95. '+')
  96. echo -n "^bg(#9CA668)^fg(#141414)"
  97. ;;
  98. ':')
  99. echo -n "^bg()^fg(#ffffff)"
  100. ;;
  101. '!')
  102. echo -n "^bg(#FF0675)^fg(#141414)"
  103. ;;
  104. *)
  105. echo -n "^bg()^fg(#ababab)"
  106. ;;
  107. esac
  108. if [ ! -z "$dzen2_svn" ] ; then
  109. # clickable tags if using SVN dzen
  110. echo -n "^ca(1,$hc_quoted focus_monitor \"$monitor\" && "
  111. echo -n "$hc_quoted use \"${i:1}\") ${i:1} ^ca()"
  112. else
  113. # non-clickable tags if using older dzen
  114. echo -n " ${i:1} "
  115. fi
  116. done
  117. echo -n "$separator"
  118. echo -n "^bg()^fg() ${windowtitle//^/^^}"
  119. # small adjustments
  120. right="$separator^bg() $date $separator"
  121. right_text_only=$(echo -n "$right" | sed 's.\^[^(]*([^)]*)..g')
  122. # get width of right aligned text.. and add some space..
  123. width=$($textwidth "$font" "$right_text_only ")
  124. echo -n "^pa($(($panel_width - $width)))$right"
  125. echo
  126. ### Data handling ###
  127. # This part handles the events generated in the event loop, and sets
  128. # internal variables based on them. The event and its arguments are
  129. # read into the array cmd, then action is taken depending on the event
  130. # name.
  131. # "Special" events (quit_panel/togglehidepanel/reload) are also handled
  132. # here.
  133. # wait for next event
  134. IFS=$'\t' read -ra cmd || break
  135. # find out event origin
  136. case "${cmd[0]}" in
  137. tag*)
  138. #echo "resetting tags" >&2
  139. IFS=$'\t' read -ra tags <<< "$(hc tag_status $monitor)"
  140. ;;
  141. date)
  142. #echo "resetting date" >&2
  143. date="${cmd[@]:1}"
  144. ;;
  145. quit_panel)
  146. exit
  147. ;;
  148. togglehidepanel)
  149. currentmonidx=$(hc list_monitors | sed -n '/\[FOCUS\]$/s/:.*//p')
  150. if [ "${cmd[1]}" -ne "$monitor" ] ; then
  151. continue
  152. fi
  153. if [ "${cmd[1]}" = "current" ] && [ "$currentmonidx" -ne "$monitor" ] ; then
  154. continue
  155. fi
  156. echo "^togglehide()"
  157. if $visible ; then
  158. visible=false
  159. hc pad $monitor 0
  160. else
  161. visible=true
  162. hc pad $monitor $panel_height
  163. fi
  164. ;;
  165. reload)
  166. exit
  167. ;;
  168. focus_changed|window_title_changed)
  169. windowtitle="${cmd[@]:2}"
  170. ;;
  171. # player)
  172. # player="${cmd[@]:1}"
  173. # ;;
  174. esac
  175. done
  176. ### dzen2 ###
  177. # After the data is gathered and processed, the output of the previous block
  178. # gets piped to dzen2.
  179. } 2> /dev/null | dzen2 -w $panel_width -x $x -y $y -fn "$font" -h $panel_height \
  180. -e "button3=;button4=exec:$hc_quoted use_index -1;button5=exec:$hc_quoted use_index +1" \
  181. -ta l -bg "$bgcolor" -fg '#efefef'