Finding a Use for Keyboard RGB

4 November 2021

Linux

Abstract

For the longest time, my fancy individually addressable LED keyboard was restricted to a boring, static backlight. While the LED animations were fun at first, once I migrated the keyboard to a Linux workstation, I didn’t feel like it was worth the effort to figure out how to get them configured again. Only after seeing a reddit post where the LEDs were used to highlight the available keyboard shortcuts, did I realize that the RGB might be useful after all. I built out a small bash utility called ckb-animation to try to recreate this behavior for myself.

Installation

The keyboard I built this around was a Corsair K70 Lux. Unfortunately if you’re looking to try to use this for yourself, you’re going to be limited to a Corsair keyboard, since everything is built on ckb-next. If you do want to try this yourself with a Corsair keyboard, it’s important to note that the K70 Lux only has red LEDs, extending this to work with different colors shouldn’t be that far of a stretch, but for now, it’s designed to work as a simple on/off.

Dependencies

The only dependencies that ckb-animation are:

  1. ckb-next: for providing the Corsair controller driver
  2. systemd: for making dealing with the fact that Corsair’s driver behavior depends on a GUI interface easier.
  3. xinput: for identifying key press and release events for control, shift, super, and alt keys.

Installing

The full installation instructions are available on the ckb-animation repo. Ideally they should be as simple as,

$ git clone https://github.com/blockjoe/ckb-animation.git
$ cd ckb-animation
$ ./install.sh

I’ve only built this around my ease of use with my K70 Lux on an Ubuntu based system though, and any other use cases remain currently untested. Assuming a clean install, the ckb-animation service will be enabled for the user, and will automatically start/stop with hot plugging of a ckb-next supported keyboard.

Using and Configuring the Keyboard Shortcuts.

With the service running, the daemon is accessible from the ckb-animation program. This daemon provides commands for the basic start/stop/restart/status commands, but also commands for managing which keys are to be illuminated when a chord is held. A chord represents holding down any of the modifier keys, Alt, Control, Super (Meta/Windows), Shift, or certain combinations of them. Currently the chords that are represented in the animation are:

  1. Control
  2. Alt
  3. Super
  4. Control+Alt
  5. Control+Shift
  6. Control+Super
  7. Alt+Shift
  8. Alt+Super
  9. Super+Shift
  10. Control+Alt+Shift

Holding any of these keys will light up a set of keys that represent available keyboard shortcuts that are available for that chord. For example holding in Control+Alt might illuminate Esc, Delete, l, while holding just Alt might illuminate Tab and the arrow keys.

ckb-animation ships with shortcuts that represent my current setup, but these certainly aren’t a one size fits all solution. The preconfigured keys can be viewed with the view command. For example, if we wanted to see the keys that represent Alt shortcuts, we would run:

$ ckb-animation view chord alt
Keys for alt

grave
tab
f3
f4
home
up
left
right
h
j
k
l

Let’s say that in our setup, we don’t use Alt+H, Alt+J, Alt+K, or Alt+L for anything, we can modify the configuration on the fly, and it will automatically update if we were to run:

$ ckb-animation remove alt h j k l

Immediately after running, it will be clear that when holding Alt, the h/j/k/l keys are no longer illuminated. Let’s say that we have shortcuts bound for Alt+F5-Alt+F9, adding these in would be as simple as:

$ ckb-animation add alt f5 f6 f7 f8 f9

The behavior extends to any of the supported multi-key chords as well – the only requirement is that the combination of the keys be separated by a -. The order and first letter case doesn’t matter, and some keys have multiple names that are all valid. For example ctrl-super, Control-Windows, and meta-control all identify the same chord.

The keys are expected to be named in the format that ckb-next uses to identify them. The expected names of the keys can be discovered by running:

$ ckb-animation view keys
Name        Key
---------------
mr          MR
m1          M1
m2          M2
m3          M3
light       Brightness
lock        Windows Lock
mute        Mute
volup       Volume Up
voldn       Volume down
...

This isn’t always the most convenient way to find the key that you wanted mid-command. Installing the bash autocomplete makes this process a lot easier. Tabbing for the key on the add command gives a full list of available keys to be added.

