Add notification framework with test and dummy driver

This commit is contained in:
Frank Praznik 2026-04-06 12:42:57 -04:00
parent 513b5d067d
commit 068e405912
22 changed files with 759 additions and 3 deletions

View file

@ -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) \

View file

@ -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)

View file

@ -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" />

View file

@ -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>

View file

@ -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

View file

@ -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_

View file

@ -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();

View file

@ -1294,3 +1294,7 @@ _SDL_aligned_alloc_zero
_SDL_wcstoul
_SDL_wcstoll
_SDL_wcstoull
_SDL_RequestNotificationPermission
_SDL_ShowNotificationWithProperties
_SDL_ShowNotification
_SDL_RemoveNotification

View file

@ -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: *;
};

View file

@ -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

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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,

View file

@ -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;
}

View file

@ -0,0 +1,30 @@
/*
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"
#ifndef SDL_notificationevents_c_h_
#define SDL_notificationevents_c_h_
extern bool SDL_SendNotificationAction(SDL_NotificationID notification_id, const char *action_id);
#endif // SDL_notificationevents_c_h_

View file

@ -0,0 +1,85 @@
/*
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"
#include <SDL3/SDL_properties.h>
SDL_NotificationID SDL_ShowNotificationWithProperties(SDL_PropertiesID props)
{
if (!props) {
SDL_InvalidParamError("props");
return 0;
}
// The title property is required.
CHECK_PARAM (true) {
const char *title = SDL_GetStringProperty(props, SDL_PROP_NOTIFICATION_TITLE_STRING, NULL);
if (!title) {
SDL_SetError("Notifications must have a title");
return 0;
}
}
return SDL_SYS_ShowNotification(props);
}
SDL_NotificationID SDL_ShowNotification(const char *title, const char *message, SDL_Surface *image, SDL_NotificationAction *actions, int num_actions)
{
SDL_NotificationID id = 0;
SDL_PropertiesID props = SDL_CreateProperties();
if (!props) {
return 0;
}
if (title) {
if (!SDL_SetStringProperty(props, SDL_PROP_NOTIFICATION_TITLE_STRING, title)) {
goto cleanup;
}
} else {
SDL_SetError("Notifications must have a title");
goto cleanup;
}
if (message) {
if (!SDL_SetStringProperty(props, SDL_PROP_NOTIFICATION_MESSAGE_STRING, message)) {
goto cleanup;
}
}
if (image) {
if (!SDL_SetPointerProperty(props, SDL_PROP_NOTIFICATION_IMAGE_POINTER, image)) {
goto cleanup;
}
}
if (actions && num_actions) {
if (!SDL_SetPointerProperty(props, SDL_PROP_NOTIFICATION_ACTIONS_POINTER, actions)) {
goto cleanup;
}
if (!SDL_SetNumberProperty(props, SDL_PROP_NOTIFICATION_ACTION_COUNT_NUMBER, num_actions)) {
goto cleanup;
}
}
id = SDL_ShowNotificationWithProperties(props);
cleanup:
SDL_DestroyProperties(props);
return id;
}

View file

@ -0,0 +1,34 @@
/*
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.
*/
#ifndef SDL_NOTIFICATION_C_H
#define SDL_NOTIFICATION_C_H
#include <SDL3/SDL_notification.h>
extern SDL_NotificationID SDL_SYS_ShowNotification(SDL_PropertiesID props);
extern void SDL_CleanupNotifications();
#ifdef SDL_VIDEO_DRIVER_WAYLAND
extern const char *SDL_GetNotificationActivationToken();
#endif
#endif // SDL_NOTIFICATION_C_H

View file

