mirror of
https://github.com/stablyai/orca.git
synced 2026-10-21 08:03:33 +00:00
fix(computer): fence macOS HID coordinate clicks
This commit is contained in:
+65
-1
@@ -6,6 +6,22 @@
|
||||
/// happens). The window server also drops a mouseUp posted back-to-back
|
||||
/// with its mouseDown, so consecutive events need a pause between them.
|
||||
public enum SyntheticMouseClickDelivery {
|
||||
public static let maxClickCount = 3
|
||||
|
||||
public struct Recipient: Equatable, Sendable {
|
||||
public let ownerPID: Int32
|
||||
public let windowID: UInt32
|
||||
|
||||
public init(ownerPID: Int32, windowID: UInt32) {
|
||||
self.ownerPID = ownerPID
|
||||
self.windowID = windowID
|
||||
}
|
||||
}
|
||||
|
||||
public enum FenceFailure: Error, Equatable {
|
||||
case recipientChanged(expected: Recipient, actual: Recipient?, deliveredPresses: Int)
|
||||
}
|
||||
|
||||
public enum Step: Equatable {
|
||||
case move
|
||||
case buttonDown(pressIndex: Int)
|
||||
@@ -21,7 +37,7 @@ public enum SyntheticMouseClickDelivery {
|
||||
/// clicks instead of independent single clicks.
|
||||
public static func steps(clickCount: Int) -> [Step] {
|
||||
var steps: [Step] = [.move]
|
||||
for press in 1...max(clickCount, 1) {
|
||||
for press in 1...min(max(clickCount, 1), maxClickCount) {
|
||||
steps.append(.buttonDown(pressIndex: press))
|
||||
steps.append(.buttonUp(pressIndex: press))
|
||||
}
|
||||
@@ -37,4 +53,52 @@ public enum SyntheticMouseClickDelivery {
|
||||
return Int64(pressIndex)
|
||||
}
|
||||
}
|
||||
|
||||
public static func uniqueWindowCandidate<Candidate>(
|
||||
from candidates: [Candidate],
|
||||
matching predicate: (Candidate) -> Bool
|
||||
) -> Candidate? {
|
||||
var match: Candidate?
|
||||
for candidate in candidates where predicate(candidate) {
|
||||
guard match == nil else { return nil }
|
||||
match = candidate
|
||||
}
|
||||
return match
|
||||
}
|
||||
|
||||
public static func deliver<Event>(
|
||||
clickCount: Int,
|
||||
target: Recipient,
|
||||
currentRecipient: () -> Recipient?,
|
||||
makeEvent: (Step) throws -> Event,
|
||||
post: (Event) -> Void,
|
||||
pause: (UInt32) -> Void
|
||||
) throws {
|
||||
post(try makeEvent(.move))
|
||||
pause(interEventPauseMicroseconds)
|
||||
for pressIndex in 1...min(max(clickCount, 1), maxClickCount) {
|
||||
let beforeDown = currentRecipient()
|
||||
guard beforeDown == target else {
|
||||
throw FenceFailure.recipientChanged(
|
||||
expected: target,
|
||||
actual: beforeDown,
|
||||
deliveredPresses: pressIndex - 1
|
||||
)
|
||||
}
|
||||
let down = try makeEvent(.buttonDown(pressIndex: pressIndex))
|
||||
let up = try makeEvent(.buttonUp(pressIndex: pressIndex))
|
||||
post(down)
|
||||
pause(interEventPauseMicroseconds)
|
||||
post(up)
|
||||
let afterUp = currentRecipient()
|
||||
guard afterUp == target else {
|
||||
throw FenceFailure.recipientChanged(
|
||||
expected: target,
|
||||
actual: afterUp,
|
||||
deliveredPresses: pressIndex
|
||||
)
|
||||
}
|
||||
pause(interEventPauseMicroseconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user