SDL_EVENT_QUIT when no window nor tray

SDL_HINT_QUIT_ON_LAST_WINDOW_CLOSE will not fire if there are active tray icons. This impacts only applications that create tray icons, and that at least one icon outlives the last visible top-level window. SDL_EVENT_QUIT will fire when the last active tray is destroyed if there are no active windows.
This commit is contained in:
Semphris
2024-12-26 20:50:46 -05:00
committed by Sam Lantinga
parent ff139fe71b
commit 0461180e25
14 changed files with 173 additions and 5 deletions

View File

@@ -79,6 +79,7 @@ LOCAL_SRC_FILES := \
$(wildcard $(LOCAL_PATH)/src/timer/*.c) \ $(wildcard $(LOCAL_PATH)/src/timer/*.c) \
$(wildcard $(LOCAL_PATH)/src/timer/unix/*.c) \ $(wildcard $(LOCAL_PATH)/src/timer/unix/*.c) \
$(wildcard $(LOCAL_PATH)/src/tray/dummy/*.c) \ $(wildcard $(LOCAL_PATH)/src/tray/dummy/*.c) \
$(wildcard $(LOCAL_PATH)/src/tray/*.c) \
$(wildcard $(LOCAL_PATH)/src/video/*.c) \ $(wildcard $(LOCAL_PATH)/src/video/*.c) \
$(wildcard $(LOCAL_PATH)/src/video/android/*.c) \ $(wildcard $(LOCAL_PATH)/src/video/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/video/yuv2rgb/*.c)) $(wildcard $(LOCAL_PATH)/src/video/yuv2rgb/*.c))

View File

@@ -838,6 +838,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Gaming.Xbox.XboxOne.x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Gaming.Xbox.XboxOne.x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Gaming.Xbox.XboxOne.x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Gaming.Xbox.XboxOne.x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\tray\SDL_tray_utils.c" />
<ClCompile Include="..\..\src\video\dummy\SDL_nullevents.c" /> <ClCompile Include="..\..\src\video\dummy\SDL_nullevents.c" />
<ClCompile Include="..\..\src\video\dummy\SDL_nullframebuffer.c" /> <ClCompile Include="..\..\src\video\dummy\SDL_nullframebuffer.c" />
<ClCompile Include="..\..\src\video\dummy\SDL_nullvideo.c" /> <ClCompile Include="..\..\src\video\dummy\SDL_nullvideo.c" />

View File

@@ -220,6 +220,7 @@
<ClCompile Include="..\..\src\time\windows\SDL_systime.c" /> <ClCompile Include="..\..\src\time\windows\SDL_systime.c" />
<ClCompile Include="..\..\src\tray\dummy\SDL_tray.c" /> <ClCompile Include="..\..\src\tray\dummy\SDL_tray.c" />
<ClCompile Include="..\..\src\tray\windows\SDL_tray.c" /> <ClCompile Include="..\..\src\tray\windows\SDL_tray.c" />
<ClCompile Include="..\..\src\tray\SDL_tray_utils.c" />
<ClCompile Include="..\..\src\video\yuv2rgb\yuv_rgb_lsx.c" /> <ClCompile Include="..\..\src\video\yuv2rgb\yuv_rgb_lsx.c" />
<ClCompile Include="..\..\src\video\yuv2rgb\yuv_rgb_sse.c" /> <ClCompile Include="..\..\src\video\yuv2rgb\yuv_rgb_sse.c" />
<ClCompile Include="..\..\src\video\yuv2rgb\yuv_rgb_std.c" /> <ClCompile Include="..\..\src\video\yuv2rgb\yuv_rgb_std.c" />

View File

@@ -673,6 +673,7 @@
<ClCompile Include="..\..\src\time\SDL_time.c" /> <ClCompile Include="..\..\src\time\SDL_time.c" />
<ClCompile Include="..\..\src\time\windows\SDL_systime.c" /> <ClCompile Include="..\..\src\time\windows\SDL_systime.c" />
<ClCompile Include="..\..\src\tray\windows\SDL_tray.c" /> <ClCompile Include="..\..\src\tray\windows\SDL_tray.c" />
<ClCompile Include="..\..\src\tray\SDL_tray_utils.c" />
<ClCompile Include="..\..\src\video\dummy\SDL_nullevents.c" /> <ClCompile Include="..\..\src\video\dummy\SDL_nullevents.c" />
<ClCompile Include="..\..\src\video\dummy\SDL_nullframebuffer.c" /> <ClCompile Include="..\..\src\video\dummy\SDL_nullframebuffer.c" />
<ClCompile Include="..\..\src\video\dummy\SDL_nullvideo.c" /> <ClCompile Include="..\..\src\video\dummy\SDL_nullvideo.c" />

View File

@@ -1235,6 +1235,9 @@
<ClCompile Include="..\..\src\tray\windows\SDL_tray.c"> <ClCompile Include="..\..\src\tray\windows\SDL_tray.c">
<Filter>video</Filter> <Filter>video</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\tray\SDL_tray_utils.c">
<Filter>video</Filter>
</ClCompile>
<ClCompile Include="..\..\src\video\SDL_RLEaccel.c"> <ClCompile Include="..\..\src\video\SDL_RLEaccel.c">
<Filter>video</Filter> <Filter>video</Filter>
</ClCompile> </ClCompile>

View File

@@ -2775,6 +2775,10 @@ extern "C" {
* - "1": SDL will send a quit event when the last window is requesting to * - "1": SDL will send a quit event when the last window is requesting to
* close. (default) * close. (default)
* *
* If there is at least one active system tray icon, SDL_EVENT_QUIT will instead
* be sent when both the last window will be closed and the last tray icon will
* be destroyed.
*
* This hint can be set anytime. * This hint can be set anytime.
* *
* \since This hint is available since SDL 3.1.3. * \since This hint is available since SDL 3.1.3.

View File

@@ -24,7 +24,7 @@
#include "SDL_events_c.h" #include "SDL_events_c.h"
#include "SDL_mouse_c.h" #include "SDL_mouse_c.h"
#include "../tray/SDL_tray_utils.h"
static bool SDLCALL RemoveSupercededWindowEvents(void *userdata, SDL_Event *event) static bool SDLCALL RemoveSupercededWindowEvents(void *userdata, SDL_Event *event)
{ {
@@ -247,7 +247,7 @@ bool SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent, int data
break; break;
} }
if (windowevent == SDL_EVENT_WINDOW_CLOSE_REQUESTED && !window->parent) { if (windowevent == SDL_EVENT_WINDOW_CLOSE_REQUESTED && !window->parent && SDL_HasNoActiveTrays()) {
int toplevel_count = 0; int toplevel_count = 0;
SDL_Window *n; SDL_Window *n;
for (n = SDL_GetVideoDevice()->windows; n; n = n->next) { for (n = SDL_GetVideoDevice()->windows; n; n = n->next) {

62
src/tray/SDL_tray_utils.c Normal file
View File

@@ -0,0 +1,62 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 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, 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.
*/
#include "SDL_internal.h"
#include "../video/SDL_sysvideo.h"
#include "../events/SDL_events_c.h"
static int active_trays = 0;
extern void SDL_IncrementTrayCount(void)
{
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);
}
}
extern void SDL_DecrementTrayCount(void)
{
int toplevel_count = 0;
SDL_Window *n;
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);
}
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;
}
}
if (toplevel_count < 1) {
SDL_SendQuit();
}
}
extern bool SDL_HasNoActiveTrays(void)
{
return active_trays < 1;
}

