––» HOME «––

lemonbar
a minimalistic (windowmanager) panel

Table of Contents

1. Lemonbar

I really like to configure everything to my own personal needs… That's why I'm using lemonbar (https://github.com/LemonBoy/bar) as my window manager panel.

lemonbar (formerly known as bar) is a lightweight bar entirely based on XCB. Provides full UTF-8 support, basic formatting, RandR and Xinerama support and EWMH compliance without wasting your precious memory

Because I am a bspwm user (a great minimalist window manager https://github.com/baskerville/bspwm), I had to think about which panel I'd actually wanted to use, since bspwm doesn't have one out of the box. I first used pypanel a lot, but… nowadays running an older python2 panel seemed inappropriate. That's when I took a look at lemonbar. It's a little C program, which puts a black (or any color you want) bar at the top or bottom of your window manager's desktop. You can pipe text into it, which it will display. That way you are able to build your very own panel by simply writing a shell script and piping it to lemonbar.

1.1. Usage

You can simply clone the repository from github and build it right away like this:

git clone https://github.com/LemonBoy/bar.git lemonbar
cd lemonbar
make
# (or maybe even make install)

After building it, you should have a binary called lemonbar. If you run it without any arguments, you should see a black bar appearing on the top of your screen. Now you're ready to write a simple shell script, - conataining a loop - and pipe it's output to lemonbar, to see the results in your panel.

1.1.1. First script

You should probably start with something elementary like:

#!/bin/bash

while true; do
  echo "%{c}%{B#770}%{F#000}Hello world"
  sleep 1
done

This example is using some format strings like %{c} to display the text at the center of the panel (as you can probably guess, it's l for left and r for right) or %{B#770} to set the background color or %{F#000} to set the font color. Colors can be set in hex values like #rrggbb (or even with an alpha value at he beginning #aarrggbb) or just #rgb.

Let's say we save this test file as test.sh. Now you can pipe the output into lemonbar like this:

./test.sh | lemonbar

This way you can easily display things like date, time, day of the week, battery status or even the CPU load. But as you can guess that simple while true loop will get confusing when we add too much stuff to display. That's when more advanced scripts come into play :D

1.1.2. More advanced scripts

I like putting the stuff to display in simple functions. So I can turn them on and off, like this:

clock() {
        DATETIME=$(date "+%a %d.%b %T")

        echo -n "$DATETIME"
}
while true; do

    buf="%{l}%{B#000}%{F#FFF} $(clock) "
# a commented out centered clock
#    buf="%{c}%{B#000}%{F#FFF} $(clock) "
    buf="${buf} %{c}Hello World!"
      echo $buf
      sleep 1
done;

This function simply runs date "+%a %d.%b %T" wich gives you an output like this: Mi 23.Nov 22:55:04. The loop below defines a variable called buf and concatinates the output of the functions (in this case only one function and a Hello World! string). As I said, I like it that way, because I can comment things out bery easy :) Then it echos the buffer. When we pipe this script into lemonbar, we get a black bar with the date output on the left and a Hello World! at the center.

  1. Battery Status

    A simple function to diplay the battey percentage might look like this:

    Battery() {
            BATPERC=$(acpi --battery | cut -d, -f2)
            echo "$BATPERC"
    }
    

    Which outputs the percentage like 96%. I like to have it on the right side of my panel, so I'd go like this:

    buf="${buf}%{r}$(Battery)"
    

    But then, I thought I'd like to see if the battery charges or looses power, or if the powersupply is plugged in. That's when I came up with something like:

    battery() {
        BATC=/sys/class/power_supply/BAT0/capacity
        BATS=/sys/class/power_supply/BAT0/status
        AC=$(acpi --ac-adapter | cut -d: -f2)
    
        if [[ $AC == *"on-line"* ]]; then
          test "`cat $BATS`" = "Charging" && echo -n '+' || echo -n '~';
        else
          test "`cat $BATS`" = "Charging" && echo -n '+' || echo -n '-';
        fi
        sed -n p $BATC
    }
    

    …and added it to the buffer ($buf) like that:

    buf="${buf} %{F#02C}BAT: %{F#FFF}$(battery)"
    
  2. Windows and Desktops

    Since using bspwm and sxhkd most of my windows are displayed fullscreen, I use multiple dektops and switch alot between them. That's why knowing the dektops I am on and displaying the name is important to me. For this reason I have two little funtions to help me out:

    # get and echo the current dektop name
    desktopname() {
        cur=`xprop -root _NET_CURRENT_DESKTOP | awk '{print $3}'`
        desktop_names=`xprop -root _NET_DESKTOP_NAMES`
        cur_name_id=`bc <<< $cur+2+$cur`
        cur_name=`echo ${desktop_names} | awk -v var="$cur_name_id" -F "\"" '{print $var}'`
        echo $cur_name
    }
    

    Let's go through step by step:

    xprop -root _NET_CURRENT_DESKTOP | awk '{print $3}' prints out the current dektop number.

    xprop -root _NET_DESKTOP_NAMES returns a list of desktop names like this:

    _NET_DESKTOP_NAMES(UTF8_STRING) = "bash", "mail", "firefox", "zeug", "stuff", "jack", "gimp", "morestuff", "Desktop"

    Then these infos given the next line calculates the id for awk (next line) then outputs the current Desktop name in the end :) I use it like this inside the panel:

    buf="${buf} «« $(desktopname) »»"
    

    The next one is an dynamic and graphic way to display the current dektop number:

    # get all dektops and display them while highlighting the current one
    groups() {
        cur=`xprop -root _NET_CURRENT_DESKTOP | awk '{print $3}'`
        tot=`xprop -root _NET_NUMBER_OF_DESKTOPS | awk '{print $3}'`
        window=`bc <<< $cur+1`
        desktop_names=`xprop -root _NET_DESKTOP_NAMES`
        cur_name_id=`bc <<< $cur+2+$cur`
        cur_name=`echo ${desktop_names} | awk -v var="$cur_name_id" -F "\"" '{print $var}'`
        for w in `seq 0 $((cur - 1))`;do line="${line}%{B#000}%{F#888}="; done
        line="${line}%{B#FFF}%{F#000} $window %{F#000}"
        for w in `seq $((cur + 2)) $tot`; do line="${line}%{B#000}%{F#888}="; done
        echo $line
    }
    

    Based on the two infos _NET_CURRENT_DESKTOP and _NET_NUMBER_OF_DEKTOPS we create a little loop to highlight the current dektop and display the other ones as = and unhighlighted. We add it to our buffer:

    buf="${buf} %{c}»»[$(groups)%{B#000}%{F#FFF}]««"
    

    And it should finally look like this (if you're on dektop 4):

    screenshot.jpg

    Figure 1: A screenshot from my lemonbar

  3. Volume, CPU, RAM

    To show my CPU usage or my used RAM I use the following two functions:

    cpuload() {
        LINE=`ps -eo pcpu |grep -vE '^\s*(0.0|%CPU)' |sed -n '1h;$!H;$g;s/\n/ +/gp'`
        X=`bc <<< $LINE`
        bc <<< $X/4
    }
    
    memused() {
        t=`grep -E 'Mem(Total)' /proc/meminfo |awk '{print $2}'`
        f=`grep -E 'Mem(Free)' /proc/meminfo |awk '{print $2}'`
        bc <<< "scale=2; 100 - $f / $t * 100" | cut -d. -f1
    }
    

    The first one grabbing the CPU percentage with ps -eo pcpu, then adding the values and dividing the result by 8 (because of my eight cores). This gives me the aproximate load in percentage :) I add it to the buffer like this:

    buf="${buf} %{F#C20}CPU: %{F#FFF}$(cpuload)"
    buf="${buf} %{F#C20}RAM: %{F#FFF}$(memused)"
    

    I need to adjust the colors to the load.. something like from green (low load) to yellow, then red (heavy load). But that's still in progress :D

    To display the current volume I use amixer like that:

    volume() {
        amixer get Master | sed -n 's/^.*\[\([0-9]\+\)%.*$/\1/p'| uniq
    }
    

    …and fill the buffer like this:

    buf="${buf} %{F#090}VOL: %{F#FFF}$(volume)"
    
  4. Network stuff

    To display my network status, like "is my device connected to any WLAN?" and "what external IP do we have here?". In order to get the network connection state I use this function:

    network() {
        wifi='wlo1'
          # I once used it to detect ethernet connections with:
        # eth0='enp0s31f6'
    
        ip link show $wifi | grep 'state UP' >/dev/null && int=$wifi
    
        # in case of ethernet this last thing would be: ... && int=eth0
    
        ping -c 1 bios-blog.com >/dev/null 2>&1 &&
            echo "$int connected" || echo "$int disconnected"
    }
    

    This simply shows the connection state of my wireless network device. To display my external IP I have a simple webservice running which responses with the IP calling the site. So I can go with curl like that:

    get_ip() {
        curl -s my.webservice.com | grep '<p></p>' | awk -F "<p></p>" '{print $1}' | awk -F " " '{print $1}'
    }
    

    I kinda like sort the IP out from the stuff written on the website with the grep and awk commands. :) In the end I append this to my buf variable like that:

    buf="${buf} %{r}%{F#990}NET: %{F#FFF}$(network)"
    

    The %{r} is only to display this on the right side of my bar…

1.2. Conclusion

Well that's not all about lemonbar and piping a script to it, but I think that's enough for this post. I guess I'll do another part in the future because I didn't mention how you make stuff happen when clicking on lemonbar with your mouse, or how I solved the color changing from green, to yellow, to red if my memory gets low or my CPU is too busy.

So let's say: to be continued…

Date: 2022-12-01 Do 01:37