Xorg and special keys: one solution for keycode>255

An historic

I've buy an IR receiver, and an universal controller to control my multimedia computer, my TV, my ADSL box and my 2.1 Home cinema.

I've been pleased to see that my universal controller do now more control than the remote that was sold with my IR receiver. But a lot of those control didn't work with Xorg, so I had to find someway to use them.

At first, my logs where spamed with line like:

imon 4-4:1.0: imon_incoming_packet: unknown keypress, code 0x100000e

(imon is the real builder of the IR receiver).

I looked for this on the internet and found

  • the problem was known
  • the fix was in the Linus kernel git repository
  • but not in any released kernel

So I waited, and Linux 3.10 was released, and the Debian Kernel Team did include it in debian sid, so I could easily use it.

But some command was still unrecognized as key by Xorg, so I had to find how to let X know them.

Reading documentation on internet, and trying some command lead nowhere: the kernel did in fact understood very well the command, but X did ignore them. After two day of trying to understood XKB, I found this bug: those key was unavailable to X and XKB because their keycode was bigger than 255.

The first solution to this problem is this patch, but I prefer to use standard tool. I finally found that udev can remap key, hence my solution:

The Solution

To use key that have keycode bigger than 255, you need to have keycode smaller than 255. keycode are determined by a map in the kernel, that udev can set when the receiver is plugged.

You need a keymap. input-kbd from the input-utils package will list the available scancode, and their current keycode (to know the number of your input device, use lsinput):

% sudo input-kbd 6
0x100007f = 106  # KEY_RIGHT
0x1000080 = 105  # KEY_LEFT
0x1007f00 = 108  # KEY_DOWN
0x1008000 = 103  # KEY_UP
0x1010000 = 272  # BTN_LEFT
0x1010080 = 272  # BTN_LEFT
0x1020000 = 273  # BTN_RIGHT
0x1020080 = 273  # BTN_RIGHT
0x200001e = 513  # KEY_NUMERIC_1
0x200001f = 514  # KEY_NUMERIC_2

(There is more, I just cut it there).

Putting this in some file (say keymap), editing it, and running sudo input-kbd -f keymap 6 should change the kernel mapping. But it don't.

It's not a problem, because the best solution is to use udev.

Wrote a keyboard mapping in say /etc/udev/imon:

0x200001e  KP1
0x200001f  KP2
0x2000020  KP3
0x2000021  KP4
0x2000022  KP5
0x2000023  KP6
0x2000024  KP7
0x2000025  KP8
0x2000026  KP9
0x2000027  KP0
0x2800000  MENU
0x288795b7 PAGEDOWN
0x289395b7 PAGEUP
  • You don't need to put there the scancode and keycode you don't divert,
  • the interesting scancode are found by input-kbd
  • you can also use input-events to find which scancode correspond to one key
  • the interesting keycode are in /usr/include/linux/input.h, note that this is not the same thing than X keycode...

You then need to tell udev to load this mapping, with /etc/udev/rules.d/96-local-keymap.rules:

ACTION=="remove", GOTO="mykeyboard_end"
KERNEL!="event*", GOTO="mykeyboard_end"
ENV{ID_INPUT_KEY}=="", GOTO="mykeyboard_end"
SUBSYSTEMS=="bluetooth", GOTO="mykeyboard_end"

SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"

ENV{ID_VENDOR}=="15c2", ENV{ID_MODEL_ID}=="0042", RUN+="keymap $name /etc/udev/imon"

LABEL="mykeyboard_end"

You will need to adapt 15c2 and 0042 to you own remote. I've found those using lsusb

Bus 004 Device 014: ID 15c2:0042 SoundGraph Inc.

I could now use xkb to further refine the way those key are used by xorg...