Ultra tuning of dwm status bar

By default dwm status bar includes 9 tags, window mode status (tiling or float) and the name of currently focused window. A little amount of information and a lot of empty space. What if we want to add extra information to status bar? Such as CPU, GPU temperature, size of internet traffic, current time, memory consumption? In this article we will do it.

Dwm status bar with CPU, GPU and memory info.
My dwm status bar. How it looks like. First, Internet traffic in kB/s. Second, CPU temperature and fan speed info. Next, the same, but with GPU. Then /tmp/ memory status, then current time and day of the week.

Add .xprofile file to start with X session

Add “exec ~/.xprofile &” command right before “exec dwm” in .xinitrc file. It should looks like below:

exec ~/.xprofile &
exec dwm

It means that .xprofile will start as a separate process with X session and dwm. If you already have it in .xinitrc file then this step is not needed.

.xprofile modification

If your /tmp directory mounted as tmpfs the method below will not use a hard disk for temporary file storage, only RAM.

We create two cycles of instructions. One is updated every 60 seconds (time and memory consumption info), and another once every 10 seconds (there may be a processor status, internet traffic info).

These cycles work as separate processes (& means start asynchronously). Since these are different processes, I didn’t manage to use common variables. So, therefore we use /tmp directory as data storage.

#!/bin/bash
while true; do

	date +%A\ %H:%M > /tmp/CurTime.tmp

	sleep 60s
done &
while true; do

	LOCALTIME=$(< /tmp/CurTime.tmp)

	xsetroot -name "$LOCALTIME"

	sleep 10s
done &
...Here may be additional settings like background image, keyboard layout and others. 

Don’t forget to restart X system when .xprofile will be modified. In dwm we can simply do it by pressing MODKEY (Alt by default) + Shift + Q to quit from X session, and then type startx in terminal to start X session with dwm again.

The following command

xsetroot -name "$LOCALTIME"

Will update dwm status bar with $LOCALTIME variable every 10 seconds. But this variable will change every 60 seconds since time update occurs every 60 seconds in the first cycle.

Let’s add CPU info. Check this article (Optimized way to grab CPU, GPU info for bash scripts) to learn how to find your own sensor modules.

#!/bin/bash
while true; do

	date +%A\ %H:%M > /tmp/CurTime.tmp

	sleep 60s
done &
while true; do

	LOCALTIME=$(< /tmp/CurTime.tmp)

        CPU_T=$(< /sys/devices/platform/coretemp.0/hwmon/hwmon0/temp2_input)
	CPU_TEMP=$(expr "$CPU_T" / 1000)
	CPU_RPM=$(< /sys/module/hwmon_vid/holders/it87/drivers/platform\:it87/it87.656/hwmon/hwmon1/fan1_input)

	xsetroot -name "${CPU_TEMP}°C $CPU_RPM | $LOCALTIME"

	sleep 10s
done &
...Other instructions if you have any...

Here we get the temperature of the processor and the rotational speed (RPM – rounds per minute) of its fan. Don’t forget to replace hwmon path with your own.

In a similar way we can get GPU information.

        GPU_RPM=$(< /sys/module/nouveau/drivers/pci\:nouveau/0000\:01\:00.0/hwmon/hwmon2/fan1_input)
	GPU_T=$(< /sys/module/nouveau/drivers/pci\:nouveau/0000\:01\:00.0/hwmon/hwmon2/temp1_input)
	GPU_TEMP=$(expr "$GPU_T" / 1000)

If you have free open-sourced drivers, then the module should share such information.

The amount of internet traffic per second:

        R1=$(< /sys/class/net/enp4s0/statistics/rx_bytes)
        T1=$(< /sys/class/net/enp4s0/statistics/tx_bytes)
        sleep 1s
        R2=$(< /sys/class/net/enp4s0/statistics/rx_bytes)
        T2=$(< /sys/class/net/enp4s0/statistics/tx_bytes)
        TBPS=$(expr "$T2" - "$T1")
        RBPS=$(expr "$R2" - "$R1")
        TKBPS=$(expr "$TBPS" / 1024)
        RKBPS=$(expr "$RBPS" / 1024)

TX bytes means the number of bytes that you transfer to the network. And RX bytes means received bytes. The bytes that we receive, for example, when downloading a file.

We make a delay of 1 second, get the value again, and when we subtract from the late value earlier, we get the difference. This is the value of received / sent bytes per second. Then we divide it by 1024 to get the value in kilobytes.

To print the memory consumption of a mounted directory, we can use simple and fast findmnt utility.

We use -n option to not include headers in output and -o option to set what column of information we exactly need. We set “used” to receive used memory.

findmnt -no used /tmp
...
16.6M

So, the output is simple and clear.

So, let’s add it in .xprofile:

#!/bin/bash
while true; do

	date +%A\ %H:%M > /tmp/CurTime.tmp
	findmnt -no used /tmp > /tmp/CurUsedTmp.tmp

	sleep 60s
done &
while true; do

	LOCALTIME=$(< /tmp/CurTime.tmp)
	USEDTMP=$(< /tmp/CurUsedTmp.tmp)

	xsetroot -name "${USEDTMP}/2.0G | $LOCALTIME"

	sleep 10s
done

Since we don’t need to use findmnt utility every 10 seconds, we can simply add it in “60 seconds” cycle.

Here is the final result:

#!/bin/bash
while true; do
	date +%A\ %H:%M > /tmp/CurTime.tmp
	findmnt -no used /tmp > /tmp/CurUsedTmp.tmp
	sleep 60s
done &
while true; do
	GPU_RPM=$(< /sys/module/nouveau/drivers/pci\:nouveau/0000\:01\:00.0/hwmon/hwmon2/fan1_input)
	GPU_T=$(< /sys/module/nouveau/drivers/pci\:nouveau/0000\:01\:00.0/hwmon/hwmon2/temp1_input)
	GPU_TEMP=$(expr "$GPU_T" / 1000)

	CPU_T=$(< /sys/devices/platform/coretemp.0/hwmon/hwmon0/temp2_input)
	CPU_TEMP=$(expr "$CPU_T" / 1000)
	CPU_RPM=$(< /sys/module/hwmon_vid/holders/it87/drivers/platform\:it87/it87.656/hwmon/hwmon1/fan1_input)

	LOCALTIME=$(< /tmp/CurTime.tmp)
	USEDTMP=$(< /tmp/CurUsedTmp.tmp)

	R1=$(< /sys/class/net/enp4s0/statistics/rx_bytes)    # Internet interface name may differ (enp4s0 in my case)
        T1=$(< /sys/class/net/enp4s0/statistics/tx_bytes)
        sleep 1s
        R2=$(< /sys/class/net/enp4s0/statistics/rx_bytes)
        T2=$(< /sys/class/net/enp4s0/statistics/tx_bytes)
        TBPS=$(expr "$T2" - "$T1")
        RBPS=$(expr "$R2" - "$R1")
        TKBPS=$(expr "$TBPS" / 1024)
        RKBPS=$(expr "$RBPS" / 1024)

	xsetroot -name "${TKBPS}/${RKBPS} kB | ${CPU_TEMP}°C $CPU_RPM | ${GPU_TEMP}°C $GPU_RPM | ${USEDTMP}/2.0G | $LOCALTIME"
	sleep 10s
done &

This is a very fast script that doesn’t load a processor at all – at the time of execution 0.3% of one processor core (the minimum and maximum value that top shows me among all processes). I didn’t see top showing for example 0.2%. So, the real load must be even lower than 0,3% (On one processor core!).

As you can see there are no utilities like cat, I tried to do a minimum of utilities, using bash to the maximum.