Getting mouse coordinates on Mojave
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
I have a really basic little command line app that grabs the mouse coordinates the next time the mouse is clicked.
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
CGFloat displayScale = 1.0f;
if ([[NSScreen mainScreen] respondsToSelector:@selector(backingScaleFactor)])
{
displayScale = [NSScreen mainScreen].backingScaleFactor;
}
CGPoint loc = CGEventGetLocation(event);
CFRelease(event);
printf("%dx%dn", (int)roundf(loc.x * displayScale), (int)roundf(loc.y * displayScale) );
exit(0);
return event;
}
int main(int argc, const char * argv) {
@autoreleasepool {
CFMachPortRef eventTap;
CGEventMask eventMask;
CFRunLoopSourceRef runLoopSource;
eventMask = 1 << kCGEventLeftMouseDown;
eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap,
1, eventMask, myCGEventCallback, @"mydata");
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
CFRunLoopRun();
}
return 0;
}
I'm building it with cmake with the following file:
cmake_minimum_required(VERSION 3.0.0)
project (location)
set(CMAKE_C_FLAGS "-arch x86_64 -mmacosx-version-min=10.12 -std=gnu11 -fobjc-arc -fmodules")
This all worked fine until the upgrade to Mojave.
A bit of poking around shows this is down to the latest set of security updates and some hints (except CGEventTapCreate() is not returning null) about settings some values in Info.plist to allow the app to use the accessibility API. But I'm struggling to work out where to put it as I just have a single .m file with the code.
Edit
- This needs to run as a none root user (company policy)
- if the only way to get it to ask for permission then it can be extended to be a "GUI" app with a minimal UI
This app is just to grab the upper left hand corner of a region of the screen to feed to a second app that streams that area of screen to a second device. The code for the streamer is common across Win/Linux/MacOS so trying to keep the screen coordinate collection totally separate
macos cocoa macos-mojave
|
show 1 more comment
I have a really basic little command line app that grabs the mouse coordinates the next time the mouse is clicked.
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
CGFloat displayScale = 1.0f;
if ([[NSScreen mainScreen] respondsToSelector:@selector(backingScaleFactor)])
{
displayScale = [NSScreen mainScreen].backingScaleFactor;
}
CGPoint loc = CGEventGetLocation(event);
CFRelease(event);
printf("%dx%dn", (int)roundf(loc.x * displayScale), (int)roundf(loc.y * displayScale) );
exit(0);
return event;
}
int main(int argc, const char * argv) {
@autoreleasepool {
CFMachPortRef eventTap;
CGEventMask eventMask;
CFRunLoopSourceRef runLoopSource;
eventMask = 1 << kCGEventLeftMouseDown;
eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap,
1, eventMask, myCGEventCallback, @"mydata");
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
CFRunLoopRun();
}
return 0;
}
I'm building it with cmake with the following file:
cmake_minimum_required(VERSION 3.0.0)
project (location)
set(CMAKE_C_FLAGS "-arch x86_64 -mmacosx-version-min=10.12 -std=gnu11 -fobjc-arc -fmodules")
This all worked fine until the upgrade to Mojave.
A bit of poking around shows this is down to the latest set of security updates and some hints (except CGEventTapCreate() is not returning null) about settings some values in Info.plist to allow the app to use the accessibility API. But I'm struggling to work out where to put it as I just have a single .m file with the code.
Edit
- This needs to run as a none root user (company policy)
- if the only way to get it to ask for permission then it can be extended to be a "GUI" app with a minimal UI
This app is just to grab the upper left hand corner of a region of the screen to feed to a second app that streams that area of screen to a second device. The code for the streamer is common across Win/Linux/MacOS so trying to keep the screen coordinate collection totally separate
macos cocoa macos-mojave
Are the command line tool and/or the app which runs the tool added to the privacy settings of system prefs?
– Willeke
Jan 4 at 12:07
I have tried manually adding it to the list and it still didn't work.
– hardillb
Jan 4 at 12:11
I tried your code (build in an Xcode project) and it worked after adding Terminal and the tool to the accessibility privacy settings.
– Willeke
Jan 4 at 13:28
Hmm, OK, but that's not a great solution. I want the app to deal with this it's self, even if it has to ask for permission on first run. Also not liking having to add the terminal to the list
– hardillb
Jan 4 at 13:33
I'm assuming this needs to work globally, and not just while your app has focus.
– Brad Allred
Jan 4 at 23:48
|
show 1 more comment
I have a really basic little command line app that grabs the mouse coordinates the next time the mouse is clicked.
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
CGFloat displayScale = 1.0f;
if ([[NSScreen mainScreen] respondsToSelector:@selector(backingScaleFactor)])
{
displayScale = [NSScreen mainScreen].backingScaleFactor;
}
CGPoint loc = CGEventGetLocation(event);
CFRelease(event);
printf("%dx%dn", (int)roundf(loc.x * displayScale), (int)roundf(loc.y * displayScale) );
exit(0);
return event;
}
int main(int argc, const char * argv) {
@autoreleasepool {
CFMachPortRef eventTap;
CGEventMask eventMask;
CFRunLoopSourceRef runLoopSource;
eventMask = 1 << kCGEventLeftMouseDown;
eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap,
1, eventMask, myCGEventCallback, @"mydata");
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
CFRunLoopRun();
}
return 0;
}
I'm building it with cmake with the following file:
cmake_minimum_required(VERSION 3.0.0)
project (location)
set(CMAKE_C_FLAGS "-arch x86_64 -mmacosx-version-min=10.12 -std=gnu11 -fobjc-arc -fmodules")
This all worked fine until the upgrade to Mojave.
A bit of poking around shows this is down to the latest set of security updates and some hints (except CGEventTapCreate() is not returning null) about settings some values in Info.plist to allow the app to use the accessibility API. But I'm struggling to work out where to put it as I just have a single .m file with the code.
Edit
- This needs to run as a none root user (company policy)
- if the only way to get it to ask for permission then it can be extended to be a "GUI" app with a minimal UI
This app is just to grab the upper left hand corner of a region of the screen to feed to a second app that streams that area of screen to a second device. The code for the streamer is common across Win/Linux/MacOS so trying to keep the screen coordinate collection totally separate
macos cocoa macos-mojave
I have a really basic little command line app that grabs the mouse coordinates the next time the mouse is clicked.
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
CGFloat displayScale = 1.0f;
if ([[NSScreen mainScreen] respondsToSelector:@selector(backingScaleFactor)])
{
displayScale = [NSScreen mainScreen].backingScaleFactor;
}
CGPoint loc = CGEventGetLocation(event);
CFRelease(event);
printf("%dx%dn", (int)roundf(loc.x * displayScale), (int)roundf(loc.y * displayScale) );
exit(0);
return event;
}
int main(int argc, const char * argv) {
@autoreleasepool {
CFMachPortRef eventTap;
CGEventMask eventMask;
CFRunLoopSourceRef runLoopSource;
eventMask = 1 << kCGEventLeftMouseDown;
eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap,
1, eventMask, myCGEventCallback, @"mydata");
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
CFRunLoopRun();
}
return 0;
}
I'm building it with cmake with the following file:
cmake_minimum_required(VERSION 3.0.0)
project (location)
set(CMAKE_C_FLAGS "-arch x86_64 -mmacosx-version-min=10.12 -std=gnu11 -fobjc-arc -fmodules")
This all worked fine until the upgrade to Mojave.
A bit of poking around shows this is down to the latest set of security updates and some hints (except CGEventTapCreate() is not returning null) about settings some values in Info.plist to allow the app to use the accessibility API. But I'm struggling to work out where to put it as I just have a single .m file with the code.
Edit
- This needs to run as a none root user (company policy)
- if the only way to get it to ask for permission then it can be extended to be a "GUI" app with a minimal UI
This app is just to grab the upper left hand corner of a region of the screen to feed to a second app that streams that area of screen to a second device. The code for the streamer is common across Win/Linux/MacOS so trying to keep the screen coordinate collection totally separate
macos cocoa macos-mojave
macos cocoa macos-mojave
edited Jan 5 at 20:53
hardillb
asked Jan 4 at 10:26
hardillbhardillb
25.5k73463
25.5k73463
Are the command line tool and/or the app which runs the tool added to the privacy settings of system prefs?
– Willeke
Jan 4 at 12:07
I have tried manually adding it to the list and it still didn't work.
– hardillb
Jan 4 at 12:11
I tried your code (build in an Xcode project) and it worked after adding Terminal and the tool to the accessibility privacy settings.
– Willeke
Jan 4 at 13:28
Hmm, OK, but that's not a great solution. I want the app to deal with this it's self, even if it has to ask for permission on first run. Also not liking having to add the terminal to the list
– hardillb
Jan 4 at 13:33
I'm assuming this needs to work globally, and not just while your app has focus.
– Brad Allred
Jan 4 at 23:48
|
show 1 more comment
Are the command line tool and/or the app which runs the tool added to the privacy settings of system prefs?
– Willeke
Jan 4 at 12:07
I have tried manually adding it to the list and it still didn't work.
– hardillb
Jan 4 at 12:11
I tried your code (build in an Xcode project) and it worked after adding Terminal and the tool to the accessibility privacy settings.
– Willeke
Jan 4 at 13:28
Hmm, OK, but that's not a great solution. I want the app to deal with this it's self, even if it has to ask for permission on first run. Also not liking having to add the terminal to the list
– hardillb
Jan 4 at 13:33
I'm assuming this needs to work globally, and not just while your app has focus.
– Brad Allred
Jan 4 at 23:48
Are the command line tool and/or the app which runs the tool added to the privacy settings of system prefs?
– Willeke
Jan 4 at 12:07
Are the command line tool and/or the app which runs the tool added to the privacy settings of system prefs?
– Willeke
Jan 4 at 12:07
I have tried manually adding it to the list and it still didn't work.
– hardillb
Jan 4 at 12:11
I have tried manually adding it to the list and it still didn't work.
– hardillb
Jan 4 at 12:11
I tried your code (build in an Xcode project) and it worked after adding Terminal and the tool to the accessibility privacy settings.
– Willeke
Jan 4 at 13:28
I tried your code (build in an Xcode project) and it worked after adding Terminal and the tool to the accessibility privacy settings.
– Willeke
Jan 4 at 13:28
Hmm, OK, but that's not a great solution. I want the app to deal with this it's self, even if it has to ask for permission on first run. Also not liking having to add the terminal to the list
– hardillb
Jan 4 at 13:33
Hmm, OK, but that's not a great solution. I want the app to deal with this it's self, even if it has to ask for permission on first run. Also not liking having to add the terminal to the list
– hardillb
Jan 4 at 13:33
I'm assuming this needs to work globally, and not just while your app has focus.
– Brad Allred
Jan 4 at 23:48
I'm assuming this needs to work globally, and not just while your app has focus.
– Brad Allred
Jan 4 at 23:48
|
show 1 more comment
2 Answers
2
active
oldest
votes
As you surmise, event taps won't work on Mojave without having accessibility access. From the documentation:
Event taps receive key up and key down events if one of the following
conditions is true: The current process is running as the root user.
Access for assistive devices is enabled. In OS X v10.4, you can enable
this feature using System Preferences, Universal Access panel,
Keyboard view.
A GUI app will prompt the user to enable accessibility the first time it's needed, but it looks like a CLI app doesn't do that (which makes sense).
There is no way to enable this programatically or through a script; the user must do it themselves.
Running your tool as root should work - can you enforce that?
Otherwise, you can direct the user to the correct place in System Preferences:
tell application "System Preferences"
reveal anchor "Privacy_Accessibility" of pane id "com.apple.preference.security"
activate
end tell
It may be possible using Carbon, if your app isn't sandboxed.
Finally, a quick test shows this is at least possible using IOHID. I shameless borrowed the KeyboardWatcher class from this answer. Then, modified the device type:
[self watchDevicesOfType:kHIDUsage_GD_Keyboard];
into:
[self watchDevicesOfType:kHIDUsage_GD_Mouse];
Finally, my callback looks like this:
static void Handle_DeviceEventCallback (void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef value)
{
IOHIDElementRef element = IOHIDValueGetElement(value);
IOHIDElementType elemType = IOHIDElementGetType(element);
if (elemType == kIOHIDElementTypeInput_Button)
{
int elementValue = (int) IOHIDValueGetIntegerValue(value);
// 1 == down 0 == up
if (elementValue == 1)
{
CGEventRef ourEvent = CGEventCreate(NULL);
CGPoint point = CGEventGetLocation(ourEvent);
printf("Mouse Position: %.2f, y = %.2f n", (float) point.x, (float) point.y);
}
}
}
That is really a quick hack job, but it demonstrates this is possible and hopefully you can refine it to your needs.
Additional comment to add: if you did get this working withNSEventand the "allow accessibility access" popup; I for one would find it really strange for a CLI tool to request control of my machine in such a way (unless it runs as root, in which case it doesn't need to ask!)
– TheNextman
Jan 5 at 5:02
See the edit, can run as root.
– hardillb
Jan 5 at 10:12
You mean "cannot" run as root, right? From your edit: convert to a Cocoa.app (NSApplication) and the user should get prompted. Note that accepting the prompt does not grant accessibility access - it simply directs the user to the proper location in System Preferences. The user has to manually add the app to the list. Once you have anNSApplication, you can instead try theaddGlobalMonitorForEventsMatchingMaskthat @BradAllred proposed, otherwise consider trying the IOKit approach above.
– TheNextman
Jan 5 at 20:30
Yeah, I meant can not, but if it just redirects to the place in the settings then I'm hosed as most of the users don't have admin access to their own machines.
– hardillb
Jan 5 at 20:52
add a comment |
I've found the CGEventTap documentation is out of date beginning with Mojave. Running as root used to act as a bypass for certain entitlements, but in Mojave this was tightened down. One bizarre side effect, as you noticed, is that root can still acquire the mach port for the tap; its just that no events can be read from it. If you try your application without running as root you should get the expected popup asking for permission.
If you do not get the popup, or need to run as root for other purposes, you can manually add your application to the trusted TCC database via SystemPreferences -> Security & Privacy -> Privacy -> Accessibility
settings some values in Info.plist to allow the app to use the accessibility API
I believe you mean adding entitlements (which are also a plist). The entitlement that allows an application to use the Accessibility API is the com.apple.private.tcc.allow entitlement (with a value of kTCCServiceAccessibility). As you can probably guess from the name it is only allowed on Apple signed binaries.
You can add these entitlements to your own app if you disable System Integrity Protection (SIP) and boot the kernel with the option amfi_get_out_of_my_way=1, but I wouldn't recommend it (and certainly any customers of yours wouldn't want to). With just SIP disabled you could manually add an entry to the TCC database to grant privileges, but still wouldn't recommend it.
Possible Alternative
You can use an event monitor:
NSEventMask mask = (NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask);
mouseEventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask: mask
handler:^(NSEvent *event){
// get the current coordinates with this
NSPoint coords = [NSEvent mouseLocation];
// event cooordinates would be event.absoluteX and event.absoluteY
... do stuff
}];
The documentation does mention:
Key-related events may only be monitored if accessibility is enabled or if your application is trusted for accessibility access (see AXIsProcessTrusted).
But I don't think that applies to mouse events.
I think you need a runningNSApplicationforaddGlobalMonitorForEventsMatchingMask...to work. He could package his code up into a Cocoa .app and it should work. Note that this is probably the same reason he's not getting the popup (and yes, Apple really needs to update the docs for this stuff....)
– TheNextman
Jan 5 at 4:59
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54037083%2fgetting-mouse-coordinates-on-mojave%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
As you surmise, event taps won't work on Mojave without having accessibility access. From the documentation:
Event taps receive key up and key down events if one of the following
conditions is true: The current process is running as the root user.
Access for assistive devices is enabled. In OS X v10.4, you can enable
this feature using System Preferences, Universal Access panel,
Keyboard view.
A GUI app will prompt the user to enable accessibility the first time it's needed, but it looks like a CLI app doesn't do that (which makes sense).
There is no way to enable this programatically or through a script; the user must do it themselves.
Running your tool as root should work - can you enforce that?
Otherwise, you can direct the user to the correct place in System Preferences:
tell application "System Preferences"
reveal anchor "Privacy_Accessibility" of pane id "com.apple.preference.security"
activate
end tell
It may be possible using Carbon, if your app isn't sandboxed.
Finally, a quick test shows this is at least possible using IOHID. I shameless borrowed the KeyboardWatcher class from this answer. Then, modified the device type:
[self watchDevicesOfType:kHIDUsage_GD_Keyboard];
into:
[self watchDevicesOfType:kHIDUsage_GD_Mouse];
Finally, my callback looks like this:
static void Handle_DeviceEventCallback (void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef value)
{
IOHIDElementRef element = IOHIDValueGetElement(value);
IOHIDElementType elemType = IOHIDElementGetType(element);
if (elemType == kIOHIDElementTypeInput_Button)
{
int elementValue = (int) IOHIDValueGetIntegerValue(value);
// 1 == down 0 == up
if (elementValue == 1)
{
CGEventRef ourEvent = CGEventCreate(NULL);
CGPoint point = CGEventGetLocation(ourEvent);
printf("Mouse Position: %.2f, y = %.2f n", (float) point.x, (float) point.y);
}
}
}
That is really a quick hack job, but it demonstrates this is possible and hopefully you can refine it to your needs.
Additional comment to add: if you did get this working withNSEventand the "allow accessibility access" popup; I for one would find it really strange for a CLI tool to request control of my machine in such a way (unless it runs as root, in which case it doesn't need to ask!)
– TheNextman
Jan 5 at 5:02
See the edit, can run as root.
– hardillb
Jan 5 at 10:12
You mean "cannot" run as root, right? From your edit: convert to a Cocoa.app (NSApplication) and the user should get prompted. Note that accepting the prompt does not grant accessibility access - it simply directs the user to the proper location in System Preferences. The user has to manually add the app to the list. Once you have anNSApplication, you can instead try theaddGlobalMonitorForEventsMatchingMaskthat @BradAllred proposed, otherwise consider trying the IOKit approach above.
– TheNextman
Jan 5 at 20:30
Yeah, I meant can not, but if it just redirects to the place in the settings then I'm hosed as most of the users don't have admin access to their own machines.
– hardillb
Jan 5 at 20:52
add a comment |
As you surmise, event taps won't work on Mojave without having accessibility access. From the documentation:
Event taps receive key up and key down events if one of the following
conditions is true: The current process is running as the root user.
Access for assistive devices is enabled. In OS X v10.4, you can enable
this feature using System Preferences, Universal Access panel,
Keyboard view.
A GUI app will prompt the user to enable accessibility the first time it's needed, but it looks like a CLI app doesn't do that (which makes sense).
There is no way to enable this programatically or through a script; the user must do it themselves.
Running your tool as root should work - can you enforce that?
Otherwise, you can direct the user to the correct place in System Preferences:
tell application "System Preferences"
reveal anchor "Privacy_Accessibility" of pane id "com.apple.preference.security"
activate
end tell
It may be possible using Carbon, if your app isn't sandboxed.
Finally, a quick test shows this is at least possible using IOHID. I shameless borrowed the KeyboardWatcher class from this answer. Then, modified the device type:
[self watchDevicesOfType:kHIDUsage_GD_Keyboard];
into:
[self watchDevicesOfType:kHIDUsage_GD_Mouse];
Finally, my callback looks like this:
static void Handle_DeviceEventCallback (void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef value)
{
IOHIDElementRef element = IOHIDValueGetElement(value);
IOHIDElementType elemType = IOHIDElementGetType(element);
if (elemType == kIOHIDElementTypeInput_Button)
{
int elementValue = (int) IOHIDValueGetIntegerValue(value);
// 1 == down 0 == up
if (elementValue == 1)
{
CGEventRef ourEvent = CGEventCreate(NULL);
CGPoint point = CGEventGetLocation(ourEvent);
printf("Mouse Position: %.2f, y = %.2f n", (float) point.x, (float) point.y);
}
}
}
That is really a quick hack job, but it demonstrates this is possible and hopefully you can refine it to your needs.
Additional comment to add: if you did get this working withNSEventand the "allow accessibility access" popup; I for one would find it really strange for a CLI tool to request control of my machine in such a way (unless it runs as root, in which case it doesn't need to ask!)
– TheNextman
Jan 5 at 5:02
See the edit, can run as root.
– hardillb
Jan 5 at 10:12
You mean "cannot" run as root, right? From your edit: convert to a Cocoa.app (NSApplication) and the user should get prompted. Note that accepting the prompt does not grant accessibility access - it simply directs the user to the proper location in System Preferences. The user has to manually add the app to the list. Once you have anNSApplication, you can instead try theaddGlobalMonitorForEventsMatchingMaskthat @BradAllred proposed, otherwise consider trying the IOKit approach above.
– TheNextman
Jan 5 at 20:30
Yeah, I meant can not, but if it just redirects to the place in the settings then I'm hosed as most of the users don't have admin access to their own machines.
– hardillb
Jan 5 at 20:52
add a comment |
As you surmise, event taps won't work on Mojave without having accessibility access. From the documentation:
Event taps receive key up and key down events if one of the following
conditions is true: The current process is running as the root user.
Access for assistive devices is enabled. In OS X v10.4, you can enable
this feature using System Preferences, Universal Access panel,
Keyboard view.
A GUI app will prompt the user to enable accessibility the first time it's needed, but it looks like a CLI app doesn't do that (which makes sense).
There is no way to enable this programatically or through a script; the user must do it themselves.
Running your tool as root should work - can you enforce that?
Otherwise, you can direct the user to the correct place in System Preferences:
tell application "System Preferences"
reveal anchor "Privacy_Accessibility" of pane id "com.apple.preference.security"
activate
end tell
It may be possible using Carbon, if your app isn't sandboxed.
Finally, a quick test shows this is at least possible using IOHID. I shameless borrowed the KeyboardWatcher class from this answer. Then, modified the device type:
[self watchDevicesOfType:kHIDUsage_GD_Keyboard];
into:
[self watchDevicesOfType:kHIDUsage_GD_Mouse];
Finally, my callback looks like this:
static void Handle_DeviceEventCallback (void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef value)
{
IOHIDElementRef element = IOHIDValueGetElement(value);
IOHIDElementType elemType = IOHIDElementGetType(element);
if (elemType == kIOHIDElementTypeInput_Button)
{
int elementValue = (int) IOHIDValueGetIntegerValue(value);
// 1 == down 0 == up
if (elementValue == 1)
{
CGEventRef ourEvent = CGEventCreate(NULL);
CGPoint point = CGEventGetLocation(ourEvent);
printf("Mouse Position: %.2f, y = %.2f n", (float) point.x, (float) point.y);
}
}
}
That is really a quick hack job, but it demonstrates this is possible and hopefully you can refine it to your needs.
As you surmise, event taps won't work on Mojave without having accessibility access. From the documentation:
Event taps receive key up and key down events if one of the following
conditions is true: The current process is running as the root user.
Access for assistive devices is enabled. In OS X v10.4, you can enable
this feature using System Preferences, Universal Access panel,
Keyboard view.
A GUI app will prompt the user to enable accessibility the first time it's needed, but it looks like a CLI app doesn't do that (which makes sense).
There is no way to enable this programatically or through a script; the user must do it themselves.
Running your tool as root should work - can you enforce that?
Otherwise, you can direct the user to the correct place in System Preferences:
tell application "System Preferences"
reveal anchor "Privacy_Accessibility" of pane id "com.apple.preference.security"
activate
end tell
It may be possible using Carbon, if your app isn't sandboxed.
Finally, a quick test shows this is at least possible using IOHID. I shameless borrowed the KeyboardWatcher class from this answer. Then, modified the device type:
[self watchDevicesOfType:kHIDUsage_GD_Keyboard];
into:
[self watchDevicesOfType:kHIDUsage_GD_Mouse];
Finally, my callback looks like this:
static void Handle_DeviceEventCallback (void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef value)
{
IOHIDElementRef element = IOHIDValueGetElement(value);
IOHIDElementType elemType = IOHIDElementGetType(element);
if (elemType == kIOHIDElementTypeInput_Button)
{
int elementValue = (int) IOHIDValueGetIntegerValue(value);
// 1 == down 0 == up
if (elementValue == 1)
{
CGEventRef ourEvent = CGEventCreate(NULL);
CGPoint point = CGEventGetLocation(ourEvent);
printf("Mouse Position: %.2f, y = %.2f n", (float) point.x, (float) point.y);
}
}
}
That is really a quick hack job, but it demonstrates this is possible and hopefully you can refine it to your needs.
answered Jan 4 at 22:13
TheNextmanTheNextman
8,12222561
8,12222561
Additional comment to add: if you did get this working withNSEventand the "allow accessibility access" popup; I for one would find it really strange for a CLI tool to request control of my machine in such a way (unless it runs as root, in which case it doesn't need to ask!)
– TheNextman
Jan 5 at 5:02
See the edit, can run as root.
– hardillb
Jan 5 at 10:12
You mean "cannot" run as root, right? From your edit: convert to a Cocoa.app (NSApplication) and the user should get prompted. Note that accepting the prompt does not grant accessibility access - it simply directs the user to the proper location in System Preferences. The user has to manually add the app to the list. Once you have anNSApplication, you can instead try theaddGlobalMonitorForEventsMatchingMaskthat @BradAllred proposed, otherwise consider trying the IOKit approach above.
– TheNextman
Jan 5 at 20:30
Yeah, I meant can not, but if it just redirects to the place in the settings then I'm hosed as most of the users don't have admin access to their own machines.
– hardillb
Jan 5 at 20:52
add a comment |
Additional comment to add: if you did get this working withNSEventand the "allow accessibility access" popup; I for one would find it really strange for a CLI tool to request control of my machine in such a way (unless it runs as root, in which case it doesn't need to ask!)
– TheNextman
Jan 5 at 5:02
See the edit, can run as root.
– hardillb
Jan 5 at 10:12
You mean "cannot" run as root, right? From your edit: convert to a Cocoa.app (NSApplication) and the user should get prompted. Note that accepting the prompt does not grant accessibility access - it simply directs the user to the proper location in System Preferences. The user has to manually add the app to the list. Once you have anNSApplication, you can instead try theaddGlobalMonitorForEventsMatchingMaskthat @BradAllred proposed, otherwise consider trying the IOKit approach above.
– TheNextman
Jan 5 at 20:30
Yeah, I meant can not, but if it just redirects to the place in the settings then I'm hosed as most of the users don't have admin access to their own machines.
– hardillb
Jan 5 at 20:52
Additional comment to add: if you did get this working with
NSEvent and the "allow accessibility access" popup; I for one would find it really strange for a CLI tool to request control of my machine in such a way (unless it runs as root, in which case it doesn't need to ask!)– TheNextman
Jan 5 at 5:02
Additional comment to add: if you did get this working with
NSEvent and the "allow accessibility access" popup; I for one would find it really strange for a CLI tool to request control of my machine in such a way (unless it runs as root, in which case it doesn't need to ask!)– TheNextman
Jan 5 at 5:02
See the edit, can run as root.
– hardillb
Jan 5 at 10:12
See the edit, can run as root.
– hardillb
Jan 5 at 10:12
You mean "cannot" run as root, right? From your edit: convert to a Cocoa.app (
NSApplication) and the user should get prompted. Note that accepting the prompt does not grant accessibility access - it simply directs the user to the proper location in System Preferences. The user has to manually add the app to the list. Once you have an NSApplication, you can instead try the addGlobalMonitorForEventsMatchingMask that @BradAllred proposed, otherwise consider trying the IOKit approach above.– TheNextman
Jan 5 at 20:30
You mean "cannot" run as root, right? From your edit: convert to a Cocoa.app (
NSApplication) and the user should get prompted. Note that accepting the prompt does not grant accessibility access - it simply directs the user to the proper location in System Preferences. The user has to manually add the app to the list. Once you have an NSApplication, you can instead try the addGlobalMonitorForEventsMatchingMask that @BradAllred proposed, otherwise consider trying the IOKit approach above.– TheNextman
Jan 5 at 20:30
Yeah, I meant can not, but if it just redirects to the place in the settings then I'm hosed as most of the users don't have admin access to their own machines.
– hardillb
Jan 5 at 20:52
Yeah, I meant can not, but if it just redirects to the place in the settings then I'm hosed as most of the users don't have admin access to their own machines.
– hardillb
Jan 5 at 20:52
add a comment |
I've found the CGEventTap documentation is out of date beginning with Mojave. Running as root used to act as a bypass for certain entitlements, but in Mojave this was tightened down. One bizarre side effect, as you noticed, is that root can still acquire the mach port for the tap; its just that no events can be read from it. If you try your application without running as root you should get the expected popup asking for permission.
If you do not get the popup, or need to run as root for other purposes, you can manually add your application to the trusted TCC database via SystemPreferences -> Security & Privacy -> Privacy -> Accessibility
settings some values in Info.plist to allow the app to use the accessibility API
I believe you mean adding entitlements (which are also a plist). The entitlement that allows an application to use the Accessibility API is the com.apple.private.tcc.allow entitlement (with a value of kTCCServiceAccessibility). As you can probably guess from the name it is only allowed on Apple signed binaries.
You can add these entitlements to your own app if you disable System Integrity Protection (SIP) and boot the kernel with the option amfi_get_out_of_my_way=1, but I wouldn't recommend it (and certainly any customers of yours wouldn't want to). With just SIP disabled you could manually add an entry to the TCC database to grant privileges, but still wouldn't recommend it.
Possible Alternative
You can use an event monitor:
NSEventMask mask = (NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask);
mouseEventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask: mask
handler:^(NSEvent *event){
// get the current coordinates with this
NSPoint coords = [NSEvent mouseLocation];
// event cooordinates would be event.absoluteX and event.absoluteY
... do stuff
}];
The documentation does mention:
Key-related events may only be monitored if accessibility is enabled or if your application is trusted for accessibility access (see AXIsProcessTrusted).
But I don't think that applies to mouse events.
I think you need a runningNSApplicationforaddGlobalMonitorForEventsMatchingMask...to work. He could package his code up into a Cocoa .app and it should work. Note that this is probably the same reason he's not getting the popup (and yes, Apple really needs to update the docs for this stuff....)
– TheNextman
Jan 5 at 4:59
add a comment |
I've found the CGEventTap documentation is out of date beginning with Mojave. Running as root used to act as a bypass for certain entitlements, but in Mojave this was tightened down. One bizarre side effect, as you noticed, is that root can still acquire the mach port for the tap; its just that no events can be read from it. If you try your application without running as root you should get the expected popup asking for permission.
If you do not get the popup, or need to run as root for other purposes, you can manually add your application to the trusted TCC database via SystemPreferences -> Security & Privacy -> Privacy -> Accessibility
settings some values in Info.plist to allow the app to use the accessibility API
I believe you mean adding entitlements (which are also a plist). The entitlement that allows an application to use the Accessibility API is the com.apple.private.tcc.allow entitlement (with a value of kTCCServiceAccessibility). As you can probably guess from the name it is only allowed on Apple signed binaries.
You can add these entitlements to your own app if you disable System Integrity Protection (SIP) and boot the kernel with the option amfi_get_out_of_my_way=1, but I wouldn't recommend it (and certainly any customers of yours wouldn't want to). With just SIP disabled you could manually add an entry to the TCC database to grant privileges, but still wouldn't recommend it.
Possible Alternative
You can use an event monitor:
NSEventMask mask = (NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask);
mouseEventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask: mask
handler:^(NSEvent *event){
// get the current coordinates with this
NSPoint coords = [NSEvent mouseLocation];
// event cooordinates would be event.absoluteX and event.absoluteY
... do stuff
}];
The documentation does mention:
Key-related events may only be monitored if accessibility is enabled or if your application is trusted for accessibility access (see AXIsProcessTrusted).
But I don't think that applies to mouse events.
I think you need a runningNSApplicationforaddGlobalMonitorForEventsMatchingMask...to work. He could package his code up into a Cocoa .app and it should work. Note that this is probably the same reason he's not getting the popup (and yes, Apple really needs to update the docs for this stuff....)
– TheNextman
Jan 5 at 4:59
add a comment |
I've found the CGEventTap documentation is out of date beginning with Mojave. Running as root used to act as a bypass for certain entitlements, but in Mojave this was tightened down. One bizarre side effect, as you noticed, is that root can still acquire the mach port for the tap; its just that no events can be read from it. If you try your application without running as root you should get the expected popup asking for permission.
If you do not get the popup, or need to run as root for other purposes, you can manually add your application to the trusted TCC database via SystemPreferences -> Security & Privacy -> Privacy -> Accessibility
settings some values in Info.plist to allow the app to use the accessibility API
I believe you mean adding entitlements (which are also a plist). The entitlement that allows an application to use the Accessibility API is the com.apple.private.tcc.allow entitlement (with a value of kTCCServiceAccessibility). As you can probably guess from the name it is only allowed on Apple signed binaries.
You can add these entitlements to your own app if you disable System Integrity Protection (SIP) and boot the kernel with the option amfi_get_out_of_my_way=1, but I wouldn't recommend it (and certainly any customers of yours wouldn't want to). With just SIP disabled you could manually add an entry to the TCC database to grant privileges, but still wouldn't recommend it.
Possible Alternative
You can use an event monitor:
NSEventMask mask = (NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask);
mouseEventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask: mask
handler:^(NSEvent *event){
// get the current coordinates with this
NSPoint coords = [NSEvent mouseLocation];
// event cooordinates would be event.absoluteX and event.absoluteY
... do stuff
}];
The documentation does mention:
Key-related events may only be monitored if accessibility is enabled or if your application is trusted for accessibility access (see AXIsProcessTrusted).
But I don't think that applies to mouse events.
I've found the CGEventTap documentation is out of date beginning with Mojave. Running as root used to act as a bypass for certain entitlements, but in Mojave this was tightened down. One bizarre side effect, as you noticed, is that root can still acquire the mach port for the tap; its just that no events can be read from it. If you try your application without running as root you should get the expected popup asking for permission.
If you do not get the popup, or need to run as root for other purposes, you can manually add your application to the trusted TCC database via SystemPreferences -> Security & Privacy -> Privacy -> Accessibility
settings some values in Info.plist to allow the app to use the accessibility API
I believe you mean adding entitlements (which are also a plist). The entitlement that allows an application to use the Accessibility API is the com.apple.private.tcc.allow entitlement (with a value of kTCCServiceAccessibility). As you can probably guess from the name it is only allowed on Apple signed binaries.
You can add these entitlements to your own app if you disable System Integrity Protection (SIP) and boot the kernel with the option amfi_get_out_of_my_way=1, but I wouldn't recommend it (and certainly any customers of yours wouldn't want to). With just SIP disabled you could manually add an entry to the TCC database to grant privileges, but still wouldn't recommend it.
Possible Alternative
You can use an event monitor:
NSEventMask mask = (NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask);
mouseEventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask: mask
handler:^(NSEvent *event){
// get the current coordinates with this
NSPoint coords = [NSEvent mouseLocation];
// event cooordinates would be event.absoluteX and event.absoluteY
... do stuff
}];
The documentation does mention:
Key-related events may only be monitored if accessibility is enabled or if your application is trusted for accessibility access (see AXIsProcessTrusted).
But I don't think that applies to mouse events.
edited Jan 4 at 23:40
answered Jan 4 at 22:35
Brad AllredBrad Allred
5,88111940
5,88111940
I think you need a runningNSApplicationforaddGlobalMonitorForEventsMatchingMask...to work. He could package his code up into a Cocoa .app and it should work. Note that this is probably the same reason he's not getting the popup (and yes, Apple really needs to update the docs for this stuff....)
– TheNextman
Jan 5 at 4:59
add a comment |
I think you need a runningNSApplicationforaddGlobalMonitorForEventsMatchingMask...to work. He could package his code up into a Cocoa .app and it should work. Note that this is probably the same reason he's not getting the popup (and yes, Apple really needs to update the docs for this stuff....)
– TheNextman
Jan 5 at 4:59
I think you need a running
NSApplication for addGlobalMonitorForEventsMatchingMask... to work. He could package his code up into a Cocoa .app and it should work. Note that this is probably the same reason he's not getting the popup (and yes, Apple really needs to update the docs for this stuff....)– TheNextman
Jan 5 at 4:59
I think you need a running
NSApplication for addGlobalMonitorForEventsMatchingMask... to work. He could package his code up into a Cocoa .app and it should work. Note that this is probably the same reason he's not getting the popup (and yes, Apple really needs to update the docs for this stuff....)– TheNextman
Jan 5 at 4:59
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54037083%2fgetting-mouse-coordinates-on-mojave%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Are the command line tool and/or the app which runs the tool added to the privacy settings of system prefs?
– Willeke
Jan 4 at 12:07
I have tried manually adding it to the list and it still didn't work.
– hardillb
Jan 4 at 12:11
I tried your code (build in an Xcode project) and it worked after adding Terminal and the tool to the accessibility privacy settings.
– Willeke
Jan 4 at 13:28
Hmm, OK, but that's not a great solution. I want the app to deal with this it's self, even if it has to ask for permission on first run. Also not liking having to add the terminal to the list
– hardillb
Jan 4 at 13:33
I'm assuming this needs to work globally, and not just while your app has focus.
– Brad Allred
Jan 4 at 23:48