@ -0,0 +1,51 @@
/*
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"
bool SDL_RequestNotificationPermission(void)
{
return SDL_Unsupported();
}
SDL_NotificationID SDL_SYS_ShowNotification(SDL_PropertiesID props)
{
SDL_Unsupported();
return 0;
}
bool SDL_RemoveNotification(SDL_NotificationID notification)
{
return SDL_Unsupported();
}
void SDL_CleanupNotifications()
{
// Nothing to do.
}
#ifdef SDL_VIDEO_DRIVER_WAYLAND
const char *SDL_GetNotificationActivationToken()
{
return NULL;
}
#endif

View file

@ -2012,6 +2012,10 @@ void SDLTest_PrintEvent(const SDL_Event *event)
SDL_Log("SDL EVENT: Camera device %" SDL_PRIu32 " permission denied",
event->cdevice.which);
break;
case SDL_EVENT_NOTIFICATION_ACTION_INVOKED:
SDL_Log("SDL EVENT: Notification action for %" SDL_PRIu32 " button_id=%s",
event->notification.which, event->notification.action_id);
break;
case SDL_EVENT_SENSOR_UPDATE:
SDL_Log("SDL EVENT: Sensor update for %" SDL_PRIu32,
event->sensor.which);

View file

@ -441,6 +441,7 @@ add_sdl_test_executable(testlocale NONINTERACTIVE SOURCES testlocale.c NAME83 lo
add_sdl_test_executable(testlock SOURCES testlock.c NAME83 lock)
add_sdl_test_executable(testrwlock SOURCES testrwlock.c NONINTERACTIVE NONINTERACTIVE_TIMEOUT 20 NAME83 rwlock)
add_sdl_test_executable(testmouse SOURCES testmouse.c NAME83 mouse)
add_sdl_test_executable(testnotification NEEDS_RESOURCES SOURCES testnotification.c NAME83 notify)
add_sdl_test_executable(testoverlay NEEDS_RESOURCES TESTUTILS SOURCES testoverlay.c NAME83 overlay)
add_sdl_test_executable(testplatform NONINTERACTIVE SOURCES testplatform.c NAME83 platform)

160
test/testnotification.c Normal file
View file

@ -0,0 +1,160 @@
/*
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.
*/
/* Simple test of the SDL Notification API */
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_test.h>
/* This enables themed Windows dialogs when building with Visual Studio */
#if defined(SDL_PLATFORM_WINDOWS) && defined(_MSC_VER)
#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
static SDLTest_CommonState *state;
static SDL_Surface *icon;
static SDL_NotificationID last_id;
static SDL_PropertiesID props;
static SDL_NotificationAction actions[] = {
{ .button = { SDL_NOTIFICATION_ACTION_TYPE_BUTTON, "action_1", "OK" } },
{ .button = { SDL_NOTIFICATION_ACTION_TYPE_BUTTON, "action_2", "Cancel" } }
};
static SDL_NotificationAction *action_array[SDL_arraysize(actions) + 1];
static bool transient;
static int sound;
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_SetAppMetadata("SDL Notification Test", "0.0.1", "org.libsdl.testnotification");
/* Initialize test framework */
state = SDLTest_CommonCreateState(argv, 0);
if (!state) {
return SDL_APP_FAILURE;
}
/* Parse commandline */
if (!SDLTest_CommonDefaultArgs(state, argc, argv)) {
return SDL_APP_FAILURE;
}
state->flags |= SDL_INIT_VIDEO;
if (!SDLTest_CommonInit(state)) {
return SDL_APP_FAILURE;
}
SDL_SetStringProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_NOTIFICATION_HEADER_ICON_STRING, "sdl-test_round.png");
props = SDL_CreateProperties();
SDL_SetStringProperty(props, SDL_PROP_NOTIFICATION_TITLE_STRING, "Test Notification");
SDL_SetStringProperty(props, SDL_PROP_NOTIFICATION_MESSAGE_STRING, "Hey, pay attention to me!");
icon = SDL_LoadPNG("sdl-test_round.png");
SDL_SetPointerProperty(props, SDL_PROP_NOTIFICATION_IMAGE_POINTER, icon);
SDL_SetPointerProperty(props, SDL_PROP_NOTIFICATION_ACTIONS_POINTER, actions);
SDL_SetNumberProperty(props, SDL_PROP_NOTIFICATION_ACTION_COUNT_NUMBER, SDL_arraysize(actions));
SDL_RequestNotificationPermission();
return SDL_APP_CONTINUE;
}
static void ShowNotification(bool replace)
{
if (replace) {
SDL_SetNumberProperty(props, SDL_PROP_NOTIFICATION_REPLACES_NUMBER, last_id);
} else {
SDL_SetNumberProperty(props, SDL_PROP_NOTIFICATION_REPLACES_NUMBER, 0);
}
SDL_SetBooleanProperty(props, SDL_PROP_NOTIFICATION_TRANSIENT_BOOLEAN, transient);
// Test showing a system notification message.
const SDL_NotificationID new_id = SDL_ShowNotificationWithProperties(props);
if (new_id) {
SDL_Log("Notification successfully dispatched. ID: %" SDL_PRIu32, new_id);
last_id = new_id;
} else {
SDL_Log("Notification dispatch failed: %s", SDL_GetError());
}
}
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
if (event->type == SDL_EVENT_KEY_DOWN) {
if (event->key.key == SDLK_SPACE) {
ShowNotification((event->key.mod & SDL_KMOD_CTRL) != 0);
} else if (event->key.key == SDLK_H) {
if (last_id) {
SDL_RemoveNotification(last_id);
}
} else if (event->key.key == SDLK_T) {
transient ^= true;
} else if (event->key.key == SDLK_S) {
sound = (sound + 1) % 3;
switch (sound) {
default:
SDL_SetStringProperty(props, SDL_PROP_NOTIFICATION_SOUND_STRING, "default");
break;
case 1:
SDL_SetStringProperty(props, SDL_PROP_NOTIFICATION_SOUND_STRING, "sword.wav");
break;
case 2:
SDL_SetStringProperty(props, SDL_PROP_NOTIFICATION_SOUND_STRING, "silent");
}
}
} else if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
ShowNotification(false);
} else if (event->type == SDL_EVENT_NOTIFICATION_ACTION_INVOKED) {
SDL_Log("User responded to notification %" SDL_PRIu32 " with action \"%s\"", event->notification.which, event->notification.action_id);
// Raise the window of the user clicked "OK".
if (SDL_strcmp(event->notification.action_id, "action_1") == 0) {
SDL_RaiseWindow(state->windows[0]);
} else if (SDL_strcmp(event->notification.action_id, "action_url") == 0) {
SDL_OpenURL("https://www.libsdl.org");
}
}
return SDLTest_CommonEventMainCallbacks(state, event);
}
SDL_AppResult SDL_AppIterate(void *appstate)
{
for (int i = 0; i < state->num_windows; ++i) {
SDL_Renderer *renderer = state->renderers[i];
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
float y = 16.0f;
SDL_RenderDebugText(renderer, 8.f, y, "Click or press space to show a notification");
y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE + 2;
SDL_RenderDebugText(renderer, 8.f, y, "Press 'H' to hide the last notification");
y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE + 2;
SDL_RenderDebugTextFormat(renderer, 8.f, y, "Press 'T' to toggle the transient property (%s)", transient ? "ON" : "OFF");
y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE + 2;
SDL_RenderDebugTextFormat(renderer, 8.f, y, "Press 'S' to toggle the sound property (%s)", sound == 0 ? "Default" : sound == 1 ? "Custom"
: "Silent");
SDL_RenderPresent(renderer);
}
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
SDL_DestroySurface(icon);
SDLTest_CommonQuit(state);
}