Skip to content

Commit

Permalink
Merge pull request #12354 from keymanapp/fix/mac/12295-catalina-start…
Browse files Browse the repository at this point in the history
…up-crash

fix(mac): avoid crash on startup with macOS 10.15 (Catalina)
  • Loading branch information
sgschantz authored Sep 5, 2024
2 parents 870e559 + 703790d commit a64ada8
Showing 1 changed file with 31 additions and 27 deletions.
58 changes: 31 additions & 27 deletions mac/Keyman4MacIM/Keyman4MacIM/Privacy/PrivacyConsent.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/**
/*
* Keyman is copyright (C) SIL International. MIT License.
*
* PrivacyConsent.m
Expand All @@ -7,24 +7,28 @@
* Created by Shawn Schantz on 2022-09-22.
*
* Used to determine if the user has provided consent to the services it needs
* and, if not, to request that consent. For versions of macOS prior to 10.15,
* Keyman requires Accessibility access. For 10.15 and later, Keyman requires
* and, if not, to request that consent. For versions of macOS prior to 11.0,
* Keyman requires Accessibility access. For 11.0 and later, Keyman requires
* PostEvent access. Both are presented to the user as a need for Accessibility
* when prompted by the system and are listed under Accessibility in the Privacy
* tab in System Preferences, Security & Privacy. Both Accessibility and
* PostEvent encapsulate ListenEvent (or Keyboard Monitoring) permission, so
* that access is not explicitly needed as long as one of the others is provided
* first.
*
* Access this functionality through the shared instance. Call
* Note that Apple's documentation states that the PostEvent access APIs are
* available with macOS 10.15 (Catalina) but testing showed that it was only
* available with macOS 11.0 (Big Sur). This is documented with issue #12295.
*
* To access this functionality, used the shared instance. Call
* requestPrivacyAccess which will check whether access has been granted. If
* not, then a dialog will be presented to the user to inform them that
* Accessibility is required. Dismissing this dialog will trigger the API call
* to prompt the user for permission. However, the prompt from the system will
* only happen once. If the user was prompted earlier and did not grant access,
* they can still go to System Preferences and enable it there. So it is useful
* to prompt with a dialog from Keyman at startup. Without Accessibility, the
* shift layer and the OSK do not work -- so Keyman is basically useless.
* shift layer and the OSK do not work.
*/

#import "PrivacyConsent.h"
Expand All @@ -43,7 +47,7 @@ + (PrivacyConsent *)shared
}