25
src/tray/SDL_tray_utils.h Normal file
View File

@@ -0,0 +1,25 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 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, 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.
*/
#include "SDL_internal.h"
extern void SDL_IncrementTrayCount(void);
extern void SDL_DecrementTrayCount(void);
extern bool SDL_HasNoActiveTrays(void);

View File

@@ -25,6 +25,7 @@
#include <Cocoa/Cocoa.h> #include <Cocoa/Cocoa.h>
#include "../SDL_tray_utils.h"
#include "../../video/SDL_surface_c.h" #include "../../video/SDL_surface_c.h"
/* applicationDockMenu */ /* applicationDockMenu */
@@ -158,6 +159,8 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
} }
skip_putting_an_icon: skip_putting_an_icon:
SDL_IncrementTrayCount();
return tray; return tray;
} }
@@ -445,6 +448,8 @@ void SDL_DestroyTray(SDL_Tray *tray)
} }
SDL_free(tray); SDL_free(tray);
SDL_DecrementTrayCount();
} }
#endif // SDL_PLATFORM_MACOS #endif // SDL_PLATFORM_MACOS

View File

@@ -23,6 +23,8 @@
#ifndef SDL_PLATFORM_MACOS #ifndef SDL_PLATFORM_MACOS
#include "../SDL_tray_utils.h"
SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
{ {
SDL_Unsupported(); SDL_Unsupported();

View File

@@ -21,6 +21,8 @@
#include "SDL_internal.h" #include "SDL_internal.h"
#include "../SDL_tray_utils.h"
#include <dlfcn.h> #include <dlfcn.h>
/* getpid() */ /* getpid() */
@@ -52,6 +54,7 @@ typedef enum
G_CONNECT_SWAPPED = 1 << 1 G_CONNECT_SWAPPED = 1 << 1
} GConnectFlags; } GConnectFlags;
gulong (*g_signal_connect_data)(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags); gulong (*g_signal_connect_data)(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags);
void (*g_object_unref)(gpointer object);
#define g_signal_connect(instance, detailed_signal, c_handler, data) \ #define g_signal_connect(instance, detailed_signal, c_handler, data) \
g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0) g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)
@@ -237,6 +240,7 @@ static bool init_gtk(void)
gtk_widget_get_sensitive = dlsym(libgtk, "gtk_widget_get_sensitive"); gtk_widget_get_sensitive = dlsym(libgtk, "gtk_widget_get_sensitive");
g_signal_connect_data = dlsym(libgdk, "g_signal_connect_data"); g_signal_connect_data = dlsym(libgdk, "g_signal_connect_data");
g_object_unref = dlsym(libgdk, "g_object_unref");
app_indicator_new = dlsym(libappindicator, "app_indicator_new"); app_indicator_new = dlsym(libappindicator, "app_indicator_new");
app_indicator_set_status = dlsym(libappindicator, "app_indicator_set_status"); app_indicator_set_status = dlsym(libappindicator, "app_indicator_set_status");
@@ -257,6 +261,7 @@ static bool init_gtk(void)
!gtk_menu_shell_insert || !gtk_menu_shell_insert ||
!gtk_widget_destroy || !gtk_widget_destroy ||
!g_signal_connect_data || !g_signal_connect_data ||
!g_object_unref ||
!app_indicator_new || !app_indicator_new ||
!app_indicator_set_status || !app_indicator_set_status ||
!app_indicator_set_icon || !app_indicator_set_icon ||
@@ -394,6 +399,8 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
app_indicator_set_status(tray->indicator, APP_INDICATOR_STATUS_ACTIVE); app_indicator_set_status(tray->indicator, APP_INDICATOR_STATUS_ACTIVE);
SDL_IncrementTrayCount();
return tray; return tray;
} }
@@ -660,5 +667,9 @@ void SDL_DestroyTray(SDL_Tray *tray)
SDL_RemovePath(tray->icon_path); SDL_RemovePath(tray->icon_path);
} }
g_object_unref(tray->indicator);
SDL_free(tray); SDL_free(tray);
SDL_DecrementTrayCount();
} }

