Atlas - SDL_dbus.c

Home / ext / SDL / src / core / linux Lines: 1 | Size: 31133 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#include "SDL_internal.h" 22#include "SDL_dbus.h" 23#include "../../stdlib/SDL_vacopy.h" 24 25#ifdef SDL_USE_LIBDBUS 26// we never link directly to libdbus. 27#define SDL_DRIVER_DBUS_DYNAMIC "libdbus-1.so.3" 28static const char *dbus_library = SDL_DRIVER_DBUS_DYNAMIC; 29static SDL_SharedObject *dbus_handle = NULL; 30static char *inhibit_handle = NULL; 31static unsigned int screensaver_cookie = 0; 32static SDL_DBusContext dbus; 33 34SDL_ELF_NOTE_DLOPEN( 35 "core-libdbus", 36 "Support for D-Bus IPC", 37 SDL_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, 38 SDL_DRIVER_DBUS_DYNAMIC 39) 40 41static bool LoadDBUSSyms(void) 42{ 43#define SDL_DBUS_SYM2_OPTIONAL(TYPE, x, y) \ 44 dbus.x = (TYPE)SDL_LoadFunction(dbus_handle, #y) 45 46#define SDL_DBUS_SYM2(TYPE, x, y) \ 47 if (!(dbus.x = (TYPE)SDL_LoadFunction(dbus_handle, #y))) \ 48 return false 49 50#define SDL_DBUS_SYM_OPTIONAL(TYPE, x) \ 51 SDL_DBUS_SYM2_OPTIONAL(TYPE, x, dbus_##x) 52 53#define SDL_DBUS_SYM(TYPE, x) \ 54 SDL_DBUS_SYM2(TYPE, x, dbus_##x) 55 56 SDL_DBUS_SYM(DBusConnection *(*)(DBusBusType, DBusError *), bus_get_private); 57 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusError *), bus_register); 58 SDL_DBUS_SYM(void (*)(DBusConnection *, const char *, DBusError *), bus_add_match); 59 SDL_DBUS_SYM(void (*)(DBusConnection *, const char *, DBusError *), bus_remove_match); 60 SDL_DBUS_SYM(const char *(*)(DBusConnection *), bus_get_unique_name); 61 SDL_DBUS_SYM(DBusConnection *(*)(const char *, DBusError *), connection_open_private); 62 SDL_DBUS_SYM(void (*)(DBusConnection *, dbus_bool_t), connection_set_exit_on_disconnect); 63 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *), connection_get_is_connected); 64 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusHandleMessageFunction, void *, DBusFreeFunction), connection_add_filter); 65 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusHandleMessageFunction, void *), connection_remove_filter); 66 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, const char *, const DBusObjectPathVTable *, void *, DBusError *), connection_try_register_object_path); 67 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusMessage *, dbus_uint32_t *), connection_send); 68 SDL_DBUS_SYM(DBusMessage *(*)(DBusConnection *, DBusMessage *, int, DBusError *), connection_send_with_reply_and_block); 69 SDL_DBUS_SYM(void (*)(DBusConnection *), connection_close); 70 SDL_DBUS_SYM(void (*)(DBusConnection *), connection_ref); 71 SDL_DBUS_SYM(void (*)(DBusConnection *), connection_unref); 72 SDL_DBUS_SYM(void (*)(DBusConnection *), connection_flush); 73 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, int), connection_read_write); 74 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, int), connection_read_write_dispatch); 75 SDL_DBUS_SYM(DBusDispatchStatus (*)(DBusConnection *), connection_dispatch); 76 SDL_DBUS_SYM(dbus_bool_t (*)(int), type_is_fixed); 77 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *, const char *), message_is_signal); 78 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *), message_has_path); 79 SDL_DBUS_SYM(DBusMessage *(*)(const char *, const char *, const char *, const char *), message_new_method_call); 80 SDL_DBUS_SYM(DBusMessage *(*)(const char *, const char *, const char *), message_new_signal); 81 SDL_DBUS_SYM(void (*)(DBusMessage *, dbus_bool_t), message_set_no_reply); 82 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, int, ...), message_append_args); 83 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, int, va_list), message_append_args_valist); 84 SDL_DBUS_SYM(void (*)(DBusMessage *, DBusMessageIter *), message_iter_init_append); 85 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, int, const char *, DBusMessageIter *), message_iter_open_container); 86 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, int, const void *), message_iter_append_basic); 87 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, DBusMessageIter *), message_iter_close_container); 88 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, DBusError *, int, ...), message_get_args); 89 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, DBusError *, int, va_list), message_get_args_valist); 90 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, DBusMessageIter *), message_iter_init); 91 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *), message_iter_next); 92 SDL_DBUS_SYM(void (*)(DBusMessageIter *, void *), message_iter_get_basic); 93 SDL_DBUS_SYM(int (*)(DBusMessageIter *), message_iter_get_arg_type); 94 SDL_DBUS_SYM(void (*)(DBusMessageIter *, DBusMessageIter *), message_iter_recurse); 95 SDL_DBUS_SYM(void (*)(DBusMessage *), message_unref); 96 SDL_DBUS_SYM(dbus_bool_t (*)(void), threads_init_default); 97 SDL_DBUS_SYM(void (*)(DBusError *), error_init); 98 SDL_DBUS_SYM(dbus_bool_t (*)(const DBusError *), error_is_set); 99 SDL_DBUS_SYM(dbus_bool_t (*)(const DBusError *, const char *), error_has_name); 100 SDL_DBUS_SYM(void (*)(DBusError *), error_free); 101 SDL_DBUS_SYM(char *(*)(void), get_local_machine_id); 102 SDL_DBUS_SYM_OPTIONAL(char *(*)(DBusError *), try_get_local_machine_id); 103 SDL_DBUS_SYM(void (*)(void *), free); 104 SDL_DBUS_SYM(void (*)(char **), free_string_array); 105 SDL_DBUS_SYM(void (*)(void), shutdown); 106 107#undef SDL_DBUS_SYM 108#undef SDL_DBUS_SYM2 109 110 return true; 111} 112 113static void UnloadDBUSLibrary(void) 114{ 115 if (dbus_handle) { 116 SDL_UnloadObject(dbus_handle); 117 dbus_handle = NULL; 118 } 119} 120 121static bool LoadDBUSLibrary(void) 122{ 123 bool result = true; 124 if (!dbus_handle) { 125 dbus_handle = SDL_LoadObject(dbus_library); 126 if (!dbus_handle) { 127 result = false; 128 // Don't call SDL_SetError(): SDL_LoadObject already did. 129 } else { 130 result = LoadDBUSSyms(); 131 if (!result) { 132 UnloadDBUSLibrary(); 133 } 134 } 135 } 136 return result; 137} 138 139static SDL_InitState dbus_init; 140 141void SDL_DBus_Init(void) 142{ 143 static bool is_dbus_available = true; 144 145 if (!is_dbus_available) { 146 return; // don't keep trying if this fails. 147 } 148 149 if (!SDL_ShouldInit(&dbus_init)) { 150 return; 151 } 152 153 if (!LoadDBUSLibrary()) { 154 goto error; 155 } 156 157 if (!dbus.threads_init_default()) { 158 goto error; 159 } 160 161 DBusError err; 162 dbus.error_init(&err); 163 // session bus is required 164 165 dbus.session_conn = dbus.bus_get_private(DBUS_BUS_SESSION, &err); 166 if (dbus.error_is_set(&err)) { 167 dbus.error_free(&err); 168 goto error; 169 } 170 dbus.connection_set_exit_on_disconnect(dbus.session_conn, 0); 171 172 // system bus is optional 173 dbus.system_conn = dbus.bus_get_private(DBUS_BUS_SYSTEM, &err); 174 if (!dbus.error_is_set(&err)) { 175 dbus.connection_set_exit_on_disconnect(dbus.system_conn, 0); 176 } 177 178 dbus.error_free(&err); 179 SDL_SetInitialized(&dbus_init, true); 180 return; 181 182error: 183 is_dbus_available = false; 184 SDL_SetInitialized(&dbus_init, true); 185 SDL_DBus_Quit(); 186} 187 188void SDL_DBus_Quit(void) 189{ 190 if (!SDL_ShouldQuit(&dbus_init)) { 191 return; 192 } 193 194 if (dbus.system_conn) { 195 dbus.connection_close(dbus.system_conn); 196 dbus.connection_unref(dbus.system_conn); 197 } 198 if (dbus.session_conn) { 199 dbus.connection_close(dbus.session_conn); 200 dbus.connection_unref(dbus.session_conn); 201 } 202 203 if (SDL_GetHintBoolean(SDL_HINT_SHUTDOWN_DBUS_ON_QUIT, false)) { 204 if (dbus.shutdown) { 205 dbus.shutdown(); 206 } 207 208 UnloadDBUSLibrary(); 209 } else { 210 /* Leaving libdbus loaded when skipping dbus_shutdown() avoids 211 * spurious leak warnings from LeakSanitizer on internal D-Bus 212 * allocations that would be freed by dbus_shutdown(). */ 213 dbus_handle = NULL; 214 } 215 216 SDL_zero(dbus); 217 if (inhibit_handle) { 218 SDL_free(inhibit_handle); 219 inhibit_handle = NULL; 220 } 221 222 SDL_SetInitialized(&dbus_init, false); 223} 224 225SDL_DBusContext *SDL_DBus_GetContext(void) 226{ 227 if (!dbus_handle || !dbus.session_conn) { 228 SDL_DBus_Init(); 229 } 230 231 return (dbus_handle && dbus.session_conn) ? &dbus : NULL; 232} 233 234static bool SDL_DBus_CallMethodInternal(DBusConnection *conn, DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *method, va_list ap) 235{ 236 bool result = false; 237 238 if (conn) { 239 DBusMessage *msg = dbus.message_new_method_call(node, path, interface, method); 240 if (msg) { 241 int firstarg; 242 va_list ap_reply; 243 va_copy(ap_reply, ap); // copy the arg list so we don't compete with D-Bus for it 244 firstarg = va_arg(ap, int); 245 if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) { 246 DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL); 247 if (reply) { 248 // skip any input args, get to output args. 249 while ((firstarg = va_arg(ap_reply, int)) != DBUS_TYPE_INVALID) { 250 // we assume D-Bus already validated all this. 251 { 252 void *dumpptr = va_arg(ap_reply, void *); 253 (void)dumpptr; 254 } 255 if (firstarg == DBUS_TYPE_ARRAY) { 256 { 257 const int dumpint = va_arg(ap_reply, int); 258 (void)dumpint; 259 } 260 } 261 } 262 firstarg = va_arg(ap_reply, int); 263 if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_get_args_valist(reply, NULL, firstarg, ap_reply)) { 264 result = true; 265 } 266 if (save_reply) { 267 *save_reply = reply; 268 } else { 269 dbus.message_unref(reply); 270 } 271 } 272 } 273 va_end(ap_reply); 274 dbus.message_unref(msg); 275 } 276 } 277 278 return result; 279} 280 281bool SDL_DBus_CallMethodOnConnection(DBusConnection *conn, DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *method, ...) 282{ 283 bool result; 284 va_list ap; 285 va_start(ap, method); 286 result = SDL_DBus_CallMethodInternal(conn, save_reply, node, path, interface, method, ap); 287 va_end(ap); 288 return result; 289} 290 291bool SDL_DBus_CallMethod(DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *method, ...) 292{ 293 bool result; 294 va_list ap; 295 va_start(ap, method); 296 result = SDL_DBus_CallMethodInternal(dbus.session_conn, save_reply, node, path, interface, method, ap); 297 va_end(ap); 298 return result; 299} 300 301static bool SDL_DBus_CallVoidMethodInternal(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap) 302{ 303 bool result = false; 304 305 if (conn) { 306 DBusMessage *msg = dbus.message_new_method_call(node, path, interface, method); 307 if (msg) { 308 int firstarg = va_arg(ap, int); 309 if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) { 310 dbus.message_set_no_reply(msg, true); 311 if (dbus.connection_send(conn, msg, NULL)) { 312 dbus.connection_flush(conn); 313 result = true; 314 } 315 } 316 317 dbus.message_unref(msg); 318 } 319 } 320 321 return result; 322} 323 324bool SDL_DBus_CallVoidMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...) 325{ 326 bool result; 327 va_list ap; 328 va_start(ap, method); 329 result = SDL_DBus_CallVoidMethodInternal(conn, node, path, interface, method, ap); 330 va_end(ap); 331 return result; 332} 333 334bool SDL_DBus_CallVoidMethod(const char *node, const char *path, const char *interface, const char *method, ...) 335{ 336 bool result; 337 va_list ap; 338 va_start(ap, method); 339 result = SDL_DBus_CallVoidMethodInternal(dbus.session_conn, node, path, interface, method, ap); 340 va_end(ap); 341 return result; 342} 343 344static bool SDL_DBus_CallWithBasicReply(DBusConnection *conn, DBusMessage **save_reply, DBusMessage *msg, const int expectedtype, void *result) 345{ 346 bool retval = false; 347 348 // Make sure we're not looking up strings here, otherwise we'd have to save and return the reply 349 SDL_assert(save_reply == NULL || *save_reply == NULL); 350 SDL_assert(save_reply != NULL || dbus.type_is_fixed(expectedtype)); 351 352 DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL); 353 if (reply) { 354 DBusMessageIter iter, actual_iter; 355 dbus.message_iter_init(reply, &iter); 356 if (dbus.message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT) { 357 dbus.message_iter_recurse(&iter, &actual_iter); 358 } else { 359 actual_iter = iter; 360 } 361 362 if (dbus.message_iter_get_arg_type(&actual_iter) == expectedtype) { 363 dbus.message_iter_get_basic(&actual_iter, result); 364 retval = true; 365 } 366 367 if (save_reply) { 368 *save_reply = reply; 369 } else { 370 dbus.message_unref(reply); 371 } 372 } 373 374 return retval; 375} 376 377bool SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *property, int expectedtype, void *result) 378{ 379 bool retval = false; 380 381 if (conn) { 382 DBusMessage *msg = dbus.message_new_method_call(node, path, "org.freedesktop.DBus.Properties", "Get"); 383 if (msg) { 384 if (dbus.message_append_args(msg, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { 385 retval = SDL_DBus_CallWithBasicReply(conn, save_reply, msg, expectedtype, result); 386 } 387 dbus.message_unref(msg); 388 } 389 } 390 391 return retval; 392} 393 394bool SDL_DBus_QueryProperty(DBusMessage **save_reply, const char *node, const char *path, const char *interface, const char *property, int expectedtype, void *result) 395{ 396 return SDL_DBus_QueryPropertyOnConnection(dbus.session_conn, save_reply, node, path, interface, property, expectedtype, result); 397} 398 399void SDL_DBus_FreeReply(DBusMessage **saved_reply) 400{ 401 DBusMessage *reply = *saved_reply; 402 if (reply) { 403 dbus.message_unref(reply); 404 *saved_reply = NULL; 405 } 406} 407 408void SDL_DBus_ScreensaverTickle(void) 409{ 410 if (screensaver_cookie == 0 && !inhibit_handle) { // no need to tickle if we're inhibiting. 411 // 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. 412 SDL_DBus_CallVoidMethod("org.gnome.ScreenSaver", "/org/gnome/ScreenSaver", "org.gnome.ScreenSaver", "SimulateUserActivity", DBUS_TYPE_INVALID); 413 SDL_DBus_CallVoidMethod("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver", "SimulateUserActivity", DBUS_TYPE_INVALID); 414 } 415} 416 417static bool SDL_DBus_AppendDictWithKeysAndValues(DBusMessageIter *iterInit, const char **keys, const char **values, int count) 418{ 419 DBusMessageIter iterDict; 420 421 if (!dbus.message_iter_open_container(iterInit, DBUS_TYPE_ARRAY, "{sv}", &iterDict)) { 422 goto failed; 423 } 424 425 for (int i = 0; i < count; i++) { 426 DBusMessageIter iterEntry, iterValue; 427 const char *key = keys[i]; 428 const char *value = values[i]; 429 430 if (!dbus.message_iter_open_container(&iterDict, DBUS_TYPE_DICT_ENTRY, NULL, &iterEntry)) { 431 goto failed; 432 } 433 434 if (!dbus.message_iter_append_basic(&iterEntry, DBUS_TYPE_STRING, &key)) { 435 goto failed; 436 } 437 438 if (!dbus.message_iter_open_container(&iterEntry, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &iterValue)) { 439 goto failed; 440 } 441 442 if (!dbus.message_iter_append_basic(&iterValue, DBUS_TYPE_STRING, &value)) { 443 goto failed; 444 } 445 446 if (!dbus.message_iter_close_container(&iterEntry, &iterValue) || !dbus.message_iter_close_container(&iterDict, &iterEntry)) { 447 goto failed; 448 } 449 } 450 451 if (!dbus.message_iter_close_container(iterInit, &iterDict)) { 452 goto failed; 453 } 454 455 return true; 456 457failed: 458 /* message_iter_abandon_container_if_open() and message_iter_abandon_container() might be 459 * missing if libdbus is too old. Instead, we just return without cleaning up any eventual 460 * open container */ 461 return false; 462} 463 464static bool SDL_DBus_AppendDictWithKeyValue(DBusMessageIter *iterInit, const char *key, const char *value) 465{ 466 const char *keys[1]; 467 const char *values[1]; 468 469 keys[0] = key; 470 values[0] = value; 471 return SDL_DBus_AppendDictWithKeysAndValues(iterInit, keys, values, 1); 472} 473 474bool SDL_DBus_ScreensaverInhibit(bool inhibit) 475{ 476 const char *default_inhibit_reason = "Playing a game"; 477 478 if ((inhibit && (screensaver_cookie != 0 || inhibit_handle)) || (!inhibit && (screensaver_cookie == 0 && !inhibit_handle))) { 479 return true; 480 } 481 482 if (!dbus.session_conn) { 483 /* We either lost connection to the session bus or were not able to 484 * load the D-Bus library at all. */ 485 return false; 486 } 487 488 if (SDL_GetSandbox() != SDL_SANDBOX_NONE) { 489 const char *bus_name = "org.freedesktop.portal.Desktop"; 490 const char *path = "/org/freedesktop/portal/desktop"; 491 const char *interface = "org.freedesktop.portal.Inhibit"; 492 const char *window = ""; // As a future improvement we could gather the X11 XID or Wayland surface identifier 493 static const unsigned int INHIBIT_IDLE = 8; // Taken from the portal API reference 494 DBusMessageIter iterInit; 495 496 if (inhibit) { 497 DBusMessage *msg; 498 bool result = false; 499 const char *key = "reason"; 500 const char *reply_path = NULL; 501 const char *reason = SDL_GetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME); 502 if (!reason || !reason[0]) { 503 reason = default_inhibit_reason; 504 } 505 506 msg = dbus.message_new_method_call(bus_name, path, interface, "Inhibit"); 507 if (!msg) { 508 return false; 509 } 510 511 if (!dbus.message_append_args(msg, DBUS_TYPE_STRING, &window, DBUS_TYPE_UINT32, &INHIBIT_IDLE, DBUS_TYPE_INVALID)) { 512 dbus.message_unref(msg); 513 return false; 514 } 515 516 dbus.message_iter_init_append(msg, &iterInit); 517 518 // a{sv} 519 if (!SDL_DBus_AppendDictWithKeyValue(&iterInit, key, reason)) { 520 dbus.message_unref(msg); 521 return false; 522 } 523 524 DBusMessage *reply = NULL; 525 if (SDL_DBus_CallWithBasicReply(dbus.session_conn, &reply, msg, DBUS_TYPE_OBJECT_PATH, &reply_path)) { 526 inhibit_handle = SDL_strdup(reply_path); 527 result = true; 528 } 529 SDL_DBus_FreeReply(&reply); 530 dbus.message_unref(msg); 531 return result; 532 } else { 533 if (!SDL_DBus_CallVoidMethod(bus_name, inhibit_handle, "org.freedesktop.portal.Request", "Close", DBUS_TYPE_INVALID)) { 534 return false; 535 } 536 SDL_free(inhibit_handle); 537 inhibit_handle = NULL; 538 } 539 } else { 540 const char *bus_name = "org.freedesktop.ScreenSaver"; 541 const char *path = "/org/freedesktop/ScreenSaver"; 542 const char *interface = "org.freedesktop.ScreenSaver"; 543 544 if (inhibit) { 545 const char *app = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING); 546 const char *reason = SDL_GetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME); 547 if (!reason || !reason[0]) { 548 reason = default_inhibit_reason; 549 } 550 551 if (!SDL_DBus_CallMethod(NULL, bus_name, path, interface, "Inhibit", 552 DBUS_TYPE_STRING, &app, DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID, 553 DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) { 554 return false; 555 } 556 return (screensaver_cookie != 0); 557 } else { 558 if (!SDL_DBus_CallVoidMethod(bus_name, path, interface, "UnInhibit", DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) { 559 return false; 560 } 561 screensaver_cookie = 0; 562 } 563 } 564 565 return true; 566} 567 568void SDL_DBus_PumpEvents(void) 569{ 570 if (dbus.session_conn) { 571 dbus.connection_read_write(dbus.session_conn, 0); 572 573 while (dbus.connection_dispatch(dbus.session_conn) == DBUS_DISPATCH_DATA_REMAINS) { 574 // Do nothing, actual work happens in DBus_MessageFilter 575 SDL_DelayNS(SDL_US_TO_NS(10)); 576 } 577 } 578} 579 580/* 581 * Get the machine ID if possible. Result must be freed with dbus->free(). 582 */ 583char *SDL_DBus_GetLocalMachineId(void) 584{ 585 DBusError err; 586 char *result; 587 588 dbus.error_init(&err); 589 590 if (dbus.try_get_local_machine_id) { 591 // Available since dbus 1.12.0, has proper error-handling 592 result = dbus.try_get_local_machine_id(&err); 593 } else { 594 /* Available since time immemorial, but has no error-handling: 595 * if the machine ID can't be read, many versions of libdbus will 596 * treat that as a fatal mis-installation and abort() */ 597 result = dbus.get_local_machine_id(); 598 } 599 600 if (result) { 601 return result; 602 } 603 604 if (dbus.error_is_set(&err)) { 605 SDL_SetError("%s: %s", err.name, err.message); 606 dbus.error_free(&err); 607 } else { 608 SDL_SetError("Error getting D-Bus machine ID"); 609 } 610 611 return NULL; 612} 613 614/* 615 * Convert file drops with mime type "application/vnd.portal.filetransfer" to file paths 616 * Result must be freed with dbus->free_string_array(). 617 * https://flatpak.github.io/xdg-desktop-portal/#gdbus-method-org-freedesktop-portal-FileTransfer.RetrieveFiles 618 */ 619char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *path_count) 620{ 621 DBusError err; 622 DBusMessageIter iter, iterDict; 623 char **paths = NULL; 624 DBusMessage *reply = NULL; 625 DBusMessage *msg = dbus.message_new_method_call("org.freedesktop.portal.Documents", // Node 626 "/org/freedesktop/portal/documents", // Path 627 "org.freedesktop.portal.FileTransfer", // Interface 628 "RetrieveFiles"); // Method 629 630 // Make sure we have a connection to the dbus session bus 631 if (!SDL_DBus_GetContext() || !dbus.session_conn) { 632 /* We either cannot connect to the session bus or were unable to 633 * load the D-Bus library at all. */ 634 return NULL; 635 } 636 637 dbus.error_init(&err); 638 639 // First argument is a "application/vnd.portal.filetransfer" key from a DnD or clipboard event 640 if (!dbus.message_append_args(msg, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) { 641 SDL_OutOfMemory(); 642 dbus.message_unref(msg); 643 goto failed; 644 } 645 646 /* Second argument is a variant dictionary for options. 647 * The spec doesn't define any entries yet so it's empty. */ 648 dbus.message_iter_init_append(msg, &iter); 649 if (!dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iterDict) || 650 !dbus.message_iter_close_container(&iter, &iterDict)) { 651 SDL_OutOfMemory(); 652 dbus.message_unref(msg); 653 goto failed; 654 } 655 656 reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, &err); 657 dbus.message_unref(msg); 658 659 if (reply) { 660 dbus.message_get_args(reply, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &paths, path_count, DBUS_TYPE_INVALID); 661 dbus.message_unref(reply); 662 } 663 664 if (paths) { 665 return paths; 666 } 667 668failed: 669 if (dbus.error_is_set(&err)) { 670 SDL_SetError("%s: %s", err.name, err.message); 671 dbus.error_free(&err); 672 } else { 673 SDL_SetError("Error retrieving paths for documents portal \"%s\"", key); 674 } 675 676 return NULL; 677} 678 679typedef struct SDL_DBus_CameraPortalMessageHandlerData 680{ 681 uint32_t response; 682 char *path; 683 DBusError *err; 684 bool done; 685} SDL_DBus_CameraPortalMessageHandlerData; 686 687static DBusHandlerResult SDL_DBus_CameraPortalMessageHandler(DBusConnection *conn, DBusMessage *msg, void *v) 688{ 689 SDL_DBus_CameraPortalMessageHandlerData *data = v; 690 const char *name, *old, *new; 691 692 if (dbus.message_is_signal(msg, "org.freedesktop.DBus", "NameOwnerChanged")) { 693 if (!dbus.message_get_args(msg, data->err, 694 DBUS_TYPE_STRING, &name, 695 DBUS_TYPE_STRING, &old, 696 DBUS_TYPE_STRING, &new, 697 DBUS_TYPE_INVALID)) { 698 data->done = true; 699 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 700 } 701 if (SDL_strcmp(name, "org.freedesktop.portal.Desktop") != 0 || 702 SDL_strcmp(new, "") != 0) { 703 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 704 } 705 data->done = true; 706 data->response = -1; 707 return DBUS_HANDLER_RESULT_HANDLED; 708 } 709 if (!dbus.message_has_path(msg, data->path) || !dbus.message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) { 710 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 711 } 712 dbus.message_get_args(msg, data->err, DBUS_TYPE_UINT32, &data->response, DBUS_TYPE_INVALID); 713 data->done = true; 714 return DBUS_HANDLER_RESULT_HANDLED; 715} 716 717#define SIGNAL_NAMEOWNERCHANGED "type='signal',\ 718 sender='org.freedesktop.DBus',\ 719 interface='org.freedesktop.DBus',\ 720 member='NameOwnerChanged',\ 721 arg0='org.freedesktop.portal.Desktop',\ 722 arg2=''" 723 724/* 725 * Requests access for the camera. Returns -1 on error, -2 on denied access or 726 * missing portal, otherwise returns a file descriptor to be used by the Pipewire driver. 727 * https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Camera.html 728 */ 729int SDL_DBus_CameraPortalRequestAccess(void) 730{ 731 SDL_DBus_CameraPortalMessageHandlerData data; 732 DBusError err; 733 DBusMessageIter iter, iterDict; 734 DBusMessage *reply, *msg; 735 int fd; 736 737 if (SDL_GetSandbox() == SDL_SANDBOX_NONE) { 738 return -2; 739 } 740 741 if (!SDL_DBus_GetContext()) { 742 return -2; 743 } 744 745 dbus.error_init(&err); 746 747 msg = dbus.message_new_method_call("org.freedesktop.portal.Desktop", 748 "/org/freedesktop/portal/desktop", 749 "org.freedesktop.portal.Camera", 750 "AccessCamera"); 751 752 dbus.message_iter_init_append(msg, &iter); 753 if (!dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iterDict) || 754 !dbus.message_iter_close_container(&iter, &iterDict)) { 755 SDL_OutOfMemory(); 756 dbus.message_unref(msg); 757 goto failed; 758 } 759 760 reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, &err); 761 dbus.message_unref(msg); 762 763 if (reply) { 764 dbus.message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, &data.path, DBUS_TYPE_INVALID); 765 if (dbus.error_is_set(&err)) { 766 dbus.message_unref(reply); 767 goto failed; 768 } 769 if ((data.path = SDL_strdup(data.path)) == NULL) { 770 dbus.message_unref(reply); 771 SDL_OutOfMemory(); 772 goto failed; 773 } 774 dbus.message_unref(reply); 775 } else { 776 if (dbus.error_has_name(&err, DBUS_ERROR_NAME_HAS_NO_OWNER)) { 777 return -2; 778 } 779 goto failed; 780 } 781 782 dbus.bus_add_match(dbus.session_conn, SIGNAL_NAMEOWNERCHANGED, &err); 783 if (dbus.error_is_set(&err)) { 784 SDL_free(data.path); 785 goto failed; 786 } 787 data.err = &err; 788 data.done = false; 789 if (!dbus.connection_add_filter(dbus.session_conn, SDL_DBus_CameraPortalMessageHandler, &data, NULL)) { 790 SDL_free(data.path); 791 SDL_OutOfMemory(); 792 goto failed; 793 } 794 while (!data.done && dbus.connection_read_write_dispatch(dbus.session_conn, -1)) { 795 ; 796 } 797 798 dbus.bus_remove_match(dbus.session_conn, SIGNAL_NAMEOWNERCHANGED, &err); 799 if (dbus.error_is_set(&err)) { 800 SDL_free(data.path); 801 goto failed; 802 } 803 dbus.connection_remove_filter(dbus.session_conn, SDL_DBus_CameraPortalMessageHandler, &data); 804 SDL_free(data.path); 805 if (!data.done) { 806 goto failed; 807 } 808 if (dbus.error_is_set(&err)) { // from the message handler 809 goto failed; 810 } 811 if (data.response == 1 || data.response == 2) { 812 return -2; 813 } else if (data.response != 0) { 814 goto failed; 815 } 816 817 msg = dbus.message_new_method_call("org.freedesktop.portal.Desktop", 818 "/org/freedesktop/portal/desktop", 819 "org.freedesktop.portal.Camera", 820 "OpenPipeWireRemote"); 821 822 dbus.message_iter_init_append(msg, &iter); 823 if (!dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iterDict) || 824 !dbus.message_iter_close_container(&iter, &iterDict)) { 825 SDL_OutOfMemory(); 826 dbus.message_unref(msg); 827 goto failed; 828 } 829 830 reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, &err); 831 dbus.message_unref(msg); 832 833 if (reply) { 834 dbus.message_get_args(reply, &err, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID); 835 dbus.message_unref(reply); 836 if (dbus.error_is_set(&err)) { 837 goto failed; 838 } 839 } else { 840 goto failed; 841 } 842 843 return fd; 844 845failed: 846 if (dbus.error_is_set(&err)) { 847 if (dbus.error_has_name(&err, DBUS_ERROR_NO_MEMORY)) { 848 SDL_OutOfMemory(); 849 } 850 SDL_SetError("%s: %s", err.name, err.message); 851 dbus.error_free(&err); 852 } else { 853 SDL_SetError("Error requesting access for the camera"); 854 } 855 856 return -1; 857} 858 859#endif 860
[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.