|
@@ -5,7 +5,9 @@ using System.Diagnostics.CodeAnalysis;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.InteropServices;
|
|
using System.Windows;
|
|
using System.Windows;
|
|
using System.Windows.Input;
|
|
using System.Windows.Input;
|
|
|
|
+using System.Windows.Interop;
|
|
using System.Windows.Threading;
|
|
using System.Windows.Threading;
|
|
|
|
+using PixiEditor.Views;
|
|
|
|
|
|
namespace PixiEditor.Helpers;
|
|
namespace PixiEditor.Helpers;
|
|
|
|
|
|
@@ -13,86 +15,91 @@ public delegate void MouseUpEventHandler(object sender, Point p, MouseButton but
|
|
|
|
|
|
// see https://stackoverflow.com/questions/22659925/how-to-capture-mouseup-event-outside-the-wpf-window
|
|
// see https://stackoverflow.com/questions/22659925/how-to-capture-mouseup-event-outside-the-wpf-window
|
|
[ExcludeFromCodeCoverage]
|
|
[ExcludeFromCodeCoverage]
|
|
-internal static class GlobalMouseHook
|
|
|
|
|
|
+internal class GlobalMouseHook
|
|
{
|
|
{
|
|
- private static int mouseHookHandle;
|
|
|
|
- private static Win32.HookProc mouseDelegate;
|
|
|
|
|
|
+ private static readonly Lazy<GlobalMouseHook> lazy = new Lazy<GlobalMouseHook>(() => new GlobalMouseHook());
|
|
|
|
+ public static GlobalMouseHook Instance => lazy.Value;
|
|
|
|
|
|
|
|
+ public event MouseUpEventHandler OnMouseUp;
|
|
|
|
|
|
- public static event MouseUpEventHandler OnMouseUp
|
|
|
|
- {
|
|
|
|
- add
|
|
|
|
- {
|
|
|
|
- // disable low-level hook in debug to prevent mouse lag when pausing in debugger
|
|
|
|
-#if !DEBUG
|
|
|
|
- Subscribe();
|
|
|
|
-#endif
|
|
|
|
- MouseUp += value;
|
|
|
|
- }
|
|
|
|
|
|
+ private int mouseHookHandle;
|
|
|
|
+ private Win32.HookProc mouseDelegate;
|
|
|
|
|
|
- remove
|
|
|
|
- {
|
|
|
|
- MouseUp -= value;
|
|
|
|
-#if !DEBUG
|
|
|
|
- Unsubscribe();
|
|
|
|
-#endif
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ private Thread mouseHookWindowThread;
|
|
|
|
+ private IntPtr mainWindowHandle;
|
|
|
|
+ private IntPtr childWindowHandle;
|
|
|
|
|
|
- private static event MouseUpEventHandler MouseUp;
|
|
|
|
|
|
+ private GlobalMouseHook() { }
|
|
|
|
|
|
- public static void RaiseMouseUp()
|
|
|
|
|
|
+ public void Initilize(MainWindow window)
|
|
{
|
|
{
|
|
- MouseUp?.Invoke(default, default, default);
|
|
|
|
- }
|
|
|
|
|
|
+ // disable low-level hook in debug to prevent mouse lag when pausing in debugger
|
|
|
|
+#if DEBUG
|
|
|
|
+ return;
|
|
|
|
+#endif
|
|
|
|
+ mainWindowHandle = new WindowInteropHelper(window).Handle;
|
|
|
|
+ if (mainWindowHandle == IntPtr.Zero)
|
|
|
|
+ throw new InvalidOperationException();
|
|
|
|
|
|
- private static void Unsubscribe()
|
|
|
|
- {
|
|
|
|
- if (mouseHookHandle != 0)
|
|
|
|
|
|
+ window.Closed += (_, _) =>
|
|
{
|
|
{
|
|
- int result = Win32.UnhookWindowsHookEx(mouseHookHandle);
|
|
|
|
- mouseHookHandle = 0;
|
|
|
|
- mouseDelegate = null;
|
|
|
|
- if (result == 0)
|
|
|
|
- {
|
|
|
|
- int errorCode = Marshal.GetLastWin32Error();
|
|
|
|
- throw new Win32Exception(errorCode);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ if (childWindowHandle != IntPtr.Zero)
|
|
|
|
+ Win32.PostMessage(childWindowHandle, Win32.WM_CLOSE, 0, 0);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ mouseHookWindowThread = new Thread(StartMouseHook)
|
|
|
|
+ {
|
|
|
|
+ Name = $"{nameof(GlobalMouseHook)} Thread"
|
|
|
|
+ };
|
|
|
|
+ mouseHookWindowThread.Start();
|
|
}
|
|
}
|
|
|
|
|
|
- private static void Subscribe()
|
|
|
|
|
|
+ private void StartMouseHook()
|
|
{
|
|
{
|
|
|
|
+ LowLevelWindow window = new LowLevelWindow(nameof(GlobalMouseHook), mainWindowHandle);
|
|
|
|
+ childWindowHandle = window.WindowHandle;
|
|
|
|
+
|
|
|
|
+ mouseDelegate = MouseHookProc;
|
|
|
|
+ mouseHookHandle = Win32.SetWindowsHookEx(
|
|
|
|
+ Win32.WH_MOUSE_LL,
|
|
|
|
+ mouseDelegate,
|
|
|
|
+ Win32.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName),
|
|
|
|
+ 0);
|
|
if (mouseHookHandle == 0)
|
|
if (mouseHookHandle == 0)
|
|
{
|
|
{
|
|
- mouseDelegate = MouseHookProc;
|
|
|
|
- mouseHookHandle = Win32.SetWindowsHookEx(
|
|
|
|
- Win32.WH_MOUSE_LL,
|
|
|
|
- mouseDelegate,
|
|
|
|
- Win32.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName),
|
|
|
|
- 0);
|
|
|
|
- if (mouseHookHandle == 0)
|
|
|
|
- {
|
|
|
|
- int errorCode = Marshal.GetLastWin32Error();
|
|
|
|
- throw new Win32Exception(errorCode);
|
|
|
|
- }
|
|
|
|
|
|
+ int errorCode = Marshal.GetLastWin32Error();
|
|
|
|
+ throw new Win32Exception(errorCode);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ window.RunEventLoop();
|
|
}
|
|
}
|
|
|
|
|
|
- private static int MouseHookProc(int nCode, int wParam, IntPtr lParam)
|
|
|
|
|
|
+ //private void Unsubscribe()
|
|
|
|
+ //{
|
|
|
|
+ // int result = Win32.UnhookWindowsHookEx(mouseHookHandle);
|
|
|
|
+ // mouseHookHandle = 0;
|
|
|
|
+ // mouseDelegate = null;
|
|
|
|
+ // if (result == 0)
|
|
|
|
+ // {
|
|
|
|
+ // int errorCode = Marshal.GetLastWin32Error();
|
|
|
|
+ // throw new Win32Exception(errorCode);
|
|
|
|
+ // }
|
|
|
|
+ //}
|
|
|
|
+
|
|
|
|
+ private int MouseHookProc(int nCode, int wParam, IntPtr lParam)
|
|
{
|
|
{
|
|
if (nCode >= 0)
|
|
if (nCode >= 0)
|
|
{
|
|
{
|
|
Win32.MSLLHOOKSTRUCT mouseHookStruct = (Win32.MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.MSLLHOOKSTRUCT));
|
|
Win32.MSLLHOOKSTRUCT mouseHookStruct = (Win32.MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.MSLLHOOKSTRUCT));
|
|
if (wParam == Win32.WM_LBUTTONUP || wParam == Win32.WM_MBUTTONUP || wParam == Win32.WM_RBUTTONUP)
|
|
if (wParam == Win32.WM_LBUTTONUP || wParam == Win32.WM_MBUTTONUP || wParam == Win32.WM_RBUTTONUP)
|
|
{
|
|
{
|
|
- if (MouseUp != null)
|
|
|
|
|
|
+ if (OnMouseUp is not null)
|
|
{
|
|
{
|
|
|
|
|
|
MouseButton button = wParam == Win32.WM_LBUTTONUP ? MouseButton.Left
|
|
MouseButton button = wParam == Win32.WM_LBUTTONUP ? MouseButton.Left
|
|
: wParam == Win32.WM_MBUTTONUP ? MouseButton.Middle : MouseButton.Right;
|
|
: wParam == Win32.WM_MBUTTONUP ? MouseButton.Middle : MouseButton.Right;
|
|
- Dispatcher.CurrentDispatcher.BeginInvoke(() =>
|
|
|
|
- MouseUp.Invoke(null, new Point(mouseHookStruct.Pt.X, mouseHookStruct.Pt.Y), button));
|
|
|
|
|
|
+ Application.Current?.Dispatcher.BeginInvoke(() =>
|
|
|
|
+ OnMouseUp.Invoke(null, new Point(mouseHookStruct.Pt.X, mouseHookStruct.Pt.Y), button));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|