Remapping keys in linux

The Problem

Keyboard on my Thinkpad W530 has one quite moronic feature: page up and page down are beside arrow keys, while Home and End are on the other side of keyboard.

I want to switch these keys.

The solution (TL;DR;)

Add following line to /etc/rc.local

setkeycodes e049 102 e051 107 e047 104 e04f 109

Solutions

You may do it in couple of ways:

  • Do it at X level remapping using xmodmap
  • Do it at lower level

I did it previously using xmodmap, this solution had many drawbacks, and since upgrade to gnome 3.8 I needed to launch xmodmap by hand. Anyways this is what everyone suggests, like everywhere, so just google this solution.

Changing key bindings using udev

When linux reads a keystroke, first thing that is registered is so called scancode than scancode is converted to keycode.

First thing you'll need to know is what scancodes you want to remap, then you'll need to know to what keycodes you'll remap. To do this you'll need the showkey program.

To know scancode of a page up key you'll need to:

jb ~ $ sudo showkey --scancodes
# Some text
# I press PgUpXF86MonBrightnessUp
^[[H0xe0 0x47 0xe0 0xc7

It outputed four hex numbers: 0xe0 0x47 0xe0 0xc7, in my case first two were a scancode of the key. First one e0 is an escape character.

Then youll need to know keycodes of your chars:

jb ~ $ sudo showkey --keycodes
# Some text
# I press PgUp
^[[Hkeycode 102 press
keycode 102 release

Now we can remap keys, to do this you'll need the setkeycodes command, it takes list of pairs of numbers. First item in each pair is a scancode sa a hexadecimal number, without the 0x (so if scancode of PgUp is 0xe0 0x47 write: e047).

In my case it was:

sudo setkeycodes e049 102 e051 107 e047 104 e04f 109

Syntax for setkeycodes is quite moronic, so here is explanation, following command setkeycodes e049 102 means: set keycode 102 (102 is in decimal) for scancode combination: 0xe0 0x49, note that both bytes are concatenated, and are in without leading 0x.

Changes made by setkeycodes are not permanent, so you'll need to find a way to execute this script at start of the system.

Warning

Using this method can harm your computer. If you'll mess keymap severly, and make changes peremanent you might be unable to login (when you for example remap letter characters).

For example insert this line inside /etc/rc.local file.

When this approach will not work, and what to do then

This will change keyboard bindings for all input devices, which might not be what you want.

You should use udev to do this, I have tried and failed, if you'll succeed please let me know :)