spiegel-keyman/windows/src/engine/keyman32/appint/aiTIP.cpp
2020-10-02 14:54:43 +10:00

706 lines
24 KiB
C++

/*
Name: aiTIP
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Create Date: 19 Jun 2007
Modified Date: 23 Feb 2016
Authors: mcdurdin
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
History: 19 Jun 2007 - mcdurdin - I890 - Fix deadkeys in TSF
13 Jul 2007 - mcdurdin - I934 - Prep for x64
27 Jan 2009 - mcdurdin - I1797 - Add fallback for AIWin2000 app integration
11 Mar 2009 - mcdurdin - I1894 - Fix threading bugs introduced in I1888
07 Sep 2009 - mcdurdin - I2096 - TSF addin should not use fake 0x88 message for langbar icon updates
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
24 Jun 2010 - mcdurdin - I2436 - Add space to context for AIWin2000Unicode when not matched
07 Nov 2012 - mcdurdin - I3549 - V9.0 - x64 version of kmtip addin
17 Nov 2012 - mcdurdin - I3572 - V9.0 - Keyman Engine should avoid processing VK_PACKET
17 Nov 2012 - mcdurdin - I3573 - V9.0 - Don't recreate the TSF AppIntegration with each keystroke
17 Nov 2012 - mcdurdin - I3574 - V9.0 - TIP AppIntegration should inherit from AIWin2000Unicode AppInt in order to support legacy fallback for transitory TSF contexts
17 Nov 2012 - mcdurdin - I3575 - V9.0 - context must be cached for legacy mode in TSF as rules are processed twice
20 Nov 2012 - mcdurdin - I3581 - V9.0 - KMTip needs to pass activated profile guid through to Keyman32 to switch keyboards
20 Nov 2012 - mcdurdin - I3583 - V9.0 - Remove AITIP::NotifyKeyboardChange as obsolete
28 Nov 2012 - mcdurdin - I3594 - V9.0 - Remove old SelectKeyboard code and related messages
28 Nov 2012 - mcdurdin - I3588 - V9.0 - Use preserved keys in TSF to handle key combinations in Keyman
28 Nov 2012 - mcdurdin - I3589 - V9.0 - Handle key state for KeyUp events in TSF
11 Aug 2013 - mcdurdin - I3775 - V9.0 - Fail I3762 - AltGr combinations are not matched on French base layout
11 Nov 2013 - mcdurdin - I3961 - V9.0 - Clean up communication between keyman32 and keyman
24 Apr 2014 - mcdurdin - I4196 - V9.0 - wm_kmmoreposting must be refactored for TIP work as it is not sequential
02 May 2014 - mcdurdin - I4223 - V9.0 - Moving with cursor keys does not reset context
10 Jun 2014 - mcdurdin - I4262 - V9.0 - TSF deadkeys do not function correctly
16 Jun 2014 - mcdurdin - I4272 - V9.0 - TIP only outputs first 127 characters of a rule result
16 Jun 2014 - mcdurdin - I4266 - V9.0 - Deadkeys only work in first 61 characters of document with TIP
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
03 Aug 2014 - mcdurdin - I4278 - V9.0 - PQR test in dead key test fails in TIP
13 Aug 2014 - mcdurdin - I4370 - Deadkeys are still not working in Winword TIP mode
13 Aug 2014 - mcdurdin - I4370 - Deadkeys are still not working in Winword TIP mode
31 Dec 2014 - mcdurdin - I4548 - V9.0 - When Alt is down, release of Ctrl, Shift is not detectable within TIP in some languages
12 Jan 2015 - mcdurdin - I4562 - V9.0 - Forced output of virtual keys no longer works
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
29 Mar 2015 - mcdurdin - I4642 - V9.0 - In Logos, generated backspace receives a scan of 0x00 instead of 0xFF
09 Aug 2015 - mcdurdin - I4793 - Race condition with preserved keys and modifiers
09 Aug 2015 - mcdurdin - I4844 - Tidy up PostDummyKeyEvent calls
23 Feb 2016 - mcdurdin - I4978 - Keyboard options do not apply correctly because they are set twice accidentally
*/
#include "pch.h" // I3583 // I4287
extern const int VKContextReset[256];
DWORD TSFShiftToShift(LPARAM shift); // I3588
extern "C" __declspec(dllexport) BOOL WINAPI TIPActivateKeyboard(GUID *profile) { // I3581
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
if(profile != NULL) {
for(int i = 0; i < _td->nKeyboards; i++) {
for(int j = 0; j < _td->lpKeyboards[i].nProfiles; j++) {
if(IsEqualGUID(_td->lpKeyboards[i].Profiles[j].Guid, *profile)) {
SelectKeyboard(_td->lpKeyboards[i].KeymanID); // I3594
ReportActiveKeyboard(_td, PC_UPDATE); // I3961
return TRUE;
}
}
}
}
SendDebugMessage(0, sdmAIDefault, 0, "TIPActivateKeyboard: Could not find profile");
SelectKeyboard(KEYMANID_NONKEYMAN); // I3594
ReportActiveKeyboard(_td, PC_UPDATE); // I3961
return FALSE;
}
extern "C" __declspec(dllexport) BOOL WINAPI TIPActivateEx(BOOL FActivate) { // I3581
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
if(!FActivate)
SelectKeyboard(KEYMANID_NONKEYMAN); // I3594
Globals::PostMasterController(wm_keyman_control, KMC_SETFOCUSINFO, 0); // I3961
UpdateKeymanUI();
return TRUE;
}
/*
Update the Keyman toggle state based on the key event
*/
void ProcessToggleChange(UINT key) { // I4793
UINT flag = 0;
switch(key) {
case VK_CAPITAL: flag = CAPITALFLAG; break;
case VK_NUMLOCK: flag = NUMLOCKFLAG; break;
default: return;
}
if (GetKeyState(key) & 1) {
SendDebugMessageFormat(0, sdmAIDefault, 0, "ProcessToggleChange: Setting %s", flag == CAPITALFLAG ? "CAPITALFLAG" : "NUMLOCKFLAG");
*Globals::ShiftState() |= flag;
}
else {
SendDebugMessageFormat(0, sdmAIDefault, 0, "ProcessToggleChange: Clearing %s", flag == CAPITALFLAG ? "CAPITALFLAG" : "NUMLOCKFLAG");
*Globals::ShiftState() &= ~flag;
}
}
extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM lParam, // I3589 // I3588
PKEYMANPROCESSOUTPUTFUNC outfunc, PKEYMANGETCONTEXTFUNC ctfunc, BOOL Updateable, BOOL Preserved) {
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) {
return FALSE;
}
WORD keyFlags = HIWORD(lParam);
BOOL isUp = keyFlags & KF_UP ? TRUE : FALSE;
BOOL extended = keyFlags & KF_EXTENDED ? TRUE : FALSE;
BYTE scan = keyFlags & 0xFF;
SendDebugMessageFormat(0, sdmAIDefault, 0, "TIPProcessKey: Enter VirtualKey=%s lParam=%x IsUp=%d Extended=%d Updateable=%d Preserved=%d", Debug_VirtualKey((WORD) wParam), lParam, isUp, extended, Updateable, Preserved);
if(_td->LastKey == wParam && scan == 0) { // I4642
scan = _td->LastScanCode;
SendDebugMessageFormat(0, sdmAIDefault, 0, "TIPProcessKey: Scan code was zero so using cached scan code %x", scan);
}
if(scan == SCAN_FLAG_KEYMAN_KEY_EVENT) { // I4370
if (wParam == VK_CAPITAL && !isUp) {
// Must also record toggle state change when Keyman has generated
// a Caps Lock event
ProcessToggleChange((UINT)wParam); // I4793
}
SendDebugMessageFormat(0, sdmAIDefault, 0, "TIPProcessKey: Virtual Key was generated by Keyman [Scan=0xFF]");
return FALSE;
}
if(!_td->lpActiveKeyboard) {
SendDebugMessageFormat(0, sdmAIDefault, 0, "TIPProcessKey: Fail -- lpActiveKeyboard = NULL");
return FALSE;
}
if(!_td->app) {
_td->app = new AITIP;
if(!_td->app) {
SendDebugMessageFormat(0, sdmAIDefault, 0, "TIPProcessKey: Fail -- could not allocate app");
return FALSE;
}
}
DWORD LocalShiftState = Globals::get_ShiftState();
if(!Preserved) {
switch(wParam) {
case VK_CAPITAL:
if(!isUp) ProcessToggleChange((UINT) wParam); // I4793
if (!Updateable) {
// We only want to process the Caps Lock key event once --
// in the first pass (!Updateable).
KeyCapsLockPress(isUp); // I4548
}
return FALSE;
case VK_SHIFT:
if (!Updateable) {
// We only want to process the Shift key event once --
// in the first pass (!Updateable).
KeyShiftPress(isUp); // I4548
}
// Fall through
case VK_MENU:
case VK_CONTROL:
return FALSE;
case VK_NUMLOCK:
if(!isUp) ProcessToggleChange((UINT) wParam); // I4793
return FALSE;
}
if(isUp) {
return FALSE; // return value ignored in this case; we only needed it for testing anyway
}
} else {
// Mask out Ctrl, Shift and Alt and include new modifiers // I4548
DWORD NewShiftState = TSFShiftToShift(lParam); // I3588
SendDebugMessageFormat(0, sdmGlobal, 0, "TIPProcessKey: TSFShiftToShift start with %x, include %x", LocalShiftState, NewShiftState);
*Globals::ShiftState() = (LocalShiftState & K_NOTMODIFIERFLAG) | NewShiftState; // I3588
}
_td->TIPFUpdateable = Updateable;
_td->TIPFPreserved = Preserved; // I4290
_td->state.msg.hwnd = GetFocus();
_td->state.msg.lParam = 0;
_td->state.msg.message = wm_keymankeydown;
_td->state.vkey = (WORD) wParam;
_td->state.lpkb = _td->lpActiveKeyboard->Keyboard;
_td->state.windowunicode = TRUE;
_td->state.startgroup = &_td->state.lpkb->dpGroupArray[_td->state.lpkb->StartGroup[BEGIN_UNICODE]];
_td->state.NoMatches = TRUE;
_td->state.LoopTimes = 0;
_td->state.StopOutput = FALSE;
_td->state.charCode = CharFromVK(&_td->state.vkey, Globals::get_ShiftState()); // I4582
_td->TIPProcessOutput = outfunc;
_td->TIPGetContext = ctfunc;
AppContextWithStores *savedContext = NULL; // I4370 // I4978
if(!Updateable) { // I4370
savedContext = new AppContextWithStores(_td->lpActiveKeyboard->Keyboard->cxStoreArray); // I4978
_td->app->SaveContext(savedContext);
}
BOOL res = ProcessHook();
if(!Updateable) { // I4370
if(res) { // I4585
// Reset the context if match found
_td->app->RestoreContext(savedContext);
}
delete savedContext;
savedContext = NULL;
}
_td->TIPProcessOutput = NULL;
_td->TIPGetContext = NULL;
SendDebugMessageFormat(0, sdmAIDefault, 0, "TIPProcessKey: Success, res=%d", res);
if(!res && Updateable) { // I4562
//if(wParam <= 0xFF && VKContextReset[wParam]) _td->app->ResetContext(); // I3438 // I4223
SendDebugMessageFormat(0, sdmAIDefault, 0, "TIPProcessKey: key not matched: %s", Debug_VirtualKey((WORD) wParam));
}
if(Preserved) {
// Restore shift state
// TODO: Is this necessary? See keybd_shift_reset() instead and I5394. Some more
// formal mapping of the modifier state machine would be helpful
SendDebugMessageFormat(0, sdmAIDefault, 0, "TIPProcessKey: restoring shift state from %x to %x", Globals::get_ShiftState(), LocalShiftState);
*Globals::ShiftState() = LocalShiftState;
}
return res;
}
/* AI constructor and destructor */
AITIP::AITIP() {
::AIWin2000Unicode(); // I3574
FIsDebugControlWindow = FALSE;
useLegacy = FALSE;
WM_KEYMANDEBUG_CANDEBUG = RegisterWindowMessage("WM_KEYMANDEBUG_CANDEBUG");
WM_KEYMANDEBUG_GETUNICODESTATUS = RegisterWindowMessage("WM_KEYMANDEBUG_GETUNICODESTATUS");
WM_KEYMANDEBUG_GETCONTEXT = RegisterWindowMessage("WM_KEYMANDEBUG_GETCONTEXT");
WM_KEYMANDEBUG_ACTION = RegisterWindowMessage("WM_KEYMANDEBUG_ACTION");
WM_KEYMANDEBUG_RULEMATCH = RegisterWindowMessage("WM_KEYMANDEBUG_RULEMATCH");
}
AITIP::~AITIP() {
}
/* Information functions */
BOOL AITIP::CanHandleWindow(HWND ahwnd) {
UNREFERENCED_PARAMETER(ahwnd);
return TRUE; // I3574
}
BOOL AITIP::HandleWindow(HWND ahwnd) {
FIsDebugControlWindow = IsDebugControlWindow(ahwnd);
return AIWin2000Unicode::HandleWindow(ahwnd); // I3574
}
BOOL AITIP::IsWindowHandled(HWND ahwnd) {
return AIWin2000Unicode::IsWindowHandled(ahwnd); // I3574
}
BOOL AITIP::IsUnicode() {
return TRUE;
}
//#define DEBUG_MERGECONTEXT
#ifdef DEBUG_MERGECONTEXT
char *debugstr(PWSTR buf) {
WCHAR *p;
char *bufout = new char[30*7];
char *q;
for(p = buf, q = bufout; *p && (p-buf < 30); p++)
{
wsprintf(q, "U+%4.4X ", *p); q = strchr(q, 0);
}
//WideCharToMultiByte(CP_ACP, 0, buf, -1, bufout, 128, NULL, NULL);
return bufout;
}
#endif
/* Context functions */
void AITIP::MergeContextWithCache(PWSTR buf, AppContext *local_context) { // I4262
WCHAR tmpbuf[MAXCONTEXT], contextExDeadkeys[MAXCONTEXT];
local_context->Get(tmpbuf, MAXCONTEXT-1);
int n = 0;
PWSTR p = tmpbuf, q = contextExDeadkeys, r = buf; // I4266
while(*p) {
if(*p == UC_SENTINEL) {
p += 2; // We know the only UC_SENTINEL CODE in the context is CODE_DEADKEY, which has only 1 parameter: UC_SENTINEL CODE_DEADKEY <deadkey_id>
n++;
} else {
*q++ = *p;
}
p++;
}
*q = 0;
if(n > 0 && wcslen(buf) > wcslen(contextExDeadkeys)) { // I4266
r += wcslen(buf) - wcslen(contextExDeadkeys);
}
// We have to cut off the context comparison from the left by #deadkeys matched to ensure we are comparing like with like,
// at least when tmpbuf len=MAXCONTEXT-1 at entry.
#ifdef DEBUG_MERGECONTEXT
char *mc1 = debugstr(buf), *mc2 = debugstr(contextExDeadkeys), *mc3 = debugstr(tmpbuf);
SendDebugMessageFormat(0, sdmAIDefault, 0, "AITIP::MergeContextWithCache TIP:'%s' Context:'%s' DKContext:'%s'",
mc1, mc2, mc3);
delete mc1;
delete mc2;
delete mc3;
#endif
if(wcscmp(r, contextExDeadkeys) != 0) {
// context has changed, reset context
#ifdef DEBUG_MERGECONTEXT
SendDebugMessageFormat(0, sdmAIDefault, 0, "AITIP::MergeContextWithCache --> load context from app (losing deadkeys)");
#endif
local_context->Set(buf);
} else {
#ifdef DEBUG_MERGECONTEXT
SendDebugMessageFormat(0, sdmAIDefault, 0, "AITIP::MergeContextWithCache --> loading cached context");
#endif
wcscpy_s(buf, MAXCONTEXT, tmpbuf);
}
}
void AITIP::ReadContext() {
if(DebugControlled()) {
Debug_FillContextBuffer();
return;
}
WCHAR buf[MAXCONTEXT];
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return;
if(_td->TIPGetContext && (*_td->TIPGetContext)(MAXCONTEXT-1, buf) == S_OK) { // I3575 // I4262
if(ShouldDebug(sdmKeyboard)) {
SendDebugMessageFormat(0, sdmAIDefault, 0, "AITIP::ReadContext: full context [Updateable=%d] %s", _td->TIPFUpdateable, Debug_UnicodeString(buf));
}
useLegacy = FALSE; // I3575
// If the text content of the context is identical, inject the deadkeys
// Otherwise, reset the cachedContext to match buf, no deadkeys
MergeContextWithCache(buf, context);
if(ShouldDebug(sdmKeyboard)) {
SendDebugMessageFormat(0, sdmAIDefault, 0, "AITIP::ReadContext: after merge [Updateable=%d] %s", _td->TIPFUpdateable, Debug_UnicodeString(buf));
}
context->Set(buf);
} else {
SendDebugMessageFormat(0, sdmAIDefault, 0, "AITIP::ReadContext: transitory context, so use buffered context [Updateable=%d]", _td->TIPFUpdateable);
useLegacy = TRUE; // I3575
}
}
AppContextWithStores::AppContextWithStores(int nKeyboardOptions) : AppContext() { // I4978
this->nKeyboardOptions = nKeyboardOptions;
KeyboardOptions = new INTKEYBOARDOPTIONS[nKeyboardOptions];
memset(KeyboardOptions, 0, sizeof(INTKEYBOARDOPTIONS) * nKeyboardOptions);
}
AppContextWithStores::~AppContextWithStores() { // I4978
for(DWORD i = 0; i < nKeyboardOptions; i++) {
if(KeyboardOptions[i].Value) delete KeyboardOptions[i].Value;
}
delete KeyboardOptions;
}
void AITIP::SaveContext(AppContextWithStores *savedContext) { // I4370 // I4978
savedContext->CopyFrom(context);
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td || !_td->lpActiveKeyboard || !_td->lpActiveKeyboard->Keyboard) return;
assert(savedContext->nKeyboardOptions == _td->lpActiveKeyboard->Keyboard->cxStoreArray);
for(DWORD i = 0; i < savedContext->nKeyboardOptions; i++) { // I4978
if(_td->lpActiveKeyboard->KeyboardOptions[i].Value != NULL) {
savedContext->KeyboardOptions[i].Value = new WCHAR[wcslen(_td->lpActiveKeyboard->KeyboardOptions[i].Value)+1];
wcscpy_s(savedContext->KeyboardOptions[i].Value, wcslen(_td->lpActiveKeyboard->KeyboardOptions[i].Value)+1, _td->lpActiveKeyboard->KeyboardOptions[i].Value);
}
}
}
void AITIP::RestoreContext(AppContextWithStores *savedContext) { // I4370 // I4978
context->CopyFrom(savedContext);
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td || !_td->lpActiveKeyboard || !_td->lpActiveKeyboard->Keyboard) return;
LPINTKEYBOARDINFO kp = _td->lpActiveKeyboard;
assert(savedContext->nKeyboardOptions == kp->Keyboard->cxStoreArray);
for(DWORD i = 0; i < savedContext->nKeyboardOptions; i++) { // I4978
if(kp->KeyboardOptions[i].Value == NULL && savedContext->KeyboardOptions[i].Value != NULL) {
// Restore the previously saved value as it was reset
kp->KeyboardOptions[i].OriginalStore = kp->Keyboard->dpStoreArray[i].dpString;
kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].Value = savedContext->KeyboardOptions[i].Value;
savedContext->KeyboardOptions[i].Value = NULL;
} else if(kp->KeyboardOptions[i].Value != NULL && savedContext->KeyboardOptions[i].Value == NULL) {
// Clear the newly saved value back to the default
delete kp->KeyboardOptions[i].Value;
kp->KeyboardOptions[i].Value = NULL;
kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].OriginalStore;
} else if(kp->KeyboardOptions[i].Value != NULL && savedContext->KeyboardOptions[i].Value != NULL &&
wcscmp(kp->KeyboardOptions[i].Value, savedContext->KeyboardOptions[i].Value) != 0) {
// Restore the previously saved value as it was changed
delete kp->KeyboardOptions[i].Value;
kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].Value = savedContext->KeyboardOptions[i].Value;
savedContext->KeyboardOptions[i].Value = NULL;
}
}
}
/* Output actions */
BOOL AITIP::QueueAction(int ItemType, DWORD dwData) {
if(DebugControlled()) {
switch(ItemType) {
case QIT_VKEYDOWN:
if((dwData & QVK_KEYMASK) <= VK__MAX && VKContextReset[(BYTE) dwData]) context->Reset(); // I3438 // I4370
break;
case QIT_DEADKEY:
context->Add(UC_SENTINEL); // I4370
context->Add(CODE_DEADKEY); // I4370
context->Add((WORD) dwData); // I4370
break;
case QIT_CHAR:
context->Add((WORD) dwData); // I4370
break;
case QIT_BACK:
if(dwData == BK_BACKSPACE)
while(context->CharIsDeadkey()) context->Delete(); // I4370
context->Delete(); // I4370
if(dwData == BK_BACKSPACE)
while(context->CharIsDeadkey()) context->Delete(); // I4370
break;
}
SendMessage(GetDebugControlWindow(), WM_KEYMANDEBUG_ACTION, ItemType, dwData);
return TRUE;
}
return AIWin2000Unicode::QueueAction(ItemType, dwData); // I3575
}
BOOL AITIP::SendActions() { // I4196
if(DebugControlled()) {
return TRUE;
}
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return FALSE;
if(!_td->TIPFUpdateable) { // I3575
QueueSize = 0;
return TRUE;
}
if(useLegacy) { // I3575
return AIWin2000Unicode::SendActions(); // I3575 // I4196
}
if(*Globals::hwndIM()) {
PostMessage(*Globals::hwndIM(), wm_keymanim_contextchanged, 0, 0);
}
return PostKeys();
}
BOOL AITIP::PostKeys() {
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) {
return FALSE;
}
if(QueueSize == 0) {
return TRUE;
}
int bk=0;
WCHAR *OutBuf = new WCHAR[QueueSize+1], *p = OutBuf; // I4272
*OutBuf = 0;
for(int n = 0; n < QueueSize; n++) {
switch(Queue[n].ItemType) {
case QIT_CHAR:
*p++ = (WCHAR) Queue[n].dwData;
*p = 0;
break;
case QIT_BELL:
MessageBeep(MB_ICONASTERISK);
break;
case QIT_BACK:
if(Queue[n].dwData == BK_DEADKEY) break;
if(p > OutBuf) {
*(--p) = 0;
} else {
bk++;
}
break;
}
}
if(_td->TIPProcessOutput && (bk > 0 || OutBuf[0])) {
SendDebugMessageFormat(0, sdmAIDefault, 0, "AITIP::PostKeys: output bk=%d outcount=%d", bk, (INT_PTR)(p-OutBuf));
(*_td->TIPProcessOutput)(bk, OutBuf, (int)(INT_PTR)(p - OutBuf));
} else {
SendDebugMessageFormat(0, sdmAIDefault, 0, "AITIP::PostKeys: no output");
}
delete[] OutBuf; // I4272
QueueSize = 0;
return TRUE;
}
/* Utility functions */
DWORD TSFShiftToShift(LPARAM shift) // I3588
{
DWORD res = 0;
if(shift & TF_MOD_ALT) res |= LALTFLAG;
if(shift & TF_MOD_CONTROL) res |= LCTRLFLAG;
if(shift & TF_MOD_SHIFT) res |= K_SHIFTFLAG;
if(shift & TF_MOD_LALT) res |= LALTFLAG;
if(shift & TF_MOD_RALT) res |= RALTFLAG;
if((shift & (TF_MOD_RALT|TF_MOD_LCONTROL)) == (TF_MOD_RALT|TF_MOD_LCONTROL)) res |= RALTFLAG; // I3775
else if(shift & TF_MOD_LCONTROL) res |= LCTRLFLAG;
if(shift & TF_MOD_RCONTROL) res |= RCTRLFLAG;
return res;
}
/* Debug Integration */
BOOL AITIP::IsDebugControlWindow(HWND hwnd)
{
static int WM_KEYMANDEBUG_CANDEBUG = RegisterWindowMessage("WM_KEYMANDEBUG_CANDEBUG");
DWORD_PTR dwResult;
SendMessageTimeout(hwnd, WM_KEYMANDEBUG_CANDEBUG, 0, 0, SMTO_BLOCK, 50, &dwResult);
return dwResult != 0;
}
HWND AITIP::GetDebugControlWindow()
{
if(!FIsDebugControlWindow) return NULL;
return hwnd;
}
BOOL AITIP::DebugControlled()
{
return FIsDebugControlWindow;
}
void AITIP::Debug_FillContextBuffer()
{
WCHAR buf[MAXCONTEXT];
if(DebugControlled() &&
SendMessage(GetDebugControlWindow(), WM_KEYMANDEBUG_GETCONTEXT, MAXCONTEXT, (LPARAM) buf))
{
context->Set(buf); // I4370
SendDebugMessageFormat(0, sdmKeyboard, 0, "AIDebugger::FillContextBuffer(%ls)", buf);
}
else
{
context->Reset(); // I4370
SendDebugMessageFormat(0, sdmKeyboard, 0, "AIDebugger::FillContextBuffer()-Reset");
}
}
#define MAXSTOREOFFSETS 20
struct AIDEBUGINFO
{
int cbSize;
int ItemType;
PWSTR Context, Output;
LPKEY Rule;
LPGROUP Group;
DWORD_PTR Flags;
WORD StoreOffsets[MAXSTOREOFFSETS*2+1]; // pairs--store, char position, terminated by 0xFFFF
};
void FillStoreOffsets(AIDEBUGINFO *di)
{
int i, n;
PWSTR p;
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return;
for(i = n = 0, p = di->Rule->dpContext; *p; p = incxstr(p), i++)
{
if(*p == UC_SENTINEL && (*(p+1) == CODE_ANY || *(p+1) == CODE_NOTANY))
{
di->StoreOffsets[n++] = *(p+2) - 1;
di->StoreOffsets[n++] = _td->IndexStack[i];
}
if(*p == UC_SENTINEL && *(p+1) == CODE_INDEX)
{
di->StoreOffsets[n++] = *(p+2) - 1;
di->StoreOffsets[n++] = _td->IndexStack[*(p+3) - 1];
}
if(n == MAXSTOREOFFSETS*2) break;
}
if(n < MAXSTOREOFFSETS*2 - 1)
for(p = di->Rule->dpOutput; *p; p = incxstr(p))
{
if(*p == UC_SENTINEL && *(p+1) == CODE_INDEX)
{
di->StoreOffsets[n++] = *(p+2) - 1;
di->StoreOffsets[n++] = _td->IndexStack[*(p+3) - 1];
}
if(n == MAXSTOREOFFSETS*2) break;
}
di->StoreOffsets[n] = 0xFFFF;
}
BOOL AITIP::QueueDebugInformation(int ItemType, LPGROUP Group, LPKEY Rule, PWSTR fcontext, PWSTR foutput, DWORD_PTR dwExtraFlags)
{
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td) return TRUE;
if(!_td->ForceFileName[0]) return TRUE;
SendDebugMessageFormat(0, sdmAIDefault, 0, "AIDebugger::QueueDebugInformation ItemType=%d", ItemType);
AIDEBUGINFO di;
di.cbSize = sizeof(AIDEBUGINFO);
di.ItemType = ItemType; // int
di.Context = fcontext; // PWSTR
di.Rule = Rule; // LPKEY
di.Group = Group; // LPGROUP
di.Output = foutput; // PWSTR
di.Flags = dwExtraFlags; // DWORD
if(di.Rule) FillStoreOffsets(&di);
// data required
// keystroke
// context for rule
// if rule, then output of rule
// match positions for all stores in rule
if(DebugControlled())
SendMessage(GetDebugControlWindow(), WM_KEYMANDEBUG_RULEMATCH, ItemType, (LPARAM) &di);
return TRUE;
}
typedef BOOL(WINAPI *PREFRESHPRESERVEDKEYSFUNC)(BOOL Activating);
void RefreshPreservedKeys(BOOL Activating) {
#ifdef _WIN64
HMODULE hModule = GetModuleHandle("kmtip64");
#else
HMODULE hModule = GetModuleHandle("kmtip");
#endif
if (hModule != NULL) {
PREFRESHPRESERVEDKEYSFUNC pRefreshPreservedKeys = (PREFRESHPRESERVEDKEYSFUNC)GetProcAddress(hModule, "RefreshPreservedKeys");
if (pRefreshPreservedKeys) {
pRefreshPreservedKeys(Activating);
}
}
}