diff --git a/WirelessTextSyncer.Windows/Services/KeyboardInjectionService.cs b/WirelessTextSyncer.Windows/Services/KeyboardInjectionService.cs index d358196..bd5407d 100644 --- a/WirelessTextSyncer.Windows/Services/KeyboardInjectionService.cs +++ b/WirelessTextSyncer.Windows/Services/KeyboardInjectionService.cs @@ -7,10 +7,13 @@ public sealed class KeyboardInjectionService : IKeyboardInjectionService private const uint InputKeyboard = 1; private const uint KeyEventFKeyUp = 0x0002; private const uint KeyEventFUnicode = 0x0004; + private const uint KlfActivate = 0x00000001; + private const uint WmInputLangChangeRequest = 0x0050; private const ushort VirtualKeyA = 0x41; private const ushort VirtualKeyBack = 0x08; private const ushort VirtualKeyControl = 0x11; private const ushort VirtualKeyReturn = 0x0D; + private static readonly IntPtr EnglishKeyboardLayout = LoadKeyboardLayout("00000409", KlfActivate); public void InsertText(string text) { @@ -20,6 +23,7 @@ public sealed class KeyboardInjectionService : IKeyboardInjectionService } AppLogger.Info($"Injecting text length: {text.Length}"); + using var keyboardLayoutScope = KeyboardLayoutScope.SwitchForegroundWindowToEnglish(); foreach (var character in text) { AppLogger.Info($"Injecting char U+{(int)character:X4}: {character}"); @@ -121,6 +125,66 @@ public sealed class KeyboardInjectionService : IKeyboardInjectionService [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint numberOfInputs, INPUT[] inputs, int sizeOfInputStructure); + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + private static extern uint GetWindowThreadProcessId(IntPtr windowHandle, out uint processId); + + [DllImport("user32.dll")] + private static extern IntPtr GetKeyboardLayout(uint threadId); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + private static extern IntPtr LoadKeyboardLayout(string keyboardLayoutId, uint flags); + + [DllImport("user32.dll", SetLastError = true)] + private static extern bool PostMessage(IntPtr windowHandle, uint message, IntPtr wParam, IntPtr lParam); + + private sealed class KeyboardLayoutScope : IDisposable + { + private readonly IntPtr foregroundWindow; + private readonly IntPtr originalKeyboardLayout; + + private KeyboardLayoutScope(IntPtr foregroundWindow, IntPtr originalKeyboardLayout) + { + this.foregroundWindow = foregroundWindow; + this.originalKeyboardLayout = originalKeyboardLayout; + } + + public static KeyboardLayoutScope SwitchForegroundWindowToEnglish() + { + var foregroundWindow = GetForegroundWindow(); + if (foregroundWindow == IntPtr.Zero) + { + AppLogger.Error("Cannot switch keyboard layout because no foreground window was found."); + return new KeyboardLayoutScope(IntPtr.Zero, IntPtr.Zero); + } + + var threadId = GetWindowThreadProcessId(foregroundWindow, out _); + var originalKeyboardLayout = GetKeyboardLayout(threadId); + AppLogger.Info($"Switching foreground keyboard layout from 0x{originalKeyboardLayout.ToInt64():X} to 0x{EnglishKeyboardLayout.ToInt64():X}."); + + if (EnglishKeyboardLayout != IntPtr.Zero) + { + PostMessage(foregroundWindow, WmInputLangChangeRequest, IntPtr.Zero, EnglishKeyboardLayout); + Thread.Sleep(50); + } + + return new KeyboardLayoutScope(foregroundWindow, originalKeyboardLayout); + } + + public void Dispose() + { + if (foregroundWindow == IntPtr.Zero || originalKeyboardLayout == IntPtr.Zero) + { + return; + } + + AppLogger.Info($"Restoring foreground keyboard layout to 0x{originalKeyboardLayout.ToInt64():X}."); + PostMessage(foregroundWindow, WmInputLangChangeRequest, IntPtr.Zero, originalKeyboardLayout); + } + } + [StructLayout(LayoutKind.Sequential)] private struct INPUT {