forked from AuroraMiddleware/gtk
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 ();
|
|
}
|