Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Investigate a possibility to use CGEvent.tapCreate for global hotkeys #1012

Open
nikitabobko opened this issue Jan 18, 2025 · 1 comment
Open

Comments

@nikitabobko
Copy link
Owner

Right now, we use Carbon API. It's a deprecated framework, but since a looot of apps use, I don't think that Apple is gonna drop it in foreseeable future

The alternative API I recently found is
https://developer.apple.com/documentation/coregraphics/cgevent/1454426-tapcreate
https://forums.developer.apple.com/forums/thread/735223

Copying the response from apple forum in case it gets deleted:

I was recently asked a very similar question in a DTS incident. Pasted in below are the relevant snippets from my response.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

There are two parts to creating an app like this:

  - Ensuring your app runs in the background (A)

  - Monitoring key presses (B)

I’ll cover each in turn.

With regards point A, running in the background, the approach I recommend is to split the core of your app off into a separate app that you then nest within your main app. Set the [LSUIElement property](https://developer.apple.com/documentation/bundleresources/information_property_list/lsuielement) on the nested app, so it doesn’t show up in the Dock or have its own menu bar.

In that nested app, use a [menu bar status item](https://developer.apple.com/documentation/appkit/menus_cursors_and_the_dock#2871629) to present your UI.

Note If you’re using SwiftUI, there’s now a [SwiftUI equivalent](https://developer.apple.com/documentation/swiftui/scenes#creating-a-menu-bar-extra).

In your main app, have a UI to control whether the nested app runs as a login item, that is, runs while the user is logged in. Use the [SMAppService.loginItem(identifier:) method](https://developer.apple.com/documentation/servicemanagement/smappservice/3945411-loginitem) to configure this.

If you need to support older system, use the [SMLoginItemSetEnabled function](https://developer.apple.com/documentation/servicemanagement/1501557-smloginitemsetenabled) instead..

IMPORTANT App Review has specific constraints about the use of login items. See clause 2.4.5(iii) of the [App Store Review Guidelines](https://developer.apple.com/app-store/review/).

With regards B, monitoring key presses, there are a variety of APIs to monitor keyboard events, including:

  - The [CGEventTap subsystme](https://developer.apple.com/documentation/coregraphics/cgevent/1454426-tapcreate)

  - AppKit’s [global event monitor](https://developer.apple.com/documentation/appkit/nsevent/1535472-addglobalmonitorforevents)

  - RegisterEventHotKey

Of these the one I like the most is RegisterEventHotKey. However, it’s intimately tied to the legacy Carbon toolbox and thus I can’t honestly recommend it.

Of the remaining options, I prefer CGEventTap because of its interactions with TCC. More on this below.

Note This is called CGEventTap because of the API name in C-based languages, CGEventTapCreate.

TCC stands for Transparency, Consent, and Control. It’s the subsystem behind the privileges in System Settings > Privacy and Security. To listen for keyboard events you’ll need the Input Monitoring privilege.

One reason I like CGEventTap is that it’s clearly associated with the APIs to determine whether you have that privilege (CGPreflightListenEventAccess) and to request that privilege (CGRequestListenEventAccess).

CGEventTap is compatible with the App Sandbox, starting with macOS 10.15.

CGEventTap is a bit tricky to use from Swift. See [this post](https://developer.apple.com/forums/thread/707680?answerId=716892022#716892022) for an example.

Maybe the alternative API allows to distinguish left and right modifiers (#28) and fn key (#1011)

@nikitabobko
Copy link
Owner Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant