#!/bin/bash # Global readonly variables can't be shadowed by local variables so wrap # our code in a function so we can declare all variables local main() { # Customizable configuration constants local -r DEFAULT_MOD_LIST='cpu mem bl vol-amixer bat dt' local -r DEFAULT_RES=.25 local -r DEFAULT_PRE=' ' local -r DEFAULT_SEP_L='| ' local -r DEFAULT_SEP_R=' ' local -r DEFAULT_SUF=' ' local -r mod_list="${1-${DEFAULT_MOD_LIST}}" local -r res="${2:-${DEFAULT_RES}}" local -r pre="${3-${DEFAULT_PRE}}" local -r sep_l="${4-${DEFAULT_SEP_L}}" local -r sep_r="${5-${DEFAULT_SEP_R}}" local -r suf="${6-${DEFAULT_SUF}}" local -r MOD_DIR="$(dirname "$0")"/module local -r ACTION_DIR=/tmp/xrsb-action local -i ACTION_DIR_LEN=${#ACTION_DIR} # Cache module values so we can reuse them without recomputing them local -A stat_cache # Since stat_cache is hash ordered, maintain the display order (as defined # by mod_list) of the keys so we can loop over the cache in display order # when generating the full status local -a stat_cache_ordered_mods local mod mod_file # Map the module file name to the module function mod_to_fn() { printf 'mod_%s' "${1//-/_}" } # For each module in the list, if the module file exists then source it, add # its name to the ordered array, and call its function and cache the value for mod in ${mod_list}; do mod_file="${MOD_DIR}/${mod}" if [[ -r "${mod_file}" ]]; then # shellcheck source=/dev/null source "${mod_file}" stat_cache_ordered_mods+=("${mod}") stat_cache["${mod}"]="$("$(mod_to_fn "${mod}")")" fi done # Construct and display the status by looping over the cached values in order draw_status() { local mod stat for mod in "${stat_cache_ordered_mods[@]}"; do printf -v stat '%b%b%b%b' \ "${stat}" "${sep_l}" "${stat_cache[${mod}]}" "${sep_r}" done # Trim the leading left separator and trailing right separator, and # display the status local -ri offset=${#sep_l} local -ri len=$((${#stat} - offset - ${#sep_r})) xsetroot -name "${pre}${stat:${offset}:${len}}${suf}" } # Draw the initial status draw_status # For each file in the action directory, remove the file, and if a module for # the action is cached, call the module function and update the cache. If # any cache entries were updated, redraw the status. process_signal () { local -a action_paths local action_path mod is_changed readarray -d '' action_paths< \ <(find "${ACTION_DIR}" -maxdepth 1 -type f -exec rm -f {} + -print0) for action_path in "${action_paths[@]}"; do mod="${action_path:$((ACTION_DIR_LEN + 1))}" if [[ -v stat_cache[${mod}] ]]; then stat_cache["${mod}"]="$("$(mod_to_fn "${mod}")")" is_changed=1 fi done if [[ -v is_changed ]]; then draw_status; fi } # Wait for signals mkdir -p "${ACTION_DIR}" trap process_signal SIGUSR1 while :; do sleep "${res}" done } main "$@"