eafton 1 день назад
Родитель
Сommit
8c024f4f3a

+ 87 - 0
src/SDL_list.c

@@ -22,6 +22,82 @@
 
 #include "./SDL_list.h"
 
+// Append
+bool SDL_ListAppend(SDL_ListNode **head, void *ent)
+{
+    SDL_ListNode *cursor;
+    SDL_ListNode *node;
+
+    if (!head) {
+        return false;
+    }
+
+    node = (SDL_ListNode *)SDL_malloc(sizeof(*node));
+    if (!node) {
+        return false;
+    }
+    node->entry = ent;
+    node->next = NULL;
+
+    if (*head) {
+        cursor = *head;
+        while (cursor->next) {
+            cursor = cursor->next;
+        }
+        cursor->next = node;
+    } else {
+        *head = node;
+    }
+
+    return true;
+}
+
+bool SDL_ListInsertAtPosition(SDL_ListNode **head, int pos, void *ent)
+{
+    SDL_ListNode *cursor;
+    SDL_ListNode *node;
+    int i;
+
+    if (pos == -1) {
+        return SDL_ListAppend(head, ent);
+    }
+
+    if (!pos) {
+        node = (SDL_ListNode *)SDL_malloc(sizeof(*node));
+        if (!node) {
+            return false;
+        }
+        node->entry = ent;
+
+        if (*head) {
+            node->next = *head;
+        } else {
+            node->next = NULL;
+        }
+
+        *head = node;
+    }
+
+    cursor = *head;
+    for (i = 1; i < pos - 1 && cursor; i++) {
+        cursor = cursor->next;
+    }
+
+    if (!cursor) {
+        return SDL_ListAppend(head, ent);
+    }
+
+    node = (SDL_ListNode *)SDL_malloc(sizeof(*node));
+    if (!node) {
+        return false;
+    }
+    node->entry = ent;
+    node->next = cursor->next;
+    cursor->next = node;
+
+    return true;
+}
+
 // Push
 bool SDL_ListAdd(SDL_ListNode **head, void *ent)
 {
@@ -84,3 +160,14 @@ void SDL_ListClear(SDL_ListNode **head)
         SDL_free(tmp);
     }
 }
+
+int SDL_ListCountEntries(SDL_ListNode **head)
+{
+    SDL_ListNode *node;
+    int count = 0;
+
+    for (node = *head; node; node = node->next) {
+        ++count;
+    }
+    return count;
+}

+ 3 - 0
src/SDL_list.h

@@ -28,9 +28,12 @@ typedef struct SDL_ListNode
     struct SDL_ListNode *next;
 } SDL_ListNode;
 
+bool SDL_ListAppend(SDL_ListNode **head, void *ent);
+bool SDL_ListInsertAtPosition(SDL_ListNode **head, int pos, void *ent);
 bool SDL_ListAdd(SDL_ListNode **head, void *ent);
 void SDL_ListPop(SDL_ListNode **head, void **ent);
 void SDL_ListRemove(SDL_ListNode **head, void *ent);
 void SDL_ListClear(SDL_ListNode **head);
+int SDL_ListCountEntries(SDL_ListNode **head);
 
 #endif // SDL_list_h_

+ 64 - 0
src/SDL_menu.h

@@ -0,0 +1,64 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2026 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.
+*/
+
+#ifndef SDL_menu_h_
+#define SDL_menu_h_
+
+#include "./SDL_list.h"
+
+typedef enum SDL_MenuItemType
+{
+    SDL_MENU_ITEM_TYPE_NORMAL,
+    SDL_MENU_ITEM_TYPE_SEPERATOR,
+    SDL_MENU_ITEM_TYPE_CHECKBOX
+} SDL_MenuItemType;
+
+typedef enum SDL_MenuItemFlags
+{
+    SDL_MENU_ITEM_FLAGS_NONE = 0,
+    SDL_MENU_ITEM_FLAGS_DISABLED = 1 << 0,
+    SDL_MENU_ITEM_FLAGS_CHECKED = 1 << 1,
+    SDL_MENU_ITEM_FLAGS_BAR_ITEM = 1 << 2
+} SDL_MenuItemFlags;
+
+/* Do not create this struct directly, users of this structure like the DBUSMENU layer use extended versions of it which need to be allocated by specfic functions. */
+/* This struct is meant to be in an SDL_List just like sub_menu */
+typedef struct SDL_MenuItem
+{
+    /* Basic properties */
+    const char *utf8;
+    SDL_MenuItemType type;
+    SDL_MenuItemFlags flags;
+
+    /* Callback */
+    void *cb_data;
+    void (*cb)(struct SDL_MenuItem *, void *);
+
+    /* Submenu, set to NULL if none */
+    SDL_ListNode *sub_menu;
+
+    /* User data slots */
+    void *udata;
+    void *udata2;
+    void *udata3;
+} SDL_MenuItem;
+
+#endif // SDL_menu_h_

