src/core/linux/SDL_dbus.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 04 Jan 2019 22:01:14 -0800
changeset 12503 806492103856
parent 12456 f8041c025fd3
child 12920 3505a39d5aa6
permissions -rw-r--r--
Updated copyright for 2019
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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 (!(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;
   177             va_list ap_reply;
   178             va_copy(ap_reply, ap);  /* copy the arg list so we don't compete with D-Bus for it */
   179             firstarg = va_arg(ap, int);
   180             if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) {
   181                 DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL);
   182                 if (reply) {
   183                     /* skip any input args, get to output args. */
   184                     while ((firstarg = va_arg(ap_reply, int)) != DBUS_TYPE_INVALID) {
   185                         /* we assume D-Bus already validated all this. */
   186                         { void *dumpptr = va_arg(ap_reply, void*); (void) dumpptr; }
   187                         if (firstarg == DBUS_TYPE_ARRAY) {
   188                             { const int dumpint = va_arg(ap_reply, int); (void) dumpint; }
   189                         }
   190                     }
   191                     firstarg = va_arg(ap_reply, int);
   192                     if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_get_args_valist(reply, NULL, firstarg, ap_reply)) {
   193                         retval = SDL_TRUE;
   194                     }
   195                     dbus.message_unref(reply);
   196                 }
   197             }
   198             va_end(ap_reply);
   199             dbus.message_unref(msg);
   200         }
   201     }
   202 
   203     return retval;
   204 }
   205 
   206 SDL_bool
   207 SDL_DBus_CallMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...)
   208 {
   209     SDL_bool retval;
   210     va_list ap;
   211     va_start(ap, method);
   212     retval = SDL_DBus_CallMethodInternal(conn, node, path, interface, method, ap);
   213     va_end(ap);
   214     return retval;
   215 }
   216 
   217 SDL_bool
   218 SDL_DBus_CallMethod(const char *node, const char *path, const char *interface, const char *method, ...)
   219 {
   220     SDL_bool retval;
   221     va_list ap;
   222     va_start(ap, method);
   223     retval = SDL_DBus_CallMethodInternal(dbus.session_conn, node, path, interface, method, ap);
   224     va_end(ap);
   225     return retval;
   226 }
   227 
   228 static SDL_bool
   229 SDL_DBus_CallVoidMethodInternal(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap)
   230 {
   231     SDL_bool retval = SDL_FALSE;
   232 
   233     if (conn) {
   234         DBusMessage *msg = dbus.message_new_method_call(node, path, interface, method);
   235         if (msg) {
   236             int firstarg = va_arg(ap, int);
   237             if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) {
   238                 if (dbus.connection_send(conn, msg, NULL)) {
   239                     dbus.connection_flush(conn);
   240                     retval = SDL_TRUE;
   241                 }
   242             }
   243 
   244             dbus.message_unref(msg);
   245         }
   246     }
   247 
   248     return retval;
   249 }
   250 
   251 SDL_bool
   252 SDL_DBus_CallVoidMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...)
   253 {
   254     SDL_bool retval;
   255     va_list ap;
   256     va_start(ap, method);
   257     retval = SDL_DBus_CallVoidMethodInternal(conn, node, path, interface, method, ap);
   258     va_end(ap);
   259     return retval;
   260 }
   261 
   262 SDL_bool
   263 SDL_DBus_CallVoidMethod(const char *node, const char *path, const char *interface, const char *method, ...)
   264 {
   265     SDL_bool retval;
   266     va_list ap;
   267     va_start(ap, method);
   268     retval = SDL_DBus_CallVoidMethodInternal(dbus.session_conn, node, path, interface, method, ap);
   269     va_end(ap);
   270     return retval;
   271 }
   272 
   273 SDL_bool
   274 SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *property, const int expectedtype, void *result)
   275 {
   276     SDL_bool retval = SDL_FALSE;
   277 
   278     if (conn) {
   279         DBusMessage *msg = dbus.message_new_method_call(node, path, "org.freedesktop.DBus.Properties", "Get");
   280         if (msg) {
   281             if (dbus.message_append_args(msg, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
   282                 DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL);
   283                 if (reply) {
   284                     DBusMessageIter iter, sub;
   285                     dbus.message_iter_init(reply, &iter);
   286                     if (dbus.message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT) {
   287                         dbus.message_iter_recurse(&iter, &sub);
   288                         if (dbus.message_iter_get_arg_type(&sub) == expectedtype) {
   289                             dbus.message_iter_get_basic(&sub, result);
   290                             retval = SDL_TRUE;
   291                         }
   292                     }
   293                     dbus.message_unref(reply);
   294                 }
   295             }
   296             dbus.message_unref(msg);
   297         }
   298     }
   299 
   300     return retval;
   301 }
   302 
   303 SDL_bool
   304 SDL_DBus_QueryProperty(const char *node, const char *path, const char *interface, const char *property, const int expectedtype, void *result)
   305 {
   306     return SDL_DBus_QueryPropertyOnConnection(dbus.session_conn, node, path, interface, property, expectedtype, result);
   307 }
   308 
   309 
   310 void
   311 SDL_DBus_ScreensaverTickle(void)
   312 {
   313     SDL_DBus_CallVoidMethod("org.gnome.ScreenSaver", "/org/gnome/ScreenSaver", "org.gnome.ScreenSaver", "SimulateUserActivity", DBUS_TYPE_INVALID);
   314 }
   315 
   316 SDL_bool
   317 SDL_DBus_ScreensaverInhibit(SDL_bool inhibit)
   318 {
   319     if ( (inhibit && (screensaver_cookie != 0)) || (!inhibit && (screensaver_cookie == 0)) ) {
   320         return SDL_TRUE;
   321     } else {
   322         const char *node = "org.freedesktop.ScreenSaver";
   323         const char *path = "/org/freedesktop/ScreenSaver";
   324         const char *interface = "org.freedesktop.ScreenSaver";
   325 
   326         if (inhibit) {
   327             const char *app = "My SDL application";
   328             const char *reason = "Playing a game";
   329             if (!SDL_DBus_CallMethod(node, path, interface, "Inhibit",
   330                     DBUS_TYPE_STRING, &app, DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID,
   331                     DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) {
   332                 return SDL_FALSE;
   333             }
   334             return (screensaver_cookie != 0) ? SDL_TRUE : SDL_FALSE;
   335         } else {
   336             if (!SDL_DBus_CallVoidMethod(node, path, interface, "UnInhibit", DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) {
   337                 return SDL_FALSE;
   338             }
   339             screensaver_cookie = 0;
   340         }
   341     }
   342 
   343     return SDL_TRUE;
   344 }
   345 #endif
   346 
   347 /* vi: set ts=4 sw=4 expandtab: */