first commit
This commit is contained in:
commit
761b95080f
404 files changed
+312608
No files matched your search
@@ -0,0 +1,151 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user