问题
I am writing a simple Cocoa app that will be launched from AppleScript just to post a Quartz Event to another app.
There is no need for a user interface so I deleted the window from the Interface Builder and the outlet to it from the Application Delegate. I call a private method postClickEvent() from the method applicationDidFinishLaunching(_:). I initialize leftMouseDown and leftMouseUp CGEvents with nil mouseEventSource at mouseCursorPosition (x: 0, y: 0) and post them at location cghidEventTap.
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
postClickEvent()
}
func applicationWillTerminate(_ aNotification: Notification) {
}
private func postClickEvent() {
let point = CGPoint(x: 0, y: 0)
let leftMouseDownEvent = CGEvent(mouseEventSource: nil, mouseType: .leftMouseDown, mouseCursorPosition: point, mouseButton: .left)
let leftMouseUpEvent = CGEvent(mouseEventSource: nil, mouseType: .leftMouseUp, mouseCursorPosition: point, mouseButton: .left)
leftMouseDownEvent?.post(tap: .cghidEventTap)
leftMouseUpEvent?.post(tap: .cghidEventTap)
}
}
I would expect the app as soon as it is launched to click in the top left corner and open the Apple menu but it does not happen.
I can think of several possible solutions.
- The app that runs in the debugger might need a permission to post a low-level user input event. Does an app need a permission to post a Quartz Event?
- The method applicationDidFinishLaunching(_:) is sent by the default notification center after the application has been launched and initialized but before it has received its first event. Maybe postClickEvent() should be called from another method of the Application Delegate?
- The events might need an Event Source at initialization. But passing the Event Source initialized with init(stateID:) for any of the three possible Event Source State IDs does not change it.
- The events can be posted just when the app receives focus after full screen Xcode so too early to click the Apple menu item. But sleeping the thread for 10 seconds does not change it.
回答1:
Using a sandbox by default is a new feature of Swift Package Manager in Xcode 9.0.
Updated package manifest interpretation and package builds on macOS to use a sandbox, which prevents network access and file system modification. This helps mitigate the effect of maliciously crafted manifests. (31107213)
Xcode Release Notes
But Quartz Events are incompatible with App Sandbox.
You cannot sandbox an app that controls another app. Posting keyboard or mouse events using functions like CGEventPost offers a way to circumvent this restriction, and is therefore not allowed from a sandboxed app.
App Sandbox Design Guide
Running the app leaves a Sandbox Violation log message in the Console that the app was denied Human Interface Device control.
Console
Disabling App Sandbox in the Xcode target editor allows to post a Quartz Event.
Xcode
来源:https://stackoverflow.com/questions/47086375/how-to-post-a-quartz-event-after-swift-application-launch