summaryrefslogtreecommitdiff
path: root/SDL-3.2.8/src/core/linux/SDL_dbus.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2026-03-06 13:26:57 -0800
committer3gg <3gg@shellblade.net>2026-03-06 13:26:57 -0800
commitf5c89b3bd5d74849757fd5b4d1a300068522a3ca (patch)
treed6f6e4c81745b393d7594b334710f30c0b2df3bd /SDL-3.2.8/src/core/linux/SDL_dbus.c
Initial commitHEADmain
Diffstat (limited to 'SDL-3.2.8/src/core/linux/SDL_dbus.c')
-rw-r--r--SDL-3.2.8/src/core/linux/SDL_dbus.c641
1 files changed, 641 insertions, 0 deletions
diff --git a/SDL-3.2.8/src/core/linux/SDL_dbus.c b/SDL-3.2.8/src/core/linux/SDL_dbus.c
new file mode 100644
index 0000000..9a2fc1e
--- /dev/null
+++ b/SDL-3.2.8/src/core/linux/SDL_dbus.c
@@ -0,0 +1,641 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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.
27static const char *dbus_library = "libdbus-1.so.3";
28static SDL_SharedObject *dbus_handle = NULL;
29static char *inhibit_handle = NULL;
30static unsigned int screensaver_cookie = 0;
31static SDL_DBusContext dbus;
32
33static bool LoadDBUSSyms(void)
34{
35#define SDL_DBUS_SYM2_OPTIONAL(TYPE, x, y) \
36 dbus.x = (TYPE)SDL_LoadFunction(dbus_handle, #y)
37
38#define SDL_DBUS_SYM2(TYPE, x, y) \
39 if (!(dbus.x = (TYPE)SDL_LoadFunction(dbus_handle, #y))) \
40 return false
41
42#define SDL_DBUS_SYM_OPTIONAL(TYPE, x) \
43 SDL_DBUS_SYM2_OPTIONAL(TYPE, x, dbus_##x)
44
45#define SDL_DBUS_SYM(TYPE, x) \
46 SDL_DBUS_SYM2(TYPE, x, dbus_##x)
47
48 SDL_DBUS_SYM(DBusConnection *(*)(DBusBusType, DBusError *), bus_get_private);
49 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusError *), bus_register);
50 SDL_DBUS_SYM(void (*)(DBusConnection *, const char *, DBusError *), bus_add_match);
51 SDL_DBUS_SYM(DBusConnection *(*)(const char *, DBusError *), connection_open_private);
52 SDL_DBUS_SYM(void (*)(DBusConnection *, dbus_bool_t), connection_set_exit_on_disconnect);
53 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *), connection_get_is_connected);
54 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusHandleMessageFunction, void *, DBusFreeFunction), connection_add_filter);
55 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusHandleMessageFunction, void *), connection_remove_filter);
56 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, const char *, const DBusObjectPathVTable *, void *, DBusError *), connection_try_register_object_path);
57 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusMessage *, dbus_uint32_t *), connection_send);
58 SDL_DBUS_SYM(DBusMessage *(*)(DBusConnection *, DBusMessage *, int, DBusError *), connection_send_with_reply_and_block);
59 SDL_DBUS_SYM(void (*)(DBusConnection *), connection_close);
60 SDL_DBUS_SYM(void (*)(DBusConnection *), connection_ref);
61 SDL_DBUS_SYM(void (*)(DBusConnection *), connection_unref);
62 SDL_DBUS_SYM(void (*)(DBusConnection *), connection_flush);
63 SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, int), connection_read_write);
64 SDL_DBUS_SYM(DBusDispatchStatus (*)(DBusConnection *), connection_dispatch);
65 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *, const char *), message_is_signal);
66 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *), message_has_path);
67 SDL_DBUS_SYM(DBusMessage *(*)(const char *, const char *, const char *, const char *), message_new_method_call);
68 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, int, ...), message_append_args);
69 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, int, va_list), message_append_args_valist);
70 SDL_DBUS_SYM(void (*)(DBusMessage *, DBusMessageIter *), message_iter_init_append);
71 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, int, const char *, DBusMessageIter *), message_iter_open_container);
72 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, int, const void *), message_iter_append_basic);
73 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, DBusMessageIter *), message_iter_close_container);
74 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, DBusError *, int, ...), message_get_args);
75 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, DBusError *, int, va_list), message_get_args_valist);
76 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, DBusMessageIter *), message_iter_init);
77 SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *), message_iter_next);
78 SDL_DBUS_SYM(void (*)(DBusMessageIter *, void *), message_iter_get_basic);
79 SDL_DBUS_SYM(int (*)(DBusMessageIter *), message_iter_get_arg_type);
80 SDL_DBUS_SYM(void (*)(DBusMessageIter *, DBusMessageIter *), message_iter_recurse);
81 SDL_DBUS_SYM(void (*)(DBusMessage *), message_unref);
82 SDL_DBUS_SYM(dbus_bool_t (*)(void), threads_init_default);
83 SDL_DBUS_SYM(void (*)(DBusError *), error_init);
84 SDL_DBUS_SYM(dbus_bool_t (*)(const DBusError *), error_is_set);
85 SDL_DBUS_SYM(void (*)(DBusError *), error_free);
86 SDL_DBUS_SYM(char *(*)(void), get_local_machine_id);
87 SDL_DBUS_SYM_OPTIONAL(char *(*)(DBusError *), try_get_local_machine_id);
88 SDL_DBUS_SYM(void (*)(void *), free);
89 SDL_DBUS_SYM(void (*)(char **), free_string_array);
90 SDL_DBUS_SYM(void (*)(void), shutdown);
91
92#undef SDL_DBUS_SYM
93#undef SDL_DBUS_SYM2
94
95 return true;
96}
97
98static void UnloadDBUSLibrary(void)
99{
100 if (dbus_handle) {
101 SDL_UnloadObject(dbus_handle);
102 dbus_handle = NULL;
103 }
104}
105
106static bool LoadDBUSLibrary(void)
107{
108 bool result = true;
109 if (!dbus_handle) {
110 dbus_handle = SDL_LoadObject(dbus_library);
111 if (!dbus_handle) {
112 result = false;
113 // Don't call SDL_SetError(): SDL_LoadObject already did.
114 } else {
115 result = LoadDBUSSyms();
116 if (!result) {
117 UnloadDBUSLibrary();
118 }
119 }
120 }
121 return result;
122}
123
124static SDL_InitState dbus_init;
125
126void SDL_DBus_Init(void)
127{
128 static bool is_dbus_available = true;
129
130 if (!is_dbus_available) {
131 return; // don't keep trying if this fails.
132 }
133
134 if (!SDL_ShouldInit(&dbus_init)) {
135 return;
136 }
137
138 if (!LoadDBUSLibrary()) {
139 goto error;
140 }
141
142 if (!dbus.threads_init_default()) {
143 goto error;
144 }
145
146 DBusError err;
147 dbus.error_init(&err);
148 // session bus is required
149
150 dbus.session_conn = dbus.bus_get_private(DBUS_BUS_SESSION, &err);
151 if (dbus.error_is_set(&err)) {
152 dbus.error_free(&err);
153 goto error;
154 }
155 dbus.connection_set_exit_on_disconnect(dbus.session_conn, 0);
156
157 // system bus is optional
158 dbus.system_conn = dbus.bus_get_private(DBUS_BUS_SYSTEM, &err);
159 if (!dbus.error_is_set(&err)) {
160 dbus.connection_set_exit_on_disconnect(dbus.system_conn, 0);
161 }
162
163 dbus.error_free(&err);
164 SDL_SetInitialized(&dbus_init, true);
165 return;
166
167error:
168 is_dbus_available = false;
169 SDL_SetInitialized(&dbus_init, true);
170 SDL_DBus_Quit();
171}
172
173void SDL_DBus_Quit(void)
174{
175 if (!SDL_ShouldQuit(&dbus_init)) {
176 return;
177 }
178
179 if (dbus.system_conn) {
180 dbus.connection_close(dbus.system_conn);
181 dbus.connection_unref(dbus.system_conn);
182 }
183 if (dbus.session_conn) {
184 dbus.connection_close(dbus.session_conn);
185 dbus.connection_unref(dbus.session_conn);
186 }
187
188 if (SDL_GetHintBoolean(SDL_HINT_SHUTDOWN_DBUS_ON_QUIT, false)) {
189 if (dbus.shutdown) {
190 dbus.shutdown();
191 }
192
193 UnloadDBUSLibrary();
194 } else {
195 /* Leaving libdbus loaded when skipping dbus_shutdown() avoids
196 * spurious leak warnings from LeakSanitizer on internal D-Bus
197 * allocations that would be freed by dbus_shutdown(). */
198 dbus_handle = NULL;
199 }
200
201 SDL_zero(dbus);
202 if (inhibit_handle) {
203 SDL_free(inhibit_handle);
204 inhibit_handle = NULL;
205 }
206
207 SDL_SetInitialized(&dbus_init, false);
208}
209
210SDL_DBusContext *SDL_DBus_GetContext(void)
211{
212 if (!dbus_handle || !dbus.session_conn) {
213 SDL_DBus_Init();
214 }
215
216 return (dbus_handle && dbus.session_conn) ? &dbus : NULL;
217}
218
219static bool SDL_DBus_CallMethodInternal(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap)
220{
221 bool result = false;
222
223 if (conn) {
224 DBusMessage *msg = dbus.message_new_method_call(node, path, interface, method);
225 if (msg) {
226 int firstarg;
227 va_list ap_reply;
228 va_copy(ap_reply, ap); // copy the arg list so we don't compete with D-Bus for it
229 firstarg = va_arg(ap, int);
230 if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) {
231 DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL);
232 if (reply) {
233 // skip any input args, get to output args.
234 while ((firstarg = va_arg(ap_reply, int)) != DBUS_TYPE_INVALID) {
235 // we assume D-Bus already validated all this.
236 {
237 void *dumpptr = va_arg(ap_reply, void *);
238 (void)dumpptr;
239 }
240 if (firstarg == DBUS_TYPE_ARRAY) {
241 {
242 const int dumpint = va_arg(ap_reply, int);
243 (void)dumpint;
244 }
245 }
246 }
247 firstarg = va_arg(ap_reply, int);
248 if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_get_args_valist(reply, NULL, firstarg, ap_reply)) {
249 result = true;
250 }
251 dbus.message_unref(reply);
252 }
253 }
254 va_end(ap_reply);
255 dbus.message_unref(msg);
256 }
257 }
258
259 return result;
260}
261
262bool SDL_DBus_CallMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...)
263{
264 bool result;
265 va_list ap;
266 va_start(ap, method);
267 result = SDL_DBus_CallMethodInternal(conn, node, path, interface, method, ap);
268 va_end(ap);
269 return result;
270}
271
272bool SDL_DBus_CallMethod(const char *node, const char *path, const char *interface, const char *method, ...)
273{
274 bool result;
275 va_list ap;
276 va_start(ap, method);
277 result = SDL_DBus_CallMethodInternal(dbus.session_conn, node, path, interface, method, ap);
278 va_end(ap);
279 return result;
280}
281
282static bool SDL_DBus_CallVoidMethodInternal(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap)
283{
284 bool result = false;
285
286 if (conn) {
287 DBusMessage *msg = dbus.message_new_method_call(node, path, interface, method);
288 if (msg) {
289 int firstarg = va_arg(ap, int);
290 if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) {
291 if (dbus.connection_send(conn, msg, NULL)) {
292 dbus.connection_flush(conn);
293 result = true;
294 }
295 }
296
297 dbus.message_unref(msg);
298 }
299 }
300
301 return result;
302}
303
304static bool SDL_DBus_CallWithBasicReply(DBusConnection *conn, DBusMessage *msg, const int expectedtype, void *result)
305{
306 bool retval = false;
307
308 DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL);
309 if (reply) {
310 DBusMessageIter iter, actual_iter;
311 dbus.message_iter_init(reply, &iter);
312 if (dbus.message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT) {
313 dbus.message_iter_recurse(&iter, &actual_iter);
314 } else {
315 actual_iter = iter;
316 }
317
318 if (dbus.message_iter_get_arg_type(&actual_iter) == expectedtype) {
319 dbus.message_iter_get_basic(&actual_iter, result);
320 retval = true;
321 }
322
323 dbus.message_unref(reply);
324 }
325
326 return retval;
327}
328
329bool SDL_DBus_CallVoidMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...)
330{
331 bool result;
332 va_list ap;
333 va_start(ap, method);
334 result = SDL_DBus_CallVoidMethodInternal(conn, node, path, interface, method, ap);
335 va_end(ap);
336 return result;
337}
338
339bool SDL_DBus_CallVoidMethod(const char *node, const char *path, const char *interface, const char *method, ...)
340{
341 bool result;
342 va_list ap;
343 va_start(ap, method);
344 result = SDL_DBus_CallVoidMethodInternal(dbus.session_conn, node, path, interface, method, ap);
345 va_end(ap);
346 return result;
347}
348
349bool SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *property, int expectedtype, void *result)
350{
351 bool retval = false;
352
353 if (conn) {
354 DBusMessage *msg = dbus.message_new_method_call(node, path, "org.freedesktop.DBus.Properties", "Get");
355 if (msg) {
356 if (dbus.message_append_args(msg, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
357 retval = SDL_DBus_CallWithBasicReply(conn, msg, expectedtype, result);
358 }
359 dbus.message_unref(msg);
360 }
361 }
362
363 return retval;
364}
365
366bool SDL_DBus_QueryProperty(const char *node, const char *path, const char *interface, const char *property, int expectedtype, void *result)
367{
368 return SDL_DBus_QueryPropertyOnConnection(dbus.session_conn, node, path, interface, property, expectedtype, result);
369}
370
371void SDL_DBus_ScreensaverTickle(void)
372{
373 if (screensaver_cookie == 0 && !inhibit_handle) { // no need to tickle if we're inhibiting.
374 // 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.
375 SDL_DBus_CallVoidMethod("org.gnome.ScreenSaver", "/org/gnome/ScreenSaver", "org.gnome.ScreenSaver", "SimulateUserActivity", DBUS_TYPE_INVALID);
376 SDL_DBus_CallVoidMethod("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver", "SimulateUserActivity", DBUS_TYPE_INVALID);
377 }
378}
379
380static bool SDL_DBus_AppendDictWithKeysAndValues(DBusMessageIter *iterInit, const char **keys, const char **values, int count)
381{
382 DBusMessageIter iterDict;
383
384 if (!dbus.message_iter_open_container(iterInit, DBUS_TYPE_ARRAY, "{sv}", &iterDict)) {
385 goto failed;
386 }
387
388 for (int i = 0; i < count; i++) {
389 DBusMessageIter iterEntry, iterValue;
390 const char *key = keys[i];
391 const char *value = values[i];
392
393 if (!dbus.message_iter_open_container(&iterDict, DBUS_TYPE_DICT_ENTRY, NULL, &iterEntry)) {
394 goto failed;
395 }
396
397 if (!dbus.message_iter_append_basic(&iterEntry, DBUS_TYPE_STRING, &key)) {
398 goto failed;
399 }
400
401 if (!dbus.message_iter_open_container(&iterEntry, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &iterValue)) {
402 goto failed;
403 }
404
405 if (!dbus.message_iter_append_basic(&iterValue, DBUS_TYPE_STRING, &value)) {
406 goto failed;
407 }
408
409 if (!dbus.message_iter_close_container(&iterEntry, &iterValue) || !dbus.message_iter_close_container(&iterDict, &iterEntry)) {
410 goto failed;
411 }
412 }
413
414 if (!dbus.message_iter_close_container(iterInit, &iterDict)) {
415 goto failed;
416 }
417
418 return true;
419
420failed:
421 /* message_iter_abandon_container_if_open() and message_iter_abandon_container() might be
422 * missing if libdbus is too old. Instead, we just return without cleaning up any eventual
423 * open container */
424 return false;
425}
426
427static bool SDL_DBus_AppendDictWithKeyValue(DBusMessageIter *iterInit, const char *key, const char *value)
428{
429 const char *keys[1];
430 const char *values[1];
431
432 keys[0] = key;
433 values[0] = value;
434 return SDL_DBus_AppendDictWithKeysAndValues(iterInit, keys, values, 1);
435}
436
437bool SDL_DBus_ScreensaverInhibit(bool inhibit)
438{
439 const char *default_inhibit_reason = "Playing a game";
440
441 if ((inhibit && (screensaver_cookie != 0 || inhibit_handle)) || (!inhibit && (screensaver_cookie == 0 && !inhibit_handle))) {
442 return true;
443 }
444
445 if (!dbus.session_conn) {
446 /* We either lost connection to the session bus or were not able to
447 * load the D-Bus library at all. */
448 return false;
449 }
450
451 if (SDL_GetSandbox() != SDL_SANDBOX_NONE) {
452 const char *bus_name = "org.freedesktop.portal.Desktop";
453 const char *path = "/org/freedesktop/portal/desktop";
454 const char *interface = "org.freedesktop.portal.Inhibit";
455 const char *window = ""; // As a future improvement we could gather the X11 XID or Wayland surface identifier
456 static const unsigned int INHIBIT_IDLE = 8; // Taken from the portal API reference
457 DBusMessageIter iterInit;
458
459 if (inhibit) {
460 DBusMessage *msg;
461 bool result = false;
462 const char *key = "reason";
463 const char *reply = NULL;
464 const char *reason = SDL_GetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME);
465 if (!reason || !reason[0]) {
466 reason = default_inhibit_reason;
467 }
468
469 msg = dbus.message_new_method_call(bus_name, path, interface, "Inhibit");
470 if (!msg) {
471 return false;
472 }
473
474 if (!dbus.message_append_args(msg, DBUS_TYPE_STRING, &window, DBUS_TYPE_UINT32, &INHIBIT_IDLE, DBUS_TYPE_INVALID)) {
475 dbus.message_unref(msg);
476 return false;
477 }
478
479 dbus.message_iter_init_append(msg, &iterInit);
480
481 // a{sv}
482 if (!SDL_DBus_AppendDictWithKeyValue(&iterInit, key, reason)) {
483 dbus.message_unref(msg);
484 return false;
485 }
486
487 if (SDL_DBus_CallWithBasicReply(dbus.session_conn, msg, DBUS_TYPE_OBJECT_PATH, &reply)) {
488 inhibit_handle = SDL_strdup(reply);
489 result = true;
490 }
491
492 dbus.message_unref(msg);
493 return result;
494 } else {
495 if (!SDL_DBus_CallVoidMethod(bus_name, inhibit_handle, "org.freedesktop.portal.Request", "Close", DBUS_TYPE_INVALID)) {
496 return false;
497 }
498 SDL_free(inhibit_handle);
499 inhibit_handle = NULL;
500 }
501 } else {
502 const char *bus_name = "org.freedesktop.ScreenSaver";
503 const char *path = "/org/freedesktop/ScreenSaver";
504 const char *interface = "org.freedesktop.ScreenSaver";
505
506 if (inhibit) {
507 const char *app = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING);
508 const char *reason = SDL_GetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME);
509 if (!reason || !reason[0]) {
510 reason = default_inhibit_reason;
511 }
512
513 if (!SDL_DBus_CallMethod(bus_name, path, interface, "Inhibit",
514 DBUS_TYPE_STRING, &app, DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID,
515 DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) {
516 return false;
517 }
518 return (screensaver_cookie != 0);
519 } else {
520 if (!SDL_DBus_CallVoidMethod(bus_name, path, interface, "UnInhibit", DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) {
521 return false;
522 }
523 screensaver_cookie = 0;
524 }
525 }
526
527 return true;
528}
529
530void SDL_DBus_PumpEvents(void)
531{
532 if (dbus.session_conn) {
533 dbus.connection_read_write(dbus.session_conn, 0);
534
535 while (dbus.connection_dispatch(dbus.session_conn) == DBUS_DISPATCH_DATA_REMAINS) {
536 // Do nothing, actual work happens in DBus_MessageFilter
537 SDL_DelayNS(SDL_US_TO_NS(10));
538 }
539 }
540}
541
542/*
543 * Get the machine ID if possible. Result must be freed with dbus->free().
544 */
545char *SDL_DBus_GetLocalMachineId(void)
546{
547 DBusError err;
548 char *result;
549
550 dbus.error_init(&err);
551
552 if (dbus.try_get_local_machine_id) {
553 // Available since dbus 1.12.0, has proper error-handling
554 result = dbus.try_get_local_machine_id(&err);
555 } else {
556 /* Available since time immemorial, but has no error-handling:
557 * if the machine ID can't be read, many versions of libdbus will
558 * treat that as a fatal mis-installation and abort() */
559 result = dbus.get_local_machine_id();
560 }
561
562 if (result) {
563 return result;
564 }
565
566 if (dbus.error_is_set(&err)) {
567 SDL_SetError("%s: %s", err.name, err.message);
568 dbus.error_free(&err);
569 } else {
570 SDL_SetError("Error getting D-Bus machine ID");
571 }
572
573 return NULL;
574}
575
576/*
577 * Convert file drops with mime type "application/vnd.portal.filetransfer" to file paths
578 * Result must be freed with dbus->free_string_array().
579 * https://flatpak.github.io/xdg-desktop-portal/#gdbus-method-org-freedesktop-portal-FileTransfer.RetrieveFiles
580 */
581char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *path_count)
582{
583 DBusError err;
584 DBusMessageIter iter, iterDict;
585 char **paths = NULL;
586 DBusMessage *reply = NULL;
587 DBusMessage *msg = dbus.message_new_method_call("org.freedesktop.portal.Documents", // Node
588 "/org/freedesktop/portal/documents", // Path
589 "org.freedesktop.portal.FileTransfer", // Interface
590 "RetrieveFiles"); // Method
591
592 // Make sure we have a connection to the dbus session bus
593 if (!SDL_DBus_GetContext() || !dbus.session_conn) {
594 /* We either cannot connect to the session bus or were unable to
595 * load the D-Bus library at all. */
596 return NULL;
597 }
598
599 dbus.error_init(&err);
600
601 // First argument is a "application/vnd.portal.filetransfer" key from a DnD or clipboard event
602 if (!dbus.message_append_args(msg, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) {
603 SDL_OutOfMemory();
604 dbus.message_unref(msg);
605 goto failed;
606 }
607
608 /* Second argument is a variant dictionary for options.
609 * The spec doesn't define any entries yet so it's empty. */
610 dbus.message_iter_init_append(msg, &iter);
611 if (!dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iterDict) ||
612 !dbus.message_iter_close_container(&iter, &iterDict)) {
613 SDL_OutOfMemory();
614 dbus.message_unref(msg);
615 goto failed;
616 }
617
618 reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, &err);
619 dbus.message_unref(msg);
620
621 if (reply) {
622 dbus.message_get_args(reply, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &paths, path_count, DBUS_TYPE_INVALID);
623 dbus.message_unref(reply);
624 }
625
626 if (paths) {
627 return paths;
628 }
629
630failed:
631 if (dbus.error_is_set(&err)) {
632 SDL_SetError("%s: %s", err.name, err.message);
633 dbus.error_free(&err);
634 } else {
635 SDL_SetError("Error retrieving paths for documents portal \"%s\"", key);
636 }
637
638 return NULL;
639}
640
641#endif