Tyler's Site

Abstract

Key re-mapping is a very common practice for a variety of computer users. For anything from swapping two keys to be in a more optimal place on the keyboard, to completely changing the keyboard layout to something like Colemak. Additionally, some keyboards require the use of different layers to provide all of the functionality that a standard keyboard would provide. Layers become more necessary as the keyboards get smaller, for example many of these keyboards would need multiple layers to be useful for most people. Generally, these re-mappings and layers are handled by the keyboard in firmware, or is handled by the host operating system. This blog post will be going over one of the operating system based solutions for this problem, kMonad.

What is KMonad

KMonad is a tool that allows for remapping keys on one or more keyboards on the system with the same or different configurations. From the project’s GitHub page:

KMonad offers advanced customization features such as layers, multi-tap, tap-hold, and much more. These features are usually available at the hardware level on the QMK-firmware enabled keyboard. However, KMonad allows you to enjoy such features in virtually any keyboard by low-level system manipulations.

Setting Up

Kmonad can be installed via their release page, just download the binary and put it in /usr/local/bin on a Linux system. Things get slightly more difficult (in my opinion) in the world of Microsoft Windows, but ultimately adding the exe to the path is all that needs to occur. From there, the application can be run with the file of the keyboard layout as an argument…at least in theory. I did this without major issues in Void Linux, simply add yourself to the input group if not already:

sudo usermod -aG input ${USER}

Logout and log back in for the changes to take effect; can confirm group membership by running groups in a terminal session as your user. From there we just need to start the configuration of the keyboard layout file. I keep mind in ~/.config/kmonad/, but the location doesn’t really matter if you would prefer somewhere else.

(defcfg
  ;; For Linux
  input  (device-file "/dev/input/by-id/usb-Razer_Razer_Huntsman_Mini_00000000001A-event-kbd")
  output (uinput-sink "My KMonad output")

  ;; Comment this is you want unhandled events not to be emitted
  fallthrough true

  ;; Set this to false to disable any command-execution in KMonad
  allow-cmd true
)

I also setup kmonad on Slackware Linux with slightly more challenges; most prominently setting up permissions for /dev/uinput; I accomplished that by setting up a udev rule:

KERNEL=="uinput", MODE="0660", GROUP="input", OPTIONS+="static_node=uinput"

Upon rebooting the system, /dev/uinput will then be owned by the input group and allow anyone in the input group to use kmonad. That is not something I had to do on my Void Linux machine, but might be helpful to know in the event that it is necessary.

Creating Keyboard Layout

Now for the part that is actually fun, creating the keyboard layout. The first step to set a custom layout is to define the keys that your keyboard will be sending to the operating system when they are pressed. Below I am using the ten keyless layout template provided by the project as an example:

(defsrc
  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  f12
  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc  ins  home pgup
  tab  q    w    e    r    t    y    u    i    o    p    [    ]    \     del  end  pgdn
  caps a    s    d    f    g    h    j    k    l    ;    '    ret
  lsft z    x    c    v    b    n    m    ,    .    /    rsft                 up
  lctl lmet lalt           spc            ralt rmet cmp  rctl            left down rght
)

The project also provides several other keyboard templates including:

You can obviously create a new template if none of those work for you; the only really syntax requirements are that the keyboard layout is between the defsrc and the closing parenthesis, each key is separated by spaces, and each row is on its own row in the layout. Making an attempt to make the keyboard layout look like the physical keyboard is not a requirement, but can make working with it a bit easier.

Once the defsrc is made, we then need to create the layers that we want on the keyboard; there is no limit to the quantity of layers on the keyboard, but the layout does have to match the defsrc. The way I usually make a new layer is just to copy the defsrc and change it from defsrc to deflayer followed by the layer name. This might be an example of a basic first layer with a modification:

(deflayer qwerty
  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  f12
  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc  ins  home pgup
  tab  q    w    e    r    t    y    u    i    o    p    [    ]    \     del  end  pgdn
  esc  a    s    d    f    g    h    j    k    l    ;    '    ret
  lsft z    x    c    v    b    n    m    ,    .    /    rsft                 up
  lctl lmet lalt           spc            ralt rmet cmp  rctl            left down rght
)

