From 068e4059124d8bf5fa478d404e66f73527eeab52 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Mon, 6 Apr 2026 12:42:57 -0400
Subject: [PATCH] Add notification framework with test and dummy driver
---
Android.mk | 2 +
CMakeLists.txt | 41 ++-
VisualC-GDK/SDL/SDL.vcxproj | 5 +
include/SDL3/SDL.h | 1 +
include/SDL3/SDL_events.h | 22 ++
include/SDL3/SDL_notification.h | 254 ++++++++++++++++++
src/SDL.c | 6 +-
src/dynapi/SDL_dynapi.exports | 4 +
src/dynapi/SDL_dynapi.sym | 4 +
src/dynapi/SDL_dynapi_overrides.h | 4 +
src/dynapi/SDL_dynapi_procs.h | 4 +
src/events/SDL_categories.c | 3 +
src/events/SDL_categories_c.h | 1 +
src/events/SDL_events.c | 5 +
src/events/SDL_notificationevents.c | 41 +++
src/events/SDL_notificationevents_c.h | 30 +++
src/notification/SDL_notification.c | 85 ++++++
src/notification/SDL_notification_c.h | 34 +++
.../dummy/SDL_dummynotification.c | 51 ++++
src/test/SDL_test_common.c | 4 +
test/CMakeLists.txt | 1 +
test/testnotification.c | 160 +++++++++++
22 files changed, 759 insertions(+), 3 deletions(-)
create mode 100644 include/SDL3/SDL_notification.h
create mode 100644 src/events/SDL_notificationevents.c
create mode 100644 src/events/SDL_notificationevents_c.h
create mode 100644 src/notification/SDL_notification.c
create mode 100644 src/notification/SDL_notification_c.h
create mode 100644 src/notification/dummy/SDL_dummynotification.c
create mode 100644 test/testnotification.c
diff --git a/Android.mk b/Android.mk
index d53bf403b1611..6959288468943 100644
--- a/Android.mk
+++ b/Android.mk
@@ -59,6 +59,8 @@ LOCAL_SRC_FILES := \
$(wildcard $(LOCAL_PATH)/src/main/generic/*.c) \
$(wildcard $(LOCAL_PATH)/src/misc/*.c) \
$(wildcard $(LOCAL_PATH)/src/misc/android/*.c) \
+ $(wildcard $(LOCAL_PATH)/src/notification/*.c) \
+ $(wildcard $(LOCAL_PATH)/src/notification/dummy/*.c) \
$(wildcard $(LOCAL_PATH)/src/power/*.c) \
$(wildcard $(LOCAL_PATH)/src/power/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/process/*.c) \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 45892a81f745e..5c61b5383259a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -268,6 +268,7 @@ define_sdl_subsystem(Power)
define_sdl_subsystem(Sensor)
define_sdl_subsystem(Dialog)
define_sdl_subsystem(Tray)
+define_sdl_subsystem(Notification)
cmake_dependent_option(SDL_FRAMEWORK "Build SDL libraries as Apple Framework" OFF "APPLE" OFF)
if(SDL_FRAMEWORK)
@@ -460,6 +461,7 @@ if (NGAGE)
set(SDL_DUMMYAUDIO OFF)
set(SDL_DUMMYCAMERA OFF)
set(SDL_DUMMYVIDEO OFF)
+ set(SDL_NOTIFICATION OFF)
set(SDL_OFFSCREEN OFF)
set(SDL_RENDER_GPU OFF)
set(SDL_TRAY OFF)
@@ -1327,6 +1329,8 @@ sdl_glob_sources(
"${SDL3_SOURCE_DIR}/src/main/*.h"
"${SDL3_SOURCE_DIR}/src/misc/*.c"
"${SDL3_SOURCE_DIR}/src/misc/*.h"
+ "${SDL3_SOURCE_DIR}/src/notification/*.c"
+ "${SDL3_SOURCE_DIR}/src/notification/*.h"
"${SDL3_SOURCE_DIR}/src/power/*.c"
"${SDL3_SOURCE_DIR}/src/power/*.h"
"${SDL3_SOURCE_DIR}/src/render/*.c"
@@ -2055,6 +2059,13 @@ elseif(UNIX AND NOT (APPLE OR RISCOS OR HAIKU OR CYGWIN))
"${SDL3_SOURCE_DIR}/src/core/linux/SDL_progressbar.c"
"${SDL3_SOURCE_DIR}/src/core/linux/SDL_progressbar.h"
)
+
+ if(SDL_NOTIFICATION)
+ sdl_sources(
+ "${SDL3_SOURCE_DIR}/src/notification/unix/SDL_dbusnotification.c"
+ )
+ set(HAVE_SDL_NOTIFICATION TRUE)
+ endif()
endif()
if(SDL_USE_IME)
@@ -2316,6 +2327,7 @@ elseif(WINDOWS OR CYGWIN)
check_include_file(audioclient.h HAVE_AUDIOCLIENT_H)
check_include_file(sensorsapi.h HAVE_SENSORSAPI_H)
check_include_file(shellscalingapi.h HAVE_SHELLSCALINGAPI_H)
+ check_include_file(Windows.ui.notifications.h HAVE_WINDOWS_UI_NOTIFICATIONS_H)
check_c_source_compiles("
#include <windows.h>
#include <mfapi.h>
@@ -2425,7 +2437,7 @@ elseif(WINDOWS OR CYGWIN)
set(HAVE_SDL_STORAGE 1)
# Libraries for Win32 native and MinGW
- sdl_link_dependency(base LIBS kernel32 user32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32 hid)
+ sdl_link_dependency(base LIBS kernel32 user32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32 hid mincore)
set(SDL_TIME_WINDOWS 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/windows/*.c")
@@ -2474,6 +2486,11 @@ elseif(WINDOWS OR CYGWIN)
set(HAVE_SDL_TRAY TRUE)
endif()
+ if(SDL_NOTIFICATION AND HAVE_WINDOWS_UI_NOTIFICATIONS_H)
+ sdl_glob_sources("${SDL3_SOURCE_DIR}/src/notification/windows/*.c")
+ set(HAVE_SDL_NOTIFICATION TRUE)
+ endif()
+
if(SDL_HIDAPI)
CheckHIDAPI()
endif()
@@ -2650,6 +2667,11 @@ elseif(APPLE)
set(HAVE_SDL_HAPTIC TRUE)
endif()
+ if(SDL_NOTIFICATION)
+ set(SDL_FRAMEWORK_SECURITY 1)
+ set(SDL_FRAMEWORK_USERNOTIFICATIONS 1)
+ endif()
+
if(SDL_POWER)
if (IOS OR TVOS OR VISIONOS OR WATCHOS)
sdl_glob_sources(
@@ -2680,6 +2702,10 @@ elseif(APPLE)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/cocoa/*.m")
set(HAVE_SDL_FILESYSTEM TRUE)
+ set(SDL_NOTIFICATION_COCOA 1)
+ sdl_glob_sources("${SDL3_SOURCE_DIR}/src/notification/cocoa/*.m")
+ set(HAVE_SDL_NOTIFICATION TRUE)
+
# TODO: SDL_STORAGE_ICLOUD
set(SDL_STORAGE_GENERIC 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c")
@@ -2842,12 +2868,19 @@ elseif(APPLE)
if(SDL_FRAMEWORK_METAL)
sdl_link_dependency(metal LIBS "$<LINK_LIBRARY:FRAMEWORK,Metal>" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,Metal")
endif()
+ if(SDL_FRAMEWORK_USERNOTIFICATIONS)
+ find_library(USERNOTIFICATIONS UserNotifications)
+ sdl_link_dependency(usernotifications LIBS "$<LINK_LIBRARY:FRAMEWORK,UserNotifications>" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,UserNotifications")
+ endif()
if(SDL_FRAMEWORK_OPENGLES)
sdl_link_dependency(opengles LIBS "$<LINK_LIBRARY:FRAMEWORK,OpenGLES>" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,OpenGLES")
endif()
if(SDL_FRAMEWORK_QUARTZCORE)
sdl_link_dependency(quartz_core LIBS "$<LINK_LIBRARY:FRAMEWORK,QuartzCore>" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,QuartzCore")
endif()
+ if(SDL_FRAMEWORK_SECURITY)
+ sdl_link_dependency(quartz_core LIBS "$<LINK_LIBRARY:FRAMEWORK,Security>" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,Security")
+ endif()
if(SDL_FRAMEWORK_UIKIT)
sdl_link_dependency(ui_kit LIBS "$<LINK_LIBRARY:FRAMEWORK,UIKit>" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,UIKit")
endif()
@@ -3754,6 +3787,12 @@ if(NOT HAVE_CAMERA)
"${SDL3_SOURCE_DIR}/src/camera/dummy/*.h"
)
endif()
+if(NOT HAVE_SDL_NOTIFICATION)
+ sdl_glob_sources(
+ "${SDL3_SOURCE_DIR}/src/notification/dummy/*.c"
+ "${SDL3_SOURCE_DIR}/src/notification/dummy/*.h"
+ )
+endif()
# We always need to have threads and timers around
if(NOT HAVE_SDL_THREADS)
diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index 4ecdb0fb457f2..d09e24c5d07a6 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -452,6 +452,7 @@
<ClInclude Include="..\..\src\events\SDL_keyboard_c.h" />
<ClInclude Include="..\..\src\events\SDL_keymap_c.h" />
<ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
+ <ClInclude Include="..\..\src\events\SDL_notificationevents_c.h" />
<ClInclude Include="..\..\src\events\SDL_touch_c.h" />
<ClInclude Include="..\..\src\events\SDL_windowevents_c.h" />
<ClInclude Include="..\..\src\filesystem\SDL_sysfilesystem.h" />
@@ -486,6 +487,7 @@
<ClInclude Include="..\..\src\locale\SDL_syslocale.h" />
<ClInclude Include="..\..\src\main\SDL_main_callbacks.h" />
<ClInclude Include="..\..\src\misc\SDL_sysurl.h" />
+ <ClInclude Include="..\..\src\notification\SDL_notification_c.h" />
<ClInclude Include="..\..\src\power\SDL_syspower.h" />
<ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h" />
<ClInclude Include="..\..\src\render\direct3d12\SDL_render_d3d12_xbox.h" />
@@ -544,6 +546,7 @@
<ClCompile Include="..\..\src\camera\SDL_camera.c" />
<ClCompile Include="..\..\src\dialog\SDL_dialog.c" />
<ClCompile Include="..\..\src\dialog\SDL_dialog_utils.c" />
+ <ClCompile Include="..\..\src\events\SDL_notificationevents.c" />
<ClCompile Include="..\..\src\filesystem\SDL_filesystem.c" />
<ClCompile Include="..\..\src\filesystem\windows\SDL_sysfsops.c" />
<ClCompile Include="..\..\src\io\generic\SDL_asyncio_generic.c" />
@@ -553,6 +556,8 @@
<ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c" />
<ClCompile Include="..\..\src\main\SDL_main_callbacks.c" />
<ClCompile Include="..\..\src\main\SDL_runapp.c" />
+ <ClCompile Include="..\..\src\notification\dummy\SDL_dummynotification.c" />
+ <ClCompile Include="..\..\src\notification\SDL_notification.c" />
<ClCompile Include="..\..\src\SDL_guid.c" />
<ClInclude Include="..\..\src\SDL_hashtable.h" />
<ClInclude Include="..\..\src\SDL_hints_c.h" />
diff --git a/include/SDL3/SDL.h b/include/SDL3/SDL.h
index 7442f0bf5e266..8fc6d5ca87b3a 100644
--- a/include/SDL3/SDL.h
+++ b/include/SDL3/SDL.h
@@ -66,6 +66,7 @@
#include <SDL3/SDL_misc.h>
#include <SDL3/SDL_mouse.h>
#include <SDL3/SDL_mutex.h>
+#include <SDL3/SDL_notification.h>
#include <SDL3/SDL_pen.h>
#include <SDL3/SDL_pixels.h>
#include <SDL3/SDL_platform.h>
diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h
index 0a3827571f342..bdb1128dedf79 100644
--- a/include/SDL3/SDL_events.h
+++ b/include/SDL3/SDL_events.h
@@ -61,6 +61,7 @@
#include <SDL3/SDL_keyboard.h>
#include <SDL3/SDL_keycode.h>
#include <SDL3/SDL_mouse.h>
+#include <SDL3/SDL_notification.h>
#include <SDL3/SDL_pen.h>
#include <SDL3/SDL_power.h>
#include <SDL3/SDL_sensor.h>
@@ -262,6 +263,9 @@ typedef enum SDL_EventType
SDL_EVENT_CAMERA_DEVICE_APPROVED, /**< A camera device has been approved for use by the user. */
SDL_EVENT_CAMERA_DEVICE_DENIED, /**< A camera device has been denied for use by the user. */
+ /* Notification events */
+ SDL_EVENT_NOTIFICATION_ACTION_INVOKED = 0x1500, /**< A user response to a system notification was received. */
+
/* Render events */
SDL_EVENT_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset and their contents need to be updated */
SDL_EVENT_RENDER_DEVICE_RESET, /**< The device has been reset and all textures need to be recreated */
@@ -764,6 +768,23 @@ typedef struct SDL_CameraDeviceEvent
SDL_CameraID which; /**< SDL_CameraID for the device being added or removed or changing */
} SDL_CameraDeviceEvent;
+/**
+ * Notification dialog event structure (event.notification.*)
+ *
+ * An `action_id` value of 'default' for an SDL_EVENT_NOTIFICATION_ACTION_INVOKED
+ * event indicates that the notification was interacted with without selecting a
+ * specific action (e.g. the body of the notification was clicked on).
+ *
+ * \since This struct is available since SDL 3.6.0.
+ */
+typedef struct SDL_NotificationEvent
+{
+ SDL_EventType type; /**< SDL_EVENT_NOTIFICATION_ACTION_INVOKED */
+ Uint32 reserved;
+ Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
+ SDL_NotificationID which; /**< The ID of the notification that generated this event. */
+ const char *action_id; /**< The identifier string of the action invoked in the notification dialog. */
+} SDL_NotificationEvent;
/**
* Renderer event structure (event.render.*)
@@ -1075,6 +1096,7 @@ typedef union SDL_Event
SDL_RenderEvent render; /**< Render event data */
SDL_DropEvent drop; /**< Drag and drop event data */
SDL_ClipboardEvent clipboard; /**< Clipboard event data */
+ SDL_NotificationEvent notification; /**< Notification event data */
/* This is necessary for ABI compatibility between Visual C++ and GCC.
Visual C++ will respect the push pack pragma and use 52 bytes (size of
diff --git a/include/SDL3/SDL_notification.h b/include/SDL3/SDL_notification.h
new file mode 100644
index 0000000000000..a18e44fbc0fa3
--- /dev/null
+++ b/include/SDL3/SDL_notification.h
@@ -0,0 +1,254 @@
+/*
+ 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.
+*/
+
+/**
+ * # CategoryNotifications
+ *
+ * Notifications are temporary popup dialogs that passively present
+ * information to the user, or prompt user action. They are managed
+ * and presented by the system, and can present simple options for
+ * user feedback, usually in the form of buttons.
+ *
+ * The capabilities of notifications, and how they are displayed,
+ * vary between systems, but they generally allow for a title,
+ * message body, an associated image, and buttons to allow the user
+ * to provide feedback.
+ *
+ * How notifications are presented and handled are subject to system
+ * policy, and it should not be assumed that showing a notification
+ * means that the user will see it immediately, if at all. The
+ * user may disable notifications for certain applications, they may
+ * be suppressed based on the current activity, and most systems
+ * provide a "do not disturb" mode that universally silences
+ * notifications when activated.
+ *
+ * There is both a customizable function `SDL_ShowNotificationWithProperties()`
+ * that offers many options for what is displayed, and also a much-simplified
+ * version `SDL_ShowSimpleNotification()`, which simply takes a header (required),
+ * body (optional), and image (optional).
+ */
+
+#ifndef SDL_notification_h_
+#define SDL_notification_h_
+
+#include <SDL3/SDL_properties.h>
+#include <SDL3/SDL_stdinc.h>
+#include <SDL3/SDL_surface.h>
+
+#include <SDL3/SDL_begin_code.h>
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The path to an image to be used as the header icon for system notifications on
+ * some platforms. This is required on:
+ * - Windows
+ * - *nix when not running in a container, and no .desktop entry is available
+ *
+ * Image types supported depend on the platform, but .png generally offers the best
+ * compatability.
+ *
+ * On *nix platforms, this can also be the name of a system icon, as specified by
+ * the Icon Naming Specification.
+ *
+ * Can be set before calling SDL_ShowNotification() or SDL_ShowSimpleNotification()
+ * for the first time.
+ *
+ * \since This macro is available since SDL 3.6.0.
+ */
+#define SDL_PROP_GLOBAL_NOTIFICATION_HEADER_ICON_STRING "SDL.notification.header_icon"
+
+typedef Uint32 SDL_NotificationID; /**< The identifier for a system notification. */
+
+typedef enum SDL_NotificationPriority
+{
+ SDL_NOTIFICATION_PRIORITY_LOW = -1, /**< Lowest priority. */
+ SDL_NOTIFICATION_PRIORITY_NORMAL = 0, /**< Normal/medium priority. */
+ SDL_NOTIFICATION_PRIORITY_HIGH = 1, /**< High/important priority. */
+ SDL_NOTIFICATION_PRIORITY_CRITICAL = 2 /**< Highest/critical priority. Note that this may override any "Do Not Disturb" settings and wake the screen. */
+} SDL_NotificationPriority;
+
+typedef enum SDL_NotificationActionType
+{
+ SDL_NOTIFICATION_ACTION_TYPE_BUTTON = 1 /**< Adds a button to the notification that generates feedback when activated. */
+} SDL_NotificationActionType;
+
+/**
+ * Notification structure describing actions that can be used to allow users
+ * to interact with notification dialogs. Exactly How they are presented depends
+ * on the platform and implementation.
+ *
+ * User interactions with a notification are reported via events with the type
+ * SDL_EVENT_NOTIFICATION_ACTION_INVOKED.
+ *
+ * Action types:
+ * - button: A button with a localized text label, which generates feedback when activated.
+ *
+ * \sa SDL_NotificationEvent
+ * \sa SDL_NotificationActionType
+ */
+typedef union SDL_NotificationAction
+{
+ SDL_NotificationActionType type;
+
+ struct
+ {
+ SDL_NotificationActionType type; /**< SDL_NOTIFICATION_ACTION_TYPE_BUTTON */
+ const char *action_id; /**< The identifier string for the button. 'default' is a reserved identifier and must not be used. */
+ const char *action_label; /**< The localized label for the button associated with the action, in UTF-8 encoding. */
+ } button;
+
+ Uint8 padding[128];
+} SDL_NotificationAction;
+
+#define SDL_PROP_NOTIFICATION_ACTIONS_POINTER "SDL.notification.actions"
+#define SDL_PROP_NOTIFICATION_ACTION_COUNT_NUMBER "SDL.notification.action_count"
+#define SDL_PROP_NOTIFICATION_IMAGE_POINTER "SDL.notification.image"
+#define SDL_PROP_NOTIFICATION_MESSAGE_STRING "SDL.notification.message"
+#define SDL_PROP_NOTIFICATION_PRIORITY_NUMBER "SDL.notification.priority"
+#define SDL_PROP_NOTIFICATION_REPLACES_NUMBER "SDL.notification.replaces"
+#define SDL_PROP_NOTIFICATION_SOUND_STRING "SDL.notification.sound"
+#define SDL_PROP_NOTIFICATION_TRANSIENT_BOOLEAN "SDL.notification.transient"
+#define SDL_PROP_NOTIFICATION_TITLE_STRING "SDL.notification.title"
+
+/**
+ * Requests permission from the system to display notifications. A return value of `true`
+ * only means that the system supports notifications, and that the request for permission
+ * was successfully issued. It does not reflect any user settings to allow or deny
+ * notifications.
+ *
+ * \returns True on success or false on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.6.0
+ *
+ * \sa SDL_ShowNotification
+ * \sa SDL_ShowNotificationWithProperties
+ * \sa SDL_NotificationAction
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_RequestNotificationPermission(void);
+
+/**
+ * Show a system notification.
+ *
+ * System notifications are small, asynchronous popup windows that notify the user
+ * of some information. How they are displayed is system dependent.
+ *
+ * These are the supported properties:
+ *
+ * - `SDL_PROP_NOTIFICATION_TITLE_STRING`: the title of the notification, in
+ * UTF-8 encoding. This property is required.
+ * - `SDL_PROP_NOTIFICATION_ACTIONS_POINTER`: An array of pointers to `SDL_NotificationAction`
+ * structs that will add actions to the notification, usually in the form of buttons or menu
+ * items. Note that systems may have a limit on the maximum number of actions that a
+ * notification can have.
+ * - `SDL_PROP_NOTIFICATIONS_ACTION_COUNT_NUMBER`: the number of actions in the array of actions,
+ * if it exists.
+ * - `SDL_PROP_NOTIFICATION_IMAGE_POINTER`: a pointer to an `SDL_Surface` containing
+ * an image that will be attached to the notification. In most cases, the image is displayed
+ * in the form of a large icon or thumbnail alongside the message body. Notifications on Apple
+ * platforms can be expanded to show a larger format image.
+ * - `SDL_PROP_NOTIFICATION_MESSAGE_STRING`: the message body of the notification,
+ * in UTF-8 encoding.
+ * - `SDL_PROP_NOTIFICATION_PRIORITY_NUMBER`: an `SDL_NotificationPriority` value representing
+ * the notification priority.
+ * - `SDL_PROP_NOTIFICATION_REPLACES_NUMBER`: the `SDL_NotificationID` of a previously
+ * shown notification that this notification should replace.
+ * - `SDL_PROP_NOTIFICATION_SOUND_STRING`: sets a sound to play when the notification is shown.
+ * This can have the value "default", to play the system default notification sound, "silent",
+ * to play no sound, or contain the path to a file with a custom sound. The paths and formats
+ * that can be used for custom sounds are system-specific, and can have some restrictions,
+ * depending on the platform:
+ * - Apple platforms require that the sound file is contained within the app bundle. Supported
+ * formats are: Linear PCM, MA4 (IMA/ADPCM), uLaw, or aLaw, in an .aiff, .wav, or .caf file.
+ * - Windows can only play custom notification sounds when the app is packaged inside an MSIX
+ * installer. Playback from arbitrary file paths is not supported. Supported formats are:
+ * .aac, .flac, .m4a, .mp3, .wav, and .wma.
+ * - Unix platforms can generally load sounds from any arbitrary path, as long as the read
+ * permissions are correct. Supported formats are: ogg/opus, ogg/vorbis, and wav/pcm.
+ * If this property is not set, the system default sound will be used.
+ * - `SDL_PROP_NOTIFICATION_TRANSIENT_BOOLEAN`: true if the notification should not persist
+ * in the system notification center after initially being shown.
+ *
+ * Not all properties are supported by all platforms.
+ *
+ * Notifications are available on:
+ * - Windows 10 or higher
+ * - macOS 10.14 or higher
+ * - iOS 11 or higher
+ * - *nix platforms that support the org.freedesktop.Notifications, or
+ * org.freedesktop.portal.Notification interfaces
+ *
+ * \param props the properties to be used when creating this notification.
+ * \returns A non-zero SDL_NotificationID on success or 0 on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.6.0
+ *
+ * \sa SDL_ShowNotification
+ * \sa SDL_NotificationAction
+ * \sa SDL_NotificationPriority
+ * \sa SDL_NotificationEvent
+ */
+extern SDL_DECLSPEC SDL_NotificationID SDLCALL SDL_ShowNotificationWithProperties(SDL_PropertiesID props);
+
+/**
+ * Show a system notification with normal priority.
+ *
+ * \param title UTF-8 title text, required.
+ * \param message UTF-8 message text, may be NULL.
+ * \param image The image associated with this notification, may be NULL.
+ * \param actions An array of actions to attach to the notification, may be NULL.
+ * \param num_actions The number of actions in the actions array.
+ * \returns A non-zero SDL_NotificationID on success or 0 on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.6.0
+ *
+ * \sa SDL_ShowNotificationWithProperties
+ * \sa SDL_NotificationAction
+ * \sa SDL_NotificationEvent
+ */
+extern SDL_DECLSPEC SDL_NotificationID SDLCALL SDL_ShowNotification(const char *title, const char *message, SDL_Surface *image, SDL_NotificationAction *actions, int num_actions);
+
+/**
+ * Remove a notification.
+ *
+ * \param notification the ID of the notification to remove.
+ * \returns True on success or false on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.6.0
+ *
+ * \sa SDL_ShowNotificationWithProperties
+ * \sa SDL_ShowNotification
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_RemoveNotification(SDL_NotificationID notification);
+
+// Ends C function definitions when using C++
+#ifdef __cplusplus
+}
+#endif
+#include <SDL3/SDL_close_code.h>
+
+#endif // SDL_notification_h_
diff --git a/src/SDL.c b/src/SDL.c
index 61d14bbff7b7b..ab814bd318169 100644
--- a/src/SDL.c
+++ b/src/SDL.c
@@ -44,9 +44,12 @@
#include "camera/SDL_camera_c.h"
#include "cpuinfo/SDL_cpuinfo_c.h"
#include "events/SDL_events_c.h"
+#include "filesystem/SDL_filesystem_c.h"
#include "haptic/SDL_haptic_c.h"
+#include "io/SDL_asyncio_c.h"
#include "joystick/SDL_gamepad_c.h"
#include "joystick/SDL_joystick_c.h"
+#include "notification/SDL_notification_c.h"
#include "render/SDL_sysrender.h"
#include "sensor/SDL_sensor_c.h"
#include "stdlib/SDL_getenv_c.h"
@@ -55,8 +58,6 @@
#include "video/SDL_pixels_c.h"
#include "video/SDL_surface_c.h"
#include "video/SDL_video_c.h"
-#include "filesystem/SDL_filesystem_c.h"
-#include "io/SDL_asyncio_c.h"
#ifdef SDL_PLATFORM_ANDROID
#include "core/android/SDL_android.h"
#endif
@@ -710,6 +711,7 @@ void SDL_Quit(void)
#endif
SDL_QuitSubSystem(SDL_ALL_SUBSYSTEM_FLAGS);
SDL_CleanupTrays();
+ SDL_CleanupNotifications();
#ifdef SDL_USE_LIBDBUS
SDL_DBus_Quit();
diff --git a/src/dynapi/SDL_dynapi.exports b/src/dynapi/SDL_dynapi.exports
index b8bf43232587b..05af5c7b89ccd 100644
--- a/src/dynapi/SDL_dynapi.exports
+++ b/src/dynapi/SDL_dynapi.exports
@@ -1294,3 +1294,7 @@ _SDL_aligned_alloc_zero
_SDL_wcstoul
_SDL_wcstoll
_SDL_wcstoull
+_SDL_RequestNotificationPermission
+_SDL_ShowNotificationWithProperties
+_SDL_ShowNotification
+_SDL_RemoveNotification
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 92ccf55be5a17..725f62128234c 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -1295,6 +1295,10 @@ SDL3_0.0.0 {
SDL_wcstoul;
SDL_wcstoll;
SDL_wcstoull;
+ SDL_RequestNotificationPermission;
+ SDL_ShowNotificationWithProperties;
+ SDL_ShowNotification;
+ SDL_RemoveNotification;
# extra symbols go here (don't modify this line)
local: *;
};
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 47841e3862c40..0c25bff621b6d 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -1321,3 +1321,7 @@
#define SDL_wcstoul SDL_wcstoul_REAL
#define SDL_wcstoll SDL_wcstoll_REAL
#define SDL_wcstoull SDL_wcstoull_REAL
+#define SDL_RequestNotificationPermission SDL_RequestNotificationPermission_REAL
+#define SDL_ShowNotificationWithProperties SDL_ShowNotificationWithProperties_REAL
+#define SDL_ShowNotification SDL_ShowNotification_REAL
+#define SDL_RemoveNotification SDL_RemoveNotification_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 1ad817ec0db4c..69c229401ca80 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1329,3 +1329,7 @@ SDL_DYNAPI_PROC(void*,SDL_aligned_alloc_zero,(size_t a,size_t b),(a,b),return)
SDL_DYNAPI_PROC(unsigned long,SDL_wcstoul,(const wchar_t *a,wchar_t **b,int c),(a,b,c),return)
SDL_DYNAPI_PROC(long long,SDL_wcstoll,(const wchar_t *a,wchar_t **b,int c),(a,b,c),return)
SDL_DYNAPI_PROC(unsigned long long,SDL_wcstoull,(const wchar_t *a,wchar_t **b,int c),(a,b,c),return)
+SDL_DYNAPI_PROC(bool,SDL_RequestNotificationPermission,(void),(),return)
+SDL_DYNAPI_PROC(SDL_NotificationID,SDL_ShowNotificationWithProperties,(SDL_PropertiesID a),(a),return)
+SDL_DYNAPI_PROC(SDL_NotificationID,SDL_ShowNotification,(const char *a,const char *b,SDL_Surface *c,SDL_NotificationAction *d,int e),(a,b,c,d,e),return)
+SDL_DYNAPI_PROC(bool,SDL_RemoveNotification,(SDL_NotificationID a),(a),return)
diff --git a/src/events/SDL_categories.c b/src/events/SDL_categories.c
index 9d7722923b761..9be9ec8cc1e81 100644
--- a/src/events/SDL_categories.c
+++ b/src/events/SDL_categories.c
@@ -185,6 +185,9 @@ SDL_EventCategory SDL_GetEventCategory(Uint32 type)
case SDL_EVENT_CAMERA_DEVICE_APPROVED:
case SDL_EVENT_CAMERA_DEVICE_DENIED:
return SDL_EVENTCATEGORY_CDEVICE;
+
+ case SDL_EVENT_NOTIFICATION_ACTION_INVOKED:
+ return SDL_EVENTCATEGORY_NOTIFICATION;
}
}
diff --git a/src/events/SDL_categories_c.h b/src/events/SDL_categories_c.h
index a3762746d53b8..59cef81eb5ad8 100644
--- a/src/events/SDL_categories_c.h
+++ b/src/events/SDL_categories_c.h
@@ -64,6 +64,7 @@ typedef enum SDL_EventCategory
SDL_EVENTCATEGORY_DROP,
SDL_EVENTCATEGORY_CLIPBOARD,
SDL_EVENTCATEGORY_RENDER,
+ SDL_EVENTCATEGORY_NOTIFICATION,
} SDL_EventCategory;
extern SDL_EventCategory SDL_GetEventCategory(Uint32 type);
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index a0ed2fc3bfb36..f682775d5e911 100644
--- a/src/events/SDL_events.c
+++ b/src/events/SDL_events.c
@@ -911,6 +911,11 @@ int SDL_GetEventDescription(const SDL_Event *event, char *buf, int buflen)
break;
#undef PRINT_CAMERADEV_EVENT
+ SDL_EVENT_CASE(SDL_EVENT_NOTIFICATION_ACTION_INVOKED)
+ (void)SDL_snprintf(details, sizeof(details), " (timestamp=%" SDL_PRIu64 " which=%d button_id='%s')",
+ event->notification.timestamp, (uint)event->notification.which, event->notification.action_id);
+ break;
+
SDL_EVENT_CASE(SDL_EVENT_SENSOR_UPDATE)
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%" SDL_PRIu64 " which=%d data[0]=%f data[1]=%f data[2]=%f data[3]=%f data[4]=%f data[5]=%f)",
event->sensor.timestamp, (int)event->sensor.which,
diff --git a/src/events/SDL_notificationevents.c b/src/events/SDL_notificationevents.c
new file mode 100644
index 0000000000000..1eee1c32b1e14
--- /dev/null
+++ b/src/events/SDL_notificationevents.c
@@ -0,0 +1,41 @@
+
+/*
+ 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_events_c.h"
+#include "SDL_notificationevents_c.h"
+
+bool SDL_SendNotificationAction(SDL_NotificationID notification_id, const char *action_id)
+{
+ if (SDL_EventEnabled(SDL_EVENT_NOTIFICATION_ACTION_INVOKED)) {
+ SDL_Event event;
+ event.type = SDL_EVENT_NOTIFICATION_ACTION_INVOKED;
+
+ SDL_NotificationEvent *nevent = &event.notification;
+ nevent->timestamp = 0;
+ nevent->which = notification_id;
+ nevent->action_id = SDL_CreateTemporaryString(action_id);
+ return SDL_PushEvent(&event);
+ }
+
+ return false;
+}
diff --git a/src/events/SDL_notificationevents_c.h b/src/events/SDL_notificationevents_c.h
new file mode 100644
index 0000000000000..7528809cce45e
--- /dev/null
+++ b/src/events/SDL_notificationevents_c.h
@@ -0,0 +1,30 @@
+
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', with
(Patch may be truncated, please check the link at the top of this post.)