diff --git a/windows/src/engine/keyman32/keymancontrol.h b/windows/include/keymancontrol.h similarity index 94% rename from windows/src/engine/keyman32/keymancontrol.h rename to windows/include/keymancontrol.h index b25d0ceebf..a83749234a 100644 --- a/windows/src/engine/keyman32/keymancontrol.h +++ b/windows/include/keymancontrol.h @@ -67,6 +67,11 @@ #define KMC_LANGUAGEHOTKEY 20 //TOUCH #define KMC_CONTEXT 19 // 9.0.450.0 +// Cross-architecture comms +#define KMC_REGISTER_HOST_WINDOW_X64 23 +#define KMC_REGISTER_HOST_WINDOW_ARM64 24 + #define RWM_KEYMAN_CONTROL "WM_KEYMAN_CONTROL" +#define RWM_KEYMAN_CONTROL_W L"WM_KEYMAN_CONTROL" #define khLanguageSwitch 8 diff --git a/windows/src/desktop/kmshell/main/UfrmMain.pas b/windows/src/desktop/kmshell/main/UfrmMain.pas index e367a795ae..a8fdfe9845 100644 --- a/windows/src/desktop/kmshell/main/UfrmMain.pas +++ b/windows/src/desktop/kmshell/main/UfrmMain.pas @@ -623,20 +623,23 @@ procedure TfrmMain.AppEventsMessage(var Msg: tagMSG; var Handled: Boolean); begin inherited; - if (Msg.message = wm_keyman_refresh) and (Msg.WParam = KR_SETTINGS_CHANGED) then + if Msg.message = wm_keyman_refresh then begin - // If we generated this refresh ourselves, ignore it - if (kmcom <> nil) and (Msg.lParam <> KeymanEngineControl.LastRefreshToken) then + if Msg.WParam = KR_SETTINGS_CHANGED then begin - // The refresh event was generated by another process or thread - // - // We may receive the notification multiple times because it is broadcast - // to all top-level windows and we probably have a bunch of them. - // So, if we've already processed it, don't do so again. - if Msg.lParam <> LastRefreshToken then + // If we generated this refresh ourselves, ignore it + if (kmcom <> nil) and (Msg.lParam <> KeymanEngineControl.LastRefreshToken) then begin - LastRefreshToken := Msg.lParam; - DoRefresh; + // The refresh event was generated by another process or thread + // + // We may receive the notification multiple times because it is broadcast + // to all top-level windows and we probably have a bunch of them. + // So, if we've already processed it, don't do so again. + if Msg.lParam <> LastRefreshToken then + begin + LastRefreshToken := Msg.lParam; + DoRefresh; + end; end; end; end; diff --git a/windows/src/desktop/kmshell/startup/help/UfrmTextEditor.pas b/windows/src/desktop/kmshell/startup/help/UfrmTextEditor.pas index d67143e8ee..f0f46cfc6d 100644 --- a/windows/src/desktop/kmshell/startup/help/UfrmTextEditor.pas +++ b/windows/src/desktop/kmshell/startup/help/UfrmTextEditor.pas @@ -144,7 +144,7 @@ type procedure editorKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); private cefFonts: TframeCEFHost; - wm_keyman_control, wm_keyman_refresh: UINT; + wm_keyman_control: UINT; FUpdating: Boolean; FCheckFontsThread: TCheckFontsThread; FCheckFontKeyboards: TCheckFontKeyboards; @@ -248,7 +248,6 @@ procedure TfrmTextEditor.TntFormCreate(Sender: TObject); begin inherited; wm_keyman_control := RegisterWindowMessage('WM_KEYMAN_CONTROL'); - wm_keyman_refresh := RegisterWindowMessage('WM_KEYMANREFRESH'); HelpTopic := 'context/text-editor'; Caption := MsgFromId(SKTextEditorCaption); @@ -724,7 +723,7 @@ end; procedure TfrmTextEditor.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean); begin - if (Msg.message = wm_keyman_refresh) and (Msg.wParam = KR_REFRESH) then + if (Msg.message = wm_keyman_control) and (Msg.wParam = KMC_REFRESH) then begin StartCheckFontsThread; end; diff --git a/windows/src/desktop/kmshell/util/kmint.pas b/windows/src/desktop/kmshell/util/kmint.pas index 334e0b0409..c63abdbc6c 100644 --- a/windows/src/desktop/kmshell/util/kmint.pas +++ b/windows/src/desktop/kmshell/util/kmint.pas @@ -1,18 +1,18 @@ (* Name: kmint Copyright: Copyright (C) SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 1 Aug 2006 Modified Date: 26 Jun 2012 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 01 Aug 2006 - mcdurdin - Remove old Keyman 6 integration 01 Aug 2006 - mcdurdin - Look for pxx file in startup folder to determine product to start 05 Nov 2007 - mcdurdin - I937, I1128 - Repair COM object if it fails at startup @@ -43,7 +43,6 @@ function LoadKMCOM: Boolean; const KEYMAN_LAYOUT_CUSTOM = $000005FE; - KR_REFRESH = 2; KR_SETTINGS_CHANGED = 3; const @@ -136,7 +135,7 @@ begin end; procedure CoFreeUnusedLibrariesEx(dwUnloadDelay, dwReserved: DWORD); stdcall; external 'ole32.dll'; - + function LoadKMCOM: Boolean; var msg: string; diff --git a/windows/src/engine/keyman/UfrmKeyman7Main.pas b/windows/src/engine/keyman/UfrmKeyman7Main.pas index 4b51f337c1..fd5c24fcf2 100644 --- a/windows/src/engine/keyman/UfrmKeyman7Main.pas +++ b/windows/src/engine/keyman/UfrmKeyman7Main.pas @@ -351,6 +351,7 @@ var wm_keyman_globalswitch, wm_keyman_globalswitch_process, wm_keyman_control, wm_keyman_control_internal, wm_test_keyman_functioning: Cardinal; FEnableCrashTest: Boolean = False; + wm_keyman_refresh: Cardinal; const KMC_StartProduct = 0; @@ -697,6 +698,10 @@ begin end; function TfrmKeyman7Main.AppMessage(var Message: TMessage): Boolean; +var + hwndTextEditor: THandle; +const + KR_PRE_REFRESH = 1; begin if Message.Msg = WM_ACTIVATEAPP then begin @@ -728,6 +733,19 @@ begin end; Result := True; end + else if Message.Msg = wm_keyman_refresh then + begin + if Message.WParam = KR_PRE_REFRESH then + begin + // The text editor needs a special prod for keyboard changes + hwndTextEditor := FindWindow('TfrmTextEditor', nil); + if hwndTextEditor <> 0 then + begin + PostMessage(hwndTextEditor, wm_keyman_control, KMC_REFRESH, 0); + end; + end; + Result := False; + end else if Message.Msg = wm_keyman_control then begin Result := True; @@ -1241,13 +1259,13 @@ begin // #12905: icon is otherwise invalid TKeymanSentryClient.ReportHandledException(E, 'SetTrayIcon: Invalid icon for keyboard "'+kbd.ID+'"; icon filename "'+kbd.IconFilename+'"'); // Use the default app icon - rp.FTrayIcon.Icon.Assign(FTrayIcon); + rp.FTrayIcon.Icon.Assign(FTrayIcon); end; end else begin // Use the default app icon - rp.FTrayIcon.Icon.Assign(FTrayIcon); + rp.FTrayIcon.Icon.Assign(FTrayIcon); end; if VisualKeyboardVisible then @@ -2119,6 +2137,7 @@ initialization wm_keyman_globalswitch_process := RegisterWindowMessage('WM_KEYMAN_GLOBALSWITCH_PROCESS'); wm_keyman_control := RegisterWindowMessage('WM_KEYMAN_CONTROL'); wm_keyman_control_internal := RegisterWindowMessage('WM_KEYMAN_CONTROL_INTERNAL'); // I3933 + wm_keyman_refresh := RegisterWindowMessage('WM_KEYMANREFRESH'); ChangeWindowMessageFilter(wm_keyman_control, MSGFLT_ADD); ChangeWindowMessageFilter(wm_keyman_globalswitch, MSGFLT_ADD); diff --git a/windows/src/engine/keyman/kmint.pas b/windows/src/engine/keyman/kmint.pas index 76170998d0..6d1f937d18 100644 --- a/windows/src/engine/keyman/kmint.pas +++ b/windows/src/engine/keyman/kmint.pas @@ -48,8 +48,7 @@ uses MessageIdentifiers; const - KR_REQUEST_REFRESH = 0; // Send this to any window, which will make Keyman post a KR_REFRESH to all top-level windows - KR_REFRESH = 2; // Finally this message get sent to all the other top-level windows in the system. + KR_REQUEST_REFRESH = 0; // Send this to any window, which will make Keyman post a KR_PRE_REFRESH to controllers, which will cause all threads to eventually refresh keyboards list KR_SETTINGS_CHANGED = 3; function KeymanCustomisation: IKeymanCustomisation; diff --git a/windows/src/engine/keyman32/appint/aiTIP.cpp b/windows/src/engine/keyman32/appint/aiTIP.cpp index be657e4a12..b1b0345556 100644 --- a/windows/src/engine/keyman32/appint/aiTIP.cpp +++ b/windows/src/engine/keyman32/appint/aiTIP.cpp @@ -68,6 +68,7 @@ extern "C" __declspec(dllexport) BOOL WINAPI TIPActivateKeyboard(GUID *profile) PKEYMAN64THREADDATA _td = ThreadGlobals(); if(!_td) return FALSE; + RefreshThreadKeyboardsIfRequired(); RefreshToggleState(); if(profile != NULL) { diff --git a/windows/src/engine/keyman32/globals.h b/windows/src/engine/keyman32/globals.h index 9db8044394..02b799f1ed 100644 --- a/windows/src/engine/keyman32/globals.h +++ b/windows/src/engine/keyman32/globals.h @@ -88,8 +88,15 @@ public: static DWORD *ShiftState(); -#ifndef _WIN64 static LONG *RefreshTag(); + static LONG get_RefreshTag(); + +#ifndef _WIN64 + static HWND get_hwndHostX64(); + static HWND get_hwndHostARM64(); + + static HWND *hwndHostX64(); + static HWND *hwndHostARM64(); #endif static HHOOK get_hhookGetMessage(); @@ -216,8 +223,7 @@ typedef struct tagKEYMAN64THREADDATA BOOL CoreProcessEventRun; // True if core process event has been run BOOL FInRefreshKeyboards; - BOOL RefreshRequired; - LONG RefreshTag_Thread; // TODO: we may be able to eliminate this with our delayed refresh pattern? + LONG RefreshTag_Thread; /* Addin Globals */ diff --git a/windows/src/engine/keyman32/k32_globals.cpp b/windows/src/engine/keyman32/k32_globals.cpp index 807b246466..19d2b0a12e 100644 --- a/windows/src/engine/keyman32/k32_globals.cpp +++ b/windows/src/engine/keyman32/k32_globals.cpp @@ -95,6 +95,12 @@ BOOL isKeymanKeyboardActive = FALSE; static DWORD dwTlsIndex = TLS_OUT_OF_INDEXES; static CRITICAL_SECTION csGlobals; +// In order that every thread starts off needing a refresh, we set the starting +// refresh tag to 1, but the thread-level refresh tag will be 0 (initialized by +// `LocalAlloc(LPTR)`) +#define INITIAL_GLOBAL_REFRESH_TAG 1 +#define INITIAL_THREAD_REFRESH_TAG 0 + PKEYMAN64THREADDATA Globals_InitThread() { EnterCriticalSection(&csGlobals); @@ -269,9 +275,13 @@ static wchar_t __declspec(align(8)) static UINT f_vk_prefix = 0; -#ifndef _WIN64 __declspec(align(8)) static LONG - f_RefreshTag = 0; + f_RefreshTag = INITIAL_GLOBAL_REFRESH_TAG; + +#ifndef _WIN64 +__declspec(align(8)) static HWND + f_hwndHostX64 = 0, + f_hwndHostARM64 = 0; #endif static BOOL @@ -310,8 +320,15 @@ DWORD *Globals::InitialisingThread() { return &f_InitialisingThread; } // I43 DWORD *Globals::ShiftState() { return &f_ShiftState; } -#ifndef _WIN64 LONG *Globals::RefreshTag() { return &f_RefreshTag; } +LONG Globals::get_RefreshTag() { return f_RefreshTag; } + +#ifndef _WIN64 +HWND Globals::get_hwndHostX64() { return f_hwndHostX64; } +HWND *Globals::hwndHostX64() { return &f_hwndHostX64; } + +HWND Globals::get_hwndHostARM64() { return f_hwndHostARM64; } +HWND *Globals::hwndHostARM64() { return &f_hwndHostARM64; } #endif HHOOK Globals::get_hhookCallWndProc() { return f_hhookCallWndProc; } @@ -436,8 +453,10 @@ BOOL Globals::ResetControllers() // I3092 f_FSingleThread = FALSE; f_hwndIM = 0; f_hwndIMAlways = 0; + f_RefreshTag = INITIAL_GLOBAL_REFRESH_TAG; #ifndef _WIN64 - f_RefreshTag = 0; + f_hwndHostX64 = NULL; + f_hwndHostARM64 = NULL; #endif Globals::Unlock(); diff --git a/windows/src/engine/keyman32/keyman32.cpp b/windows/src/engine/keyman32/keyman32.cpp index 312865c61f..c60f674bf5 100644 --- a/windows/src/engine/keyman32/keyman32.cpp +++ b/windows/src/engine/keyman32/keyman32.cpp @@ -106,6 +106,9 @@ HINSTANCE g_hInstance; +void RefreshKeyboards(); +void LoadKeyboardSettings(); + /*******************************************************************************************/ /* */ /* Keyman Initialisation */ @@ -216,7 +219,7 @@ BOOL InitThread() _td->FInitialising = TRUE; // Control re-entrancy, this is thread safe because the variable is per-thread - RefreshKeyboards(TRUE); + LoadKeyboardSettings(); _td->FInitialising = FALSE; // Control re-entrancy, this is thread safe because the variable is per-thread @@ -412,7 +415,9 @@ extern "C" BOOL _declspec(dllexport) WINAPI Keyman_Initialise(HWND Handle, BOOL UpdateActiveWindows(); *Globals::Keyman_Initialised() = TRUE; - RefreshKeyboards(TRUE); // I4786 + + // We initialize keyboards on the controller thread immediately + RefreshThreadKeyboardsIfRequired(); SendDebugMessageFormat("Keyman is now initialised"); @@ -576,27 +581,15 @@ extern "C" DWORD _declspec(dllexport) WINAPI GetActiveKeymanID() // //--------------------------------------------------------------------------------------------------------- -// This function prevents a refresh event from being processed more than once -// by a thread, for instance if a thread has multiple top-level windows -BOOL UpdateRefreshTag(LONG tag) // I1835 - Reduce chatter -{ +void HandleRefresh(int code, LONG tag) { PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return FALSE; - if(_td->RefreshTag_Thread != tag) - { - _td->RefreshTag_Thread = tag; - return TRUE; - } - return FALSE; -} + if(!_td) return; -void HandleRefresh(int code, LONG tag) -{ switch (code) { case KR_REQUEST_REFRESH: // This is sent by Keyman COM API, ApplyToRunningKeymanEngine - SendDebugMessageFormat("#### Refresh Requested ####"); + SendDebugMessageFormat("HandleRefresh(KR_REQUEST_REFRESH, %d); global refresh tag = %d; input thread refresh tag = %d", tag, Globals::get_RefreshTag(), _td->RefreshTag_Thread); // We ask the master controller window to tell all instances // of keyman32/keyman64 that a refresh is coming through @@ -608,41 +601,34 @@ void HandleRefresh(int code, LONG tag) break; case KR_PRE_REFRESH: + + SendDebugMessageFormat("HandleRefresh(KR_PRE_REFRESH, %d); global refresh tag = %d; input thread refresh tag = %d", tag, Globals::get_RefreshTag(), _td->RefreshTag_Thread); + + // The tag parameter will be zero for x86 controller, but will be the + // current tag for other architectures, coming from the x86 controller + + if(Globals::get_InitialisingThread() != GetCurrentThreadId()) { + SendDebugMessageFormat("HandleRefresh: unexpected KR_PRE_REFRESH on a non-controller thread"); + break; + } + #ifndef _WIN64 - // We only need to broadcast the message from Win32; this avoids - // a double-broadcast which could happen if both keyman32 and keyman64 - // receive the message, as they have independently managed RefreshTags + // This RefreshTag is only ever incremented here by the controller thread + // for x86 so this is a thread safe operation + *Globals::RefreshTag() = Globals::get_RefreshTag() + 1; - // This RefreshTag is only ever incremented here by the master controller - // so this is a thread safe operation - tag = ++(*Globals::RefreshTag()); - - if (UpdateRefreshTag(tag)) { - // The Keyman process gets the update first - RefreshKeyboards(FALSE); - PostMessage(HWND_BROADCAST, wm_keyman_refresh, KR_REFRESH, tag); + if(Globals::get_hwndHostX64() != NULL) { + PostMessage(Globals::get_hwndHostX64(), wm_keyman_refresh, KR_PRE_REFRESH, Globals::get_RefreshTag()); } + if(Globals::get_hwndHostARM64() != NULL) { + PostMessage(Globals::get_hwndHostARM64(), wm_keyman_refresh, KR_PRE_REFRESH, Globals::get_RefreshTag()); + } +#else + *Globals::RefreshTag() = tag; #endif - break; - - case KR_REFRESH: - // All threads need to have their keyboard list - // refreshed after an update, but only once per - // refresh request - if (UpdateRefreshTag(tag)) { -#ifdef _WIN64 - if (Globals::get_InitialisingThread() == GetCurrentThreadId()) { - // If this is the keyman.x64 or keymanarm64 thread, then we should - // go ahead and process the refresh immediately so - // that global settings are updated - RefreshKeyboards(FALSE); - } - else -#endif - // We'll update when we are called into action - ScheduleRefresh(); - } + SendDebugMessageFormat("HandleRefresh: new tag = %d", Globals::get_RefreshTag()); + RefreshThreadKeyboardsIfRequired(); break; } } @@ -695,6 +681,40 @@ void LoadBaseLayoutSettings() { // I4552 // I4583 delete reg; } +/** + * If (per-architecture) global refresh tag differs from the current thread + * refresh tag, that indicates that a change has happened to the list of loaded + * keyboards, and we need to refresh the list now. + */ +void RefreshThreadKeyboardsIfRequired() { + PKEYMAN64THREADDATA _td = ThreadGlobals(); + if (!_td) { + return; + } + + SendDebugMessageFormat("RefreshThreadKeyboardsIfRequired(): global refresh tag = %d; input thread refresh tag = %d", Globals::get_RefreshTag(), _td->RefreshTag_Thread); + + if (_td->RefreshTag_Thread != Globals::get_RefreshTag()) { + _td->RefreshTag_Thread = Globals::get_RefreshTag(); + RefreshKeyboards(); + } +} + +/** + * Read the keyboard switch hotkeys, and base layout settings + */ +void LoadKeyboardSettings() { +#ifndef _WIN64 + Hotkeys::Reload(); // I4326 // I4390 +#endif + + // Read global underlying keyboard flags + + if(Globals::get_InitialisingThread() == GetCurrentThreadId()) { + LoadBaseLayoutSettings(); // I4583 + } +} + // ----------------------------------------------------------------------------------------------- // - - // - RefreshKeyboards: .... - @@ -743,32 +763,8 @@ void RefreshKeyboardProfiles(INTKEYBOARDINFO *kp, BOOL isTransient) { } } -void ScheduleRefresh() { - if (GetCurrentThreadId() == GetWindowThreadProcessId(GetForegroundWindow(), NULL)) { - RefreshKeyboards(FALSE); - } - else { - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if (!_td) { - return; - } - _td->RefreshRequired = TRUE; - } -} -void CheckScheduledRefresh() { - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if (!_td) { - return; - } - if (_td->RefreshRequired) { - RefreshKeyboards(FALSE); - _td->RefreshRequired = FALSE; - } -} - -void RefreshKeyboards(BOOL Initialising) -{ +void RefreshKeyboards() { OutputThreadDebugString("RefreshKeyboards"); char sz[_MAX_FNAME]; char oldname[_MAX_FNAME]; @@ -784,29 +780,17 @@ void RefreshKeyboards(BOOL Initialising) oldname[0] = 0; - if(!Initialising) - { - if(_td->lpActiveKeyboard) - strcpy(oldname, _td->lpActiveKeyboard->Name); - - if(_td->lpActiveKeyboard) DeactivateDLLs(_td->lpActiveKeyboard); - _td->lpActiveKeyboard = NULL; - _td->ActiveKeymanID = KEYMANID_NONKEYMAN; + if(_td->lpActiveKeyboard) { + strcpy(oldname, _td->lpActiveKeyboard->Name); } + if(_td->lpActiveKeyboard) DeactivateDLLs(_td->lpActiveKeyboard); + _td->lpActiveKeyboard = NULL; + _td->ActiveKeymanID = KEYMANID_NONKEYMAN; + ReleaseKeyboards(TRUE); - /* Read the "keyboard off hotkey", simulate Alt+Gr, Hotkeys-Toggle flags */ - -#ifndef _WIN64 - Hotkeys::Reload(); // I4326 // I4390 -#endif - - // Read global underlying keyboard flags - - if(Globals::get_InitialisingThread() == GetCurrentThreadId()) { - LoadBaseLayoutSettings(); // I4583 - } + LoadKeyboardSettings(); /* Read the keyboards */ @@ -854,13 +838,13 @@ void RefreshKeyboards(BOOL Initialising) } delete reg; // I2714 - if(!Initialising) - for(int i = 0; i < _td->nKeyboards; i++) - if(!_strcmpi(_td->lpKeyboards[i].Name, oldname)) - { - SelectKeyboard(_td->lpKeyboards[i].KeymanID); // I3594 - break; - } + for(int i = 0; i < _td->nKeyboards; i++) { + if(!_strcmpi(_td->lpKeyboards[i].Name, oldname)) + { + SelectKeyboard(_td->lpKeyboards[i].KeymanID); // I3594 + break; + } + } _td->FInRefreshKeyboards = FALSE; diff --git a/windows/src/engine/keyman32/keyman32.vcxproj b/windows/src/engine/keyman32/keyman32.vcxproj index 5d246b7bbe..dd5643290e 100644 --- a/windows/src/engine/keyman32/keyman32.vcxproj +++ b/windows/src/engine/keyman32/keyman32.vcxproj @@ -1156,7 +1156,7 @@ - + diff --git a/windows/src/engine/keyman32/keyman32.vcxproj.filters b/windows/src/engine/keyman32/keyman32.vcxproj.filters index c5b38a60d1..a812f04249 100644 --- a/windows/src/engine/keyman32/keyman32.vcxproj.filters +++ b/windows/src/engine/keyman32/keyman32.vcxproj.filters @@ -210,7 +210,7 @@ Header Files - + Header Files diff --git a/windows/src/engine/keyman32/keyman64.h b/windows/src/engine/keyman32/keyman64.h index eb199722c7..c062c73cb6 100644 --- a/windows/src/engine/keyman32/keyman64.h +++ b/windows/src/engine/keyman32/keyman64.h @@ -108,7 +108,6 @@ #define KR_REQUEST_REFRESH 0 #define KR_PRE_REFRESH 1 -#define KR_REFRESH 2 #define KR_SETTINGS_CHANGED 3 // Broadcast when Keyman Configuration settings change /* WM_KEY* message analysis */ diff --git a/windows/src/engine/keyman32/keymanengine.h b/windows/src/engine/keyman32/keymanengine.h index 15bf2f1b59..6c769951e5 100644 --- a/windows/src/engine/keyman32/keymanengine.h +++ b/windows/src/engine/keyman32/keymanengine.h @@ -207,12 +207,8 @@ void _OutputThreadDebugString(char* s); /* Keyboard selection functions */ void HandleRefresh(int code, LONG tag); -void RefreshKeyboards(BOOL Initialising); -void CheckScheduledRefresh(); -void ScheduleRefresh(); +void RefreshThreadKeyboardsIfRequired(); void ReleaseKeyboards(BOOL Lock); -void CheckScheduledRefresh(); -void ScheduleRefresh(); /* Glossary conversion functions */ @@ -245,7 +241,7 @@ void keybd_shift(LPINPUT pInputs, int* n, BOOL isReset, LPBYTE const kbd); #include "keystate.h" #include "calldll.h" -#include "keymancontrol.h" +#include "..\..\..\include\keymancontrol.h" #include "keyboardoptions.h" #include "kmprocessactions.h" #include "appcontext.h" diff --git a/windows/src/engine/keyman32/kmhook_callwndproc.cpp b/windows/src/engine/keyman32/kmhook_callwndproc.cpp index eedb06bc0b..38d1fd3909 100644 --- a/windows/src/engine/keyman32/kmhook_callwndproc.cpp +++ b/windows/src/engine/keyman32/kmhook_callwndproc.cpp @@ -163,7 +163,7 @@ LRESULT _kmnCallWndProc(int nCode, WPARAM wParam, LPARAM lParam) { case WM_KILLFOCUS: { - CheckScheduledRefresh(); + RefreshThreadKeyboardsIfRequired(); HWND hwnd = IsLanguageSwitchWindowVisible(); // I4326 if(hwnd != NULL && !Globals::IsControllerThread(GetCurrentThreadId())) SendToLanguageSwitchWindow(hwnd, VK_ESCAPE, 0); // I3025 @@ -178,7 +178,7 @@ LRESULT _kmnCallWndProc(int nCode, WPARAM wParam, LPARAM lParam) } break; case WM_SETFOCUS: - CheckScheduledRefresh(); + RefreshThreadKeyboardsIfRequired(); if (IsSysTrayWindow(cp->hwnd)) // I2443 - always do the focus change now? really unsure about this one SendDebugMessageFormat("WM_SETFOCUS -- not hooking because IsSysTrayWindow"); else if (Globals::IsControllerThread(GetCurrentThreadId())) @@ -193,11 +193,11 @@ LRESULT _kmnCallWndProc(int nCode, WPARAM wParam, LPARAM lParam) case WM_ACTIVATE: if(cp->wParam == WA_ACTIVE || cp->wParam == WA_CLICKACTIVE) { - CheckScheduledRefresh(); + RefreshThreadKeyboardsIfRequired(); } break; case WM_INPUTLANGCHANGE: - CheckScheduledRefresh(); + RefreshThreadKeyboardsIfRequired(); SendDebugMessageFormat("WM_INPUTLANGCHANGE %x %x Hwnd=%x Parent=%x Focus=%x Active=%x", cp->wParam, cp->lParam, cp->hwnd, GetParent(cp->hwnd), GetFocus(), GetActiveWindow()); ReportActiveKeyboard(PC_UPDATE); // I4288 diff --git a/windows/src/engine/keyman32/kmhook_getmessage.cpp b/windows/src/engine/keyman32/kmhook_getmessage.cpp index f000c2661e..cef36c382a 100644 --- a/windows/src/engine/keyman32/kmhook_getmessage.cpp +++ b/windows/src/engine/keyman32/kmhook_getmessage.cpp @@ -157,7 +157,7 @@ LRESULT _kmnGetMessageProc(int nCode, WPARAM wParam, LPARAM lParam) BYTE scan = KEYMSG_LPARAM_SCAN(mp->lParam); BYTE keyTransitionEvent = KEYMSG_FLAG_TRANSITION(mp->lParam); - CheckScheduledRefresh(); + RefreshThreadKeyboardsIfRequired(); UpdateLastKeyCache(_td, mp->wParam, scan, keyTransitionEvent); switch (mp->wParam) { @@ -393,20 +393,24 @@ void ProcessWMKeymanControlInternal(HWND hwnd, WPARAM wParam, LPARAM lParam) void ProcessWMKeymanControl(WPARAM wParam, LPARAM lParam) { - switch (wParam) { - case KMC_PROFILECHANGED: - { - if (!Globals::IsControllerThread(GetCurrentThreadId())) { - break; - } - WORD wAtom = HIWORD(lParam); - char atomStr[128]; - if (GlobalGetAtomName(wAtom, atomStr, 128)) { - isKeymanKeyboardActive = strstr(atomStr, cs_clsidKMTipTextService) != nullptr; - } - break; + if (!Globals::IsControllerThread(GetCurrentThreadId())) { + return; + } + + if (wParam == KMC_PROFILECHANGED) { + WORD wAtom = HIWORD(lParam); + char atomStr[128]; + if (GlobalGetAtomName(wAtom, atomStr, 128)) { + isKeymanKeyboardActive = strstr(atomStr, cs_clsidKMTipTextService) != nullptr; } } +#ifndef _WIN64 + else if(wParam == KMC_REGISTER_HOST_WINDOW_X64) { + *Globals::hwndHostX64() = (HWND) lParam; + } else if(wParam == KMC_REGISTER_HOST_WINDOW_ARM64) { + *Globals::hwndHostARM64() = (HWND) lParam; + } +#endif } /** diff --git a/windows/src/engine/keymanhp/keymanhp.cpp b/windows/src/engine/keymanhp/keymanhp.cpp index 81d386c000..483bbfa701 100644 --- a/windows/src/engine/keymanhp/keymanhp.cpp +++ b/windows/src/engine/keymanhp/keymanhp.cpp @@ -98,9 +98,7 @@ const PWSTR szFail_UnknownError = L"Unknown error %d", szFail_ErrorFormat = L"%s: %s (%d)", - szFail_ErrorFormat_OtherUnknown = L"%s: An unknown error occurred", - - szWindowClass_x86_Wnd = L"TfrmKeyman7Main"; // Do not localize + szFail_ErrorFormat_OtherUnknown = L"%s: An unknown error occurred"; //const char *szGPA_ChangeWindowMessageFilter = "ChangeWindowMessageFilter"; // Do not localize #define KEYMAN_SENTRY_LOGGER_DESKTOP_ENGINE_KEYMANHP KEYMAN_SENTRY_LOGGER_DESKTOP_ENGINE ARCH_LOG_FILE @@ -353,6 +351,15 @@ BOOL StartKeyman(HWND hWnd) if(!Keyman_Initialise(hWnd, FALSE)) return Fail(hWnd, szError_FailedToInitialise); + // Tell the x86 controller our architecture's controller window handle which + // will process wm_keyman_refresh messages (in keyman##.dll) + UINT wm_keyman_control = RegisterWindowMessage(RWM_KEYMAN_CONTROL_W); +#if defined(_M_X64) + PostMessage(hwndController, wm_keyman_control, KMC_REGISTER_HOST_WINDOW_X64, (LPARAM) hWnd); +#elif defined(_M_ARM64) + PostMessage(hwndController, wm_keyman_control, KMC_REGISTER_HOST_WINDOW_ARM64, (LPARAM) hWnd); +#endif + KeymanStarted = TRUE; return TRUE; diff --git a/windows/src/engine/keymanhp/keymanhp.h b/windows/src/engine/keymanhp/keymanhp.h index 28a1f391d5..823a16ad45 100644 --- a/windows/src/engine/keymanhp/keymanhp.h +++ b/windows/src/engine/keymanhp/keymanhp.h @@ -1,18 +1,18 @@ /* Name: keymanhp Copyright: Copyright (C) SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 11 Dec 2009 Modified Date: 11 Dec 2009 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 11 Dec 2009 - mcdurdin - I934 - x64 - Initial version */ #pragma once @@ -23,9 +23,9 @@ #define WINVER 0x0600 // Change this to the appropriate value to target other versions of Windows. #endif -#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. #define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. -#endif +#endif #ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. #define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE. @@ -35,3 +35,5 @@ #include #include #include + +#include "..\..\..\include\keymancontrol.h" \ No newline at end of file diff --git a/windows/src/engine/keymanhp/keymanhp.vcxproj b/windows/src/engine/keymanhp/keymanhp.vcxproj index 3aedb14b70..37e63c2707 100644 --- a/windows/src/engine/keymanhp/keymanhp.vcxproj +++ b/windows/src/engine/keymanhp/keymanhp.vcxproj @@ -280,6 +280,7 @@ true + diff --git a/windows/src/engine/keymanhp/keymanhp.vcxproj.filters b/windows/src/engine/keymanhp/keymanhp.vcxproj.filters index 7c736dcc17..65eddfbff0 100644 --- a/windows/src/engine/keymanhp/keymanhp.vcxproj.filters +++ b/windows/src/engine/keymanhp/keymanhp.vcxproj.filters @@ -32,6 +32,9 @@ Header Files + + Header Files + diff --git a/windows/src/global/delphi/general/KeymanControlMessages.pas b/windows/src/global/delphi/general/KeymanControlMessages.pas index 601abee40a..ac143e2825 100644 --- a/windows/src/global/delphi/general/KeymanControlMessages.pas +++ b/windows/src/global/delphi/general/KeymanControlMessages.pas @@ -63,6 +63,8 @@ const WHR_INIT_FAILURE = 1; // 19.0 - hook failed to (re)install WHR_UNINIT_FAILURE = 2; // 19.0 - hook failed to uninstall + KMC_REGISTER_HOST_WINDOW_X64 = 23; + KMC_REGISTER_HOST_WINDOW_ARM64 = 24; PC_UPDATE = 0; PC_UPDATE_LANGUAGESWITCH = 1;