/**
* For macOS earlier than 10.15: check for Accessibility access.
* For macOS earlier than 11.0: check for Accessibility access.
*/
- (BOOL)checkAccessibility
{
Expand All @@ -66,7 +70,7 @@ - (void)requestPrivacyAccess:(void (^)(void))withCompletionHandler
BOOL hasAccessibility = NO;

// check if we already have accessibility
if (@available(macOS 10.15, *)) {
if (@available(macOS 11.0, *)) {
hasAccessibility = [self checkPostEventAccess];
} else {
hasAccessibility = [self checkAccessibility];
Expand All @@ -85,7 +89,7 @@ - (void)requestPrivacyAccess:(void (^)(void))withCompletionHandler
}

/**
* For macOS earlier than 10.15: request Accessibility access.
* For macOS earlier than 11.0: request Accessibility access.
*/
- (void)requestAccessibility
{
Expand Down Expand Up @@ -113,18 +117,18 @@ - (NSWindowController *)privacyDialog {
*/
- (void)configureDialogForOsVersion {

if (@available(macOS 10.15, *)) {
[self configureDialogForCatalinaAndLater];
if (@available(macOS 11.0, *)) {
[self configureDialogForBigSurAndLater];
} else {
[self configureDialogForPreCatalina];
[self configureDialogForPreBigSur];
}
}

/**
* Initialize privacy dialog for macOS versions prior to Catalina (10.15).
* Initialize privacy dialog for macOS versions prior to Big Sur (11.0).
* In this case, request Accessibility access, not PostEvent.
*/
- (void)configureDialogForPreCatalina {
- (void)configureDialogForPreBigSur {
void (^consentPrompt)(void) = ^(void) {
[self requestAccessibility];

Expand All @@ -136,10 +140,10 @@ - (void)configureDialogForPreCatalina {
}

/**
* Initialize privacy dialog for macOS versions of Catalina (10.15) or later.
* Initialize privacy dialog for macOS versions of Big Sur (11.0) or later.
* In this case, request PostEvent access, not Accessibility.
*/
- (void)configureDialogForCatalinaAndLater {
- (void)configureDialogForBigSurAndLater {
void (^consentPrompt)(void) = ^(void) {
[self requestPostEventAccess];

Expand All @@ -160,7 +164,7 @@ - (void)showPrivacyDialog {

/**
* Check whether the user has allowed ListenEvent access.
* Only available with macOS Catalina (10.15) or later.
* Only available with macOS Big Sur (11.0) or later.
*
* If user has granted Accessibility or PostEvent access, then
* ListenEvent access is also granted.
Expand All @@ -170,18 +174,18 @@ - (BOOL)checkListenEventAccess
BOOL hasAccess = NO;

// below checks for ListenEvent access
if (@available(macOS 10.15, *)) {
if (@available(macOS 11.0, *)) {
hasAccess = CGPreflightListenEventAccess();
os_log([KMLogs privacyLog], "CGPreflightListenEventAccess() returned %@", hasAccess ? @"YES" : @"NO");
} else {
os_log([KMLogs privacyLog], "CGPreflightListenEventAccess not available before macOS version 10.15");
os_log([KMLogs privacyLog], "CGPreflightListenEventAccess not available before macOS version 11.0");
}
return hasAccess;
}

/**
* Check whether the user has allowed PostEvent access.
* Only available with macOS Catalina (10.15) or later.
* Only available with macOS Big Sur (11.0) or later.
*
* If user has granted Accessibility, then PostEvent access is also granted.
*/
Expand All @@ -190,46 +194,46 @@ - (BOOL)checkPostEventAccess
BOOL hasAccess = NO;

// below checks for PostEvent access
if (@available(macOS 10.15, *)) {
if (@available(macOS 11.0, *)) {
hasAccess = CGPreflightPostEventAccess();

os_log([KMLogs privacyLog], "CGPreflightPostEventAccess() returned %@", hasAccess ? @"YES" : @"NO");
} else {
os_log([KMLogs privacyLog], "CGPreflightPostEventAccess not available before macOS version 10.15");
os_log([KMLogs privacyLog], "CGPreflightPostEventAccess not available before macOS version 11.0");
}
return hasAccess;
}

/**
* Request ListenEvent access.
* Only available with macOS Catalina (10.15) or later.
* Only available with macOS Big Sur (11.0) or later.
*/
- (BOOL)requestListenEventAccess
{
BOOL granted = NO;

if (@available(macOS 10.15, *)) {
if (@available(macOS 11.0, *)) {
granted = CGRequestListenEventAccess();
os_log([KMLogs privacyLog], "CGRequestListenEventAccess() returned %@", granted ? @"YES" : @"NO");
} else {
os_log([KMLogs privacyLog], "CGRequestListenEventAccess not available before macOS version 10.15");
os_log([KMLogs privacyLog], "CGRequestListenEventAccess not available before macOS version 11.0");
}
return granted;
}

/**
* Request PostEvent access.
* Only available with macOS Catalina (10.15) or later.
* Only available with macOS Big Sur (11.0) or later.
*/
- (BOOL)requestPostEventAccess
{
BOOL granted = NO;

if (@available(macOS 10.15, *)) {
if (@available(macOS 11.0, *)) {
granted = CGRequestPostEventAccess();
os_log([KMLogs privacyLog], "CGRequestPostEventAccess() returned %@", granted ? @"YES" : @"NO");
} else {
os_log([KMLogs privacyLog], "CGRequestPostEventAccess not available before macOS version 10.15");
os_log([KMLogs privacyLog], "CGRequestPostEventAccess not available before macOS version 11.0");
}
return granted;
}
Expand Down

0 comments on commit a64ada8

Please sign in to comment.