Просмотр исходного кода

x11: Register IM destruction/instantiation callbacks

Register callbacks to listen for IM destruction and instantiation to handle the IME shutting down and restarting during runtime.
Frank Praznik 1 неделя назад
Родитель
Сommit
7e8a850246
3 измененных файлов с 100 добавлено и 31 удалено
  1. 97 25
      src/video/x11/SDL_x11keyboard.c
  2. 3 0
      src/video/x11/SDL_x11sym.h
  3. 0 6
      src/video/x11/SDL_x11video.c

+ 97 - 25
src/video/x11/SDL_x11keyboard.c

@@ -107,6 +107,84 @@ static SDL_Scancode X11_KeyCodeToSDLScancode(SDL_VideoDevice *_this, KeyCode key
     return SDL_GetScancodeFromKeySym(keysym, keycode);
     return SDL_GetScancodeFromKeySym(keysym, keycode);
 }
 }
 
 
+#ifdef X_HAVE_UTF8_STRING
+static bool X11_InitIM(SDL_VideoData *videodata);
+
+static void im_instantiate_handler(Display *display, XPointer clientData, XPointer callData)
+{
+    SDL_VideoData *videodata = (SDL_VideoData *)clientData;
+
+    if (X11_InitIM(videodata)) {
+        X11_XUnregisterIMInstantiateCallback(videodata->display, NULL, NULL, NULL, im_instantiate_handler, clientData);
+    }
+}
+
+static void im_destroyed_handler(XIM im, XPointer clientData, XPointer callData)
+{
+    SDL_VideoData *videodata = (SDL_VideoData *)clientData;
+
+    videodata->im = NULL;
+    X11_XRegisterIMInstantiateCallback(videodata->display, NULL, NULL, NULL, im_instantiate_handler, clientData);
+}
+
+static bool X11_InitIM(SDL_VideoData *videodata)
+{
+    /* Set the locale, and call XSetLocaleModifiers before XOpenIM so that
+     * Compose keys will work correctly.
+     */
+    char *prev_locale = setlocale(LC_ALL, NULL);
+    char *prev_xmods = X11_XSetLocaleModifiers(NULL);
+
+    if (prev_locale) {
+        prev_locale = SDL_strdup(prev_locale);
+    }
+
+    if (prev_xmods) {
+        prev_xmods = SDL_strdup(prev_xmods);
+    }
+
+    (void)setlocale(LC_ALL, "");
+    X11_XSetLocaleModifiers("");
+
+    XIM im = X11_XOpenIM(videodata->display, NULL, NULL, NULL);
+
+    /* Reset the locale + X locale modifiers back to how they were,
+     * locale first because the X locale modifiers depend on it.
+     */
+    (void)setlocale(LC_ALL, prev_locale);
+    X11_XSetLocaleModifiers(prev_xmods);
+
+    SDL_free(prev_locale);
+    SDL_free(prev_xmods);
+
+    if (im) {
+        videodata->im = im;
+
+        XIMCallback destroyed_callback;
+        destroyed_callback.client_data = (XPointer)videodata;
+        destroyed_callback.callback = (XIMProc)im_destroyed_handler;
+
+        X11_XSetIMValues(im, XNDestroyCallback, &destroyed_callback, NULL);
+
+        /* Attach the new IC to any existing windows.
+         *
+         * If we wound up here before any old input contexts were destroyed,
+         * they will be recreated in the IC destroyed callback.
+         */
+        for (int i = 0; i < videodata->numwindows; ++i) {
+            SDL_WindowData *w = videodata->windowlist[i];
+            if (!w->ic) {
+                X11_CreateInputContext(w);
+            }
+        }
+
+        return true;
+    }
+
+    return false;
+}
+#endif // X_HAVE_UTF8_STRING
+
 bool X11_InitKeyboard(SDL_VideoDevice *_this)
 bool X11_InitKeyboard(SDL_VideoDevice *_this)
 {
 {
     SDL_VideoData *data = _this->internal;
     SDL_VideoData *data = _this->internal;
@@ -160,32 +238,9 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this)
     // Open a connection to the X input manager
     // Open a connection to the X input manager
 #ifdef X_HAVE_UTF8_STRING
 #ifdef X_HAVE_UTF8_STRING
     if (SDL_X11_HAVE_UTF8) {
     if (SDL_X11_HAVE_UTF8) {
-        /* Set the locale, and call XSetLocaleModifiers before XOpenIM so that
-           Compose keys will work correctly. */
-        char *prev_locale = setlocale(LC_ALL, NULL);
-        char *prev_xmods = X11_XSetLocaleModifiers(NULL);
-
-        if (prev_locale) {
-            prev_locale = SDL_strdup(prev_locale);
+        if (!X11_InitIM(data)) {
+            X11_XRegisterIMInstantiateCallback(data->display, NULL, NULL, NULL, im_instantiate_handler, (XPointer)data);
         }
         }
-
-        if (prev_xmods) {
-            prev_xmods = SDL_strdup(prev_xmods);
-        }
-
-        (void)setlocale(LC_ALL, "");
-        X11_XSetLocaleModifiers("");
-
-        data->im = X11_XOpenIM(data->display, NULL, NULL, NULL);
-
-        /* Reset the locale + X locale modifiers back to how they were,
-           locale first because the X locale modifiers depend on it. */
-        (void)setlocale(LC_ALL, prev_locale);
-        X11_XSetLocaleModifiers(prev_xmods);
-
-        SDL_free(prev_locale);
-
-        SDL_free(prev_xmods);
     }
     }
 #endif
 #endif
     // Try to determine which scancodes are being used based on fingerprint
     // Try to determine which scancodes are being used based on fingerprint
@@ -522,6 +577,15 @@ void X11_QuitKeyboard(SDL_VideoDevice *_this)
 {
 {
     SDL_VideoData *data = _this->internal;
     SDL_VideoData *data = _this->internal;
 
 
+#ifdef X_HAVE_UTF8_STRING
+    if (data->im) {
+        X11_XCloseIM(data->im);
+        data->im = NULL;
+    } else {
+        X11_XUnregisterIMInstantiateCallback(data->display, NULL, NULL, NULL, im_instantiate_handler, (XPointer)data);
+    }
+#endif // X_HAVE_UTF8_STRING
+
 #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
 #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
     if (data->keyboard.xkb_enabled) {
     if (data->keyboard.xkb_enabled) {
         for (int i = 0; i < XkbNumKbdGroups; ++i) {
         for (int i = 0; i < XkbNumKbdGroups; ++i) {
@@ -584,6 +648,7 @@ static void X11_SendEditingEvent(SDL_WindowData *data)
     data->ime_needs_clear_composition = true;
     data->ime_needs_clear_composition = true;
 }
 }
 
 
+#ifdef X_HAVE_UTF8_STRING
 static int preedit_start_callback(XIC xic, XPointer client_data, XPointer call_data)
 static int preedit_start_callback(XIC xic, XPointer client_data, XPointer call_data)
 {
 {
     // No limit on preedit text length
     // No limit on preedit text length
@@ -716,7 +781,14 @@ static void ic_destroyed_handler(XIC ic, XPointer clientData, XPointer callData)
 {
 {
     SDL_WindowData *data = (SDL_WindowData *)clientData;
     SDL_WindowData *data = (SDL_WindowData *)clientData;
     data->ic = NULL;
     data->ic = NULL;
+    X11_DestroyInputContext(data);
+
+    // If the IM was already recreated, create a new IC.
+    if (data->videodata->im) {
+        X11_CreateInputContext(data);
+    }
 }
 }
+#endif // X_HAVE_UTF8_STRING
 
 
 void X11_CreateInputContext(SDL_WindowData *data)
 void X11_CreateInputContext(SDL_WindowData *data)
 {
 {

+ 3 - 0
src/video/x11/SDL_x11sym.h

@@ -231,6 +231,9 @@ SDL_X11_SYM(void,XSetICFocus,(XIC a))
 SDL_X11_SYM(void,XUnsetICFocus,(XIC a))
 SDL_X11_SYM(void,XUnsetICFocus,(XIC a))
 SDL_X11_SYM(XIM,XOpenIM,(Display* a,struct _XrmHashBucketRec* b,char* c,char* d))
 SDL_X11_SYM(XIM,XOpenIM,(Display* a,struct _XrmHashBucketRec* b,char* c,char* d))
 SDL_X11_SYM(Status,XCloseIM,(XIM a))
 SDL_X11_SYM(Status,XCloseIM,(XIM a))
+SDL_X11_SYM(char*, XSetIMValues,(XIM,...))
+SDL_X11_SYM(Bool,XRegisterIMInstantiateCallback,(Display*,struct _XrmHashBucketRec*,char*,char*,XIDProc,XPointer))
+SDL_X11_SYM(Bool,XUnregisterIMInstantiateCallback,(Display*,struct _XrmHashBucketRec*,char*,char*,XIDProc,XPointer))
 SDL_X11_SYM(void,Xutf8DrawString,(Display *a, Drawable b, XFontSet c, GC d, int e, int f, _Xconst char *g, int h))
 SDL_X11_SYM(void,Xutf8DrawString,(Display *a, Drawable b, XFontSet c, GC d, int e, int f, _Xconst char *g, int h))
 SDL_X11_SYM(int,Xutf8TextExtents,(XFontSet a, _Xconst char* b, int c, XRectangle* d, XRectangle* e))
 SDL_X11_SYM(int,Xutf8TextExtents,(XFontSet a, _Xconst char* b, int c, XRectangle* d, XRectangle* e))
 SDL_X11_SYM(char*,XSetLocaleModifiers,(const char *a))
 SDL_X11_SYM(char*,XSetLocaleModifiers,(const char *a))

+ 0 - 6
src/video/x11/SDL_x11video.c

@@ -489,12 +489,6 @@ void X11_VideoQuit(SDL_VideoDevice *_this)
         X11_XDestroyWindow(data->display, data->xsettings_window);
         X11_XDestroyWindow(data->display, data->xsettings_window);
     }
     }
 
 
-#ifdef X_HAVE_UTF8_STRING
-    if (data->im) {
-        X11_XCloseIM(data->im);
-    }
-#endif
-
     X11_QuitXinput2(_this);
     X11_QuitXinput2(_this);
     X11_QuitModes(_this);
     X11_QuitModes(_this);
     X11_QuitKeyboard(_this);
     X11_QuitKeyboard(_this);