Files
Toolkit2/Toolkit.Windows/PointerMonitor.cs
T
2024-11-02 20:08:57 +00:00

209 lines
5.9 KiB
C#

using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using Toolkit.Foundation;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.UI.WindowsAndMessaging;
namespace Toolkit.Windows;
public class WndProcMonitor(IPublisher publisher) :
IWndProcMonitor
{
private WNDPROC? handler;
private readonly IPublisher publisher = publisher;
public IntPtr Handle { get; private set; }
public void Dispose()
{
PInvoke.DestroyWindow((HWND)Handle);
}
private unsafe void InitializeWndProc()
{
var windowName = Guid.NewGuid().ToString();
handler = Wndproc;
WNDCLASSW wndProcWindow;
wndProcWindow.style = 0;
wndProcWindow.lpfnWndProc = handler;
wndProcWindow.cbClsExtra = 0;
wndProcWindow.cbWndExtra = 0;
wndProcWindow.hInstance = new HINSTANCE();
wndProcWindow.hIcon = new HICON();
wndProcWindow.hCursor = new HCURSOR();
wndProcWindow.hbrBackground = new HBRUSH();
fixed (char* menuName = "")
{
wndProcWindow.lpszMenuName = new PCWSTR(menuName);
}
fixed (char* className = windowName)
{
wndProcWindow.lpszClassName = new PCWSTR(className);
}
PInvoke.RegisterClass(wndProcWindow);
Handle = PInvoke.CreateWindowEx(0, wndProcWindow.lpszClassName,
new PCWSTR(), 0, 0, 0, 0, 0, new HWND(),
new HMENU(),
new HINSTANCE());
}
private LRESULT Wndproc(HWND param0, uint param1, WPARAM param2, LPARAM param3)
{
publisher.Publish(new WndProcEventArgs(param1, (uint)param2.Value, (uint)param3.Value));
return PInvoke.DefWindowProc(param0, param1, param2, param3);
}
public void Initialize()
{
InitializeWndProc();
}
}
public class PointerMonitor(IPublisher publisher) :
IPointerMonitor
{
private bool isDisposed;
private bool isPointerDrag;
private bool isPointerPressed;
private HOOKPROC? mouseEventDelegate;
private UnhookWindowsHookExSafeHandle? mouseHandle;
~PointerMonitor()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public unsafe void Initialize() =>
InitializeHook();
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
RemoveHook();
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)
{
if (TryGetPointerLocation(out var location))
{
switch ((uint)wParam.Value)
{
case (uint)WndProcMessages.WM_MOUSEMOVE:
SendPointerMoved(location);
break;
case (uint)WndProcMessages.WM_LBUTTONUP:
SendPointerReleased(location, PointerButton.Left);
break;
case (uint)WndProcMessages.WM_MBUTTONUP:
SendPointerReleased(location, PointerButton.Middle);
break;
case (uint)WndProcMessages.WM_RBUTTONUP:
SendPointerReleased(location, PointerButton.Right);
break;
case (uint)WndProcMessages.WM_LBUTTONDOWN:
SendPointerPressed(location, PointerButton.Left);
break;
case (uint)WndProcMessages.WM_MBUTTONDOWN:
SendPointerPressed(location, PointerButton.Middle);
break;
case (uint)WndProcMessages.WM_RBUTTONDOWN:
SendPointerPressed(location, PointerButton.Right);
break;
}
}
}
return PInvoke.CallNextHookEx(mouseHandle, nCode, wParam, lParam);
}
private unsafe void RemoveHook()
{
if (mouseHandle is not null && mouseHandle.DangerousGetHandle() != nint.Zero)
{
PInvoke.UnhookWindowsHookEx((HHOOK)mouseHandle.DangerousGetHandle());
}
}
private void SendPointerMoved(PointerLocation location)
{
if (isPointerPressed)
{
if (!isPointerDrag)
{
isPointerDrag = true;
}
publisher.Publish(new PointerDragEventArgs(location));
}
publisher.Publish(new PointerMovedEventArgs(location));
}
private void SendPointerPressed(PointerLocation location, PointerButton button)
{
isPointerPressed = true;
publisher.Publish(new PointerPressedEventArgs(location, button));
}
private void SendPointerReleased(PointerLocation location, PointerButton button)
{
if (isPointerPressed)
{
if (isPointerDrag)
{
isPointerDrag = false;
publisher.Publish(new PointerDragReleasedEventArgs(location, button));
}
isPointerPressed = false;
publisher.Publish(new PointerReleasedEventArgs(location, button));
}
}
private unsafe bool TryGetPointer(out Point point)
{
fixed (Point* lpPointLocal = &point)
{
return PInvoke.GetPhysicalCursorPos(lpPointLocal);
}
}
private bool TryGetPointerLocation([MaybeNullWhen(false)] out PointerLocation location)
{
if (TryGetPointer(out Point point))
{
location = new PointerLocation(point.X, point.Y);
return true;
}
location = null;
return false;
}
}