Reputation: 262
I am currently developping a GUI for linux (under Raspberry Pi Hardware) and i have a (pretty basic) problem about the keyboard. I am able to handle keypressed, keyreleased ... by using the input/eventX file but the only thing i get is the keyCode (obviously).
Upvotes: 3
Views: 5063
Reputation: 39426
This mapping is done by the kernel for console terminals (see dumpkeys or run sudo dumpkeys -l
to see the current console mapping), and by the X server for X server applications (use xmodmap -pm -pk
to see the mapping; first column is without any modifiers, second column is with the first modifier alone, third column is with second modifier, fourth column is with the two first modifiers, fifth column is with the third modifier, and so on). In general, the keymap files are described in man 5 keymaps.
Your question is, how to do this mapping yourself.
First, you need to understand how the mapping works. In the Linux input subsystem, there are 9 modifiers, logical states, that can be combined to produce any of 29 = 512 different modifier states. These have completely arbitrary names that are not related to any specific keys per se. (They are Shift (20 = 1), AltGr (21 = 2), Control (22 = 4), Alt (23 = 8), ShiftL (24 = 16), ShiftR (25 = 32), CtrlL (26 = 64), CtrlR (27 = 128), and CapsShift (28 = 256). The "active" ones are summed together to get the actual modifier state.)
Let's assume you use int
to describe each keyboard result; positive for Unicode code points (characters), and negative for custom action keys (like "cursor up", "page down", and so on). The mapping for each key can then be described using something like the following minimal example:
struct mod_one {
__u16 down; /* State change when pressed */
__u16 up; /* State change when released */
};
struct key_one {
size_t count; /* Number of states for this keycode */
unsigned short *state; /* Array of states */
int *result; /* Array of keyboard_event() return values */
};
struct key_map {
/* Modifier keys */
size_t mod_count;
__u16 *mod_code;
struct mod_one *mod_xor;
/* Non-modifier keys */
size_t key_count;
__u16 *key_code;
struct key_one *key_map;
};
int keyboard_event(const struct input_event *const ev, unsigned short *const state, const struct key_map *const map)
{
/* Note: Uses linear search for simplicity.
* In practice, you want the arrays to be sorted,
* and use a binary search instead.
*/
/* Sanity check for NULL pointers. */
if (!ev || !state || !map)
return 0;
if (ev->type == EV_KEY) {
size_t i, k;
/* Is it a modifier key? */
for (i = 0; i < map->mod_count; i++)
if (ev->code == map->mod_code[i]) {
/* Yes. Apply the state change. */
if (ev->code == 0)
*state ^= map->mod_xor[i].down;
else
if (ev->code == 1)
*state ^= map->mod_xor[i].up;
break;
}
/* Does this event map to a keypress?
* We rely on kernel/device autorepeat. */
if (ev->code == 0 || ev->code == 2)
for (i = 0; i < map->key_count; i++)
if (ev->key == map->key_code[i]) {
for (k = 0; k < map->key_map[i].count; k++)
if (*state == map->key_map[i].state[k])
return map->key_map[i].result[k];
}
}
return 0;
}
Do note that the above really is the absolute minimum, and is untested.
It does not support Uncaps_Shift (which releases capslock without pressing the caps lock key), and defining a struct key_map
from a keymap file is a bit tedious. But, hopefully, you can see the logic (by perhaps trying it with a simple struct key_map
and observing the *state
changes and return values). In a real program, I think I'd handle the shift/capslock state separately, and use a single mask per modifier key (that or's the keystate when pressed, and nand's (and not) the keystate when released), with shift and caps lock states being xor's of each other, so that if the modifier state somehow gets confused, tapping these keys a couple of times will always fix the states.
Personally, I would recommend (as an user, demand) that you use Unicode, and not ASCII or Windows-1252 or ISO-8859-x. It is trivial to convert the Unicode codes to normal UTF-8 strings and check how many char
s they take. Editing strings, especially moving the cursor in the string, and deleting individual characters, is easier if you use Unicode strings during editing, though. (I prefer to use UTF-8 for "immutable" strings, but in editing widgets, convert them to wchar_t
or similar, for simplicity. UTF-8 ⇔ UCS4 transforms are trivial.)
(In case you don't know it yet, the character € is available on most European keyboards as AltGr+E. It is Unicode U+20AC, or code 8364, and in UTF-8 is represented by three chars, \xE2\x82\xAC
. If you cannot support it, don't expect Europeans to use your UI. And no, ISO-8859-15 does not really cut it either, even if it does have €.)
Finally, for your UI, I recommend you use a single dedicated pthread in your program to read from the input event devices -- all keyboards, mice, and pointers --, and supplying keypresses (and notifications of pointer changes) into a circular buffer, using atomic counters and a semaphore, or a mutex and a condition variable to maintain the state of the buffer between threads. The dedicated thread only needs a minimal stack (16384 or 32768 bytes should suffice), and since it blocks waiting for new events for most of the time, it won't consume much resources at all.
Having the dedicated thread maintain both keyboard and pointer input devices (and inserting pointer moved or did something "characters" to the input buffer) means your program can use a simple loop to check for user inputs; whenever the user presses a key or moves a pointer, the loop will iterate. No need for polling, you see. It works really well for simple framebuffer-based graphical interfaces.
Upvotes: 4