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