From cef6f6a9e01267064e2820a652855061c542410a Mon Sep 17 00:00:00 2001 From: huiyadanli Date: Sat, 25 Nov 2023 17:04:36 +0800 Subject: [PATCH] use new hotkey module --- BetterGenshinImpact.sln | 12 ++ .../BetterGenshinImpact.csproj | 2 +- .../Macro/QuickEnhanceArtifactMacro.cs | 2 +- .../Model/HotKeySettingModel.cs | 27 ++- .../ViewModel/Pages/HotKeyPageViewModel.cs | 12 +- .../Fischless.HotkeyCapture.csproj | 17 ++ Fischless.HotkeyCapture/Hotkey.cs | 100 +++++++++++ Fischless.HotkeyCapture/HotkeyHolder.cs | 41 +++++ Fischless.HotkeyCapture/HotkeyHook.cs | 78 ++++++++ .../KeyPressedEventArgs.cs | 15 ++ Fischless.HotkeyCapture/SystemErrorCodes.cs | 7 + .../Fischless.KeyboardCapture.csproj | 18 ++ Fischless.KeyboardCapture/KeyboardHook.cs | 98 ++++++++++ Fischless.KeyboardCapture/KeyboardItem.cs | 15 ++ Fischless.KeyboardCapture/KeyboardReader.cs | 167 ++++++++++++++++++ Fischless.KeyboardCapture/KeyboardResult.cs | 49 +++++ 16 files changed, 643 insertions(+), 17 deletions(-) create mode 100644 Fischless.HotkeyCapture/Fischless.HotkeyCapture.csproj create mode 100644 Fischless.HotkeyCapture/Hotkey.cs create mode 100644 Fischless.HotkeyCapture/HotkeyHolder.cs create mode 100644 Fischless.HotkeyCapture/HotkeyHook.cs create mode 100644 Fischless.HotkeyCapture/KeyPressedEventArgs.cs create mode 100644 Fischless.HotkeyCapture/SystemErrorCodes.cs create mode 100644 Fischless.KeyboardCapture/Fischless.KeyboardCapture.csproj create mode 100644 Fischless.KeyboardCapture/KeyboardHook.cs create mode 100644 Fischless.KeyboardCapture/KeyboardItem.cs create mode 100644 Fischless.KeyboardCapture/KeyboardReader.cs create mode 100644 Fischless.KeyboardCapture/KeyboardResult.cs diff --git a/BetterGenshinImpact.sln b/BetterGenshinImpact.sln index fb91d11b..92b305cc 100644 --- a/BetterGenshinImpact.sln +++ b/BetterGenshinImpact.sln @@ -13,6 +13,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fischless.GameCapture", "Fi EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vision.WindowCapture.Test", "Vision.WindowCapture.Test\Vision.WindowCapture.Test.csproj", "{D35CB953-C666-4E57-9A9A-3AAE5BF78402}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fischless.HotkeyCapture", "Fischless.HotkeyCapture\Fischless.HotkeyCapture.csproj", "{08152E44-2564-46C5-B5B2-54DD43C01A79}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fischless.KeyboardCapture", "Fischless.KeyboardCapture\Fischless.KeyboardCapture.csproj", "{10A48327-7E58-4B51-B1FC-55506C703C8F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -39,6 +43,14 @@ Global {D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Debug|x64.Build.0 = Debug|x64 {D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Release|x64.ActiveCfg = Release|x64 {D35CB953-C666-4E57-9A9A-3AAE5BF78402}.Release|x64.Build.0 = Release|x64 + {08152E44-2564-46C5-B5B2-54DD43C01A79}.Debug|x64.ActiveCfg = Debug|x64 + {08152E44-2564-46C5-B5B2-54DD43C01A79}.Debug|x64.Build.0 = Debug|x64 + {08152E44-2564-46C5-B5B2-54DD43C01A79}.Release|x64.ActiveCfg = Release|x64 + {08152E44-2564-46C5-B5B2-54DD43C01A79}.Release|x64.Build.0 = Release|x64 + {10A48327-7E58-4B51-B1FC-55506C703C8F}.Debug|x64.ActiveCfg = Debug|x64 + {10A48327-7E58-4B51-B1FC-55506C703C8F}.Debug|x64.Build.0 = Debug|x64 + {10A48327-7E58-4B51-B1FC-55506C703C8F}.Release|x64.ActiveCfg = Release|x64 + {10A48327-7E58-4B51-B1FC-55506C703C8F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BetterGenshinImpact/BetterGenshinImpact.csproj b/BetterGenshinImpact/BetterGenshinImpact.csproj index 19d57c12..877631a1 100644 --- a/BetterGenshinImpact/BetterGenshinImpact.csproj +++ b/BetterGenshinImpact/BetterGenshinImpact.csproj @@ -26,7 +26,6 @@ - @@ -52,6 +51,7 @@ + diff --git a/BetterGenshinImpact/GameTask/Macro/QuickEnhanceArtifactMacro.cs b/BetterGenshinImpact/GameTask/Macro/QuickEnhanceArtifactMacro.cs index a32c6650..1c7f7f32 100644 --- a/BetterGenshinImpact/GameTask/Macro/QuickEnhanceArtifactMacro.cs +++ b/BetterGenshinImpact/GameTask/Macro/QuickEnhanceArtifactMacro.cs @@ -15,7 +15,7 @@ public class QuickEnhanceArtifactMacro return; } - SystemControl.ActivateWindow(TaskContext.Instance().GameHandle); + // SystemControl.ActivateWindow(TaskContext.Instance().GameHandle); var captureArea = TaskContext.Instance().SystemInfo.CaptureAreaRect; var assetScale = TaskContext.Instance().SystemInfo.AssetScale; diff --git a/BetterGenshinImpact/Model/HotKeySettingModel.cs b/BetterGenshinImpact/Model/HotKeySettingModel.cs index 7d51240d..e193dc7b 100644 --- a/BetterGenshinImpact/Model/HotKeySettingModel.cs +++ b/BetterGenshinImpact/Model/HotKeySettingModel.cs @@ -4,6 +4,8 @@ using System.Text.Json.Serialization; using System.Windows; using BetterGenshinImpact.Helpers; using CommunityToolkit.Mvvm.ComponentModel; +using Fischless.HotkeyCapture; +using Gma.System.MouseKeyHook.HotKeys; namespace BetterGenshinImpact.Model; @@ -18,11 +20,11 @@ public partial class HotKeySettingModel : ObservableObject public string ConfigPropertyName { get; set; } - public Action OnKeyAction { get; set; } + public Action OnKeyAction { get; set; } - public mrousavy.HotKey? KeyBindInfo { get; set; } + public HotkeyHook? KeyBindInfo { get; set; } - public HotKeySettingModel(string functionName, string configPropertyName, string hotkey, Action onKeyAction) + public HotKeySettingModel(string functionName, string configPropertyName, string hotkey, Action onKeyAction) { FunctionName = functionName; ConfigPropertyName = configPropertyName; @@ -39,12 +41,14 @@ public partial class HotKeySettingModel : ObservableObject try { - KeyBindInfo = new mrousavy.HotKey( - HotKey.Modifiers, - HotKey.Key, - UIDispatcherHelper.MainWindow, - OnKeyAction - ); + Fischless.HotkeyCapture.Hotkey hotkey = new(HotKey.ToString()); + + KeyBindInfo?.Dispose(); + KeyBindInfo = new HotkeyHook(); + KeyBindInfo.KeyPressed -= OnKeyPressed; + KeyBindInfo.KeyPressed += OnKeyPressed; + KeyBindInfo.RegisterHotKey(hotkey.ModifierKey, hotkey.Key); + } catch (Exception e) { @@ -54,6 +58,11 @@ public partial class HotKeySettingModel : ObservableObject } + private void OnKeyPressed(object? sender, KeyPressedEventArgs e) + { + OnKeyAction.Invoke(sender, e); + } + public void UnRegisterHotKey() { KeyBindInfo?.Dispose(); diff --git a/BetterGenshinImpact/ViewModel/Pages/HotKeyPageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/HotKeyPageViewModel.cs index 51d12d0e..e0ee4a62 100644 --- a/BetterGenshinImpact/ViewModel/Pages/HotKeyPageViewModel.cs +++ b/BetterGenshinImpact/ViewModel/Pages/HotKeyPageViewModel.cs @@ -64,7 +64,7 @@ public partial class HotKeyPageViewModel : ObservableObject "自动拾取开关", nameof(Config.HotKeyConfig.AutoPickEnabledHotkey), Config.HotKeyConfig.AutoPickEnabledHotkey, - hotKey => + (_, _) => { TaskContext.Instance().Config.AutoPickConfig.Enabled = !TaskContext.Instance().Config.AutoPickConfig.Enabled; _logger.LogInformation("切换{Name}状态为[{Enabled}]", "自动拾取", ToChinese(TaskContext.Instance().Config.AutoPickConfig.Enabled)); @@ -76,7 +76,7 @@ public partial class HotKeyPageViewModel : ObservableObject "自动剧情开关", nameof(Config.HotKeyConfig.AutoSkipEnabledHotkey), Config.HotKeyConfig.AutoSkipEnabledHotkey, - hotKey => + (_, _) => { TaskContext.Instance().Config.AutoSkipConfig.Enabled = !TaskContext.Instance().Config.AutoSkipConfig.Enabled; _logger.LogInformation("切换{Name}状态为[{Enabled}]", "自动剧情", ToChinese(TaskContext.Instance().Config.AutoSkipConfig.Enabled)); @@ -88,7 +88,7 @@ public partial class HotKeyPageViewModel : ObservableObject "自动钓鱼开关", nameof(Config.HotKeyConfig.AutoFishingEnabledHotkey), Config.HotKeyConfig.AutoFishingEnabledHotkey, - hotKey => + (_, _) => { TaskContext.Instance().Config.AutoFishingConfig.Enabled = !TaskContext.Instance().Config.AutoFishingConfig.Enabled; _logger.LogInformation("切换{Name}状态为[{Enabled}]", "自动钓鱼", ToChinese(TaskContext.Instance().Config.AutoFishingConfig.Enabled)); @@ -100,7 +100,7 @@ public partial class HotKeyPageViewModel : ObservableObject "长按旋转视角 - 那维莱特转圈", nameof(Config.HotKeyConfig.TurnAroundHotkey), Config.HotKeyConfig.TurnAroundHotkey, - hotKey => { TurnAroundMacro.Done(); } + (_, _) => { TurnAroundMacro.Done(); } ); HotKeySettingModels.Add(turnAroundHotKeySettingModel); @@ -108,7 +108,7 @@ public partial class HotKeyPageViewModel : ObservableObject "按下快速强化圣遗物", nameof(Config.HotKeyConfig.EnhanceArtifactHotkey), Config.HotKeyConfig.EnhanceArtifactHotkey, - hotKey => { QuickEnhanceArtifactMacro.Done(); } + (_, _) => { QuickEnhanceArtifactMacro.Done(); } ); HotKeySettingModels.Add(enhanceArtifactHotKeySettingModel); @@ -116,7 +116,7 @@ public partial class HotKeyPageViewModel : ObservableObject "启动/停止自动七圣召唤", nameof(Config.HotKeyConfig.AutoGeniusInvokation), Config.HotKeyConfig.AutoGeniusInvokation, - hotKey => { _taskSettingsPageViewModel.OnSwitchAutoGeniusInvokation(); } + (_, _) => { _taskSettingsPageViewModel.OnSwitchAutoGeniusInvokation(); } )); } diff --git a/Fischless.HotkeyCapture/Fischless.HotkeyCapture.csproj b/Fischless.HotkeyCapture/Fischless.HotkeyCapture.csproj new file mode 100644 index 00000000..cb2f3b19 --- /dev/null +++ b/Fischless.HotkeyCapture/Fischless.HotkeyCapture.csproj @@ -0,0 +1,17 @@ + + + + net7.0-windows10.0.22621.0 + enable + enable + x64 + 12.0 + True + True + + + + + + + diff --git a/Fischless.HotkeyCapture/Hotkey.cs b/Fischless.HotkeyCapture/Hotkey.cs new file mode 100644 index 00000000..e378314c --- /dev/null +++ b/Fischless.HotkeyCapture/Hotkey.cs @@ -0,0 +1,100 @@ +using Vanara.PInvoke; + +namespace Fischless.HotkeyCapture; + +public sealed class Hotkey +{ + public bool Alt { get; set; } + public bool Control { get; set; } + public bool Shift { get; set; } + public bool Windows { get; set; } + + private Keys key; + + public Keys Key + { + get => key; + set + { + if (value != Keys.ControlKey && value != Keys.Alt && value != Keys.Menu && value != Keys.ShiftKey) + { + key = value; + } + else + { + key = Keys.None; + } + } + } + + public User32.HotKeyModifiers ModifierKey => + (Windows ? User32.HotKeyModifiers.MOD_WIN : User32.HotKeyModifiers.MOD_NONE) | + (Control ? User32.HotKeyModifiers.MOD_CONTROL : User32.HotKeyModifiers.MOD_NONE) | + (Shift ? User32.HotKeyModifiers.MOD_SHIFT : User32.HotKeyModifiers.MOD_NONE) | + (Alt ? User32.HotKeyModifiers.MOD_ALT : User32.HotKeyModifiers.MOD_NONE); + + public Hotkey() + { + Reset(); + } + + public Hotkey(string hotkeyStr) + { + try + { + string[] keyStrs = hotkeyStr.Replace(" ", string.Empty).Split('+'); + + foreach (string keyStr in keyStrs) + { + if (keyStr.Equals("Win", StringComparison.OrdinalIgnoreCase)) + { + Windows = true; + } + else if (keyStr.Equals("Ctrl", StringComparison.OrdinalIgnoreCase)) + { + Control = true; + } + else if (keyStr.Equals("Shift", StringComparison.OrdinalIgnoreCase)) + { + Shift = true; + } + else if (keyStr.Equals("Alt", StringComparison.OrdinalIgnoreCase)) + { + Alt = true; + } + else + { + Key = (Keys)Enum.Parse(typeof(Keys), keyStr); + } + } + } + catch + { + throw new ArgumentException("Invalid Hotkey"); + } + } + + public override string ToString() + { + string str = string.Empty; + if (Key != Keys.None) + { + str = string.Format("{0}{1}{2}{3}{4}", + Windows ? "Win + " : string.Empty, + Control ? "Ctrl + " : string.Empty, + Shift ? "Shift + " : string.Empty, + Alt ? "Alt + " : string.Empty, + Key); + } + return str; + } + + public void Reset() + { + Alt = false; + Control = false; + Shift = false; + Windows = false; + Key = Keys.None; + } +} diff --git a/Fischless.HotkeyCapture/HotkeyHolder.cs b/Fischless.HotkeyCapture/HotkeyHolder.cs new file mode 100644 index 00000000..a27cb4db --- /dev/null +++ b/Fischless.HotkeyCapture/HotkeyHolder.cs @@ -0,0 +1,41 @@ +namespace Fischless.HotkeyCapture; + +public sealed class HotkeyHolder +{ + private static Hotkey? hotkey; + private static HotkeyHook? hotkeyHook; + private static Action? keyPressed; + + public static void RegisterHotKey(string hotkeyStr, Action keyPressed = null!) + { + if (string.IsNullOrEmpty(hotkeyStr)) + { + UnregisterHotKey(); + return; + } + + hotkey = new Hotkey(hotkeyStr); + + hotkeyHook?.Dispose(); + hotkeyHook = new HotkeyHook(); + hotkeyHook.KeyPressed -= OnKeyPressed; + hotkeyHook.KeyPressed += OnKeyPressed; + HotkeyHolder.keyPressed = keyPressed; + hotkeyHook.RegisterHotKey(hotkey.ModifierKey, hotkey.Key); + } + + public static void UnregisterHotKey() + { + if (hotkeyHook != null) + { + hotkeyHook.KeyPressed -= OnKeyPressed; + hotkeyHook.UnregisterHotKey(); + hotkeyHook.Dispose(); + } + } + + private static void OnKeyPressed(object? sender, KeyPressedEventArgs e) + { + keyPressed?.Invoke(sender, e); + } +} diff --git a/Fischless.HotkeyCapture/HotkeyHook.cs b/Fischless.HotkeyCapture/HotkeyHook.cs new file mode 100644 index 00000000..ac08ce9b --- /dev/null +++ b/Fischless.HotkeyCapture/HotkeyHook.cs @@ -0,0 +1,78 @@ +using System.Runtime.InteropServices; +using Vanara.PInvoke; + +namespace Fischless.HotkeyCapture; + +public sealed class HotkeyHook : IDisposable +{ + public event EventHandler? KeyPressed = null; + + private readonly Window window = new(); + private int currentId; + + private class Window : NativeWindow, IDisposable + { + public event EventHandler? KeyPressed = null; + + public Window() + { + CreateHandle(new CreateParams()); + } + + protected override void WndProc(ref Message m) + { + base.WndProc(ref m); + + if (m.Msg == (int)User32.WindowMessage.WM_HOTKEY) + { + Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF); + User32.HotKeyModifiers modifier = (User32.HotKeyModifiers)((int)m.LParam & 0xFFFF); + + KeyPressed?.Invoke(this, new KeyPressedEventArgs(modifier, key)); + } + } + + public void Dispose() + { + DestroyHandle(); + } + } + + public HotkeyHook() + { + window.KeyPressed += (sender, args) => + { + KeyPressed?.Invoke(this, args); + }; + } + + public void RegisterHotKey(User32.HotKeyModifiers modifier, Keys key) + { + currentId += 1; + if (!User32.RegisterHotKey(window!.Handle, currentId, modifier, (uint)key)) + { + if (Marshal.GetLastWin32Error() == SystemErrorCodes.ERROR_HOTKEY_ALREADY_REGISTERED) + { + throw new InvalidOperationException("Hotkey already registered"); + } + else + { + throw new InvalidOperationException("Hotkey registration failed"); + } + } + } + + public void UnregisterHotKey() + { + for (int i = currentId; i > 0; i--) + { + User32.UnregisterHotKey(window!.Handle, i); + } + } + + public void Dispose() + { + UnregisterHotKey(); + window?.Dispose(); + } +} diff --git a/Fischless.HotkeyCapture/KeyPressedEventArgs.cs b/Fischless.HotkeyCapture/KeyPressedEventArgs.cs new file mode 100644 index 00000000..9eac8c6f --- /dev/null +++ b/Fischless.HotkeyCapture/KeyPressedEventArgs.cs @@ -0,0 +1,15 @@ +using Vanara.PInvoke; + +namespace Fischless.HotkeyCapture; + +public class KeyPressedEventArgs : EventArgs +{ + public User32.HotKeyModifiers Modifier { get; } + public Keys Key { get; } + + internal KeyPressedEventArgs(User32.HotKeyModifiers modifier, Keys key) + { + Modifier = modifier; + Key = key; + } +} diff --git a/Fischless.HotkeyCapture/SystemErrorCodes.cs b/Fischless.HotkeyCapture/SystemErrorCodes.cs new file mode 100644 index 00000000..db8411ca --- /dev/null +++ b/Fischless.HotkeyCapture/SystemErrorCodes.cs @@ -0,0 +1,7 @@ +namespace Fischless.HotkeyCapture; + +internal sealed class SystemErrorCodes +{ + public const int ERROR_HOTKEY_ALREADY_REGISTERED = 0x581; + public const int ERROR_HOTKEY_NOT_REGISTERED = 0x58B; +} diff --git a/Fischless.KeyboardCapture/Fischless.KeyboardCapture.csproj b/Fischless.KeyboardCapture/Fischless.KeyboardCapture.csproj new file mode 100644 index 00000000..a2184742 --- /dev/null +++ b/Fischless.KeyboardCapture/Fischless.KeyboardCapture.csproj @@ -0,0 +1,18 @@ + + + + net8.0-windows10.0.22621.0 + enable + enable + x64 + 12.0 + True + true + + + + + + + + diff --git a/Fischless.KeyboardCapture/KeyboardHook.cs b/Fischless.KeyboardCapture/KeyboardHook.cs new file mode 100644 index 00000000..7ab8cb31 --- /dev/null +++ b/Fischless.KeyboardCapture/KeyboardHook.cs @@ -0,0 +1,98 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using Vanara.PInvoke; + +namespace Fischless.KeyboardCapture; + +public sealed class KeyboardHook : IDisposable +{ + public event KeyEventHandler KeyDown = null!; + + public event KeyPressEventHandler KeyPress = null!; + + public event KeyEventHandler KeyUp = null!; + + private User32.SafeHHOOK hook = new(IntPtr.Zero); + private User32.HookProc? hookProc; + + ~KeyboardHook() + { + Dispose(); + } + + public void Dispose() + { + Stop(); + } + + public void Start() + { + if (hook.IsNull) + { + hookProc = KeyboardHookProc; + hook = User32.SetWindowsHookEx(User32.HookType.WH_KEYBOARD_LL, hookProc, Kernel32.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0); + + User32.SetWindowsHookEx(User32.HookType.WH_KEYBOARD_LL, hookProc, IntPtr.Zero, (int)Kernel32.GetCurrentThreadId()); + + if (hook.IsNull) + { + Stop(); + throw new SystemException("Failed to install keyboard hook"); + } + } + } + + public void Stop() + { + bool retKeyboard = true; + + if (!hook.IsNull) + { + retKeyboard = User32.UnhookWindowsHookEx(hook); + hook = new(IntPtr.Zero); + } + + if (!retKeyboard) + { + throw new SystemException("Failed to uninstall hook"); + } + } + + private nint KeyboardHookProc(int nCode, nint wParam, nint lParam) + { + if (nCode >= 0) + { + if (KeyDown != null || KeyUp != null || KeyPress != null) + { + User32.KBDLLHOOKSTRUCT hookStruct = (User32.KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(User32.KBDLLHOOKSTRUCT))!; + + if (KeyDown != null && (wParam == (nint)User32.WindowMessage.WM_KEYDOWN || wParam == (nint)User32.WindowMessage.WM_SYSKEYDOWN)) + { + Keys keyData = (Keys)hookStruct.vkCode; + KeyEventArgs e = new(keyData); + KeyDown(this, e); + } + + if (KeyPress != null && wParam == (nint)User32.WindowMessage.WM_KEYDOWN) + { + byte[] keyState = new byte[256]; + _ = User32.GetKeyboardState(keyState); + + if (User32.ToAscii(hookStruct.vkCode, hookStruct.scanCode, keyState, out ushort lpChar, hookStruct.flags) == 1) + { + KeyPressEventArgs e = new((char)lpChar); + KeyPress(this, e); + } + } + + if (KeyUp != null && (wParam == (nint)User32.WindowMessage.WM_KEYUP || wParam == (nint)User32.WindowMessage.WM_SYSKEYUP)) + { + Keys keyData = (Keys)hookStruct.vkCode; + KeyEventArgs e = new(keyData); + KeyUp(this, e); + } + } + } + return User32.CallNextHookEx(hook, nCode, wParam, lParam); + } +} diff --git a/Fischless.KeyboardCapture/KeyboardItem.cs b/Fischless.KeyboardCapture/KeyboardItem.cs new file mode 100644 index 00000000..ed9ee98d --- /dev/null +++ b/Fischless.KeyboardCapture/KeyboardItem.cs @@ -0,0 +1,15 @@ +namespace Fischless.KeyboardCapture; + +public record struct KeyboardItem +{ + public DateTime DateTime; + public Keys KeyCode; + public string Key; + + public KeyboardItem(DateTime dateTime, Keys keyCode, string? key = null) + { + DateTime = dateTime; + KeyCode = keyCode; + Key = key; + } +} diff --git a/Fischless.KeyboardCapture/KeyboardReader.cs b/Fischless.KeyboardCapture/KeyboardReader.cs new file mode 100644 index 00000000..7e1ff11b --- /dev/null +++ b/Fischless.KeyboardCapture/KeyboardReader.cs @@ -0,0 +1,167 @@ +using System.Diagnostics; + +namespace Fischless.KeyboardCapture; + +[DebuggerDisplay("{result.ToString()}")] +public class KeyboardReader : IDisposable +{ + public static KeyboardReader Default { get; } = new(); + + public event EventHandler Received = null!; + + public bool IsCombinationOnly = false; + public bool IsCaseSensitived = false; + protected KeyboardHook KeyboardHook = new(); + protected bool IsShift = false; + protected bool IsCtrl = false; + protected bool IsAlt = false; + protected bool IsWin = false; + + public KeyboardReader() + { + Start(); + } + + ~KeyboardReader() + { + Dispose(); + } + + public void Dispose() + { + Stop(); + } + + public void Start() + { + KeyboardHook.KeyDown -= OnKeyDown; + KeyboardHook.KeyDown += OnKeyDown; + KeyboardHook.KeyUp -= OnKeyUp; + KeyboardHook.KeyUp += OnKeyUp; + KeyboardHook.Start(); + } + + public void Stop() + { + KeyboardHook.Stop(); + KeyboardHook.KeyDown -= OnKeyDown; + KeyboardHook.KeyUp -= OnKeyUp; + } + + private void OnKeyDown(object? sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Shift + || e.KeyCode == Keys.ShiftKey + || e.KeyCode == Keys.LShiftKey + || e.KeyCode == Keys.RShiftKey) + { + IsShift = true; + if (IsCombinationOnly) + { + return; + } + } + else if (e.KeyCode == Keys.Control + || e.KeyCode == Keys.ControlKey + || e.KeyCode == Keys.LControlKey + || e.KeyCode == Keys.RControlKey) + { + IsCtrl = true; + if (IsCombinationOnly) + { + return; + } + } + else if (e.KeyCode == Keys.LWin + || e.KeyCode == Keys.RWin) + { + IsWin = true; + if (IsCombinationOnly) + { + return; + } + } + else if (e.KeyCode == Keys.Alt + || e.KeyCode == Keys.LMenu + || e.KeyCode == Keys.RMenu) + { + IsAlt = true; + if (IsCombinationOnly) + { + return; + } + } + + var now = DateTime.Now; + +#if FALSE + Debug.WriteLine(e.KeyCode); +#endif + + KeyboardItem item; + + if (IsCaseSensitived) + { + bool isUpper = Control.IsKeyLocked(Keys.CapsLock) ? !IsShift : IsShift; + + if (isUpper) + { + item = new(now, e.KeyCode, char.ToUpper((char)e.KeyCode).ToString()); + } + else + { + item = new(now, e.KeyCode, char.ToLower((char)e.KeyCode).ToString()); + } + } + else + { + item = new(now, e.KeyCode); + } + + KeyboardResult result = new(item) + { + IsShift = IsShift, + IsCtrl = IsCtrl, + IsAlt = IsAlt, + IsWin = IsWin, + }; + + Received?.Invoke(this, result); +#if FALSE + Debug.WriteLine(result.ToString()); +#endif + } + + private void OnKeyUp(object? sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Shift + || e.KeyCode == Keys.ShiftKey + || e.KeyCode == Keys.LShiftKey + || e.KeyCode == Keys.RShiftKey) + { + IsShift = false; + return; + } + else if (e.KeyCode == Keys.Control + || e.KeyCode == Keys.ControlKey + || e.KeyCode == Keys.LControlKey + || e.KeyCode == Keys.RControlKey) + { + IsCtrl = false; + return; + } + else if (e.KeyCode == Keys.LWin + || e.KeyCode == Keys.RWin) + { + IsWin = false; + return; + } + else if (e.KeyCode == Keys.Alt + || e.KeyCode == Keys.LMenu + || e.KeyCode == Keys.RMenu) + { + IsAlt = false; + return; + } + } +} diff --git a/Fischless.KeyboardCapture/KeyboardResult.cs b/Fischless.KeyboardCapture/KeyboardResult.cs new file mode 100644 index 00000000..c85321bb --- /dev/null +++ b/Fischless.KeyboardCapture/KeyboardResult.cs @@ -0,0 +1,49 @@ +namespace Fischless.KeyboardCapture; + +public sealed class KeyboardResult +{ + public KeyboardItem KeyboardItem { get; init; } = default; + public string Key => KeyboardItem.Key ?? KeyboardItem.KeyCode.ToString(); + public bool IsShift { get; set; } = false; + public bool IsCtrl { get; set; } = false; + public bool IsAlt { get; set; } = false; + public bool IsWin { get; set; } = false; + + public KeyboardResult(KeyboardItem keyboardItem) + { + KeyboardItem = keyboardItem; + } + + public override string ToString() + { + List keyModifiers = new(); + + if (IsCtrl) + { + keyModifiers.Add("Ctrl"); + } + + if (IsShift) + { + keyModifiers.Add("Shift"); + } + + if (IsAlt) + { + keyModifiers.Add("Alt"); + } + + string keyModifiersStr = string.Join("+", keyModifiers); + + if (!string.IsNullOrEmpty(keyModifiersStr) && !string.IsNullOrEmpty(Key)) + { + return $"{keyModifiersStr}+{Key}"; + } + else + { + return Key; + } + } + + public static implicit operator string(KeyboardResult result) => result?.ToString(); +}