Разница между файлами не показана из-за своего большого размера
+ 20 - 0
src/core/linux/SDL_dbus.c


+ 26 - 2
src/core/linux/SDL_dbus.h

@@ -28,14 +28,20 @@
 #define SDL_USE_LIBDBUS 1
 #include <dbus/dbus.h>
 
+#include "../../SDL_list.h"
+#include "../../SDL_menu.h"
+
 #ifndef DBUS_TIMEOUT_USE_DEFAULT
 #define DBUS_TIMEOUT_USE_DEFAULT -1
 #endif
 #ifndef DBUS_TIMEOUT_INFINITE
-#define DBUS_TIMEOUT_INFINITE ((int) 0x7fffffff)
+#define DBUS_TIMEOUT_INFINITE ((int)0x7fffffff)
 #endif
 #ifndef DBUS_TYPE_UNIX_FD
-#define DBUS_TYPE_UNIX_FD ((int) 'h')
+#define DBUS_TYPE_UNIX_FD ((int)'h')
+#endif
+#ifndef DBUS_ERROR_UNKNOWN_PROPERTY
+#define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty"
 #endif
 
 typedef struct SDL_DBusContext
@@ -95,6 +101,15 @@ typedef struct SDL_DBusContext
     void (*free_string_array)(char **);
     void (*shutdown)(void);
 
+    /* New symbols for SNI and menu export */
+    int (*bus_request_name)(DBusConnection *, const char *, unsigned int, DBusError *);
+    dbus_bool_t (*message_is_method_call)(DBusMessage *, const char *, const char *);
+    DBusMessage *(*message_new_error)(DBusMessage *, const char *, const char *);
+    DBusMessage *(*message_new_method_return)(DBusMessage *);
+    dbus_bool_t (*message_iter_append_fixed_array)(DBusMessageIter *, int, const void *, int);
+    void (*message_iter_get_fixed_array)(DBusMessageIter *, void *, int *);
+    dbus_bool_t (*connection_unregister_object_path)(DBusConnection *, const char *);
+    dbus_bool_t (*connection_get_object_path_data)(DBusConnection *, const char *, void **);
 } SDL_DBusContext;
 
 extern void SDL_DBus_Init(void);
@@ -128,6 +143,15 @@ extern char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *files_
 
 extern int SDL_DBus_CameraPortalRequestAccess(void);
 
+// Menu export functions
+#define SDL_DBUS_UPDATE_MENU_FLAGS_NONE 0
+extern SDL_MenuItem *SDL_DBus_CreateMenuItem(void);
+extern const char *SDL_DBus_ExportMenu(SDL_DBusContext *ctx, DBusConnection *conn, SDL_ListNode *menu);
+extern void SDL_DBus_UpdateMenu(SDL_DBusContext *ctx, DBusConnection *conn, SDL_ListNode *menu, const char *path, void (*cb)(SDL_ListNode *, const char *, void *), void *cbdata, unsigned char flags);
+extern void SDL_DBus_RegisterMenuOpenCallback(SDL_ListNode *menu, bool (*cb)(SDL_ListNode *, void *), void *cbdata);
+extern void SDL_DBus_TransferMenuItemProperties(SDL_MenuItem *src, SDL_MenuItem *dst);
+extern void SDL_DBus_RetractMenu(SDL_DBusContext *ctx, DBusConnection *conn, const char **path);
+
 #endif // HAVE_DBUS_DBUS_H
 
 #endif // SDL_dbus_h_

