spiegel-keyman/windows/src/engine/keyman32/kmprocess.cpp

274 lines
13 KiB
C++

/*
Name: kmprocess
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Create Date: 14 Sep 2006
Modified Date: 28 Mar 2016
Authors: mcdurdin
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
History: 14 Sep 2006 - mcdurdin - Remove NoSetShift flag
04 Jan 2007 - mcdurdin - Add CODE_NOTANY
22 Jan 2007 - mcdurdin - Fix for K_NPENTER
19 Jun 2007 - mcdurdin - I890 - Flush end-of-textstore deadkeys with non-character keystrokes
13 Jul 2007 - mcdurdin - I934 - Prep for x64
23 Aug 2007 - mcdurdin - I994 - Fix TSF keystrokes out of order
05 Nov 2007 - mcdurdin - I1133 - Rule regression - Mismatched parentheses (fix for .239 fix)
05 Nov 2007 - mcdurdin - I1130 - Speed up keystrokes by avoiding unnecessary debug string building
14 Jun 2008 - mcdurdin - I1389 - Supplementary chars on Vista default to a single BKSP
16 Jan 2009 - mcdurdin - I1622 - Fix deadkeys not working correctly in stores
27 Jan 2009 - mcdurdin - I1797 - Add fallback for AIWin2000 app integration
11 Mar 2009 - mcdurdin - I1894 - Fix threading bugs introduced in I1888
30 Nov 2009 - mcdurdin - I2156 - Remove invalid CODE_CONTEXT test
11 Dec 2009 - mcdurdin - I934 - x64 - Initial version
12 Mar 2010 - mcdurdin - I934 - x64 - Complete
12 Mar 2010 - mcdurdin - I2229 - Remove hints and warnings
04 May 2010 - mcdurdin - I2351 - Robustness - verify _td return value
25 May 2010 - mcdurdin - I1632 - Keyboard Options
15 Jun 2010 - mcdurdin - I2424 - Use AIWin2000Unicode unless disabled
24 Jun 2010 - mcdurdin - I2436 - Add U+0020 to context manually when not matched
29 Jun 2010 - mcdurdin - I2439 - If keyboard fails to load, IsKeyboardUnicode will crash
25 Mar 2011 - mcdurdin - I2590 - Keyman Engine crashes if if() statement is not first character of rule
05 Sep 2011 - mcdurdin - I3054 - WM_UNICHAR support regression
17 Aug 2012 - mcdurdin - I3432 - V9.0 - Add support for &platform and &baselayout to Keyman Engine
27 Aug 2012 - mcdurdin - I3439 - V9.0 - Refactor xstring support in C++ code
27 Aug 2012 - mcdurdin - I3438 - V9.0 - Add support for custom virtual keys Created
02 Dec 2011 - mcdurdin - I3162 - Potential buffer overflow for keyboards with complex contexts
02 Dec 2011 - mcdurdin - I3158 - Keyman Engine can crash other applications when multiple OEM products are started, with a _tcscpy assertion
04 Nov 2012 - mcdurdin - I3536 - V9.0 - Merge of I3162 - Potential buffer overflow for keyboards with complex contexts
04 Nov 2012 - mcdurdin - I3524 - V9.0 - Merge of I3158 - Keyman Engine can crash other applications when multiple OEM products are started, with a _tcscpy assertion
17 Nov 2012 - mcdurdin - I3573 - V9.0 - Don't recreate the TSF AppIntegration with each keystroke
17 Nov 2012 - mcdurdin - I3577 - V9.0 - Keyman becomes responsible for all key outputs when using TSF, even those it wouldn't normally eat
01 Dec 2012 - mcdurdin - I3617 - V9.0 - Keyboard hook obsolete, strip out code
10 Jan 2014 - mcdurdin - I4024 - V9.0 - Shift states not working with default keys in V9
16 Apr 2014 - mcdurdin - I4169 - V9.0 - Mnemonic layouts should be recompiled to positional based on user-selected base keyboard
24 Apr 2014 - mcdurdin - I4196 - V9.0 - wm_kmmoreposting must be refactored for TIP work as it is not sequential
01 May 2014 - mcdurdin - I4128 - V9.0 - Shift states still not working with unprocessed keys in V9
28 May 2014 - mcdurdin - I4220 - V9.0 - Remove references to LoadKeyboardLayout, Preload, Substitutes, etc. and use only TSF
23 Jun 2014 - mcdurdin - I4287 - V9.0 - Remove extraneous AppIntegration class type tests
26 Jun 2014 - mcdurdin - I4290 - Keys that have rules but are not matched due to context do not generate output
14 Nov 2014 - mcdurdin - I4516 - V9.0 - Language hotkeys associated with non-primary keyboards do not trigger language change
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
03 Feb 2015 - mcdurdin - I4582 - V9.0 - Most underlying layout code in Keyman32 is now obsolete and needs to be removed
04 Feb 2015 - mcdurdin - I4585 - V9.0 - Keyman Engine appears to ignore space in context when no rule for it
06 Feb 2015 - mcdurdin - I4583 - V9.0 - Remove altgr lookup test from keyman32 and put it into the registry
23 Jun 2015 - mcdurdin - I4033 - V9.0 - Enable SHIFT+ESC to pause debugger with TSF
03 Aug 2015 - mcdurdin - I4826 - Navigation keys don't work in debugger
03 Aug 2015 - mcdurdin - I4827 - Key down debug events are not always processed correctly
09 Aug 2015 - mcdurdin - I4845 - Debugger does not permit Delete key
10 Aug 2015 - mcdurdin - I4838 - Backspace and Delete don't work in the keyboard debugger
28 Mar 2016 - mcdurdin - I4933 - Compat issue with Firefox 42 and IE and Keyman 9 TSF
*/
#include "pch.h" // I4575
BOOL fOutputKeystroke;
/*char *getcontext()
{
WCHAR buf[128];
static char bufout[128];
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return "";
_td->app->GetWindowContext(buf, 128);
WideCharToMultiByte(CP_ACP, 0, buf, -1, bufout, 128, NULL, NULL);
return bufout;
}*/
char *getcontext_debug() {
//return "";
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return "";
return Debug_UnicodeString(_td->app->ContextBufMax(128));
}
/**
* Process the key stroke using the core processor
*
* @param _td Pointer to KEYMAN64THREADDATA
* @return BOOL Return TRUE if keystroke event is passed successfully
*/
static BOOL
Process_Event_Core(PKEYMAN64THREADDATA _td) {
PWSTR contextBuf = _td->app->ContextBufMax(MAXCONTEXT);
km_kbp_context_item *citems = nullptr;
ContextItemsFromAppContext(contextBuf, &citems);
if (KM_KBP_STATUS_OK != km_kbp_context_set(km_kbp_state_context(_td->lpActiveKeyboard->lpCoreKeyboardState), citems)) {
km_kbp_context_items_dispose(citems);
return FALSE;
}
km_kbp_context_items_dispose(citems);
SendDebugMessageFormat(
0, sdmGlobal, 0, "ProcessEvent: vkey[%d] ShiftState[%d] isDown[%d]", _td->state.vkey,
static_cast<uint16_t>(Globals::get_ShiftState() & (KM_KBP_MODIFIER_MASK_ALL | KM_KBP_MODIFIER_MASK_CAPS)), (uint8_t)_td->state.isDown);
// Mask the bits supported according to `km_kbp_modifier_state` enum, update the mask if this enum is expanded.
if (KM_KBP_STATUS_OK != km_kbp_process_event(
_td->lpActiveKeyboard->lpCoreKeyboardState, _td->state.vkey,
static_cast<uint16_t>(Globals::get_ShiftState() & (KM_KBP_MODIFIER_MASK_ALL | KM_KBP_MODIFIER_MASK_CAPS)), (uint8_t)_td->state.isDown, KM_KBP_EVENT_FLAG_DEFAULT)) {
SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessEvent CoreProcessEvent Result:False %d ", FALSE);
return FALSE;
}
return TRUE;
}
/*
* BOOL ProcessHook();
*
* Parameters: none
*
* Returns: TRUE if keystroke should be eaten
*
* Called by: FilterFunc
*
* ProcessHook organizes the messages and gives them to the appropriate routines to
* process, and checks the state of Windows for the keyboard handling.
*/
BOOL ProcessHook()
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
fOutputKeystroke = FALSE; // TODO: 5442 no longer needs to be global once we use core processor
//
// If we are running in the debugger, don't do a second run through
//
if(_td->app->DebugControlled() && !_td->TIPFUpdateable) { // I4287
if(_td->state.vkey == VK_ESCAPE || (_td->state.vkey >= VK_PRIOR && _td->state.vkey <= VK_DOWN) || (_td->state.vkey == VK_DELETE)) return FALSE; // I4033 // I4826 // I4845
else return TRUE;
}
//app->NoSetShift = FALSE;
_td->app->ReadContext();
if(_td->state.msg.message == wm_keymankeydown) { // I4827
if (ShouldDebug(sdmKeyboard)) {
SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0, "Key pressed: %s Context '%s'",
Debug_VirtualKey(_td->state.vkey), getcontext_debug());
}
}
// For applications not using the TSF kmtip calls this function twice for each keystroke,
// first to determine if we are doing processing work (TIPFUpdateable == FALSE),
// if we say yes it will call a second time to actually do the work.
// We call the core process event only once and use the core's queued actions
// on the second pass.
// For the TSF in most cases kmtip (except OnPreservedKey) will not call the non-updateable test parse.
// Therfore the core process event will need to be called before processing the actions.
// CoreProcessEventRun would be a sufficient test however testing TIPFUpdateable defines
// the status of the keystroke processing more precisely.
if (!_td->TIPFUpdateable || !_td->CoreProcessEventRun) {
if (!Process_Event_Core(_td)) {
return FALSE;
}
}
if (!_td->TIPFUpdateable) {
ProcessActionsNonUpdatableParse(&fOutputKeystroke);
} else {
ProcessActions(&fOutputKeystroke);
}
if (fOutputKeystroke && !_td->app->IsQueueEmpty()) {
//
// #2759: The keyboard has requested that the default output
// should be emitted. With .kmn, this is done with 'use(final)'
// where 'group(final) using keys' is an empty group. Typical
// use case is to allow ENTER, TAB, etc, to be emitted as
// normal but to do some preprocessing first. A classic example
// is final sigma for Greek which changes only when the word is
// 'complete'.
//
// We have some events in the queue, so we need to
// block the default keystroke, emit those characters, and
// then synthesize the original keystroke
//
SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessHook: %d events in queue and default output requested. [IsLegacy:%d, IsUpdateable:%d]", _td->app->GetQueueSize(), _td->app->IsLegacy(), _td->TIPFUpdateable);
if (_td->app->IsLegacy()) {
_td->app->QueueAction(QIT_VSHIFTDOWN, Globals::get_ShiftState());
_td->app->QueueAction(QIT_VKEYDOWN, _td->state.vkey);
_td->app->QueueAction(QIT_VKEYUP, _td->state.vkey);
_td->app->QueueAction(QIT_VSHIFTUP, Globals::get_ShiftState());
fOutputKeystroke = FALSE;
}
else if (!_td->TIPFUpdateable) {
//
// #2759: kmtip calls this function twice for each keystroke, first to
// determine if we are doing processing work (IsUpdateable() == FALSE),
// then to actually do the work. We want to say, "yes we are doing
// processing work", which we currently do by returning TRUE
// (that is, setting fOutputKeystroke to FALSE), and then for the second
// pass, we will do the work and then output the keytroke. Tricky.
//
fOutputKeystroke = FALSE;
}
}
if (fOutputKeystroke && _td->app->DebugControlled()) {
// The debug memo does not receive default key events because
// we capture them all here. So we synthesize the key event for
// the debugger.
_td->app->QueueAction(QIT_VSHIFTDOWN, Globals::get_ShiftState());
_td->app->QueueAction(QIT_VKEYDOWN, _td->state.vkey);
_td->app->QueueAction(QIT_VKEYUP, _td->state.vkey);
_td->app->QueueAction(QIT_VSHIFTUP, Globals::get_ShiftState());
fOutputKeystroke = FALSE;
}
if(*Globals::hwndIM() == 0 || *Globals::hwndIMAlways())
{
_td->app->SetCurrentShiftState(Globals::get_ShiftState());
_td->app->SendActions(); // I4196
}
// output context for debugging
// PWSTR contextBuf = _td->app->ContextBufMax(MAXCONTEXT);
// SendDebugMessageFormat(0, sdmAIDefault, 0, "Kmprocess::ProcessHook After cxt=%s", Debug_UnicodeString(contextBuf, 1));
return !fOutputKeystroke;
}
PWSTR strtowstr(PSTR in)
{
PWSTR result;
size_t len;
mbstowcs_s(&len, NULL, 0, in, strlen(in));
result = new WCHAR[len+1];
mbstowcs_s(&len, result, len, in, strlen(in));
result[len] = 0;
return result;
}
PSTR wstrtostr(PCWSTR in)
{
PSTR result;
size_t len;
wcstombs_s(&len, NULL, 0, in, wcslen(in));
result = new CHAR[len+1];
wcstombs_s(&len, result, len, in, wcslen(in));
result[len] = 0;
return result;
}
// I4220
void SelectApplicationIntegration() // I4287
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return;
if(!_td->app) {
_td->app = new AITIP;
}
}