From 398f252db54256e9ef12798d415c468b10135ee4 Mon Sep 17 00:00:00 2001 From: arithex Date: Sun, 8 Jan 2023 03:12:50 -0800 Subject: [PATCH] first pass for mouse-button and -movement support, in Win32_SendInput --- src/Win32_RawInput.cs | 2 +- src/Win32_SendInput.cs | 111 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 109 insertions(+), 4 deletions(-) diff --git a/src/Win32_RawInput.cs b/src/Win32_RawInput.cs index b3dfa22..211c7a1 100644 --- a/src/Win32_RawInput.cs +++ b/src/Win32_RawInput.cs @@ -532,7 +532,7 @@ internal static extern int HidP_GetUsages(//aka "GetButtonsCurrentlyPressedInThi [In, Out] ref uint usageLength, IntPtr pPreparsedData, IntPtr refHidReportBuffer, - ulong reportLength + uint reportLength ); } diff --git a/src/Win32_SendInput.cs b/src/Win32_SendInput.cs index 211388d..9a883a9 100644 --- a/src/Win32_SendInput.cs +++ b/src/Win32_SendInput.cs @@ -1,6 +1,7 @@ /* - * Interop wrapper for programmatically generating keystroke, and manipulating mousewheel. - * Not done: mouse-move, mouse buttons. + * Interop wrapper for programmatically generating keystrokes, and manipulating mouse. + * + * TODO: test with multi-monitor.. coords relative to "primary" desktop? virtual desktop-space? */ using System; using System.ComponentModel; @@ -27,6 +28,50 @@ public static void SendKeystroke( ushort scanCode, bool keyDown, bool isExtended return; } + //---------------------------------------- + public static void MoveMouseAbsolute( int x, int y ) + { + //?: multiple monitors? virtual desktop? + _Interop_User32.InputStruct[] inputs = new _Interop_User32.InputStruct[] { + _PrepMousePosition(x, y, virtualCoords:true ) //? + }; + + int status = _Interop_User32.SendInput(inputs.Length, inputs, _Interop_User32.InputStruct.MarshalSize); + if (status != inputs.Length) + throw new Win32Exception(); + + return; + } + + //---------------------------------------- + public static void MoveMouseRelative( int dx, int dy ) + { + _Interop_User32.InputStruct[] inputs = new _Interop_User32.InputStruct[] { + _PrepMouseMovement(dx, dy, virtualCoords:true ) //? + }; + + int status = _Interop_User32.SendInput(inputs.Length, inputs, _Interop_User32.InputStruct.MarshalSize); + if (status != inputs.Length) + throw new Win32Exception(); + + return; + } + + //---------------------------------------- + public static void SendMouseButton( int mouseButton, bool buttonDown ) + { + //?: swap mouse buttons? + _Interop_User32.InputStruct[] inputs = new _Interop_User32.InputStruct[] { + _PrepMouseButton(mouseButton, buttonDown) + }; + + int status = _Interop_User32.SendInput(inputs.Length, inputs, _Interop_User32.InputStruct.MarshalSize); + if (status != inputs.Length) + throw new Win32Exception(); + + return; + } + //---------------------------------------- public static void MoveMouseWheel( int wheelDelta ) { @@ -44,6 +89,56 @@ public static void MoveMouseWheel( int wheelDelta ) //-------------------------------------------------------------- // Managed helpers + static _Interop_User32.InputStruct _PrepMousePosition( int x, int y, bool virtualCoords ) + { + _Interop_User32.InputStruct inputStruct = new _Interop_User32.InputStruct(); + inputStruct.type = _Interop_User32.INPUT_MOUSE; + inputStruct._union.mouseInput.dx = x; + inputStruct._union.mouseInput.dy = y; + inputStruct._union.mouseInput.flags = (_Interop_User32.MOUSEEVENTF_MOVE | _Interop_User32.MOUSEEVENTF_ABSOLUTE); + + if (virtualCoords) + inputStruct._union.mouseInput.flags |= _Interop_User32.MOUSEEVENTF_VIRTUALDESK; + + return inputStruct; + } + + static _Interop_User32.InputStruct _PrepMouseMovement( int dx, int dy, bool virtualCoords ) + { + _Interop_User32.InputStruct inputStruct = new _Interop_User32.InputStruct(); + inputStruct.type = _Interop_User32.INPUT_MOUSE; + inputStruct._union.mouseInput.dx = dx; + inputStruct._union.mouseInput.dy = dy; + inputStruct._union.mouseInput.flags = (_Interop_User32.MOUSEEVENTF_MOVE); + + if (virtualCoords) + inputStruct._union.mouseInput.flags |= _Interop_User32.MOUSEEVENTF_VIRTUALDESK; + + return inputStruct; + } + + static _Interop_User32.InputStruct _PrepMouseButton( int mouseButton, bool buttonDown ) + { + _Interop_User32.InputStruct inputStruct = new _Interop_User32.InputStruct(); + inputStruct.type = _Interop_User32.INPUT_MOUSE; + switch (mouseButton) + { + case 0: //left + inputStruct._union.mouseInput.flags = buttonDown ? _Interop_User32.MOUSEEVENTF_LEFTDOWN : _Interop_User32.MOUSEEVENTF_LEFTUP; + break; + case 1: //right + inputStruct._union.mouseInput.flags = buttonDown ? _Interop_User32.MOUSEEVENTF_RIGHTDOWN : _Interop_User32.MOUSEEVENTF_RIGHTUP; + break; + case 2: //middle + inputStruct._union.mouseInput.flags = buttonDown ? _Interop_User32.MOUSEEVENTF_MIDDLEDOWN : _Interop_User32.MOUSEEVENTF_MIDDLEUP; + break; + default: + throw new ArgumentException(); + } + + return inputStruct; + } + static _Interop_User32.InputStruct _PrepMouseWheelDelta( int wheelDelta ) { _Interop_User32.InputStruct inputStruct = new _Interop_User32.InputStruct(); @@ -79,8 +174,18 @@ static class _Interop_User32 internal const uint INPUT_MOUSE = 0; internal const uint INPUT_KEYBOARD = 1; + internal const ushort MOUSEEVENTF_MOVE = 0x0001; + internal const ushort MOUSEEVENTF_LEFTDOWN = 0x0002; + internal const ushort MOUSEEVENTF_LEFTUP = 0x0004; + internal const ushort MOUSEEVENTF_RIGHTDOWN = 0x0008; + internal const ushort MOUSEEVENTF_RIGHTUP = 0x0010; + internal const ushort MOUSEEVENTF_MIDDLEDOWN = 0x0020; + internal const ushort MOUSEEVENTF_MIDDLEUP = 0x0040; internal const ushort MOUSEEVENTF_WHEEL = 0x0800; + internal const ushort MOUSEEVENTF_VIRTUALDESK = 0x4000; + internal const ushort MOUSEEVENTF_ABSOLUTE = 0x8000; + internal const ushort KEYEVENTF_SCANCODE = 0x0008; internal const ushort KEYEVENTF_KEYUP = 0x0002; internal const ushort KEYEVENTF_EXTENDEDKEY = 0x0001; @@ -108,7 +213,7 @@ internal struct InputStruct_union [FieldOffset(0)] internal KeybdInputStruct keybdInput; - //NB: This option seems unimplemented? And smaller size than others, so irrelevant. + //NB: This option seems unimplemented? And smaller size than others, so irrelevant as part of the union. //[FieldOffset(0)] //internal HARDWAREINPUT hi; }