Add server side dbus wrappers and tests 98/150198/14
authorRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Fri, 8 Sep 2017 08:48:31 +0000 (10:48 +0200)
committerRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Fri, 29 Sep 2017 13:30:52 +0000 (15:30 +0200)
Add dbus wrappers for server-side functionality (publishing interfaces,
emiting signals and so on). Add tests for all functionality.

Change-Id: I3f5ddcdc72f372b708ae99f15d0c9b63c9264f55

12 files changed:
src/Atspi.cpp
src/Atspi.hpp
src/CallActivity.cpp
src/DBus.cpp
src/DBus.hpp
src/GestureActivity.cpp
src/NavigationInterface.cpp
src/ScreenSwitchProvider.cpp
src/ScreenSwitchProvider.hpp
src/TextToSpeech.cpp
src/UIElement.cpp
tests/no-ui-scenarios/DBusTests.cpp [new file with mode: 0644]

index 85d0f23..84b8b49 100644 (file)
@@ -4,6 +4,8 @@
 #include <functional>
 #include <limits>
 
+using namespace DBus;
+
 #define PRINT_ERROR_AND_FREE(error) \
        do { \
                if(error) { \
@@ -64,20 +66,20 @@ template <typename T> struct InterfaceNameFromType {
 #define ADD_INTROSPECTION_FUNCTIONS(TYPE, NAME)                                                           \
        template <> const std::string InterfaceNameFromType<Atspi ## TYPE>::interfaceName =                   \
                        ATSPI_DBUS_INTERFACE_ ## NAME;                                                                \
-       static std::string getBusName(const std::shared_ptr<Atspi ## TYPE> &o)                                \
+       std::string Atspi::getBusName(const std::shared_ptr<Atspi ## TYPE> &o)                                \
        {                                                                                                     \
                return getBusNameImpl(o.get());                                                                   \
        }                                                                                                     \
-       static std::string getPath(const std::shared_ptr<Atspi ## TYPE> &o)                                   \
+       std::string Atspi::getPath(const std::shared_ptr<Atspi ## TYPE> &o)                                   \
        {                                                                                                     \
                return getPathImpl(o.get());                                                                      \
        }                                                                                                     \
        static void convert(std::shared_ptr<Atspi ## TYPE> &r,                                                \
-                                               const AtspiAccessiblePtr &obj)                                      \
+                                               const AtspiAccessiblePtr &obj)                                                    \
        {                                                                                                     \
                r =  { ATSPI_ ## NAME(g_object_ref(obj.get())), g_object_unref };                                 \
        }                                                                                                     \
-       void Atspi::get ## TYPE ## Interface(const AtspiAccessiblePtr &obj,                     \
+       void Atspi::get ## TYPE ## Interface(const AtspiAccessiblePtr &obj,                                   \
                                                                                 AsyncCallback<std::shared_ptr<Atspi ## TYPE>> callback) const    \
        {                                                                                                     \
                getInterface<Atspi ## TYPE>(obj, std::move(callback));                                            \
@@ -121,13 +123,13 @@ Atspi::Atspi()
 {
        ConnectAtClient();
 
-       DBus proxy{"org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus"};
+       DBusClient proxy{"org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus"};
        auto addr = proxy.method<std::string()>("GetAddress").call();
 
        if (!addr) {
                ERROR("failed");
        } else {
-               eldbusConnection = { eldbus_address_connection_get((*addr).c_str()), eldbus_connection_unref };
+               eldbusConnection = DBus::EldbusConnectionCallbackHandle{ eldbus_address_connection_get(std::get<0>(addr).c_str()), eldbus_connection_unref };
        }
 }
 
@@ -292,19 +294,30 @@ AtspiAccessiblePtr Atspi::getObjectInRelation(const AtspiAccessiblePtr &accessib
        return {relationObj, g_object_unref};
 }
 
-template <typename T> DBus Atspi::getProxy(const std::shared_ptr<T> &obj, const std::string &interface) const
+template <typename T> DBusClient Atspi::getProxy(const std::shared_ptr<T> &obj, const std::string &interface) const
 {
        auto bus = getBusName(obj);
        auto path = getPath(obj);
 
-       return DBus { bus, path, interface, eldbusConnection };
+       return DBusClient { bus, path, interface, eldbusConnection };
 }
 
+template <typename T> struct function_return_type_trait {
+       using type = ValueOrError<T>;
+       using stripped_type = T;
+};
+template <typename ... T> struct function_return_type_trait<ValueOrError<T...>> {
+       using type = ValueOrError<T...>;
+};
+template <typename> struct FunctionCallbackType;
+template <typename RetType, typename ... ARGS> struct FunctionCallbackType<RetType(ARGS...)> {
+       using type = std::function<void(typename function_return_type_trait<RetType>::type)>;
+};
 template <typename> struct PropertyCallback;
 template <typename RetType> struct PropertyCallback<RetType(void)> {
-       using VariantReturnType = EldbusVariant<RetType>;
+       using VariantReturnType = ValueOrError<EldbusVariant<RetType>>;
        using RealReturnType = RetType;
-       using CallType = VariantReturnType(std::string, std::string);
+       using CallType = EldbusVariant<RetType>(std::string, std::string);
 };
 template <typename> struct PropertySetterCallback;
 template <typename ValueType> struct PropertySetterCallback<void(ValueType)> {
@@ -312,66 +325,66 @@ template <typename ValueType> struct PropertySetterCallback<void(ValueType)> {
        using RealReturnType = void;
        using CallType = void(std::string, std::string, VariantSetterType);
 };
-template <typename> struct returnType;
-template <typename RetType, typename ... ARGS> struct returnType<RetType(ARGS...)> {
-       using type = RetType;
-};
 template <typename CallType, typename InterfaceType, typename ... ARGS> void callFunction(
        const EldbusConnectionCallbackHandle &eldbusConnection,
        const std::string &interface,
        const std::shared_ptr<InterfaceType> &obj,
-       const std::string &func_name,
-       typename detail::AsyncCallbackHelper<typename returnType<CallType>::type>::type callback,
+       const std::string &funcName,
+       typename FunctionCallbackType<CallType>::type callback,
        ARGS &&... args
 )
 {
-       auto bus = getBusName(obj);
-       auto path = getPath(obj);
+       auto bus = Atspi::getBusName(obj);
+       auto path = Atspi::getPath(obj);
 
-       auto dbus = DBus { bus, path, interface, eldbusConnection };
-       dbus.method<CallType>(func_name).asyncCall(std::forward<ARGS>(args)..., std::move(callback));
+       auto dbus = DBusClient { bus, path, interface, eldbusConnection };
+       dbus.method<CallType>(funcName).asyncCall(std::move(callback), std::forward<ARGS>(args)...);
 }
 
 template <typename CallType, typename InterfaceType, typename ... ARGS> void callFunction(
        const EldbusConnectionCallbackHandle &eldbusConnection,
        const std::shared_ptr<InterfaceType> &obj,
-       const std::string &func_name,
-       typename detail::AsyncCallbackHelper<typename returnType<CallType>::type>::type callback,
+       const std::string &funcName,
+       typename FunctionCallbackType<CallType>::type callback,
        ARGS &&... args
 )
 {
        callFunction<CallType>(eldbusConnection, InterfaceNameFromType<InterfaceType>::interfaceName,
-                                                  obj, func_name, std::move(callback), std::forward<ARGS>(args)...);
+                                                  obj, funcName, std::move(callback), std::forward<ARGS>(args)...);
 }
 
 template <typename CallType, typename InterfaceType, typename ... ARGS> void getProperty(
        const EldbusConnectionCallbackHandle &eldbusConnection,
        const std::string &interface,
        const std::shared_ptr<InterfaceType> &obj,
-       const std::string &func_name,
-       typename detail::AsyncCallbackHelper<typename returnType<CallType>::type>::type callback,
+       const std::string &funcName,
+       typename FunctionCallbackType<CallType>::type callback,
        ARGS &&... args
 )
 {
        callFunction<typename PropertyCallback<CallType>::CallType>(
                eldbusConnection, DBUS_INTERFACE_PROPERTIES, obj, "Get",
-       [callback](Optional<typename PropertyCallback<CallType>::VariantReturnType> val) {
-               if (!val) callback({});
-               else callback(std::move((*val).value));
+       [callback](typename PropertyCallback<CallType>::VariantReturnType val) {
+               if (!val) {
+                       callback(Error { val.getError() });
+               } else {
+                       auto z = std::get<0>(val);
+                       callback(z.value);
+               }
        },
-       interface, func_name);
+       interface, funcName);
 }
 
 template <typename CallType, typename InterfaceType, typename ... ARGS> void getProperty(
        const EldbusConnectionCallbackHandle &eldbusConnection,
        const std::shared_ptr<InterfaceType> &obj,
-       const std::string &func_name,
-       typename detail::AsyncCallbackHelper<typename returnType<CallType>::type>::type callback,
+       const std::string &funcName,
+       typename FunctionCallbackType<CallType>::type callback,
        ARGS &&... args
 )
 {
        getProperty<CallType>(eldbusConnection, InterfaceNameFromType<InterfaceType>::interfaceName,
-                                                 obj, func_name, std::move(callback), std::forward<ARGS>(args)...);
+                                                 obj, funcName, std::move(callback), std::forward<ARGS>(args)...);
 }
 
 template <typename CallType, typename InterfaceType, typename Value> void setProperty(
@@ -470,11 +483,10 @@ void Atspi::getRole(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<Atspi
                eldbusConnection,
                accessibleObj,
                "GetRole",
-       [callback](Optional<uint32_t> val) {
-               if (!val) callback({});
-               else callback((AtspiRole)*val);
-       }
-       );
+       [callback](ValueOrError<uint32_t> val) {
+               if (!val) callback(Error{ val.getError() });
+               else callback((AtspiRole)std::get<0>(val));
+       });
 }
 
 void Atspi::getChildrenCount(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<size_t> callback) const
@@ -483,11 +495,9 @@ void Atspi::getChildrenCount(const AtspiAccessiblePtr &accessibleObj, AsyncCallb
                eldbusConnection,
                accessibleObj,
                "ChildCount",
-       [callback](Optional<int> val) {
-               if (!val || *val < 0)
-                       callback({});
-               else
-                       callback((size_t)*val);
+       [callback](ValueOrError<int> val) {
+               if (!val) callback(Error{ val.getError() });
+               else callback((size_t)std::get<0>(val));
        }
        );
 }
@@ -509,29 +519,28 @@ void Atspi::getStateSet(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<S
                eldbusConnection,
                accessibleObj,
                "GetState",
-       [callback](Optional<std::array<uint32_t, 2>> data) {
-               if (data) {
+       [callback](ValueOrError<std::array<uint32_t, 2>> data) {
+               if (!data) {
+                       callback(Error{ data.getError() });
+               } else {
                        StateSet s;
                        for (size_t i = 0; i < (size_t)ATSPI_STATE_LAST_DEFINED; ++i) {
-                               if ((*data)[i >> 5] & (1 << (i & 31))) {
+                               if (std::get<0>(data)[i >> 5] & (1 << (i & 31))) {
                                        s[i] = true;
-                                       assert(s[i]);
+                                       ASSERT(s[i]);
                                }
                        }
                        callback(s);
-               } else {
-                       callback({});
                }
-       }
-       );
+       });
 }
 
 void Atspi::getChildren(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::vector<AtspiAccessiblePtr>> callback) const
 {
        getChildrenCount(accessibleObj,
-       [accessibleObj, callback](Optional<size_t> childrenCount) {
+       [accessibleObj, callback](ValueOrError<size_t> childrenCount) {
                if (!childrenCount) {
-                       callback({});
+                       callback(Error{ childrenCount.getError() });
                } else {
                        struct State {
                                AsyncCallback<std::vector<AtspiAccessiblePtr>> callback;
@@ -541,19 +550,19 @@ void Atspi::getChildren(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<s
                        };
                        auto state = std::make_shared<State>();
                        state->callback = std::move(callback);
-                       state->children.resize(*childrenCount);
-                       state->todoCount = *childrenCount;
-                       for (size_t index = 0; index < *childrenCount; ++index) {
-                               auto cback = [state, index](Optional<AtspiAccessiblePtr> child) {
+                       state->children.resize(std::get<0>(childrenCount));
+                       state->todoCount = std::get<0>(childrenCount);
+                       for (size_t index = 0; index < std::get<0>(childrenCount); ++index) {
+                               auto cback = [state, index](ValueOrError<AtspiAccessiblePtr> child) {
                                        if (state->failed)
                                                return;
                                        if (!child) {
                                                state->failed = true;
-                                               state->callback({});
+                                               state->callback(Error { child.getError() });
                                                return;
                                        }
-                                       state->children[index] = std::move(*child);
-                                       assert(state->todoCount > 0);
+                                       state->children[index] = std::move(std::get<0>(child));
+                                       ASSERT(state->todoCount > 0);
                                        --state->todoCount;
                                        if (state->todoCount == 0)
                                                state->callback(std::move(state->children));
@@ -573,20 +582,19 @@ void Atspi::getRelationSet(const std::shared_ptr<AtspiAccessible> &accessibleObj
                eldbusConnection,
                accessibleObj,
                "GetRelationSet",
-       [callback](Optional<RelationSet> relationSet) {
+       [callback](ValueOrError<RelationSet> relationSet) {
                if (!relationSet) {
                        ERROR("dbus getRelationSet failed");
-                       callback({});
+                       callback(relationSet.getError());
                        return;
                }
 
-               std::vector<AccessibilityRelation> relations(relationSet->size());
-               for (auto &r : *relationSet)
+               std::vector<AccessibilityRelation> relations(std::get<0>(relationSet).size());
+               for (auto &r : std::get<0>(relationSet))
                        relations.push_back({std::get<0>(r), std::move(std::get<1>(r))});
 
                callback(std::move(relations));
-       }
-       );
+       });
 }
 
 void Atspi::getObjectInRelation(const std::shared_ptr<AtspiAccessible> &accessibleObj, AtspiRelationType searchType,
@@ -594,15 +602,15 @@ void Atspi::getObjectInRelation(const std::shared_ptr<AtspiAccessible> &accessib
 {
        auto obj = accessibleObj;
        getRelationSet(accessibleObj,
-       [callback, obj, searchType](Optional<std::vector<AccessibilityRelation>> relations) {
+       [callback, obj, searchType](ValueOrError<std::vector<AccessibilityRelation>> relations) {
                if (!relations) {
                        DEBUG("Relation set do not exist");
                        callback({});
                        return;
                }
 
-               DEBUG("Relations found: %d", relations->size());
-               for (auto r : *relations) {
+               DEBUG("Relations found: %d", std::get<0>(relations).size());
+               for (auto r : std::get<0>(relations)) {
                        DEBUG("Relation type: %d", r. type);
                        if (r.type == searchType) {
                                DEBUG("Searched relation found");
@@ -618,20 +626,13 @@ void Atspi::getObjectInRelation(const std::shared_ptr<AtspiAccessible> &accessib
 namespace
 {
        void getValueTemplateFunction(const EldbusConnectionCallbackHandle &eldbusConnection, const std::shared_ptr<AtspiValue> &valueInterface, const std::string paramName,
-                                                                 detail::AsyncCallbackHelper<double>::type callback)
+                                                                 AsyncCallback<double> callback)
        {
                getProperty<double()>(
                        eldbusConnection,
                        valueInterface,
                        paramName,
-               [callback](Optional<double> value) {
-                       if (!value) {
-                               ERROR("can not get value");
-                               callback({});
-                               return;
-                       }
-                       callback(std::move(value));
-               });
+                       std::move(callback));
        }
 }
 
@@ -667,46 +668,40 @@ template <typename T> void Atspi::getInterface(const AtspiAccessiblePtr &accessi
                eldbusConnection,
                accessibleObj,
                "GetInterfaces",
-       [this, callback, accessibleObj](Optional<std::vector<std::string>> val) {
+       [this, callback, accessibleObj](ValueOrError<std::vector<std::string>> val) {
                const auto interfaceName = InterfaceNameFromType<T>::interfaceName;
-               if (val) {
-                       for (auto &z : *val) {
+               if (!val) {
+                       callback(Error{ val.getError() });
+               } else {
+                       std::shared_ptr<T> r;
+                       for (auto &z : std::get<0>(val)) {
                                if (z == interfaceName) {
-                                       std::shared_ptr<T> r;
                                        convert(r, accessibleObj);
-                                       callback(std::move(r));
-                                       return;
+                                       break;
                                }
                        }
+                       callback(std::move(r));
                }
-               std::string interfaces;
-               for (auto &z : *val) {
-                       if (!interfaces.empty()) interfaces += " ";
-                       interfaces += z;
-               }
-               callback({});
-       }
-       );
+       });
 }
 
 void Atspi::getMatchedElements(const AtspiCollectionPtr &rootObj, AtspiCollectionSortOrder sortOrder,
                                                           size_t maximumFoundElements, Matcher m, bool reverse,
                                                           AsyncCallback<std::vector<AtspiAccessiblePtr>> callback)
 {
-       assert(maximumFoundElements <= (size_t)std::numeric_limits<int32_t>::max());
+       ASSERT(maximumFoundElements <= (size_t)std::numeric_limits<int32_t>::max());
 
        auto bus = getBusName(rootObj);
        auto path = getPath(rootObj);
 
-       auto pr = DBus { bus, path, ATSPI_DBUS_INTERFACE_COLLECTION, eldbusConnection };
+       auto pr = DBusClient { bus, path, ATSPI_DBUS_INTERFACE_COLLECTION, eldbusConnection };
        std::get<8>(m.value) = reverse;
        auto mm = pr.method<std::vector<AtspiAccessiblePtr>(decltype(Matcher::value), uint32_t, int32_t, bool)>("GetMatches");
-       auto v = mm.call(
+       mm.asyncCall(std::move(callback),
                                 std::move(m.value),
                                 (uint32_t)sortOrder,
                                 (int32_t)maximumFoundElements,
                                 true);
-       callback(v);
 }
 
 void Atspi::getAtPoint(Point pt, CoordType type, const AtspiAccessiblePtr &root, AsyncCallback<AtspiAccessiblePtr> callback) const
@@ -720,21 +715,21 @@ void Atspi::getAtPoint(Point pt, CoordType type, const AtspiAccessiblePtr &root,
                ctype = ATSPI_COORD_TYPE_WINDOW;
                break;
        }
-       using returnType = std::tuple<AtspiAccessiblePtr, uint8_t, AtspiAccessiblePtr>;
+       using ReturnType = ValueOrError<AtspiAccessiblePtr, uint8_t, AtspiAccessiblePtr>;
        struct handler {
                const Atspi *atspi;
                Point pt;
                uint32_t ctype;
                AsyncCallback<AtspiAccessiblePtr> callback;
 
-               static void process(std::shared_ptr<handler> self, Optional<returnType> value)
+               static void process(std::shared_ptr<handler> self, ReturnType value)
                {
                        if (!value) {
-                               self->callback({});
+                               self->callback(Error { value.getError() });
                        } else {
-                               auto &ptr = std::get<0>(*value);
-                               auto recurse = std::get<1>(*value);
-                               auto &deputy = std::get<2>(*value);
+                               auto ptr = std::get<0>(value);
+                               auto recurse = std::get<1>(value);
+                               auto deputy = std::get<2>(value);
                                if (!ptr && deputy)
                                        ptr = std::move(deputy);
                                if (!recurse) {
@@ -745,11 +740,11 @@ void Atspi::getAtPoint(Point pt, CoordType type, const AtspiAccessiblePtr &root,
                                        }
                                        return;
                                }
-                               callFunction<returnType(int32_t, int32_t, uint32_t)>(
+                               callFunction<ReturnType(int32_t, int32_t, uint32_t)>(
                                        self->atspi->eldbusConnection,
                                        ptr,
                                        "GetNavigableAtPoint",
-                               [self](Optional<returnType> value) {
+                               [self](ReturnType value) {
                                        process(std::move(self), std::move(value));
                                },
                                self->pt.x, self->pt.y, self->ctype);
@@ -757,11 +752,11 @@ void Atspi::getAtPoint(Point pt, CoordType type, const AtspiAccessiblePtr &root,
                }
        };
        auto h = std::make_shared<handler>(handler{this, pt, (uint32_t)ctype, std::move(callback)});
-       callFunction<std::tuple<AtspiAccessiblePtr, uint8_t, AtspiAccessiblePtr>(int32_t, int32_t, uint32_t)>(
+       callFunction<ReturnType(int32_t, int32_t, uint32_t)>(
                eldbusConnection,
                root,
                "GetNavigableAtPoint",
-       [h](Optional<returnType> value) {
+       [h](ReturnType value) {
                handler::process(std::move(h), std::move(value));
        },
        pt.x, pt.y, (uint32_t)ctype);
@@ -769,21 +764,24 @@ void Atspi::getAtPoint(Point pt, CoordType type, const AtspiAccessiblePtr &root,
 
 void Atspi::getAllAcceptedObjects(const AtspiAccessiblePtr &root, AsyncCallback<std::tuple<std::vector<AcceptedObjectInfo>, Rectangle>> callback) const
 {
-       using returnType = std::tuple<int32_t, int32_t, int32_t, int32_t,
+       using ReturnType = ValueOrError<int32_t, int32_t, int32_t, int32_t,
                  std::vector<std::tuple<AtspiAccessiblePtr, int32_t, int32_t, int32_t, int32_t>>>;
 
-       callFunction<returnType()>(
+       callFunction<ReturnType()>(
                eldbusConnection,
                root,
                "GetAllAcceptedObjects",
-       [ = ](Optional<returnType> value) {
+       [ = ](ReturnType value) {
                if (!value) {
-                       callback({});
+                       callback(Error { value.getError() });
                } else {
                        std::tuple<std::vector<AcceptedObjectInfo>, Rectangle> res;
                        auto &ois = std::get<0>(res);
-                       std::get<1>(res) = { { std::get<0>(*value), std::get<1>(*value) }, { std::get<2>(*value), std::get<3>(*value) }};
-                       auto &v = std::get<4>(*value);
+                       std::get<1>(res) = {
+                               { std::get<0>(value), std::get<1>(value) },
+                               { std::get<2>(value), std::get<3>(value) }
+                       };
+                       auto v = std::get<4>(value);
                        ois.reserve(v.size());
                        for (auto &a : v) {
                                ois.push_back({});
@@ -799,7 +797,7 @@ void Atspi::getAllAcceptedObjects(const AtspiAccessiblePtr &root, AsyncCallback<
 
 std::vector<std::pair<std::string, std::string>> Atspi::getAttributes(const AtspiAccessiblePtr &accessibleObj) const
 {
-       assert(accessibleObj == root_object);
+       ASSERT(accessibleObj == root_object);
        return root_object_attributes;
 #if 0
        // do not delete - root_* hack is temporary
index 141274e..156d4f3 100644 (file)
 #include <bitset>
 #include <array>
 
-namespace detail
-{
-       template <typename T>
-       struct AsyncCallbackHelper {
-               using type = std::function<void(Optional<T>)>;
-       };
-
-       template <>
-       struct AsyncCallbackHelper<void> {
-               using type = std::function<void(bool)>;
-       };
-}
-
 using AtspiAccessiblePtr = std::shared_ptr<AtspiAccessible>;
 using AtspiActionPtr = std::shared_ptr<AtspiAction>;
 using AtspiSelectionPtr = std::shared_ptr<AtspiSelection>;
 using AtspiCollectionPtr = std::shared_ptr<AtspiCollection>;
 using AtspiValuePtr = std::shared_ptr<AtspiValue>;
 using AtspiEditableTextPtr = std::shared_ptr<AtspiEditableText>;
-template <typename T> using AsyncCallback = typename detail::AsyncCallbackHelper<T>::type;
+template <typename ... T> using AsyncCallback = typename std::function<void(DBus::ValueOrError<T...>)>;
 
 class Atspi
 {
-       template <typename T> using AsyncCallback = typename detail::AsyncCallbackHelper<T>::type;
-
 public:
        using StateSet = std::bitset<ATSPI_STATE_LAST_DEFINED>;
 
@@ -176,11 +161,13 @@ public:
        void getMaximumValue(const std::shared_ptr<AtspiValue> &valueInterface, AsyncCallback<double> callback) const;
        void getMinimumValue(const std::shared_ptr<AtspiValue> &valueInterface, AsyncCallback<double> callback) const;
 
-#define ADD_INTROSPECTION_FUNCTIONS(TYPE, NAME)                                                 \
-       void get ## TYPE ## Interface(const AtspiAccessiblePtr &obj,              \
-                                                                 AsyncCallback<std::shared_ptr<Atspi ## TYPE>> callback) const;              \
-       std::string getUniqueId(const std::shared_ptr<Atspi ## TYPE> &obj) const;               \
-       AtspiAccessiblePtr getBase(const std::shared_ptr<Atspi ## TYPE> &) const;
+#define ADD_INTROSPECTION_FUNCTIONS(TYPE, NAME)                                                   \
+       void get ## TYPE ## Interface(const AtspiAccessiblePtr &obj,                                  \
+                                                                 AsyncCallback<std::shared_ptr<Atspi ## TYPE>> callback) const;  \
+       std::string getUniqueId(const std::shared_ptr<Atspi ## TYPE> &obj) const;                     \
+       AtspiAccessiblePtr getBase(const std::shared_ptr<Atspi ## TYPE> &) const;                     \
+       static std::string getBusName(const std::shared_ptr<Atspi ## TYPE> &o);                       \
+       static std::string getPath(const std::shared_ptr<Atspi ## TYPE> &o);
 
        ADD_INTROSPECTION_FUNCTIONS(Accessible, ACCESSIBLE);
        ADD_INTROSPECTION_FUNCTIONS(Action, ACTION);
@@ -198,8 +185,8 @@ private:
 
        efl::eldbus::eldbus_init eldbus;
        efl::eldbus::connection connection;
-       EldbusConnectionCallbackHandle eldbusConnection;
-       template <typename T> DBus getProxy(const std::shared_ptr<T> &obj, const std::string &interface) const;
+       DBus::EldbusConnectionCallbackHandle eldbusConnection;
+       template <typename T> DBus::DBusClient getProxy(const std::shared_ptr<T> &obj, const std::string &interface) const;
        template <typename T> void getInterface(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::shared_ptr<T>> callback) const;
        void getInterface(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<AtspiAccessiblePtr> callback) const
        {
index cd4ba99..c62f175 100644 (file)
@@ -20,7 +20,7 @@ public:
 
        bool process() override
        {
-               DBus dbus{dbusLocators::callmgr::BUS, dbusLocators::callmgr::OBJ_PATH, dbusLocators::callmgr::INTERFACE, DBus::ConnectionType::SYSTEM};
+               DBus::DBusClient dbus{dbusLocators::callmgr::BUS, dbusLocators::callmgr::OBJ_PATH, dbusLocators::callmgr::INTERFACE, DBus::ConnectionType::SYSTEM};
                dbus.method<int(int)>("AnswerCall").call(CM_TEL_CALL_ANSWER_TYPE_NORMAL);
                return true;
        }
@@ -37,7 +37,7 @@ public:
 
        bool process() override
        {
-               DBus dbus{dbusLocators::callmgr::BUS, dbusLocators::callmgr::OBJ_PATH, dbusLocators::callmgr::INTERFACE, DBus::ConnectionType::SYSTEM};
+               DBus::DBusClient dbus{dbusLocators::callmgr::BUS, dbusLocators::callmgr::OBJ_PATH, dbusLocators::callmgr::INTERFACE, DBus::ConnectionType::SYSTEM};
                dbus.method<int()>("RejectCall").call();
                return true;
        }
@@ -54,7 +54,7 @@ public:
 
        bool process() override
        {
-               DBus dbus{dbusLocators::callmgr::BUS, dbusLocators::callmgr::OBJ_PATH, dbusLocators::callmgr::INTERFACE, DBus::ConnectionType::SYSTEM};
+               DBus::DBusClient dbus{dbusLocators::callmgr::BUS, dbusLocators::callmgr::OBJ_PATH, dbusLocators::callmgr::INTERFACE, DBus::ConnectionType::SYSTEM};
                dbus.method<int(unsigned, int)>("EndCall").call(0, CM_TEL_CALL_RELEASE_TYPE_ALL_CALLS);
                return true;
        }
index f3d106d..39d4e29 100644 (file)
 #include <sstream>
 #include "Atspi.hpp"
 #include "Singleton.hpp"
+#include <mutex>
 
-unsigned int detail::CallId::LastId = 0;
+#define DBUS_DEBUG(...) do { DBus::debugPrint(__FILE__, __LINE__, __VA_ARGS__); } while (0)
 
-DBus::DBus()
+std::atomic<unsigned int> DBus::detail::CallId::LastId { 0 };
+static std::function<void(const char *, size_t)> debugPrinter;
+static std::mutex debugLock;
+
+void DBus::setDebugPrinter(std::function<void(const char *, size_t)> printer)
+{
+       std::lock_guard<std::mutex> lock(debugLock);
+       debugPrinter = std::move(printer);
+}
+
+void DBus::debugPrint(const char *file, size_t line, const char *format, ...)
+{
+       std::function<void(const char *, size_t)> debugPrintFunc;
+       {
+               std::lock_guard<std::mutex> lock(debugLock);
+               if (!debugPrinter) return;
+               debugPrintFunc = debugPrinter;
+       }
+       std::vector<char> buf(4096);
+       int offset;
+       while (true) {
+               offset = snprintf(buf.data(), buf.size(), "%s:%u: ", file, (unsigned int)line);
+               if (offset < 0) return;
+               if ((size_t)offset < buf.size()) break;
+               buf.resize(offset + 1);
+       }
+
+       while (true) {
+               va_list args;
+               va_start(args, format);
+               int z = vsnprintf(buf.data() + offset, buf.size(), format, args);
+               va_end(args);
+               if (z < 0) return;
+               if ((size_t)z + (size_t)offset < buf.size()) break;
+               buf.resize((size_t)z + (size_t)offset + 1);
+       }
+       debugPrintFunc(buf.data(), buf.size());
+}
+
+DBus::EldbusConnectionCallbackHandle DBus::getDBusConnectionByName(const std::string &name)
 {
+       eldbus_init();
+       auto z = getDBusConnectionByType(ConnectionType::SYSTEM);
+       auto connection = eldbus_address_connection_get(name.c_str());
+       return { connection, [](Eldbus_Connection * c)
+       {
+               eldbus_connection_unref(c);
+               eldbus_shutdown();
+       } };
 }
 
-static EldbusConnectionCallbackHandle getConnectionByType(DBus::ConnectionType connectionType)
+DBus::EldbusConnectionCallbackHandle DBus::getDBusConnectionByType(ConnectionType connectionType)
 {
+       Eldbus_Connection_Type eldbusType = ELDBUS_CONNECTION_TYPE_SYSTEM;
+
        switch (connectionType) {
-       case DBus::ConnectionType::SYSTEM:
-               return { eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM), eldbus_connection_unref };
-       case DBus::ConnectionType::SESSION:
-               return { eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION), eldbus_connection_unref };
+       case ConnectionType::SYSTEM:
+               eldbusType = ELDBUS_CONNECTION_TYPE_SYSTEM;
+               break;
+       case ConnectionType::SESSION:
+               eldbusType = ELDBUS_CONNECTION_TYPE_SESSION;
+               break;
        default:
                ASSERT(0);
        }
-       return {};
+
+       eldbus_init();
+       return { eldbus_connection_get(eldbusType), [](Eldbus_Connection * c)
+       {
+               eldbus_connection_unref(c);
+               eldbus_shutdown();
+       } };
 }
 
-DBus::DBus(std::string bus_name, std::string path_name, std::string interface_name, ConnectionType tp) :
-       DBus(std::move(bus_name), std::move(path_name), std::move(interface_name), getConnectionByType(tp))
+DBus::DBusClient::DBusClient(std::string bus_name, std::string path_name, std::string interface_name, ConnectionType tp) :
+       DBusClient(std::move(bus_name), std::move(path_name), std::move(interface_name), getDBusConnectionByType(tp))
 {
 }
 
-DBus::DBus(std::string bus_name, std::string path_name, std::string interface_name, const EldbusConnectionCallbackHandle &conn)
+struct caller_eldbus_object_unref {
+       void operator()(Eldbus_Object *p) const
+       {
+               eldbus_object_unref(p);
+       }
+};
+
+DBus::DBusClient::DBusClient(std::string bus_name, std::string path_name, std::string interface_name, const EldbusConnectionCallbackHandle &conn)
 {
        if (!conn)
-               connectionState.connection = getConnectionByType(DBus::ConnectionType::SESSION);
+               connectionState.connection = getDBusConnectionByType(ConnectionType::SESSION);
        else
                connectionState.connection = conn;
 
        std::ostringstream o;
        o << "bus = " << bus_name << " path = " << path_name << " connection = " <<
          eldbus_connection_unique_name_get(connectionState.connection.get());
-       if (interface_name != DBUS_INTERFACE_PROPERTIES)
-               o << " iname = " << interface_name;
        info = o.str();
-       connectionState.object = { eldbus_object_get(connectionState.connection.get(), bus_name.c_str(), path_name.c_str()), eldbus_object_unref };
+
+       auto c = connectionState.connection;
+       connectionState.object = {
+               eldbus_object_get(connectionState.connection.get(), bus_name.c_str(), path_name.c_str()),
+               [c](Eldbus_Object * p)
+               {
+                       eldbus_object_unref(p);
+               }
+       };
        if (connectionState.object) {
-               connectionState.proxy = { eldbus_proxy_get(connectionState.object.get(), interface_name.c_str()), eldbus_proxy_unref };
+               connectionState.proxy = eldbus_proxy_get(connectionState.object.get(), interface_name.c_str());
+               if (interface_name != DBUS_INTERFACE_PROPERTIES) {
+                       connectionState.propertiesProxy = eldbus_proxy_get(connectionState.object.get(), DBUS_INTERFACE_PROPERTIES);
+               } else {
+                       connectionState.propertiesProxy = connectionState.proxy;
+               }
        }
+       interfaceName = std::move(interface_name);
 }
 
-DBus::~DBus()
+DBus::DBusClient::~DBusClient()
 {
-       for (auto &p : callbacks)
-               delete p;
+       for (auto &p : destructors) {
+               p();
+       }
 }
 
-bool detail::signature<std::shared_ptr<AtspiAccessible>>::get(Eldbus_Message_Iter *iter, AtspiAccessiblePtr &v)
+bool DBus::detail::signature<std::shared_ptr<AtspiAccessible>>::get(Eldbus_Message_Iter *iter, AtspiAccessiblePtr &v)
 {
        subtype s;
        if (!signature<subtype>::get(iter, s)) return false;
        v = Singleton<Atspi>::instance().make(s.first.c_str(), s.second.c_str());
        return true;
 }
+
+DBus::DBusServer::DBusServer()
+{
+}
+
+DBus::DBusServer::DBusServer(ConnectionType tp) : DBus::DBusServer(DBus::getDBusConnectionByType(tp))
+{
+}
+
+DBus::DBusServer::DBusServer(const EldbusConnectionCallbackHandle &conn)
+{
+       if (!conn)
+               connection = getDBusConnectionByType(ConnectionType::SESSION);
+       else
+               connection = conn;
+}
+
+DBus::DBusServer::~DBusServer()
+{
+       for (auto &p : destructors)
+               p();
+}
+
+DBus::DBusInterfaceDescription::DBusInterfaceDescription(std::string interfaceName) : interfaceName(std::move(interfaceName))
+{
+}
+
+struct Implementation {
+       Eldbus_Service_Interface_Desc dsc;
+       std::vector<Eldbus_Method> methods;
+       std::vector<Eldbus_Signal> signals;
+       std::vector<Eldbus_Property> properties;
+       DBus::detail::StringStorage strings;
+
+       std::unordered_map<std::string, DBus::DBusInterfaceDescription::MethodInfo> methodsMap;
+       std::unordered_map<std::string, DBus::DBusInterfaceDescription::PropertyInfo> propertiesMap;
+       std::unordered_map<unsigned int, DBus::DBusInterfaceDescription::SignalInfo> signalsMap;
+
+       Eldbus_Connection *connection = nullptr;
+};
+
+static std::unordered_map<const Eldbus_Service_Interface *, std::unique_ptr<Implementation>> globalEntries;
+static std::mutex globalEntriesMutex;
+static thread_local const char *currentObjectPath = "";
+static thread_local Eldbus_Connection *currentConnection = nullptr;
+
+class CurrentObjectSetter
+{
+public:
+       CurrentObjectSetter(Eldbus_Connection *con, const Eldbus_Message *m)
+       {
+               currentObjectPath = eldbus_message_path_get(m);
+               currentConnection = con;
+       }
+       ~CurrentObjectSetter()
+       {
+               currentObjectPath = "";
+               currentConnection = nullptr;
+       }
+       CurrentObjectSetter(const CurrentObjectSetter &) = delete;
+       CurrentObjectSetter(CurrentObjectSetter &&) = delete;
+       void operator = (const CurrentObjectSetter &) = delete;
+       void operator = (CurrentObjectSetter &&) = delete;
+};
+
+std::string DBus::DBusServer::getCurrentObjectPath()
+{
+       return currentObjectPath;
+}
+
+DBus::EldbusConnectionCallbackHandle DBus::DBusServer::getCurrentConnection()
+{
+       auto c = currentConnection;
+       return { eldbus_connection_ref(c), detail::caller_eldbus_connection_unref() };
+}
+
+static Eina_Bool property_get_callback(const Eldbus_Service_Interface *iface, const char *propertyName, Eldbus_Message_Iter *iter,
+                                                                          const Eldbus_Message *message, Eldbus_Message **error)
+{
+       Implementation *impl = nullptr;
+       {
+               std::lock_guard<std::mutex> lock(globalEntriesMutex);
+               auto it = globalEntries.find(iface);
+               if (it != globalEntries.end()) impl = it->second.get();
+       }
+       if (!impl) return EINA_FALSE;
+
+       auto it = impl->propertiesMap.find(propertyName);
+       if (it == impl->propertiesMap.end() || !it->second.getCallback) return EINA_FALSE;
+
+       CurrentObjectSetter currentObjectSetter(impl->connection, message);
+       auto reply = it->second.getCallback(message, iter);
+       if (!reply) {
+               if (error)
+                       *error = eldbus_message_error_new(message, "org.freedesktop.DBus.Error.Failed", reply.getError().message.c_str());
+               return EINA_FALSE;
+       }
+
+       return EINA_TRUE;
+}
+
+static Eldbus_Message *property_set_callback(const Eldbus_Service_Interface *iface, const char *propertyName, Eldbus_Message_Iter *iter,
+               const Eldbus_Message *message)
+{
+       Implementation *impl = nullptr;
+       {
+               std::lock_guard<std::mutex> lock(globalEntriesMutex);
+               auto it = globalEntries.find(iface);
+               if (it != globalEntries.end()) impl = it->second.get();
+       }
+       if (!impl) {
+               auto ret = eldbus_message_error_new(message, "org.freedesktop.DBus.Error.Failed", "Unknown interface");
+               return ret;
+       }
+       auto it = impl->propertiesMap.find(propertyName);
+       if (it == impl->propertiesMap.end() || !it->second.setCallback) {
+               auto ret = eldbus_message_error_new(message, "org.freedesktop.DBus.Error.Failed", "Unknown setter");
+               return ret;
+       }
+       CurrentObjectSetter currentObjectSetter(impl->connection, message);
+       auto reply = it->second.setCallback(message, iter);
+
+       Eldbus_Message *ret = nullptr;
+       if (!reply) {
+               ret = eldbus_message_error_new(message, "org.freedesktop.DBus.Error.Failed", reply.getError().message.c_str());
+       } else {
+               ret = eldbus_message_method_return_new(message);
+       }
+       return ret;
+}
+
+static Eldbus_Message *method_callback(const Eldbus_Service_Interface *iface, const Eldbus_Message *message)
+{
+       Implementation *impl = nullptr;
+       {
+               std::lock_guard<std::mutex> lock(globalEntriesMutex);
+               auto it = globalEntries.find(iface);
+               if (it != globalEntries.end()) impl = it->second.get();
+       }
+       if (!impl) {
+               auto ret = eldbus_message_error_new(message, "org.freedesktop.DBus.Error.Failed", "Unknown interface");
+               return ret;
+       }
+       std::string memberName = eldbus_message_member_get(message);
+       auto it = impl->methodsMap.find(memberName);
+       if (it == impl->methodsMap.end()) {
+               auto ret = eldbus_message_error_new(message, "org.freedesktop.DBus.Error.Failed", "Unknown method");
+               return ret;
+       }
+       CurrentObjectSetter currentObjectSetter(impl->connection, message);
+       auto reply = it->second.callback(message);
+       return reply;
+}
+
+static void addInterfaceImpl(bool fallback, const std::string &pathName,
+                                                        const DBus::EldbusConnectionCallbackHandle &connection,
+                                                        const std::string &interfaceName,
+                                                        std::unordered_map<unsigned int, std::pair<const Eldbus_Service_Interface *, unsigned int>> &signalData,
+                                                        DBus::detail::StringStorage &strings,
+                                                        std::vector<DBus::DBusInterfaceDescription::MethodInfo> &dscrMethods,
+                                                        std::vector<DBus::DBusInterfaceDescription::PropertyInfo> &dscrProperties,
+                                                        std::vector<DBus::DBusInterfaceDescription::SignalInfo> &dscrSignals,
+                                                        std::vector<std::function<void()>> &destructors)
+{
+       std::vector<Eldbus_Method> methods;
+       std::vector<Eldbus_Signal> signals;
+       std::vector<Eldbus_Property> properties;
+       std::unordered_map<std::string, DBus::DBusInterfaceDescription::MethodInfo> methodsMap;
+       std::unordered_map<std::string, DBus::DBusInterfaceDescription::PropertyInfo> propertiesMap;
+       std::unordered_map<unsigned int, DBus::DBusInterfaceDescription::SignalInfo> signalsMap;
+
+       DBUS_DEBUG("interface %s path %s on bus %s", interfaceName.c_str(), pathName.c_str(), DBus::getConnectionName(connection).c_str());
+       for (auto &ee : dscrMethods) {
+               auto key = ee.memberName;
+               DBUS_DEBUG("adding method %s", ee.memberName.c_str());
+               for (auto &r : ee.in) {
+                       if (!r.name) break;
+                       DBUS_DEBUG("in %s '%s'", r.name, r.signature);
+               }
+               for (auto &r : ee.out) {
+                       if (!r.name) break;
+                       DBUS_DEBUG("out %s '%s'", r.name, r.signature);
+               }
+               auto &e = (methodsMap[key] = std::move(ee));
+               methods.push_back({});
+               auto &m = methods.back();
+               m.member = e.memberName.c_str();
+               m.in = e.in.data();
+               m.out = e.out.data();
+               m.cb = method_callback;
+               m.flags = 0;
+       }
+       for (auto &ee : dscrProperties) {
+               auto key = ee.memberName;
+               DBUS_DEBUG("adding property %s", ee.memberName.c_str());
+               auto &e = (propertiesMap[key] = std::move(ee));
+               properties.push_back({});
+               auto &m = properties.back();
+               m.name = e.memberName.c_str();
+               m.type = e.typeSignature.c_str();
+               m.get_func = e.getCallback ? property_get_callback : nullptr;
+               m.set_func = e.setCallback ? property_set_callback : nullptr;
+               m.flags = 0;
+       }
+       unsigned int signalIndex = 0;
+       std::vector<unsigned int> signalIds;
+       for (auto &ee : dscrSignals) {
+               DBUS_DEBUG("adding signal %s", ee.memberName.c_str());
+               auto &e = (signalsMap[ee.id.id] = std::move(ee));
+               signals.push_back({});
+               auto &m = signals.back();
+               m.name = e.memberName.c_str();
+               m.args = e.args.data();
+               m.flags = 0;
+               signalData[e.id.id].second = signalIndex++;
+               signalIds.push_back(e.id.id);
+       }
+       dscrMethods.clear();
+       dscrProperties.clear();
+       dscrSignals.clear();
+
+       methods.push_back({ nullptr, nullptr, nullptr, nullptr, 0 });
+       signals.push_back({ nullptr, nullptr, 0 });
+       properties.push_back({ nullptr, nullptr, nullptr, nullptr, 0 });
+
+       auto impl = std::make_unique<Implementation>(Implementation{
+               {
+                       interfaceName.c_str(),
+                       methods.data(),
+                       signals.data(),
+                       properties.data(),
+                       nullptr,
+                       nullptr
+               },
+               std::move(methods),
+               std::move(signals),
+               std::move(properties),
+               std::move(strings),
+               std::move(methodsMap),
+               std::move(propertiesMap),
+               std::move(signalsMap),
+               connection.get()
+       });
+
+       {
+               std::lock_guard<std::mutex> lock(globalEntriesMutex);
+               auto v = fallback ?
+                                eldbus_service_interface_fallback_register(connection.get(), pathName.c_str(), &impl->dsc) :
+                                eldbus_service_interface_register(connection.get(), pathName.c_str(), &impl->dsc);
+               ASSERT(v);
+               globalEntries[v] = std::move(impl);
+               DBUS_DEBUG("registering interface %p (%d)", v, fallback ? 1 : 0);
+               destructors.push_back([ = ]() {
+                       eldbus_service_interface_unregister(v);
+                       std::lock_guard<std::mutex> lock(globalEntriesMutex);
+                       globalEntries.erase(v);
+               });
+               for (auto id : signalIds) {
+                       signalData[id].first = v;
+               }
+       }
+}
+
+void DBus::DBusServer::addInterface(const std::string &pathName, DBusInterfaceDescription &dscr, bool fallback)
+{
+       addInterfaceImpl(fallback, pathName, connection, dscr.interfaceName, signalData, dscr.strings, dscr.methods, dscr.properties, dscr.signals, destructors);
+}
+
+std::string DBus::DBusServer::getBusName() const
+{
+       return getConnectionName(connection);
+}
+
+std::string DBus::getConnectionName(const EldbusConnectionCallbackHandle &c)
+{
+       return eldbus_connection_unique_name_get(c.get());
+}
index 867ce18..954f62c 100644 (file)
 #include <unordered_map>
 #include <map>
 #include <array>
+#include <sstream>
+#include <type_traits>
+#include <tuple>
+#include <atomic>
 
-namespace detail
+#define DBUS_DEBUG(...) do { DBus::debugPrint(__FILE__, __LINE__, __VA_ARGS__); } while(0)
+
+namespace DBus
 {
-       struct caller_eldbus_connection_unref {
-               void operator()(Eldbus_Connection *p) const
-               {
-                       eldbus_connection_unref(p);
-               }
-       };
+       class DBusServer;
+       class DBusClient;
+       class DBusInterfaceDescription;
 
-       struct caller_eldbus_message_unref {
-               void operator()(Eldbus_Message *p) const
-               {
-                       eldbus_message_unref(p);
-               }
-       };
+       void debugPrint(const char *file, size_t line, const char *format, ...);
+       void setDebugPrinter(std::function<void(const char *, size_t)>);
 
-       struct caller_eldbus_object_unref {
-               void operator()(Eldbus_Object *p) const
-               {
-                       eldbus_object_unref(p);
-               }
-       };
+       struct Error {
+               std::string message;
 
-       struct caller_eldbus_proxy_unref {
-               void operator()(Eldbus_Proxy *p) const
+               Error() = default;
+               Error(std::string msg) : message(std::move(msg))
                {
-                       eldbus_proxy_unref(p);
+                       ASSERT(!message.empty());
                }
        };
-}
-
-template <typename A> struct EldbusVariant {
-       A value;
-};
-
-using EldbusConnectionCallbackHandle = std::shared_ptr<Eldbus_Connection>;
-using EldbusMessageCallbackHandle = std::unique_ptr<Eldbus_Message, detail::caller_eldbus_message_unref>;
-using EldbusObjectCallbackHandle = std::shared_ptr<Eldbus_Object>;
-using EldbusProxyCallbackHandle = std::shared_ptr<Eldbus_Proxy>;
-
-namespace detail
-{
-       template <typename T, typename = void> struct signature;
-       template <typename ... ARGS> struct signature<std::tuple<ARGS...>>;
-       template <typename A, typename B> struct signature<std::pair<A, B>>;
-       template <typename A> struct signature<std::vector<A>>;
-       template <typename A, typename B> struct signature<std::vector<std::pair<A, B>>>;
-       template <typename A, size_t N> struct signature<std::array<A, N>>;
-       template <typename A, typename B, size_t N> struct signature<std::array<std::pair<A, B>, N>>;
-       template <typename A, typename B> struct signature<std::unordered_map<A, B>>;
-       template <typename A, typename B> struct signature<std::map<A, B>>;
+       struct Success { };
 
-       template <typename T> struct signature<T, typename std::enable_if<std::is_enum<T>::value, void>::type> {
-               static std::string sig()
+       template <typename ... ARGS> class ValueOrError
+       {
+       public:
+               ValueOrError() = default;
+               ValueOrError(ARGS ... t) : value(std::move(t)...) { }
+               ValueOrError(std::tuple<ARGS...> t) : value(std::move(t)) { }
+               ValueOrError(Error e) : error(std::move(e))
                {
-                       return "i";
+                       ASSERT(!error.message.empty());
                }
-               static void set(Eldbus_Message_Iter *iter, T v)
-               {
-                       // TODO: add check for failure in marshalling arguments
 
-                       eldbus_message_iter_arguments_append(iter, sig().c_str(), (int)v);
-               }
-               static bool get(Eldbus_Message_Iter *iter, T &v)
+               explicit operator bool () const
                {
-                       int q;
-                       auto z = eldbus_message_iter_get_and_next(iter, sig()[0], &q);
-                       v = (T)q;
-                       return z;
+                       return error.message.empty();
                }
-       };
-
-#define SIGNATURE(T, S)                                                    \
-       template <> struct signature<T> {                                      \
-               static std::string sig() { return #S; }                            \
-               static void set(Eldbus_Message_Iter *iter, T v) {                  \
-                       eldbus_message_iter_arguments_append(iter, sig().c_str(), v);  \
-               }                                                                  \
-               static bool get(Eldbus_Message_Iter *iter, T &v) {                 \
-                       return eldbus_message_iter_get_and_next(iter, sig()[0], &v);   \
-               }                                                                  \
-       };
-       SIGNATURE(uint8_t, y);
-       SIGNATURE(uint16_t, q);
-       SIGNATURE(uint32_t, u);
-       SIGNATURE(uint64_t, t);
-       SIGNATURE(int16_t, n);
-       SIGNATURE(int32_t, i);
-       SIGNATURE(int64_t, x);
-       SIGNATURE(double, d);
-#undef SIGNATURE
-
-       template <> struct signature<bool> {
-               static std::string sig()
+               const Error &getError() const
                {
-                       return "b";
+                       return error;
                }
-               static void set(Eldbus_Message_Iter *iter, bool v)
+               std::tuple<ARGS...> &getValues()
                {
-                       eldbus_message_iter_arguments_append(iter, sig().c_str(), v ? 1 : 0);
+                       ASSERT(*this);
+                       return value;
                }
-               static bool get(Eldbus_Message_Iter *iter, bool &v)
+               const std::tuple<ARGS...> &getValues() const
                {
-                       unsigned char q;
-                       auto z = eldbus_message_iter_get_and_next(iter, sig()[0], &q);
-                       v = q != 0;
-                       return z;
+                       ASSERT(*this);
+                       return value;
                }
+       protected:
+               std::tuple<ARGS...> value;
+               Error error;
        };
-       template <> struct signature<std::string> {
-               static std::string sig()
-               {
-                       return "s";
-               }
-               static void set(Eldbus_Message_Iter *iter, const std::string &v)
-               {
-                       eldbus_message_iter_arguments_append(iter, sig().c_str(), v.c_str());
-               }
-               static void set(Eldbus_Message_Iter *iter, const char *v)
-               {
-                       eldbus_message_iter_arguments_append(iter, sig().c_str(), v);
-               }
-               static bool get(Eldbus_Message_Iter *iter, std::string &v)
-               {
-                       const char *q;
-                       if (!eldbus_message_iter_get_and_next(iter, 's', &q)) {
-                               if (!eldbus_message_iter_get_and_next(iter, 'o', &q))
-                                       return false;
-                       }
-                       v = q;
-                       return true;
-               }
-       };
-       template <size_t INDEX, typename A, typename ... ARGS> struct signature_tuple_element_type_helper {
-               using type = typename signature_tuple_element_type_helper < INDEX - 1, ARGS... >::type;
-       };
-       template <typename A, typename ... ARGS> struct signature_tuple_element_type_helper<0, A, ARGS...> {
-               using type = A;
-       };
-
-       template <size_t INDEX, size_t SIZE, typename ... ARGS> struct signature_tuple_helper {
-               using current_type = typename signature_tuple_element_type_helper<INDEX, ARGS...>::type;
 
-               static std::string sig()
-               {
-                       return signature<current_type>::sig() + signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::sig();
-               }
-               static void set(Eldbus_Message_Iter *iter, const std::tuple<ARGS...> &args)
+       template <> class ValueOrError<>
+       {
+       public:
+               ValueOrError() = default;
+               ValueOrError(std::tuple<> t) { }
+               ValueOrError(Error e) : error(std::move(e))
                {
-                       signature<current_type>::set(iter, std::get<INDEX>(args));
-                       signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::set(iter, args);
+                       ASSERT(!error.message.empty());
                }
-               static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
+
+               explicit operator bool () const
                {
-                       return signature<current_type>::get(iter, std::get<INDEX>(args)) &&
-                                  signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::get(iter, args);
+                       return error.message.empty();
                }
-       };
-       template <size_t SIZE, typename ... ARGS> struct signature_tuple_helper<SIZE, SIZE, ARGS...> {
-               static std::string sig()
+               const Error &getError() const
                {
-                       return "";
+                       return error;
                }
-               static void set(Eldbus_Message_Iter *iter, const std::tuple<ARGS...> &args)
+               std::tuple<> &getValues()
                {
+                       ASSERT(*this);
+                       static std::tuple<> t;
+                       return t;
                }
-               static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
+               std::tuple<> getValues() const
                {
-                       return true;
+                       ASSERT(*this);
+                       return {};
                }
+       protected:
+               Error error;
        };
-       template <typename ... ARGS> struct signature<std::tuple<ARGS...>> {
-               static std::string sig()
+
+       template <> class ValueOrError<void>
+       {
+       public:
+               ValueOrError() = default;
+               ValueOrError(Success) { }
+               ValueOrError(Error e) : error(std::move(e))
                {
-                       return "(" + signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig() + ")";
+                       ASSERT(!error.message.empty());
                }
-               static void set(Eldbus_Message_Iter *iter, const std::tuple<ARGS...> &args)
+
+               explicit operator bool () const
                {
-                       auto entry = eldbus_message_iter_container_new(iter, 'r', NULL);
-                       signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::set(entry, args);
-                       eldbus_message_iter_container_close(iter, entry);
+                       return error.message.empty();
                }
-               static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
+               const Error &getError() const
                {
-                       Eldbus_Message_Iter *entry;
-                       if (!eldbus_message_iter_get_and_next(iter, 'r', &entry)) return false;
-                       auto z = signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::get(entry, args);
-                       eldbus_message_iter_container_close(iter, entry);
-                       return z;
+                       return error;
                }
-       };
-       template <typename A, typename B> struct signature<std::pair<A, B>> {
-               static std::string sig()
+               std::tuple<> &getValues()
                {
-                       return "(" + signature_tuple_helper<0, 2, A, B>::sig() + ")";
+                       ASSERT(*this);
+                       static std::tuple<> t;
+                       return t;
                }
-               static void set(Eldbus_Message_Iter *iter, const std::pair<A, B> &ab, char sg = 'r')
+               std::tuple<> getValues() const
                {
-                       auto entry = eldbus_message_iter_container_new(iter, sg, NULL);
-                       signature_tuple_helper<0, 2, A, B>::set(entry, ab);
-                       eldbus_message_iter_container_close(iter, entry);
+                       ASSERT(*this);
+                       return {};
                }
-               static bool get(Eldbus_Message_Iter *iter, std::pair<A, B> &ab, char sg = 'r')
-               {
-                       Eldbus_Message_Iter *entry;
-                       if (!eldbus_message_iter_get_and_next(iter, sg, &entry)) return false;
-                       std::tuple<A, B> ab_tmp;
-                       auto z = signature_tuple_helper<0, 2, A, B>::get(entry, ab_tmp);
-                       if (z) {
-                               ab.first = std::move(std::get<0>(ab_tmp));
-                               ab.second = std::move(std::get<1>(ab_tmp));
+       protected:
+               Error error;
+       };
+
+       namespace detail
+       {
+               struct caller_eldbus_connection_unref {
+                       void operator()(Eldbus_Connection *p) const
+                       {
+                               eldbus_connection_unref(p);
                        }
-                       eldbus_message_iter_container_close(iter, entry);
-                       return z;
-               }
+               };
+
+               struct caller_eldbus_message_unref {
+                       void operator()(Eldbus_Message *p) const
+                       {
+                               eldbus_message_unref(p);
+                       }
+               };
+
+               struct caller_eldbus_proxy_unref {
+                       void operator()(Eldbus_Proxy *p) const
+                       {
+                               eldbus_proxy_unref(p);
+                       }
+               };
+       }
+
+       template <typename A> struct EldbusVariant {
+               A value;
        };
-       template <typename A> struct signature<std::vector<A>> {
-               static std::string sig()
-               {
-                       return "a" + signature<A>::sig();
-               }
-               static void set(Eldbus_Message_Iter *iter, const std::vector<A> &v)
-               {
-                       auto lst = eldbus_message_iter_container_new(iter, 'a', signature<A>::sig().c_str());
-                       ASSERT(lst);
-                       for (auto &a : v) {
-                               signature<A>::set(lst, a);
+
+       using EldbusConnectionCallbackHandle = std::shared_ptr<Eldbus_Connection>;
+       using EldbusMessageCallbackHandle = std::unique_ptr<Eldbus_Message, detail::caller_eldbus_message_unref>;
+       using EldbusObjectCallbackHandle = std::shared_ptr<Eldbus_Object>;
+
+       namespace detail
+       {
+               template <typename T, typename = void> struct signature;
+               template <typename ... ARGS> struct signature<std::tuple<ARGS...>>;
+               template <typename A, typename B> struct signature<std::pair<A, B>>;
+               template <typename A> struct signature<std::vector<A>>;
+               template <typename A, size_t N> struct signature<std::array<A, N>>;
+               template <typename A, typename B> struct signature<std::unordered_map<A, B>>;
+               template <typename A, typename B> struct signature<std::map<A, B>>;
+
+               template <typename T> struct signature<T, typename std::enable_if<std::is_enum<T>::value, void>::type> {
+                       static std::string name()
+                       {
+                               return "enum";
                        }
-                       eldbus_message_iter_container_close(iter, lst);
-               }
-               static bool get(Eldbus_Message_Iter *iter, std::vector<A> &v)
-               {
-                       Eldbus_Message_Iter *s;
-                       v.clear();
-                       if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
-                       A a;
-                       while (signature<A>::get(s, a))
-                               v.push_back(std::move(a));
-                       return true;
-               }
+                       static std::string sig()
+                       {
+                               // TODO: add check for failure in marshalling arguments
+                               return "i";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, T v)
+                       {
+                               eldbus_message_iter_arguments_append(iter, sig().c_str(), (int)v);
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, T &v)
+                       {
+                               int q;
+                               auto z = eldbus_message_iter_get_and_next(iter, sig()[0], &q);
+                               v = (T)q;
+                               return z;
+                       }
+               };
+
+#define SIGNATURE(T, S)                                                    \
+       template <> struct signature<T> {                                      \
+               static std::string name() { return #T; }                           \
+               static std::string sig() { return #S; }                            \
+               static void set(Eldbus_Message_Iter *iter, T v) {                  \
+                       eldbus_message_iter_arguments_append(iter, sig().c_str(), v);  \
+               }                                                                  \
+               static bool get(Eldbus_Message_Iter *iter, T &v) {                 \
+                       return eldbus_message_iter_get_and_next(iter, sig()[0], &v);   \
+               }                                                                  \
        };
-       template <typename A, size_t N> struct signature<std::array<A, N>> {
-               static std::string sig()
-               {
-                       return "a" + signature<A>::sig();
-               }
-               static void set(Eldbus_Message_Iter *iter, const std::array<A, N> &v)
-               {
-                       auto lst = eldbus_message_iter_container_new(iter, 'a', signature<A>::sig().c_str());
-                       assert(lst);
-                       for (auto &a : v) {
-                               signature<A>::set(lst, a);
+               SIGNATURE(uint8_t, y);
+               SIGNATURE(uint16_t, q);
+               SIGNATURE(uint32_t, u);
+               SIGNATURE(uint64_t, t);
+               SIGNATURE(int16_t, n);
+               SIGNATURE(int32_t, i);
+               SIGNATURE(int64_t, x);
+               SIGNATURE(double, d);
+#undef SIGNATURE
+
+               template <> struct signature<bool> {
+                       static std::string name()
+                       {
+                               return "bool";
                        }
-                       eldbus_message_iter_container_close(iter, lst);
-               }
-               static bool get(Eldbus_Message_Iter *iter, std::array<A, N> &v)
+                       static std::string sig()
+                       {
+                               return "b";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, bool v)
+                       {
+                               eldbus_message_iter_arguments_append(iter, sig().c_str(), v ? 1 : 0);
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, bool &v)
+                       {
+                               unsigned char q;
+                               auto z = eldbus_message_iter_get_and_next(iter, sig()[0], &q);
+                               v = q != 0;
+                               return z;
+                       }
+               };
+               template <> struct signature<std::string> {
+                       static std::string name()
+                       {
+                               return "string";
+                       }
+                       static std::string sig()
+                       {
+                               return "s";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const std::string &v)
+                       {
+                               eldbus_message_iter_arguments_append(iter, sig().c_str(), v.c_str());
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const char *v)
+                       {
+                               eldbus_message_iter_arguments_append(iter, sig().c_str(), v);
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, std::string &v)
+                       {
+                               const char *q;
+                               if (!eldbus_message_iter_get_and_next(iter, 's', &q)) {
+                                       if (!eldbus_message_iter_get_and_next(iter, 'o', &q))
+                                               return false;
+                               }
+                               v = q;
+                               return true;
+                       }
+               };
+               template <> struct signature<char *> {
+                       static std::string name()
+                       {
+                               return "string";
+                       }
+                       static std::string sig()
+                       {
+                               return "s";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const std::string &v)
+                       {
+                               eldbus_message_iter_arguments_append(iter, sig().c_str(), v.c_str());
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const char *v)
+                       {
+                               eldbus_message_iter_arguments_append(iter, sig().c_str(), v);
+                       }
+               };
+               template <size_t N> struct signature<char [N]> {
+                       static std::string name()
+                       {
+                               return "string";
+                       }
+                       static std::string sig()
+                       {
+                               return "s";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const std::string &v)
+                       {
+                               eldbus_message_iter_arguments_append(iter, sig().c_str(), v.c_str());
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const char *v)
+                       {
+                               eldbus_message_iter_arguments_append(iter, sig().c_str(), v);
+                       }
+               };
+               template <size_t INDEX, typename A, typename ... ARGS> struct signature_tuple_element_type_helper {
+                       using type = typename signature_tuple_element_type_helper < INDEX - 1, ARGS... >::type;
+               };
+               template <typename A, typename ... ARGS> struct signature_tuple_element_type_helper<0, A, ARGS...> {
+                       using type = A;
+               };
+
+               template <size_t INDEX, size_t SIZE, typename ... ARGS> struct signature_tuple_helper {
+                       using current_type = typename signature_tuple_element_type_helper<INDEX, ARGS...>::type;
+
+                       static std::string name()
+                       {
+                               if (INDEX + 1 >= SIZE)
+                                       return signature<current_type>::name();
+                               return signature<current_type>::name() + ", " + signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::name();
+                       }
+                       static std::string sig()
+                       {
+                               return signature<current_type>::sig() + signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::sig();
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const std::tuple<ARGS...> &args)
+                       {
+                               signature<current_type>::set(iter, std::get<INDEX>(args));
+                               signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::set(iter, args);
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
+                       {
+                               return signature<current_type>::get(iter, std::get<INDEX>(args)) &&
+                                          signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::get(iter, args);
+                       }
+               };
+               template <size_t SIZE, typename ... ARGS> struct signature_tuple_helper<SIZE, SIZE, ARGS...> {
+                       static std::string name()
+                       {
+                               return "";
+                       }
+                       static std::string sig()
+                       {
+                               return "";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const std::tuple<ARGS...> &args)
+                       {
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
+                       {
+                               return true;
+                       }
+               };
+               template <typename ... ARGS> struct signature<std::tuple<ARGS...>> {
+                       static std::string name()
+                       {
+                               return "tuple<" + signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::name() + ">";
+                       }
+                       static std::string sig()
+                       {
+                               return "(" + signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig() + ")";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const std::tuple<ARGS...> &args)
+                       {
+                               auto entry = eldbus_message_iter_container_new(iter, 'r', NULL);
+                               signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::set(entry, args);
+                               eldbus_message_iter_container_close(iter, entry);
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
+                       {
+                               Eldbus_Message_Iter *entry;
+                               if (!eldbus_message_iter_get_and_next(iter, 'r', &entry)) return false;
+                               auto z = signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::get(entry, args);
+                               return z;
+                       }
+               };
+               template <typename ... ARGS> struct signature<ValueOrError<ARGS...>> {
+                       static std::string name()
+                       {
+                               return "ValueOrError<" + signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::name() + ">";
+                       }
+                       static std::string sig()
+                       {
+                               return signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig();
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const ValueOrError<ARGS...> &args)
+                       {
+                               signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::set(iter, args.getValues());
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, ValueOrError<ARGS...> &args)
+                       {
+                               return signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::get(iter, args.getValues());
+                       }
+               };
+               template <> struct signature<ValueOrError<void>> {
+                       static std::string name()
+                       {
+                               return "";
+                       }
+                       static std::string sig()
+                       {
+                               return "";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const ValueOrError<void> &args)
+                       {
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, ValueOrError<void> &args)
+                       {
+                               return true;
+                       }
+               };
+               template <typename A, typename B> struct signature<std::pair<A, B>> {
+                       static std::string name()
+                       {
+                               return "pair<" + signature_tuple_helper<0, 2, A, B>::name() + ">";
+                       }
+                       static std::string sig()
+                       {
+                               return "(" + signature_tuple_helper<0, 2, A, B>::sig() + ")";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const std::pair<A, B> &ab, bool dictionary = false)
+                       {
+                               auto entry = eldbus_message_iter_container_new(iter, dictionary ? 'e' : 'r', NULL);
+                               signature_tuple_helper<0, 2, A, B>::set(entry, ab);
+                               eldbus_message_iter_container_close(iter, entry);
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, std::pair<A, B> &ab)
+                       {
+                               char sg = 'r';
+                               char *t = eldbus_message_iter_signature_get(iter);
+                               if (t && t[0] == '{')
+                                       sg = '{';
+                               free(t);
+
+                               Eldbus_Message_Iter *entry;
+                               if (!eldbus_message_iter_get_and_next(iter, sg, &entry)) return false;
+                               std::tuple<A, B> ab_tmp;
+                               auto z = signature_tuple_helper<0, 2, A, B>::get(entry, ab_tmp);
+                               if (z) {
+                                       ab.first = std::move(std::get<0>(ab_tmp));
+                                       ab.second = std::move(std::get<1>(ab_tmp));
+                               }
+                               return z;
+                       }
+               };
+               template <typename A> struct signature<std::vector<A>> {
+                       static std::string name()
+                       {
+                               return "vector<" + signature<A>::name() + ">";
+                       }
+                       static std::string sig()
+                       {
+                               return "a" + signature<A>::sig();
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const std::vector<A> &v)
+                       {
+                               auto lst = eldbus_message_iter_container_new(iter, 'a', signature<A>::sig().c_str());
+                               ASSERT(lst);
+                               for (auto &a : v) {
+                                       signature<A>::set(lst, a);
+                               }
+                               eldbus_message_iter_container_close(iter, lst);
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, std::vector<A> &v)
+                       {
+                               Eldbus_Message_Iter *s;
+                               v.clear();
+                               if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
+                               A a;
+                               while (signature<A>::get(s, a))
+                                       v.push_back(std::move(a));
+
+                               return true;
+                       }
+               };
+               template <typename A, size_t N> struct signature<std::array<A, N>> {
+                       static std::string name()
+                       {
+                               return "array<" + signature<A>::name() + ", " + std::to_string(N) + ">";
+                       }
+                       static std::string sig()
+                       {
+                               return "a" + signature<A>::sig();
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const std::array<A, N> &v)
+                       {
+                               auto lst = eldbus_message_iter_container_new(iter, 'a', signature<A>::sig().c_str());
+                               ASSERT(lst);
+                               for (auto &a : v) {
+                                       signature<A>::set(lst, a);
+                               }
+                               eldbus_message_iter_container_close(iter, lst);
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, std::array<A, N> &v)
+                       {
+                               Eldbus_Message_Iter *s;
+                               if (!eldbus_message_iter_get_and_next(iter, 'a', &s))
+                                       return false;
+                               for (auto &a : v) {
+                                       if (!signature<A>::get(s, a))
+                                               return false;
+                               }
+                               return true;
+                       }
+               };
+               template <typename A> struct signature<EldbusVariant<A>> {
+                       static std::string name()
+                       {
+                               return "variant<" + signature<A>::name() + ">";
+                       }
+                       static std::string sig()
+                       {
+                               return "v";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const EldbusVariant<A> &v)
+                       {
+                               set(iter, v.value);
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const A &v)
+                       {
+                               auto var = eldbus_message_iter_container_new(iter, 'v', signature<A>::sig().c_str());
+                               signature<A>::set(var, v);
+                               eldbus_message_iter_container_close(iter, var);
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, EldbusVariant<A> &v)
+                       {
+                               Eldbus_Message_Iter *s;
+                               if (!eldbus_message_iter_get_and_next(iter, 'v', &s)) return false;
+                               return signature<A>::get(s, v.value);
+                       }
+               };
+               template <typename A, typename B> struct signature<std::unordered_map<A, B>> {
+                       static std::string name()
+                       {
+                               return "unordered_map<" + signature<A>::name() + ", " + signature<B>::name() + ">";
+                       }
+                       static std::string sig()
+                       {
+                               return "a{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const std::unordered_map<A, B> &v)
+                       {
+                               auto sig = "{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
+                               auto lst = eldbus_message_iter_container_new(iter, 'a', sig.c_str());
+                               ASSERT(lst);
+                               for (auto &a : v) {
+                                       signature<std::pair<A, B>>::set(lst, a, true);
+                               }
+                               eldbus_message_iter_container_close(iter, lst);
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, std::unordered_map<A, B> &v)
+                       {
+                               Eldbus_Message_Iter *s;
+                               v.clear();
+                               if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
+                               std::pair<A, B> a;
+                               while (signature<std::pair<A, B>>::get(s, a))
+                                       v.insert(std::move(a));
+                               return true;
+                       }
+               };
+               template <typename A, typename B> struct signature<std::map<A, B>> {
+                       static std::string name()
+                       {
+                               return "map<" + signature<A>::name() + ", " + signature<B>::name() + ">";
+                       }
+                       static std::string sig()
+                       {
+                               return "a{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const std::map<A, B> &v)
+                       {
+                               auto sig = "{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
+                               auto lst = eldbus_message_iter_container_new(iter, 'a', sig.c_str());
+                               ASSERT(lst);
+                               for (auto &a : v) {
+                                       signature<std::pair<A, B>>::set(lst, a, true);
+                               }
+                               eldbus_message_iter_container_close(iter, lst);
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, std::map<A, B> &v)
+                       {
+                               Eldbus_Message_Iter *s;
+                               if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
+                               std::pair<A, B> a;
+                               while (signature<std::pair<A, B>>::get(s, a))
+                                       v.insert(std::move(a));
+                               return true;
+                       }
+               };
+               template <typename A> struct signature<const A &> {
+                       static std::string name()
+                       {
+                               return "const " + signature<A>::name() + "&";
+                       }
+                       static std::string sig()
+                       {
+                               return signature<A>::sig();
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const A &v)
+                       {
+                               signature<A>::set(iter, v);
+                       }
+                       static void get(Eldbus_Message_Iter *iter, A &v)
+                       {
+                               signature<A>::get(iter, v);
+                       }
+               };
+               template <typename A> struct signature<A &> {
+                       static std::string name()
+                       {
+                               return signature<A>::name() + "&";
+                       }
+                       static std::string sig()
+                       {
+                               return signature<A>::sig();
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const A &v)
+                       {
+                               signature<A>::set(iter, v);
+                       }
+                       static void get(Eldbus_Message_Iter *iter, A &v)
+                       {
+                               signature<A>::get(iter, v);
+                       }
+               };
+               template <typename A> struct signature<const A> {
+                       static std::string name()
+                       {
+                               return "const " + signature<A>::name();
+                       }
+                       static std::string sig()
+                       {
+                               return signature<A>::sig();
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const A &v)
+                       {
+                               signature<A>::set(iter, v);
+                       }
+                       static void get(Eldbus_Message_Iter *iter, A &v)
+                       {
+                               signature<A>::get(iter, v);
+                       }
+               };
+               template <> struct signature<std::shared_ptr<AtspiAccessible>> {
+                       using subtype = std::pair<std::string, std::string>;
+
+                       static std::string name()
+                       {
+                               return "AtspiAccessiblePtr";
+                       }
+                       static std::string sig()
+                       {
+                               return "(so)";
+                       }
+                       static void set(Eldbus_Message_Iter *iter, const std::shared_ptr<AtspiAccessible> &v)
+                       {
+                               const auto prefixPath = "/org/a11y/atspi/accessible/";
+                               const auto nullPath = "/org/a11y/atspi/null";
+
+                               if (v) {
+                                       auto bus = atspi_accessible_get_bus_name(v.get(), NULL);
+                                       auto path = atspi_accessible_get_path(v.get(), NULL);
+                                       signature<subtype>::set(iter, { bus, std::string{prefixPath} + path });
+                                       g_free(path);
+                                       g_free(bus);
+                               } else {
+                                       signature<subtype>::set(iter, { {}, std::string{nullPath} });
+                               }
+                       }
+                       static bool get(Eldbus_Message_Iter *iter, std::shared_ptr<AtspiAccessible> &v);
+               };
+               struct CallId {
+                       friend class ::DBus::DBusServer;
+                       friend class ::DBus::DBusClient;
+                       friend class ::DBus::DBusInterfaceDescription;
+                       static std::atomic<unsigned int> LastId;
+                       unsigned int id = ++LastId;
+               };
+               template <typename ValueType> ValueType unpackValues(CallId callId, const Eldbus_Message *msg)
                {
-                       Eldbus_Message_Iter *s;
-                       if (!eldbus_message_iter_get_and_next(iter, 'a', &s))
-                               return false;
-                       for (auto &a : v) {
-                               if (!signature<A>::get(s, a))
-                                       return false;
+                       auto iter = eldbus_message_iter_get(msg);
+                       ValueType r;
+
+                       if (iter) {
+                               if (!signature<ValueType>::get(iter, r)) {
+                                       DBUS_DEBUG("ValueType is %s", signature<ValueType>::name().c_str());
+                                       r = Error { "call " + std::to_string(callId.id) + ": failed to unpack values, got signature '" +
+                                                               eldbus_message_signature_get(msg) + "', expected '" + signature<ValueType>::sig() + "'" };
+                               }
+                       } else {
+                               r = Error { "call " + std::to_string(callId.id) + ": failed to get iterator" };
                        }
-                       return true;
+                       return r;
                }
-       };
-       template <typename A, typename B, size_t N> struct signature<std::array<std::pair<A, B>, N>> {
-               static std::string sig()
+               inline void packValues_helper(Eldbus_Message_Iter *iter) {}
+               template <typename A, typename ...ARGS> void packValues_helper(Eldbus_Message_Iter *iter, A &&a, ARGS &&... r)
                {
-                       return "a" + signature<std::pair<A, B>>::sig();
+                       signature<A>::set(iter, std::forward<A>(a));
+                       packValues_helper(iter, std::forward<ARGS>(r)...);
                }
-               static void set(Eldbus_Message_Iter *iter, const std::array<std::pair<A, B>, N> &v)
+               template <typename ...ARGS> void packValues(CallId callId, Eldbus_Message *msg, ARGS &&... r)
                {
-                       auto lst = eldbus_message_iter_container_new(iter, 'a', signature<std::pair<A, B>>::sig().c_str());
-                       assert(lst);
-                       for (auto &a : v) {
-                               signature<std::pair<A, B>>::set(lst, a);
-                       }
-                       eldbus_message_iter_container_close(iter, lst);
+                       auto iter = eldbus_message_iter_get(msg);
+                       packValues_helper(iter, std::forward<ARGS>(r)...);
                }
-               static bool get(Eldbus_Message_Iter *iter, std::array<std::pair<A, B>, N> &v)
-               {
-                       Eldbus_Message_Iter *s;
-                       char sig = 'r';
-                       if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
-                       char *t = eldbus_message_iter_signature_get(s);
-                       if (t && t[0] == '{')
-                               sig = '{';
-                       free(t);
-                       for (auto &a : v) {
-                               if (!signature<std::pair<A, B>>::get(s, a, sig))
-                                       return false;
+
+               template<int ...> struct seq {};
+               template<int N, int ...S> struct gens : gens < N - 1, N - 1, S... > {};
+               template<int ...S> struct gens<0, S...> {
+                       typedef seq<S...> type;
+               };
+               template <typename> struct ReturnType;
+               template <typename R, typename ... ARGS> struct ReturnType<R(ARGS...)> {
+                       using type = R;
+               };
+               template <typename R, typename ... ARGS> struct ReturnType<std::function<R(ARGS...)>> {
+                       using type = R;
+               };
+               template <typename C, typename ... ARGS> struct apply_helper {
+                       const std::function<C> &c;
+                       const std::tuple<ARGS...> &args;
+
+                       template<int ... S> auto apply_2(seq<S...>)
+                       {
+                               return c(std::get<S>(args)...);
                        }
-                       return true;
-               }
-       };
-       template <typename A, typename B> struct signature<std::vector<std::pair<A, B>>> {
-               static std::string sig()
-               {
-                       return "a" + signature<std::pair<A, B>>::sig();
-               }
-               static void set(Eldbus_Message_Iter *iter, const std::vector<std::pair<A, B>> &v)
-               {
-                       auto lst = eldbus_message_iter_container_new(iter, 'a', signature<std::pair<A, B>>::sig().c_str());
-                       ASSERT(lst);
-                       for (auto &a : v) {
-                               signature<std::pair<A, B>>::set(lst, a);
+                       auto apply_1()
+                       {
+                               return apply_2(typename gens<sizeof...(ARGS)>::type());
                        }
-                       eldbus_message_iter_container_close(iter, lst);
-               }
-               static bool get(Eldbus_Message_Iter *iter, std::vector<std::pair<A, B>> &v)
-               {
-                       Eldbus_Message_Iter *s;
-                       v.clear();
-                       char sig = 'r';
-                       if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
-                       char *t = eldbus_message_iter_signature_get(s);
+               };
+               template <typename C, typename D, typename ... ARGS> struct apply_helper_2 {
+                       const std::function<C> &c;
+                       const D &d;
+                       const std::tuple<ARGS...> &args;
 
-                       // TODO: move handling of std::pair special case involing '{' to signature<std::pair<*, *>>
-                       if (t && t[0] == '{')
-                               sig = '{';
-                       free(t);
-                       std::pair<A, B> a;
-                       while (signature<std::pair<A, B>>::get(s, a, sig))
-                               v.push_back(std::move(a));
-                       return true;
-               }
-       };
-       template <typename A> struct signature<EldbusVariant<A>> {
-               static std::string sig()
-               {
-                       return "v";
-               }
-               static void set(Eldbus_Message_Iter *iter, const EldbusVariant<A> &v)
-               {
-                       set(iter, v.value);
-               }
-               static void set(Eldbus_Message_Iter *iter, const A &v)
-               {
-                       auto var = eldbus_message_iter_container_new(iter, 'v', signature<A>::sig().c_str());
-                       signature<A>::set(var, v);
-                       eldbus_message_iter_container_close(iter, var);
-               }
-               static bool get(Eldbus_Message_Iter *iter, EldbusVariant<A> &v)
-               {
-                       Eldbus_Message_Iter *s;
-                       if (!eldbus_message_iter_get_and_next(iter, 'v', &s)) return false;
-                       return signature<A>::get(s, v.value);
-               }
-       };
-       template <typename A, typename B> struct signature<std::unordered_map<A, B>> {
-               static std::string sig()
-               {
-                       return "a{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
-               }
-               static void set(Eldbus_Message_Iter *iter, const std::unordered_map<A, B> &v)
-               {
-                       auto sig = "{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
-                       auto lst = eldbus_message_iter_container_new(iter, 'a', sig.c_str());
-                       assert(lst);
-                       for (auto &a : v) {
-                               signature<std::pair<A, B>>::set(lst, a, 'e');
+                       template<int ... S> auto apply_2(seq<S...>)
+                       {
+                               return c(d, std::get<S>(args)...);
                        }
-                       eldbus_message_iter_container_close(iter, lst);
-               }
-               static bool get(Eldbus_Message_Iter *iter, std::unordered_map<A, B> &v)
-               {
-                       Eldbus_Message_Iter *s;
-                       v.clear();
-                       if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
-                       std::pair<A, B> a;
-                       while (signature<std::pair<A, B>>::get(s, a, '{'))
-                               v.insert(std::move(a));
-                       return true;
-               }
-       };
-       template <typename A, typename B> struct signature<std::map<A, B>> {
-               static std::string sig()
-               {
-                       return "a{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
-               }
-               static void set(Eldbus_Message_Iter *iter, const std::map<A, B> &v)
-               {
-                       auto sig = "{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
-                       auto lst = eldbus_message_iter_container_new(iter, 'a', sig.c_str());
-                       assert(lst);
-                       for (auto &a : v) {
-                               signature<std::pair<A, B>>::set(lst, a, 'e');
+                       auto apply_1()
+                       {
+                               return apply_2(typename gens<sizeof...(ARGS)>::type());
                        }
-                       eldbus_message_iter_container_close(iter, lst);
-               }
-               static bool get(Eldbus_Message_Iter *iter, std::map<A, B> &v)
-               {
-                       Eldbus_Message_Iter *s;
-                       v.clear();
-                       if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
-                       std::pair<A, B> a;
-                       while (signature<std::pair<A, B>>::get(s, a, '{'))
-                               v.insert(std::move(a));
-                       return true;
-               }
-       };
-       template <typename A> struct signature<const A &> {
-               static std::string sig()
+               };
+               template <typename C, typename ... ARGS> auto apply(const std::function<C> &c, const std::tuple<ARGS...> &args) -> typename ReturnType<C>::type
                {
-                       return signature<A>::sig();
+                       apply_helper<C, ARGS...> ah { c, args };
+                       return ah.apply_1();
                }
-               static void set(Eldbus_Message_Iter *iter, const A &v)
+               template <typename C, typename D, typename ... ARGS> auto apply(const std::function<C> &c, const D &d, const std::tuple<ARGS...> &args) -> typename ReturnType<C>::type
                {
-                       signature<A>::set(iter, v);
+                       apply_helper_2<C, D, ARGS...> ah { c, d, args };
+                       return ah.apply_1();
                }
-               static void get(Eldbus_Message_Iter *iter, A &v)
+
+               struct EldbusProxyBase {
+                       EldbusProxyBase()
+                       {
+                               eldbus_init();
+                       }
+                       ~EldbusProxyBase()
+                       {
+                               eldbus_shutdown();
+                       }
+               };
+
+               constexpr static int ELDBUS_CALL_TIMEOUT = 1000;
+
+               struct ConnectionState {
+                       EldbusConnectionCallbackHandle connection;
+                       EldbusObjectCallbackHandle object;
+                       Eldbus_Proxy *proxy = nullptr; // proxy lives only as long as object
+                       Eldbus_Proxy *propertiesProxy = nullptr;
+               };
+               static void callAsyncCb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
                {
-                       signature<A>::get(iter, v);
+                       auto d = (std::pair<CallId, std::function<void(const Eldbus_Message *)>> *)data;
+                       DBUS_DEBUG("call %d: got reply", d->first.id);
+                       d->second(msg);
                }
-       };
-       template <typename A> struct signature<A &> {
-               static std::string sig()
+               static void pendingFreeCb(void *data, const void *)
                {
-                       return signature<A>::sig();
+                       auto d = (std::pair<CallId, std::function<void(const Eldbus_Message *)>> *)data;
+                       DBUS_DEBUG("call %d: deleting", d->first.id);
+                       delete d;
                }
-               static void set(Eldbus_Message_Iter *iter, const A &v)
+               template <typename RETTYPE, typename ... ARGS>
+               RETTYPE call(CallId callId, ConnectionState &connectionState, bool property, const std::string &funcName, const ARGS &... args)
                {
-                       signature<A>::set(iter, v);
+                       auto proxy = property ? connectionState.propertiesProxy : connectionState.proxy;
+                       if (!proxy) {
+                               DBUS_DEBUG("call %d: not initialized", callId.id);
+                               return Error { "not initialized" };
+                       }
+
+                       DBUS_DEBUG("call %d: calling '%s'", callId.id, funcName.c_str());
+                       EldbusMessageCallbackHandle msg{eldbus_proxy_method_call_new(proxy, funcName.c_str())};
+                       if (!msg) {
+                               DBUS_DEBUG("call %d: failed", callId.id);
+                               return Error { "failed to create message" };
+                       }
+
+                       detail::packValues(callId, msg.get(), args...);
+                       auto replyRawPtr = eldbus_proxy_send_and_block(proxy, msg.release(), ELDBUS_CALL_TIMEOUT);
+                       EldbusMessageCallbackHandle reply{ replyRawPtr };
+                       DBUS_DEBUG("call %d: calling '%s' done", callId.id, funcName.c_str());
+                       if (!reply) {
+                               DBUS_DEBUG("call %d: failed", callId.id);
+                               return Error { "eldbus returned null as reply" };
+                       }
+                       const char *errname, *errmsg;
+                       if (eldbus_message_error_get(reply.get(), &errname, &errmsg)) {
+                               DBUS_DEBUG("call %d: %s: %s", callId.id, errname, errmsg);
+                               return Error { std::string(errname) + ": " + errmsg };
+                       }
+                       DBUS_DEBUG("call %d: got reply with signature '%s'", callId.id, eldbus_message_signature_get(reply.get()));
+                       return detail::unpackValues<RETTYPE>(callId, reply.get());
                }
-               static void get(Eldbus_Message_Iter *iter, A &v)
-               {
-                       signature<A>::get(iter, v);
+
+               template <typename RETTYPE, typename ... ARGS>
+               void asyncCall(CallId callId, ConnectionState connectionState, bool property, const std::string &funcName,
+                                          std::function<void(RETTYPE)> callback, const ARGS &... args)
+               {
+                       auto proxy = property ? connectionState.propertiesProxy : connectionState.proxy;
+                       if (!proxy) {
+                               DBUS_DEBUG("call %d: not initialized", callId.id);
+                               callback(Error { "not initialized" });
+                               return;
+                       }
+
+                       EldbusMessageCallbackHandle msg{eldbus_proxy_method_call_new(proxy, funcName.c_str())};
+                       if (!msg) {
+                               DBUS_DEBUG("call %d: failed", callId.id);
+                               callback(Error { "failed to create message" });
+                               return;
+                       }
+
+                       auto cbData = new std::pair<CallId, std::function<void(const Eldbus_Message *)>> {callId,
+                                       [callback, connectionState, callId](const Eldbus_Message * reply)
+                       {
+                               DBUS_DEBUG("call %d: calling done", callId.id);
+                               if (!reply) {
+                                       DBUS_DEBUG("call %d: failed", callId.id);
+                                       callback(Error { "eldbus returned null as reply" });
+                               } else {
+                                       const char *errname, *errmsg;
+                                       if (eldbus_message_error_get(reply, &errname, &errmsg)) {
+                                               DBUS_DEBUG("call %d: %s: %s", callId.id, errname, errmsg);
+                                               callback(Error { std::string(errname) + ": " + errmsg });
+                                       } else {
+                                               DBUS_DEBUG("call %d: got reply with signature '%s'", callId.id, eldbus_message_signature_get(reply));
+                                               callback(detail::unpackValues<RETTYPE>(callId, reply));
+                                       }
+                               }
+                       }
+                                                                                                                                                                                        };
+                       detail::packValues(callId, msg.get(), args...);
+                       auto pending = eldbus_proxy_send(proxy, msg.release(), callAsyncCb, cbData, ELDBUS_CALL_TIMEOUT);
+                       if (pending) {
+                               eldbus_pending_free_cb_add(pending, pendingFreeCb, cbData);
+                               DBUS_DEBUG("call %d: call sent", callId.id);
+                       } else {
+                               DBUS_DEBUG("call %d: failed to send call", callId.id);
+                               callback(Error { "failed to send call" });
+                       }
                }
-       };
-       template <typename A> struct signature<const A> {
-               static std::string sig()
+               inline void displayDebugCallInfo(CallId callId, const std::string &funcName, const std::string &info, const std::string &interfaceName)
                {
-                       return signature<A>::sig();
+                       DBUS_DEBUG("call %d: %s iname = %s fname = %s", callId.id, info.c_str(), interfaceName.c_str(), funcName.c_str());
                }
-               static void set(Eldbus_Message_Iter *iter, const A &v)
+               inline void displayDebugCallInfoSignal(CallId callId, const std::string &funcName, const std::string &info, const std::string &interfaceName)
                {
-                       signature<A>::set(iter, v);
+                       DBUS_DEBUG("call %d: %s signal iname = %s fname = %s", callId.id, info.c_str(), interfaceName.c_str(), funcName.c_str());
                }
-               static void get(Eldbus_Message_Iter *iter, A &v)
+               inline void displayDebugCallInfoProperty(CallId callId, const std::string &funcName, std::string info, const std::string &interfaceName,
+                               const std::string &propertyName)
                {
-                       signature<A>::get(iter, v);
+                       DBUS_DEBUG("call %d: %s iname = %s pname = %s", callId.id, info.c_str(), interfaceName.c_str(), propertyName.c_str());
                }
-       };
-       template <> struct signature<std::shared_ptr<AtspiAccessible>> {
-               using subtype = std::pair<std::string, std::string>;
 
-               static std::string sig()
-               {
-                       return "(so)";
-               }
-               static void set(Eldbus_Message_Iter *iter, const std::shared_ptr<AtspiAccessible> &v)
+               class StringStorage
                {
-                       const auto prefixPath = "/org/a11y/atspi/accessible/";
-                       const auto nullPath = "/org/a11y/atspi/null";
-
-                       if (v) {
-                               auto bus = atspi_accessible_get_bus_name(v.get(), NULL);
-                               auto path = atspi_accessible_get_path(v.get(), NULL);
-                               signature<subtype>::set(iter, { bus, std::string{prefixPath} + path });
-                               g_free(path);
-                               g_free(bus);
-                       } else {
-                               signature<subtype>::set(iter, { {}, std::string{nullPath} });
+                       struct char_ptr_deleter {
+                               void operator()(char *p)
+                               {
+                                       free(p);
+                               }
+                       };
+                       std::vector<std::unique_ptr<char, char_ptr_deleter>> storage;
+               public:
+                       const char *add(const char *txt)
+                       {
+                               auto ptr = strdup(txt);
+                               storage.push_back(std::unique_ptr<char, char_ptr_deleter>(ptr));
+                               return storage.back().get();
                        }
-               }
-               static bool get(Eldbus_Message_Iter *iter, std::shared_ptr<AtspiAccessible> &v);
-       };
-       struct CallId {
-               static unsigned int LastId;
-               unsigned int id = ++LastId;
-       };
-       template <typename R> struct unpackValues_Helper {
-               bool operator()(CallId callId, R &r, const Eldbus_Message *msg) const
-               {
-                       auto iter = eldbus_message_iter_get(msg);
+                       const char *add(const std::string &txt)
+                       {
+                               return add(txt.c_str());
+                       }
+               };
+               template <typename A, typename ... ARGS> struct EldbusArgGenerator_Helper {
+                       static void add(std::vector<Eldbus_Arg_Info> &r, StringStorage &strings)
+                       {
+                               auto s = r.size();
+                               auto sig = signature<A>::sig();
+                               ASSERT(!sig.empty());
+                               auto name = "p" + std::to_string(s + 1);
+                               r.push_back(Eldbus_Arg_Info{ strings.add(sig), strings.add(name) });
+                               EldbusArgGenerator_Helper<ARGS...>::add(r, strings);
+                       }
+               };
+               template <> struct EldbusArgGenerator_Helper<void> {
+                       static void add(std::vector<Eldbus_Arg_Info> &, StringStorage &)
+                       {
+                       }
+               };
+               template <typename ... ARGS> struct EldbusArgGenerator_Helper<std::tuple<ARGS...>> {
+                       static void add(std::vector<Eldbus_Arg_Info> &r, StringStorage &strings)
+                       {
+                               EldbusArgGenerator_Helper<ARGS..., void>::add(r, strings);
+                       }
+               };
+               template <typename RetType> struct dbus_interface_return_type_traits {
+                       using type = ValueOrError<RetType>;
+               };
+               template <typename ... ARGS> struct dbus_interface_return_type_traits<ValueOrError<ARGS...>> {
+                       using type = ValueOrError<ARGS...>;
+               };
+               template <typename T> struct dbus_interface_traits;
+               template <typename RetType, typename ... ARGS> struct dbus_interface_traits<RetType(ARGS...)> {
+                       using Ret = typename dbus_interface_return_type_traits<RetType>::type;
+                       using SyncCB = std::function<Ret(ARGS...)>;
+                       using AsyncCB = std::function<void(std::function<void(Ret)>, ARGS...)>;
+                       using VEArgs = ValueOrError<ARGS...>;
+               };
+               template <typename T> struct EldbusArgGenerator_Args;
+               template <typename RetType, typename ... ARGS> struct EldbusArgGenerator_Args<RetType(ARGS...)> {
+                       static auto name()
+                       {
+                               return signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::name();
+                       }
+                       static auto get(StringStorage &strings)
+                       {
+                               std::vector<Eldbus_Arg_Info> tmp;
+                               EldbusArgGenerator_Helper<ARGS..., void>::add(tmp, strings);
+                               tmp.push_back({ nullptr, nullptr });
+                               return tmp;
+                       }
+               };
+               template <typename T> struct EldbusArgGenerator_ReturnType;
+               template <typename RetType, typename ... ARGS> struct EldbusArgGenerator_ReturnType<RetType(ARGS...)> {
+                       static std::string name()
+                       {
+                               return signature<RetType>::name();
+                       }
+                       static auto get(StringStorage &strings)
+                       {
+                               std::vector<Eldbus_Arg_Info> tmp;
+                               EldbusArgGenerator_Helper<RetType, void>::add(tmp, strings);
+                               tmp.push_back({ nullptr, nullptr });
+                               return tmp;
+                       }
+               };
+               template <typename T> struct EldbusArgGenerator_ReturnType;
+               template <typename ... ARGS> struct EldbusArgGenerator_ReturnType<void(ARGS...)> {
+                       static std::string name()
+                       {
+                               return "";
+                       }
+                       static auto get(StringStorage &strings)
+                       {
+                               std::vector<Eldbus_Arg_Info> tmp;
+                               tmp.push_back({ nullptr, nullptr });
+                               return tmp;
+                       }
+               };
+       }
 
-                       if (iter) {
-                               if (signature<R>::get(iter, r))
-                                       return true;
-                               ERROR("call %d: signature mismatch, got %s, expected %s",
-                                         callId.id,
-                                         eldbus_message_signature_get(msg),
-                                         signature<R>::sig().c_str());
-                       } else
-                               ERROR("call %d: failed to get iter", callId.id);
-                       return false;
-               }
-       };
-       template <typename ...ARGS> struct unpackValues_Helper<std::tuple<ARGS...>> {
-               bool operator()(CallId callId, std::tuple<ARGS...> &r, const Eldbus_Message *msg) const
-               {
-                       auto iter = eldbus_message_iter_get(msg);
-                       if (iter) {
-                               if (signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::get(iter, r))
-                                       return true;
-                               ERROR("call %d: signature mismatch, got %s, expected %s",
-                                         callId.id,
-                                         eldbus_message_signature_get(msg),
-                                         signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig().c_str());
-                       } else
-                               ERROR("call %d: failed to get iter", callId.id);
-                       return false;
-               }
+       enum class ConnectionType {
+               SYSTEM, SESSION
        };
-       template <typename R> bool unpackValues(CallId callId, R &r, const Eldbus_Message *msg)
-       {
-               return unpackValues_Helper<R>()(callId, r, msg);
-       }
-       template <typename R> void packValues(CallId callId, Eldbus_Message *msg, const R &r)
-       {
-               DEBUG("call %d: packing values with signature %s", callId.id, signature<R>::sig().c_str());
-               auto iter = eldbus_message_iter_get(msg);
-               signature<R>::set(iter, r);
-               DEBUG("call %d: signature is %s", callId.id, eldbus_message_signature_get(msg));
-       }
-       inline void packValues_helper(Eldbus_Message_Iter *iter) {}
-       template <typename A, typename ...ARGS> void packValues_helper(Eldbus_Message_Iter *iter, A &&a, ARGS &&... r)
-       {
-               signature<A>::set(iter, std::forward<A>(a));
-               packValues_helper(iter, std::forward<ARGS>(r)...);
-       }
-       template <typename ...ARGS> void packValues(CallId callId, Eldbus_Message *msg, ARGS &&... r)
+
+       class DBusClient : private detail::EldbusProxyBase
        {
-               DEBUG("call %d: packing values with signature %s", callId.id, signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig().c_str());
-               auto iter = eldbus_message_iter_get(msg);
-               packValues_helper(iter, std::forward<ARGS>(r)...);
-               DEBUG("call %d: signature is %s", callId.id, eldbus_message_signature_get(msg));
-       }
+       public:
+               DBusClient() = default;
+               DBusClient(std::string bus_name, std::string path_name, std::string interface_name,
+                                  ConnectionType tp);
+               DBusClient(std::string bus_name, std::string path_name, std::string interface_name,
+                                  const EldbusConnectionCallbackHandle &conn = {});
+               ~DBusClient();
 
-       template<int ...> struct seq {};
-       template<int N, int ...S> struct gens : gens < N - 1, N - 1, S... > {};
-       template<int ...S> struct gens<0, S...> {
-               typedef seq<S...> type;
-       };
+               DBusClient(const DBusClient &) = delete;
+               DBusClient(DBusClient &&) = default;
 
-       template <typename C, typename R, typename ... ARGS> struct apply_helper {
-               const C &c;
-               const std::tuple<ARGS...> &args;
+               DBusClient &operator = (DBusClient &&) = default;
+               DBusClient &operator = (const DBusClient &) = delete;
 
-               template<int ... S> R apply_2(seq<S...>)
+               explicit operator bool () const
                {
-                       return c(std::get<S>(args)...);
+                       return bool(connectionState.proxy);
                }
-               R apply_1()
-               {
-                       return apply_2(typename gens<sizeof...(ARGS)>::type());
-               }
-       };
-       template <typename C, typename R, typename ... ARGS> R apply(const C &c, const std::tuple<ARGS...> &args)
-       {
-               apply_helper<C, R, ARGS...> ah { c, args };
-               return ah.apply_1();
-       }
 
-       struct EldbusProxyBase {
-               EldbusProxyBase()
+               template <typename T> struct Method {
+                       using RetType = typename detail::dbus_interface_traits<T>::Ret;
+                       detail::ConnectionState connectionState;
+                       std::string funcName, interfaceName;
+                       std::string info;
+
+                       template <typename ... ARGS> RetType call(const ARGS &... args)
+                       {
+                               detail::CallId callId;
+                               detail::displayDebugCallInfo(callId, funcName, info, interfaceName);
+                               return detail::call<RetType>(callId, connectionState, false, funcName, args...);
+                       }
+                       template <typename ... ARGS> void asyncCall(std::function<void(RetType)> callback, const ARGS &... args)
+                       {
+                               detail::CallId callId;
+                               detail::displayDebugCallInfo(callId, funcName, info, interfaceName);
+                               auto connectionState = this->connectionState;
+                               detail::asyncCall<RetType>(callId, connectionState, false, funcName, std::move(callback), args...);
+                       }
+               };
+               template <typename T> struct Property {
+                       using RetType = typename detail::dbus_interface_return_type_traits<T>::type;
+                       using VariantRetType = typename detail::dbus_interface_return_type_traits<EldbusVariant<T>>::type;
+                       detail::ConnectionState connectionState;
+                       std::string propName, interfaceName;
+                       std::string info;
+
+                       RetType get()
+                       {
+                               detail::CallId callId;
+                               detail::displayDebugCallInfoProperty(callId, "Get", info, interfaceName, propName);
+                               auto z = detail::call<VariantRetType>(callId, connectionState, true, "Get", interfaceName, propName);
+                               if (!z) return z.getError();
+                               return { std::get<0>(z.getValues()).value };
+                       }
+                       void asyncGet(std::function<void(RetType)> callback)
+                       {
+                               detail::CallId callId;
+                               detail::displayDebugCallInfoProperty(callId, "Get", info, interfaceName, propName);
+                               auto connectionState = this->connectionState;
+                               auto cc = [callback](VariantRetType reply) {
+                                       if (reply)
+                                               callback(std::move(std::get<0>(reply.getValues()).value));
+                                       else
+                                               callback(reply.getError());
+                               };
+                               detail::asyncCall<VariantRetType>(callId, connectionState, true, "Get", std::move(cc), interfaceName, propName);
+                       }
+                       ValueOrError<void> set(const T &r)
+                       {
+                               detail::CallId callId;
+                               detail::displayDebugCallInfoProperty(callId, "Set", info, interfaceName, propName);
+                               EldbusVariant<T> variantValue { std::move(r) };
+                               return detail::call<ValueOrError<void>>(callId, connectionState, true, "Set", interfaceName, propName, variantValue);
+                       }
+                       void asyncSet(std::function<void(ValueOrError<void>)> callback, const T &r)
+                       {
+                               detail::CallId callId;
+                               detail::displayDebugCallInfoProperty(callId, "Set", info, interfaceName, propName);
+                               EldbusVariant<T> variantValue { std::move(r) };
+                               detail::asyncCall<ValueOrError<void>>(callId, connectionState, true, "Set", std::move(callback), interfaceName, propName, variantValue);
+                       }
+               };
+
+               template <typename PropertyType>
+               auto property(std::string propName)
                {
-                       eldbus_init();
+                       return Property<PropertyType> { connectionState, std::move(propName), interfaceName, info };
                }
-               ~EldbusProxyBase()
+               template <typename MethodType>
+               auto method(std::string funcName)
                {
-                       eldbus_shutdown();
+                       return Method<MethodType> { connectionState, std::move(funcName), interfaceName, info };
                }
-       };
-}
 
-namespace detail
-{
-       template <typename Q> struct Method;
-       template <typename R, typename ... ARGS> struct Method<R(ARGS...)>;
-       template <typename ... ARGS> struct Method<void(ARGS...)>;
-       template <typename Q> struct Listen;
-       template <typename ... ARGS> struct Listen<void(ARGS...)>;
-
-       constexpr static int ELDBUS_CALL_TIMEOUT = 1000;
+               template <typename SignalType> void addSignal(std::string signalName, std::function<SignalType> callback)
+               {
+                       detail::CallId callId;
+                       detail::displayDebugCallInfoSignal(callId, signalName, info, interfaceName);
+                       auto tmp = new std::function<void(const Eldbus_Message *msg)> {
+                               [ callId, connectionState = this->connectionState, callback ](const Eldbus_Message * msg) -> void {
+                                       const char *errname, *aux;
+                                       if (eldbus_message_error_get(msg, &errname, &aux))
+                                       {
+                                               DBUS_DEBUG("call %d: Eldbus error: %s %s", callId.id, errname, aux);
+                                               return;
+                                       }
 
-       struct ConnectionState {
-               EldbusConnectionCallbackHandle connection;
-               EldbusObjectCallbackHandle object;
-               EldbusProxyCallbackHandle proxy;
-       };
-       static void callAsyncCb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
-       {
-               auto d = (std::pair<CallId, std::function<void(const Eldbus_Message *)>> *)data;
-               DEBUG("call %d: got reply", d->first.id);
-               d->second(msg);
-       }
-       static void pendingFreeCb(void *data, const void *)
-       {
-               auto d = (std::pair<CallId, std::function<void(const Eldbus_Message *)>> *)data;
-               DEBUG("call %d: deleting", d->first.id);
-               delete d;
-       }
-       template <typename ... ARGS>
-       EldbusMessageCallbackHandle call(CallId callId, ConnectionState &connectionState, const std::string &func_name, const ARGS &... args)
-       {
-               if (!connectionState.proxy) {
-                       DEBUG("call %d: not initialized", callId.id);
-                       return {};
+                                       DBUS_DEBUG("call %d: received signal with signature '%s'", callId.id, eldbus_message_signature_get(msg));
+                                       using ParamsType = typename detail::dbus_interface_traits<SignalType>::VEArgs;
+                                       auto params = detail::unpackValues<ParamsType>(callId, msg);
+                                       if (!params)
+                                       {
+                                               DBUS_DEBUG("call %d: failed: %s", callId.id, params.getError().message.c_str());
+                                               return;
+                                       }
+                                       detail::apply(callback, params.getValues());
+                               }
+                       };
+                       eldbus_proxy_signal_handler_add(connectionState.proxy, signalName.c_str(), listenerCallback, tmp);
+                       destructors.push_back([ = ]() {
+                               delete tmp;
+                       });
                }
 
-               DEBUG("call %d: calling '%s'", callId.id, func_name.c_str());
-               EldbusMessageCallbackHandle msg{eldbus_proxy_method_call_new(connectionState.proxy.get(), func_name.c_str())};
-               if (!msg) {
-                       DEBUG("call %d: failed", callId.id);
-                       return {};
-               }
+       private:
+               detail::ConnectionState connectionState;
+               std::vector<std::function<void()>> destructors;
+               std::string info, interfaceName;
 
-               detail::packValues(callId, msg.get(), args...);
-               auto replyRawPtr = eldbus_proxy_send_and_block(connectionState.proxy.get(), msg.release(), ELDBUS_CALL_TIMEOUT);
-               EldbusMessageCallbackHandle reply{ replyRawPtr };
-               DEBUG("call %d: calling '%s' done", callId.id, func_name.c_str());
-               if (!reply) {
-                       DEBUG("call %d: failed", callId.id);
-                       return {};
-               }
-               const char *errname, *errmsg;
-               if (eldbus_message_error_get(reply.get(), &errname, &errmsg)) {
-                       DEBUG("call %d: %s: %s", callId.id, errname, errmsg);
-                       return {};
+               static void listenerCallback(void *data, const Eldbus_Message *msg)
+               {
+                       (*((std::function<void(const Eldbus_Message *msg)> *)data))(msg);
                }
-               DEBUG("call %d: got reply with signature '%s'", callId.id, eldbus_message_signature_get(reply.get()));
-               return reply;
-       }
+       };
 
-       template <typename ... ARGS>
-       void asyncCall(CallId callId, ConnectionState connectionState, const std::string &func_name,
-                                  std::function<void(const Eldbus_Message *)> callback, const ARGS &... args)
+       class DBusInterfaceDescription
        {
-               if (!connectionState.proxy) {
-                       DEBUG("call %d: not initialized", callId.id);
-                       callback({});
-                       return;
-               }
+               friend class DBusServer;
+       public:
+               enum class type {
+                       method, property
+               };
+               struct MethodInfo {
+                       detail::CallId id;
+                       std::string memberName;
+                       std::vector<Eldbus_Arg_Info> in, out;
+                       std::function<Eldbus_Message *(const Eldbus_Message *msg)> callback;
+               };
+               struct SignalInfo {
+                       detail::CallId id;
+                       std::string memberName;
+                       std::vector<Eldbus_Arg_Info> args;
+                       unsigned int uniqueId;
+               };
+               struct PropertyInfo {
+                       detail::CallId setterId, getterId;
+                       std::string memberName, typeSignature;
+                       std::function<ValueOrError<void>(const Eldbus_Message *src, Eldbus_Message_Iter *dst)> getCallback, setCallback;
+               };
+               class SignalId
+               {
+                       friend class ::DBus::DBusServer;
+                       friend class ::DBus::DBusClient;
+                       friend class ::DBus::DBusInterfaceDescription;
+                       detail::CallId id;
 
-               EldbusMessageCallbackHandle msg{eldbus_proxy_method_call_new(connectionState.proxy.get(), func_name.c_str())};
-               if (!msg) {
-                       DEBUG("call %d: failed", callId.id);
-                       callback({});
-                       return;
-               }
+                       SignalId() = default;
+                       SignalId(detail::CallId id) : id(id) { }
+               };
+               DBusInterfaceDescription(std::string interfaceName);
 
-               auto cbData = new std::pair<CallId, std::function<void(const Eldbus_Message *)>> {
-                       callId, [callback, connectionState, callId](const Eldbus_Message * reply)
-                       {
-                               DEBUG("call %d: calling done", callId.id);
-                               if (!reply)
-                                       DEBUG("call %d: failed", callId.id);
-                               else {
-                                       const char *errname, *errmsg;
-                                       if (eldbus_message_error_get(reply, &errname, &errmsg)) {
-                                               DEBUG("call %d: %s: %s", callId.id, errname, errmsg);
-                                               reply = nullptr;
-                                       } else
-                                               DEBUG("call %d: got reply with signature '%s'", callId.id, eldbus_message_signature_get(reply));
-                               }
-                               callback(reply);
+               template <typename T> void addMethod(const std::string &memberName, typename detail::dbus_interface_traits<T>::SyncCB callback)
+               {
+                       detail::CallId callId;
+                       MethodInfo mi;
+                       methods.push_back(std::move(mi));
+                       auto &z = methods.back();
+                       z.in = detail::EldbusArgGenerator_Args<T>::get(strings);
+                       z.out = detail::EldbusArgGenerator_ReturnType<T>::get(strings);
+                       z.memberName = memberName;
+                       DBUS_DEBUG("call %d: method %s, in %s, out %s", callId.id, memberName.c_str(),
+                                          detail::EldbusArgGenerator_Args<T>::name().c_str(),
+                                          detail::EldbusArgGenerator_ReturnType<T>::name().c_str()
+                                         );
+                       z.callback = construct<T>(callId, callback);
+                       z.id = callId;
+               }
+               template <typename T> void addAsyncMethod(const std::string &memberName, typename detail::dbus_interface_traits<T>::AsyncCB callback);
+               template <typename T> void addProperty(const std::string &memberName, std::function<ValueOrError<T>()> getter, std::function<ValueOrError<void>(T)> setter)
+               {
+                       properties.push_back({});
+                       auto &z = properties.back();
+                       z.memberName = memberName;
+                       z.typeSignature = detail::signature<T>::sig();
+                       if (getter) {
+                               detail::CallId getterId;
+                               z.getterId = getterId;
+                               DBUS_DEBUG("call %d: property %s (get) type %s", getterId.id, memberName.c_str(), detail::signature<T>::name().c_str());
+                               z.getCallback = [ = ](const Eldbus_Message * src, Eldbus_Message_Iter * dst) -> ValueOrError<void> {
+                                       auto v = detail::apply(getter, std::tuple<>{});
+                                       if (v)
+                                       {
+                                               detail::signature<T>::set(dst, std::get<0>(v.getValues()));
+                                               DBUS_DEBUG("call %d: success", getterId.id);
+                                               return Success{};
+                                       }
+                                       DBUS_DEBUG("call %d: failed: %s", getterId.id, v.getError().message.c_str());
+                                       return v.getError();
+                               };
+                       }
+                       if (setter) {
+                               detail::CallId setterId;
+                               z.setterId = setterId;
+                               DBUS_DEBUG("call %d: property %s (set) type %s", setterId.id, memberName.c_str(), detail::signature<T>::name().c_str());
+                               z.setCallback = [ = ](const Eldbus_Message * src, Eldbus_Message_Iter * src_iter) -> ValueOrError<void> {
+                                       std::tuple<T> value;
+                                       auto src_signature = eldbus_message_iter_signature_get(src_iter);
+                                       if (detail::signature<T>::get(src_iter, std::get<0>(value)))
+                                       {
+                                               auto v = detail::apply(setter, std::move(value));
+                                               if (v) {
+                                                       DBUS_DEBUG("call %d: success", setterId.id);
+                                                       return Success{};
+                                               }
+                                               DBUS_DEBUG("call %d: failed: %s", setterId.id, v.getError().message.c_str());
+                                               free(src_signature);
+                                               return v.getError();
+                                       }
+                                       DBUS_DEBUG("call %d: failed to unpack values, got signature '%s', expected '%s'", setterId.id,
+                                                          src_signature, detail::signature<T>::sig().c_str());
+                                       return Error { "call " + std::to_string(setterId.id) + ": failed to unpack values, got signature '" +
+                                                                  src_signature + "', expected '" + detail::signature<T>::sig() + "'" };
+                               };
                        }
-               };
 
-               detail::packValues(callId, msg.get(), args...);
-               auto pending = eldbus_proxy_send(connectionState.proxy.get(), msg.release(), callAsyncCb, cbData, ELDBUS_CALL_TIMEOUT);
-               if (pending)
-                       eldbus_pending_free_cb_add(pending, pendingFreeCb, cbData);
-               else {
-                       DEBUG("call %d: sending failed", callId.id);
-                       delete cbData;
-                       callback({});
-                       return;
                }
-               DEBUG("call %d: call sent", callId.id);
-       }
-       static void listenerCallback(void *data, const Eldbus_Message *msg)
-       {
-               (*((std::function<void(const Eldbus_Message *msg)> *)data))(msg);
-       }
-       template <typename ... ARGS> void addListener(CallId callId, ConnectionState &connectionState,
-                       const std::string &func_name,
-                       std::vector<std::function<void(const Eldbus_Message *msg)>*> &callbacks,
-                       std::function<void(ARGS...)> callback)
-       {
-               auto tmp = new std::function<void(const Eldbus_Message *msg)> {
-                       [ callId, connectionState, callback ](const Eldbus_Message * msg) -> void {
-                               const char *errname, *aux;
-                               if (eldbus_message_error_get(msg, &errname, &aux))
+               template <typename ... ARGS> SignalId addSignal(const std::string &memberName)
+               {
+                       detail::CallId callId;
+                       signals.push_back({});
+                       auto &z = signals.back();
+                       z.memberName = memberName;
+                       z.args = detail::EldbusArgGenerator_Args<void(ARGS...)>::get(strings);
+                       z.id = callId;
+                       DBUS_DEBUG("call %d: signal %s", callId.id, memberName.c_str());
+                       return SignalId { callId };
+               }
+
+       private:
+               std::vector<MethodInfo> methods;
+               std::vector<PropertyInfo> properties;
+               std::vector<SignalInfo> signals;
+               std::string interfaceName;
+               detail::StringStorage strings;
+
+               template <typename T> std::function<Eldbus_Message*(const Eldbus_Message *msg)> construct(detail::CallId callId,
+                               typename detail::dbus_interface_traits<T>::SyncCB callback)
+               {
+                       using VEArgs = typename detail::dbus_interface_traits<T>::VEArgs;
+                       return [ = ](const Eldbus_Message * msg) -> Eldbus_Message* {
+                               Eldbus_Message *ret = nullptr;
+                               auto args = detail::unpackValues<VEArgs>(callId, msg);
+                               if (args)
+                               {
+                                       auto v = detail::apply(callback, std::move(args.getValues()));
+                                       if (v) {
+                                               DBUS_DEBUG("call %d: success", callId.id);
+                                               ret = eldbus_message_method_return_new(msg);
+                                               packValues(callId, ret, v);
+                                       } else {
+                                               DBUS_DEBUG("call %d: failed: %s", callId.id, v.getError().message.c_str());
+                                               ret = eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.Failed", v.getError().message.c_str());
+                                       }
+                               } else
                                {
-                                       ERROR("call %d: Eldbus error: %s %s", callId.id, errname, aux);
-                                       return;
+                                       std::ostringstream err;
+                                       err << "expected signature '" << detail::signature<VEArgs>::sig() <<
+                                               "', got '" << eldbus_message_signature_get(msg) << "'";
+                                       auto str = err.str();
+                                       DBUS_DEBUG("call %d: failed: %s", callId.id, str.c_str());
+                                       ret = eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.InvalidArgs", str.c_str());
                                }
+                               return ret;
+                       };
+               }
+       };
 
-                               DEBUG("call %d: received signal with signature '%s'", callId.id, eldbus_message_signature_get(msg));
-                               std::tuple<ARGS...> params;
-                               if (detail::unpackValues(callId, params, msg))
-                                       detail::apply<std::function<void(ARGS...)>, void, ARGS...>(callback, params);
-                       }
-               };
-               eldbus_proxy_signal_handler_add(connectionState.proxy.get(), func_name.c_str(), listenerCallback, std::move(tmp));
-               callbacks.push_back(tmp);
-       }
-       template <typename ... ARGS>
-       void displayDebugCallInfo(CallId callId, const std::string &func_name, const std::string &info, const ARGS &...)
-       {
-               DEBUG("call %d: %s fname = %s", callId, info.c_str(), func_name.c_str());
-       }
-       template <typename ... ARGS>
-       void displayDebugCallInfo(CallId callId, const std::string &func_name, std::string info, std::string a, const std::string &b, const ARGS &...)
+       class DBusServer : private detail::EldbusProxyBase
        {
-               if (func_name == "Get" && info.find(" iname = ") == std::string::npos)
-                       DEBUG("call %d: %s iname = %s pname = %s", callId, info.c_str(), a.c_str(), b.c_str());
-               else
-                       displayDebugCallInfo(callId, func_name, info);
-       }
-}
-
-class DBus : private detail::EldbusProxyBase
-{
-public:
-       enum class ConnectionType {
-               SYSTEM, SESSION
-       };
-       DBus();
-       DBus(std::string bus_name, std::string path_name, std::string interface_name,
-                ConnectionType tp);
-       DBus(std::string bus_name, std::string path_name, std::string interface_name,
-                const EldbusConnectionCallbackHandle &conn = {});
-       ~DBus();
+       public:
+               DBusServer();
+               DBusServer(ConnectionType tp);
+               DBusServer(const EldbusConnectionCallbackHandle &conn);
+               ~DBusServer();
 
-       DBus(const DBus &) = default;
-       DBus(DBus &&) = default;
+               DBusServer(const DBusServer &) = delete;
+               DBusServer(DBusServer &&) = default;
 
-       DBus &operator = (DBus &&) = default;
-       DBus &operator = (const DBus &) = default;
+               DBusServer &operator = (DBusServer &&) = default;
+               DBusServer &operator = (const DBusServer &) = delete;
 
-       explicit operator bool () const
-       {
-               return bool(connectionState.proxy);
-       }
+               void addInterface(const std::string &pathName, DBusInterfaceDescription &dscr, bool fallback = false);
+               std::string getBusName() const;
 
-       template <typename Q> using Method = detail::Method<Q>;
-       template <typename Q> using Listen = detail::Listen<Q>;
+               template <typename ... ARGS> void emit(DBusInterfaceDescription::SignalId signal, const ARGS &... args)
+               {
+                       auto it = signalData.find(signal.id.id);
+                       if (it != signalData.end()) {
+                               auto msg = eldbus_service_signal_new(it->second.first, it->second.second);
+                               detail::packValues(signal.id, msg, args...);
+                               eldbus_service_signal_send(it->second.first, msg);
+                       } else {
+                               DBUS_DEBUG("signal %d not found", signal.id.id);
+                       }
+               }
+               static std::string getCurrentObjectPath();
+               static EldbusConnectionCallbackHandle getCurrentConnection();
+       private:
+               EldbusConnectionCallbackHandle connection;
+               std::vector<std::function<void()>> destructors;
+               std::unordered_map<unsigned int, std::pair<const Eldbus_Service_Interface *, unsigned int>> signalData;
+       };
 
-       template <typename R>
-       Method<R> method(std::string func_name)
+       template <typename T> void DBusInterfaceDescription::addAsyncMethod(const std::string &memberName, typename detail::dbus_interface_traits<T>::AsyncCB callback)
        {
-               return Method<R> { connectionState, std::move(func_name), info };
-       }
+               detail::CallId callId;
+               MethodInfo mi;
+               methods.push_back(std::move(mi));
+               auto &z = methods.back();
+               z.in = detail::EldbusArgGenerator_Args<T>::get(strings);
+               z.out = detail::EldbusArgGenerator_ReturnType<T>::get(strings);
+               z.memberName = memberName;
+               DBUS_DEBUG("call %d: method %s, in %s, out %s", callId.id, memberName.c_str(),
+                                  detail::EldbusArgGenerator_Args<T>::name().c_str(),
+                                  detail::EldbusArgGenerator_ReturnType<T>::name().c_str()
+                                 );
+               using VEArgs = typename detail::dbus_interface_traits<T>::VEArgs;
+               z.callback = [ = ](const Eldbus_Message * msg) -> Eldbus_Message* {
+                       struct CallState {
+                               bool replyRunning = true;
+                               Eldbus_Message *reply = nullptr;
+                               EldbusMessageCallbackHandle message;
+                       };
+                       auto callState = std::make_shared<CallState>();
+                       callState->message.reset(eldbus_message_ref(const_cast<Eldbus_Message *>(msg)));
+                       auto connection = DBusServer::getCurrentConnection();
+                       auto retCallback = [ = ](typename detail::dbus_interface_traits<T>::Ret v)
+                       {
+                               DBUS_DEBUG(".");
+                               if (v) {
+                                       DBUS_DEBUG(".");
+                                       callState->reply = eldbus_message_method_return_new(callState->message.get());
+                                       packValues(callId, callState->reply, v);
+                               } else {
+                                       DBUS_DEBUG("call %d: failed: %s", callId.id, v.getError().message.c_str());
+                                       callState->reply = eldbus_message_error_new(callState->message.get(), "org.freedesktop.DBus.Error.Failed", v.getError().message.c_str());
+                               }
+                               if (!callState->replyRunning) {
+                                       DBUS_DEBUG(".");
+                                       eldbus_connection_send(connection.get(), callState->reply, NULL, NULL, -1);
+                               }
+                       };
+                       Eldbus_Message *ret = nullptr;
+                       auto args = detail::unpackValues<VEArgs>(callId, msg);
+                       if (args)
+                       {
+                               detail::apply(callback, std::move(retCallback), std::move(args.getValues()));
+                               callState->replyRunning = false;
+                               ret = callState->reply;
+                       } else
+                       {
+                               std::ostringstream err;
+                               err << "expected signature '" << detail::signature<VEArgs>::sig() <<
+                                       "', got '" << eldbus_message_signature_get(msg) << "'";
+                               auto str = err.str();
+                               ret = eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.InvalidArgs", str.c_str());
+                       }
+                       return ret;
+               };
 
-       template <typename R>
-       Listen<R> listen(std::string func_name)
-       {
-               return Listen<R> { connectionState, callbacks, std::move(func_name), info };
+               z.id = callId;
        }
 
-private:
-       detail::ConnectionState connectionState;
-       std::vector<std::function<void(const Eldbus_Message *)>*> callbacks;
-       std::string info;
-};
-
-template <typename RetType, typename ... ARGS> struct detail::Method<RetType(ARGS...)> {
-       ConnectionState connectionState;
-       std::string func_name;
-       std::string info;
+       EldbusConnectionCallbackHandle getDBusConnectionByType(ConnectionType tp);
+       EldbusConnectionCallbackHandle getDBusConnectionByName(const std::string &name);
+       std::string getConnectionName(const EldbusConnectionCallbackHandle &);
+}
 
-       Optional<RetType> call(const ARGS &... args)
+namespace std
+{
+       template <size_t INDEX, typename ... ARGS> inline auto &get(DBus::ValueOrError<ARGS...> &v)
        {
-               CallId callId;
-               detail::displayDebugCallInfo(callId, func_name, info, args...);
-               auto reply = detail::call(callId, connectionState, func_name, args...);
-               if (reply) {
-                       RetType r;
-                       if (detail::unpackValues<RetType>(callId, r, reply.get()))
-                               return r;
-               }
-               return {};
+               return std::get<INDEX>(v.getValues());
        }
-       void asyncCall(const ARGS &... args, std::function<void(Optional<RetType>)> callback)
+       template <size_t INDEX, typename ... ARGS> inline auto get(const DBus::ValueOrError<ARGS...> &v)
        {
-               CallId callId;
-               detail::displayDebugCallInfo(callId, func_name, info, args...);
-               auto connectionState = this->connectionState;
-               auto fn = func_name;
-               auto cc = [callId, connectionState, callback](const Eldbus_Message * reply) {
-                       DEBUG("call %d: processing asynchronous reply (%d)", callId.id, reply ? 1 : 0);
-                       if (!reply)
-                               callback({});
-                       else {
-                               RetType r;
-                               if (!detail::unpackValues<RetType>(callId, r, reply))
-                                       callback({});
-                               else
-                                       callback(r);
-                       }
-                       DEBUG("call %d: processing asynchronous reply done", callId.id);
-               };
-               detail::asyncCall(callId, connectionState, func_name, std::move(cc), args...);
+               return std::get<INDEX>(v.getValues());
        }
-};
-template <typename ... ARGS> struct detail::Method<void(ARGS...)> {
-       ConnectionState connectionState;
-       std::string func_name;
-       std::string info;
+}
 
-       bool call(const ARGS &... args)
-       {
-               CallId callId;
-               detail::displayDebugCallInfo(callId, func_name, info, args...);
-               auto reply = detail::call(callId, connectionState, func_name, args...);
-               return bool(reply);
-       }
-       void asyncCall(const ARGS &... args, std::function<void(bool)> callback)
-       {
-               CallId callId;
-               detail::displayDebugCallInfo(callId, func_name, info, args...);
-               auto connectionState = this->connectionState;
-               auto fn = func_name;
-               auto cc = [callId, connectionState, callback](const Eldbus_Message * reply) {
-                       DEBUG("call %d: processing asynchronous reply", callId.id);
-                       if (!reply)
-                               callback(false);
-                       else {
-                               callback(true);
-                       }
-                       DEBUG("call %d: processing asynchronous reply done", callId.id);
-               };
-               detail::asyncCall(callId, connectionState, func_name, std::move(cc), args...);
-       }
-};
-template <typename ... ARGS> struct detail::Listen<void(ARGS...)> {
-       detail::ConnectionState connectionState;
-       std::vector<std::function<void(const Eldbus_Message *)>*> &callbacks;
-       std::string func_name;
-       std::string info;
-
-       void add(std::function<void(ARGS...)> cc)
-       {
-               CallId callId;
-               detail::displayDebugCallInfo(callId, func_name, info);
-               detail::addListener<ARGS...>(callId, connectionState, func_name, callbacks, std::move(cc));
-       }
-};
+#undef DBUS_DEBUG
 
 #endif
index 9663958..a89df17 100644 (file)
@@ -35,7 +35,7 @@ public:
        bool process() override
        {
                auto point = uiElement->getScanningCoordinates();
-               DBus dbus {dbusLocators::accessibilityEMod::BUS, dbusLocators::accessibilityEMod::OBJ_PATH, dbusLocators::accessibilityEMod::INTERFACE, DBus::ConnectionType::SYSTEM};
+               DBus::DBusClient dbus {dbusLocators::accessibilityEMod::BUS, dbusLocators::accessibilityEMod::OBJ_PATH, dbusLocators::accessibilityEMod::INTERFACE, DBus::ConnectionType::SYSTEM};
                dbus.method<void(int, int, int)>("DispatchGestureEvent").call(swipeType, point.x, point.y);
                return true;
        }
index 0435b08..8300f52 100644 (file)
@@ -42,19 +42,20 @@ class NavigationImpl : public NavigationInterface
 public:
        NavigationImpl()
        {
-               DBus proxy{"org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus"};
+               DBus::DBusClient proxy{"org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus"};
                auto addr = proxy.method<std::string()>("GetAddress").call();
                if (addr) {
-                       DEBUG("got dbus %s", (*addr).c_str());
-                       EldbusConnectionCallbackHandle connection { eldbus_address_connection_get((*addr).c_str()), eldbus_connection_unref };
+                       DEBUG("got dbus %s", std::get<0>(addr).c_str());
+                       DBus::EldbusConnectionCallbackHandle connection { eldbus_address_connection_get(std::get<0>(addr).c_str()), eldbus_connection_unref };
                        navProxy = { "org.tizen.ScreenNavigator", "/org/tizen/ScreenNavigator", "org.tizen.ScreenNavigator", std::move(connection) };
+                       navProxyServer = { connection };
                }
 
                if (!navProxy)
                        return;
 
 #define LISTEN(n) \
-       do { navProxy.listen<void()>(#n).add([=]() { emitCallback<NavigationCallbackType::n>(); }); } while (0)
+       do { navProxy.addSignal<void()>(#n, [=]() { emitCallback<NavigationCallbackType::n>(); }); } while (0)
                LISTEN(FirstRow);
                LISTEN(LastRow);
                LISTEN(FirstElementInRow);
@@ -62,20 +63,20 @@ public:
 #undef LISTEN
 
 #define LISTEN(n, SignalName) \
-       do { navProxy.listen<void()>(SignalName).add([=]() { emitCallback<NavigationCallbackType::n>(); }); } while (0)
+       do { navProxy.addSignal<void()>(SignalName, [=]() { emitCallback<NavigationCallbackType::n>(); }); } while (0)
                LISTEN(DashedRow, "SpecialRow");
                LISTEN(DashedElementInRow, "SpecialElementInRow");
 #undef LISTEN
-               navProxy.listen<void(AtspiAccessiblePtr, int, int, int, int)>("ContextChanged").add(
+               navProxy.addSignal<void(AtspiAccessiblePtr, int, int, int, int)>("ContextChanged",
                [ = ](AtspiAccessiblePtr obj, int x, int y, int w, int h) {
                        emitCallback<NavigationCallbackType::ContextChanged>(std::make_shared<UIElement>(obj), x, y, w, h);
                        DEBUG("got element %s", Singleton<Atspi>::instance().getUniqueId(obj).c_str());
                });
-               navProxy.listen<void(int, int, int, int, BoxPositionMode)>("BoxMoved").add(
+               navProxy.addSignal<void(int, int, int, int, BoxPositionMode)>("BoxMoved",
                [ = ](int x, int y, int w, int h, BoxPositionMode mode) {
                        emitCallback<NavigationCallbackType::BoxMoved>(x, y, w, h, mode);
                });
-               navProxy.listen<void(AtspiAccessiblePtr, std::vector<std::pair<std::string, std::string>>)>("HackAttributesForRootAfterContextChanged").add(
+               navProxy.addSignal<void(AtspiAccessiblePtr, std::vector<std::pair<std::string, std::string>>)>("HackAttributesForRootAfterContextChanged",
                [ = ](AtspiAccessiblePtr root, std::vector<std::pair<std::string, std::string>> attrs) {
                        hack_setRootObjectAttributes(root, std::move(attrs));
                });
@@ -112,13 +113,13 @@ public:
        std::shared_ptr<UIElement> getCurrentElement() override
        {
                auto current = navProxy.method<AtspiAccessiblePtr()>("GetCurrentElement").call();
-               return std::make_shared<UIElement>(std::move(*current));
+               return std::make_shared<UIElement>(std::move(std::get<0>(current)));
        }
 
        std::shared_ptr<UIElement> getElementAtPoint(int x, int y) override
        {
                auto elem = navProxy.method<AtspiAccessiblePtr(int, int)>("GetElementAtPoint").call(x, y);
-               return std::make_shared<UIElement>(std::move(*elem), Point{x, y});
+               return std::make_shared<UIElement>(std::move(std::get<0>(elem)), Point{x, y});
        }
 
 private:
@@ -134,7 +135,8 @@ private:
                }
        }
 
-       DBus navProxy;
+       DBus::DBusClient navProxy;
+       DBus::DBusServer navProxyServer;
 };
 
 template<>
index cc54a28..8675469 100644 (file)
@@ -8,11 +8,11 @@ ScreenSwitchProvider::ScreenSwitchProvider()
 {
        switches.emplace_back(std::make_shared<Switch>("touch", switchProviderId));
 
-       dbus.listen<void(int)>("MouseDown").add([this](auto x) {
+       dbus.addSignal<void(int)>("MouseDown", [this](auto x) {
                this->onTouch(x);
        });
 
-       dbus.listen<void(int)>("MouseUp").add([this](auto x) {
+       dbus.addSignal<void(int)>("MouseUp", [this](auto x) {
                this->onRelease(x);
        });
 }
index 918560f..9f83cd2 100644 (file)
@@ -22,7 +22,7 @@ private:
 
        static bool isSingleTouch(int deviceId);
 
-       DBus dbus;
+       DBus::DBusClient dbus;
 
        static constexpr int SINGLE_TOUCH = 0; //When event comes multitouch is recognized as touch with indexes higher than 0
 };
index f9eeae6..74e4882 100644 (file)
@@ -125,9 +125,9 @@ void TextToSpeech::speak(const std::shared_ptr<UIElement> &element) const
        }
 
        Singleton<UniversalSwitch>::instance().getAtspi()->getName(element->getObject(),
-       [ptr = shared_from_this()](Optional<std::string> name) {
+       [ptr = shared_from_this()](DBus::ValueOrError<std::string> name) {
                if (name)
-                       ptr->speak(*name);
+                       ptr->speak(std::get<0>(name));
        });
 }
 
index 5bba9fa..ff85778 100644 (file)
@@ -22,13 +22,13 @@ std::shared_ptr<AtspiAccessible> UIElement::getObject() const
 void UIElement::getAttributesAsync(std::function<void()> callback)
 {
        atspi->getAttributes(obj,
-       [ptr = shared_from_this(), callback](Optional<std::unordered_map<std::string, std::string>> data) {
+       [ptr = shared_from_this(), callback](DBus::ValueOrError<std::unordered_map<std::string, std::string>> data) {
                if (!data) {
                        ASSERT(0, "getAttributesAsync failed");
                        return;
                }
 
-               ptr->attributes = std::move(data);
+               ptr->attributes = std::move(std::get<0>(data));
                for (const auto &a : *ptr->attributes)
                        DEBUG("%s = %s", std::get<0>(a).c_str(), std::get<1>(a).c_str());
 
@@ -76,28 +76,28 @@ namespace
        void doSelect(std::shared_ptr<Atspi> atspi, const std::shared_ptr<AtspiAccessible> &obj)
        {
                atspi->getParent(obj,
-               [ = ](Optional<std::shared_ptr<AtspiAccessible>> parent) {
+               [ = ](DBus::ValueOrError<std::shared_ptr<AtspiAccessible>> parent) {
                        if (!parent) {
                                DEBUG("no parent");
                                return;
                        }
 
                        atspi->getIndexInParent(obj,
-                       [ = ](Optional<int> index) {
+                       [ = ](DBus::ValueOrError<int> index) {
                                if (!index) {
                                        DEBUG("cant get index in parent");
                                        return;
                                }
 
-                               atspi->getSelectionInterface(*parent,
-                               [ = ](Optional<std::shared_ptr<AtspiSelection>> sel) {
+                               atspi->getSelectionInterface(std::get<0>(parent),
+                               [ = ](DBus::ValueOrError<std::shared_ptr<AtspiSelection>> sel) {
                                        if (!sel) {
                                                DEBUG("no selection interface");
                                                return;
                                        }
 
-                                       atspi->selectChild(*sel, *index,
-                                       [](bool isSuccessful) {
+                                       atspi->selectChild(std::get<0>(sel), std::get<0>(index),
+                                       [](DBus::ValueOrError<void> isSuccessful) {
                                                if (!isSuccessful) {
                                                        DEBUG("failed to select");
                                                        return;
@@ -112,15 +112,15 @@ namespace
        void doActivate(std::shared_ptr<Atspi> atspi, const std::shared_ptr<AtspiAccessible> obj)
        {
                atspi->getActionInterface(obj,
-               [atspi, obj](Optional<std::shared_ptr<AtspiAction>> action) {
+               [atspi, obj](DBus::ValueOrError<std::shared_ptr<AtspiAction>> action) {
                        if (!action) {
                                doSelect(atspi, obj);
                                return;
                        }
 
-                       atspi->doActionName(*action, "activate",
-                       [atspi, obj](Optional<bool> status) {
-                               if (!status || !(*status)) {
+                       atspi->doActionName(std::get<0>(action), "activate",
+                       [atspi, obj](DBus::ValueOrError<bool> status) {
+                               if (!status || !std::get<0>(status)) {
                                        doSelect(atspi, obj);
                                        return;
                                }
@@ -149,49 +149,53 @@ namespace
        void changeValue(std::shared_ptr<AtspiAccessible> obj, ChangeDirection change)
        {
                auto setCurrentValue = [change](auto valueInterface, auto currentValue, auto maximumValue, auto minimumValue) {
-                       auto stepSize = (*maximumValue - *minimumValue) / NUMBER_OF_STEPS;
+                       auto stepSize = (maximumValue - minimumValue) / NUMBER_OF_STEPS;
                        auto newValue = 0.0;
                        switch (change) {
                        case ChangeDirection::INCREMENT:
-                               newValue = *currentValue + stepSize;
+                               newValue = currentValue + stepSize;
                                break;
                        case ChangeDirection::DECREMENT:
-                               newValue = *currentValue - stepSize;
+                               newValue = currentValue - stepSize;
                                break;
                        }
 
-                       Singleton<Atspi>::instance().setCurrentValue(*valueInterface, newValue,
-                       [](bool isSuccessful) {
+                       Singleton<Atspi>::instance().setCurrentValue(valueInterface, newValue,
+                       [](DBus::ValueOrError<void> isSuccessful) {
                                if (!isSuccessful)
                                        DEBUG("Value change failed");
                        });
                };
 
                Singleton<Atspi>::instance().getObjectInRelation(obj, ATSPI_RELATION_CONTROLLER_FOR,
-               [obj, setCurrentValue](Optional<std::shared_ptr<AtspiAccessible>> objInRelation) {
+               [obj, setCurrentValue](DBus::ValueOrError<std::shared_ptr<AtspiAccessible>> objInRelation) {
                        if (!objInRelation)
                                objInRelation = obj;
 
-                       Singleton<Atspi>::instance().getValueInterface(*objInRelation,
-                       [setCurrentValue](Optional<std::shared_ptr<AtspiValue>> valueInterface) {
+                       Singleton<Atspi>::instance().getValueInterface(std::get<0>(objInRelation),
+                       [setCurrentValue](DBus::ValueOrError<std::shared_ptr<AtspiValue>> valueInterface) {
                                if (!valueInterface)
                                        return;
 
-                               Singleton<Atspi>::instance().getCurrentValue(*valueInterface,
-                               [setCurrentValue, valueInterface](Optional<double> currentValue) {
+                               Singleton<Atspi>::instance().getCurrentValue(std::get<0>(valueInterface),
+                               [setCurrentValue, valueInterface](DBus::ValueOrError<double> currentValue) {
                                        if (!currentValue)
                                                return;
 
-                                       Singleton<Atspi>::instance().getMaximumValue(*valueInterface,
-                                       [setCurrentValue, valueInterface, currentValue](Optional<double> maximumValue) {
+                                       Singleton<Atspi>::instance().getMaximumValue(std::get<0>(valueInterface),
+                                       [setCurrentValue, valueInterface, currentValue](DBus::ValueOrError<double> maximumValue) {
                                                if (!maximumValue)
                                                        return;
 
-                                               Singleton<Atspi>::instance().getMinimumValue(*valueInterface,
-                                               [setCurrentValue, valueInterface, currentValue, maximumValue](Optional<double> minimumValue) {
+                                               Singleton<Atspi>::instance().getMinimumValue(std::get<0>(valueInterface),
+                                               [setCurrentValue, valueInterface, currentValue, maximumValue](DBus::ValueOrError<double> minimumValue) {
                                                        if (!minimumValue)
                                                                return;
-                                                       setCurrentValue(valueInterface, currentValue, maximumValue, minimumValue);
+                                                       setCurrentValue(
+                                                               std::get<0>(valueInterface),
+                                                               std::get<0>(currentValue),
+                                                               std::get<0>(maximumValue),
+                                                               std::get<0>(minimumValue));
                                                });
                                        });
                                });
diff --git a/tests/no-ui-scenarios/DBusTests.cpp b/tests/no-ui-scenarios/DBusTests.cpp
new file mode 100644 (file)
index 0000000..7cc02e9
--- /dev/null
@@ -0,0 +1,694 @@
+#include "DBus.hpp"
+#include <gtest/gtest.h>
+#include <atspi/atspi.h>
+#include <glib.h>
+#include <iostream>
+#include <Ecore.h>
+#include <Ecore_Input.h>
+#include <Elementary.h>
+#include <tizen.h>
+#include <app.h>
+#include <thread>
+#include <functional>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+struct TimerData {
+       std::function<void()> callback;
+       Ecore_Timer *timer = nullptr;
+};
+static std::vector<std::unique_ptr<TimerData>> callbacks;
+
+class DBusTest : public ::testing::Test
+{
+protected:
+       bool success = false, done = false;
+
+       static Eina_Bool callback_fnc(void *data)
+       {
+               auto index = reinterpret_cast<unsigned int>(data);
+               callbacks[index]->callback();
+               callbacks[index]->timer = nullptr;
+               return ECORE_CALLBACK_CANCEL;
+       }
+       void SetUp(void) override
+       {
+               static bool init = true;
+               if (init) {
+                       init = false;
+                       auto z = ecore_init();
+                       ASSERT_EQ(z, 1);
+                       ecore_event_init();
+                       eldbus_init();
+                       // DBus::setDebugPrinter([](const char *txt, size_t s) {
+                       //      std::cerr << txt << std::endl;
+                       // });
+               }
+       }
+       void cancelAll()
+       {
+               for (auto &a : callbacks) {
+                       if (a->timer)
+                               ecore_timer_del(a->timer);
+               }
+               callbacks.resize(0);
+       }
+       void TearDown() override
+       {
+               cancelAll();
+       }
+       void finish(bool succ)
+       {
+               cancelAll();
+               success = succ;
+               ecore_main_loop_quit();
+       }
+       void addTimer(float timeout, std::function<void()> callback)
+       {
+               auto t = std::make_unique<TimerData>();
+               t->callback = std::move(callback);
+               auto index = (unsigned int)callbacks.size();
+               callbacks.push_back(std::move(t));
+               callbacks.back()->timer = ecore_timer_add(timeout, callback_fnc, reinterpret_cast<void *>(index));
+       }
+       void runTestInMainLoop(float timeout)
+       {
+               success = false;
+               done = false;
+               addTimer(timeout, [this]() {
+                       done = true;
+                       ecore_main_loop_quit();
+                       return ECORE_CALLBACK_CANCEL;
+               });
+               ecore_main_loop_begin();
+       }
+
+       static void addValues(std::map<int, int> &dst, std::map<int, int> &s1, std::map<int, int> &s2)
+       {
+               for (auto &a : s1)
+                       dst[a.first] = a.second + s2[a.first];
+       }
+       static void addValues(std::unordered_map<int, int> &dst, std::unordered_map<int, int> &s1, std::unordered_map<int, int> &s2)
+       {
+               for (auto &a : s1)
+                       dst[a.first] = a.second + s2[a.first];
+       }
+       static void addValues(std::vector<int> &dst, std::vector<int> &s1, std::vector<int> &s2)
+       {
+               dst.resize(s1.size());
+               for (size_t i = 0; i < s1.size(); ++i) {
+                       dst[i] = s1[i] + s2[i];
+               }
+       }
+       template <size_t I> static void addValues(std::array<int, I> &dst, std::array<int, I> &s1, std::array<int, I> &s2)
+       {
+               for (size_t i = 0; i < I; ++i) {
+                       dst[i] = s1[i] + s2[i];
+               }
+       }
+       template <typename T1, typename T2> static void compareValues(T1 src, T2 expected)
+       {
+               EXPECT_EQ(src.size(), expected.size());
+               if (src.size() == expected.size()) {
+                       using T = std::reference_wrapper<typename T1::value_type>;
+                       std::vector<T> s { src.begin(), src.end() }, e { expected.begin(), expected.end() };
+                       std::sort(s.begin(), s.end(), [](auto a, auto b) {
+                               return a.get() < b.get();
+                       });
+                       std::sort(e.begin(), e.end(), [](auto a, auto b) {
+                               return a.get() < b.get();
+                       });
+                       for (size_t i = 0; i < s.size(); ++i) {
+                               EXPECT_EQ(s[i].get(), e[i].get());
+                       }
+               }
+       }
+
+       template <typename S, typename T = S> void sendAndReceiveT(S source, T expected)
+       {
+               auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+               DBus::DBusInterfaceDescription dsc("itest.itest");
+               dsc.addMethod<void(T)>("ftest", [ = ](T a) -> DBus::ValueOrError<void> {
+                       EXPECT_EQ(a.size(), expected.size());
+                       if (a.size() == expected.size())
+                       {
+                               compareValues(a, expected);
+                       }
+                       return {};
+               });
+
+               DBus::DBusServer server{connection};
+               server.addInterface("/ptest", dsc);
+
+               DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+               db.method<void(S)>("ftest").asyncCall([ = ](DBus::ValueOrError<void> val) {
+                       EXPECT_TRUE(val);
+                       finish(true);
+               }, source);
+               runTestInMainLoop(5.0f);
+               EXPECT_TRUE(success);
+       }
+       template <typename S, typename R> void sendAndReceiveMultipleArgs()
+       {
+               auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+               DBus::DBusInterfaceDescription dsc("itest.itest");
+               dsc.addMethod<R(int, int)>("ftest", [](int a, int b) -> DBus::ValueOrError<R> {
+                       return { { a, b} };
+               });
+
+               DBus::DBusServer server{connection};
+               server.addInterface("/ptest", dsc);
+
+               DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+               db.method<S(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<S> val) {
+                       EXPECT_TRUE(val);
+                       if (val) {
+                               auto v = std::get<0>(val);
+                               EXPECT_EQ(std::get<0>(v), 7);
+                               EXPECT_EQ(std::get<1>(v), 15);
+                       }
+                       finish(bool(val));
+               }, 7, 15);
+               runTestInMainLoop(5.0f);
+               EXPECT_TRUE(success);
+       }
+};
+
+TEST_F(DBusTest, timeouted)
+{
+       runTestInMainLoop(0.1f);
+       EXPECT_FALSE(success);
+}
+
+// calls asynchronously a method. checks, error is passed correctly.
+TEST_F(DBusTest, sendAndReceiveError)
+{
+       auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+       DBus::DBusInterfaceDescription dsc("itest.itest");
+       dsc.addMethod<int(int, int)>("ftest", [](int a, int b) -> DBus::ValueOrError<int> {
+               return DBus::Error{ "error"};
+       });
+
+       DBus::DBusServer server{connection};
+       server.addInterface("/ptest", dsc);
+
+       DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+       db.method<int(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<int> val) {
+               EXPECT_FALSE(val);
+               finish(true);
+       }, 7, 15);
+       runTestInMainLoop(5.0f);
+       EXPECT_TRUE(success);
+}
+
+// calls asynchronously a method. checks, int marshalls property.
+TEST_F(DBusTest, sendAndReceiveInt)
+{
+       auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+       DBus::DBusInterfaceDescription dsc("itest.itest");
+       dsc.addMethod<int(int, int)>("ftest", [](int a, int b) -> DBus::ValueOrError<int> {
+               return a + b;
+       });
+
+       DBus::DBusServer server{connection};
+       server.addInterface("/ptest", dsc);
+
+       DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+       db.method<int(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<int> val) {
+               EXPECT_TRUE(val);
+               if (val)
+                       EXPECT_EQ(std::get<0>(val), 7 + 15);
+               finish(true);
+       }, 7, 15);
+       runTestInMainLoop(5.0f);
+       EXPECT_TRUE(success);
+}
+
+// calls method on extern process asynchronously, which in turn calls
+// method asynchronously on the original process. Checks, if asynchronous return value production
+// works - responding callback receives reply callback, which should be called, when return data is ready
+TEST_F(DBusTest, sendAndReceiveAsyncInt)
+{
+       auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+       DBus::DBusInterfaceDescription dsc("itest.itest");
+       dsc.addAsyncMethod<int(int, int)>("ftest", [&](std::function<void(DBus::ValueOrError<int>)> reply, int a, int b) {
+               if (b == 0) {
+                       reply(a + 10);
+               } else if (b == 1) {
+                       reply(DBus::Error{"error1"});
+               } else if (b == 2) {
+                       addTimer(0.0f, [ = ]() {
+                               reply(a + 15);
+                       });
+               } else if (b == 3) {
+                       addTimer(0.0f, [ = ]() {
+                               reply(DBus::Error { "error2"});
+                       });
+               } else {
+                       EXPECT_TRUE(0);
+               }
+       });
+
+       DBus::DBusServer server{connection};
+       server.addInterface("/ptest", dsc);
+
+       DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+       db.method<int(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<int> val) {
+               EXPECT_TRUE(val);
+               if (val) {
+                       EXPECT_EQ(std::get<0>(val), 7 + 10);
+                       finish(true);
+               } else {
+                       finish(false);
+               }
+       }, 7, 0);
+       runTestInMainLoop(1.0f);
+       ASSERT_TRUE(success);
+
+       db.method<int(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<int> val) {
+               EXPECT_FALSE(val);
+               if (!val) {
+                       EXPECT_EQ(val.getError().message, "org.freedesktop.DBus.Error.Failed: error1");
+                       finish(true);
+               } else {
+                       finish(false);
+               }
+       }, 17, 1);
+       runTestInMainLoop(1.0f);
+       ASSERT_TRUE(success);
+
+       db.method<int(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<int> val) {
+               EXPECT_TRUE(val);
+               if (val) {
+                       EXPECT_EQ(std::get<0>(val), 27 + 15);
+                       finish(true);
+               } else {
+                       finish(false);
+               }
+       }, 27, 2);
+       runTestInMainLoop(1.0f);
+       ASSERT_TRUE(success);
+
+       db.method<int(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<int> val) {
+               EXPECT_FALSE(val);
+               if (!val) {
+                       EXPECT_EQ(val.getError().message, "org.freedesktop.DBus.Error.Failed: error2");
+                       finish(true);
+               } else {
+                       finish(false);
+               }
+       }, 37, 3);
+       runTestInMainLoop(1.0f);
+       ASSERT_TRUE(success);
+}
+
+// calls asynchronously a method. checks, std::vector marshalls property.
+TEST_F(DBusTest, sendAndReceiveVector)
+{
+       sendAndReceiveT<std::vector<int>>({ 1, 2 }, { 1, 2 });
+}
+
+// calls asynchronously a method. checks, std::vector can be received as std::array
+TEST_F(DBusTest, sendAndReceiveVector2)
+{
+       std::array<int, 2> arr { 1, 2 };
+       sendAndReceiveT<std::vector<int>, std::array<int, 2>>({ 1, 2 }, arr);
+}
+
+// calls asynchronously a method. checks, std::array marshalls property.
+TEST_F(DBusTest, sendAndReceiveArray)
+{
+       std::array<int, 2> arr { 1, 2 };
+       sendAndReceiveT<std::array<int, 2>>(arr, arr);
+}
+
+// calls asynchronously a method. checks, std::array can be received as std::vector
+TEST_F(DBusTest, sendAndReceiveArray2)
+{
+       std::array<int, 2> arr { 1, 2 };
+       sendAndReceiveT<std::array<int, 2>, std::vector<int>>(arr, { 1, 2 });
+}
+
+// calls asynchronously a method. checks, std::map marshalls property.
+TEST_F(DBusTest, sendAndReceiveMap)
+{
+       sendAndReceiveT<std::map<int, int>>({ { 1, 1}, {2, 2} }, { { 1, 1}, {2, 2} });
+}
+
+// calls asynchronously a method. checks, std::unordered_map marshalls property.
+TEST_F(DBusTest, sendAndReceiveUnorderedMap)
+{
+       sendAndReceiveT<std::unordered_map<int, int>>({ { 1, 1}, {2, 2} }, { { 1, 1}, {2, 2} });
+}
+
+// calls asynchronously a method. checks, if std::tuple of values can be
+// received as std::pair
+TEST_F(DBusTest, sendAndReceiveMultipleArgs)
+{
+       sendAndReceiveMultipleArgs<std::tuple<int, int>, std::pair<int, int>>();
+}
+
+// calls asynchronously a method. checks, std::tuple marshalls property.
+TEST_F(DBusTest, sendAndReceiveMultipleArgs2)
+{
+       sendAndReceiveMultipleArgs<std::tuple<int, int>, std::tuple<int, int>>();
+}
+
+// calls asynchronously a method. checks, std::pair marshalls property.
+TEST_F(DBusTest, sendAndReceiveMultipleArgs3)
+{
+       sendAndReceiveMultipleArgs<std::pair<int, int>, std::pair<int, int>>();
+}
+
+// calls asynchronously a method. checks, if std::pair of values can be
+// received as std::tuple
+TEST_F(DBusTest, sendAndReceiveMultipleArgs4)
+{
+       sendAndReceiveMultipleArgs<std::pair<int, int>, std::tuple<int, int>>();
+}
+
+// gets property asynchronously, then sets property asynchronously,
+// then gets it again to check, if it has correct value
+TEST_F(DBusTest, sendAndReceiveProperty)
+{
+       auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+       DBus::DBusInterfaceDescription dsc("itest.itest");
+       int realVal = 10;
+       dsc.addProperty<int>("ftest",
+       [&]() -> DBus::ValueOrError<int> {
+               return realVal;
+       },
+       [&](int q) -> DBus::ValueOrError<void> {
+               realVal = q;
+               return DBus::Success{};
+       });
+
+       DBus::DBusServer server{connection};
+       server.addInterface("/ptest", dsc);
+
+       DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+       db.property<int>("ftest").asyncGet([ & ](DBus::ValueOrError<int> val) {
+               EXPECT_TRUE(val);
+               if (val) {
+                       EXPECT_EQ(std::get<0>(val), 10);
+                       EXPECT_EQ(realVal, 10);
+
+                       db.property<int>("ftest").asyncSet([ & ](DBus::ValueOrError<void> val) {
+                               EXPECT_TRUE(val);
+                               EXPECT_EQ(realVal, 15);
+                               if (val) {
+                                       db.property<int>("ftest").asyncGet([ & ](DBus::ValueOrError<int> val) {
+                                               EXPECT_TRUE(val);
+                                               if (val) {
+                                                       EXPECT_EQ(std::get<0>(val), 15);
+                                                       EXPECT_EQ(realVal, 15);
+                                                       finish(true);
+                                               } else {
+                                                       finish(false);
+                                               }
+                                       });
+                               } else {
+                                       finish(false);
+                               }
+                       }, 15);
+               } else {
+                       finish(false);
+               }
+       });
+       runTestInMainLoop(1.0f);
+       EXPECT_TRUE(success);
+}
+
+// gets property asynchronously, then sets property asynchronously,
+// then gets it again to check, if it has correct value
+// uses fallback interface (request path must begins with property interface path)
+TEST_F(DBusTest, sendAndReceiveFallbackProperty)
+{
+       auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+       DBus::DBusInterfaceDescription dsc("itest.itest");
+       std::unordered_map<std::string, int> values;
+
+       dsc.addProperty<int>("ftest",
+       [&]() -> DBus::ValueOrError<int> {
+               auto it = values.find(DBus::DBusServer::getCurrentObjectPath());
+               EXPECT_TRUE(it != values.end());
+               if (it != values.end()) return it->second;
+               return -1;
+       },
+       [&](int q) -> DBus::ValueOrError<void> {
+               values[DBus::DBusServer::getCurrentObjectPath()] = q;
+               return DBus::Success{};
+       });
+
+       DBus::DBusServer server{connection};
+       server.addInterface("/ptest", dsc, true);
+       unsigned int doneCount = 0;
+       std::vector<std::string> paths { "/ptest", "/ptest/p1", "/ptest/p2" };
+       for (auto p : paths)
+               values[p] = 10 + (int)p.size();
+
+       for (auto path : paths) {
+               DBus::DBusClient db{server.getBusName(), path, "itest.itest", connection};
+               db.property<int>("ftest").asyncGet([ & ](DBus::ValueOrError<int> val) {
+                       EXPECT_TRUE(val);
+                       if (val) {
+                               EXPECT_EQ(std::get<0>(val), 10 + (int)path.size());
+                               EXPECT_EQ(values[path], 10 + (int)path.size());
+
+                               db.property<int>("ftest").asyncSet([ & ](DBus::ValueOrError<void> val) {
+                                       EXPECT_TRUE(val);
+                                       EXPECT_EQ(values[path], 30 + (int)path.size());
+                                       if (val) {
+                                               db.property<int>("ftest").asyncGet([ & ](DBus::ValueOrError<int> val) {
+                                                       EXPECT_TRUE(val);
+                                                       ++doneCount;
+                                                       if (val) {
+                                                               EXPECT_EQ(std::get<0>(val), 30 + (int)path.size());
+                                                               EXPECT_EQ(values[path], 30 + (int)path.size());
+                                                               finish(true);
+                                                       } else {
+                                                               finish(false);
+                                                       }
+                                               });
+                                       } else {
+                                               finish(false);
+                                       }
+                               }, 30 + path.size());
+                       } else {
+                               finish(false);
+                       }
+               });
+               runTestInMainLoop(1.0f);
+               ASSERT_TRUE(success);
+       }
+}
+
+// emits and catches signal
+TEST_F(DBusTest, sendAndReceiveSignal)
+{
+       auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+       DBus::DBusInterfaceDescription dsc("itest.itest");
+       std::unordered_map<std::string, int> values;
+
+       auto signalId = dsc.addSignal<int, int>("ftest");
+       DBus::DBusServer server{connection};
+       server.addInterface("/ptest", dsc, false);
+
+       DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+       int signalValue = 1;
+       db.addSignal<void(int, int)>("ftest", [&](int a1, int a2) {
+               signalValue = a1 + a2;
+               finish(true);
+       });
+       addTimer(0.0f, [&]() {
+               server.emit<int, int>(signalId, 11, 356);
+       });
+       runTestInMainLoop(1.0f);
+       EXPECT_TRUE(success);
+       EXPECT_EQ(signalValue, 11 + 356);
+}
+
+static const std::string syncInterfaceName = "sitest.sitest";
+static const std::string syncPathName = "/stest";
+static std::string testBusName;
+
+// Calls method on external process asynchronously
+TEST_F(DBusTest, sendAndReceiveMethodAsync)
+{
+       auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+       DBus::DBusClient db{testBusName, syncPathName, syncInterfaceName, connection};
+       db.method<int(int, int)>("add").asyncCall([ = ](DBus::ValueOrError<int> val) {
+               EXPECT_TRUE(val);
+               if (val) {
+                       EXPECT_EQ(std::get<0>(val), 10 + 55);
+                       finish(true);
+               } else {
+                       finish(false);
+               }
+       }, 10, 55);
+       runTestInMainLoop(1.0f);
+       EXPECT_TRUE(success);
+}
+
+// Calls method on external process synchronously
+TEST_F(DBusTest, synchronousCall)
+{
+       auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+       DBus::DBusClient db{testBusName, syncPathName, syncInterfaceName, connection};
+       auto r = db.method<int(int, int)>("add").call(10, 55);
+       ASSERT_TRUE(r);
+       ASSERT_EQ(std::get<0>(r), 10 + 55);
+       r = db.method<int(int, int)>("add").call(-10, 55);
+       ASSERT_FALSE(r);
+       ASSERT_EQ(r.getError().message, "org.freedesktop.DBus.Error.Failed: negative");
+}
+
+// Calls method 'call' on external process asynchronously,
+// which calls back synchronously method 'setReply' on original process
+TEST_F(DBusTest, recursiveCall)
+{
+       auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+
+       DBus::DBusInterfaceDescription dsc("itest.itest2");
+       int calledValue = 1;
+       dsc.addMethod<int(int)>("setReply", [&](int val) -> DBus::ValueOrError<int> {
+               calledValue = val;
+               return val + 10;
+       });
+
+       DBus::DBusServer server{connection};
+       server.addInterface("/ptesttest", dsc, false);
+
+       DBus::DBusClient db{testBusName, syncPathName, syncInterfaceName, connection};
+       db.method<int(int, int)>("call").asyncCall([&](DBus::ValueOrError<int> val) {
+               EXPECT_TRUE(val);
+               if (val)
+                       EXPECT_EQ(std::get<0>(val), 111 + 10);
+               finish(bool(val));
+       }, server.getBusName(), "/ptesttest", "itest.itest2", "setReply", 111);
+
+       runTestInMainLoop(1.0f);
+       ASSERT_TRUE(success);
+       ASSERT_EQ(calledValue, 111);
+}
+
+// Calls method 'emitS' on external process asynchronously,
+// which emits signal, which is received on original process
+TEST_F(DBusTest, recursiveSignal)
+{
+       auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+
+       DBus::DBusClient db{testBusName, syncPathName, syncInterfaceName, connection};
+       int realValue = 1;
+       db.addSignal<void(int)>("signal", [&](int val) {
+               EXPECT_EQ(val, 20);
+               realValue = val;
+               finish(true);
+       });
+
+       db.method<int(int)>("emitS").asyncCall([&](DBus::ValueOrError<int> val) {
+               EXPECT_TRUE(val);
+               if (val)
+                       EXPECT_EQ(std::get<0>(val), 10 + 30);
+               finish(bool(val));
+       }, 10);
+
+       runTestInMainLoop(1.0f);
+       ASSERT_TRUE(success);
+       ASSERT_EQ(realValue, 20);
+}
+
+std::pair<std::vector<std::unique_ptr<DBus::DBusClient>>, std::vector<std::unique_ptr<DBus::DBusServer>>>
+               initializeServices(char *busNameDest, size_t busNameSize)
+{
+       auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+       std::string busName = eldbus_connection_unique_name_get(connection.get());
+       assert(busName.size() + 2 < busNameSize);
+       strncpy(busNameDest + 1, busName.c_str(), busName.size());
+       busNameDest[0] = 1;
+
+       std::vector<std::unique_ptr<DBus::DBusClient>> clients;
+       std::vector<std::unique_ptr<DBus::DBusServer>> servers;
+
+       auto server = [&]() -> DBus::DBusServer * {
+               auto s = std::make_unique<DBus::DBusServer>(connection);
+               servers.push_back(std::move(s));
+               return servers.back().get();
+       };
+
+       auto s = server();
+       DBus::DBusInterfaceDescription descr(syncInterfaceName);
+
+       auto signal = descr.addSignal<int>("signal");
+       descr.addMethod<int(int, int)>("add", [ = ](int a, int b) -> DBus::ValueOrError<int> {
+               if (a >= 0 && b >= 0)
+               {
+                       return a + b;
+               }
+               return DBus::Error { "negative" };
+       });
+       descr.addMethod<int(int)>("emitS", [ = ](int a) -> DBus::ValueOrError<int> {
+               s->emit<int>(signal, a + 10);
+               return a + 20;
+       });
+       descr.addMethod<int(std::string, std::string, std::string, std::string, int)>("call", [ = ](std::string busName, std::string pName, std::string iName, std::string methodName, int a) -> DBus::ValueOrError<int> {
+               auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+               DBus::DBusClient client { busName, std::move(pName), std::move(iName), connection };
+               return client.method<int(int)>(methodName).call(a);
+       });
+       s->addInterface(syncPathName, descr, false);
+       return { std::move(clients), std::move(servers) };
+}
+
+void runServer(char *busNameDest, size_t busNameSize)
+{
+       // DBus::setDebugPrinter([](const char *txt, size_t s) {
+       //      std::cerr << txt << std::endl;
+       // });
+       ecore_init();
+       ecore_event_init();
+       eldbus_init();
+       auto s = initializeServices(busNameDest, busNameSize);
+       ecore_main_loop_begin();
+}
+
+int main(int argc, char *argv[])
+{
+       constexpr unsigned int size = 4096;
+       char *sharedMem = (char *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
+       if (!sharedMem) return 1;
+       memset(sharedMem, 0, size);
+
+       auto c = fork();
+       if (c == 0) {
+               // child
+               runServer(sharedMem, size);
+       } else if (c > 0) {
+               // parent
+               DEBUG("main function called");
+               auto now = std::chrono::high_resolution_clock::now() + std::chrono::seconds(1);
+
+               while (now > std::chrono::high_resolution_clock::now()) {
+                       if (((volatile char *)sharedMem)[0]) break;
+                       std::this_thread::sleep_for(std::chrono::milliseconds(10));
+               }
+               if (!((volatile char *)sharedMem)[0]) return 1;
+               testBusName = sharedMem + 1;
+
+               int r = 1;
+               try {
+                       ::testing::InitGoogleTest(&argc, argv);
+                       r = RUN_ALL_TESTS();
+               } catch (...) {
+                       DEBUG("exception");
+                       r = 1;
+               }
+               kill(c, 9);
+               return r;
+       } else {
+               ASSERT(0, "failed to fork");
+               return 1;
+       }
+       return 0;
+}