hands test
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import AppKit
|
||||
import ApplicationServices
|
||||
|
||||
func point(_ v: Any?, _ info: WindowInfo) throws -> CGPoint {
|
||||
func point(_ v: Any?, _ info: WindowInfo, _ table: RefTable) throws -> CGPoint {
|
||||
if let ref = v as? String {
|
||||
guard let f = frame(try element(ref, in: info)) else {
|
||||
guard let f = frame(try element(ref, in: info, table)) else {
|
||||
throw ToolError("\(ref) has no position on screen")
|
||||
}
|
||||
return CGPoint(x: f.midX, y: f.midY)
|
||||
@@ -26,48 +26,48 @@ func isInput(_ step: [String: Any]) -> Bool {
|
||||
return step.keys.contains(where: inputKeys.contains)
|
||||
}
|
||||
|
||||
func runStep(_ step: [String: Any], _ id: Int, _ w: AXUIElement) async throws -> String {
|
||||
func runStep(_ step: [String: Any], _ id: Int, _ w: AXUIElement, _ table: RefTable) async throws -> String {
|
||||
var info = try window(id)
|
||||
if isInput(step) {
|
||||
info = try await focusWindow(id, w)
|
||||
}
|
||||
if let ref = step.string("press") {
|
||||
try check(AXUIElementPerformAction(try element(ref, in: info), kAXPressAction as CFString), "press \(ref)")
|
||||
try check(AXUIElementPerformAction(try element(ref, in: info, table), kAXPressAction as CFString), "press \(ref)")
|
||||
return "pressed \(ref)"
|
||||
}
|
||||
if let ref = step.string("confirm") {
|
||||
try check(AXUIElementPerformAction(try element(ref, in: info), kAXConfirmAction as CFString), "confirm \(ref)")
|
||||
try check(AXUIElementPerformAction(try element(ref, in: info, table), kAXConfirmAction as CFString), "confirm \(ref)")
|
||||
return "confirmed \(ref)"
|
||||
}
|
||||
if let ref = step.string("focus") {
|
||||
try check(AXUIElementSetAttributeValue(try element(ref, in: info), kAXFocusedAttribute as CFString, kCFBooleanTrue), "focus \(ref)")
|
||||
try check(AXUIElementSetAttributeValue(try element(ref, in: info, table), kAXFocusedAttribute as CFString, kCFBooleanTrue), "focus \(ref)")
|
||||
return "focused \(ref)"
|
||||
}
|
||||
if let ref = step.string("set") {
|
||||
let value = try step.require("value", step.string)
|
||||
try check(AXUIElementSetAttributeValue(try element(ref, in: info), kAXValueAttribute as CFString, value as CFString), "set \(ref)")
|
||||
try check(AXUIElementSetAttributeValue(try element(ref, in: info, table), kAXValueAttribute as CFString, value as CFString), "set \(ref)")
|
||||
return "set \(ref)"
|
||||
}
|
||||
if let name = step.string("action") {
|
||||
let ref = try step.require("ref", step.string)
|
||||
try check(AXUIElementPerformAction(try element(ref, in: info), name as CFString), "\(name) \(ref)")
|
||||
try check(AXUIElementPerformAction(try element(ref, in: info, table), name as CFString), "\(name) \(ref)")
|
||||
return "\(name) \(ref)"
|
||||
}
|
||||
for (k, right, count) in [("click", false, 1), ("double_click", false, 2), ("right_click", true, 1)] {
|
||||
if step[k] != nil {
|
||||
let p = try point(step[k], info)
|
||||
let p = try point(step[k], info, table)
|
||||
await click(p, right: right, count: count)
|
||||
return "\(k) at screen \(Int(p.x)),\(Int(p.y))"
|
||||
}
|
||||
}
|
||||
if step["drag"] != nil {
|
||||
let from = try point(step["drag"], info)
|
||||
let to = try point(step["to"], info)
|
||||
let from = try point(step["drag"], info, table)
|
||||
let to = try point(step["to"], info, table)
|
||||
await drag(from, to)
|
||||
return "dragged"
|
||||
}
|
||||
if step["scroll"] != nil {
|
||||
let p = try point(step["scroll"], info)
|
||||
let p = try point(step["scroll"], info, table)
|
||||
let dx = step.int("dx") ?? 0
|
||||
let dy = step.int("dy") ?? 0
|
||||
guard (-1000...1000).contains(dx), (-1000...1000).contains(dy) else {
|
||||
@@ -91,8 +91,7 @@ func runStep(_ step: [String: Any], _ id: Int, _ w: AXUIElement) async throws ->
|
||||
throw ToolError("unknown step \(step.keys.sorted())")
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func waitFor(_ args: [String: Any], acted: Bool, _ present: @MainActor (String) async throws -> Bool) async throws -> String? {
|
||||
func waitFor(_ args: [String: Any], acted: Bool, _ present: (String) async throws -> Bool) async throws -> String? {
|
||||
let wait = try args.ms("wait_ms")
|
||||
let timeout = Double(try args.ms("timeout_ms") ?? 10000) / 1000
|
||||
if let wait {
|
||||
@@ -115,11 +114,11 @@ func waitFor(_ args: [String: Any], acted: Bool, _ present: @MainActor (String)
|
||||
}
|
||||
}
|
||||
|
||||
func runSteps(_ steps: [[String: Any]], _ id: Int, _ w: AXUIElement) async throws -> [String] {
|
||||
func runSteps(_ steps: [[String: Any]], _ id: Int, _ w: AXUIElement, _ table: RefTable) async throws -> [String] {
|
||||
var log: [String] = []
|
||||
for (i, step) in steps.enumerated() {
|
||||
do {
|
||||
log.append("\(i + 1). " + (try await runStep(step, id, w)))
|
||||
log.append("\(i + 1). " + (try await runStep(step, id, w, table)))
|
||||
await pause(50)
|
||||
} catch let e as ToolError {
|
||||
log.append("\(i + 1). " + e.message + "; stopped here")
|
||||
@@ -130,6 +129,7 @@ func runSteps(_ steps: [[String: Any]], _ id: Int, _ w: AXUIElement) async throw
|
||||
}
|
||||
|
||||
func actTool(_ args: [String: Any]) async throws -> [Content] {
|
||||
let table = currentRefs()
|
||||
let id = try args.require("window", args.int)
|
||||
let (_, w) = try axWindow(id)
|
||||
let steps = args["steps"] as? [[String: Any]] ?? []
|
||||
@@ -139,14 +139,14 @@ func actTool(_ args: [String: Any]) async throws -> [Content] {
|
||||
close(lock)
|
||||
}
|
||||
}
|
||||
var log = try await runSteps(steps, id, w)
|
||||
if let note = try await waitFor(args, acted: !steps.isEmpty, { windowText(w, contains: $0) }) {
|
||||
var log = try await runSteps(steps, id, w, table)
|
||||
if let note = try await waitFor(args, acted: !steps.isEmpty, { try windowText(w, contains: $0) }) {
|
||||
log.append(note)
|
||||
}
|
||||
var out: [Content] = []
|
||||
if args.bool("tree") {
|
||||
let current = try window(id)
|
||||
log.append("\n" + axTree(w, RefSource(pid: current.pid, window: id), treeOptions(args, origin: current.frame.origin)))
|
||||
log.append("\n" + (try axTree(w, RefSource(pid: current.pid, window: id), treeOptions(args, origin: current.frame.origin))))
|
||||
}
|
||||
if args.bool("capture") {
|
||||
let shot = try await capture(window: id, display: nil, crop: args.dict("crop"), maxEdge: args.double("max_edge") ?? 1568)
|
||||
|
||||
Reference in New Issue
Block a user