View File

@@ -21,7 +21,9 @@
#include "SDL_internal.h" #include "SDL_internal.h"
#include "../SDL_tray_utils.h"
#include "../../core/windows/SDL_windows.h" #include "../../core/windows/SDL_windows.h"
#include <windowsx.h> #include <windowsx.h>
#include <shellapi.h> #include <shellapi.h>
#include <stdlib.h> /* FIXME: for mbstowcs_s, wcslen */ #include <stdlib.h> /* FIXME: for mbstowcs_s, wcslen */
@@ -228,6 +230,8 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
SetWindowLongPtr(tray->hwnd, GWLP_USERDATA, (LONG_PTR) tray); SetWindowLongPtr(tray->hwnd, GWLP_USERDATA, (LONG_PTR) tray);
SDL_IncrementTrayCount();
return tray; return tray;
} }
@@ -568,4 +572,6 @@ void SDL_DestroyTray(SDL_Tray *tray)
} }
SDL_free(tray); SDL_free(tray);
SDL_DecrementTrayCount();
} }

View File

@@ -9,6 +9,20 @@ static void SDLCALL tray_quit(void *ptr, SDL_TrayEntry *entry)
SDL_PushEvent(&e); SDL_PushEvent(&e);
} }
static bool trays_destroyed = false;
static void SDLCALL tray_close(void *ptr, SDL_TrayEntry *entry)
{
SDL_Tray **trays = (SDL_Tray **) ptr;
trays_destroyed = true;
SDL_DestroyTray(trays[0]);
SDL_DestroyTray(trays[1]);
SDL_free(trays);
}
static void SDLCALL apply_icon(void *ptr, const char * const *filelist, int filter) static void SDLCALL apply_icon(void *ptr, const char * const *filelist, int filter)
{ {
if (!*filelist) { if (!*filelist) {
@@ -500,6 +514,13 @@ int main(int argc, char **argv)
return 1; return 1;
} }
SDL_Window *w = SDL_CreateWindow("", 640, 480, 0);
if (!w) {
SDL_Log("Couldn't create window: %s", SDL_GetError());
goto quit;
}
/* TODO: Resource paths? */ /* TODO: Resource paths? */
SDL_Surface *icon = SDL_LoadBMP("../test/sdl-test_round.bmp"); SDL_Surface *icon = SDL_LoadBMP("../test/sdl-test_round.bmp");
@@ -517,7 +538,7 @@ int main(int argc, char **argv)
if (!tray) { if (!tray) {
SDL_Log("Couldn't create control tray: %s", SDL_GetError()); SDL_Log("Couldn't create control tray: %s", SDL_GetError());
goto quit; goto clean_window;
} }
SDL_Tray *tray2 = SDL_CreateTray(icon2, "SDL Tray example"); SDL_Tray *tray2 = SDL_CreateTray(icon2, "SDL Tray example");
@@ -545,7 +566,20 @@ int main(int argc, char **argv)
SDL_TrayEntry *entry_quit = SDL_InsertTrayEntryAt(menu, -1, "Quit", SDL_TRAYENTRY_BUTTON); SDL_TrayEntry *entry_quit = SDL_InsertTrayEntryAt(menu, -1, "Quit", SDL_TRAYENTRY_BUTTON);
CHECK(entry_quit); CHECK(entry_quit);
SDL_TrayEntry *entry_close = SDL_InsertTrayEntryAt(menu, -1, "Close", SDL_TRAYENTRY_BUTTON);
CHECK(entry_close);
/* TODO: Track memory! */
SDL_Tray **trays = SDL_malloc(sizeof(SDL_Tray *) * 2);
if (!trays) {
goto clean_all;
}
trays[0] = tray;
trays[1] = tray2;
SDL_SetTrayEntryCallback(entry_quit, tray_quit, NULL); SDL_SetTrayEntryCallback(entry_quit, tray_quit, NULL);
SDL_SetTrayEntryCallback(entry_close, tray_close, trays);
SDL_InsertTrayEntryAt(menu, -1, NULL, 0); SDL_InsertTrayEntryAt(menu, -1, NULL, 0);
@@ -582,14 +616,26 @@ int main(int argc, char **argv)
while (SDL_WaitEvent(&e)) { while (SDL_WaitEvent(&e)) {
if (e.type == SDL_EVENT_QUIT) { if (e.type == SDL_EVENT_QUIT) {
break; break;
} else if (e.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
SDL_DestroyWindow(w);
w = NULL;
} }
} }
clean_all: clean_all:
if (!trays_destroyed) {
SDL_DestroyTray(tray2); SDL_DestroyTray(tray2);
}
clean_tray1: clean_tray1:
if (!trays_destroyed) {
SDL_DestroyTray(tray); SDL_DestroyTray(tray);
}
clean_window:
if (w) {
SDL_DestroyWindow(w);
}
quit: quit:
SDL_Quit(); SDL_Quit();