Add HotKeyListener

This commit is contained in:
Daniel Clark
2025-03-04 17:00:00 +00:00
parent dad7c54f0d
commit cee6557fb4
19 changed files with 424 additions and 297 deletions
+5
View File
@@ -0,0 +1,5 @@
using Toolkit.Foundation;
namespace Toolkit.Windows;
public record HotKeyDescriptor(ModifierKey Modifiers, VirtualKey VirtualKey);
+49
View File
@@ -0,0 +1,49 @@
using CommunityToolkit.Mvvm.Messaging;
using Toolkit.Foundation;
namespace Toolkit.Windows;
public class HotKeyListener(ICache<int, HotKeyDescriptor> cache,
IMessenger messenger) :
IHotKeyListener,
IRecipient<WndProcEventArgs>
{
private bool isDisposed;
~HotKeyListener()
{
Dispose(false);
}
public void Initialize()
{
messenger.RegisterAll(this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
isDisposed = true;
}
}
public void Receive(WndProcEventArgs message)
{
const int WM_HOTKEY = 0x0312;
if (message.Message == WM_HOTKEY)
{
int key = (int)message.WParam;
if (cache.Contains(key))
{
messenger.Send(new HotKeyPressedEventArgs(key));
}
}
}
}
+57
View File
@@ -0,0 +1,57 @@
using Toolkit.Foundation;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Input.KeyboardAndMouse;
namespace Toolkit.Windows;
public class HotKeyManager(IWndProc wndProc,
ICache<int, HotKeyDescriptor> cache) :
IHotKeyManager
{
public void Add(int key, HotKeyDescriptor descriptor)
{
HOT_KEY_MODIFIERS modifiers = 0;
if ((descriptor.Modifiers & ModifierKey.Alt) == ModifierKey.Alt)
modifiers |= HOT_KEY_MODIFIERS.MOD_ALT;
if ((descriptor.Modifiers & ModifierKey.Ctrl) == ModifierKey.Ctrl)
modifiers |= HOT_KEY_MODIFIERS.MOD_CONTROL;
if ((descriptor.Modifiers & ModifierKey.Shift) == ModifierKey.Shift)
modifiers |= HOT_KEY_MODIFIERS.MOD_SHIFT;
if ((descriptor.Modifiers & ModifierKey.Win) == ModifierKey.Win)
modifiers |= HOT_KEY_MODIFIERS.MOD_WIN;
uint vk = (uint)descriptor.VirtualKey;
if (PInvoke.RegisterHotKey(new HWND(wndProc.Handle), key, modifiers, vk))
{
cache.Add(key, descriptor);
}
}
public bool Contains(int key) =>
cache.Contains(key);
public void Remove(int key)
{
PInvoke.UnregisterHotKey(new HWND(wndProc.Handle), key);
cache.Remove(key);
}
public void Dispose()
{
HWND hwnd = new(wndProc.Handle);
foreach (KeyValuePair<int, HotKeyDescriptor> item in cache)
{
PInvoke.UnregisterHotKey(hwnd, item.Key);
}
cache.Clear();
GC.SuppressFinalize(this);
}
~HotKeyManager()
{
Dispose();
}
}
@@ -0,0 +1,3 @@
namespace Toolkit.Windows;
public record HotKeyPressedEventArgs(int Key);
@@ -2,6 +2,6 @@
namespace Toolkit.Windows;
public interface ITaskbarButtonMonitor :
public interface IHotKeyListener :
IInitialization,
IDisposable;
+11
View File
@@ -0,0 +1,11 @@
namespace Toolkit.Windows;
public interface IHotKeyManager :
IDisposable
{
void Add(int key, HotKeyDescriptor descriptor);
bool Contains(int key);
void Remove(int key);
}
@@ -2,6 +2,6 @@
namespace Toolkit.Windows;
public interface IPointer :
public interface IPointerListener :
IInitialization,
IDisposable;
@@ -0,0 +1,7 @@
using Toolkit.Foundation;
namespace Toolkit.Windows;
public interface ITaskbarButtonListener :
IInitialization,
IDisposable;
+3 -1
View File
@@ -48,4 +48,6 @@ TranslateMessage
DispatchMessage
PostQuitMessage
DestroyWindow
GetModuleHandle
GetModuleHandle
RegisterHotKey
UnregisterHotKey
+1 -1
View File
@@ -62,7 +62,7 @@ public class NotifyIcon(IWndProc wndProc,
GC.SuppressFinalize(this);
}
public unsafe PointerLocation GetPointerPosition()
public static unsafe PointerLocation GetPointerPosition()
{
Point point = new();
_ = PInvoke.GetCursorPos(&point);
@@ -1,14 +1,13 @@
using CommunityToolkit.Mvvm.Messaging;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
namespace Toolkit.Windows;
public class Pointer(IMessenger messenger) :
IPointer
public class PointerListener(IMessenger messenger) :
IPointerListener
{
private bool isDisposed;
private bool isPointerDrag;
@@ -16,7 +15,7 @@ public class Pointer(IMessenger messenger) :
private HOOKPROC? mouseEventDelegate;
private UnhookWindowsHookExSafeHandle? mouseHandle;
~Pointer()
~PointerListener()
{
Dispose(false);
}
@@ -27,25 +26,22 @@ public class Pointer(IMessenger messenger) :
GC.SuppressFinalize(this);
}
public unsafe void Initialize() =>
InitializeHook();
public unsafe void Initialize()
{
mouseEventDelegate = new HOOKPROC(MouseProc);
mouseHandle = PInvoke.SetWindowsHookEx(WINDOWS_HOOK_ID.WH_MOUSE_LL, mouseEventDelegate,
PInvoke.GetModuleHandle("user32.dll"), 0);
}
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
RemoveHook();
Remove();
isDisposed = true;
}
}
private unsafe void InitializeHook()
{
mouseEventDelegate = new HOOKPROC(MouseProc);
mouseHandle = PInvoke.SetWindowsHookEx(WINDOWS_HOOK_ID.WH_MOUSE_LL, mouseEventDelegate,
PInvoke.GetModuleHandle("user32.dll"), 0);
}
private LRESULT MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode >= 0)
@@ -82,7 +78,7 @@ public class Pointer(IMessenger messenger) :
return PInvoke.CallNextHookEx(mouseHandle, nCode, wParam, lParam);
}
private unsafe void RemoveHook()
private unsafe void Remove()
{
if (mouseHandle is not null && mouseHandle.DangerousGetHandle() != nint.Zero)
{
+1 -1
View File
@@ -42,7 +42,7 @@ public class TaskbarButton :
public void Receive(PointerReleasedEventArgs args)
{
if (!isDrag && isWithinBounds)
if (args.Button is PointerButton.Left && !isDrag && isWithinBounds)
{
messenger.Send(new TaskbarButtonInvokedEventArgs(this));
}
@@ -4,8 +4,8 @@ using UIAutomationClient;
namespace Toolkit.Windows;
public class TaskbarButtonMonitor :
ITaskbarButtonMonitor
public class TaskbarButtonListener :
ITaskbarButtonListener
{
private readonly IDispatcherTimer dispatcherTimer;
private readonly IDispatcherTimerFactory dispatcherTimerFactory;
@@ -19,7 +19,7 @@ public class TaskbarButtonMonitor :
private IUIAutomationElement? taskListElement;
private IntPtr taskListHandle;
public TaskbarButtonMonitor(ITaskbarList taskbarList,
public TaskbarButtonListener(ITaskbarList taskbarList,
IMessenger messenger,
IDispatcherTimerFactory dispatcherTimerFactory,
IServiceFactory serviceFactory,