URI: 
       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 [![barmk showcase](http://pub.z3bra.org/monochromatic/img/thumb/2014-04-02-barmk.png)](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.*[![barmk vs conky](http://pub.z3bra.org/monochromatic/img/thumb/2014-04-10-conky-mkbar.png)](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** ?!