From 44b5d06583cd21ac237eb8f6263db03faf1726c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 22 Jan 2020 20:12:36 +0100 Subject: [PATCH] X11: Add support for XIM callbacks This adds support for the XIM instantiate and destroy callbacks, letting GLFW detect both when the current input method disappears and when a new one is started. Tested with ibus. --- README.md | 2 ++ src/x11_init.c | 59 +++++++++++++++++++++++++++++++++++------- src/x11_platform.h | 10 ++++++++ src/x11_window.c | 64 +++++++++++++++++++++++++++++++--------------- 4 files changed, 105 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index f63f3efd..3977f0ff 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,8 @@ information on what to include when reporting a bug. - [X11] Bugfix: `glfwFocusWindow` could terminate on older WMs or without a WM - [X11] Bugfix: Querying a disconnected monitor could segfault (#1602) - [X11] Bugfix: IME input of CJK was broken for "C" locale (#1587,#1636) + - [X11] Bugfix: Termination would segfault if the IM had been destroyed + - [X11] Bugfix: Any IM started after initialization would not be detected - [Wayland] Removed support for `wl_shell` (#1443) - [Wayland] Bugfix: The `GLFW_HAND_CURSOR` shape used the wrong image (#1432) - [Wayland] Bugfix: `CLOCK_MONOTONIC` was not correctly enabled diff --git a/src/x11_init.c b/src/x11_init.c index 2ce7b4ef..d640a8fe 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -349,6 +349,40 @@ static GLFWbool hasUsableInputMethodStyle(void) return found; } +static void inputMethodDestroyCallback(XIM im, XPointer clientData, XPointer callData) +{ + _glfw.x11.im = NULL; +} + +static void inputMethodInstantiateCallback(Display* display, + XPointer clientData, + XPointer callData) +{ + if (_glfw.x11.im) + return; + + _glfw.x11.im = XOpenIM(_glfw.x11.display, 0, NULL, NULL); + if (_glfw.x11.im) + { + if (!hasUsableInputMethodStyle()) + { + XCloseIM(_glfw.x11.im); + _glfw.x11.im = NULL; + } + } + + if (_glfw.x11.im) + { + XIMCallback callback; + callback.callback = (XIMProc) inputMethodDestroyCallback; + callback.client_data = NULL; + XSetIMValues(_glfw.x11.im, XNDestroyCallback, &callback, NULL); + + for (_GLFWwindow* window = _glfw.windowListHead; window; window = window->next) + _glfwCreateInputContextX11(window); + } +} + // Check whether the specified atom is supported // static Atom getSupportedAtom(Atom* supportedAtoms, @@ -1066,6 +1100,8 @@ int _glfwPlatformInit(void) _glfw_dlsym(_glfw.x11.xlib.handle, "XQueryPointer"); _glfw.x11.xlib.RaiseWindow = (PFN_XRaiseWindow) _glfw_dlsym(_glfw.x11.xlib.handle, "XRaiseWindow"); + _glfw.x11.xlib.RegisterIMInstantiateCallback = (PFN_XRegisterIMInstantiateCallback) + _glfw_dlsym(_glfw.x11.xlib.handle, "XRegisterIMInstantiateCallback"); _glfw.x11.xlib.ResizeWindow = (PFN_XResizeWindow) _glfw_dlsym(_glfw.x11.xlib.handle, "XResizeWindow"); _glfw.x11.xlib.ResourceManagerString = (PFN_XResourceManagerString) @@ -1082,6 +1118,8 @@ int _glfwPlatformInit(void) _glfw_dlsym(_glfw.x11.xlib.handle, "XSetErrorHandler"); _glfw.x11.xlib.SetICFocus = (PFN_XSetICFocus) _glfw_dlsym(_glfw.x11.xlib.handle, "XSetICFocus"); + _glfw.x11.xlib.SetIMValues = (PFN_XSetIMValues) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSetIMValues"); _glfw.x11.xlib.SetInputFocus = (PFN_XSetInputFocus) _glfw_dlsym(_glfw.x11.xlib.handle, "XSetInputFocus"); _glfw.x11.xlib.SetLocaleModifiers = (PFN_XSetLocaleModifiers) @@ -1142,6 +1180,8 @@ int _glfwPlatformInit(void) _glfw_dlsym(_glfw.x11.xlib.handle, "XrmInitialize"); _glfw.x11.xrm.UniqueQuark = (PFN_XrmUniqueQuark) _glfw_dlsym(_glfw.x11.xlib.handle, "XrmUniqueQuark"); + _glfw.x11.xlib.UnregisterIMInstantiateCallback = (PFN_XUnregisterIMInstantiateCallback) + _glfw_dlsym(_glfw.x11.xlib.handle, "XUnregisterIMInstantiateCallback"); _glfw.x11.xlib.utf8LookupString = (PFN_Xutf8LookupString) _glfw_dlsym(_glfw.x11.xlib.handle, "Xutf8LookupString"); _glfw.x11.xlib.utf8SetWMProperties = (PFN_Xutf8SetWMProperties) @@ -1184,15 +1224,11 @@ int _glfwPlatformInit(void) { XSetLocaleModifiers(""); - _glfw.x11.im = XOpenIM(_glfw.x11.display, 0, NULL, NULL); - if (_glfw.x11.im) - { - if (!hasUsableInputMethodStyle()) - { - XCloseIM(_glfw.x11.im); - _glfw.x11.im = NULL; - } - } + // If an IM is already present our callback will be called right away + XRegisterIMInstantiateCallback(_glfw.x11.display, + NULL, NULL, NULL, + inputMethodInstantiateCallback, + NULL); } #if defined(__linux__) @@ -1229,6 +1265,11 @@ void _glfwPlatformTerminate(void) free(_glfw.x11.primarySelectionString); free(_glfw.x11.clipboardString); + XUnregisterIMInstantiateCallback(_glfw.x11.display, + NULL, NULL, NULL, + inputMethodInstantiateCallback, + NULL); + if (_glfw.x11.im) { XCloseIM(_glfw.x11.im); diff --git a/src/x11_platform.h b/src/x11_platform.h index d6b345dd..43a0ce19 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -104,6 +104,7 @@ typedef int (* PFN_XPending)(Display*); typedef Bool (* PFN_XQueryExtension)(Display*,const char*,int*,int*,int*); typedef Bool (* PFN_XQueryPointer)(Display*,Window,Window*,Window*,int*,int*,int*,int*,unsigned int*); typedef int (* PFN_XRaiseWindow)(Display*,Window); +typedef Bool (* PFN_XRegisterIMInstantiateCallback)(Display*,void*,char*,char*,XIDProc,XPointer); typedef int (* PFN_XResizeWindow)(Display*,Window,unsigned int,unsigned int); typedef char* (* PFN_XResourceManagerString)(Display*); typedef int (* PFN_XSaveContext)(Display*,XID,XContext,const char*); @@ -112,6 +113,7 @@ typedef Status (* PFN_XSendEvent)(Display*,Window,Bool,long,XEvent*); typedef int (* PFN_XSetClassHint)(Display*,Window,XClassHint*); typedef XErrorHandler (* PFN_XSetErrorHandler)(XErrorHandler); typedef void (* PFN_XSetICFocus)(XIC); +typedef char* (* PFN_XSetIMValues)(XIM,...); typedef int (* PFN_XSetInputFocus)(Display*,Window,int,Time); typedef char* (* PFN_XSetLocaleModifiers)(const char*); typedef int (* PFN_XSetScreenSaver)(Display*,int,int,int,int); @@ -142,6 +144,7 @@ typedef Bool (* PFN_XrmGetResource)(XrmDatabase,const char*,const char*,char**,X typedef XrmDatabase (* PFN_XrmGetStringDatabase)(const char*); typedef void (* PFN_XrmInitialize)(void); typedef XrmQuark (* PFN_XrmUniqueQuark)(void); +typedef Bool (* PFN_XUnregisterIMInstantiateCallback)(Display*,void*,char*,char*,XIDProc,XPointer); typedef int (* PFN_Xutf8LookupString)(XIC,XKeyPressedEvent*,char*,int,KeySym*,Status*); typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char*,char**,int,XSizeHints*,XWMHints*,XClassHint*); #define XAllocClassHint _glfw.x11.xlib.AllocClassHint @@ -200,6 +203,7 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char #define XQueryExtension _glfw.x11.xlib.QueryExtension #define XQueryPointer _glfw.x11.xlib.QueryPointer #define XRaiseWindow _glfw.x11.xlib.RaiseWindow +#define XRegisterIMInstantiateCallback _glfw.x11.xlib.RegisterIMInstantiateCallback #define XResizeWindow _glfw.x11.xlib.ResizeWindow #define XResourceManagerString _glfw.x11.xlib.ResourceManagerString #define XSaveContext _glfw.x11.xlib.SaveContext @@ -208,6 +212,7 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char #define XSetClassHint _glfw.x11.xlib.SetClassHint #define XSetErrorHandler _glfw.x11.xlib.SetErrorHandler #define XSetICFocus _glfw.x11.xlib.SetICFocus +#define XSetIMValues _glfw.x11.xlib.SetIMValues #define XSetInputFocus _glfw.x11.xlib.SetInputFocus #define XSetLocaleModifiers _glfw.x11.xlib.SetLocaleModifiers #define XSetScreenSaver _glfw.x11.xlib.SetScreenSaver @@ -238,6 +243,7 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char #define XrmGetStringDatabase _glfw.x11.xrm.GetStringDatabase #define XrmInitialize _glfw.x11.xrm.Initialize #define XrmUniqueQuark _glfw.x11.xrm.UniqueQuark +#define XUnregisterIMInstantiateCallback _glfw.x11.xlib.UnregisterIMInstantiateCallback #define Xutf8LookupString _glfw.x11.xlib.utf8LookupString #define Xutf8SetWMProperties _glfw.x11.xlib.utf8SetWMProperties @@ -553,6 +559,7 @@ typedef struct _GLFWlibraryX11 PFN_XQueryExtension QueryExtension; PFN_XQueryPointer QueryPointer; PFN_XRaiseWindow RaiseWindow; + PFN_XRegisterIMInstantiateCallback RegisterIMInstantiateCallback; PFN_XResizeWindow ResizeWindow; PFN_XResourceManagerString ResourceManagerString; PFN_XSaveContext SaveContext; @@ -561,6 +568,7 @@ typedef struct _GLFWlibraryX11 PFN_XSetClassHint SetClassHint; PFN_XSetErrorHandler SetErrorHandler; PFN_XSetICFocus SetICFocus; + PFN_XSetIMValues SetIMValues; PFN_XSetInputFocus SetInputFocus; PFN_XSetLocaleModifiers SetLocaleModifiers; PFN_XSetScreenSaver SetScreenSaver; @@ -577,6 +585,7 @@ typedef struct _GLFWlibraryX11 PFN_XUnsetICFocus UnsetICFocus; PFN_XVisualIDFromVisual VisualIDFromVisual; PFN_XWarpPointer WarpPointer; + PFN_XUnregisterIMInstantiateCallback UnregisterIMInstantiateCallback; PFN_Xutf8LookupString utf8LookupString; PFN_Xutf8SetWMProperties utf8SetWMProperties; } xlib; @@ -753,4 +762,5 @@ void _glfwReleaseErrorHandlerX11(void); void _glfwInputErrorX11(int error, const char* message); void _glfwPushSelectionToManagerX11(void); +void _glfwCreateInputContextX11(_GLFWwindow* window); diff --git a/src/x11_window.c b/src/x11_window.c index 48913d07..7eda5b8b 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -590,6 +590,14 @@ static void enableCursor(_GLFWwindow* window) updateCursorImage(window); } +// Clear its handle when the input context has been destroyed +// +static void inputContextDestroyCallback(XIC ic, XPointer clientData, XPointer callData) +{ + _GLFWwindow* window = (_GLFWwindow*) clientData; + window->x11.ic = NULL; +} + // Create the X11 window (and its colormap) // static GLFWbool createNativeWindow(_GLFWwindow* window, @@ -768,27 +776,10 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, PropModeReplace, (unsigned char*) &version, 1); } - _glfwPlatformSetWindowTitle(window, wndconfig->title); - if (_glfw.x11.im) - { - window->x11.ic = XCreateIC(_glfw.x11.im, - XNInputStyle, - XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, - window->x11.handle, - XNFocusWindow, - window->x11.handle, - NULL); - } - - if (window->x11.ic) - { - unsigned long filter = 0; - if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL) - XSelectInput(_glfw.x11.display, window->x11.handle, wa.event_mask | filter); - } + _glfwCreateInputContextX11(window); + _glfwPlatformSetWindowTitle(window, wndconfig->title); _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos); _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height); @@ -1173,8 +1164,7 @@ static void processEvent(XEvent *event) if (event->type == KeyPress || event->type == KeyRelease) keycode = event->xkey.keycode; - if (_glfw.x11.im) - filtered = XFilterEvent(event, None); + filtered = XFilterEvent(event, None); if (_glfw.x11.randr.available) { @@ -1961,6 +1951,38 @@ void _glfwPushSelectionToManagerX11(void) } } +void _glfwCreateInputContextX11(_GLFWwindow* window) +{ + XIMCallback callback; + callback.callback = (XIMProc) inputContextDestroyCallback; + callback.client_data = (XPointer) window; + + window->x11.ic = XCreateIC(_glfw.x11.im, + XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, + window->x11.handle, + XNFocusWindow, + window->x11.handle, + XNDestroyCallback, + &callback, + NULL); + + if (window->x11.ic) + { + XWindowAttributes attribs; + XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs); + + unsigned long filter = 0; + if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL) + { + XSelectInput(_glfw.x11.display, + window->x11.handle, + attribs.your_event_mask | filter); + } + } +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API //////