Add asynchronous at-spi calls 73/146273/18
authorRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Fri, 25 Aug 2017 14:36:47 +0000 (16:36 +0200)
committerRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Fri, 15 Sep 2017 14:11:34 +0000 (16:11 +0200)
Universal-switch displays it's own user interface. When querrying at-spi
properties of an object it's possible for universal-switch to inquire
about it's own user interface objects. When using synchronous calls it
leads to no-reply error, as thread, which should produce an answer is
stuck on waiting for it. This patch adds supports for sending
asynchronous dbus calls, which will be used to implement at-spi
asynchronous calls.

Change-Id: Icc88cea9416a004536d221a99d1c2769d763150b

src/Atspi.cpp
src/Atspi.hpp
src/DBus.cpp [new file with mode: 0644]
src/DBus.hpp [new file with mode: 0644]
src/NavigationInterface.cpp

index 0b9a178..93305ff 100644 (file)
@@ -1,6 +1,8 @@
 #include "Atspi.hpp"
+#include "Singleton.hpp"
 #include <memory>
 #include <functional>
+#include <limits>
 
 #define PRINT_ERROR_AND_FREE(error) \
        do { \
                } \
        } while (0)
 
-#define GERROR_CHECK(error) \
-       do { \
-               PRINT_ERROR_AND_FREE(error); \
-               error = nullptr; \
-       } while (0)
-
 #define EXIT_IF_NULLPTR(obj) \
        do { \
                if (!obj) { \
@@ -40,10 +36,81 @@ namespace
        }
 }
 
-static std::shared_ptr<AtspiAccessible> root_object;
+static AtspiAccessiblePtr root_object;
 static std::vector<std::pair<std::string, std::string>> root_object_attributes;
 
