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