spiegel-keyman/windows/src/engine/keyman32/appint/aiTIP.cpp
Marc Durdin d5790ca9b6 fix(windows): cleanup pointer to int typecasts
Fixes #3084.

This does two things:

1. Cleans up a bunch of places where we used to use `(int)` typecasts
   for pointer math, which was problematic. We now use `(INT_PTR)` per
   MSDN https://docs.microsoft.com/en-us/windows/win32/winprog64/rules-for-using-pointers
   and then cast that down to `(int)` where necessary, e.g. when storing
   string lengths which are never going to be more than a few hundred
   characters! Doing this explicitly helps to clarify that we are aware
   of the typecast and believe it to be safe.

2. Adds in some build infrastructure for future use of Coverity Scan
   https://scan.coverity.com/ which we plan to use for further code
   quality updates. I have submitted the project to Coverity and are
   now waiting for approval so we can check results. Once we have
   approval, I do plan to add this to the nightly build (we need to
   keep submissions under 3 builds/day).

   Note: I have not yet added Keyman Core (Windows) to this project,
   nor are we currently building Keyman Core (macOS) or Keyman for
   Linux, but we should consider adding those in future.
2020-09-24 14:39:59 +10:00

703 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
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)
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);
}
}
}