gtk/gdk/win32/gdkwin32langnotification.c
Руслан Ижбулатов 64ab82c403 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.
2019-03-22 16:58:59 +00:00

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 ();
}