Saturday, November 21, 2015

Often, special function keys (e.g. for controlling volume, brightness, sleep etc.) will not be automatically configured in many Linux distros. Luckily, in most cases it is easy to manually set this up. As with most other settings, Openbox allows custom key bindings to be added via entries in ~/.config/openbox/rc.xml. This way, functionality can be assigned to Fn keys not recognized out-of-the-box.

This is especially easy for standard functions (like volume-up and down, mute, sleep, brightness-up and down etc.) as these already have special key-codes assigned to them. For example, brightness-up button will be detected as XF86MonBrightnessUp allowing functionality to be directly bound to this code.

Discovering the key code to use

The best way to discover what happens when a key is pressed is using xev. Fire it from the terminal, and it will give you a window that captures events and logs them to the console. There, you'll be able to see the code for each button or button combination you press. Later on, you can use these codes to bind commands to them.

Configure keys using a graphical interface

Instead of editing Openbox's rc.xml manually, you can use obkey (Openbox Key Editor), which can automate the procedure of capturing the key codes for you and binding commands to them. See below for example commands you can bind to keys.

List of common function keys and example configurations

Increase/decrease brightness

Brightness can be controlled using xbacklight, so assigning the following bindings will make the brightness function keys work:

Sleep and Hibernate

Assuming a SystemD setup, systemctl can be used to suspend the system to RAM (a.k.a. sleep), to disk (a.k.a hibernate) or both (a.k.a. hybrid sleep). Add the following to make the sleep function key work:

If you have additional keys (apart from sleep), you can try the following binding (make sure hibernation is properly configured and working on your system, and your user has the permissions to use it first):

Volume up/down/mute

There's a few options for dealing with volume keys.

Let volumeicon manage the function keys

volumeicon is often used with Openbox and can be configured to manage volume buttons. Additionally, it can display OSD notifications nicely, and it supports multiple back-ends (GTK+ popups, libnotify, and possibly more).
If you want to go this route, edit your ~/.config/volumeicon/volumeicon to contain the following:

Bind volume buttons to amixer commands

amixer can be called directly to control the volume. If you're using Alsa only, you may have to first find out the name of the mixer control your sound card exposes by using amixer scontrols. It is usually called Master, but not always. With PulseAudio, it is always called Master.

Adding OSD notifications to commands

Most DEs come with a notification server you can use to display on-screen messages. Find out which one your DE/distro uses and simply wrap the commands used above in a script that also fires notifications. notify-send is a simple utility that comes with libnotify itself and can be used in most setups. It should be noted though that it doesn't offer a universal way to re-draw or replace a notification, making it hard to display a progress bar (e.g. for displaying volume or brightness level) and will keep creating new notifications each time it is called.

Some back-ends, like notify-osd, implement an extension supporting this scenario, but you'll have to know what you have in your distro. A back-end-agnostic drop-in replacement called notify-send.sh can also be used to work around the issue with back-ends that don't support this feature themselves.

Simple notify-send example

For back-ends supporting the extension, a commands similar to the following could be used to increment the volume and show the level on screen:

This, of course, could be enriched to choose the correct icon based on the volume level etc.

Using notify-send.sh

For back-ends without the extension, notify-send.sh can be used with a command similar to the above, but with adding --print-id as an argument. This will make the command return the notification ID that can later to be used to replace the notification with a new one by providing --replace=$ID. Of course, the ID would have to persisted somewhere between the calls, like a global variable of a file.