spiegel-keyman/windows/src/engine/keyman/GlobalKeyboardChangeManager.pas
Marc Durdin 738e1946a6 [Windows] Debug logging and proof of concept tests for keyboarding support within metro-style apps
[Windows] More tidyup and robustness for metro app support - debug cleanup and serialization of input (not quite finished)

[windows] Refactor serialized input code when used with key event thread model

[windows] Add consistent precompiled headers for other projects

[windows] Merge console window test into metro support

[Windows] Tidy up work and identify additional TODOs for metro-style app support

[Windows] Ensure error case falls through to default hook processing for console windows

[Windows] Refactor shared memory into memory mapped file so we can cross 32-64 bit boundary

[Windows] Tweaks to C++ security calls and parameters

[Windows] Start refactor of SerialKeyEvent* classes

[Windows] Rename to SerialKeyEventServer (refactoring)

[Windows] Complete refactoring of SerialKeyEventClient class

[Windows] Further encapsulation and cleanup with 'interfaces' to reduce header pollution

[Windows] Complete serialization fix with move of modifier state management from client thread to server thread to guarantee consistency

[Windows] Replace atom-based keyboard switching with memory mapped file indexed to avoid security constraints

[Windows] Fixup Left Alt+Shift interaction with serializer

[Windows] Use Windows 8.1 SDK for test
2018-10-18 20:00:49 +11:00

142 lines
3.3 KiB
ObjectPascal

unit GlobalKeyboardChangeManager;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.Classes,
System.SysUtils,
Keyman.System.SharedBuffers,
LangSwitchManager;
type
TGlobalKeyboardChangeManager = class(TThread)
private type
TLastKeyboard = record
TIP: TSelectKeyboardBuffer;
Keyboard: HKL;
end;
private var
FWindow: HWND;
FLastKeyboard: TLastKeyboard;
procedure WndProc(var Message: TMessage);
procedure ProcessChange(Mode: Integer; Param: NativeUInt);
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
procedure PostChange(Keyboard: TLangSwitchKeyboard);
end;
implementation
uses
Keyman.System.DebugLogClient,
UfrmKeyman7Main;
const
WM_USER_DESTROY = WM_USER + 1;
WM_USER_CHANGE = WM_USER + 2;
{ TGlobalKeyboardChangeManager }
constructor TGlobalKeyboardChangeManager.Create;
begin
inherited Create(False);
end;
destructor TGlobalKeyboardChangeManager.Destroy;
begin
if FWindow <> 0 then
PostMessage(FWindow, WM_USER_DESTROY, 0, 0);
inherited Destroy;
end;
procedure TGlobalKeyboardChangeManager.Execute;
var
msg: TMsg;
begin
FWindow := AllocateHwnd(WndProc); // TODO: Not thread safe!
while GetMessage(msg, 0, 0, 0) do
begin
DispatchMessage(msg);
end;
DeallocateHwnd(FWindow);
end;
procedure TGlobalKeyboardChangeManager.PostChange(
Keyboard: TLangSwitchKeyboard);
var
TIP: TLangSwitchKeyboard_TIP;
WinKB: TLangSwitchKeyboard_WinKeyboard;
lk: TLastKeyboard;
FIdentity: DWORD;
begin
lk := FLastKeyboard;
if Keyboard is TLangSwitchKeyboard_TIP then
begin
TIP := Keyboard as TLangSwitchKeyboard_TIP;
lk.TIP.LangID := TIP.Profile.langid;
lk.TIP.CLSID := TIP.Profile.clsid;
lk.TIP.GUIDProfile := TIP.Profile.guidProfile;
TDebugLogClient.Instance.WriteMessage('TGlobalKeyboardChangeManager.PostChange TIP', []);
if not CompareMem(@lk, @FLastKeyboard, SizeOf(TLastKeyboard)) then
begin
FLastKeyboard := lk;
FIdentity := TSharedBufferManager.Identity.WriteSelectKeyboardBuffer(lk.TIP);
PostMessage(FWindow, WM_USER_CHANGE, KMCI_SELECTKEYBOARD_BACKGROUND_TSF, FIdentity);
end;
end
else if Keyboard is TLangSwitchKeyboard_WinKeyboard then
begin
WinKB := Keyboard as TLangSwitchKeyboard_WinKeyboard;
lk.Keyboard := WinKB.HKL;
TDebugLogClient.Instance.WriteMessage('TGlobalKeyboardChangeManager.PostChange HKL', []);
if not CompareMem(@lk, @FLastKeyboard, SizeOf(TLastKeyboard)) then
begin
FLastKeyboard := lk;
PostMessage(FWindow, WM_USER_CHANGE, KMCI_SELECTKEYBOARD_BACKGROUND, lk.Keyboard);
end;
end
else
begin
Exit;
end;
end;
procedure TGlobalKeyboardChangeManager.ProcessChange(Mode: Integer; Param: NativeUInt);
begin
SendMessageTimeout(HWND_BROADCAST, wm_keyman_control_internal, Mode, Param, SMTO_ABORTIFHUNG, 250, nil);
end;
procedure TGlobalKeyboardChangeManager.WndProc(var Message: TMessage);
begin
with Message do
begin
case Message.Msg of
WM_USER_DESTROY:
DestroyWindow(FWindow);
WM_DESTROY:
begin
PostQuitMessage(0);
Result := 0;
end;
WM_USER_CHANGE:
ProcessChange(WParam, LParam);
else
Message.Result := DefWindowProc(FWindow, Msg, WParam, LParam);
end;
end;
end;
end.