summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Walzer <kw@codebykevin.com>2021-06-01 02:20:57 (GMT)
committerKevin Walzer <kw@codebykevin.com>2021-06-01 02:20:57 (GMT)
commit0eb891fc9c85b4bf6feb7741d4d6987e654d3bfd (patch)
treea5c0728cd983ef626f86d94ac8556930c7d18b60
parent7281e267cab2d7ed5a7f43cad8b8c70a72b4c847 (diff)
downloadtk-0eb891fc9c85b4bf6feb7741d4d6987e654d3bfd.zip
tk-0eb891fc9c85b4bf6feb7741d4d6987e654d3bfd.tar.gz
tk-0eb891fc9c85b4bf6feb7741d4d6987e654d3bfd.tar.bz2
Re-work macOS sysnotify implementation to remove code-signing requirement and multiple API's
-rw-r--r--doc/sysnotify.n21
-rw-r--r--macosx/tkMacOSXSysTray.c410
2 files changed, 13 insertions, 418 deletions
diff --git a/doc/sysnotify.n b/doc/sysnotify.n
index 8951cf0..e375c7a 100644
--- a/doc/sysnotify.n
+++ b/doc/sysnotify.n
@@ -32,25 +32,8 @@ accompany the text.
.TP
\fBmacOS\fR
.
-The macOS version embeds two separate under-the-hood implementations
-using different notification APIs. The choice of which one to use
-depends on which version of the OS is being run and the state of the
-Tk application code. The newer API, introduced in macOS 10.14,
-requires that the application accessing the API be code-signed, or the
-notification will not display. (A self-signed certificate seems to be
-sufficient.) The older API was deprecated but not removed in macOS
-11.0. Tk uses the newer API only for signed applications running on
-macOS 10.14 or newer. Otherwise it falls back to the older API. A
-quirk which developers should be aware of is that if an unsigned
-version of Wish (or an application derived from it) is installed on
-top of a signed version after the signed version has been registered
-with System Preferences then neither API will be allowed to show
-notifications, making Tk's automatic fallback to the older API
-ineffective. To re-enable notifications the application must be
-deleted from Apple's System Preferences Notifications section. (There
-is no removal button, so this is done by selecting the application and
-pressing the Delete key.)
-.
+The macOS version will request permission from the user to authorize
+notifications. This must be activated in Apple's System Preferences Notifications section.
.TP
\fBWindows\fR
.
diff --git a/macosx/tkMacOSXSysTray.c b/macosx/tkMacOSXSysTray.c
index a0f0829..d2ecfaf 100644
--- a/macosx/tkMacOSXSysTray.c
+++ b/macosx/tkMacOSXSysTray.c
@@ -19,71 +19,6 @@
#include "tkMacOSXPrivate.h"
/*
- * Prior to macOS 10.14 user notifications were handled by the NSApplication's
- * NSUserNotificationCenter via a NSUserNotificationCenterDelegate object.
- * These classes were defined in the CoreFoundation framework. In macOS 10.14
- * a separate UserNotifications framework was introduced which adds some
- * additional features, including custom controls on the notification window
- * but primarily a requirement that an application must be authorized before
- * being allowed to post a notification. This framework uses a different
- * class, the UNUserNotificationCenter, and its delegate follows a different
- * protocol, named UNUserNotificationCenterDelegate.
- *
- * In macOS 11.0 the NSUserNotificationCenter and its delegate protocol were
- * deprecated. To make matters more complicated, it turns out that there is a
- * secret undocumented additional requirement that an app which is not signed
- * can never be authorized to send notifications via the UNNotificationCenter.
- * (As of 11.0, it appears that it is sufficient to sign the app with a
- * self-signed certificate, however.)
- *
- * The workaround implemented here is to define two classes, TkNSNotifier and
- * TkUNNotifier, each of which provides one of these protocols on macOS 10.14
- * and newer. If the TkUSNotifier is able to obtain authorization it is used.
- * Otherwise, TkNSNotifier is used. Building TkNSNotifier on 11.0 or later
- * produces deprecation warnings which are suppressed by enclosing the
- * interface and implementation in #pragma blocks. The first time that the tk
- * systray command in initialized in an interpreter an attempt is made to
- * obtain authorization for sending notifications with the UNNotificationCenter
- * on systems and the result is saved in a static variable.
- */
-
-//#define DEBUG
-#ifdef DEBUG
-
-/*
- * This macro uses the do ... while(0) trick to swallow semicolons. It logs to
- * a temp file because apps launched from an icon have no stdout or stderr and
- * because NSLog has a tendency to not produce any console messages at certain
- * stages of launching an app.
- */
-
-#define DEBUG_LOG(format, ...) \
- do { \
- FILE* logfile = fopen("/tmp/tklog", "a"); \
- fprintf(logfile, format, ##__VA_ARGS__); \
- fflush(logfile); \
- fclose(logfile); } while (0)
-#else
-#define DEBUG_LOG(format, ...)
-#endif
-
-#define BUILD_TARGET_HAS_NOTIFICATION (MAC_OS_X_VERSION_MAX_ALLOWED >= 101000)
-#define BUILD_TARGET_HAS_UN_FRAMEWORK (MAC_OS_X_VERSION_MAX_ALLOWED >= 101400)
-#if MAC_OS_X_VERSION_MAX_ALLOWED > 101500
-#define ALERT_OPTION UNNotificationPresentationOptionList | \
- UNNotificationPresentationOptionBanner
-#else
-#define ALERT_OPTION UNNotificationPresentationOptionAlert
-#endif
-
-#if BUILD_TARGET_HAS_UN_FRAMEWORK
-#import <UserNotifications/UserNotifications.h>
-static NSString *TkNotificationCategory;
-#endif
-
-#if BUILD_TARGET_HAS_NOTIFICATION
-
-/*
* Class declaration for TkStatusItem.
*/
@@ -107,97 +42,7 @@ static NSString *TkNotificationCategory;
@end
-/*
- * Class declaration for TkNSNotifier. A TkNSNotifier object has no attributes
- * but implements the NSUserNotificationCenterDelegate protocol. It also has
- * one additional method which posts a user notification. There is one
- * TkNSNotifier for the application, shared by all interpreters.
- */
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-@interface TkNSNotifier: NSObject {
-}
-
-/*
- * Post a notification.
- */
-
-- (void) postNotificationWithTitle : (NSString *) title message: (NSString *) detail;
-
-/*
- * The following methods comprise the NSUserNotificationCenterDelegate protocol.
- */
-
-- (void) userNotificationCenter:(NSUserNotificationCenter *)center
- didDeliverNotification:(NSUserNotification *)notification;
-
-- (void) userNotificationCenter:(NSUserNotificationCenter *)center
- didActivateNotification:(NSUserNotification *)notification;
-
-- (BOOL) userNotificationCenter:(NSUserNotificationCenter *)center
- shouldPresentNotification:(NSUserNotification *)notification;
-
-@end
-#pragma clang diagnostic pop
-
-/*
- * The singleton instance of TkNSNotifier shared by all interpreters in this
- * application.
- */
-
-static TkNSNotifier *NSnotifier = nil;
-#if BUILD_TARGET_HAS_UN_FRAMEWORK
-
-/*
- * Class declaration for TkUNNotifier. A TkUNNotifier object has no attributes
- * but implements the UNUserNotificationCenterDelegate protocol It also has two
- * additional methods. One requests authorization to post notification via the
- * UserNotification framework and the other posts a user notification. There is
- * at most one TkUNNotifier for the application, shared by all interpreters.
- */
-
-@interface TkUNNotifier: NSObject {
-}
-
- /*
- * Request authorization to post a notification.
- */
-
-- (void) requestAuthorization;
-
-/*
- * Post a notification.
- */
-
-- (void) postNotificationWithTitle : (NSString *) title message: (NSString *) detail;
-
-/*
- * The following methods comprise the UNNotificationCenterDelegate protocol:
- */
-
-- (void)userNotificationCenter:(UNUserNotificationCenter *)center
- didReceiveNotificationResponse:(UNNotificationResponse *)response
- withCompletionHandler:(void (^)(void))completionHandler;
-
-- (void)userNotificationCenter:(UNUserNotificationCenter *)center
- willPresentNotification:(UNNotification *)notification
- withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler;
-
-- (void)userNotificationCenter:(UNUserNotificationCenter *)center
- openSettingsForNotification:(UNNotification *)notification;
-
-@end
-
-/*
- * The singleton instance of TkUNNotifier shared by all interpeters is stored
- * in this static variable.
- */
-
-static TkUNNotifier *UNnotifier = nil;
-
-#endif
/*
* Class declaration for TkStatusItem. A TkStatusItem represents an icon posted
@@ -297,163 +142,7 @@ static TkUNNotifier *UNnotifier = nil;
typedef TkStatusItem** StatusItemInfo;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-@implementation TkNSNotifier : NSObject
-- (void) postNotificationWithTitle : (NSString * ) title
- message: (NSString * ) detail
-{
- NSUserNotification *notification;
- NSUserNotificationCenter *center;
-
- center = [NSUserNotificationCenter defaultUserNotificationCenter];
- notification = [[NSUserNotification alloc] init];
- notification.title = title;
- notification.informativeText = detail;
- notification.soundName = NSUserNotificationDefaultSoundName;
- DEBUG_LOG("Sending NSNotification.\n");
- [center deliverNotification:notification];
-}
-
-/*
- * Implementation of the NSUserNotificationDelegate protocol.
- */
-
-- (BOOL) userNotificationCenter: (NSUserNotificationCenter *) center
- shouldPresentNotification: (NSUserNotification *)notification
-{
- (void) center;
- (void) notification;
-
- return YES;
-}
-
-- (void) userNotificationCenter:(NSUserNotificationCenter *)center
- didDeliverNotification:(NSUserNotification *)notification
-{
- (void) center;
- (void) notification;
-}
-
-- (void) userNotificationCenter:(NSUserNotificationCenter *)center
- didActivateNotification:(NSUserNotification *)notification
-{
- (void) center;
- (void) notification;
-}
-
-@end
-#pragma clang diagnostic pop
-
-/*
- * Static variable which records whether the app is authorized to send
- * notifications via the UNUserNotificationCenter.
- */
-
-#if BUILD_TARGET_HAS_UN_FRAMEWORK
-
-@implementation TkUNNotifier : NSObject
-
-- (void) requestAuthorization
-{
- UNUserNotificationCenter *center;
- UNAuthorizationOptions options = UNAuthorizationOptionAlert |
- UNAuthorizationOptionSound |
- UNAuthorizationOptionBadge |
- UNAuthorizationOptionProvidesAppNotificationSettings;
- if (![NSApp isSigned]) {
-
- /*
- * No point in even asking.
- */
-
- DEBUG_LOG("Unsigned app: UNUserNotifications are not available.\n");
- return;
- }
-
- center = [UNUserNotificationCenter currentNotificationCenter];
- [center requestAuthorizationWithOptions: options
- completionHandler: ^(BOOL granted, NSError* error)
- {
- if (error || granted == NO) {
- DEBUG_LOG("Authorization for UNUserNotifications denied\n");
- }
- }];
-}
-
-- (void) postNotificationWithTitle: (NSString * ) title
- message: (NSString * ) detail
-{
- UNUserNotificationCenter *center;
- UNMutableNotificationContent* content;
- UNNotificationRequest *request;
- center = [UNUserNotificationCenter currentNotificationCenter];
- center.delegate = (id) self;
- content = [[UNMutableNotificationContent alloc] init];
- content.title = title;
- content.body = detail;
- content.sound = [UNNotificationSound defaultSound];
- content.categoryIdentifier = TkNotificationCategory;
- request = [UNNotificationRequest
- requestWithIdentifier:[[NSUUID UUID] UUIDString]
- content:content
- trigger:nil
- ];
- [center addNotificationRequest: request
- withCompletionHandler: ^(NSError* error) {
- if (error) {
- DEBUG_LOG("addNotificationRequest: error = %s\n", \
- [NSString stringWithFormat:@"%@", \
- error.userInfo].UTF8String);
- }
- }];
-}
-
-/*
- * Implementation of the UNUserNotificationDelegate protocol.
- */
-
-- (void) userNotificationCenter:(UNUserNotificationCenter *)center
- didReceiveNotificationResponse:(UNNotificationResponse *)response
- withCompletionHandler:(void (^)(void))completionHandler
-{
- /*
- * Called when the user dismisses a notification.
- */
-
- DEBUG_LOG("didReceiveNotification\n");
- completionHandler();
-}
-
-- (void) userNotificationCenter:(UNUserNotificationCenter *)center
- willPresentNotification:(UNNotification *)notification
- withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
-{
-
- /*
- * This is called before presenting a notification, even when the user has
- * turned off notifications.
- */
-
- DEBUG_LOG("willPresentNotification\n");
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
- if (@available(macOS 11.0, *)) {
- completionHandler(ALERT_OPTION);
- }
-#endif
-}
-
-- (void) userNotificationCenter:(UNUserNotificationCenter *)center
- openSettingsForNotification:(UNNotification *)notification
-{
- DEBUG_LOG("openSettingsForNotification\n");
- // Does something need to be done here?
-}
-
-@end
-
-#endif
/*
*----------------------------------------------------------------------
@@ -729,47 +418,20 @@ static int SysNotifyObjCmd(
NSString *title = [NSString stringWithUTF8String: Tcl_GetString(objv[1])];
NSString *message = [NSString stringWithUTF8String: Tcl_GetString(objv[2])];
-
- /*
- * Update the authorization status in case the user enabled or disabled
- * notifications after the app started up.
- */
-
-#if BUILD_TARGET_HAS_UN_FRAMEWORK
-
- if (UNnotifier && [NSApp isSigned]) {
- UNUserNotificationCenter *center;
-
- center = [UNUserNotificationCenter currentNotificationCenter];
- [center getNotificationSettingsWithCompletionHandler:
- ^(UNNotificationSettings *settings)
- {
-#if !defined(DEBUG)
- (void) settings;
-#endif
- DEBUG_LOG("Reported authorization status is %ld\n",
- settings.authorizationStatus);
- }];
- }
-
-#endif
-
- if ([NSApp macOSVersion] < 101400 || ![NSApp isSigned]) {
- DEBUG_LOG("Using the NSUserNotificationCenter\n");
- [NSnotifier postNotificationWithTitle : title message: message];
- } else {
-
-#if BUILD_TARGET_HAS_UN_FRAMEWORK
-
- DEBUG_LOG("Using the UNUserNotificationCenter\n");
- [UNnotifier postNotificationWithTitle : title message: message];
-#endif
- }
+ NSMutableString *notify = [NSMutableString new];
+ [notify appendString: @"display notification "];
+ [notify appendString:@"\""];
+ [notify appendString:message];
+ [notify appendString:@"\""];
+ [notify appendString:@" with title \""];
+ [notify appendString:title];
+ [notify appendString:@"\""];
+ NSAppleScript *scpt = [[[NSAppleScript alloc] initWithSource:notify] autorelease];
+ NSAppleEventDescriptor *result = [scpt executeAndReturnError:nil];
return TCL_OK;
}
-#endif // if BUILD_TARGET_HAS_NOTIFICATION
/*
*----------------------------------------------------------------------
@@ -791,73 +453,23 @@ static int SysNotifyObjCmd(
*----------------------------------------------------------------------
*/
-#if BUILD_TARGET_HAS_NOTIFICATION
-
int
MacSystrayInit(Tcl_Interp *interp)
{
/*
- * Initialize the TkStatusItem for this interpreter and, if necessary,
- * the shared TkNSNotifier and TkUNNotifier.
+ * Initialize the TkStatusItem for this interpreter.
*/
StatusItemInfo info = (StatusItemInfo) ckalloc(sizeof(StatusItemInfo));
*info = 0;
- if (NSnotifier == nil) {
- NSnotifier = [[TkNSNotifier alloc] init];
- }
-
-#if BUILD_TARGET_HAS_UN_FRAMEWORK
-
- if (@available(macOS 10.14, *)) {
- UNUserNotificationCenter *center;
- UNNotificationCategory *category;
- NSSet *categories;
-
- if (UNnotifier == nil) {
- UNnotifier = [[TkUNNotifier alloc] init];
-
- /*
- * Request authorization to use the UserNotification framework. If
- * the app code is signed and there are no notification preferences
- * settings for this app, a dialog will be opened to prompt the
- * user to choose settings. Note that the request is asynchronous,
- * so even if the preferences setting exists the result is not
- * available immediately.
- */
-
- [UNnotifier requestAuthorization];
- }
- TkNotificationCategory = @"Basic Tk Notification";
- center = [UNUserNotificationCenter currentNotificationCenter];
- center = [UNUserNotificationCenter currentNotificationCenter];
- category = [UNNotificationCategory
- categoryWithIdentifier:TkNotificationCategory
- actions:@[]
- intentIdentifiers:@[]
- options: UNNotificationCategoryOptionNone];
- categories = [NSSet setWithObjects:category, nil];
- [center setNotificationCategories: categories];
- }
-#endif
-
Tcl_CreateObjCommand(interp, "::tk::systray::_systray", MacSystrayObjCmd, info,
(Tcl_CmdDeleteProc *)MacSystrayDestroy);
Tcl_CreateObjCommand(interp, "::tk::sysnotify::_sysnotify", SysNotifyObjCmd, NULL, NULL);
return TCL_OK;
}
-#else
-
-int
-MacSystrayInit(TCL_UNUSED(Tcl_Interp *))
-{
- return TCL_OK;
-}
-
-#endif // BUILD_TARGET_HAS_NOTIFICATION
/*
* Local Variables: