tray: improved error checking

Also clean up any existing trays when the program quits

Fixes https://github.com/libsdl-org/SDL/issues/11893
This commit is contained in:
Sam Lantinga
2025-01-19 09:59:12 -08:00
parent b716eeefef
commit 7570ab106d
9 changed files with 334 additions and 83 deletions

View File

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

View File

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

View File

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

View File

@@ -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));
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);
SDL_SetObjectValid(tray, SDL_OBJECT_TYPE_TRAY, false);
--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)
{
return active_trays < 1;
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 == 0;
}

View File

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

View File

@@ -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 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
}
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 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
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 @@ 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;
@@ -243,11 +268,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;
}
@@ -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 @@ SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *la
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

View File

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

View File

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

View File

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