diff --git a/configure.ac b/configure.ac index 950086d7..2661aad5 100644 --- a/configure.ac +++ b/configure.ac @@ -379,6 +379,28 @@ if test "x$enable_colord" != "xno"; then fi AM_CONDITIONAL(ENABLE_COLORD, test "x$enable_colord" = "xyes") +# dbus support +AC_ARG_ENABLE(dbus, + AS_HELP_STRING([--disable-dbus], + [do not build with dbus support]),, + enable_dbus=auto) +if test "x$enable_dbus" != "xno"; then + PKG_CHECK_MODULES(DBUS, + dbus-1 >= 1.6, + have_dbus=yes, + have_dbus=no) + if test "x$have_dbus" = "xno" -a "x$enable_dbus" = "xyes"; then + AC_MSG_ERROR([dbus support explicitly requested, but libdbus couldn't be found]) + fi + if test "x$have_dbus" = "xyes"; then + enable_dbus=yes + AC_DEFINE([HAVE_DBUS], [1], [Build with dbus support]) + else + enable_dbus=no + fi +fi +AM_CONDITIONAL(ENABLE_DBUS, test "x$enable_dbus" = "xyes") + AC_ARG_ENABLE(wcap-tools, [ --disable-wcap-tools],, enable_wcap_tools=yes) AM_CONDITIONAL(BUILD_WCAP_TOOLS, test x$enable_wcap_tools = xyes) if test x$enable_wcap_tools = xyes; then @@ -467,6 +489,7 @@ AC_MSG_RESULT([ EGL ${enable_egl} libxkbcommon ${enable_xkbcommon} XWayland ${enable_xwayland} + dbus ${enable_dbus} Build wcap utility ${enable_wcap_tools} Build Tablet Shell ${enable_tablet_shell} diff --git a/src/Makefile.am b/src/Makefile.am index b0eae7c3..8cd7ccaa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -49,6 +49,14 @@ weston_SOURCES = \ weston-launch.h \ weston-egl-ext.h +if ENABLE_DBUS +weston_SOURCES += \ + dbus.h \ + dbus.c +weston_CFLAGS += $(DBUS_CFLAGS) +weston_LDADD += $(DBUS_LIBS) +endif + git-version.h : .FORCE $(AM_V_GEN)(echo "#define BUILD_ID \"$(shell git --git-dir=$(top_srcdir)/.git describe --always --dirty) $(shell git --git-dir=$(top_srcdir)/.git log -1 --format='%s (%ci)')\"" > $@-new; \ cmp -s $@ $@-new || cp $@-new $@; \ diff --git a/src/dbus.c b/src/dbus.c new file mode 100644 index 00000000..4de8c9c4 --- /dev/null +++ b/src/dbus.c @@ -0,0 +1,336 @@ +/* + * Copyright © 2013 David Herrmann + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DBus Helpers + * This file contains the dbus mainloop integration and several helpers to + * make lowlevel libdbus access easier. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compositor.h" +#include "dbus.h" + +/* + * DBus Mainloop Integration + * weston_dbus_bind() and weston_dbus_unbind() allow to bind an existing + * DBusConnection to an existing wl_event_loop object. All dbus dispatching + * is then nicely integrated into the wayland event loop. + * Note that this only provides basic watch and timeout dispatching. No + * remote thread wakeup, signal handling or other dbus insanity is supported. + * This is fine as long as you don't use any of the deprecated libdbus + * interfaces (like waking up remote threads..). There is really no rational + * reason to support these. + */ + +static int weston_dbus_dispatch_watch(int fd, uint32_t mask, void *data) +{ + DBusWatch *watch = data; + uint32_t flags = 0; + + if (dbus_watch_get_enabled(watch)) { + if (mask & WL_EVENT_READABLE) + flags |= DBUS_WATCH_READABLE; + if (mask & WL_EVENT_WRITABLE) + flags |= DBUS_WATCH_WRITABLE; + if (mask & WL_EVENT_HANGUP) + flags |= DBUS_WATCH_HANGUP; + if (mask & WL_EVENT_ERROR) + flags |= DBUS_WATCH_ERROR; + + dbus_watch_handle(watch, flags); + } + + return 0; +} + +static dbus_bool_t weston_dbus_add_watch(DBusWatch *watch, void *data) +{ + struct wl_event_loop *loop = data; + struct wl_event_source *s; + int fd; + uint32_t mask = 0, flags; + + if (dbus_watch_get_enabled(watch)) { + flags = dbus_watch_get_flags(watch); + if (flags & DBUS_WATCH_READABLE) + mask |= WL_EVENT_READABLE; + if (flags & DBUS_WATCH_WRITABLE) + mask |= WL_EVENT_WRITABLE; + } + + fd = dbus_watch_get_unix_fd(watch); + s = wl_event_loop_add_fd(loop, fd, mask, weston_dbus_dispatch_watch, + watch); + if (!s) + return FALSE; + + dbus_watch_set_data(watch, s, NULL); + return TRUE; +} + +static void weston_dbus_remove_watch(DBusWatch *watch, void *data) +{ + struct wl_event_source *s; + + s = dbus_watch_get_data(watch); + if (!s) + return; + + wl_event_source_remove(s); +} + +static void weston_dbus_toggle_watch(DBusWatch *watch, void *data) +{ + struct wl_event_source *s; + uint32_t mask = 0, flags; + + s = dbus_watch_get_data(watch); + if (!s) + return; + + if (dbus_watch_get_enabled(watch)) { + flags = dbus_watch_get_flags(watch); + if (flags & DBUS_WATCH_READABLE) + mask |= WL_EVENT_READABLE; + if (flags & DBUS_WATCH_WRITABLE) + mask |= WL_EVENT_WRITABLE; + } + + wl_event_source_fd_update(s, mask); +} + +static int weston_dbus_dispatch_timeout(void *data) +{ + DBusTimeout *timeout = data; + + if (dbus_timeout_get_enabled(timeout)) + dbus_timeout_handle(timeout); + + return 0; +} + +static int weston_dbus_adjust_timeout(DBusTimeout *timeout, + struct wl_event_source *s) +{ + int64_t t = 0; + + if (dbus_timeout_get_enabled(timeout)) + t = dbus_timeout_get_interval(timeout); + + return wl_event_source_timer_update(s, t); +} + +static dbus_bool_t weston_dbus_add_timeout(DBusTimeout *timeout, void *data) +{ + struct wl_event_loop *loop = data; + struct wl_event_source *s; + int r; + + s = wl_event_loop_add_timer(loop, weston_dbus_dispatch_timeout, + timeout); + if (!s) + return FALSE; + + r = weston_dbus_adjust_timeout(timeout, s); + if (r < 0) { + wl_event_source_remove(s); + return FALSE; + } + + dbus_timeout_set_data(timeout, s, NULL); + return TRUE; +} + +static void weston_dbus_remove_timeout(DBusTimeout *timeout, void *data) +{ + struct wl_event_source *s; + + s = dbus_timeout_get_data(timeout); + if (!s) + return; + + wl_event_source_remove(s); +} + +static void weston_dbus_toggle_timeout(DBusTimeout *timeout, void *data) +{ + struct wl_event_source *s; + + s = dbus_timeout_get_data(timeout); + if (!s) + return; + + weston_dbus_adjust_timeout(timeout, s); +} + +static int weston_dbus_dispatch(int fd, uint32_t mask, void *data) +{ + DBusConnection *c = data; + int r; + + do { + r = dbus_connection_dispatch(c); + if (r == DBUS_DISPATCH_COMPLETE) + r = 0; + else if (r == DBUS_DISPATCH_DATA_REMAINS) + r = -EAGAIN; + else if (r == DBUS_DISPATCH_NEED_MEMORY) + r = -ENOMEM; + else + r = -EIO; + } while (r == -EAGAIN); + + if (r) + weston_log("cannot dispatch dbus events: %d\n", r); + + return 0; +} + +static int weston_dbus_bind(struct wl_event_loop *loop, DBusConnection *c, + struct wl_event_source **ctx_out) +{ + bool b; + int r, fd; + + /* Idle events cannot reschedule themselves, therefore we use a dummy + * event-fd and mark it for post-dispatch. Hence, the dbus + * dispatcher is called after every dispatch-round. + * This is required as dbus doesn't allow dispatching events from + * within its own event sources. */ + fd = eventfd(0, EFD_CLOEXEC); + if (fd < 0) + return -errno; + + *ctx_out = wl_event_loop_add_fd(loop, fd, 0, weston_dbus_dispatch, c); + close(fd); + + if (!*ctx_out) + return -ENOMEM; + + wl_event_source_check(*ctx_out); + + b = dbus_connection_set_watch_functions(c, + weston_dbus_add_watch, + weston_dbus_remove_watch, + weston_dbus_toggle_watch, + loop, + NULL); + if (!b) { + r = -ENOMEM; + goto error; + } + + b = dbus_connection_set_timeout_functions(c, + weston_dbus_add_timeout, + weston_dbus_remove_timeout, + weston_dbus_toggle_timeout, + loop, + NULL); + if (!b) { + r = -ENOMEM; + goto error; + } + + dbus_connection_ref(c); + return 0; + +error: + dbus_connection_set_timeout_functions(c, NULL, NULL, NULL, + NULL, NULL); + dbus_connection_set_watch_functions(c, NULL, NULL, NULL, + NULL, NULL); + wl_event_source_remove(*ctx_out); + *ctx_out = NULL; + return r; +} + +static void weston_dbus_unbind(DBusConnection *c, struct wl_event_source *ctx) +{ + dbus_connection_set_timeout_functions(c, NULL, NULL, NULL, + NULL, NULL); + dbus_connection_set_watch_functions(c, NULL, NULL, NULL, + NULL, NULL); + dbus_connection_unref(c); + wl_event_source_remove(ctx); +} + +/* + * Convenience Helpers + * Several convenience helpers are provided to make using dbus in weston + * easier. We don't use any of the gdbus or qdbus helpers as they pull in + * huge dependencies and actually are quite awful to use. Instead, we only + * use the basic low-level libdbus library. + */ + +int weston_dbus_open(struct wl_event_loop *loop, DBusBusType bus, + DBusConnection **out, struct wl_event_source **ctx_out) +{ + DBusConnection *c; + int r; + + /* Ihhh, global state.. stupid dbus. */ + dbus_connection_set_change_sigpipe(FALSE); + + /* This is actually synchronous. It blocks for some authentication and + * setup. We just trust the dbus-server here and accept this blocking + * call. There is no real reason to complicate things further and make + * this asynchronous/non-blocking. A context should be created during + * thead/process/app setup, so blocking calls should be fine. */ + c = dbus_bus_get_private(bus, NULL); + if (!c) + return -EIO; + + dbus_connection_set_exit_on_disconnect(c, FALSE); + + r = weston_dbus_bind(loop, c, ctx_out); + if (r < 0) + goto error; + + *out = c; + return r; + +error: + dbus_connection_close(c); + dbus_connection_unref(c); + return r; +} + +void weston_dbus_close(DBusConnection *c, struct wl_event_source *ctx) +{ + weston_dbus_unbind(c, ctx); + dbus_connection_close(c); + dbus_connection_unref(c); +} diff --git a/src/dbus.h b/src/dbus.h new file mode 100644 index 00000000..90ab8f61 --- /dev/null +++ b/src/dbus.h @@ -0,0 +1,67 @@ +/* + * Copyright © 2013 David Herrmann + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WESTON_DBUS_H_ +#define _WESTON_DBUS_H_ + +#include "config.h" + +#include +#include + +#include "compositor.h" + +#ifdef HAVE_DBUS + +#include + +/* + * weston_dbus_open() - Open new dbus connection + * + * Opens a new dbus connection to the bus given as @bus. It automatically + * integrates the new connection into the main-loop @loop. The connection + * itself is returned in @out. + * This also returns a context source used for dbus dispatching. It is + * returned on success in @ctx_out and must be passed to weston_dbus_close() + * unchanged. You must not access it from outside of a dbus helper! + * + * Returns 0 on success, negative error code on failure. + */ +int weston_dbus_open(struct wl_event_loop *loop, DBusBusType bus, + DBusConnection **out, struct wl_event_source **ctx_out); + +/* + * weston_dbus_close() - Close dbus connection + * + * Closes a dbus connection that was previously opened via weston_dbus_open(). + * It unbinds the connection from the main-loop it was previously bound to, + * closes the dbus connection and frees all resources. If you want to access + * @c after this call returns, you must hold a dbus-reference to it. But + * notice that the connection is closed after this returns so it cannot be + * used to spawn new dbus requests. + * You must pass the context source returns by weston_dbus_open() as @ctx. + */ +void weston_dbus_close(DBusConnection *c, struct wl_event_source *ctx); + +#endif /* HAVE_DBUS */ + +#endif // _WESTON_DBUS_H_