spiegel-keyman/windows/src/engine/kmtip/keys.cpp

364 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Name: keys
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Create Date: 7 Sep 2009
Modified Date: 27 Jan 2015
Authors: mcdurdin
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
History: 07 Sep 2009 - mcdurdin - I1713 - Sticky shift keys with TSF addin
07 Sep 2009 - mcdurdin - I2096 - 0x88 callback change
17 Nov 2012 - mcdurdin - I3566 - V9.0 - When legacy output is used, we need to avoid eating Keyman-generated characters
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
16 Jun 2014 - mcdurdin - I4274 - V9.0 - kmtip does not work if already active before KM starts
16 Jun 2014 - mcdurdin - I3605 - V9.0 - Resolving preserved keys and shift states
03 Aug 2014 - mcdurdin - I4325 - V9.0 - If KM not running, disable all preserved keys annoying otherwise
31 Dec 2014 - mcdurdin - I4548 - V9.0 - When Alt is down, release of Ctrl, Shift is not detectable within TIP in some languages
27 Jan 2015 - mcdurdin - I4575 - V9.0 - Support output of TAB and ENTER for unmatched key events
27 Jan 2015 - mcdurdin - I4575 - V9.0 - Support output of TAB and ENTER for unmatched key events
*/
//#define _DEBUG_LOGKEY
// keys.cpp
//
// ITfKeyEventSink implementation.
//
#include "pch.h"
#include "kmtip.h"
#include "../../../../common/windows/cpp/include/vkeys.h"
//+---------------------------------------------------------------------------
//
// _InitKeystrokeSink
//
// Advise a keystroke sink.
//----------------------------------------------------------------------------
BOOL CKMTipTextService::_InitKeystrokeSink()
{
ITfKeystrokeMgr *pKeystrokeMgr;
HRESULT hr;
Log(L"InitKeystrokeSink");
if (!_pThreadMgr) {
SendDebugMessage(L"_InitKeystrokeSink called but pThreadMgr == NULL");
return FALSE; // I3714 -> app hangs when switching kmtip off when keyman32 not loaded, due to not init. // I3714
}
if (_keystrokeSinkInitialized) {
_UninitKeystrokeSink();
}
_keystrokeSinkInitialized = FALSE;
hr = _pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr);
if(hr!= S_OK) {
DebugLastError0(L"QueryInterface(ITfKeystrokeMgr)", hr);
return FALSE;
}
hr = pKeystrokeMgr->AdviseKeyEventSink(_tfClientId, (ITfKeyEventSink *)this, TRUE);
if (hr != S_OK) {
DebugLastError0(L"AdviseKeyEventSink", hr);
}
pKeystrokeMgr->Release();
memset(fEatenBuf, 0, sizeof(fEatenBuf));
return _keystrokeSinkInitialized = (hr == S_OK);
}
BOOL CKMTipTextService::_InitPreservedKeys() { // I4274
ITfKeystrokeMgr *pKeystrokeMgr;
HRESULT hr = S_OK;
Log(L"_InitPreservedKeys");
if (!_pThreadMgr) {
Log(L"_InitPreservedKeys: pThreadMgr == NULL");
return FALSE; // I3714 -> app hangs when switching kmtip off when keyman32 not loaded, due to not init. // I3714
}
hr = _pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr);
if (hr != S_OK) {
DebugLastError0(L"QueryInterface(ITfKeystrokeMgr)", hr);
return FALSE;
}
hr = _PreserveAltKeys(pKeystrokeMgr); // I3588
pKeystrokeMgr->Release();
return (hr == S_OK);
}
//+---------------------------------------------------------------------------
//
// _UninitKeystrokeSink
//
// Unadvise a keystroke sink. Assumes we have advised one already.
//----------------------------------------------------------------------------
void CKMTipTextService::_UninitKeystrokeSink()
{
LogEnter();
ITfKeystrokeMgr *pKeystrokeMgr;
if (!_pThreadMgr) return; // I3714 -> app hangs when switching kmtip off when keyman32 not loaded, due to not init.
HRESULT hr;
hr = _pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr);
if (hr != S_OK) {
DebugLastError0(L"QueryInterface(ITfKeystrokeMgr)", hr);
return;
}
hr = pKeystrokeMgr->UnadviseKeyEventSink(_tfClientId);
if (hr != S_OK) {
DebugLastError0(L"UnadviseKeyEventSink", hr);
}
_UnpreserveAltKeys(pKeystrokeMgr); // I3588
pKeystrokeMgr->Release();
}
//+---------------------------------------------------------------------------
//
// OnSetFocus
//
// Called by the system whenever this service gets the keystroke device focus.
//----------------------------------------------------------------------------
STDAPI CKMTipTextService::OnSetFocus(BOOL fForeground)
{
LogEnter();
return S_OK;
}
#ifdef _DEBUG_LOGKEY
void LogKey(PSTR func, UINT msg, WPARAM wParam, LPARAM lParam) { // I4548
if(wParam >= 0 && wParam <= 0xFF) {
Log(L"%hs(%hs[%x],%x)\n", func, VKeyNames[wParam], wParam, lParam);
} else {
Log(L"%hs(%x,%x)\n", func, wParam, lParam);
}
}
#else
#define LogKey(a,b,c,d)
#endif
//+---------------------------------------------------------------------------
//
// OnTestKeyDown
//
// Called by the system to query this service wants a potential keystroke.
//----------------------------------------------------------------------------
STDAPI CKMTipTextService::OnTestKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
{
LogKey("CKMTipTextService::OnTestKeyDown", 0, wParam, lParam);
// If the keystroke is a Keyman-generated key, ignore it
// But we need to pass Caps Lock through, even if we generated it, so we can track Caps Lock state.
// TODO: Fix magic constants
if ((lParam & 0x00FF0000L) == 0xFF0000L &&
wParam != VK_CAPITAL) {
*pfEaten = FALSE;
}
else {
*pfEaten = _KeymanProcessKeystroke(pContext, wParam, lParam, FALSE, FALSE); // I3588
// SendDebugMessageFormat("CKMTipTextService::OnTestKeyDown(pfEaten=%s)", *pfEaten ? "TRUE" : "FALSE");
}
return S_OK;
}
//+---------------------------------------------------------------------------
//
// OnKeyDown
//
// Called by the system to offer this service a keystroke. If *pfEaten == TRUE
// on exit, the application will not handle the keystroke.
//----------------------------------------------------------------------------
STDAPI CKMTipTextService::OnKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
{
LogKey("CKMTipTextService::OnKeyDown", 1, wParam, lParam);
fEatenBuf[wParam] = *pfEaten = _KeymanProcessKeystroke(pContext, wParam, lParam, TRUE, FALSE); // I3588
// SendDebugMessageFormat("CKMTipTextService::OnKeyDown(pfEaten=%s)", *pfEaten ? "TRUE" : "FALSE");
return S_OK;
}
//+---------------------------------------------------------------------------
//
// OnTestKeyUp
//
// Called by the system to query this service wants a potential keystroke.
//----------------------------------------------------------------------------
STDAPI CKMTipTextService::OnTestKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
{
LogKey("CKMTipTextService::OnTestKeyUp", 2, wParam, lParam);
// If the keystroke is a Keyman-generated key, ignore it
// But we need to pass Caps Lock through, even if we generated it, so we can track Caps Lock state.
if ((lParam & 0x00FF0000L) == 0xFF0000L &&
wParam != VK_CAPITAL) { // I3566
*pfEaten = FALSE;
}
else {
_KeymanProcessKeystroke(pContext, wParam, lParam, FALSE, FALSE); // I3588
*pfEaten = fEatenBuf[wParam];
}
// SendDebugMessageFormat("CKMTipTextService::OnTestKeyUp(pfEaten=%s)", *pfEaten ? "TRUE" : "FALSE");
return S_OK;
}
//+---------------------------------------------------------------------------
//
// OnKeyUp
//
// Called by the system to offer this service a keystroke. If *pfEaten == TRUE
// on exit, the application will not handle the keystroke.
//----------------------------------------------------------------------------
STDAPI CKMTipTextService::OnKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
{
LogKey("CKMTipTextService::OnKeyUp", 3, wParam, lParam);
// If the keystroke is a Keyman-generated key, ignore it
// But we need to pass Caps Lock through, even if we generated it, so we can track Caps Lock state.
if ((lParam & 0x00FF0000L) == 0xFF0000L &&
wParam != VK_CAPITAL) { // I3566 // I3605
*pfEaten = FALSE;
}
else
{
_KeymanProcessKeystroke(pContext, wParam, lParam, TRUE, FALSE); // I3588 // I3605
*pfEaten = fEatenBuf[wParam];
}
// SendDebugMessageFormat("CKMTipTextService::OnKeyUp(pfEaten=%s)", *pfEaten ? "TRUE" : "FALSE");
return S_OK;
}
//+---------------------------------------------------------------------------
//
// _PreserveAltKeys
//
// Identify and preserve all Alt+ combinations that Keyman Engine needs to
// pass through to Keyman keyboard
//----------------------------------------------------------------------------
HRESULT CKMTipTextService::_PreserveAltKeys(ITfKeystrokeMgr *pKeystrokeMgr) // I3588
{
LogEnter();
_UnpreserveAltKeys(pKeystrokeMgr);
if(!Keyman32Interface::GetKeyboardPreservedKeys(NULL, &_cPreservedKeyCount))
{
Log(L"GetKeyboardPreservedKeys failed");
return E_FAIL;
}
_PreservedKeys = new PreservedKey[_cPreservedKeyCount];
if(!Keyman32Interface::GetKeyboardPreservedKeys(&_PreservedKeys, &_cPreservedKeyCount))
{
Log(L"GetKeyboardPreservedKeys(2nd) failed");
return E_FAIL;
}
HRESULT hr = S_OK;
for(size_t i = 0; i < _cPreservedKeyCount; i++)
{
hr = pKeystrokeMgr->PreserveKey(_tfClientId, _PreservedKeys[i].guid, &_PreservedKeys[i].key, NULL, 0);
if (hr != S_OK) {
if(_PreservedKeys[i].key.uVKey < 256)
Log(L"Failed to preserve key %d: %s %x, %x", i, VKeyNames[_PreservedKeys[i].key.uVKey], _PreservedKeys[i].key.uModifiers, hr);
else
Log(L"Failed to preserve key %d: %x %x, %x", i, _PreservedKeys[i].key.uVKey, _PreservedKeys[i].key.uModifiers, hr);
}
#ifdef _DEBUG_LOGKEY
else {
if (_PreservedKeys[i].key.uVKey < 256)
Log(L"Preserved key %d: %s %x, %x", i, VKeyNames[_PreservedKeys[i].key.uVKey], _PreservedKeys[i].key.uModifiers, hr);
else
Log(L"Preserved key %d: %x %x, %x", i, _PreservedKeys[i].key.uVKey, _PreservedKeys[i].key.uModifiers, hr);
}
#endif
}
return S_OK;
}
//+---------------------------------------------------------------------------
//
// _UnpreserveAltKeys
//
// Release all preserved Alt+ combinations
//----------------------------------------------------------------------------
HRESULT CKMTipTextService::_UnpreserveAltKeys(ITfKeystrokeMgr *pKeystrokeMgr)
{
LogEnter();
HRESULT hr = S_OK;
for(size_t i = 0; i < _cPreservedKeyCount; i++)
{
hr = pKeystrokeMgr->UnpreserveKey(_PreservedKeys[i].guid, &_PreservedKeys[i].key);
if(hr != S_OK)
Log(L"Failed to unpreserve key %d, %x", i, hr);
}
hr = S_OK;
if(_PreservedKeys != NULL)
{
delete _PreservedKeys;
_PreservedKeys = NULL;
}
_cPreservedKeyCount = 0;
return hr;
}
//+---------------------------------------------------------------------------
//
// OnPreservedKey
//
// Called by the system when a preserved key combination is pressed.
//----------------------------------------------------------------------------
STDMETHODIMP CKMTipTextService::OnPreservedKey(ITfContext *pContext, REFGUID rguid, BOOL *pfEaten)
{
LogEnter();
for(size_t i = 0; i < _cPreservedKeyCount; i++) // I3588
{
if(IsEqualGUID(_PreservedKeys[i].guid, rguid))
{
Log(L"preserved key %d found", i); // I4575
*pfEaten = _KeymanProcessKeystroke(pContext, _PreservedKeys[i].key.uVKey, _PreservedKeys[i].key.uModifiers, FALSE, TRUE);
if(*pfEaten) _KeymanProcessKeystroke(pContext, _PreservedKeys[i].key.uVKey, _PreservedKeys[i].key.uModifiers, TRUE, TRUE);
break;
}
}
*pfEaten = FALSE;
return S_OK;
}