As you can see, the caps lock key was replaced with escape in that layer. Even with just this basic functionality, it can already be used to entirely change the keyboard layout from something like qwerty to dvorak or colmak. If the goal was to create a layer called ‘colmak’ with a colmak keyboard layout, that might look as follows:

(deflayer colmak
  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  f12
  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc  ins  home pgup
  tab  q    w    f    p    g    j    l    u    y    ;    [    ]    \     del  end  pgdn
  caps a    r    s    t    d    h    n    e    i    o    '    ret
  lsft z    x    c    v    b    k    m    ,    .    /    rsft                 up
  lctl lmet lalt           spc            ralt rmet cmp  rctl            left down rght
)

Kmonad will automatically pick the first deflayer statement as the default when it is started; to be able to switch layers and have more complex functionality than switching key behavior, a defalias statement will need to be made.

Defalias

This section is where the magic of kmonad really comes into play. Keys can be setup to have different functionality depending on whether the key was pressed or held, or have different functionality depending on how many times the key was pressed. I am just going to go over the basics for things I have implemented with my keyboard; there is a tutorial layout that goes over each of the functions in depth. Use that for a more complete reference for what is possible with the tool as well as how to go about accomplishing it.

The two main functions I use are the ‘layer-switch’ and the ‘tap-hold’ functions. Layer-switch does basically what it sounds like, simply switching to a different layer. The main layer I use this with is to re-implement a numpad on my keyboard that does not already have one. The actual layout for that isn’t particularly important, but the defalias would look something like this:

(defalias
    default (layer-switch qwerty)
    numlayer (layer-switch numpad)
    num (tap-hold 300 @numlayer ralt)
    cap (tap-hold 300 esc caps)
)

As stated previously, the ‘switch-layer’ function is simply just to switch to a different layer defined in a ‘deflayer’ statement. So when the key bound to that function is pressed, the new layer will be activated. The ‘tap-hold’ function is a bit more interesting; when the tap-hold function is written, it is given a time in milliseconds (300 has been a good timing for me personally), then the behavior on a tap, then finally the behavior when the button is held for more than the timing. Each of the functions are given a name that can be bound to a key. For example, in the above defalias statement, binding the ‘cap’ function (that acts like escape on a press but caps lock on hold) could be defined on a key with @cap. You can also see that we can combine functions together in multiple functions to get more complex behavior; I have a ‘layer-switch’ function setup that is used in a ‘tap-hold’ function so that if the key is pressed it will switch to the numpad layer, but if it is held, it will act as a right alt key.

Again, I am using just the most basic functionality of this tool and it has completely changed how I interact with my keyboard. Kmonad is capable of doing much, much more than I am describing here.

Outstanding Challenges

While I do like this tool, and I am certainly going to continue to use it, I have had two minor challenges with it. The first one is that the key remapping is not always detected by software correctly. I have only had this happen in games, and it very well could be something that Proton is doing rather than kmonad, but it has certainly happened where I had to force quit a game I was playing because I couldn’t open the pause menu. This can easily be worked around by having a “gaming” layer that is mostly the default layout with one unimportant key remapped to switch to the other layers.

The second challenge is that I have not found a good way to run it on FreeBSD yet; it is software that might run in the Linuxulator, but it did not compile naively. This is something that I have a hard time griping at too hard though. It would be nice to have FreeBSD support, but the devs certainly are not bound to my whims. And ultimately it is open source software, if I wanted FreeBSD support bad enough, I could make a PR for it.

Closing Thoughts

Kmonad is an overall amazing tool for remapping keyboards that is much more powerful than something like setxkbmap, which is what I was using previously to rebind my caps lock key to escape. This tool allows me to do quite a bit more, also making using small keyboards much more pleasant. Normally I would put a ‘more resources’ section here, but the documentation in the projects GitHub page is quite good and explains things fairly well, so I would recommend trying that out before doing much digging around online.