spiegel-keyman/windows/src/engine/kmtip/kmtip.cpp
Marc Durdin 738e1946a6 [Windows] Debug logging and proof of concept tests for keyboarding support within metro-style apps
[Windows] More tidyup and robustness for metro app support - debug cleanup and serialization of input (not quite finished)

[windows] Refactor serialized input code when used with key event thread model

[windows] Add consistent precompiled headers for other projects

[windows] Merge console window test into metro support

[Windows] Tidy up work and identify additional TODOs for metro-style app support

[Windows] Ensure error case falls through to default hook processing for console windows

[Windows] Refactor shared memory into memory mapped file so we can cross 32-64 bit boundary

[Windows] Tweaks to C++ security calls and parameters

[Windows] Start refactor of SerialKeyEvent* classes

[Windows] Rename to SerialKeyEventServer (refactoring)

[Windows] Complete refactoring of SerialKeyEventClient class

[Windows] Further encapsulation and cleanup with 'interfaces' to reduce header pollution

[Windows] Complete serialization fix with move of modifier state management from client thread to server thread to guarantee consistency

[Windows] Replace atom-based keyboard switching with memory mapped file indexed to avoid security constraints

[Windows] Fixup Left Alt+Shift interaction with serializer

[Windows] Use Windows 8.1 SDK for test
2018-10-18 20:00:49 +11:00

283 lines
7 KiB
C++

