 3b244c9f11
			
		
	
	3b244c9f11
	
	
	
		
			
			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.
		
			
				
	
	
		
			173 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			173 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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 ();
 | |
| }
 |