totalmapper
is a simple utility for remapping keys using the Linux event handling system.
It is more flexible than tools like xmodmap
and xkb
in that it lets you use any key as a modifier, enabling more complex layouts than can be achieved with the usual combination of alts, shifts, and controls.
- Use any key as a modifier
- Use any number of modifiers
- Write layouts in a simple JSON syntax that is easy to generate programmatically
- Run it on any Linux platform, including Chrome OS (in developer mode)
- Run it on X and Wayland and with all window managers and GUI frameworks
- Use a consistent layout across remote desktops and virtual machines
- Change repeat behavior per-key (e.g., disable repeat or repeat with a different code than the initial press)
- Prevent TYping LIke THis by making Shift only apply to one key
- Ubuntu amd64:
- Self-contained Linux binaries (useful for Chrome OS):
With cargo
:
cargo build --release
sudo cp ./target/release/totalmapper /usr/bin
To run totalmapper
manually, make sure you have write permissions to /dev/uinput
. Below is an example of correct permissions:
$ ls -l /dev/uinput
crw-rw----+ 1 root input 10, 223 Mar 12 22:37 /dev/uinput
$ groups
sys network power lp input
The add_systemd_service
command, discussed below, will automatically create a user with the correct permissions.
Try one of the builtin layouts with:
totalmapper remap --default-layout caps-for-movement --all-keyboards
See the list of builtin layouts with:
totalmapper list_default_layouts
See the JSON source for a builtin layout with:
totalmapper print_default_layout caps-for-movement
Define your own layout (see below) and remap your keyboard with:
totalmapper remap --layout-file my-layout.json --all-keyboards
If your system uses systemd
, you can add a udev
rule that will automatically run totalmapper
whenever a new keyboard is plugged in:
sudo totalmapper add_systemd_service --default-layout caps-for-movement
This will install a service definition in /etc/systemd/system/totalmapper@.service
that will run totalmapper
under a new user (totalmapper
) in the input
group.
If you have a keyboard that you do not want to be remapped, you can exclude it with the --exclude
option, which takes a glob-like pattern. First, use totalmapper list_keyboards
to find the name of the keyboard you want to exclude:
$ totalmapper list_keyboards
AT Translated Set 2 keyboard: /dev/input/event4
Then, use the --exclude
option to exclude it:
sudo totalmapper add_systemd_service --default-layout caps-for-movement --exclude 'AT Translated Set 2 keyboard'
If your system does not use systemd
(such as Chrome OS), you can have totalmapper
monitor for new keyboards itself:
totalmapper remap --default-layout caps-for-movement --auto-all-keyboards
Examples can be found using totalmapper list_default_layouts
and totalmapper print_default_layout <name>
.
Layouts are defined with a simple JSON syntax:
{
"mappings": [
{ "from": [ "CAPSLOCK" ], "to": [] },
{ "from": [ "CAPSLOCK", "J" ], "to": [ "LEFT" ] },
{ "from": [ "CAPSLOCK", "I" ], "to": [ "UP" ] },
{ "from": [ "CAPSLOCK", "K" ], "to": [ "DOWN" ] },
{ "from": [ "CAPSLOCK", "L" ], "to": [ "RIGHT" ] },
{ "from": [ "CAPSLOCK", "H" ], "to": [ "HOME" ] },
{ "from": [ "CAPSLOCK", "SEMICOLON" ], "to": [ "END" ] },
{ "from": [ "CAPSLOCK", "U" ], "to": [ "PAGEUP" ] },
{ "from": [ "CAPSLOCK", "M" ], "to": [ "PAGEDOWN" ] },
{ "from": [ "CAPSLOCK", "N" ], "to": [ "LEFTCTRL", "LEFT" ] },
{ "from": [ "CAPSLOCK", "COMMA" ], "to": [ "LEFTCTRL", "RIGHT" ] }
]
}
The names of keys are taken from the Linux header, minus the KEY_
prefix. Some keys you may not expect are:
- The backtick/tilde key is called
“GRAVE”
. - The left and right “windows” keys are
“LEFTMETA”
and“RIGHTMETA”
. - The period is
“DOT”
. - The single quote is
“APOSTROPHE”
.
You can use any key as a modifier. You don't have to tell totalmapper which keys are modifiers; simply creating a mapping that uses the key in combination with another makes it act like a modifier.
Be careful that if you want to use a key as a modifier that normally has another function, you will want to map the key by itself to []
, as in the example above.
The key names used to define JSON mappings correspond to kernel constants for keycodes. Typically, these names correspond to a physical QWERTY layout even if the labels on your keyboard are not QWERTY and even if you have set a non-QWERTY layout in X or Wayland.
Because of this, JSON mappings usually must be defined using labels that correspond to a QWERTY layout regardless of which layout you have configured (e.g. Coleman, Dvorak).
For example, the following mapping would trigger when pressing the key that maps to the character H
in a Dvorak layout (keycode 36) because it maps to the character J
in a QWERTY layout:
{ "from": [ "J" ], "to": [ "DOWN" ] }
To figure out what keycodes your keyboard uses, you may use evtest
.
A basic mapping maps some combination of keys to another combination of keys:
{ "from" : ["LEFTCTRL", "C"], "to": ["LEFTALT", "1"] }
The above mapping will be triggered when the user presses the left control key and then taps the 'C' key, and will make it as if the left alt were pressed while tapping '1'.
As of version 1.4, totalmapper
supports shorthands for common remapping situations.
You can remap an entire row of keys using the shorthand { "from": {"row": <rowname>}, "to": {"letters": <letters>} }
. For example:
{
"mappings": [
{ "from": {"row": "A"}, "to": {"letters": "aoeu"} }
]
}
The above example is equivalent to the following individual mappings:
{
"mappings": [
{ "from": "A", "to": "A" },
{ "from": "S", "to": "O" },
{ "from": "D", "to": "E" },
{ "from": "F", "to": "U" }
]
}
This shorthand can be combined with modifiers like so:
{
"mappings": [
{ "from": ["CAPSLOCK", {"row": "A"}], "to": {"letters": "=+-"} }
]
}
This is equivalent to:
{
"mappings": [
{ "from": ["CAPSLOCK", "A"], "to": "EQUAL" },
{ "from": ["CAPSLOCK", "O"], "to": ["LEFTSHIFT", "EQUAL"] },
{ "from": ["CAPSLOCK", "U"], "to": "MINUS" }
]
}
Note that in the above example, using “letters”
automatically includes the “LEFTSHIFT”
necessary to make the +
symbol on a US QWERTY keyboard.
The available rows are the following rows on a US QWERTY keyboard:
“`”
- The row starting with“GRAVE”
. You can also use use“1”
to refer to this row but starting with the“1”
key.“Q”
- The row starting with“Q”
.“A”
- The row starting with“A”
.“Z”
- The row starting with“Z”
.
If you have layouts that use the Shift keys, it can be tedious to duplicate each mapping for “LEFTSHIFT”
and “RIGHTSHIFT”
. Instead, you can use an alias, which is any word starting with @
:
{
"mappings": [
{ "from": "LEFTSHIFT", "to": "@shift" },
{ "from": "RIGHTSHIFT", "to": "@shift" },
{ "from": ["@shift", "SPACE"], "to": "BACKSPACE" }
]
}
See the super-dvorak
default layout for an example that makes heavy use of aliases.
totalmapper
works with keycodes, not key symbols. There are many more symbols than keycodes. For example, a
and A
are separate symbols, but in keycodes, A
is just Shift + A.
Typically on Linux, the mapping between keycodes and symbols is defined through a system like XKB. You can use XKB and totalmapper
together: totalmapper
will remap keycodes, and XKB will apply the appropriate symbols to the resulting keycodes.
If your keyboard has non-English symbols on it (such as ñ, ü, ㄴㄷㄹㅁ, or д), totalmapper
will remap those key codes the same as it remaps any other key codes—without regard for the symbols they stand for.
To figure out what keycodes correspond to your physical keys, you can inspect your keyboard device with evtest
. Here is an example output from evtest /dev/input/event2
:
Event: time 1623709383.272708, type 4 (EV_MSC), code 4 (MSC_SCAN), value 40
Event: time 1623709383.272708, type 1 (EV_KEY), code 64 (KEY_F6), value 0
Event: time 1623709383.272708, -------------- SYN_REPORT ------------
Event: time 1623709403.107286, type 4 (EV_MSC), code 4 (MSC_SCAN), value 1e
Event: time 1623709403.107286, type 1 (EV_KEY), code 30 (KEY_A), value 1
Event: time 1623709403.107286, -------------- SYN_REPORT ------------
Event: time 1623709403.240861, type 4 (EV_MSC), code 4 (MSC_SCAN), value 1e
Event: time 1623709403.240861, type 1 (EV_KEY), code 30 (KEY_A), value 0
Event: time 1623709403.240861, -------------- SYN_REPORT ------------
Event: time 1623709405.606143, type 4 (EV_MSC), code 4 (MSC_SCAN), value 1f
Event: time 1623709405.606143, type 1 (EV_KEY), code 31 (KEY_S), value 1
Event: time 1623709405.606143, -------------- SYN_REPORT ------------
Event: time 1623709405.722982, type 4 (EV_MSC), code 4 (MSC_SCAN), value 1f
Event: time 1623709405.722982, type 1 (EV_KEY), code 31 (KEY_S), value 0
The KEY_
part tells you what keycode was typed. Remove the KEY_
prefix and you can use it as a key in totalmapper
.
Because totalmapper
only works with keycodes, it can’t directly produce non-US-QWERTY symbols. However, totalmapper
can work alongside utilities like xkb
that can translate keycodes to a wide variety of symbols.
As an example, I like to have the section symbol (§) on my keyboard. totalmapper
cannot directly produce this symbol because there is no "§"
key on a standard US QWERTY keyboard. So, I use the following technique:
In my xkb
layout file, I include an AltGr key (also known as an ISO_Level3_Shift
) as the right Alt key:
key <RALT> { type[Group1]="ONE_LEVEL", symbols[Group1] = [ ISO_Level3_Shift ] };
Also in my xkb
layout file, I remap AltGr + S to “$”
:
key <AC02> {
type = "FOUR_LEVEL_ALPHABETIC",
symbols[Group1] = [ s, S, section, section ]
};
Then, in my totalmapper
layout file, I remap the appropriate keys to produce the AltGr + S combination:
{
"mappings": [
{ "from": "LEFTSHIFT", "to": "@shift" },
{ "from": "RIGHTSHIFT", "to": "@shift" },
{ "from": "CAPSLOCK", "to": "@symbol" },
{ "from": "RIGHTALT", "to": "@symbol" },
{ "from": ["@symbol", "@shift", "S"], "to": ["RIGHTALT", "S"] }
]
}
You can use totalmapper
to make specific keys or combinations of keys not repeat:
{ "from": [ "RIGHTSHIFT", "SLASH" ], "to": [ "LEFTSHIFT", "Z" ], "repeat": "Disabled" }
You can make a key repeat with a different code than the initial press:
{ "from": [ "SEMICOLON" ], "to": [ "S" ], "repeat": { "Special": { "keys": ["F21"], "delay_ms": 180, "interval_ms": 30 } } }
This will cause the first press of the ; key to generate the code for S, but, if held down, the repeat code will be F21. This can be used to make a key that repeats in some apps but not others by configuring how those apps treat the repeat code. I personally use it to make Vim movement letters (h, j, k, l) only repeat in Vim normal mode.
If you're like me, you have a tendancy to HOld DOwn SHift TOo LOong, resulting in WOrds LIke THis. totalmapper
can be used to make a modifier only apply to a single key stroke:
{ "from": [ "LEFTSHIFT", "L" ], "to": [ "LEFTSHIFT", "N" ], "absorbing": [ "LEFTSHIFT" ] }
The absorbing
option tells totalmapper
that after it applies this mapping, it should "absorb" the LEFTSHIFT
modifier so that it is not used for any subsequent keypresses.
The self-contained packages will run on Intel or ARM chromebooks in developer mode. There is no need to install crouton. The binary must be copied to a filesystem that allows code execution, such as /usr/local/bin
.
The chronos
user is part of the input
group but not the uinput
group. You can fix this problem with:
sudo chown root:input /dev/uinput
On some devices, the remapped keyboard will not automatically disable in tablet mode, which is annoying. Use the --tablet-mode-switch-device
option to have totalmapper read the tablet mode switch device and turn itself off:
totalmapper remap --dev-file /dev/input/event2 --tablet-mode-switch-device /dev/input/event5 --default-layout caps-for-movement
You can inspect devices under /dev/input
with evtest
to find your tablet mode switch.