A picture of my tmux status bar.

Adding custom dynamic elements to tmux's status bar

written on August 6, 2024

categories: engineering

tags: linux, tooling

I've been using the Alacritty + tmux combo for a while now, and I've been pretty happy with it. It allows me to have a nice, neat terminal while also being able to split it into multiple panes and hop between them very easily.

The thing is, I prefer having Alacritty in fullscreen mode, which means that when I have a terminal pulled up, I can't really see anything else... except for tmux's status bar, which is that horrendous-neon-green-by-default thing you see on the bottom with a non-customized config:

Default tmux status bar

The default tmux look (status bar at the bottom).

You can of course customize this status bar pretty easily. For example, this is how you set the left part of it to display some fixed string:

.tmux.conf

set -g status-left "hello tmux :)"

How about dynamic elements, like date/time? Well, there are some predefined sequences you can use for those. For example, this is how you set the right part of the status bar to display the date and time in a YYYY-MM-DD HH:MM format:

.tmux.conf

set -g status-right "%Y-%m-%d %H:%M"

This is all nice and good, but what about "arbitrary" customization? For example, it would be nice to be able to see the battery level on the status bar, so that I know when my laptop is about to die!

Well... that one is a little more complicated. Technically, there is a way to execute shell commands inside the tmux configuration file, but they get very unwieldy very fast. There seems to be a way to do if-statements too, but I find it awkward as well. Overall, it seems like the simplest way to do this is to just delegate all of this to a shell script, and then do something like run-shell /path/to/shell/script from the tmux config file. So, I set out to write a little shell script that can take care of setting the battery status (and possibly other things in the future) on the tmux status bar.

First attempt

At first, my reasoning was this: since the tmux configuration language is kind of awkward to use, maybe it's best to delegate the entire status bar configuration to a (or multiple) shell script(s), and add on stuff as needed in the future through that mechanism.

So, the general "architecture" would be this:

set-status-bar.sh

battery_status() {
    # ...
}

date_and_time() {
    # ...
}

tmux set-option -g status-right "$(battery_status) | $(date_and_time)"


.tmux.conf

run-shell /path/to/set-status-bar.sh

There is one small hiccup with this: how to find set-status-bar.sh. This is easily circumvented if you use the classic dotfiles + GNU stow approach, in which case you can simply store set-status-bar.sh (and any other scripts) under ~/tmux/ for example, and then you would know what the absolute path to it is.

As a small aside, getting the battery status should be pretty easy; I use upower:

$ upower -i $(upower -e | grep BAT) | grep percentage
    percentage:          79%

This works pretty well, in the sense that you do get the battery status to display on the tmux status bar. However... how do you update it? I mean, tmux does update the status bar regularly, but it doesn't do it by re-sourcing the entire config file! To my understanding, it simply re-runs whatever command was used to set the status bar in the first place. The problem here is that the set command is not run in the tmux config itself, but in the shell script, so it's run indirectly through run-shell. I guess you could set up a cron thing to make it refresh regularly, but that sounds extremely complicated for such a small thing!

Second attempt

A very simple solution to this issue would be to somehow still have a set -g status-right command present in the tmux config file. So how can we still delegate the rest of the work to the shell script? Turns out, you can just run any shell command and capture its output in a string inside of the tmux config file like this:

.tmux.conf

set -g status-right "#(echo hi)"

So the solution is pretty simple: just make the shell script print out the status bar element instead, and incorporate it into the final status bar in the tmux config file via that shell-command-output-to-string syntax! As a bonus, we can simplify the shell script and only keep whatever's relevant (simpler things such as static strings or date/time can be handled natively inside the tmux config file):

set-status-bar.sh

battery_status() {
    # ...
}

echo -n "$(battery_status)"


.tmux.conf

set -g status-right "#(bash ~/tmux/set-status-bar.sh) | %Y-%m-%d %H:%M"

I hope this little hack saves you some time when customizing your tmux config :)


< Who's afraid of the big bad Math On John Densmore's interview with Samuel Andreyev >