diff --git a/src/x11_init.c b/src/x11_init.c index 3d07736c..6fa7debc 100644 --- a/src/x11_init.c +++ b/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(); diff --git a/src/x11_platform.h b/src/x11_platform.h index 8cfa4917..499f239a 100644 --- a/src/x11_platform.h +++ b/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; diff --git a/src/x11_window.c b/src/x11_window.c index 41f05ce6..007a0af5 100644 --- a/src/x11_window.c +++ b/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; - // Convert to Unicode (see x11_unicode.c) - return (int) _glfwKeySym2Unicode(keysym); + // 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; + } + } + + 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);