tmeeting-at-the-bar.txt - monochromatic - monochromatic blog: http://blog.z3bra.org
HTML git clone git://z3bra.org/monochromatic
DIR Log
DIR Files
DIR Refs
---
tmeeting-at-the-bar.txt (20295B)
---
1 # Meeting at the bar
2
3 02 April, 2014
4
5 ## Introduction
6
7 Hi everybody ! This post is more or less the continuation of my previous one:
8 [Home, sweet home](/2013/10/home-sweet-home.html). We will take desktop
9 customisations a little further here, so I'll assume that you, the reader, know
10 the base of linux system administration.
11
12 Status bar can display anything you feed them with, so let's make that food
13 useful to us. We will learn how to display system informations on your screen.
14
15 Here we go!
16
17 ## Summary
18
19 * [head-up display](#hud)
20 * [fetching informations](#grab)
21
22
23 <h3 id='hud'>head-up display</h3>
24
25 First of all, let's understand what an HUD is. Gamers can go to the next
26 paragraph. An HUD display some information you (most of the time) wants to see.
27 In video-games, that will be your life/armor points, or the number of ammos
28 loaded in your gun. Those informations are almost always visible and are updated
29 in real-time.
30
31 But we're not playing video games here. And you're probably not reading with a
32 loaded gun in your hand. So, sitting in front of your computer, you want other
33 kind of informations. We will talk about those informations later.
34 First, let's see HOW we will display them on-screen. I currently know 4 ways of
35 doing it (understand, I've not tried the alternatives):
36
37 * dzen
38 * conky
39 * tmux
40 * bar
41
42 ### dzen
43
44 From the official website:
45
46 > Dzen is a general purpose messaging, notification and menuing program for
47 > X11. It was designed to be scriptable in any language and integrate well
48 > with window managers like dwm, wmii and xmonad though it will work with
49 > any windowmanger.
50
51 Seems great ! Dzen is really simple to use, pipe text to it, and watch it
52 appear on your screen:
53
54 echo shblah | dzen2 -w 120 -x 10 -y 10 -p 5
55
56 I'm not a huge fan of dzen anymore. I used to be (You can do awesome things
57 with dzen, just check [earsplit's dekstop](http://i.imgur.com/bZegioR.gif), but
58 I discovered a new tool that is basically dzen simplified, and written on top of
59 XCB (see the fourth alternative: bar).
60
61 ### conky
62
63 Here comes the king of HUDs, ladies and gentlemen, please put a knee to the
64 ground!
65 Conky's job is to display every possible information on your screen, in a really
66 eye-candy way. I made
67 [this](http://pix.toile-libre.org/upload/original/1360670013.jpg), monthes ago
68 using conky (do not ask for configs or wallpaper, I don't have them anymore).
69
70 It is extensible in lua and has a heavy set of features built-in.
71 Check this out: [conky's variables](http://conky.sourceforge.net/variables.html)
72 I've been using conky for weeks, but I just got bored. I realised that I did not
73 need so much infos in real-time. But that was a fun period !
74
75 Conky reads its configuration from the `~/.conkyrc` file. A minimalist config
76 would be:
77
78 cat <<EOF > ~/.conkyrc
79 alignment tl
80 gap_x 10
81 gap_y 40
82
83 TEXT
84 shblah
85 EOF
86 conky &
87
88 But for such a simple thing, that's a bit overkill.
89 Note that there is also conky-cli that outputs informations to stdout. That is
90 useful to build informations lines to feed a bar with. To have a quick idea of
91 how this works, check this
92 [nice forum post](http://nixers.net/showthread.php?tid=117) by jmbi.
93
94 ### tmux statusbar
95
96 This one is a bit out of competition, but worth mentionning. TMux stands for
97 terminal multiplexer. Short story, you can have multiple terminal within a
98 single terminal window. But it offers a nice feature: a status bar, that is
99 displayed at the bottom of the window. You can run any command in here, or even
100 shell scripts, and the output will sit just there.
101
102 Tmux actually have a left and right status bar. So just pick one (or both) to
103 display some infos:
104
105 echo "set-option -g status-left "shblah" >> ~/.tmux.conf
106 tmux
107
108 Phyrne wrote a [nice article](http://calummacrae.blogspot.co.uk/2012/12/dropping-status-bars-for-tmux-as-im.html)
109 about it. Just read it.
110
111 ### bar
112
113 My last and prefered option, the bar made by LemonBoy !
114 This small piece of software is a stripped down clone of dzen, written on top
115 of XCB (a better librairy to communicate with the X server). It's fast, it's
116 light and you can even script it, as it now has a clickable item that you can
117 use to start applications. More infos [here](https://github.com/LemonBoy/bar).
118
119 Bar is pretty easy to use. It works the same way dzen does, by piping text to
120 it:
121
122 echo 'shblah' | bar -g 120x20+10+80 -p
123
124 Starting from now, I will use bar as my tool of choice, but use the one you
125 prefer, they can all do such a thing (well, conky has pretty much everything
126 done for you, but meh) !
127
128 <h3 id='grab'>fetching informations</h3>
129
130 Once you now which tool you'll use to display your informations, you need to
131 decide which one you want. For the purpose of the article, I'll settle on 8 of
132 them:
133
134 * current date / time
135 * battery level
136 * sound level
137 * CPU load
138 * RAM used
139 * network connection state
140 * window manager groups
141 * mpd's current playing song
142
143 I choosed those to show you many ways to fetch informations on your computer.
144
145 Before going any further, I need to introduce you to the tools we'll need, and
146 that we just CAN'T avoid to fetch informations..
147
148 * `awk` -- a powerfull script language, I don't know enough about this, though
149 * `cut` -- cut a string in small parts, and pick some parts of it
150 * `grep` -- should I really present 'grep' ?
151 * `sed` -- stream editor, it's useful to format outputs
152 * `test` -- test an expression and return 0 if it's true, >0 otherwise
153 * `tr` -- translate or delete characters from the input
154
155 By the way, that would be a **HUGE** plus to know about [regular
156 expressions](https://en.wikipedia.org/wiki/Regular_expression), because we are
157 going to use them _a lot_ with `sed`.
158 So, here we go!
159
160 ### current date / time
161
162 There is nothing hard with it. The `date` utility has a parameter to format its
163 output. So we'll just use that:
164
165 date '+%Y-%m-%d %H:%M' # print current date and time: yyyy-mm-dd HH:MM
166
167 ### battery level
168
169 There is this tool, `acpi` that can be used to output some infos on your system
170 power. But that's just not fun! We'll be messing with the `/sys` directory
171 instead, which is a goldmine. Feel free to navigate it, to see what you can
172 find.
173
174 Back to the battery. We are interested in two information, the current
175 charge of the battery (in percent) and if the charger is plugged in, or not. _on
176 my system_ (because it may be different on yours), I have those two files:
177
178 /sys/class/power_supply/BAT1/capacity # contains a value from 0 to 100
179 /sys/class/power_supply/BAT1/status # either "Charging" or "Discharging"
180
181 We will then be able to output the battery level, and do some action, depending
182 on the battery state. To get the info:
183
184 BATC=/sys/class/power_supply/BAT1/capacity
185 BATS=/sys/class/power_supply/BAT1/status
186
187 # prepend percentage with a '+' if charging, '-' otherwise
188 test "`cat $BATS`" = "Charging" && echo -n '+' || echo -n '-'
189
190 # print out the content (forced myself to use `sed` :P)
191 sed -n p $BATC
192
193 ### sound level
194
195 This one is always a pain.. I will assume that you use ALSA as your sound
196 system (because I have no idea how OSS or PulseAudio works).
197
198 First, you need to know which channel your want to watch. Most of the time,
199 'Master' is a good choice. I personnally use `alsamixer` to navigate between the
200 channels to see what they are related to. The `alsa-utils` packages (name may
201 vary depending on the distribution) contains a utility named `amixer` to
202 interact with the system. The special command `amixer get <CONTROL>` is used to
203 query informations about a channel. But the output is awful to look at, so we'll
204 need to format it. Example output:
205
206 ───── amixer get Master
207 Simple mixer control 'Master',0
208 Capabilities: pvolume pvolume-joined pswitch pswitch-joined
209 Playback channels: Mono
210 Limits: Playback 0 - 64
211 Mono: Playback 53 [84%] [-10.00dB] [on]
212
213 You can notice that the info we're interested in sits at the end of the output.
214 That will make things easier.
215
216 # parse amixer output to get ONLY the level. Will output "84%"
217 # we need `uniq` because on some hardware, The master is listed twice in
218 # "Front Left" and Front Right" (because laptop speakers I guess)
219 amixer get Master | sed -n 's/^.*\[\([0-9]\+\)%.*$/\1/p'| uniq
220
221 ### CPU load
222
223 There are many way to get the current CPU load. `iostat` is one of them, and as
224 it's easy to parse its output, i'll go with a trickier approach, using `ps` and
225 `bc`.
226
227 To get the current CPU load used by every program, one can use this command:
228
229 # gives you the CPU load used by every running program
230 # 'args' is used here just so you can see the programs command lines
231
232 ps -eo pcpu,args
233
234 We don't care about idling programs that uses '0.0' load or the header '%CPU',
235 so we can just remove them with `grep -vE '^\s*(0.0|%CPU)'`.
236
237 ps -eo pcpu | grep -vE '^\s*(0.0|%CPU)'
238
239 We now have a list of the CPU loads actually used, but per program. We just need
240 to sum them up!
241 The problem is: bash _CAN'T_ perform floating point operations. And thus, we
242 will need the help of the great `bc` to do so (if you don't have this installed,
243 I recommend that you just get it right away!).
244
245 `bc` takes operations from stdin, and outputs to stdout. Pretty simple. Pretty
246 good.
247 Thanks to
248 [randomcrocodile](http://www.reddit.com/r/unixporn/comments/220diq/howto_create_an_informative_status_bar_for_your/cgi9hve)
249 for pointing out the two digit problem (and other things)
250
251 # use the "here-line" feature.
252 # The whole line goes to bc which outputs the result
253
254 LINE=`ps -eo pcpu |grep -vE '^\s*(0.0|%CPU)' |sed -n '1h;$!H;$g;s/\n/ +/gp'`
255 bc <<< $LINE
256
257 **NOTE**: *verkgw* on irc.blinkenshell.org proposed a faster `awk` alternative.
258 I don't know awk enough to come up with this kind of line, so I'll just continue
259 with `grep` and `sed`. See the comparison [here](http://i.imgur.com/Aefbl8U.png)
260
261 ps -eo pcpu | awk 'BEGIN {sum=0.0f} {sum+=$1} END {print sum}'
262
263 ### RAM used
264
265 To display RAM usage (percentage of RAM actually by the system), we will use
266 another place of the filesystem: `/proc`. This will be easier to find memory
267 usage here, than battery level in `/sys`:
268
269 ───── ls /proc/ | grep 'mem'
270 iomem
271 meminfo
272
273 If you take a quick look at `iomem`, you'll understand that it's **NOT** the
274 file we want here (I don't understand a bit of it)! Instead, let's take a look
275 at meminfo:
276
277 ───── sed 8q /proc/meminfo
278 MemTotal: 2748648 kB
279 MemFree: 2209672 kB
280 Buffers: 34016 kB
281 Cached: 270728 kB
282 SwapCached: 0 kB
283 Active: 182292 kB
284 Inactive: 272636 kB
285 Active(anon): 150948 kB
286
287 Good, good, exactly the information we want! So let's just extract them, using
288 `awk` to fetch _ONLY_ the column containing the value (Yeah, that's why I use
289 awk for mostly. I'll need to dive a little more in that language):
290
291 ───── grep -E 'Mem(Total|Free)' /proc/meminfo |awk '{print $2}'
292 2748648
293 2204288
294
295 At this point, you might realise that those two number are not really useful. We
296 will need to modify them a little by converting them to Mib, and making a ratio
297 out of them. A neat alternative would be to ignore cached memory and buffers, to
298 know exactly how much the applications are taking:
299
300 # store the total and free memory in two variables
301 read t f <<< `grep -E 'Mem(Total|Free)' /proc/meminfo |awk '{print $2}'`
302 read b c <<< `grep -E '^(Buffers|Cached)' /proc/meminfo |awk '{print $2}'`
303
304 # then, calcultate the percentage of memory used
305 bc <<< "100($t -$f -$c -$b) / $t"
306
307 ### network connection state
308
309 Mmh, this one can be tricky! Ther are two cases here:
310
311 * You have one interface
312 * You have more than one interface
313
314 The first one is quite simple: use your interface name directly, and skip the
315 following section.
316
317 Now what if you have, let's say two interfaces: ethernet, and wifi. Let's find
318 out HOW to get the currently used.
319 We will need two tools for that: `ip` (from `iproute2`) and `iwconfig` (from
320 `wireless_tools`). We will get the interfaces with `ip`, and recognize the wifi
321 interface using `iwconfig`. Sounds easy huh ?
322
323 # The following assumes you have 3 interfaces: loopback, ethernet, wifi
324 read lo int1 int2 <<< `ip link | sed -n 's/^[0-9]: \(.*\):.*$/\1/p'`
325
326 # iwconfig returns an error code if the interface tested has no wireless
327 # extensions
328 if iwconfig $int1 >/dev/null 2>&1; then
329 wifi=$int1
330 eth0=$int2
331 else
332 wifi=$int2
333 eth0=$int1
334 fi
335
336 # in case you have only one interface, just set it here:
337 # int=eth0
338
339 # this line will set the variable $int to $eth0 if it's up, and $wifi
340 # otherwise. I assume that if ethernet is UP, then it has priority over
341 # wifi. If you have a better idea, please share :)
342 ip link show $eth0 | grep 'state UP' >/dev/null && int=$eth0 || int=$wifi
343
344 This is now the time to see if network is up or not. For that, a simple `ping`
345 would do the trick:
346
347 # just output the interface name. Could obviously be done in the 'ping'
348 # query
349 echo -n "$int"
350
351 # Send a single packet, to speed up the test. I use google's DNS 8.8.8.8,
352 # but feel free to use any ip address you want. Be sure to put an IP, not a
353 # domain name. You'll bypass the DNS resolution that can take some precious
354 # miliseconds ;)
355 ping -c 1 8.8.8.8 >/dev/null 2>&1 && echo "connected" || echo "disconnected"
356
357
358 ### window manager groups
359
360 Aaah, the information that has the most way to be fetched! The problem with
361 this, is that every window manager provide a different way to fetch the number
362 of workspaces, and the current one. If you're lucky, and that your WM is
363 [EWMH](https://en.wikipedia.org/wiki/EWMH) compliant, `xprop` will be the way to
364 go. For the others, you will need to find a proper way on your own. For exemple,
365 to get the number of groups and the current group with ratpoison:
366
367 echo "`ratpoison -c groups| cut -sd'*' -f1`/`ratpoison -c groups| wc -l`"
368
369 Back to the topic, fetching current group out of all the groups. To make this a
370 little more exiting, we will output something like "`==|=====`", '|' being the
371 current desktop, '=' being the other desktops.
372
373 The first step is to fetch the number of desktops, and the index of the current
374 one. To do that, let's use `xprop`
375
376 cur=`xprop -root _NET_CURRENT_DESKTOP | awk '{print $3}'
377 tot=`xprop -root _NET_NUMBER_OF_DESKTOPS | awk '{print $3}'
378
379 If that enough for you, you can obviously just output `$cur/$tot` ;)
380 But now, the desktop indicator. To do that, there is two solutions:
381
382 * cicle through all the groups and output either '=' or '|'
383 * ouput the correct number of '|' before and after '|'
384
385 I tried both versions, and `time` reports that they are they're _almost_ the
386 same:
387
388 ───── time cicle.sh
389 ==|=======
390
391 real 0m0.025s
392 user 0m0.013s
393 system 0m0.000s
394
395 ───── time fillup.sh
396 ==|=======
397
398 real 0m0.020s
399 user 0m00m0.013s
400 system 0m0.000s
401
402 We will then use the 'fillup' one. To improve performances, we will first fill a
403 variable with the 'group line', and then output it. It goes like this:
404
405 # Desktop numbers start at 0. if you want desktop 2 to be in second place,
406 # start counting from 1 instead of 0. But wou'll lose a group ;)
407 for w in `seq 0 $((cur - 1))`; do line="${line}="; done
408
409 # enough =, let's print the current desktop
410 line="${line}|"
411
412 # En then the other groups
413 for w in `seq $((cur + 2)) $tot`; do line="${line}="; done
414
415 # don't forget to print that line!
416 echo $line
417
418 ### mpd's current playing song
419
420 After all that we did alredy, printing the current playing should bequite easy
421 as:
422
423 cur=`mpc current`
424 test -n "$cur" && echo $cur || echo "- stopped -"
425
426 Easy isn't it ? So let's add some difficulties to it. What if you have only 120
427 pixels to display that ?
428 Aaaah trickyer isn't it ?
429
430 Don't worry, I wrote a small tool for that:
431 [skroll](http://git.z3bra.org/skroll/log.html). You can see it in action
432 [here](http://pub.z3bra.org/monochromatic/img/2014-03-28-skroll.gif).
433
434 So now, our output has just become:
435
436 cur=`mpc current`
437 test -n "$cur" && echo $cur |skroll -n 20 -d0.5 -r || echo "- stopped -"
438
439 A small drawback with this approach: you can't put other infos in the same bar
440 as a `skroll`ing output, because it uses a `\n` or a `\r` to print the output.
441
442 ### wrap it all !
443
444 Now that we have a whole bunch of informations, it's time to put them all in a
445 script, that we will pipe later to our HUD.
446
447 #!/bin/sh
448 #
449 # z3bra - (c) wtfpl 2014
450 # Fetch infos on your computer, and print them to stdout every second.
451
452 clock() {
453 date '+%Y-%m-%d %H:%M'
454 }
455
456 battery() {
457 BATC=/sys/class/power_supply/BAT1/capacity
458 BATS=/sys/class/power_supply/BAT1/status
459
460 test "`cat $BATS`" = "Charging" && echo -n '+' || echo -n '-'
461
462 sed -n p $BATC
463 }
464
465 volume() {
466 amixer get Master | sed -n 'N;s/^.*\[\([0-9]\+%\).*$/\1/p'
467 }
468
469 cpuload() {
470 LINE=`ps -eo pcpu |grep -vE '^\s*(0.0|%CPU)' |sed -n '1h;$!H;$g;s/\n/ +/gp'`
471 bc <<< $LINE
472 }
473
474 memused() {
475 read t f <<< `grep -E 'Mem(Total|Free)' /proc/meminfo |awk '{print $2}'`
476 bc <<< "scale=2; 100 - $f / $t * 100" | cut -d. -f1
477 }
478
479 network() {
480 read lo int1 int2 <<< `ip link | sed -n 's/^[0-9]: \(.*\):.*$/\1/p'`
481 if iwconfig $int1 >/dev/null 2>&1; then
482 wifi=$int1
483 eth0=$int2
484 else
485 wifi=$int2
486 eth0=$int1
487 fi
488 ip link show $eth0 | grep 'state UP' >/dev/null && int=$eth0 ||int=$wifi
489
490 #int=eth0
491
492 ping -c 1 8.8.8.8 >/dev/null 2>&1 &&
493 echo "$int connected" || echo "$int disconnected"
494 }
495
496 groups() {
497 cur=`xprop -root _NET_CURRENT_DESKTOP | awk '{print $3}'`
498 tot=`xprop -root _NET_NUMBER_OF_DESKTOPS | awk '{print $3}'`
499
500 for w in `seq 0 $((cur - 1))`; do line="${line}="; done
501 line="${line}|"
502 for w in `seq $((cur + 2)) $tot`; do line="${line}="; done
503 echo $line
504 }
505
506 nowplaying() {
507 cur=`mpc current`
508 # this line allow to choose whether the output will scroll or not
509 test "$1" = "scroll" && PARSER='skroll -n20 -d0.5 -r' || PARSER='cat'
510 test -n "$cur" && $PARSER <<< $cur || echo "- stopped -"
511 }
512
513 # This loop will fill a buffer with our infos, and output it to stdout.
514 while :; do
515 buf=""
516 buf="${buf} [$(groups)] -- "
517 buf="${buf} CLK: $(clock) -"
518 buf="${buf} NET: $(network) -"
519 buf="${buf} CPU: $(cpuload)%% -"
520 buf="${buf} RAM: $(memused)%% -"
521 buf="${buf} VOL: $(volume)%%"
522 buf="${buf} MPD: $(nowplaying)"
523
524 echo $buf
525 # use `nowplaying scroll` to get a scrolling output!
526 sleep 1 # The HUD will be updated every second
527 done
528
529
530 All you have to do now is to pipe this script to your status bar of choice:
531 `./barmk.sh | bar`.
532
533 There you are! You now know how (ow ow ow) make your system talk to you.
534 Obviously, this is a raw script, and it can be heavily improved (eg, add some
535 colors, parse CLI arguments, etc..).
536
537 But I'm pretty sure that it's a good start for your imagination. By the way, if
538 you find neat tricks to improve the performances of the functions listed above,
539 feel free to mail me these, I'll be glad to modify them!
540
541 [](http://pub.z3bra.org/monochromatic/img/2014-04-02-barmk.png)
542 *Before leaving you, here is what I got using this script
543 (with some tweaks, see the `diff` output)*
544
545 *Oh, and for reference. It tried to get the same result
546 with conky and barmk, just to see the difference. Note how conky does not
547 display float numbers (for CPU). Also, I was not able to recreate a desktop bar with conky, so I "downgraded"
548 the barmk script to make the battle more fair.*[](http://pub.z3bra.org/monochromatic/img/2014-04-10-conky-mkbar.png)
549
550 Now go ahead, and watch how your computer tell you how he (or she) feels...
551 Isn't that **amazing** ?!