mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-06-13 07:03:54 +00:00
Add the Cocoa notification driver
Supported on macOS 10.14+ and iOS.
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
360
src/notification/cocoa/SDL_cocoanotification.m
Normal file
360
src/notification/cocoa/SDL_cocoanotification.m
Normal 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?
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user