$ ckb-animation add ctrl <tab><y>
Display all 142 possibilities? (y or n)
0           f1          g8          n           r
1           f10         g9          next        ralt
2           f11         grave       num0        rbrace
3           f12         h           num1        rctrl
4           f2          hash        num2        right
5           f3          henkan      num3        rmenu
6           f4          home        num4        ro
7           f5          i           num5        rshift
8           f6          ins         num6        rwin
9           f7          j           num7        s
a           f8          k           num8        scroll
b           f9          katahira    num9        slash
bslash      g           l           numdot      space
bslash_iso  g1          lalt        numenter    stop
bspace      g10         lbrace      numlock     t
c           g11         lctrl       numminus    tab
caps        g12         left        numplus     u
colon       g13         light       numslash    up
comma       g14         lock        numstar     v
d           g15         lshift      o           voldn
del         g16         lwin        p           volup
dot         g17         m           pause       w
down        g18         m1          pgdn        x
e           g2          m2          pgup        y
end         g3          m3          play        yen
enter       g4          minus       prev        z
equal       g5          mr          prtscn
esc         g6          muhenkan    q
f           g7          mute        quote
$ ckb-animation add ctrl 

Tabbing for the key on the remove command will only list keys that are currently defined for that command as well.

$ ckb-animation remove alt <tab>
f3     f4     grave  h      home   j      
k      l      left   right  tab    up
$ ckb-animation remove alt 

I find that I pretty frequently need to make a minor change where I create a new chorded shortcut, or change an existing one, and this workflow makes updating the keyboard animation pretty seamless. On top of that, the chords are saved via files in the ~/.config directory, which makes it possible to manage the animation configuration as you would any other dotfiles.

Potential Improvements

For now this solution works pretty flawlessly for my given setup, but assuming I were to change things, there’s a few things that I would probably want to build upon.

More Colors

I currently have 4 main categories of chord shortcuts:

  1. KDE
  2. Terminator
  3. Vim
  4. Firefox

The KDE shortcuts should be accessible at all times, but the shortcuts associated with the other groups will only be relevant if the application is open and in focus. If I had more than one color to work with, I could represent at least which context that shortcut applies to. In my case, Alt+hjkl corresponds to movement in KDE windows, Super+hjkl corresponds to movement in Terminator splits, and Ctrl+hjkl corresponds to movement in Vim splits. Seeing that represented only in one color means that I know I have shortcuts for 3 different kinds of movement on hjkl chords, but having more colors means that I could immediately tell the difference between each. Additionally, having the ability to encode application specific information might motivate shortcuts only being displayed if the relevant context has focus.

Dynamic Chords

Currently the chords are hard-coded around the modifiers that are relevant to me. The ckb-animation repo has details about how to manually add support for a new chord. If I were to move to a workflow that utilizes many modifier keys present in terms of macro keys, making defining new chords accessible from the CLI might be worth considering.

Remove the systemd dependence

The only reason I made this into a systemd service instead of just calling ckb-animation start/stop directly from udev has to do with the fact that ckb-next currently binds itself to a GUI session. Since ckb-next is run as the user, I felt it was consistent to have ckb-animation run as the user as well. udev does provide options for running a script as a user, but it didn’t look nearly as straightforward as making a user systemd service.

Remove the X dependence

The solution isn’t currently compatible with Wayland, as I currently rely on xinput for identifying the keyboard to monitor, and then monitoring the events of that keyboard for determining the key down/release events for identifying the current chords. There’s nothing here that binds me to xinput besides the fact that is was what was familiar.

The following two lines are the only two in which I directly rely on xinput

# find the keyboard id
keyboard_id=$(xinput list | grep "vKB" | cut -f 2 | cut -d"=" -f 2) 
# read the keyboard event loop
xinput test "$keyboard_id"  | while read event; do 

I should be able to change this to where I can use libinput list-devices to look for the keyboard, and then use libinput debug-events --show-keycodes /dev/input/eventX to get my loop of keyboard events. Instead of extracting the id from xinput list, I would want to extract the kernel device with libinput list-devices. Passing along the show-keycodes would mean that I can reuse the base logic for identifying key up/down events for my chord keys, although the pattern I would be looking for in the line would change slightly. A left control press/release event currently looks like

key press 37
key release 37

would instead look like

 event2  KEYBOARD_KEY  +1.784s	KEY_LEFTCTRL (37) pressed
 event2  KEYBOARD_KEY  +1.880s	KEY_LEFTCTRL (37) released

This is change that could be implemented in where I currently define the key up/down patterns at the top of bin/ckb-animation, but one that I probably won’t get around to until I migrate over to Wayland myself. I’d imagine this would look something similar to the following:

down=" pressed" # down="key press "
up=" released" # up="key release "

ctrl_l_up="(37)${up}" # ctrl_l_up="${up}37"
ctrl_l_down="(37)${down}" # ctrl_l_down="${down}37"

6