From 686e0a8579c1b89fa2f2ea95784a588e06f10f85 Mon Sep 17 00:00:00 2001 From: Sam Lantinga 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 | 52 +++++++++++----- src/tray/SDL_tray_utils.h | 5 +- src/tray/cocoa/SDL_tray.m | 120 +++++++++++++++++++++++++++++------- src/tray/dummy/SDL_tray.c | 32 ++++------ src/tray/unix/SDL_tray.c | 80 ++++++++++++++++++++++-- src/tray/windows/SDL_tray.c | 89 +++++++++++++++++++++++--- 9 files changed, 326 insertions(+), 75 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..e3740e6a1ff8a 100644 --- a/src/tray/SDL_tray_utils.c +++ b/src/tray/SDL_tray_utils.c @@ -27,38 +27,60 @@ 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) +{ + 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]); + } +} + +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..f413c0ee63aba 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,11 +105,6 @@ 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 @@ -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..2a0206ef6d3be 100644 --- a/src/tray/unix/SDL_tray.c +++ b/src/tray/unix/SDL_tray.c @@ -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,16 +659,29 @@ 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) { + return; + } + if (!(entry->flags & SDL_TRAYENTRY_CHECKBOX)) { SDL_SetError("Cannot update check for entry not created with SDL_TRAYENTRY_CHECKBOX"); return; @@ -647,8 +694,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 +703,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 +746,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 +761,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 +795,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(); }