|
|
@@ -22,326 +22,87 @@
|
|
|
#include "SDL_internal.h"
|
|
|
|
|
|
#include "../SDL_tray_utils.h"
|
|
|
-#include "../../video/SDL_stb_c.h"
|
|
|
-
|
|
|
-#include <dlfcn.h>
|
|
|
-#include <errno.h>
|
|
|
-
|
|
|
-/* getpid() */
|
|
|
-#include <unistd.h>
|
|
|
-
|
|
|
-/* APPINDICATOR_HEADER is not exposed as a build setting, but the code has been
|
|
|
- written nevertheless to make future maintenance easier. */
|
|
|
-#ifdef APPINDICATOR_HEADER
|
|
|
-#include APPINDICATOR_HEADER
|
|
|
-#else
|
|
|
-#include "../../core/unix/SDL_gtk.h"
|
|
|
-
|
|
|
-/* ------------------------------------------------------------------------- */
|
|
|
-/* BEGIN THIRD-PARTY HEADER CONTENT */
|
|
|
-/* ------------------------------------------------------------------------- */
|
|
|
-/* AppIndicator */
|
|
|
-
|
|
|
-typedef enum {
|
|
|
- APP_INDICATOR_CATEGORY_APPLICATION_STATUS,
|
|
|
- APP_INDICATOR_CATEGORY_COMMUNICATIONS,
|
|
|
- APP_INDICATOR_CATEGORY_SYSTEM_SERVICES,
|
|
|
- APP_INDICATOR_CATEGORY_HARDWARE,
|
|
|
- APP_INDICATOR_CATEGORY_OTHER
|
|
|
-} AppIndicatorCategory;
|
|
|
-
|
|
|
-typedef enum {
|
|
|
- APP_INDICATOR_STATUS_PASSIVE,
|
|
|
- APP_INDICATOR_STATUS_ACTIVE,
|
|
|
- APP_INDICATOR_STATUS_ATTENTION
|
|
|
-} AppIndicatorStatus;
|
|
|
-
|
|
|
-typedef struct _AppIndicator AppIndicator;
|
|
|
-
|
|
|
-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 void *libappindicator = NULL;
|
|
|
-
|
|
|
-static void quit_appindicator(void)
|
|
|
-{
|
|
|
- if (libappindicator) {
|
|
|
- dlclose(libappindicator);
|
|
|
- libappindicator = NULL;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-const char *appindicator_names[] = {
|
|
|
-#ifdef SDL_PLATFORM_OPENBSD
|
|
|
- "libayatana-appindicator3.so",
|
|
|
- "libappindicator3.so",
|
|
|
-#else
|
|
|
- "libayatana-appindicator3.so.1",
|
|
|
- "libappindicator3.so.1",
|
|
|
-#endif
|
|
|
- NULL
|
|
|
-};
|
|
|
-
|
|
|
-static void *find_lib(const char **names)
|
|
|
-{
|
|
|
- const char **name_ptr = names;
|
|
|
- void *handle = NULL;
|
|
|
-
|
|
|
- do {
|
|
|
- handle = dlopen(*name_ptr, RTLD_LAZY);
|
|
|
- } while (*++name_ptr && !handle);
|
|
|
-
|
|
|
- return handle;
|
|
|
-}
|
|
|
-
|
|
|
-static bool init_appindicator(void)
|
|
|
-{
|
|
|
- if (libappindicator) {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- libappindicator = find_lib(appindicator_names);
|
|
|
-
|
|
|
- if (!libappindicator) {
|
|
|
- quit_appindicator();
|
|
|
- return SDL_SetError("Could not load AppIndicator libraries");
|
|
|
- }
|
|
|
-
|
|
|
- app_indicator_new = dlsym(libappindicator, "app_indicator_new");
|
|
|
- app_indicator_set_status = dlsym(libappindicator, "app_indicator_set_status");
|
|
|
- app_indicator_set_icon = dlsym(libappindicator, "app_indicator_set_icon");
|
|
|
- app_indicator_set_menu = dlsym(libappindicator, "app_indicator_set_menu");
|
|
|
-
|
|
|
- if (!app_indicator_new ||
|
|
|
- !app_indicator_set_status ||
|
|
|
- !app_indicator_set_icon ||
|
|
|
- !app_indicator_set_menu) {
|
|
|
- quit_appindicator();
|
|
|
- return SDL_SetError("Could not load AppIndicator functions");
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-struct SDL_TrayMenu {
|
|
|
- GtkMenuShell *menu;
|
|
|
-
|
|
|
- int nEntries;
|
|
|
- SDL_TrayEntry **entries;
|
|
|
-
|
|
|
- SDL_Tray *parent_tray;
|
|
|
- SDL_TrayEntry *parent_entry;
|
|
|
-};
|
|
|
+#include "SDL_unixtray.h"
|
|
|
|
|
|
-struct SDL_TrayEntry {
|
|
|
- SDL_TrayMenu *parent;
|
|
|
- GtkWidget *item;
|
|
|
+static SDL_TrayDriver *driver = NULL;
|
|
|
|
|
|
- /* Checkboxes are "activated" when programmatically checked/unchecked; this
|
|
|
- is a workaround. */
|
|
|
- bool ignore_signal;
|
|
|
-
|
|
|
- SDL_TrayEntryFlags flags;
|
|
|
- SDL_TrayCallback callback;
|
|
|
- void *userdata;
|
|
|
- SDL_TrayMenu *submenu;
|
|
|
-};
|
|
|
-
|
|
|
-struct SDL_Tray {
|
|
|
- AppIndicator *indicator;
|
|
|
- SDL_TrayMenu *menu;
|
|
|
- char *icon_dir;
|
|
|
- char *icon_path;
|
|
|
-
|
|
|
- GtkMenuShell *menu_cached;
|
|
|
-};
|
|
|
-
|
|
|
-static void call_callback(GtkMenuItem *item, gpointer ptr)
|
|
|
+void SDL_UpdateTrays(void)
|
|
|
{
|
|
|
- SDL_TrayEntry *entry = ptr;
|
|
|
-
|
|
|
- /* Not needed with AppIndicator, may be needed with other frameworks */
|
|
|
- /* if (entry->flags & SDL_TRAYENTRY_CHECKBOX) {
|
|
|
- SDL_SetTrayEntryChecked(entry, !SDL_GetTrayEntryChecked(entry));
|
|
|
- } */
|
|
|
+ SDL_Tray **trays;
|
|
|
+ int active_trays;
|
|
|
+ int count;
|
|
|
+ int i;
|
|
|
|
|
|
- if (entry->ignore_signal) {
|
|
|
+ active_trays = SDL_GetActiveTrayCount();
|
|
|
+ if (!active_trays) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (entry->callback) {
|
|
|
- entry->callback(entry->userdata, entry);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static bool new_tmp_filename(SDL_Tray *tray)
|
|
|
-{
|
|
|
- static int count = 0;
|
|
|
-
|
|
|
- int would_have_written = SDL_asprintf(&tray->icon_path, "%s/%d.png", tray->icon_dir, count++);
|
|
|
-
|
|
|
- if (would_have_written >= 0) {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- tray->icon_path = NULL;
|
|
|
- SDL_SetError("Failed to format new temporary filename");
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-static const char *get_appindicator_id(void)
|
|
|
-{
|
|
|
- static int count = 0;
|
|
|
- static char buffer[256];
|
|
|
-
|
|
|
- int would_have_written = SDL_snprintf(buffer, sizeof(buffer), "sdl-appindicator-%d-%d", getpid(), count++);
|
|
|
-
|
|
|
- if (would_have_written <= 0 || would_have_written >= sizeof(buffer) - 1) {
|
|
|
- SDL_SetError("Couldn't fit %d bytes in buffer of size %d", would_have_written, (int) sizeof(buffer));
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- return buffer;
|
|
|
-}
|
|
|
-
|
|
|
-static void DestroySDLMenu(SDL_TrayMenu *menu)
|
|
|
-{
|
|
|
- for (int i = 0; i < menu->nEntries; i++) {
|
|
|
- if (menu->entries[i] && menu->entries[i]->submenu) {
|
|
|
- DestroySDLMenu(menu->entries[i]->submenu);
|
|
|
- }
|
|
|
- SDL_free(menu->entries[i]);
|
|
|
+ trays = SDL_calloc(active_trays, sizeof(SDL_Tray *));
|
|
|
+ if (!trays) {
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- if (menu->menu) {
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (gtk) {
|
|
|
- gtk->g.object_unref(menu->menu);
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
+ count = SDL_GetObjects(SDL_OBJECT_TYPE_TRAY, (void **)trays, active_trays);
|
|
|
+ SDL_assert(count == active_trays);
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
+ if (trays[i]) {
|
|
|
+ if (SDL_ObjectValid(trays[i], SDL_OBJECT_TYPE_TRAY)) {
|
|
|
+ trays[i]->driver->UpdateTray(trays[i]);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- SDL_free(menu->entries);
|
|
|
- SDL_free(menu);
|
|
|
-}
|
|
|
-
|
|
|
-void SDL_UpdateTrays(void)
|
|
|
-{
|
|
|
- if (SDL_HasActiveTrays()) {
|
|
|
- SDL_UpdateGtk();
|
|
|
- }
|
|
|
+ SDL_free(trays);
|
|
|
}
|
|
|
|
|
|
SDL_Tray *SDL_CreateTrayWithProperties(SDL_PropertiesID props)
|
|
|
{
|
|
|
+ SDL_Tray *tray;
|
|
|
+
|
|
|
if (!SDL_IsMainThread()) {
|
|
|
SDL_SetError("This function should be called on the main thread");
|
|
|
return NULL;
|
|
|
}
|
|
|
-
|
|
|
- if (!init_appindicator()) {
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- SDL_Surface *icon = (SDL_Surface *)SDL_GetPointerProperty(props, SDL_PROP_TRAY_CREATE_ICON_POINTER, NULL);
|
|
|
-
|
|
|
- SDL_Tray *tray = NULL;
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (!gtk) {
|
|
|
- goto tray_error;
|
|
|
- }
|
|
|
-
|
|
|
- tray = (SDL_Tray *)SDL_calloc(1, sizeof(*tray));
|
|
|
- if (!tray) {
|
|
|
- goto tray_error;
|
|
|
+
|
|
|
+ if (!driver) {
|
|
|
+#ifdef SDL_USE_LIBDBUS
|
|
|
+ driver = SDL_Tray_CreateDBusDriver();
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
- const gchar *cache_dir = gtk->g.get_user_cache_dir();
|
|
|
- if (!cache_dir) {
|
|
|
- SDL_SetError("Cannot get user cache directory: %s", strerror(errno));
|
|
|
- goto tray_error;
|
|
|
- }
|
|
|
+ if (driver) {
|
|
|
+ tray = driver->CreateTray(driver, props);
|
|
|
|
|
|
- char *sdl_dir;
|
|
|
- SDL_asprintf(&sdl_dir, "%s/SDL", cache_dir);
|
|
|
- if (!SDL_GetPathInfo(sdl_dir, NULL)) {
|
|
|
- if (!SDL_CreateDirectory(sdl_dir)) {
|
|
|
- SDL_SetError("Cannot create directory for tray icon: %s", strerror(errno));
|
|
|
- goto sdl_dir_error;
|
|
|
+ if (tray) {
|
|
|
+ SDL_RegisterTray(tray);
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- /* On success, g_mkdtemp edits its argument in-place to replace the Xs
|
|
|
- * with a random directory name, which it creates safely and atomically.
|
|
|
- * On failure, it sets errno. */
|
|
|
- SDL_asprintf(&tray->icon_dir, "%s/tray-XXXXXX", sdl_dir);
|
|
|
- if (!gtk->g.mkdtemp(tray->icon_dir)) {
|
|
|
- SDL_SetError("Cannot create directory for tray icon: %s", strerror(errno));
|
|
|
- goto icon_dir_error;
|
|
|
- }
|
|
|
-
|
|
|
- if (icon) {
|
|
|
- if (!new_tmp_filename(tray)) {
|
|
|
- goto icon_dir_error;
|
|
|
- }
|
|
|
-
|
|
|
- SDL_SavePNG(icon, tray->icon_path);
|
|
|
} else {
|
|
|
- // allocate a dummy icon path
|
|
|
- SDL_asprintf(&tray->icon_path, " ");
|
|
|
+ tray = NULL;
|
|
|
}
|
|
|
|
|
|
- tray->indicator = app_indicator_new(get_appindicator_id(), tray->icon_path,
|
|
|
- APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
|
|
|
-
|
|
|
- app_indicator_set_status(tray->indicator, APP_INDICATOR_STATUS_ACTIVE);
|
|
|
-
|
|
|
- // The tray icon isn't shown before a menu is created; create one early.
|
|
|
- tray->menu_cached = (GtkMenuShell *)gtk->g.object_ref_sink(gtk->gtk.menu_new());
|
|
|
- app_indicator_set_menu(tray->indicator, GTK_MENU(tray->menu_cached));
|
|
|
-
|
|
|
- SDL_RegisterTray(tray);
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
- SDL_free(sdl_dir);
|
|
|
-
|
|
|
return tray;
|
|
|
-
|
|
|
-icon_dir_error:
|
|
|
- SDL_free(tray->icon_dir);
|
|
|
-
|
|
|
-sdl_dir_error:
|
|
|
- SDL_free(sdl_dir);
|
|
|
-
|
|
|
-tray_error:
|
|
|
- SDL_free(tray);
|
|
|
-
|
|
|
- if (gtk) {
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
- }
|
|
|
-
|
|
|
- return NULL;
|
|
|
}
|
|
|
|
|
|
SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
|
|
|
{
|
|
|
SDL_Tray *tray;
|
|
|
- SDL_PropertiesID props = SDL_CreateProperties();
|
|
|
+ SDL_PropertiesID props;
|
|
|
+
|
|
|
+ props = SDL_CreateProperties();
|
|
|
+
|
|
|
if (!props) {
|
|
|
return NULL;
|
|
|
}
|
|
|
+
|
|
|
if (icon) {
|
|
|
SDL_SetPointerProperty(props, SDL_PROP_TRAY_CREATE_ICON_POINTER, icon);
|
|
|
}
|
|
|
+
|
|
|
if (tooltip) {
|
|
|
SDL_SetStringProperty(props, SDL_PROP_TRAY_CREATE_TOOLTIP_STRING, tooltip);
|
|
|
}
|
|
|
+
|
|
|
tray = SDL_CreateTrayWithProperties(props);
|
|
|
SDL_DestroyProperties(props);
|
|
|
return tray;
|
|
|
@@ -349,65 +110,41 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
|
|
|
|
|
|
void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon)
|
|
|
{
|
|
|
- if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
|
|
|
+ CHECK_PARAM(!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY))
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("tray");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (tray->icon_path) {
|
|
|
- SDL_RemovePath(tray->icon_path);
|
|
|
- SDL_free(tray->icon_path);
|
|
|
- tray->icon_path = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- /* AppIndicator caches the icon files; always change filename to avoid caching */
|
|
|
-
|
|
|
- if (icon && new_tmp_filename(tray)) {
|
|
|
- SDL_SavePNG(icon, tray->icon_path);
|
|
|
- app_indicator_set_icon(tray->indicator, tray->icon_path);
|
|
|
- } else {
|
|
|
- SDL_free(tray->icon_path);
|
|
|
- tray->icon_path = NULL;
|
|
|
- app_indicator_set_icon(tray->indicator, NULL);
|
|
|
- }
|
|
|
+ tray->driver->SetTrayIcon(tray, icon);
|
|
|
}
|
|
|
|
|
|
void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip)
|
|
|
{
|
|
|
- /* AppIndicator provides no tooltip support. */
|
|
|
+ CHECK_PARAM(!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY))
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("tray");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ tray->driver->SetTrayTooltip(tray, tooltip);
|
|
|
}
|
|
|
|
|
|
SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray)
|
|
|
{
|
|
|
- CHECK_PARAM(!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
|
|
|
+ CHECK_PARAM(!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY))
|
|
|
+ {
|
|
|
SDL_InvalidParamError("tray");
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (!gtk) {
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- tray->menu = (SDL_TrayMenu *)SDL_calloc(1, sizeof(*tray->menu));
|
|
|
- if (!tray->menu) {
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- tray->menu->menu = gtk->g.object_ref(tray->menu_cached);
|
|
|
- tray->menu->parent_tray = tray;
|
|
|
- tray->menu->parent_entry = NULL;
|
|
|
- tray->menu->nEntries = 0;
|
|
|
- tray->menu->entries = NULL;
|
|
|
-
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
-
|
|
|
- return tray->menu;
|
|
|
+ return tray->driver->CreateTrayMenu(tray);
|
|
|
}
|
|
|
|
|
|
SDL_TrayMenu *SDL_GetTrayMenu(SDL_Tray *tray)
|
|
|
{
|
|
|
- CHECK_PARAM(!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) {
|
|
|
+ CHECK_PARAM(!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY))
|
|
|
+ {
|
|
|
SDL_InvalidParamError("tray");
|
|
|
return NULL;
|
|
|
}
|
|
|
@@ -417,314 +154,163 @@ SDL_TrayMenu *SDL_GetTrayMenu(SDL_Tray *tray)
|
|
|
|
|
|
SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry)
|
|
|
{
|
|
|
- CHECK_PARAM(!entry) {
|
|
|
+ CHECK_PARAM(!entry)
|
|
|
+ {
|
|
|
SDL_InvalidParamError("entry");
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- if (entry->submenu) {
|
|
|
- SDL_SetError("Tray entry submenu already exists");
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- if (!(entry->flags & SDL_TRAYENTRY_SUBMENU)) {
|
|
|
- SDL_SetError("Cannot create submenu for entry not created with SDL_TRAYENTRY_SUBMENU");
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (!gtk) {
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- entry->submenu = (SDL_TrayMenu *)SDL_calloc(1, sizeof(*entry->submenu));
|
|
|
- if (!entry->submenu) {
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- entry->submenu->menu = gtk->g.object_ref_sink(gtk->gtk.menu_new());
|
|
|
- entry->submenu->parent_tray = NULL;
|
|
|
- entry->submenu->parent_entry = entry;
|
|
|
- entry->submenu->nEntries = 0;
|
|
|
- entry->submenu->entries = NULL;
|
|
|
-
|
|
|
- gtk->gtk.menu_item_set_submenu(GTK_MENU_ITEM(entry->item), GTK_WIDGET(entry->submenu->menu));
|
|
|
-
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
-
|
|
|
- return entry->submenu;
|
|
|
+ return entry->parent->parent_tray->driver->CreateTraySubmenu(entry);
|
|
|
}
|
|
|
|
|
|
SDL_TrayMenu *SDL_GetTraySubmenu(SDL_TrayEntry *entry)
|
|
|
{
|
|
|
- CHECK_PARAM(!entry) {
|
|
|
+ CHECK_PARAM(!entry)
|
|
|
+ {
|
|
|
SDL_InvalidParamError("entry");
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- return entry->submenu;
|
|
|
+ return entry->parent->parent_tray->driver->GetTraySubmenu(entry);
|
|
|
}
|
|
|
|
|
|
const SDL_TrayEntry **SDL_GetTrayEntries(SDL_TrayMenu *menu, int *count)
|
|
|
{
|
|
|
- CHECK_PARAM(!menu) {
|
|
|
+ CHECK_PARAM(!menu)
|
|
|
+ {
|
|
|
SDL_InvalidParamError("menu");
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- if (count) {
|
|
|
- *count = menu->nEntries;
|
|
|
+ CHECK_PARAM(!count)
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("count");
|
|
|
+ return NULL;
|
|
|
}
|
|
|
- return (const SDL_TrayEntry **)menu->entries;
|
|
|
+
|
|
|
+ return (const SDL_TrayEntry **)menu->parent_tray->driver->GetTrayEntries(menu, count);
|
|
|
}
|
|
|
|
|
|
void SDL_RemoveTrayEntry(SDL_TrayEntry *entry)
|
|
|
{
|
|
|
- if (!entry) {
|
|
|
+ CHECK_PARAM(!entry)
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("entry");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- SDL_TrayMenu *menu = entry->parent;
|
|
|
-
|
|
|
- bool found = false;
|
|
|
- for (int i = 0; i < menu->nEntries - 1; i++) {
|
|
|
- if (menu->entries[i] == entry) {
|
|
|
- found = true;
|
|
|
- }
|
|
|
-
|
|
|
- if (found) {
|
|
|
- menu->entries[i] = menu->entries[i + 1];
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (entry->submenu) {
|
|
|
- DestroySDLMenu(entry->submenu);
|
|
|
- }
|
|
|
-
|
|
|
- menu->nEntries--;
|
|
|
- SDL_TrayEntry **new_entries = (SDL_TrayEntry **)SDL_realloc(menu->entries, (menu->nEntries + 1) * sizeof(*new_entries));
|
|
|
-
|
|
|
- /* Not sure why shrinking would fail, but even if it does, we can live with a "too big" array */
|
|
|
- if (new_entries) {
|
|
|
- menu->entries = new_entries;
|
|
|
- menu->entries[menu->nEntries] = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (gtk) {
|
|
|
- gtk->gtk.widget_destroy(entry->item);
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
- }
|
|
|
- SDL_free(entry);
|
|
|
+ entry->parent->parent_tray->driver->RemoveTrayEntry(entry);
|
|
|
}
|
|
|
|
|
|
SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags)
|
|
|
{
|
|
|
- CHECK_PARAM(!menu) {
|
|
|
+ CHECK_PARAM(!menu)
|
|
|
+ {
|
|
|
SDL_InvalidParamError("menu");
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- CHECK_PARAM(pos < -1 || pos > menu->nEntries) {
|
|
|
- SDL_InvalidParamError("pos");
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- if (pos == -1) {
|
|
|
- pos = menu->nEntries;
|
|
|
- }
|
|
|
-
|
|
|
- SDL_TrayEntry *entry = NULL;
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (!gtk) {
|
|
|
- goto error;
|
|
|
- }
|
|
|
-
|
|
|
- entry = (SDL_TrayEntry *)SDL_calloc(1, sizeof(*entry));
|
|
|
- if (!entry) {
|
|
|
- goto error;
|
|
|
- }
|
|
|
-
|
|
|
- entry->parent = menu;
|
|
|
- entry->item = NULL;
|
|
|
- entry->ignore_signal = false;
|
|
|
- entry->flags = flags;
|
|
|
- entry->callback = NULL;
|
|
|
- entry->userdata = NULL;
|
|
|
- entry->submenu = NULL;
|
|
|
-
|
|
|
- if (label == NULL) {
|
|
|
- entry->item = gtk->gtk.separator_menu_item_new();
|
|
|
- } else if (flags & SDL_TRAYENTRY_CHECKBOX) {
|
|
|
- entry->item = gtk->gtk.check_menu_item_new_with_label(label);
|
|
|
- gboolean active = ((flags & SDL_TRAYENTRY_CHECKED) != 0);
|
|
|
- gtk->gtk.check_menu_item_set_active(GTK_CHECK_MENU_ITEM(entry->item), active);
|
|
|
- } else {
|
|
|
- entry->item = gtk->gtk.menu_item_new_with_label(label);
|
|
|
- }
|
|
|
-
|
|
|
- gboolean sensitive = ((flags & SDL_TRAYENTRY_DISABLED) == 0);
|
|
|
- gtk->gtk.widget_set_sensitive(entry->item, sensitive);
|
|
|
-
|
|
|
- SDL_TrayEntry **new_entries = (SDL_TrayEntry **)SDL_realloc(menu->entries, (menu->nEntries + 2) * sizeof(*new_entries));
|
|
|
-
|
|
|
- if (!new_entries) {
|
|
|
- goto error;
|
|
|
- }
|
|
|
-
|
|
|
- menu->entries = new_entries;
|
|
|
- menu->nEntries++;
|
|
|
-
|
|
|
- for (int i = menu->nEntries - 1; i > pos; i--) {
|
|
|
- menu->entries[i] = menu->entries[i - 1];
|
|
|
- }
|
|
|
-
|
|
|
- new_entries[pos] = entry;
|
|
|
- new_entries[menu->nEntries] = NULL;
|
|
|
-
|
|
|
- gtk->gtk.widget_show(entry->item);
|
|
|
- gtk->gtk.menu_shell_insert(menu->menu, entry->item, (pos == menu->nEntries) ? -1 : pos);
|
|
|
-
|
|
|
- gtk->g.signal_connect(entry->item, "activate", call_callback, entry);
|
|
|
-
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
-
|
|
|
- return entry;
|
|
|
-
|
|
|
-error:
|
|
|
- if (entry) {
|
|
|
- SDL_free(entry);
|
|
|
- }
|
|
|
-
|
|
|
- if (gtk) {
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
- }
|
|
|
-
|
|
|
- return NULL;
|
|
|
+ return menu->parent_tray->driver->InsertTrayEntryAt(menu, pos, label, flags);
|
|
|
}
|
|
|
|
|
|
void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label)
|
|
|
{
|
|
|
- if (!entry) {
|
|
|
+ CHECK_PARAM(!entry)
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("entry");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (gtk) {
|
|
|
- gtk->gtk.menu_item_set_label(GTK_MENU_ITEM(entry->item), label);
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
- }
|
|
|
+ entry->parent->parent_tray->driver->SetTrayEntryLabel(entry, label);
|
|
|
}
|
|
|
|
|
|
const char *SDL_GetTrayEntryLabel(SDL_TrayEntry *entry)
|
|
|
{
|
|
|
- CHECK_PARAM(!entry) {
|
|
|
+ CHECK_PARAM(!entry)
|
|
|
+ {
|
|
|
SDL_InvalidParamError("entry");
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- const char *label = NULL;
|
|
|
-
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (gtk) {
|
|
|
- label = gtk->gtk.menu_item_get_label(GTK_MENU_ITEM(entry->item));
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
- }
|
|
|
-
|
|
|
- return label;
|
|
|
+ return entry->parent->parent_tray->driver->GetTrayEntryLabel(entry);
|
|
|
}
|
|
|
|
|
|
void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked)
|
|
|
{
|
|
|
- if (!entry || !(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
|
|
|
+ CHECK_PARAM(!entry)
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("entry");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (gtk) {
|
|
|
- entry->ignore_signal = true;
|
|
|
- gtk->gtk.check_menu_item_set_active(GTK_CHECK_MENU_ITEM(entry->item), checked);
|
|
|
- entry->ignore_signal = false;
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
- }
|
|
|
+ entry->parent->parent_tray->driver->SetTrayEntryChecked(entry, checked);
|
|
|
}
|
|
|
|
|
|
bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry)
|
|
|
{
|
|
|
- if (!entry || !(entry->flags & SDL_TRAYENTRY_CHECKBOX)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- bool checked = false;
|
|
|
-
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (gtk) {
|
|
|
- checked = gtk->gtk.check_menu_item_get_active(GTK_CHECK_MENU_ITEM(entry->item));
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
+ CHECK_PARAM(!entry)
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("entry");
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
- return checked;
|
|
|
+ return entry->parent->parent_tray->driver->GetTrayEntryChecked(entry);
|
|
|
}
|
|
|
|
|
|
void SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled)
|
|
|
{
|
|
|
- if (!entry) {
|
|
|
+ CHECK_PARAM(!entry)
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("entry");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (gtk) {
|
|
|
- gtk->gtk.widget_set_sensitive(entry->item, enabled);
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
- }
|
|
|
+ entry->parent->parent_tray->driver->SetTrayEntryEnabled(entry, enabled);
|
|
|
}
|
|
|
|
|
|
bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry)
|
|
|
{
|
|
|
- if (!entry) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- bool enabled = false;
|
|
|
-
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (gtk) {
|
|
|
- enabled = gtk->gtk.widget_get_sensitive(entry->item);
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
+ CHECK_PARAM(!entry)
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("entry");
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
- return enabled;
|
|
|
+ return entry->parent->parent_tray->driver->GetTrayEntryEnabled(entry);
|
|
|
}
|
|
|
|
|
|
void SDL_SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata)
|
|
|
{
|
|
|
- if (!entry) {
|
|
|
+ CHECK_PARAM(!entry)
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("entry");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ CHECK_PARAM(!callback)
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("callback");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- entry->callback = callback;
|
|
|
- entry->userdata = userdata;
|
|
|
+ entry->parent->parent_tray->driver->SetTrayEntryCallback(entry, callback, userdata);
|
|
|
}
|
|
|
|
|
|
void SDL_ClickTrayEntry(SDL_TrayEntry *entry)
|
|
|
{
|
|
|
- if (!entry) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (entry->flags & SDL_TRAYENTRY_CHECKBOX) {
|
|
|
- SDL_SetTrayEntryChecked(entry, !SDL_GetTrayEntryChecked(entry));
|
|
|
- }
|
|
|
+ CHECK_PARAM(!entry)
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("entry");
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- if (entry->callback) {
|
|
|
- entry->callback(entry->userdata, entry);
|
|
|
- }
|
|
|
+ entry->parent->parent_tray->driver->GetTrayEntryEnabled(entry);
|
|
|
}
|
|
|
|
|
|
SDL_TrayMenu *SDL_GetTrayEntryParent(SDL_TrayEntry *entry)
|
|
|
{
|
|
|
- CHECK_PARAM(!entry) {
|
|
|
+ CHECK_PARAM(!entry)
|
|
|
+ {
|
|
|
SDL_InvalidParamError("entry");
|
|
|
return NULL;
|
|
|
}
|
|
|
@@ -734,12 +320,19 @@ SDL_TrayMenu *SDL_GetTrayEntryParent(SDL_TrayEntry *entry)
|
|
|
|
|
|
SDL_TrayEntry *SDL_GetTrayMenuParentEntry(SDL_TrayMenu *menu)
|
|
|
{
|
|
|
+ CHECK_PARAM(!menu)
|
|
|
+ {
|
|
|
+ SDL_InvalidParamError("menu");
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
return menu->parent_entry;
|
|
|
}
|
|
|
|
|
|
SDL_Tray *SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu)
|
|
|
{
|
|
|
- CHECK_PARAM(!menu) {
|
|
|
+ CHECK_PARAM(!menu)
|
|
|
+ {
|
|
|
SDL_InvalidParamError("menu");
|
|
|
return NULL;
|
|
|
}
|
|
|
@@ -754,33 +347,10 @@ void SDL_DestroyTray(SDL_Tray *tray)
|
|
|
}
|
|
|
|
|
|
SDL_UnregisterTray(tray);
|
|
|
+ tray->driver->DestroyTray(tray);
|
|
|
|
|
|
- if (tray->menu) {
|
|
|
- DestroySDLMenu(tray->menu);
|
|
|
- }
|
|
|
-
|
|
|
- if (tray->icon_path) {
|
|
|
- SDL_RemovePath(tray->icon_path);
|
|
|
- SDL_free(tray->icon_path);
|
|
|
+ if (!SDL_GetActiveTrayCount()) {
|
|
|
+ driver->DestroyDriver(driver);
|
|
|
+ driver = NULL;
|
|
|
}
|
|
|
-
|
|
|
- if (tray->icon_dir) {
|
|
|
- SDL_RemovePath(tray->icon_dir);
|
|
|
- SDL_free(tray->icon_dir);
|
|
|
- }
|
|
|
-
|
|
|
- SDL_GtkContext *gtk = SDL_Gtk_EnterContext();
|
|
|
- if (gtk) {
|
|
|
- if (tray->menu_cached) {
|
|
|
- gtk->g.object_unref(tray->menu_cached);
|
|
|
- }
|
|
|
-
|
|
|
- if (tray->indicator) {
|
|
|
- gtk->g.object_unref(tray->indicator);
|
|
|
- }
|
|
|
-
|
|
|
- SDL_Gtk_ExitContext(gtk);
|
|
|
- }
|
|
|
-
|
|
|
- SDL_free(tray);
|
|
|
}
|