I had the same problem and I've just found a solution. Let's start with the obvious first.
If you want to get specific keys such as "W" or "4", no matter where they're located, you can just convert the keycode you receive from the event into a KeySym. In this case "W" is XK_W
and XK_w
and "4" is XK_4
(and XK_dollar
on most keyboards).
However, sometimes you want to get keys such as "the nth key of the mth row". You need key names to do that. In this case "W" is AD02
and "4" is AE04
on QWERTY keyboards.
Let's say you are making a game in which the player needs to use the WASD keys to move. If you look for KeySyms it's going to work fine on QWERTY keyboards, but people using other keyboard layouts such as AZERTY, QWERTZ and DVORAK will have trouble. So in this case it's better to use key names.
Using key names is actually pretty easy, but the documentation is very messy (but I still recommend you take a look at it). I had to take a look at GLFW's source code (specifically src/x11_init.c) because I was clueless. This method requires Xkb, but you were already using it so I guess that's no problem.
First you need to get the keyboard map and obtain symbolic names. We only want key names so we use XkbKeyNamesMask
.
#include <X11/XKBlib.h>
XkbDescPtr KbDesc = XkbGetMap(XDisplay, 0, XkbUseCoreKbd);
XkbGetNames(XDisplay, XkbKeyNamesMask, KbDesc);
Then, at the event loop you can use the KbDesc->names->keys array to get the key name for a specific keycode:
XEvent Event;
XNextEvent(XDisplay, &Event);
switch (Event.type)
{
case KeyPress:
/* I'm not sure this 'if' is necessary, but better safe than sorry */
if ((Event.xkey.keycode >= KbDesc->min_key_code) && (Event.xkey.keycode <= KbDesc->max_key_code))
{
/* Copy key name into Name */
char Name[XkbKeyNameLength + 1];
memcpy(Name, KbDesc->names->keys[Event.xkey.keycode].name, XkbKeyNameLength);
Name[XkbKeyNameLength] = '\0'; /* Null terminator */
if (strcmp(Name, "AD02") == 0) /* Is it W (for QWERTY and QWERTZ) / Z (for AZERTY) / comma (for DVORAK) / ц (for Russian) etc... ? */
{
/* Do something... */
}
else if (strcmp(Name, "AE04") == 0) /* Is it 4 (for most keyboards) / whatever's in its place? */
{
/* Do something... */
}
/* ... */
}
/* ... */
}
And that's it. It seems to work pretty well so far. I'd like to mention that special keys have very different key names. For example, Left Shift is LFSH
, Left Control is LCTL
, Space is SPCE
and Escape is ESC
.
I hope it helps.
/usr/share/X11/xkb/keycodes
. – Cumulationsetxkbmap -print -verbose 10
and you will see a geometry include statement. Examine files in X11/xkb/geometry (most probably one named "pc"). You will also see which mapping is in use. I don't know programmatic equivalent tosetxkbmap -print
but there definitely is one. I'm not sure what happens if the user remaps his keyboard with xmodmap, but I'd say let them deal with it. – Cumulation