diff --git a/README.md b/README.md index 6d03b7c5..67a95459 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,7 @@ GLFW bundles a number of dependencies in the `deps/` directory. - [X11] Bugfix: Workaround for legacy Compiz caused flickering during resize - [X11] Bugfix: The name pointer of joysticks were not cleared on disconnection - [X11] Bugfix: Video mode dimensions were not rotated to match the CRTC + - [X11] Bugfix: Unicode character input ignored dead keys ## Contact @@ -174,6 +175,7 @@ skills. - Sylvain Hellegouarch - Matthew Henry - heromyth + - Lucas Hinderberger - Paul Holden - Toni Jovanoski - Arseny Kapoulkine diff --git a/src/x11_init.c b/src/x11_init.c index 6fa7debc..028d715d 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -329,6 +329,30 @@ static void updateKeyCodeLUT(void) } } +// Check whether the IM has a usable style +// +static GLboolean hasUsableInputMethodStyle(void) +{ + unsigned int i; + GLboolean found = GL_FALSE; + XIMStyles* styles = NULL; + + if (XGetIMValues(_glfw.x11.im, XNQueryInputStyle, &styles, NULL) != NULL) + return GL_FALSE; + + for (i = 0; i < styles->count_styles; i++) + { + if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) + { + found = GL_TRUE; + break; + } + } + + XFree(styles); + return found; +} + // Check whether the specified atom is supported // static Atom getSupportedAtom(Atom* supportedAtoms, @@ -449,9 +473,6 @@ 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", @@ -582,43 +603,6 @@ 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); @@ -726,8 +710,6 @@ int _glfwPlatformInit(void) { XInitThreads(); - _glfw.x11.im = NULL; - _glfw.x11.display = XOpenDisplay(NULL); if (!_glfw.x11.display) { @@ -744,6 +726,21 @@ int _glfwPlatformInit(void) _glfw.x11.cursor = createNULLCursor(); + if (XSupportsLocale()) + { + XSetLocaleModifiers(""); + + _glfw.x11.im = XOpenIM(_glfw.x11.display, 0, 0, 0); + if (_glfw.x11.im) + { + if (!hasUsableInputMethodStyle()) + { + XCloseIM(_glfw.x11.im); + _glfw.x11.im = NULL; + } + } + } + if (!_glfwInitContextAPI()) return GL_FALSE; diff --git a/src/x11_platform.h b/src/x11_platform.h index 499f239a..7c4e72e2 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -78,6 +78,7 @@ typedef struct _GLFWwindowX11 { Colormap colormap; Window handle; + XIC ic; GLboolean overrideRedirect; @@ -90,8 +91,6 @@ typedef struct _GLFWwindowX11 // The last position the cursor was warped to by GLFW int warpPosX, warpPosY; - // The window's input context - XIC ic; } _GLFWwindowX11; @@ -107,6 +106,8 @@ typedef struct _GLFWlibraryX11 Cursor cursor; // Context for mapping window XIDs to _GLFWwindow pointers XContext context; + // XIM input method + XIM im; // True if window manager supports EWMH GLboolean hasEWMH; // Most recent error code received by X error handler @@ -204,9 +205,6 @@ 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 007a0af5..ce3d8a66 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -95,58 +95,6 @@ static int translateKey(int keycode) return _glfw.x11.keyCodeLUT[keycode]; } -// Translates an X Window event to Unicode -// -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); - - // 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 // static _GLFWwindow* findWindowByHandle(Window handle) @@ -240,8 +188,6 @@ 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 @@ -477,13 +423,18 @@ 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); + + if (_glfw.x11.im) + { + 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); @@ -876,20 +827,45 @@ 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 wchar_t * characters = translateChar(event, window, &n_chars); + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); - _glfwInputKey(window, key, event->xkey.keycode, GLFW_PRESS, mods); + if (event->xkey.keycode) + _glfwInputKey(window, key, event->xkey.keycode, GLFW_PRESS, mods); - for (i = 0; i < n_chars; i++) + if (window->x11.ic) { - if (characters[i] != -1) + // Translate keys to characters with XIM input context + + int i; + Status status; + wchar_t buffer[16]; + + if (XFilterEvent(event, None)) { - const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); - _glfwInputChar(window, (unsigned int)characters[i], mods, plain); + // Discard intermediary (dead key) events for character input + break; } + + const int count = XwcLookupString(window->x11.ic, + &event->xkey, + buffer, sizeof(buffer), + NULL, &status); + + for (i = 0; i < count; i++) + _glfwInputChar(window, buffer[i], mods, plain); + } + else + { + // Translate keys to characters with fallback lookup table + + KeySym keysym; + XLookupString(&event->xkey, NULL, 0, &keysym, NULL); + + const long character = _glfwKeySym2Unicode(keysym); + if (character != -1) + _glfwInputChar(window, character, mods, plain); } break; @@ -1418,7 +1394,7 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) { if (window->monitor) leaveFullscreenMode(window); - + if (window->x11.ic) { XDestroyIC(window->x11.ic);