From 8c024f4f3ad72e87384935d8a311f08a05d5c52a Mon Sep 17 00:00:00 2001
From: eafton <[EMAIL REDACTED]>
Date: Sat, 11 Apr 2026 00:11:38 +0300
Subject: [PATCH] SNI/DBus tray support (#15189)
---
src/SDL_list.c | 87 +++
src/SDL_list.h | 3 +
src/SDL_menu.h | 64 ++
src/core/linux/SDL_dbus.c | 1088 ++++++++++++++++++++++++++++++-
src/core/linux/SDL_dbus.h | 28 +-
src/tray/SDL_tray_utils.c | 8 +-
src/tray/SDL_tray_utils.h | 1 +
src/tray/unix/SDL_dbustray.c | 1177 ++++++++++++++++++++++++++++++++++
src/tray/unix/SDL_tray.c | 692 ++++----------------
src/tray/unix/SDL_unixtray.h | 81 +++
10 files changed, 2652 insertions(+), 577 deletions(-)
create mode 100644 src/SDL_menu.h
create mode 100644 src/tray/unix/SDL_dbustray.c
create mode 100644 src/tray/unix/SDL_unixtray.h
diff --git a/src/SDL_list.c b/src/SDL_list.c
index a17c40787afee..558a38d823c81 100644
--- a/src/SDL_list.c
+++ b/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;
+}
diff --git a/src/SDL_list.h b/src/SDL_list.h
index bdfbc35160dc1..b1e6fdbfea7fc 100644
--- a/src/SDL_list.h
+++ b/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_
diff --git a/src/SDL_menu.h b/src/SDL_menu.h
new file mode 100644
index 0000000000000..2f0d0bd5eab96
--- /dev/null
+++ b/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_
diff --git a/src/core/linux/SDL_dbus.c b/src/core/linux/SDL_dbus.c
index 9a2bef87e1e04..0532fb87c9ecb 100644
--- a/src/core/linux/SDL_dbus.c
+++ b/src/core/linux/SDL_dbus.c
@@ -18,14 +18,31 @@
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
+
#include "SDL_internal.h"
#include "SDL_dbus.h"
+#include "../../SDL_list.h"
+#include "../../SDL_menu.h"
#include "../../stdlib/SDL_vacopy.h"
#include <fcntl.h>
#include <unistd.h>
#ifdef SDL_USE_LIBDBUS
+
+typedef struct SDL_DBusMenuItem
+{
+ SDL_MenuItem _parent;
+
+ SDL_DBusContext *dbus;
+ dbus_int32_t id;
+ dbus_uint32_t revision;
+
+ /* Right click event handler */
+ void *cbdata;
+ bool (*cb)(SDL_ListNode *, void *);
+} SDL_DBusMenuItem;
+
// we never link directly to libdbus.
#define SDL_DRIVER_DBUS_DYNAMIC "libdbus-1.so.3"
static const char *dbus_library = SDL_DRIVER_DBUS_DYNAMIC;
@@ -34,6 +51,11 @@ static char *inhibit_handle = NULL;
static unsigned int screensaver_cookie = 0;
static SDL_DBusContext dbus;
+#define DBUS_MENU_INTERFACE "com.canonical.dbusmenu"
+#define DBUS_MENU_OBJECT_PATH "/Menu"
+#define SDL_DBUS_UPDATE_MENU_FLAG_DO_NOT_REPLACE (1 << 0)
+static const char *menu_introspect = "<?xml version=\"1.0\"?><node name=\"/\"><interface name=\"com.canonical.dbusmenu\"><property name=\"Version\" type=\"u\" access=\"read\"></property><property name=\"TextDirection\" type=\"s\" access=\"read\"></property><property name=\"Status\" type=\"s\" access=\"read\"></property><property name=\"IconThemePath\" type=\"as\" access=\"read\"></property><method name=\"GetLayout\"><arg type=\"i\" name=\"parentId\" direction=\"in\"></arg><arg type=\"i\" name=\"recursionDepth\" direction=\"in\"></arg><arg type=\"as\" name=\"propertyNames\" direction=\"in\"></arg><arg type=\"u\" name=\"revision\" direction=\"out\"></arg><arg type=\"(ia{sv}av)\" name=\"layout\" direction=\"out\"></arg></method><method name=\"GetGroupProperties\"><arg type=\"ai\" name=\"ids\" direction=\"in\"></arg><arg type=\"as\" name=\"propertyNames\" direction=\"in\"></arg><arg type=\"a(ia{sv})\" name=\"properties\" direction=\"out\"></arg></method><method name=\"GetProperty\"><arg type=\"i\" name=\"id\" direction=\"in\"></arg><arg type=\"s\" name=\"name\" direction=\"in\"></arg><arg type=\"v\" name=\"value\" direction=\"out\"></arg></method><method name=\"Event\"><arg type=\"i\" name=\"id\" direction=\"in\"></arg><arg type=\"s\" name=\"eventId\" direction=\"in\"></arg><arg type=\"v\" name=\"data\" direction=\"in\"></arg><arg type=\"u\" name=\"timestamp\" direction=\"in\"></arg></method><method name=\"EventGroup\"><arg type=\"a(isvu)\" name=\"events\" direction=\"in\"></arg><arg type=\"ai\" name=\"idErrors\" direction=\"out\"></arg></method><method name=\"AboutToShow\"><arg type=\"i\" name=\"id\" direction=\"in\"></arg><arg type=\"b\" name=\"needUpdate\" direction=\"out\"></arg></method><method name=\"AboutToShowGroup\"><arg type=\"ai\" name=\"ids\" direction=\"in\"></arg><arg type=\"ai\" name=\"updatesNeeded\" direction=\"out\"></arg><arg type=\"ai\" name=\"idErrors\" direction=\"out\"></arg></method><signal name=\"ItemsPropertiesUpdated\"><arg type=\"a(ia{sv})\" name=\"updatedProps\" direction=\"out\"/><arg type=\"a(ias)\" name=\"removedProps\" direction=\"out\"/></signal><signal name=\"LayoutUpdated\"><arg type=\"u\" name=\"revision\" direction=\"out\"></arg><arg type=\"i\" name=\"parent\" direction=\"out\"></arg></signal><signal name=\"ItemActivationRequested\"><arg type=\"i\" name=\"id\" direction=\"out\"></arg><arg type=\"u\" name=\"timestamp\" direction=\"out\"></arg></signal></interface></node>";
+
SDL_ELF_NOTE_DLOPEN(
"core-libdbus",
"Support for D-Bus IPC",
@@ -43,12 +65,12 @@ SDL_ELF_NOTE_DLOPEN(
static bool LoadDBUSSyms(void)
{
-#define SDL_DBUS_SYM2_OPTIONAL(TYPE, x, y) \
+#define SDL_DBUS_SYM2_OPTIONAL(TYPE, x, y) \
dbus.x = (TYPE)SDL_LoadFunction(dbus_handle, #y)
#define SDL_DBUS_SYM2(TYPE, x, y) \
if (!(dbus.x = (TYPE)SDL_LoadFunction(dbus_handle, #y))) \
- return false
+ return false
#define SDL_DBUS_SYM_OPTIONAL(TYPE, x) \
SDL_DBUS_SYM2_OPTIONAL(TYPE, x, dbus_##x)
@@ -107,6 +129,16 @@ static bool LoadDBUSSyms(void)
SDL_DBUS_SYM(void (*)(char **), free_string_array);
SDL_DBUS_SYM(void (*)(void), shutdown);
+ /* New symbols for SNI and menu export */
+ SDL_DBUS_SYM(int (*)(DBusConnection *, const char *, unsigned int, DBusError *), bus_request_name);
+ SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *, const char *), message_is_method_call);
+ SDL_DBUS_SYM(DBusMessage *(*)(DBusMessage *, const char *, const char *), message_new_error);
+ SDL_DBUS_SYM(DBusMessage *(*)(DBusMessage *), message_new_method_return);
+ SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, int, const void *, int), message_iter_append_fixed_array);
+ SDL_DBUS_SYM(void (*)(DBusMessageIter *, void *, int *), message_iter_get_fixed_array);
+ SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, const char *, void **), connection_get_object_path_data);
+ SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, const char *), connection_unregister_object_path);
+
#undef SDL_DBUS_SYM
#undef SDL_DBUS_SYM2
@@ -466,12 +498,12 @@ static bool SDL_DBus_AppendDictWithKeysAndValues(DBusMessageIter *iterInit, cons
static bool SDL_DBus_AppendDictWithKeyValue(DBusMessageIter *iterInit, const char *key, const char *value)
{
- const char *keys[1];
- const char *values[1];
+ const char *keys[1];
+ const char *values[1];
- keys[0] = key;
- values[0] = value;
- return SDL_DBus_AppendDictWithKeysAndValues(iterInit, keys, values, 1);
+ keys[0] = key;
+ values[0] = value;
+ return SDL_DBus_AppendDictWithKeysAndValues(iterInit, keys, values, 1);
}
bool SDL_DBus_OpenURI(const char *uri, const char *window_id, const char *activation_token)
@@ -753,7 +785,7 @@ char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *path_count)
* The spec doesn't define any entries yet so it's empty. */
dbus.message_iter_init_append(msg, &iter);
if (!dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iterDict) ||
- !dbus.message_iter_close_container(&iter, &iterDict)) {
+ !dbus.message_iter_close_container(&iter, &iterDict)) {
SDL_OutOfMemory();
dbus.message_unref(msg);
goto failed;
@@ -797,10 +829,10 @@ static DBusHandlerResult SDL_DBus_CameraPortalMessageHandler(DBusConnection *con
if (dbus.message_is_signal(msg, "org.freedesktop.DBus", "NameOwnerChanged")) {
if (!dbus.message_get_args(msg, data->err,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_STRING, &old,
- DBUS_TYPE_STRING, &new,
- DBUS_TYPE_INVALID)) {
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old,
+ DBUS_TYPE_STRING, &new,
+ DBUS_TYPE_INVALID)) {
data->done = true;
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
@@ -962,4 +994,1036 @@ int SDL_DBus_CameraPortalRequestAccess(void)
return -1;
}
+/* DBUSMENU LAYER BEGINS HERE */
+
+/* Special thanks to the kind Hayden Gray (thag_iceman/A1029384756) from the SDL community for his help! */
+
+static SDL_DBusMenuItem *MenuGetItemById(SDL_ListNode *menu, dbus_int32_t id)
+{
+ SDL_ListNode *cursor;
+
+ cursor = menu;
+ while (cursor) {
+ SDL_MenuItem *item;
+ SDL_DBusMenuItem *dbus_item;
+
+ item = cursor->entry;
+ dbus_item = cursor->entry;
+
+ if (dbus_item->id == id) {
+ return dbus_item;
+ }
+
+ if (item->sub_menu) {
+ SDL_DBusMenuItem *found;
+
+ found = MenuGetItemById(item->sub_menu, id);
+ if (found) {
+ return found;
+ }
+ }
+
+ cursor = cursor->next;
+ }
+ return NULL;
+}
+
+static void MenuAppendItemProperties(SDL_DBusContext *ctx, SDL_DBusMenuItem *dbus_item, DBusMessageIter *dict_iter)
+{
+ SDL_MenuItem *item;
+ DBusMessageIter entry_iter;
+ DBusMessageIter variant_iter;
+ const char *key;
+ const char *value;
+ int value_int;
+ dbus_bool_t value_bool;
+
+ item = (SDL_MenuItem *)dbus_item;
+
+ key = "label";
+ value = item->utf8 ? item->utf8 : "";
+ ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter);
+ ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key);
+ ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter);
+ ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value);
+ ctx->message_iter_close_container(&entry_iter, &variant_iter);
+ ctx->message_iter_close_container(dict_iter, &entry_iter);
+
+ key = "type";
+ if (item->type == SDL_MENU_ITEM_TYPE_SEPERATOR) {
+ value = "separator";
+ } else {
+ value = "standard";
+ }
+ ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter);
+ ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key);
+ ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter);
+ ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value);
+ ctx->message_iter_close_container(&entry_iter, &variant_iter);
+ ctx->message_iter_close_container(dict_iter, &entry_iter);
+
+ key = "enabled";
+ value_bool = !(item->flags & SDL_MENU_ITEM_FLAGS_DISABLED);
+ ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter);
+ ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key);
+ ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "b", &variant_iter);
+ ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_BOOLEAN, &value_bool);
+ ctx->message_iter_close_container(&entry_iter, &variant_iter);
+ ctx->message_iter_close_container(dict_iter, &entry_iter);
+
+ key = "visible";
+ value_bool = TRUE;
+ ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter);
+ ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key);
+ ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "b", &variant_iter);
+ ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_BOOLEAN, &value_bool);
+ ctx->message_iter_close_container(&entry_iter, &variant_iter);
+ ctx->message_iter_close_container(dict_iter, &entry_iter);
+
+ key = "toggle-type";
+ value = (item->type == SDL_MENU_ITEM_TYPE_CHECKBOX) ? "checkmark" : "";
+ ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter);
+ ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key);
+ ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter);
+ ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value);
+ ctx->message_iter_close_container(&entry_iter, &variant_iter);
+ ctx->message_iter_close_container(dict_iter, &entry_iter);
+
+ key = "toggle-state";
+ value_int = (item->flags & SDL_MENU_ITEM_FLAGS_CHECKED) ? 1 : 0;
+ ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter);
+ ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key);
+ ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "i", &variant_iter);
+ ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_INT32, &value_int);
+ ctx->message_iter_close_container(&entry_iter, &variant_iter);
+ ctx->message_iter_close_container(dict_iter, &entry_iter);
+
+ key = "children-display";
+ value = item->sub_menu ? "submenu" : "";
+ ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter);
+ ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key);
+ ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter);
+ ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value);
+ ctx->message_iter_close_container(&entry_iter, &variant_iter);
+ ctx->message_iter_close_container(dict_iter, &entry_iter);
+}
+
+static void MenuAppendItem(SDL_DBusContext *ctx, SDL_DBusMenuItem *dbus_item, DBusMessageIter *array_iter, int depth)
+{
+ SDL_MenuItem *item;
+ DBusMessageIter struct_iter, dict_iter, children_iter;
+
+ item = (SDL_MenuItem *)dbus_item;
+
+ ctx->message_iter_open_container(array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter);
+ ctx->message_iter_append_basic(&struct_iter, DBUS_TYPE_INT32, &dbus_item->id);
+ ctx->message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter);
+ MenuAppendItemProperties(ctx, dbus_item, &dict_iter);
+ ctx->message_iter_close_container(&struct_iter, &dict_iter);
+ ctx->message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "v", &children_iter);
+
+ if (item->sub_menu && depth > 0) {
+ SDL_ListNode *cursor;
+
+ cursor = item->sub_menu;
+ while (cursor) {
+ SDL_DBusMenuItem *child;
+ DBusMessageIter variant_iter;
+
+ child = cursor->entry;
+ ctx->message_iter_open_container(&children_iter, DBUS_TYPE_VARIANT, "(ia{sv}av)", &variant_iter);
+ MenuAppendItem(ctx, child, &variant_iter, depth - 1);
+ ctx->message_iter_close_container(&children_iter, &variant_iter);
+ cursor = cursor->next;
+ }
+ }
+
+ ctx->message_iter_close_container(&struct_iter, &children_iter);
+ ctx->message_iter_close_container(array_iter, &struct_iter);
+}
+
+static DBusHandlerResult MenuHandleGetLayout(SDL_DBusContext *ctx, SDL_ListNode *menu, DBusConnection *conn, DBusMessage *msg)
+{
+ DBusMessage *reply;
+ DBusMessageIter reply_iter, struct_iter, dict_iter, children_iter;
+ DBusMessageIter entry_iter, variant_iter;
+ DBusMessageIter args;
+ const char *key;
+ const char *val;
+ dbus_int32_t parent_id;
+ dbus_int32_t recursion_depth;
+ dbus_int32_t root_id;
+ dbus_uint32_t revision;
+
+ ctx->message_iter_init(msg, &args);
+ if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_INT32) {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ ctx->message_iter_get_basic(&args, &parent_id);
+ ctx->message_iter_next(&args);
+ if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_INT32) {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ ctx->message_iter_get_basic(&args, &recursion_depth);
+ if (recursion_depth == -1) {
+ recursion_depth = 100;
+ }
+
+ reply = ctx->message_new_method_return(msg);
+ ctx->message_iter_init_append(reply, &reply_iter);
+
+ revision = 0;
+ if (menu) {
+ if (menu->entry) {
+ revision = ((SDL_DBusMenuItem *)menu->entry)->revision;
+ }
+ }
+ ctx->message_iter_append_basic(&reply_iter, DBUS_TYPE_UINT32, &revision);
+
+ ctx->message_iter_open_container(&reply_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter);
+
+ root_id = 0;
+ ctx->message_iter_append_basic(&struct_iter, DBUS_TYPE_INT32, &root_id);
+
+ key = "children-display";
+ val = "submenu";
+ ctx->message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter);
+ ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter);
+ ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key);
+ ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter);
+ ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &val);
+ ctx->message_iter_close_container(&entry_iter, &variant_iter);
+ ctx->message_iter_close_container(&dict_iter, &entry_iter);
+ ctx->message_iter_close_container(&struct_iter, &dict_iter);
+
+ ctx->message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "v", &children_iter);
+ if (!parent_id && menu) {
+ SDL_ListNode *cursor;
+
+ cursor = menu;
+ while (cursor) {
+ SDL_MenuItem *item;
+ SDL_DBusMenuItem *dbus_item;
+ DBusMessageIter cvariant_iter, item_struct, item_dict, item_children;
+
+ item = cursor->entry;
+ dbus_item = cursor->entry;
+ ctx->message_iter_open_container(&children_iter, DBUS_TYPE_VARIANT, "(ia{sv}av)", &cvariant_iter);
+ ctx->message_iter_open_container(&cvariant_iter, DBUS_TYPE_STRUCT, NULL, &item_struct);
+ ctx->message_iter_append_basic(&item_struct, DBUS_TYPE_INT32, &dbus_item->id);
+ ctx->message_iter_open_container(&item_struct, DBUS_TYPE_ARRAY, "{sv}", &item_dict);
+ MenuAppendItemProperties(ctx, dbus_item, &item_dict);
+ ctx->message_iter_close_container(&item_struct, &item_dict);
+ ctx->message_iter_open_container(&item_struct, DBUS_TYPE_ARRAY, "v", &item_children);
+ if (item->sub_menu && recursion_depth) {
+ SDL_ListNode *child_cursor;
+
+ child_cursor = item->sub_menu;
+ while (child_cursor) {
+ SDL_DBusMenuItem *child;
+ DBusMessageIter child_variant;
+
+ child = child_cursor->entry;
+ ctx->message_iter_open_container(&item_children, DBUS_TYPE_VARIANT, "(ia{sv}av)", &child_variant);
+ MenuAppendItem(ctx, child, &child_variant, recursion_depth - 1);
+ ctx->message_iter_close_container(&item_children, &child_variant);
+ child_cursor = child_cursor->next;
+ }
+ }
+ ctx->message_iter_close_container(&item_struct, &item_children);
+ ctx->message_iter_close_container(&cvariant_iter, &item_struct);
+
+ ctx->message_iter_close_container(&children_iter, &cvariant_iter);
+ cursor = cursor->next;
+ }
+ } else if (parent_id) {
+ SDL_DBusMenuItem *parent;
+ SDL_MenuItem *parent_item;
+
+ parent = MenuGetItemById(menu, parent_id);
+ parent_item = (SDL_MenuItem *)parent;
+ if (parent_item && parent_item->sub_menu) {
+ SDL_ListNode *cursor;
+
+ cursor = parent_item->sub_menu;
+ while (cursor) {
+ SDL_MenuItem *item;
+ SDL_DBusMenuItem *dbus_item;
+ DBusMessageIter cvariant_iter, item_struct, item_dict, item_children;
+
+ item = cursor->entry;
+ dbus_item = cursor->entry;
+ ctx->message_iter_open_container(&children_iter, DBUS_TYPE_VARIANT, "(ia{sv}av)", &cvariant_iter);
+ ctx->message_iter_open_container(&cvariant_iter, DBUS_TYPE_STRUCT, NULL, &item_struct);
+ ctx->message_iter_append_basic(&item_struct, DBUS_TYPE_INT32, &dbus_item->id);
+ ctx->message_iter_open_container(&item_struct, DBUS_TYPE_ARRAY, "{sv}", &item_dict);
+ MenuAppendItemProperties(ctx, dbus_item, &item_dict);
+ ctx->message_iter_close_container(&item_struct, &item_dict);
+ ctx->message_iter_open_container(&item_struct, DBUS_TYPE_ARRAY, "v", &item_children);
+ if (item->sub_menu && recursion_depth) {
+ SDL_ListNode *child_cursor;
+
+ child_cursor = item->sub_menu;
+ while (child_cursor) {
+ SDL_DBusMenuItem *child;
+ DBusMessageIter child_variant;
+
+ child = child_cursor->entry;
+ ctx->message_iter_open_container(&item_children, DBUS_TYPE_VARIANT, "(ia{sv}av)", &child_variant);
+ MenuAppendItem(ctx, child, &child_variant, recursion_depth - 1);
+ ctx->message_iter_close_container(&item_children, &child_variant);
+ child_cursor = child_cursor->next;
+ }
+ }
+ ctx->message_iter_close_container(&item_struct, &item_children);
+ ctx->message_iter_close_container(&cvariant_iter, &item_struct);
+
+ ctx->message_iter_close_container(&children_iter, &cvariant_iter);
+
+ cursor = cursor->next;
+ }
+ }
+ }
+ ctx->message_iter_close_container(&struct_iter, &children_iter);
+ ctx->message_iter_close_container(&reply_iter, &struct_iter);
+
+ ctx->connection_send(conn, reply, NULL);
+ ctx->message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult MenuHandleEvent(SDL_DBusContext *ctx, SDL_ListNode *menu, DBusConnection *conn, DBusMessage *msg)
+{
+ SDL_MenuItem *item;
+ SDL_DBusMenuItem *dbus_item;
+ DBusMessage *reply;
+ const char *event_id;
+ DBusMessageIter args;
+ Uint32 id;
+
+ ctx->message_iter_init(msg, &args);
+ ctx->message_iter_get_basic(&args, &id);
+ ctx->message_iter_next(&args);
+ ctx->message_iter_get_basic(&args, &event_id);
+
+ item = NULL;
+ dbus_item = NULL;
+ if (!SDL_strcmp(event_id, "clicked")) {
+ dbus_item = MenuGetItemById(menu, id);
+ item = (SDL_MenuItem *)dbus_item;
+ }
+
+ reply = ctx->message_new_method_return(msg);
+ ctx->connection_send(conn, reply, NULL);
+ ctx->message_unref(reply);
+
+ if (item) {
+ if (item->type == SDL_MENU_ITEM_TYPE_CHECKBOX) {
+ item->flags ^= SDL_MENU_ITEM_FLAGS_CHECKED;
+ SDL_DBus_UpdateMenu(ctx, conn, menu, NULL, NULL, NULL, SDL_DBUS_UPDATE_MENU_FLAG_DO_NOT_REPLACE);
+ }
+
+ if (item->cb) {
+ item->cb(item, item->cb_data);
+ }
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult MenuHandleEventGroup(SDL_DBusContext *ctx, SDL_ListNode *menu, DBusConnection *conn, DBusMessage *msg)
+{
+ DBusMessage *reply;
+ DBusMessageIter reply_iter, id_errors_iter;
+ DBusMessageIter args, array_iter;
+
+ ctx->message_iter_init(msg, &args);
+ if (ctx->message_iter_get_arg_type(&args) == DBUS_TYPE_ARRAY) {
+ ctx->message_iter_recurse(&args, &array_iter);
+ while (ctx->message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRUCT) {
+ DBusMessageIter struct_iter;
+ const char *event_id;
+ dbus_int32_t id;
+
+ ctx->message_iter_recurse(&array_iter, &struct_iter);
+ if (ctx->message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_INT32) {
+ ctx->message_iter_get_basic(&struct_iter, &id);
+ ctx->message_iter_next(&struct_iter);
+ if (ctx->message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_STRING) {
+ ctx->message_iter_get_basic(&struct_iter, &event_id);
+
+ if (!SDL_strcmp(event_id, "clicked")) {
+ SDL_DBusMenuItem *dbus_item;
+ SDL_MenuItem *item;
+
+ dbus_item = MenuGetItemById(menu, id);
+ item = (SDL_MenuItem *)dbus_item;
+
+ if (item) {
+ if (item->type == SDL_MENU_ITEM_TYPE_CHECKBOX) {
+ item->flags ^= SDL_MENU_ITEM_FLAGS_CHECKED;
+ SDL_DBus_UpdateMenu(ctx, conn, menu, NULL, NULL, NULL, SDL_DBUS_UPDATE_MENU_FLAG_DO_NOT_REPLACE);
+ }
+
+ if (item->cb) {
+ item->cb(item, item->cb_data);
+ }
+ }
+ }
+ }
+ }
+ ctx->message_iter_next(&array_iter);
+ }
+ }
+
+ reply = ctx->message_new_method_return(msg);
+ ctx->message_iter_init_append(reply, &reply_iter);
+ ctx->message_iter_open_container(&reply_iter, DBUS_TYPE_ARRAY, "i", &id_errors_iter);
+ ctx->message_iter_close_container(&reply_iter, &id_errors_iter);
+ ctx->connection_send(conn, reply, NULL);
+ ctx->message_unref(reply);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult MenuHandleGetProperty(SDL_DBusContext *ctx, SDL_ListNode *menu, DBusConnection *conn, DBusMessage *msg)
+{
+ SDL_MenuItem *item;
+ SDL_DBusMenuItem *dbus_item;
+ DBusMessage *reply;
+ const char *property;
+ const char *val;
+ DBusMessageIter args;
+ DBusMessageIter iter, variant_iter;
+ dbus_int32_t id;
+ int int_val;
+ dbus_bool_t bool_val;
+
+ ctx->message_iter_init(msg, &args);
+ if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_INT32) {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ ctx->message_iter_get_basic(&args, &id);
+ ctx->message_iter_next(&args);
+ if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ ctx->message_iter_get_basic(&args, &property);
+
+ dbus_item = MenuGetItemById(menu, id);
+ item = (SDL_MenuItem *)dbus_item;
+ if (!item) {
+ DBusMessage *error;
+
+ error = ctx->message_new_error(msg, "com.canonical.dbusmenu.Error", "Item not found");
+ ctx->connection_send(conn, error, NULL);
+ ctx->message_unref(error);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ reply = ctx->message_new_method_return(msg);
+ ctx->message_iter_init_append(reply, &iter);
+ if (!SDL_strcmp(property, "label")) {
+ val = item->utf8 ? item->utf8 : "";
+ ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter);
+ ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &val);
+ ctx->message_iter_close_container(&iter, &variant_iter);
+ } else if (!SDL_strcmp(property, "enab
(Patch may be truncated, please check the link at the top of this post.)