diff --git a/src/x11_init.c b/src/x11_init.c index ab07fe323..bc5f6c155 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -432,6 +432,8 @@ static void detectEWMH(void) static GLboolean initExtensions(void) { Bool supported; + unsigned int u; + XIMStyles * styles = NULL; // Find or create window manager atoms _glfw.x11.WM_PROTOCOLS = XInternAtom(_glfw.x11.display, @@ -552,6 +554,44 @@ static GLboolean initExtensions(void) XInternAtom(_glfw.x11.display, "CLIPBOARD_MANAGER", False); _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); @@ -663,6 +703,8 @@ void _glfwInputXError(int error, const char* message) int _glfwPlatformInit(void) { XInitThreads(); + + _glfw.x11.im = NULL; _glfw.x11.display = XOpenDisplay(NULL); if (!_glfw.x11.display) @@ -699,6 +741,12 @@ void _glfwPlatformTerminate(void) } free(_glfw.x11.selection.string); + + 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 fca8d3e12..081791a1b 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -94,6 +94,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; @@ -222,6 +224,9 @@ typedef struct _GLFWlibraryX11 int buttonCount; char* name; } joystick[GLFW_JOYSTICK_LAST + 1]; + + // Input method and context + XIM im; } _GLFWlibraryX11; diff --git a/src/x11_window.c b/src/x11_window.c index a585dadf4..c9788f205 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]; + + // 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); - // Get X11 keysym - XLookupString(event, NULL, 0, &keysym, NULL); - - // Convert to Unicode (see x11_unicode.c) - return (int) _glfwKeySym2Unicode(keysym); + // 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; } // Splits a text/uri-list into separate file paths @@ -161,6 +200,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 @@ -396,6 +437,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); @@ -608,14 +656,15 @@ 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) - _glfwInputChar(window, character); + for (i = 0; i < n_chars; i++) + _glfwInputChar(window, (unsigned int)characters[i]); break; } @@ -1123,6 +1172,12 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) { if (window->monitor) leaveFullscreenMode(window); + + if (window->x11.ic) + { + XDestroyIC(window->x11.ic); + window->x11.ic = NULL; + } _glfwDestroyContext(window);