[gtk/gtk-3-24: 1/3] GDK W32: Test for IME correctly



commit 3b244c9f11edb92e4196f6d0ba25e2c8b928a166
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Sat Jan 12 21:32:48 2019 +0000

    GDK W32: Test for IME correctly
    
    ImmIsIME() doesn't work (always returns TRUE) since Vista.
    Use ITfActiveLanguageProfileNotifySink to detect TSF changes,
    which are equal to IME changes for us.
    
    Also make sure that IMMultiContext re-loads the IM when keyboard layout
    changes, otherwise there's a subtle bug that could happen:
    * Run GTK application with non-IME layout (US, for example)
    * Focus on an editable widget (GtkEntry, for example)
    * IM Context is initialized to use the simple IM
    * Switch to an IME layout (such as Korean)
    * Start typing
    * Since IME module is not loaded yet, keypresses are handled
      by a default MS IME handler
    * Once IME commits a character, GDK will get a WM_KEYDOWN,
      which will trigger a GdkKeyEvent, which will be handled by
      an event filter in IM Context, which will finally re-evaluate
      its status and load IME, and only after that GTK will get
      to handle IME by itself - but by that point input would
      already be broken.
    To avoid this we can emit a dummy event (with Void keyval),
    which will cause IM Context to load the appropriate module
    immediately.

 gdk/win32/Makefile.am                |   2 +
 gdk/win32/gdkdisplay-win32.c         |   4 +
 gdk/win32/gdkevents-win32.c          |  17 +++-
 gdk/win32/gdkglobals-win32.c         |   2 +-
 gdk/win32/gdkmain-win32.c            |   1 -
 gdk/win32/gdkwin32langnotification.c | 172 +++++++++++++++++++++++++++++++++++
 gdk/win32/gdkwin32langnotification.h |  26 ++++++
 7 files changed, 221 insertions(+), 3 deletions(-)
---
diff --git a/gdk/win32/Makefile.am b/gdk/win32/Makefile.am
index 0504428580..e77ec652f2 100644
--- a/gdk/win32/Makefile.am
+++ b/gdk/win32/Makefile.am
@@ -66,6 +66,8 @@ libgdk_win32_la_SOURCES = \
        gdkwin32dnd.h \
        gdkwin32dnd-private.h \
        gdkwin32glcontext.h \
+       gdkwin32langnotification.c \
+       gdkwin32langnotification.h \
        gdkwin32.h \
        gdkwin32id.c \
        gdkwin32keys.h \
diff --git a/gdk/win32/gdkdisplay-win32.c b/gdk/win32/gdkdisplay-win32.c
index 4f095019b7..eabf139393 100644
--- a/gdk/win32/gdkdisplay-win32.c
+++ b/gdk/win32/gdkdisplay-win32.c
@@ -35,6 +35,8 @@
 # include <epoxy/egl.h>
 #endif
 
+#include "gdkwin32langnotification.h"
+
 static int debug_indent = 0;
 
 static GdkMonitor *
@@ -414,6 +416,7 @@ _gdk_win32_display_open (const gchar *display_name)
                                                "display", _gdk_display,
                                                NULL);
 
+  _gdk_win32_lang_notification_init ();
   _gdk_dnd_init ();
 
   /* Precalculate display name */
@@ -869,6 +872,7 @@ gdk_win32_display_finalize (GObject *object)
 
   _gdk_win32_display_finalize_cursors (display_win32);
   _gdk_win32_dnd_exit ();
+  _gdk_win32_lang_notification_exit ();
 
   g_ptr_array_free (display_win32->monitors, TRUE);
 
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index 7c415377e5..7aef277c27 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -2464,7 +2464,6 @@ gdk_event_translate (MSG  *msg,
     case WM_INPUTLANGCHANGE:
       _gdk_input_locale = (HKL) msg->lParam;
       _gdk_win32_keymap_set_active_layout (GDK_WIN32_KEYMAP (_gdk_win32_display_get_keymap (_gdk_display)), 
_gdk_input_locale);
-      _gdk_input_locale_is_ime = ImmIsIME (_gdk_input_locale);
       GetLocaleInfo (MAKELCID (LOWORD (_gdk_input_locale), SORT_DEFAULT),
                     LOCALE_IDEFAULTANSICODEPAGE,
                     buf, sizeof (buf));
@@ -2476,6 +2475,22 @@ gdk_event_translate (MSG  *msg,
                         (gpointer) msg->lParam, _gdk_input_locale_is_ime ? " (IME)" : "",
                         _gdk_input_codepage));
       gdk_settings_notify (window, "gtk-im-module", GDK_SETTING_ACTION_CHANGED);
