Atlas - SDL_dbus.c
Home / ext / SDL / src / core / linux Lines: 1 | Size: 81788 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22#include "SDL_internal.h" 23 24#include "../../SDL_list.h" 25#include "../../SDL_menu.h" 26#include "../../stdlib/SDL_vacopy.h" 27#include "SDL_dbus.h" 28 29#include <fcntl.h> 30#include <unistd.h> 31 32#ifdef SDL_USE_LIBDBUS 33 34typedef struct SDL_DBusMenuItem 35{ 36 SDL_MenuItem _parent; 37 38 SDL_DBusContext *dbus; 39 dbus_int32_t id; 40 dbus_uint32_t revision; 41 42 /* Right click event handler */ 43 void *cbdata; 44 bool (*cb)(SDL_ListNode *, void *); 45} SDL_DBusMenuItem; 46 47// we never link directly to libdbus. 48#define SDL_DRIVER_DBUS_DYNAMIC "libdbus-1.so.3" 49static const char *dbus_library = SDL_DRIVER_DBUS_DYNAMIC; 50static SDL_SharedObject *dbus_handle = NULL; 51static char *inhibit_handle = NULL; 52static unsigned int screensaver_cookie = 0; 53static SDL_DBusContext dbus; 54 55#define DBUS_MENU_INTERFACE "com.canonical.dbusmenu" 56#define DBUS_MENU_OBJECT_PATH "/StatusNotifierItem/menu" 57 58#define SDL_DBUS_UPDATE_MENU_FLAG_DO_NOT_REPLACE (1 << 0) 59static 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>"; 60 61SDL_ELF_NOTE_DLOPEN( 62 "core-libdbus", 63 "Support for D-Bus IPC", 64 SDL_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, 65 SDL_DRIVER_DBUS_DYNAMIC) 66 67static bool LoadDBUSSyms(void) 68{ 69#define SDL_DBUS_SYM2_OPTIONAL(TYPE, x, y) \ 70 dbus.x = (TYPE)SDL_LoadFunction(dbus_handle, #y) 71 72#define SDL_DBUS_SYM2(TYPE, x, y) \ 73 if (!(dbus.x = (TYPE)SDL_LoadFunction(dbus_handle, #y))) \ 74 return false 75 76#define SDL_DBUS_SYM_OPTIONAL(TYPE, x) \ 77 SDL_DBUS_SYM2_OPTIONAL(TYPE, x, dbus_##x) 78 79#define SDL_DBUS_SYM(TYPE, x) \ 80 SDL_DBUS_SYM2(TYPE, x, dbus_##x) 81 82 SDL_DBUS_SYM(DBusConnection *(*)(DBusBusType, DBusError *), bus_get_private); 83 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusError *), bus_register); 84 SDL_DBUS_SYM(void (*)(DBusConnection *, const char *, DBusError *), bus_add_match); 85 SDL_DBUS_SYM(void (*)(DBusConnection *, const char *, DBusError *), bus_remove_match); 86 SDL_DBUS_SYM(const char *(*)(DBusConnection *), bus_get_unique_name); 87 SDL_DBUS_SYM(DBusConnection *(*)(const char *, DBusError *), connection_open_private); 88 SDL_DBUS_SYM(void (*)(DBusConnection *, dbus_bool_t), connection_set_exit_on_disconnect); 89 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *), connection_get_is_connected); 90 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusHandleMessageFunction, void *, DBusFreeFunction), connection_add_filter); 91 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusHandleMessageFunction, void *), connection_remove_filter); 92 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, const char *, const DBusObjectPathVTable *, void *, DBusError *), connection_try_register_object_path); 93 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusMessage *, dbus_uint32_t *), connection_send); 94 SDL_DBUS_SYM(DBusMessage *(*)(DBusConnection *, DBusMessage *, int, DBusError *), connection_send_with_reply_and_block); 95 SDL_DBUS_SYM(void (*)(DBusConnection *), connection_close); 96 SDL_DBUS_SYM(void (*)(DBusConnection *), connection_ref); 97 SDL_DBUS_SYM(void (*)(DBusConnection *), connection_unref); 98 SDL_DBUS_SYM(void (*)(DBusConnection *), connection_flush); 99 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, int), connection_read_write); 100 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, int), connection_read_write_dispatch); 101 SDL_DBUS_SYM(DBusDispatchStatus (*)(DBusConnection *), connection_dispatch); 102 SDL_DBUS_SYM(dbus_bool_t (*)(int), type_is_fixed); 103 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *, const char *), message_is_signal); 104 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *), message_has_path); 105 SDL_DBUS_SYM(DBusMessage *(*)(const char *, const char *, const char *, const char *), message_new_method_call); 106 SDL_DBUS_SYM(DBusMessage *(*)(const char *, const char *, const char *), message_new_signal); 107 SDL_DBUS_SYM(void (*)(DBusMessage *, dbus_bool_t), message_set_no_reply); 108 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, int, ...), message_append_args); 109 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, int, va_list), message_append_args_valist); 110 SDL_DBUS_SYM(void (*)(DBusMessage *, DBusMessageIter *), message_iter_init_append); 111 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, int, const char *, DBusMessageIter *), message_iter_open_container); 112 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, int, const void *), message_iter_append_basic); 113 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, DBusMessageIter *), message_iter_close_container); 114 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, DBusError *, int, ...), message_get_args); 115 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, DBusError *, int, va_list), message_get_args_valist); 116 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, DBusMessageIter *), message_iter_init); 117 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *), message_iter_next); 118 SDL_DBUS_SYM(void (*)(DBusMessageIter *, void *), message_iter_get_basic); 119 SDL_DBUS_SYM(int (*)(DBusMessageIter *), message_iter_get_arg_type); 120 SDL_DBUS_SYM(void (*)(DBusMessageIter *, DBusMessageIter *), message_iter_recurse); 121 SDL_DBUS_SYM(void (*)(DBusMessage *), message_unref); 122 SDL_DBUS_SYM(dbus_bool_t (*)(void), threads_init_default); 123 SDL_DBUS_SYM(void (*)(DBusError *), error_init); 124 SDL_DBUS_SYM(dbus_bool_t (*)(const DBusError *), error_is_set); 125 SDL_DBUS_SYM(dbus_bool_t (*)(const DBusError *, const char *), error_has_name); 126 SDL_DBUS_SYM(void (*)(DBusError *), error_free); 127 SDL_DBUS_SYM(char *(*)(void), get_local_machine_id); 128 SDL_DBUS_SYM_OPTIONAL(char *(*)(DBusError *), try_get_local_machine_id); 129 SDL_DBUS_SYM(void (*)(void *), free); 130 SDL_DBUS_SYM(void (*)(char **), free_string_array); 131 SDL_DBUS_SYM(void (*)(void), shutdown); 132 133 /* New symbols for SNI and menu export */ 134 SDL_DBUS_SYM(int (*)(DBusConnection *, const char *, unsigned int, DBusError *), bus_request_name); 135 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *, const char *), message_is_method_call); 136 SDL_DBUS_SYM(DBusMessage *(*)(DBusMessage *, const char *, const char *), message_new_error); 137 SDL_DBUS_SYM(DBusMessage *(*)(DBusMessage *), message_new_method_return); 138 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, int, const void *, int), message_iter_append_fixed_array); 139 SDL_DBUS_SYM(void (*)(DBusMessageIter *, void *, int *), message_iter_get_fixed_array); 140 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, const char *, void **), connection_get_object_path_data); 141 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, const char *), connection_unregister_object_path); 142 143#undef SDL_DBUS_SYM 144#undef SDL_DBUS_SYM2 145 146 return true; 147} 148 149static void UnloadDBUSLibrary(void) 150{ 151 if (dbus_handle) { 152 SDL_UnloadObject(dbus_handle); 153 dbus_handle = NULL; 154 } 155} 156 157static bool LoadDBUSLibrary(void) 158{ 159 bool result = true; 160 if (!dbus_handle) { 161 dbus_handle = SDL_LoadObject(dbus_library); 162 if (!dbus_handle) { 163 result = false; 164 // Don't call SDL_SetError(): SDL_LoadObject already did. 165 } else { 166 result = LoadDBUSSyms(); 167 if (!result) { 168 UnloadDBUSLibrary(); 169 } 170 } 171 } 172 return result; 173} 174 175static SDL_InitState dbus_init; 176 177void SDL_DBus_Init(void) 178{ 179 static bool is_dbus_available = true; 180 181 if (!is_dbus_available) { 182 return; // don't keep trying if this fails. 183 } 184 185 if (!SDL_ShouldInit(&dbus_init)) { 186 return; 187 } 188 189 if (!LoadDBUSLibrary()) { 190 goto error; 191 } 192 193 if (!dbus.threads_init_default()) { 194 goto error; 195 } 196 197 DBusError err; 198 dbus.error_init(&err); 199 // session bus is required 200 201 dbus.session_conn = dbus.bus_get_private(DBUS_BUS_SESSION, &err); 202 if (dbus.error_is_set(&err)) { 203 dbus.error_free(&err); 204 goto error; 205 } 206 dbus.connection_set_exit_on_disconnect(dbus.session_conn, 0); 207 208 // system bus is optional 209 dbus.system_conn = dbus.bus_get_private(DBUS_BUS_SYSTEM, &err); 210 if (!dbus.error_is_set(&err)) { 211 dbus.connection_set_exit_on_disconnect(dbus.system_conn, 0); 212 } 213 214 dbus.error_free(&err); 215 SDL_SetInitialized(&dbus_init, true); 216 return; 217 218error: 219 is_dbus_available = false; 220 SDL_SetInitialized(&dbus_init, true); 221 SDL_DBus_Quit(); 222} 223 224void SDL_DBus_Quit(void) 225{ 226 if (!SDL_ShouldQuit(&dbus_init)) { 227 return; 228 } 229 230 if (dbus.system_conn) { 231 dbus.connection_close(dbus.system_conn); 232 dbus.connection_unref(dbus.system_conn); 233 } 234 if (dbus.session_conn) { 235 dbus.connection_close(dbus.session_conn); 236 dbus.connection_unref(dbus.session_conn); 237 } 238 239 if (SDL_GetHintBoolean(SDL_HINT_SHUTDOWN_DBUS_ON_QUIT, false)) { 240 if (dbus.shutdown) { 241 dbus.shutdown(); 242 } 243 244 UnloadDBUSLibrary(); 245 } else { 246 /* Leaving libdbus loaded when skipping dbus_shutdown() avoids 247 * spurious leak warnings from LeakSanitizer on internal D-Bus 248 * allocations that would be freed by dbus_shutdown(). */ 249 dbus_handle = NULL; 250 } 251 252 SDL_zero(dbus); 253 if (inhibit_handle) { 254 SDL_free(inhibit_handle); 255 inhibit_handle = NULL; 256 } 257 258 SDL_SetInitialized(&dbus_init, false); 259} 260 261SDL_DBusContext *SDL_DBus_GetContext(void) 262{ 263 if (!dbus_handle || !dbus.session_conn) { 264 SDL_DBus_Init(); 265 } 266 267 return (dbus_handle && dbus.session_conn) ? &dbus : NULL; 268} 269 270static bool SDL_DBus_CallMethodInternal(DBusConnection *conn, DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *method, va_list ap) 271{ 272 bool result = false; 273 274 if (conn) { 275 DBusMessage *msg = dbus.message_new_method_call(node, path, interface, method); 276 if (msg) { 277 int firstarg; 278 va_list ap_reply; 279 va_copy(ap_reply, ap); // copy the arg list so we don't compete with D-Bus for it 280 firstarg = va_arg(ap, int); 281 if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) { 282 DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL); 283 if (reply) { 284 // skip any input args, get to output args. 285 while ((firstarg = va_arg(ap_reply, int)) != DBUS_TYPE_INVALID) { 286 // we assume D-Bus already validated all this. 287 { 288 void *dumpptr = va_arg(ap_reply, void *); 289 (void)dumpptr; 290 } 291 if (firstarg == DBUS_TYPE_ARRAY) { 292 { 293 const int dumpint = va_arg(ap_reply, int); 294 (void)dumpint; 295 } 296 } 297 } 298 firstarg = va_arg(ap_reply, int); 299 if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_get_args_valist(reply, NULL, firstarg, ap_reply)) { 300 result = true; 301 } 302 if (save_reply) { 303 *save_reply = reply; 304 } else { 305 dbus.message_unref(reply); 306 } 307 } 308 } 309 va_end(ap_reply); 310 dbus.message_unref(msg); 311 } 312 } 313 314 return result; 315} 316 317bool SDL_DBus_CallMethodOnConnection(DBusConnection *conn, DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *method, ...) 318{ 319 bool result; 320 va_list ap; 321 va_start(ap, method); 322 result = SDL_DBus_CallMethodInternal(conn, save_reply, node, path, interface, method, ap); 323 va_end(ap); 324 return result; 325} 326 327bool SDL_DBus_CallMethod(DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *method, ...) 328{ 329 bool result; 330 va_list ap; 331 va_start(ap, method); 332 result = SDL_DBus_CallMethodInternal(dbus.session_conn, save_reply, node, path, interface, method, ap); 333 va_end(ap); 334 return result; 335} 336 337static bool SDL_DBus_CallVoidMethodInternal(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap) 338{ 339 bool result = false; 340 341 if (conn) { 342 DBusMessage *msg = dbus.message_new_method_call(node, path, interface, method); 343 if (msg) { 344 int firstarg = va_arg(ap, int); 345 if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) { 346 dbus.message_set_no_reply(msg, true); 347 if (dbus.connection_send(conn, msg, NULL)) { 348 dbus.connection_flush(conn); 349 result = true; 350 } 351 } 352 353 dbus.message_unref(msg); 354 } 355 } 356 357 return result; 358} 359 360bool SDL_DBus_CallVoidMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...) 361{ 362 bool result; 363 va_list ap; 364 va_start(ap, method); 365 result = SDL_DBus_CallVoidMethodInternal(conn, node, path, interface, method, ap); 366 va_end(ap); 367 return result; 368} 369 370bool SDL_DBus_CallVoidMethod(const char *node, const char *path, const char *interface, const char *method, ...) 371{ 372 bool result; 373 va_list ap; 374 va_start(ap, method); 375 result = SDL_DBus_CallVoidMethodInternal(dbus.session_conn, node, path, interface, method, ap); 376 va_end(ap); 377 return result; 378} 379 380static bool SDL_DBus_CallWithBasicReply(DBusConnection *conn, DBusMessage **save_reply, DBusMessage *msg, const int expectedtype, void *result) 381{ 382 bool retval = false; 383 384 // Make sure we're not looking up strings here, otherwise we'd have to save and return the reply 385 SDL_assert(save_reply == NULL || *save_reply == NULL); 386 SDL_assert(save_reply != NULL || dbus.type_is_fixed(expectedtype)); 387 388 DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL); 389 if (reply) { 390 DBusMessageIter iter, actual_iter; 391 dbus.message_iter_init(reply, &iter); 392 if (dbus.message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT) { 393 dbus.message_iter_recurse(&iter, &actual_iter); 394 } else { 395 actual_iter = iter; 396 } 397 398 if (dbus.message_iter_get_arg_type(&actual_iter) == expectedtype) { 399 dbus.message_iter_get_basic(&actual_iter, result); 400 retval = true; 401 } 402 403 if (save_reply) { 404 *save_reply = reply; 405 } else { 406 dbus.message_unref(reply); 407 } 408 } 409 410 return retval; 411} 412 413bool SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *property, int expectedtype, void *result) 414{ 415 bool retval = false; 416 417 if (conn) { 418 DBusMessage *msg = dbus.message_new_method_call(node, path, "org.freedesktop.DBus.Properties", "Get"); 419 if (msg) { 420 if (dbus.message_append_args(msg, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { 421 retval = SDL_DBus_CallWithBasicReply(conn, save_reply, msg, expectedtype, result); 422 } 423 dbus.message_unref(msg); 424 } 425 } 426 427 return retval; 428} 429 430bool SDL_DBus_QueryProperty(DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *property, int expectedtype, void *result) 431{ 432 return SDL_DBus_QueryPropertyOnConnection(dbus.session_conn, save_reply, node, path, interface, property, expectedtype, result); 433} 434 435void SDL_DBus_FreeReply(DBusMessage **saved_reply) 436{ 437 DBusMessage *reply = *saved_reply; 438 if (reply) { 439 dbus.message_unref(reply); 440 *saved_reply = NULL; 441 } 442} 443 444void SDL_DBus_ScreensaverTickle(void) 445{ 446 if (screensaver_cookie == 0 && !inhibit_handle) { // no need to tickle if we're inhibiting. 447 // org.gnome.ScreenSaver is the legacy interface, but it'll either do nothing or just be a second harmless tickle on newer systems, so we leave it for now. 448 SDL_DBus_CallVoidMethod("org.gnome.ScreenSaver", "/org/gnome/ScreenSaver", "org.gnome.ScreenSaver", "SimulateUserActivity", DBUS_TYPE_INVALID); 449 SDL_DBus_CallVoidMethod("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver", "SimulateUserActivity", DBUS_TYPE_INVALID); 450 } 451} 452 453static bool SDL_DBus_AppendDictWithKeysAndValues(DBusMessageIter *iterInit, const char **keys, const char **values, int count) 454{ 455 DBusMessageIter iterDict; 456 457 if (!dbus.message_iter_open_container(iterInit, DBUS_TYPE_ARRAY, "{sv}", &iterDict)) { 458 goto failed; 459 } 460 461 for (int i = 0; i < count; i++) { 462 DBusMessageIter iterEntry, iterValue; 463 const char *key = keys[i]; 464 const char *value = values[i]; 465 466 if (!dbus.message_iter_open_container(&iterDict, DBUS_TYPE_DICT_ENTRY, NULL, &iterEntry)) { 467 goto failed; 468 } 469 470 if (!dbus.message_iter_append_basic(&iterEntry, DBUS_TYPE_STRING, &key)) { 471 goto failed; 472 } 473 474 if (!dbus.message_iter_open_container(&iterEntry, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &iterValue)) { 475 goto failed; 476 } 477 478 if (!dbus.message_iter_append_basic(&iterValue, DBUS_TYPE_STRING, &value)) { 479 goto failed; 480 } 481 482 if (!dbus.message_iter_close_container(&iterEntry, &iterValue) || !dbus.message_iter_close_container(&iterDict, &iterEntry)) { 483 goto failed; 484 } 485 } 486 487 if (!dbus.message_iter_close_container(iterInit, &iterDict)) { 488 goto failed; 489 } 490 491 return true; 492 493failed: 494 /* message_iter_abandon_container_if_open() and message_iter_abandon_container() might be 495 * missing if libdbus is too old. Instead, we just return without cleaning up any eventual 496 * open container */ 497 return false; 498} 499 500static bool SDL_DBus_AppendDictWithKeyValue(DBusMessageIter *iterInit, const char *key, const char *value) 501{ 502 const char *keys[1]; 503 const char *values[1]; 504 505 keys[0] = key; 506 values[0] = value; 507 return SDL_DBus_AppendDictWithKeysAndValues(iterInit, keys, values, 1); 508} 509 510bool SDL_DBus_OpenURI(const char *uri, const char *window_id, const char *activation_token) 511{ 512 static const char *bus_name = "org.freedesktop.portal.Desktop"; 513 static const char *path = "/org/freedesktop/portal/desktop"; 514 static const char *interface = "org.freedesktop.portal.OpenURI"; 515 516 if (!dbus.session_conn) { 517 /* We either lost connection to the session bus or were not able to 518 * load the D-Bus library at all. 519 */ 520 return false; 521 } 522 523 DBusMessageIter iterInit; 524 DBusMessage *msg = NULL; 525 int fd = -1; 526 bool ret = false; 527 const bool has_file_scheme = SDL_strncasecmp(uri, "file:/", 6) == 0; 528 529 // The OpenURI method can't open 'file://' URIs or local paths, so OpenFile must be used instead. 530 if (has_file_scheme || !SDL_IsURI(uri)) { 531 char *decoded_path = NULL; 532 533 // Decode the path if it is a URI. 534 if (has_file_scheme) { 535 const size_t len = SDL_strlen(uri) + 1; 536 decoded_path = SDL_malloc(len); 537 if (!decoded_path) { 538 goto done; 539 } 540 if (SDL_URIToLocal(uri, decoded_path) < 0) { 541 SDL_free(decoded_path); 542 goto done; 543 } 544 uri = decoded_path; 545 } 546 fd = open(uri, O_RDWR | O_CLOEXEC); 547 SDL_free(decoded_path); 548 if (fd >= 0) { 549 msg = dbus.message_new_method_call(bus_name, path, interface, "OpenFile"); 550 } 551 } else { 552 msg = dbus.message_new_method_call(bus_name, path, interface, "OpenURI"); 553 } 554 if (!msg) { 555 goto done; 556 } 557 558 dbus.message_iter_init_append(msg, &iterInit); 559 560 if (!window_id) { 561 window_id = ""; 562 } 563 if (!dbus.message_iter_append_basic(&iterInit, DBUS_TYPE_STRING, &window_id)) { 564 goto done; 565 } 566 567 if (fd >= 0) { 568 if (!dbus.message_iter_append_basic(&iterInit, DBUS_TYPE_UNIX_FD, &fd)) { 569 goto done; 570 } 571 } else { 572 if (!dbus.message_iter_append_basic(&iterInit, DBUS_TYPE_STRING, &uri)) { 573 goto done; 574 } 575 } 576 577 if (activation_token) { 578 if (!SDL_DBus_AppendDictWithKeyValue(&iterInit, "activation_token", activation_token)) { 579 goto done; 580 } 581 } else { 582 // The array must be in the parameter list, even if empty. 583 DBusMessageIter iterArray; 584 if (!dbus.message_iter_open_container(&iterInit, DBUS_TYPE_ARRAY, "{sv}", &iterArray)) { 585 goto done; 586 } 587 if (!dbus.message_iter_close_container(&iterInit, &iterArray)) { 588 goto done; 589 } 590 } 591 592 { 593 DBusMessage *reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, -1, NULL); 594 if (reply) { 595 ret = true; 596 dbus.message_unref(reply); 597 } 598 } 599 600done: 601 if (msg) { 602 dbus.message_unref(msg); 603 } 604 605 // The file descriptor is duplicated by D-Bus, so it can be closed on this end. 606 if (fd >= 0) { 607 close(fd); 608 } 609 610 return ret; 611} 612 613bool SDL_DBus_ScreensaverInhibit(bool inhibit) 614{ 615 static bool interface_unavailable = false; 616 const char *default_inhibit_reason = "Playing a game"; 617 618 // If the interface was previously queried and is unavailable, return false. 619 if (interface_unavailable) { 620 return false; 621 } 622 623 if ((inhibit && (screensaver_cookie != 0 || inhibit_handle)) || (!inhibit && (screensaver_cookie == 0 && !inhibit_handle))) { 624 return true; 625 } 626 627 if (!dbus.session_conn) { 628 /* We either lost connection to the session bus or were not able to 629 * load the D-Bus library at all. */ 630 return false; 631 } 632 633 if (SDL_GetSandbox() != SDL_SANDBOX_NONE) { 634 const char *bus_name = "org.freedesktop.portal.Desktop"; 635 const char *path = "/org/freedesktop/portal/desktop"; 636 const char *interface = "org.freedesktop.portal.Inhibit"; 637 const char *window = ""; // As a future improvement we could gather the X11 XID or Wayland surface identifier 638 static const unsigned int INHIBIT_IDLE = 8; // Taken from the portal API reference 639 DBusMessageIter iterInit; 640 641 if (inhibit) { 642 DBusMessage *msg; 643 bool result = false; 644 const char *key = "reason"; 645 const char *reply_path = NULL; 646 const char *reason = SDL_GetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME); 647 if (!reason || !reason[0]) { 648 reason = default_inhibit_reason; 649 } 650 651 msg = dbus.message_new_method_call(bus_name, path, interface, "Inhibit"); 652 if (!msg) { 653 return false; 654 } 655 656 if (!dbus.message_append_args(msg, DBUS_TYPE_STRING, &window, DBUS_TYPE_UINT32, &INHIBIT_IDLE, DBUS_TYPE_INVALID)) { 657 dbus.message_unref(msg); 658 return false; 659 } 660 661 dbus.message_iter_init_append(msg, &iterInit); 662 663 // a{sv} 664 if (!SDL_DBus_AppendDictWithKeyValue(&iterInit, key, reason)) { 665 dbus.message_unref(msg); 666 return false; 667 } 668 669 DBusMessage *reply = NULL; 670 if (SDL_DBus_CallWithBasicReply(dbus.session_conn, &reply, msg, DBUS_TYPE_OBJECT_PATH, &reply_path)) { 671 inhibit_handle = SDL_strdup(reply_path); 672 result = true; 673 } else { 674 interface_unavailable = true; 675 } 676 SDL_DBus_FreeReply(&reply); 677 dbus.message_unref(msg); 678 return result; 679 } else { 680 if (!SDL_DBus_CallVoidMethod(bus_name, inhibit_handle, "org.freedesktop.portal.Request", "Close", DBUS_TYPE_INVALID)) { 681 return false; 682 } 683 SDL_free(inhibit_handle); 684 inhibit_handle = NULL; 685 } 686 } else { 687 const char *bus_name = "org.freedesktop.ScreenSaver"; 688 const char *path = "/org/freedesktop/ScreenSaver"; 689 const char *interface = "org.freedesktop.ScreenSaver"; 690 691 if (inhibit) { 692 const char *app = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING); 693 const char *reason = SDL_GetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME); 694 if (!reason || !reason[0]) { 695 reason = default_inhibit_reason; 696 } 697 698 if (!SDL_DBus_CallMethod(NULL, bus_name, path, interface, "Inhibit", 699 DBUS_TYPE_STRING, &app, DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID, 700 DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) { 701 interface_unavailable = true; 702 return false; 703 } 704 return (screensaver_cookie != 0); 705 } else { 706 if (!SDL_DBus_CallVoidMethod(bus_name, path, interface, "UnInhibit", DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) { 707 return false; 708 } 709 screensaver_cookie = 0; 710 } 711 } 712 713 return true; 714} 715 716void SDL_DBus_PumpEvents(void) 717{ 718 if (dbus.session_conn) { 719 dbus.connection_read_write(dbus.session_conn, 0); 720 721 while (dbus.connection_dispatch(dbus.session_conn) == DBUS_DISPATCH_DATA_REMAINS) { 722 // Do nothing, actual work happens in DBus_MessageFilter 723 SDL_DelayNS(SDL_US_TO_NS(10)); 724 } 725 } 726} 727 728/* 729 * Get the machine ID if possible. Result must be freed with dbus->free(). 730 */ 731char *SDL_DBus_GetLocalMachineId(void) 732{ 733 DBusError err; 734 char *result; 735 736 dbus.error_init(&err); 737 738 if (dbus.try_get_local_machine_id) { 739 // Available since dbus 1.12.0, has proper error-handling 740 result = dbus.try_get_local_machine_id(&err); 741 } else { 742 /* Available since time immemorial, but has no error-handling: 743 * if the machine ID can't be read, many versions of libdbus will 744 * treat that as a fatal mis-installation and abort() */ 745 result = dbus.get_local_machine_id(); 746 } 747 748 if (result) { 749 return result; 750 } 751 752 if (dbus.error_is_set(&err)) { 753 SDL_SetError("%s: %s", err.name, err.message); 754 dbus.error_free(&err); 755 } else { 756 SDL_SetError("Error getting D-Bus machine ID"); 757 } 758 759 return NULL; 760} 761 762/* 763 * Convert file drops with mime type "application/vnd.portal.filetransfer" to file paths 764 * Result must be freed with dbus->free_string_array(). 765 * https://flatpak.github.io/xdg-desktop-portal/#gdbus-method-org-freedesktop-portal-FileTransfer.RetrieveFiles 766 */ 767char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *path_count) 768{ 769 DBusError err; 770 DBusMessageIter iter, iterDict; 771 char **paths = NULL; 772 DBusMessage *reply = NULL; 773 DBusMessage *msg = dbus.message_new_method_call("org.freedesktop.portal.Documents", // Node 774 "/org/freedesktop/portal/documents", // Path 775 "org.freedesktop.portal.FileTransfer", // Interface 776 "RetrieveFiles"); // Method 777 778 // Make sure we have a connection to the dbus session bus 779 if (!SDL_DBus_GetContext() || !dbus.session_conn) { 780 /* We either cannot connect to the session bus or were unable to 781 * load the D-Bus library at all. */ 782 return NULL; 783 } 784 785 dbus.error_init(&err); 786 787 // First argument is a "application/vnd.portal.filetransfer" key from a DnD or clipboard event 788 if (!dbus.message_append_args(msg, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) { 789 SDL_OutOfMemory(); 790 dbus.message_unref(msg); 791 goto failed; 792 } 793 794 /* Second argument is a variant dictionary for options. 795 * The spec doesn't define any entries yet so it's empty. */ 796 dbus.message_iter_init_append(msg, &iter); 797 if (!dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iterDict) || 798 !dbus.message_iter_close_container(&iter, &iterDict)) { 799 SDL_OutOfMemory(); 800 dbus.message_unref(msg); 801 goto failed; 802 } 803 804 reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, &err); 805 dbus.message_unref(msg); 806 807 if (reply) { 808 dbus.message_get_args(reply, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &paths, path_count, DBUS_TYPE_INVALID); 809 dbus.message_unref(reply); 810 } 811 812 if (paths) { 813 return paths; 814 } 815 816failed: 817 if (dbus.error_is_set(&err)) { 818 SDL_SetError("%s: %s", err.name, err.message); 819 dbus.error_free(&err); 820 } else { 821 SDL_SetError("Error retrieving paths for documents portal \"%s\"", key); 822 } 823 824 return NULL; 825} 826 827typedef struct SDL_DBus_CameraPortalMessageHandlerData 828{ 829 uint32_t response; 830 char *path; 831 DBusError *err; 832 bool done; 833} SDL_DBus_CameraPortalMessageHandlerData; 834 835static DBusHandlerResult SDL_DBus_CameraPortalMessageHandler(DBusConnection *conn, DBusMessage *msg, void *v) 836{ 837 SDL_DBus_CameraPortalMessageHandlerData *data = v; 838 const char *name, *old, *new; 839 840 if (dbus.message_is_signal(msg, "org.freedesktop.DBus", "NameOwnerChanged")) { 841 if (!dbus.message_get_args(msg, data->err, 842 DBUS_TYPE_STRING, &name, 843 DBUS_TYPE_STRING, &old, 844 DBUS_TYPE_STRING, &new, 845 DBUS_TYPE_INVALID)) { 846 data->done = true; 847 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 848 } 849 if (SDL_strcmp(name, "org.freedesktop.portal.Desktop") != 0 || 850 SDL_strcmp(new, "") != 0) { 851 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 852 } 853 data->done = true; 854 data->response = -1; 855 return DBUS_HANDLER_RESULT_HANDLED; 856 } 857 if (!dbus.message_has_path(msg, data->path) || !dbus.message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) { 858 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 859 } 860 dbus.message_get_args(msg, data->err, DBUS_TYPE_UINT32, &data->response, DBUS_TYPE_INVALID); 861 data->done = true; 862 return DBUS_HANDLER_RESULT_HANDLED; 863} 864 865#define SIGNAL_NAMEOWNERCHANGED "type='signal',\ 866 sender='org.freedesktop.DBus',\ 867 interface='org.freedesktop.DBus',\ 868 member='NameOwnerChanged',\ 869 arg0='org.freedesktop.portal.Desktop',\ 870 arg2=''" 871 872/* 873 * Requests access for the camera. Returns -1 on error, -2 on denied access or 874 * missing portal, otherwise returns a file descriptor to be used by the Pipewire driver. 875 * https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Camera.html 876 */ 877int SDL_DBus_CameraPortalRequestAccess(void) 878{ 879 SDL_DBus_CameraPortalMessageHandlerData data; 880 DBusError err; 881 DBusMessageIter iter, iterDict; 882 DBusMessage *reply, *msg; 883 int fd; 884 885 if (SDL_GetSandbox() == SDL_SANDBOX_NONE) { 886 return -2; 887 } 888 889 if (!SDL_DBus_GetContext()) { 890 return -2; 891 } 892 893 dbus.error_init(&err); 894 895 msg = dbus.message_new_method_call("org.freedesktop.portal.Desktop", 896 "/org/freedesktop/portal/desktop", 897 "org.freedesktop.portal.Camera", 898 "AccessCamera"); 899 900 dbus.message_iter_init_append(msg, &iter); 901 if (!dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iterDict) || 902 !dbus.message_iter_close_container(&iter, &iterDict)) { 903 SDL_OutOfMemory(); 904 dbus.message_unref(msg); 905 goto failed; 906 } 907 908 reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, &err); 909 dbus.message_unref(msg); 910 911 if (reply) { 912 dbus.message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, &data.path, DBUS_TYPE_INVALID); 913 if (dbus.error_is_set(&err)) { 914 dbus.message_unref(reply); 915 goto failed; 916 } 917 if ((data.path = SDL_strdup(data.path)) == NULL) { 918 dbus.message_unref(reply); 919 SDL_OutOfMemory(); 920 goto failed; 921 } 922 dbus.message_unref(reply); 923 } else { 924 if (dbus.error_has_name(&err, DBUS_ERROR_NAME_HAS_NO_OWNER)) { 925 return -2; 926 } 927 goto failed; 928 } 929 930 dbus.bus_add_match(dbus.session_conn, SIGNAL_NAMEOWNERCHANGED, &err); 931 if (dbus.error_is_set(&err)) { 932 SDL_free(data.path); 933 goto failed; 934 } 935 data.err = &err; 936 data.done = false; 937 if (!dbus.connection_add_filter(dbus.session_conn, SDL_DBus_CameraPortalMessageHandler, &data, NULL)) { 938 SDL_free(data.path); 939 SDL_OutOfMemory(); 940 goto failed; 941 } 942 while (!data.done && dbus.connection_read_write_dispatch(dbus.session_conn, -1)) { 943 ; 944 } 945 946 dbus.bus_remove_match(dbus.session_conn, SIGNAL_NAMEOWNERCHANGED, &err); 947 if (dbus.error_is_set(&err)) { 948 SDL_free(data.path); 949 goto failed; 950 } 951 dbus.connection_remove_filter(dbus.session_conn, SDL_DBus_CameraPortalMessageHandler, &data); 952 SDL_free(data.path); 953 if (!data.done) { 954 goto failed; 955 } 956 if (dbus.error_is_set(&err)) { // from the message handler 957 goto failed; 958 } 959 if (data.response == 1 || data.response == 2) { 960 return -2; 961 } else if (data.response != 0) { 962 goto failed; 963 } 964 965 msg = dbus.message_new_method_call("org.freedesktop.portal.Desktop", 966 "/org/freedesktop/portal/desktop", 967 "org.freedesktop.portal.Camera", 968 "OpenPipeWireRemote"); 969 970 dbus.message_iter_init_append(msg, &iter); 971 if (!dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iterDict) || 972 !dbus.message_iter_close_container(&iter, &iterDict)) { 973 SDL_OutOfMemory(); 974 dbus.message_unref(msg); 975 goto failed; 976 } 977 978 reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, &err); 979 dbus.message_unref(msg); 980 981 if (reply) { 982 dbus.message_get_args(reply, &err, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID); 983 dbus.message_unref(reply); 984 if (dbus.error_is_set(&err)) { 985 goto failed; 986 } 987 } else { 988 goto failed; 989 } 990 991 return fd; 992 993failed: 994 if (dbus.error_is_set(&err)) { 995 if (dbus.error_has_name(&err, DBUS_ERROR_NO_MEMORY)) { 996 SDL_OutOfMemory(); 997 } 998 SDL_SetError("%s: %s", err.name, err.message); 999 dbus.error_free(&err); 1000 } else { 1001 SDL_SetError("Error requesting access for the camera"); 1002 } 1003 1004 return -1; 1005} 1006 1007/* DBUSMENU LAYER BEGINS HERE */ 1008 1009/* Special thanks to the kind Hayden Gray (thag_iceman/A1029384756) from the SDL community for his help! */ 1010 1011static SDL_DBusMenuItem *MenuGetItemById(SDL_ListNode *menu, dbus_int32_t id) 1012{ 1013 SDL_ListNode *cursor; 1014 1015 cursor = menu; 1016 while (cursor) { 1017 SDL_MenuItem *item; 1018 SDL_DBusMenuItem *dbus_item; 1019 1020 item = cursor->entry; 1021 dbus_item = cursor->entry; 1022 1023 if (dbus_item->id == id) { 1024 return dbus_item; 1025 } 1026 1027 if (item->sub_menu) { 1028 SDL_DBusMenuItem *found; 1029 1030 found = MenuGetItemById(item->sub_menu, id); 1031 if (found) { 1032 return found; 1033 } 1034 } 1035 1036 cursor = cursor->next; 1037 } 1038 return NULL; 1039} 1040 1041static void MenuAppendItemProperties(SDL_DBusContext *ctx, SDL_DBusMenuItem *dbus_item, DBusMessageIter *dict_iter) 1042{ 1043 SDL_MenuItem *item; 1044 DBusMessageIter entry_iter; 1045 DBusMessageIter variant_iter; 1046 const char *key; 1047 const char *value; 1048 int value_int; 1049 dbus_bool_t value_bool; 1050 1051 item = (SDL_MenuItem *)dbus_item; 1052 1053 key = "label"; 1054 value = item->utf8 ? item->utf8 : ""; 1055 ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1056 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 1057 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1058 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value); 1059 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1060 ctx->message_iter_close_container(dict_iter, &entry_iter); 1061 1062 key = "type"; 1063 if (item->type == SDL_MENU_ITEM_TYPE_SEPERATOR) { 1064 value = "separator"; 1065 } else { 1066 value = "standard"; 1067 } 1068 ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1069 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 1070 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1071 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value); 1072 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1073 ctx->message_iter_close_container(dict_iter, &entry_iter); 1074 1075 key = "enabled"; 1076 value_bool = !(item->flags & SDL_MENU_ITEM_FLAGS_DISABLED); 1077 ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1078 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 1079 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "b", &variant_iter); 1080 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_BOOLEAN, &value_bool); 1081 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1082 ctx->message_iter_close_container(dict_iter, &entry_iter); 1083 1084 key = "visible"; 1085 value_bool = TRUE; 1086 ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1087 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 1088 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "b", &variant_iter); 1089 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_BOOLEAN, &value_bool); 1090 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1091 ctx->message_iter_close_container(dict_iter, &entry_iter); 1092 1093 key = "toggle-type"; 1094 value = (item->type == SDL_MENU_ITEM_TYPE_CHECKBOX) ? "checkmark" : ""; 1095 ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1096 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 1097 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1098 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value); 1099 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1100 ctx->message_iter_close_container(dict_iter, &entry_iter); 1101 1102 key = "toggle-state"; 1103 value_int = (item->flags & SDL_MENU_ITEM_FLAGS_CHECKED) ? 1 : 0; 1104 ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1105 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 1106 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "i", &variant_iter); 1107 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_INT32, &value_int); 1108 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1109 ctx->message_iter_close_container(dict_iter, &entry_iter); 1110 1111 key = "children-display"; 1112 value = item->sub_menu ? "submenu" : ""; 1113 ctx->message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1114 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 1115 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1116 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value); 1117 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1118 ctx->message_iter_close_container(dict_iter, &entry_iter); 1119} 1120 1121static void MenuAppendItem(SDL_DBusContext *ctx, SDL_DBusMenuItem *dbus_item, DBusMessageIter *array_iter, int depth) 1122{ 1123 SDL_MenuItem *item; 1124 DBusMessageIter struct_iter, dict_iter, children_iter; 1125 1126 item = (SDL_MenuItem *)dbus_item; 1127 1128 ctx->message_iter_open_container(array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); 1129 ctx->message_iter_append_basic(&struct_iter, DBUS_TYPE_INT32, &dbus_item->id); 1130 ctx->message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter); 1131 MenuAppendItemProperties(ctx, dbus_item, &dict_iter); 1132 ctx->message_iter_close_container(&struct_iter, &dict_iter); 1133 ctx->message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "v", &children_iter); 1134 1135 if (item->sub_menu && depth > 0) { 1136 SDL_ListNode *cursor; 1137 1138 cursor = item->sub_menu; 1139 while (cursor) { 1140 SDL_DBusMenuItem *child; 1141 DBusMessageIter variant_iter; 1142 1143 child = cursor->entry; 1144 ctx->message_iter_open_container(&children_iter, DBUS_TYPE_VARIANT, "(ia{sv}av)", &variant_iter); 1145 MenuAppendItem(ctx, child, &variant_iter, depth - 1); 1146 ctx->message_iter_close_container(&children_iter, &variant_iter); 1147 cursor = cursor->next; 1148 } 1149 } 1150 1151 ctx->message_iter_close_container(&struct_iter, &children_iter); 1152 ctx->message_iter_close_container(array_iter, &struct_iter); 1153} 1154 1155static DBusHandlerResult MenuHandleGetLayout(SDL_DBusContext *ctx, SDL_ListNode *menu, DBusConnection *conn, DBusMessage *msg) 1156{ 1157 DBusMessage *reply; 1158 DBusMessageIter reply_iter, struct_iter, dict_iter, children_iter; 1159 DBusMessageIter entry_iter, variant_iter; 1160 DBusMessageIter args; 1161 const char *key; 1162 const char *val; 1163 dbus_int32_t parent_id; 1164 dbus_int32_t recursion_depth; 1165 dbus_int32_t root_id; 1166 dbus_uint32_t revision; 1167 1168 ctx->message_iter_init(msg, &args); 1169 if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_INT32) { 1170 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1171 } 1172 1173 ctx->message_iter_get_basic(&args, &parent_id); 1174 ctx->message_iter_next(&args); 1175 if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_INT32) { 1176 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1177 } 1178 1179 ctx->message_iter_get_basic(&args, &recursion_depth); 1180 if (recursion_depth == -1) { 1181 recursion_depth = 100; 1182 } 1183 1184 reply = ctx->message_new_method_return(msg); 1185 ctx->message_iter_init_append(reply, &reply_iter); 1186 1187 revision = 0; 1188 if (menu) { 1189 if (menu->entry) { 1190 revision = ((SDL_DBusMenuItem *)menu->entry)->revision; 1191 } 1192 } 1193 ctx->message_iter_append_basic(&reply_iter, DBUS_TYPE_UINT32, &revision); 1194 1195 ctx->message_iter_open_container(&reply_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); 1196 1197 root_id = 0; 1198 ctx->message_iter_append_basic(&struct_iter, DBUS_TYPE_INT32, &root_id); 1199 1200 key = "children-display"; 1201 val = "submenu"; 1202 ctx->message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter); 1203 ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1204 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 1205 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1206 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &val); 1207 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1208 ctx->message_iter_close_container(&dict_iter, &entry_iter); 1209 ctx->message_iter_close_container(&struct_iter, &dict_iter); 1210 1211 ctx->message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "v", &children_iter); 1212 if (!parent_id && menu) { 1213 SDL_ListNode *cursor; 1214 1215 cursor = menu; 1216 while (cursor) { 1217 SDL_MenuItem *item; 1218 SDL_DBusMenuItem *dbus_item; 1219 DBusMessageIter cvariant_iter, item_struct, item_dict, item_children; 1220 1221 item = cursor->entry; 1222 dbus_item = cursor->entry; 1223 ctx->message_iter_open_container(&children_iter, DBUS_TYPE_VARIANT, "(ia{sv}av)", &cvariant_iter); 1224 ctx->message_iter_open_container(&cvariant_iter, DBUS_TYPE_STRUCT, NULL, &item_struct); 1225 ctx->message_iter_append_basic(&item_struct, DBUS_TYPE_INT32, &dbus_item->id); 1226 ctx->message_iter_open_container(&item_struct, DBUS_TYPE_ARRAY, "{sv}", &item_dict); 1227 MenuAppendItemProperties(ctx, dbus_item, &item_dict); 1228 ctx->message_iter_close_container(&item_struct, &item_dict); 1229 ctx->message_iter_open_container(&item_struct, DBUS_TYPE_ARRAY, "v", &item_children); 1230 if (item->sub_menu && recursion_depth) { 1231 SDL_ListNode *child_cursor; 1232 1233 child_cursor = item->sub_menu; 1234 while (child_cursor) { 1235 SDL_DBusMenuItem *child; 1236 DBusMessageIter child_variant; 1237 1238 child = child_cursor->entry; 1239 ctx->message_iter_open_container(&item_children, DBUS_TYPE_VARIANT, "(ia{sv}av)", &child_variant); 1240 MenuAppendItem(ctx, child, &child_variant, recursion_depth - 1); 1241 ctx->message_iter_close_container(&item_children, &child_variant); 1242 child_cursor = child_cursor->next; 1243 } 1244 } 1245 ctx->message_iter_close_container(&item_struct, &item_children); 1246 ctx->message_iter_close_container(&cvariant_iter, &item_struct); 1247 1248 ctx->message_iter_close_container(&children_iter, &cvariant_iter); 1249 cursor = cursor->next; 1250 } 1251 } else if (parent_id) { 1252 SDL_DBusMenuItem *parent; 1253 SDL_MenuItem *parent_item; 1254 1255 parent = MenuGetItemById(menu, parent_id); 1256 parent_item = (SDL_MenuItem *)parent; 1257 if (parent_item && parent_item->sub_menu) { 1258 SDL_ListNode *cursor; 1259 1260 cursor = parent_item->sub_menu; 1261 while (cursor) { 1262 SDL_MenuItem *item; 1263 SDL_DBusMenuItem *dbus_item; 1264 DBusMessageIter cvariant_iter, item_struct, item_dict, item_children; 1265 1266 item = cursor->entry; 1267 dbus_item = cursor->entry; 1268 ctx->message_iter_open_container(&children_iter, DBUS_TYPE_VARIANT, "(ia{sv}av)", &cvariant_iter); 1269 ctx->message_iter_open_container(&cvariant_iter, DBUS_TYPE_STRUCT, NULL, &item_struct); 1270 ctx->message_iter_append_basic(&item_struct, DBUS_TYPE_INT32, &dbus_item->id); 1271 ctx->message_iter_open_container(&item_struct, DBUS_TYPE_ARRAY, "{sv}", &item_dict); 1272 MenuAppendItemProperties(ctx, dbus_item, &item_dict); 1273 ctx->message_iter_close_container(&item_struct, &item_dict); 1274 ctx->message_iter_open_container(&item_struct, DBUS_TYPE_ARRAY, "v", &item_children); 1275 if (item->sub_menu && recursion_depth) { 1276 SDL_ListNode *child_cursor; 1277 1278 child_cursor = item->sub_menu; 1279 while (child_cursor) { 1280 SDL_DBusMenuItem *child; 1281 DBusMessageIter child_variant; 1282 1283 child = child_cursor->entry; 1284 ctx->message_iter_open_container(&item_children, DBUS_TYPE_VARIANT, "(ia{sv}av)", &child_variant); 1285 MenuAppendItem(ctx, child, &child_variant, recursion_depth - 1); 1286 ctx->message_iter_close_container(&item_children, &child_variant); 1287 child_cursor = child_cursor->next; 1288 } 1289 } 1290 ctx->message_iter_close_container(&item_struct, &item_children); 1291 ctx->message_iter_close_container(&cvariant_iter, &item_struct); 1292 1293 ctx->message_iter_close_container(&children_iter, &cvariant_iter); 1294 1295 cursor = cursor->next; 1296 } 1297 } 1298 } 1299 ctx->message_iter_close_container(&struct_iter, &children_iter); 1300 ctx->message_iter_close_container(&reply_iter, &struct_iter); 1301 1302 ctx->connection_send(conn, reply, NULL); 1303 ctx->message_unref(reply); 1304 1305 return DBUS_HANDLER_RESULT_HANDLED; 1306} 1307 1308static DBusHandlerResult MenuHandleEvent(SDL_DBusContext *ctx, SDL_ListNode *menu, DBusConnection *conn, DBusMessage *msg) 1309{ 1310 SDL_MenuItem *item; 1311 SDL_DBusMenuItem *dbus_item; 1312 DBusMessage *reply; 1313 const char *event_id; 1314 DBusMessageIter args; 1315 Uint32 id; 1316 1317 ctx->message_iter_init(msg, &args); 1318 ctx->message_iter_get_basic(&args, &id); 1319 ctx->message_iter_next(&args); 1320 ctx->message_iter_get_basic(&args, &event_id); 1321 1322 item = NULL; 1323 dbus_item = NULL; 1324 if (!SDL_strcmp(event_id, "clicked")) { 1325 dbus_item = MenuGetItemById(menu, id); 1326 item = (SDL_MenuItem *)dbus_item; 1327 } 1328 1329 reply = ctx->message_new_method_return(msg); 1330 ctx->connection_send(conn, reply, NULL); 1331 ctx->message_unref(reply); 1332 1333 if (item) { 1334 if (item->type == SDL_MENU_ITEM_TYPE_CHECKBOX) { 1335 item->flags ^= SDL_MENU_ITEM_FLAGS_CHECKED; 1336 SDL_DBus_UpdateMenu(ctx, conn, menu, NULL, NULL, NULL, SDL_DBUS_UPDATE_MENU_FLAG_DO_NOT_REPLACE); 1337 } 1338 1339 if (item->cb) { 1340 item->cb(item, item->cb_data); 1341 } 1342 } 1343 1344 return DBUS_HANDLER_RESULT_HANDLED; 1345} 1346 1347static DBusHandlerResult MenuHandleEventGroup(SDL_DBusContext *ctx, SDL_ListNode *menu, DBusConnection *conn, DBusMessage *msg) 1348{ 1349 DBusMessage *reply; 1350 DBusMessageIter reply_iter, id_errors_iter; 1351 DBusMessageIter args, array_iter; 1352 1353 ctx->message_iter_init(msg, &args); 1354 if (ctx->message_iter_get_arg_type(&args) == DBUS_TYPE_ARRAY) { 1355 ctx->message_iter_recurse(&args, &array_iter); 1356 while (ctx->message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRUCT) { 1357 DBusMessageIter struct_iter; 1358 const char *event_id; 1359 dbus_int32_t id; 1360 1361 ctx->message_iter_recurse(&array_iter, &struct_iter); 1362 if (ctx->message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_INT32) { 1363 ctx->message_iter_get_basic(&struct_iter, &id); 1364 ctx->message_iter_next(&struct_iter); 1365 if (ctx->message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_STRING) { 1366 ctx->message_iter_get_basic(&struct_iter, &event_id); 1367 1368 if (!SDL_strcmp(event_id, "clicked")) { 1369 SDL_DBusMenuItem *dbus_item; 1370 SDL_MenuItem *item; 1371 1372 dbus_item = MenuGetItemById(menu, id); 1373 item = (SDL_MenuItem *)dbus_item; 1374 1375 if (item) { 1376 if (item->type == SDL_MENU_ITEM_TYPE_CHECKBOX) { 1377 item->flags ^= SDL_MENU_ITEM_FLAGS_CHECKED; 1378 SDL_DBus_UpdateMenu(ctx, conn, menu, NULL, NULL, NULL, SDL_DBUS_UPDATE_MENU_FLAG_DO_NOT_REPLACE); 1379 } 1380 1381 if (item->cb) { 1382 item->cb(item, item->cb_data); 1383 } 1384 } 1385 } 1386 } 1387 } 1388 ctx->message_iter_next(&array_iter); 1389 } 1390 } 1391 1392 reply = ctx->message_new_method_return(msg); 1393 ctx->message_iter_init_append(reply, &reply_iter); 1394 ctx->message_iter_open_container(&reply_iter, DBUS_TYPE_ARRAY, "i", &id_errors_iter); 1395 ctx->message_iter_close_container(&reply_iter, &id_errors_iter); 1396 ctx->connection_send(conn, reply, NULL); 1397 ctx->message_unref(reply); 1398 return DBUS_HANDLER_RESULT_HANDLED; 1399} 1400 1401static DBusHandlerResult MenuHandleGetProperty(SDL_DBusContext *ctx, SDL_ListNode *menu, DBusConnection *conn, DBusMessage *msg) 1402{ 1403 SDL_MenuItem *item; 1404 SDL_DBusMenuItem *dbus_item; 1405 DBusMessage *reply; 1406 const char *property; 1407 const char *val; 1408 DBusMessageIter args; 1409 DBusMessageIter iter, variant_iter; 1410 dbus_int32_t id; 1411 int int_val; 1412 dbus_bool_t bool_val; 1413 1414 ctx->message_iter_init(msg, &args); 1415 if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_INT32) { 1416 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1417 } 1418 1419 ctx->message_iter_get_basic(&args, &id); 1420 ctx->message_iter_next(&args); 1421 if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) { 1422 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1423 } 1424 1425 ctx->message_iter_get_basic(&args, &property); 1426 1427 dbus_item = MenuGetItemById(menu, id); 1428 item = (SDL_MenuItem *)dbus_item; 1429 if (!item) { 1430 DBusMessage *error; 1431 1432 error = ctx->message_new_error(msg, "com.canonical.dbusmenu.Error", "Item not found"); 1433 ctx->connection_send(conn, error, NULL); 1434 ctx->message_unref(error); 1435 return DBUS_HANDLER_RESULT_HANDLED; 1436 } 1437 1438 reply = ctx->message_new_method_return(msg); 1439 ctx->message_iter_init_append(reply, &iter); 1440 if (!SDL_strcmp(property, "label")) { 1441 val = item->utf8 ? item->utf8 : ""; 1442 ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1443 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &val); 1444 ctx->message_iter_close_container(&iter, &variant_iter); 1445 } else if (!SDL_strcmp(property, "enabled")) { 1446 bool_val = !(item->flags & SDL_MENU_ITEM_FLAGS_DISABLED); 1447 ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "b", &variant_iter); 1448 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_BOOLEAN, &bool_val); 1449 ctx->message_iter_close_container(&iter, &variant_iter); 1450 } else if (!SDL_strcmp(property, "visible")) { 1451 bool_val = 1; 1452 ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "b", &variant_iter); 1453 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_BOOLEAN, &bool_val); 1454 ctx->message_iter_close_container(&iter, &variant_iter); 1455 } else if (!SDL_strcmp(property, "type")) { 1456 if (item->type == SDL_MENU_ITEM_TYPE_SEPERATOR) { 1457 val = "separator"; 1458 } else { 1459 val = "standard"; 1460 } 1461 ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1462 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &val); 1463 ctx->message_iter_close_container(&iter, &variant_iter); 1464 } else if (!SDL_strcmp(property, "toggle-type")) { 1465 if (item->type == SDL_MENU_ITEM_TYPE_CHECKBOX) { 1466 val = "checkmark"; 1467 } else { 1468 val = ""; 1469 } 1470 ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1471 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &val); 1472 ctx->message_iter_close_container(&iter, &variant_iter); 1473 } else if (!SDL_strcmp(property, "toggle-state")) { 1474 int_val = (item->flags & SDL_MENU_ITEM_FLAGS_CHECKED) ? 1 : 0; 1475 ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "i", &variant_iter); 1476 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_INT32, &int_val); 1477 ctx->message_iter_close_container(&iter, &variant_iter); 1478 } else if (!SDL_strcmp(property, "children-display")) { 1479 val = item->sub_menu ? "submenu" : ""; 1480 ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1481 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &val); 1482 ctx->message_iter_close_container(&iter, &variant_iter); 1483 } else { 1484 val = ""; 1485 ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1486 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &val); 1487 ctx->message_iter_close_container(&iter, &variant_iter); 1488 } 1489 1490 ctx->connection_send(conn, reply, NULL); 1491 ctx->message_unref(reply); 1492 return DBUS_HANDLER_RESULT_HANDLED; 1493} 1494 1495static DBusHandlerResult MenuHandleGetGroupProperties(SDL_DBusContext *ctx, SDL_ListNode *menu, DBusConnection *conn, DBusMessage *msg) 1496{ 1497#define FILTER_PROPS_SZ 32 1498 1499 DBusMessage *reply; 1500 DBusMessageIter args, array_iter, prop_iter; 1501 DBusMessageIter iter, reply_array_iter; 1502 const char *filter_props[FILTER_PROPS_SZ]; 1503 dbus_int32_t *ids; 1504 int ids_sz; 1505 int filter_sz; 1506 int i; 1507 int j; 1508 1509 ids_sz = 0; 1510 ctx->message_iter_init(msg, &args); 1511 if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) { 1512 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1513 } 1514 1515 ctx->message_iter_recurse(&args, &array_iter); 1516 if (ctx->message_iter_get_arg_type(&array_iter) == DBUS_TYPE_INT32) { 1517 ctx->message_iter_get_fixed_array(&array_iter, &ids, &ids_sz); 1518 } 1519 1520 ctx->message_iter_next(&args); 1521 if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) { 1522 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1523 } 1524 1525 ctx->message_iter_recurse(&args, &prop_iter); 1526 filter_sz = 0; 1527 while (ctx->message_iter_get_arg_type(&prop_iter) == DBUS_TYPE_STRING) { 1528 if (filter_sz < FILTER_PROPS_SZ) { 1529 ctx->message_iter_get_basic(&prop_iter, &filter_props[filter_sz]); 1530 filter_sz++; 1531 } 1532 ctx->message_iter_next(&prop_iter); 1533 } 1534 1535 reply = ctx->message_new_method_return(msg); 1536 ctx->message_iter_init_append(reply, &iter); 1537 ctx->message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ia{sv})", &reply_array_iter); 1538 1539 for (i = 0; i < ids_sz; i++) { 1540 SDL_MenuItem *item; 1541 SDL_DBusMenuItem *dbus_item; 1542 1543 dbus_item = MenuGetItemById(menu, ids[i]); 1544 item = (SDL_MenuItem *)dbus_item; 1545 if (item) { 1546 DBusMessageIter struct_iter, dict_iter; 1547 1548 ctx->message_iter_open_container(&reply_array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); 1549 ctx->message_iter_append_basic(&struct_iter, DBUS_TYPE_INT32, &ids[i]); 1550 ctx->message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter); 1551 1552 if (filter_sz == 0) { 1553 MenuAppendItemProperties(ctx, dbus_item, &dict_iter); 1554 } else { 1555 for (j = 0; j < filter_sz; j++) { 1556 DBusMessageIter entry_iter, variant_iter; 1557 const char *prop; 1558 const char *val; 1559 int int_val; 1560 dbus_bool_t bool_val; 1561 1562 prop = filter_props[j]; 1563 if (!SDL_strcmp(prop, "label")) { 1564 val = (item->utf8) ? item->utf8 : ""; 1565 ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1566 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &prop); 1567 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1568 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &val); 1569 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1570 ctx->message_iter_close_container(&dict_iter, &entry_iter); 1571 } else if (!SDL_strcmp(prop, "type")) { 1572 val = (item->type == SDL_MENU_ITEM_TYPE_SEPERATOR) ? "separator" : "standard"; 1573 ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1574 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &prop); 1575 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1576 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &val); 1577 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1578 ctx->message_iter_close_container(&dict_iter, &entry_iter); 1579 } else if (!SDL_strcmp(prop, "enabled")) { 1580 bool_val = !(item->flags & SDL_MENU_ITEM_FLAGS_DISABLED); 1581 ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1582 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &prop); 1583 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "b", &variant_iter); 1584 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_BOOLEAN, &bool_val); 1585 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1586 ctx->message_iter_close_container(&dict_iter, &entry_iter); 1587 } else if (!SDL_strcmp(prop, "visible")) { 1588 bool_val = 1; 1589 ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1590 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &prop); 1591 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "b", &variant_iter); 1592 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_BOOLEAN, &bool_val); 1593 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1594 ctx->message_iter_close_container(&dict_iter, &entry_iter); 1595 } else if (!SDL_strcmp(prop, "toggle-type")) { 1596 val = (item->type == SDL_MENU_ITEM_TYPE_CHECKBOX) ? "checkmark" : ""; 1597 ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1598 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &prop); 1599 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1600 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &val); 1601 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1602 ctx->message_iter_close_container(&dict_iter, &entry_iter); 1603 } else if (!SDL_strcmp(prop, "toggle-state")) { 1604 int_val = (item->flags & SDL_MENU_ITEM_FLAGS_CHECKED) ? 1 : 0; 1605 ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1606 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &prop); 1607 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "i", &variant_iter); 1608 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_INT32, &int_val); 1609 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1610 ctx->message_iter_close_container(&dict_iter, &entry_iter); 1611 } else if (!SDL_strcmp(prop, "children-display")) { 1612 val = (item->sub_menu) ? "submenu" : ""; 1613 ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1614 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &prop); 1615 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1616 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &val); 1617 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1618 ctx->message_iter_close_container(&dict_iter, &entry_iter); 1619 } 1620 } 1621 } 1622 1623 ctx->message_iter_close_container(&struct_iter, &dict_iter); 1624 ctx->message_iter_close_container(&reply_array_iter, &struct_iter); 1625 } 1626 } 1627 1628 ctx->message_iter_close_container(&iter, &reply_array_iter); 1629 ctx->connection_send(conn, reply, NULL); 1630 ctx->message_unref(reply); 1631 return DBUS_HANDLER_RESULT_HANDLED; 1632} 1633 1634static dbus_int32_t MenuGetMaxItemId(SDL_ListNode *menu) 1635{ 1636 SDL_ListNode *cursor; 1637 dbus_int32_t max_id; 1638 1639 max_id = 0; 1640 cursor = menu; 1641 while (cursor) { 1642 SDL_MenuItem *item; 1643 SDL_DBusMenuItem *dbus_item; 1644 1645 dbus_item = cursor->entry; 1646 item = cursor->entry; 1647 if (item) { 1648 if (dbus_item->id > max_id) { 1649 max_id = dbus_item->id; 1650 } 1651 1652 if (item->sub_menu) { 1653 dbus_int32_t sub_max; 1654 1655 sub_max = MenuGetMaxItemId(item->sub_menu); 1656 if (sub_max > max_id) { 1657 max_id = sub_max; 1658 } 1659 } 1660 } 1661 cursor = cursor->next; 1662 } 1663 return max_id; 1664} 1665 1666static void MenuAssignItemIds(SDL_ListNode *menu, dbus_int32_t *next_id) 1667{ 1668 SDL_ListNode *cursor; 1669 1670 cursor = menu; 1671 while (cursor) { 1672 SDL_MenuItem *item; 1673 SDL_DBusMenuItem *dbus_item; 1674 1675 dbus_item = cursor->entry; 1676 item = cursor->entry; 1677 if (item) { 1678 if (!dbus_item->id) { 1679 dbus_item->id = (*next_id)++; 1680 } 1681 1682 if (item->sub_menu) { 1683 MenuAssignItemIds(item->sub_menu, next_id); 1684 } 1685 } 1686 cursor = cursor->next; 1687 } 1688} 1689 1690SDL_MenuItem *SDL_DBus_CreateMenuItem(void) 1691{ 1692 SDL_DBusMenuItem *item; 1693 item = SDL_malloc(sizeof(SDL_DBusMenuItem)); 1694 item->id = 0; 1695 item->revision = 0; 1696 item->cb = NULL; 1697 item->cbdata = NULL; 1698 return (SDL_MenuItem *)item; 1699} 1700 1701static DBusHandlerResult MenuMessageHandler(DBusConnection *conn, DBusMessage *msg, void *user_data) 1702{ 1703 SDL_ListNode *menu; 1704 SDL_DBusMenuItem *item; 1705 SDL_DBusContext *ctx; 1706 1707 menu = user_data; 1708 if (!menu) { 1709 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1710 } 1711 1712 if (!menu->entry) { 1713 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1714 } 1715 1716 item = (SDL_DBusMenuItem *)menu->entry; 1717 ctx = item->dbus; 1718 1719 if (ctx->message_is_method_call(msg, DBUS_MENU_INTERFACE, "GetLayout")) { 1720 return MenuHandleGetLayout(ctx, menu, conn, msg); 1721 } else if (ctx->message_is_method_call(msg, DBUS_MENU_INTERFACE, "Event")) { 1722 return MenuHandleEvent(ctx, menu, conn, msg); 1723 } else if (ctx->message_is_method_call(msg, DBUS_MENU_INTERFACE, "EventGroup")) { 1724 return MenuHandleEventGroup(ctx, menu, conn, msg); 1725 } else if (ctx->message_is_method_call(msg, DBUS_MENU_INTERFACE, "AboutToShow")) { 1726 DBusMessage *reply; 1727 dbus_bool_t need_update; 1728 1729 if (item->cb) { 1730 item->cb(menu, item->cbdata); 1731 } 1732 1733 need_update = FALSE; 1734 reply = ctx->message_new_method_return(msg); 1735 ctx->message_append_args(reply, DBUS_TYPE_BOOLEAN, &need_update, DBUS_TYPE_INVALID); 1736 ctx->connection_send(conn, reply, NULL); 1737 ctx->message_unref(reply); 1738 return DBUS_HANDLER_RESULT_HANDLED; 1739 } else if (ctx->message_is_method_call(msg, DBUS_MENU_INTERFACE, "AboutToShowGroup")) { 1740 DBusMessage *reply; 1741 DBusMessageIter iter, arr_iter; 1742 1743 reply = ctx->message_new_method_return(msg); 1744 ctx->message_iter_init_append(reply, &iter); 1745 ctx->message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "i", &arr_iter); 1746 ctx->message_iter_close_container(&iter, &arr_iter); 1747 ctx->message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "i", &arr_iter); 1748 ctx->message_iter_close_container(&iter, &arr_iter); 1749 ctx->connection_send(conn, reply, NULL); 1750 ctx->message_unref(reply); 1751 return DBUS_HANDLER_RESULT_HANDLED; 1752 } else if (ctx->message_is_method_call(msg, DBUS_MENU_INTERFACE, "GetGroupProperties")) { 1753 return MenuHandleGetGroupProperties(ctx, menu, conn, msg); 1754 } else if (ctx->message_is_method_call(msg, DBUS_MENU_INTERFACE, "GetProperty")) { 1755 return MenuHandleGetProperty(ctx, menu, conn, msg); 1756 } else if (ctx->message_is_method_call(msg, "org.freedesktop.DBus.Properties", "Get")) { 1757 DBusMessage *reply; 1758 const char *interface_name; 1759 const char *property_name; 1760 const char *str_val; 1761 DBusMessageIter args, iter, variant_iter; 1762 dbus_uint32_t version; 1763 1764 ctx->message_iter_init(msg, &args); 1765 if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) { 1766 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1767 } 1768 ctx->message_iter_get_basic(&args, &interface_name); 1769 ctx->message_iter_next(&args); 1770 if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) { 1771 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1772 } 1773 ctx->message_iter_get_basic(&args, &property_name); 1774 1775 if (!SDL_strcmp(interface_name, DBUS_MENU_INTERFACE)) { 1776 reply = ctx->message_new_method_return(msg); 1777 ctx->message_iter_init_append(reply, &iter); 1778 if (!SDL_strcmp(property_name, "Version")) { 1779 version = 3; 1780 ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "u", &variant_iter); 1781 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_UINT32, &version); 1782 ctx->message_iter_close_container(&iter, &variant_iter); 1783 } else if (!SDL_strcmp(property_name, "Status")) { 1784 str_val = "normal"; 1785 1786 ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1787 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &str_val); 1788 ctx->message_iter_close_container(&iter, &variant_iter); 1789 } else if (!SDL_strcmp(property_name, "TextDirection")) { 1790 str_val = "ltr"; 1791 1792 ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1793 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &str_val); 1794 ctx->message_iter_close_container(&iter, &variant_iter); 1795 } else if (!SDL_strcmp(property_name, "IconThemePath")) { 1796 DBusMessageIter array_iter; 1797 1798 ctx->message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "as", &variant_iter); 1799 ctx->message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, "s", &array_iter); 1800 ctx->message_iter_close_container(&variant_iter, &array_iter); 1801 ctx->message_iter_close_container(&iter, &variant_iter); 1802 } else { 1803 ctx->message_unref(reply); 1804 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1805 } 1806 ctx->connection_send(conn, reply, NULL); 1807 ctx->message_unref(reply); 1808 return DBUS_HANDLER_RESULT_HANDLED; 1809 } 1810 } else if (ctx->message_is_method_call(msg, "org.freedesktop.DBus.Properties", "GetAll")) { 1811 DBusMessage *reply; 1812 const char *interface_name; 1813 const char *key; 1814 DBusMessageIter args, iter, dict_iter, entry_iter, variant_iter; 1815 dbus_uint32_t version; 1816 1817 ctx->message_iter_init(msg, &args); 1818 if (ctx->message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) { 1819 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1820 } 1821 ctx->message_iter_get_basic(&args, &interface_name); 1822 1823 if (!SDL_strcmp(interface_name, DBUS_MENU_INTERFACE)) { 1824 DBusMessageIter array_iter; 1825 const char *str_val; 1826 1827 reply = ctx->message_new_method_return(msg); 1828 ctx->message_iter_init_append(reply, &iter); 1829 ctx->message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter); 1830 1831 key = "Version"; 1832 version = 3; 1833 ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1834 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 1835 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "u", &variant_iter); 1836 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_UINT32, &version); 1837 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1838 ctx->message_iter_close_container(&dict_iter, &entry_iter); 1839 1840 key = "Status"; 1841 str_val = "normal"; 1842 ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1843 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 1844 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1845 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &str_val); 1846 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1847 ctx->message_iter_close_container(&dict_iter, &entry_iter); 1848 1849 key = "TextDirection"; 1850 str_val = "ltr"; 1851 ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1852 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 1853 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter); 1854 ctx->message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &str_val); 1855 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1856 ctx->message_iter_close_container(&dict_iter, &entry_iter); 1857 1858 key = "IconThemePath"; 1859 ctx->message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter); 1860 ctx->message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key); 1861 ctx->message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT, "as", &variant_iter); 1862 ctx->message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, "s", &array_iter); 1863 ctx->message_iter_close_container(&variant_iter, &array_iter); 1864 ctx->message_iter_close_container(&entry_iter, &variant_iter); 1865 ctx->message_iter_close_container(&dict_iter, &entry_iter); 1866 1867 ctx->message_iter_close_container(&iter, &dict_iter); 1868 ctx->connection_send(conn, reply, NULL); 1869 ctx->message_unref(reply); 1870 return DBUS_HANDLER_RESULT_HANDLED; 1871 } 1872 } else if (ctx->message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) { 1873 DBusMessage *reply; 1874 1875 reply = ctx->message_new_method_return(msg); 1876 ctx->message_append_args(reply, DBUS_TYPE_STRING, &menu_introspect, DBUS_TYPE_INVALID); 1877 ctx->connection_send(conn, reply, NULL); 1878 ctx->message_unref(reply); 1879 return DBUS_HANDLER_RESULT_HANDLED; 1880 } 1881 1882 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 1883} 1884 1885const char *SDL_DBus_ExportMenu(SDL_DBusContext *ctx, DBusConnection *conn, SDL_ListNode *menu) 1886{ 1887 DBusObjectPathVTable vtable; 1888 dbus_int32_t next_id; 1889 1890 if (!ctx || !menu) { 1891 return NULL; 1892 } 1893 1894 next_id = 1; 1895 MenuAssignItemIds(menu, &next_id); 1896 1897 if (menu->entry) { 1898 SDL_DBusMenuItem *item; 1899 1900 item = menu->entry; 1901 item->dbus = ctx; 1902 item->revision++; 1903 } 1904 1905 vtable.message_function = MenuMessageHandler; 1906 vtable.unregister_function = NULL; 1907 if (!ctx->connection_try_register_object_path(conn, DBUS_MENU_OBJECT_PATH, &vtable, menu, NULL)) { 1908 return NULL; 1909 } 1910 1911 return DBUS_MENU_OBJECT_PATH; 1912} 1913 1914void 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) 1915{ 1916 DBusMessage *signal; 1917 dbus_uint32_t revision; 1918 dbus_int32_t next_id; 1919 1920 if (!ctx) { 1921 return; 1922 } 1923 1924 if (!menu) { 1925 goto REPLACE_MENU; 1926 } 1927 1928 next_id = MenuGetMaxItemId(menu) + 1; 1929 MenuAssignItemIds(menu, &next_id); 1930 1931 revision = 0; 1932 if (menu->entry) { 1933 SDL_DBusMenuItem *item; 1934 1935 item = menu->entry; 1936 item->revision++; 1937 item->dbus = ctx; 1938 revision = item->revision; 1939 } 1940 1941 if (flags & SDL_DBUS_UPDATE_MENU_FLAG_DO_NOT_REPLACE) { 1942 goto SEND_SIGNAL; 1943 } 1944 1945REPLACE_MENU: 1946 if (path) { 1947 void *udata; 1948 1949 ctx->connection_get_object_path_data(conn, path, &udata); 1950 1951 if (udata != menu) { 1952 DBusObjectPathVTable vtable; 1953 1954 vtable.message_function = MenuMessageHandler; 1955 vtable.unregister_function = NULL; 1956 ctx->connection_unregister_object_path(conn, path); 1957 ctx->connection_try_register_object_path(conn, path, &vtable, menu, NULL); 1958 ctx->connection_flush(conn); 1959 1960 if (cb) { 1961 cb(menu, NULL, cbdata); 1962 } 1963 } 1964 } else { 1965 DBusObjectPathVTable vtable; 1966 SDL_DBusMenuItem *item; 1967 1968 if (!menu) { 1969 goto SEND_SIGNAL; 1970 } 1971 1972 next_id = MenuGetMaxItemId(menu) + 1; 1973 MenuAssignItemIds(menu, &next_id); 1974 revision = 0; 1975 if (menu->entry) { 1976 item = menu->entry; 1977 item->dbus = ctx; 1978 item->revision++; 1979 revision = item->revision; 1980 } 1981 1982 vtable.message_function = MenuMessageHandler; 1983 vtable.unregister_function = NULL; 1984 ctx->connection_try_register_object_path(conn, DBUS_MENU_OBJECT_PATH, &vtable, menu, NULL); 1985 ctx->connection_flush(conn); 1986 1987 if (cb) { 1988 cb(menu, DBUS_MENU_OBJECT_PATH, cbdata); 1989 } 1990 ctx->connection_flush(conn); 1991 } 1992 1993SEND_SIGNAL: 1994 if (path) { 1995 signal = ctx->message_new_signal(path, DBUS_MENU_INTERFACE, "LayoutUpdated"); 1996 } else { 1997 signal = ctx->message_new_signal(DBUS_MENU_OBJECT_PATH, DBUS_MENU_INTERFACE, "LayoutUpdated"); 1998 } 1999 2000 if (signal) { 2001 dbus_int32_t parent; 2002 2003 parent = 0; 2004 ctx->message_append_args(signal, DBUS_TYPE_UINT32, &revision, DBUS_TYPE_INT32, &parent, DBUS_TYPE_INVALID); 2005 ctx->connection_send(conn, signal, NULL); 2006 ctx->message_unref(signal); 2007 ctx->connection_flush(conn); 2008 } 2009} 2010 2011void SDL_DBus_RegisterMenuOpenCallback(SDL_ListNode *menu, bool (*cb)(SDL_ListNode *, void *), void *cbdata) 2012{ 2013 SDL_DBusMenuItem *item; 2014 2015 item = (SDL_DBusMenuItem *)menu->entry; 2016 item->cb = cb; 2017 item->cbdata = cbdata; 2018} 2019 2020void SDL_DBus_TransferMenuItemProperties(SDL_MenuItem *src, SDL_MenuItem *dst) 2021{ 2022 SDL_DBusMenuItem *src_dbus; 2023 SDL_DBusMenuItem *dst_dbus; 2024 2025 src_dbus = (SDL_DBusMenuItem *)src; 2026 dst_dbus = (SDL_DBusMenuItem *)dst; 2027 dst_dbus->dbus = src_dbus->dbus; 2028 dst_dbus->revision = src_dbus->revision; 2029 dst_dbus->cb = src_dbus->cb; 2030 dst_dbus->cbdata = src_dbus->cbdata; 2031} 2032 2033void SDL_DBus_RetractMenu(SDL_DBusContext *ctx, DBusConnection *conn, const char **path) 2034{ 2035 ctx->connection_unregister_object_path(conn, *path); 2036 *path = NULL; 2037} 2038 2039#endif // SDL_USE_LIBDBUS 2040[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.