152 lines
5.4 KiB
C#
152 lines
5.4 KiB
C#
using anjianjingling.Native;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace anjianjingling.Input
|
|
{
|
|
public enum MouseButton { Left, Right, Middle }
|
|
|
|
/// <summary>
|
|
/// 系统级鼠标模拟(SendInput)。
|
|
/// </summary>
|
|
public sealed class MouseInput
|
|
{
|
|
private static readonly Random Rng = new();
|
|
|
|
/// <summary>虚拟桌面(含多显示器)尺寸与原点。</summary>
|
|
private static Rectangle VirtualScreen()
|
|
{
|
|
int x = Win32.GetSystemMetrics(Win32.SM_XVIRTUALSCREEN);
|
|
int y = Win32.GetSystemMetrics(Win32.SM_YVIRTUALSCREEN);
|
|
int w = Win32.GetSystemMetrics(Win32.SM_CXVIRTUALSCREEN);
|
|
int h = Win32.GetSystemMetrics(Win32.SM_CYVIRTUALSCREEN);
|
|
return new Rectangle(x, y, w, h);
|
|
}
|
|
|
|
/// <summary>把屏幕物理坐标换算为 SendInput 绝对坐标(0..65535,含虚拟桌面偏移)。</summary>
|
|
private static void ToAbsolute(int x, int y, out int ax, out int ay)
|
|
{
|
|
var vs = VirtualScreen();
|
|
ax = (int)((x - vs.Left) * 65535.0 / (vs.Width - 1));
|
|
ay = (int)((y - vs.Top) * 65535.0 / (vs.Height - 1));
|
|
}
|
|
|
|
private static void SendSingle(Win32.MOUSEINPUT mi)
|
|
{
|
|
var input = new Win32.INPUT
|
|
{
|
|
type = Win32.INPUT_MOUSE,
|
|
u = new Win32.INPUTUNION { mi = mi }
|
|
};
|
|
Win32.SendInput(1, new[] { input }, Marshal.SizeOf<Win32.INPUT>());
|
|
}
|
|
|
|
/// <summary>移动鼠标到指定屏幕坐标(瞬时)。</summary>
|
|
public void MoveTo(int x, int y)
|
|
{
|
|
ToAbsolute(x, y, out int ax, out int ay);
|
|
SendSingle(new Win32.MOUSEINPUT
|
|
{
|
|
dx = ax,
|
|
dy = ay,
|
|
dwFlags = Win32.MOUSEEVENTF_MOVE | Win32.MOUSEEVENTF_ABSOLUTE | Win32.MOUSEEVENTF_VIRTUALDESK
|
|
});
|
|
}
|
|
|
|
/// <summary>相对移动。</summary>
|
|
public void MoveR(int dx, int dy)
|
|
{
|
|
var p = Position;
|
|
MoveTo(p.X + dx, p.Y + dy);
|
|
}
|
|
|
|
/// <summary>带随机偏移的移动(拟人化)。</summary>
|
|
public void MoveToJitter(int x, int y, int jitterPx = 3)
|
|
{
|
|
MoveTo(x + Rng.Next(-jitterPx, jitterPx + 1),
|
|
y + Rng.Next(-jitterPx, jitterPx + 1));
|
|
}
|
|
|
|
public void ButtonDown(MouseButton button)
|
|
=> SendSingle(new Win32.MOUSEINPUT { dwFlags = button switch
|
|
{
|
|
MouseButton.Left => Win32.MOUSEEVENTF_LEFTDOWN,
|
|
MouseButton.Right => Win32.MOUSEEVENTF_RIGHTDOWN,
|
|
MouseButton.Middle => Win32.MOUSEEVENTF_MIDDLEDOWN,
|
|
_ => throw new ArgumentOutOfRangeException(nameof(button))
|
|
} });
|
|
|
|
public void ButtonUp(MouseButton button)
|
|
=> SendSingle(new Win32.MOUSEINPUT { dwFlags = button switch
|
|
{
|
|
MouseButton.Left => Win32.MOUSEEVENTF_LEFTUP,
|
|
MouseButton.Right => Win32.MOUSEEVENTF_RIGHTUP,
|
|
MouseButton.Middle => Win32.MOUSEEVENTF_MIDDLEUP,
|
|
_ => throw new ArgumentOutOfRangeException(nameof(button))
|
|
} });
|
|
|
|
/// <summary>在当前位置点击;jitter=true 时落点带 ±3px 随机偏移(仅当指定坐标时)。</summary>
|
|
public void Click(MouseButton button, int? x = null, int? y = null, bool jitter = false)
|
|
{
|
|
if (x.HasValue && y.HasValue)
|
|
{
|
|
if (jitter) MoveToJitter(x.Value, y.Value);
|
|
else MoveTo(x.Value, y.Value);
|
|
Thread.Sleep(60 + Rng.Next(0, 50));
|
|
}
|
|
ButtonDown(button);
|
|
Thread.Sleep(40 + Rng.Next(0, 40));
|
|
ButtonUp(button);
|
|
}
|
|
|
|
/// <summary>双击(左键)。</summary>
|
|
public void DoubleClick(int? x = null, int? y = null, bool jitter = false)
|
|
{
|
|
Click(MouseButton.Left, x, y, jitter);
|
|
Thread.Sleep(50 + Rng.Next(0, 60));
|
|
Click(MouseButton.Left);
|
|
}
|
|
|
|
/// <summary>滚轮:正值向上滚,负值向下滚,单位为格(每格 120)。</summary>
|
|
public void Wheel(int delta)
|
|
{
|
|
SendSingle(new Win32.MOUSEINPUT
|
|
{
|
|
mouseData = (uint)(delta * Win32.WHEEL_DELTA),
|
|
dwFlags = Win32.MOUSEEVENTF_WHEEL
|
|
});
|
|
}
|
|
|
|
/// <summary>从一点拖拽到另一点,按 duration 插值移动,模拟人手。</summary>
|
|
public void Drag(int x1, int y1, int x2, int y2, int durationMs = 400)
|
|
{
|
|
MoveTo(x1, y1);
|
|
Thread.Sleep(100);
|
|
ButtonDown(MouseButton.Left);
|
|
|
|
int steps = Math.Max(5, durationMs / 16);
|
|
for (int i = 1; i <= steps; i++)
|
|
{
|
|
double t = (double)i / steps;
|
|
// 轻微非线性,模拟加减速
|
|
double e = t * t * (3 - 2 * t);
|
|
int cx = (int)(x1 + (x2 - x1) * e);
|
|
int cy = (int)(y1 + (y2 - y1) * e);
|
|
MoveTo(cx, cy);
|
|
Thread.Sleep(16);
|
|
}
|
|
Thread.Sleep(80);
|
|
ButtonUp(MouseButton.Left);
|
|
}
|
|
|
|
/// <summary>当前鼠标屏幕坐标。</summary>
|
|
public Point Position
|
|
{
|
|
get
|
|
{
|
|
Win32.GetCursorPos(out var p);
|
|
return p;
|
|
}
|
|
}
|
|
}
|
|
}
|