Checking in Christian Walther's patch for x11 keyboard input. Minor code tweaks by Bob.
authorBob Pendleton <bob@pendleton.com>
Tue, 08 Jan 2008 00:10:46 +0000
changeset 2295 dbc6d1893869
parent 2294 386ebf50dd91
child 2296 0869721b488f
Checking in Christian Walther's patch for x11 keyboard input. Minor code tweaks by Bob.
include/SDL_compat.h
include/SDL_keysym.h
src/events/SDL_keyboard.c
src/joystick/darwin/SDL_sysjoystick.c
src/video/SDL_renderer_gl.c
src/video/x11/SDL_x11events.c
src/video/x11/SDL_x11keyboard.c
src/video/x11/SDL_x11keyboard.h
src/video/x11/SDL_x11video.c
src/video/x11/SDL_x11video.h
src/video/x11/imKStoUCS.c
src/video/x11/imKStoUCS.h
test/checkkeys.c
--- a/include/SDL_compat.h	Thu Jan 03 06:07:30 2008 +0000
+++ b/include/SDL_compat.h	Tue Jan 08 00:10:46 2008 +0000
@@ -185,7 +185,10 @@
 #define SDLK_SCROLLOCK SDLK_SCROLLLOCK
 #define SDLK_PRINT SDLK_PRINTSCREEN
 
-/* These key constants are obsoleted the new keyboard handling, their definitions here correspond to how they appear on a US keyboard. */
+/* These key constants are obsoleted by the new keyboard handling,
+   their definitions here correspond to how they appear on a US
+   keyboard. */
+
 #define SDLK_EXCLAIM SDLK_1
 #define SDLK_QUOTEDBL SDLK_APOSTROPHE
 #define SDLK_HASH SDLK_3
--- a/include/SDL_keysym.h	Thu Jan 03 06:07:30 2008 +0000
+++ b/include/SDL_keysym.h	Tue Jan 08 00:10:46 2008 +0000
@@ -300,9 +300,10 @@
     SDLK_MUTE = SDL_PHYSICAL_KEY(127),
     SDLK_VOLUMEUP = SDL_PHYSICAL_KEY(128),
     SDLK_VOLUMEDOWN = SDL_PHYSICAL_KEY(129),
-    /*SDLK_LOCKINGCAPSLOCK = SDL_PHYSICAL_KEY(130), not sure whether there's a reason to enable these
-       SDLK_LOCKINGNUMLOCK = SDL_PHYSICAL_KEY(131),
-       SDLK_LOCKINGSCROLLLOCK = SDL_PHYSICAL_KEY(132), */
+/* not sure whether there's a reason to enable these */
+/*     SDLK_LOCKINGCAPSLOCK = SDL_PHYSICAL_KEY(130),  */
+/*     SDLK_LOCKINGNUMLOCK = SDL_PHYSICAL_KEY(131), */
+/*     SDLK_LOCKINGSCROLLLOCK = SDL_PHYSICAL_KEY(132), */
     SDLK_KP_COMMA = SDL_PHYSICAL_KEY(133) | SDL_KEY_KEYPAD_BIT,
     SDLK_KP_EQUALSAS400 = SDL_PHYSICAL_KEY(134) | SDL_KEY_KEYPAD_BIT,
 
--- a/src/events/SDL_keyboard.c	Thu Jan 03 06:07:30 2008 +0000
+++ b/src/events/SDL_keyboard.c	Tue Jan 08 00:10:46 2008 +0000
@@ -36,6 +36,48 @@
 static int SDL_current_keyboard;
 static SDL_Keyboard **SDL_keyboards;
 
+/* Taken from SDL_iconv() */
+static char *
+encodeUtf8(Uint32 ch, char *dst)
+{
+    Uint8 *p = (Uint8 *) dst;
+    if (ch <= 0x7F) {
+        *p = (Uint8) ch;
+        ++dst;
+    } else if (ch <= 0x7FF) {
+        p[0] = 0xC0 | (Uint8) ((ch >> 6) & 0x1F);
+        p[1] = 0x80 | (Uint8) (ch & 0x3F);
+        dst += 2;
+    } else if (ch <= 0xFFFF) {
+        p[0] = 0xE0 | (Uint8) ((ch >> 12) & 0x0F);
+        p[1] = 0x80 | (Uint8) ((ch >> 6) & 0x3F);
+        p[2] = 0x80 | (Uint8) (ch & 0x3F);
+        dst += 3;
+    } else if (ch <= 0x1FFFFF) {
+        p[0] = 0xF0 | (Uint8) ((ch >> 18) & 0x07);
+        p[1] = 0x80 | (Uint8) ((ch >> 12) & 0x3F);
+        p[2] = 0x80 | (Uint8) ((ch >> 6) & 0x3F);
+        p[3] = 0x80 | (Uint8) (ch & 0x3F);
+        dst += 4;
+    } else if (ch <= 0x3FFFFFF) {
+        p[0] = 0xF8 | (Uint8) ((ch >> 24) & 0x03);
+        p[1] = 0x80 | (Uint8) ((ch >> 18) & 0x3F);
+        p[2] = 0x80 | (Uint8) ((ch >> 12) & 0x3F);
+        p[3] = 0x80 | (Uint8) ((ch >> 6) & 0x3F);
+        p[4] = 0x80 | (Uint8) (ch & 0x3F);
+        dst += 5;
+    } else {
+        p[0] = 0xFC | (Uint8) ((ch >> 30) & 0x01);
+        p[1] = 0x80 | (Uint8) ((ch >> 24) & 0x3F);
+        p[2] = 0x80 | (Uint8) ((ch >> 18) & 0x3F);
+        p[3] = 0x80 | (Uint8) ((ch >> 12) & 0x3F);
+        p[4] = 0x80 | (Uint8) ((ch >> 6) & 0x3F);
+        p[5] = 0x80 | (Uint8) (ch & 0x3F);
+        dst += 6;
+    }
+    return dst;
+}
+
 /* Public functions */
 int
 SDL_KeyboardInit(void)
@@ -227,21 +269,14 @@
         /* SDLK_INDEX(layoutKey) is the unicode code point of the character generated by the key */
         static char buffer[9];  /* 6 (maximal UTF-8 char length) + 2 ([] for keypad) + 1 (null teminator) */
         char *bufferPtr = &buffer[1];
-        SDL_iconv_t cd;
-        size_t inbytesleft = 4, outbytesleft = 8;
         Uint32 codepoint = SDLK_INDEX(layoutKey);
-        const char *codepointPtr = (const char *) &codepoint;
 
         /* Unaccented letter keys on latin keyboards are normally labeled in upper case (and probably on others like Greek or Cyrillic too, so if you happen to know for sure, please adapt this). */
         if (codepoint >= 'a' && codepoint <= 'z') {
             codepoint -= 32;
         }
 
-        cd = SDL_iconv_open("UTF-8", "UCS-4");
-        if (cd == (SDL_iconv_t) (-1))
-            return "";
-        SDL_iconv(cd, &codepointPtr, &inbytesleft, &bufferPtr, &outbytesleft);
-        SDL_iconv_close(cd);
+        bufferPtr = encodeUtf8(codepoint, bufferPtr);
         *bufferPtr = '\0';
 
         if ((layoutKey & SDL_KEY_KEYPAD_BIT) != 0) {
--- a/src/joystick/darwin/SDL_sysjoystick.c	Thu Jan 03 06:07:30 2008 +0000
+++ b/src/joystick/darwin/SDL_sysjoystick.c	Tue Jan 08 00:10:46 2008 +0000
@@ -820,9 +820,9 @@
 
         range = (element->max - element->min + 1);
         value = HIDGetElementValue(device, element) - element->min;
-        if (range == 4)  /* 4 position hatswitch - scale up value */
+        if (range == 4)         /* 4 position hatswitch - scale up value */
             value *= 2;
-        else if (range != 8)     /* Neither a 4 nor 8 positions - fall back to default position (centered) */
+        else if (range != 8)    /* Neither a 4 nor 8 positions - fall back to default position (centered) */
             value = -1;
         switch (value) {
         case 0:
--- a/src/video/SDL_renderer_gl.c	Thu Jan 03 06:07:30 2008 +0000
+++ b/src/video/SDL_renderer_gl.c	Tue Jan 08 00:10:46 2008 +0000
@@ -295,12 +295,11 @@
         GL_DestroyRenderer(renderer);
         return NULL;
     }
-
 #ifdef __MACOSX__
     /* Enable multi-threaded rendering */
     /* Disabled until Ryan finishes his VBO/PBO code...
-    CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine);
-    */
+       CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine);
+     */
 #endif
 
     if (flags & SDL_RENDERER_PRESENTVSYNC) {
@@ -579,14 +578,15 @@
         renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
                                     GL_STORAGE_CACHED_APPLE);
     }
