SDL_dbus.c 23 KB

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