src/core/linux/SDL_dbus.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 13 Feb 2018 08:07:52 -0800
changeset 11868 5ec7bc5b3d12
parent 11839 971881e55d61
child 11872 e917e911dab6
permissions -rw-r--r--
Fixed bug 3950 - Don't always call dbus_shutdown in SDL_DBus

Alexander Larsson

dbus_shutdown() is a debug feature which closes all global resources in the dbus library. Calling this should be done by the app, not a library, because if there are multiple users of dbus in the process then SDL could shut it down even though another part is using it.

For example, i had an issue with this in mGBA, which uses both Qt and SDL, both using libdbus. I had a session bus, but no system bus (this was in a flatpak sandbox), and when SDL_DBus_Init() failed to init the system bus it called dbus_shudown() and continued on. This caused issues for Qt when running due to its session bus connections having disappeared beneath it.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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 
    24 #if SDL_USE_LIBDBUS
    25 /* we never link directly to libdbus. */
    26 #include "SDL_loadso.h"
    27 static const char *dbus_library = "libdbus-1.so.3";
    28 static void *dbus_handle = NULL;
    29 static unsigned int screensaver_cookie = 0;
    30 static SDL_DBusContext dbus;
    31 
    32 static int
    33 LoadDBUSSyms(void)
    34 {
    35     #define SDL_DBUS_SYM2(x, y) \
    36         if (!(*(void**)&dbus.x = SDL_LoadFunction(dbus_handle, #y))) return -1
    37         
    38     #define SDL_DBUS_SYM(x) \
    39         SDL_DBUS_SYM2(x, dbus_##x)
    40 
    41     SDL_DBUS_SYM(bus_get_private);
    42     SDL_DBUS_SYM(bus_register);
    43     SDL_DBUS_SYM(bus_add_match);
    44     SDL_DBUS_SYM(connection_open_private);
    45     SDL_DBUS_SYM(connection_set_exit_on_disconnect);
    46     SDL_DBUS_SYM(connection_get_is_connected);
    47     SDL_DBUS_SYM(connection_add_filter);
    48     SDL_DBUS_SYM(connection_try_register_object_path);
    49     SDL_DBUS_SYM(connection_send);
    50     SDL_DBUS_SYM(connection_send_with_reply_and_block);
    51     SDL_DBUS_SYM(connection_close);
    52     SDL_DBUS_SYM(connection_unref);
    53     SDL_DBUS_SYM(connection_flush);
    54     SDL_DBUS_SYM(connection_read_write);
    55     SDL_DBUS_SYM(connection_dispatch);
    56     SDL_DBUS_SYM(message_is_signal);
    57     SDL_DBUS_SYM(message_new_method_call);
    58     SDL_DBUS_SYM(message_append_args);
    59     SDL_DBUS_SYM(message_append_args_valist);
    60     SDL_DBUS_SYM(message_get_args);
    61     SDL_DBUS_SYM(message_get_args_valist);
    62     SDL_DBUS_SYM(message_iter_init);
    63     SDL_DBUS_SYM(message_iter_next);
    64     SDL_DBUS_SYM(message_iter_get_basic);
    65     SDL_DBUS_SYM(message_iter_get_arg_type);
    66     SDL_DBUS_SYM(message_iter_recurse);
    67     SDL_DBUS_SYM(message_unref);
    68     SDL_DBUS_SYM(error_init);
    69     SDL_DBUS_SYM(error_is_set);
    70     SDL_DBUS_SYM(error_free);
    71     SDL_DBUS_SYM(get_local_machine_id);
    72     SDL_DBUS_SYM(free);
    73     SDL_DBUS_SYM(free_string_array);
    74     SDL_DBUS_SYM(shutdown);
    75 
    76     #undef SDL_DBUS_SYM
    77     #undef SDL_DBUS_SYM2
    78 
    79     return 0;
    80 }
    81 
    82 static void
    83 UnloadDBUSLibrary(void)
    84 {
    85     if (dbus_handle != NULL) {
    86         SDL_UnloadObject(dbus_handle);
    87         dbus_handle = NULL;
    88     }
    89 }
    90 
    91 static int
    92 LoadDBUSLibrary(void)
    93 {
    94     int retval = 0;
    95     if (dbus_handle == NULL) {
    96         dbus_handle = SDL_LoadObject(dbus_library);
    97         if (dbus_handle == NULL) {
    98             retval = -1;
    99             /* Don't call SDL_SetError(): SDL_LoadObject already did. */
   100         } else {
   101             retval = LoadDBUSSyms();
   102             if (retval < 0) {
   103                 UnloadDBUSLibrary();
   104             }
   105         }
   106     }
   107 
   108     return retval;
   109 }
   110 
   111 void
   112 SDL_DBus_Init(void)
   113 {
   114     if (!dbus.session_conn && LoadDBUSLibrary() != -1) {
   115         DBusError err;
   116         dbus.error_init(&err);
   117         dbus.session_conn = dbus.bus_get_private(DBUS_BUS_SESSION, &err);
   118         if (!dbus.error_is_set(&err)) {
   119             dbus.system_conn = dbus.bus_get_private(DBUS_BUS_SYSTEM, &err);
   120         }
   121         if (dbus.error_is_set(&err)) {
   122             dbus.error_free(&err);
   123             SDL_DBus_Quit();
   124             return;  /* oh well */
   125         }
   126         dbus.connection_set_exit_on_disconnect(dbus.system_conn, 0);
   127         dbus.connection_set_exit_on_disconnect(dbus.session_conn, 0);
   128     }
   129 }
   130 
   131 void
   132 SDL_DBus_Quit(void)
   133 {
   134     if (dbus.system_conn) {
   135         dbus.connection_close(dbus.system_conn);
   136         dbus.connection_unref(dbus.system_conn);
   137     }
   138     if (dbus.session_conn) {
   139         dbus.connection_close(dbus.session_conn);
   140         dbus.connection_unref(dbus.session_conn);
   141     }
   142 /* Don't do this - bug 3950
   143    dbus_shutdown() is a debug feature which closes all global resources in the dbus library. Calling this should be done by the app, not a library, because if there are multiple users of dbus in the process then SDL could shut it down even though another part is using it.
   144 */
   145 #if 0
   146     if (dbus.shutdown) {
   147         dbus.shutdown();
   148     }
   149 #endif
   150     SDL_zero(dbus);
   151     UnloadDBUSLibrary();
   152 }
   153 
   154 SDL_DBusContext *
   155 SDL_DBus_GetContext(void)
   156 {
   157     if(!dbus_handle || !dbus.session_conn){
   158         SDL_DBus_Init();
   159     }
   160     
   161     if(dbus_handle && dbus.session_conn){
   162         return &dbus;
   163     } else {
   164         return NULL;
   165     }
   166 }
   167 
   168 static SDL_bool
   169 SDL_DBus_CallMethodInternal(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap)
   170 {
   171     SDL_bool retval = SDL_FALSE;
   172 
   173     if (conn) {
   174         DBusMessage *msg = dbus.message_new_method_call(node, path, interface, method);
   175         if (msg) {
   176             int firstarg = va_arg(ap, int);
   177             if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) {
   178                 DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL);
   179                 if (reply) {
   180                     firstarg = va_arg(ap, int);
   181                     if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_get_args_valist(reply, NULL, firstarg, ap)) {
   182                         retval = SDL_TRUE;
   183                     }
   184                     dbus.message_unref(reply);
   185                 }
   186             }
   187             dbus.message_unref(msg);
   188         }
   189     }
   190 
   191     return retval;
   192 }
   193 
   194 SDL_bool
   195 SDL_DBus_CallMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...)
   196 {
   197     SDL_bool retval;
   198     va_list ap;
   199     va_start(ap, method);
   200     retval = SDL_DBus_CallMethodInternal(conn, node, path, interface, method, ap);
   201     va_end(ap);
   202     return retval;
   203 }
   204 
   205 SDL_bool
   206 SDL_DBus_CallMethod(const char *node, const char *path, const char *interface, const char *method, ...)
   207 {
   208     SDL_bool retval;
   209     va_list ap;
   210     va_start(ap, method);
   211     retval = SDL_DBus_CallMethodInternal(dbus.session_conn, node, path, interface, method, ap);
   212     va_end(ap);
   213     return retval;
   214 }
   215 
   216 static SDL_bool
   217 SDL_DBus_CallVoidMethodInternal(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap)
   218 {
   219     SDL_bool retval = SDL_FALSE;
   220 
   221     if (conn) {
   222         DBusMessage *msg = dbus.message_new_method_call(node, path, interface, method);
   223         if (msg) {
   224             int firstarg = va_arg(ap, int);
   225             if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) {
   226                 if (dbus.connection_send(conn, msg, NULL)) {
   227                     dbus.connection_flush(conn);
   228                     retval = SDL_TRUE;
   229                 }
   230             }
   231 
   232             dbus.message_unref(msg);
   233         }
   234     }
   235 
   236     return retval;
   237 }
   238 
   239 SDL_bool
   240 SDL_DBus_CallVoidMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...)
   241 {
   242     SDL_bool retval;
   243     va_list ap;
   244     va_start(ap, method);
   245     retval = SDL_DBus_CallVoidMethodInternal(conn, node, path, interface, method, ap);
   246     va_end(ap);
   247     return retval;
   248 }
   249 
   250 SDL_bool
   251 SDL_DBus_CallVoidMethod(const char *node, const char *path, const char *interface, const char *method, ...)
   252 {
   253     SDL_bool retval;
   254     va_list ap;
   255     va_start(ap, method);
   256     retval = SDL_DBus_CallVoidMethodInternal(dbus.session_conn, node, path, interface, method, ap);
   257     va_end(ap);
   258     return retval;
   259 }
   260 
   261 SDL_bool
   262 SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *property, const int expectedtype, void *result)
   263 {
   264     SDL_bool retval = SDL_FALSE;
   265 
   266     if (conn) {
   267         DBusMessage *msg = dbus.message_new_method_call(node, path, "org.freedesktop.DBus.Properties", "Get");
   268         if (msg) {
   269             if (dbus.message_append_args(msg, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
   270                 DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL);
   271                 if (reply) {
   272                     DBusMessageIter iter, sub;
   273                     dbus.message_iter_init(reply, &iter);
   274                     if (dbus.message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT) {
   275                         dbus.message_iter_recurse(&iter, &sub);
   276                         if (dbus.message_iter_get_arg_type(&sub) == expectedtype) {
   277                             dbus.message_iter_get_basic(&sub, result);
   278                             retval = SDL_TRUE;
   279                         }
   280                     }
   281                     dbus.message_unref(reply);
   282                 }
   283             }
   284             dbus.message_unref(msg);
   285         }
   286     }
   287 
   288     return retval;
   289 }
   290 
   291 SDL_bool
   292 SDL_DBus_QueryProperty(const char *node, const char *path, const char *interface, const char *property, const int expectedtype, void *result)
   293 {
   294     return SDL_DBus_QueryPropertyOnConnection(dbus.session_conn, node, path, interface, property, expectedtype, result);
   295 }
   296 
   297 
   298 void
   299 SDL_DBus_ScreensaverTickle(void)
   300 {
   301     SDL_DBus_CallVoidMethod("org.gnome.ScreenSaver", "/org/gnome/ScreenSaver", "org.gnome.ScreenSaver", "SimulateUserActivity", DBUS_TYPE_INVALID);
   302 }
   303 
   304 SDL_bool
   305 SDL_DBus_ScreensaverInhibit(SDL_bool inhibit)
   306 {
   307     if ( (inhibit && (screensaver_cookie != 0)) || (!inhibit && (screensaver_cookie == 0)) ) {
   308         return SDL_TRUE;
   309     } else {
   310         const char *node = "org.freedesktop.ScreenSaver";
   311         const char *path = "/org/freedesktop/ScreenSaver";
   312         const char *interface = "org.freedesktop.ScreenSaver";
   313 
   314         if (inhibit) {
   315             const char *app = "My SDL application";
   316             const char *reason = "Playing a game";
   317             if (!SDL_DBus_CallMethod(node, path, interface, "Inhibit",
   318                     DBUS_TYPE_STRING, &app, DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID,
   319                     DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) {
   320                 return SDL_FALSE;
   321             }
   322             return (screensaver_cookie != 0) ? SDL_TRUE : SDL_FALSE;
   323         } else {
   324             if (!SDL_DBus_CallVoidMethod(node, path, interface, "UnInhibit", DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) {
   325                 return SDL_FALSE;
   326             }
   327             screensaver_cookie = 0;
   328         }
   329     }
   330 
   331     return SDL_TRUE;
   332 }
   333 #endif
   334 
   335 /* vi: set ts=4 sw=4 expandtab: */