+ 6 - 2
src/tray/SDL_tray_utils.c

@@ -20,11 +20,10 @@
 */
 #include "SDL_internal.h"
 
-#include "../video/SDL_sysvideo.h"
 #include "../events/SDL_events_c.h"
+#include "../video/SDL_sysvideo.h"
 #include "SDL_tray_utils.h"
 
-
 static int active_trays = 0;
 
 void SDL_RegisterTray(SDL_Tray *tray)
@@ -91,3 +90,8 @@ bool SDL_HasActiveTrays(void)
 {
     return (active_trays > 0);
 }
+
+int SDL_GetActiveTrayCount(void)
+{
+    return active_trays;
+}

+ 1 - 0
src/tray/SDL_tray_utils.h

@@ -26,3 +26,4 @@ extern void SDL_RegisterTray(SDL_Tray *tray);
 extern void SDL_UnregisterTray(SDL_Tray *tray);
 extern void SDL_CleanupTrays(void);
 extern bool SDL_HasActiveTrays(void);
+extern int SDL_GetActiveTrayCount(void);

Разница между файлами не показана из-за своего большого размера
+ 81 - 0
src/tray/unix/SDL_dbustray.c


+ 131 - 561
src/tray/unix/SDL_tray.c

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

+ 81 - 0
src/tray/unix/SDL_unixtray.h

@@ -0,0 +1,81 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2026 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"
+
+#ifndef SDL_unixtray_h_
+#define SDL_unixtray_h_
+
+#include "../../core/linux/SDL_dbus.h"
+#include "../SDL_tray_utils.h"
+
+typedef struct SDL_TrayDriver
+{
+    const char *name;
+
+    unsigned int count;
+
+    SDL_Tray *(*CreateTray)(struct SDL_TrayDriver *, SDL_PropertiesID);
+    void (*DestroyTray)(SDL_Tray *);
+    void (*UpdateTray)(SDL_Tray *);
+    void (*SetTrayIcon)(SDL_Tray *, SDL_Surface *);
+    void (*SetTrayTooltip)(SDL_Tray *, const char *);
+    SDL_TrayMenu *(*CreateTrayMenu)(SDL_Tray *);
+    SDL_TrayEntry *(*InsertTrayEntryAt)(SDL_TrayMenu *, int, const char *, SDL_TrayEntryFlags);
+    SDL_TrayMenu *(*CreateTraySubmenu)(SDL_TrayEntry *);
+    SDL_TrayMenu *(*GetTraySubmenu)(SDL_TrayEntry *);
+    SDL_TrayEntry **(*GetTrayEntries)(SDL_TrayMenu *, int *);
+    void (*RemoveTrayEntry)(SDL_TrayEntry *);
+    void (*SetTrayEntryLabel)(SDL_TrayEntry *, const char *);
+    const char *(*GetTrayEntryLabel)(SDL_TrayEntry *);
+    void (*SetTrayEntryChecked)(SDL_TrayEntry *, bool);
+    void (*ClickTrayEntry)(SDL_TrayEntry *);
+    bool (*GetTrayEntryChecked)(SDL_TrayEntry *);
+    void (*SetTrayEntryEnabled)(SDL_TrayEntry *, bool);
+    bool (*GetTrayEntryEnabled)(SDL_TrayEntry *);
+    void (*SetTrayEntryCallback)(SDL_TrayEntry *, SDL_TrayCallback, void *);
+
+    void (*DestroyDriver)(struct SDL_TrayDriver *);
+} SDL_TrayDriver;
+
+struct SDL_TrayMenu
+{
+    SDL_Tray *parent_tray;
+    SDL_TrayEntry *parent_entry;
+};
+
+struct SDL_TrayEntry
+{
+    SDL_TrayMenu *parent;
+};
+
+struct SDL_Tray
+{
+    SDL_TrayDriver *driver;
+
+    SDL_TrayMenu *menu;
+};
+
+#ifdef SDL_USE_LIBDBUS
+extern SDL_TrayDriver *SDL_Tray_CreateDBusDriver(void);
+#endif
+
+#endif // SDL_unixtray_h_

Некоторые файлы не были показаны из-за большого количества измененных файлов