Use Function Pointer to handle the low level keyboard proc

This commit is contained in:
Kevin Zhang 2021-11-27 13:50:22 -06:00
parent b51e5d2583
commit 1c60b8e586
5 changed files with 18 additions and 35 deletions

View file

@ -9,13 +9,14 @@ namespace Flow.Launcher.Infrastructure.Hotkey
/// Listens keyboard globally.
/// <remarks>Uses WH_KEYBOARD_LL.</remarks>
/// </summary>
public class GlobalHotkey : IDisposable
public unsafe static class GlobalHotkey
{
private static GlobalHotkey instance;
private InterceptKeys.LowLevelKeyboardProc hookedLowLevelKeyboardProc;
private IntPtr hookId = IntPtr.Zero;
private static readonly IntPtr hookId;
public delegate bool KeyboardCallback(KeyEvent keyEvent, int vkCode, SpecialKeyState state);
public event KeyboardCallback hookedKeyboardCallback;
internal static Func<KeyEvent, int, SpecialKeyState, bool> hookedKeyboardCallback;
//Modifier key constants
private const int VK_SHIFT = 0x10;
@ -23,27 +24,14 @@ namespace Flow.Launcher.Infrastructure.Hotkey
private const int VK_ALT = 0x12;
private const int VK_WIN = 91;
public static GlobalHotkey Instance
static GlobalHotkey()
{
get
{
if (instance == null)
{
instance = new GlobalHotkey();
}
return instance;
}
}
private GlobalHotkey()
{
// We have to store the LowLevelKeyboardProc, so that it is not garbage collected runtime
hookedLowLevelKeyboardProc = LowLevelKeyboardProc;
// Set the hook
hookId = InterceptKeys.SetHook(hookedLowLevelKeyboardProc);
hookId = InterceptKeys.SetHook(& LowLevelKeyboardProc);
AppDomain.CurrentDomain.ProcessExit += (_, _) => Dispose();
}
public SpecialKeyState CheckModifiers()
public static SpecialKeyState CheckModifiers()
{
SpecialKeyState state = new SpecialKeyState();
if ((InterceptKeys.GetKeyState(VK_SHIFT) & 0x8000) != 0)
@ -71,7 +59,7 @@ namespace Flow.Launcher.Infrastructure.Hotkey
}
[MethodImpl(MethodImplOptions.NoInlining)]
private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
private static IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
{
bool continues = true;
@ -94,12 +82,7 @@ namespace Flow.Launcher.Infrastructure.Hotkey
return (IntPtr)1;
}
~GlobalHotkey()
{
Dispose();
}
public void Dispose()
public static void Dispose()
{
InterceptKeys.UnhookWindowsHookEx(hookId);
}

View file

@ -4,13 +4,13 @@ using System.Runtime.InteropServices;
namespace Flow.Launcher.Infrastructure.Hotkey
{
internal static class InterceptKeys
internal static unsafe class InterceptKeys
{
public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);
private const int WH_KEYBOARD_LL = 13;
public static IntPtr SetHook(LowLevelKeyboardProc proc)
public static IntPtr SetHook(delegate*<int, UIntPtr, IntPtr, IntPtr> proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
@ -20,7 +20,7 @@ namespace Flow.Launcher.Infrastructure.Hotkey
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
public static extern IntPtr SetWindowsHookEx(int idHook, delegate*<int, UIntPtr, IntPtr, IntPtr> lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]

View file

@ -33,7 +33,7 @@ namespace Flow.Launcher
//when alt is pressed, the real key should be e.SystemKey
Key key = (e.Key == Key.System ? e.SystemKey : e.Key);
SpecialKeyState specialKeyState = GlobalHotkey.Instance.CheckModifiers();
SpecialKeyState specialKeyState = GlobalHotkey.CheckModifiers();
var hotkeyModel = new HotkeyModel(
specialKeyState.AltPressed,

View file

@ -40,7 +40,7 @@ namespace Flow.Launcher
_settingsVM = settingsVM;
_mainVM = mainVM;
_alphabet = alphabet;
GlobalHotkey.Instance.hookedKeyboardCallback += KListener_hookedKeyboardCallback;
GlobalHotkey.hookedKeyboardCallback += KListener_hookedKeyboardCallback;
WebRequest.RegisterPrefix("data", new DataWebRequestFactory());
}

View file

@ -227,7 +227,7 @@ namespace Flow.Launcher.ViewModel
{
bool hideWindow = result.Action != null && result.Action(new ActionContext
{
SpecialKeyState = GlobalHotkey.Instance.CheckModifiers()
SpecialKeyState = GlobalHotkey.CheckModifiers()
});
if (hideWindow)