infobar

Unixy status monitor
git clone git://wolog.xyz/infobar
Log | Files | Refs | README | LICENSE

infobar (7154B)


      1 #!/bin/sh
      2 
      3 # Copyright (C) Raymond Cole <rc@wolog.xyz>
      4 # License: GPL-3.0-or-later
      5 
      6 cleanup() {
      7 	set -- $(cat "$tmpdir"/pid/* 2>/dev/null)
      8 	[ $# -eq 0 ] || kill "$@"
      9 	rm -rf "$tmpdir"
     10 }
     11 
     12 humanize() {
     13 	n="$1"
     14 	b="$2"
     15 	shift 2
     16 	for u in "$@"; do
     17 		[ "$n" -ge "$b" ] || break
     18 		n=$(( (n + b / 2) / b ))
     19 	done
     20 	echo "$n$u"
     21 }
     22 
     23 runinformer() {
     24 	f="$1"; shift
     25 	[ -p "$tmpdir/fifo/$f" ] || mkfifo "$tmpdir/fifo/$f" || exit
     26 	[ -e "$tmpdir/pid/$f" ] && kill "$(cat "$tmpdir/pid/$f")"
     27 	"$@" >"$tmpdir/fifo/$f" &
     28 	echo "$!" >"$tmpdir/pid/$f"
     29 }
     30 
     31 updatebat() {
     32 	set -- "$(cat /sys/class/power_supply/BAT0/capacity)" "$bat"
     33 	for bat in '20:' '40:' '60:' '80:' '100:'; do
     34 		[ "$1" -lt "${bat%:*}" ] && break
     35 	done
     36 	bat="${bat#*:} $1%"
     37 	if grep -xFq Charging /sys/class/power_supply/BAT0/status; then
     38 		bat="$bat "
     39 	fi
     40 	[ "$bat" != "$2" ]; return  # return the result of the test
     41 }
     42 
     43 updateclk() {
     44 	case "$bar" in
     45 	*%C*) clk="$(date +'%a %b %d %T %Z %Y')" ;;
     46 	*) clk="$(date +'%a %b %d %H:%M')" ;;
     47 	esac
     48 	return 0
     49 }
     50 
     51 updatecpu() {
     52 	o="$cpu"
     53 	set -- $(cat /proc/loadavg)
     54 	cpu=" $1"
     55 	case "$bar" in
     56 	*%P*)
     57 		set -- $(head -n 1 /proc/stat)
     58 		shift
     59 		u=$(( $1 + $2 + $3 + $6 + $7 ))
     60 		s=$(( u + $4 + $5 ))
     61 		if [ -n "$cpuuse$cpusum" ]; then
     62 			cpu="$cpu $(( (u - cpuuse) * 100 / (s - cpusum) ))%"
     63 		fi
     64 		cpuuse="$u"
     65 		cpusum="$s"
     66 		if f="$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq)"; then
     67 			t="$(humanize "$(( f * 10 ))" 1000 '' K M G)"
     68 			f="${t%[[:digit:]]*}"
     69 			f="${f:-0}.${t#$f}"
     70 			cpu="$cpu ${f}Hz"
     71 		fi
     72 	;;
     73 	esac
     74 	[ "$cpu" != "$o" ]; return
     75 }
     76 
     77 updatemem() {
     78 	set -- $(free | sed -n '2{p;q}')
     79 	shift
     80 	o="$mem"
     81 	x="$(humanize "$(( $1 * 10 ))" 1024 Ki Mi Gi)"
     82 	t="${x%[[:digit:]]*}"
     83 	t="${t:-0}.${x#$t}B"
     84 	x="$(humanize "$(( $2 * 10 ))" 1024 Ki Mi Gi)"
     85 	u="${x%[[:digit:]]*}"
     86 	u="${u:-0}.${x#$u}B"
     87 	mem=" $u/$t"
     88 	[ "$mem" != "$o" ]; return
     89 }
     90 
     91 updatenet() {
     92 	set -- "$net" $(ip route | sed -n 's/^default\( via \([^ ]*\)\)* dev \([^ ]*\).*/\3 \2/p;q')
     93 	if [ $# -ge 2 ] && grep -xFq up "/sys/class/net/$2/operstate"; then
     94 		case "$2" in
     95 		en*|eth*) net='' ;;
     96 		wl*) net='' ;;
     97 		ww*) net='' ;;
     98 		*) net="$2" ;;
     99 		esac
    100 		[ -n "$3" ] || net="$net "
    101 		case "$bar" in
    102 		*%N*)
    103 			case "$2" in
    104 			wl*)
    105 				x="$(sed -n "s/^$2:"' *[^ ]* *[^ ]* *\([^.]*\).*/\1/p' /proc/net/wireless)"
    106 				net="$net ${x}dBm"
    107 				;;
    108 			esac
    109 			x="$(cat "/sys/class/net/$2/statistics/rx_bytes")"
    110 			[ -z "$netrx" ] || net="$net  $(humanize $(( x - netrx )) 1024 B KiB MiB)"
    111 			netrx="$x"
    112 			x="$(cat "/sys/class/net/$2/statistics/tx_bytes")"
    113 			[ -z "$nettx" ] || net="$net  $(humanize $(( x - nettx )) 1024 B KiB MiB)"
    114 			nettx="$x"
    115 			;;
    116 		esac
    117 	else
    118 		net=''
    119 	fi
    120 	[ "$net" != "$1" ]; return
    121 }
    122 
    123 updatevol() {
    124 	set -- $(amixer sget Master | sed -n 's/^.*\[\([0-9]\+\)%\].*\[\(on\|off\)\]/\1 \2/p;tL;d;:L;q')
    125 	if [ "$2" = on ]; then
    126 		for vol in '0:' '30:' '100:'; do
    127 			[ "$1" -le "${vol%:*}" ] && break
    128 		done
    129 		vol="${vol#*:} $1%"
    130 	else
    131 		vol=" $1%"
    132 	fi
    133 	return 0
    134 }
    135 
    136 setbar() {
    137 	bar="$1"
    138 	modules=
    139 	o=
    140 	s="$IFS"
    141 	IFS=' %'
    142 	for x in $bar; do
    143 		modules="$x $modules"
    144 		case "$x" in
    145 		[[:upper:]]) o="s${o#s}" ;;
    146 		[bc]) o="${o%m}m" ;;
    147 		esac
    148 	done
    149 	IFS="$s"
    150 	if [ "$ttopts" != "$o" ]; then
    151 		runinformer t ticktack ${o:+-$o}
    152 		ttopts="$o"
    153 	fi
    154 }
    155 
    156 changebar() {
    157 	setbar "${bar%%"$1"*}$2${bar#*"$1"}"
    158 }
    159 
    160 putbar() {
    161 	s="$IFS"
    162 	IFS=%
    163 	for x in $bar; do
    164 		t="${x%% *}"
    165 		case "${t#%}" in
    166 		b)    t="$bat" ;;
    167 		[Cc]) t="$clk" ;;
    168 		M)    t="$mem" ;;
    169 		[Nn]) t="$net" ;;
    170 		[Pp]) t="$cpu" ;;
    171 		v)    t="$vol" ;;
    172 		esac
    173 		printf '%s' "$t${x##*[! ]}"
    174 	done
    175 	IFS="$s"
    176 	echo
    177 }
    178 
    179 tmpdir="/tmp/infobar.$$"
    180 ttopts=
    181 bat=
    182 clk=
    183 cpu=
    184 cpuuse=
    185 cpusum=
    186 mem=
    187 net=
    188 netrx=
    189 nettx=
    190 vol=
    191 
    192 trap cleanup INT TERM EXIT
    193 mkdir "$tmpdir" && mkdir "$tmpdir/fifo" && mkdir "$tmpdir/pid" || exit
    194 
    195 # Some available modules:
    196 #
    197 # + %n - network connection (depends on the ip command)
    198 # + %v - volume and mutedness (depends on alsactl)
    199 # + %p - processor load over the last minute
    200 # + %b - battery percentage and charging state (depends on acpi_listen)
    201 # + %c - clock with the precision of minutes
    202 #
    203 # Among them %n and %v are updated upon relevant output of `ip monitor
    204 # route` and `alsactl monitor`, respectively.  %p, %b and %c are updated
    205 # upon every minute.  %b is in addition updated upon relevant output of
    206 # `acpi_listen`.
    207 #
    208 # The other modules have uppercase names and are updated every second:
    209 #
    210 # + %N - like %n but also shows signal strength (if wireless) and network traffic
    211 # + %P - like %p but also shows CPU usage and frequency
    212 # + %C - like %c but more detailed and shows seconds
    213 # + %M - memory usage
    214 #
    215 # Generally, the modules should be used in a specific format: Modules
    216 # should be separated by two or more spaces and nothing else.  No module
    217 # should appear more than once, and a lowercase module and its uppercase
    218 # counterpart shouldn't both be used.  The freedom lies mainly in deciding
    219 # which modules are included and in what order.
    220 
    221 setbar ' %n  %v  %p  %b  %c '
    222 
    223 # remove the %b module if there is no BAT0 (e.g. on desktop computer)
    224 [ -r /sys/class/power_supply/BAT0/capacity ] || changebar ' %b ' ''
    225 
    226 for mod in $modules; do
    227 	case "$mod" in
    228 	b)
    229 		runinformer a acpi_listen
    230 		updatebat
    231 		;;
    232 	[Cc])
    233 		updateclk
    234 		;;
    235 	M)
    236 		updatemem
    237 		;;
    238 	[Nn])
    239 		runinformer n ip monitor route
    240 		updatenet
    241 		;;
    242 	[Pp])
    243 		updatecpu
    244 		;;
    245 	v)
    246 		runinformer v alsactl monitor
    247 		updatevol
    248 		;;
    249 	esac
    250 done
    251 
    252 putbar
    253 
    254 fifolog - "$tmpdir"/fifo/* | while read -r src tok toks; do
    255 	case "$src" in
    256 	-)
    257 		for mod in $modules; do
    258 			[ "$tok" -gt 1 ] || break
    259 			tok=$(( tok - 1 ))
    260 		done
    261 		button="${toks%% *}"
    262 		case "$mod" in
    263 		[Cc])
    264 			if [ "$button" -eq 1 ]; then
    265 				if [ "$mod" = c ]; then
    266 					changebar %c %C
    267 				else
    268 					changebar %C %c
    269 				fi
    270 				updateclk && putbar
    271 			fi
    272 			;;
    273 		[MPp])
    274 			if [ "$button" -eq 1 ]; then
    275 				case "$bar" in
    276 				*'%P  %M'*)
    277 					changebar '%P  %M' %p
    278 					cpuuse=
    279 					cpusum=
    280 					updatecpu
    281 					putbar
    282 					;;
    283 				*%p*)
    284 					changebar %p '%P  %M'
    285 					updatecpu
    286 					updatemem
    287 					putbar
    288 					;;
    289 				esac
    290 			fi
    291 			;;
    292 		N)
    293 			if [ "$button" -eq 1 ]; then
    294 				changebar %N %n
    295 				netrx=
    296 				nettx=
    297 				updatenet && putbar
    298 			fi
    299 			;;
    300 		n)
    301 			if [ "$button" -eq 1 ]; then
    302 				changebar %n %N
    303 				updatenet && putbar
    304 			fi
    305 			;;
    306 		v)
    307 			if [ "$button" -eq 3 ]; then
    308 				amixer -q set Master toggle
    309 				updatevol && putbar
    310 			elif [ "$button" -eq 4 ]; then
    311 				amixer -q set Master 5%+
    312 				updatevol && putbar
    313 			elif [ "$button" -eq 5 ]; then
    314 				amixer -q set Master 5%-
    315 				updatevol && putbar
    316 			fi
    317 			;;
    318 		esac
    319 		;;
    320 	a)
    321 		case "$bar" in
    322 		*%b*)
    323 			[ "$tok" = ac_adapter ] && updatebat && putbar
    324 			;;
    325 		esac
    326 		;;
    327 	[Nn])
    328 		case "$tok" in
    329 		Deleted|default)
    330 			updatenet && putbar
    331 			;;
    332 		esac
    333 		;;
    334 	t)
    335 		tokbar="$tok$bar"
    336 		case "$tokbar" in
    337 		[sm]*%b*) updatebat && putbar ;
    338 		esac
    339 		case "$tokbar" in
    340 		m*%c*|[sm]*%C*) updateclk && putbar ;;
    341 		esac
    342 		case "$tokbar" in
    343 		[sm]*%M*) updatemem && putbar ;;
    344 		esac
    345 		case "$tokbar" in
    346 		[sm]*%N*) updatenet && putbar ;;
    347 		esac
    348 		case "$tokbar" in
    349 		[sm]*%[Pp]*) updatecpu && putbar ;;
    350 		esac
    351 		;;
    352 	v)
    353 		updatevol && putbar
    354 		;;
    355 	!)
    356 		[ "$tok" = t ] || exit 1
    357 		;;
    358 	esac
    359 done