-void hack_setRootObjectAttributes(std::shared_ptr<AtspiAccessible> obj, std::vector<std::pair<std::string, std::string>> attrs)
+template <typename T> static std::string getBusNameImpl(T *o)
+{
+       if (!o) return "";
+       auto z = atspi_accessible_get_bus_name(reinterpret_cast<AtspiAccessible *>(o), NULL);
+       std::string q = z;
+       g_free(z);
+       return std::move(q);
+}
+
+template <typename T> static std::string getPathImpl(T *o)
+{
+       if (!o) return "/org/a11y/atspi/null";
+       auto z = atspi_accessible_get_path(reinterpret_cast<AtspiAccessible *>(o), NULL);
+       std::string q = z;
+       g_free(z);
+       return "/org/a11y/atspi/accessible/" + q;
+}
+
+template <typename T> struct InterfaceNameFromType {
+       static const std::string interfaceName;
+};
+
+#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)                                \
+       {                                                                                                     \
+               return getBusNameImpl(o.get());                                                                   \
+       }                                                                                                     \
+       static std::string getPath(const std::shared_ptr<Atspi ## TYPE> &o)                                   \
+       {                                                                                                     \
+               return getPathImpl(o.get());                                                                      \
+       }                                                                                                     \
+       static void convert(std::shared_ptr<Atspi ## TYPE> &r,                                                \
+                                               const AtspiAccessiblePtr &obj)                                      \
+       {                                                                                                     \
+               r =  { ATSPI_ ## NAME(obj.get()), g_object_unref };                                               \
+       }                                                                                                     \
+       void Atspi::get ## TYPE ## Interface(const AtspiAccessiblePtr &obj,                     \
+                                                                                AsyncCallback<std::shared_ptr<Atspi ## TYPE>> callback) const    \
+       {                                                                                                     \
+               getInterface<Atspi ## TYPE>(obj, std::move(callback));                                            \
+       }                                                                                                     \
+       std::string Atspi::getUniqueId(const std::shared_ptr<Atspi ## TYPE> &obj) const                       \
+       {                                                                                                     \
+               if (!obj)                                                                                         \
+                       return "(null)";                                                                              \
+               GError *error = nullptr;                                                                          \
+               auto v = atspi_accessible_get_unique_id(ATSPI_ACCESSIBLE(obj.get()), &error);                     \
+               PRINT_ERROR_AND_FREE(error);                                                                      \
+               if (!error) return "(failed)";                                                                    \
+               auto z = std::string { v };                                                                       \
+               g_free(v);                                                                                        \
+               return std::move(z);                                                                              \
+       }                                                                                                     \
+       AtspiAccessiblePtr Atspi::getBase(                                                      \
+                       const std::shared_ptr<Atspi ## TYPE> &obj) const                                              \
+       {                                                                                                     \
+               if (!obj) return {};                                                                              \
+               return { ATSPI_ACCESSIBLE(obj.get()), g_object_unref };                                           \
+       }
+
+ADD_INTROSPECTION_FUNCTIONS(Accessible, ACCESSIBLE);
+ADD_INTROSPECTION_FUNCTIONS(Action, ACTION);
+ADD_INTROSPECTION_FUNCTIONS(Selection, SELECTION);
+ADD_INTROSPECTION_FUNCTIONS(Collection, COLLECTION);
+ADD_INTROSPECTION_FUNCTIONS(Value, VALUE);
+ADD_INTROSPECTION_FUNCTIONS(EditableText, EDITABLE_TEXT);
+
+#undef ADD_INTROSPECTION_FUNCTIONS
+
+void hack_setRootObjectAttributes(AtspiAccessiblePtr obj, std::vector<std::pair<std::string, std::string>> attrs)
 {
        root_object = std::move(obj);
        root_object_attributes = std::move(attrs);
@@ -53,6 +120,15 @@ Atspi::Atspi()
        : eldbus(), connection(efl::eldbus::session)
 {
        ConnectAtClient();
+
+       DBus 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 };
+       }
 }
 
 Atspi::~Atspi()
@@ -66,8 +142,9 @@ bool Atspi::ConnectAtClient()
        if (error != 0 && error != 1) {
                ERROR("Error code %d", error);
                return false;
-       } else if (error == 1)
+       } else if (error == 1) {
                INFO("Atspi has been already initialized");
+       }
 
        auto dbusStatus = setPropertyBool("org.a11y.Bus", "/org/a11y/bus", "org.a11y.Status", "IsEnabled", true);
        if (!dbusStatus) {
@@ -140,26 +217,72 @@ bool Atspi::setPropertyBool(const char *bus,
        return true;
 }
 
-std::shared_ptr<AtspiAccessible> Atspi::getObjectInRelation(const std::shared_ptr<AtspiAccessible> &accessibleObj, AtspiRelationType searchType) const
+Optional<std::string> Atspi::getName(const AtspiAccessiblePtr &accessibleObj) const
+{
+       GError *error = nullptr;
+       auto name = atspi_accessible_get_name(accessibleObj.get(), &error);
+       if (error) {
+               PRINT_ERROR_AND_FREE(error);
+               if (name) g_free(name);
+               return {};
+       }
+       std::string z = name;
+       g_free(name);
+       return std::move(z);
+}
+
+Optional<AtspiRole> Atspi::getRole(const AtspiAccessiblePtr &accessibleObj) const
+{
+       GError *error = nullptr;
+       auto role = atspi_accessible_get_role(accessibleObj.get(), &error);
+       if (error) {
+               PRINT_ERROR_AND_FREE(error);
+               return {};
+       }
+       return role;
+}
+
+Atspi::StateSet Atspi::getStateSet(const AtspiAccessiblePtr &accessibleObj) const
+{
+       auto states = atspi_accessible_get_state_set(accessibleObj.get());
+       StateSet result;
+       auto v = states->states;
+       g_object_unref(states);
+       for (unsigned int i = 0; i < result.size(); ++i) {
+               if (v & 1) result.set(i);
+               v >>= 1;
+       }
+       return result;
+}
+
+Optional<unsigned int> Atspi::getProcessId(const AtspiAccessiblePtr &accessibleObj) const
+{
+       GError *error = nullptr;
+       auto pid = atspi_accessible_get_process_id(accessibleObj.get(), &error);
+       if (error) {
+               PRINT_ERROR_AND_FREE(error);
+               return {};
+       }
+       return (unsigned int)pid;
+}
+
+AtspiAccessiblePtr Atspi::getObjectInRelation(const AtspiAccessiblePtr &accessibleObj, AtspiRelationType searchType) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        GError *error = nullptr;
        auto relations = atspi_accessible_get_relation_set(accessibleObj.get(), &error);
-       GERROR_CHECK(error);
+       PRINT_ERROR_AND_FREE(error);
        if (!relations) {
                DEBUG("Relation set do not exist");
                return {};
        }
 
-       DEBUG("Relations found: %d", relations->len);
        AtspiAccessible *relationObj = nullptr;
        for (unsigned i = 0; i < relations->len; ++i) {
                AtspiRelation *relation = g_array_index(relations, AtspiRelation *, i);
                auto type = atspi_relation_get_relation_type(relation);
-               DEBUG("Relation type: %d", type);
 
                if (type == searchType) {
-                       DEBUG("Searched relation found");
                        relationObj = atspi_relation_get_target(relation, 0);
                        break;
                }
@@ -169,7 +292,382 @@ std::shared_ptr<AtspiAccessible> Atspi::getObjectInRelation(const std::shared_pt
        return {relationObj, g_object_unref};
 }
 
-std::vector<std::pair<std::string, std::string>> Atspi::getAttributes(const std::shared_ptr<AtspiAccessible> &accessibleObj) const
+template <typename T> DBus 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 };
+}
+
+template <typename> struct PropertyCallback;
+template <typename RetType, typename ... ARGS> struct PropertyCallback<RetType(ARGS...)> {
+       using VariantReturnType = EldbusVariant<RetType>;
+       using RealReturnType = RetType;
+       using CallType = VariantReturnType(std::string, std::string, ARGS...);
+};
+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,
+       ARGS &&... args
+)
+{
+       auto bus = getBusName(obj);
+       auto path = getPath(obj);
+
+       auto dbus = DBus { bus, path, interface, eldbusConnection };
+       dbus.method<CallType>(func_name).asyncCall(std::forward<ARGS>(args)..., std::move(callback));
+}
+
+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,
+       ARGS &&... args
+)
+{
+       callFunction<CallType>(eldbusConnection, InterfaceNameFromType<InterfaceType>::interfaceName,
+                                                  obj, func_name, 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,
+       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));
+       },
+       interface, func_name);
+}
+
+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,
+       ARGS &&... args
+)
+{
+       getProperty<CallType>(eldbusConnection, InterfaceNameFromType<InterfaceType>::interfaceName,
+                                                 obj, func_name, std::move(callback), std::forward<ARGS>(args)...);
+}
+
+void Atspi::getAttributes(const AtspiAccessiblePtr &accessibleObj,
+                                                 AsyncCallback<std::unordered_map<std::string, std::string>> callback) const
+{
+       callFunction<std::unordered_map<std::string, std::string>()>(
+               eldbusConnection,
+               accessibleObj,
+               "GetAttributes",
+               std::move(callback)
+       );
+}
+
+void Atspi::doActionName(const AtspiActionPtr &accessibleObj, const std::string &action, AsyncCallback<bool> callback) const
+{
+       callFunction<bool(std::string)>(
+               eldbusConnection,
+               accessibleObj,
+               "DoActionName",
+               std::move(callback),
+               action
+       );
+}
+
+void Atspi::getParent(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<AtspiAccessiblePtr> callback) const
+{
+       getProperty<AtspiAccessiblePtr()>(
+               eldbusConnection,
+               accessibleObj,
+               "Parent",
+               std::move(callback)
+       );
+}
+
+void Atspi::getIndexInParent(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<int> callback) const
+{
+       callFunction<int()>(
+               eldbusConnection,
+               accessibleObj,
+               "GetIndexInParent",
+               std::move(callback)
+       );
+}
+
+void Atspi::selectChild(const AtspiSelectionPtr &accessibleObj, int index, AsyncCallback<void> callback) const
+{
+       callFunction<void(int)>(
+               eldbusConnection,
+               accessibleObj,
+               "SelectChild",
+               std::move(callback),
+               index
+       );
+}
+
+void Atspi::getName(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::string> callback) const
+{
+       getProperty<std::string()>(
+               eldbusConnection,
+               accessibleObj,
+               "Name",
+               std::move(callback)
+       );
+}
+
+void Atspi::getRole(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<AtspiRole> callback) const
+{
+       callFunction<uint32_t()>(
+               eldbusConnection,
+               accessibleObj,
+               "GetRole",
+       [callback](Optional<uint32_t> val) {
+               if (!val) callback({});
+               else callback((AtspiRole)*val);
+       }
+       );
+}
+
+void Atspi::getChildrenCount(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<size_t> callback) const
+{
+       getProperty<int()>(
+               eldbusConnection,
+               accessibleObj,
+               "ChildCount",
+       [callback](Optional<int> val) {
+               if (!val || *val < 0)
+                       callback({});
+               else
+                       callback((size_t)*val);
+       }
+       );
+}
+
+void Atspi::getChildAtIndex(const AtspiAccessiblePtr &accessibleObj, size_t index, AsyncCallback<AtspiAccessiblePtr> callback) const
+{
+       callFunction<AtspiAccessiblePtr(int)>(
+               eldbusConnection,
+               accessibleObj,
+               "GetChildAtIndex",
+               std::move(callback),
+               (int)index
+       );
+}
+
+void Atspi::getStateSet(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<StateSet> callback) const
+{
+       callFunction<std::array<uint32_t, 2>()>(
+               eldbusConnection,
+               accessibleObj,
+               "GetState",
+       [callback](Optional<std::array<uint32_t, 2>> data) {
+               if (data) {
+                       StateSet s;
+                       for (size_t i = 0; i < (size_t)ATSPI_STATE_LAST_DEFINED; ++i) {
+                               if ((*data)[i >> 5] & (1 << (i & 31))) {
+                                       s[i] = true;
+                                       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) {
+               if (!childrenCount) {
+                       callback({});
+               } else {
+                       struct State {
+                               AsyncCallback<std::vector<AtspiAccessiblePtr>> callback;
+                               size_t todoCount;
+                               std::vector<AtspiAccessiblePtr> children;
+                               bool failed = false;
+                       };
+                       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) {
+                                       if (state->failed)
+                                               return;
+                                       if (!child) {
+                                               state->failed = true;
+                                               state->callback({});
+                                               return;
+                                       }
+                                       state->children[index] = std::move(*child);
+                                       assert(state->todoCount > 0);
+                                       --state->todoCount;
+                                       if (state->todoCount == 0)
+                                               state->callback(std::move(state->children));
+                               };
+                               Singleton<Atspi>::instance().getChildAtIndex(accessibleObj, index, std::move(cback));
+                       }
+               }
+       });
+}
+
+template <typename T> void Atspi::getInterface(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::shared_ptr<T>> callback) const
+{
+       callFunction<std::vector<std::string>()>(
+               eldbusConnection,
+               accessibleObj,
+               "GetInterfaces",
+       [this, callback, accessibleObj](Optional<std::vector<std::string>> val) {
+               const auto interfaceName = InterfaceNameFromType<T>::interfaceName;
+               if (val) {
+                       for (auto &z : *val) {
+                               if (z == interfaceName) {
+                                       std::shared_ptr<T> r;
+                                       convert(r, accessibleObj);
+                                       callback(std::move(r));
+                                       return;
+                               }
+                       }
+               }
+               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());
+
+       auto bus = getBusName(rootObj);
+       auto path = getPath(rootObj);
+
+       auto pr = DBus { 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(
+                                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
+{
+       AtspiCoordType ctype = ATSPI_COORD_TYPE_SCREEN;
+       switch (type) {
+       case CoordType::Screen:
+               ctype = ATSPI_COORD_TYPE_SCREEN;
+               break;
+       case CoordType::Window:
+               ctype = ATSPI_COORD_TYPE_WINDOW;
+               break;
+       }
+       using returnType = std::tuple<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)
+               {
+                       if (!value) {
+                               self->callback({});
+                       } else {
+                               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) {
+                                       if (!ptr) {
+                                               self->callback({});
+                                       } else {
+                                               self->callback(std::move(ptr));
+                                       }
+                                       return;
+                               }
+                               callFunction<returnType(int32_t, int32_t, uint32_t)>(
+                                       self->atspi->eldbusConnection,
+                                       ptr,
+                                       "GetNavigableAtPoint",
+                               [self](Optional<returnType> value) {
+                                       process(std::move(self), std::move(value));
+                               },
+                               self->pt.x, self->pt.y, self->ctype);
+                       }
+               }
+       };
+       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)>(
+               eldbusConnection,
+               root,
+               "GetNavigableAtPoint",
+       [h](Optional<returnType> value) {
+               handler::process(std::move(h), std::move(value));
+       },
+       pt.x, pt.y, (uint32_t)ctype);
+}
+
+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,
+                 std::vector<std::tuple<AtspiAccessiblePtr, int32_t, int32_t, int32_t, int32_t>>>;
+
+       callFunction<returnType()>(
+               eldbusConnection,
+               root,
+               "GetAllAcceptedObjects",
+       [ = ](Optional<returnType> value) {
+               if (!value) {
+                       callback({});
+               } 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);
+                       ois.reserve(v.size());
+                       for (auto &a : v) {
+                               ois.push_back({});
+                               auto &o = ois.back();
+                               o.object = std::move(std::get<0>(a));
+                               o.pos = { { std::get<1>(a), std::get<2>(a)}, { std::get<3>(a), std::get<4>(a) } };
+                       }
+                       callback(std::move(res));
+               }
+       }
+       );
+}
+
+std::vector<std::pair<std::string, std::string>> Atspi::getAttributes(const AtspiAccessiblePtr &accessibleObj) const
 {
        assert(accessibleObj == root_object);
        return root_object_attributes;
@@ -179,7 +677,7 @@ std::vector<std::pair<std::string, std::string>> Atspi::getAttributes(const std:
        EXIT_IF_NULLPTR(accessibleObj);
        GError *error = nullptr;
        GHashTable *attr = atspi_accessible_get_attributes(accessibleObj.get(), nullptr);
-       GERROR_CHECK(error);
+       PRINT_ERROR_AND_FREE(error);
        if (!attr) {
                DEBUG("object do not has attributes");
                return {};
@@ -192,7 +690,6 @@ std::vector<std::pair<std::string, std::string>> Atspi::getAttributes(const std:
        std::vector<std::pair<std::string, std::string>> attributes;
        while (g_hash_table_iter_next(&attr_iter, &attr_key, &attr_value)) {
                attributes.emplace_back((char *) attr_key, (char *) attr_value);
-               DEBUG("atribute: %s = %s", std::get<0>(attributes.back()).c_str(), std::get<1>(attributes.back()).c_str());
        }
        g_hash_table_unref(attr);
 
@@ -200,7 +697,7 @@ std::vector<std::pair<std::string, std::string>> Atspi::getAttributes(const std:
 #endif
 }
 
-std::shared_ptr<AtspiAction> Atspi::getActionInterface(const std::shared_ptr<AtspiAccessible> &accessibleObj) const
+AtspiActionPtr Atspi::getActionInterface(const AtspiAccessiblePtr &accessibleObj) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        auto action = atspi_accessible_get_action_iface(accessibleObj.get());
@@ -209,12 +706,12 @@ std::shared_ptr<AtspiAction> Atspi::getActionInterface(const std::shared_ptr<Ats
        return {action, g_object_unref};
 }
 
-bool Atspi::doAction(const std::shared_ptr<AtspiAction> &accessibleObj, int action) const
+bool Atspi::doAction(const AtspiActionPtr &accessibleObj, int action) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        GError *error = nullptr;
        auto status = atspi_action_do_action(accessibleObj.get(), action, &error);
-       GERROR_CHECK(error);
+       PRINT_ERROR_AND_FREE(error);
        if (!status) {
                DEBUG("Action (%d) execution failure", action);
                return false;
@@ -222,12 +719,12 @@ bool Atspi::doAction(const std::shared_ptr<AtspiAction> &accessibleObj, int acti
        return true;
 }
 
-bool Atspi::doActionName(const std::shared_ptr<AtspiAction> &accessibleObj, const std::string &action) const
+bool Atspi::doActionName(const AtspiActionPtr &accessibleObj, const std::string &action) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        GError *error = nullptr;
        auto status = atspi_action_do_action_name(accessibleObj.get(), action.c_str(), &error);
-       GERROR_CHECK(error);
+       PRINT_ERROR_AND_FREE(error);
        if (!status) {
                DEBUG("Action (%s) execution failure", action.c_str());
                return false;
@@ -235,7 +732,16 @@ bool Atspi::doActionName(const std::shared_ptr<AtspiAction> &accessibleObj, cons
        return true;
 }
 
-std::shared_ptr<AtspiValue> Atspi::getValueInterface(const std::shared_ptr<AtspiAccessible> &accessibleObj) const
+AtspiCollectionPtr Atspi::getCollectionInterface(const AtspiAccessiblePtr &accessibleObj) const
+{
+       EXIT_IF_NULLPTR(accessibleObj);
+       auto collectionInterface = atspi_accessible_get_collection_iface(accessibleObj.get());
+       if (!collectionInterface)
+               DEBUG("Object %p do not has collection interface", accessibleObj.get());
+       return {collectionInterface, g_object_unref};
+}
+
+AtspiValuePtr Atspi::getValueInterface(const AtspiAccessiblePtr &accessibleObj) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        auto valueInterface = atspi_accessible_get_value_iface(accessibleObj.get());
@@ -244,44 +750,43 @@ std::shared_ptr<AtspiValue> Atspi::getValueInterface(const std::shared_ptr<Atspi
        return {valueInterface, g_object_unref};
 }
 
-Optional<double> Atspi::getCurrentValue(const std::shared_ptr<AtspiValue> &valueInterface) const
+Optional<double> Atspi::getCurrentValue(const AtspiValuePtr &valueInterface) const
 {
        return getValueTemplateFunction<double>(atspi_value_get_current_value, valueInterface.get());
 }
 
-bool Atspi::setCurrentValue(const std::shared_ptr<AtspiValue> &valueInterface, double newValue) const
+bool Atspi::setCurrentValue(const AtspiValuePtr &valueInterface, double newValue) const
 {
        EXIT_IF_NULLPTR(valueInterface);
        GError *error = nullptr;
        auto isSuccessful = atspi_value_set_current_value(valueInterface.get(), newValue, &error);
-       GERROR_CHECK(error);
+       PRINT_ERROR_AND_FREE(error);
        return isSuccessful;
 }
 
-Optional<double> Atspi::getMinimumIncrement(const std::shared_ptr<AtspiValue> &valueInterface) const
+Optional<double> Atspi::getMinimumIncrement(const AtspiValuePtr &valueInterface) const
 {
        return getValueTemplateFunction<double>(atspi_value_get_minimum_increment, valueInterface.get());
 }
 
-Optional<double> Atspi::getMaximumValue(const std::shared_ptr<AtspiValue> &valueInterface) const
+Optional<double> Atspi::getMaximumValue(const AtspiValuePtr &valueInterface) const
 {
        return getValueTemplateFunction<double>(atspi_value_get_maximum_value, valueInterface.get());
 }
 
-Optional<double> Atspi::getMinimumValue(const std::shared_ptr<AtspiValue> &valueInterface) const
+Optional<double> Atspi::getMinimumValue(const AtspiValuePtr &valueInterface) const
 {
        return getValueTemplateFunction<double>(atspi_value_get_minimum_value, valueInterface.get());
 }
 
-std::shared_ptr<AtspiAccessible> Atspi::getDesktop() const
+AtspiAccessiblePtr Atspi::getDesktop() const
 {
        auto res = atspi_get_desktop(0);
        ASSERT(res);
-       DEBUG("root is %s", atspi_accessible_get_unique_id(res, NULL));
        return {res, g_object_unref};
 }
 
-std::shared_ptr<AtspiAccessible> Atspi::getAtPoint(int x, int y, CoordType type, std::shared_ptr<AtspiAccessible> root) const
+AtspiAccessiblePtr Atspi::getAtPoint(Point pt, CoordType type, AtspiAccessiblePtr root) const
 {
        AtspiCoordType ctype = ATSPI_COORD_TYPE_SCREEN;
        switch (type) {
@@ -294,13 +799,13 @@ std::shared_ptr<AtspiAccessible> Atspi::getAtPoint(int x, int y, CoordType type,
        }
 
        if (!root) {
-               auto is_visible = [](const std::shared_ptr<AtspiAccessible> &ptr) -> bool {
+               auto is_visible = [](const AtspiAccessiblePtr & ptr) -> bool {
                        AtspiStateSet *state_set = atspi_accessible_get_state_set(ptr.get());
                        auto visible = bool(atspi_state_set_contains(state_set, ATSPI_STATE_SHOWING));
                        g_object_unref(state_set);
                        return visible;
                };
-               auto get_at_point = [ = ](const std::shared_ptr<AtspiAccessible> &ptr) -> std::shared_ptr<AtspiAccessible> {
+               auto get_at_point = [ = ](const AtspiAccessiblePtr & ptr) -> AtspiAccessiblePtr {
                        if (!ATSPI_IS_COMPONENT(ptr.get()))
                        {
                                DEBUG("%s is not a component", atspi_accessible_get_unique_id(ptr.get(), NULL));
@@ -308,24 +813,24 @@ std::shared_ptr<AtspiAccessible> Atspi::getAtPoint(int x, int y, CoordType type,
                        }
                        auto compo = ATSPI_COMPONENT(ptr.get());
                        GError *error = nullptr;
-                       auto res = atspi_component_get_accessible_at_point(compo, x, y, ctype, &error);
-                       GERROR_CHECK(error);
-                       if (res)
-                               DEBUG("result is %s", atspi_accessible_get_unique_id(res, NULL));
-                       return std::shared_ptr<AtspiAccessible>{res, g_object_unref};
+                       auto res = atspi_component_get_accessible_at_point(compo, pt.x, pt.y, ctype, &error);
+                       PRINT_ERROR_AND_FREE(error);
+                       return AtspiAccessiblePtr{res, g_object_unref};
                };
 
                auto desktop = getDesktop();
                auto chs = getChildren(desktop);
-               for (auto c : chs) {
-                       while (true) {
-                               auto n = get_at_point(c);
-                               if (!n) break;
-                               c = std::move(n);
-                       }
-                       if (is_visible(c)) {
-                               root = c;
-                               break;
+               if (chs) {
+                       for (auto c : *chs) {
+                               while (true) {
+                                       auto n = get_at_point(c);
+                                       if (!n) break;
+                                       c = std::move(n);
+                               }
+                               if (is_visible(c)) {
+                                       root = c;
+                                       break;
+                               }
                        }
                }
                if (!root) {
@@ -333,17 +838,14 @@ std::shared_ptr<AtspiAccessible> Atspi::getAtPoint(int x, int y, CoordType type,
                        return {};
                }
        }
-       DEBUG("root is %s", atspi_accessible_get_unique_id(root.get(), NULL));
 
        GError *error = nullptr;
-       auto res = atspi_accessible_get_navigable_at_point(root.get(), x, y, ctype, &error);
-       GERROR_CHECK(error);
-       if (res)
-               DEBUG("result is %s", atspi_accessible_get_unique_id(res, NULL));
+       auto res = atspi_accessible_get_navigable_at_point(root.get(), pt.x, pt.y, ctype, &error);
+       PRINT_ERROR_AND_FREE(error);
        return {res, g_object_unref};
 }
 
-Optional<size_t> Atspi::getChildrenCount(const std::shared_ptr<AtspiAccessible> &accessibleObj) const
+Optional<size_t> Atspi::getChildrenCount(const AtspiAccessiblePtr &accessibleObj) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        GError *error = nullptr;
@@ -353,76 +855,76 @@ Optional<size_t> Atspi::getChildrenCount(const std::shared_ptr<AtspiAccessible>
        return (size_t)count;
 }
 
-std::shared_ptr<AtspiAccessible> Atspi::getChildAtIndex(const std::shared_ptr<AtspiAccessible> &accessibleObj, size_t index) const
+AtspiAccessiblePtr Atspi::getChildAtIndex(const AtspiAccessiblePtr &accessibleObj, size_t index) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        GError *error = nullptr;
        auto res = atspi_accessible_get_child_at_index(accessibleObj.get(), (gint)index, &error);
-       GERROR_CHECK(error);
+       PRINT_ERROR_AND_FREE(error);
        return {res, g_object_unref};
 }
 
-std::vector<std::shared_ptr<AtspiAccessible>> Atspi::getChildren(const std::shared_ptr<AtspiAccessible> &accessibleObj) const
+Optional<std::vector<AtspiAccessiblePtr>> Atspi::getChildren(const AtspiAccessiblePtr &accessibleObj) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        auto count = getChildrenCount(accessibleObj);
        if (!count) return {};
 
-       std::vector<std::shared_ptr<AtspiAccessible>> tmp(*count);
+       std::vector<AtspiAccessiblePtr> tmp(*count);
        for (size_t i = 0; i < *count; ++i) {
                auto c = getChildAtIndex(accessibleObj, i);
                if (!c) return {};
                tmp.push_back(std::move(c));
        }
-       return tmp;
+       return std::move(tmp);
 }
 
-std::string Atspi::getUniqueId(const std::shared_ptr<AtspiAccessible> &accessibleObj) const
+AtspiAccessiblePtr Atspi::make(AtspiAccessible *obj, bool increase_reference) const
 {
-       EXIT_IF_NULLPTR(accessibleObj);
-       GError *error = nullptr;
-       auto v = atspi_accessible_get_unique_id(accessibleObj.get(), &error);
-       GERROR_CHECK(error);
-       if (!v) return {};
+       if (!obj) return {};
+       if (increase_reference)
+               g_object_ref(obj);
+       return { obj, g_object_unref };
+}
 
-       std::string z = v;
-       g_free(v);
-       return std::move(z);
+AtspiAccessiblePtr Atspi::make(const std::string &bus, const std::string &path) const
+{
+       return { ref_accessible(bus.c_str(), path.c_str()), g_object_unref };
 }
 
-std::shared_ptr<AtspiAccessible> Atspi::getParent(const std::shared_ptr<AtspiAccessible> &accessibleObj) const
+AtspiAccessiblePtr Atspi::getParent(const AtspiAccessiblePtr &accessibleObj) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        GError *error = nullptr;
        auto v = atspi_accessible_get_parent(accessibleObj.get(), &error);
-       GERROR_CHECK(error);
+       PRINT_ERROR_AND_FREE(error);
        if (!v) return {};
        return {v, g_object_unref};
 }
 
-Optional<size_t> Atspi::getIndexInParent(const std::shared_ptr<AtspiAccessible> &accessibleObj) const
+Optional<size_t> Atspi::getIndexInParent(const AtspiAccessiblePtr &accessibleObj) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        GError *error = nullptr;
        auto v = atspi_accessible_get_index_in_parent(accessibleObj.get(), &error);
-       GERROR_CHECK(error);
+       PRINT_ERROR_AND_FREE(error);
        if (v < 0) return {};
        return (size_t)v;
 }
 
-std::shared_ptr<AtspiSelection> Atspi::getSelectionInterface(const std::shared_ptr<AtspiAccessible> &accessibleObj) const
+AtspiSelectionPtr Atspi::getSelectionInterface(const AtspiAccessiblePtr &accessibleObj) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        auto res = atspi_accessible_get_selection_iface(accessibleObj.get());
        return {res, g_object_unref};
 }
 
-bool Atspi::selectChild(const std::shared_ptr<AtspiSelection> &accessibleObj, size_t index) const
+bool Atspi::selectChild(const AtspiSelectionPtr &accessibleObj, size_t index) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        GError *error = nullptr;
        atspi_selection_select_child(accessibleObj.get(), (int)index, &error);
        bool isSuccessful = (error == nullptr);
-       GERROR_CHECK(error);
+       PRINT_ERROR_AND_FREE(error);
        return isSuccessful;
 }
index 5dac7f4..7928e36 100644 (file)
@@ -1,16 +1,40 @@
-#ifndef ATSPI_ADAPTER_HPP
-#define ATSPI_ADAPTER_HPP
+#ifndef ATSPI_HPP
+#define ATSPI_HPP
 
 #include "Optional.hpp"
 #include "eldbus.hpp"
+#include "DBus.hpp"
+#include "Geometry.hpp"
 
 #include <atspi/atspi.h>
 #include <memory>
 #include <vector>
+#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;
 
 class Atspi
 {
 public:
+       using StateSet = std::bitset<ATSPI_STATE_LAST_DEFINED>;
+
        enum class CoordType {
                Screen,
                Window
@@ -19,31 +43,134 @@ public:
        Atspi();
        ~Atspi();
 
-       std::shared_ptr<AtspiAccessible> getObjectInRelation(const std::shared_ptr<AtspiAccessible> &accessibleObj, AtspiRelationType searchType) const;
-       std::vector<std::pair<std::string, std::string>> getAttributes(const std::shared_ptr<AtspiAccessible> &accessibleObj) const;
+       AtspiAccessiblePtr make(const std::string &bus, const std::string &path) const;
+       AtspiAccessiblePtr make(AtspiAccessible *obj, bool increase_reference) const;
+       AtspiAccessiblePtr getObjectInRelation(const AtspiAccessiblePtr &accessibleObj, AtspiRelationType searchType) const;
+       std::vector<std::pair<std::string, std::string>> getAttributes(const AtspiAccessiblePtr &accessibleObj) const;
 
-       std::shared_ptr<AtspiAction> getActionInterface(const std::shared_ptr<AtspiAccessible> &accessibleObj) const;
-       bool doAction(const std::shared_ptr<AtspiAction> &accessibleObj, int action) const;
-       bool doActionName(const std::shared_ptr<AtspiAction> &accessibleObj, const std::string &action) const;
+       AtspiActionPtr getActionInterface(const AtspiAccessiblePtr &accessibleObj) const;
+       bool doAction(const AtspiActionPtr &accessibleObj, int action) const;
+       bool doActionName(const AtspiActionPtr &accessibleObj, const std::string &action) const;
 
-       std::shared_ptr<AtspiValue> getValueInterface(const std::shared_ptr<AtspiAccessible> &accessibleObj) const;
-       Optional<double> getCurrentValue(const std::shared_ptr<AtspiValue> &valueInterface) const;
-       bool setCurrentValue(const std::shared_ptr<AtspiValue> &valueInterface, double newValue) const;
-       Optional<double> getMinimumIncrement(const std::shared_ptr<AtspiValue> &valueInterface) const;
-       Optional<double> getMaximumValue(const std::shared_ptr<AtspiValue> &valueInterface) const;
-       Optional<double> getMinimumValue(const std::shared_ptr<AtspiValue> &valueInterface) const;
+       AtspiCollectionPtr getCollectionInterface(const AtspiAccessiblePtr &accessibleObj) const;
+       AtspiValuePtr getValueInterface(const AtspiAccessiblePtr &accessibleObj) const;
+       Optional<double> getCurrentValue(const AtspiValuePtr &valueInterface) const;
+       bool setCurrentValue(const AtspiValuePtr &valueInterface, double newValue) const;
+       Optional<double> getMinimumIncrement(const AtspiValuePtr &valueInterface) const;
+       Optional<double> getMaximumValue(const AtspiValuePtr &valueInterface) const;
+       Optional<double> getMinimumValue(const AtspiValuePtr &valueInterface) const;
+
+       AtspiAccessiblePtr getDesktop() const;
+       AtspiAccessiblePtr getAtPoint(Point pt, CoordType type, AtspiAccessiblePtr root) const;
+       Optional<size_t> getChildrenCount(const AtspiAccessiblePtr &accessibleObj) const;
+       Optional<unsigned int> getProcessId(const AtspiAccessiblePtr &accessibleObj) const;
+       AtspiAccessiblePtr getChildAtIndex(const AtspiAccessiblePtr &accessibleObj, size_t index) const;
+       Optional<std::vector<AtspiAccessiblePtr>> getChildren(const AtspiAccessiblePtr &accessibleObj) const;
+       Optional<AtspiRole> getRole(const AtspiAccessiblePtr &accessibleObj) const;
+       Optional<std::string> getName(const AtspiAccessiblePtr &accessibleObj) const;
+       StateSet getStateSet(const AtspiAccessiblePtr &accessibleObj) const;
+
+       AtspiAccessiblePtr getParent(const AtspiAccessiblePtr &accessibleObj) const;
+       Optional<size_t> getIndexInParent(const AtspiAccessiblePtr &accessibleObj) const;
+       AtspiSelectionPtr getSelectionInterface(const AtspiAccessiblePtr &accessibleObj) const;
+       bool selectChild(const AtspiSelectionPtr &accessibleObj, size_t index) const;
+
+       struct Matcher {
+               std::tuple <
+               std::array<int32_t, 2>, int32_t,// states & match type
+                       std::unordered_map<std::string, std::string>, int32_t, // attributes & match type
+                       std::array<int32_t, 4>, int32_t, // roles & match type
+                       std::vector<std::string>, int32_t, // interfaces & match type
+                       bool // invert
+               > value = {
+                       { 0, 0 }, ATSPI_Collection_MATCH_INVALID,
+                       {}, ATSPI_Collection_MATCH_INVALID,
+                       { 0, 0, 0, 0}, ATSPI_Collection_MATCH_INVALID,
+                       {}, ATSPI_Collection_MATCH_INVALID,
+                       false
+               };
+               struct Index {
+                       enum {
+                               States, StatesMatchType,
+                               Attributes, AttributesMatchType,
+                               Roles, RolesMatchType,
+                               Interfaces, InterfacesMatchType,
+                       };
+               };
+               Matcher &states(std::initializer_list<AtspiStateType> st, AtspiCollectionMatchType type)
+               {
+                       for (auto a : st)
+                               std::get<Index::States>(value)[a >> 5] |= 1 << (a & 31);
+                       std::get<Index::StatesMatchType>(value) = (int32_t)type;
+                       return *this;
+               }
+               Matcher &attributes(std::unordered_map<std::string, std::string> st, AtspiCollectionMatchType type)
+               {
+                       std::get<Index::Attributes>(value) = std::move(st);
+                       std::get<Index::AttributesMatchType>(value) = (int32_t)type;
+                       return *this;
+               }
+               template <typename A> Matcher &attributes(A begin, A end, AtspiCollectionMatchType type)
+               {
+                       while (begin != end)
+                               std::get<Index::Attributes>(value).insert(*begin++);
+                       std::get<Index::AttributesMatchType>(value) = (int32_t)type;
+                       return *this;
+               }
+               Matcher &roles(std::initializer_list<AtspiRole> st, AtspiCollectionMatchType type)
+               {
+                       for (auto a : st)
+                               std::get<Index::Roles>(value)[a >> 5] |= 1 << (a & 31);
+                       std::get<Index::RolesMatchType>(value) = (int32_t)type;
+                       return *this;
+               }
+               Matcher &interfaces(std::vector<std::string> st, AtspiCollectionMatchType type)
+               {
+                       std::get<Index::Interfaces>(value) = std::move(st);
+                       std::get<Index::InterfacesMatchType>(value) = (int32_t)type;
+                       return *this;
+               }
+               template <typename A> Matcher &interfaces(A begin, A end, AtspiCollectionMatchType type)
+               {
+                       while (begin != end)
+                               std::get<Index::Interfaces>(value).push_back(*begin++);
+                       std::get<Index::InterfacesMatchType>(value) = (int32_t)type;
+                       return *this;
+               }
+       };
+       void getMatchedElements(const AtspiCollectionPtr &root, AtspiCollectionSortOrder sortOrder,
+                                                       size_t maximumFoundElements, Matcher m, bool reverse, AsyncCallback<std::vector<AtspiAccessiblePtr>> callback);
+       void getChildrenCount(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<size_t> callback) const;
+       void getChildAtIndex(const AtspiAccessiblePtr &accessibleObj, size_t index, AsyncCallback<AtspiAccessiblePtr> callback) const;
+       void getChildren(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::vector<AtspiAccessiblePtr>> callback) const;
+       void getName(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::string> callback) const;
+       void getRole(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<AtspiRole> callback) const;
+       void getAttributes(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::unordered_map<std::string, std::string>> callback) const;
+       void doActionName(const AtspiActionPtr &accessibleObj, const std::string &action, AsyncCallback<bool> callback) const;
+       void getParent(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<AtspiAccessiblePtr> callback) const;
+       void getIndexInParent(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<int> callback) const;
+       void selectChild(const AtspiSelectionPtr &accessibleObj, int index, AsyncCallback<void> callback) const;
+       void getStateSet(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<StateSet> callback) const;
+       struct AcceptedObjectInfo {
+               AtspiAccessiblePtr object;
+               Rectangle pos;
+       };
+       void getAllAcceptedObjects(const AtspiAccessiblePtr &root, AsyncCallback<std::tuple<std::vector<AcceptedObjectInfo>, Rectangle>> callback) const;
+       void getAtPoint(Point pt, CoordType type, const AtspiAccessiblePtr &root, AsyncCallback<AtspiAccessiblePtr> callback) const;
 
-       std::shared_ptr<AtspiAccessible> getDesktop() const;
-       std::shared_ptr<AtspiAccessible> getAtPoint(int x, int y, CoordType type, std::shared_ptr<AtspiAccessible> root = {}) const;
-       Optional<size_t> getChildrenCount(const std::shared_ptr<AtspiAccessible> &accessibleObj) const;
-       std::shared_ptr<AtspiAccessible> getChildAtIndex(const std::shared_ptr<AtspiAccessible> &accessibleObj, size_t index) const;
-       std::vector<std::shared_ptr<AtspiAccessible>> getChildren(const std::shared_ptr<AtspiAccessible> &accessibleObj) const;
-       std::string getUniqueId(const std::shared_ptr<AtspiAccessible> &accessibleObj) 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;
 
-       std::shared_ptr<AtspiAccessible> getParent(const std::shared_ptr<AtspiAccessible> &accessibleObj) const;
-       Optional<size_t> getIndexInParent(const std::shared_ptr<AtspiAccessible> &accessibleObj) const;
-       std::shared_ptr<AtspiSelection> getSelectionInterface(const std::shared_ptr<AtspiAccessible> &accessibleObj) const;
-       bool selectChild(const std::shared_ptr<AtspiSelection> &accessibleObj, size_t index) const;
+       ADD_INTROSPECTION_FUNCTIONS(Accessible, ACCESSIBLE);
+       ADD_INTROSPECTION_FUNCTIONS(Action, ACTION);
+       ADD_INTROSPECTION_FUNCTIONS(Selection, SELECTION);
+       ADD_INTROSPECTION_FUNCTIONS(Collection, COLLECTION);
+       ADD_INTROSPECTION_FUNCTIONS(Value, VALUE);
+       ADD_INTROSPECTION_FUNCTIONS(EditableText, EDITABLE_TEXT);
+#undef ADD_INTROSPECTION_FUNCTIONS
 
 private:
        bool ConnectAtClient();
@@ -53,6 +180,13 @@ 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;
+       template <typename T> void getInterface(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::shared_ptr<T>> callback) const;
+       void getInterface(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<AtspiAccessiblePtr> callback) const
+       {
+               callback(accessibleObj);
+       }
 };
 
 #endif
diff --git a/src/DBus.cpp b/src/DBus.cpp
new file mode 100644 (file)
index 0000000..f3d106d
--- /dev/null
@@ -0,0 +1,61 @@
+#include "DBus.hpp"
+#include <sstream>
+#include "Atspi.hpp"
+#include "Singleton.hpp"
+
+unsigned int detail::CallId::LastId = 0;
+
+DBus::DBus()
+{
+}
+
+static EldbusConnectionCallbackHandle getConnectionByType(DBus::ConnectionType connectionType)
+{
+       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 };
+       default:
+               ASSERT(0);
+       }
+       return {};
+}
+
+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::DBus(std::string bus_name, std::string path_name, std::string interface_name, const EldbusConnectionCallbackHandle &conn)
+{
+       if (!conn)
+               connectionState.connection = getConnectionByType(DBus::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 };
+       if (connectionState.object) {
+               connectionState.proxy = { eldbus_proxy_get(connectionState.object.get(), interface_name.c_str()), eldbus_proxy_unref };
+       }
+}
+
+DBus::~DBus()
+{
+       for (auto &p : callbacks)
+               delete p;
+}
+
+bool 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;
+}
diff --git a/src/DBus.hpp b/src/DBus.hpp
new file mode 100644 (file)
index 0000000..8a2d6f0
--- /dev/null
@@ -0,0 +1,860 @@
+#ifndef DBUS_HPP
+#define DBUS_HPP
+
+#include <Eldbus.h>
+#include <memory>
+
+#include "Optional.hpp"
+#include <atspi/atspi.h>
+#include <cstdint>
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <map>
+#include <array>
+
+namespace detail
+{
+       struct caller_eldbus_connection_unref {
+               void operator()(Eldbus_Connection *p) const
+               {
+                       eldbus_connection_unref(p);
+               }
+       };
+
+       struct caller_eldbus_message_unref {
+               void operator()(Eldbus_Message *p) const
+               {
+                       eldbus_message_unref(p);
+               }
+       };
+
+       struct caller_eldbus_object_unref {
+               void operator()(Eldbus_Object *p) const
+               {
+                       eldbus_object_unref(p);
+               }
+       };
+
+       struct caller_eldbus_proxy_unref {
+               void operator()(Eldbus_Proxy *p) const
+               {
+                       eldbus_proxy_unref(p);
+               }
+       };
+}
+
+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>>;
+
+       template <typename T> struct signature<T, typename std::enable_if<std::is_enum<T>::value, void>::type> {
+               static std::string sig()
+               {
+                       return "i";
+               }
+               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)
+               {
+                       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 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()
+               {
+                       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 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)
+               {
+                       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 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 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);
+                       eldbus_message_iter_container_close(iter, entry);
+                       return z;
+               }
+       };
+       template <typename A, typename B> struct signature<std::pair<A, B>> {
+               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, char sg = 'r')
+               {
+                       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);
+               }
+               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));
+                       }
+                       eldbus_message_iter_container_close(iter, entry);
+                       return z;
+               }
+       };
+       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);
+                       }
+                       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 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, typename B, size_t N> struct signature<std::array<std::pair<A, B>, N>> {
+               static std::string sig()
+               {
+                       return "a" + signature<std::pair<A, B>>::sig();
+               }
+               static void set(Eldbus_Message_Iter *iter, const std::array<std::pair<A, B>, N> &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);
+                       }
+                       eldbus_message_iter_container_close(iter, lst);
+               }
+               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;
+                       }
+                       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);
+                       }
+                       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);
+
+                       // 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))
+                               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.value);
+                       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');
+                       }
+                       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');
+                       }
+                       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<A>::get(s, a))
+                               v.insert(std::move(a));
+                       return true;
+               }
+       };
+       template <typename A> struct signature<const A &> {
+               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 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 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 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 {
+               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);
+
+                       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;
+               }
+       };
+       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)
+       {
+               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));
+       }
+
+       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 C, typename R, typename ... ARGS> struct apply_helper {
+               const C &c;
+               const std::tuple<ARGS...> &args;
+
+               template<int ... S> R apply_2(seq<S...>)
+               {
+                       return c(std::get<S>(args)...);
+               }
+               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()
+               {
+                       eldbus_init();
+               }
+               ~EldbusProxyBase()
+               {
+                       eldbus_shutdown();
+               }
+       };
+}
+
+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;
+
+       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 {};
+               }
+
+               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 {};
+               }
+
+               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 {};
+               }
+               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)
+       {
+               if (!connectionState.proxy) {
+                       DEBUG("call %d: not initialized", callId.id);
+                       callback({});
+                       return;
+               }
+
+               EldbusMessageCallbackHandle msg{eldbus_proxy_method_call_new(connectionState.proxy.get(), func_name.c_str())};
+               if (!msg) {
+                       DEBUG("call %d: failed", callId.id);
+                       callback({});
+                       return;
+               }
+
+               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);
+                       }
+               };
+
+               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))
+                               {
+                                       ERROR("call %d: Eldbus error: %s %s", callId.id, errname, aux);
+                                       return;
+                               }
+
+                               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 &...)
+       {
+               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();
+
+       DBus(const DBus &) = default;
+       DBus(DBus &&) = default;
+
+       DBus &operator = (DBus &&) = default;
+       DBus &operator = (const DBus &) = default;
+
+       explicit operator bool () const
+       {
+               return bool(connectionState.proxy);
+       }
+
+       template <typename Q> using Method = detail::Method<Q>;
+       template <typename Q> using Listen = detail::Listen<Q>;
+
+       template <typename R>
+       Method<R> method(std::string func_name)
+       {
+               return Method<R> { connectionState, std::move(func_name), info };
+       }
+
+       template <typename R>
+       Listen<R> listen(std::string func_name)
+       {
+               return Listen<R> { connectionState, callbacks, std::move(func_name), info };
+       }
+
+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;
+
+       Optional<RetType> call(const ARGS &... args)
+       {
+               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 {};
+       }
+       void asyncCall(const ARGS &... args, std::function<void(Optional<RetType>)> 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 (%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...);
+       }
+};
+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));
+       }
+};
+
+#endif
index 88d30d8..b882f13 100644 (file)
@@ -2,6 +2,7 @@
 #include "UniversalSwitchLog.hpp"
 #include "Atspi.hpp"
 #include "UIElement.hpp"
