I shall walk through the Valley of Death, and I shall fear not, for it shall be a walk in the park compared with X's low-level keyboard handling :-)
I am trying to configure X for a custom keyboard. Both the archives of
this list and Keith Packard have been very helpful in figuring out what
is the problem when interfacing a new keyboard to X. Unfortunately, as
is often the case, the tricky part is how to make it right.
First, I'll summarize what I have learned so far about X's keyboard
handling, so anybody reading this can (hopefully) point out any mistakes
in my understanding.
The process of triggering an action (such as inserting a character)
through a keystroke works as follows: the keyboard detects the
keystroke, and sends to the computer the key's scancode, which is one or
two bytes identifying the key. Low-level software in the computer (the
kernel, for instance, or X's own xf86Events.c) converts this scancode
into a keycode. This intermediate coding is useful for attaining
cross-platform compatibility: no matter what scancode the keyboard
generates for a given (say, the Enter key), the software can map it to
the same keycode, and thus all upper layers don't need to care about
every keyboard's own scancode mapping. Further up the chain, this
keycode is mapped to a "keysym" (short for "keysymbol"), which is what
finally is used by the applications to determine which action should be
taken.
The mapping from keycodes to keysyms is the less problematic of all. If
the low-level software generates a distinct keycode for each scancode
the keyboard produces, most environments offer tools to configue this
mapping any way you like it. For instance, the Linux kernel does it
through the 'loadkeys' program, and X seems to offer two alternatives
for the same task: the XKB extension, and the 'xmodmap' program (and as
far as I can tell, you can use BOTH at the same time). From what I
gather, the "real" way to configure a keyboard mapping is through XKB,
which means creating a set of files similar to the ones under
/usr/X11R6/lib/X11/xkb, but the format is complex and largely
undocumented, so most people I know just use xmodmap, which has a very
simple syntax, and even a great graphical front-end by jwz, 'xkeycaps'.
Now, the problem arises if your keyboard generates non-standard
scancodes (although it may be well argued that "standard scancodes" is
an oxymoron): the software may not know how to map these scancodes to
keycodes in a manner than makes any sense. The Linux kernel solves the
issue by providing an API and a program, 'setkeycodes', which allows the
admin of the system to remap the keycode bindings almost arbitrarily
(for some reason, the keycodes below a certain numeric value cannot be
remapped). X, on the other hand, maps scancodes to keycodes in the
ProcessInputEvents() function in xf86Events.c, which you can find in the
X sorce tree under xc/programs/Xserver/hw/xfree86/common. This function
contains a huge switch() statement which looks like it has been tweaked,
hacked, ifdef'd and just generally abused in every way known to
hackerdom in a desperate (but surprisingly successful) attempt to make
it work in as many platforms as possible.
Work this approach does... but it forces X to be kept in sync with
keyboard development through further tweaking of a stretch of code that
is already tense beyond recognition. This has caused trouble with USB
keyboards that sport non-standard keys, or the newer "internet"
keyboards, or with my custom keyboard (more on it later), because
ProcessInputEvents() often maps two (or more?) distinc scancodes into a
single keycode, making them impossible for XKB or xmodmap to distinguish
between them.
An attempt has been made under Linux to take advantage of the kernel's
flexibility: upon startup, the server reads the keycode mappings into a
table, and somehow converts it into a format suitable for use by
ProcessInputEvents(). The idea is that if you then enable this table by
including the "CustomKeycodes" in the keyboard section of XF86config-4
(I found the need for this option only through browsing the source code,
I couldn't find it documented anywhere) X would use the same
scancode->keycode mapping as the kernel.In practice, however, I have
found this not to be quite the case. For instance, the up-arrow key in
my laptop (scancode 0xe0 0x48) is mapped by Linux into keycode 103. When
started without "CustomKeycodes", X maps this scancode (and also
scancode 0x5a) into keycode 98, as can be seen in this portion of xev
output:
KeyPress event, serial 29, synthetic NO, window 0x2800001,
root 0x31, subw 0x0, time 2948375797, (210,207), root:(275,294),
state 0x0, keycode 98 (keysym 0xff52, Up), same_screen YES,
XLookupString gives 0 characters: ""
KeyRelease event, serial 29, synthetic NO, window 0x2800001,
root 0x31, subw 0x0, time 2948375879, (210,207), root:(275,294),
state 0x0, keycode 98 (keysym 0xff52, Up), same_screen YES,
XLookupString gives 0 characters: ""
If I start the server with CustomKeycodes, however, I would expect to
see keycode 103 come to life. But xev reports, for the same *single*
keystroke (press-release), the following sequence:
KeyPress event, serial 29, synthetic NO, window 0x1200001,
root 0x31, subw 0x0, time 2948842557, (-165,-547), root:(484,11),
state 0x0, keycode 104 (keysym 0xff54, Down), same_screen YES,
XLookupString gives 0 characters: ""
KeyRelease event, serial 29, synthetic NO, window 0x1200001,
root 0x31, subw 0x0, time 2948842557, (-165,-547), root:(484,11),
state 0x0, keycode 104 (keysym 0xff54, Down), same_screen YES,
XLookupString gives 0 characters: ""
KeyPress event, serial 29, synthetic NO, window 0x1200001,
root 0x31, subw 0x0, time 2948842562, (-165,-547), root:(484,11),
state 0x0, keycode 80 (keysym 0xff97, KP_Up), same_screen YES,
XLookupString gives 0 characters: ""
KeyPress event, serial 29, synthetic NO, window 0x1200001,
root 0x31, subw 0x0, time 2948842629, (-165,-547), root:(484,11),
state 0x0, keycode 104 (keysym 0xff54, Down), same_screen YES,
XLookupString gives 0 characters: ""
KeyRelease event, serial 29, synthetic NO, window 0x1200001,
root 0x31, subw 0x0, time 2948842629, (-165,-547), root:(484,11),
state 0x0, keycode 104 (keysym 0xff54, Down), same_screen YES,
XLookupString gives 0 characters: ""
KeyRelease event, serial 29, synthetic NO, window 0x1200001,
root 0x31, subw 0x0, time 2948842634, (-165,-547), root:(484,11),
state 0x0, keycode 80 (keysym 0xff97, KP_Up), same_screen YES,
XLookupString gives 0 characters: ""
So either I'm doing something awfully wrong, or X is.
In any case, I'm left with the problem of how to get my keyboard to talk
properly to X, but I'm afraid that it's only the tip of the iceberg.
After all, I can patch the server and get my stuff running. But we X
users are going to find it increasingly difficult to keep up with new
keyboards and new keyboard concepts. Mine is just a (rather
unspectacular and not particularly new) example: it's a chord keyboard
designed to interface with a PC through the PS/2 keyboard connector. One
of chord keyboards drawbacks is that modifier keys (Shift, Ctrl, etc)
are expensive: in a standard keyboard they count as 1/2 a keystroke, but
in a chorded keyboard they require a full extra chord. This can be
alleviated by the fact that, if we equate chord=key, a chord keyboard
has many more "keys" than a traditional keyboard, so we can use some of
these chords to access characters that are usually shifted, such as "#"
for instance.
Note that none of this touches the much-loved-hated XKB stuff. XKB makes
this neither easier nor more difficult. The problem is that we need a
configurable ProcessInputEvents() function. Now, this doesn't look like
anything hard to accomplish, it's just a table lookup so once the
problem has been identified it shouldn't be much more than afternoon's
work, but then why has nobody undertaken it yet? Is it the difficulty of
trying to figure out what mappings result from which
OS/processor/keyboard combination and creating the corresponding
configuration files?
Confused minds want to know...
Federico
signature.asc
Description: This is a digitally signed message part
