Browse Source

Fixed dead keys in X11.

The library will now try to create an X input context to handle dead
keys properly on international keyboards.  This makes it possible to
enter for example an e with accent grave on a German keyboard without
further efforts.  A fallback mechanism is provided in case the client
does not support X input method / context creation.  In that case, the
library will behave as it did before.
Lucas Hinderberger 12 years ago
parent
commit
cec63f3cb5
3 changed files with 120 additions and 9 deletions
  1. 48 0
      src/x11_init.c
  2. 5 0
      src/x11_platform.h
  3. 67 9
      src/x11_window.c

+ 48 - 0
src/x11_init.c

@@ -449,6 +449,9 @@ static void detectEWMH(void)
 //
 static GLboolean initExtensions(void)
 {
+    unsigned int u;
+    XIMStyles * styles = NULL;
+
     // Find or create window manager atoms
     _glfw.x11.WM_PROTOCOLS = XInternAtom(_glfw.x11.display,
                                          "WM_PROTOCOLS",
@@ -579,6 +582,43 @@ static GLboolean initExtensions(void)
     _glfw.x11.SAVE_TARGETS =
         XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False);
 
+    // ------------------------------------------------------------------------
+    // Optional extensions (function returns always true from here!)
+
+    // Open input method
+    if (!XSupportsLocale())
+        return GL_TRUE;
+
+    XSetLocaleModifiers("");
+    _glfw.x11.im = XOpenIM(_glfw.x11.display, 0, 0, 0);
+    if (!_glfw.x11.im)
+        return GL_TRUE;
+
+    // Get available input styles
+    if (XGetIMValues(_glfw.x11.im, XNQueryInputStyle, &styles, NULL) || !styles)
+    {
+        XCloseIM(_glfw.x11.im);
+        _glfw.x11.im = NULL;
+        return GL_TRUE;
+    }
+
+    // Search for needed input style
+    for (u = 0; u < styles->count_styles; u++)
+    {
+        if (styles->supported_styles[u] == (XIMPreeditNothing | XIMStatusNothing))
+            break;
+    }
+
+    if (u >= styles->count_styles)
+    {
+        XFree(styles);
+        XCloseIM(_glfw.x11.im);
+        _glfw.x11.im = NULL;
+        return GL_TRUE;
+    }
+
+    XFree(styles);
+
     // Find Xdnd (drag and drop) atoms, if available
     _glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", True);
     _glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", True);
@@ -686,6 +726,8 @@ int _glfwPlatformInit(void)
 {
     XInitThreads();
 
+    _glfw.x11.im = NULL;
+
     _glfw.x11.display = XOpenDisplay(NULL);
     if (!_glfw.x11.display)
     {
@@ -721,6 +763,12 @@ void _glfwPlatformTerminate(void)
 
     free(_glfw.x11.clipboardString);
 
+    if (_glfw.x11.im)
+    {
+        XCloseIM(_glfw.x11.im);
+        _glfw.x11.im = NULL;
+    }
+
     _glfwTerminateJoysticks();
     _glfwTerminateContextAPI();
 

+ 5 - 0
src/x11_platform.h

@@ -90,6 +90,8 @@ typedef struct _GLFWwindowX11
     // The last position the cursor was warped to by GLFW
     int             warpPosX, warpPosY;
 
+    // The window's input context
+    XIC ic;
 } _GLFWwindowX11;
 
 
@@ -202,6 +204,9 @@ typedef struct _GLFWlibraryX11
         Window      source;
     } xdnd;
 
+    // Input method and context
+    XIM im;
+
 } _GLFWlibraryX11;
 
 

+ 67 - 9
src/x11_window.c

@@ -97,15 +97,54 @@ static int translateKey(int keycode)
 
 // Translates an X Window event to Unicode
 //
-static int translateChar(XKeyEvent* event)
+static wchar_t * translateChar(XEvent * event, _GLFWwindow * window, int * count)
 {
     KeySym keysym;
+    static wchar_t buffer[16];
 
-    // Get X11 keysym
-    XLookupString(event, NULL, 0, &keysym, NULL);
+    // If there is no input method / context available, use the old fallback
+    // mechanism
+    if (!window || !window->x11.ic)
+    {
+        long uc;
+
+        // Get X11 keysym
+        XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
+
+        // Convert to Unicode (see x11_unicode.c)
+        uc = _glfwKeySym2Unicode(keysym);
+        if (uc < 0 || uc > 0xFFFF)
+        {
+            *count = 0;
+            return NULL;
+        }
+
+        buffer[0] = (unsigned int)uc;
+        *count = 1;
+    }
+    // Else lookup the wide char string with respect to dead characters
+    else
+    {
+        Status dummy;
+
+        // Check if the given event is a dead char. In that case, it does not
+        // produce a unicode char.
+        if (XFilterEvent(event, None))
+        {
+            *count = 0;
+            return NULL;
+        }
+
+        // Retrieve unicode string
+        *count = XwcLookupString(window->x11.ic, &event->xkey, buffer, 16 * sizeof(wchar_t), 0, &dummy);
+        if (*count < 0)
+        {
+            *count = 0;
+            return NULL;
+        }
+    }
 
-    // Convert to Unicode (see x11_unicode.c)
-    return (int) _glfwKeySym2Unicode(keysym);
+    return buffer;
 }
 
 // Return the GLFW window corresponding to the specified X11 window
@@ -201,6 +240,8 @@ static GLboolean createWindow(_GLFWwindow* window,
     unsigned long wamask;
     XSetWindowAttributes wa;
     XVisualInfo* visual = _GLFW_X11_CONTEXT_VISUAL;
+    
+    window->x11.ic = NULL;
 
     // Every window needs a colormap
     // Create one based on the visual used by the current context
@@ -436,6 +477,13 @@ static GLboolean createWindow(_GLFWwindow* window,
 
     XRRSelectInput(_glfw.x11.display, window->x11.handle,
                    RRScreenChangeNotifyMask);
+    
+    // Try to create an input context. If this function returns NULL, ic is
+    // set to NULL and we know we have to use fallback mechanisms to parse
+    // char events.
+    window->x11.ic = XCreateIC(_glfw.x11.im, XNInputStyle,
+        XIMPreeditNothing | XIMStatusNothing, XNClientWindow,
+        window->x11.handle, XNFocusWindow, window->x11.handle, NULL);
 
     _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
     _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
@@ -828,16 +876,20 @@ static void processEvent(XEvent *event)
     {
         case KeyPress:
         {
+            int i, n_chars;
             const int key = translateKey(event->xkey.keycode);
             const int mods = translateState(event->xkey.state);
-            const int character = translateChar(&event->xkey);
+            const wchar_t * characters = translateChar(event, window, &n_chars);
 
             _glfwInputKey(window, key, event->xkey.keycode, GLFW_PRESS, mods);
 
-            if (character != -1)
+            for (i = 0; i < n_chars; i++)
             {
-                const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
-                _glfwInputChar(window, character, mods, plain);
+                if (characters[i] != -1)
+                {
+                    const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
+                    _glfwInputChar(window, (unsigned int)characters[i], mods, plain);
+                }
             }
 
             break;
@@ -1366,6 +1418,12 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
 {
     if (window->monitor)
         leaveFullscreenMode(window);
+    
+    if (window->x11.ic)
+    {
+        XDestroyIC(window->x11.ic);
+        window->x11.ic = NULL;
+    }
 
     _glfwDestroyContext(window);