+#include "DBus.hpp"
 
 #include <glib.h>
 #include <glib-object.h>
@@ -10,7 +11,7 @@
 #include <memory>
 #include <functional>
 
-constexpr static int ELDBUS_CALL_TIMEOUT = 1000;
+using AtspiAccessiblePtr = std::shared_ptr<AtspiAccessible>;
 
 NavigationInterface::~NavigationInterface()
 {
@@ -34,329 +35,6 @@ void NavigationInterface::unregisterCb(CallbackHandleBase *cb)
        }
 }
 
-struct caller_eldbus_connection_unref {
-       void operator()(Eldbus_Connection *p) const
-       {
-               eldbus_connection_unref(p);
-       }
-};
-
-struct caller_eldbus_message_unref {
-       void operator()(Eldbus_Message *p) const
-       {
-               eldbus_message_unref(p);
-       }
-};
-
-struct caller_eldbus_object_unref {
-       void operator()(Eldbus_Object *p) const
-       {
-               eldbus_object_unref(p);
-       }
-};
-
-struct caller_eldbus_proxy_unref {
-       void operator()(Eldbus_Proxy *p) const
-       {
-               eldbus_proxy_unref(p);
-       }
-};
-
-using EldbusConnectionCallbackHandle = std::unique_ptr<Eldbus_Connection, caller_eldbus_connection_unref>;
-using EldbusMessageCallbackHandle = std::unique_ptr<Eldbus_Message, caller_eldbus_message_unref>;
-using EldbusObjectCallbackHandle = std::unique_ptr<Eldbus_Object, caller_eldbus_object_unref>;
-using EldbusProxyCallbackHandle = std::unique_ptr<Eldbus_Proxy, caller_eldbus_proxy_unref>;
-using AtspiAccessiblePtr = std::shared_ptr<AtspiAccessible>;
-
-namespace
-{
-       void eldbusParamSet(const EldbusMessageCallbackHandle &msg)
-       {}
-
-       void eldbusParamSet(const EldbusMessageCallbackHandle &msg, int arg)
-       {
-               eldbus_message_arguments_append(msg.get(), "i", arg);
-       }
-
-       template <typename A, typename ... ARGS>
-       void eldbusParamSet(const EldbusMessageCallbackHandle &msg, A &&arg, ARGS &&... args)
-       {
-               eldbusParamSet(msg, std::forward<A>(arg));
-               eldbusParamSet(msg, std::forward<ARGS>(args)...);
-       }
-
-       bool eldbusParamGet(Eldbus_Message_Iter *iter, int &v)
-       {
-               return eldbus_message_iter_get_and_next(iter, 'i', &v);
-       }
-
-       bool eldbusParamGet(Eldbus_Message_Iter *iter, AtspiAccessiblePtr &v)
-       {
-               char *busname, *path;
-               if (!eldbus_message_iter_get_and_next(iter, 's', &path)) return false;
-               if (!eldbus_message_iter_get_and_next(iter, 's', &busname)) return false;
-               if (!path || !busname) return false;
-               if (!*path || !*busname) {
-                       v = {};
-                       return true;
-               }
-               auto ptr = ref_accessible(busname, (std::string("/org/a11y/atspi/accessible/") + path).c_str());
-               v = AtspiAccessiblePtr{ ptr, g_object_unref };
-               return true;
-       }
-
-       bool eldbusParamGet(Eldbus_Message_Iter *iter, std::string &v)
-       {
-               char *t;
-               if (!eldbus_message_iter_get_and_next(iter, 's', &t)) return false;
-               v = t;
-               return true;
-       }
-
-       bool eldbusParamGet(Eldbus_Message_Iter *iter, std::tuple<> &)
-       {
-               Eldbus_Message_Iter *s;
-               if (!eldbus_message_iter_get_and_next(iter, 'r', &s)) return false;
-               return true;
-       }
-
-       template <typename A, typename B> bool eldbusParamGet(Eldbus_Message_Iter *iter, std::pair<A, B> &v);
-       template <typename ... ARGS> bool eldbusParamGet(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &v);
-       template <typename A> bool eldbusParamGet(Eldbus_Message_Iter *iter, std::vector<A> &v);
-
-       template <typename T>
-       typename std::enable_if<std::is_enum<T>::value, bool>::type eldbusParamGet(Eldbus_Message_Iter *iter, T &v)
-       {
-               int i;
-               if (!eldbus_message_iter_get_and_next(iter, 'i', &i))
-                       return false;
-               v = (T)i;
-               return true;
-       }
-
-       template <int I, typename ... ARGS>
-       struct EldbusParamGetTuple {
-               static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
-               {
-                       if (!EldbusParamGetTuple < I - 1, ARGS... >::get(iter, args))
-                               return false;
-                       return eldbusParamGet(iter, std::get<I>(args));
-               }
-       };
-
-       template <typename ... ARGS>
-       struct EldbusParamGetTuple<0, ARGS...> {
-               static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
-               {
-                       return eldbusParamGet(iter, std::get<0>(args));
-               }
-       };
-
-       template <typename A, typename B> bool eldbusParamGet(Eldbus_Message_Iter *iter, std::pair<A, B> &v)
-       {
-               Eldbus_Message_Iter *s;
-               if (!eldbus_message_iter_get_and_next(iter, 'r', &s)) return false;
-               if (!eldbusParamGet(s, v.first)) return false;
-               if (!eldbusParamGet(s, v.second)) return false;
-               return true;
-       }
-
-       template <typename ... ARGS> bool eldbusParamGet(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &v)
-       {
-               Eldbus_Message_Iter *s;
-               if (!eldbus_message_iter_get_and_next(iter, 'r', &s)) return false;
-               return EldbusParamGetTuple < sizeof...(ARGS) - 1, ARGS... >::get(s, v);
-       }
-
-       template <typename A> bool eldbusParamGet(Eldbus_Message_Iter *iter, std::vector<A> &v)
-       {
-               Eldbus_Message_Iter *s;
-               if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
-               v.resize(0);
-               while (true) {
-                       v.push_back(A{});
-                       if (!eldbusParamGet(s, v.back())) {
-                               v.pop_back();
-                               break;
-                       }
-               }
-               return true;
-       }
-
-       template <class A>
-       bool eldbusParamGetFromMsg(const Eldbus_Message *msg, A &v)
-       {
-               auto iter = eldbus_message_iter_get(msg);
-               return eldbusParamGet(iter, v);
-       }
-
-       template <typename ... ARGS>
-       bool eldbusParamGetFromMsg(const Eldbus_Message *msg, std::tuple<ARGS...> &v)
-       {
-               auto iter = eldbus_message_iter_get(msg);
-               return EldbusParamGetTuple < sizeof...(ARGS) - 1, ARGS... >::get(iter, v);
-       }
-
-       bool eldbusParamGetFromMsg(const Eldbus_Message *msg, std::tuple<> &v)
-       {
-               return true;
-       }
-
-       class EldbusInitializer
-       {
-       protected:
-               EldbusInitializer()
-               {
-                       if (eldbus_init() == 0) {
-                               ERROR("Unable to initialize eldbus module");
-                       }
-               }
-
-               ~EldbusInitializer()
-               {
-                       eldbus_shutdown();
-               }
-       };
-
-       template <typename A>
-       Optional<A> eldbusParamsGet(const Eldbus_Message *msg)
-       {
-               A a;
-               if (!eldbusParamGetFromMsg(msg, a)) return {};
-               return std::move(a);
-       }
-
-       class EldbusProxy : public EldbusInitializer
-       {
-       public:
-               EldbusProxy() = default;
-
-               EldbusProxy(EldbusProxy &&) = default;
-
-               EldbusProxy(std::string bus_name, std::string path_name, std::string interface_name, EldbusConnectionCallbackHandle conn = {})
-               {
-                       if (!conn)
-                               conn.reset(eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION));
-                       connection = std::move(conn);
-                       if (connection) {
-                               object.reset(eldbus_object_get(connection.get(), bus_name.c_str(), path_name.c_str()));
-                               if (object) {
-                                       proxy.reset(eldbus_proxy_get(object.get(), interface_name.c_str()));
-                               }
-                       }
-               }
-
-               EldbusProxy &operator = (EldbusProxy &&) = default;
-
-               explicit operator bool () const
-               {
-                       return bool(proxy);
-               }
-
-               template <typename R, typename ... ARGS>
-               typename std::enable_if<std::is_same<void, R>::value, bool>::type call(const std::string &func_name, ARGS &&... args)
-               {
-                       auto reply = _call(func_name, std::forward<ARGS>(args)...);
-                       return bool(reply);
-               }
-
-               template <typename R, typename ... ARGS>
-               typename std::enable_if < !std::is_same<void, R>::value, Optional<R >>::type call(const std::string &func_name, ARGS &&... args)
-               {
-                       auto reply = _call(func_name, std::forward<ARGS>(args)...);
-                       return eldbusParamsGet<R>(reply.get());
-               }
-
-               template <typename R, typename ... ARGS>
-               void listen(const std::string &func_name, std::function<R(ARGS...)> fnc)
-               {
-                       auto tmp = [ = ](const Eldbus_Message * msg) -> void {
-                               DEBUG("received signal '%s' '%s'", func_name.c_str(), eldbus_message_signature_get(msg));
-                               auto params = eldbusParamsGet<std::tuple<ARGS...>>(msg);
-                               if (!params)
-                               {
-                                       ERROR("failed to parse arguments from msg with signature '%s'", eldbus_message_signature_get(msg));
-                                       return;
-                               }
-                               _invoke(fnc, std::move(*params));
-                       };
-                       auto callback = new std::function<void(const Eldbus_Message *msg)>(std::move(tmp));
-                       eldbus_proxy_signal_handler_add(proxy.get(), func_name.c_str(), listener_callback, callback);
-               }
-
-       private:
-               EldbusConnectionCallbackHandle connection;
-               EldbusObjectCallbackHandle object;
-               EldbusProxyCallbackHandle proxy;
-
-               template <typename R>
-               static R _invoke(std::function<R()> fnc, const std::tuple<> &t)
-               {
-                       return fnc();
-               }
-
-               template <typename R, typename A1>
-               static R _invoke(std::function<R(A1)> fnc, const std::tuple<A1> &t)
-               {
-                       return fnc(std::get<0>(t));
-               }
-
-               template <typename R, typename A1, typename A2>
-               static R _invoke(std::function<R(A1, A2)> fnc, const std::tuple<A1, A2> &t)
-               {
-                       return fnc(std::get<0>(t), std::get<1>(t));
-               }
-
-               template <typename R, typename A1, typename A2, typename A3>
-               static R _invoke(std::function<R(A1, A2, A3)> fnc, const std::tuple<A1, A2, A3> &t)
-               {
-                       return fnc(std::get<0>(t), std::get<1>(t), std::get<2>(t));
-               }
-
-               template <typename R, typename A1, typename A2, typename A3, typename A4>
-               static R _invoke(std::function<R(A1, A2, A3, A4)> fnc, const std::tuple<A1, A2, A3, A4> &t)
-               {
-                       return fnc(std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t));
-               }
-
-               template <typename R, typename A1, typename A2, typename A3, typename A4, typename A5>
-               static R _invoke(std::function<R(A1, A2, A3, A4, A5)> fnc, const std::tuple<A1, A2, A3, A4, A5> &t)
-               {
-                       return fnc(std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t), std::get<4>(t));
-               }
-
-               static void listener_callback(void *data, const Eldbus_Message *msg)
-               {
-                       (*((std::function<void(const Eldbus_Message *msg)> *)data))(msg);
-               }
-
-               template <typename ... ARGS>
-               EldbusMessageCallbackHandle _call(const std::string &func_name, ARGS &&... args)
-               {
-                       DEBUG("calling '%s'", func_name.c_str());
-                       EldbusMessageCallbackHandle msg{eldbus_proxy_method_call_new(proxy.get(), func_name.c_str())};
-                       if (!msg) {
-                               DEBUG("failed");
-                               return {};
-                       }
-                       eldbusParamSet(msg, std::forward<ARGS>(args)...);
-                       EldbusMessageCallbackHandle reply{eldbus_proxy_send_and_block(proxy.get(), msg.release(), ELDBUS_CALL_TIMEOUT)};
-                       DEBUG("calling '%s' done", func_name.c_str());
-                       if (!reply) {
-                               DEBUG("failed");
-                               return {};
-                       }
-                       const char *errname, *errmsg;
-                       if (eldbus_message_error_get(reply.get(), &errname, &errmsg)) {
-                               DEBUG("%s: %s", errname, errmsg);
-                               return {};
-                       }
-                       DEBUG("got reply with signature '%s'", eldbus_message_signature_get(reply.get()));
-                       return reply;
-               }
-       };
-}
-
 void hack_setRootObjectAttributes(std::shared_ptr<AtspiAccessible> obj, std::vector<std::pair<std::string, std::string>> attrs);
 
 class NavigationImpl : public NavigationInterface
