How I use keyd to remap my keyboard in Ubuntu 22.04 with Wayland
Think about how you use your keyboard. Imagine how good it would be if your
CapsLock
can be used as Ctrl
, how much better it would be for your left
pinky? That is the power of remapping the keyboard. It means you can customize
the functionality of each key on your keyboard.
This is a walkthrough of my setup in Ubuntu using keyd
. A guide I wish it
existed when I first try to find out how to remap my keyboard in Ubuntu.
Why I Remap my Keyboard
After I dual-boot with Ubuntu and used it as my daily driver for work and personal use, one key thing I missed is AutoHotkey. It is a software that I used to remap my keyboard in Windows and create shortcuts and "hotstrings". For example,
- Remapping
CapsLock
toCtrl
on hold, but works asEsc
when clicked alone z+d
to scroll down andz+u
to scroll up, and other ways to move my mousez+e
andz+g
to insert my email addresses|!=
,|->
,|<-
will be replaced by not equal (≠), right arrow (→) and left arrow (←), etc., automatically
I shared my AHK configuration in this repository if you are interested. I might write about it later too.
I think remapping keyboard is one of the most important change I made to my
laptop ever since I know how to use a computer. It is like the first time you
discovered you can use Ctrl+C
rather than right click and select copy. I
highly encourage everyone to try it, especially for keys like CapsLock
that is
in a very convenient location but is rarely used.
So once I boot to Ubuntu, the first thing I wanted to do is to replicate this setup. Unfortunately, AHK only works for Windows, so I need to look for alternatives. The first difficulty that you might face as well is Ubuntu 22.04 by default uses Wayland as the window system, but a lot of tools available online works for X11 only. For example, there is AutoKey that seems to be popular but only works for X11.
I first tried input-remapper
. It
looks promising with nice graphical user interface, but it didn't work out when
I tried to add more complicated logics. I also tried
keyboard, a Python module which allows me
to create custom keyboard events in Python code. It's like coding my own daemon,
but I found it too much overhead and quite laggy. In the end, I found
a list of input remapping utilities
provided by Arch Linux wiki. Going through the list and I chose
keyd
which works for me quite well over the
past year.
If you are using Windows, you can check
AutoHotkey as linked above. If you are on Mac, I
read that
Karabiner
is good, but I have not used a Mac before. If your keyboard supports it,
QMK/VIA
might be good for you. Even if keyd
doesn't work for you, you may follow along
to get some inspiration even though the syntax of the config is not the same.
Basic Concepts in keyd
The very first thing to understand in keyd
config is it operates in terms of
layers. The most intuitive way for me to understand a layer is the Shift
key.
When the Shift
key is pressed and hold, a different layer is activated and all
the keys on your keyboard have a different meaning. And what keyd
allows you
to do is to define custom layers, that is, defining how the layers are activated
and deactivated, and what each key means in each of the defined layer.
The major reason I select keyd
is it works in X, sway and gnome in Wayland.
Second, it natively supports key overloading, which allows me to configure the
CapsLock
key to behave as Ctrl
on hold while Esc
when tapped. From what I
test, it is instant and fast too.
To get started, install keyd
from source. Clone
the repository and build it from source.
Follow the instructions in the
README to
install it.
Here are some helpful commands to know:
sudo systemctl enable keyd
: startkeyd
, probably run it once in your lifetimesudo keyd reload
: reload the config every time after you edit the configsudo keyd monitor
: print key events, useful to debug what is remappedkeyd list-keys
: list all the valid key names, useful to check the possibilitiesbackspace+escape+enter
keyboard combo: terminatekeyd
anywhere anytime in case you severely messed up (happened once to me)
Sharing my Configuration
Here is the full config if you are interested. I will explain it line by line below.
[ids]
*
[main]
# Maps capslock to escape when pressed and control when held.
capslock = overload(control, esc)
# Maps z to a custom layer, but just 'z' when pressed.
z = overload(z, z)
# Shift layer
[shift:S]
## shift+capslock is capslock
capslock = capslock
# Custom z layer
[z]
## Escape common patterns
i = macro(zi)
o = macro(zo)
## Emails
e = macro(hi@ethanppl.com)
g = macro(hi@ethanppl.com)
## Simplify complicated shortcut keys
v = C-S-v
n = C-S-a
x = A-f4
s = command(systemctl suspend)
## Arrows
j = down
k = up
h = left
l = right
## Media / modifier
m = oneshot(media)
[media]
j = previoussong
k = playpause
l = nextsong
a = macro({ enter 10ms "Aut 10ms hor 10ms iza 10ms tio 10ms n": space "Bea 10ms rer space C-v)
Main layer
Let's go through it line by line.
capslock = overload(control, esc)
This is the most important feature that I need, as introduced in the beginning.
According to the man page, overload(<layer>, <action>)
"activates the layer on
hold while executes the action on tap". This line means CapsLock
will act like
Ctrl
when used with other keys. But when I tap it only, it works as Esc
.
This makes key combo like Ctrl+C
way easier than before, where the Ctrl
key
is in the bottom left. It also makes Esc
easier, which is used a lot in Vim.
This single line is the biggest reason why I picked keyd
.
z = overload(z, z)
This might seem weird when you first look at it, but think about the key z
as
its own layer (remember, layer is like the Shift
key). So when z
is hold, it
activates a z
layer, like holding the Shift
key activate the shift layer,
but it acts as z
when tap alone. This gives me another modifier key (e.g.
Ctrl
, Alt
, Shift
), without overriding what the default keyboard shortcuts
that come with software programs. But before we go into this special z
layer,
we need to fix one thing first.
Shift layer
[shift:S]
capslock = capslock
We don't have a CapsLock
key after we remap it. What this two lines do is
that, in the shift layer, map CapsLock
to work as CapsLock
. So to summarize,
right now holding CapsLock
is Ctrl
, tapping CapsLock
once is Esc
, and
doing Shift+CapsLock
is CapsLock
.
The z
layer
i = macro(zi)
o = macro(zo)
First thing is since I did this custom z
layer with AutoHotkey in Windows, I
realized the character i
and o
commonly follows the z
key (e.g. amazing
and amazon). To avoid delay in typing or keys being ignored because I typed i
before releasing z
. I mapped press and hold z
then i
(z+i
) to output
zi
and z+o
to output zo
here.
I use +
sign to mean press and hold the first key and type the second key. But
keyd
use +
sign to mean chording, which means two keys to be pressed at the
same time. I didn't use chording in my config and most of the documentation for
keyboard shortcuts often use +
sign like Ctrl+c
, so I hope it's easy to
understand.
e = macro(hi@ethanppl.com)
g = macro(hi@ethanppl.com)
Next, I mapped z+e
and z+g
to two emails that I used the most for
communication and sign in. You will be amazed how many times you type your email
each day. And how much better you don't need to type @
anymore.
v = C-S-v
n = C-S-a
x = A-f4
s = command(systemctl suspend)
Here I simplified some commonly used shortcuts with the z
layer. In keyd
,
capitalized C
, S
and A
means Ctrl
, Shift
and Alt
key respectively.
And the hyphen -
means press and hold. For example, z+v
is an alias of
Ctrl+Shift+V
which is often used as paste text only or the markdown preview in
VSCode. z+n
is an alias of Ctrl+Shift+a
which shows information of all tabs
in Chrome. z+x
is an alias of Alt-F4
which closes a window. And z+s
run
the systemctl suspend
command, which will suspend the laptop. I find this
helpful, and somehow I trust it to suspend my laptop successfully more than just
closing the lid of my laptop.
j = down
k = up
h = left
l = right
Here I mapped j
, k
, h
, l
to be arrow keys. For example, holding z+l
will produce the right arrow key. The reason for these mappings (e.g. why j
is
down) are based on Vim motions. These are helpful because arrows are usually
unreachable unless I move my palm away from my keyboard. Doing z+l
allows me
to do things like autocomplete in terminal without moving my palm.
You might also notice that I try to pair keys that are comfortable to reach when
holding z
down, like I would avoid mapping anything to z+a
that is just
complicated and unnatural to type.
The z+m
layer, a layer on top of a layer
m = oneshot(media)
Still in the z
layer, I defined the m
key to activate the media
layer. It
activates this layer as oneshot
. The man page defined oneshot
as "If tapped,
activate the supplied layer for the duration of the next key press". It means
the layer is activated once tapped, and it will be toggled off only after
another key is pressed. This means the media
layer is activated once we tap
z+m
, and we don't have to hold it for it to be active (unlike the shift
or
z
layer).
[media]
j = previoussong
k = playpause
l = nextsong
I discovered these keys when browsing through the keyd list-keys
command. And
Ubuntu support these keys. How this works is once I pressed z+m
, then tap l
,
it will emit a nextsong
key press. What this allows me to do is whichever
active window I am in, I can use z+m
, then j
, k
, or l
to go back, pause,
or skip a song in Spotify, which I think is pretty amazing.
a = macro({ enter 10ms "Aut 10ms hor 10ms iza 10ms tio 10ms n": space "Bea 10ms rer space C-v)
One last line which doesn't relate to media actually, but I put it in any way.
It is used to help me type the authorization header in GraphQL playground. What
I have to do is copy the token that I want to use, then type z+m
and a
, it
will help me generate the whole
{
"Authorization": "Bearer <token_copied_here>"
}
It is taking advantage of the GraphQL playground I used that will help me close
the {
curly braces. With some trial and error I realize I cannot make keyd
to type all keys at once, so I leave some delay in between and that works
better. Having it run Ctrl+V
to paste also helps a lot. I find this saves me
quite some time each day.
Other thoughts
There are many other features in keyd
that is up to you to explore. I have
added different configs in and out over the year until I settle down to this set
of commands. For example, initially I also configured a shortcut to type
console.log
and IO.inspect
for TypeScript and Elixir, other than the
authorization header shortcut, but I found out I rarely used them and I removed
them.
I also tried oneshot(shift)
which is recommended in the keyd
README. But it
didn't work for me. I find out I often tap Shift
but changed my mind
afterwards, which makes me accidentally typed characters in uppercase. I also
find out oneshot(shift)
doesn't work well with Shift and drag to select in
bulk with mouse because it doesn't understand there is a mouse click and
deactivate the shift layer after I realize the Shift
key.
Another thing I found is the command()
call doesn't always work. I once
installed copyq
to get clipboard history and I configured z+c
to be
command(copyq show)
, but it never worked. It's not a dealbreaker and I didn't
spend time to debug why.
One thing you might already notice is there is no more hotstrings, which I had
in my AutoHotkey configuration. I can no
longer type ≠
, →
, ←
and other special characters that easily. I realize
configuration like l = ←
doesn't work. After reading the man page, I believe I
can make it works by setting up Unicode support, which have some other external
configuration required, and I have not spent the time investigating.
There is also no more mouse control. There is no way to move my mouse with
keyd
because all keyd
does is to remap keys. I know there are other daemons
in Linux that are designed for that, but I have not tried. I remember I found
one before, but it only works for X11.
There are also some minor problems that I wish to solve in the future. For
example, Ctrl+<arrows>
is a common key combination that I do, but I can't
easily do that with CapsLock+z+<hjkl>
because the relative position of
CapsLock
and z
is too close.
I would like to try mapping specific shortcuts to specific applications too. But
most of the time I find the default configs coming with the app works good
enough. Also, I did not configure any keys to launch an application because I
find meta+<num>
good enough to open the windows that are pinned to the task
bar. For example, win+2
always open my browser and win+3
open VSCode.
That is how my configuration in keyd
works and some of my reasoning behind it.
I hope you like this explanation, and it inspires you to remap your keyboard
too. It genuinely improved my life.
You might be interested in this page about keyboards in my Wiki too.
Useful links
keyd
repositorykeyd
examples by the creator- If you are stuck, you might find a solution by searching the
issues in
keyd