Add the Cocoa notification driver

Supported on macOS 10.14+ and iOS.
This commit is contained in:
Frank Praznik
2026-04-06 12:46:29 -04:00
parent 984b03680f
commit ba7c0b897b
5 changed files with 420 additions and 0 deletions

View File

@@ -58,6 +58,9 @@
1485C3312BBA4AF30063985B /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1485C32F2BBA4A0C0063985B /* UniformTypeIdentifiers.framework */; platformFilters = (maccatalyst, macos, ); settings = {ATTRIBUTES = (Weak, ); }; };
3AFD09EA2F9766BA00208BA9 /* SDL_CurvedUIShader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AFD09E92F9766BA00208BA9 /* SDL_CurvedUIShader.swift */; platformFilters = (xros, ); };
557D0CFA254586CA003913E3 /* CoreHaptics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F37DC5F225350EBC0002E6F7 /* CoreHaptics.framework */; platformFilters = (ios, maccatalyst, macos, tvos, xros, ); settings = {ATTRIBUTES = (Weak, ); }; };
30840D4C2F76A822000F1D1B /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30840D4B2F76A822000F1D1B /* UserNotifications.framework */; };
30840D4E2F76A8E7000F1D1B /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30840D4D2F76A8E7000F1D1B /* Security.framework */; };
557D0CFA254586CA003913E3 /* CoreHaptics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F37DC5F225350EBC0002E6F7 /* CoreHaptics.framework */; platformFilters = (ios, maccatalyst, macos, tvos, ); settings = {ATTRIBUTES = (Weak, ); }; };
557D0CFB254586D7003913E3 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A75FDABD23E28B6200529352 /* GameController.framework */; settings = {ATTRIBUTES = (Required, ); }; };
5616CA4C252BB2A6005D5928 /* SDL_url.c in Sources */ = {isa = PBXBuildFile; fileRef = 5616CA49252BB2A5005D5928 /* SDL_url.c */; };
5616CA4D252BB2A6005D5928 /* SDL_sysurl.h in Headers */ = {isa = PBXBuildFile; fileRef = 5616CA4A252BB2A6005D5928 /* SDL_sysurl.h */; };
@@ -566,6 +569,12 @@
F3FD042E2C9B755700824C4C /* SDL_hidapi_nintendo.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FD042C2C9B755700824C4C /* SDL_hidapi_nintendo.h */; };
F3FD042F2C9B755700824C4C /* SDL_hidapi_steam_hori.c in Sources */ = {isa = PBXBuildFile; fileRef = F3FD042D2C9B755700824C4C /* SDL_hidapi_steam_hori.c */; };
FA73671D19A540EF004122E4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA73671C19A540EF004122E4 /* CoreVideo.framework */; platformFilters = (ios, maccatalyst, macos, tvos, xros, ); settings = {ATTRIBUTES = (Required, ); }; };
FA73671D19A540EF004122E4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA73671C19A540EF004122E4 /* CoreVideo.framework */; platformFilters = (ios, maccatalyst, macos, tvos, ); settings = {ATTRIBUTES = (Required, ); }; };
00009183ED11C92F23FC0000 /* SDL_notification.c in Sources */ = {isa = PBXBuildFile; fileRef = 000059D16599F687D87B0000 /* SDL_notification.c */; };
0000B6DBAE1F178E87010000 /* SDL_notification_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 000045BF87FD2865AB1C0000 /* SDL_notification_c.h */; };
0000FB5C9B8CE5929A250000 /* SDL_cocoanotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 000088B5AFB11E40F7920000 /* SDL_cocoanotification.m */; };
0000AF1D2CED20010C2D0000 /* SDL_notificationevents.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000D6CCB566B8AE47FE0000 /* SDL_notificationevents.c */; };
00004A19C923458228C10000 /* SDL_notificationevents_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 0000FA7334391F6720820000 /* SDL_notificationevents_c.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -624,6 +633,8 @@
02D6A1C128A84B8F00A7F001 /* SDL_hidapi_sinput.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_sinput.c; sourceTree = "<group>"; };
1485C32F2BBA4A0C0063985B /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; };
3AFD09E92F9766BA00208BA9 /* SDL_CurvedUIShader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDL_CurvedUIShader.swift; sourceTree = "<group>"; };
30840D4B2F76A822000F1D1B /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; };
30840D4D2F76A8E7000F1D1B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
5616CA49252BB2A5005D5928 /* SDL_url.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_url.c; sourceTree = "<group>"; };
5616CA4A252BB2A6005D5928 /* SDL_sysurl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sysurl.h; sourceTree = "<group>"; };
5616CA4B252BB2A6005D5928 /* SDL_sysurl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_sysurl.m; sourceTree = "<group>"; };
@@ -1168,6 +1179,11 @@
F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = "<group>"; };
F5A2EF3900C6A39A01000001 /* BUGS.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = BUGS.txt; path = ../../BUGS.txt; sourceTree = SOURCE_ROOT; };
FA73671C19A540EF004122E4 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
000059D16599F687D87B0000 /* SDL_notification.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_notification.c; path = SDL_notification.c; sourceTree = "<group>"; };
000045BF87FD2865AB1C0000 /* SDL_notification_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_notification_c.h; path = SDL_notification_c.h; sourceTree = "<group>"; };
000088B5AFB11E40F7920000 /* SDL_cocoanotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDL_cocoanotification.m; path = SDL_cocoanotification.m; sourceTree = "<group>"; };
0000D6CCB566B8AE47FE0000 /* SDL_notificationevents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_notificationevents.c; path = SDL_notificationevents.c; sourceTree = "<group>"; };
0000FA7334391F6720820000 /* SDL_notificationevents_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_notificationevents_c.h; path = SDL_notificationevents_c.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -1176,11 +1192,13 @@
buildActionMask = 2147483647;
files = (
1485C3312BBA4AF30063985B /* UniformTypeIdentifiers.framework in Frameworks */,
30840D4E2F76A8E7000F1D1B /* Security.framework in Frameworks */,
A7381E971D8B6A0300B177DD /* AudioToolbox.framework in Frameworks */,
00D0D0D810675E46004B05EF /* Carbon.framework in Frameworks */,
007317A40858DECD00B2BC32 /* Cocoa.framework in Frameworks */,
A7381E961D8B69D600B177DD /* CoreAudio.framework in Frameworks */,
557D0CFA254586CA003913E3 /* CoreHaptics.framework in Frameworks */,
30840D4C2F76A822000F1D1B /* UserNotifications.framework in Frameworks */,
00D0D08410675DD9004B05EF /* CoreFoundation.framework in Frameworks */,
FA73671D19A540EF004122E4 /* CoreVideo.framework in Frameworks */,
00CFA89D106B4BA100758660 /* ForceFeedback.framework in Frameworks */,
@@ -1445,6 +1463,7 @@
F3E5A6EA2AD5E0E600293D83 /* SDL_properties.c */,
F386F6E62884663E001840AA /* SDL_utils.c */,
F386F6E52884663E001840AA /* SDL_utils_c.h */,
0000A12B47E1FC5391780000 /* notification */,
);
name = "Library Source";
path = ../../src;
@@ -1474,6 +1493,8 @@
564624341FF821B70074AC87 /* Frameworks */ = {
isa = PBXGroup;
children = (
30840D4D2F76A8E7000F1D1B /* Security.framework */,
30840D4B2F76A822000F1D1B /* UserNotifications.framework */,
1485C32F2BBA4A0C0063985B /* UniformTypeIdentifiers.framework */,
F382339B2738ED6600F7F527 /* CoreBluetooth.framework */,
F376F7272559B77100CFC0BC /* CoreAudio.framework */,
@@ -2330,6 +2351,8 @@
A7D8A93723E2514000DCD162 /* SDL_touch_c.h */,
A7D8A92F23E2514000DCD162 /* SDL_windowevents.c */,
A7D8A94323E2514000DCD162 /* SDL_windowevents_c.h */,
0000D6CCB566B8AE47FE0000 /* SDL_notificationevents.c */,
0000FA7334391F6720820000 /* SDL_notificationevents_c.h */,
);
path = events;
sourceTree = "<group>";
@@ -2551,6 +2574,24 @@
path = resources;
sourceTree = "<group>";
};
0000A12B47E1FC5391780000 /* notification */ = {
isa = PBXGroup;
children = (
000059D16599F687D87B0000 /* SDL_notification.c */,
000045BF87FD2865AB1C0000 /* SDL_notification_c.h */,
0000B071CC4D6AB5CE640000 /* cocoa */,
);
path = notification;
sourceTree = "<group>";
};
0000B071CC4D6AB5CE640000 /* cocoa */ = {
isa = PBXGroup;
children = (
000088B5AFB11E40F7920000 /* SDL_cocoanotification.m */,
);
path = cocoa;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@@ -3199,6 +3240,9 @@
0000A03C0F32C43816F40000 /* SDL_asyncio_windows_ioring.c in Sources */,
0000A877C7DB9FA935FC0000 /* SDL_uikitpen.m in Sources */,
63124A422E5C357500A53610 /* SDL_hidapi_zuiki.c in Sources */,
00009183ED11C92F23FC0000 /* SDL_notification.c in Sources */,
0000FB5C9B8CE5929A250000 /* SDL_cocoanotification.m in Sources */,
0000AF1D2CED20010C2D0000 /* SDL_notificationevents.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -359,6 +359,11 @@ bool SDL_InitSubSystem(SDL_InitFlags flags)
SDL_DBus_Init();
#endif
#ifdef SDL_PLATFORM_APPLE
// Apple platforms require the notification delegate to be registered early.
Cocoa_RegisterNotificationDelegate();
#endif
#ifdef SDL_PLATFORM_WINDOWS
if (flags & (SDL_INIT_HAPTIC | SDL_INIT_JOYSTICK)) {
if (!SDL_HelperWindowCreate()) {

View File

@@ -27,6 +27,10 @@
extern SDL_NotificationID SDL_SYS_ShowNotification(SDL_PropertiesID props);
extern void SDL_CleanupNotifications();
#ifdef SDL_PLATFORM_APPLE
extern void Cocoa_RegisterNotificationDelegate();
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND
extern const char *SDL_GetNotificationActivationToken();
#endif

View File

@@ -0,0 +1,360 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "../SDL_notification_c.h"
// tvOS doesn't support the notification features SDL cares about.
#ifndef SDL_PLATFORM_TVOS
#include "../../events/SDL_notificationevents_c.h"
#include "../../video/SDL_surface_c.h"
#import <Foundation/Foundation.h>
#import <UserNotifications/UNNotification.h>
#import <UserNotifications/UNNotificationAction.h>
#import <UserNotifications/UNNotificationAttachment.h>
#import <UserNotifications/UNNotificationCategory.h>
#import <UserNotifications/UNNotificationContent.h>
#import <UserNotifications/UNNotificationRequest.h>
#import <UserNotifications/UNNotificationResponse.h>
#import <UserNotifications/UNNotificationSettings.h>
#import <UserNotifications/UNNotificationSound.h>
#import <UserNotifications/UNUserNotificationCenter.h>
@interface SDLNotificationDelegate : NSObject <UNUserNotificationCenterDelegate>
@end
@implementation SDLNotificationDelegate
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
API_AVAILABLE(macos(10.14))
{
if (@available(macOS 11, iOS 14, *)) {
completionHandler(UNNotificationPresentationOptionBanner + UNNotificationPresentationOptionSound);
} else {
completionHandler(UNNotificationPresentationOptionAlert + UNNotificationPresentationOptionSound);
}
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
API_AVAILABLE(macos(10.14))
{
NSString *SDL_Identifier = @"SDL_LocalNotification-";
NSString *identifier = [[[response notification] request] identifier];
// const char *identifier = [[[[response notification] request] identifier] UTF8String];
SDL_NotificationID id = 0;
if ([identifier compare:SDL_Identifier options:0 range:NSMakeRange(0, [SDL_Identifier length])] == 0) {
id = (SDL_NotificationID)[[identifier substringFromIndex:[SDL_Identifier length]] integerValue];
}
if (id) {
NSString *action_id = [response actionIdentifier];
if (action_id) {
if ([action_id isEqualToString:UNNotificationDefaultActionIdentifier]) {
SDL_SendNotificationAction(id, "default");
} else {
if ([action_id length] != 0) {
SDL_SendNotificationAction(id, [action_id UTF8String]);
}
}
}
}
completionHandler();
}
@end
API_AVAILABLE(macos(10.14))
static UNUserNotificationCenter *center;
static SDLNotificationDelegate *delegate;
static bool ShouldEnableNotifications()
{
#if defined(SDL_PLATFORM_MACOS)
/* Notifications outside of an app bundle are unsupported, and will crash with an
* unhandled exception error, deep within a system library.
*
* FIXME: These functions are deprecated, find a modern way.
*/
CFBundleRef bundle = CFBundleGetMainBundle();
CFURLRef bundleUrl = CFBundleCopyBundleURL(bundle);
CFStringRef uti;
if (CFURLCopyResourcePropertyForKey(bundleUrl, kCFURLTypeIdentifierKey, &uti, NULL) &&
uti && UTTypeConformsTo(uti, kUTTypeApplicationBundle)) {
return true;
}
return false;
#else
// iOS can always enable notificaions.
return true;
#endif
}
static NSURL *SaveTempImage(SDL_Surface *image)
{
@autoreleasepool {
const UInt32 hash = SDL_murmur3_32(image->pixels, image->pitch * image->h, 0);
NSString *tempFileName = [NSString stringWithFormat:@"SDL_tmpimage-%u.png", hash];
NSString *tempFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:tempFileName];
if (!SDL_SavePNG(image, [tempFilePath fileSystemRepresentation])) {
return nil;
}
return [NSURL fileURLWithPath:tempFilePath];
}
}
void Cocoa_RegisterNotificationDelegate()
{
if (!ShouldEnableNotifications()) {
return;
}
if (@available(macOS 10.14, *)) {
@autoreleasepool {
if (!center) {
center = [UNUserNotificationCenter currentNotificationCenter];
}
if (!delegate) {
delegate = [SDLNotificationDelegate new];
[center setDelegate:delegate];
}
}
}
}
bool SDL_RequestNotificationPermission(void)
{
@autoreleasepool {
if (@available(macOS 10.14, *)) {
// Notifications not initialized (not in a bundle).
if (!center) {
return SDL_SetError("macOS notifications not supported outside an application bundle");
}
// Check authorization to send notifications, and request it if necessary.
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *_Nonnull settings) {
if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) {
UNAuthorizationOptions options = UNAuthorizationOptionAlert + UNAuthorizationOptionSound;
[center requestAuthorizationWithOptions:options
completionHandler:^(BOOL granted, NSError *_Nullable error) {}];
}
}];
return true;
} else {
SDL_SetError("Notifications require macOS 10.14 or higher");
return false;
}
}
return false;
}
bool SDL_RemoveNotification(SDL_NotificationID notification)
{
@autoreleasepool {
if (@available(macOS 10.14, *)) {
// Notifications not initialized (not in a bundle).
if (!center) {
return SDL_SetError("macOS notifications not supported outside an application bundle");
}
NSString *identifier = [NSString stringWithFormat:@"SDL_LocalNotification-%u", notification];
[center removePendingNotificationRequestsWithIdentifiers:@[ identifier ]];
[center removeDeliveredNotificationsWithIdentifiers:@[ identifier ]];
}
}
return true;
}
SDL_NotificationID SDL_SYS_ShowNotification(SDL_PropertiesID props)
{
@autoreleasepool {
if (@available(macOS 10.14, *)) {
if (!SDL_RequestNotificationPermission()) {
return false;
}
// Get the notification properties.
const char *title = SDL_GetStringProperty(props, SDL_PROP_NOTIFICATION_TITLE_STRING, NULL);
const char *message = SDL_GetStringProperty(props, SDL_PROP_NOTIFICATION_MESSAGE_STRING, "");
const char *sound = SDL_GetStringProperty(props, SDL_PROP_NOTIFICATION_SOUND_STRING, "default");
SDL_Surface *image = SDL_GetPointerProperty(props, SDL_PROP_NOTIFICATION_IMAGE_POINTER, NULL);
const SDL_NotificationID replaces = (SDL_NotificationID)SDL_GetNumberProperty(props, SDL_PROP_NOTIFICATION_REPLACES_NUMBER, 0);
const SDL_NotificationPriority priority = (SDL_NotificationPriority)SDL_GetNumberProperty(props, SDL_PROP_NOTIFICATION_PRIORITY_NUMBER, SDL_NOTIFICATION_PRIORITY_NORMAL);
const SDL_NotificationAction *sdlactions = SDL_GetPointerProperty(props, SDL_PROP_NOTIFICATION_ACTIONS_POINTER, NULL);
const int num_sdlactions = (int)SDL_GetNumberProperty(props, SDL_PROP_NOTIFICATION_ACTION_COUNT_NUMBER, 0);
const bool transient = SDL_GetBooleanProperty(props, SDL_PROP_NOTIFICATION_TRANSIENT_BOOLEAN, false);
// Generate a new ID.
Uint32 new_id;
if (replaces) {
new_id = replaces;
} else if (SecRandomCopyBytes(kSecRandomDefault, sizeof(new_id), &new_id) != errSecSuccess) {
new_id = (Uint32)SDL_GetTicksNS();
}
// Build the action array.
NSMutableArray *actions = nil;
if (sdlactions && num_sdlactions) {
actions = [NSMutableArray array];
for (int i = 0; i < num_sdlactions; ++i) {
if (sdlactions[i].type == SDL_NOTIFICATION_ACTION_TYPE_BUTTON) {
UNNotificationAction *action = [UNNotificationAction actionWithIdentifier:[NSString stringWithUTF8String:sdlactions[i].button.action_id]
title:[NSString stringWithUTF8String:sdlactions[i].button.action_label]
options:UNNotificationActionOptionNone];
actions[i] = action;
}
}
}
// Create the category.
NSString *category_id = nil;
if (actions && [actions count]) {
// Create the notification category.
category_id = [[NSUUID new] UUIDString];
UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:category_id
actions:actions
intentIdentifiers:@[]
options:UNNotificationCategoryOptionNone];
NSSet *categories = [NSSet setWithObject:category];
[center setNotificationCategories:categories];
}
// Configure the content.
UNMutableNotificationContent *content = [UNMutableNotificationContent new];
content.title = [NSString stringWithUTF8String:title];
content.body = [NSString stringWithUTF8String:message];
content.categoryIdentifier = category_id;
if (SDL_strcmp(sound, "default") == 0) {
if (priority == SDL_NOTIFICATION_PRIORITY_CRITICAL) {
// defaultCriticalSound is only in iOS 12+
if (@available(iOS 12, *)) {
content.sound = [UNNotificationSound defaultCriticalSound];
} else {
content.sound = [UNNotificationSound defaultSound];
}
} else {
content.sound = [UNNotificationSound defaultSound];
}
} else if (SDL_strcmp(sound, "silent") != 0) {
if (priority == SDL_NOTIFICATION_PRIORITY_CRITICAL) {
if (@available(iOS 12, *)) {
content.sound = [UNNotificationSound criticalSoundNamed:[NSString stringWithUTF8String:sound]];
} else {
content.sound = [UNNotificationSound soundNamed:[NSString stringWithUTF8String:sound]];
}
} else {
content.sound = [UNNotificationSound soundNamed:[NSString stringWithUTF8String:sound]];
}
}
if (@available(macOS 12, iOS 15, *)) {
switch (priority) {
case SDL_NOTIFICATION_PRIORITY_LOW:
content.interruptionLevel = UNNotificationInterruptionLevelPassive;
break;
case SDL_NOTIFICATION_PRIORITY_CRITICAL:
content.interruptionLevel = UNNotificationInterruptionLevelCritical;
break;
case SDL_NOTIFICATION_PRIORITY_NORMAL:
case SDL_NOTIFICATION_PRIORITY_HIGH:
default:
content.interruptionLevel = UNNotificationInterruptionLevelActive;
break;
}
}
// Notifications load images from file paths, so save it to a temporary location.
if (image) {
NSURL *url = SaveTempImage(image);
if (url) {
UNNotificationAttachment *attach = [UNNotificationAttachment attachmentWithIdentifier:@"" URL:url options:nil error:nil];
content.attachments = @[ attach ];
}
}
NSString *identifier = [NSString stringWithFormat:@"SDL_LocalNotification-%u", new_id];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier
content:content
trigger:nil];
[center addNotificationRequest:request
withCompletionHandler:^(NSError *_Nullable error) {
if (error != nil) {
SDL_SetError("Failed to show notification");
}
}];
/* There is no way to tell if the notification was ignored or wound up
* in the notification center, so just remove transient notifications
* after a brief period via a timer.
*/
if (transient) {
[NSTimer scheduledTimerWithTimeInterval:7 repeats:NO block:^(NSTimer *timer) {
SDL_RemoveNotification(new_id);
}];
}
return new_id;
} else {
SDL_SetError("macOS 10.14+ required for notifications");
}
return 0;
}
}
#else
// Notifications on tvOS are just for updating badges, and are of no use here.
SDL_NotificationID SDL_SYS_ShowNotification(SDL_PropertiesID props)
{
SDL_SetError("Notifications not supported on tvOS");
return 0;
}
void Cocoa_RegisterNotificationDelegate()
{
}
bool SDL_RequestNotificationPermission(void)
{
return SDL_SetError("Notifications not supported on tvOS");
}
bool SDL_RemoveNotification(SDL_NotificationID notification)
{
return SDL_SetError("Notifications not supported on tvOS");
}
#endif
void SDL_CleanupNotifications()
{
// TODO: Anything to do here?
}

View File

@@ -43,6 +43,13 @@ void SDL_CleanupNotifications()
// Nothing to do.
}
#ifdef SDL_PLATFORM_APPLE
void Cocoa_RegisterNotificationDelegate()
{
// Nothing to do.
}
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND
const char *SDL_GetNotificationActivationToken()
{