Files
Robovoice/Robovoice.App/PttHotkey.cs
T

95 lines
2.7 KiB
C#
Raw Normal View History

2026-08-10 11:27:28 +00:00
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Robovoice.App;
internal sealed class PttHotkey : IDisposable
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x0101;
private const int WM_SYSKEYDOWN = 0x0104;
private const int WM_SYSKEYUP = 0x0105;
private readonly LowLevelKeyboardProc _proc;
private IntPtr _hook = IntPtr.Zero;
private bool _isDown;
private bool _disposed;
public Keys Key { get; set; } = Keys.F8;
public event EventHandler? Pressed;
public event EventHandler? Released;
public PttHotkey()
{
_proc = HookCallback;
}
public void Install()
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (_hook != IntPtr.Zero) return;
IntPtr hModule = GetModuleHandle(null);
_hook = SetWindowsHookEx(WH_KEYBOARD_LL, _proc, hModule, 0);
}
public void Uninstall()
{
if (_hook != IntPtr.Zero)
{
UnhookWindowsHookEx(_hook);
_hook = IntPtr.Zero;
_isDown = false;
}
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
int vkCode = Marshal.ReadInt32(lParam);
Keys key = (Keys)vkCode;
bool isDown = wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN;
bool isUp = wParam == WM_KEYUP || wParam == WM_SYSKEYUP;
if (key == Key && isDown && !_isDown)
{
_isDown = true;
Pressed?.Invoke(this, EventArgs.Empty);
}
else if (key == Key && isUp && _isDown)
{
_isDown = false;
Released?.Invoke(this, EventArgs.Empty);
}
}
return CallNextHookEx(_hook, nCode, wParam, lParam);
}
public void Dispose()
{
if (_disposed) return;
Uninstall();
_disposed = true;
}
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr GetModuleHandle(string? lpModuleName);
}