/* Elisp bindings for D-Bus.
Copyright (C) 2007-2012 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see . */
#include
#ifdef HAVE_DBUS
#include
#include
#include
#include "lisp.h"
#include "frame.h"
#include "termhooks.h"
#include "keyboard.h"
#include "process.h"
#ifndef DBUS_NUM_MESSAGE_TYPES
#define DBUS_NUM_MESSAGE_TYPES 5
#endif
/* Subroutines. */
static Lisp_Object Qdbus_init_bus;
static Lisp_Object Qdbus_get_unique_name;
static Lisp_Object Qdbus_message_internal;
/* D-Bus error symbol. */
static Lisp_Object Qdbus_error;
/* Lisp symbols of the system and session buses. */
static Lisp_Object QCdbus_system_bus, QCdbus_session_bus;
/* Lisp symbol for method call timeout. */
static Lisp_Object QCdbus_timeout;
/* Lisp symbols of D-Bus types. */
static Lisp_Object QCdbus_type_byte, QCdbus_type_boolean;
static Lisp_Object QCdbus_type_int16, QCdbus_type_uint16;
static Lisp_Object QCdbus_type_int32, QCdbus_type_uint32;
static Lisp_Object QCdbus_type_int64, QCdbus_type_uint64;
static Lisp_Object QCdbus_type_double, QCdbus_type_string;
static Lisp_Object QCdbus_type_object_path, QCdbus_type_signature;
#ifdef DBUS_TYPE_UNIX_FD
static Lisp_Object QCdbus_type_unix_fd;
#endif
static Lisp_Object QCdbus_type_array, QCdbus_type_variant;
static Lisp_Object QCdbus_type_struct, QCdbus_type_dict_entry;
/* Lisp symbols of objects in `dbus-registered-objects-table'. */
static Lisp_Object QCdbus_registered_serial, QCdbus_registered_method;
static Lisp_Object QCdbus_registered_signal;
/* Alist of D-Bus buses we are polling for messages.
The key is the symbol or string of the bus, and the value is the
connection address. */
static Lisp_Object xd_registered_buses;
/* Whether we are reading a D-Bus event. */
static 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 { \
/* Remove the trailing newline. */ \
char const *mess = error.message; \
char const *nl = strchr (mess, '\n'); \
Lisp_Object err = make_string (mess, nl ? nl - mess : strlen (mess)); \
dbus_error_free (&error); \
XD_SIGNAL1 (err); \
} while (0)
/* Macros for debugging. In order to enable them, build with
"env MYCPPFLAGS='-DDBUS_DEBUG -Wall' make". */
#ifdef DBUS_DEBUG
#define XD_DEBUG_MESSAGE(...) \
do { \
char s[1024]; \
snprintf (s, sizeof s, __VA_ARGS__); \
if (!noninteractive) \
printf ("%s: %s\n", __func__, s); \
message ("%s: %s", __func__, s); \
} while (0)
#define XD_DEBUG_VALID_LISP_OBJECT_P(object) \
do { \
if (!valid_lisp_object_p (object)) \
{ \
XD_DEBUG_MESSAGE ("%d Assertion failure", __LINE__); \
XD_SIGNAL1 (build_string ("Assertion failure")); \
} \
} while (0)
#else /* !DBUS_DEBUG */
#define XD_DEBUG_MESSAGE(...) \
do { \
if (!NILP (Vdbus_debug)) \
{ \
char s[1024]; \
snprintf (s, sizeof s, __VA_ARGS__); \
message ("%s: %s", __func__, s); \
} \
} while (0)
#define XD_DEBUG_VALID_LISP_OBJECT_P(object)
#endif
/* Check whether TYPE is a basic DBusType. */
#ifdef DBUS_TYPE_UNIX_FD
#define XD_BASIC_DBUS_TYPE(type) \
((type == DBUS_TYPE_BYTE) \
|| (type == DBUS_TYPE_BOOLEAN) \
|| (type == DBUS_TYPE_INT16) \
|| (type == DBUS_TYPE_UINT16) \
|| (type == DBUS_TYPE_INT32) \
|| (type == DBUS_TYPE_UINT32) \
|| (type == DBUS_TYPE_INT64) \
|| (type == DBUS_TYPE_UINT64) \
|| (type == DBUS_TYPE_DOUBLE) \
|| (type == DBUS_TYPE_STRING) \
|| (type == DBUS_TYPE_OBJECT_PATH) \
|| (type == DBUS_TYPE_SIGNATURE) \
|| (type == DBUS_TYPE_UNIX_FD))
#else
#define XD_BASIC_DBUS_TYPE(type) \
((type == DBUS_TYPE_BYTE) \
|| (type == DBUS_TYPE_BOOLEAN) \
|| (type == DBUS_TYPE_INT16) \
|| (type == DBUS_TYPE_UINT16) \
|| (type == DBUS_TYPE_INT32) \
|| (type == DBUS_TYPE_UINT32) \
|| (type == DBUS_TYPE_INT64) \
|| (type == DBUS_TYPE_UINT64) \
|| (type == DBUS_TYPE_DOUBLE) \
|| (type == DBUS_TYPE_STRING) \
|| (type == DBUS_TYPE_OBJECT_PATH) \
|| (type == DBUS_TYPE_SIGNATURE))
#endif
/* This was a macro. On Solaris 2.11 it was said to compile for
hours, when optimization 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. */
static int
xd_symbol_to_dbus_type (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
#ifdef DBUS_TYPE_UNIX_FD
: (EQ (object, QCdbus_type_unix_fd)) ? DBUS_TYPE_UNIX_FD
#endif
: (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)))
/* Determine the DBusType of a given Lisp OBJECT. It is used to
convert Lisp objects, being arguments of `dbus-call-method' or
`dbus-send-signal', into corresponding C values appended as
arguments to a D-Bus message. */
#define XD_OBJECT_TO_DBUS_TYPE(object) \
((EQ (object, Qt) || EQ (object, Qnil)) ? DBUS_TYPE_BOOLEAN \
: (NATNUMP (object)) ? DBUS_TYPE_UINT32 \
: (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_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. */
#define XD_NEXT_VALUE(object) \
((XD_DBUS_TYPE_P (CAR_SAFE (object))) ? CDR_SAFE (object) : object)
/* Transform the message type to its string representation for debug
messages. */
#define XD_MESSAGE_TYPE_TO_STRING(mtype) \
((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")
/* Transform the object to its string representation for debug
messages. */
#define XD_OBJECT_TO_STRING(object) \
SDATA (format2 ("%s", object, Qnil))
/* Check whether X is a valid dbus serial number. If valid, set
SERIAL to its value. Otherwise, signal an error. */
#define XD_CHECK_DBUS_SERIAL(x, serial) \
do { \
dbus_uint32_t DBUS_SERIAL_MAX = -1; \
if (NATNUMP (x) && XINT (x) <= DBUS_SERIAL_MAX) \
serial = XINT (x); \
else if (MOST_POSITIVE_FIXNUM < DBUS_SERIAL_MAX \
&& FLOATP (x) \
&& 0 <= XFLOAT_DATA (x) \
&& XFLOAT_DATA (x) <= DBUS_SERIAL_MAX) \
serial = XFLOAT_DATA (x); \
else \
XD_SIGNAL2 (build_string ("Invalid dbus serial"), x); \
} while (0)
#define XD_DBUS_VALIDATE_BUS_ADDRESS(bus) \
do { \
if (STRINGP (bus)) \
{ \
DBusAddressEntry **entries; \
int len; \
DBusError derror; \
dbus_error_init (&derror); \
if (!dbus_parse_address (SSDATA (bus), &entries, &len, &derror)) \
XD_ERROR (derror); \
/* Cleanup. */ \
dbus_error_free (&derror); \
dbus_address_entries_free (entries); \
} \
\
else \
{ \
CHECK_SYMBOL (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); \
} \
} while (0)
#if (HAVE_DBUS_VALIDATE_BUS_NAME || HAVE_DBUS_VALIDATE_PATH \
|| XD_DBUS_VALIDATE_OBJECT || HAVE_DBUS_VALIDATE_MEMBER)
#define XD_DBUS_VALIDATE_OBJECT(object, func) \
do { \
if (!NILP (object)) \
{ \
DBusError derror; \
CHECK_STRING (object); \
dbus_error_init (&derror); \
if (!func (SSDATA (object), &derror)) \
XD_ERROR (derror); \
/* Cleanup. */ \
dbus_error_free (&derror); \
} \
} while (0)
#endif
#if HAVE_DBUS_VALIDATE_BUS_NAME
#define XD_DBUS_VALIDATE_BUS_NAME(bus_name) \
XD_DBUS_VALIDATE_OBJECT(bus_name, dbus_validate_bus_name);
#else
#define XD_DBUS_VALIDATE_BUS_NAME(bus_name) \
if (!NILP (bus_name)) CHECK_STRING (bus_name);
#endif
#if HAVE_DBUS_VALIDATE_PATH
#define XD_DBUS_VALIDATE_PATH(path) \
XD_DBUS_VALIDATE_OBJECT(path, dbus_validate_path);
#else
#define XD_DBUS_VALIDATE_PATH(path) \
if (!NILP (path)) CHECK_STRING (path);
#endif
#if HAVE_DBUS_VALIDATE_INTERFACE
#define XD_DBUS_VALIDATE_INTERFACE(interface) \
XD_DBUS_VALIDATE_OBJECT(interface, dbus_validate_interface);
#else
#define XD_DBUS_VALIDATE_INTERFACE(interface) \
if (!NILP (interface)) CHECK_STRING (interface);
#endif
#if HAVE_DBUS_VALIDATE_MEMBER
#define XD_DBUS_VALIDATE_MEMBER(member) \
XD_DBUS_VALIDATE_OBJECT(member, dbus_validate_member);
#else
#define XD_DBUS_VALIDATE_MEMBER(member) \
if (!NILP (member)) CHECK_STRING (member);
#endif
/* Append to SIGNATURE a copy of X, making sure SIGNATURE does
not become too long. */
static void
xd_signature_cat (char *signature, char const *x)
{
ptrdiff_t siglen = strlen (signature);
ptrdiff_t xlen = strlen (x);
if (DBUS_MAXIMUM_SIGNATURE_LENGTH - xlen <= siglen)
string_overflow ();
strcat (signature, x);
}
/* Compute SIGNATURE of OBJECT. It must have a form that it can be
used in dbus_message_iter_open_container. DTYPE is the DBusType
the object is related to. It is passed as argument, because it
cannot be detected in basic type objects, when they are preceded by
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. */
static void
xd_signature (char *signature, unsigned int dtype, unsigned int parent_type, Lisp_Object object)
{
unsigned int subtype;
Lisp_Object elt;
char const *subsig;
int subsiglen;
char x[DBUS_MAXIMUM_SIGNATURE_LENGTH];
elt = object;
switch (dtype)
{
case DBUS_TYPE_BYTE:
case DBUS_TYPE_UINT16:
CHECK_NATNUM (object);
sprintf (signature, "%c", dtype);
break;
case DBUS_TYPE_BOOLEAN:
if (!EQ (object, Qt) && !EQ (object, Qnil))
wrong_type_argument (intern ("booleanp"), object);
sprintf (signature, "%c", dtype);
break;
case DBUS_TYPE_INT16:
CHECK_NUMBER (object);
sprintf (signature, "%c", dtype);
break;
case DBUS_TYPE_UINT32:
case DBUS_TYPE_UINT64:
#ifdef DBUS_TYPE_UNIX_FD
case DBUS_TYPE_UNIX_FD:
#endif
case DBUS_TYPE_INT32:
case DBUS_TYPE_INT64:
case DBUS_TYPE_DOUBLE:
CHECK_NUMBER_OR_FLOAT (object);
sprintf (signature, "%c", dtype);
break;
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_SIGNATURE:
CHECK_STRING (object);
sprintf (signature, "%c", dtype);
break;
case DBUS_TYPE_ARRAY:
/* Check that all list elements have the same D-Bus type. For
complex element types, we just check the container type, not
the whole element's signature. */
CHECK_CONS (object);
/* Type symbol is optional. */
if (EQ (QCdbus_type_array, CAR_SAFE (elt)))
elt = XD_NEXT_VALUE (elt);
/* If the array is empty, DBUS_TYPE_STRING is the default
element type. */
if (NILP (elt))
{
subtype = DBUS_TYPE_STRING;
subsig = DBUS_TYPE_STRING_AS_STRING;
}
else
{
subtype = XD_OBJECT_TO_DBUS_TYPE (CAR_SAFE (elt));
xd_signature (x, subtype, dtype, CAR_SAFE (XD_NEXT_VALUE (elt)));
subsig = x;
}
/* If the element type is DBUS_TYPE_SIGNATURE, and this is the
only element, the value of this element is used as the
array's element signature. */
if ((subtype == DBUS_TYPE_SIGNATURE)
&& STRINGP (CAR_SAFE (XD_NEXT_VALUE (elt)))
&& NILP (CDR_SAFE (XD_NEXT_VALUE (elt))))
subsig = SSDATA (CAR_SAFE (XD_NEXT_VALUE (elt)));
while (!NILP (elt))
{
if (subtype != XD_OBJECT_TO_DBUS_TYPE (CAR_SAFE (elt)))
wrong_type_argument (intern ("D-Bus"), CAR_SAFE (elt));
elt = CDR_SAFE (XD_NEXT_VALUE (elt));
}
subsiglen = snprintf (signature, DBUS_MAXIMUM_SIGNATURE_LENGTH,
"%c%s", dtype, subsig);
if (! (0 <= subsiglen && subsiglen < DBUS_MAXIMUM_SIGNATURE_LENGTH))
string_overflow ();
break;
case DBUS_TYPE_VARIANT:
/* Check that there is exactly one list element. */
CHECK_CONS (object);
elt = XD_NEXT_VALUE (elt);
subtype = XD_OBJECT_TO_DBUS_TYPE (CAR_SAFE (elt));
xd_signature (x, subtype, dtype, CAR_SAFE (XD_NEXT_VALUE (elt)));
if (!NILP (CDR_SAFE (XD_NEXT_VALUE (elt))))
wrong_type_argument (intern ("D-Bus"),
CAR_SAFE (CDR_SAFE (XD_NEXT_VALUE (elt))));
sprintf (signature, "%c", dtype);
break;
case DBUS_TYPE_STRUCT:
/* A struct list might contain any number of elements with
different types. No further check needed. */
CHECK_CONS (object);
elt = XD_NEXT_VALUE (elt);
/* Compose the signature from the elements. It is enclosed by
parentheses. */
sprintf (signature, "%c", DBUS_STRUCT_BEGIN_CHAR );
while (!NILP (elt))
{
subtype = XD_OBJECT_TO_DBUS_TYPE (CAR_SAFE (elt));
xd_signature (x, subtype, dtype, CAR_SAFE (XD_NEXT_VALUE (elt)));
xd_signature_cat (signature, x);
elt = CDR_SAFE (XD_NEXT_VALUE (elt));
}
xd_signature_cat (signature, DBUS_STRUCT_END_CHAR_AS_STRING);
break;
case DBUS_TYPE_DICT_ENTRY:
/* Check that there are exactly two list elements, and the first
one is of basic type. The dictionary entry itself must be an
element of an array. */
CHECK_CONS (object);
/* Check the parent object type. */
if (parent_type != DBUS_TYPE_ARRAY)
wrong_type_argument (intern ("D-Bus"), object);
/* Compose the signature from the elements. It is enclosed by
curly braces. */
sprintf (signature, "%c", DBUS_DICT_ENTRY_BEGIN_CHAR);
/* First element. */
elt = XD_NEXT_VALUE (elt);
subtype = XD_OBJECT_TO_DBUS_TYPE (CAR_SAFE (elt));
xd_signature (x, subtype, dtype, CAR_SAFE (XD_NEXT_VALUE (elt)));
xd_signature_cat (signature, x);
if (!XD_BASIC_DBUS_TYPE (subtype))
wrong_type_argument (intern ("D-Bus"), CAR_SAFE (XD_NEXT_VALUE (elt)));
/* Second element. */
elt = CDR_SAFE (XD_NEXT_VALUE (elt));
subtype = XD_OBJECT_TO_DBUS_TYPE (CAR_SAFE (elt));
xd_signature (x, subtype, dtype, CAR_SAFE (XD_NEXT_VALUE (elt)));
xd_signature_cat (signature, x);
if (!NILP (CDR_SAFE (XD_NEXT_VALUE (elt))))
wrong_type_argument (intern ("D-Bus"),
CAR_SAFE (CDR_SAFE (XD_NEXT_VALUE (elt))));
/* Closing signature. */
xd_signature_cat (signature, DBUS_DICT_ENTRY_END_CHAR_AS_STRING);
break;
default:
wrong_type_argument (intern ("D-Bus"), object);
}
XD_DEBUG_MESSAGE ("%s", signature);
}
/* Append C value, extracted from Lisp OBJECT, to iteration ITER.
DTYPE must be a valid DBusType. It is used to convert Lisp
objects, being arguments of `dbus-call-method' or
`dbus-send-signal', into corresponding C values appended as
arguments to a D-Bus message. */
static void
xd_append_arg (unsigned int dtype, Lisp_Object object, DBusMessageIter *iter)
{
char signature[DBUS_MAXIMUM_SIGNATURE_LENGTH];
DBusMessageIter subiter;
if (XD_BASIC_DBUS_TYPE (dtype))
switch (dtype)
{
case DBUS_TYPE_BYTE:
CHECK_NATNUM (object);
{
unsigned char val = XFASTINT (object) & 0xFF;
XD_DEBUG_MESSAGE ("%c %u", dtype, val);
if (!dbus_message_iter_append_basic (iter, dtype, &val))
XD_SIGNAL2 (build_string ("Unable to append argument"), object);
return;
}
case DBUS_TYPE_BOOLEAN:
{
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))
XD_SIGNAL2 (build_string ("Unable to append argument"), object);
return;
}
case DBUS_TYPE_INT16:
CHECK_NUMBER (object);
{
dbus_int16_t val = XINT (object);
int pval = val;
XD_DEBUG_MESSAGE ("%c %d", dtype, pval);
if (!dbus_message_iter_append_basic (iter, dtype, &val))
XD_SIGNAL2 (build_string ("Unable to append argument"), object);
return;
}
case DBUS_TYPE_UINT16:
CHECK_NATNUM (object);
{
dbus_uint16_t val = XFASTINT (object);
unsigned int pval = val;
XD_DEBUG_MESSAGE ("%c %u", dtype, pval);
if (!dbus_message_iter_append_basic (iter, dtype, &val))
XD_SIGNAL2 (build_string ("Unable to append argument"), object);
return;
}
case DBUS_TYPE_INT32:
{
dbus_int32_t val = extract_float (object);
int pval = val;
XD_DEBUG_MESSAGE ("%c %d", dtype, pval);
if (!dbus_message_iter_append_basic (iter, dtype, &val))
XD_SIGNAL2 (build_string ("Unable to append argument"), object);
return;
}
case DBUS_TYPE_UINT32:
#ifdef DBUS_TYPE_UNIX_FD
case DBUS_TYPE_UNIX_FD:
#endif
{
dbus_uint32_t val = extract_float (object);
unsigned int pval = val;
XD_DEBUG_MESSAGE ("%c %u", dtype, pval);
if (!dbus_message_iter_append_basic (iter, dtype, &val))
XD_SIGNAL2 (build_string ("Unable to append argument"), object);
return;
}
case DBUS_TYPE_INT64:
{
dbus_int64_t val = extract_float (object);
printmax_t pval = val;
XD_DEBUG_MESSAGE ("%c %"pMd, dtype, pval);
if (!dbus_message_iter_append_basic (iter, dtype, &val))
XD_SIGNAL2 (build_string ("Unable to append argument"), object);
return;
}
case DBUS_TYPE_UINT64:
{
dbus_uint64_t val = extract_float (object);
uprintmax_t pval = val;
XD_DEBUG_MESSAGE ("%c %"pMu, dtype, pval);
if (!dbus_message_iter_append_basic (iter, dtype, &val))
XD_SIGNAL2 (build_string ("Unable to append argument"), object);
return;
}
case DBUS_TYPE_DOUBLE:
{
double val = extract_float (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 = SSDATA (object);
XD_DEBUG_MESSAGE ("%c %s", dtype, val);
if (!dbus_message_iter_append_basic (iter, dtype, &val))
XD_SIGNAL2 (build_string ("Unable to append argument"), object);
return;
}
}
else /* Compound types. */
{
/* All compound types except array have a type symbol. For
array, it is optional. Skip it. */
if (!XD_BASIC_DBUS_TYPE (XD_OBJECT_TO_DBUS_TYPE (CAR_SAFE (object))))
object = XD_NEXT_VALUE (object);
/* Open new subiteration. */
switch (dtype)
{
case DBUS_TYPE_ARRAY:
/* An array has only elements of the same type. So it is
sufficient to check the first element's signature
only. */
if (NILP (object))
/* If the array is empty, DBUS_TYPE_STRING is the default
element type. */
strcpy (signature, DBUS_TYPE_STRING_AS_STRING);
else
/* If the element type is DBUS_TYPE_SIGNATURE, and this is
the only element, the value of this element is used as
the array's element signature. */
if ((XD_OBJECT_TO_DBUS_TYPE (CAR_SAFE (object))
== DBUS_TYPE_SIGNATURE)
&& STRINGP (CAR_SAFE (XD_NEXT_VALUE (object)))
&& NILP (CDR_SAFE (XD_NEXT_VALUE (object))))
{
strcpy (signature, SSDATA (CAR_SAFE (XD_NEXT_VALUE (object))));
object = CDR_SAFE (XD_NEXT_VALUE (object));
}
else
xd_signature (signature,
XD_OBJECT_TO_DBUS_TYPE (CAR_SAFE (object)),
dtype, CAR_SAFE (XD_NEXT_VALUE (object)));
XD_DEBUG_MESSAGE ("%c %s %s", dtype, signature,
XD_OBJECT_TO_STRING (object));
if (!dbus_message_iter_open_container (iter, dtype,
signature, &subiter))
XD_SIGNAL3 (build_string ("Cannot open container"),
make_number (dtype), build_string (signature));
break;
case DBUS_TYPE_VARIANT:
/* A variant has just one element. */
xd_signature (signature, XD_OBJECT_TO_DBUS_TYPE (CAR_SAFE (object)),
dtype, CAR_SAFE (XD_NEXT_VALUE (object)));
XD_DEBUG_MESSAGE ("%c %s %s", dtype, signature,
XD_OBJECT_TO_STRING (object));
if (!dbus_message_iter_open_container (iter, dtype,
signature, &subiter))
XD_SIGNAL3 (build_string ("Cannot open container"),
make_number (dtype), build_string (signature));
break;
case DBUS_TYPE_STRUCT:
case DBUS_TYPE_DICT_ENTRY:
/* These containers do not require a signature. */
XD_DEBUG_MESSAGE ("%c %s", dtype, XD_OBJECT_TO_STRING (object));
if (!dbus_message_iter_open_container (iter, dtype, NULL, &subiter))
XD_SIGNAL2 (build_string ("Cannot open container"),
make_number (dtype));
break;
}
/* Loop over list elements. */
while (!NILP (object))
{
dtype = XD_OBJECT_TO_DBUS_TYPE (CAR_SAFE (object));
object = XD_NEXT_VALUE (object);
xd_append_arg (dtype, CAR_SAFE (object), &subiter);
object = CDR_SAFE (object);
}
/* Close the subiteration. */
if (!dbus_message_iter_close_container (iter, &subiter))
XD_SIGNAL2 (build_string ("Cannot close container"),
make_number (dtype));
}
}
/* Retrieve C value from a DBusMessageIter structure ITER, and return
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. */
static Lisp_Object
xd_retrieve_arg (unsigned int dtype, DBusMessageIter *iter)
{
switch (dtype)
{
case DBUS_TYPE_BYTE:
{
unsigned int val;
dbus_message_iter_get_basic (iter, &val);
val = val & 0xFF;
XD_DEBUG_MESSAGE ("%c %u", dtype, val);
return make_number (val);
}
case DBUS_TYPE_BOOLEAN:
{
dbus_bool_t val;
dbus_message_iter_get_basic (iter, &val);
XD_DEBUG_MESSAGE ("%c %s", dtype, (val == FALSE) ? "false" : "true");
return (val == FALSE) ? Qnil : Qt;
}
case DBUS_TYPE_INT16:
{
dbus_int16_t val;
int pval;
dbus_message_iter_get_basic (iter, &val);
pval = val;
XD_DEBUG_MESSAGE ("%c %d", dtype, pval);
return make_number (val);
}
case DBUS_TYPE_UINT16:
{
dbus_uint16_t val;
int pval;
dbus_message_iter_get_basic (iter, &val);
pval = val;
XD_DEBUG_MESSAGE ("%c %d", dtype, pval);
return make_number (val);
}
case DBUS_TYPE_INT32:
{
dbus_int32_t val;
int pval;
dbus_message_iter_get_basic (iter, &val);
pval = val;
XD_DEBUG_MESSAGE ("%c %d", dtype, pval);
return make_fixnum_or_float (val);
}
case DBUS_TYPE_UINT32:
#ifdef DBUS_TYPE_UNIX_FD
case DBUS_TYPE_UNIX_FD:
#endif
{
dbus_uint32_t val;
unsigned int pval = val;
dbus_message_iter_get_basic (iter, &val);
pval = val;
XD_DEBUG_MESSAGE ("%c %u", dtype, pval);
return make_fixnum_or_float (val);
}
case DBUS_TYPE_INT64:
{
dbus_int64_t val;
printmax_t pval;
dbus_message_iter_get_basic (iter, &val);
pval = val;
XD_DEBUG_MESSAGE ("%c %"pMd, dtype, pval);
return make_fixnum_or_float (val);
}
case DBUS_TYPE_UINT64:
{
dbus_uint64_t val;
uprintmax_t pval;
dbus_message_iter_get_basic (iter, &val);
pval = val;
XD_DEBUG_MESSAGE ("%c %"pMd, dtype, pval);
return make_fixnum_or_float (val);
}
case DBUS_TYPE_DOUBLE:
{
double val;
dbus_message_iter_get_basic (iter, &val);
XD_DEBUG_MESSAGE ("%c %f", dtype, val);
return make_float (val);
}
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_SIGNATURE:
{
char *val;
dbus_message_iter_get_basic (iter, &val);
XD_DEBUG_MESSAGE ("%c %s", dtype, val);
return build_string (val);
}
case DBUS_TYPE_ARRAY:
case DBUS_TYPE_VARIANT:
case DBUS_TYPE_STRUCT:
case DBUS_TYPE_DICT_ENTRY:
{
Lisp_Object result;
struct gcpro gcpro1;
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)
{
result = Fcons (xd_retrieve_arg (subtype, &subiter), result);
dbus_message_iter_next (&subiter);
}
XD_DEBUG_MESSAGE ("%c %s", dtype, XD_OBJECT_TO_STRING (result));
RETURN_UNGCPRO (Fnreverse (result));
}
default:
XD_DEBUG_MESSAGE ("DBusType '%c' not supported", dtype);
return Qnil;
}
}
/* Return the number of references of the shared CONNECTION. */
static int
xd_get_connection_references (DBusConnection *connection)
{
ptrdiff_t *refcount;
/* We cannot access the DBusConnection structure, it is not public.
But we know, that the reference counter is the first field in
that structure. */
refcount = (void *) &connection;
refcount = (void *) *refcount;
return *refcount;
}
/* Return D-Bus connection address. BUS is either a Lisp symbol,
:system or :session, or a string denoting the bus address. */
static DBusConnection *
xd_get_connection_address (Lisp_Object bus)
{
DBusConnection *connection;
Lisp_Object val;
val = CDR_SAFE (Fassoc (bus, xd_registered_buses));
if (NILP (val))
XD_SIGNAL2 (build_string ("No connection to bus"), bus);
else
connection = (DBusConnection *) (intptr_t) XFASTINT (val);
if (!dbus_connection_get_is_connected (connection))
XD_SIGNAL2 (build_string ("No connection to bus"), bus);
return connection;
}
/* Return the file descriptor for WATCH, -1 if not found. */
static int
xd_find_watch_fd (DBusWatch *watch)
{
#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
return fd;
}
/* Prototype. */
static void
xd_read_queued_messages (int fd, void *data, int for_read);
/* Start monitoring WATCH for possible I/O. */
static dbus_bool_t
xd_add_watch (DBusWatch *watch, void *data)
{
unsigned int flags = dbus_watch_get_flags (watch);
int fd = xd_find_watch_fd (watch);
XD_DEBUG_MESSAGE ("fd %d, write %d, enabled %d",
fd, flags & DBUS_WATCH_WRITABLE,
dbus_watch_get_enabled (watch));
if (fd == -1)
return FALSE;
if (dbus_watch_get_enabled (watch))
{
if (flags & DBUS_WATCH_WRITABLE)
add_write_fd (fd, xd_read_queued_messages, data);
if (flags & DBUS_WATCH_READABLE)
add_read_fd (fd, xd_read_queued_messages, data);
}
return TRUE;
}
/* Stop monitoring WATCH for possible I/O.
DATA is the used bus, either a string or QCdbus_system_bus or
QCdbus_session_bus. */
static void
xd_remove_watch (DBusWatch *watch, void *data)
{
unsigned int flags = dbus_watch_get_flags (watch);
int fd = xd_find_watch_fd (watch);
XD_DEBUG_MESSAGE ("fd %d", fd);
if (fd == -1)
return;
/* Unset session environment. */
if (XSYMBOL (QCdbus_session_bus) == data)
{
// XD_DEBUG_MESSAGE ("unsetenv DBUS_SESSION_BUS_ADDRESS");
// unsetenv ("DBUS_SESSION_BUS_ADDRESS");
}
if (flags & DBUS_WATCH_WRITABLE)
delete_write_fd (fd);
if (flags & DBUS_WATCH_READABLE)
delete_read_fd (fd);
}
/* Toggle monitoring WATCH for possible I/O. */
static void
xd_toggle_watch (DBusWatch *watch, void *data)
{
if (dbus_watch_get_enabled (watch))
xd_add_watch (watch, data);
else
xd_remove_watch (watch, data);
}
/* Close connection to D-Bus BUS. */
static void
xd_close_bus (Lisp_Object bus)
{
DBusConnection *connection;
Lisp_Object val;
/* Check whether we are connected. */
val = Fassoc (bus, xd_registered_buses);
if (NILP (val))
return;
/* Retrieve bus address. */
connection = xd_get_connection_address (bus);
/* Close connection, if there isn't another shared application. */
if (xd_get_connection_references (connection) == 1)
{
XD_DEBUG_MESSAGE ("Close connection to bus %s",
XD_OBJECT_TO_STRING (bus));
dbus_connection_close (connection);
}
/* Decrement reference count. */
dbus_connection_unref (connection);
/* Remove bus from list of registered buses. */
xd_registered_buses = Fdelete (val, xd_registered_buses);
/* Return. */
return;
}
DEFUN ("dbus-init-bus", Fdbus_init_bus, Sdbus_init_bus, 1, 2, 0,
doc: /* Establish the connection to D-Bus BUS.
BUS can be either the symbol `:system' or the symbol `:session', or it
can be a string denoting the address of the corresponding bus. For
the system and session buses, this function is called when loading
`dbus.el', there is no need to call it again.
The function returns a number, which counts the connections this Emacs
session has established to the BUS under the same unique name (see
`dbus-get-unique-name'). It depends on the libraries Emacs is linked
with, and on the environment Emacs is running. For example, if Emacs
is linked with the gtk toolkit, and it runs in a GTK-aware environment
like Gnome, another connection might already be established.
When PRIVATE is non-nil, a new connection is established instead of
reusing an existing one. It results in a new unique name at the bus.
This can be used, if it is necessary to distinguish from another
connection used in the same Emacs process, like the one established by
GTK+. It should be used with care for at least the `:system' and
`:session' buses, because other Emacs Lisp packages might already use
this connection to those buses. */)
(Lisp_Object bus, Lisp_Object private)
{
DBusConnection *connection;
DBusError derror;
Lisp_Object val;
int refcount;
/* Check parameter. */
XD_DBUS_VALIDATE_BUS_ADDRESS (bus);
/* Close bus if it is already open. */
xd_close_bus (bus);
/* Initialize. */
dbus_error_init (&derror);
/* Open the connection. */
if (STRINGP (bus))
if (NILP (private))
connection = dbus_connection_open (SSDATA (bus), &derror);
else
connection = dbus_connection_open_private (SSDATA (bus), &derror);
else
if (NILP (private))
connection = dbus_bus_get (EQ (bus, QCdbus_system_bus)
? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION,
&derror);
else
connection = dbus_bus_get_private (EQ (bus, QCdbus_system_bus)
? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION,
&derror);
if (dbus_error_is_set (&derror))
XD_ERROR (derror);
if (connection == NULL)
XD_SIGNAL2 (build_string ("No connection to bus"), bus);
/* If it is not the system or session bus, we must register
ourselves. Otherwise, we have called dbus_bus_get, which has
configured us to exit if the connection closes - we undo this
setting. */
if (STRINGP (bus))
dbus_bus_register (connection, &derror);
else
dbus_connection_set_exit_on_disconnect (connection, FALSE);
if (dbus_error_is_set (&derror))
XD_ERROR (derror);
/* Add the watch functions. We pass also the bus as data, in order
to distinguish between the buses in xd_remove_watch. */
if (!dbus_connection_set_watch_functions (connection,
xd_add_watch,
xd_remove_watch,
xd_toggle_watch,
SYMBOLP (bus)
? (void *) XSYMBOL (bus)
: (void *) XSTRING (bus),
NULL))
XD_SIGNAL1 (build_string ("Cannot add watch functions"));
/* Add bus to list of registered buses. */
XSETFASTINT (val, (intptr_t) connection);
xd_registered_buses = Fcons (Fcons (bus, val), xd_registered_buses);
/* We do not want to abort. */
putenv ((char *) "DBUS_FATAL_WARNINGS=0");
/* Cleanup. */
dbus_error_free (&derror);
/* Return reference counter. */
refcount = xd_get_connection_references (connection);
XD_DEBUG_MESSAGE ("Bus %s, Reference counter %d",
XD_OBJECT_TO_STRING (bus), refcount);
return make_number (refcount);
}
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. */)
(Lisp_Object bus)
{
DBusConnection *connection;
const char *name;
/* Check parameter. */
XD_DBUS_VALIDATE_BUS_ADDRESS (bus);
/* Retrieve bus address. */
connection = xd_get_connection_address (bus);
/* Request the name. */
name = dbus_bus_get_unique_name (connection);
if (name == NULL)
XD_SIGNAL1 (build_string ("No unique name available"));
/* Return. */
return build_string (name);
}
DEFUN ("dbus-message-internal", Fdbus_message_internal, Sdbus_message_internal,
4, MANY, 0,
doc: /* Send a D-Bus message.
This is an internal function, it shall not be used outside dbus.el.
The following usages are expected:
`dbus-call-method', `dbus-call-method-asynchronously':
\(dbus-message-internal
dbus-message-type-method-call BUS SERVICE PATH INTERFACE METHOD HANDLER
&optional :timeout TIMEOUT &rest ARGS)
`dbus-send-signal':
\(dbus-message-internal
dbus-message-type-signal BUS SERVICE PATH INTERFACE SIGNAL &rest ARGS)
`dbus-method-return-internal':
\(dbus-message-internal
dbus-message-type-method-return BUS SERVICE SERIAL &rest ARGS)
`dbus-method-error-internal':
\(dbus-message-internal
dbus-message-type-error BUS SERVICE SERIAL &rest ARGS)
usage: (dbus-message-internal &rest REST) */)
(ptrdiff_t nargs, Lisp_Object *args)
{
Lisp_Object message_type, bus, service, handler;
Lisp_Object path = Qnil;
Lisp_Object interface = Qnil;
Lisp_Object member = Qnil;
Lisp_Object result;
struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5, gcpro6;
DBusConnection *connection;
DBusMessage *dmessage;
DBusMessageIter iter;
unsigned int dtype;
unsigned int mtype;
dbus_uint32_t serial = 0;
unsigned int ui_serial;
int timeout = -1;
ptrdiff_t count;
char signature[DBUS_MAXIMUM_SIGNATURE_LENGTH];
/* Initialize parameters. */
message_type = args[0];
bus = args[1];
service = args[2];
handler = Qnil;
CHECK_NATNUM (message_type);
mtype = XFASTINT (message_type);
if ((mtype <= DBUS_MESSAGE_TYPE_INVALID) || (mtype >= DBUS_NUM_MESSAGE_TYPES))
XD_SIGNAL2 (build_string ("Invalid message type"), message_type);
if ((mtype == DBUS_MESSAGE_TYPE_METHOD_CALL)
|| (mtype == DBUS_MESSAGE_TYPE_SIGNAL))
{
path = args[3];
interface = args[4];
member = args[5];
if (mtype == DBUS_MESSAGE_TYPE_METHOD_CALL)
handler = args[6];
count = (mtype == DBUS_MESSAGE_TYPE_METHOD_CALL) ? 7 : 6;
}
else /* DBUS_MESSAGE_TYPE_METHOD_RETURN, DBUS_MESSAGE_TYPE_ERROR */
{
XD_CHECK_DBUS_SERIAL (args[3], serial);
count = 4;
}
/* Check parameters. */
XD_DBUS_VALIDATE_BUS_ADDRESS (bus);
XD_DBUS_VALIDATE_BUS_NAME (service);
if (nargs < count)
xsignal2 (Qwrong_number_of_arguments,
Qdbus_message_internal,
make_number (nargs));
if ((mtype == DBUS_MESSAGE_TYPE_METHOD_CALL)
|| (mtype == DBUS_MESSAGE_TYPE_SIGNAL))
{
XD_DBUS_VALIDATE_PATH (path);
XD_DBUS_VALIDATE_INTERFACE (interface);
XD_DBUS_VALIDATE_MEMBER (member);
if (!NILP (handler) && (!FUNCTIONP (handler)))
wrong_type_argument (Qinvalid_function, handler);
}
/* Protect Lisp variables. */
GCPRO6 (bus, service, path, interface, member, handler);
/* Trace parameters. */
switch (mtype)
{
case DBUS_MESSAGE_TYPE_METHOD_CALL:
XD_DEBUG_MESSAGE ("%s %s %s %s %s %s %s",
XD_MESSAGE_TYPE_TO_STRING (mtype),
XD_OBJECT_TO_STRING (bus),
XD_OBJECT_TO_STRING (service),
XD_OBJECT_TO_STRING (path),
XD_OBJECT_TO_STRING (interface),
XD_OBJECT_TO_STRING (member),
XD_OBJECT_TO_STRING (handler));
break;
case DBUS_MESSAGE_TYPE_SIGNAL:
XD_DEBUG_MESSAGE ("%s %s %s %s %s %s",
XD_MESSAGE_TYPE_TO_STRING (mtype),
XD_OBJECT_TO_STRING (bus),
XD_OBJECT_TO_STRING (service),
XD_OBJECT_TO_STRING (path),
XD_OBJECT_TO_STRING (interface),
XD_OBJECT_TO_STRING (member));
break;
default: /* DBUS_MESSAGE_TYPE_METHOD_RETURN, DBUS_MESSAGE_TYPE_ERROR */
ui_serial = serial;
XD_DEBUG_MESSAGE ("%s %s %s %u",
XD_MESSAGE_TYPE_TO_STRING (mtype),
XD_OBJECT_TO_STRING (bus),
XD_OBJECT_TO_STRING (service),
ui_serial);
}
/* Retrieve bus address. */
connection = xd_get_connection_address (bus);
/* Create the D-Bus message. */
dmessage = dbus_message_new (mtype);
if (dmessage == NULL)
{
UNGCPRO;
XD_SIGNAL1 (build_string ("Unable to create a new message"));
}
if (STRINGP (service))
{
if (mtype != DBUS_MESSAGE_TYPE_SIGNAL)
/* Set destination. */
{
if (!dbus_message_set_destination (dmessage, SSDATA (service)))
{
UNGCPRO;
XD_SIGNAL2 (build_string ("Unable to set the destination"),
service);
}
}
else
/* Set destination for unicast signals. */
{
Lisp_Object uname;
/* If it is the same unique name as we are registered at the
bus or an unknown name, we regard it as broadcast message
due to backward compatibility. */
if (dbus_bus_name_has_owner (connection, SSDATA (service), NULL))
uname = call2 (intern ("dbus-get-name-owner"), bus, service);
else
uname = Qnil;
if (STRINGP (uname)
&& (strcmp (dbus_bus_get_unique_name (connection), SSDATA (uname))
!= 0)
&& (!dbus_message_set_destination (dmessage, SSDATA (service))))
{
UNGCPRO;
XD_SIGNAL2 (build_string ("Unable to set signal destination"),
service);
}
}
}
/* Set message parameters. */
if ((mtype == DBUS_MESSAGE_TYPE_METHOD_CALL)
|| (mtype == DBUS_MESSAGE_TYPE_SIGNAL))
{
if ((!dbus_message_set_path (dmessage, SSDATA (path)))
|| (!dbus_message_set_interface (dmessage, SSDATA (interface)))
|| (!dbus_message_set_member (dmessage, SSDATA (member))))
{
UNGCPRO;
XD_SIGNAL1 (build_string ("Unable to set the message parameter"));
}
}
else /* DBUS_MESSAGE_TYPE_METHOD_RETURN, DBUS_MESSAGE_TYPE_ERROR */
{
if (!dbus_message_set_reply_serial (dmessage, serial))
{
UNGCPRO;
XD_SIGNAL1 (build_string ("Unable to create a return message"));
}
if ((mtype == DBUS_MESSAGE_TYPE_ERROR)
&& (!dbus_message_set_error_name (dmessage, DBUS_ERROR_FAILED)))
{
UNGCPRO;
XD_SIGNAL1 (build_string ("Unable to create a error message"));
}
}
/* Check for timeout parameter. */
if ((count+2 <= nargs) && (EQ ((args[count]), QCdbus_timeout)))
{
CHECK_NATNUM (args[count+1]);
timeout = XFASTINT (args[count+1]);
count = count+2;
}
/* Initialize parameter list of message. */
dbus_message_iter_init_append (dmessage, &iter);
/* Append parameters to the message. */
for (; count < nargs; ++count)
{
dtype = XD_OBJECT_TO_DBUS_TYPE (args[count]);
if (XD_DBUS_TYPE_P (args[count]))
{
XD_DEBUG_VALID_LISP_OBJECT_P (args[count]);
XD_DEBUG_VALID_LISP_OBJECT_P (args[count+1]);
XD_DEBUG_MESSAGE ("Parameter%"pD"d %s %s", count - 4,
XD_OBJECT_TO_STRING (args[count]),
XD_OBJECT_TO_STRING (args[count+1]));
++count;
}
else
{
XD_DEBUG_VALID_LISP_OBJECT_P (args[count]);
XD_DEBUG_MESSAGE ("Parameter%"pD"d %s", count - 4,
XD_OBJECT_TO_STRING (args[count]));
}
/* 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[count]);
xd_append_arg (dtype, args[count], &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))
{
UNGCPRO;
XD_SIGNAL1 (build_string ("Cannot send message"));
}
/* The result is the key in Vdbus_registered_objects_table. */
serial = dbus_message_get_serial (dmessage);
result = list3 (QCdbus_registered_serial,
bus, make_fixnum_or_float (serial));
/* 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))
{
UNGCPRO;
XD_SIGNAL1 (build_string ("Cannot send message"));
}
result = Qnil;
}
XD_DEBUG_MESSAGE ("Message sent: %s", XD_OBJECT_TO_STRING (result));
/* Cleanup. */
dbus_message_unref (dmessage);
/* Return the result. */
RETURN_UNGCPRO (result);
}
/* Read one queued incoming message of the D-Bus BUS.
BUS is either a Lisp symbol, :system or :session, or a string denoting
the bus address. */
static void
xd_read_message_1 (DBusConnection *connection, Lisp_Object bus)
{
Lisp_Object args, key, value;
struct gcpro gcpro1;
struct input_event event;
DBusMessage *dmessage;
DBusMessageIter iter;
unsigned int dtype;
unsigned int mtype;
dbus_uint32_t serial;
unsigned int ui_serial;
const char *uname, *path, *interface, *member;
dmessage = dbus_connection_pop_message (connection);
/* Return if there is no queued message. */
if (dmessage == NULL)
return;
/* Collect the parameters. */
args = Qnil;
GCPRO1 (args);
/* Loop over the resulting parameters. Construct a list. */
if (dbus_message_iter_init (dmessage, &iter))
{
while ((dtype = dbus_message_iter_get_arg_type (&iter))
!= DBUS_TYPE_INVALID)
{
args = Fcons (xd_retrieve_arg (dtype, &iter), args);
dbus_message_iter_next (&iter);
}
/* The arguments are stored in reverse order. Reorder them. */
args = Fnreverse (args);
}
/* Read message type, message serial, unique name, object path,
interface and member from the message. */
mtype = dbus_message_get_type (dmessage);
ui_serial = 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);
XD_DEBUG_MESSAGE ("Event received: %s %u %s %s %s %s %s",
XD_MESSAGE_TYPE_TO_STRING (mtype),
ui_serial, uname, path, interface, member,
XD_OBJECT_TO_STRING (args));
if (mtype == DBUS_MESSAGE_TYPE_INVALID)
goto cleanup;
else if ((mtype == DBUS_MESSAGE_TYPE_METHOD_RETURN)
|| (mtype == DBUS_MESSAGE_TYPE_ERROR))
{
/* Search for a registered function of the message. */
key = list3 (QCdbus_registered_serial, bus,
make_fixnum_or_float (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);
}
else /* DBUS_MESSAGE_TYPE_METHOD_CALL, DBUS_MESSAGE_TYPE_SIGNAL. */
{
/* 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 = list4 ((mtype == DBUS_MESSAGE_TYPE_METHOD_CALL)
? QCdbus_registered_method
: QCdbus_registered_signal,
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))
{
key = CAR_SAFE (value);
/* key has the structure (UNAME SERVICE PATH HANDLER). */
if (((uname == NULL)
|| (NILP (CAR_SAFE (key)))
|| (strcmp (uname, SSDATA (CAR_SAFE (key))) == 0))
&& ((path == NULL)
|| (NILP (CAR_SAFE (CDR_SAFE (CDR_SAFE (key)))))
|| (strcmp (path,
SSDATA (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);
}
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_fixnum_or_float (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", XD_OBJECT_TO_STRING (event.arg));
/* Cleanup. */
cleanup:
dbus_message_unref (dmessage);
UNGCPRO;
}
/* Read queued incoming messages of the D-Bus BUS.
BUS is either a Lisp symbol, :system or :session, or a string denoting
the bus address. */
static Lisp_Object
xd_read_message (Lisp_Object bus)
{
/* Retrieve bus address. */
DBusConnection *connection = xd_get_connection_address (bus);
/* Non blocking read of the next available message. */
dbus_connection_read_write (connection, 0);
while (dbus_connection_get_dispatch_status (connection)
!= DBUS_DISPATCH_COMPLETE)
xd_read_message_1 (connection, bus);
return Qnil;
}
/* Callback called when something is ready to read or write. */
static void
xd_read_queued_messages (int fd, void *data, int for_read)
{
Lisp_Object busp = xd_registered_buses;
Lisp_Object bus = Qnil;
Lisp_Object key;
/* Find bus related to fd. */
if (data != NULL)
while (!NILP (busp))
{
key = CAR_SAFE (CAR_SAFE (busp));
if ((SYMBOLP (key) && XSYMBOL (key) == data)
|| (STRINGP (key) && XSTRING (key) == data))
bus = key;
busp = CDR_SAFE (busp);
}
if (NILP (bus))
return;
/* We ignore all Lisp errors during the call. */
xd_in_read_queued_messages = 1;
internal_catch (Qdbus_error, xd_read_message, bus);
xd_in_read_queued_messages = 0;
}
void
syms_of_dbusbind (void)
{
DEFSYM (Qdbus_init_bus, "dbus-init-bus");
defsubr (&Sdbus_init_bus);
DEFSYM (Qdbus_get_unique_name, "dbus-get-unique-name");
defsubr (&Sdbus_get_unique_name);
DEFSYM (Qdbus_message_internal, "dbus-message-internal");
defsubr (&Sdbus_message_internal);
DEFSYM (Qdbus_error, "dbus-error");
Fput (Qdbus_error, Qerror_conditions,
list2 (Qdbus_error, Qerror));
Fput (Qdbus_error, Qerror_message,
make_pure_c_string ("D-Bus error"));
DEFSYM (QCdbus_system_bus, ":system");
DEFSYM (QCdbus_session_bus, ":session");
DEFSYM (QCdbus_timeout, ":timeout");
DEFSYM (QCdbus_type_byte, ":byte");
DEFSYM (QCdbus_type_boolean, ":boolean");
DEFSYM (QCdbus_type_int16, ":int16");
DEFSYM (QCdbus_type_uint16, ":uint16");
DEFSYM (QCdbus_type_int32, ":int32");
DEFSYM (QCdbus_type_uint32, ":uint32");
DEFSYM (QCdbus_type_int64, ":int64");
DEFSYM (QCdbus_type_uint64, ":uint64");
DEFSYM (QCdbus_type_double, ":double");
DEFSYM (QCdbus_type_string, ":string");
DEFSYM (QCdbus_type_object_path, ":object-path");
DEFSYM (QCdbus_type_signature, ":signature");
#ifdef DBUS_TYPE_UNIX_FD
DEFSYM (QCdbus_type_unix_fd, ":unix-fd");
#endif
DEFSYM (QCdbus_type_array, ":array");
DEFSYM (QCdbus_type_variant, ":variant");
DEFSYM (QCdbus_type_struct, ":struct");
DEFSYM (QCdbus_type_dict_entry, ":dict-entry");
DEFSYM (QCdbus_registered_serial, ":serial");
DEFSYM (QCdbus_registered_method, ":method");
DEFSYM (QCdbus_registered_signal, ":signal");
DEFVAR_LISP ("dbus-compiled-version",
Vdbus_compiled_version,
doc: /* The version of D-Bus Emacs is compiled against. */);
#ifdef DBUS_VERSION_STRING
Vdbus_compiled_version = make_pure_c_string (DBUS_VERSION_STRING);
#else
Vdbus_compiled_version = Qnil;
#endif
DEFVAR_LISP ("dbus-runtime-version",
Vdbus_runtime_version,
doc: /* The version of D-Bus Emacs runs with. */);
{
#ifdef DBUS_VERSION
int major, minor, micro;
char s[1024];
dbus_get_version (&major, &minor, µ);
snprintf (s, sizeof s, "%d.%d.%d", major, minor, micro);
Vdbus_runtime_version = make_string (s, strlen (s));
#else
Vdbus_runtime_version = Qnil;
#endif
}
DEFVAR_LISP ("dbus-message-type-invalid",
Vdbus_message_type_invalid,
doc: /* This value is never a valid message type. */);
Vdbus_message_type_invalid = make_number (DBUS_MESSAGE_TYPE_INVALID);
DEFVAR_LISP ("dbus-message-type-method-call",
Vdbus_message_type_method_call,
doc: /* Message type of a method call message. */);
Vdbus_message_type_method_call = make_number (DBUS_MESSAGE_TYPE_METHOD_CALL);
DEFVAR_LISP ("dbus-message-type-method-return",
Vdbus_message_type_method_return,
doc: /* Message type of a method return message. */);
Vdbus_message_type_method_return
= make_number (DBUS_MESSAGE_TYPE_METHOD_RETURN);
DEFVAR_LISP ("dbus-message-type-error",
Vdbus_message_type_error,
doc: /* Message type of an error reply message. */);
Vdbus_message_type_error = make_number (DBUS_MESSAGE_TYPE_ERROR);
DEFVAR_LISP ("dbus-message-type-signal",
Vdbus_message_type_signal,
doc: /* Message type of a signal message. */);
Vdbus_message_type_signal = make_number (DBUS_MESSAGE_TYPE_SIGNAL);
DEFVAR_LISP ("dbus-registered-objects-table",
Vdbus_registered_objects_table,
doc: /* Hash table of registered functions for D-Bus.
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 (TYPE BUS
INTERFACE MEMBER). TYPE is one of the Lisp symbols `:method',
`:signal' or `:property'. BUS is either a Lisp symbol, `:system' or
`:session', or a string denoting the bus address. 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 OBJECT [RULE]) ...). SERVICE is the service name as
registered, UNAME is the corresponding 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 (TYPE `:method' and
`:signal'), or a cons cell containing the value of the property (TYPE
`:property').
For entries of type `:signal', there is also a fifth element RULE,
which keeps the match string the signal is registered with.
In the second case, the key in the hash table is the list (:serial BUS
SERIAL). BUS is either a Lisp symbol, `:system' or `:session', or a
string denoting the bus address. 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. */);
{
Lisp_Object args[2];
args[0] = QCtest;
args[1] = Qequal;
Vdbus_registered_objects_table = Fmake_hash_table (2, args);
}
DEFVAR_LISP ("dbus-debug", Vdbus_debug,
doc: /* If non-nil, debug messages of D-Bus bindings are raised. */);
#ifdef DBUS_DEBUG
Vdbus_debug = Qt;
/* We can also set environment variable DBUS_VERBOSE=1 in order to
see more traces. This requires libdbus-1 to be configured with
--enable-verbose-mode. */
#else
Vdbus_debug = Qnil;
#endif
/* Initialize internal objects. */
xd_registered_buses = Qnil;
staticpro (&xd_registered_buses);
Fprovide (intern_c_string ("dbusbind"), Qnil);
}
#endif /* HAVE_DBUS */