diff --git a/windows/src/engine/keyman/UfrmKeyman7Main.dfm b/windows/src/engine/keyman/UfrmKeyman7Main.dfm index c62349ce3c..35fe2372ca 100644 --- a/windows/src/engine/keyman/UfrmKeyman7Main.dfm +++ b/windows/src/engine/keyman/UfrmKeyman7Main.dfm @@ -34,4 +34,10 @@ object frmKeyman7Main: TfrmKeyman7Main Left = 280 Top = 104 end + object tmrCheckInputPane: TTimer + Interval = 500 + OnTimer = tmrCheckInputPaneTimer + Left = 96 + Top = 104 + end end diff --git a/windows/src/engine/keyman/UfrmKeyman7Main.pas b/windows/src/engine/keyman/UfrmKeyman7Main.pas index 010e5af2a9..68da5b982a 100644 --- a/windows/src/engine/keyman/UfrmKeyman7Main.pas +++ b/windows/src/engine/keyman/UfrmKeyman7Main.pas @@ -150,6 +150,7 @@ uses Keyman.System.DebugLogClient, Keyman.System.DebugLogManager, + Keyman.System.FrameworkInputPane, keymanapi_TLB, //TOUCH UfrmTouchKeyboard, @@ -198,10 +199,12 @@ type mnu: TPopupMenu; tmrTestKeymanFunctioning: TTimer; tmrOnlineUpdateCheck: TTimer; + tmrCheckInputPane: TTimer; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure tmrTestKeymanFunctioningTimer(Sender: TObject); procedure tmrOnlineUpdateCheckTimer(Sender: TObject); + procedure tmrCheckInputPaneTimer(Sender: TObject); private InMenuLoop: Integer; // I1082 - Avoid menu nasty flicker with rapid click FClosingApp: Boolean; @@ -225,6 +228,9 @@ type FHotkeyWindow: HWND; FHotkeys: TIntegerList; + FInputPane: TFrameworkInputPane; + FIsInputPaneVisible: Boolean; + //TOUCH FCurrentContext: string; function AddTaskbarIcon: Boolean; @@ -533,6 +539,8 @@ begin ChangeWindowMessageFilter(sMsg_TaskbarRestart, MSGFLT_ADD); ChangeWindowMessageFilter(WM_USER_PlatformComms, MSGFLT_ADD); + FInputPane := TFrameworkInputPane.Create; + PostMessage(Handle, WM_USER_Start, 0, 0); end; @@ -543,6 +551,8 @@ end;} procedure TfrmKeyman7Main.FormDestroy(Sender: TObject); begin + FreeAndNil(FInputPane); + ClosePlatformComms64; UnregisterHotkeys; @@ -1766,6 +1776,23 @@ end; //TOUCH else Result := kmcom.Options['koUseTouchLayout'].Value; //TOUCH end; +procedure TfrmKeyman7Main.tmrCheckInputPaneTimer(Sender: TObject); +var + r: TRect; + isVisible: Boolean; +begin + if Assigned(FInputPane) and FInputPane.GetLocation(r) then + begin + isVisible := not r.IsEmpty; + if FIsInputPaneVisible <> isVisible then + begin + FIsInputPaneVisible := isVisible; + kmint.KeymanEngineControl.UpdateTouchPanelVisibility(isVisible); + end; + //TDebugLogClient.Instance.WriteMessage('InputPane Location: %d, %d, %d, %d', [r.Left, r.Top, r.Right, r.Bottom]); + end; +end; + procedure TfrmKeyman7Main.tmrOnlineUpdateCheckTimer(Sender: TObject); begin with TRegistryErrorControlled.Create do // I2890 diff --git a/windows/src/engine/keyman/keyman.dpr b/windows/src/engine/keyman/keyman.dpr index 2f9bed1197..7fc5b4d64a 100644 --- a/windows/src/engine/keyman/keyman.dpr +++ b/windows/src/engine/keyman/keyman.dpr @@ -140,7 +140,8 @@ uses Keyman.System.DebugLogCommon in '..\..\global\delphi\debug\Keyman.System.DebugLogCommon.pas', Keyman.System.SharedBuffers in 'Keyman.System.SharedBuffers.pas', Keyman.System.Security in '..\..\global\delphi\general\Keyman.System.Security.pas', - Keyman.Winapi.VersionHelpers in '..\..\global\delphi\winapi\Keyman.Winapi.VersionHelpers.pas'; + Keyman.Winapi.VersionHelpers in '..\..\global\delphi\winapi\Keyman.Winapi.VersionHelpers.pas', + Keyman.System.FrameworkInputPane in 'touchkeyboard\Keyman.System.FrameworkInputPane.pas'; {$R ICONS.RES} {$R VERSION.RES} diff --git a/windows/src/engine/keyman/keyman.dproj b/windows/src/engine/keyman/keyman.dproj index ff068dcffb..88961fc506 100644 --- a/windows/src/engine/keyman/keyman.dproj +++ b/windows/src/engine/keyman/keyman.dproj @@ -7,7 +7,7 @@ 1 Console VCL - 18.4 + 18.7 Win32 @@ -265,6 +265,7 @@ + Cfg_2 @@ -327,9 +328,9 @@ False - + - keyman.exe + .\ true @@ -339,9 +340,9 @@ true - + - .\ + keyman.exe true @@ -350,7 +351,6 @@ 1 - Contents\MacOS 0 @@ -360,6 +360,12 @@ 1 + + + res\xml + 1 + + library\lib\armeabi-v7a @@ -396,6 +402,18 @@ 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + res\drawable @@ -432,6 +450,36 @@ 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + res\drawable-small @@ -456,6 +504,12 @@ 1 + + + res\values + 1 + + 1 @@ -472,6 +526,10 @@ 1 .framework + + 1 + .framework + 0 @@ -481,6 +539,10 @@ 1 .dylib + + 1 + .dylib + 0 .dll;.bpl @@ -503,6 +565,10 @@ 1 .dylib + + 1 + .dylib + 0 .bpl @@ -524,6 +590,9 @@ 0 + + 0 + 0 @@ -539,6 +608,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -550,6 +630,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -561,6 +674,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -572,6 +740,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -605,6 +883,28 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -634,6 +934,7 @@ 1 + @@ -641,6 +942,10 @@ Contents\Resources 1 + + Contents\Resources + 1 + @@ -662,6 +967,9 @@ 1 + + 1 + 0 @@ -701,6 +1009,7 @@ + diff --git a/windows/src/engine/keyman/touchkeyboard/Keyman.System.FrameworkInputPane.pas b/windows/src/engine/keyman/touchkeyboard/Keyman.System.FrameworkInputPane.pas new file mode 100644 index 0000000000..5b8163b1e9 --- /dev/null +++ b/windows/src/engine/keyman/touchkeyboard/Keyman.System.FrameworkInputPane.pas @@ -0,0 +1,106 @@ +unit Keyman.System.FrameworkInputPane; + +interface + +uses + Winapi.Windows; + +// See https://stackoverflow.com/a/55513524/1836776 +// https://gist.github.com/DelphiWorlds/2098ebafd20aa43f6c5a69503b06c4ca +// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-iframeworkinputpane + +const + CLSID_FrameworkInputPane: TGUID = '{D5120AA3-46BA-44C5-822D-CA8092C1FC72}'; + +type + IFrameworkInputPaneHandler = interface + ['{226C537B-1E76-4D9E-A760-33DB29922F18}'] + + function Showing( + var rcInputPaneScreenLocation: TRect; + fEnsureFocusedElementInView: BOOL + ): HResult; stdcall; + + function Hiding( + fEnsureFocusedElementInView: BOOL + ): HResult; stdcall; + end; + + IFrameworkInputPane = interface + ['{5752238B-24F0-495A-82F1-2FD593056796}'] + + function Advise( + pWindow: IUnknown; + pHandler: IFrameworkInputPaneHandler; + var pdwCookie: DWORD + ): HRESULT; stdcall; + + function AdviseWithHWND( + hwnd: HWND; + pHandler: IFrameworkInputPaneHandler; + var pdwCookie: DWORD + ): HRESULT; stdcall; + + function Unadvise( + dwCookie: DWORD + ): HRESULT; stdcall; + + function Location( + var prcInputPaneScreenLocation: TRect + ): HRESULT; stdcall; + end; + + TFrameworkInputPane = class + private + FInputPane: IFrameworkInputPane; + public + constructor Create; + destructor Destroy; override; + + function GetLocation(var rt: TRECT): Boolean; + end; + +implementation + +uses + Winapi.ActiveX, + Keyman.System.DebugLogClient; + +{ TFrameworkInputWrapper } + +constructor TFrameworkInputPane.Create; +var + hr: HRESULT; +begin + inherited Create; + + FInputPane := nil; + + hr := CoCreateInstance( + CLSID_FrameworkInputPane, nil, CLSCTX_ALL {CLSCTX_INPROC_SERVER}, IFrameworkInputPane, FInputPane); + if not Succeeded(hr) then + begin + // We'll silently fail; this may be an older OS + FInputPane := nil; + TDebugLogClient.Instance.WriteMessage('Unable to instantiate IFrameworkInputPane: %x', [hr]); + end; +end; + +destructor TFrameworkInputPane.Destroy; +begin + FInputPane := nil; + inherited Destroy; +end; + +function TFrameworkInputPane.GetLocation(var rt: TRECT): Boolean; +begin + Result := False; + rt := rt.Empty; + + if Assigned(FInputPane) then + begin + Result := not FAILED(FInputPane.Location(rt)); + end; +end; + +end. diff --git a/windows/src/engine/keyman32/KEYMAN32.DEF b/windows/src/engine/keyman32/KEYMAN32.DEF index cf866b87bb..199297f8e9 100644 --- a/windows/src/engine/keyman32/KEYMAN32.DEF +++ b/windows/src/engine/keyman32/KEYMAN32.DEF @@ -34,3 +34,5 @@ EXPORTS Keyman_ResetInitialisation Keyman_WriteDebugEvent + + Keyman_UpdateTouchPanelVisibility diff --git a/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp b/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp index fc16f037cd..c57f02a796 100644 --- a/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp +++ b/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp @@ -94,6 +94,25 @@ BOOL IsConsoleWindow(HWND hwnd) { return last_isConsoleWindow; } + +/* + Test for touch panel visibility (#2450). UpdateTouchPanelVisibility is called periodically by + keyman.exe to refresh the visibility flag. +*/ + +static BOOL touchPanelVisible; + +void WINAPI Keyman_UpdateTouchPanelVisibility(BOOL isVisible) { + touchPanelVisible = isVisible; + SendDebugMessageFormat(0, sdmAIDefault, 0, "Keyman_UpdateTouchPanelVisibility: isVisible=%d", touchPanelVisible); +} + +BOOL IsTouchPanelVisible() { + // Note: GetCurrentInputMessageSource does not work in this context + // Using IFrameworkInputPaneHandler events only works for a specific window, so not helpful for us. + return touchPanelVisible; +} + /* Cache UseRegisterHotkey debug flag for this session */ @@ -132,6 +151,13 @@ LRESULT _kmnLowLevelKeyboardProc( return CallNextHookEx(Globals::get_hhookLowLevelKeyboardProc(), nCode, wParam, lParam); } + if (IsTouchPanelVisible()) { + // See #2450. The touch panel will close automatically if we reprocess key events + // So we don't want to reprocess events when it is visible. + //SendDebugMessageFormat(0, sdmAIDefault, 0, "kmnLowLevelKeyboardProc: touch panel is visible. Not reprocessing keystrokes"); + return CallNextHookEx(Globals::get_hhookLowLevelKeyboardProc(), nCode, wParam, lParam); + } + DWORD Flag = 0; if (UseRegisterHotkey()) { switch (hs->vkCode) { diff --git a/windows/src/engine/kmcomapi/com/system/keymancontrol.pas b/windows/src/engine/kmcomapi/com/system/keymancontrol.pas index c5d55d478a..693ab1922d 100644 --- a/windows/src/engine/kmcomapi/com/system/keymancontrol.pas +++ b/windows/src/engine/kmcomapi/com/system/keymancontrol.pas @@ -73,6 +73,7 @@ type TKeyman32GetInitialisedFunction = function(var FSingleApp: BOOL): BOOL; stdcall; TKeyman32ControllerSendMessageFunction = function(msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; TKeyman32ControllerPostMessageFunction = procedure(msg: UINT; wParam: WPARAM; lParam: LPARAM); stdcall; + TKeyman32UpdateTouchPanelVisibilityFunction = procedure(Value: BOOL); stdcall; TKeymanControl = class(TKeymanAutoObject, IKeymanCustomisationAccess, IIntKeymanControl, IKeymanControl, IKeymanEngineControl) private @@ -92,6 +93,7 @@ type FKeyman_PostMasterController: TKeyman32ControllerPostMessageFunction; FKeyman_PostControllers: TKeyman32ControllerPostMessageFunction; FKeyman_StartExit: TKeyman32ExitFunction; // I3092 + FKeyman_UpdateTouchPanelVisibility: TKeyman32UpdateTouchPanelVisibilityFunction; procedure LoadKeyman32; procedure StartKeyman32; procedure Do_Keyman_Exit; @@ -133,7 +135,7 @@ type procedure StopKeyman; safecall; procedure StopVisualKeyboard; safecall; - { IKeymanControlRestart } + { IKeymanEngineControl } procedure RestartEngine; safecall; // 32 bit only procedure ShutdownKeyman32Engine; safecall; // 32 bit only procedure StartKeyman32Engine; safecall; // 32 bit only @@ -143,6 +145,7 @@ type procedure UnregisterControllerWindow(Value: LongWord); safecall; // 32 bit only procedure DisableUserInterface; safecall; procedure EnableUserInterface; safecall; + procedure UpdateTouchPanelVisibility(Value: Boolean); safecall; { IIntKeymanControl } procedure AutoApplyKeyman; @@ -570,6 +573,16 @@ begin {$ENDIF} end; +procedure TKeymanControl.UpdateTouchPanelVisibility(Value: Boolean); +begin +{$IFDEF WIN64} + Error(Cardinal(E_NOTIMPL)); +{$ELSE} + LoadKeyman32; + FKeyman_UpdateTouchPanelVisibility(Value); +{$ENDIF} +end; + procedure TKeymanControl.ShutdownKeyman32Engine; begin {$IFDEF WIN64} @@ -707,6 +720,7 @@ begin @FKeyman_SendMasterController := ProcAddr('Keyman_SendMasterController'); @FKeyman_PostMasterController := ProcAddr('Keyman_PostMasterController'); @FKeyman_PostControllers := ProcAddr('Keyman_PostControllers'); + @FKeyman_UpdateTouchPanelVisibility := ProcAddr('Keyman_UpdateTouchPanelVisibility'); end; end; diff --git a/windows/src/global/delphi/general/KeymanEngineControl.pas b/windows/src/global/delphi/general/KeymanEngineControl.pas index 6fae144f2c..5ddf601f1a 100644 --- a/windows/src/global/delphi/general/KeymanEngineControl.pas +++ b/windows/src/global/delphi/general/KeymanEngineControl.pas @@ -31,6 +31,7 @@ type procedure UnregisterControllerWindow(Value: LongWord); safecall; procedure DisableUserInterface; safecall; procedure EnableUserInterface; safecall; + procedure UpdateTouchPanelVisibility(Value: Boolean); safecall; end; implementation