mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-13 20:29:24 +00:00
Merge branch 'master' of https://github.com/keymanapp/keyman into refactor/developer/6026-kmcompx-compiler-cross-platform-part-2-NEW_VERSION # Conflicts: # common/core/desktop/src/kmx/kmx_context.h # common/core/desktop/src/kmx/kmx_processor.cpp # common/windows/cpp/include/keymanversion.h # common/windows/cpp/include/kkmnkbd/Compfile.h # common/windows/cpp/include/kmtip_guids.h # common/windows/cpp/src/kmtip_guids.cpp # core/src/kmx/kmx_conversion.cpp # core/src/kmx/kmx_conversion.h # core/src/kmx/kmx_file.cpp # core/src/kmx/kmx_processevent.cpp # core/src/kmx/kmx_processor.hpp # core/src/kmx/kmx_xstring.cpp # developer/js/.gitignore # developer/js/package-lock.json # developer/js/tests/tsconfig.json # developer/src/kmcmpdll/CasedKeys.cpp # developer/src/kmcmpdll/CheckFilenameConsistency.cpp # developer/src/kmcmpdll/CheckFilenameConsistency.h # developer/src/kmcmpdll/CheckForDuplicates.cpp # developer/src/kmcmpdll/CheckForDuplicates.h # developer/src/kmcmpdll/CheckNCapsConsistency.cpp # developer/src/kmcmpdll/CheckNCapsConsistency.h # developer/src/kmcmpdll/Compiler.cpp # developer/src/kmcmpdll/DeprecationChecks.cpp # developer/src/kmcmpdll/NamedCodeConstants.cpp # developer/src/kmcmpdll/UnreachableRules.cpp # developer/src/kmcmpdll/UnreachableRules.h # developer/src/kmcmpdll/compfile.h # developer/src/kmcmpdll/kcframe/kcframe.cpp # developer/src/kmcmpdll/kcframe/kcframe.vcxproj # developer/src/kmcmpdll/kcframe/kcframe.vcxproj.filters # developer/src/kmcmpdll/kmcmpdll.vcxproj # developer/src/kmcmpdll/kmcmpdll.vcxproj.filters # developer/src/kmcmpdll/version.rc # developer/src/kmcmpdll/versioning.cpp # developer/src/kmlmc/bundle.sh # developer/src/kmlmc/tests/test-join-word-breaker.ts # developer/src/kmlmc/tests/test-override-script-defaults.ts # developer/src/server/tsconfig.json # windows/src/developer/kmcmpdll/Makefile # windows/src/developer/kmcmpdll/vkeys.h # windows/src/engine/keyman32/keymanengine.h # windows/src/global/inc/Vkeys.h # windows/src/global/inc/rc4.h # windows/src/global/vc/rc4.cpp
798 lines
23 KiB
C++
798 lines
23 KiB
C++
/*
|
|
Copyright: Copyright (C) 2003-2018 SIL International.
|
|
Authors: mcdurdin
|
|
*/
|
|
/*
|
|
#include "kmx_processevent.h"
|
|
#include "state.hpp"
|
|
#include <keyman/keyboardprocessor_consts.h>
|
|
#include "../keyman/keyboardprocessor_consts.h"
|
|
|
|
using namespace km::kbp;
|
|
using namespace kmx;
|
|
|
|
|
|
KMX_BOOL km::kbp::kmx::g_debug_ToConsole = false;
|
|
KMX_BOOL km::kbp::kmx::g_debug_KeymanLog =true;
|
|
KMX_BOOL km::kbp::kmx::g_silent = false;
|
|
|
|
|
|
KMX_ProcessEvent::KMX_ProcessEvent() : m_actions(&m_context), m_options(&m_keyboard, m_actions) {
|
|
m_indexStack = new KMX_WORD[GLOBAL_ContextStackSize];
|
|
m_miniContext = new KMX_WCHAR[GLOBAL_ContextStackSize];
|
|
m_miniContextIfLen = 0;
|
|
m_debug_items = nullptr;
|
|
}
|
|
|
|
KMX_ProcessEvent::~KMX_ProcessEvent() {
|
|
delete[] m_indexStack;
|
|
delete[] m_miniContext;
|
|
DeleteInternalDebugItems();
|
|
}
|
|
|
|
char VKeyToChar(KMX_UINT modifiers, KMX_UINT vk) {
|
|
// We only map SHIFT and UNSHIFTED, and CAPS LOCK
|
|
|
|
if ((modifiers & ~(K_SHIFTFLAG | CAPITALFLAG)) != 0) {
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
shifted = (modifiers & K_SHIFTFLAG) == K_SHIFTFLAG,
|
|
caps = (modifiers & CAPITALFLAG) == CAPITALFLAG;
|
|
|
|
if (vk == KM_KBP_VKEY_SPACE) {
|
|
// Override for space because it is the same for
|
|
// shifted and unshifted.
|
|
return 32;
|
|
}
|
|
|
|
for (int i = 0; s_char_to_vkey[i].vk; i++) {
|
|
if (s_char_to_vkey[i].caps && caps) {
|
|
if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == !shifted) return i + 32;
|
|
}
|
|
else {
|
|
if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == shifted) return i + 32;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Process Events
|
|
*
|
|
* ProcessEvent organizes the messages and gives them to the appropriate routines to
|
|
* process, and checks the state of Windows for the keyboard handling.
|
|
* Called by FilterFunc.
|
|
*
|
|
* @param state A pointer to the state object.
|
|
* @param vkey A virtual key to be processed.
|
|
* @param modifiers The combinations of modifier keys set at the time vkey was pressed,
|
|
* bitmask from the km_kbp_modifier_state enum.
|
|
* @param isKeyDown TRUE if this is called on KeyDown event, FALSE if called on KeyUp event
|
|
*
|
|
* @return TRUE if keystroke should be eaten
|
|
*/
|
|
/*
|
|
KMX_BOOL KMX_ProcessEvent::ProcessEvent(
|
|
km_kbp_state *state,
|
|
KMX_UINT vkey,
|
|
KMX_DWORD modifiers,
|
|
KMX_BOOL isKeyDown
|
|
) {
|
|
LPKMX_KEYBOARD kbd = m_keyboard.Keyboard;
|
|
|
|
m_kbp_state = state;
|
|
|
|
// If debugging is enabled, then ...
|
|
DeleteInternalDebugItems();
|
|
|
|
state->debug_items().clear();
|
|
if(state->debug_items().is_enabled()) {
|
|
CreateInternalDebugItems();
|
|
} else {
|
|
// We want to have a clean debug state even if it is not in use
|
|
state->debug_items().push_end(m_actions.Length(), 0);
|
|
}
|
|
|
|
ResetCapsLock(modifiers, isKeyDown);
|
|
|
|
m_state.vkey = vkey;
|
|
m_state.charCode = VKeyToChar(modifiers, vkey);
|
|
m_modifiers = modifiers;
|
|
m_state.LoopTimes = 0;
|
|
|
|
if (kbd->StartGroup[BEGIN_UNICODE] == (KMX_DWORD) -1) {
|
|
DebugLog("Non-Unicode keyboards are not supported.");
|
|
m_kbp_state = nullptr;
|
|
return false;
|
|
}
|
|
|
|
switch (vkey) {
|
|
case KM_KBP_VKEY_CAPS:
|
|
if (KeyCapsLockPress(modifiers, isKeyDown))
|
|
return true;
|
|
break;
|
|
case KM_KBP_VKEY_SHIFT:
|
|
KeyShiftPress(modifiers, isKeyDown);
|
|
return true;
|
|
case KM_KBP_VKEY_CONTROL:
|
|
case KM_KBP_VKEY_ALT:
|
|
return true;
|
|
}
|
|
|
|
if (!isKeyDown) {
|
|
m_kbp_state = nullptr;
|
|
return false;
|
|
}
|
|
|
|
LPKMX_GROUP gp = &kbd->dpGroupArray[kbd->StartGroup[BEGIN_UNICODE]];
|
|
|
|
KMX_BOOL fOutputKeystroke = false;
|
|
|
|
if(m_debug_items) {
|
|
km_kbp_state_debug_key_info key_info;
|
|
key_info.character = m_state.charCode;
|
|
key_info.modifier_state = modifiers;
|
|
key_info.vk = m_state.vkey;
|
|
m_debug_items->push_begin(&key_info, KM_KBP_DEBUG_FLAG_UNICODE);
|
|
}
|
|
|
|
ProcessGroup(gp, &fOutputKeystroke);
|
|
|
|
if(m_debug_items) {
|
|
m_debug_items->push_end(m_actions.Length(), fOutputKeystroke ? KM_KBP_DEBUG_FLAG_OUTPUTKEYSTROKE : 0);
|
|
// m_debug_items is just a helper class that pushes to state->debug_items(),
|
|
// so we can throw it away after we are done with it
|
|
DeleteInternalDebugItems();
|
|
}
|
|
|
|
m_kbp_state = nullptr;
|
|
|
|
return !fOutputKeystroke;
|
|
}
|
|
|
|
/*
|
|
* PRIVATE KMX_BOOL ProcessGroup(LPGROUP gp);
|
|
*
|
|
* Parameters: gp Pointer to group to process inside
|
|
*
|
|
* Returns: TRUE if messages are to be sent,
|
|
* and FALSE if no messages are to be sent.
|
|
*
|
|
* Called by: ProcessEvent, recursive inside groups
|
|
*
|
|
* ProcessGroup is where the keystroke conversion and output takes place. This routine
|
|
* has a lot of crucial code in it!
|
|
*/
|
|
/*
|
|
KMX_BOOL KMX_ProcessEvent::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
|
|
{
|
|
KMX_DWORD i;
|
|
LPKEY kkp = NULL;
|
|
PKMX_WCHAR p;
|
|
int sdmfI;
|
|
|
|
if(m_debug_items) {
|
|
m_debug_items->push_group_enter(m_actions.Length(), gp);
|
|
}
|
|
|
|
LPKEYBOARD kbd = m_keyboard.Keyboard;
|
|
|
|
sdmfI = -1;
|
|
|
|
for (i = 0; i < kbd->cxGroupArray; i++) {
|
|
if (gp == &kbd->dpGroupArray[i]) {
|
|
sdmfI = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
If the number of nested groups goes higher than 50, then break out - this is
|
|
a limitation of stack size. This is basically a catch-all for freaky apps that
|
|
cause message loopbacks and nasty things like that. Okay, it's really a catch all
|
|
for bugs! This means the user's system shouldn't hang.
|
|
*/
|
|
/*
|
|
if (++m_state.LoopTimes > 50) {
|
|
DebugLog("Aborting output: m_state.LoopTimes exceeded.");
|
|
m_state.StopOutput = TRUE;
|
|
if(m_debug_items) {
|
|
m_debug_items->push_group_exit(m_actions.Length(), KM_KBP_DEBUG_FLAG_RECURSIVE_OVERFLOW, gp);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
The rule matching loop.
|
|
|
|
This loop iterates through all the rules in the group that is currently being
|
|
processed. Each rule in a group can be of three different types:
|
|
1. A virtual key rule, where the key to be matched is a virtual key
|
|
2. A normal key rule (WM_CHAR), where the key to be matched is an Ascii char.
|
|
3. A rule in a keyless group, where only the context is matched.
|
|
|
|
The loop goes through and checks the rules like that. This loop could be optimized
|
|
with standard searching techniques - the ContextMatch may be difficult.
|
|
*/
|
|
/*
|
|
DebugLog("m_state.vkey: %s shiftFlags: %x; charCode: %X", Debug_VirtualKey(m_state.vkey), m_modifiers, m_state.charCode); // I4582
|
|
DebugLog("m_context: %s", Debug_UnicodeString(m_context.GetFullContext()));
|
|
|
|
if(gp)
|
|
{
|
|
for(kkp = gp->dpKeyArray, i=0; i < gp->cxKeyArray; i++, kkp++)
|
|
{
|
|
if(!ContextMatch(kkp)) continue;
|
|
if(!gp->fUsingKeys)
|
|
{
|
|
if(kkp->dpContext[0] != 0) break; else continue;
|
|
}
|
|
|
|
//if(kkp->Key == m_state.vkey)
|
|
//SendDebugMessageFormat(m_state.msg.hwnd, sdmKeyboard, 0, "kkp->Key: %d kkp->ShiftFlags: %x",
|
|
// kkp->Key, kkp->ShiftFlags);
|
|
|
|
if(IsEquivalentShift(kkp->ShiftFlags, m_modifiers))
|
|
{
|
|
if(kkp->Key > VK__MAX && kkp->Key == m_state.vkey) break; // I3438 // I4582
|
|
else if(kkp->Key == m_state.vkey) break; // I4169
|
|
}
|
|
else if(kkp->ShiftFlags == 0 && kkp->Key == m_state.charCode && m_state.charCode != 0) break;
|
|
}
|
|
}
|
|
|
|
if(!gp || i == gp->cxKeyArray)
|
|
{
|
|
/*
|
|
No rule was found that corresponded to the current state of the context and
|
|
keyboard. NoMatch should be checked for everything except virtual keys; and
|
|
context should also be kept.
|
|
|
|
If the message was a virtual key, then just return without checking NoMatch.
|
|
NoMatch shouldn't be used for virtual keys because it will mean that no key
|
|
can ever get through that isn't matched - including arrows, func. keys, etc !!
|
|
Context is not kept for virtual keys being output.
|
|
*/
|
|
/*
|
|
DebugLog("No match was found in group %d of %d", sdmfI, m_keyboard.Keyboard->cxGroupArray);
|
|
|
|
if(!gp || (m_state.charCode == 0 && gp->fUsingKeys)) // I4585
|
|
// 7.0.241.0: I1133 - Fix mismatched parentheses on m_state.charCode - ie. we don't want to output this letter if !gp->fUsingKeys
|
|
{
|
|
KMX_BOOL fIsBackspace = m_state.vkey == KM_KBP_VKEY_BKSP && (m_modifiers & (LCTRLFLAG|RCTRLFLAG|LALTFLAG|RALTFLAG)) == 0; // I4128
|
|
|
|
if(fIsBackspace) { // I4838 // I4933
|
|
|
|
// Delete deadkeys prior to insertion point
|
|
PKMX_WCHAR pdeletecontext = m_context.Buf(1); // I4933
|
|
while(pdeletecontext && *pdeletecontext == UC_SENTINEL) {
|
|
m_actions.QueueAction(QIT_BACK, BK_DEADKEY); // side effect: also removes last char from context
|
|
pdeletecontext = m_context.Buf(1);
|
|
}
|
|
|
|
// If there is now no character in the context, we want to
|
|
// emit the backspace for application to use
|
|
if(!pdeletecontext || *pdeletecontext == 0) { // I4933
|
|
m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0);
|
|
if(m_debug_items) {
|
|
m_debug_items->push_group_exit(m_actions.Length(), KM_KBP_DEBUG_FLAG_NOMATCH, gp);
|
|
}
|
|
*pOutputKeystroke = TRUE; // I4933
|
|
return FALSE; // I4933
|
|
}
|
|
|
|
// Emit a backspace to delete the character
|
|
m_actions.QueueAction(QIT_BACK, BK_DEFAULT); // side effect: also removes last char from context
|
|
|
|
// And delete any deadkeys prior to insertion point again
|
|
pdeletecontext = m_context.Buf(1);
|
|
while(pdeletecontext && *pdeletecontext == UC_SENTINEL) {
|
|
m_actions.QueueAction(QIT_BACK, BK_DEADKEY); // side effect: also removes last char from context
|
|
pdeletecontext = m_context.Buf(1);
|
|
}
|
|
} else { // I4024 // I4128 // I4287 // I4290
|
|
DebugLog(" ... IsLegacy = FALSE; IsTIP = TRUE"); // I4128
|
|
m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0);
|
|
if(m_debug_items) {
|
|
m_debug_items->push_group_exit(m_actions.Length(), KM_KBP_DEBUG_FLAG_NOMATCH, gp);
|
|
}
|
|
*pOutputKeystroke = TRUE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (gp->dpNoMatch != NULL && *gp->dpNoMatch != 0)
|
|
{
|
|
if(m_debug_items) {
|
|
m_debug_items->push_nomatch_enter(m_actions.Length(), gp);
|
|
}
|
|
PostString(gp->dpNoMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke);
|
|
if(m_debug_items) {
|
|
m_debug_items->push_nomatch_exit(m_actions.Length(), gp);
|
|
}
|
|
}
|
|
else if (m_state.charCode != 0 && m_state.charCode != 0xFFFF && gp->fUsingKeys)
|
|
{
|
|
m_actions.QueueAction(QIT_CHAR, m_state.charCode);
|
|
}
|
|
|
|
if(m_debug_items) {
|
|
m_debug_items->push_group_exit(m_actions.Length(), KM_KBP_DEBUG_FLAG_NOMATCH, gp);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
DebugLog("match found in rule %d", i);
|
|
|
|
/*
|
|
Save the context that will be used for output when the 'context' keyword is used.
|
|
For each deadkey, we need to add 2 characters; look in related stores as well...
|
|
*/
|
|
/*
|
|
assert(kkp != NULL);
|
|
|
|
m_miniContextIfLen = xstrlen(kkp->dpContext) - xstrlen_ignoreifopt(kkp->dpContext);
|
|
|
|
// 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support
|
|
if(*kkp->dpContext == UC_SENTINEL && *(kkp->dpContext+1) == CODE_NUL)
|
|
u16cpy(m_miniContext, m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)-1) ); // I3162 // I3536
|
|
else
|
|
u16cpy(m_miniContext, m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)) ); // I3162 // I3536
|
|
|
|
m_miniContext[GLOBAL_ContextStackSize-1] = 0;
|
|
|
|
if(m_debug_items) {
|
|
m_debug_items->push_rule_enter(m_actions.Length(), gp, kkp, m_miniContext, m_indexStack);
|
|
}
|
|
|
|
/*
|
|
The next section includes several optimizations that make the code a little harder
|
|
to read, but are probably worth it in the time that they save.
|
|
|
|
If the output string doesn't have a "context" byte at the start, post backspaces
|
|
to erase the appropriate number of characters in the application. If it does have
|
|
a "context" byte at the start, then the string won't change, and no backspaces are
|
|
necessary. You could go one step further with this optimization, in PostAllKeys,
|
|
by comparing the starts of the strings to see what is same, and not backspacing
|
|
that, but it is probably not necessary.
|
|
*/
|
|
/*
|
|
p = kkp->dpOutput;
|
|
if(*p != UC_SENTINEL || *(p+1) != CODE_CONTEXT)
|
|
{
|
|
for(p = decxstr((KMX_WCHAR *) u16chr(m_miniContext, 0), m_miniContext); p != NULL; p = decxstr(p, m_miniContext))
|
|
{
|
|
if(*p == UC_SENTINEL)
|
|
switch(*(p+1))
|
|
{
|
|
case CODE_DEADKEY: m_actions.QueueAction(QIT_BACK, BK_DEADKEY); break;
|
|
case CODE_NUL: break; // 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support
|
|
}
|
|
else
|
|
{
|
|
m_actions.QueueAction(QIT_BACK, 0);
|
|
}
|
|
}
|
|
p = kkp->dpOutput;
|
|
}
|
|
else
|
|
p = incxstr(p); // otherwise, the "context" entry has to be jumped over
|
|
|
|
|
|
KMX_BOOL shouldProcessNomatch = PostString(p, m_keyboard.Keyboard, NULL, pOutputKeystroke) == psrCheckMatches;
|
|
|
|
if(m_debug_items) {
|
|
m_debug_items->push_rule_exit(m_actions.Length(), gp, kkp, m_miniContext, m_indexStack);
|
|
}
|
|
|
|
if(shouldProcessNomatch && gp->dpMatch && *gp->dpMatch) {
|
|
if(m_debug_items) {
|
|
m_debug_items->push_match_enter(m_actions.Length(), gp);
|
|
}
|
|
PostString(gp->dpMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke);
|
|
if(m_debug_items) {
|
|
m_debug_items->push_match_exit(m_actions.Length(), gp);
|
|
}
|
|
}
|
|
|
|
if(m_debug_items) {
|
|
m_debug_items->push_group_exit(m_actions.Length(), 0, gp);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* int PostString( PKMX_CHAR str, KMX_BOOL *useMode, LPMSG mp,
|
|
* LPKEYBOARD lpkb );
|
|
*
|
|
* Parameters: str Pointer to string to send
|
|
* useMode Pointer to KMX_BOOL about whether a "use" command was found
|
|
* mp Pointer to MSG structure to copy in outputting messages
|
|
* lpkb Pointer to global keyboard structure
|
|
*
|
|
* Returns: 0 to continue, 1 and 2 to return.
|
|
*
|
|
* Called by: ProcessGroup
|
|
*
|
|
* PostString posts a string of "context", "index", "beep", characters and virtual keys
|
|
* to the active application, via the Keyman PostKey buffer.
|
|
*/
|
|
/*
|
|
int KMX_ProcessEvent::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr, KMX_BOOL *pOutputKeystroke)
|
|
{
|
|
PKMX_WCHAR p, q, temp;
|
|
LPSTORE s;
|
|
int n1, n2;
|
|
int i, n, shift;
|
|
KMX_BOOL FoundUse = FALSE;
|
|
|
|
// TODO: Refactor to use incxstr
|
|
for(p = str; *p && (p < endstr || !endstr); p++)
|
|
{
|
|
if(*p == UC_SENTINEL)
|
|
switch(*(++p))
|
|
{
|
|
case CODE_EXTENDED: // Start of a virtual key section w/shift codes
|
|
p++;
|
|
|
|
shift = *p; //(*p<<8) | *(p+1);
|
|
m_actions.QueueAction(QIT_VSHIFTDOWN, shift);
|
|
|
|
p++;
|
|
|
|
m_actions.QueueAction(QIT_VKEYDOWN, *p);
|
|
m_actions.QueueAction(QIT_VKEYUP, *p);
|
|
|
|
m_actions.QueueAction(QIT_VSHIFTUP, shift);
|
|
|
|
p++; // CODE_EXTENDEDEND
|
|
////// CODE_EXTENDEDEND will be incremented by loop
|
|
|
|
break;
|
|
|
|
case CODE_DEADKEY: // A deadkey to be output
|
|
p++;
|
|
m_actions.QueueAction(QIT_DEADKEY, *p);
|
|
break;
|
|
case CODE_BEEP: // Sound an 'iconasterisk' beep
|
|
m_actions.QueueAction(QIT_BELL, 0);
|
|
break;
|
|
case CODE_CONTEXT: // copy the context to the output
|
|
{
|
|
KMX_BOOL ignoreOutputKeystroke;
|
|
PostString(m_miniContext, lpkb, (PKMX_WCHAR) u16chr(m_miniContext, 0), &ignoreOutputKeystroke);
|
|
break;
|
|
}
|
|
case CODE_CONTEXTEX:
|
|
p++;
|
|
for (q = m_miniContext, i = m_miniContextIfLen; *q && i < *p - 1; i++, q = incxstr(q));
|
|
if (*q) {
|
|
KMX_BOOL ignoreOutputKeystroke;
|
|
temp = incxstr(q);
|
|
PostString(q, lpkb, temp, &ignoreOutputKeystroke);
|
|
}
|
|
break;
|
|
case CODE_RETURN: // stop processing and start PostAllKeys
|
|
m_state.StopOutput = TRUE;
|
|
return psrPostMessages;
|
|
|
|
case CODE_CALL:
|
|
p++;
|
|
DebugLog("CallDLL not supported [store=%d].\n", *p-1);
|
|
FoundUse = TRUE;
|
|
break;
|
|
case CODE_USE: // use another group
|
|
p++;
|
|
ProcessGroup(&lpkb->dpGroupArray[*p-1], pOutputKeystroke);
|
|
if(m_state.StopOutput) return psrPostMessages;
|
|
FoundUse = TRUE;
|
|
break;
|
|
case CODE_CLEARCONTEXT:
|
|
// no longer supported, no-op
|
|
break;
|
|
case CODE_INDEX:
|
|
p++;
|
|
s = &m_keyboard.Keyboard->dpStoreArray[*p - 1];
|
|
p++;
|
|
|
|
n = m_indexStack[*p - 1];
|
|
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
|
|
PostString(temp, lpkb, incxstr(temp), pOutputKeystroke);
|
|
break;
|
|
case CODE_SETOPT:
|
|
p++;
|
|
n1 = *p - 1;
|
|
p++;
|
|
n2 = *p - 1;
|
|
GetOptions()->Set(n1, n2);
|
|
break;
|
|
case CODE_RESETOPT:
|
|
p++;
|
|
n1 = *p - 1;
|
|
GetOptions()->Reset(m_kbp_state->processor(), n1);
|
|
break;
|
|
case CODE_SAVEOPT:
|
|
p++;
|
|
n1 = *p - 1;
|
|
GetOptions()->Save(*m_kbp_state, n1);
|
|
break;
|
|
case CODE_IFSYSTEMSTORE:
|
|
p+=3;
|
|
break;
|
|
case CODE_SETSYSTEMSTORE:
|
|
p+=2;
|
|
break;
|
|
}
|
|
else {
|
|
if(Uni_IsSurrogate1(*p) && Uni_IsSurrogate2(*(p+1))) {
|
|
m_actions.QueueAction(QIT_CHAR, Uni_SurrogateToUTF32(*p, *(p+1)));
|
|
p++;
|
|
} else {
|
|
m_actions.QueueAction(QIT_CHAR, *p);
|
|
}
|
|
}
|
|
}
|
|
return FoundUse ? psrPostMessages : psrCheckMatches;
|
|
}
|
|
|
|
|
|
KMX_BOOL KMX_ProcessEvent::IsMatchingBaseLayout(PKMX_WCHAR layoutName) // I3432
|
|
{
|
|
KMX_BOOL bEqual = u16icmp(layoutName, static_cast<const km_kbp_cp *>(m_environment.baseLayout().c_str())) == 0 || // I4583
|
|
u16icmp(layoutName, static_cast<const km_kbp_cp*>(m_environment.baseLayoutAlt().c_str())) == 0; // I4583
|
|
|
|
return bEqual;
|
|
}
|
|
|
|
KMX_BOOL KMX_ProcessEvent::IsMatchingPlatformString(PKMX_WCHAR platform) // I3432
|
|
{
|
|
// TODO retrieve platform string from client environment
|
|
// TODO cleanup to use a vector<string>.
|
|
PKMX_WCHAR t = new KMX_WCHAR[u16len(m_environment.platform().c_str()) + 1], context = NULL;
|
|
u16cpy(t, m_environment.platform().c_str());
|
|
PKMX_WCHAR p = u16tok(t, u' ', &context);
|
|
while (p != NULL) {
|
|
if (u16icmp(platform, p) == 0) {
|
|
delete [] t;
|
|
return TRUE;
|
|
}
|
|
p = u16tok(NULL, u' ', &context);
|
|
}
|
|
|
|
delete [] t;
|
|
return FALSE;
|
|
}
|
|
|
|
KMX_BOOL KMX_ProcessEvent::IsMatchingPlatform(LPSTORE s) // I3432
|
|
{
|
|
PKMX_WCHAR t = new KMX_WCHAR[u16len(s->dpString)+1];
|
|
u16cpy(t, s->dpString);
|
|
PKMX_WCHAR context = NULL;
|
|
PKMX_WCHAR platform = u16tok(t, u' ', &context);
|
|
while(platform != NULL)
|
|
{
|
|
if(!IsMatchingPlatformString(platform))
|
|
{
|
|
s->dwSystemID = TSS_PLATFORM_NOMATCH;
|
|
delete [] t;
|
|
return FALSE;
|
|
}
|
|
platform = u16tok(NULL, u' ', &context);
|
|
}
|
|
|
|
s->dwSystemID = TSS_PLATFORM_MATCH;
|
|
delete [] t;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* KMX_BOOL ContextMatch( LPKEY kkp );
|
|
*
|
|
* Parameters: kkp Rule to compare
|
|
*
|
|
* Returns: 0 on OK, 1 on not equal
|
|
*
|
|
* Called by: ProcessGroup
|
|
*
|
|
* ContextMatch compares the context of a rule with the current context.
|
|
*//*
|
|
KMX_BOOL KMX_ProcessEvent::ContextMatch(LPKEY kkp)
|
|
{
|
|
KMX_WORD n;
|
|
PKMX_WCHAR p, q, qbuf, temp;
|
|
PKMX_WORD indexp;
|
|
LPSTORE s, t;
|
|
KMX_BOOL bEqual;
|
|
|
|
memset(m_indexStack, 0, GLOBAL_ContextStackSize*sizeof(KMX_WORD));
|
|
|
|
p = kkp->dpContext;
|
|
|
|
if(*p == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if(*p == UC_SENTINEL && *(p+1) == CODE_NUL)
|
|
{
|
|
// If context buf is longer than the context, then obviously not start of doc.
|
|
if(m_context.Buf(xstrlen_ignoreifopt(p))) return FALSE;
|
|
p = incxstr(p);
|
|
if(*p == 0) return TRUE;
|
|
}
|
|
|
|
for(PKMX_WCHAR pp = p; pp && *pp; pp = incxstr(pp))
|
|
{
|
|
if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFOPT)
|
|
{
|
|
s = &m_keyboard.Keyboard->dpStoreArray[(*(pp+2))-1];
|
|
t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1];
|
|
|
|
bEqual = u16cmp(s->dpString, t->dpString) == 0;
|
|
if(*(pp+3) == 1 && bEqual) return FALSE;
|
|
if(*(pp+3) == 2 && !bEqual) return FALSE;
|
|
}
|
|
else if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFSYSTEMSTORE)
|
|
{
|
|
KMX_DWORD dwSystemID = *(pp+2)-1;
|
|
t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1];
|
|
switch(dwSystemID)
|
|
{
|
|
case TSS_PLATFORM_MATCH: // Cached platform result - a matching platform
|
|
bEqual = TRUE;
|
|
break;
|
|
case TSS_PLATFORM_NOMATCH: // Cached platform result - not a matching platform
|
|
bEqual = FALSE;
|
|
break;
|
|
case TSS_PLATFORM:
|
|
bEqual = IsMatchingPlatform(t);
|
|
break;
|
|
case TSS_BASELAYOUT:
|
|
bEqual = IsMatchingBaseLayout(t->dpString);
|
|
break;
|
|
default:
|
|
{
|
|
PKMX_WCHAR ss = GetSystemStore(m_keyboard.Keyboard, dwSystemID);
|
|
if(ss == NULL) return FALSE;
|
|
bEqual = u16cmp(ss, t->dpString) == 0;
|
|
}
|
|
}
|
|
if(*(pp+3) == 1 && bEqual) return FALSE;
|
|
if(*(pp+3) == 2 && !bEqual) return FALSE;
|
|
}
|
|
}
|
|
|
|
q = qbuf = m_context.Buf(xstrlen_ignoreifopt(p));
|
|
if(!q)
|
|
{
|
|
return FALSE; // context buf is too short!
|
|
}
|
|
indexp = m_indexStack;
|
|
|
|
for(; *p && *q; p = incxstr(p))
|
|
{
|
|
*indexp = 0;
|
|
|
|
if(*p == UC_SENTINEL)
|
|
{
|
|
switch(*(p+1))
|
|
{
|
|
case CODE_DEADKEY:
|
|
if(*q != UC_SENTINEL || *(q+1) != CODE_DEADKEY || *(q+2) != *(p+2))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case CODE_ANY:
|
|
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
|
|
|
|
temp = xstrchr(s->dpString, q);
|
|
|
|
if(temp != NULL)
|
|
*indexp = (KMX_WORD) xstrpos(temp, s->dpString);
|
|
|
|
else
|
|
return FALSE;
|
|
break;
|
|
case CODE_NOTANY:
|
|
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
|
|
|
|
if((temp = xstrchr(s->dpString, q)) != NULL)
|
|
return FALSE;
|
|
|
|
break;
|
|
case CODE_INDEX:
|
|
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
|
|
*indexp = n = m_indexStack[(*(p+3))-1];
|
|
|
|
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
|
|
if(n != 0) return FALSE;
|
|
if(xchrcmp(temp, q) != 0) return FALSE;
|
|
break;
|
|
case CODE_CONTEXTEX:
|
|
// only the nth character
|
|
for(n = *(p+2) - 1, temp = qbuf; temp < q && n > 0; n--, temp = incxstr(temp));
|
|
if(n == 0)
|
|
if(xchrcmp(temp, q) != 0) return FALSE;
|
|
break;
|
|
case CODE_IFOPT:
|
|
case CODE_IFSYSTEMSTORE:
|
|
indexp++;
|
|
continue; // don't increment q
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if(xchrcmp(p, q) != 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
indexp++;
|
|
q = incxstr(q);
|
|
}
|
|
|
|
while(*p == UC_SENTINEL && (*(p+1) == CODE_IFOPT || *(p+1) == CODE_IFSYSTEMSTORE)) p = incxstr(p); // already tested
|
|
|
|
return *p == *q; /*at least one must ==0 at this point*/
|
|
}
|
|
/*
|
|
KMX_Actions *KMX_ProcessEvent::GetActions() {
|
|
return &m_actions;
|
|
}
|
|
|
|
KMX_Context *KMX_ProcessEvent::GetContext() {
|
|
return &m_context;
|
|
}
|
|
|
|
KMX_Options *KMX_ProcessEvent::GetOptions() {
|
|
return &m_options;
|
|
}
|
|
|
|
KMX_Options const *KMX_ProcessEvent::GetOptions() const {
|
|
return &m_options;
|
|
}
|
|
|
|
KMX_Environment *KMX_ProcessEvent::GetEnvironment() {
|
|
return &m_environment;
|
|
}
|
|
|
|
KMX_Environment const *KMX_ProcessEvent::GetEnvironment() const {
|
|
return &m_environment;
|
|
}
|
|
|
|
LPINTKEYBOARDINFO KMX_ProcessEvent::GetKeyboard() {
|
|
return &m_keyboard;
|
|
}
|
|
|
|
|
|
KMX_BOOL KMX_ProcessEvent::ReleaseKeyboardMemory(LPKEYBOARD kbd)
|
|
{
|
|
if (!kbd) return TRUE;
|
|
delete kbd;
|
|
return TRUE;
|
|
}
|
|
|
|
PKMX_WCHAR KMX_ProcessEvent::GetSystemStore(LPKEYBOARD kb, KMX_DWORD SystemID)
|
|
{
|
|
for (KMX_DWORD i = 0; i < kb->cxStoreArray; i++)
|
|
if (kb->dpStoreArray[i].dwSystemID == SystemID) return kb->dpStoreArray[i].dpString;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void KMX_ProcessEvent::CreateInternalDebugItems() {
|
|
assert(m_debug_items == nullptr);
|
|
assert(m_kbp_state != nullptr);
|
|
m_debug_items = new KMX_DebugItems(&m_kbp_state->debug_items());
|
|
m_options.SetInternalDebugItems(m_debug_items);
|
|
}
|
|
|
|
void KMX_ProcessEvent::DeleteInternalDebugItems() {
|
|
if(m_debug_items) {
|
|
delete m_debug_items;
|
|
m_debug_items = nullptr;
|
|
}
|
|
m_options.SetInternalDebugItems(m_debug_items);
|
|
}
|