Reputation: 35194
public static class SampleMouseMove
{
static Random random = new Random();
static int mouseSpeed = 15;
public static void MoveMouse(int x, int y, int rx, int ry)
{
Point c = new Point();
GetCursorPos(out c);
x += random.Next(rx);
y += random.Next(ry);
double randomSpeed = Math.Max((random.Next(mouseSpeed) / 2.0 + mouseSpeed) / 10.0, 0.1);
WindMouse(c.X, c.Y, x, y, 9.0, 3.0, 10.0 / randomSpeed,
15.0 / randomSpeed, 10.0 * randomSpeed, 10.0 * randomSpeed);
}
static void WindMouse(double xs, double ys, double xe, double ye,
double gravity, double wind, double minWait, double maxWait,
double maxStep, double targetArea)
{
double dist, windX = 0, windY = 0, veloX = 0, veloY = 0, randomDist, veloMag, step;
int oldX, oldY, newX = (int)Math.Round(xs), newY = (int)Math.Round(ys);
double waitDiff = maxWait - minWait;
double sqrt2 = Math.Sqrt(2.0);
double sqrt3 = Math.Sqrt(3.0);
double sqrt5 = Math.Sqrt(5.0);
dist = Hypot(xe - xs, ye - ys);
while (dist > 1.0)
{
wind = Math.Min(wind, dist);
if (dist >= targetArea)
{
int w = random.Next((int)Math.Round(wind) * 2 + 1);
windX = windX / sqrt3 + (w - wind) / sqrt5;
windY = windY / sqrt3 + (w - wind) / sqrt5;
}
else
{
windX = windX / sqrt2;
windY = windY / sqrt2;
if (maxStep < 3)
maxStep = random.Next(3) + 3.0;
else
maxStep = maxStep / sqrt5;
}
veloX += windX;
veloY += windY;
veloX = veloX + gravity * (xe - xs) / dist;
veloY = veloY + gravity * (ye - ys) / dist;
if (Hypot(veloX, veloY) > maxStep)
{
randomDist = maxStep / 2.0 + random.Next((int)Math.Round(maxStep) / 2);
veloMag = Hypot(veloX, veloY);
veloX = (veloX / veloMag) * randomDist;
veloY = (veloY / veloMag) * randomDist;
}
oldX = (int)Math.Round(xs);
oldY = (int)Math.Round(ys);
xs += veloX;
ys += veloY;
dist = Hypot(xe - xs, ye - ys);
newX = (int)Math.Round(xs);
newY = (int)Math.Round(ys);
if (oldX != newX || oldY != newY)
SetCursorPos(newX, newY);
step = Hypot(xs - oldX, ys - oldY);
int wait = (int)Math.Round(waitDiff * (step / maxStep) + minWait);
Thread.Sleep(wait); //<---
}
int endX = (int)Math.Round(xe);
int endY = (int)Math.Round(ye);
if (endX != newX || endY != newY)
SetCursorPos(endX, endY);
}
static double Hypot(double dx, double dy)
{
return Math.Sqrt(dx * dx + dy * dy);
}
[DllImport("user32.dll")]
static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out Point p);
}
This is my code to move the mouse cursor to a certain point.
I would like to replace Thread.Sleep(wait)
above with something async that doesn't make the UI thread freeze. I figured out how to make a timer, but I'm not sure how to use it in the sample above.
var timer = new System.Windows.Forms.Timer();
timer.Interval = wait;
timer.Tick += (sender, args) => { /* do stuff */ };
timer.Start();
Upvotes: 1
Views: 1450
Reputation: 729
Keep in mind that your primary thread for your application is going to be the one that processes messages from windows. This includes the ability to respond to keyboard and mouse events and redrawing the screen. If you put your primary thread to sleep (or just make it do a bunch of synchronous work) your UI will appear to "freeze" until the thread can resume processing events from the message pump.
The Timer object can be useful in a scenario like this, but there used to be issues with memory usage an long-running timers. A kind of memory leak problem with the class. I honestly don't know if that's been fixed.
That said, you could write a separate thread to do this work for you without much difficulty.
I have included the code as described below. Notice the change to the while loop, also, and the addition of the "StopMoveMouse" method. I have also added a ManualResetEvent field, which will enable you to "break out" of the sleep when the user requests to stop moving the mouse without having to wait for the timeout to expire. This really isn't necessary if the wait time is very short, but I added it for example, just in case. I've also added a boolean field which allows me to check to see if the thread is already running, to prevent a second call to BeginMoveMouse without stopping it first. This check could easily be modified to simply return and do nothing.
public class SampleMouseMove
{
delegate void BeginMoveMouseDelegate(int x, int y, int rx, int ry);
Random random = new Random();
int mouseSpeed = 15;
bool killMove = false;
bool running = false;
ManualResetEvent mre = new ManualResetEvent(false);
public SampleMouseMove()
{ }
public void BeginMoveMouse(int x, int y, int rx, int ry)
{
if (running)
throw new Exception("Mouse is already being moved.");
BeginMoveMouseDelegate del = new BeginMoveMouseDelegate(MoveMouse);
del.BeginInvoke(x, y, rx, ry, new AsyncCallback(BeginMoveMouseCallback), del);
running = true;
}
public void StopMoveMouse()
{
killMove = true;
mre.Set();
}
void BeginMoveMouseCallback(IAsyncResult state)
{
BeginMoveMouseDelegate del = (BeginMoveMouseDelegate)state.AsyncState;
del.EndInvoke(state);
mre.Reset();
killMove = false;
running = false;
}
public void MoveMouse(int x, int y, int rx, int ry)
{
Point c = new Point();
GetCursorPos(out c);
x += random.Next(rx);
y += random.Next(ry);
double randomSpeed = Math.Max((random.Next(mouseSpeed) / 2.0 + mouseSpeed) / 10.0, 0.1);
WindMouse(c.X, c.Y, x, y, 9.0, 3.0, 10.0 / randomSpeed,
15.0 / randomSpeed, 10.0 * randomSpeed, 10.0 * randomSpeed);
}
void WindMouse(double xs, double ys, double xe, double ye,
double gravity, double wind, double minWait, double maxWait,
double maxStep, double targetArea)
{
double dist, windX = 0, windY = 0, veloX = 0, veloY = 0, randomDist, veloMag, step;
int oldX, oldY, newX = (int)Math.Round(xs), newY = (int)Math.Round(ys);
double waitDiff = maxWait - minWait;
double sqrt2 = Math.Sqrt(2.0);
double sqrt3 = Math.Sqrt(3.0);
double sqrt5 = Math.Sqrt(5.0);
dist = Hypot(xe - xs, ye - ys);
while (dist > 1.0 && !killMove)
{
wind = Math.Min(wind, dist);
if (dist >= targetArea)
{
int w = random.Next((int)Math.Round(wind) * 2 + 1);
windX = windX / sqrt3 + (w - wind) / sqrt5;
windY = windY / sqrt3 + (w - wind) / sqrt5;
}
else
{
windX = windX / sqrt2;
windY = windY / sqrt2;
if (maxStep < 3)
maxStep = random.Next(3) + 3.0;
else
maxStep = maxStep / sqrt5;
}
veloX += windX;
veloY += windY;
veloX = veloX + gravity * (xe - xs) / dist;
veloY = veloY + gravity * (ye - ys) / dist;
if (Hypot(veloX, veloY) > maxStep)
{
randomDist = maxStep / 2.0 + random.Next((int)Math.Round(maxStep) / 2);
veloMag = Hypot(veloX, veloY);
veloX = (veloX / veloMag) * randomDist;
veloY = (veloY / veloMag) * randomDist;
}
oldX = (int)Math.Round(xs);
oldY = (int)Math.Round(ys);
xs += veloX;
ys += veloY;
dist = Hypot(xe - xs, ye - ys);
newX = (int)Math.Round(xs);
newY = (int)Math.Round(ys);
if (oldX != newX || oldY != newY)
SetCursorPos(newX, newY);
step = Hypot(xs - oldX, ys - oldY);
int wait = (int)Math.Round(waitDiff * (step / maxStep) + minWait);
mre.WaitOne(wait); // <-- this works like Thread.Sleep(), but we can cancel the "wait" by calling mre.Set();
}
int endX = (int)Math.Round(xe);
int endY = (int)Math.Round(ye);
if (endX != newX || endY != newY)
SetCursorPos(endX, endY);
}
double Hypot(double dx, double dy)
{
return Math.Sqrt(dx * dx + dy * dy);
}
[DllImport("user32.dll")]
static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out Point p);
}
Upvotes: 1
Reputation: 203829
Change the signature of the method to: private static async Task WindMouse()
Replace Thread.Sleep(wait);
with await Task.Delay(wait);
Upvotes: 2
Reputation: 1989
You are putting the UI thread to sleep directly. If you want to do what you are asking about, you need to put your while loop into it's own thread and put THAT thread to sleep instead of the UI thread.
Either that, or put your entire WindMouse method off into it's own background thread.
Upvotes: 2