Skip to content

Instantly share code, notes, and snippets.

@vorce
Last active May 4, 2024 15:02
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save vorce/04e660526473beecdc3029cf7c5a761c to your computer and use it in GitHub Desktop.
Save vorce/04e660526473beecdc3029cf7c5a761c to your computer and use it in GitHub Desktop.
Mouse move and click test thing for macos in swift
import Cocoa
import Foundation
// Move around and click automatically at random places in macos, kinda human like in a cheap way.
// Moves the mouse pointer to `moves` random locations on the screen and runs the `action` function at
// each point with the point as argument.
func mouseMoveWithAction(moves: Int, action: (CGPoint) -> Void = defaultAction) {
let screenSize = NSScreen.main?.visibleFrame.size
let currentLocation = NSEvent.mouseLocation
var currentPoint = CGPoint(x: currentLocation.x, y: currentLocation.y)
for _ in 0 ... (moves - 1) {
let randomXPos = CGFloat.random(in: 0..<screenSize!.width)
let randomYPos = CGFloat.random(in: 0..<screenSize!.height)
let destination = CGPoint(x: randomXPos, y: randomYPos)
let easing = Float.random(in: 270..<650)
humanizedMouseMove(from: currentPoint, to: destination, easing: easing)
let pause = pauseTime()
print("pause: ", pause)
usleep(useconds_t(pause))
currentPoint = destination
action(currentPoint)
}
print("done with \(moves) moves")
}
func defaultAction(point: CGPoint) {
print("<no action at \(point)>")
}
// Pause for slightly longer 20% of the time
func pauseTime() -> Int {
let pauseType = Int.random(in: 0..<100)
if(pauseType >= 80) {
let longPause = Int.random(in: 100_000..<2_000_000)
return longPause
} else {
let shortPause = Int.random(in: 10_000..<500_000)
return shortPause
}
}
func moveMouseTo(point: CGPoint) {
CGEvent(mouseEventSource: nil, mouseType: CGEventType.mouseMoved, mouseCursorPosition: point, mouseButton: CGMouseButton.left)?.post(tap: CGEventTapLocation.cghidEventTap)
}
func mouseClick(point: CGPoint, mouseButton: CGMouseButton = CGMouseButton.left) {
CGEvent(mouseEventSource: nil, mouseType: CGEventType.leftMouseDown, mouseCursorPosition: point, mouseButton: mouseButton)?.post(tap: CGEventTapLocation.cghidEventTap)
usleep(useconds_t(Int.random(in: 400_010..<600_200)))
CGEvent(mouseEventSource: nil, mouseType: CGEventType.leftMouseUp, mouseCursorPosition: point, mouseButton: mouseButton)?.post(tap: CGEventTapLocation.cghidEventTap)
}
// Moves the mouse from point `from` to point `to`, with some `easing` factor.
// Easing means ramping up the movement smoothly in the beginning, and ramping it down towards the end.
// The cheap "humanized" factor.
func humanizedMouseMove(from: CGPoint, to: CGPoint, easing: Float = 100.0) {
print("current location: ", from)
print("moving to: ", to)
let distance = distanceBetween(from: from, to: to)
let steps = Int(distance * CGFloat(easing) / 100) + 1;
let xDiff = to.x - from.x
let yDiff = to.y - from.y
let stepSize = 1.0 / Double(steps)
for i in 0 ... steps {
let factor = cubicEaseOut(point: Float(stepSize) * Float(i))
let stepPoint = CGPoint(x: from.x + (CGFloat(factor) * xDiff), y: from.y + (CGFloat(factor) * yDiff))
moveMouseTo(point: stepPoint)
usleep(useconds_t(stepPause()))
}
}
func stepPause() -> Int {
return Int.random(in: 200..<300)
}
func distanceBetween(from: CGPoint, to: CGPoint) -> CGFloat {
let distanceX = from.x - to.x
let distanceY = from.y - to.y
return sqrt(distanceX * distanceX + distanceY * distanceY)
}
// stolen from cliclick
func cubicEaseOut(point: Float) -> Float {
if(point < 0.5) {
return 4 * point * point * point
} else {
let f = ((2 * point) - 2)
return 0.5 * f * f * f + 1
}
}
mouseMoveWithAction(moves: 2) { mouseClick(point: $0) }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment