From 57b3bd70d16db5152fc56a0897121b483795c07a Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 25 Jan 2022 16:50:36 +1100 Subject: [PATCH] feat(windows): handle key output in testhost In order to avoid the test host interfering too badly with the debugger, we will bypass SendInput and instead post all input to the local edit window. This can result in out-of-sequence input for rapid typing, but for a test host this is not important -- it's better that the input doesn't end up in the debugger window due to focus changes! --- windows/src/engine/keyman32/KEYMAN32.DEF | 2 + .../keyman32/appint/aiWin2000Unicode.cpp | 7 +- windows/src/engine/keyman32/globals.h | 10 +++ windows/src/engine/keyman32/k32_globals.cpp | 6 ++ .../src/engine/keyman32/keyboardoptions.cpp | 3 - windows/src/engine/testhost/testhost.cpp | 83 ++++++++++++++++++- 6 files changed, 105 insertions(+), 6 deletions(-) diff --git a/windows/src/engine/keyman32/KEYMAN32.DEF b/windows/src/engine/keyman32/KEYMAN32.DEF index 5368ed4035..84f0f24a91 100644 --- a/windows/src/engine/keyman32/KEYMAN32.DEF +++ b/windows/src/engine/keyman32/KEYMAN32.DEF @@ -44,3 +44,5 @@ EXPORTS Keyman_UnregisterMasterController Keyman_RegisterControllerThread Keyman_UnregisterControllerThread + + SetCustomPostKeyCallback diff --git a/windows/src/engine/keyman32/appint/aiWin2000Unicode.cpp b/windows/src/engine/keyman32/appint/aiWin2000Unicode.cpp index 52f051c63c..19da87a543 100644 --- a/windows/src/engine/keyman32/appint/aiWin2000Unicode.cpp +++ b/windows/src/engine/keyman32/appint/aiWin2000Unicode.cpp @@ -191,10 +191,15 @@ BOOL AIWin2000Unicode::PostKeys() return TRUE; } + if (_td->CustomPostKeyCallback != nullptr) { + BOOL res = _td->CustomPostKeyCallback(Queue, QueueSize); + QueueSize = 0; + return res; + } + int n = 0; /* 6.0.153.0: Fix repeat state for virtual keys */ - LPINPUT pInputs = new INPUT[QueueSize*100]; // TODO: Tidy this up. Horrid doing a junky alloc like this each event int i = 0; diff --git a/windows/src/engine/keyman32/globals.h b/windows/src/engine/keyman32/globals.h index 03f82a4393..da71ff8f81 100644 --- a/windows/src/engine/keyman32/globals.h +++ b/windows/src/engine/keyman32/globals.h @@ -188,6 +188,8 @@ typedef struct tagKEYMANHKLPAIR #define MAXCACHEDKEYBOARDLAYOUTS 128 +typedef BOOL (WINAPI *CUSTOMPOSTKEYCALLBACKPROC)(APPACTIONQUEUEITEM* Queue, int QueueSize); + typedef struct tagKEYMAN64THREADDATA { LPINTKEYBOARDINFO lpKeyboards; // keyboard definitions @@ -252,6 +254,10 @@ typedef struct tagKEYMAN64THREADDATA ISerialKeyEventClient *pSerialKeyEventClient; ISharedBufferManager *pSharedBufferManager; + /* Test host integration */ + + CUSTOMPOSTKEYCALLBACKPROC CustomPostKeyCallback; + } KEYMAN64THREADDATA, *PKEYMAN64THREADDATA; extern UINT @@ -293,4 +299,8 @@ BOOL Globals_ProcessInitialised(); BOOL Reg_GetDebugFlag(LPSTR pszFlagRegistrySetting, BOOL bDefault); +/* Test Host integration */ + +void WINAPI SetCustomPostKeyCallback(CUSTOMPOSTKEYCALLBACKPROC proc); + #endif diff --git a/windows/src/engine/keyman32/k32_globals.cpp b/windows/src/engine/keyman32/k32_globals.cpp index f49e632714..8aa8b76ee7 100644 --- a/windows/src/engine/keyman32/k32_globals.cpp +++ b/windows/src/engine/keyman32/k32_globals.cpp @@ -711,3 +711,9 @@ void Globals::LoadDebugSettings() { f_debug_ToConsole = FALSE; // I3951 } } + +void WINAPI SetCustomPostKeyCallback(CUSTOMPOSTKEYCALLBACKPROC proc) { + PKEYMAN64THREADDATA _td = ThreadGlobals(); + if (!_td) return; + _td->CustomPostKeyCallback = proc; +} diff --git a/windows/src/engine/keyman32/keyboardoptions.cpp b/windows/src/engine/keyman32/keyboardoptions.cpp index 6e272ad415..3e5edcff5b 100644 --- a/windows/src/engine/keyman32/keyboardoptions.cpp +++ b/windows/src/engine/keyman32/keyboardoptions.cpp @@ -58,9 +58,6 @@ void LoadSharedKeyboardOptions(LPINTKEYBOARDINFO kp) void FreeKeyboardOptions(LPINTKEYBOARDINFO kp) { - if (!DebugAssert(!Globals::get_CoreIntegration(), "FreeKeyboardOptions: Error called in core integration mode")) { - return; - } // This is a cleanup routine; we don't want to precondition all calls to it // so we do not assert if (kp == NULL || kp->Keyboard == NULL || kp->KeyboardOptions == NULL) diff --git a/windows/src/engine/testhost/testhost.cpp b/windows/src/engine/testhost/testhost.cpp index 0bd42e1d5b..8942a14f71 100644 --- a/windows/src/engine/testhost/testhost.cpp +++ b/windows/src/engine/testhost/testhost.cpp @@ -180,6 +180,9 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_DESTROY: PostQuitMessage(0); break; + case WM_SETFOCUS: + SetFocus(hWndEdit); + break; default: return DefWindowProc(hWnd, message, wParam, lParam); } @@ -208,13 +211,86 @@ INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) HMODULE hlibKeyman32 = nullptr; +/* From appint.h */ + +typedef struct +{ + int ItemType; + DWORD dwData; +} APPACTIONQUEUEITEM; + +// QueueAction ItemTypes +#define QIT_VKEYDOWN 0 +#define QIT_VKEYUP 1 +#define QIT_VSHIFTDOWN 2 +#define QIT_VSHIFTUP 3 +#define QIT_CHAR 4 +#define QIT_DEADKEY 5 +#define QIT_BELL 6 +#define QIT_BACK 7 + +#define QVK_EXTENDED 0x00010000 // Flag for QIT_VKEYDOWN to indicate an extended key +#define QVK_KEYMASK 0x0000FFFF +#define QVK_FLAGMASK 0xFFFF0000 + + + +typedef BOOL(WINAPI* CUSTOMPOSTKEYCALLBACKPROC)(APPACTIONQUEUEITEM* Queue, int QueueSize); + typedef BOOL(WINAPI* KEYMAN_INITIALISE)(HWND hwnd, BOOL singleApp); typedef BOOL(WINAPI* KEYMAN_EXIT)(); typedef BOOL(WINAPI* KEYMAN_REGISTERMASTERCONTROLLER)(HWND hwnd); +typedef void(WINAPI* SETCUSTOMPOSTKEYCALLBACK)(CUSTOMPOSTKEYCALLBACKPROC proc); KEYMAN_INITIALISE Keyman_Initialise = nullptr; KEYMAN_EXIT Keyman_Exit = nullptr; KEYMAN_REGISTERMASTERCONTROLLER Keyman_RegisterMasterController = nullptr; +SETCUSTOMPOSTKEYCALLBACK SetCustomPostKeyCallback = nullptr; + +BOOL WINAPI PostKeyCallback(APPACTIONQUEUEITEM* Queue, int QueueSize) { + // Note: copied from aiWin2000Unicode and dumbed down for test purposes + for (int n = 0; n < QueueSize; n++) { + switch (Queue[n].ItemType) { + case QIT_VKEYDOWN: + if ((Queue[n].dwData & QVK_KEYMASK) == 0x05) Queue[n].dwData = (Queue[n].dwData & QVK_FLAGMASK) | VK_RETURN; // I649 // I3438 + if ((Queue[n].dwData & QVK_KEYMASK) <= 255) { + PostMessage(hWndEdit, WM_KEYDOWN, Queue[n].dwData & 0xFF, 0); // TODO: lparam + //pInputs[i].ki.wScan = SCAN_FLAG_KEYMAN_KEY_EVENT; + //pInputs[i].ki.dwFlags = ((Queue[n].dwData & QVK_EXTENDED) ? KEYEVENTF_EXTENDEDKEY : 0); + } + break; + case QIT_VKEYUP: + if ((Queue[n].dwData & QVK_KEYMASK) == 0x05) Queue[n].dwData = (Queue[n].dwData & QVK_FLAGMASK) | VK_RETURN; // I649 // I3438 + if ((Queue[n].dwData & QVK_KEYMASK) <= 255) { + PostMessage(hWndEdit, WM_KEYUP, Queue[n].dwData & 0xFF, 0); // TODO: lparam + //pInputs[i].ki.wScan = SCAN_FLAG_KEYMAN_KEY_EVENT; + //pInputs[i].ki.dwFlags = KEYEVENTF_KEYUP | ((Queue[n].dwData & QVK_EXTENDED) ? KEYEVENTF_EXTENDEDKEY : 0); // I3438 + } + break; + case QIT_VSHIFTDOWN: + break; + case QIT_VSHIFTUP: + break; + case QIT_CHAR: + // TODO: surrogate pairs + PostMessage(hWndEdit, WM_CHAR, (WORD)Queue[n].dwData, 0); + break; + case QIT_DEADKEY: + break; + case QIT_BELL: + MessageBeep(MB_ICONASTERISK); + break; + case QIT_BACK: +#define BK_DEADKEY 1 + if (Queue[n].dwData & BK_DEADKEY) break; + PostMessage(hWndEdit, WM_CHAR, 8, 0); + break; + } + } + + return TRUE; +} + void Fail(PCWSTR message) { @@ -261,7 +337,7 @@ StartKeyman() { keyman32 = fs::path(keyman_root.data()) / "windows" / "src" / "engine" / KEYMAN32_DEBUG; if (!fs::exists(keyman32)) { keyman32 = fs::path(keyman_root.data()) / "windows" / "src" / "engine" / KEYMAN32_RELEASE; - + if (!fs::exists(keyman32)) keyman32 = ""; } @@ -286,7 +362,8 @@ StartKeyman() { Keyman_Exit = (KEYMAN_EXIT)GetProcAddress(hlibKeyman32, "Keyman_Exit"); Keyman_RegisterMasterController = (KEYMAN_REGISTERMASTERCONTROLLER)GetProcAddress(hlibKeyman32, "Keyman_RegisterMasterController"); - if (!Keyman_Initialise || !Keyman_Exit || !Keyman_RegisterMasterController) { + SetCustomPostKeyCallback = (SETCUSTOMPOSTKEYCALLBACK)GetProcAddress(hlibKeyman32, "SetCustomPostKeyCallback"); + if (!Keyman_Initialise || !Keyman_Exit || !Keyman_RegisterMasterController || !SetCustomPostKeyCallback) { Keyman_Exit = nullptr; StopKeyman(); Fail(L"Failed to get proc addresses"); @@ -298,6 +375,7 @@ StartKeyman() { Fail(L"Failed to initialise Keyman Engine"); return; } + SetCustomPostKeyCallback(PostKeyCallback); } void @@ -313,5 +391,6 @@ StopKeyman() { Keyman_Initialise = nullptr; Keyman_Exit = nullptr; Keyman_RegisterMasterController = nullptr; + SetCustomPostKeyCallback = nullptr; hlibKeyman32 = nullptr; }