incremental
diff --git a/boost-dbus/.clang-format b/boost-dbus/.clang-format
new file mode 100644
index 0000000..6f1017f
--- /dev/null
+++ b/boost-dbus/.clang-format
@@ -0,0 +1,98 @@
+---
+BasedOnStyle: Google
+AccessModifierOffset: -1
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: true
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: true
+AlwaysBreakTemplateDeclarations: true
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterClass: false
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: false
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Attach
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: true
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeCategories:
+
+ - Regex: '^[<"](crow)'
+ Priority: 5
+ - Regex: '^[<"](boost)'
+ Priority: 6
+ - Regex: '^[<"](gtest|gmock)'
+ Priority: 7
+ - Regex: '^<.*\.h>'
+ Priority: 1
+ - Regex: '^<.*\.hpp>'
+ Priority: 2
+ - Regex: '^<.*'
+ Priority: 3
+ - Regex: '.*'
+ Priority: 4
+IndentCaseLabels: true
+IndentWidth: 2
+IndentWrappedFunctionNames: false
+KeepEmptyLinesAtTheStartOfBlocks: false
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: false
+PenaltyBreakBeforeFirstCallParameter: 1
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 200
+PointerAlignment: Left
+ReflowComments: true
+SortIncludes: true
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Auto
+TabWidth: 8
+UseTab: Never
+...
+
diff --git a/boost-dbus/.gitignore b/boost-dbus/.gitignore
new file mode 100644
index 0000000..378eac2
--- /dev/null
+++ b/boost-dbus/.gitignore
@@ -0,0 +1 @@
+build
diff --git a/boost-dbus/CMakeLists.txt b/boost-dbus/CMakeLists.txt
new file mode 100644
index 0000000..9d07a5b
--- /dev/null
+++ b/boost-dbus/CMakeLists.txt
@@ -0,0 +1,50 @@
+# Copyright (c) Benjamin Kietzman (github.com/bkietz)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+project(boost-dbus CXX)
+
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+###############
+# CMake options
+cmake_minimum_required(VERSION 2.8)
+
+###############
+# C++ options
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")#-std=c++0x")
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/test)
+
+###############
+# import Boost
+find_package(Boost REQUIRED)
+include_directories(${Boost_INCLUDE_DIRS})
+link_directories(${Boost_LIBRARY_DIRS})
+
+###############
+# import D-Bus
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(DBus dbus-1)
+include_directories(${DBus_INCLUDE_DIRS})
+link_directories(${DBus_LIBRARY_DIRS})
+
+##############
+# import GTest
+find_package(GTest REQUIRED)
+include_directories(${GTEST_INCLUDE_DIRS})
+
+##############
+# Tests
+enable_testing()
+
+
+add_executable(dbustests "test/avahi.cpp" "test/message.cpp")
+target_link_libraries(dbustests ${Boost_LIBRARIES})
+target_link_libraries(dbustests ${DBus_LIBRARIES})
+target_link_libraries(dbustests ${GTEST_BOTH_LIBRARIES} gmock)
+target_link_libraries(dbustests -pthread)
+add_test(dbustests dbustests "--gtest_output=xml:${test_name}.xml")
+
diff --git a/boost-dbus/LICENSE_1_0.txt b/boost-dbus/LICENSE_1_0.txt
new file mode 100644
index 0000000..36b7cd9
--- /dev/null
+++ b/boost-dbus/LICENSE_1_0.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/boost-dbus/README.md b/boost-dbus/README.md
new file mode 100644
index 0000000..0887854
--- /dev/null
+++ b/boost-dbus/README.md
@@ -0,0 +1,55 @@
+Boost D-Bus
+===========
+
+This is a simple D-Bus binding powered by Boost.Asio.
+As far as possible, I try to follow Asio's idioms.
+
+Code Sample
+-----------
+
+```c++
+#include <iostream>
+
+#include <boost/asio.hpp>
+#include <dbus.hpp>
+
+using namespace std;
+using namespace boost::asio;
+using boost::system::error_code;
+
+struct logger
+{
+ void operator()(error_code ec, message m)
+ {
+ cout << m << endl;
+ }
+};
+
+void main()
+{
+ io_service io;
+ dbus::proxy avahi(io,
+ dbus::endpoint(
+ "org.freedesktop.Avahi", // proxied object process
+ "/", // proxied object path
+ "org.freedesktop.Avahi.Server")); // interface
+
+ dbus::message browser_spec(-1, -1,
+ "_http._tcp", "local", unsigned(0));
+
+ dbus::message response =
+ avahi.call("ServiceBrowserNew", browser_spec);
+
+ dbus::proxy browser(io,
+ dbus::endpoint(
+ "org.freedesktop.Avahi",
+ response.get(0),
+ "org.freedesktop.Avahi.ServiceBrowser"));
+
+ browser.async_receive("ItemNew", logger());
+
+ io.run();
+}
+
+
+```
diff --git a/boost-dbus/include/dbus/connection.hpp b/boost-dbus/include/dbus/connection.hpp
new file mode 100644
index 0000000..a7185a5
--- /dev/null
+++ b/boost-dbus/include/dbus/connection.hpp
@@ -0,0 +1,130 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_CONNECTION_HPP
+#define DBUS_CONNECTION_HPP
+
+#include <dbus/connection_service.hpp>
+#include <dbus/element.hpp>
+#include <dbus/message.hpp>
+#include <chrono>
+#include <string>
+#include <boost/asio.hpp>
+
+namespace dbus {
+
+class filter;
+class match;
+
+/// Root D-Bus IO object
+/**
+ * A connection to a bus, through which messages may be sent or received.
+ */
+class connection : public boost::asio::basic_io_object<connection_service> {
+ public:
+ /// Open a connection to a specified address.
+ /**
+ * @param io_service The io_service object that the connection will use to
+ * wire D-Bus for asynchronous operation.
+ *
+ * @param address The address of the bus to connect to.
+ *
+ * @throws boost::system::system_error When opening the connection failed.
+ */
+ connection(boost::asio::io_service& io, const string& address)
+ : basic_io_object<connection_service>(io) {
+ this->get_service().open(this->get_implementation(), address);
+ }
+
+ /// Open a connection to a well-known bus.
+ /**
+ * D-Bus connections are usually opened to well-known buses like the
+ * system or session bus.
+ *
+ * @param bus The well-known bus to connect to.
+ *
+ * @throws boost::system::system_error When opening the connection failed.
+ */
+ // TODO: change this unsigned to an enumeration
+ connection(boost::asio::io_service& io, const int bus)
+ : basic_io_object<connection_service>(io) {
+ this->get_service().open(this->get_implementation(), bus);
+ }
+
+ /// Send a message.
+ /**
+ * @param m The message to send.
+ *
+ * @return The reply received.
+ *
+ * @throws boost::system::system_error When the response timed out or
+ * there was some other error.
+ */
+ message send(message& m) {
+ return this->get_service().send(this->get_implementation(), m);
+ }
+
+ /// Send a message.
+ /**
+ * @param m The message to send.
+ *
+ * @param t Time to wait for a reply. Passing 0 as the timeout means
+ * that you wish to ignore the reply. (Or catch it later somehow...)
+ *
+ * @return The reply received.
+ *
+ * @throws boost::system::system_error When the response timed out (if
+ * timeout was not 0), or there was some other error.
+ */
+ template <typename Duration>
+ message send(message& m, const Duration& t) {
+ return this->get_service().send(this->get_implementation(), m, t);
+ }
+
+ /// Send a message asynchronously.
+ /**
+ * @param m The message to send.
+ *
+ * @param handler Handler for the reply.
+ *
+ * @return Asynchronous result
+ */
+ template <typename MessageHandler>
+ inline BOOST_ASIO_INITFN_RESULT_TYPE(MessageHandler,
+ void(boost::system::error_code, message))
+ async_send(message& m, BOOST_ASIO_MOVE_ARG(MessageHandler) handler) {
+ return this->get_service().async_send(
+ this->get_implementation(), m,
+ BOOST_ASIO_MOVE_CAST(MessageHandler)(handler));
+ }
+
+ /// Create a new match.
+ void new_match(match& m) {
+ this->get_service().new_match(this->get_implementation(), m);
+ }
+
+ /// Destroy a match.
+ void delete_match(match& m) {
+ this->get_service().delete_match(this->get_implementation(), m);
+ }
+
+ /// Create a new filter.
+ void new_filter(filter& f) {
+ this->get_service().new_filter(this->get_implementation(), f);
+ }
+
+ /// Destroy a filter.
+ void delete_filter(filter& f) {
+ this->get_service().delete_filter(this->get_implementation(), f);
+ }
+
+ // FIXME the only way around this I see is to expose start() here, which seems
+ // ugly
+ friend class filter;
+};
+
+} // namespace dbus
+
+#endif // DBUS_CONNECTION_HPP
diff --git a/boost-dbus/include/dbus/connection_service.hpp b/boost-dbus/include/dbus/connection_service.hpp
new file mode 100644
index 0000000..28318c6
--- /dev/null
+++ b/boost-dbus/include/dbus/connection_service.hpp
@@ -0,0 +1,105 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_CONNECTION_SERVICE_HPP
+#define DBUS_CONNECTION_SERVICE_HPP
+
+#include <boost/asio.hpp>
+#include <boost/asio/io_service.hpp>
+
+#include <dbus/detail/async_send_op.hpp>
+#include <dbus/element.hpp>
+#include <dbus/error.hpp>
+#include <dbus/message.hpp>
+
+#include <dbus/impl/connection.ipp>
+
+namespace dbus {
+namespace bus {
+static const int session = DBUS_BUS_SESSION;
+static const int system = DBUS_BUS_SYSTEM;
+static const int starter = DBUS_BUS_STARTER;
+} // namespace bus
+
+class filter;
+class match;
+class connection;
+
+class connection_service : public boost::asio::detail::service_base<connection_service> {
+ public:
+ typedef impl::connection implementation_type;
+
+ inline explicit connection_service(boost::asio::io_service& io)
+ : boost::asio::detail::service_base<connection_service>(io) {}
+
+ inline void construct(implementation_type& impl) {}
+
+ inline void destroy(implementation_type& impl) {}
+
+ inline void shutdown_service() {
+ // TODO is there anything that needs shutting down?
+ }
+
+ inline void open(implementation_type& impl, const string& address) {
+ boost::asio::io_service& io = this->get_io_service();
+
+ impl.open(io, address);
+ }
+
+ inline void open(implementation_type& impl, const int bus = bus::system) {
+ boost::asio::io_service& io = this->get_io_service();
+
+ impl.open(io, bus);
+ }
+
+ inline message send(implementation_type& impl, message& m) {
+ return impl.send_with_reply_and_block(m);
+ }
+
+ template <typename Duration>
+ inline message send(implementation_type& impl, message& m, const Duration& timeout) {
+ if (timeout == Duration::zero()) {
+ // TODO this can return false if it failed
+ impl.send(m);
+ return message();
+ } else {
+ return impl.send_with_reply_and_block(
+ m, std::chrono::milliseconds(timeout).count());
+ }
+ }
+
+ template <typename MessageHandler>
+ inline BOOST_ASIO_INITFN_RESULT_TYPE(MessageHandler,
+ void(boost::system::error_code, message))
+ async_send(implementation_type& impl, message& m,
+ BOOST_ASIO_MOVE_ARG(MessageHandler) handler) {
+ // begin asynchronous operation
+ impl.start(this->get_io_service());
+
+ boost::asio::detail::async_result_init<
+ MessageHandler, void(boost::system::error_code, message)>
+ init(BOOST_ASIO_MOVE_CAST(MessageHandler)(handler));
+ detail::async_send_op<typename boost::asio::handler_type<
+ MessageHandler, void(boost::system::error_code, message)>::type>(
+ this->get_io_service(),
+ BOOST_ASIO_MOVE_CAST(MessageHandler)(init.handler))(impl, m);
+
+ return init.result.get();
+ }
+
+ private:
+ friend connection;
+ inline void new_match(implementation_type& impl, match& m);
+
+ inline void delete_match(implementation_type& impl, match& m);
+
+ inline void new_filter(implementation_type& impl, filter& f);
+
+ inline void delete_filter(implementation_type& impl, filter& f);
+};
+
+} // namespace dbus
+
+#endif // DBUS_CONNECTION_SERVICE_HPP
diff --git a/boost-dbus/include/dbus/detail/async_send_op.hpp b/boost-dbus/include/dbus/detail/async_send_op.hpp
new file mode 100644
index 0000000..996a4e7
--- /dev/null
+++ b/boost-dbus/include/dbus/detail/async_send_op.hpp
@@ -0,0 +1,78 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_ASYNC_SEND_OP_HPP
+#define DBUS_ASYNC_SEND_OP_HPP
+
+#include <boost/scoped_ptr.hpp>
+
+#include <dbus/dbus.h>
+#include <dbus/error.hpp>
+#include <dbus/message.hpp>
+
+#include <dbus/impl/connection.ipp>
+
+namespace dbus {
+namespace detail {
+
+template <typename MessageHandler>
+struct async_send_op {
+ boost::asio::io_service& io_;
+ message message_;
+ MessageHandler handler_;
+ async_send_op(boost::asio::io_service& io,BOOST_ASIO_MOVE_ARG(MessageHandler) handler);
+ static void callback(DBusPendingCall* p, void* userdata); // for C API
+ void operator()(impl::connection& c, message& m); // initiate operation
+ void operator()(); // bound completion handler form
+};
+
+template <typename MessageHandler>
+async_send_op<MessageHandler>::async_send_op(boost::asio::io_service& io,BOOST_ASIO_MOVE_ARG(MessageHandler)handler)
+ : io_(io), handler_(BOOST_ASIO_MOVE_CAST(MessageHandler)(handler)) {}
+
+template <typename MessageHandler>
+void async_send_op<MessageHandler>::operator()(impl::connection& c,
+ message& m) {
+ DBusPendingCall* p;
+ c.send_with_reply(m, &p, -1);
+
+ // We have to throw this onto the heap so that the
+ // C API can store it as `void *userdata`
+ async_send_op* op =
+ new async_send_op(BOOST_ASIO_MOVE_CAST(async_send_op)(*this));
+
+ dbus_pending_call_set_notify(p, &callback, op, NULL);
+
+ // FIXME Race condition: another thread might have
+ // processed the pending call's reply before a notify
+ // function could be set. If so, the notify function
+ // will never trigger, so it must be called manually:
+ if (dbus_pending_call_get_completed(p)) {
+ // TODO: does this work, or might it call the notify
+ // function too many times? Might have to use steal_reply
+ // callback(p, op);
+ }
+}
+
+template <typename MessageHandler>
+void async_send_op<MessageHandler>::callback(DBusPendingCall* p,
+ void* userdata) {
+ boost::scoped_ptr<async_send_op> op(static_cast<async_send_op*>(userdata));
+
+ op->message_ = dbus_pending_call_steal_reply(p);
+ dbus_pending_call_unref(p);
+
+ op->io_.post(BOOST_ASIO_MOVE_CAST(async_send_op)(*op));
+}
+
+template <typename MessageHandler>
+void async_send_op<MessageHandler>::operator()() {
+ handler_(error(message_).error_code(), message_);
+}
+
+} // namespace detail
+} // namespace dbus
+
+#endif // DBUS_ASYNC_SEND_OP_HPP
diff --git a/boost-dbus/include/dbus/detail/queue.hpp b/boost-dbus/include/dbus/detail/queue.hpp
new file mode 100644
index 0000000..c435af3
--- /dev/null
+++ b/boost-dbus/include/dbus/detail/queue.hpp
@@ -0,0 +1,98 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_QUEUE_HPP
+#define DBUS_QUEUE_HPP
+
+#include <deque>
+#include <functional>
+#include <boost/asio.hpp>
+#include <boost/asio/detail/mutex.hpp>
+
+namespace dbus {
+namespace detail {
+
+template <typename Message>
+class queue {
+ public:
+ typedef ::boost::asio::detail::mutex mutex_type;
+ typedef Message message_type;
+ typedef std::function<void(boost::system::error_code, Message)> handler_type;
+
+ private:
+ boost::asio::io_service& io;
+ mutex_type mutex;
+ std::deque<message_type> messages;
+ std::deque<handler_type> handlers;
+
+ public:
+ queue(boost::asio::io_service& io_service) : io(io_service) {}
+
+ private:
+ class closure {
+ handler_type handler_;
+ message_type message_;
+ boost::system::error_code error_;
+
+ public:
+ void operator()() { handler_(error_, message_); }
+ closure(BOOST_ASIO_MOVE_ARG(handler_type) h, Message m,
+ boost::system::error_code e = boost::system::error_code())
+ : handler_(h), message_(m), error_(e) {}
+ };
+
+ public:
+ void push(message_type m) {
+ mutex_type::scoped_lock lock(mutex);
+ if (handlers.empty())
+ messages.push_back(m);
+ else {
+ handler_type h = handlers.front();
+ handlers.pop_front();
+
+ lock.unlock();
+
+ io.post(closure(BOOST_ASIO_MOVE_CAST(handler_type)(h), m));
+ }
+ }
+
+ template <typename MessageHandler>
+ inline BOOST_ASIO_INITFN_RESULT_TYPE(MessageHandler,
+ void(boost::system::error_code,
+ message_type))
+ async_pop(BOOST_ASIO_MOVE_ARG(MessageHandler) h) {
+ typedef ::boost::asio::detail::async_result_init<
+ MessageHandler, void(boost::system::error_code, message_type)>
+ init_type;
+
+ mutex_type::scoped_lock lock(mutex);
+ if (messages.empty()) {
+ init_type init(BOOST_ASIO_MOVE_CAST(MessageHandler)(h));
+
+ handlers.push_back(init.handler);
+
+ lock.unlock();
+
+ return init.result.get();
+
+ } else {
+ message_type m = messages.front();
+ messages.pop_front();
+
+ lock.unlock();
+
+ init_type init(BOOST_ASIO_MOVE_CAST(MessageHandler)(h));
+
+ io.post(closure(BOOST_ASIO_MOVE_CAST(handler_type)(init.handler), m));
+
+ return init.result.get();
+ }
+ }
+};
+
+} // namespace detail
+} // namespace dbus
+
+#endif // DBUS_QUEUE_HPP
diff --git a/boost-dbus/include/dbus/detail/watch_timeout.hpp b/boost-dbus/include/dbus/detail/watch_timeout.hpp
new file mode 100644
index 0000000..ef2e708
--- /dev/null
+++ b/boost-dbus/include/dbus/detail/watch_timeout.hpp
@@ -0,0 +1,151 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_WATCH_TIMEOUT_HPP
+#define DBUS_WATCH_TIMEOUT_HPP
+
+#include <dbus/dbus.h>
+#include <boost/asio/generic/stream_protocol.hpp>
+#include <boost/asio/steady_timer.hpp>
+
+#include <chrono>
+
+namespace dbus {
+namespace detail {
+
+static void watch_toggled(DBusWatch *dbus_watch, void *data);
+struct watch_handler {
+ DBusWatchFlags flags;
+ DBusWatch *dbus_watch;
+ watch_handler(DBusWatchFlags f, DBusWatch *w) : flags(f), dbus_watch(w) {}
+ void operator()(boost::system::error_code ec, size_t) {
+ if (ec) return;
+ dbus_watch_handle(dbus_watch, flags);
+
+ boost::asio::generic::stream_protocol::socket &socket = *static_cast<boost::asio::generic::stream_protocol::socket *>(
+ dbus_watch_get_data(dbus_watch));
+
+ watch_toggled(dbus_watch, &socket.get_io_service());
+ }
+};
+static void watch_toggled(DBusWatch *dbus_watch, void *data) {
+ boost::asio::generic::stream_protocol::socket &socket =
+ *static_cast<boost::asio::generic::stream_protocol::socket *>(dbus_watch_get_data(dbus_watch));
+
+ if (dbus_watch_get_enabled(dbus_watch)) {
+ if (dbus_watch_get_flags(dbus_watch) & DBUS_WATCH_READABLE)
+ socket.async_read_some(boost::asio::null_buffers(),
+ watch_handler(DBUS_WATCH_READABLE, dbus_watch));
+
+ if (dbus_watch_get_flags(dbus_watch) & DBUS_WATCH_WRITABLE)
+ socket.async_write_some(boost::asio::null_buffers(),
+ watch_handler(DBUS_WATCH_WRITABLE, dbus_watch));
+
+ } else {
+ socket.cancel();
+ }
+}
+
+static dbus_bool_t add_watch(DBusWatch *dbus_watch, void *data) {
+ if (!dbus_watch_get_enabled(dbus_watch)) return TRUE;
+
+ boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
+
+ int fd = dbus_watch_get_unix_fd(dbus_watch);
+
+ if (fd == -1)
+ // socket based watches
+ fd = dbus_watch_get_socket(dbus_watch);
+
+ boost::asio::generic::stream_protocol::socket &socket = *new boost::asio::generic::stream_protocol::socket(io);
+
+ socket.assign(boost::asio::generic::stream_protocol(0, 0), fd);
+
+ dbus_watch_set_data(dbus_watch, &socket, NULL);
+
+ watch_toggled(dbus_watch, &io);
+ return TRUE;
+}
+
+static void remove_watch(DBusWatch *dbus_watch, void *data) {
+ delete static_cast<boost::asio::generic::stream_protocol::socket *>(
+ dbus_watch_get_data(dbus_watch));
+}
+
+struct timeout_handler {
+ DBusTimeout *dbus_timeout;
+ timeout_handler(DBusTimeout *t) : dbus_timeout(t) {}
+ void operator()(boost::system::error_code ec) {
+ if (ec) return;
+ dbus_timeout_handle(dbus_timeout);
+ }
+};
+
+static void timeout_toggled(DBusTimeout *dbus_timeout, void *data) {
+ boost::asio::steady_timer &timer =
+ *static_cast<boost::asio::steady_timer *>(dbus_timeout_get_data(dbus_timeout));
+
+ if (dbus_timeout_get_enabled(dbus_timeout)) {
+ boost::asio::steady_timer::duration interval =
+ std::chrono::milliseconds(dbus_timeout_get_interval(dbus_timeout));
+ timer.expires_from_now(interval);
+ timer.cancel();
+ timer.async_wait(timeout_handler(dbus_timeout));
+ } else {
+ timer.cancel();
+ }
+}
+
+static dbus_bool_t add_timeout(DBusTimeout *dbus_timeout, void *data) {
+ if (!dbus_timeout_get_enabled(dbus_timeout)) return TRUE;
+
+ boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
+
+ boost::asio::steady_timer &timer = *new boost::asio::steady_timer(io);
+
+ dbus_timeout_set_data(dbus_timeout, &timer, NULL);
+
+ timeout_toggled(dbus_timeout, &io);
+ return TRUE;
+}
+
+static void remove_timeout(DBusTimeout *dbus_timeout, void *data) {
+ delete static_cast<boost::asio::steady_timer *>(dbus_timeout_get_data(dbus_timeout));
+}
+
+struct dispatch_handler {
+ boost::asio::io_service &io;
+ DBusConnection *conn;
+ dispatch_handler(boost::asio::io_service &i, DBusConnection *c)
+ : io(i), conn(c) {}
+ void operator()() {
+ if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS)
+ io.post(dispatch_handler(io, conn));
+ }
+};
+
+static void dispatch_status(DBusConnection *conn, DBusDispatchStatus new_status,
+ void *data) {
+ boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
+ if (new_status == DBUS_DISPATCH_DATA_REMAINS)
+ io.post(dispatch_handler(io, conn));
+}
+
+static void set_watch_timeout_dispatch_functions(DBusConnection *conn,
+ boost::asio::io_service &io) {
+ dbus_connection_set_watch_functions(conn, &add_watch, &remove_watch,
+ &watch_toggled, &io, NULL);
+
+ dbus_connection_set_timeout_functions(conn, &add_timeout, &remove_timeout,
+ &timeout_toggled, &io, NULL);
+
+ dbus_connection_set_dispatch_status_function(conn, &dispatch_status, &io,
+ NULL);
+}
+
+} // namespace detail
+} // namespace dbus
+
+#endif // DBUS_WATCH_TIMEOUT_HPP
diff --git a/boost-dbus/include/dbus/element.hpp b/boost-dbus/include/dbus/element.hpp
new file mode 100644
index 0000000..276b593
--- /dev/null
+++ b/boost-dbus/include/dbus/element.hpp
@@ -0,0 +1,182 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_ELEMENT_HPP
+#define DBUS_ELEMENT_HPP
+
+#include <dbus/dbus.h>
+#include <string>
+#include <boost/cstdint.hpp>
+
+namespace dbus {
+
+/// Message elements
+/**
+ * D-Bus Messages are composed of simple elements of one of these types
+ */
+// bool // is this simply valid? It might pack wrong...
+// http://maemo.org/api_refs/5.0/5.0-final/dbus/api/group__DBusTypes.html
+typedef boost::uint8_t byte;
+
+typedef boost::int16_t int16;
+typedef boost::uint16_t uint16;
+typedef boost::int32_t int32;
+typedef boost::uint32_t uint32;
+
+typedef boost::int64_t int64;
+typedef boost::uint64_t uint64;
+// double
+// unix_fd
+
+typedef std::string string;
+struct object_path {
+ string value;
+};
+struct signature {
+ string value;
+};
+
+/// Traits template for message elements
+/**
+ * D-Bus Message elements are identified by unique integer type codes.
+ */
+template <typename InvalidType>
+struct element {
+ static const int code = DBUS_TYPE_INVALID;
+};
+
+template <>
+struct element<bool> {
+ static const int code = DBUS_TYPE_BOOLEAN;
+};
+
+template <>
+struct element<byte> {
+ static const int code = DBUS_TYPE_BYTE;
+};
+
+template <>
+struct element<int16> {
+ static const int code = DBUS_TYPE_INT16;
+};
+
+template <>
+struct element<uint16> {
+ static const int code = DBUS_TYPE_UINT16;
+};
+
+template <>
+struct element<int32> {
+ static const int code = DBUS_TYPE_INT32;
+};
+
+template <>
+struct element<uint32> {
+ static const int code = DBUS_TYPE_UINT32;
+};
+
+template <>
+struct element<int64> {
+ static const int code = DBUS_TYPE_INT64;
+};
+
+template <>
+struct element<uint64> {
+ static const int code = DBUS_TYPE_UINT64;
+};
+
+template <>
+struct element<double> {
+ static const int code = DBUS_TYPE_DOUBLE;
+};
+
+template <>
+struct element<string> {
+ static const int code = DBUS_TYPE_STRING;
+};
+
+template <>
+struct element<object_path> {
+ static const int code = DBUS_TYPE_OBJECT_PATH;
+};
+
+template <>
+struct element<signature> {
+ static const int code = DBUS_TYPE_SIGNATURE;
+};
+
+template <typename InvalidType>
+struct is_fixed_type {
+ static const int value = false;
+};
+
+template <>
+struct is_fixed_type<bool> {
+ static const int value = true;
+};
+
+template <>
+struct is_fixed_type<byte> {
+ static const int value = true;
+};
+
+template <>
+struct is_fixed_type<int16> {
+ static const int value = true;
+};
+
+template <>
+struct is_fixed_type<uint16> {
+ static const int value = true;
+};
+
+template <>
+struct is_fixed_type<int32> {
+ static const int value = true;
+};
+
+template <>
+struct is_fixed_type<uint32> {
+ static const int value = true;
+};
+
+template <>
+struct is_fixed_type<int64> {
+ static const int value = true;
+};
+
+template <>
+struct is_fixed_type<uint64> {
+ static const int value = true;
+};
+
+template <>
+struct is_fixed_type<double> {
+ static const int value = true;
+};
+
+template <typename InvalidType>
+struct is_string_type {
+ static const bool value = false;
+};
+
+template <>
+struct is_string_type<string> {
+ static const bool value = true;
+};
+
+template <>
+struct is_string_type<object_path> {
+ static const bool value = true;
+};
+
+template <>
+struct is_string_type<signature> {
+ static const bool value = true;
+};
+
+} // namespace dbus
+
+#endif // DBUS_ELEMENT_HPP
diff --git a/boost-dbus/include/dbus/endpoint.hpp b/boost-dbus/include/dbus/endpoint.hpp
new file mode 100644
index 0000000..a574e8f
--- /dev/null
+++ b/boost-dbus/include/dbus/endpoint.hpp
@@ -0,0 +1,34 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_ENDPOINT_HPP
+#define DBUS_ENDPOINT_HPP
+
+#include <dbus/dbus.h>
+#include <dbus/element.hpp>
+#include <dbus/message.hpp>
+
+namespace dbus {
+
+class endpoint {
+ string process_name_;
+ string path_;
+ string interface_;
+
+ public:
+ endpoint(const string& process_name, const string& path,
+ const string& interface)
+ : process_name_(process_name), path_(path), interface_(interface) {}
+
+ const string& get_path() const { return path_; }
+
+ const string& get_interface() const { return interface_; }
+
+ const string& get_process_name() const { return process_name_; }
+};
+
+} // namespace dbus
+
+#endif // DBUS_ENDPOINT_HPP
diff --git a/boost-dbus/include/dbus/error.hpp b/boost-dbus/include/dbus/error.hpp
new file mode 100644
index 0000000..3b07c9f
--- /dev/null
+++ b/boost-dbus/include/dbus/error.hpp
@@ -0,0 +1,64 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_ERROR_HPP
+#define DBUS_ERROR_HPP
+
+#include <dbus/dbus.h>
+#include <dbus/element.hpp>
+#include <dbus/message.hpp>
+#include <boost/system/error_code.hpp>
+#include <boost/system/system_error.hpp>
+
+namespace dbus {
+
+class error : public boost::system::error_category {
+ DBusError error_;
+
+ public:
+ error() { dbus_error_init(&error_); }
+
+ error(DBusError *src) {
+ dbus_error_init(&error_);
+ dbus_move_error(src, &error_);
+ }
+
+ error(dbus::message &m) {
+ dbus_error_init(&error_);
+ dbus_set_error_from_message(&error_, m);
+ }
+
+ ~error() { dbus_error_free(&error_); }
+
+ const char *name() const BOOST_SYSTEM_NOEXCEPT { return error_.name; }
+
+ string message(int value) const { return error_.message; }
+
+ bool is_set() const { return dbus_error_is_set(&error_); }
+
+ operator const DBusError *() const { return &error_; }
+
+ operator DBusError *() { return &error_; }
+
+ boost::system::error_code error_code() const;
+ boost::system::system_error system_error() const;
+ void throw_if_set() const;
+};
+
+inline boost::system::error_code error::error_code() const {
+ return boost::system::error_code(is_set(), *this);
+}
+
+inline boost::system::system_error error::system_error() const {
+ return boost::system::system_error(error_code());
+}
+
+inline void error::throw_if_set() const {
+ if (is_set()) throw system_error();
+}
+
+} // namespace dbus
+
+#endif // DBUS_ERROR_HPP
diff --git a/boost-dbus/include/dbus/filter.hpp b/boost-dbus/include/dbus/filter.hpp
new file mode 100644
index 0000000..5d60d33
--- /dev/null
+++ b/boost-dbus/include/dbus/filter.hpp
@@ -0,0 +1,56 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_FILTER_HPP
+#define DBUS_FILTER_HPP
+
+#include <dbus/connection.hpp>
+#include <dbus/detail/queue.hpp>
+#include <dbus/message.hpp>
+#include <functional>
+#include <boost/asio.hpp>
+
+namespace dbus {
+
+/// Represents a filter of incoming messages.
+/**
+ * Filters examine incoming messages, demuxing them to multiple queues.
+ */
+class filter {
+ connection& connection_;
+ std::function<bool(message&)> predicate_;
+ detail::queue<message> queue_;
+
+ public:
+ bool offer(message& m) {
+ bool filtered = predicate_(m);
+ if (filtered) queue_.push(m);
+ return filtered;
+ }
+
+ template <typename MessagePredicate>
+ filter(connection& c, BOOST_ASIO_MOVE_ARG(MessagePredicate) p)
+ : connection_(c),
+ predicate_(BOOST_ASIO_MOVE_CAST(MessagePredicate)(p)),
+ queue_(connection_.get_io_service()) {
+ connection_.new_filter(*this);
+ }
+
+ ~filter() { connection_.delete_filter(*this); }
+
+ template <typename MessageHandler>
+ inline BOOST_ASIO_INITFN_RESULT_TYPE(MessageHandler,
+ void(boost::system::error_code, message))
+ async_dispatch(BOOST_ASIO_MOVE_ARG(MessageHandler) handler) {
+ // begin asynchronous operation
+ connection_.get_implementation().start(connection_.get_io_service());
+
+ return queue_.async_pop(BOOST_ASIO_MOVE_CAST(MessageHandler)(handler));
+ }
+};
+} // namespace dbus
+
+#include <dbus/impl/filter.ipp>
+#endif // DBUS_FILTER_HPP
diff --git a/boost-dbus/include/dbus/impl/connection.ipp b/boost-dbus/include/dbus/impl/connection.ipp
new file mode 100644
index 0000000..24a257d
--- /dev/null
+++ b/boost-dbus/include/dbus/impl/connection.ipp
@@ -0,0 +1,102 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_CONNECTION_IPP
+#define DBUS_CONNECTION_IPP
+
+#include <dbus/dbus.h>
+#include <dbus/detail/watch_timeout.hpp>
+
+#include <boost/atomic.hpp>
+
+namespace dbus {
+namespace impl {
+
+class connection {
+ public:
+ boost::atomic<bool> is_paused;
+ DBusConnection* conn;
+
+ connection() : is_paused(true), conn(NULL) {}
+
+ void open(boost::asio::io_service& io, int bus) {
+ error e;
+ conn = dbus_bus_get_private((DBusBusType)bus, e);
+ e.throw_if_set();
+
+ dbus_connection_set_exit_on_disconnect(conn, false);
+
+ detail::set_watch_timeout_dispatch_functions(conn, io);
+ }
+
+ void open(boost::asio::io_service& io, const string& address) {
+ error e;
+ conn = dbus_connection_open_private(address.c_str(), e);
+ e.throw_if_set();
+
+ dbus_bus_register(conn, e);
+ e.throw_if_set();
+
+ dbus_connection_set_exit_on_disconnect(conn, false);
+
+ detail::set_watch_timeout_dispatch_functions(conn, io);
+ }
+
+ ~connection() {
+ if (conn != NULL) {
+ dbus_connection_close(conn);
+ dbus_connection_unref(conn);
+ }
+ }
+
+ operator DBusConnection*() { return conn; }
+ operator const DBusConnection*() const { return conn; }
+
+ message send_with_reply_and_block(message& m,
+ int timeout_in_milliseconds = -1) {
+ error e;
+ DBusMessage* out = dbus_connection_send_with_reply_and_block(
+ conn, m, timeout_in_milliseconds, e);
+ e.throw_if_set();
+ message reply(out);
+
+ return reply;
+ }
+
+ void send(message& m) {
+ // ignoring message serial for now
+ dbus_connection_send(conn, m, NULL);
+ }
+
+ void send_with_reply(message& m, DBusPendingCall** p,
+ int timeout_in_milliseconds = -1) {
+ dbus_connection_send_with_reply(conn, m, p, timeout_in_milliseconds);
+ }
+
+ // begin asynchronous operation
+ // FIXME should not get io from an argument
+ void start(boost::asio::io_service& io) {
+ bool old_value(true);
+ if (is_paused.compare_exchange_strong(old_value, false)) {
+ // If two threads call connection::async_send()
+ // simultaneously on a paused connection, then
+ // only one will pass the CAS instruction and
+ // only one dispatch_handler will be injected.
+ io.post(detail::dispatch_handler(io, conn));
+ }
+ }
+
+ void cancel(boost::asio::io_service& io) {
+ bool old_value(false);
+ if (is_paused.compare_exchange_strong(old_value, true)) {
+ // TODO
+ }
+ }
+};
+
+} // namespace impl
+} // namespace dbus
+
+#endif // DBUS_CONNECTION_IPP
diff --git a/boost-dbus/include/dbus/impl/filter.ipp b/boost-dbus/include/dbus/impl/filter.ipp
new file mode 100644
index 0000000..a64d6fd
--- /dev/null
+++ b/boost-dbus/include/dbus/impl/filter.ipp
@@ -0,0 +1,39 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_FILTER_IPP
+#define DBUS_FILTER_IPP
+
+namespace dbus {
+namespace impl {
+
+inline DBusHandlerResult filter_callback(DBusConnection* c, DBusMessage* m,
+ void* userdata) {
+ try {
+ filter& f = *static_cast<filter*>(userdata);
+ message m_(m);
+ if (f.offer(m_)) {
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ } catch (...) {
+ // do not throw in C callbacks. Just don't.
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+} // namespace impl
+
+void connection_service::new_filter(implementation_type& impl, filter& f) {
+ dbus_connection_add_filter(impl, &impl::filter_callback, &f, NULL);
+}
+
+void connection_service::delete_filter(implementation_type& impl, filter& f) {
+ dbus_connection_remove_filter(impl, &impl::filter_callback, &f);
+}
+
+} // namespace dbus
+
+#endif // DBUS_FILTER_IPP
diff --git a/boost-dbus/include/dbus/impl/match.ipp b/boost-dbus/include/dbus/impl/match.ipp
new file mode 100644
index 0000000..9f6a5da
--- /dev/null
+++ b/boost-dbus/include/dbus/impl/match.ipp
@@ -0,0 +1,26 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_MATCH_IPP
+#define DBUS_MATCH_IPP
+
+namespace dbus {
+void connection_service::new_match(implementation_type& impl, match& m) {
+ error e;
+ dbus_bus_add_match(impl, m.get_expression().c_str(), e);
+ e.throw_if_set();
+ // eventually, for complete asynchronicity, this should connect to
+ // org.freedesktop.DBus and call AddMatch
+}
+
+void connection_service::delete_match(implementation_type& impl, match& m) {
+ error e;
+ dbus_bus_remove_match(impl, m.get_expression().c_str(), e);
+ e.throw_if_set();
+}
+
+} // namespace dbus
+
+#endif // DBUS_MATCH_IPP
diff --git a/boost-dbus/include/dbus/impl/message_iterator.hpp b/boost-dbus/include/dbus/impl/message_iterator.hpp
new file mode 100644
index 0000000..44bcf2e
--- /dev/null
+++ b/boost-dbus/include/dbus/impl/message_iterator.hpp
@@ -0,0 +1,50 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_IMPL_MESSAGE_ITERATOR_HPP
+#define DBUS_IMPL_MESSAGE_ITERATOR_HPP
+
+#include <dbus/dbus.h>
+
+namespace dbus {
+
+class message;
+
+namespace impl {
+
+class message_iterator {
+ DBusMessageIter DBusMessageIter_;
+
+ public:
+ // writing
+ static void init_append(message &m, message_iterator &i);
+
+ void append_basic(int code, const void *value);
+
+ void open_container(int code, const char *signature, message_iterator &);
+ void close_container(message_iterator &);
+ void abandon_container(message_iterator &);
+
+ void append_fixed_array(int code, const void *value, int n_elements);
+
+ // reading
+ static bool init(message &m, message_iterator &i);
+
+ bool next();
+ bool has_next();
+ int get_arg_type();
+
+ void get_basic(void *value);
+
+ void recurse(message_iterator &);
+
+ int get_element_type();
+ void get_fixed_array(void *value, int *n_elements);
+};
+
+} // namespace impl
+} // namespace dbus
+
+#endif // DBUS_IMPL_MESSAGE_ITERATOR_HPP
diff --git a/boost-dbus/include/dbus/impl/message_iterator.ipp b/boost-dbus/include/dbus/impl/message_iterator.ipp
new file mode 100644
index 0000000..eb2584f
--- /dev/null
+++ b/boost-dbus/include/dbus/impl/message_iterator.ipp
@@ -0,0 +1,89 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_IMPL_MESSAGE_ITERATOR_IPP
+#define DBUS_IMPL_MESSAGE_ITERATOR_IPP
+
+#include <dbus/impl/message_iterator.hpp>
+
+namespace dbus {
+namespace impl {
+
+inline void message_iterator::init_append(message& m, message_iterator& i)
+{
+ dbus_message_iter_init_append(m, &i.DBusMessageIter_);
+}
+inline void message_iterator::append_basic(int code, const void *value)
+{
+ // returns false if not enough memory- throw bad_alloc
+ dbus_message_iter_append_basic(&DBusMessageIter_, code, value);
+}
+inline void message_iterator::open_container(int code, const char *signature, message_iterator& sub)
+{
+ // returns false if not enough memory- throw bad_alloc
+ dbus_message_iter_open_container(&DBusMessageIter_, code, signature, &sub.DBusMessageIter_);
+}
+
+inline void message_iterator::close_container(message_iterator& sub)
+{
+ // returns false if not enough memory- throw bad_alloc
+ dbus_message_iter_close_container(&DBusMessageIter_, &sub.DBusMessageIter_);
+}
+
+inline void message_iterator::abandon_container(message_iterator& sub)
+{
+ dbus_message_iter_abandon_container(&DBusMessageIter_, &sub.DBusMessageIter_);
+}
+
+inline void message_iterator::append_fixed_array(int code, const void *value, int n_elements)
+{
+ // returns false if not enough memory- throw bad_alloc
+ dbus_message_iter_append_fixed_array(&DBusMessageIter_, code, value, n_elements);
+}
+
+inline bool message_iterator::init(message& m, message_iterator& i)
+{
+ return dbus_message_iter_init(m, &i.DBusMessageIter_);
+}
+
+inline bool message_iterator::next()
+{
+ return dbus_message_iter_next(&DBusMessageIter_);
+}
+
+inline bool message_iterator::has_next()
+{
+ return dbus_message_iter_has_next(&DBusMessageIter_);
+}
+
+inline int message_iterator::get_arg_type()
+{
+ return dbus_message_iter_get_arg_type(&DBusMessageIter_);
+}
+
+inline void message_iterator::get_basic(void *value)
+{
+ dbus_message_iter_get_basic(&DBusMessageIter_, value);
+}
+
+inline void message_iterator::recurse(message_iterator& sub)
+{
+ dbus_message_iter_recurse(&DBusMessageIter_, &sub.DBusMessageIter_);
+}
+
+inline int message_iterator::get_element_type()
+{
+ return dbus_message_iter_get_element_type(&DBusMessageIter_);
+}
+
+inline void message_iterator::get_fixed_array(void *value, int *n_elements)
+{
+ dbus_message_iter_get_fixed_array(&DBusMessageIter_, value, n_elements);
+}
+
+} // namespace impl
+} // namespace dbus
+
+#endif // DBUS_IMPL_MESSAGE_ITERATOR_IPP
diff --git a/boost-dbus/include/dbus/match.hpp b/boost-dbus/include/dbus/match.hpp
new file mode 100644
index 0000000..0488aa0
--- /dev/null
+++ b/boost-dbus/include/dbus/match.hpp
@@ -0,0 +1,46 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_MATCH_HPP
+#define DBUS_MATCH_HPP
+
+#include <string>
+#include <boost/asio.hpp>
+
+#include <dbus/connection.hpp>
+#include <dbus/error.hpp>
+
+namespace dbus {
+
+/// Simple placeholder object for a match rule.
+/**
+ * A match rule determines what messages will be received by this application.
+ *
+ * Each rule will be represented by an instance of match. To remove that rule,
+ * dispose of the object.
+ */
+class match {
+ connection& connection_;
+ std::string expression_;
+
+ public:
+ match(connection& c, BOOST_ASIO_MOVE_ARG(std::string) e)
+ : connection_(c), expression_(BOOST_ASIO_MOVE_CAST(std::string)(e)) {
+ connection_.new_match(*this);
+ }
+
+ ~match() { connection_.delete_match(*this); }
+
+ const std::string& get_expression() const { return expression_; }
+
+ match(match&&) = delete;
+ match& operator=(match&&) = delete;
+};
+
+} // namespace dbus
+
+#include <dbus/impl/match.ipp>
+
+#endif // DBUS_MATCH_HPP
diff --git a/boost-dbus/include/dbus/message.hpp b/boost-dbus/include/dbus/message.hpp
new file mode 100644
index 0000000..aa5e89c
--- /dev/null
+++ b/boost-dbus/include/dbus/message.hpp
@@ -0,0 +1,224 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_MESSAGE_HPP
+#define DBUS_MESSAGE_HPP
+
+#include <dbus/dbus.h>
+#include <dbus/element.hpp>
+#include <dbus/endpoint.hpp>
+#include <dbus/impl/message_iterator.hpp>
+#include <iostream>
+#include <vector>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/utility/enable_if.hpp>
+
+inline void intrusive_ptr_add_ref(DBusMessage* m) { dbus_message_ref(m); }
+
+inline void intrusive_ptr_release(DBusMessage* m) { dbus_message_unref(m); }
+
+namespace dbus {
+
+class message {
+ boost::intrusive_ptr<DBusMessage> message_;
+
+ public:
+ /// Create a method call message
+ static message new_call(const endpoint& destination,
+ const string& method_name) {
+ return dbus_message_new_method_call(
+ destination.get_process_name().c_str(), destination.get_path().c_str(),
+ destination.get_interface().c_str(), method_name.c_str());
+ }
+
+ /// Create a method return message
+ static message new_return(message& call) {
+ return dbus_message_new_method_return(call);
+ }
+
+ /// Create an error message
+ static message new_error(message& call, const string& error_name,
+ const string& error_message) {
+ return dbus_message_new_error(call, error_name.c_str(),
+ error_message.c_str());
+ }
+
+ /// Create a signal message
+ static message new_signal(const endpoint& origin, const string& signal_name) {
+ return dbus_message_new_signal(origin.get_path().c_str(),
+ origin.get_interface().c_str(),
+ signal_name.c_str());
+ }
+
+ message() {}
+
+ message(DBusMessage* m) : message_(dbus_message_ref(m)) {}
+
+ operator DBusMessage*() { return message_.get(); }
+
+ operator const DBusMessage*() const { return message_.get(); }
+
+ string get_path() const {
+ return sanitize(dbus_message_get_path(message_.get()));
+ }
+
+ string get_interface() const {
+ return sanitize(dbus_message_get_interface(message_.get()));
+ }
+
+ string get_member() const {
+ return sanitize(dbus_message_get_member(message_.get()));
+ }
+
+ string get_type() const {
+ return sanitize(
+ dbus_message_type_to_string(dbus_message_get_type(message_.get())));
+ }
+
+ string get_sender() const {
+ return sanitize(dbus_message_get_sender(message_.get()));
+ }
+
+ string get_destination() const {
+ return sanitize(dbus_message_get_destination(message_.get()));
+ }
+
+ uint32 get_serial() { return dbus_message_get_serial(message_.get()); }
+
+ message& set_serial(uint32 serial) {
+ dbus_message_set_serial(message_.get(), serial);
+ return *this;
+ }
+
+ uint32 get_reply_serial() {
+ return dbus_message_get_reply_serial(message_.get());
+ }
+
+ message& set_reply_serial(uint32 reply_serial) {
+ dbus_message_set_reply_serial(message_.get(), reply_serial);
+ return *this;
+ }
+
+ struct packer {
+ impl::message_iterator iter_;
+ packer(message& m) { impl::message_iterator::init_append(m, iter_); }
+ template <typename Element>
+ packer& pack(const Element& e) {
+ return *this << e;
+ }
+ };
+ struct unpacker {
+ impl::message_iterator iter_;
+ unpacker(message& m) { impl::message_iterator::init(m, iter_); }
+
+ template <typename Element>
+ unpacker& unpack(Element& e) {
+ return *this >> e;
+ }
+ };
+
+ template <typename Element>
+ packer pack(const Element& e) {
+ return packer(*this).pack(e);
+ }
+
+ template <typename Element>
+ unpacker unpack(Element& e) {
+ return unpacker(*this).unpack(e);
+ }
+
+ private:
+ static std::string sanitize(const char* str) {
+ return (str == NULL) ? "(null)" : str;
+ }
+};
+
+template <typename Element>
+message::packer operator<<(message m, const Element& e) {
+ return message::packer(m).pack(e);
+}
+
+template <typename Element>
+typename boost::enable_if<is_fixed_type<Element>, message::packer&>::type
+operator<<(message::packer& p, const Element& e) {
+ p.iter_.append_basic(element<Element>::code, &e);
+ return p;
+}
+
+inline message::packer& operator<<(message::packer& p, const char* c) {
+ p.iter_.append_basic(element<string>::code, &c);
+ return p;
+}
+
+inline message::packer& operator<<(message::packer& p, const string& e) {
+ const char* c = e.c_str();
+ return p << c;
+}
+
+template <typename Element>
+message::unpacker operator>>(message m, Element& e) {
+ return message::unpacker(m).unpack(e);
+}
+
+template <typename Element>
+typename boost::enable_if<is_fixed_type<Element>, message::unpacker&>::type
+operator>>(message::unpacker& u, Element& e) {
+ u.iter_.get_basic(&e);
+ u.iter_.next();
+ return u;
+}
+
+inline message::unpacker& operator>>(message::unpacker& u, string& s) {
+ const char* c;
+ u.iter_.get_basic(&c);
+ s.assign(c);
+ u.iter_.next();
+ return u;
+}
+
+template <typename Element>
+inline message::unpacker& operator>>(message::unpacker& u,
+ std::vector<Element>& s) {
+ static_assert(std::is_same<Element, std::string>::value,
+ "only std::vector<std::string> is implemented for now");
+ impl::message_iterator sub;
+ u.iter_.recurse(sub);
+
+ const char* c;
+ while (sub.has_next()) {
+ sub.get_basic(&c);
+ s.emplace_back(c);
+ sub.next();
+ }
+
+ // TODO(ed)
+ // Make this generic for all types. The below code is close, but there's
+ // template issues and things I don't understand;
+ /*
+ auto e = message::unpacker(sub);
+ while (sub.has_next()) {
+ s.emplace_back();
+ Element& element = s.back();
+ e.unpack(element);
+ }
+*/
+ return u;
+}
+
+inline std::ostream& operator<<(std::ostream& os, const message& m) {
+ os << "type='" << m.get_type() << "',"
+ << "sender='" << m.get_sender() << "',"
+ << "interface='" << m.get_interface() << "',"
+ << "member='" << m.get_member() << "',"
+ << "path='" << m.get_path() << "',"
+ << "destination='" << m.get_destination() << "'";
+ return os;
+}
+
+} // namespace dbus
+
+#include <dbus/impl/message_iterator.ipp>
+
+#endif // DBUS_MESSAGE_HPP
diff --git a/boost-dbus/include/dbus/utility.hpp b/boost-dbus/include/dbus/utility.hpp
new file mode 100644
index 0000000..47fad1c
--- /dev/null
+++ b/boost-dbus/include/dbus/utility.hpp
@@ -0,0 +1,39 @@
+// Copyright (c) Ed Tanous
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef DBUS_UTILITY_HPP
+#define DBUS_UTILITY_HPP
+
+#include <boost/iostreams/stream.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+
+namespace dbus {
+
+inline void read_dbus_xml_names(std::string& xml_data_in,
+ std::vector<std::string>& values_out) {
+ // populate tree structure pt
+ using boost::property_tree::ptree;
+ ptree pt;
+ std::cout << xml_data_in;
+ std::stringstream ss;
+ ss << xml_data_in;
+ read_xml(ss, pt);
+
+ // traverse node to find other nodes
+ for (const auto& interface : pt.get_child("node")) {
+ if (interface.first == "node") {
+ auto t = interface.second.get<std::string>("<xmlattr>", "default");
+ for (const auto& subnode : interface.second.get_child("<xmlattr>")) {
+ if (subnode.first == "name") {
+ std::string t = subnode.second.get("", "unknown");
+ values_out.push_back(t);
+ }
+ }
+ }
+ }
+}
+}
+#endif // DBUS_UTILITY_HPP
\ No newline at end of file
diff --git a/boost-dbus/test/avahi.cpp b/boost-dbus/test/avahi.cpp
new file mode 100644
index 0000000..debbff3
--- /dev/null
+++ b/boost-dbus/test/avahi.cpp
@@ -0,0 +1,176 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#include <dbus/connection.hpp>
+#include <dbus/endpoint.hpp>
+#include <dbus/filter.hpp>
+#include <dbus/match.hpp>
+#include <dbus/message.hpp>
+#include <dbus/utility.hpp>
+#include <functional>
+
+#include <unistd.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+TEST(AvahiTest, GetHostName) {
+ dbus::endpoint test_daemon("org.freedesktop.Avahi", "/",
+ "org.freedesktop.Avahi.Server");
+ boost::asio::io_service io;
+ dbus::connection system_bus(io, dbus::bus::system);
+
+ dbus::message m = dbus::message::new_call(test_daemon, "GetHostName");
+
+ system_bus.async_send(
+ m, [&](const boost::system::error_code ec, dbus::message r) {
+
+ std::string avahi_hostname;
+ std::string hostname;
+
+ // get hostname from a system call
+ char c[1024];
+ gethostname(c, 1024);
+ hostname = c;
+
+ r.unpack(avahi_hostname);
+
+ // Get only the host name, not the fqdn
+ auto unix_hostname = hostname.substr(0, hostname.find("."));
+ EXPECT_EQ(unix_hostname, avahi_hostname);
+
+ io.stop();
+ });
+ boost::asio::deadline_timer t(io, boost::posix_time::seconds(10));
+ t.async_wait([&](const boost::system::error_code& /*e*/) {
+ io.stop();
+ FAIL() << "Callback was never called\n";
+ });
+ io.run();
+}
+
+TEST(AvahiTest, ServiceBrowser) {
+ boost::asio::io_service io;
+ dbus::connection system_bus(io, dbus::bus::system);
+
+ dbus::endpoint test_daemon("org.freedesktop.Avahi", "/",
+ "org.freedesktop.Avahi.Server");
+ // create new service browser
+ dbus::message m1 = dbus::message::new_call(test_daemon, "ServiceBrowserNew");
+ m1.pack<int32_t>(-1)
+ .pack<int32_t>(-1)
+ .pack<std::string>("_http._tcp")
+ .pack<std::string>("local")
+ .pack<uint32_t>(0);
+
+ dbus::message r = system_bus.send(m1);
+ std::string browser_path;
+ r.unpack(browser_path);
+ testing::Test::RecordProperty("browserPath", browser_path);
+
+ dbus::match ma(system_bus, "type='signal',path='" + browser_path + "'");
+ dbus::filter f(system_bus, [](dbus::message& m) {
+ auto member = m.get_member();
+ return member == "NameAcquired";
+ });
+
+ std::function<void(boost::system::error_code, dbus::message)> event_handler =
+ [&](boost::system::error_code ec, dbus::message s) {
+ testing::Test::RecordProperty("firstSignal", s.get_member());
+ std::string a = s.get_member();
+ std::string dude;
+ s.unpack(dude);
+ f.async_dispatch(event_handler);
+ io.stop();
+ };
+ f.async_dispatch(event_handler);
+
+ boost::asio::deadline_timer t(io, boost::posix_time::seconds(10));
+ t.async_wait([&](const boost::system::error_code& /*e*/) {
+ io.stop();
+ FAIL() << "Callback was never called\n";
+ });
+ io.run();
+}
+
+TEST(BOOST_DBUS, ListServices) {
+ boost::asio::io_service io;
+ boost::asio::deadline_timer t(io, boost::posix_time::seconds(10));
+ t.async_wait([&](const boost::system::error_code& /*e*/) {
+ io.stop();
+ FAIL() << "Callback was never called\n";
+ });
+
+ dbus::connection system_bus(io, dbus::bus::system);
+
+ dbus::endpoint test_daemon("org.freedesktop.DBus", "/",
+ "org.freedesktop.DBus");
+ // create new service browser
+ dbus::message m = dbus::message::new_call(test_daemon, "ListNames");
+ system_bus.async_send(
+ m, [&](const boost::system::error_code ec, dbus::message r) {
+ io.stop();
+ std::vector<std::string> services;
+ r.unpack(services);
+ // Test a couple things that should always be present.... adapt if
+ // neccesary
+ EXPECT_THAT(services, testing::Contains("org.freedesktop.DBus"));
+ EXPECT_THAT(services, testing::Contains("org.freedesktop.Accounts"));
+
+ });
+
+ io.run();
+}
+
+void query_interfaces(dbus::connection& system_bus, std::string& service_name,
+ std::string& object_name) {
+ dbus::endpoint service_daemon(service_name, object_name,
+ "org.freedestop.DBus.Introspectable");
+ dbus::message m = dbus::message::new_call(service_daemon, "Introspect");
+ try {
+ auto r = system_bus.send(m);
+ std::vector<std::string> names;
+ // Todo(ed) figure out why we're occassionally getting access
+ // denied errors
+ // EXPECT_EQ(ec, boost::system::errc::success);
+
+ std::string xml;
+ r.unpack(xml);
+ // TODO(ed) names needs lock for multithreaded access
+ dbus::read_dbus_xml_names(xml, names);
+ // loop over the newly added items
+ for (auto name : names) {
+ std::cout << name << "\n";
+ auto new_service_string = object_name + "/" + name;
+ query_interfaces(system_bus, service_name, new_service_string);
+ }
+ } catch (boost::system::error_code e) {
+ std::cout << e;
+ }
+}
+
+TEST(BOOST_DBUS, ListObjects) {
+ boost::asio::io_service io;
+ dbus::connection system_bus(io, dbus::bus::system);
+
+ dbus::endpoint test_daemon("org.freedesktop.DBus", "/",
+ "org.freedesktop.DBus");
+
+ // create new service browser
+ dbus::message m = dbus::message::new_call(test_daemon, "ListNames");
+ auto r = system_bus.send(m);
+
+ std::vector<std::string> services;
+ r.unpack(services);
+ // todo(ed) find out why this needs to be static
+ static std::atomic<int> dbus_count(0);
+ std::cout << dbus_count << " Callers\n";
+ auto names = std::make_shared<std::vector<std::string>>();
+ for (auto& service : services) {
+ std::string name = "/";
+ query_interfaces(system_bus, service, name);
+ }
+
+ io.run();
+}
diff --git a/boost-dbus/test/avahi.py b/boost-dbus/test/avahi.py
new file mode 100644
index 0000000..1bcd24d
--- /dev/null
+++ b/boost-dbus/test/avahi.py
@@ -0,0 +1,42 @@
+# Copyright (c) Benjamin Kietzman (github.com/bkietz)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+import unittest
+import dbus
+from dbus.mainloop.glib import DBusGMainLoop
+from gobject import MainLoop
+from socket import gethostname
+
+class AvahiTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(c):
+ c.system_bus = dbus.SystemBus(mainloop=DBusGMainLoop())
+
+ def setUp(self):
+ None
+
+ def testAvahi(self):
+ # Connect to Avahi Daemon's interface:
+ avahi_remote = AvahiTest.system_bus.get_object('org.freedesktop.Avahi', '/')
+ avahi = dbus.Interface(avahi_remote, 'org.freedesktop.Avahi.Server')
+ self.assertEqual(gethostname(), avahi.GetHostName())
+
+ # Use the Avahi Daemon to produce a new
+ # ServiceBrowser and connect to its interface:
+ browser_path = avahi.ServiceBrowserNew(-1, -1, "_http._tcp", "local", dbus.UInt32(0))
+ browser_remote = AvahiTest.system_bus.get_object('org.freedesktop.Avahi', browser_path)
+
+ browser = dbus.Interface(browser_remote, 'org.freedesktop.Avahi.ServiceBrowser')
+
+ # Connect to the ItemNew signal from the browser:
+ def new_item_handler(interface, protocol, instance_name, instance_type, domain, flags):
+ print "Found service '%s'" % instance_name
+
+ browser.connect_to_signal("ItemNew", new_item_handler)
+
+if __name__ == '__main__':
+ unittest.main()
+ MainLoop().run()
diff --git a/boost-dbus/test/export_sample.py b/boost-dbus/test/export_sample.py
new file mode 100644
index 0000000..be8a04b
--- /dev/null
+++ b/boost-dbus/test/export_sample.py
@@ -0,0 +1,39 @@
+# Copyright (c) Benjamin Kietzman (github.com/bkietz)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+import dbus
+import dbus.service
+from dbus.mainloop.glib import DBusGMainLoop
+from gobject import MainLoop
+
+bus_name = 'com.example.Sample'
+
+class Example(dbus.service.Object):
+ def __init__(self, connection, path):
+ dbus.service.Object.__init__(self, connection, path)
+ self._last_input = None
+
+ @dbus.service.method(bus_name+'.Iface', in_signature='v', out_signature='s')
+ def StringifyVariant(self, var):
+ self.LastInputChanged(var) # emits the signal
+ return str(var)
+
+ @dbus.service.signal(bus_name+'.Iface', signature='v')
+ def LastInputChanged(self, var):
+ # run just before the signal is actually emitted
+ # just put "pass" if nothing should happen
+ self._last_input = var
+
+ @dbus.service.method(bus_name+'.Iface', in_signature='', out_signature='v')
+ def GetLastInput(self):
+ return self._last_input
+
+bus = dbus.SessionBus(mainloop=DBusGMainLoop())
+bus.request_name(bus_name)
+
+example = Example(bus, '/path/to/obj')
+
+print bus.get_name_owner(bus_name)
+MainLoop().run()
diff --git a/boost-dbus/test/message.cpp b/boost-dbus/test/message.cpp
new file mode 100644
index 0000000..d591f61
--- /dev/null
+++ b/boost-dbus/test/message.cpp
@@ -0,0 +1,62 @@
+// Copyright (c) Benjamin Kietzman (github.com/bkietz)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#include <dbus/error.hpp>
+#include <dbus/connection.hpp>
+#include <dbus/endpoint.hpp>
+#include <dbus/filter.hpp>
+#include <dbus/match.hpp>
+#include <dbus/message.hpp>
+#include <gtest/gtest.h>
+
+TEST(MessageTest, CallMessage) {
+ const dbus::message m =
+ dbus::message::new_call(dbus::endpoint("org.freedesktop.Avahi", "/",
+ "org.freedesktop.Avahi.Server"),
+ "GetHostName");
+
+ ASSERT_EQ("org.freedesktop.Avahi", m.get_destination());
+ ASSERT_EQ("/", m.get_path());
+ ASSERT_EQ("org.freedesktop.Avahi.Server", m.get_interface());
+ ASSERT_EQ("GetHostName", m.get_member());
+
+ dbus::message m2 =
+ dbus::message::new_call(dbus::endpoint("org.freedesktop.Avahi", "/",
+ "org.freedesktop.Avahi.Server"),
+ "GetHostName");
+
+ m2 << 1;
+ int i;
+ m2 >> i;
+ ASSERT_EQ(i, 1);
+
+ // m.get_sender();
+}
+
+// I actually don't know what to do with these yet.
+/*
+TEST(MessageTest, ErrorMessage)
+{
+
+ dbus::message m = dbus::message::new_call(
+ dbus::endpoint(
+ "org.freedesktop.Avahi",
+ "/",
+ "org.freedesktop.Avahi.Server"),
+ "GetHostName");
+
+ m.set_reply_serial(42);
+ m.set_serial(43);
+
+ dbus::message em = dbus::message::new_error(
+ m,
+ "com.skizizo.NoHostname",
+ "No hostname for you!");
+
+ const error e(em);
+
+ e.throw_if_set();
+}
+*/
diff --git a/boost-dbus/test/proxy_sample.py b/boost-dbus/test/proxy_sample.py
new file mode 100644
index 0000000..a467410
--- /dev/null
+++ b/boost-dbus/test/proxy_sample.py
@@ -0,0 +1,19 @@
+# Copyright (c) Benjamin Kietzman (github.com/bkietz)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+import dbus
+from dbus.mainloop.glib import DBusGMainLoop
+from gobject import MainLoop
+
+bus_name = 'com.example.Sample'
+session_bus = dbus.SessionBus(mainloop=DBusGMainLoop())
+
+example_remote = session_bus.get_object(bus_name, '/path/to/obj')
+example = dbus.Interface(example_remote, bus_name+'.Iface')
+
+example.StringifyVariant(123)
+print example.GetLastInput()
+
+MainLoop().run()