mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-13 20:29:24 +00:00
274 lines
13 KiB
C++
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;
|
|
}
|
|
}
|