diff --git a/include/SDL3/SDL.h b/include/SDL3/SDL.h index cb51cd7ac2f8a4..337af08909d62a 100644 --- a/include/SDL3/SDL.h +++ b/include/SDL3/SDL.h @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include diff --git a/include/SDL3/SDL_notification.h b/include/SDL3/SDL_notification.h new file mode 100644 index 00000000000000..03c221a35a2350 --- /dev/null +++ b/include/SDL3/SDL_notification.h @@ -0,0 +1,97 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + 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. +*/ + +/** + * \file SDL_notification.h + * + * \brief Header file for notification API + */ + +#ifndef SDL_notification_h_ +#define SDL_notification_h_ + +#include + +#include +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief SDL_Notification flags. + */ +typedef enum +{ + SDL_NOTIFICATION_PRIORITY_LOW = 0x00000010, /**< lowest */ + SDL_NOTIFICATION_PRIORITY_NORMAL = 0x00000020, /**< normal/medium */ + SDL_NOTIFICATION_PRIORITY_HIGH = 0x00000040 /**< high/important/critical */ +} SDL_NotificationFlags; + +/** + * \brief SDL_Icon flags. + */ +typedef enum +{ + SDL_ICON_TYPE_SINGLE_FILE = 0x00000010, /**< A single icon file. */ + SDL_ICON_TYPE_ICON_DIRECTORY = 0x00000020, /**< A directory containing icons in "heightxwidth.bmp" format. */ + SDL_ICON_TYPE_SURFACE = 0x00000040 /**< Icon inside an SDL surface. */ +} SDL_IconFlags; + +typedef struct +{ + Uint32 flags; + union { + const char *path; + SDL_Surface *surface; + } icon; +} SDL_Icon; + +/** + * \brief Create a simple system notification. + * + * If a window is specified it's icon will be used for the notification, + * and the notifcation will open that window when clicked on supported + * platforms. Alternatively if an icon is specified for the icon this will, + * be used instead. + * + * \param flags Notification/Priority Flags + * \param title UTF-8 title text + * \param message UTF-8 message text + * \param window Window to associate to the notfication. + * \param icon Icon (if different to window icon). + * + * \return 0 on success, -1 on error + * + * \sa SDL_ShowSimpleNotification + */ +extern DECLSPEC int SDLCALL SDL_ShowSimpleNotification(Uint32 flags, const char *title, const char *message, + SDL_Window *window, SDL_Icon *icon); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include + + +#endif /* SDL_notification_h_ */ + diff --git a/src/core/linux/SDL_dbus.c b/src/core/linux/SDL_dbus.c index 16a7c13f94e886..bee1eb7e47be3a 100644 --- a/src/core/linux/SDL_dbus.c +++ b/src/core/linux/SDL_dbus.c @@ -498,4 +498,54 @@ SDL_DBus_ScreensaverInhibit(SDL_bool inhibit) return SDL_TRUE; } + +SDL_bool SDL_DBus_SendNotification(const char *app, const char *title, const char *body) +{ + dbus_bool_t status = 0; + DBusConnection *conn = dbus.session_conn; + DBusMessage *msg = NULL; + Uint32 replace_id = 0; + Sint32 timeout = -1; + const char *icon = ""; + DBusMessageIter iter; + DBusMessageIter sub; + + if (conn == NULL) { + return SDL_FALSE; + } + + msg = dbus.message_new_method_call("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + "Notify"); + if (msg != NULL) { + dbus.message_iter_init_append(msg, &iter); + status = dbus.message_iter_append_basic(&iter, DBUS_TYPE_STRING, &app); + status = dbus.message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &replace_id); + status = dbus.message_iter_append_basic(&iter, DBUS_TYPE_STRING, &icon); + status = dbus.message_iter_append_basic(&iter, DBUS_TYPE_STRING, &title); + status = dbus.message_iter_append_basic(&iter, DBUS_TYPE_STRING, &body); + status = dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub); + status = dbus.message_iter_close_container(&iter, &sub); + status = dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub); + status = dbus.message_iter_close_container(&iter, &sub); + status = dbus.message_iter_append_basic(&iter, DBUS_TYPE_INT32, &timeout); + if (!status) { + SDL_SetError("Error appending to dbus parameters (notification)"); + } else { + status = dbus.connection_send(conn, msg, NULL); + if (!status) { + SDL_SetError("Error sending to dbus (notification)"); + } else { + dbus.connection_flush(conn); + } + } + + dbus.message_unref(msg); + } + + return (status) ? SDL_TRUE : SDL_FALSE; +} + + #endif diff --git a/src/core/linux/SDL_dbus.h b/src/core/linux/SDL_dbus.h index 2a90e0a21c0445..9710abf5c0a1f8 100644 --- a/src/core/linux/SDL_dbus.h +++ b/src/core/linux/SDL_dbus.h @@ -93,6 +93,7 @@ extern SDL_bool SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, const c extern void SDL_DBus_ScreensaverTickle(void); extern SDL_bool SDL_DBus_ScreensaverInhibit(SDL_bool inhibit); +extern SDL_bool SDL_DBus_SendNotification(const char *app, const char *title, const char *body); #endif /* HAVE_DBUS_DBUS_H */ diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 4b334c254cb3d5..6e8715d4a4a6e7 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -841,6 +841,7 @@ SDL3_0.0.0 { SDL_GetSystemTheme; SDL_CreatePopupWindow; SDL_GetWindowParent; + SDL_ShowSimpleNotification; # 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 0d4b3c9c7954f2..a64f58258b7e93 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -868,3 +868,4 @@ #define SDL_GetSystemTheme SDL_GetSystemTheme_REAL #define SDL_CreatePopupWindow SDL_CreatePopupWindow_REAL #define SDL_GetWindowParent SDL_GetWindowParent_REAL +#define SDL_ShowSimpleNotification SDL_ShowSimpleNotification_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 8945a153515b48..aa88bd0c3e5b8e 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -913,3 +913,4 @@ SDL_DYNAPI_PROC(int,SDL_GetRenderWindowSize,(SDL_Renderer *a, int *b, int *c),(a SDL_DYNAPI_PROC(SDL_SystemTheme,SDL_GetSystemTheme,(void),(),return) SDL_DYNAPI_PROC(SDL_Window*,SDL_CreatePopupWindow,(SDL_Window *a, int b, int c, int d, int e, Uint32 f),(a,b,c,d,e,f),return) SDL_DYNAPI_PROC(SDL_Window*,SDL_GetWindowParent,(SDL_Window *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_ShowSimpleNotification,(Uint32 a, const char *b, const char *c, SDL_Window *d, SDL_Icon *e),(a,b,c,d,e),return) diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index b256f2744728af..c92b145158bbcf 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -344,6 +344,9 @@ struct SDL_VideoDevice /* Hit-testing */ int (*SetWindowHitTest)(SDL_Window *window, SDL_bool enabled); + /* Notification */ + int (*ShowNotification)(_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid); + /* Tell window that app enabled drag'n'drop events */ void (*AcceptDragAndDrop)(SDL_Window *window, SDL_bool accept); diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index fa3ffe68aca0d9..d47f3e322e7269 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -5043,3 +5043,35 @@ void *SDL_Metal_GetLayer(SDL_MetalView view) return NULL; } } + + +#if defined(__LINUX__) +#include "../core/linux/SDL_dbus.h" +#endif + +static int SDL_PrivateShowNotification(Uint32 flags, const char *title, const char *message, SDL_Window *window, SDL_Icon *icon) +{ + int retval = -1; + +#if SDL_USE_LIBDBUS + /* Currently DBUS is initialized in X11 Video Init and needs to moved out */ + if (retval == -1 && + SDL_DBus_SendNotification(title, title, message) == SDL_TRUE) { + retval = 0; + } +#endif + + if (retval == -1) { + retval = SDL_ShowSimpleMessageBox(0, title, message, window); + } + + return retval; +} + +int SDL_ShowSimpleNotification(Uint32 flags, const char *title, const char *message, SDL_Window *window, SDL_Icon *icon) +{ + /* This is to allow expansion of the API later on. + Assuming you would create a notification which could then be updated later via a pointer. + This would be useful for progress notifications etc. */ + return SDL_PrivateShowNotification(flags, title, message, window, icon); +} diff --git a/test/testmessage.c b/test/testmessage.c index f94e2a63ccb14a..03a8a3888ee204 100644 --- a/test/testmessage.c +++ b/test/testmessage.c @@ -200,11 +200,25 @@ int main(int argc, char *argv[]) quit(1); } + + /* Test showing a system notification message with a parent window */ + success = SDL_ShowSimpleNotification(SDL_NOTIFICATION_PRIORITY_NORMAL, + "Simple Notification", + "Hey this window needs attention!", + window, NULL); + if (success == -1) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error Presenting Notification: %s\n", SDL_GetError()); + quit(1); + } + while (SDL_WaitEvent(&event)) { if (event.type == SDL_EVENT_QUIT || event.type == SDL_EVENT_KEY_UP) { break; } } + + + SDL_DestroyWindow(window); } SDL_Quit();