Here, as promised, are the gory details of keystroke handling and key mapping from kernel to XBMC
This is only known to be accurate for XBMC 12.2 (Frodo) on Ubuntu 12.04 LTS (Precise Pangolin). The part that has changed most frequently between releases is udev.
There are five levels where keystrokes are handled.
Code:
+------------------+
| kernel |
+------------------+
|
v
+------------------+
| udev |
+------------------+
|
v
+------------------+
| evdev / X Server |
+------------------+
|
v
+------------------+
| SDL |
+------------------+
|
v
+------------------+
| XBMC |
+------------------+
1. Keyboard scan codes to udev key codes
----------------------------------------
The kernel passes information about the device (in the case I was investigating, an RM518 wireless RF remote control) to udev from which udev sets up a node in /dev/input from which it then reads key events.
udev or the kernel has a database of known keyboards - apparently compiled in - which it uses to map scan codes to key codes. It produces perfectly reasonable codes for all the keys on the remote.
udev keycodes (KEY_FOO) are defined in /usr/include/linux/input.h
The easiest way to view scan and key codes is
Code:
sudo /lib/udev/keymap -i /dev/input/<device>
where <device> can be either "eventX" or "by-id/<name>". In the first case, replace X with the number of the device of interest. "evtest", see below, will show you a list of devices by name and number. For the RM518, pick the second "Holtek" device. Note that the numbers are likely to change across reboots. In the second case, for the RM518 remote <name> is
"usb-HOLTEK_Wireless_2.4GHz_Trackball_Keyboard-if01-event-mouse". This will not change across reboots. Scan codes and key codes are shown in the form needed in udev keymaps. I.e, keycodes are shown in symbolic form.
Key and scan codes can be also be viewed with "sudo evtest". For the RM518, pick the second Holtek device from the list. The scan code is displayed as the "value" entry in the MSC_SCAN (miscellaneous scan) event that is reported just before the key code of interest. The key code is shown using the KEY_FOO name from input.h and numerically. The value field of the key code event shows whether the key was pressed (1) or released (0).
The second "Holtek" device receives mouse events from both trackballs (keyboard and remote) and key events for the remote-only keys. These are the keys of interest.
The key mapping can be modified by:
- a rule in /etc/udev/rules.d (the place for local rules like this, the main rules are in /lib/udev/rules.d). The rule can contain maps for a small number of keys or it can point to a keymap file. If the keymap file is not in /lib/udev/keymaps, (e.g. it is in /etc/udev/keymaps then the rule needs the full path of keymap file.
Note that in the udev keymaps KEY_FOO becomes "foo" so each line will be something like "0x02" foo
To test the keymap file
Code:
sudo /lib/udev/keymap input/event4 /etc/udev/keymaps/holtek_rm518_remote
It will make the specified mappings. Note the missing /dev. Have not tested to see if it will work with /dev.
To view the information needed to write a udev rule do
Code:
udevadm info --query=all --name=/dev/input/event4
Using the symlink in /dev/input/by-id does not work...
To test parsing of the udev rules
Code:
udevadm test /sys/devices/pci0000:00/0000:00:1d.2/usb4/4-1/4-1:1.1/input/input4/event4
This takes no action, just reports what it would do. The final parameter is the DEVPATH reported by the info command above, preceded by "/sys".
2. udev key codes to X evdev driver key codes
---------------------------------------------
The evdev driver is hardwired to add MINIMUM_KEYCODE (=8) to keycodes coming in from udev. There is no per-code mapping though a modified evdev with per-code mapping is available at
http://www.thenautilus.net/SW/xf86-input-evdev/
So e.g., the udev keycode 130 (KEY_PROPS) is remapped to the X evdev code 138 which is known in the Xfree86world as keycode <PROPS>.
scancodes and keycodes can be viewed with "sudo showkey" except it does not show the codes for keys with codes > 255.
Issue is that the multimedia keys on the RM518 (the 8 keys that are unique to remote) have keycodes from udev > 255 so they must be remapped using a udev keymap. See my previous post in this thread for the file I am using.
3. X evdev key codes to X keysyms
---------------------------------
Key codes and keysyms can be viewed with xev.
X maps all the evdev key codes to keysyms. For example <PROPS> is mapped to the keysym SunProps.
Mapping can be modified using xmodmap as described below.
X keysyms are a sort of descriptive string like XF86AudioMedia, XF86WWW etc. but we can not use random names. A list of X keysyms can be found in the files /usr/lib/X11/XKeysymDB or /usr/share/X11/XKeysymDB
Then press a multimedia key. If you are lucky it already has a keysym bound to it, so the output of xev for that key will be something like this:
KeyRelease event, serial 36, synthetic NO, window 0x4000001,
root 0xab, subw 0x0, time 23936605, (-236,-203), root
865,338),
state 0x0, keycode 138 (keysym 0x1005ff70, SunProps), same_screen YES,
XLookupString gives 0 bytes:
XFilterEvent returns: False
The third row is the one of interest: it says that you have a keycode for that key (138) as well as keysym (SunProps). If you have a keysym then you can use that string to represent your key for purposes such as gnome keybindings or metacity keybindings but to use it with XBMC the keysym must be recognized by both SDL and XBMC. See below.
If you find that the key does not have any keysym assigned to it, like this:
KeyRelease event, serial 28, synthetic NO, window 0x3200001,
root 0xb7, subw 0x0, time 137355697, (401,146), root
413,264),
state 0x10, keycode 178 (keysym 0x0, NoSymbol), same_screen YES,
XLookupString gives 0 bytes:
you will have to assign a keysym to the relevant keycode (178). This is done with xmodmap.
First, create a file with your current X keyboard map, in a terminal type:
Code:
xmodmap -pke > xmodmap.conf
Then you are going to add all the missing keysyms to this file: use xev to see which keycode to use, look in the /usr/lib/X11/XKeysymDB or /usr/share/X11/XKeysymDB to find keysym names, open the xmodmap.conf file and fill in the missing keysym using a name which makes sense (i.e. if you have a button with a calculator printed on it, use XF86Calculator as keysym).
Repeat this passage for all your multimedia keys.
When finished, you can apply the changes with:
Code:
xmodmap xmodmap.conf
Now you want to load your new xmodmap.conf when X starts. Copy your xmodmap.conf file into ~/.xmodmap.
4. X keysyms to SDL keysyms
---------------------------
The mapping is hardwired in the code. Unrecognized X keysyms are set to an SDL keysym of 0. None of the X keysyms starting with XF86 is recognized so all are set to 0 in the SDL events.
All the code mentioned in the following is in src/video/X11/SDL_x11events.c in SDL 1.2.15.
Event dispatch is initiated by X11_PumpEvents which calls X11_DispatchEvent for each pending event. X11_DispatchEvent calls XNextEvent to get event from the X server. For key press events it essentially does the following (with some special handling for unicode affecting the .mod & .unicode fields).
Code:
keysym.scancode = keycode; // incoming X11 keycode
keysym.sym = X11_TranslateKeycode(SDL_Display, keycode);
keysym.mod = KMOD_NONE;
keysym.unicode = 0;
posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym);
X11_TranslateKeycode, for key codes with upper 8 bits of 0x00 to 0x0D, masks off the upper 8 bits. For codes beginning 0xFE it uses the hardwired ODD_keymap and for codes beginnng 0xFF it uses MISC_keymap using the lower 8 bits to index the maps. For any keycode having other values in the upper 8 bits, besides a couple of SunXK keysyms having upper 8 bits of 0x1005FF, or for any keys not in the ODD or MISC keymaps SDLK_UNKNOWN (0) is returned.
The keymaps are initialized in X11_InitKeymap but it is all hard-coded. No environment variables or files are read.
XF86* symbols have key codes beginning 0x1008FF so all of these get converted to SDLK_UKNOWN.
Note how the X key code is now referred to as a scan code, even though it is no such thing.
SDL_PrivateKeyboard (found in src/events/SD_keyboard.c) figures out the current modifier state from the global state modified by any modifier key (shift, etc.) in the passed in keysym->sym and sets keysym->mod to the new value. It then creates an SDL_Event "event" with:
Code:
// parameters to the function are state and keysym.
event.type = state == SDL_PRESSED ? SDL_KEYDOWN : SDL_KEYUP;
event.key.state = state;
event.key.keysym = *keysym;
and pushes this SDL_Event to the SDL client, i.e., XBMC.
5. SDL keysyms to XBMC actions
------------------------------
Starts in xbmc/windowing/WinEventsSDL.cpp CWinEventsSDL::MessagePump.
For keydown:
The incoming SDL_Event is converted to an almost identical XBMC_Event
- the key.keysym.sym field has the struct type XBMCKey but is otherwise identical to the SDL_Event's.
On linux if key.keysym.sym is 0 XBMC attempts to set sym from a pair of small hardwired tables, SymMappingsEvDev and SymMappingsUbuntu. The former is used when X Windows/evdev is the source of the events. This is my case. If the key.keysym.scancode is not found in these tables sym remains 0.
MessagePump Calls CApplication::OnEvent in xbmc/Application.cpp
CApplicationOnEvent
- calls CKeyboard:rocessKeyDown passing it the key.keysym field from the incoming XBMC_Event; this returns a CKey.
- calls CApplication:OnKey with the returned CKey.
CKeyboard:
rocessKeyDown (xbmc/input/KeyboardStat.cpp)
- converts keysym.mod values to a modifiers variable using CKeys:: values
- Logs "Keyboard: scancode: 0x%02x, sym: 0x%04x, unicode: 0x%04x, modifier: 0x%x" when in debug mode.
- Attempts to convert the keysym.sym to an XBMC vkey value. If keysym.sym is 0 or unrecognized, vkey is set to 0xF200.
Note that the scancode in the incoming event is NOT included in the returned CKey.
CApplication::OnKey(const CKey& key)
- calls CButtonTranslator::GetInstance().GetAction(iWin, key). iWin is the active window.
- uses CKey.GetButtonCode() as the code to match in the translator maps created from the keyboard.xml files.
CKey is in xbmc/guilib/Key.cpp
- the "button code" is either "vkey | KEY_VKEY | modifiers" or "KEY_UNICODE".
In other words it is the XBMC code for the key so <key id=""> in the keyboard.xml file is fairly useless. You have to plow through the XBMC source
to find the code for any key of interest.
It would be much more interesting if the scancode was included in CKey and a keymap.xml file could include something like
Code:
<key code="0x0F00">command</key>
Yes its not portable but local keyboard.xml files don't need to be portable. If this was done one would not need to worry about SDL and XBMC dropping unrecognized key symbols. You could get the key code from xev and have keyboard.xml map that to a command.
As noted, neither SDL nor XBMC recognize any X symbols names beginning XF86… and there are quite a few of those that would be useful with remote controls.
(2013-03-23, 16:02)msfc Wrote: I suspect maybe adding a <remote device="" name=""> section to keyboard.xml might do it but the instructions on how to specify @Name or @device attributes are extremely scant: "cat /proc/bus/input/devices".
As I have now learned from my code study, the code that processes incoming keyboard events uses only the map built from a <keyboard> section of a keymap. It pays no attention to <remote device=""name""> sections.