X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/142e26a72e9b8bbbec23c6bf7234e9f2544b5f89..ad3e6f4474d2737be89456332319e8efbdb382c4:/src/dbusbind.c diff --git a/src/dbusbind.c b/src/dbusbind.c index 122d39158b..f710741b59 100644 --- a/src/dbusbind.c +++ b/src/dbusbind.c @@ -1,5 +1,5 @@ /* Elisp bindings for D-Bus. - Copyright (C) 2007, 2008 Free Software Foundation, Inc. + Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -22,6 +22,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include "lisp.h" #include "frame.h" #include "termhooks.h" @@ -29,9 +30,12 @@ along with GNU Emacs. If not, see . */ /* Subroutines. */ +Lisp_Object Qdbus_init_bus; Lisp_Object Qdbus_get_unique_name; Lisp_Object Qdbus_call_method; +Lisp_Object Qdbus_call_method_asynchronously; Lisp_Object Qdbus_method_return_internal; +Lisp_Object Qdbus_method_error_internal; Lisp_Object Qdbus_send_signal; Lisp_Object Qdbus_register_signal; Lisp_Object Qdbus_register_method; @@ -56,25 +60,54 @@ Lisp_Object QCdbus_type_array, QCdbus_type_variant; Lisp_Object QCdbus_type_struct, QCdbus_type_dict_entry; /* Hash table which keeps function definitions. */ -Lisp_Object Vdbus_registered_functions_table; +Lisp_Object Vdbus_registered_objects_table; /* Whether to debug D-Bus. */ Lisp_Object Vdbus_debug; +/* Whether we are reading a D-Bus event. */ +int xd_in_read_queued_messages = 0; + /* We use "xd_" and "XD_" as prefix for all internal symbols, because we don't want to poison other namespaces with "dbus_". */ +/* Raise a signal. If we are reading events, we cannot signal; we + throw to xd_read_queued_messages then. */ +#define XD_SIGNAL1(arg) \ + do { \ + if (xd_in_read_queued_messages) \ + Fthrow (Qdbus_error, Qnil); \ + else \ + xsignal1 (Qdbus_error, arg); \ + } while (0) + +#define XD_SIGNAL2(arg1, arg2) \ + do { \ + if (xd_in_read_queued_messages) \ + Fthrow (Qdbus_error, Qnil); \ + else \ + xsignal2 (Qdbus_error, arg1, arg2); \ + } while (0) + +#define XD_SIGNAL3(arg1, arg2, arg3) \ + do { \ + if (xd_in_read_queued_messages) \ + Fthrow (Qdbus_error, Qnil); \ + else \ + xsignal3 (Qdbus_error, arg1, arg2, arg3); \ + } while (0) + /* Raise a Lisp error from a D-Bus ERROR. */ #define XD_ERROR(error) \ do { \ char s[1024]; \ - strcpy (s, error.message); \ + strncpy (s, error.message, 1023); \ dbus_error_free (&error); \ /* Remove the trailing newline. */ \ if (strchr (s, '\n') != NULL) \ s[strlen (s) - 1] = '\0'; \ - xsignal1 (Qdbus_error, build_string (s)); \ + XD_SIGNAL1 (build_string (s)); \ } while (0) /* Macros for debugging. In order to enable them, build with @@ -83,7 +116,7 @@ Lisp_Object Vdbus_debug; #define XD_DEBUG_MESSAGE(...) \ do { \ char s[1024]; \ - sprintf (s, __VA_ARGS__); \ + snprintf (s, 1023, __VA_ARGS__); \ printf ("%s: %s\n", __func__, s); \ message ("%s: %s", __func__, s); \ } while (0) @@ -92,7 +125,7 @@ Lisp_Object Vdbus_debug; if (!valid_lisp_object_p (object)) \ { \ XD_DEBUG_MESSAGE ("%d Assertion failure", __LINE__); \ - xsignal1 (Qdbus_error, build_string ("Assertion failure")); \ + XD_SIGNAL1 (build_string ("Assertion failure")); \ } \ } while (0) @@ -102,7 +135,7 @@ Lisp_Object Vdbus_debug; if (!NILP (Vdbus_debug)) \ { \ char s[1024]; \ - sprintf (s, __VA_ARGS__); \ + snprintf (s, 1023, __VA_ARGS__); \ message ("%s: %s", __func__, s); \ } \ } while (0) @@ -124,30 +157,38 @@ Lisp_Object Vdbus_debug; || (type == DBUS_TYPE_OBJECT_PATH) \ || (type == DBUS_TYPE_SIGNATURE)) +/* This was a macro. On Solaris 2.11 it was said to compile for + hours, when optimzation is enabled. So we have transferred it into + a function. */ /* Determine the DBusType of a given Lisp symbol. OBJECT must be one of the predefined D-Bus type symbols. */ -#define XD_SYMBOL_TO_DBUS_TYPE(object) \ - ((EQ (object, QCdbus_type_byte)) ? DBUS_TYPE_BYTE \ - : (EQ (object, QCdbus_type_boolean)) ? DBUS_TYPE_BOOLEAN \ - : (EQ (object, QCdbus_type_int16)) ? DBUS_TYPE_INT16 \ - : (EQ (object, QCdbus_type_uint16)) ? DBUS_TYPE_UINT16 \ - : (EQ (object, QCdbus_type_int32)) ? DBUS_TYPE_INT32 \ - : (EQ (object, QCdbus_type_uint32)) ? DBUS_TYPE_UINT32 \ - : (EQ (object, QCdbus_type_int64)) ? DBUS_TYPE_INT64 \ - : (EQ (object, QCdbus_type_uint64)) ? DBUS_TYPE_UINT64 \ - : (EQ (object, QCdbus_type_double)) ? DBUS_TYPE_DOUBLE \ - : (EQ (object, QCdbus_type_string)) ? DBUS_TYPE_STRING \ - : (EQ (object, QCdbus_type_object_path)) ? DBUS_TYPE_OBJECT_PATH \ - : (EQ (object, QCdbus_type_signature)) ? DBUS_TYPE_SIGNATURE \ - : (EQ (object, QCdbus_type_array)) ? DBUS_TYPE_ARRAY \ - : (EQ (object, QCdbus_type_variant)) ? DBUS_TYPE_VARIANT \ - : (EQ (object, QCdbus_type_struct)) ? DBUS_TYPE_STRUCT \ - : (EQ (object, QCdbus_type_dict_entry)) ? DBUS_TYPE_DICT_ENTRY \ - : DBUS_TYPE_INVALID) +static int +xd_symbol_to_dbus_type (object) + Lisp_Object object; +{ + return + ((EQ (object, QCdbus_type_byte)) ? DBUS_TYPE_BYTE + : (EQ (object, QCdbus_type_boolean)) ? DBUS_TYPE_BOOLEAN + : (EQ (object, QCdbus_type_int16)) ? DBUS_TYPE_INT16 + : (EQ (object, QCdbus_type_uint16)) ? DBUS_TYPE_UINT16 + : (EQ (object, QCdbus_type_int32)) ? DBUS_TYPE_INT32 + : (EQ (object, QCdbus_type_uint32)) ? DBUS_TYPE_UINT32 + : (EQ (object, QCdbus_type_int64)) ? DBUS_TYPE_INT64 + : (EQ (object, QCdbus_type_uint64)) ? DBUS_TYPE_UINT64 + : (EQ (object, QCdbus_type_double)) ? DBUS_TYPE_DOUBLE + : (EQ (object, QCdbus_type_string)) ? DBUS_TYPE_STRING + : (EQ (object, QCdbus_type_object_path)) ? DBUS_TYPE_OBJECT_PATH + : (EQ (object, QCdbus_type_signature)) ? DBUS_TYPE_SIGNATURE + : (EQ (object, QCdbus_type_array)) ? DBUS_TYPE_ARRAY + : (EQ (object, QCdbus_type_variant)) ? DBUS_TYPE_VARIANT + : (EQ (object, QCdbus_type_struct)) ? DBUS_TYPE_STRUCT + : (EQ (object, QCdbus_type_dict_entry)) ? DBUS_TYPE_DICT_ENTRY + : DBUS_TYPE_INVALID); +} /* Check whether a Lisp symbol is a predefined D-Bus type symbol. */ #define XD_DBUS_TYPE_P(object) \ - (SYMBOLP (object) && ((XD_SYMBOL_TO_DBUS_TYPE (object) != DBUS_TYPE_INVALID))) + (SYMBOLP (object) && ((xd_symbol_to_dbus_type (object) != DBUS_TYPE_INVALID))) /* Determine the DBusType of a given Lisp OBJECT. It is used to convert Lisp objects, being arguments of `dbus-call-method' or @@ -159,10 +200,13 @@ Lisp_Object Vdbus_debug; : (INTEGERP (object)) ? DBUS_TYPE_INT32 \ : (FLOATP (object)) ? DBUS_TYPE_DOUBLE \ : (STRINGP (object)) ? DBUS_TYPE_STRING \ - : (XD_DBUS_TYPE_P (object)) ? XD_SYMBOL_TO_DBUS_TYPE (object) \ - : (CONSP (object)) ? ((XD_DBUS_TYPE_P (CAR_SAFE (object))) \ - ? XD_SYMBOL_TO_DBUS_TYPE (CAR_SAFE (object)) \ - : DBUS_TYPE_ARRAY) \ + : (XD_DBUS_TYPE_P (object)) ? xd_symbol_to_dbus_type (object) \ + : (CONSP (object)) \ + ? ((XD_DBUS_TYPE_P (CAR_SAFE (object))) \ + ? ((XD_BASIC_DBUS_TYPE (xd_symbol_to_dbus_type (CAR_SAFE (object)))) \ + ? DBUS_TYPE_ARRAY \ + : xd_symbol_to_dbus_type (CAR_SAFE (object))) \ + : DBUS_TYPE_ARRAY) \ : DBUS_TYPE_INVALID) /* Return a list pointer which does not have a Lisp symbol as car. */ @@ -176,8 +220,8 @@ Lisp_Object Vdbus_debug; a type symbol. PARENT_TYPE is the DBusType of a container this signature is embedded, or DBUS_TYPE_INVALID. It is needed for the check that DBUS_TYPE_DICT_ENTRY occurs only as array element. */ -void -xd_signature(signature, dtype, parent_type, object) +static void +xd_signature (signature, dtype, parent_type, object) char *signature; unsigned int dtype, parent_type; Lisp_Object object; @@ -296,7 +340,7 @@ xd_signature(signature, dtype, parent_type, object) strcat (signature, x); elt = CDR_SAFE (XD_NEXT_VALUE (elt)); } - sprintf (signature, "%s%c", signature, DBUS_STRUCT_END_CHAR); + strcat (signature, DBUS_STRUCT_END_CHAR_AS_STRING); break; case DBUS_TYPE_DICT_ENTRY: @@ -333,7 +377,7 @@ xd_signature(signature, dtype, parent_type, object) CAR_SAFE (CDR_SAFE (XD_NEXT_VALUE (elt)))); /* Closing signature. */ - sprintf (signature, "%s%c", signature, DBUS_DICT_ENTRY_END_CHAR); + strcat (signature, DBUS_DICT_ENTRY_END_CHAR_AS_STRING); break; default: @@ -348,7 +392,7 @@ xd_signature(signature, dtype, parent_type, object) objects, being arguments of `dbus-call-method' or `dbus-send-signal', into corresponding C values appended as arguments to a D-Bus message. */ -void +static void xd_append_arg (dtype, object, iter) unsigned int dtype; Lisp_Object object; @@ -361,12 +405,12 @@ xd_append_arg (dtype, object, iter) switch (dtype) { case DBUS_TYPE_BYTE: + CHECK_NUMBER (object); { unsigned char val = XUINT (object) & 0xFF; XD_DEBUG_MESSAGE ("%c %d", dtype, val); if (!dbus_message_iter_append_basic (iter, dtype, &val)) - xsignal2 (Qdbus_error, - build_string ("Unable to append argument"), object); + XD_SIGNAL2 (build_string ("Unable to append argument"), object); return; } @@ -375,88 +419,93 @@ xd_append_arg (dtype, object, iter) dbus_bool_t val = (NILP (object)) ? FALSE : TRUE; XD_DEBUG_MESSAGE ("%c %s", dtype, (val == FALSE) ? "false" : "true"); if (!dbus_message_iter_append_basic (iter, dtype, &val)) - xsignal2 (Qdbus_error, - build_string ("Unable to append argument"), object); + XD_SIGNAL2 (build_string ("Unable to append argument"), object); return; } case DBUS_TYPE_INT16: + CHECK_NUMBER (object); { dbus_int16_t val = XINT (object); XD_DEBUG_MESSAGE ("%c %d", dtype, (int) val); if (!dbus_message_iter_append_basic (iter, dtype, &val)) - xsignal2 (Qdbus_error, - build_string ("Unable to append argument"), object); + XD_SIGNAL2 (build_string ("Unable to append argument"), object); return; } case DBUS_TYPE_UINT16: + CHECK_NUMBER (object); { dbus_uint16_t val = XUINT (object); XD_DEBUG_MESSAGE ("%c %u", dtype, (unsigned int) val); if (!dbus_message_iter_append_basic (iter, dtype, &val)) - xsignal2 (Qdbus_error, - build_string ("Unable to append argument"), object); + XD_SIGNAL2 (build_string ("Unable to append argument"), object); return; } case DBUS_TYPE_INT32: + CHECK_NUMBER (object); { dbus_int32_t val = XINT (object); XD_DEBUG_MESSAGE ("%c %d", dtype, val); if (!dbus_message_iter_append_basic (iter, dtype, &val)) - xsignal2 (Qdbus_error, - build_string ("Unable to append argument"), object); + XD_SIGNAL2 (build_string ("Unable to append argument"), object); return; } case DBUS_TYPE_UINT32: + CHECK_NUMBER (object); { dbus_uint32_t val = XUINT (object); XD_DEBUG_MESSAGE ("%c %u", dtype, val); if (!dbus_message_iter_append_basic (iter, dtype, &val)) - xsignal2 (Qdbus_error, - build_string ("Unable to append argument"), object); + XD_SIGNAL2 (build_string ("Unable to append argument"), object); return; } case DBUS_TYPE_INT64: + CHECK_NUMBER (object); { dbus_int64_t val = XINT (object); XD_DEBUG_MESSAGE ("%c %d", dtype, (int) val); if (!dbus_message_iter_append_basic (iter, dtype, &val)) - xsignal2 (Qdbus_error, - build_string ("Unable to append argument"), object); + XD_SIGNAL2 (build_string ("Unable to append argument"), object); return; } case DBUS_TYPE_UINT64: + CHECK_NUMBER (object); { dbus_uint64_t val = XUINT (object); XD_DEBUG_MESSAGE ("%c %u", dtype, (unsigned int) val); if (!dbus_message_iter_append_basic (iter, dtype, &val)) - xsignal2 (Qdbus_error, - build_string ("Unable to append argument"), object); + XD_SIGNAL2 (build_string ("Unable to append argument"), object); return; } case DBUS_TYPE_DOUBLE: - XD_DEBUG_MESSAGE ("%c %f", dtype, XFLOAT_DATA (object)); - if (!dbus_message_iter_append_basic (iter, dtype, - &XFLOAT_DATA (object))) - xsignal2 (Qdbus_error, - build_string ("Unable to append argument"), object); - return; + CHECK_FLOAT (object); + { + double val = XFLOAT_DATA (object); + XD_DEBUG_MESSAGE ("%c %f", dtype, val); + if (!dbus_message_iter_append_basic (iter, dtype, &val)) + XD_SIGNAL2 (build_string ("Unable to append argument"), object); + return; + } case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: case DBUS_TYPE_SIGNATURE: + CHECK_STRING (object); { + /* We need to send a valid UTF-8 string. We could encode `object' + but by not encoding it, we guarantee it's valid utf-8, even if + it contains eight-bit-bytes. Of course, you can still send + manually-crafted junk by passing a unibyte string. */ char *val = SDATA (object); XD_DEBUG_MESSAGE ("%c %s", dtype, val); if (!dbus_message_iter_append_basic (iter, dtype, &val)) - xsignal2 (Qdbus_error, - build_string ("Unable to append argument"), object); + XD_SIGNAL2 (build_string ("Unable to append argument"), object); return; } } @@ -504,9 +553,8 @@ xd_append_arg (dtype, object, iter) SDATA (format2 ("%s", object, Qnil))); if (!dbus_message_iter_open_container (iter, dtype, signature, &subiter)) - xsignal3 (Qdbus_error, - build_string ("Cannot open container"), - make_number (dtype), build_string (signature)); + XD_SIGNAL3 (build_string ("Cannot open container"), + make_number (dtype), build_string (signature)); break; case DBUS_TYPE_VARIANT: @@ -518,9 +566,8 @@ xd_append_arg (dtype, object, iter) SDATA (format2 ("%s", object, Qnil))); if (!dbus_message_iter_open_container (iter, dtype, signature, &subiter)) - xsignal3 (Qdbus_error, - build_string ("Cannot open container"), - make_number (dtype), build_string (signature)); + XD_SIGNAL3 (build_string ("Cannot open container"), + make_number (dtype), build_string (signature)); break; case DBUS_TYPE_STRUCT: @@ -529,9 +576,8 @@ xd_append_arg (dtype, object, iter) XD_DEBUG_MESSAGE ("%c %s", dtype, SDATA (format2 ("%s", object, Qnil))); if (!dbus_message_iter_open_container (iter, dtype, NULL, &subiter)) - xsignal2 (Qdbus_error, - build_string ("Cannot open container"), - make_number (dtype)); + XD_SIGNAL2 (build_string ("Cannot open container"), + make_number (dtype)); break; } @@ -548,9 +594,8 @@ xd_append_arg (dtype, object, iter) /* Close the subiteration. */ if (!dbus_message_iter_close_container (iter, &subiter)) - xsignal2 (Qdbus_error, - build_string ("Cannot close container"), - make_number (dtype)); + XD_SIGNAL2 (build_string ("Cannot close container"), + make_number (dtype)); } } @@ -558,7 +603,7 @@ xd_append_arg (dtype, object, iter) a converted Lisp object. The type DTYPE of the argument of the D-Bus message must be a valid DBusType. Compound D-Bus types result always in a Lisp list. */ -Lisp_Object +static Lisp_Object xd_retrieve_arg (dtype, iter) unsigned int dtype; DBusMessageIter *iter; @@ -584,6 +629,13 @@ xd_retrieve_arg (dtype, iter) } case DBUS_TYPE_INT16: + { + dbus_int16_t val; + dbus_message_iter_get_basic (iter, &val); + XD_DEBUG_MESSAGE ("%c %d", dtype, val); + return make_number (val); + } + case DBUS_TYPE_UINT16: { dbus_uint16_t val; @@ -593,19 +645,29 @@ xd_retrieve_arg (dtype, iter) } case DBUS_TYPE_INT32: + { + dbus_int32_t val; + dbus_message_iter_get_basic (iter, &val); + XD_DEBUG_MESSAGE ("%c %d", dtype, val); + return make_fixnum_or_float (val); + } + case DBUS_TYPE_UINT32: { - /* Assignment to EMACS_INT stops GCC whining about limited - range of data type. */ dbus_uint32_t val; - EMACS_INT val1; dbus_message_iter_get_basic (iter, &val); XD_DEBUG_MESSAGE ("%c %d", dtype, val); - val1 = val; - return make_fixnum_or_float (val1); + return make_fixnum_or_float (val); } case DBUS_TYPE_INT64: + { + dbus_int64_t val; + dbus_message_iter_get_basic (iter, &val); + XD_DEBUG_MESSAGE ("%c %d", dtype, (int) val); + return make_fixnum_or_float (val); + } + case DBUS_TYPE_UINT64: { dbus_uint64_t val; @@ -639,10 +701,10 @@ xd_retrieve_arg (dtype, iter) { Lisp_Object result; struct gcpro gcpro1; - result = Qnil; - GCPRO1 (result); DBusMessageIter subiter; int subtype; + result = Qnil; + GCPRO1 (result); dbus_message_iter_recurse (iter, &subiter); while ((subtype = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID) @@ -662,7 +724,7 @@ xd_retrieve_arg (dtype, iter) /* Initialize D-Bus connection. BUS is a Lisp symbol, either :system or :session. It tells which D-Bus to be initialized. */ -DBusConnection * +static DBusConnection * xd_initialize (bus) Lisp_Object bus; { @@ -671,8 +733,13 @@ xd_initialize (bus) /* Parameter check. */ CHECK_SYMBOL (bus); - if (!((EQ (bus, QCdbus_system_bus)) || (EQ (bus, QCdbus_session_bus)))) - xsignal2 (Qdbus_error, build_string ("Wrong bus name"), bus); + if (!(EQ (bus, QCdbus_system_bus) || EQ (bus, QCdbus_session_bus))) + XD_SIGNAL2 (build_string ("Wrong bus name"), bus); + + /* We do not want to have an autolaunch for the session bus. */ + if (EQ (bus, QCdbus_session_bus) + && getenv ("DBUS_SESSION_BUS_ADDRESS") == NULL) + XD_SIGNAL2 (build_string ("No connection to bus"), bus); /* Open a connection to the bus. */ dbus_error_init (&derror); @@ -686,12 +753,111 @@ xd_initialize (bus) XD_ERROR (derror); if (connection == NULL) - xsignal2 (Qdbus_error, build_string ("No connection"), bus); + XD_SIGNAL2 (build_string ("No connection to bus"), bus); + + /* Cleanup. */ + dbus_error_free (&derror); /* Return the result. */ return connection; } + +/* Add connection file descriptor to input_wait_mask, in order to + let select() detect, whether a new message has been arrived. */ +dbus_bool_t +xd_add_watch (watch, data) + DBusWatch *watch; + void *data; +{ + /* We check only for incoming data. */ + if (dbus_watch_get_flags (watch) & DBUS_WATCH_READABLE) + { +#if HAVE_DBUS_WATCH_GET_UNIX_FD + /* TODO: Reverse these on Win32, which prefers the opposite. */ + int fd = dbus_watch_get_unix_fd(watch); + if (fd == -1) + fd = dbus_watch_get_socket(watch); +#else + int fd = dbus_watch_get_fd(watch); +#endif + XD_DEBUG_MESSAGE ("fd %d", fd); + + if (fd == -1) + return FALSE; + + /* Add the file descriptor to input_wait_mask. */ + add_keyboard_wait_descriptor (fd); + } + + /* Return. */ + return TRUE; +} + +/* Remove connection file descriptor from input_wait_mask. DATA is + the used bus, either QCdbus_system_bus or QCdbus_session_bus. */ +void +xd_remove_watch (watch, data) + DBusWatch *watch; + void *data; +{ + /* We check only for incoming data. */ + if (dbus_watch_get_flags (watch) & DBUS_WATCH_READABLE) + { +#if HAVE_DBUS_WATCH_GET_UNIX_FD + /* TODO: Reverse these on Win32, which prefers the opposite. */ + int fd = dbus_watch_get_unix_fd(watch); + if (fd == -1) + fd = dbus_watch_get_socket(watch); +#else + int fd = dbus_watch_get_fd(watch); +#endif + XD_DEBUG_MESSAGE ("fd %d", fd); + + if (fd == -1) + return; + + /* Unset session environment. */ + if ((data != NULL) && (data == (void*) XHASH (QCdbus_session_bus))) + { + XD_DEBUG_MESSAGE ("unsetenv DBUS_SESSION_BUS_ADDRESS"); + unsetenv ("DBUS_SESSION_BUS_ADDRESS"); + } + + /* Remove the file descriptor from input_wait_mask. */ + delete_keyboard_wait_descriptor (fd); + } + + /* Return. */ + return; +} + +DEFUN ("dbus-init-bus", Fdbus_init_bus, Sdbus_init_bus, 1, 1, 0, + doc: /* Initialize connection to D-Bus BUS. +This is an internal function, it shall not be used outside dbus.el. */) + (bus) + Lisp_Object bus; +{ + DBusConnection *connection; + + /* Check parameters. */ + CHECK_SYMBOL (bus); + + /* Open a connection to the bus. */ + connection = xd_initialize (bus); + + /* Add the watch functions. We pass also the bus as data, in order + to distinguish between the busses in xd_remove_watch. */ + if (!dbus_connection_set_watch_functions (connection, + xd_add_watch, + xd_remove_watch, + NULL, (void*) XHASH (bus), NULL)) + XD_SIGNAL1 (build_string ("Cannot add watch functions")); + + /* Return. */ + return Qnil; +} + DEFUN ("dbus-get-unique-name", Fdbus_get_unique_name, Sdbus_get_unique_name, 1, 1, 0, doc: /* Return the unique name of Emacs registered at D-Bus BUS. */) @@ -710,7 +876,7 @@ DEFUN ("dbus-get-unique-name", Fdbus_get_unique_name, Sdbus_get_unique_name, /* Request the name. */ name = dbus_bus_get_unique_name (connection); if (name == NULL) - xsignal1 (Qdbus_error, build_string ("No unique name available")); + XD_SIGNAL1 (build_string ("No unique name available")); /* Return. */ return build_string (name); @@ -726,8 +892,8 @@ object path SERVICE is registered at. INTERFACE is an interface offered by SERVICE. It must provide METHOD. If the parameter `:timeout' is given, the following integer TIMEOUT -specifies the maximun number of milliseconds the method call must -return. The default value is 25.000. If the method call doesn't +specifies the maximum number of milliseconds the method call must +return. The default value is 25,000. If the method call doesn't return in time, a D-Bus error is raised. All other arguments ARGS are passed to METHOD as arguments. They are @@ -783,9 +949,7 @@ object is returned instead of a list containing this single Lisp object. => "i686" -usage: (dbus-call-method - BUS SERVICE PATH INTERFACE METHOD - &optional :timeout TIMEOUT &rest ARGS) */) +usage: (dbus-call-method BUS SERVICE PATH INTERFACE METHOD &optional :timeout TIMEOUT &rest ARGS) */) (nargs, args) int nargs; register Lisp_Object *args; @@ -833,7 +997,7 @@ usage: (dbus-call-method SDATA (method)); UNGCPRO; if (dmessage == NULL) - xsignal1 (Qdbus_error, build_string ("Unable to create a new message")); + XD_SIGNAL1 (build_string ("Unable to create a new message")); /* Check for timeout parameter. */ if ((i+2 <= nargs) && (EQ ((args[i]), QCdbus_timeout))) @@ -884,7 +1048,7 @@ usage: (dbus-call-method XD_ERROR (derror); if (reply == NULL) - xsignal1 (Qdbus_error, build_string ("No reply")); + XD_SIGNAL1 (build_string ("No reply")); XD_DEBUG_MESSAGE ("Message sent"); @@ -909,6 +1073,7 @@ usage: (dbus-call-method } /* Cleanup. */ + dbus_error_free (&derror); dbus_message_unref (dmessage); dbus_message_unref (reply); @@ -920,6 +1085,178 @@ usage: (dbus-call-method RETURN_UNGCPRO (Fnreverse (result)); } +DEFUN ("dbus-call-method-asynchronously", Fdbus_call_method_asynchronously, + Sdbus_call_method_asynchronously, 6, MANY, 0, + doc: /* Call METHOD on the D-Bus BUS asynchronously. + +BUS is either the symbol `:system' or the symbol `:session'. + +SERVICE is the D-Bus service name to be used. PATH is the D-Bus +object path SERVICE is registered at. INTERFACE is an interface +offered by SERVICE. It must provide METHOD. + +HANDLER is a Lisp function, which is called when the corresponding +return message has arrived. If HANDLER is nil, no return message will +be expected. + +If the parameter `:timeout' is given, the following integer TIMEOUT +specifies the maximum number of milliseconds the method call must +return. The default value is 25,000. If the method call doesn't +return in time, a D-Bus error is raised. + +All other arguments ARGS are passed to METHOD as arguments. They are +converted into D-Bus types via the following rules: + + t and nil => DBUS_TYPE_BOOLEAN + number => DBUS_TYPE_UINT32 + integer => DBUS_TYPE_INT32 + float => DBUS_TYPE_DOUBLE + string => DBUS_TYPE_STRING + list => DBUS_TYPE_ARRAY + +All arguments can be preceded by a type symbol. For details about +type symbols, see Info node `(dbus)Type Conversion'. + +Unless HANDLER is nil, the function returns a key into the hash table +`dbus-registered-objects-table'. The corresponding entry in the hash +table is removed, when the return message has been arrived, and +HANDLER is called. + +Example: + +\(dbus-call-method-asynchronously + :system "org.freedesktop.Hal" "/org/freedesktop/Hal/devices/computer" + "org.freedesktop.Hal.Device" "GetPropertyString" 'message + "system.kernel.machine") + + => (:system 2) + + -| i686 + +usage: (dbus-call-method-asynchronously BUS SERVICE PATH INTERFACE METHOD HANDLER &optional :timeout TIMEOUT &rest ARGS) */) + (nargs, args) + int nargs; + register Lisp_Object *args; +{ + Lisp_Object bus, service, path, interface, method, handler; + Lisp_Object result; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5, gcpro6; + DBusConnection *connection; + DBusMessage *dmessage; + DBusMessageIter iter; + unsigned int dtype; + int timeout = -1; + int i = 6; + char signature[DBUS_MAXIMUM_SIGNATURE_LENGTH]; + + /* Check parameters. */ + bus = args[0]; + service = args[1]; + path = args[2]; + interface = args[3]; + method = args[4]; + handler = args[5]; + + CHECK_SYMBOL (bus); + CHECK_STRING (service); + CHECK_STRING (path); + CHECK_STRING (interface); + CHECK_STRING (method); + if (!NILP (handler) && !FUNCTIONP (handler)) + wrong_type_argument (intern ("functionp"), handler); + GCPRO6 (bus, service, path, interface, method, handler); + + XD_DEBUG_MESSAGE ("%s %s %s %s", + SDATA (service), + SDATA (path), + SDATA (interface), + SDATA (method)); + + /* Open a connection to the bus. */ + connection = xd_initialize (bus); + + /* Create the message. */ + dmessage = dbus_message_new_method_call (SDATA (service), + SDATA (path), + SDATA (interface), + SDATA (method)); + if (dmessage == NULL) + XD_SIGNAL1 (build_string ("Unable to create a new message")); + + /* Check for timeout parameter. */ + if ((i+2 <= nargs) && (EQ ((args[i]), QCdbus_timeout))) + { + CHECK_NATNUM (args[i+1]); + timeout = XUINT (args[i+1]); + i = i+2; + } + + /* Initialize parameter list of message. */ + dbus_message_iter_init_append (dmessage, &iter); + + /* Append parameters to the message. */ + for (; i < nargs; ++i) + { + dtype = XD_OBJECT_TO_DBUS_TYPE (args[i]); + if (XD_DBUS_TYPE_P (args[i])) + { + XD_DEBUG_VALID_LISP_OBJECT_P (args[i]); + XD_DEBUG_VALID_LISP_OBJECT_P (args[i+1]); + XD_DEBUG_MESSAGE ("Parameter%d %s %s", i-4, + SDATA (format2 ("%s", args[i], Qnil)), + SDATA (format2 ("%s", args[i+1], Qnil))); + ++i; + } + else + { + XD_DEBUG_VALID_LISP_OBJECT_P (args[i]); + XD_DEBUG_MESSAGE ("Parameter%d %s", i-4, + SDATA (format2 ("%s", args[i], Qnil))); + } + + /* Check for valid signature. We use DBUS_TYPE_INVALID as + indication that there is no parent type. */ + xd_signature (signature, dtype, DBUS_TYPE_INVALID, args[i]); + + xd_append_arg (dtype, args[i], &iter); + } + + if (!NILP (handler)) + { + /* Send the message. The message is just added to the outgoing + message queue. */ + if (!dbus_connection_send_with_reply (connection, dmessage, + NULL, timeout)) + XD_SIGNAL1 (build_string ("Cannot send message")); + + /* The result is the key in Vdbus_registered_objects_table. */ + result = (list2 (bus, make_number (dbus_message_get_serial (dmessage)))); + + /* Create a hash table entry. */ + Fputhash (result, handler, Vdbus_registered_objects_table); + } + else + { + /* Send the message. The message is just added to the outgoing + message queue. */ + if (!dbus_connection_send (connection, dmessage, NULL)) + XD_SIGNAL1 (build_string ("Cannot send message")); + + result = Qnil; + } + + /* Flush connection to ensure the message is handled. */ + dbus_connection_flush (connection); + + XD_DEBUG_MESSAGE ("Message sent"); + + /* Cleanup. */ + dbus_message_unref (dmessage); + + /* Return the result. */ + RETURN_UNGCPRO (result); +} + DEFUN ("dbus-method-return-internal", Fdbus_method_return_internal, Sdbus_method_return_internal, 3, MANY, 0, @@ -950,7 +1287,7 @@ usage: (dbus-method-return-internal BUS SERIAL SERVICE &rest ARGS) */) CHECK_STRING (service); GCPRO3 (bus, serial, service); - XD_DEBUG_MESSAGE ("%d %s ", XUINT (serial), SDATA (service)); + XD_DEBUG_MESSAGE ("%lu %s ", (unsigned long) XUINT (serial), SDATA (service)); /* Open a connection to the bus. */ connection = xd_initialize (bus); @@ -962,8 +1299,7 @@ usage: (dbus-method-return-internal BUS SERIAL SERVICE &rest ARGS) */) || (!dbus_message_set_destination (dmessage, SDATA (service)))) { UNGCPRO; - xsignal1 (Qdbus_error, - build_string ("Unable to create a return message")); + XD_SIGNAL1 (build_string ("Unable to create a return message")); } UNGCPRO; @@ -1001,7 +1337,102 @@ usage: (dbus-method-return-internal BUS SERIAL SERVICE &rest ARGS) */) /* Send the message. The message is just added to the outgoing message queue. */ if (!dbus_connection_send (connection, dmessage, NULL)) - xsignal1 (Qdbus_error, build_string ("Cannot send message")); + XD_SIGNAL1 (build_string ("Cannot send message")); + + /* Flush connection to ensure the message is handled. */ + dbus_connection_flush (connection); + + XD_DEBUG_MESSAGE ("Message sent"); + + /* Cleanup. */ + dbus_message_unref (dmessage); + + /* Return. */ + return Qt; +} + +DEFUN ("dbus-method-error-internal", Fdbus_method_error_internal, + Sdbus_method_error_internal, + 3, MANY, 0, + doc: /* Return error message for message SERIAL on the D-Bus BUS. +This is an internal function, it shall not be used outside dbus.el. + +usage: (dbus-method-error-internal BUS SERIAL SERVICE &rest ARGS) */) + (nargs, args) + int nargs; + register Lisp_Object *args; +{ + Lisp_Object bus, serial, service; + struct gcpro gcpro1, gcpro2, gcpro3; + DBusConnection *connection; + DBusMessage *dmessage; + DBusMessageIter iter; + unsigned int dtype; + int i; + char signature[DBUS_MAXIMUM_SIGNATURE_LENGTH]; + + /* Check parameters. */ + bus = args[0]; + serial = args[1]; + service = args[2]; + + CHECK_SYMBOL (bus); + CHECK_NUMBER (serial); + CHECK_STRING (service); + GCPRO3 (bus, serial, service); + + XD_DEBUG_MESSAGE ("%lu %s ", (unsigned long) XUINT (serial), SDATA (service)); + + /* Open a connection to the bus. */ + connection = xd_initialize (bus); + + /* Create the message. */ + dmessage = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR); + if ((dmessage == NULL) + || (!dbus_message_set_error_name (dmessage, DBUS_ERROR_FAILED)) + || (!dbus_message_set_reply_serial (dmessage, XUINT (serial))) + || (!dbus_message_set_destination (dmessage, SDATA (service)))) + { + UNGCPRO; + XD_SIGNAL1 (build_string ("Unable to create a error message")); + } + + UNGCPRO; + + /* Initialize parameter list of message. */ + dbus_message_iter_init_append (dmessage, &iter); + + /* Append parameters to the message. */ + for (i = 3; i < nargs; ++i) + { + dtype = XD_OBJECT_TO_DBUS_TYPE (args[i]); + if (XD_DBUS_TYPE_P (args[i])) + { + XD_DEBUG_VALID_LISP_OBJECT_P (args[i]); + XD_DEBUG_VALID_LISP_OBJECT_P (args[i+1]); + XD_DEBUG_MESSAGE ("Parameter%d %s %s", i-2, + SDATA (format2 ("%s", args[i], Qnil)), + SDATA (format2 ("%s", args[i+1], Qnil))); + ++i; + } + else + { + XD_DEBUG_VALID_LISP_OBJECT_P (args[i]); + XD_DEBUG_MESSAGE ("Parameter%d %s", i-2, + SDATA (format2 ("%s", args[i], Qnil))); + } + + /* Check for valid signature. We use DBUS_TYPE_INVALID as + indication that there is no parent type. */ + xd_signature (signature, dtype, DBUS_TYPE_INVALID, args[i]); + + xd_append_arg (dtype, args[i], &iter); + } + + /* Send the message. The message is just added to the outgoing + message queue. */ + if (!dbus_connection_send (connection, dmessage, NULL)) + XD_SIGNAL1 (build_string ("Cannot send message")); /* Flush connection to ensure the message is handled. */ dbus_connection_flush (connection); @@ -1086,7 +1517,7 @@ usage: (dbus-send-signal BUS SERVICE PATH INTERFACE SIGNAL &rest ARGS) */) SDATA (signal)); UNGCPRO; if (dmessage == NULL) - xsignal1 (Qdbus_error, build_string ("Unable to create a new message")); + XD_SIGNAL1 (build_string ("Unable to create a new message")); /* Initialize parameter list of message. */ dbus_message_iter_init_append (dmessage, &iter); @@ -1121,7 +1552,7 @@ usage: (dbus-send-signal BUS SERVICE PATH INTERFACE SIGNAL &rest ARGS) */) /* Send the message. The message is just added to the outgoing message queue. */ if (!dbus_connection_send (connection, dmessage, NULL)) - xsignal1 (Qdbus_error, build_string ("Cannot send message")); + XD_SIGNAL1 (build_string ("Cannot send message")); /* Flush connection to ensure the message is handled. */ dbus_connection_flush (connection); @@ -1135,9 +1566,46 @@ usage: (dbus-send-signal BUS SERVICE PATH INTERFACE SIGNAL &rest ARGS) */) return Qt; } +/* Check, whether there is pending input in the message queue of the + D-Bus BUS. BUS is a Lisp symbol, either :system or :session. */ +int +xd_get_dispatch_status (bus) + Lisp_Object bus; +{ + DBusConnection *connection; + + /* Open a connection to the bus. */ + connection = xd_initialize (bus); + + /* Non blocking read of the next available message. */ + dbus_connection_read_write (connection, 0); + + /* Return. */ + return + (dbus_connection_get_dispatch_status (connection) + == DBUS_DISPATCH_DATA_REMAINS) + ? TRUE : FALSE; +} + +/* Check for queued incoming messages from the system and session buses. */ +int +xd_pending_messages () +{ + + /* Vdbus_registered_objects_table will be initialized as hash table + in dbus.el. When this package isn't loaded yet, it doesn't make + sense to handle D-Bus messages. */ + return (HASH_TABLE_P (Vdbus_registered_objects_table) + ? (xd_get_dispatch_status (QCdbus_system_bus) + || ((getenv ("DBUS_SESSION_BUS_ADDRESS") != NULL) + ? xd_get_dispatch_status (QCdbus_session_bus) + : FALSE)) + : FALSE); +} + /* Read queued incoming message of the D-Bus BUS. BUS is a Lisp symbol, either :system or :session. */ -Lisp_Object +static Lisp_Object xd_read_message (bus) Lisp_Object bus; { @@ -1148,7 +1616,7 @@ xd_read_message (bus) DBusMessage *dmessage; DBusMessageIter iter; unsigned int dtype; - int mtype; + int mtype, serial; const char *uname, *path, *interface, *member; /* Open a connection to the bus. */ @@ -1179,71 +1647,118 @@ xd_read_message (bus) args = Fnreverse (args); } - /* Read message type, unique name, object path, interface and member - from the message. */ - mtype = dbus_message_get_type (dmessage); - uname = dbus_message_get_sender (dmessage); - path = dbus_message_get_path (dmessage); + /* Read message type, message serial, unique name, object path, + interface and member from the message. */ + mtype = dbus_message_get_type (dmessage); + serial = + ((mtype == DBUS_MESSAGE_TYPE_METHOD_RETURN) + || (mtype == DBUS_MESSAGE_TYPE_ERROR)) + ? dbus_message_get_reply_serial (dmessage) + : dbus_message_get_serial (dmessage); + uname = dbus_message_get_sender (dmessage); + path = dbus_message_get_path (dmessage); interface = dbus_message_get_interface (dmessage); - member = dbus_message_get_member (dmessage); - - /* Vdbus_registered_functions_table requires non-nil interface and member. */ - if ((NULL == interface) || (NULL == member)) - goto cleanup; - - XD_DEBUG_MESSAGE ("Event received: %d %s %s %s %s %s", - mtype, uname, path, interface, member, + member = dbus_message_get_member (dmessage); + + XD_DEBUG_MESSAGE ("Event received: %s %d %s %s %s %s %s", + (mtype == DBUS_MESSAGE_TYPE_INVALID) + ? "DBUS_MESSAGE_TYPE_INVALID" + : (mtype == DBUS_MESSAGE_TYPE_METHOD_CALL) + ? "DBUS_MESSAGE_TYPE_METHOD_CALL" + : (mtype == DBUS_MESSAGE_TYPE_METHOD_RETURN) + ? "DBUS_MESSAGE_TYPE_METHOD_RETURN" + : (mtype == DBUS_MESSAGE_TYPE_ERROR) + ? "DBUS_MESSAGE_TYPE_ERROR" + : "DBUS_MESSAGE_TYPE_SIGNAL", + serial, uname, path, interface, member, SDATA (format2 ("%s", args, Qnil))); - /* Search for a registered function of the message. */ - key = list3 (bus, build_string (interface), build_string (member)); - value = Fgethash (key, Vdbus_registered_functions_table, Qnil); + if ((mtype == DBUS_MESSAGE_TYPE_METHOD_RETURN) + || (mtype == DBUS_MESSAGE_TYPE_ERROR)) + { + /* Search for a registered function of the message. */ + key = list2 (bus, make_number (serial)); + value = Fgethash (key, Vdbus_registered_objects_table, Qnil); + + /* There shall be exactly one entry. Construct an event. */ + if (NILP (value)) + goto cleanup; + + /* Remove the entry. */ + Fremhash (key, Vdbus_registered_objects_table); + + /* Construct an event. */ + EVENT_INIT (event); + event.kind = DBUS_EVENT; + event.frame_or_window = Qnil; + event.arg = Fcons (value, args); + } - /* Loop over the registered functions. Construct an event. */ - while (!NILP (value)) + else /* (mtype != DBUS_MESSAGE_TYPE_METHOD_RETURN) */ { - key = CAR_SAFE (value); - /* key has the structure (UNAME SERVICE PATH HANDLER). */ - if (((uname == NULL) - || (NILP (CAR_SAFE (key))) - || (strcmp (uname, SDATA (CAR_SAFE (key))) == 0)) - && ((path == NULL) - || (NILP (CAR_SAFE (CDR_SAFE (CDR_SAFE (key))))) - || (strcmp (path, SDATA (CAR_SAFE (CDR_SAFE (CDR_SAFE (key))))) - == 0)) - && (!NILP (CAR_SAFE (CDR_SAFE (CDR_SAFE (CDR_SAFE (key))))))) + /* Vdbus_registered_objects_table requires non-nil interface and + member. */ + if ((interface == NULL) || (member == NULL)) + goto cleanup; + + /* Search for a registered function of the message. */ + key = list3 (bus, build_string (interface), build_string (member)); + value = Fgethash (key, Vdbus_registered_objects_table, Qnil); + + /* Loop over the registered functions. Construct an event. */ + while (!NILP (value)) { - EVENT_INIT (event); - event.kind = DBUS_EVENT; - event.frame_or_window = Qnil; - event.arg = Fcons (CAR_SAFE (CDR_SAFE (CDR_SAFE (CDR_SAFE (key)))), - args); - - /* Add uname, path, interface and member to the event. */ - event.arg = Fcons (build_string (member), event.arg); - event.arg = Fcons (build_string (interface), event.arg); - event.arg = Fcons ((path == NULL ? Qnil : build_string (path)), - event.arg); - event.arg = Fcons ((uname == NULL ? Qnil : build_string (uname)), - event.arg); - - /* Add the message serial if needed, or nil. */ - event.arg = Fcons ((mtype == DBUS_MESSAGE_TYPE_METHOD_CALL - ? make_number (dbus_message_get_serial (dmessage)) - : Qnil), - event.arg); - - /* Add the bus symbol to the event. */ - event.arg = Fcons (bus, event.arg); - - /* Store it into the input event queue. */ - kbd_buffer_store_event (&event); + key = CAR_SAFE (value); + /* key has the structure (UNAME SERVICE PATH HANDLER). */ + if (((uname == NULL) + || (NILP (CAR_SAFE (key))) + || (strcmp (uname, SDATA (CAR_SAFE (key))) == 0)) + && ((path == NULL) + || (NILP (CAR_SAFE (CDR_SAFE (CDR_SAFE (key))))) + || (strcmp (path, + SDATA (CAR_SAFE (CDR_SAFE (CDR_SAFE (key))))) + == 0)) + && (!NILP (CAR_SAFE (CDR_SAFE (CDR_SAFE (CDR_SAFE (key))))))) + { + EVENT_INIT (event); + event.kind = DBUS_EVENT; + event.frame_or_window = Qnil; + event.arg = Fcons (CAR_SAFE (CDR_SAFE (CDR_SAFE (CDR_SAFE (key)))), + args); + break; + } + value = CDR_SAFE (value); } - value = CDR_SAFE (value); + + if (NILP (value)) + goto cleanup; } + /* Add type, serial, uname, path, interface and member to the event. */ + event.arg = Fcons ((member == NULL ? Qnil : build_string (member)), + event.arg); + event.arg = Fcons ((interface == NULL ? Qnil : build_string (interface)), + event.arg); + event.arg = Fcons ((path == NULL ? Qnil : build_string (path)), + event.arg); + event.arg = Fcons ((uname == NULL ? Qnil : build_string (uname)), + event.arg); + event.arg = Fcons (make_number (serial), event.arg); + event.arg = Fcons (make_number (mtype), event.arg); + + /* Add the bus symbol to the event. */ + event.arg = Fcons (bus, event.arg); + + /* Store it into the input event queue. */ + kbd_buffer_store_event (&event); + + XD_DEBUG_MESSAGE ("Event stored: %s", + SDATA (format2 ("%s", event.arg, Qnil))); + + /* Cleanup. */ cleanup: dbus_message_unref (dmessage); + RETURN_UNGCPRO (Qnil); } @@ -1252,21 +1767,21 @@ void xd_read_queued_messages () { - /* Vdbus_registered_functions_table will be initialized as hash - table in dbus.el. When this package isn't loaded yet, it doesn't - make sense to handle D-Bus messages. Furthermore, we ignore all - Lisp errors during the call. */ - if (HASH_TABLE_P (Vdbus_registered_functions_table)) + /* Vdbus_registered_objects_table will be initialized as hash table + in dbus.el. When this package isn't loaded yet, it doesn't make + sense to handle D-Bus messages. Furthermore, we ignore all Lisp + errors during the call. */ + if (HASH_TABLE_P (Vdbus_registered_objects_table)) { - internal_condition_case_1 (xd_read_message, QCdbus_system_bus, - Qerror, Fidentity); - internal_condition_case_1 (xd_read_message, QCdbus_session_bus, - Qerror, Fidentity); + xd_in_read_queued_messages = 1; + internal_catch (Qdbus_error, xd_read_message, QCdbus_system_bus); + internal_catch (Qdbus_error, xd_read_message, QCdbus_session_bus); + xd_in_read_queued_messages = 0; } } DEFUN ("dbus-register-signal", Fdbus_register_signal, Sdbus_register_signal, - 6, 6, 0, + 6, MANY, 0, doc: /* Register for signal SIGNAL on the D-Bus BUS. BUS is either the symbol `:system' or the symbol `:session'. @@ -1281,8 +1796,14 @@ nil if the path name of incoming signals shall not be checked. INTERFACE is an interface offered by SERVICE. It must provide SIGNAL. HANDLER is a Lisp function to be called when the signal is received. -It must accept as arguments the values SIGNAL is sending. INTERFACE, -SIGNAL and HANDLER must not be nil. Example: +It must accept as arguments the values SIGNAL is sending. + +All other arguments ARGS, if specified, must be strings. They stand +for the respective arguments of the signal in their order, and are +used for filtering as well. A nil argument might be used to preserve +the order. + +INTERFACE, SIGNAL and HANDLER must not be nil. Example: \(defun my-signal-handler (device) (message "Device %s added" device)) @@ -1295,16 +1816,30 @@ SIGNAL and HANDLER must not be nil. Example: ("org.freedesktop.Hal" "/org/freedesktop/Hal/Manager" my-signal-handler)) `dbus-register-signal' returns an object, which can be used in -`dbus-unregister-object' for removing the registration. */) - (bus, service, path, interface, signal, handler) - Lisp_Object bus, service, path, interface, signal, handler; +`dbus-unregister-object' for removing the registration. + +usage: (dbus-register-signal BUS SERVICE PATH INTERFACE SIGNAL HANDLER &rest ARGS) */) + (nargs, args) + int nargs; + register Lisp_Object *args; { + Lisp_Object bus, service, path, interface, signal, handler; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5, gcpro6; Lisp_Object uname, key, key1, value; DBusConnection *connection; + int i; char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; + char x[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; DBusError derror; /* Check parameters. */ + bus = args[0]; + service = args[1]; + path = args[2]; + interface = args[3]; + signal = args[4]; + handler = args[5]; + CHECK_SYMBOL (bus); if (!NILP (service)) CHECK_STRING (service); if (!NILP (path)) CHECK_STRING (path); @@ -1312,6 +1847,7 @@ SIGNAL and HANDLER must not be nil. Example: CHECK_STRING (signal); if (!FUNCTIONP (handler)) wrong_type_argument (intern ("functionp"), handler); + GCPRO6 (bus, service, path, interface, signal, handler); /* Retrieve unique name of service. If service is a known name, we will register for the corresponding unique name, if any. Signals @@ -1326,7 +1862,7 @@ SIGNAL and HANDLER must not be nil. Example: /* When there is no unique name, we mark it with an empty string. */ if (NILP (uname)) - uname = build_string (""); + uname = empty_unibyte_string; } else uname = service; @@ -1346,16 +1882,37 @@ SIGNAL and HANDLER must not be nil. Example: /* Add unique name and path to the rule if they are non-nil. */ if (!NILP (uname)) - sprintf (rule, "%s,sender='%s'", rule, SDATA (uname)); + { + sprintf (x, ",sender='%s'", SDATA (uname)); + strcat (rule, x); + } if (!NILP (path)) - sprintf (rule, "%s,path='%s'", rule, SDATA (path)); + { + sprintf (x, ",path='%s'", SDATA (path)); + strcat (rule, x); + } + + /* Add arguments to the rule if they are non-nil. */ + for (i = 6; i < nargs; ++i) + if (!NILP (args[i])) + { + CHECK_STRING (args[i]); + sprintf (x, ",arg%d='%s'", i-6, SDATA (args[i])); + strcat (rule, x); + } /* Add the rule to the bus. */ dbus_error_init (&derror); dbus_bus_add_match (connection, rule, &derror); if (dbus_error_is_set (&derror)) - XD_ERROR (derror); + { + UNGCPRO; + XD_ERROR (derror); + } + + /* Cleanup. */ + dbus_error_free (&derror); XD_DEBUG_MESSAGE ("Matching rule \"%s\" created", rule); } @@ -1363,13 +1920,13 @@ SIGNAL and HANDLER must not be nil. Example: /* Create a hash table entry. */ key = list3 (bus, interface, signal); key1 = list4 (uname, service, path, handler); - value = Fgethash (key, Vdbus_registered_functions_table, Qnil); + value = Fgethash (key, Vdbus_registered_objects_table, Qnil); if (NILP (Fmember (key1, value))) - Fputhash (key, Fcons (key1, value), Vdbus_registered_functions_table); + Fputhash (key, Fcons (key1, value), Vdbus_registered_objects_table); /* Return object. */ - return list2 (key, list3 (service, path, handler)); + RETURN_UNGCPRO (list2 (key, list3 (service, path, handler))); } DEFUN ("dbus-register-method", Fdbus_register_method, Sdbus_register_method, @@ -1415,15 +1972,17 @@ used for composing the returning D-Bus message. */) if (dbus_error_is_set (&derror)) XD_ERROR (derror); - /* Create a hash table entry. */ + /* Create a hash table entry. We use nil for the unique name, + because the method might be called from anybody. */ key = list3 (bus, interface, method); key1 = list4 (Qnil, service, path, handler); - value = Fgethash (key, Vdbus_registered_functions_table, Qnil); + value = Fgethash (key, Vdbus_registered_objects_table, Qnil); - /* We use nil for the unique name, because the method might be - called from everybody. */ if (NILP (Fmember (key1, value))) - Fputhash (key, Fcons (key1, value), Vdbus_registered_functions_table); + Fputhash (key, Fcons (key1, value), Vdbus_registered_objects_table); + + /* Cleanup. */ + dbus_error_free (&derror); /* Return object. */ return list2 (key, list3 (service, path, handler)); @@ -1434,113 +1993,138 @@ void syms_of_dbusbind () { - Qdbus_get_unique_name = intern ("dbus-get-unique-name"); + Qdbus_init_bus = intern_c_string ("dbus-init-bus"); + staticpro (&Qdbus_init_bus); + defsubr (&Sdbus_init_bus); + + Qdbus_get_unique_name = intern_c_string ("dbus-get-unique-name"); staticpro (&Qdbus_get_unique_name); defsubr (&Sdbus_get_unique_name); - Qdbus_call_method = intern ("dbus-call-method"); + Qdbus_call_method = intern_c_string ("dbus-call-method"); staticpro (&Qdbus_call_method); defsubr (&Sdbus_call_method); - Qdbus_method_return_internal = intern ("dbus-method-return-internal"); + Qdbus_call_method_asynchronously = intern_c_string ("dbus-call-method-asynchronously"); + staticpro (&Qdbus_call_method_asynchronously); + defsubr (&Sdbus_call_method_asynchronously); + + Qdbus_method_return_internal = intern_c_string ("dbus-method-return-internal"); staticpro (&Qdbus_method_return_internal); defsubr (&Sdbus_method_return_internal); - Qdbus_send_signal = intern ("dbus-send-signal"); + Qdbus_method_error_internal = intern_c_string ("dbus-method-error-internal"); + staticpro (&Qdbus_method_error_internal); + defsubr (&Sdbus_method_error_internal); + + Qdbus_send_signal = intern_c_string ("dbus-send-signal"); staticpro (&Qdbus_send_signal); defsubr (&Sdbus_send_signal); - Qdbus_register_signal = intern ("dbus-register-signal"); + Qdbus_register_signal = intern_c_string ("dbus-register-signal"); staticpro (&Qdbus_register_signal); defsubr (&Sdbus_register_signal); - Qdbus_register_method = intern ("dbus-register-method"); + Qdbus_register_method = intern_c_string ("dbus-register-method"); staticpro (&Qdbus_register_method); defsubr (&Sdbus_register_method); - Qdbus_error = intern ("dbus-error"); + Qdbus_error = intern_c_string ("dbus-error"); staticpro (&Qdbus_error); Fput (Qdbus_error, Qerror_conditions, list2 (Qdbus_error, Qerror)); Fput (Qdbus_error, Qerror_message, - build_string ("D-Bus error")); + make_pure_c_string ("D-Bus error")); - QCdbus_system_bus = intern (":system"); + QCdbus_system_bus = intern_c_string (":system"); staticpro (&QCdbus_system_bus); - QCdbus_session_bus = intern (":session"); + QCdbus_session_bus = intern_c_string (":session"); staticpro (&QCdbus_session_bus); - QCdbus_timeout = intern (":timeout"); + QCdbus_timeout = intern_c_string (":timeout"); staticpro (&QCdbus_timeout); - QCdbus_type_byte = intern (":byte"); + QCdbus_type_byte = intern_c_string (":byte"); staticpro (&QCdbus_type_byte); - QCdbus_type_boolean = intern (":boolean"); + QCdbus_type_boolean = intern_c_string (":boolean"); staticpro (&QCdbus_type_boolean); - QCdbus_type_int16 = intern (":int16"); + QCdbus_type_int16 = intern_c_string (":int16"); staticpro (&QCdbus_type_int16); - QCdbus_type_uint16 = intern (":uint16"); + QCdbus_type_uint16 = intern_c_string (":uint16"); staticpro (&QCdbus_type_uint16); - QCdbus_type_int32 = intern (":int32"); + QCdbus_type_int32 = intern_c_string (":int32"); staticpro (&QCdbus_type_int32); - QCdbus_type_uint32 = intern (":uint32"); + QCdbus_type_uint32 = intern_c_string (":uint32"); staticpro (&QCdbus_type_uint32); - QCdbus_type_int64 = intern (":int64"); + QCdbus_type_int64 = intern_c_string (":int64"); staticpro (&QCdbus_type_int64); - QCdbus_type_uint64 = intern (":uint64"); + QCdbus_type_uint64 = intern_c_string (":uint64"); staticpro (&QCdbus_type_uint64); - QCdbus_type_double = intern (":double"); + QCdbus_type_double = intern_c_string (":double"); staticpro (&QCdbus_type_double); - QCdbus_type_string = intern (":string"); + QCdbus_type_string = intern_c_string (":string"); staticpro (&QCdbus_type_string); - QCdbus_type_object_path = intern (":object-path"); + QCdbus_type_object_path = intern_c_string (":object-path"); staticpro (&QCdbus_type_object_path); - QCdbus_type_signature = intern (":signature"); + QCdbus_type_signature = intern_c_string (":signature"); staticpro (&QCdbus_type_signature); - QCdbus_type_array = intern (":array"); + QCdbus_type_array = intern_c_string (":array"); staticpro (&QCdbus_type_array); - QCdbus_type_variant = intern (":variant"); + QCdbus_type_variant = intern_c_string (":variant"); staticpro (&QCdbus_type_variant); - QCdbus_type_struct = intern (":struct"); + QCdbus_type_struct = intern_c_string (":struct"); staticpro (&QCdbus_type_struct); - QCdbus_type_dict_entry = intern (":dict-entry"); + QCdbus_type_dict_entry = intern_c_string (":dict-entry"); staticpro (&QCdbus_type_dict_entry); - DEFVAR_LISP ("dbus-registered-functions-table", - &Vdbus_registered_functions_table, + DEFVAR_LISP ("dbus-registered-objects-table", + &Vdbus_registered_objects_table, doc: /* Hash table of registered functions for D-Bus. -The key in the hash table is the list (BUS INTERFACE MEMBER). BUS is -either the symbol `:system' or the symbol `:session'. INTERFACE is a -string which denotes a D-Bus interface, and MEMBER, also a string, is -either a method or a signal INTERFACE is offering. All arguments but -BUS must not be nil. +There are two different uses of the hash table: for accessing +registered interfaces properties, targeted by signals or method calls, +and for calling handlers in case of non-blocking method call returns. + +In the first case, the key in the hash table is the list (BUS +INTERFACE MEMBER). BUS is either the symbol `:system' or the symbol +`:session'. INTERFACE is a string which denotes a D-Bus interface, +and MEMBER, also a string, is either a method, a signal or a property +INTERFACE is offering. All arguments but BUS must not be nil. The value in the hash table is a list of quadruple lists -\((UNAME SERVICE PATH HANDLER) (UNAME SERVICE PATH HANDLER) ...). +\((UNAME SERVICE PATH OBJECT) (UNAME SERVICE PATH OBJECT) ...). SERVICE is the service name as registered, UNAME is the corresponding -unique name. PATH is the object path of the sending object. All of -them can be nil, which means a wildcard then. HANDLER is the function -to be called when a D-Bus message, which matches the key criteria, -arrives. */); - /* We initialize Vdbus_registered_functions_table in dbus.el, - because we need to define a hash table function first. */ - Vdbus_registered_functions_table = Qnil; +unique name. In case of registered methods and properties, UNAME is +nil. PATH is the object path of the sending object. All of them can +be nil, which means a wildcard then. OBJECT is either the handler to +be called when a D-Bus message, which matches the key criteria, +arrives (methods and signals), or a cons cell containing the value of +the property. + +In the second case, the key in the hash table is the list (BUS SERIAL). +BUS is either the symbol `:system' or the symbol `:session'. SERIAL +is the serial number of the non-blocking method call, a reply is +expected. Both arguments must not be nil. The value in the hash +table is HANDLER, the function to be called when the D-Bus reply +message arrives. */); + /* We initialize Vdbus_registered_objects_table in dbus.el, because + we need to define a hash table function first. */ + Vdbus_registered_objects_table = Qnil; DEFVAR_LISP ("dbus-debug", &Vdbus_debug, doc: /* If non-nil, debug messages of D-Bus bindings are raised. */); @@ -1550,7 +2134,7 @@ arrives. */); Vdbus_debug = Qnil; #endif - Fprovide (intern ("dbusbind"), Qnil); + Fprovide (intern_c_string ("dbusbind"), Qnil); }