+
+      /* Generate a dummy key event to "nudge" IMContext */
+      event = gdk_event_new (GDK_KEY_PRESS);
+      event->key.window = window;
+      event->key.time = _gdk_win32_get_next_tick (msg->time);
+      event->key.keyval = GDK_KEY_VoidSymbol;
+      event->key.string = NULL;
+      event->key.length = 0;
+      event->key.hardware_keycode = 0;
+      gdk_event_set_scancode (event, 0);
+      gdk_event_set_device (event, device_manager_win32->core_keyboard);
+      gdk_event_set_source_device (event, device_manager_win32->system_keyboard);
+      gdk_event_set_seat (event, gdk_device_get_seat (device_manager_win32->core_keyboard));
+      event->key.is_modifier = FALSE;
+      event->key.state = 0;
+      _gdk_win32_append_event (event);
       break;
 
     case WM_SYSKEYUP:
diff --git a/gdk/win32/gdkglobals-win32.c b/gdk/win32/gdkglobals-win32.c
index 8ba5bf97db..78a43782f3 100644
--- a/gdk/win32/gdkglobals-win32.c
+++ b/gdk/win32/gdkglobals-win32.c
@@ -38,7 +38,7 @@ HINSTANCE       _gdk_app_hmodule;
 gint             _gdk_input_ignore_core;
 
 HKL              _gdk_input_locale;
-gboolean         _gdk_input_locale_is_ime;
+gboolean         _gdk_input_locale_is_ime = FALSE;
 UINT             _gdk_input_codepage;
 
 gint             _gdk_input_ignore_wintab = FALSE;
diff --git a/gdk/win32/gdkmain-win32.c b/gdk/win32/gdkmain-win32.c
index 392c42a5fc..3c03e5f5db 100644
--- a/gdk/win32/gdkmain-win32.c
+++ b/gdk/win32/gdkmain-win32.c
@@ -93,7 +93,6 @@ _gdk_win32_windowing_init (void)
   _gdk_display_hdc = CreateDC ("DISPLAY", NULL, NULL, NULL);
   _gdk_input_locale = GetKeyboardLayout (0);
   _gdk_win32_keymap_set_active_layout (GDK_WIN32_KEYMAP (_gdk_win32_display_get_keymap (_gdk_display)), 
_gdk_input_locale);
-  _gdk_input_locale_is_ime = ImmIsIME (_gdk_input_locale);
   GetLocaleInfo (MAKELCID (LOWORD (_gdk_input_locale), SORT_DEFAULT),
                 LOCALE_IDEFAULTANSICODEPAGE,
                 buf, sizeof (buf));
