问题
I have the following code which moves the mouse cursor on a Mac:
void moveCursorPos()
{
CGPoint ppos;
ppos.x = 100;
ppos.y = 100;
CGEventRef e = CGEventCreateMouseEvent(nullptr, kCGEventMouseMoved, ppos, kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, e);
CFRelease(e);
}
It works when I run my software directly from its binary - e.g. ./foo.app/Contents/MacOS/foo
.
It does not not work when I start it through open foo.app
.
Doing open ./foo.app/Contents/MacOS/foo
works.
The first time I launched it and called that function, macOS asked me for the permission to use accessibility APIs (or something in that vein), which I granted - if I go to the "Accessibility > Privacy" pane of the security settings of macOS, all checkboxes are checked, everything is granted to foo
, etc...
- How do I fix it on my machine ?
- What can I do in terms of code so that the users of my software do not ever encounter that issue as it breaks a core UI interaction of my software ?
回答1:
How do I fix it on my machine?
It really depends on what you did. I usually broke these things when I run an app from Xcode directly, distribution, ... and all of them have same bundle identifier (one entry in System Preferences, but more binaries around).
To reset Accessibility for your app just run:
sudo tccutil reset Accessibility com.your.bundle.Identifier
It will reset Accessibility settings for all occurrences of your binaries and you can start again.
What can I do in terms of code so that the users of my software do not ever encounter that issue as it breaks a core UI interaction of my software?
Do the following in your AppDelegate.applicationDidFinishLaunching
:
acquireAccessibilityPrivileges(acquired: {
self.moveCursorPos()
}, nope: {
NSApp.terminate(self)
})
Other functions:
func acquireAccessibilityPrivileges(acquired: @escaping () -> Void, nope: @escaping () -> Void) {
let options = [kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true]
let enabled = AXIsProcessTrustedWithOptions(options as CFDictionary)
if enabled {
acquired()
} else {
let alert = NSAlert()
alert.messageText = "Enable XYZ"
alert.informativeText = "Click OK once you enabled XYZ in System Preferences - ..."
alert.beginSheetModal(for: self.window, completionHandler: { response in
if AXIsProcessTrustedWithOptions(options as CFDictionary) {
acquired()
} else {
nope()
}
})
}
}
func moveCursorPos() {
let event = CGEvent(mouseEventSource: nil, mouseType: .mouseMoved, mouseCursorPosition: CGPoint(x: 100, y: 100), mouseButton: .left)
event?.post(tap: .cghidEventTap)
}
open foo.app
vs ./foo
There's also one thing to be aware of - the difference between open foo.app
, ./foo.app/Contents/MacOS/foo
, ...
open Foo.app
:
Foo.app
needs this accessibility permissions
-+= 00001 root /sbin/launchd
|--= 83677 zrzka /Users/zrzka/Desktop/Foo.app/Contents/MacOS/Foo
Foo.app/Contents/MacOS/Foo
:
Iterm.app
needs this accessibility permissions
-+= 00001 root /sbin/launchd
|-+= 53984 zrzka /Applications/iTerm.app/Contents/MacOS/iTerm2
| |-+= 53986 zrzka /Applications/iTerm.app/Contents/MacOS/iTerm2 --server /usr/bin/login -fpl zrzka /Applications/iTerm.app/Contents/MacOS/iTerm2 --launch_shell
| | \-+= 53987 root /usr/bin/login -fpl zrzka /Applications/iTerm.app/Contents/MacOS/iTerm2 --launch_shell
| | \-+= 53988 zrzka -zsh
| | \--= 84461 zrzka Foo.app/Contents/MacOS/Foo
open Foo.app/Contents/MacOS/Foo
:
Terminal.app
is launched andFoo
in it- That's because I do use iTerm, but the default one is Terminal
Terminal.app
needs this accessibility permissions
-+= 00001 root /sbin/launchd
|-+= 73320 zrzka /System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal
| \-+= 75674 root login -pf zrzka
| \-+= 75679 zrzka -zsh
| \--= 75717 zrzka /Users/zrzka/Desktop/Foo.app/Contents/MacOS/Foo
More tips
It happens, from time to time, that my app no longer appears in the System Preferences - Security & Privacy - Accessibility. I don't know why, but it's somehow connected with my tccutil
resets. Open Finder and drag & drop the Foo.app
into the pane.
来源:https://stackoverflow.com/questions/61843481/macos-simulated-mouse-event-only-works-when-launching-binary-not-application-b