diff --git a/include/SDL3/SDL_tray.h b/include/SDL3/SDL_tray.h index 54123b5020..f1fbc01cb5 100644 --- a/include/SDL3/SDL_tray.h +++ b/include/SDL3/SDL_tray.h @@ -498,6 +498,17 @@ extern SDL_DECLSPEC SDL_TrayEntry *SDLCALL SDL_GetTrayMenuParentEntry(SDL_TrayMe */ extern SDL_DECLSPEC SDL_Tray *SDLCALL SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu); +/** + * Update the trays. + * + * This is called automatically by the event loop and is only needed if you're using trays but aren't handling SDL events. + * + * \since This function is available since SDL 3.2.0. + * + * \threadsafety This function should only be called on the main thread. + */ +extern SDL_DECLSPEC void SDLCALL SDL_UpdateTrays(void); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index f0e66fc834..b4dcc38396 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1232,6 +1232,7 @@ SDL3_0.0.0 { SDL_GetThreadState; SDL_AudioStreamDevicePaused; SDL_ClickTrayEntry; + SDL_UpdateTrays; # 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 e23fe2ed28..d90b6fd074 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1257,3 +1257,4 @@ #define SDL_GetThreadState SDL_GetThreadState_REAL #define SDL_AudioStreamDevicePaused SDL_AudioStreamDevicePaused_REAL #define SDL_ClickTrayEntry SDL_ClickTrayEntry_REAL +#define SDL_UpdateTrays SDL_UpdateTrays_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index d74f9597c6..e57603d185 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1265,3 +1265,4 @@ SDL_DYNAPI_PROC(SDL_Tray*,SDL_GetTrayMenuParentTray,(SDL_TrayMenu *a),(a),return SDL_DYNAPI_PROC(SDL_ThreadState,SDL_GetThreadState,(SDL_Thread *a),(a),return) SDL_DYNAPI_PROC(bool,SDL_AudioStreamDevicePaused,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(void,SDL_ClickTrayEntry,(SDL_TrayEntry *a),(a),) +SDL_DYNAPI_PROC(void,SDL_UpdateTrays,(void),(),) diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index f7b598304b..c82fed18b7 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -1399,6 +1399,8 @@ static void SDL_PumpEventsInternal(bool push_sentinel) } #endif + SDL_UpdateTrays(); + SDL_SendPendingSignalEvents(); // in case we had a signal handler fire, etc. if (push_sentinel && SDL_EventEnabled(SDL_EVENT_POLL_SENTINEL)) { diff --git a/src/tray/cocoa/SDL_tray.m b/src/tray/cocoa/SDL_tray.m index 9d2f55e2db..ae8d6be835 100644 --- a/src/tray/cocoa/SDL_tray.m +++ b/src/tray/cocoa/SDL_tray.m @@ -78,6 +78,10 @@ static void DestroySDLMenu(SDL_TrayMenu *menu) SDL_free(menu); } +void SDL_UpdateTrays(void) +{ +} + SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) { if (!SDL_IsMainThread()) { diff --git a/src/tray/dummy/SDL_tray.c b/src/tray/dummy/SDL_tray.c index 55a1e64558..db76db2526 100644 --- a/src/tray/dummy/SDL_tray.c +++ b/src/tray/dummy/SDL_tray.c @@ -25,6 +25,10 @@ #include "../SDL_tray_utils.h" +void SDL_UpdateTrays(void) +{ +} + SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) { SDL_Unsupported(); diff --git a/src/tray/unix/SDL_tray.c b/src/tray/unix/SDL_tray.c index c26da85a58..5f017c2f59 100644 --- a/src/tray/unix/SDL_tray.c +++ b/src/tray/unix/SDL_tray.c @@ -54,9 +54,10 @@ typedef enum G_CONNECT_AFTER = 1 << 0, G_CONNECT_SWAPPED = 1 << 1 } GConnectFlags; -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); -gchar *(*g_mkdtemp)(gchar *template); + +static gulong (*g_signal_connect_data)(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags); +static void (*g_object_unref)(gpointer object); +static gchar *(*g_mkdtemp)(gchar *template); #define g_signal_connect(instance, detailed_signal, c_handler, data) \ g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0) @@ -78,24 +79,23 @@ typedef struct _GtkMenuShell GtkMenuShell; typedef struct _GtkWidget GtkWidget; typedef struct _GtkCheckMenuItem GtkCheckMenuItem; -gboolean (*gtk_init_check)(int *argc, char ***argv); -void (*gtk_main)(void); -void (*gtk_main_quit)(void); -GtkWidget* (*gtk_menu_new)(void); -GtkWidget* (*gtk_separator_menu_item_new)(void); -GtkWidget* (*gtk_menu_item_new_with_label)(const gchar *label); -void (*gtk_menu_item_set_submenu)(GtkMenuItem *menu_item, GtkWidget *submenu); -GtkWidget* (*gtk_check_menu_item_new_with_label)(const gchar *label); -void (*gtk_check_menu_item_set_active)(GtkCheckMenuItem *check_menu_item, gboolean is_active); -void (*gtk_widget_set_sensitive)(GtkWidget *widget, gboolean sensitive); -void (*gtk_widget_show)(GtkWidget *widget); -void (*gtk_menu_shell_append)(GtkMenuShell *menu_shell, GtkWidget *child); -void (*gtk_menu_shell_insert)(GtkMenuShell *menu_shell, GtkWidget *child, gint position); -void (*gtk_widget_destroy)(GtkWidget *widget); -const gchar *(*gtk_menu_item_get_label)(GtkMenuItem *menu_item); -void (*gtk_menu_item_set_label)(GtkMenuItem *menu_item, const gchar *label); -gboolean (*gtk_check_menu_item_get_active)(GtkCheckMenuItem *check_menu_item); -gboolean (*gtk_widget_get_sensitive)(GtkWidget *widget); +static gboolean (*gtk_init_check)(int *argc, char ***argv); +static gboolean (*gtk_main_iteration_do)(gboolean blocking); +static GtkWidget* (*gtk_menu_new)(void); +static GtkWidget* (*gtk_separator_menu_item_new)(void); +static GtkWidget* (*gtk_menu_item_new_with_label)(const gchar *label); +static void (*gtk_menu_item_set_submenu)(GtkMenuItem *menu_item, GtkWidget *submenu); +static GtkWidget* (*gtk_check_menu_item_new_with_label)(const gchar *label); +static void (*gtk_check_menu_item_set_active)(GtkCheckMenuItem *check_menu_item, gboolean is_active); +static void (*gtk_widget_set_sensitive)(GtkWidget *widget, gboolean sensitive); +static void (*gtk_widget_show)(GtkWidget *widget); +static void (*gtk_menu_shell_append)(GtkMenuShell *menu_shell, GtkWidget *child); +static void (*gtk_menu_shell_insert)(GtkMenuShell *menu_shell, GtkWidget *child, gint position); +static void (*gtk_widget_destroy)(GtkWidget *widget); +static const gchar *(*gtk_menu_item_get_label)(GtkMenuItem *menu_item); +static void (*gtk_menu_item_set_label)(GtkMenuItem *menu_item, const gchar *label); +static gboolean (*gtk_check_menu_item_get_active)(GtkCheckMenuItem *check_menu_item); +static gboolean (*gtk_widget_get_sensitive)(GtkWidget *widget); #define GTK_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_MENU_ITEM, GtkMenuItem)) #define GTK_WIDGET(widget) (G_TYPE_CHECK_INSTANCE_CAST ((widget), GTK_TYPE_WIDGET, GtkWidget)) @@ -119,23 +119,17 @@ typedef enum { } AppIndicatorStatus; typedef struct _AppIndicator AppIndicator; -AppIndicator *(*app_indicator_new)(const gchar *id, const gchar *icon_name, AppIndicatorCategory category); -void (*app_indicator_set_status)(AppIndicator *self, AppIndicatorStatus status); -void (*app_indicator_set_icon)(AppIndicator *self, const gchar *icon_name); -void (*app_indicator_set_menu)(AppIndicator *self, GtkMenu *menu); + +static AppIndicator *(*app_indicator_new)(const gchar *id, const gchar *icon_name, AppIndicatorCategory category); +static void (*app_indicator_set_status)(AppIndicator *self, AppIndicatorStatus status); +static void (*app_indicator_set_icon)(AppIndicator *self, const gchar *icon_name); +static void (*app_indicator_set_menu)(AppIndicator *self, GtkMenu *menu); + /* ------------------------------------------------------------------------- */ /* END THIRD-PARTY HEADER CONTENT */ /* ------------------------------------------------------------------------- */ #endif -static int main_gtk_thread(void *data) -{ - gtk_main(); - return 0; -} - -static bool gtk_thread_active = false; - #ifdef APPINDICATOR_HEADER static void quit_gtk(void) @@ -232,8 +226,7 @@ static bool init_gtk(void) } gtk_init_check = dlsym(libgtk, "gtk_init_check"); - gtk_main = dlsym(libgtk, "gtk_main"); - gtk_main_quit = dlsym(libgtk, "gtk_main_quit"); + gtk_main_iteration_do = dlsym(libgtk, "gtk_main_iteration_do"); gtk_menu_new = dlsym(libgtk, "gtk_menu_new"); gtk_separator_menu_item_new = dlsym(libgtk, "gtk_separator_menu_item_new"); gtk_menu_item_new_with_label = dlsym(libgtk, "gtk_menu_item_new_with_label"); @@ -262,8 +255,7 @@ static bool init_gtk(void) app_indicator_set_menu = dlsym(libappindicator, "app_indicator_set_menu"); if (!gtk_init_check || - !gtk_main || - !gtk_main_quit || + !gtk_main_iteration_do || !gtk_menu_new || !gtk_separator_menu_item_new || !gtk_menu_item_new_with_label || @@ -396,6 +388,13 @@ static void DestroySDLMenu(SDL_TrayMenu *menu) SDL_free(menu); } +void SDL_UpdateTrays(void) +{ + if (SDL_HasActiveTrays()) { + gtk_main_iteration_do(FALSE); + } +} + SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) { if (!SDL_IsMainThread()) { @@ -407,11 +406,6 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) return NULL; } - if (!gtk_thread_active) { - SDL_DetachThread(SDL_CreateThread(main_gtk_thread, "tray gtk", NULL)); - gtk_thread_active = true; - } - SDL_Tray *tray = (SDL_Tray *)SDL_calloc(1, sizeof(*tray)); if (!tray) { return NULL; @@ -794,9 +788,4 @@ void SDL_DestroyTray(SDL_Tray *tray) } SDL_free(tray); - - if (!SDL_HasActiveTrays()) { - gtk_main_quit(); - gtk_thread_active = false; - } } diff --git a/src/tray/windows/SDL_tray.c b/src/tray/windows/SDL_tray.c index 0afd62768f..a7e27c0699 100644 --- a/src/tray/windows/SDL_tray.c +++ b/src/tray/windows/SDL_tray.c @@ -209,6 +209,10 @@ static HICON load_default_icon() return LoadIcon(NULL, IDI_APPLICATION); } +void SDL_UpdateTrays(void) +{ +} + SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) { if (!SDL_IsMainThread()) {