diff --git a/gdk/win32/gdkwin32langnotification.c b/gdk/win32/gdkwin32langnotification.c
new file mode 100644
index 0000000000..94814ecd6a
--- /dev/null
+++ b/gdk/win32/gdkwin32langnotification.c
@@ -0,0 +1,172 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2019 Руслан Ижбулатов <lrn1986 gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#define COBJMACROS
+#include <msctf.h>
+
+#include <gdk/gdk.h>
+
+#include "gdkprivate-win32.h"
+
+struct _GdkWin32ALPNSink
+{
+  ITfActiveLanguageProfileNotifySink itf_alpn_sink;
+
+  gint ref_count;
+};
+
+typedef struct _GdkWin32ALPNSink GdkWin32ALPNSink;
+
+static GdkWin32ALPNSink *actlangchangenotify = NULL;
+static ITfSource *itf_source = NULL;
+static DWORD actlangchangenotify_id = 0;
+
+static ULONG STDMETHODCALLTYPE
+alpn_sink_addref (ITfActiveLanguageProfileNotifySink *This)
+{
+  GdkWin32ALPNSink *alpn_sink = (GdkWin32ALPNSink *) This;
+  int ref_count = ++alpn_sink->ref_count;
+
+  return ref_count;
+}
+
+static HRESULT STDMETHODCALLTYPE
+alpn_sink_queryinterface (ITfActiveLanguageProfileNotifySink *This,
+                          REFIID                              riid,
+                          LPVOID                             *ppvObject)
+{
+  *ppvObject = NULL;
+
+  if (IsEqualGUID (riid, &IID_IUnknown))
+    {
+      ITfActiveLanguageProfileNotifySink_AddRef (This);
+      *ppvObject = This;
+      return S_OK;
+    }
+
+  if (IsEqualGUID (riid, &IID_ITfActiveLanguageProfileNotifySink))
+    {
+      ITfActiveLanguageProfileNotifySink_AddRef (This);
+      *ppvObject = This;
+      return S_OK;
+    }
+
+  return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE
+alpn_sink_release (ITfActiveLanguageProfileNotifySink *This)
+{
+  GdkWin32ALPNSink *alpn_sink = (GdkWin32ALPNSink *) This;
+  int ref_count = --alpn_sink->ref_count;
+
+  if (ref_count == 0)
+    {
+      g_free (This);
+    }
+
+  return ref_count;
+}
+
+static HRESULT STDMETHODCALLTYPE
+alpn_sink_on_activated (ITfActiveLanguageProfileNotifySink *This,
+                        REFCLSID                            clsid,
+                        REFGUID                             guidProfile,
+                        BOOL                                fActivated)
+{
+  _gdk_input_locale_is_ime = fActivated;
+  return S_OK;
+}
+
+static ITfActiveLanguageProfileNotifySinkVtbl alpn_sink_vtbl = {
+  alpn_sink_queryinterface,
+  alpn_sink_addref,
+  alpn_sink_release,
+  alpn_sink_on_activated,
+};
+
+static GdkWin32ALPNSink *
+alpn_sink_new ()
+{
+  GdkWin32ALPNSink *result;
+
+  result = g_new0 (GdkWin32ALPNSink, 1);
+  result->itf_alpn_sink.lpVtbl = &alpn_sink_vtbl;
+  result->ref_count = 0;
+
+  ITfActiveLanguageProfileNotifySink_AddRef (&result->itf_alpn_sink);
+
+  return result;
+}
+
+
+void
+_gdk_win32_lang_notification_init ()
+{
+  HRESULT hr;
+  ITfThreadMgr *itf_threadmgr;
+
+  CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
+
+  if (actlangchangenotify != NULL)
+    return;
+
+  hr = CoCreateInstance (&CLSID_TF_ThreadMgr,
+                         NULL,
+                         CLSCTX_INPROC_SERVER,
+                         &IID_ITfThreadMgr,
+                         (LPVOID *) &itf_threadmgr);
+
+  if (!SUCCEEDED (hr))
+    return;
+
+  hr = ITfThreadMgr_QueryInterface (itf_threadmgr, &IID_ITfSource, (VOID **) &itf_source);
+  ITfThreadMgr_Release (itf_threadmgr);
+
+  if (!SUCCEEDED (hr))
+    return;
+
+  actlangchangenotify = alpn_sink_new ();
+
+  hr = ITfSource_AdviseSink (itf_source,
+                             &IID_ITfActiveLanguageProfileNotifySink,
+                             (IUnknown *) actlangchangenotify,
+                             &actlangchangenotify_id);
+
+  if (!SUCCEEDED (hr))
+    {
+      ITfActiveLanguageProfileNotifySink_Release (&actlangchangenotify->itf_alpn_sink);
+      actlangchangenotify = NULL;
+      ITfSource_Release (itf_source);
+      itf_source = NULL;
+    }
+}
+
+void
+_gdk_win32_lang_notification_exit ()
+{
+  if (actlangchangenotify != NULL && itf_source != NULL)
+    {
+      ITfSource_UnadviseSink (itf_source, actlangchangenotify_id);
+      ITfSource_Release (itf_source);
+      ITfActiveLanguageProfileNotifySink_Release (&actlangchangenotify->itf_alpn_sink);
+    }
+
+  CoUninitialize ();
+}
diff --git a/gdk/win32/gdkwin32langnotification.h b/gdk/win32/gdkwin32langnotification.h
new file mode 100644
index 0000000000..c67e18fc0e
--- /dev/null
+++ b/gdk/win32/gdkwin32langnotification.h
@@ -0,0 +1,26 @@
+/*
+ * gdkwin32langnotification.h
+ *
+ * Copyright 2019 Руслан Ижбулатов <lrn1986 gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_WIN32_LANGNOTIFICATION_H__
+#define __GDK_WIN32_LANGNOTIFICATION_H__
+
+void _gdk_win32_lang_notification_init (void);
+void _gdk_win32_lang_notification_exit (void);
+
+#endif
\ No newline at end of file


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]