How To: Refresh your status bar in response to a hotkey - Desktop Customization & Workflow

Users browsing this thread: 1 Guest(s)
mort
Members
(This post tries to explain how every part works in some detail. If you're just interested in code to copy/paste, look at the end of the post.)

A lot of you are probably using status bars where the actual status bar process just essentially renders the text printed by a script. I know at least both swaybar and i3bar work like that. A simple status script would be this:

Code:
update() {
    echo \
        "VOL:$(pactl list sinks \
            | grep "^\s*Volume:" | cut -d/ -f2 \
            | tr -d '\n' | sed 's/\s\s*/ /g')|" \
        "$(date -Iminutes | sed 's/T/ /; s/+.*//')"
}

while :; do update; sleep 5; done

That works well enough, it shows the current volume of your audio devices and shows the current date. However, it's not great; it only updates once every 5 seconds, which makes it annoying to use to adjust your volume. You could decrease the update interval, but it would be stupid to have a status script running every 100ms when nothing almost ever changes.

One decent solution would be to make the status script update after your
Code:
pactl set-sink-volume @DEFAULT_SINK@ +10%
hotkey runs. We can use Unix signals to achieve this.

A signal is basically one of the ways a Unix-like kernel (like Linux) can use to communicate with its processes. The kernel sends a SIGSEGV signal ("Segmentation Fault") when the application tries to access memory it's not supposed to, it sends a SIGWINCH signal when the terminal window's dimensions changes, it sends a SIGINT signal when you interrupt the process (such as by hitting ctrl-c in a terminal), it sends a SIGCHLD signal when a child process terminates. Processes can install signal handlers, which is code which is executed when the process receives a signal.

Gracefully, the designers of Unix gave us two signals which don't have any semantic meaning in and of themselves; SIGUSR1 and SIGUSR2. The purpose of these signals is to trigger application-specific behavior. We can make the script call the update function again when it receives the SIGUSR1 signal:

Code:
update() {
    echo \
        "VOL:$(pactl list sinks \
            | grep "^\s*Volume:" | cut -d/ -f2 \
            | tr -d '\n' | sed 's/\s\s*/ /g')|" \
        "$(date -Iminutes | sed 's/T/ /; s/+.*//')"
}

trap true USR1
while :; do update; sleep 5 & wait $!; done

This does two things: it "traps" the SIGUSR1 signal (this gets rid of the default SIGUSR1 behavior, which is to exit the process), and it replaces the sleep 5 with sleep 5 & wait $!. The meaning of sleep 5 & wait $! is basically: "Spawn a new background child process (sleep 5), and wait for it to exit", but the crucial difference is that the "wait" command is aborted when a signal is received. (The $! variable always refers to the previous background process that was spawned, in this case the sleep process.)

So now, when we send our script a SIGUSR1 signal, it will instantly print a new status line. Now we need to run
Code:
kill -USR1 <our script's process ID>
in response to the volume keys. In order to do that, there has to be a way to find the process ID of the status script. One way to do that is to use a PID file; the status script just has to write its PID to some defined location, such as /tmp/status-$USER.pid:

Code:
update() {
    echo \
        "VOL:$(pactl list sinks \
            | grep "^\s*Volume:" | cut -d/ -f2 \
            | tr -d '\n' | sed 's/\s\s*/ /g')|" \
        "$(date -Iminutes | sed 's/T/ /; s/+.*//')"
}

echo "$$" > "/tmp/status-$USER.pid"
trap true USR1
while :; do update; sleep 5 & wait $!; done

The $$ variable always refers to the PID of the currently executing script.

Now, everything that remains is to add
Code:
kill -USR1 "$(cat "/tmp/status-$USER.pid")"
to your volume change hotkeys. For example, the volume up hotkey could run:
Code:
pactl set-sink-volume @DEFAULT_SINK@ +10% && kill -USR1 "$(cat "/tmp/status-$USER.pid")"

Here's my status script, which uses something like what's described here to update in response to my hotkeys: https://github.com/mortie/dots/blob/mast.../status.sh


Messages In This Thread
How To: Refresh your status bar in response to a hotkey - by mort - 13-08-2020, 03:05 PM