Saturday, November 21, 2015

Configuring function (Fn) keys in Linux under Openbox

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:

        <keybind key="XF86MonBrightnessUp">
          <action name="Execute">
            <command>xbacklight -inc 40</command>
          </action>
        </keybind>
    
        <keybind key="XF86MonBrightnessDown">
          <action name="Execute">
            <command>xbacklight -dec 40</command>
          </action>
        </keybind>
    
  • 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:
        <keybind key="XF86Sleep">
          <action name="Execute">
            <command>systemctl suspend</command>
          </action>
        </keybind>
    
    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):
        <keybind key="XF86Standby">
          <action name="Execute">
            <command>systemctl hibernate</command> <!-- "systemctl hybrid-sleep" could be used instead -->
          </action>
        </keybind>
    
  • Search

    Many keyboards have a dedicated search key. To bind a command appropriate for your system (exemplified using SpaceFM) use the following:
        <keybind key="XF86Search">
          <action name="Execute">
            <command>spacefm --find-files %F</command>
          </action>
        </keybind>
    
  • Screen lock

    Bind any screen-locking (screensaver) utility, like slock or gnome-screensaver-command, as exemplified below to make the lock-screen button work:
        <keybind key="XF86ScreenSaver">
          <action name="Execute">
            <command>slock</command> <!-- Replace with "gnome-screensaver-command -l" or "xscreensaver-command -lock" if you wish -->
          </action>
        </keybind>
    
  • 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:
      [Hotkeys]
      up_enabled=true
      down_enabled=true
      mute_enabled=true
      up=XF86AudioRaiseVolume
      down=XF86AudioLowerVolume
      mute=XF86AudioMute
      
    • 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.
          <keybind key="XF86AudioRaiseVolume">    
            <action name="Execute">
              <!-- Increase volume by 5& -->
              <command>amixer set Master 5%+ unmute</command> <!-- If needed, replace "Master" with whatever "amixer scontrols" has given you as the control name -->
            </action>
          </keybind>
      
          <keybind key="XF86AudioLowerVolume">
            <action name="Execute">
              <command>amixer set Master 5%- unmute</command>
            </action>
          </keybind>
      
          <keybind key="XF86AudioMute">
            <action name="Execute">
              <command>amixer set Master toggle</command>
            </action>
          </keybind>
      
  • Projector/Presentation mode

    The projector/presentation mode button will most often simply be understood as Super (Windows key) + P, and not as a separate key code, so you can bind it as such.
        <keybind key="XF86Search">
          <action name="Execute">
            <command>xrandr --auto</command> <!-- A more sophisticated xrandr command (to setup extended display etc) can be used here -->
          </action>
        </keybind>
    

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:

    notify-send " " -i notification-audio-volume-medium -h int:value:$(amixer set Master 5%+ unmute | grep -m 1 "%]" | cut -d "[" -f2|cut -d "%" -f1) -h string:synchronous:volume

    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.