@@ -364,12 +42,11 @@ class NavigationImpl : public NavigationInterface
 public:
        NavigationImpl()
        {
-               EldbusProxy proxy{"org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus"};
-               auto addr = proxy.call<std::string>("GetAddress");
-               if (!addr)
-                       ERROR("failed");
-               else {
-                       EldbusConnectionCallbackHandle connection { eldbus_address_connection_get((*addr).c_str()) };
+               DBus 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 };
                        navProxy = { "org.tizen.ScreenNavigator", "/org/tizen/ScreenNavigator", "org.tizen.ScreenNavigator", std::move(connection) };
                }
 
@@ -377,7 +54,7 @@ public:
                        return;
 
 #define LISTEN(n) \
-       do { navProxy.listen(#n, std::function<void()>([=]() { emitCallback(CallbackType::n); })); } while(0)
+       do { navProxy.listen<void()>(#n).add([=]() { emitCallback(CallbackType::n); }); } while (0)
                LISTEN(FirstRow);
                LISTEN(LastRow);
                LISTEN(FirstElementInRow);
@@ -385,62 +62,62 @@ public:
 #undef LISTEN
 
 #define LISTEN(n, SignalName) \
-       do { navProxy.listen(SignalName, std::function<void()>([=]() { emitCallback(CallbackType::n); })); } while(0)
+       do { navProxy.listen<void()>(SignalName).add([=]() { emitCallback(CallbackType::n); }); } while (0)
                LISTEN(DashedRow, "SpecialRow");
                LISTEN(DashedElementInRow, "SpecialElementInRow");
 #undef LISTEN
-
-               navProxy.listen("ContextChanged", std::function<void(AtspiAccessiblePtr, int, int, int, int)>(
-               [this](AtspiAccessiblePtr obj, int x, int y, int width, int height) {
-                       emitCallback(CallbackType::ContextChanged, std::make_shared<UIElement>(obj), x, y, width, height);
-               }));
-               navProxy.listen("BoxMoved", std::function<void(int, int, int, int, BoxPositionMode)>(
-               [this](int x, int y, int w, int h, BoxPositionMode mode) {
+               navProxy.listen<void(AtspiAccessiblePtr)>("ContextChanged").add(
+               [ = ](AtspiAccessiblePtr obj) {
+                       emitCallback(CallbackType::ContextChanged, std::make_shared<UIElement>(obj));
+                       DEBUG("got element %s", Singleton<Atspi>::instance().getUniqueId(obj).c_str());
+               });
+               navProxy.listen<void(int, int, int, int, BoxPositionMode)>("BoxMoved").add(
+               [ = ](int x, int y, int w, int h, BoxPositionMode mode) {
                        emitCallback(CallbackType::BoxMoved, x, y, w, h, mode);
-               }));
-
-               navProxy.listen("HackAttributesForRootAfterContextChanged", std::function<void(AtspiAccessiblePtr, std::vector<std::pair<std::string, std::string>>)>(
-               [this](AtspiAccessiblePtr root, std::vector<std::pair<std::string, std::string>> attrs) {
+               });
+               navProxy.listen<void(AtspiAccessiblePtr, std::vector<std::pair<std::string, std::string>>)>("HackAttributesForRootAfterContextChanged").add(
+               [ = ](AtspiAccessiblePtr root, std::vector<std::pair<std::string, std::string>> attrs) {
                        hack_setRootObjectAttributes(root, std::move(attrs));
-               }));
+               });
+
        }
 
        ~NavigationImpl() = default;
 
        void resetPosition() override
        {
-               navProxy.call<void>("ResetIndexes");
+               navProxy.method<void()>("ResetIndexes").call();
        }
 
        void nextRow() override
        {
-               navProxy.call<void>("NextRow");
+               navProxy.method<void()>("NextRow").call();
        }
 
        void prevRow() override
        {
-               navProxy.call<void>("PreviousRow");
+               navProxy.method<void()>("PreviousRow").call();
        }
 
        void nextElementInRow() override
        {
-               navProxy.call<void>("NextElementInRow");
+               navProxy.method<void()>("NextElementInRow").call();
        }
 
        void prevElementInRow() override
        {
-               navProxy.call<void>("PreviousElementInRow");
+               navProxy.method<void()>("PreviousElementInRow").call();
        }
 
        std::shared_ptr<UIElement> getCurrentElement() override
        {
-               auto current = navProxy.call<AtspiAccessiblePtr>("GetCurrentElement");
+               auto current = navProxy.method<AtspiAccessiblePtr()>("GetCurrentElement").call();
                return std::make_shared<UIElement>(std::move(*current));
        }
 
        std::shared_ptr<UIElement> getElementAtPoint(int x, int y) override
        {
-               auto elem = navProxy.call<AtspiAccessiblePtr>("GetElementAtPoint", x, y);
+               auto elem = navProxy.method<AtspiAccessiblePtr(int, int)>("GetElementAtPoint").call(x, y);
                return std::make_shared<UIElement>(std::move(*elem), Point{x, y});
        }
 
@@ -456,7 +133,7 @@ private:
                }
        }
 
-       EldbusProxy navProxy;
+       DBus navProxy;
 };
 
 template<>