/*
Name: kmtip
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Create Date: 7 Sep 2009
Modified Date: 28 Mar 2016
Authors: mcdurdin
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
History: 07 Sep 2009 - mcdurdin - I2095 - TSF addin is not threadsafe
20 Nov 2012 - mcdurdin - I3582 - V9.0 - Remove language bar integration
20 Nov 2012 - mcdurdin - I3581 - V9.0 - KMTip needs to pass activated profile guid through to Keyman32 to switch keyboards
28 Nov 2012 - mcdurdin - I3590 - V9.0 - Initialise keystroke sink after we know which keyboard to activate
28 Nov 2012 - mcdurdin - I3588 - V9.0 - Use preserved keys in TSF to handle key combinations in Keyman
01 Jan 2013 - mcdurdin - I3714 - V9.0 - Applications hang when switching kmtip off if Keyman not running
01 May 2014 - mcdurdin - I4216 - V9.0 - Keyman TIP should use ITfTextInputProcessorEx
28 May 2014 - mcdurdin - I3706 - V9.0 - If kmtip CKMTipTextService::Activate fails, should we release pThreadMgr?
16 Jun 2014 - mcdurdin - I4274 - V9.0 - kmtip does not work if already active before KM starts
*/
//
// kmtip.cpp
//
// IUnknown, ITfTextInputProcessor implementation.
//
#include "pch.h"
#include "kmtip.h"
//+---------------------------------------------------------------------------
//
// CreateInstance
//
//----------------------------------------------------------------------------
/* static */
HRESULT CKMTipTextService::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj)
{
CKMTipTextService *pKMTip;
HRESULT hr;
if (ppvObj == NULL)
return E_INVALIDARG;
*ppvObj = NULL;
if (NULL != pUnkOuter)
return CLASS_E_NOAGGREGATION;
if ((pKMTip = new CKMTipTextService) == NULL)
return E_OUTOFMEMORY;
hr = pKMTip->QueryInterface(riid, ppvObj);
pKMTip->Release(); // caller still holds ref if hr == S_OK
return hr;
}
//+---------------------------------------------------------------------------
//
// ctor
//
//----------------------------------------------------------------------------
__declspec(thread) CKMTipTextService *CKMTipTextService::ThreadThis;
CKMTipTextService::CKMTipTextService()
{
DllAddRef();
_pThreadMgr = NULL;
_tfClientId = TF_CLIENTID_NULL;
// I3582
_dwThreadMgrEventSinkCookie = TF_INVALID_COOKIE;
_cRef = 1;
ThreadThis = this;
}
//+---------------------------------------------------------------------------
//
// dtor
//
//----------------------------------------------------------------------------
CKMTipTextService::~CKMTipTextService()
{
ThreadThis = NULL;
DllRelease();
}
//+---------------------------------------------------------------------------
//
// QueryInterface
//
//----------------------------------------------------------------------------
STDAPI CKMTipTextService::QueryInterface(REFIID riid, void **ppvObj)
{
if (ppvObj == NULL)
return E_INVALIDARG;
*ppvObj = NULL;
if (IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_ITfTextInputProcessor))
{
*ppvObj = (ITfTextInputProcessor *)this;
}
else if (IsEqualIID(riid, IID_ITfThreadMgrEventSink))
{
*ppvObj = (ITfThreadMgrEventSink *)this;
}
else if (IsEqualIID(riid, IID_ITfActiveLanguageProfileNotifySink)) // I3581
{
*ppvObj = (ITfActiveLanguageProfileNotifySink *)this;
}
else if (IsEqualIID(riid, IID_ITfThreadFocusSink))
{
*ppvObj = (ITfThreadFocusSink *)this;
}
else if (IsEqualIID(riid, IID_ITfTextEditSink))
{
*ppvObj = (ITfTextEditSink *)this;
}
else if (IsEqualIID(riid, IID_ITfKeyEventSink))
{
*ppvObj = (ITfKeyEventSink *)this;
}
if (*ppvObj)
{
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
//+---------------------------------------------------------------------------
//
// AddRef
//
//----------------------------------------------------------------------------
STDAPI_(ULONG) CKMTipTextService::AddRef()
{
return ++_cRef;
}
//+---------------------------------------------------------------------------
//
// Release
//
//----------------------------------------------------------------------------
STDAPI_(ULONG) CKMTipTextService::Release()
{
LONG cr = --_cRef;
assert(_cRef >= 0);
if (_cRef == 0)
{
delete this;
}
return cr;
}
//+---------------------------------------------------------------------------
//
// Activate
//
//----------------------------------------------------------------------------
STDAPI CKMTipTextService::ActivateEx(ITfThreadMgr *ptim, TfClientId tid, DWORD dwFlags) { // I4216
Log(L"Activating text service ex");
ITfDocumentMgr *pFocusDoc;
_cPreservedKeyCount = 0; // I3588 // I3714
_PreservedKeys = NULL; // I3588 // I3714
_pThreadMgr = ptim;
_pThreadMgr->AddRef();
_tfClientId = tid;
if(!_InitThreadMgrSink()) {
Log(L"Could not initialise thread manager sink");
goto ExitError;
}
if(_LoadKeyman()) { // I3590 // I4274
if(!_InitKeyman()) {
Log(L"Could not initialise Keyman");
goto ExitError;
}
}
// start tracking the focus doc
if(_pThreadMgr->GetFocus(&pFocusDoc) == S_OK) {
// The system will call OnSetFocus only for focus events after Activate
// is called.
OnSetFocus(pFocusDoc, NULL);
pFocusDoc->Release();
}
return S_OK;
ExitError:
Deactivate(); // cleanup any half-finished init // I3706
return E_FAIL;
}
STDAPI CKMTipTextService::Activate(ITfThreadMgr *pThreadMgr, TfClientId tfClientId)
{
Log(L"Activating text service");
ITfDocumentMgr *pFocusDoc;
_cPreservedKeyCount = 0; // I3588 // I3714
_PreservedKeys = NULL; // I3588 // I3714
_pThreadMgr = pThreadMgr;
_pThreadMgr->AddRef();
_tfClientId = tfClientId;
if (!_InitThreadMgrSink())
{
Log(L"Could not initialise thread manager sink");
goto ExitError;
}
if(_LoadKeyman()) { // I4274
if(!_InitKeyman()) {
Log(L"Could not initialise Keyman");
goto ExitError;
}
}
// start tracking the focus doc
if (_pThreadMgr->GetFocus(&pFocusDoc) == S_OK)
{
// The system will call OnSetFocus only for focus events after Activate
// is called.
OnSetFocus(pFocusDoc, NULL);
pFocusDoc->Release();
}
return S_OK;
ExitError:
Deactivate(); // cleanup any half-finished init // I3706
return E_FAIL;
}
//+---------------------------------------------------------------------------
//
// Deactivate
//
//----------------------------------------------------------------------------
STDAPI CKMTipTextService::Deactivate()
{
_UninitThreadMgrSink();
_UninitKeystrokeSink();
_UninitKeyman();
// we MUST release all refs to _pThreadMgr in Deactivate
SafeReleaseClear(_pThreadMgr);
_tfClientId = TF_CLIENTID_NULL;
return S_OK;
}