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.
This commit is contained in:
Camilla Löwy 2020-01-22 20:12:36 +01:00
parent 350ba73267
commit 44b5d06583
4 changed files with 105 additions and 30 deletions

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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 //////