-    if (texture->access == SDL_TEXTUREACCESS_STREAMING && texture->format == SDL_PIXELFORMAT_ARGB8888 ) {
+    if (texture->access == SDL_TEXTUREACCESS_STREAMING
+        && texture->format == SDL_PIXELFORMAT_ARGB8888) {
         /*
-        if (renderdata->glTextureRangeAPPLE) {
-            renderdata->glTextureRangeAPPLE(data->type,
-                                            texture->h * data->pitch,
-                                            data->pixels);
-        }
-        */
+           if (renderdata->glTextureRangeAPPLE) {
+           renderdata->glTextureRangeAPPLE(data->type,
+           texture->h * data->pitch,
+           data->pixels);
+           }
+         */
         renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
         renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
                                  texture_h, 0, format, type, data->pixels);
--- a/src/video/x11/SDL_x11events.c	Thu Jan 03 06:07:30 2008 +0000
+++ b/src/video/x11/SDL_x11events.c	Tue Jan 08 00:10:46 2008 +0000
@@ -30,6 +30,28 @@
 #include "../../events/SDL_events_c.h"
 
 
+/* Check to see if this is a repeated key.
+   (idea shamelessly lifted from GII -- thanks guys! :)
+ */
+static int
+X11_KeyRepeat(Display * display, XEvent * event)
+{
+    XEvent peekevent;
+    int repeated;
+
+    repeated = 0;
+    if (XPending(display)) {
+        XPeekEvent(display, &peekevent);
+        if ((peekevent.type == KeyPress) &&
+            (peekevent.xkey.keycode == event->xkey.keycode) &&
+            ((peekevent.xkey.time - event->xkey.time) < 2)) {
+            repeated = 1;
+            XNextEvent(display, &peekevent);
+        }
+    }
+    return (repeated);
+}
+
 static void
 X11_DispatchEvent(_THIS)
 {
@@ -167,95 +189,44 @@
 
         /* Key press? */
     case KeyPress:{
-#if 0                           /* FIXME */
-            static SDL_keysym saved_keysym;
-            SDL_keysym keysym;
             KeyCode keycode = xevent.xkey.keycode;
 
 #ifdef DEBUG_XEVENTS
             printf("KeyPress (X11 keycode = 0x%X)\n", xevent.xkey.keycode);
 #endif
-            /* Get the translated SDL virtual keysym */
-            if (keycode) {
-                keysym.scancode = keycode;
-                keysym.sym = X11_TranslateKeycode(SDL_Display, keycode);
-                keysym.mod = KMOD_NONE;
-                keysym.unicode = 0;
-            } else {
-                keysym = saved_keysym;
-            }
-
-            /* If we're not doing translation, we're done! */
-            if (!SDL_TranslateUNICODE) {
-                posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym);
-                break;
-            }
-
-            if (XFilterEvent(&xevent, None)) {
-                if (xevent.xkey.keycode) {
-                    posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym);
-                } else {
-                    /* Save event to be associated with IM text
-                       In 1.3 we'll have a text event instead.. */
-                    saved_keysym = keysym;
+            if (!X11_KeyRepeat(videodata->display, &xevent)) {
+                SDLKey physicalKey = videodata->keyCodeToSDLKTable[keycode];
+                SDL_SendKeyboardKey(videodata->keyboard, SDL_PRESSED,
+                                    (Uint8) keycode, physicalKey);
+#if 1
+                if (physicalKey == SDLK_UNKNOWN) {
+                    fprintf(stderr,
+                            "The key you just pressed is not recognized by SDL. To help get this fixed, report this to the SDL mailing list <sdl@libsdl.org> or to Christian Walther <cwalther@gmx.ch>. X11 KeyCode is %d, X11 KeySym 0x%X.\n",
+                            (int) keycode,
+                            (unsigned int) XKeycodeToKeysym(videodata->
+                                                            display, keycode,
+                                                            0));
                 }
-                break;
+#endif
             }
-
-            /* Look up the translated value for the key event */
-#ifdef X_HAVE_UTF8_STRING
-            if (data->ic != NULL) {
-                static Status state;
-                /* A UTF-8 character can be at most 6 bytes */
-                char keybuf[6];
-                if (Xutf8LookupString(data->ic, &xevent.xkey,
-                                      keybuf, sizeof(keybuf), NULL, &state)) {
-                    keysym.unicode = Utf8ToUcs4((Uint8 *) keybuf);
-                }
-            } else
-#endif
-            {
-                static XComposeStatus state;
-                char keybuf[32];
-
-                if (XLookupString(&xevent.xkey,
-                                  keybuf, sizeof(keybuf), NULL, &state)) {
-                    /*
-                     * FIXME: XLookupString() may yield more than one
-                     * character, so we need a mechanism to allow for
-                     * this (perhaps null keypress events with a
-                     * unicode value)
-                     */
-                    keysym.unicode = (Uint8) keybuf[0];
-                }
-            }
-            posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym);
-#endif // 0
         }
         break;
 
         /* Key release? */
     case KeyRelease:{
-#if 0                           /* FIXME */
-            SDL_keysym keysym;
             KeyCode keycode = xevent.xkey.keycode;
 
 #ifdef DEBUG_XEVENTS
             printf("KeyRelease (X11 keycode = 0x%X)\n", xevent.xkey.keycode);
 #endif
             /* Check to see if this is a repeated key */
-            if (X11_KeyRepeat(SDL_Display, &xevent)) {
+            if (X11_KeyRepeat(videodata->display, &xevent)) {
                 break;
             }
 
-            /* Get the translated SDL virtual keysym */
-            keysym.scancode = keycode;
-            keysym.sym = X11_TranslateKeycode(SDL_Display, keycode);
-            keysym.mod = KMOD_NONE;
-            keysym.unicode = 0;
-
-            posted = SDL_PrivateKeyboard(SDL_RELEASED, &keysym);
-#endif // 0
+            SDL_SendKeyboardKey(videodata->keyboard, SDL_RELEASED,
+                                (Uint8) keycode,
+                                videodata->keyCodeToSDLKTable[keycode]);
         }
         break;
 
--- a/src/video/x11/SDL_x11keyboard.c	Thu Jan 03 06:07:30 2008 +0000
+++ b/src/video/x11/SDL_x11keyboard.c	Tue Jan 08 00:10:46 2008 +0000
@@ -25,14 +25,993 @@
 
 #include "../../events/SDL_keyboard_c.h"
 
-void
+#include <X11/keysym.h>
+
+#include "imKStoUCS.h"
+
+/* Used for two purposes: - by X11_GetLayoutKey(), with physical =
+    false, to convert a KeySym to the corresponding layout key code
+    (SDLK_ ones and some character ones - most character KeySyms are
+    handled by X11_KeySymToUcs4() after this function returns
+    SDLK_UNKNOWN for them).  - by X11_InitKeyboard(), with physical =
+    true, to build a makeshift translation table based on the KeySyms
+    when none of the predefined KeyCode- to-SDLKey tables fits. This
+    is *not* correct anywhere but on a US layout, since the
+    translation table deals with physical key codes, while the X11
+    KeySym corresponds to our concept of a layout key code, but it's
+    better than nothing.
+*/
+
+/* KeyCode-to-SDLKey translation tables for various X servers. Which one to use
+   is decided in X11_InitKeyboard().
+*/
+
+static SDLKey macKeyCodeToSDLK[];
+static SDLKey xorgLinuxKeyCodeToSDLK[];
+
+static SDLKey *keyCodeToSDLKeyTables[] = {
+    xorgLinuxKeyCodeToSDLK,
+    macKeyCodeToSDLK,
+    NULL
+};
+
+/* *INDENT-OFF* */
+
+/* These are just Mac virtual key codes + 8 (see SDL/src/video/cocoa/
+   SDL_cocoakeys.h for more info). Observed to work with Apple X11 on
+   Mac OS X 10.4. May also work on older Linux distributions on Mac
+   hardware.
+*/
+static SDLKey macKeyCodeToSDLK[] = {
+    /*   0 */   SDLK_UNKNOWN,
+    /*   1 */   SDLK_UNKNOWN,
+    /*   2 */   SDLK_UNKNOWN,
+    /*   3 */   SDLK_UNKNOWN,
+    /*   4 */   SDLK_UNKNOWN,
+    /*   5 */   SDLK_UNKNOWN,
+    /*   6 */   SDLK_UNKNOWN,
+    /*   7 */   SDLK_UNKNOWN,
+    /*   8 */   SDLK_A,
+    /*   9 */   SDLK_S,
+    /*  10 */   SDLK_D,
+    /*  11 */   SDLK_F,
+    /*  12 */   SDLK_H,
+    /*  13 */   SDLK_G,
+    /*  14 */   SDLK_Z,
+    /*  15 */   SDLK_X,
+    /*  16 */   SDLK_C,
+    /*  17 */   SDLK_V,
+    /*  18 */   SDLK_GRAVE,
+    /*  19 */   SDLK_B,
+    /*  20 */   SDLK_Q,
+    /*  21 */   SDLK_W,
+    /*  22 */   SDLK_E,
+    /*  23 */   SDLK_R,
+    /*  24 */   SDLK_Y,
+    /*  25 */   SDLK_T,
+    /*  26 */   SDLK_1,
+    /*  27 */   SDLK_2,
+    /*  28 */   SDLK_3,
+    /*  29 */   SDLK_4,
+    /*  30 */   SDLK_6,
+    /*  31 */   SDLK_5,
+    /*  32 */   SDLK_EQUALS,
+    /*  33 */   SDLK_9,
+    /*  34 */   SDLK_7,
+    /*  35 */   SDLK_HYPHENMINUS,
+    /*  36 */   SDLK_8,
+    /*  37 */   SDLK_0,
+    /*  38 */   SDLK_RIGHTBRACKET,
+    /*  39 */   SDLK_O,
+    /*  40 */   SDLK_U,
+    /*  41 */   SDLK_LEFTBRACKET,
+    /*  42 */   SDLK_I,
+    /*  43 */   SDLK_P,
+    /*  44 */   SDLK_RETURN,
+    /*  45 */   SDLK_L,
+    /*  46 */   SDLK_J,
+    /*  47 */   SDLK_APOSTROPHE,
+    /*  48 */   SDLK_K,
+    /*  49 */   SDLK_SEMICOLON,
+    /*  50 */   SDLK_BACKSLASH,
+    /*  51 */   SDLK_COMMA,
+    /*  52 */   SDLK_SLASH,
+    /*  53 */   SDLK_N,
+    /*  54 */   SDLK_M,
+    /*  55 */   SDLK_PERIOD,
+    /*  56 */   SDLK_TAB,
+    /*  57 */   SDLK_SPACE,
+    /*  58 */   SDLK_NONUSBACKSLASH,
+    /*  59 */   SDLK_BACKSPACE,
+    /*  60 */   SDLK_KP_ENTER,
+    /*  61 */   SDLK_ESCAPE,
+    /*  62 */   SDLK_RMETA,
+    /*  63 */   SDLK_LMETA,
+    /*  64 */   SDLK_LSHIFT,
+    /*  65 */   SDLK_CAPSLOCK,
+    /*  66 */   SDLK_LALT,
+    /*  67 */   SDLK_LCTRL,
+    /*  68 */   SDLK_RSHIFT,
+    /*  69 */   SDLK_RALT,
+    /*  70 */   SDLK_RCTRL,
+    /*  71 */   SDLK_NONE,
+    /*  72 */   SDLK_UNKNOWN,
+    /*  73 */   SDLK_KP_PERIOD,
+    /*  74 */   SDLK_UNKNOWN,
+    /*  75 */   SDLK_KP_MULTIPLY,
+    /*  76 */   SDLK_UNKNOWN,
+    /*  77 */   SDLK_KP_PLUS,
+    /*  78 */   SDLK_UNKNOWN,
+    /*  79 */   SDLK_KP_NUMLOCKCLEAR,
+    /*  80 */   SDLK_VOLUMEUP,
+    /*  81 */   SDLK_VOLUMEDOWN,
+    /*  82 */   SDLK_MUTE,
+    /*  83 */   SDLK_KP_DIVIDE,
+    /*  84 */   SDLK_KP_ENTER,
+    /*  85 */   SDLK_UNKNOWN,
+    /*  86 */   SDLK_KP_MINUS,
+    /*  87 */   SDLK_UNKNOWN,
+    /*  88 */   SDLK_UNKNOWN,
+    /*  89 */   SDLK_KP_EQUALS,
+    /*  90 */   SDLK_KP_0,
+    /*  91 */   SDLK_KP_1,
+    /*  92 */   SDLK_KP_2,
+    /*  93 */   SDLK_KP_3,
+    /*  94 */   SDLK_KP_4,
+    /*  95 */   SDLK_KP_5,
+    /*  96 */   SDLK_KP_6,
+    /*  97 */   SDLK_KP_7,
+    /*  98 */   SDLK_UNKNOWN,
+    /*  99 */   SDLK_KP_8,
+    /* 100 */   SDLK_KP_9,
+    /* 101 */   SDLK_INTERNATIONAL3,
+    /* 102 */   SDLK_INTERNATIONAL1,
+    /* 103 */   SDLK_KP_COMMA,
+    /* 104 */   SDLK_F5,
+    /* 105 */   SDLK_F6,
+    /* 106 */   SDLK_F7,
+    /* 107 */   SDLK_F3,
+    /* 108 */   SDLK_F8,
+    /* 109 */   SDLK_F9,
+    /* 110 */   SDLK_LANG2,
+    /* 111 */   SDLK_F11,
+    /* 112 */   SDLK_LANG1,
+    /* 113 */   SDLK_PRINTSCREEN,
+    /* 114 */   SDLK_F16,
+    /* 115 */   SDLK_SCROLLLOCK,
+    /* 116 */   SDLK_UNKNOWN,
+    /* 117 */   SDLK_F10,
+    /* 118 */   SDLK_APPLICATION,
+    /* 119 */   SDLK_F12,
+    /* 120 */   SDLK_UNKNOWN,
+    /* 121 */   SDLK_PAUSE,
+    /* 122 */   SDLK_INSERT,
+    /* 123 */   SDLK_HOME,
+    /* 124 */   SDLK_PAGEUP,
+    /* 125 */   SDLK_DELETE,
+    /* 126 */   SDLK_F4,
+    /* 127 */   SDLK_END,
+    /* 128 */   SDLK_F2,
+    /* 129 */   SDLK_PAGEDOWN,
+    /* 130 */   SDLK_F1,
+    /* 131 */   SDLK_LEFT,
+    /* 132 */   SDLK_RIGHT,
+    /* 133 */   SDLK_DOWN,
+    /* 134 */   SDLK_UP,
+    /* 135 */   SDLK_POWER,
+    /* 136 */   SDLK_UNKNOWN, /* codes higher than 135 shouldn't occur as Mac virtual keycodes only go to 127 */
+    /* 137 */   SDLK_UNKNOWN,
+    /* 138 */   SDLK_UNKNOWN,
+    /* 139 */   SDLK_UNKNOWN,
+    /* 140 */   SDLK_UNKNOWN,
+    /* 141 */   SDLK_UNKNOWN,
+    /* 142 */   SDLK_UNKNOWN,
+    /* 143 */   SDLK_UNKNOWN,
+    /* 144 */   SDLK_UNKNOWN,
+    /* 145 */   SDLK_UNKNOWN,
+    /* 146 */   SDLK_UNKNOWN,
+    /* 147 */   SDLK_UNKNOWN,
+    /* 148 */   SDLK_UNKNOWN,
+    /* 149 */   SDLK_UNKNOWN,
+    /* 150 */   SDLK_UNKNOWN,
+    /* 151 */   SDLK_UNKNOWN,
+    /* 152 */   SDLK_UNKNOWN,
+    /* 153 */   SDLK_UNKNOWN,
+    /* 154 */   SDLK_UNKNOWN,
+    /* 155 */   SDLK_UNKNOWN,
+    /* 156 */   SDLK_UNKNOWN,
+    /* 157 */   SDLK_UNKNOWN,
+    /* 158 */   SDLK_UNKNOWN,
+    /* 159 */   SDLK_UNKNOWN,
+    /* 160 */   SDLK_UNKNOWN,
+    /* 161 */   SDLK_UNKNOWN,
+    /* 162 */   SDLK_UNKNOWN,
+    /* 163 */   SDLK_UNKNOWN,
+    /* 164 */   SDLK_UNKNOWN,
+    /* 165 */   SDLK_UNKNOWN,
+    /* 166 */   SDLK_UNKNOWN,
+    /* 167 */   SDLK_UNKNOWN,
+    /* 168 */   SDLK_UNKNOWN,
+    /* 169 */   SDLK_UNKNOWN,
+    /* 170 */   SDLK_UNKNOWN,
+    /* 171 */   SDLK_UNKNOWN,
+    /* 172 */   SDLK_UNKNOWN,
+    /* 173 */   SDLK_UNKNOWN,
+    /* 174 */   SDLK_UNKNOWN,
+    /* 175 */   SDLK_UNKNOWN,
+    /* 176 */   SDLK_UNKNOWN,
+    /* 177 */   SDLK_UNKNOWN,
+    /* 178 */   SDLK_UNKNOWN,
+    /* 179 */   SDLK_UNKNOWN,
+    /* 180 */   SDLK_UNKNOWN,
+    /* 181 */   SDLK_UNKNOWN,
+    /* 182 */   SDLK_UNKNOWN,
+    /* 183 */   SDLK_UNKNOWN,
+    /* 184 */   SDLK_UNKNOWN,
+    /* 185 */   SDLK_UNKNOWN,
+    /* 186 */   SDLK_UNKNOWN,
+    /* 187 */   SDLK_UNKNOWN,
+    /* 188 */   SDLK_UNKNOWN,
+    /* 189 */   SDLK_UNKNOWN,
+    /* 190 */   SDLK_UNKNOWN,
+    /* 191 */   SDLK_UNKNOWN,
+    /* 192 */   SDLK_UNKNOWN,
+    /* 193 */   SDLK_UNKNOWN,
+    /* 194 */   SDLK_UNKNOWN,
+    /* 195 */   SDLK_UNKNOWN,
+    /* 196 */   SDLK_UNKNOWN,
+    /* 197 */   SDLK_UNKNOWN,
+    /* 198 */   SDLK_UNKNOWN,
+    /* 199 */   SDLK_UNKNOWN,
+    /* 200 */   SDLK_UNKNOWN,
+    /* 201 */   SDLK_UNKNOWN,
+    /* 202 */   SDLK_UNKNOWN,
+    /* 203 */   SDLK_UNKNOWN,
+    /* 204 */   SDLK_UNKNOWN,
+    /* 205 */   SDLK_UNKNOWN,
+    /* 206 */   SDLK_UNKNOWN,
+    /* 207 */   SDLK_UNKNOWN,
+    /* 208 */   SDLK_UNKNOWN,
+    /* 209 */   SDLK_UNKNOWN,
+    /* 210 */   SDLK_UNKNOWN,
+    /* 211 */   SDLK_UNKNOWN,
+    /* 212 */   SDLK_UNKNOWN,
+    /* 213 */   SDLK_UNKNOWN,
+    /* 214 */   SDLK_UNKNOWN,
+    /* 215 */   SDLK_UNKNOWN,
+    /* 216 */   SDLK_UNKNOWN,
+    /* 217 */   SDLK_UNKNOWN,
+    /* 218 */   SDLK_UNKNOWN,
+    /* 219 */   SDLK_UNKNOWN,
+    /* 220 */   SDLK_UNKNOWN,
+    /* 221 */   SDLK_UNKNOWN,
+    /* 222 */   SDLK_UNKNOWN,
+    /* 223 */   SDLK_UNKNOWN,
+    /* 224 */   SDLK_UNKNOWN,
+    /* 225 */   SDLK_UNKNOWN,
+    /* 226 */   SDLK_UNKNOWN,
+    /* 227 */   SDLK_UNKNOWN,
+    /* 228 */   SDLK_UNKNOWN,
+    /* 229 */   SDLK_UNKNOWN,
+    /* 230 */   SDLK_UNKNOWN,
+    /* 231 */   SDLK_UNKNOWN,
+    /* 232 */   SDLK_UNKNOWN,
+    /* 233 */   SDLK_UNKNOWN,
+    /* 234 */   SDLK_UNKNOWN,
+    /* 235 */   SDLK_UNKNOWN,
+    /* 236 */   SDLK_UNKNOWN,
+    /* 237 */   SDLK_UNKNOWN,
+    /* 238 */   SDLK_UNKNOWN,
+    /* 239 */   SDLK_UNKNOWN,
+    /* 240 */   SDLK_UNKNOWN,
+    /* 241 */   SDLK_UNKNOWN,
+    /* 242 */   SDLK_UNKNOWN,
+    /* 243 */   SDLK_UNKNOWN,
+    /* 244 */   SDLK_UNKNOWN,
+    /* 245 */   SDLK_UNKNOWN,
+    /* 246 */   SDLK_UNKNOWN,
+    /* 247 */   SDLK_UNKNOWN,
+    /* 248 */   SDLK_UNKNOWN,
+    /* 249 */   SDLK_UNKNOWN,
+    /* 250 */   SDLK_UNKNOWN,
+    /* 251 */   SDLK_UNKNOWN,
+    /* 252 */   SDLK_UNKNOWN,
+    /* 253 */   SDLK_UNKNOWN,
+    /* 254 */   SDLK_UNKNOWN,
+    /* 255 */   SDLK_UNKNOWN
+};
+
+/* Found mostly by experimentation with X.org on Linux (Fedora Core 4 and
+   Ubuntu Dapper) on PC and PPC Mac hardware, some parts (especially about
+   the "multimedia"/"internet" keys) from various sources on the web.
+*/
+static SDLKey xorgLinuxKeyCodeToSDLK[] = {
+    /*   0 */   SDLK_UNKNOWN,
+    /*   1 */   SDLK_UNKNOWN,
+    /*   2 */   SDLK_UNKNOWN,
+    /*   3 */   SDLK_UNKNOWN,
+    /*   4 */   SDLK_UNKNOWN,
+    /*   5 */   SDLK_UNKNOWN,
+    /*   6 */   SDLK_UNKNOWN,
+    /*   7 */   SDLK_UNKNOWN,
+    /*   8 */   SDLK_UNKNOWN,
+    /*   9 */   SDLK_ESCAPE,
+    /*  10 */   SDLK_1,
+    /*  11 */   SDLK_2,
+    /*  12 */   SDLK_3,
+    /*  13 */   SDLK_4,
+    /*  14 */   SDLK_5,
+    /*  15 */   SDLK_6,
+    /*  16 */   SDLK_7,
+    /*  17 */   SDLK_8,
+    /*  18 */   SDLK_9,
+    /*  19 */   SDLK_0,
+    /*  20 */   SDLK_HYPHENMINUS,
+    /*  21 */   SDLK_EQUALS,
+    /*  22 */   SDLK_BACKSPACE,
+    /*  23 */   SDLK_TAB,
+    /*  24 */   SDLK_Q,
+    /*  25 */   SDLK_W,
+    /*  26 */   SDLK_E,
+    /*  27 */   SDLK_R,
+    /*  28 */   SDLK_T,
+    /*  29 */   SDLK_Y,
+    /*  30 */   SDLK_U,
+    /*  31 */   SDLK_I,
+    /*  32 */   SDLK_O,
+    /*  33 */   SDLK_P,
+    /*  34 */   SDLK_LEFTBRACKET,
+    /*  35 */   SDLK_RIGHTBRACKET,
+    /*  36 */   SDLK_RETURN,
+    /*  37 */   SDLK_LCTRL,
+    /*  38 */   SDLK_A,
+    /*  39 */   SDLK_S,
+    /*  40 */   SDLK_D,
+    /*  41 */   SDLK_F,
+    /*  42 */   SDLK_G,
+    /*  43 */   SDLK_H,
+    /*  44 */   SDLK_J,
+    /*  45 */   SDLK_K,
+    /*  46 */   SDLK_L,
+    /*  47 */   SDLK_SEMICOLON,
+    /*  48 */   SDLK_APOSTROPHE,
+    /*  49 */   SDLK_GRAVE,
+    /*  50 */   SDLK_LSHIFT,
+    /*  51 */   SDLK_BACKSLASH,
+    /*  52 */   SDLK_Z,
+    /*  53 */   SDLK_X,
+    /*  54 */   SDLK_C,
+    /*  55 */   SDLK_V,
+    /*  56 */   SDLK_B,
+    /*  57 */   SDLK_N,
+    /*  58 */   SDLK_M,
+    /*  59 */   SDLK_COMMA,
+    /*  60 */   SDLK_PERIOD,
+    /*  61 */   SDLK_SLASH,
+    /*  62 */   SDLK_RSHIFT,
+    /*  63 */   SDLK_KP_MULTIPLY,
+    /*  64 */   SDLK_LALT,
+    /*  65 */   SDLK_SPACE,
+    /*  66 */   SDLK_CAPSLOCK,
+    /*  67 */   SDLK_F1,
+    /*  68 */   SDLK_F2,
+    /*  69 */   SDLK_F3,
+    /*  70 */   SDLK_F4,
+    /*  71 */   SDLK_F5,
+    /*  72 */   SDLK_F6,
+    /*  73 */   SDLK_F7,
+    /*  74 */   SDLK_F8,
+    /*  75 */   SDLK_F9,
+    /*  76 */   SDLK_F10,
+    /*  77 */   SDLK_KP_NUMLOCKCLEAR,
+    /*  78 */   SDLK_SCROLLLOCK,
+    /*  79 */   SDLK_KP_7,
+    /*  80 */   SDLK_KP_8,
+    /*  81 */   SDLK_KP_9,
+    /*  82 */   SDLK_KP_MINUS,
+    /*  83 */   SDLK_KP_4,
+    /*  84 */   SDLK_KP_5,
+    /*  85 */   SDLK_KP_6,
+    /*  86 */   SDLK_KP_PLUS,
+    /*  87 */   SDLK_KP_1,
+    /*  88 */   SDLK_KP_2,
+    /*  89 */   SDLK_KP_3,
+    /*  90 */   SDLK_KP_0,
+    /*  91 */   SDLK_KP_PERIOD,
+    /*  92 */   SDLK_SYSREQ,
+    /*  93 */   SDLK_MODE, /* is translated to XK_Mode_switch by my X server, but I have no keyboard that generates this code, so I'm not sure if this is correct */
+    /*  94 */   SDLK_NONUSBACKSLASH,
+    /*  95 */   SDLK_F11,
+    /*  96 */   SDLK_F12,
+    /*  97 */   SDLK_HOME,
+    /*  98 */   SDLK_UP,
+    /*  99 */   SDLK_PAGEUP,
+    /* 100 */   SDLK_LEFT,
+    /* 101 */   SDLK_BRIGHTNESSDOWN, /* on PowerBook G4 */
+    /* 102 */   SDLK_RIGHT,
+    /* 103 */   SDLK_END,
+    /* 104 */   SDLK_DOWN,
+    /* 105 */   SDLK_PAGEDOWN,
+    /* 106 */   SDLK_INSERT,
+    /* 107 */   SDLK_DELETE,
+    /* 108 */   SDLK_KP_ENTER,
+    /* 109 */   SDLK_RCTRL,
+    /* 110 */   SDLK_PAUSE,
+    /* 111 */   SDLK_PRINTSCREEN,
+    /* 112 */   SDLK_KP_DIVIDE,
+    /* 113 */   SDLK_RALT,
+    /* 114 */   SDLK_UNKNOWN,
+    /* 115 */   SDLK_LMETA,
+    /* 116 */   SDLK_RMETA,
+    /* 117 */   SDLK_APPLICATION,
+    /* 118 */   SDLK_F13,
+    /* 119 */   SDLK_F14,
+    /* 120 */   SDLK_F15,
+    /* 121 */   SDLK_F16,
+    /* 122 */   SDLK_F17,
+    /* 123 */   SDLK_UNKNOWN,
+    /* 124 */   SDLK_UNKNOWN, /* is translated to XK_ISO_Level3_Shift by my X server, but I have no keyboard that generates this code, so I don't know what the correct SDLK_* for it is */
+    /* 125 */   SDLK_UNKNOWN,
+    /* 126 */   SDLK_KP_EQUALS,
+    /* 127 */   SDLK_UNKNOWN,
+    /* 128 */   SDLK_UNKNOWN,
+    /* 129 */   SDLK_UNKNOWN,
+    /* 130 */   SDLK_UNKNOWN,
+    /* 131 */   SDLK_UNKNOWN,
+    /* 132 */   SDLK_UNKNOWN,
+    /* 133 */   SDLK_INTERNATIONAL3, /* Yen */
+    /* 134 */   SDLK_UNKNOWN,
+    /* 135 */   SDLK_AGAIN,
+    /* 136 */   SDLK_UNDO,
+    /* 137 */   SDLK_UNKNOWN,
+    /* 138 */   SDLK_UNKNOWN,
+    /* 139 */   SDLK_UNKNOWN,
+    /* 140 */   SDLK_UNKNOWN,
+    /* 141 */   SDLK_UNKNOWN,
+    /* 142 */   SDLK_UNKNOWN,
+    /* 143 */   SDLK_UNKNOWN,
+    /* 144 */   SDLK_AUDIOPREV,
+    /* 145 */   SDLK_UNKNOWN,
+    /* 146 */   SDLK_UNKNOWN,
+    /* 147 */   SDLK_UNKNOWN,
+    /* 148 */   SDLK_UNKNOWN,
+    /* 149 */   SDLK_UNKNOWN,
+    /* 150 */   SDLK_UNKNOWN,
+    /* 151 */   SDLK_UNKNOWN,
+    /* 152 */   SDLK_UNKNOWN,
+    /* 153 */   SDLK_AUDIONEXT,
+    /* 154 */   SDLK_UNKNOWN,
+    /* 155 */   SDLK_UNKNOWN,
+    /* 156 */   SDLK_UNKNOWN,
+    /* 157 */   SDLK_KP_EQUALS, /* on PowerBook G4 */
+    /* 158 */   SDLK_UNKNOWN,
+    /* 159 */   SDLK_UNKNOWN,
+    /* 160 */   SDLK_MUTE,
+    /* 161 */   SDLK_CALC,
+    /* 162 */   SDLK_AUDIOPLAY,
+    /* 163 */   SDLK_UNKNOWN,
+    /* 164 */   SDLK_AUDIOSTOP,
+    /* 165 */   SDLK_UNKNOWN,
+    /* 166 */   SDLK_UNKNOWN,
+    /* 167 */   SDLK_UNKNOWN,
+    /* 168 */   SDLK_UNKNOWN,
+    /* 169 */   SDLK_UNKNOWN,
+    /* 170 */   SDLK_UNKNOWN,
+    /* 171 */   SDLK_UNKNOWN,
+    /* 172 */   SDLK_UNKNOWN,
+    /* 173 */   SDLK_UNKNOWN,
+    /* 174 */   SDLK_VOLUMEDOWN,
+    /* 175 */   SDLK_UNKNOWN,
+    /* 176 */   SDLK_VOLUMEUP,
+    /* 177 */   SDLK_UNKNOWN,
+    /* 178 */   SDLK_WWW,
+    /* 179 */   SDLK_UNKNOWN,
+    /* 180 */   SDLK_UNKNOWN,
+    /* 181 */   SDLK_UNKNOWN,
+    /* 182 */   SDLK_UNKNOWN,
+    /* 183 */   SDLK_UNKNOWN,
+    /* 184 */   SDLK_UNKNOWN,
+    /* 185 */   SDLK_UNKNOWN,
+    /* 186 */   SDLK_UNKNOWN,
+    /* 187 */   SDLK_HELP,
+    /* 188 */   SDLK_UNKNOWN,
+    /* 189 */   SDLK_UNKNOWN,
+    /* 190 */   SDLK_UNKNOWN,
+    /* 191 */   SDLK_UNKNOWN,
+    /* 192 */   SDLK_UNKNOWN,
+    /* 193 */   SDLK_UNKNOWN,
+    /* 194 */   SDLK_UNKNOWN,
+    /* 195 */   SDLK_UNKNOWN,
+    /* 196 */   SDLK_UNKNOWN,
+    /* 197 */   SDLK_UNKNOWN,
+    /* 198 */   SDLK_UNKNOWN,
+    /* 199 */   SDLK_UNKNOWN,
+    /* 200 */   SDLK_UNKNOWN,
+    /* 201 */   SDLK_UNKNOWN,
+    /* 202 */   SDLK_UNKNOWN,
+    /* 203 */   SDLK_UNKNOWN,
+    /* 204 */   SDLK_EJECT, /* on PowerBook G4 */
+    /* 205 */   SDLK_UNKNOWN,
+    /* 206 */   SDLK_UNKNOWN,
+    /* 207 */   SDLK_UNKNOWN,
+    /* 208 */   SDLK_UNKNOWN,
+    /* 209 */   SDLK_UNKNOWN,
+    /* 210 */   SDLK_UNKNOWN,
+    /* 211 */   SDLK_UNKNOWN,
+    /* 212 */   SDLK_BRIGHTNESSUP, /* on PowerBook G4 */
+    /* 213 */   SDLK_UNKNOWN,
+    /* 214 */   SDLK_DISPLAYSWITCH, /* on PowerBook G4 */
+    /* 215 */   SDLK_KBDILLUMTOGGLE,
+    /* 216 */   SDLK_KBDILLUMDOWN,
+    /* 217 */   SDLK_KBDILLUMUP,
+    /* 218 */   SDLK_UNKNOWN,
+    /* 219 */   SDLK_UNKNOWN,
+    /* 220 */   SDLK_UNKNOWN,
+    /* 221 */   SDLK_UNKNOWN,
+    /* 222 */   SDLK_POWER,
+    /* 223 */   SDLK_SLEEP,
+    /* 224 */   SDLK_UNKNOWN,
+    /* 225 */   SDLK_UNKNOWN,
+    /* 226 */   SDLK_UNKNOWN,
+    /* 227 */   SDLK_UNKNOWN,
+    /* 228 */   SDLK_UNKNOWN,
+    /* 229 */   SDLK_SEARCH,
+    /* 230 */   SDLK_BOOKMARKS,
+    /* 231 */   SDLK_BROWSERRELOAD,
+    /* 232 */   SDLK_BROWSERSTOP,
+    /* 233 */   SDLK_BROWSERFORWARD,
+    /* 234 */   SDLK_BROWSERBACK,
+    /* 235 */   SDLK_COMPUTER,
+    /* 236 */   SDLK_EMAIL,
+    /* 237 */   SDLK_MEDIA,
+    /* 238 */   SDLK_UNKNOWN,
+    /* 239 */   SDLK_UNKNOWN,
+    /* 240 */   SDLK_UNKNOWN,
+    /* 241 */   SDLK_UNKNOWN,
+    /* 242 */   SDLK_UNKNOWN,
+    /* 243 */   SDLK_UNKNOWN,
+    /* 244 */   SDLK_UNKNOWN,
+    /* 245 */   SDLK_UNKNOWN,
+    /* 246 */   SDLK_UNKNOWN,
+    /* 247 */   SDLK_UNKNOWN,
+    /* 248 */   SDLK_UNKNOWN,
+    /* 249 */   SDLK_UNKNOWN,
+    /* 250 */   SDLK_UNKNOWN,
+    /* 251 */   SDLK_UNKNOWN,
+    /* 252 */   SDLK_UNKNOWN,
+    /* 253 */   SDLK_UNKNOWN,
+    /* 254 */   SDLK_UNKNOWN,
+    /* 255 */   SDLK_UNKNOWN
+};
+
+/* *INDENT-ON* */
+
+/*---------------------------------------------------------------------------*/
+
+/* Used by X11_KeySymToSDLKey(). This is a hybrid of a KeySym-to-layout-key
+    mapping (needed in X11_GetLayoutKey()) and a fallback KeySym-to-physical-key
+    mapping under the assumption of a US keyboard layout (needed in
+    X11_InitKeyboard()). If for a given KeySym...
+    - the layout and physical codes are the same (must be an SDLK_ constant):
+      there is one entry,
+    - the layout and physical codes differ: there are two entries, with the
+      layout one first,
+    - there is only a physical code in the table (must be an SDLK_ constant):
+      it's marked by X11_KEY_PHYSICAL_ONLY_BIT (this is the case when the layout
+      key code is handled by KeySymToUcs4()),
+    - there is only a layout code in the table (can't be an SDLK_ constant):
+      recognizable by the absence of SDL_KEY_CAN_BE_PHYSICAL_BIT.
+    This list is sorted by KeySym to allow a binary search.
+*/
+#define X11_KEY_PHYSICAL_ONLY_BIT SDL_KEY_LAYOUT_SPECIAL_BIT
+/* SDL_KEY_LAYOUT_SPECIAL_BIT cannot possibly occur in an SDLK_ constant, so we may repurpose that bit for our own use. */
+static struct
+{
+    KeySym sym;
+    SDLKey key;
+} keySymToSDLKey[] = {
+    /* 0x00xx */
+    {
+    XK_space, SDLK_SPACE}, {
+    XK_apostrophe, SDLK_APOSTROPHE | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_comma, SDLK_COMMA | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_minus, SDLK_HYPHENMINUS | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_period, SDLK_PERIOD | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_slash, SDLK_SLASH | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_0, SDLK_0 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_1, SDLK_1 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_2, SDLK_2 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_3, SDLK_3 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_4, SDLK_4 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_5, SDLK_5 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_6, SDLK_6 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_7, SDLK_7 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_8, SDLK_8 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_9, SDLK_9 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_semicolon, SDLK_SEMICOLON | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_less, SDLK_NONUSBACKSLASH | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_equal, SDLK_EQUALS | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_bracketleft, SDLK_LEFTBRACKET | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_backslash, SDLK_BACKSLASH | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_bracketright, SDLK_RIGHTBRACKET | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_grave, SDLK_GRAVE | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_a, SDLK_A | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_b, SDLK_B | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_c, SDLK_C | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_d, SDLK_D | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_e, SDLK_E | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_f, SDLK_F | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_g, SDLK_G | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_h, SDLK_H | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_i, SDLK_I | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_j, SDLK_J | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_k, SDLK_K | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_l, SDLK_L | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_m, SDLK_M | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_n, SDLK_N | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_o, SDLK_O | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_p, SDLK_P | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_q, SDLK_Q | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_r, SDLK_R | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_s, SDLK_S | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_t, SDLK_T | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_u, SDLK_U | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_v, SDLK_V | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_w, SDLK_W | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_x, SDLK_X | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_y, SDLK_Y | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_z, SDLK_Z | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_section, SDLK_NONUSBACKSLASH | X11_KEY_PHYSICAL_ONLY_BIT},
+        /* 0xFExx */
+    {
+    XK_ISO_Level3_Shift, SDLK_RALT}, {
+    XK_dead_grave, '`'}, {
+    XK_dead_acute, 0xB4}, {
+    XK_dead_circumflex, '^'}, {
+    XK_dead_tilde, '~'}, {
+    XK_dead_macron, 0xAF}, {
+    XK_dead_breve, 0x2D8}, {
+    XK_dead_abovedot, 0x2D9}, {
+    XK_dead_diaeresis, 0xA8}, {
+    XK_dead_abovering, 0x2DA}, {
+    XK_dead_doubleacute, 0x2DD}, {
+    XK_dead_caron, 0x2C7}, {
+    XK_dead_cedilla, 0xB8}, {
+    XK_dead_ogonek, 0x2DB}, {
+    XK_dead_iota, 0x3B9}, {
+    XK_dead_voiced_sound, 0x309B}, {
+    XK_dead_semivoiced_sound, 0x309C}, {
+    XK_dead_belowdot, 0xB7},    /* that's actually MIDDLE DOT, but I haven't found a non-combining DOT BELOW */
+        /* XK_dead_hook, XK_dead_horn: I haven't found non-combining HOOK and HORN characters */
+        /* 0xFFxx */
+    {
+    XK_BackSpace, SDLK_BACKSPACE}, {
+    XK_Tab, SDLK_TAB}, {
+    XK_Return, SDLK_RETURN}, {
+    XK_Pause, SDLK_PAUSE}, {
+    XK_Scroll_Lock, SDLK_SCROLLLOCK}, {
+    XK_Escape, SDLK_ESCAPE}, {
+    XK_Home, SDLK_HOME}, {
+    XK_Left, SDLK_LEFT}, {
+    XK_Up, SDLK_UP}, {
+    XK_Right, SDLK_RIGHT}, {
+    XK_Down, SDLK_DOWN}, {
+    XK_Page_Up, SDLK_PAGEUP}, {
+    XK_Page_Down, SDLK_PAGEDOWN}, {
+    XK_End, SDLK_END}, {
+    XK_Print, SDLK_PRINTSCREEN}, {
+    XK_Insert, SDLK_INSERT}, {
+    XK_Menu, SDLK_APPLICATION}, {
+    XK_Break, SDLK_PAUSE}, {
+    XK_Mode_switch, SDLK_MODE}, {
+    XK_Num_Lock, SDLK_KP_NUMLOCKCLEAR}, {
+    XK_KP_Enter, SDLK_KP_ENTER}, {
+    XK_KP_Home, SDLK_KP_7 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_KP_Left, SDLK_KP_4 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_KP_Up, SDLK_KP_8 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_KP_Right, SDLK_KP_6 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_KP_Down, SDLK_KP_2 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_KP_Page_Up, SDLK_KP_9 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_KP_Page_Down, SDLK_KP_3 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_KP_End, SDLK_KP_1 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_KP_Begin, SDLK_KP_5 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_KP_Insert, SDLK_KP_0 | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_KP_Delete, SDLK_KP_PERIOD | X11_KEY_PHYSICAL_ONLY_BIT}, {
+    XK_KP_Multiply, '*'}, {
+    XK_KP_Multiply, SDLK_KP_MULTIPLY}, {
+    XK_KP_Add, '+'}, {
+    XK_KP_Add, SDLK_KP_PLUS}, {
+    XK_KP_Separator, '.'}, {
+    XK_KP_Separator, SDLK_KP_PERIOD}, {
+    XK_KP_Subtract, '-'}, {
+    XK_KP_Subtract, SDLK_KP_MINUS}, {
+    XK_KP_Decimal, '.'}, {
+    XK_KP_Decimal, SDLK_KP_PERIOD}, {
+    XK_KP_Divide, '/'}, {
+    XK_KP_Divide, SDLK_KP_DIVIDE}, {
+    XK_KP_0, '0'}, {
+    XK_KP_0, SDLK_KP_0}, {
+    XK_KP_1, '1'}, {
+    XK_KP_1, SDLK_KP_1}, {
+    XK_KP_2, '2'}, {
+    XK_KP_2, SDLK_KP_2}, {
+    XK_KP_3, '3'}, {
+    XK_KP_3, SDLK_KP_3}, {
+    XK_KP_4, '4'}, {
+    XK_KP_4, SDLK_KP_4}, {
+    XK_KP_5, '5'}, {
+    XK_KP_5, SDLK_KP_5}, {
+    XK_KP_6, '6'}, {
+    XK_KP_6, SDLK_KP_6}, {
+    XK_KP_7, '7'}, {
+    XK_KP_7, SDLK_KP_7}, {
+    XK_KP_8, '8'}, {
+    XK_KP_8, SDLK_KP_8}, {
+    XK_KP_9, '9'}, {
+    XK_KP_9, SDLK_KP_9}, {
+    XK_KP_Equal, '='}, {
+    XK_KP_Equal, SDLK_KP_EQUALS}, {
+    XK_F1, SDLK_F1}, {
+    XK_F2, SDLK_F2}, {
+    XK_F3, SDLK_F3}, {
+    XK_F4, SDLK_F4}, {
+    XK_F5, SDLK_F5}, {
+    XK_F6, SDLK_F6}, {
+    XK_F7, SDLK_F7}, {
+    XK_F8, SDLK_F8}, {
+    XK_F9, SDLK_F9}, {
+    XK_F10, SDLK_F10}, {
+    XK_F11, SDLK_F11}, {
+    XK_F12, SDLK_F12}, {
+    XK_F13, SDLK_F13}, {
+    XK_F14, SDLK_F14}, {
+    XK_F15, SDLK_F15}, {
+    XK_F16, SDLK_F16}, {
+    XK_F17, SDLK_F17}, {
+    XK_F18, SDLK_F18}, {
+    XK_F19, SDLK_F19}, {
+    XK_F20, SDLK_F20}, {
+    XK_F21, SDLK_F21}, {
+    XK_F22, SDLK_F22}, {
+    XK_F23, SDLK_F23}, {
+    XK_F24, SDLK_F24}, {
+    XK_Shift_L, SDLK_LSHIFT}, {
+    XK_Shift_R, SDLK_RSHIFT}, {
+    XK_Control_L, SDLK_LCTRL}, {
+    XK_Control_R, SDLK_RCTRL}, {
+    XK_Caps_Lock, SDLK_CAPSLOCK}, {
+    XK_Shift_Lock, SDLK_CAPSLOCK}, {
+    XK_Meta_L, SDLK_LMETA}, {
+    XK_Meta_R, SDLK_RMETA}, {
+    XK_Alt_L, SDLK_LALT}, {
+    XK_Alt_R, SDLK_RALT}, {
+    XK_Super_L, SDLK_LMETA}, {
+    XK_Super_R, SDLK_RMETA}, {
+    XK_Hyper_L, SDLK_LMETA}, {
+    XK_Hyper_R, SDLK_RMETA}, {
+    XK_Delete, SDLK_DELETE}, {
+    0x1000003, SDLK_KP_ENTER}   /* keyboard enter on Mac OS X */
+};
+
+static SDLKey
+X11_KeySymToSDLKey(KeySym sym, SDL_bool physical)
+{
+    /* Do a binary search for sym in the keySymToSDLKey table */
+    SDLKey key = SDLK_UNKNOWN;
+    int start = -1;
+    int end = SDL_arraysize(keySymToSDLKey);
+    int i;
+    /* Invariant: keySymToSDLKey[start].sym < sym <
+       keySymToSDLKey[end].sym (imagine ...[-1] = -inf and
+       ...[arraysize] = inf, these entries needn't be there in reality
+       because they're never accessed) */
+    while (end > start + 1) {
+        i = (start + end) / 2;
+        if (keySymToSDLKey[i].sym == sym) {
+            /* found an entry, if it's the second of two back up to the first */
+            if (keySymToSDLKey[i - 1].sym == sym)
+                i--;
+            /* if there are two, the physical one is the second */
+            if (physical && keySymToSDLKey[i + 1].sym == sym)
+                i++;
+            key = keySymToSDLKey[i].key;
+            break;
+        } else if (keySymToSDLKey[i].sym < sym)
+            start = i;
+        else
+            end = i;
+    }
+
+    /* if we're being asked for a layout key code, but the table only
+       has a physical one, or vice versa, return SDLK_UNKNOWN) */
+
+    if (!physical && ((key & X11_KEY_PHYSICAL_ONLY_BIT) != 0)
+        || physical && ((key & SDL_KEY_CAN_BE_PHYSICAL_BIT) == 0))
+        key = SDLK_UNKNOWN;
+    key &= ~X11_KEY_PHYSICAL_ONLY_BIT;
+    return key;
+}
+
+int
 X11_InitKeyboard(_THIS)
 {
     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
     SDL_Keyboard keyboard;
+    SDLKey **table;
+    SDLKey *foundTable;
+    int i;
+    int code;
+    SDLKey sdlkey;
+
+    /* A random collection of KeySym/SDLKey pairs that should be valid
+       in any keyboard layout (if this isn't the case on yours,
+       please adjust). Using XKeysymToKeycode on these KeySyms
+       creates a "fingerprint" of the X server's key-to-KeyCode
+       mapping which is then matched against all our predefined
+       KeyCodeToSDLK tables to find the right one (if any).
+     */
+    struct
+    {
+        KeySym sym;
+        SDLKey key;
+    } fingerprint[] = {
+        {
+        XK_Tab, SDLK_TAB}, {
+        XK_Return, SDLK_RETURN}, {
+        XK_Escape, SDLK_ESCAPE}, {
+        XK_space, SDLK_SPACE}
+    };
 
     SDL_zero(keyboard);
     data->keyboard = SDL_AddKeyboard(&keyboard, -1);
+
+    /* Determine which X11 KeyCode to SDL physical key code table to use */
+
+    foundTable = NULL;
+    table = keyCodeToSDLKeyTables;
+    while ((NULL == foundTable) && (NULL != (*table))) {
+        foundTable = *table;
+        for (i = 0; i < SDL_arraysize(fingerprint); i++) {
+            code = XKeysymToKeycode(data->display, fingerprint[i].sym);
+            if ((code != 0) && ((*table)[code] != fingerprint[i].key)) {
+                foundTable = NULL;
+                break;
+            }
+        }
+        table++;
+    }
+
+    if (NULL != foundTable) {
+        /* Found a suitable one among the predefined tables */
+        data->keyCodeToSDLKTable = foundTable;
+    } else {
+        /* No suitable table found - build a makeshift table based on
+           the KeySyms, assuming a US keyboard layout */
+
+#if 1
+        fprintf(stderr,
+                "The key codes of your X server are unknown to SDL. Keys may not be recognized properly. To help get this fixed, report this to the SDL mailing list <sdl@libsdl.org> or to Christian Walther <cwalther@gmx.ch>.\n");
+#endif
+        data->keyCodeToSDLKTable =
+            SDL_malloc(SDL_arraysize(xorgLinuxKeyCodeToSDLK));
+        if (data->keyCodeToSDLKTable == NULL) {
+            SDL_OutOfMemory();
+            return -1;
+        }
+        for (code = SDL_arraysize(xorgLinuxKeyCodeToSDLK); code >= 0; code--) {
+            data->keyCodeToSDLKTable[code] =
+                X11_KeySymToSDLKey(XKeycodeToKeysym(data->display, code, 0),
+                                   SDL_TRUE);
+        }
+    }
+
+    /* Set some non-default key names */
+
+    for (code = 0; code < SDL_arraysize(xorgLinuxKeyCodeToSDLK); code++) {
+        sdlkey = data->keyCodeToSDLKTable[code];
+        switch (sdlkey) {
+            /* The SDLK_*META keys are used as XK_Meta_* by some X
+               servers, as XK_Super_* by others */
+        case SDLK_LMETA:
+        case SDLK_RMETA:
+            switch (XKeycodeToKeysym(data->display, code, 0)) {
+                /* nothing to do for XK_Meta_* because that's already the default name */
+            case XK_Super_L:
+                SDL_SetKeyName(sdlkey, "left super");
+                break;
+            case XK_Super_R:
+                SDL_SetKeyName(sdlkey, "right super");
+                break;
+            }
+            break;
+        }
+    }
+    SDL_SetKeyName(SDLK_APPLICATION, "menu");
+
+    return 0;
+}
+
+SDLKey
+X11_GetLayoutKey(_THIS, SDLKey physicalKey)
+{
+    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
+    int code = 0;
+    KeySym sym;
+    SDLKey layoutKey;
+
+    switch (physicalKey) {
+    case SDLK_UNKNOWN:
+        return physicalKey;
+
+        /* Shortcut handling of keypad numbers because on my PC their
+           primary KeySyms are not the numbers that I want but
+           XK_KP_Home, XK_KP_Up etc. The downside is that this gets us
+           latin numerals even on e.g. an Arabic layout. */
+    case SDLK_KP_1:
+        return '1';
+    case SDLK_KP_2:
+        return '2';
+    case SDLK_KP_3:
+        return '3';
+    case SDLK_KP_4:
+        return '4';
+    case SDLK_KP_5:
+        return '5';
+    case SDLK_KP_6:
+        return '6';
+    case SDLK_KP_7:
+        return '7';
+    case SDLK_KP_8:
+        return '8';
+    case SDLK_KP_9:
+        return '9';
+    case SDLK_KP_0:
+        return '0';
+    case SDLK_KP_PERIOD:
+        return '.';
+    default:
+        break;                  /* just to avoid a compiler warning */
+    }
+
+    /* Look up physicalKey to get an X11 KeyCode - linear search isn't
+       terribly efficient, this might have to be optimized. */
+    while ((code < SDL_arraysize(xorgLinuxKeyCodeToSDLK) && physicalKey)
+           != data->keyCodeToSDLKTable[code]) {
+        code++;
+    }
+
+    if (code == SDL_arraysize(xorgLinuxKeyCodeToSDLK)) {
+        return physicalKey;
+    }
+    /* Get the corresponding KeySym - this is where the keyboard
+       layout comes into play */
+    sym = XKeycodeToKeysym(data->display, code, 0);
+
+    /* Try our own KeySym-to-layout-key-code table: it handles all
+       keys whose layout code is an SDLK_ one, including a few where
+       X11_KeySymToUcs4() would yield a character, but not a suitable
+       one as a key name (e.g. space), and some that are character
+       keys for our purposes, but aren't handled by X11_KeySymToUcs4()
+       (dead keys, keypad operations). */
+
+    layoutKey = X11_KeySymToSDLKey(sym, SDL_FALSE);
+
+    /* If it wasn't handled by X11_KeySymToSDLKey(), it's most
+       probably a plain character KeySym that X11_KeySymToUcs4()
+       (ripped from X.org) knows. */
+
+    if (layoutKey == SDLK_UNKNOWN) {
+        unsigned int ucs = X11_KeySymToUcs4(sym);
+        if (ucs != 0)
+            layoutKey = (SDLKey) ucs;
+    }
+
+    /* Still no success? Give up. */
+    if (layoutKey == SDLK_UNKNOWN) {
+        return physicalKey;
+    }
+
+    return layoutKey;
 }
 
 void
@@ -40,6 +1019,18 @@
 {
     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
 
+    if (data->keyCodeToSDLKTable != NULL) {
+        /* If it's not one of the predefined tables, it was malloced
+           and must be freed */
+        SDLKey **table = keyCodeToSDLKeyTables;
+        while (*table != NULL && *table != data->keyCodeToSDLKTable) {
+            table++;
+        }
+        if (*table == NULL)
+            SDL_free(data->keyCodeToSDLKTable);
+        data->keyCodeToSDLKTable = NULL;
+    }
+
     SDL_DelKeyboard(data->keyboard);
 }
 
--- a/src/video/x11/SDL_x11keyboard.h	Thu Jan 03 06:07:30 2008 +0000
+++ b/src/video/x11/SDL_x11keyboard.h	Tue Jan 08 00:10:46 2008 +0000
@@ -24,7 +24,8 @@
 #ifndef _SDL_x11keyboard_h
 #define _SDL_x11keyboard_h
 
-extern void X11_InitKeyboard(_THIS);
+extern int X11_InitKeyboard(_THIS);
+extern SDLKey X11_GetLayoutKey(_THIS, SDLKey physicalKey);
 extern void X11_QuitKeyboard(_THIS);
 
 #endif /* _SDL_x11keyboard_h */
--- a/src/video/x11/SDL_x11video.c	Thu Jan 03 06:07:30 2008 +0000
+++ b/src/video/x11/SDL_x11video.c	Tue Jan 08 00:10:46 2008 +0000
@@ -171,6 +171,7 @@
     device->SetDisplayGammaRamp = X11_SetDisplayGammaRamp;
     device->GetDisplayGammaRamp = X11_GetDisplayGammaRamp;
     device->PumpEvents = X11_PumpEvents;
+    device->GetLayoutKey = X11_GetLayoutKey;
 
     device->CreateWindow = X11_CreateWindow;
     device->CreateWindowFrom = X11_CreateWindowFrom;
@@ -235,14 +236,9 @@
 
     X11_InitModes(_this);
 
-//#if SDL_VIDEO_RENDER_D3D
-//    D3D_AddRenderDriver(_this);
-//#endif
-//#if SDL_VIDEO_RENDER_GDI
-//    GDI_AddRenderDriver(_this);
-//#endif
-
-    X11_InitKeyboard(_this);
+    if (X11_InitKeyboard(_this) != 0) {
+        return -1;
+    }
     X11_InitMouse(_this);
 
     return 0;
--- a/src/video/x11/SDL_x11video.h	Thu Jan 03 06:07:30 2008 +0000
+++ b/src/video/x11/SDL_x11video.h	Tue Jan 08 00:10:46 2008 +0000
@@ -70,6 +70,7 @@
     int mouse;
     int keyboard;
     Atom WM_DELETE_WINDOW;
+    SDLKey *keyCodeToSDLKTable;
 } SDL_VideoData;
 
 #endif /* _SDL_x11video_h */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/x11/imKStoUCS.c	Tue Jan 08 00:10:46 2008 +0000
@@ -0,0 +1,345 @@
+/* Copyright (C) 1994-2003 The XFree86 Project, Inc.  All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the XFree86 Project shall not
+be used in advertising or otherwise to promote the sale, use or other deal-
+ings in this Software without prior written authorization from the XFree86
+Project.
+*/
+
+/* $XFree86: xc/lib/X11/imKStoUCS.c,v 1.4 2003/04/29 11:29:18 pascal Exp $ */
+
+#include <X11/X.h>
+#include "imKStoUCS.h"
+
+static unsigned short const keysym_to_unicode_1a1_1ff[] = {
+    0x0104, 0x02d8, 0x0141, 0x0000, 0x013d, 0x015a, 0x0000,     /* 0x01a0-0x01a7 */
+    0x0000, 0x0160, 0x015e, 0x0164, 0x0179, 0x0000, 0x017d, 0x017b,     /* 0x01a8-0x01af */
+    0x0000, 0x0105, 0x02db, 0x0142, 0x0000, 0x013e, 0x015b, 0x02c7,     /* 0x01b0-0x01b7 */
+    0x0000, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c,     /* 0x01b8-0x01bf */
+    0x0154, 0x0000, 0x0000, 0x0102, 0x0000, 0x0139, 0x0106, 0x0000,     /* 0x01c0-0x01c7 */
+    0x010c, 0x0000, 0x0118, 0x0000, 0x011a, 0x0000, 0x0000, 0x010e,     /* 0x01c8-0x01cf */
+    0x0110, 0x0143, 0x0147, 0x0000, 0x0000, 0x0150, 0x0000, 0x0000,     /* 0x01d0-0x01d7 */
+    0x0158, 0x016e, 0x0000, 0x0170, 0x0000, 0x0000, 0x0162, 0x0000,     /* 0x01d8-0x01df */
+    0x0155, 0x0000, 0x0000, 0x0103, 0x0000, 0x013a, 0x0107, 0x0000,     /* 0x01e0-0x01e7 */
+    0x010d, 0x0000, 0x0119, 0x0000, 0x011b, 0x0000, 0x0000, 0x010f,     /* 0x01e8-0x01ef */
+    0x0111, 0x0144, 0x0148, 0x0000, 0x0000, 0x0151, 0x0000, 0x0000,     /* 0x01f0-0x01f7 */
+    0x0159, 0x016f, 0x0000, 0x0171, 0x0000, 0x0000, 0x0163, 0x02d9      /* 0x01f8-0x01ff */
+};
+
+static unsigned short const keysym_to_unicode_2a1_2fe[] = {
+    0x0126, 0x0000, 0x0000, 0x0000, 0x0000, 0x0124, 0x0000,     /* 0x02a0-0x02a7 */
+    0x0000, 0x0130, 0x0000, 0x011e, 0x0134, 0x0000, 0x0000, 0x0000,     /* 0x02a8-0x02af */
+    0x0000, 0x0127, 0x0000, 0x0000, 0x0000, 0x0000, 0x0125, 0x0000,     /* 0x02b0-0x02b7 */
+    0x0000, 0x0131, 0x0000, 0x011f, 0x0135, 0x0000, 0x0000, 0x0000,     /* 0x02b8-0x02bf */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x010a, 0x0108, 0x0000,     /* 0x02c0-0x02c7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x02c8-0x02cf */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0120, 0x0000, 0x0000,     /* 0x02d0-0x02d7 */
+    0x011c, 0x0000, 0x0000, 0x0000, 0x0000, 0x016c, 0x015c, 0x0000,     /* 0x02d8-0x02df */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x010b, 0x0109, 0x0000,     /* 0x02e0-0x02e7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x02e8-0x02ef */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0121, 0x0000, 0x0000,     /* 0x02f0-0x02f7 */
+    0x011d, 0x0000, 0x0000, 0x0000, 0x0000, 0x016d, 0x015d      /* 0x02f8-0x02ff */
+};
+
+static unsigned short const keysym_to_unicode_3a2_3fe[] = {
+    0x0138, 0x0156, 0x0000, 0x0128, 0x013b, 0x0000,     /* 0x03a0-0x03a7 */
+    0x0000, 0x0000, 0x0112, 0x0122, 0x0166, 0x0000, 0x0000, 0x0000,     /* 0x03a8-0x03af */
+    0x0000, 0x0000, 0x0000, 0x0157, 0x0000, 0x0129, 0x013c, 0x0000,     /* 0x03b0-0x03b7 */
+    0x0000, 0x0000, 0x0113, 0x0123, 0x0167, 0x014a, 0x0000, 0x014b,     /* 0x03b8-0x03bf */
+    0x0100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x012e,     /* 0x03c0-0x03c7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0116, 0x0000, 0x0000, 0x012a,     /* 0x03c8-0x03cf */
+    0x0000, 0x0145, 0x014c, 0x0136, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x03d0-0x03d7 */
+    0x0000, 0x0172, 0x0000, 0x0000, 0x0000, 0x0168, 0x016a, 0x0000,     /* 0x03d8-0x03df */
+    0x0101, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x012f,     /* 0x03e0-0x03e7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0117, 0x0000, 0x0000, 0x012b,     /* 0x03e8-0x03ef */
+    0x0000, 0x0146, 0x014d, 0x0137, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x03f0-0x03f7 */
+    0x0000, 0x0173, 0x0000, 0x0000, 0x0000, 0x0169, 0x016b      /* 0x03f8-0x03ff */
+};
+
+static unsigned short const keysym_to_unicode_4a1_4df[] = {
+    0x3002, 0x3008, 0x3009, 0x3001, 0x30fb, 0x30f2, 0x30a1,     /* 0x04a0-0x04a7 */
+    0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30e3, 0x30e5, 0x30e7, 0x30c3,     /* 0x04a8-0x04af */
+    0x30fc, 0x30a2, 0x30a4, 0x30a6, 0x30a8, 0x30aa, 0x30ab, 0x30ad,     /* 0x04b0-0x04b7 */
+    0x30af, 0x30b1, 0x30b3, 0x30b5, 0x30b7, 0x30b9, 0x30bb, 0x30bd,     /* 0x04b8-0x04bf */
+    0x30bf, 0x30c1, 0x30c4, 0x30c6, 0x30c8, 0x30ca, 0x30cb, 0x30cc,     /* 0x04c0-0x04c7 */
+    0x30cd, 0x30ce, 0x30cf, 0x30d2, 0x30d5, 0x30d8, 0x30db, 0x30de,     /* 0x04c8-0x04cf */
+    0x30df, 0x30e0, 0x30e1, 0x30e2, 0x30e4, 0x30e6, 0x30e8, 0x30e9,     /* 0x04d0-0x04d7 */
+    0x30ea, 0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f3, 0x309b, 0x309c      /* 0x04d8-0x04df */
+};
+
+static unsigned short const keysym_to_unicode_590_5fe[] = {
+    0x06f0, 0x06f1, 0x06f2, 0x06f3, 0x06f4, 0x06f5, 0x06f6, 0x06f7,     /* 0x0590-0x0597 */
+    0x06f8, 0x06f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x0598-0x059f */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x066a, 0x0670, 0x0679,     /* 0x05a0-0x05a7 */
+
+    0x067e, 0x0686, 0x0688, 0x0691, 0x060c, 0x0000, 0x06d4, 0x0000,     /* 0x05ac-0x05af */
+    0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667,     /* 0x05b0-0x05b7 */
+    0x0668, 0x0669, 0x0000, 0x061b, 0x0000, 0x0000, 0x0000, 0x061f,     /* 0x05b8-0x05bf */
+    0x0000, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627,     /* 0x05c0-0x05c7 */
+    0x0628, 0x0629, 0x062a, 0x062b, 0x062c, 0x062d, 0x062e, 0x062f,     /* 0x05c8-0x05cf */
+    0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637,     /* 0x05d0-0x05d7 */
+    0x0638, 0x0639, 0x063a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x05d8-0x05df */
+    0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647,     /* 0x05e0-0x05e7 */
+    0x0648, 0x0649, 0x064a, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f,     /* 0x05e8-0x05ef */
+    0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0698, 0x06a4,     /* 0x05f0-0x05f7 */
+    0x06a9, 0x06af, 0x06ba, 0x06be, 0x06cc, 0x06d2, 0x06c1      /* 0x05f8-0x05fe */
+};
+
+static unsigned short keysym_to_unicode_680_6ff[] = {
+    0x0492, 0x0496, 0x049a, 0x049c, 0x04a2, 0x04ae, 0x04b0, 0x04b2,     /* 0x0680-0x0687 */
+    0x04b6, 0x04b8, 0x04ba, 0x0000, 0x04d8, 0x04e2, 0x04e8, 0x04ee,     /* 0x0688-0x068f */
+    0x0493, 0x0497, 0x049b, 0x049d, 0x04a3, 0x04af, 0x04b1, 0x04b3,     /* 0x0690-0x0697 */
+    0x04b7, 0x04b9, 0x04bb, 0x0000, 0x04d9, 0x04e3, 0x04e9, 0x04ef,     /* 0x0698-0x069f */
+    0x0000, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457,     /* 0x06a0-0x06a7 */
+    0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x0491, 0x045e, 0x045f,     /* 0x06a8-0x06af */
+    0x2116, 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407,     /* 0x06b0-0x06b7 */
+    0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x0490, 0x040e, 0x040f,     /* 0x06b8-0x06bf */
+    0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,     /* 0x06c0-0x06c7 */
+    0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e,     /* 0x06c8-0x06cf */
+    0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,     /* 0x06d0-0x06d7 */
+    0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a,     /* 0x06d8-0x06df */
+    0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,     /* 0x06e0-0x06e7 */
+    0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e,     /* 0x06e8-0x06ef */
+    0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,     /* 0x06f0-0x06f7 */
+    0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a      /* 0x06f8-0x06ff */
+};
+
+static unsigned short const keysym_to_unicode_7a1_7f9[] = {
+    0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c,     /* 0x07a0-0x07a7 */
+    0x038e, 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015,     /* 0x07a8-0x07af */
+    0x0000, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc,     /* 0x07b0-0x07b7 */
+    0x03cd, 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x07b8-0x07bf */
+    0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,     /* 0x07c0-0x07c7 */
+    0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,     /* 0x07c8-0x07cf */
+    0x03a0, 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7,     /* 0x07d0-0x07d7 */
+    0x03a8, 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x07d8-0x07df */
+    0x0000, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,     /* 0x07e0-0x07e7 */
+    0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,     /* 0x07e8-0x07ef */
+    0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7,     /* 0x07f0-0x07f7 */
+    0x03c8, 0x03c9              /* 0x07f8-0x07ff */
+};
+
+static unsigned short const keysym_to_unicode_8a4_8fe[] = {
+    0x2320, 0x2321, 0x0000, 0x231c,     /* 0x08a0-0x08a7 */
+    0x231d, 0x231e, 0x231f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x08a8-0x08af */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x08b0-0x08b7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222b,     /* 0x08b8-0x08bf */
+    0x2234, 0x0000, 0x221e, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000,     /* 0x08c0-0x08c7 */
+    0x2245, 0x2246, 0x0000, 0x0000, 0x0000, 0x0000, 0x22a2, 0x0000,     /* 0x08c8-0x08cf */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221a, 0x0000,     /* 0x08d0-0x08d7 */
+    0x0000, 0x0000, 0x2282, 0x2283, 0x2229, 0x222a, 0x2227, 0x2228,     /* 0x08d8-0x08df */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x08e0-0x08e7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x08e8-0x08ef */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000,     /* 0x08f0-0x08f7 */
+    0x0000, 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193      /* 0x08f8-0x08ff */
+};
+
+static unsigned short const keysym_to_unicode_9df_9f8[] = {
+    0x2422,                     /* 0x09d8-0x09df */
+    0x2666, 0x25a6, 0x2409, 0x240c, 0x240d, 0x240a, 0x0000, 0x0000,     /* 0x09e0-0x09e7 */
+    0x240a, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x2500,     /* 0x09e8-0x09ef */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x251c, 0x2524, 0x2534, 0x252c,     /* 0x09f0-0x09f7 */
+    0x2502                      /* 0x09f8-0x09ff */
+};
+
+static unsigned short const keysym_to_unicode_aa1_afe[] = {
+    0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009,     /* 0x0aa0-0x0aa7 */
+    0x200a, 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025,     /* 0x0aa8-0x0aaf */
+    0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a,     /* 0x0ab0-0x0ab7 */
+    0x2105, 0x0000, 0x0000, 0x2012, 0x2039, 0x2024, 0x203a, 0x0000,     /* 0x0ab8-0x0abf */
+    0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000,     /* 0x0ac0-0x0ac7 */
+    0x0000, 0x2122, 0x2120, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25ad,     /* 0x0ac8-0x0acf */
+    0x2018, 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033,     /* 0x0ad0-0x0ad7 */
+    0x0000, 0x271d, 0x0000, 0x220e, 0x25c2, 0x2023, 0x25cf, 0x25ac,     /* 0x0ad8-0x0adf */
+    0x25e6, 0x25ab, 0x25ae, 0x25b5, 0x25bf, 0x2606, 0x2022, 0x25aa,     /* 0x0ae0-0x0ae7 */
+    0x25b4, 0x25be, 0x261a, 0x261b, 0x2663, 0x2666, 0x2665, 0x0000,     /* 0x0ae8-0x0aef */
+    0x2720, 0x2020, 0x2021, 0x2713, 0x2612, 0x266f, 0x266d, 0x2642,     /* 0x0af0-0x0af7 */
+    0x2640, 0x2121, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e      /* 0x0af8-0x0aff */
+};
+
+/* none of the APL keysyms match the Unicode characters */
+
+static unsigned short const keysym_to_unicode_cdf_cfa[] = {
+    0x2017,                     /* 0x0cd8-0x0cdf */
+    0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7,     /* 0x0ce0-0x0ce7 */
+    0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df,     /* 0x0ce8-0x0cef */
+    0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7,     /* 0x0cf0-0x0cf7 */
+    0x05e8, 0x05e9, 0x05ea      /* 0x0cf8-0x0cff */
+};
+
+static unsigned short const keysym_to_unicode_da1_df9[] = {
+    0x0e01, 0x0e02, 0x0e03, 0x0e04, 0x0e05, 0x0e06, 0x0e07,     /* 0x0da0-0x0da7 */
+    0x0e08, 0x0e09, 0x0e0a, 0x0e0b, 0x0e0c, 0x0e0d, 0x0e0e, 0x0e0f,     /* 0x0da8-0x0daf */
+    0x0e10, 0x0e11, 0x0e12, 0x0e13, 0x0e14, 0x0e15, 0x0e16, 0x0e17,     /* 0x0db0-0x0db7 */
+    0x0e18, 0x0e19, 0x0e1a, 0x0e1b, 0x0e1c, 0x0e1d, 0x0e1e, 0x0e1f,     /* 0x0db8-0x0dbf */
+    0x0e20, 0x0e21, 0x0e22, 0x0e23, 0x0e24, 0x0e25, 0x0e26, 0x0e27,     /* 0x0dc0-0x0dc7 */
+    0x0e28, 0x0e29, 0x0e2a, 0x0e2b, 0x0e2c, 0x0e2d, 0x0e2e, 0x0e2f,     /* 0x0dc8-0x0dcf */
+    0x0e30, 0x0e31, 0x0e32, 0x0e33, 0x0e34, 0x0e35, 0x0e36, 0x0e37,     /* 0x0dd0-0x0dd7 */
+    0x0e38, 0x0e39, 0x0e3a, 0x0000, 0x0000, 0x0000, 0x0e3e, 0x0e3f,     /* 0x0dd8-0x0ddf */
+    0x0e40, 0x0e41, 0x0e42, 0x0e43, 0x0e44, 0x0e45, 0x0e46, 0x0e47,     /* 0x0de0-0x0de7 */
+    0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x0e4d, 0x0000, 0x0000,     /* 0x0de8-0x0def */
+    0x0e50, 0x0e51, 0x0e52, 0x0e53, 0x0e54, 0x0e55, 0x0e56, 0x0e57,     /* 0x0df0-0x0df7 */
+    0x0e58, 0x0e59              /* 0x0df8-0x0dff */
+};
+
+static unsigned short const keysym_to_unicode_ea0_eff[] = {
+    0x0000, 0x1101, 0x1101, 0x11aa, 0x1102, 0x11ac, 0x11ad, 0x1103,     /* 0x0ea0-0x0ea7 */
+    0x1104, 0x1105, 0x11b0, 0x11b1, 0x11b2, 0x11b3, 0x11b4, 0x11b5,     /* 0x0ea8-0x0eaf */
+    0x11b6, 0x1106, 0x1107, 0x1108, 0x11b9, 0x1109, 0x110a, 0x110b,     /* 0x0eb0-0x0eb7 */
+    0x110c, 0x110d, 0x110e, 0x110f, 0x1110, 0x1111, 0x1112, 0x1161,     /* 0x0eb8-0x0ebf */
+    0x1162, 0x1163, 0x1164, 0x1165, 0x1166, 0x1167, 0x1168, 0x1169,     /* 0x0ec0-0x0ec7 */
+    0x116a, 0x116b, 0x116c, 0x116d, 0x116e, 0x116f, 0x1170, 0x1171,     /* 0x0ec8-0x0ecf */
+    0x1172, 0x1173, 0x1174, 0x1175, 0x11a8, 0x11a9, 0x11aa, 0x11ab,     /* 0x0ed0-0x0ed7 */
+    0x11ac, 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3,     /* 0x0ed8-0x0edf */
+    0x11b4, 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb,     /* 0x0ee0-0x0ee7 */
+    0x11bc, 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x0000,     /* 0x0ee8-0x0eef */
+    0x0000, 0x0000, 0x1140, 0x0000, 0x0000, 0x1159, 0x119e, 0x0000,     /* 0x0ef0-0x0ef7 */
+    0x11eb, 0x0000, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9,     /* 0x0ef8-0x0eff */
+};
+
+static unsigned short keysym_to_unicode_12a1_12fe[] = {
+    0x1e02, 0x1e03, 0x0000, 0x0000, 0x0000, 0x1e0a, 0x0000,     /* 0x12a0-0x12a7 */
+    0x1e80, 0x0000, 0x1e82, 0x1e0b, 0x1ef2, 0x0000, 0x0000, 0x0000,     /* 0x12a8-0x12af */
+    0x1e1e, 0x1e1f, 0x0000, 0x0000, 0x1e40, 0x1e41, 0x0000, 0x1e56,     /* 0x12b0-0x12b7 */
+    0x1e81, 0x1e57, 0x1e83, 0x1e60, 0x1ef3, 0x1e84, 0x1e85, 0x1e61,     /* 0x12b8-0x12bf */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x12c0-0x12c7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x12c8-0x12cf */
+    0x0174, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1e6a,     /* 0x12d0-0x12d7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0176, 0x0000,     /* 0x12d8-0x12df */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x12e0-0x12e7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x12e8-0x12ef */
+    0x0175, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1e6b,     /* 0x12f0-0x12f7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0177      /* 0x12f0-0x12ff */
+};
+
+static unsigned short const keysym_to_unicode_13bc_13be[] = {
+    0x0152, 0x0153, 0x0178      /* 0x13b8-0x13bf */
+};
+
+static unsigned short keysym_to_unicode_14a1_14ff[] = {
+    0x2741, 0x00a7, 0x0589, 0x0029, 0x0028, 0x00bb, 0x00ab,     /* 0x14a0-0x14a7 */
+    0x2014, 0x002e, 0x055d, 0x002c, 0x2013, 0x058a, 0x2026, 0x055c,     /* 0x14a8-0x14af */
+    0x055b, 0x055e, 0x0531, 0x0561, 0x0532, 0x0562, 0x0533, 0x0563,     /* 0x14b0-0x14b7 */
+    0x0534, 0x0564, 0x0535, 0x0565, 0x0536, 0x0566, 0x0537, 0x0567,     /* 0x14b8-0x14bf */
+    0x0538, 0x0568, 0x0539, 0x0569, 0x053a, 0x056a, 0x053b, 0x056b,     /* 0x14c0-0x14c7 */
+    0x053c, 0x056c, 0x053d, 0x056d, 0x053e, 0x056e, 0x053f, 0x056f,     /* 0x14c8-0x14cf */
+    0x0540, 0x0570, 0x0541, 0x0571, 0x0542, 0x0572, 0x0543, 0x0573,     /* 0x14d0-0x14d7 */
+    0x0544, 0x0574, 0x0545, 0x0575, 0x0546, 0x0576, 0x0547, 0x0577,     /* 0x14d8-0x14df */
+    0x0548, 0x0578, 0x0549, 0x0579, 0x054a, 0x057a, 0x054b, 0x057b,     /* 0x14e0-0x14e7 */
+    0x054c, 0x057c, 0x054d, 0x057d, 0x054e, 0x057e, 0x054f, 0x057f,     /* 0x14e8-0x14ef */
+    0x0550, 0x0580, 0x0551, 0x0581, 0x0552, 0x0582, 0x0553, 0x0583,     /* 0x14f0-0x14f7 */
+    0x0554, 0x0584, 0x0555, 0x0585, 0x0556, 0x0586, 0x2019, 0x0027,     /* 0x14f8-0x14ff */
+};
+
+static unsigned short keysym_to_unicode_15d0_15f6[] = {
+    0x10d0, 0x10d1, 0x10d2, 0x10d3, 0x10d4, 0x10d5, 0x10d6, 0x10d7,     /* 0x15d0-0x15d7 */
+    0x10d8, 0x10d9, 0x10da, 0x10db, 0x10dc, 0x10dd, 0x10de, 0x10df,     /* 0x15d8-0x15df */
+    0x10e0, 0x10e1, 0x10e2, 0x10e3, 0x10e4, 0x10e5, 0x10e6, 0x10e7,     /* 0x15e0-0x15e7 */
+    0x10e8, 0x10e9, 0x10ea, 0x10eb, 0x10ec, 0x10ed, 0x10ee, 0x10ef,     /* 0x15e8-0x15ef */
+    0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6      /* 0x15f0-0x15f7 */
+};
+
+static unsigned short keysym_to_unicode_16a0_16f6[] = {
+    0x0000, 0x0000, 0xf0a2, 0x1e8a, 0x0000, 0xf0a5, 0x012c, 0xf0a7,     /* 0x16a0-0x16a7 */
+    0xf0a8, 0x01b5, 0x01e6, 0x0000, 0x0000, 0x0000, 0x0000, 0x019f,     /* 0x16a8-0x16af */
+    0x0000, 0x0000, 0xf0b2, 0x1e8b, 0x01d1, 0xf0b5, 0x012d, 0xf0b7,     /* 0x16b0-0x16b7 */
+    0xf0b8, 0x01b6, 0x01e7, 0x0000, 0x0000, 0x01d2, 0x0000, 0x0275,     /* 0x16b8-0x16bf */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x018f, 0x0000,     /* 0x16c0-0x16c7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x16c8-0x16cf */
+    0x0000, 0x1e36, 0xf0d2, 0xf0d3, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x16d0-0x16d7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x16d8-0x16df */
+    0x0000, 0x1e37, 0xf0e2, 0xf0e3, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x16e0-0x16e7 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     /* 0x16e8-0x16ef */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0259      /* 0x16f0-0x16f6 */
+};
+
+static unsigned short const keysym_to_unicode_1e9f_1eff[] = {
+    0x0303,
+    0x1ea0, 0x1ea1, 0x1ea2, 0x1ea3, 0x1ea4, 0x1ea5, 0x1ea6, 0x1ea7,     /* 0x1ea0-0x1ea7 */
+    0x1ea8, 0x1ea9, 0x1eaa, 0x1eab, 0x1eac, 0x1ead, 0x1eae, 0x1eaf,     /* 0x1ea8-0x1eaf */
+    0x1eb0, 0x1eb1, 0x1eb2, 0x1eb3, 0x1eb4, 0x1eb5, 0x1eb6, 0x1eb7,     /* 0x1eb0-0x1eb7 */
+    0x1eb8, 0x1eb9, 0x1eba, 0x1ebb, 0x1ebc, 0x1ebd, 0x1ebe, 0x1ebf,     /* 0x1eb8-0x1ebf */
+    0x1ec0, 0x1ec1, 0x1ec2, 0x1ec3, 0x1ec4, 0x1ec5, 0x1ec6, 0x1ec7,     /* 0x1ec0-0x1ec7 */
+    0x1ec8, 0x1ec9, 0x1eca, 0x1ecb, 0x1ecc, 0x1ecd, 0x1ece, 0x1ecf,     /* 0x1ec8-0x1ecf */
+    0x1ed0, 0x1ed1, 0x1ed2, 0x1ed3, 0x1ed4, 0x1ed5, 0x1ed6, 0x1ed7,     /* 0x1ed0-0x1ed7 */
+    0x1ed8, 0x1ed9, 0x1eda, 0x1edb, 0x1edc, 0x1edd, 0x1ede, 0x1edf,     /* 0x1ed8-0x1edf */
+    0x1ee0, 0x1ee1, 0x1ee2, 0x1ee3, 0x1ee4, 0x1ee5, 0x1ee6, 0x1ee7,     /* 0x1ee0-0x1ee7 */
+    0x1ee8, 0x1ee9, 0x1eea, 0x1eeb, 0x1eec, 0x1eed, 0x1eee, 0x1eef,     /* 0x1ee8-0x1eef */
+    0x1ef0, 0x1ef1, 0x0300, 0x0301, 0x1ef4, 0x1ef5, 0x1ef6, 0x1ef7,     /* 0x1ef0-0x1ef7 */
+    0x1ef8, 0x1ef9, 0x01a0, 0x01a1, 0x01af, 0x01b0, 0x0309, 0x0323      /* 0x1ef8-0x1eff */
+};
+
+static unsigned short const keysym_to_unicode_20a0_20ac[] = {
+    0x20a0, 0x20a1, 0x20a2, 0x20a3, 0x20a4, 0x20a5, 0x20a6, 0x20a7,     /* 0x20a0-0x20a7 */
+    0x20a8, 0x20a9, 0x20aa, 0x20ab, 0x20ac      /* 0x20a8-0x20af */
+};
+
+unsigned int
+X11_KeySymToUcs4(KeySym keysym)
+{
+    /* 'Unicode keysym' */
+    if ((keysym & 0xff000000) == 0x01000000)
+        return (keysym & 0x00ffffff);
+
+    if (keysym > 0 && keysym < 0x100)
+        return keysym;
+    else if (keysym > 0x1a0 && keysym < 0x200)
+        return keysym_to_unicode_1a1_1ff[keysym - 0x1a1];
+    else if (keysym > 0x2a0 && keysym < 0x2ff)
+        return keysym_to_unicode_2a1_2fe[keysym - 0x2a1];
+    else if (keysym > 0x3a1 && keysym < 0x3ff)
+        return keysym_to_unicode_3a2_3fe[keysym - 0x3a2];
+    else if (keysym > 0x4a0 && keysym < 0x4e0)
+        return keysym_to_unicode_4a1_4df[keysym - 0x4a1];
+    else if (keysym > 0x589 && keysym < 0x5ff)
+        return keysym_to_unicode_590_5fe[keysym - 0x590];
+    else if (keysym > 0x67f && keysym < 0x700)
+        return keysym_to_unicode_680_6ff[keysym - 0x680];
+    else if (keysym > 0x7a0 && keysym < 0x7fa)
+        return keysym_to_unicode_7a1_7f9[keysym - 0x7a1];
+    else if (keysym > 0x8a3 && keysym < 0x8ff)
+        return keysym_to_unicode_8a4_8fe[keysym - 0x8a4];
+    else if (keysym > 0x9de && keysym < 0x9f9)
+        return keysym_to_unicode_9df_9f8[keysym - 0x9df];
+    else if (keysym > 0xaa0 && keysym < 0xaff)
+        return keysym_to_unicode_aa1_afe[keysym - 0xaa1];
+    else if (keysym > 0xcde && keysym < 0xcfb)
+        return keysym_to_unicode_cdf_cfa[keysym - 0xcdf];
+    else if (keysym > 0xda0 && keysym < 0xdfa)
+        return keysym_to_unicode_da1_df9[keysym - 0xda1];
+    else if (keysym > 0xe9f && keysym < 0xf00)
+        return keysym_to_unicode_ea0_eff[keysym - 0xea0];
+    else if (keysym > 0x12a0 && keysym < 0x12ff)
+        return keysym_to_unicode_12a1_12fe[keysym - 0x12a1];
+    else if (keysym > 0x13bb && keysym < 0x13bf)
+        return keysym_to_unicode_13bc_13be[keysym - 0x13bc];
+    else if (keysym > 0x14a0 && keysym < 0x1500)
+        return keysym_to_unicode_14a1_14ff[keysym - 0x14a1];
+    else if (keysym > 0x15cf && keysym < 0x15f7)
+        return keysym_to_unicode_15d0_15f6[keysym - 0x15d0];
+    else if (keysym > 0x169f && keysym < 0x16f7)
+        return keysym_to_unicode_16a0_16f6[keysym - 0x16a0];
+    else if (keysym > 0x1e9e && keysym < 0x1f00)
+        return keysym_to_unicode_1e9f_1eff[keysym - 0x1e9f];
+    else if (keysym > 0x209f && keysym < 0x20ad)
+        return keysym_to_unicode_20a0_20ac[keysym - 0x20a0];
+    else
+        return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/x11/imKStoUCS.h	Tue Jan 08 00:10:46 2008 +0000
@@ -0,0 +1,31 @@
+#ifndef _imKStoUCS_h
+#define _imKStoUCS_h
+
+/* Copyright (C) 1994-2003 The XFree86 Project, Inc.  All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the XFree86 Project shall not
+be used in advertising or otherwise to promote the sale, use or other deal-
+ings in this Software without prior written authorization from the XFree86
+Project.
+*/
+
+extern unsigned int X11_KeySymToUcs4(KeySym keysym);
+
+#endif /*_imKStoUCS_h */
--- a/test/checkkeys.c	Thu Jan 03 06:07:30 2008 +0000
+++ b/test/checkkeys.c	Tue Jan 08 00:10:46 2008 +0000
@@ -57,11 +57,15 @@
 {
     /* Print the keycode, name and state */
     if (sym->sym) {
-        printf("Key %s:  %d-%s ", pressed ? "pressed" : "released",
-               sym->sym, SDL_GetKeyName(sym->sym));
+        printf("Key %s:  physical 0x%08X = %s, layout 0x%08X = %s ",
+               pressed ? "pressed " : "released",
+               sym->sym,
+               SDL_GetKeyName(sym->sym),
+               SDL_GetLayoutKey(sym->sym),
+               SDL_GetKeyName(SDL_GetLayoutKey(sym->sym)));
     } else {
-        printf("Unknown Key (scancode = %d) %s ", sym->scancode,
-               pressed ? "pressed" : "released");
+        printf("Unknown Key (scancode = 0x%08X) %s ",
+               sym->scancode, pressed ? "pressed" : "released");
     }
 
     /* Print the translated character, if one exists */