Making CapsLock useful on Ubuntu
CapsLock is in the wrong spot
I use vim
as my default text editor for most tasks, which means I use both the Esc
and Ctrl
keys quite a lot. On most keyboards, however, these two keys are not in very comfortable positions, and I need to stretch my little finger to hit either.
Meanwhile, the CapsLock
key is sitting right in prime real estate to the left of the A
key, but is a key I never really use. I’ve already rectified this situation on my macOS computers with Karabiner, and on my Windows computers with AutoHotKey. I recently switched an old 2012 Samsung laptop over to running elementaryOS, which is based on Ubuntu, and needed to accomplish the same task.
The swap
Basically, I want to move the CapsLock key somewhere unobtrusive on the keyboard (since I never use it), and then make the former CapsLock key perform two different functions:
- If it’s pressed by itself (i.e., not held down while pressing another key), then I want it to act as the Esc key.
- If it’s held down while pressing another key, I want it to act like the Ctrl key.
This way I get two very useful keyboard actions out of the same key, and can save my little finger some stress and strain for my most commonly used modifier keys.
Remapping CapsLock
There’s two different utilities I use to accomplish this swap. First is the setxkbmap
command that’s part of X Server. I want to move the CapsLock key to the original LeftCtrl position, and the Ctrl key to the original CapsLock position, so I’m using this:
$ setxkbmap -option ctrl:swapcaps
Setting Ctrl to Escape if pressed alone
Next, I want to make Ctrl (in its new position) act as Esc if pressed by itself, but as Ctrl if pressed with another key. I’m using a tool called xcape
for this, which isn’t part of a standard Ubuntu/elementaryOS installation, but can be easily installed via instructions on its GitHub page. Once I have xcape installed somewhere on my PATH, I use it like this:
$ xcape -e 'Control_L=Escape'
Keeping the changes set after sleep
Originally, I put the two commands above in my .zshrc
file so whenever I opened a new terminal it would apply the changes. This was mostly OK, except that whenever the laptop came out of sleep mode, the changes would be reset and I’d need to either open a new terminal, or re-source the .zshrc file. Neither is particularly optimal, so I spent some time figuring out how to get a script to run on system wakeup/resume. I’m still not sure my method is the “right way” to do this, but it has worked so far.
I created a new file in /lib/systemd/system-sleep/
called resume
(Note: you’ll need to use sudo
to be able to save/edit a file here), and added these lines:
#!/bin/bash
# make caps lock useful
case $1 in
post)
sleep 5
declare -x DISPLAY=":0.0"
declare -x XAUTHORITY="/home/<your-user-name>/.Xauthority"
setxkbmap -option ctrl:swapcaps
xcape -e 'Control_L=Escape'
;;
esac
Once you have saved this file, make it executable with:
$ sudo chmod +x /lib/systemd/system-sleep/resume
Basically, this is only running in the post
-sleep case, is ensuring we have an XServer reference, and then running the two commands I already had. Originally I didn’t have the two declare
lines, and kept getting errors like:
Unable to connect to X11 display. Is $DISPLAY set
These errors are fixed by the declare
lines I mentioned above. If the script isn’t working for you and you’re not sure why, you can add this line just below the #!/bin/bash
line in the script, and then check the contents after system resume:
exec 2> /tmp/systemd_suspend_test_err.txt
This line will send the STDERR stream to the temporary file that you can examine later.
If you can’t get this to work for you, have a better way of doing it, or just have a question, feel free to leave a comment below.