#!/usr/bin/env bash . "${HOME}/.cache/wal/colors.sh" quote() { local q="$(printf '%q ' "$@")" printf '%s' "${q% }" } hc_quoted="$(quote "${herbstclient_command[@]:-herbstclient}")" hc() { "${herbstclient_command[@]:-herbstclient}" "$@"; } monitor=${1:-0} geometry=($(hc monitor_rect "$monitor")) if [ -z "$geometry" ]; then echo "Invalid monitor $monitor" exit 1 fi # geometry has the format W H X Y x=$((${geometry[0]} + 12)) y=$((${geometry[1]} + 12)) panel_width=$((${geometry[2]} - 24)) panel_height=29 font="*mono*" #### # Try to find textwidth binary. # In e.g. Ubuntu, this is named dzen2-textwidth. if which textwidth &>/dev/null; then textwidth="textwidth" elif which dzen2-textwidth &>/dev/null; then textwidth="dzen2-textwidth" elif which xftwidth &>/dev/null; then # For guix textwidth="xtfwidth" else echo "This script requires the textwidth tool of the dzen2 project." exit 1 fi #### # true if we are using the svn version of dzen2 # depending on version/distribution, this seems to have version strings like # "dzen-" or "dzen-x.x.x-svn" if dzen2 -v 2>&1 | head -n 1 | grep -q '^dzen-\([^,]*-svn\|\),'; then dzen2_svn="true" else dzen2_svn="" fi if awk -Wv 2>/dev/null | head -1 | grep -q '^mawk'; then mawk needs "-W interactive" to line-buffer stdout correctly # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=593504 uniq_linebuffered() { awk -W interactive '$0 != l { print ; l=$0 ; fflush(); }' "$@" } else # other awk versions (e.g. gawk) issue a warning with "-W interactive", so # we don't want to use it there. uniq_linebuffered() { awk '$0 != l { print ; l=$0 ; fflush(); }' "$@" } fi # get the current pulseaudio volume, or state incase muted pulsegetvol() { if [ $(pulsemixer --get-mute) == 1 ]; then echo -ne "^fg($foreground)muted\n" else volume=$(pulsemixer --get-volume | awk '{print $1; exit}') echo -n "^fg($foreground)$volume" fi } # using pactl we can subscribe to pulseaudio and check for changes pulsesub() { pactl subscribe | grep --line-buffered "change.*sink" | while read -r line; do echo -ne "pulseaudio\t$(pulsegetvol)\n" done } # used when reloading / quitting, kills all event generators as they no longer have a use list_descendants() { local children=$(ps -o pid= --ppid "$1") for pid in $children; do list_descendants "$pid" done echo "$children" } hc pad $monitor $(($panel_height + 12)) { ### Event generator ### # based on different input data (mpc, date, hlwm hooks, ...) this generates events, formed like this: # \t [...] # e.g. # date ^fg(#efefef)18:33^fg(#909090), 2013-10-^fg(#efefef)29 #mpc idleloop player & pulsesub & while true; do # "date" output is checked once a second, but an event is only # generated if the output changed compared to the previous run. date +$"date ^fg($foreground)%a %b %d %H:%M" sleep 1 || break done > >(uniq_linebuffered) & hc --idle } 2>/dev/null | { IFS=$'\t' read -ra tags <<<"$(hc tag_status $monitor)" visible=true date="" windowtitle="" volume="$(pulsegetvol)" while true; do ### Output ### # This part prints dzen data based on the _previous_ data handling run, # and then waits for the next event to happen. separator="^bg()^fg($color8)|" # draw tags for i in "${tags[@]}"; do case ${i:0:1} in '#') # tag is focused echo -n "^bg($color2)^fg()" ;; ':') # tag is not empty echo -n "^bg()^fg($foreground)" ;; '!') # tag containts an urgent window echo -n "^bg($color6)^fg()" ;; '-') # tag is viewed on a non-focused monitor echo -n "^bg($color1)^fg()" ;; *) # tag is empty echo -n "^bg()^fg($color8)" ;; esac if [ ! -z "$dzen2_svn" ]; then # clickable tags if using SVN dzen echo -n "^ca(1,$hc_quoted focus_monitor \"$monitor\" && " echo -n "$hc_quoted use \"${i:1}\") ${i:1} ^ca()" else # non-clickable tags if using older dzen echo -n " ${i:1} " fi done echo -n "$separator" echo -n "^bg()^fg() ${windowtitle//^/^^}" # small adjustments right="$volume $separator $date" right_text_only=$(echo -n "$right" | sed 's.\^[^(]*([^)]*)..g') # get width of right aligned text.. and add some space.. width=$($textwidth "$font" "$right_text_only ") echo -n "^pa($(($panel_width - $width)))$right" echo ### Data handling ### # This part handles the events generated in the event loop, and sets # internal variables based on them. The event and its arguments are # read into the array cmd, then action is taken depending on the event # name. # "Special" events (quit_panel/togglehidepanel/reload) are also handled # here. # wait for next event IFS=$'\t' read -ra cmd || break # find out event origin case "${cmd[0]}" in tag*) #echo "resetting tags" >&2 IFS=$'\t' read -ra tags <<<"$(hc tag_status $monitor)" ;; date) #echo "resetting date" >&2 date="${cmd[@]:1}" ;; quit_panel) kill $(list_descendants $$) exit ;; togglehidepanel) currentmonidx=$(hc list_monitors | sed -n '/\[FOCUS\]$/s/:.*//p') if [ "${cmd[1]}" -ne "$monitor" ]; then continue fi if [ "${cmd[1]}" = "current" ] && [ "$currentmonidx" -ne "$monitor" ]; then continue fi echo "^togglehide()" if $visible; then visible=false hc pad $monitor 0 else visible=true hc pad $monitor $(($panel_height + 12)) fi ;; reload) kill $(list_descendants $$) exit ;; focus_changed | window_title_changed) windowtitle="${cmd[@]:2}" ;; pulseaudio) volume="${cmd[@]:1}" ;; esac done ### dzen2 ### # After the data is gathered and processed, the output of the previous block # gets piped to dzen2. } 2>/dev/null | dzen2 -w $panel_width -x $x -y $y -fn mononoki-15 -h $panel_height \ -e "button3=;button4=exec:$hc_quoted use_index -1;button5=exec:$hc_quoted use_index +1" \ -ta l -bg "$background" -fg "$foreground"