diff options
| author | 3gg <3gg@shellblade.net> | 2026-03-06 13:26:57 -0800 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2026-03-06 13:26:57 -0800 |
| commit | f5c89b3bd5d74849757fd5b4d1a300068522a3ca (patch) | |
| tree | d6f6e4c81745b393d7594b334710f30c0b2df3bd /SDL-3.2.8/src/core/linux/SDL_dbus.c | |
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.c | 641 |
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. | ||
| 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 | if (dbus_handle) { | ||
| 101 | SDL_UnloadObject(dbus_handle); | ||
| 102 | dbus_handle = NULL; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | static 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 | |||
| 124 | static SDL_InitState dbus_init; | ||
| 125 | |||
| 126 | void 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 | |||
| 167 | error: | ||
| 168 | is_dbus_available = false; | ||
| 169 | SDL_SetInitialized(&dbus_init, true); | ||
| 170 | SDL_DBus_Quit(); | ||
| 171 | } | ||
| 172 | |||
| 173 | void 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 | |||
| 210 | SDL_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 | |||
| 219 | static 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 | |||
| 262 | bool 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 | |||
| 272 | bool 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 | |||
| 282 | static 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 | |||
| 304 | static 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 | |||
| 329 | bool 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 | |||
| 339 | bool 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 | |||
| 349 | bool 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 | |||
| 366 | bool 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 | |||
| 371 | void 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 | |||
| 380 | static 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 | |||
| 420 | failed: | ||
| 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 | |||
| 427 | static 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 | |||
| 437 | bool 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 | |||
| 530 | void 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 | */ | ||
| 545 | char *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 | */ | ||
| 581 | char **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 | |||
| 630 | failed: | ||
| 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 | ||
