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

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