From 7570ab106da5888803bb4269144c0a687f849614 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 19 Jan 2025 09:59:12 -0800
Subject: [PATCH] tray: improved error checking
Also clean up any existing trays when the program quits
Fixes https://github.com/libsdl-org/SDL/issues/11893
---
src/SDL.c | 2 +
src/SDL_utils.c | 19 ++++++
src/SDL_utils_c.h | 2 +
src/tray/SDL_tray_utils.c | 57 +++++++++++-----
src/tray/SDL_tray_utils.h | 5 +-
src/tray/cocoa/SDL_tray.m | 130 ++++++++++++++++++++++++++++--------
src/tray/dummy/SDL_tray.c | 32 +++------
src/tray/unix/SDL_tray.c | 81 +++++++++++++++++++---
src/tray/windows/SDL_tray.c | 89 +++++++++++++++++++++---
9 files changed, 334 insertions(+), 83 deletions(-)
diff --git a/src/SDL.c b/src/SDL.c
index 90191d1b0c4d6..e9c9fb1a2353f 100644
--- a/src/SDL.c
+++ b/src/SDL.c
@@ -51,6 +51,7 @@
#include "sensor/SDL_sensor_c.h"
#include "stdlib/SDL_getenv_c.h"
#include "thread/SDL_thread_c.h"
+#include "tray/SDL_tray_utils.h"
#include "video/SDL_pixels_c.h"
#include "video/SDL_surface_c.h"
#include "video/SDL_video_c.h"
@@ -642,6 +643,7 @@ void SDL_Quit(void)
SDL_HelperWindowDestroy();
#endif
SDL_QuitSubSystem(SDL_INIT_EVERYTHING);
+ SDL_CleanupTrays();
#ifdef SDL_USE_LIBDBUS
SDL_DBus_Quit();
diff --git a/src/SDL_utils.c b/src/SDL_utils.c
index d061becb4faea..a877360f6218d 100644
--- a/src/SDL_utils.c
+++ b/src/SDL_utils.c
@@ -178,6 +178,22 @@ bool SDL_ObjectValid(void *object, SDL_ObjectType type)
return (((SDL_ObjectType)(uintptr_t)object_type) == type);
}
+int SDL_GetObjects(SDL_ObjectType type, void **objects, int count)
+{
+ const void *object, *object_type;
+ void *iter = NULL;
+ int num_objects = 0;
+ while (SDL_IterateHashTable(SDL_objects, &object, &object_type, &iter)) {
+ if ((SDL_ObjectType)(uintptr_t)object_type == type) {
+ if (num_objects < count) {
+ objects[num_objects] = (void *)object;
+ }
+ ++num_objects;
+ }
+ }
+ return num_objects;
+}
+
void SDL_SetObjectsInvalid(void)
{
if (SDL_ShouldQuit(&SDL_objects_init)) {
@@ -217,6 +233,9 @@ void SDL_SetObjectsInvalid(void)
case SDL_OBJECT_TYPE_THREAD:
type = "thread";
break;
+ case SDL_OBJECT_TYPE_TRAY:
+ type = "SDL_Tray";
+ break;
default:
type = "unknown object";
break;
diff --git a/src/SDL_utils_c.h b/src/SDL_utils_c.h
index 68dfcec327cba..557dad49a59d0 100644
--- a/src/SDL_utils_c.h
+++ b/src/SDL_utils_c.h
@@ -61,12 +61,14 @@ typedef enum
SDL_OBJECT_TYPE_HIDAPI_DEVICE,
SDL_OBJECT_TYPE_HIDAPI_JOYSTICK,
SDL_OBJECT_TYPE_THREAD,
+ SDL_OBJECT_TYPE_TRAY,
} SDL_ObjectType;
extern Uint32 SDL_GetNextObjectID(void);
extern void SDL_SetObjectValid(void *object, SDL_ObjectType type, bool valid);
extern bool SDL_ObjectValid(void *object, SDL_ObjectType type);
+extern int SDL_GetObjects(SDL_ObjectType type, void **objects, int count);
extern void SDL_SetObjectsInvalid(void);
extern const char *SDL_GetPersistentString(const char *string);
diff --git a/src/tray/SDL_tray_utils.c b/src/tray/SDL_tray_utils.c
index b21d40cd652ea..ce792adc479a7 100644
--- a/src/tray/SDL_tray_utils.c
+++ b/src/tray/SDL_tray_utils.c
@@ -27,38 +27,65 @@
static int active_trays = 0;
-extern void SDL_IncrementTrayCount(void)
+void SDL_RegisterTray(SDL_Tray *tray)
{
- if (++active_trays < 1) {
- SDL_Log("Active tray count corrupted (%d < 1), this is a bug. The app may close or fail to close unexpectedly.", active_trays);
- }
+ SDL_SetObjectValid(tray, SDL_OBJECT_TYPE_TRAY, true);
+
+ ++active_trays;
}
-extern void SDL_DecrementTrayCount(void)
+void SDL_UnregisterTray(SDL_Tray *tray)
{
- int toplevel_count = 0;
- SDL_Window *n;
+ SDL_assert(SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY));
+
+ SDL_SetObjectValid(tray, SDL_OBJECT_TYPE_TRAY, false);
- if (--active_trays < 0) {
- SDL_Log("Active tray count corrupted (%d < 0), this is a bug. The app may close or fail to close unexpectedly.", active_trays);
+ --active_trays;
+ if (active_trays > 0) {
+ return;
}
if (!SDL_GetHintBoolean(SDL_HINT_QUIT_ON_LAST_WINDOW_CLOSE, true)) {
return;
}
- for (n = SDL_GetVideoDevice()->windows; n; n = n->next) {
- if (!n->parent && !(n->flags & SDL_WINDOW_HIDDEN)) {
- ++toplevel_count;
+ int toplevel_count = 0;
+ SDL_Window **windows = SDL_GetWindows(NULL);
+ if (windows) {
+ for (int i = 0; windows[i]; ++i) {
+ SDL_Window *window = windows[i];
+ if (!window->parent && !(window->flags & SDL_WINDOW_HIDDEN)) {
+ ++toplevel_count;
+ }
}
+ SDL_free(windows);
}
- if (toplevel_count < 1) {
+ if (toplevel_count == 0) {
SDL_SendQuit();
}
}
-extern bool SDL_HasNoActiveTrays(void)
+void SDL_CleanupTrays(void)
+{
+ if (active_trays == 0) {
+ return;
+ }
+
+ void **trays = (void **)SDL_malloc(active_trays * sizeof(*trays));
+ if (!trays) {
+ return;
+ }
+
+ int count = SDL_GetObjects(SDL_OBJECT_TYPE_TRAY, trays, active_trays);
+ SDL_assert(count == active_trays);
+ for (int i = 0; i < count; ++i) {
+ SDL_DestroyTray((SDL_Tray *)trays[i]);
+ }
+ SDL_free(trays);
+}
+
+bool SDL_HasNoActiveTrays(void)
{
- return active_trays < 1;
+ return active_trays == 0;
}
diff --git a/src/tray/SDL_tray_utils.h b/src/tray/SDL_tray_utils.h
index f8f7a7058b963..8dc2249d2231a 100644
--- a/src/tray/SDL_tray_utils.h
+++ b/src/tray/SDL_tray_utils.h
@@ -20,6 +20,7 @@
*/
#include "SDL_internal.h"
-extern void SDL_IncrementTrayCount(void);
-extern void SDL_DecrementTrayCount(void);
+extern void SDL_RegisterTray(SDL_Tray *tray);
+extern void SDL_UnregisterTray(SDL_Tray *tray);
+extern void SDL_CleanupTrays(void);
extern bool SDL_HasNoActiveTrays(void);
diff --git a/src/tray/cocoa/SDL_tray.m b/src/tray/cocoa/SDL_tray.m
index 66f99a2b28a5a..15de54b24e9a6 100644
--- a/src/tray/cocoa/SDL_tray.m
+++ b/src/tray/cocoa/SDL_tray.m
@@ -80,8 +80,16 @@ static void DestroySDLMenu(SDL_TrayMenu *menu)
SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
{
+ if (icon) {
+ icon = SDL_ConvertSurface(icon, SDL_PIXELFORMAT_RGBA32);
+ if (!icon) {
+ return NULL;
+ }
+ }
+
SDL_Tray *tray = (SDL_Tray *)SDL_calloc(1, sizeof(*tray));
if (!tray) {
+ SDL_DestroySurface(icon);
return NULL;
}
@@ -97,22 +105,17 @@ static void DestroySDLMenu(SDL_TrayMenu *menu)
}
if (icon) {
- SDL_Surface *iconfmt = SDL_ConvertSurface(icon, SDL_PIXELFORMAT_RGBA32);
- if (!iconfmt) {
- goto skip_putting_an_icon;
- }
-
- NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **)&iconfmt->pixels
- pixelsWide:iconfmt->w
- pixelsHigh:iconfmt->h
+ NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **)&icon->pixels
+ pixelsWide:icon->w
+ pixelsHigh:icon->h
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
- bytesPerRow:iconfmt->pitch
+ bytesPerRow:icon->pitch
bitsPerPixel:32];
- NSImage *iconimg = [[NSImage alloc] initWithSize:NSMakeSize(iconfmt->w, iconfmt->h)];
+ NSImage *iconimg = [[NSImage alloc] initWithSize:NSMakeSize(icon->w, icon->h)];
[iconimg addRepresentation:bitmap];
/* A typical icon size is 22x22 on macOS. Failing to resize the icon
@@ -125,39 +128,42 @@ static void DestroySDLMenu(SDL_TrayMenu *menu)
tray->statusItem.button.image = iconimg22;
- SDL_DestroySurface(iconfmt);
+ SDL_DestroySurface(icon);
}
-skip_putting_an_icon:
- SDL_IncrementTrayCount();
+ SDL_RegisterTray(tray);
return tray;
}
void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon)
{
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
+ return;
+ }
+
if (!icon) {
tray->statusItem.button.image = nil;
return;
}
- SDL_Surface *iconfmt = SDL_ConvertSurface(icon, SDL_PIXELFORMAT_RGBA32);
- if (!iconfmt) {
+ icon = SDL_ConvertSurface(icon, SDL_PIXELFORMAT_RGBA32);
+ if (!icon) {
tray->statusItem.button.image = nil;
return;
}
- NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **)&iconfmt->pixels
- pixelsWide:iconfmt->w
- pixelsHigh:iconfmt->h
+ NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **)&icon->pixels
+ pixelsWide:icon->w
+ pixelsHigh:icon->h
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
- bytesPerRow:iconfmt->pitch
+ bytesPerRow:icon->pitch
bitsPerPixel:32];
- NSImage *iconimg = [[NSImage alloc] initWithSize:NSMakeSize(iconfmt->w, iconfmt->h)];
+ NSImage *iconimg = [[NSImage alloc] initWithSize:NSMakeSize(icon->w, icon->h)];
[iconimg addRepresentation:bitmap];
/* A typical icon size is 22x22 on macOS. Failing to resize the icon
@@ -170,11 +176,15 @@ void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon)
tray->statusItem.button.image = iconimg22;
- SDL_DestroySurface(iconfmt);
+ SDL_DestroySurface(icon);
}
void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip)
{
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
+ return;
+ }
+
if (tooltip) {
tray->statusItem.button.toolTip = [NSString stringWithUTF8String:tooltip];
} else {
@@ -184,6 +194,11 @@ void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip)
SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray)
{
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
+ SDL_InvalidParamError("tray");
+ return NULL;
+ }
+
SDL_TrayMenu *menu = (SDL_TrayMenu *)SDL_calloc(1, sizeof(*menu));
if (!menu) {
return NULL;
@@ -206,11 +221,21 @@ void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip)
SDL_TrayMenu *SDL_GetTrayMenu(SDL_Tray *tray)
{
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
+ SDL_InvalidParamError("tray");
+ return NULL;
+ }
+
return tray->menu;
}
SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ SDL_InvalidParamError("entry");
+ return NULL;
+ }
+
if (entry->submenu) {
SDL_SetError("Tray entry submenu already exists");
return NULL;
@@ -243,11 +268,21 @@ void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip)
SDL_TrayMenu *SDL_GetTraySubmenu(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ SDL_InvalidParamError("entry");
+ return NULL;
+ }
+
return entry->submenu;
}
const SDL_TrayEntry **SDL_GetTrayEntries(SDL_TrayMenu *menu, int *size)
{
+ if (!menu) {
+ SDL_InvalidParamError("menu");
+ return NULL;
+ }
+
if (size) {
*size = menu->nEntries;
}
@@ -293,6 +328,11 @@ void SDL_RemoveTrayEntry(SDL_TrayEntry *entry)
SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags)
{
+ if (!menu) {
+ SDL_InvalidParamError("menu");
+ return NULL;
+ }
+
if (pos < -1 || pos > menu->nEntries) {
SDL_InvalidParamError("pos");
return NULL;
@@ -347,28 +387,44 @@ void SDL_RemoveTrayEntry(SDL_TrayEntry *entry)
void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label)
{
+ if (!entry) {
+ return;
+ }
+
[entry->nsitem setTitle:[NSString stringWithUTF8String:label]];
}
const char *SDL_GetTrayEntryLabel(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ SDL_InvalidParamError("entry");
+ return NULL;
+ }
+
return [[entry->nsitem title] UTF8String];
}
void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked)
{
+ if (!entry) {
+ return;
+ }
+
[entry->nsitem setState:(checked ? NSControlStateValueOn : NSControlStateValueOff)];
}
bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ return false;
+ }
+
return entry->nsitem.state == NSControlStateValueOn;
}
void SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled)
{
- if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
- SDL_SetError("Cannot update check for entry not created with SDL_TRAYENTRY_CHECKBOX");
+ if (!entry || !(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
return;
}
@@ -377,8 +433,7 @@ void SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled)
bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry)
{
- if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
- SDL_SetError("Cannot fetch check for entry not created with SDL_TRAYENTRY_CHECKBOX");
+ if (!entry || !(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
return false;
}
@@ -387,6 +442,10 @@ bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry)
void SDL_SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata)
{
+ if (!entry) {
+ return;
+ }
+
entry->callback = callback;
entry->userdata = userdata;
}
@@ -408,25 +467,42 @@ void SDL_ClickTrayEntry(SDL_TrayEntry *entry)
SDL_TrayMenu *SDL_GetTrayEntryParent(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ SDL_InvalidParamError("entry");
+ return NULL;
+ }
+
return entry->parent;
}
SDL_TrayEntry *SDL_GetTrayMenuParentEntry(SDL_TrayMenu *menu)
{
+ if (!menu) {
+ SDL_InvalidParamError("menu");
+ return NULL;
+ }
+
return menu->parent_entry;
}
SDL_Tray *SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu)
{
+ if (!menu) {
+ SDL_InvalidParamError("menu");
+ return NULL;
+ }
+
return menu->parent_tray;
}
void SDL_DestroyTray(SDL_Tray *tray)
{
- if (!tray) {
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
return;
}
+ SDL_UnregisterTray(tray);
+
[[NSStatusBar systemStatusBar] removeStatusItem:tray->statusItem];
if (tray->menu) {
@@ -434,8 +510,6 @@ void SDL_DestroyTray(SDL_Tray *tray)
}
SDL_free(tray);
-
- SDL_DecrementTrayCount();
}
#endif // SDL_PLATFORM_MACOS
diff --git a/src/tray/dummy/SDL_tray.c b/src/tray/dummy/SDL_tray.c
index 59d7e8a7bb0cf..55a1e645586f4 100644
--- a/src/tray/dummy/SDL_tray.c
+++ b/src/tray/dummy/SDL_tray.c
@@ -33,29 +33,27 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon)
{
- SDL_Unsupported();
}
void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip)
{
- SDL_Unsupported();
}
SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray)
{
- SDL_Unsupported();
+ SDL_InvalidParamError("tray");
return NULL;
}
SDL_TrayMenu *SDL_GetTrayMenu(SDL_Tray *tray)
{
- SDL_Unsupported();
+ SDL_InvalidParamError("tray");
return NULL;
}
SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry)
{
- SDL_Unsupported();
+ SDL_InvalidParamError("entry");
return NULL;
}
@@ -66,57 +64,50 @@ SDL_TrayMenu *SDL_GetTraySubmenu(SDL_TrayEntry *entry)
const SDL_TrayEntry **SDL_GetTrayEntries(SDL_TrayMenu *menu, int *size)
{
- SDL_Unsupported();
+ SDL_InvalidParamError("menu");
return NULL;
}
void SDL_RemoveTrayEntry(SDL_TrayEntry *entry)
{
- SDL_Unsupported();
}
SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags)
{
- SDL_Unsupported();
+ SDL_InvalidParamError("menu");
return NULL;
}
void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label)
{
- SDL_Unsupported();
}
const char *SDL_GetTrayEntryLabel(SDL_TrayEntry *entry)
{
- SDL_Unsupported();
+ SDL_InvalidParamError("entry");
return NULL;
}
void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked)
{
- SDL_Unsupported();
}
bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry)
{
- SDL_Unsupported();
- return false;
+ return SDL_InvalidParamError("entry");
}
void SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled)
{
- SDL_Unsupported();
}
bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry)
{
- SDL_Unsupported();
- return false;
+ return SDL_InvalidParamError("entry");
}
void SDL_SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata)
{
- SDL_Unsupported();
}
void SDL_ClickTrayEntry(SDL_TrayEntry *entry)
@@ -125,25 +116,24 @@ void SDL_ClickTrayEntry(SDL_TrayEntry *entry)
SDL_TrayMenu *SDL_GetTrayEntryParent(SDL_TrayEntry *entry)
{
- SDL_Unsupported();
+ SDL_InvalidParamError("entry");
return NULL;
}
SDL_TrayEntry *SDL_GetTrayMenuParentEntry(SDL_TrayMenu *menu)
{
- SDL_Unsupported();
+ SDL_InvalidParamError("menu");
return NULL;
}
SDL_Tray *SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu)
{
- SDL_Unsupported();
+ SDL_InvalidParamError("menu");
return NULL;
}
void SDL_DestroyTray(SDL_Tray *tray)
{
- SDL_Unsupported();
}
#endif // !SDL_PLATFORM_MACOS
diff --git a/src/tray/unix/SDL_tray.c b/src/tray/unix/SDL_tray.c
index e1bd27c095a02..3d5fafab0c5c5 100644
--- a/src/tray/unix/SDL_tray.c
+++ b/src/tray/unix/SDL_tray.c
@@ -144,7 +144,7 @@ static void quit_gtk(void)
static bool init_gtk(void)
{
-
+ return true;
}
#else
@@ -434,13 +434,17 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
app_indicator_set_status(tray->indicator, APP_INDICATOR_STATUS_ACTIVE);
- SDL_IncrementTrayCount();
+ SDL_RegisterTray(tray);
return tray;
}
void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon)
{
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
+ return;
+ }
+
if (*tray->icon_path) {
SDL_RemovePath(tray->icon_path);
}
@@ -463,6 +467,11 @@ void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip)
SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray)
{
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
+ SDL_InvalidParamError("tray");
+ return NULL;
+ }
+
tray->menu = (SDL_TrayMenu *)SDL_calloc(1, sizeof(*tray->menu));
if (!tray->menu) {
return NULL;
@@ -481,11 +490,21 @@ SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray)
SDL_TrayMenu *SDL_GetTrayMenu(SDL_Tray *tray)
{
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
+ SDL_InvalidParamError("tray");
+ return NULL;
+ }
+
return tray->menu;
}
SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ SDL_InvalidParamError("entry");
+ return NULL;
+ }
+
if (entry->submenu) {
SDL_SetError("Tray entry submenu already exists");
return NULL;
@@ -514,11 +533,21 @@ SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry)
SDL_TrayMenu *SDL_GetTraySubmenu(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ SDL_InvalidParamError("entry");
+ return NULL;
+ }
+
return entry->submenu;
}
const SDL_TrayEntry **SDL_GetTrayEntries(SDL_TrayMenu *menu, int *size)
{
+ if (!menu) {
+ SDL_InvalidParamError("menu");
+ return NULL;
+ }
+
if (size) {
*size = menu->nEntries;
}
@@ -563,6 +592,11 @@ void SDL_RemoveTrayEntry(SDL_TrayEntry *entry)
SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags)
{
+ if (!menu) {
+ SDL_InvalidParamError("menu");
+ return NULL;
+ }
+
if (pos < -1 || pos > menu->nEntries) {
SDL_InvalidParamError("pos");
return NULL;
@@ -625,18 +659,26 @@ SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *la
void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label)
{
+ if (!entry) {
+ return;
+ }
+
gtk_menu_item_set_label(GTK_MENU_ITEM(entry->item), label);
}
const char *SDL_GetTrayEntryLabel(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ SDL_InvalidParamError("entry");
+ return NULL;
+ }
+
return gtk_menu_item_get_label(GTK_MENU_ITEM(entry->item));
}
void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked)
{
- if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
- SDL_SetError("Cannot update check for entry not created with SDL_TRAYENTRY_CHECKBOX");
+ if (!entry || !(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
return;
}
@@ -647,8 +689,7 @@ void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked)
bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry)
{
- if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
- SDL_SetError("Cannot fetch check for entry not created with SDL_TRAYENTRY_CHECKBOX");
+ if (!entry || !(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
return false;
}
@@ -657,16 +698,28 @@ bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry)
void SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled)
{
+ if (!entry) {
+ return;
+ }
+
gtk_widget_set_sensitive(entry->item, enabled);
}
bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ return false;
+ }
+
return gtk_widget_get_sensitive(entry->item);
}
void SDL_SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata)
{
+ if (!entry) {
+ return;
+ }
+
entry->callback = callback;
entry->userdata = userdata;
}
@@ -688,6 +741,11 @@ void SDL_ClickTrayEntry(SDL_TrayEntry *entry)
SDL_TrayMenu *SDL_GetTrayEntryParent(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ SDL_InvalidParamError("entry");
+ return NULL;
+ }
+
return entry->parent;
}
@@ -698,15 +756,22 @@ SDL_TrayEntry *SDL_GetTrayMenuParentEntry(SDL_TrayMenu *menu)
SDL_Tray *SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu)
{
+ if (!menu) {
+ SDL_InvalidParamError("menu");
+ return NULL;
+ }
+
return menu->parent_tray;
}
void SDL_DestroyTray(SDL_Tray *tray)
{
- if (!tray) {
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
return;
}
+ SDL_UnregisterTray(tray);
+
if (tray->menu) {
DestroySDLMenu(tray->menu);
}
@@ -725,8 +790,6 @@ void SDL_DestroyTray(SDL_Tray *tray)
SDL_free(tray);
- SDL_DecrementTrayCount();
-
if (SDL_HasNoActiveTrays()) {
gtk_main_quit();
gtk_thread_active = false;
diff --git a/src/tray/windows/SDL_tray.c b/src/tray/windows/SDL_tray.c
index fe072eb017c0f..dbfbabafa27a6 100644
--- a/src/tray/windows/SDL_tray.c
+++ b/src/tray/windows/SDL_tray.c
@@ -252,13 +252,17 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
SetWindowLongPtr(tray->hwnd, GWLP_USERDATA, (LONG_PTR) tray);
- SDL_IncrementTrayCount();
+ SDL_RegisterTray(tray);
return tray;
}
void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon)
{
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
+ return;
+ }
+
if (tray->icon) {
DestroyIcon(tray->icon);
}
@@ -281,6 +285,10 @@ void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon)
void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip)
{
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
+ return;
+ }
+
if (tooltip) {
wchar_t *tooltipw = WIN_UTF8ToStringW(tooltip);
SDL_wcslcpy(tray->nid.szTip, tooltipw, sizeof(tray->nid.szTip) / sizeof(*tray->nid.szTip));
@@ -294,6 +302,11 @@ void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip)
SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray)
{
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
+ SDL_InvalidParamError("tray");
+ return NULL;
+ }
+
tray->menu = (SDL_TrayMenu *)SDL_calloc(1, sizeof(*tray->menu));
if (!tray->menu) {
@@ -309,13 +322,24 @@ SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray)
SDL_TrayMenu *SDL_GetTrayMenu(SDL_Tray *tray)
{
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
+ SDL_InvalidParamError("tray");
+ return NULL;
+ }
+
return tray->menu;
}
SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ SDL_InvalidParamError("entry");
+ return NULL;
+ }
+
if (!entry->submenu) {
SDL_SetError("Cannot create submenu for entry not created with SDL_TRAYENTRY_SUBMENU");
+ return NULL;
}
return entry->submenu;
@@ -323,11 +347,21 @@ SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry)
SDL_TrayMenu *SDL_GetTraySubmenu(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ SDL_InvalidParamError("entry");
+ return NULL;
+ }
+
return entry->submenu;
}
const SDL_TrayEntry **SDL_GetTrayEntries(SDL_TrayMenu *menu, int *size)
{
+ if (!menu) {
+ SDL_InvalidParamError("menu");
+ return NULL;
+ }
+
if (size) {
*size = menu->nEntries;
}
@@ -376,6 +410,11 @@ void SDL_RemoveTrayEntry(SDL_TrayEntry *entry)
SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags)
{
+ if (!menu) {
+ SDL_InvalidParamError("menu");
+ return NULL;
+ }
+
if (pos < -1 || pos > menu->nEntries) {
SDL_InvalidParamError("pos");
return NULL;
@@ -474,6 +513,10 @@ SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *la
void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label)
{
+ if (!entry) {
+ return;
+ }
+
SDL_snprintf(entry->label_cache, sizeof(entry->label_cache), "%s", label);
wchar_t *label_w = escape_label(label);
@@ -498,13 +541,17 @@ void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label)
const char *SDL_GetTrayEntryLabel(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ SDL_InvalidParamError("entry");
+ return NULL;
+ }
+
return entry->label_cache;
}
void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked)
{
- if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
- SDL_SetError("Can't check/uncheck tray entry not created with SDL_TRAYENTRY_CHECKBOX");
+ if (!entry || !(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
return;
}
@@ -513,8 +560,7 @@ void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked)
bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry)
{
- if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
- SDL_SetError("Can't get check status of tray entry not created with SDL_TRAYENTRY_CHECKBOX");
+ if (!entry || !(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
return false;
}
@@ -529,11 +575,19 @@ bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry)
void SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled)
{
+ if (!entry) {
+ return;
+ }
+
EnableMenuItem(entry->parent->hMenu, (UINT) entry->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : (MF_DISABLED | MF_GRAYED)));
}
bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ return false;
+ }
+
MENUITEMINFOW mii;
mii.cbSize = sizeof(MENUITEMINFOW);
mii.fMask = MIIM_STATE;
@@ -545,6 +599,10 @@ bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry)
void SDL_SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata)
{
+ if (!entry) {
+ return;
+ }
+
entry->callback = callback;
entry->userdata = userdata;
}
@@ -566,25 +624,42 @@ void SDL_ClickTrayEntry(SDL_TrayEntry *entry)
SDL_TrayMenu *SDL_GetTrayEntryParent(SDL_TrayEntry *entry)
{
+ if (!entry) {
+ SDL_InvalidParamError("entry");
+ return NULL;
+ }
+
return entry->parent;
}
SDL_TrayEntry *SDL_GetTrayMenuParentEntry(SDL_TrayMenu *menu)
{
+ if (!menu) {
+ SDL_InvalidParamError("menu");
+ return NULL;
+ }
+
return menu->parent_entry;
}
SDL_Tray *SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu)
{
+ if (!menu) {
+ SDL_InvalidParamError("menu");
+ return NULL;
+ }
+
return menu->parent_tray;
}
void SDL_DestroyTray(SDL_Tray *tray)
{
- if (!tray) {
+ if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
return;
}
+ SDL_UnregisterTray(tray);
+
Shell_NotifyIconW(NIM_DELETE, &tray->nid);
if (tray->menu) {
@@ -600,6 +675,4 @@ void SDL_DestroyTray(SDL_Tray *tray)
}
SDL_free(tray);
-
- SDL_DecrementTrayCount();
}