diff options
author | Ray Strode <rstrode@redhat.com> | 2009-01-05 00:57:39 -0500 |
---|---|---|
committer | Ray Strode <rstrode@redhat.com> | 2009-01-05 01:05:19 -0500 |
commit | d923e259629ee10213d78ded2e5b173084b362e6 (patch) | |
tree | d601c9087ba97a31e58058f4bc623f3020cae51a | |
parent | af9e25c870a69161ff77f961d464da3c771a046b (diff) |
Add a mechanism for exporting methods to the bus
This commit adds a lot of the machinery needed to
convert from a local function/objecct to one that's
remotely invokable. It uses ffi to translate
dbus messages to C function calls, and then sends
the results from those calls back on the as
reply messages.
Right now we only export the singleton object
/org/freedesktop/Wayland with no methods on it's
org.freedesktop.Wayland interface. There
is api in place to add methods, though. We'll
use this api to add a "GetAddress" method that
clients can call to find the server socket.
All registered objects get introspection for
free. This is accomplished by implicitly attaching
the Introspectable interface to the object when
it's registered.
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | dbus.c | 607 | ||||
-rw-r--r-- | wayland-system-compositor.h | 40 |
3 files changed, 648 insertions, 1 deletions
diff --git a/Makefile.in b/Makefile.in index 565ff6b..5d33d92 100644 --- a/Makefile.in +++ b/Makefile.in @@ -28,6 +28,8 @@ $(libs) : $(compositors) $(clients) : CFLAGS += @LIBDRM_CFLAGS@ +wayland-system-compositor : CFLAGS += @FFI_CFLAGS@ +wayland-system-compositor : LDLIBS += @FFI_LIBS@ -ldl -rdynamic wayland-system-compositor : \ wayland-system-compositor.o \ dbus.o \ @@ -17,20 +17,48 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define _GNU_SOURCE +#include <pwd.h> #include <signal.h> #include <stdbool.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#include <ffi.h> #include <dbus/dbus.h> #include "wayland.h" #include "wayland-system-compositor.h" +#define SINGLETON_OBJECT_PATH "/org/freedesktop/Wayland" +#define SINGLETON_OBJECT_INTERFACE "org.freedesktop.Wayland" +#define MAX(a, b) ((a) > (b)? (a): (b)) + +struct wlsc_dbus_interface_ref { + struct wlsc_dbus_interface *interface; + struct wl_list link; +}; + +struct wlsc_dbus_object { + char *path; + struct wl_list interface_list; + uint32_t is_opaque : 1; +}; + +struct wlsc_dbus_object_ref { + struct wlsc_dbus_object *object; + struct wl_list link; +}; + struct wlsc_dbus { DBusConnection *connection; struct wl_event_loop *loop; struct wl_event_source *dispatch_source; + struct wl_list object_list; + struct wlsc_dbus_object *singleton_object; }; static DBusHandlerResult wlsc_dbus_filter(DBusConnection *connection, DBusMessage *message, void *data) @@ -45,6 +73,103 @@ static DBusHandlerResult wlsc_dbus_filter(DBusConnection *connection, DBusMessag return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } +#define MAX_METHODS_FOR_SINGLETON 1 +static struct wlsc_dbus_method singleton_object_methods[MAX_METHODS_FOR_SINGLETON] = { +}; + +static struct wlsc_dbus_interface singleton_object_interface = { + SINGLETON_OBJECT_INTERFACE, 0, singleton_object_methods, +}; + +static char *introspect(struct wlsc_dbus_object *object) +{ + struct wlsc_dbus_interface_ref *ref; + int byte_count; + ssize_t buffer_size; + char *buffer; + + /* we do a dry run once to compute the size then come back + * after allocating the buffer. It's a little groaty. We + * could really use a dynamic string api + */ + buffer = NULL; + buffer_size = 0; +fill_buffer: + byte_count = 0; + byte_count += snprintf(buffer + byte_count, MAX(buffer_size - byte_count, 0), "%s", DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); + byte_count += snprintf(buffer + byte_count, MAX(buffer_size - byte_count, 0), "<node>\n"); + ref = container_of(object->interface_list.next, + struct wlsc_dbus_interface_ref, link); + while (&ref->link != &object->interface_list) { + struct wlsc_dbus_interface *interface; + int i; + + interface = ref->interface; + byte_count += snprintf(buffer + byte_count, MAX(buffer_size - byte_count, 0), + " <interface name=\"%s\">\n", + interface->name); + for (i = 0; i < interface->method_count; i++) { + struct wlsc_dbus_method *method; + int j; + method = &interface->methods[i]; + + if (method->name == buffer + byte_count) + continue; + + byte_count += snprintf(buffer + byte_count, MAX(buffer_size - byte_count, 0), " <method name=\"%s\">\n", interface->methods[i].name); + for (j = 0; j < method->in_parameter_count; j++) { + struct wlsc_dbus_method_parameter *parameter; + parameter = &method->in_parameters[j]; + byte_count += snprintf(buffer + byte_count, MAX(buffer_size - byte_count, 0), + " <arg name=\"%s\" direction=\"in\" type=\"%s\"/>\n", + parameter->name, parameter->signature); + } + + for (j = 0; j < method->out_parameter_count; j++) { + struct wlsc_dbus_method_parameter *parameter; + parameter = &method->out_parameters[j]; + byte_count += snprintf(buffer + byte_count, MAX(buffer_size - byte_count, 0), + " <arg name=\"%s\" direction=\"out\" type=\"%s\"/>\n", + parameter->name, parameter->signature); + } + byte_count += snprintf(buffer + byte_count, MAX(buffer_size - byte_count, 0), " </method>\n"); + } + byte_count += snprintf(buffer + byte_count, MAX(buffer_size - byte_count, 0), + " </interface>\n"); + + ref = container_of(ref->link.next, + struct wlsc_dbus_interface_ref, link); + } + byte_count += snprintf(buffer + byte_count, MAX(buffer_size - byte_count, 0), "</node>\n"); + + if (buffer == NULL) { + buffer_size = byte_count + 1; + buffer = malloc(buffer_size); + buffer[byte_count] = '\0'; + goto fill_buffer; + } + + return buffer; +} + +static struct wlsc_dbus_method_parameter introspect_out_parameters[] = { + { "introspection_xml", "s" } +}; + +static struct wlsc_dbus_method introspectable_methods[] = { + { "Introspect", + NULL, 0, + introspect_out_parameters, ARRAY_LENGTH(introspect_out_parameters), + (wlsc_dbus_method_handler_t) introspect + } +}; + +static struct wlsc_dbus_interface introspectable_interface = { + DBUS_INTERFACE_INTROSPECTABLE , + ARRAY_LENGTH(introspectable_methods), + introspectable_methods +}; + static void on_watch_data(int fd, uint32_t mask, void *data) { DBusWatch *watch = data; @@ -204,10 +329,425 @@ static void on_dispatch_status_changed(DBusConnection *connection, DBusDispatchS } } +static void unregister_object(DBusConnection *connection, void *user_data) +{ + struct wlsc_dbus *dbus = user_data; +} + +static struct wlsc_dbus_method *wlsc_dbus_interface_lookup_method(struct wlsc_dbus_interface *interface, const char *method_name) +{ + int i; + for (i = 0; i < interface->method_count; i++) { + if (interface->methods[i].name == NULL) + continue; + if (strcmp(interface->methods[i].name, method_name) == 0) + return &interface->methods[i]; + } + return NULL; +} + +static struct wlsc_dbus_interface *wlsc_dbus_object_lookup_interface(struct wlsc_dbus_object *object, const char *interface_name) +{ + struct wlsc_dbus_interface_ref *ref; + + ref = container_of(object->interface_list.next, + struct wlsc_dbus_interface_ref, link); + while (&ref->link != &object->interface_list) { + + if (strcmp(ref->interface->name, interface_name) == 0) + return ref->interface; + + ref = container_of(ref->link.next, + struct wlsc_dbus_interface_ref, link); + } + + return NULL; +} + +static struct wlsc_dbus_object *wlsc_dbus_lookup_object(struct wlsc_dbus *dbus, const char *path) +{ + struct wlsc_dbus_object_ref *ref; + + ref = container_of(dbus->object_list.next, + struct wlsc_dbus_object_ref, link); + fprintf(stderr, "ref->link %p, object_list %p\n", &ref->link, &dbus->object_list); + while (&ref->link != &dbus->object_list) { + + fprintf(stderr, "have %s need %s\n", ref->object->path, path); + if (strcmp(ref->object->path, path) == 0) + return ref->object; + + ref = container_of(ref->link.next, + struct wlsc_dbus_object_ref, link); + } + + return NULL; +} + +static ffi_type * +get_ffi_type_from_basic_dbus_type(int dbus_type) +{ + struct { + int dbus_type; + ffi_type *ffi_type; + } ffi_dbus_map[] = { + /* FIXME: handle all basic types */ + { DBUS_TYPE_INVALID, &ffi_type_void }, + { DBUS_TYPE_STRING, &ffi_type_pointer }, + { DBUS_TYPE_ARRAY, &ffi_type_pointer }, + { -1, NULL } + }; + int i; + + for (i = 0; ffi_dbus_map[i].dbus_type != -1; i++) { + if (ffi_dbus_map[i].dbus_type == dbus_type) + return ffi_dbus_map[i].ffi_type; + } + + return NULL; +} + +static int +get_argument_count_for_method(struct wlsc_dbus_method *method) +{ + int i; + int argument_count; + + argument_count = 0; + + /* we use strlen as a shortcut because we currently only support + * basic types and 1d arrays. For basic types there is a 1 to 1 + * correspondance between signature bytes and args (e.g., "i" -> int). + * For arrays there is a 2 to 2 correspondance + * (e.g., "ai" -> int *, unsigned long len) + */ + for (i = 0; i < method->in_parameter_count; i++) { + argument_count += strlen(method->in_parameters[i].signature); + } + + for (i = 0; i < method->out_parameter_count; i++) { + argument_count += strlen(method->out_parameters[i].signature); + } + + return argument_count; +} + +/* This function demarshals the passed in dbus method call message, + * marshals it back up in a form libffi can use, invokes the passed in + * method using libffi, marshals the out arguments into a dbus reply + * message and returns that reply. It's not complete, it only handles the + * subset of stuff that's needed right now. + * + * The convention is something like + * + * first_out_argument function(in args..., *remaining out args..., object) + * + * If this function is called on the global singleton object, then it hides + * the object argument and instead passes the method user_data. + * + * With the exception of arrays, each type in the dbus signature maps to + * one argument in the call. Arrays map to two arguments: type *data, + * unsigned long *length. + */ +static DBusMessage *wlsc_dbus_object_invoke_method_from_message(struct wlsc_dbus *dbus, struct wlsc_dbus_object *object, struct wlsc_dbus_method *method, DBusMessage *message) +{ + DBusMessage *reply; + DBusSignatureIter out_signature_iter; + DBusMessageIter message_iter; + ffi_cif cif; + ffi_arg return_value; + ffi_type *return_type; + ffi_type **parameter_types; + void **arguments; + void **out_argument_pointers; + int argument_count; + int dbus_type; + int i, first_out_argument; + + /* count the number of in and out arguments from the method description */ + argument_count = get_argument_count_for_method(method); + + if (method->out_parameter_count > 0) { + /* use first out parameter for return value */ + dbus_signature_iter_init(&out_signature_iter, method->out_parameters[0].signature); + dbus_type = dbus_signature_iter_get_current_type(&out_signature_iter); + return_type = get_ffi_type_from_basic_dbus_type(dbus_type); + argument_count--; + + /* the allocation size is an approximation (but an upper bound), since + * one dbus array can expand into 2 ffi arguments */ + out_argument_pointers = malloc(2 * method->out_parameter_count * sizeof (void *)); + } else { + return_type = NULL; + out_argument_pointers = NULL; + } + + parameter_types = malloc((argument_count + 1) * sizeof *parameter_types); + arguments = malloc((argument_count + 1) * sizeof *arguments); + + i = 0; + /* convert message arguments to in args that ffi can use */ + dbus_message_iter_init(message, &message_iter); + while ((dbus_type = dbus_message_iter_get_arg_type(&message_iter)) != DBUS_TYPE_INVALID) { + parameter_types[i] = get_ffi_type_from_basic_dbus_type(dbus_type); + dbus_message_iter_get_basic(&message_iter, &arguments[i]); + i++; + dbus_message_iter_next(&message_iter); + } + + /* next add some pointers for the out args */ + first_out_argument = i; + while (i < argument_count) { + parameter_types[i] = &ffi_type_pointer; + arguments[i] = &out_argument_pointers[i]; + i++; + } + + /* and finally, the object (or method user data if the object is opaque + * to the user) */ + parameter_types[i] = &ffi_type_pointer; + if (object->is_opaque && method->private_data != NULL) + arguments[i] = &method->private_data; + else + arguments[i] = &object; + i++; + + ffi_prep_cif(&cif, FFI_DEFAULT_ABI, argument_count + 1, return_type, parameter_types); + ffi_call(&cif, method->function, &return_value, arguments); + reply = dbus_message_new_method_return(message); + dbus_message_iter_init_append(reply, &message_iter); + + i = first_out_argument; + if (method->out_parameter_count > 0) { + int j = 0; + + dbus_signature_iter_init(&out_signature_iter, method->out_parameters[j].signature); + j++; + + dbus_type = dbus_signature_iter_get_current_type(&out_signature_iter); + if (dbus_type == DBUS_TYPE_ARRAY) { + DBusMessageIter array_iter; + DBusSignatureIter array_signature_iter; + int array_type; + char *array_signature; + const void *array; + unsigned long array_length; + dbus_signature_iter_recurse(&out_signature_iter, &array_signature_iter); + array_type = dbus_signature_iter_get_current_type(&array_signature_iter); + array_signature = dbus_signature_iter_get_signature(&array_signature_iter); + dbus_message_iter_open_container(&message_iter, DBUS_TYPE_ARRAY, + array_signature, + &array_iter); + dbus_free(array_signature); + array = (void *) return_value; + array_length = *(unsigned long *) out_argument_pointers[i - first_out_argument]; + i++; + dbus_message_iter_append_fixed_array(&array_iter, array_type, &array, array_length); + dbus_message_iter_close_container(&message_iter, &array_iter); + } else if (dbus_type_is_basic(dbus_type)) { + dbus_message_iter_append_basic(&message_iter, dbus_type, &return_value); + } + + while (j < method->out_parameter_count) { + dbus_signature_iter_init(&out_signature_iter, method->out_parameters[j].signature); + j++; + + dbus_type = dbus_signature_iter_get_current_type(&out_signature_iter); + if (dbus_type == DBUS_TYPE_ARRAY) { + DBusMessageIter array_iter; + DBusSignatureIter array_signature_iter; + int array_type; + char *array_signature; + const void *array; + unsigned long array_length; + dbus_signature_iter_recurse(&out_signature_iter, &array_signature_iter); + array_type = dbus_signature_iter_get_current_type(&array_signature_iter); + array_signature = dbus_signature_iter_get_signature(&array_signature_iter); + dbus_message_iter_open_container(&message_iter, DBUS_TYPE_ARRAY, + array_signature, + &array_iter); + dbus_free(array_signature); + array = (void *) out_argument_pointers[i - first_out_argument]; + i++; + array_length = *(unsigned long *) out_argument_pointers[i - first_out_argument]; + i++; + dbus_message_iter_append_fixed_array(&array_iter, array_type, &array, array_length); + dbus_message_iter_close_container(&message_iter, &array_iter); + } else if (dbus_type_is_basic(dbus_type)) { + dbus_message_iter_append_basic(&message_iter, dbus_type, &arguments[i]); + i++; + } + } + } + + free(parameter_types); + free(arguments); + free(out_argument_pointers); + return reply; +} + +static bool wlsc_dbus_method_has_signature(struct wlsc_dbus_method *method, const char *signature) +{ + char *method_signature; + bool has_signature; + int i; + size_t offset; + + if (signature[0] == '\0' || signature == NULL) { + return method->in_parameter_count == 0; + } + + method_signature = malloc(2 * method->in_parameter_count); + method_signature[0] = '\0'; + offset = 0; + for (i = 0; i < method->in_parameter_count; i++) { + offset += snprintf(method_signature + offset, 2, + method->in_parameters[i].signature); + } + + has_signature = strcmp(method_signature, signature) == 0; + free(method_signature); + + return has_signature; +} + +static DBusHandlerResult on_message(DBusConnection *connection, DBusMessage *message, void *user_data) +{ + struct wlsc_dbus *dbus = user_data; + const char *object_path, *interface_name, *method_name; + struct wlsc_dbus_object *object; + struct wlsc_dbus_interface *interface; + struct wlsc_dbus_method *method; + DBusError error; + DBusMessage *reply = NULL; + unsigned long sender_uid; + + /* FIXME: we need to defer some of the guts of this function until + * we're able to ask PolicyKit if the user is allowed to talk to us. + * At an absolute minimum we should delay work until we asynchronously + * call GetConnetionUnixUser instead of using the blocking function below. + */ + dbus_error_init(&error); + sender_uid = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error); + dbus_error_free(&error); + + if (sender_uid != getuid()) { + struct passwd *passwd; + char *us = NULL, *them = NULL; + + passwd = getpwuid(getuid()); + if (passwd != NULL && passwd->pw_name != NULL) + asprintf(&us, "user '%s'", passwd->pw_name); + else + asprintf(&us, "uid %lu", (unsigned long) getuid()); + + passwd = getpwuid(sender_uid); + if (passwd != NULL && passwd->pw_name != NULL) + asprintf(&them, "user '%s'", passwd->pw_name); + else + asprintf(&them, "uid %lu", sender_uid); + + reply = dbus_message_new_error_printf(message, + DBUS_ERROR_ACCESS_DENIED, + "must be %s not %s", + us, them); + free(us); + free(them); + goto done; + } + + object_path = dbus_message_get_path(message); + + if (object_path == NULL) { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + object = wlsc_dbus_lookup_object(dbus, object_path); + + if (object == NULL) { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + interface_name = dbus_message_get_interface(message); + interface = wlsc_dbus_object_lookup_interface(object, interface_name); + + if (interface == NULL) { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + method_name = dbus_message_get_member(message); + method = wlsc_dbus_interface_lookup_method(interface, method_name); + + if (method == NULL) { + reply = dbus_message_new_error_printf(message, + DBUS_ERROR_UNKNOWN_METHOD, + "unknown method '%s'", method_name); + goto done; + } + + if (!wlsc_dbus_method_has_signature(method, dbus_message_get_signature(message))) { + reply = dbus_message_new_error_printf(message, + DBUS_ERROR_INVALID_ARGS, + "method '%s' called with incorrect arguments", method_name); + goto done; + } + + reply = wlsc_dbus_object_invoke_method_from_message(dbus, object, method, message); +done: + if (reply != NULL) { + dbus_connection_send(dbus->connection, reply, NULL); + dbus_message_unref(reply); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static struct DBusObjectPathVTable dbus_object_vtable = { + unregister_object, + on_message +}; + +struct wlsc_dbus_object *wlsc_dbus_object_create(void) +{ + struct wlsc_dbus_object *object; + + object = malloc(sizeof *object); + if (object == NULL) + return NULL; + + object->path = NULL; + wl_list_init(&object->interface_list); + + object->is_opaque = false; + + return object; +} + +void wlsc_dbus_object_set_path(struct wlsc_dbus_object *object, const char *path) +{ + free(object->path); + object->path = strdup(path); +} + +void wlsc_dbus_object_register_interface(struct wlsc_dbus_object *object, struct wlsc_dbus_interface *interface) +{ + struct wlsc_dbus_interface_ref *ref; + + ref = malloc(sizeof *ref); + + if (ref == NULL) { + return; + } + + ref->interface = interface; + wl_list_insert(object->interface_list.prev, &ref->link); +} + struct wlsc_dbus * wlsc_dbus_init(struct wl_event_loop *loop) { struct wlsc_dbus *dbus; + struct wlsc_dbus_object_ref *ref; DBusError error; dbus = malloc(sizeof *dbus); @@ -248,11 +788,32 @@ wlsc_dbus_init(struct wl_event_loop *loop) dbus->dispatch_source = wl_event_loop_add_idle(dbus->loop, dispatch_idle, dbus); if (!dbus_connection_add_filter(dbus->connection, - wlsc_dbus_filter, dbus, NULL)) + wlsc_dbus_filter, dbus, NULL)) { + free(dbus); fprintf(stderr, "failed to add filter\n"); + return NULL; + } + + dbus_connection_register_object_path(dbus->connection, + SINGLETON_OBJECT_PATH, + &dbus_object_vtable, + dbus); dbus_connection_set_exit_on_disconnect(dbus->connection, false); + wl_list_init(&dbus->object_list); + + dbus->singleton_object = wlsc_dbus_object_create(); + dbus->singleton_object->is_opaque = true; + + wlsc_dbus_object_set_path(dbus->singleton_object, SINGLETON_OBJECT_PATH); + wlsc_dbus_object_register_interface(dbus->singleton_object, &singleton_object_interface); + wlsc_dbus_object_register_interface(dbus->singleton_object, &introspectable_interface); + + ref = malloc(sizeof *ref); + ref->object = dbus->singleton_object; + wl_list_insert(dbus->object_list.prev, &ref->link); + return dbus; } @@ -279,3 +840,47 @@ bool wlsc_dbus_take_name(struct wlsc_dbus *dbus, const char *name, bool should_r return result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER; } + +void wlsc_dbus_register_method(struct wlsc_dbus *dbus, struct wlsc_dbus_method *method, void *user_data) +{ + struct wlsc_dbus_interface *interface; + + interface = wlsc_dbus_object_lookup_interface(dbus->singleton_object, SINGLETON_OBJECT_INTERFACE); + + if (interface->method_count >= MAX_METHODS_FOR_SINGLETON) { + fprintf(stderr, "dbus interface '"SINGLETON_OBJECT_INTERFACE"' has maximum number of methods registered\n"); + return; + } + + interface->methods[interface->method_count] = *method; + interface->methods[interface->method_count].private_data = user_data; + interface->method_count++; +} + +void wlsc_dbus_register_object(struct wlsc_dbus *dbus, const char *path, struct wlsc_dbus_object *object) +{ + struct wlsc_dbus_object *old_object; + struct wlsc_dbus_object_ref *ref; + + old_object = wlsc_dbus_lookup_object(dbus, object->path); + + if (old_object != NULL) { + fprintf(stderr, "dbus object '%s' is already registered\n", object->path); + return; + } + + ref = malloc(sizeof *ref); + + if (ref == NULL) { + return; + } + + ref->object = object; + wl_list_insert(dbus->object_list.prev, &ref->link); + + dbus_connection_register_object_path(dbus->connection, + object->path, + &dbus_object_vtable, + dbus); +} + diff --git a/wayland-system-compositor.h b/wayland-system-compositor.h index 4b1b01e..f8d5fa0 100644 --- a/wayland-system-compositor.h +++ b/wayland-system-compositor.h @@ -29,6 +29,46 @@ wlsc_dbus_init(struct wl_event_loop *loop); bool wlsc_dbus_take_name(struct wlsc_dbus *dbus, const char *name, bool should_replace); +typedef void (* wlsc_dbus_method_handler_t) (void); + +struct wlsc_dbus_method_parameter { + const char *name; + const char *signature; +}; + +struct wlsc_dbus_method { + const char *name; + struct wlsc_dbus_method_parameter *in_parameters; + int in_parameter_count; + struct wlsc_dbus_method_parameter *out_parameters; + int out_parameter_count; + wlsc_dbus_method_handler_t function; + void *private_data; +}; + +struct wlsc_dbus_interface { + const char *name; + int method_count; + struct wlsc_dbus_method *methods; +}; + +struct wlsc_dbus_object; + +struct wlsc_dbus_object * +wlsc_dbus_object_create(void); + +void +wlsc_dbus_object_set_path(struct wlsc_dbus_object *object, const char *path); + +void +wlsc_dbus_object_register_interface(struct wlsc_dbus_object *object, struct wlsc_dbus_interface *interface); + +void +wlsc_dbus_register_object(struct wlsc_dbus *dbus, const char *path, struct wlsc_dbus_object *object); + +void +wlsc_dbus_register_method(struct wlsc_dbus *dbus, struct wlsc_dbus_method *method, void *user_data); + struct wlsc_input_device; void wlsc_device_get_position(struct wlsc_input_device *device, int32_t *x, int32_t *y); |