spiegel-keyman/windows/src/engine/kmtip/kmtip.cpp
Marc Durdin 097ce9ac9a fix(windows): avoid disabling Keyman when speech recognition starts
Fixes #4965.

When speech recognition is enabled, it sometimes activates after a
Keyman keyboard is selected. This would cause Keyman to think it is
being deactivated. We needed to check in
`CKMTipTextService::OnActivated` that it was a keyboard-type TIP that
was being activated, and only deactivate the Keyman hooks in that
situation.

This commit also adds a little logging helper function, and removes an
unused variable `guidActiveProfile`.
2021-04-30 09:04:12 +10:00

293 lines
7.3 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"
#include "registryw.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;
_keystrokeSinkInitialized = FALSE;
_dwActiveLanguageProfileNotifySinkCookie = 0;
_PreservedKeys = NULL;
_cPreservedKeyCount = 0;
_dwDeepIntegration = 0;
RegistryReadOnlyW reg(HKEY_CURRENT_USER);
_tryAndStartKeyman = reg.OpenKeyReadOnly(REGSZ_KeymanEngineDebug_CU) && reg.ValueExists(REGSZ_Flag_UseAutoStartTask) && reg.ReadInteger(REGSZ_Flag_UseAutoStartTask) != 0;
_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)
{
Log(L"CKMTipTextService::Release --> deleting");
delete this;
}
return cr;
}
//+---------------------------------------------------------------------------
//
// Activate
//
//----------------------------------------------------------------------------
STDAPI CKMTipTextService::ActivateEx(ITfThreadMgr *ptim, TfClientId tid, DWORD dwFlags) { // I4216
LogEnter();
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(!_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)
{
LogEnter();
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(!_InitKeyman()) {
_TryAndStartKeyman();
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()
{
LogEnter();
_UninitThreadMgrSink();
_UninitKeystrokeSink();
_UninitKeyman();
// we MUST release all refs to _pThreadMgr in Deactivate
SafeReleaseClear(_pThreadMgr);
_tfClientId = TF_CLIENTID_NULL;
return S_OK;
}