Mapping caps lock to escape when pressed once and control when held makes a lot of sense. Many combinations become easier and more comfortable. We all know the more time spent on the home row, the more Vim efficiency and speed we get.

MacOS and Windows have plenty of support already by software such as Karabiner or AutoHotKey. When it comes to Linux, XCape would be the comparable option. I tried it out for a couple of weeks, played with its configuration, and found that it didn't keep up.

Seeing as the main point of this rebinding is largely Vim-fu, making several quick keystrokes involving a change of mode has to work perfectly. Unfortunately, XCape frequently misses input causing minor confusion at best and leaving you making random incorrect keystrokes in Normal mode at worst.

I did locate an issue for this but it will take some time to fix. Luckily there is another option. caps2esc is a piece of software that sits closer to Linux's input layer than XCape. Trying it out, I have observed no lag issue and it does feel faster. If you're not used to compiling code from source, it's a little harder to install, but let me save you some time and show you how.

Building and Installing

Caps2esc depends on a second library called Interception Tools. You'll need to compile and install both.

cd ~/your/code/directory
git clone https://gitlab.com/interception/linux/tools interception_tools
git clone https://gitlab.com/interception/linux/plugins/caps2esc

Interception Tools needs some libraries. The readme indicates which ones and here's how to get them on Fedora:

sudo dnf install cmake yaml-cpp-devel libevdev-devel systemd-devel -y

Build each according to their readme. Run sudo make install in each build directory and verify correct installation:

which udevmon
which caps2esc

Configure Interception Tools

Their readme provides a configuration example:

- JOB: "intercept -g $DEVNODE | y2z | x2y | uinput -d $DEVNODE"
  DEVICE:
    EVENTS:
      EV_KEY: [KEY_X, KEY_Y]

The step in the pipeline where you see y2z / x2y is the point where you plug caps2esc in. We also need to specify the keys to intercept -- KEY_CAPSLOCK and KEY_ESC. Create this file at something like /etc/caps2esc.yaml:

- JOB: "intercept -g $DEVNODE | caps2esc | uinput -d $DEVNODE"
  DEVICE:
    EVENTS:
      EV_KEY: [KEY_CAPSLOCK, KEY_ESC]

Daemonize Udevmon

Now you need to daemonize udevmon. It's a service and ultimately what listens for caps lock input when fed your YAML config file. You'll add it to Systemd so that it will start on bootup and continously run in the background.

/etc/systemd/system/caps2esc.service:

[Unit]
Description=caps2esc

[Service]
ExecStart=/usr/bin/nice -n -20 /usr/local/bin/udevmon -c /etc/caps2esc.yaml

[Install]
WantedBy=multi-user.target

With your newly added .service file, you should now be able to manage it via systemctl:

sudo systemctl daemon-reload
sudo systemctl enable caps2esc # Start on bootup
sudo systemctl start caps2esc # Start now too

Now try pressing Caps + C. If you see it working the same way a Ctrl + C works in your terminal (goes to a new clear line), it's now working.

Repurpose your freed up keys

You have now freed up two convenient keys on your keyboard. You can experiment with remapping them to other useful functions. Looking at your escape key, it now behaves as Caps Lock so it still has toggling behavior. You can make it behave like a simple keypress by adding this to .bashrc:

setxkbmap -option ctrl:nocaps # Unbind capslock (escape)

You can use xev to show debug information about keyboard input. Note the keycode:

xev -event keyboard

Bring its small window to the foreground and try pressing escape. You should see something like:

KeyPress event, serial 28, synthetic NO, window 0x5a00001,
root 0x14e, subw 0x0, time 15226248, (1188,694), root:(1233,803),
state 0x0, keycode 66 (keysym 0xffffff, VoidSymbol), same_screen YES,
XLookupString gives 0 bytes:
XmbLookupString gives 0 bytes:
XFilterEvent returns: False

With the correct keycode, you can remap it to something useful. For example, you could make your escape key run your nearest test. In .bashrc:

xmodmap -e 'keycode 66=minus' # Rebind capslock (escape) to '-'

You can then remap hyphen to run Vim command :TestNearest, assuming you're using vim-test. In .vimrc or init.vim:

noremap - :w<CR>:TestNearest<CR>

As you can tell you can really get creative. You can do the same for your freed up Control_L key, but tread carefully (read on).

See also a full list of keysyms that work with xmodmap.

Opening links in new tabs

Before this remap, I didn't even realize how often I control + clicked on links in browsers and in my terminal emulator. Sadly that does not work with caps2esc. Without getting into browser extensions, we're left leaning on the right click context menu to open in a new tab, or shift + clicking to open them in a new window. Hoping the maintainer will eventually address the issue.

Crouching in games

If you are a gamer, caps lock likely won't feel natural. I found it behaved pretty squirrely depending on what you were trying to do. For that reason you may not want to remap your control key.

So that's where I'm at. Overall, more than worth it to remap. Your pinky will thank you.

More blog posts