From: Radoslaw Cybulski Date: Fri, 25 Aug 2017 14:36:47 +0000 (+0200) Subject: Add asynchronous at-spi calls X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=99d63604a4a00952346a731fca4569d1bccfa3fe;p=platform%2Fcore%2Faccessibility%2Funiversal-switch.git Add asynchronous at-spi calls 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 --- diff --git a/src/Atspi.cpp b/src/Atspi.cpp index 0b9a178..93305ff 100644 --- a/src/Atspi.cpp +++ b/src/Atspi.cpp @@ -1,6 +1,8 @@ #include "Atspi.hpp" +#include "Singleton.hpp" #include #include +#include #define PRINT_ERROR_AND_FREE(error) \ do { \ @@ -10,12 +12,6 @@ } \ } 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 root_object; +static AtspiAccessiblePtr root_object; static std::vector> root_object_attributes; -void hack_setRootObjectAttributes(std::shared_ptr obj, std::vector> attrs) +template static std::string getBusNameImpl(T *o) +{ + if (!o) return ""; + auto z = atspi_accessible_get_bus_name(reinterpret_cast(o), NULL); + std::string q = z; + g_free(z); + return std::move(q); +} + +template static std::string getPathImpl(T *o) +{ + if (!o) return "/org/a11y/atspi/null"; + auto z = atspi_accessible_get_path(reinterpret_cast(o), NULL); + std::string q = z; + g_free(z); + return "/org/a11y/atspi/accessible/" + q; +} + +template struct InterfaceNameFromType { + static const std::string interfaceName; +}; + +#define ADD_INTROSPECTION_FUNCTIONS(TYPE, NAME) \ + template <> const std::string InterfaceNameFromType::interfaceName = \ + ATSPI_DBUS_INTERFACE_ ## NAME; \ + static std::string getBusName(const std::shared_ptr &o) \ + { \ + return getBusNameImpl(o.get()); \ + } \ + static std::string getPath(const std::shared_ptr &o) \ + { \ + return getPathImpl(o.get()); \ + } \ + static void convert(std::shared_ptr &r, \ + const AtspiAccessiblePtr &obj) \ + { \ + r = { ATSPI_ ## NAME(obj.get()), g_object_unref }; \ + } \ + void Atspi::get ## TYPE ## Interface(const AtspiAccessiblePtr &obj, \ + AsyncCallback> callback) const \ + { \ + getInterface(obj, std::move(callback)); \ + } \ + std::string Atspi::getUniqueId(const std::shared_ptr &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 &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> 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("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 Atspi::getObjectInRelation(const std::shared_ptr &accessibleObj, AtspiRelationType searchType) const +Optional 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 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 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 Atspi::getObjectInRelation(const std::shared_pt return {relationObj, g_object_unref}; } -std::vector> Atspi::getAttributes(const std::shared_ptr &accessibleObj) const +template DBus Atspi::getProxy(const std::shared_ptr &obj, const std::string &interface) const +{ + auto bus = getBusName(obj); + auto path = getPath(obj); + + return DBus { bus, path, interface, eldbusConnection }; +} + +template struct PropertyCallback; +template struct PropertyCallback { + using VariantReturnType = EldbusVariant; + using RealReturnType = RetType; + using CallType = VariantReturnType(std::string, std::string, ARGS...); +}; +template struct returnType; +template struct returnType { + using type = RetType; +}; +template void callFunction( + const EldbusConnectionCallbackHandle &eldbusConnection, + const std::string &interface, + const std::shared_ptr &obj, + const std::string &func_name, + typename detail::AsyncCallbackHelper::type>::type callback, + ARGS &&... args +) +{ + auto bus = getBusName(obj); + auto path = getPath(obj); + + auto dbus = DBus { bus, path, interface, eldbusConnection }; + dbus.method(func_name).asyncCall(std::forward(args)..., std::move(callback)); +} + +template void callFunction( + const EldbusConnectionCallbackHandle &eldbusConnection, + const std::shared_ptr &obj, + const std::string &func_name, + typename detail::AsyncCallbackHelper::type>::type callback, + ARGS &&... args +) +{ + callFunction(eldbusConnection, InterfaceNameFromType::interfaceName, + obj, func_name, std::move(callback), std::forward(args)...); +} + +template void getProperty( + const EldbusConnectionCallbackHandle &eldbusConnection, + const std::string &interface, + const std::shared_ptr &obj, + const std::string &func_name, + typename detail::AsyncCallbackHelper::type>::type callback, + ARGS &&... args +) +{ + callFunction::CallType>( + eldbusConnection, DBUS_INTERFACE_PROPERTIES, obj, "Get", + [callback](Optional::VariantReturnType> val) { + if (!val) callback({}); + else callback(std::move((*val).value)); + }, + interface, func_name); +} + +template void getProperty( + const EldbusConnectionCallbackHandle &eldbusConnection, + const std::shared_ptr &obj, + const std::string &func_name, + typename detail::AsyncCallbackHelper::type>::type callback, + ARGS &&... args +) +{ + getProperty(eldbusConnection, InterfaceNameFromType::interfaceName, + obj, func_name, std::move(callback), std::forward(args)...); +} + +void Atspi::getAttributes(const AtspiAccessiblePtr &accessibleObj, + AsyncCallback> callback) const +{ + callFunction()>( + eldbusConnection, + accessibleObj, + "GetAttributes", + std::move(callback) + ); +} + +void Atspi::doActionName(const AtspiActionPtr &accessibleObj, const std::string &action, AsyncCallback callback) const +{ + callFunction( + eldbusConnection, + accessibleObj, + "DoActionName", + std::move(callback), + action + ); +} + +void Atspi::getParent(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const +{ + getProperty( + eldbusConnection, + accessibleObj, + "Parent", + std::move(callback) + ); +} + +void Atspi::getIndexInParent(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const +{ + callFunction( + eldbusConnection, + accessibleObj, + "GetIndexInParent", + std::move(callback) + ); +} + +void Atspi::selectChild(const AtspiSelectionPtr &accessibleObj, int index, AsyncCallback callback) const +{ + callFunction( + eldbusConnection, + accessibleObj, + "SelectChild", + std::move(callback), + index + ); +} + +void Atspi::getName(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const +{ + getProperty( + eldbusConnection, + accessibleObj, + "Name", + std::move(callback) + ); +} + +void Atspi::getRole(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const +{ + callFunction( + eldbusConnection, + accessibleObj, + "GetRole", + [callback](Optional val) { + if (!val) callback({}); + else callback((AtspiRole)*val); + } + ); +} + +void Atspi::getChildrenCount(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const +{ + getProperty( + eldbusConnection, + accessibleObj, + "ChildCount", + [callback](Optional val) { + if (!val || *val < 0) + callback({}); + else + callback((size_t)*val); + } + ); +} + +void Atspi::getChildAtIndex(const AtspiAccessiblePtr &accessibleObj, size_t index, AsyncCallback callback) const +{ + callFunction( + eldbusConnection, + accessibleObj, + "GetChildAtIndex", + std::move(callback), + (int)index + ); +} + +void Atspi::getStateSet(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const +{ + callFunction()>( + eldbusConnection, + accessibleObj, + "GetState", + [callback](Optional> 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> callback) const +{ + getChildrenCount(accessibleObj, + [accessibleObj, callback](Optional childrenCount) { + if (!childrenCount) { + callback({}); + } else { + struct State { + AsyncCallback> callback; + size_t todoCount; + std::vector children; + bool failed = false; + }; + auto state = std::make_shared(); + 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 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::instance().getChildAtIndex(accessibleObj, index, std::move(cback)); + } + } + }); +} + +template void Atspi::getInterface(const AtspiAccessiblePtr &accessibleObj, AsyncCallback> callback) const +{ + callFunction()>( + eldbusConnection, + accessibleObj, + "GetInterfaces", + [this, callback, accessibleObj](Optional> val) { + const auto interfaceName = InterfaceNameFromType::interfaceName; + if (val) { + for (auto &z : *val) { + if (z == interfaceName) { + std::shared_ptr 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> callback) +{ + assert(maximumFoundElements <= (size_t)std::numeric_limits::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(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 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; + struct handler { + const Atspi *atspi; + Point pt; + uint32_t ctype; + AsyncCallback callback; + + static void process(std::shared_ptr self, Optional 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( + self->atspi->eldbusConnection, + ptr, + "GetNavigableAtPoint", + [self](Optional value) { + process(std::move(self), std::move(value)); + }, + self->pt.x, self->pt.y, self->ctype); + } + } + }; + auto h = std::make_shared(handler{this, pt, (uint32_t)ctype, std::move(callback)}); + callFunction(int32_t, int32_t, uint32_t)>( + eldbusConnection, + root, + "GetNavigableAtPoint", + [h](Optional value) { + handler::process(std::move(h), std::move(value)); + }, + pt.x, pt.y, (uint32_t)ctype); +} + +void Atspi::getAllAcceptedObjects(const AtspiAccessiblePtr &root, AsyncCallback, Rectangle>> callback) const +{ + using returnType = std::tuple>>; + + callFunction( + eldbusConnection, + root, + "GetAllAcceptedObjects", + [ = ](Optional value) { + if (!value) { + callback({}); + } else { + std::tuple, 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> Atspi::getAttributes(const AtspiAccessiblePtr &accessibleObj) const { assert(accessibleObj == root_object); return root_object_attributes; @@ -179,7 +677,7 @@ std::vector> 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> Atspi::getAttributes(const std: std::vector> 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> Atspi::getAttributes(const std: #endif } -std::shared_ptr Atspi::getActionInterface(const std::shared_ptr &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 Atspi::getActionInterface(const std::shared_ptr &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 &accessibleObj, int acti return true; } -bool Atspi::doActionName(const std::shared_ptr &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 &accessibleObj, cons return true; } -std::shared_ptr Atspi::getValueInterface(const std::shared_ptr &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 Atspi::getValueInterface(const std::shared_ptr Atspi::getCurrentValue(const std::shared_ptr &valueInterface) const +Optional Atspi::getCurrentValue(const AtspiValuePtr &valueInterface) const { return getValueTemplateFunction(atspi_value_get_current_value, valueInterface.get()); } -bool Atspi::setCurrentValue(const std::shared_ptr &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 Atspi::getMinimumIncrement(const std::shared_ptr &valueInterface) const +Optional Atspi::getMinimumIncrement(const AtspiValuePtr &valueInterface) const { return getValueTemplateFunction(atspi_value_get_minimum_increment, valueInterface.get()); } -Optional Atspi::getMaximumValue(const std::shared_ptr &valueInterface) const +Optional Atspi::getMaximumValue(const AtspiValuePtr &valueInterface) const { return getValueTemplateFunction(atspi_value_get_maximum_value, valueInterface.get()); } -Optional Atspi::getMinimumValue(const std::shared_ptr &valueInterface) const +Optional Atspi::getMinimumValue(const AtspiValuePtr &valueInterface) const { return getValueTemplateFunction(atspi_value_get_minimum_value, valueInterface.get()); } -std::shared_ptr 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 Atspi::getAtPoint(int x, int y, CoordType type, std::shared_ptr 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 Atspi::getAtPoint(int x, int y, CoordType type, } if (!root) { - auto is_visible = [](const std::shared_ptr &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 &ptr) -> std::shared_ptr { + 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 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{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 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 Atspi::getChildrenCount(const std::shared_ptr &accessibleObj) const +Optional Atspi::getChildrenCount(const AtspiAccessiblePtr &accessibleObj) const { EXIT_IF_NULLPTR(accessibleObj); GError *error = nullptr; @@ -353,76 +855,76 @@ Optional Atspi::getChildrenCount(const std::shared_ptr return (size_t)count; } -std::shared_ptr Atspi::getChildAtIndex(const std::shared_ptr &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> Atspi::getChildren(const std::shared_ptr &accessibleObj) const +Optional> Atspi::getChildren(const AtspiAccessiblePtr &accessibleObj) const { EXIT_IF_NULLPTR(accessibleObj); auto count = getChildrenCount(accessibleObj); if (!count) return {}; - std::vector> tmp(*count); + std::vector 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 &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 Atspi::getParent(const std::shared_ptr &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 Atspi::getIndexInParent(const std::shared_ptr &accessibleObj) const +Optional 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 Atspi::getSelectionInterface(const std::shared_ptr &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 &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; } diff --git a/src/Atspi.hpp b/src/Atspi.hpp index 5dac7f4..7928e36 100644 --- a/src/Atspi.hpp +++ b/src/Atspi.hpp @@ -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 #include #include +#include +#include + +namespace detail +{ + template struct AsyncCallbackHelper { + using type = std::function)>; + }; + template <> struct AsyncCallbackHelper { + using type = std::function; + }; +} + +using AtspiAccessiblePtr = std::shared_ptr; +using AtspiActionPtr = std::shared_ptr; +using AtspiSelectionPtr = std::shared_ptr; +using AtspiCollectionPtr = std::shared_ptr; +using AtspiValuePtr = std::shared_ptr; +using AtspiEditableTextPtr = std::shared_ptr; +template using AsyncCallback = typename detail::AsyncCallbackHelper::type; class Atspi { public: + using StateSet = std::bitset; + enum class CoordType { Screen, Window @@ -19,31 +43,134 @@ public: Atspi(); ~Atspi(); - std::shared_ptr getObjectInRelation(const std::shared_ptr &accessibleObj, AtspiRelationType searchType) const; - std::vector> getAttributes(const std::shared_ptr &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> getAttributes(const AtspiAccessiblePtr &accessibleObj) const; - std::shared_ptr getActionInterface(const std::shared_ptr &accessibleObj) const; - bool doAction(const std::shared_ptr &accessibleObj, int action) const; - bool doActionName(const std::shared_ptr &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 getValueInterface(const std::shared_ptr &accessibleObj) const; - Optional getCurrentValue(const std::shared_ptr &valueInterface) const; - bool setCurrentValue(const std::shared_ptr &valueInterface, double newValue) const; - Optional getMinimumIncrement(const std::shared_ptr &valueInterface) const; - Optional getMaximumValue(const std::shared_ptr &valueInterface) const; - Optional getMinimumValue(const std::shared_ptr &valueInterface) const; + AtspiCollectionPtr getCollectionInterface(const AtspiAccessiblePtr &accessibleObj) const; + AtspiValuePtr getValueInterface(const AtspiAccessiblePtr &accessibleObj) const; + Optional getCurrentValue(const AtspiValuePtr &valueInterface) const; + bool setCurrentValue(const AtspiValuePtr &valueInterface, double newValue) const; + Optional getMinimumIncrement(const AtspiValuePtr &valueInterface) const; + Optional getMaximumValue(const AtspiValuePtr &valueInterface) const; + Optional getMinimumValue(const AtspiValuePtr &valueInterface) const; + + AtspiAccessiblePtr getDesktop() const; + AtspiAccessiblePtr getAtPoint(Point pt, CoordType type, AtspiAccessiblePtr root) const; + Optional getChildrenCount(const AtspiAccessiblePtr &accessibleObj) const; + Optional getProcessId(const AtspiAccessiblePtr &accessibleObj) const; + AtspiAccessiblePtr getChildAtIndex(const AtspiAccessiblePtr &accessibleObj, size_t index) const; + Optional> getChildren(const AtspiAccessiblePtr &accessibleObj) const; + Optional getRole(const AtspiAccessiblePtr &accessibleObj) const; + Optional getName(const AtspiAccessiblePtr &accessibleObj) const; + StateSet getStateSet(const AtspiAccessiblePtr &accessibleObj) const; + + AtspiAccessiblePtr getParent(const AtspiAccessiblePtr &accessibleObj) const; + Optional 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,// states & match type + std::unordered_map, int32_t, // attributes & match type + std::array, int32_t, // roles & match type + std::vector, 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 st, AtspiCollectionMatchType type) + { + for (auto a : st) + std::get(value)[a >> 5] |= 1 << (a & 31); + std::get(value) = (int32_t)type; + return *this; + } + Matcher &attributes(std::unordered_map st, AtspiCollectionMatchType type) + { + std::get(value) = std::move(st); + std::get(value) = (int32_t)type; + return *this; + } + template Matcher &attributes(A begin, A end, AtspiCollectionMatchType type) + { + while (begin != end) + std::get(value).insert(*begin++); + std::get(value) = (int32_t)type; + return *this; + } + Matcher &roles(std::initializer_list st, AtspiCollectionMatchType type) + { + for (auto a : st) + std::get(value)[a >> 5] |= 1 << (a & 31); + std::get(value) = (int32_t)type; + return *this; + } + Matcher &interfaces(std::vector st, AtspiCollectionMatchType type) + { + std::get(value) = std::move(st); + std::get(value) = (int32_t)type; + return *this; + } + template Matcher &interfaces(A begin, A end, AtspiCollectionMatchType type) + { + while (begin != end) + std::get(value).push_back(*begin++); + std::get(value) = (int32_t)type; + return *this; + } + }; + void getMatchedElements(const AtspiCollectionPtr &root, AtspiCollectionSortOrder sortOrder, + size_t maximumFoundElements, Matcher m, bool reverse, AsyncCallback> callback); + void getChildrenCount(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const; + void getChildAtIndex(const AtspiAccessiblePtr &accessibleObj, size_t index, AsyncCallback callback) const; + void getChildren(const AtspiAccessiblePtr &accessibleObj, AsyncCallback> callback) const; + void getName(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const; + void getRole(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const; + void getAttributes(const AtspiAccessiblePtr &accessibleObj, AsyncCallback> callback) const; + void doActionName(const AtspiActionPtr &accessibleObj, const std::string &action, AsyncCallback callback) const; + void getParent(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const; + void getIndexInParent(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const; + void selectChild(const AtspiSelectionPtr &accessibleObj, int index, AsyncCallback callback) const; + void getStateSet(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const; + struct AcceptedObjectInfo { + AtspiAccessiblePtr object; + Rectangle pos; + }; + void getAllAcceptedObjects(const AtspiAccessiblePtr &root, AsyncCallback, Rectangle>> callback) const; + void getAtPoint(Point pt, CoordType type, const AtspiAccessiblePtr &root, AsyncCallback callback) const; - std::shared_ptr getDesktop() const; - std::shared_ptr getAtPoint(int x, int y, CoordType type, std::shared_ptr root = {}) const; - Optional getChildrenCount(const std::shared_ptr &accessibleObj) const; - std::shared_ptr getChildAtIndex(const std::shared_ptr &accessibleObj, size_t index) const; - std::vector> getChildren(const std::shared_ptr &accessibleObj) const; - std::string getUniqueId(const std::shared_ptr &accessibleObj) const; +#define ADD_INTROSPECTION_FUNCTIONS(TYPE, NAME) \ + void get ## TYPE ## Interface(const AtspiAccessiblePtr &obj, \ + AsyncCallback> callback) const; \ + std::string getUniqueId(const std::shared_ptr &obj) const; \ + AtspiAccessiblePtr getBase(const std::shared_ptr &) const; - std::shared_ptr getParent(const std::shared_ptr &accessibleObj) const; - Optional getIndexInParent(const std::shared_ptr &accessibleObj) const; - std::shared_ptr getSelectionInterface(const std::shared_ptr &accessibleObj) const; - bool selectChild(const std::shared_ptr &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 DBus getProxy(const std::shared_ptr &obj, const std::string &interface) const; + template void getInterface(const AtspiAccessiblePtr &accessibleObj, AsyncCallback> callback) const; + void getInterface(const AtspiAccessiblePtr &accessibleObj, AsyncCallback callback) const + { + callback(accessibleObj); + } }; #endif diff --git a/src/DBus.cpp b/src/DBus.cpp new file mode 100644 index 0000000..f3d106d --- /dev/null +++ b/src/DBus.cpp @@ -0,0 +1,61 @@ +#include "DBus.hpp" +#include +#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>::get(Eldbus_Message_Iter *iter, AtspiAccessiblePtr &v) +{ + subtype s; + if (!signature::get(iter, s)) return false; + v = Singleton::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 index 0000000..8a2d6f0 --- /dev/null +++ b/src/DBus.hpp @@ -0,0 +1,860 @@ +#ifndef DBUS_HPP +#define DBUS_HPP + +#include +#include + +#include "Optional.hpp" +#include +#include +#include +#include +#include +#include +#include + +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 struct EldbusVariant { + A value; +}; + +using EldbusConnectionCallbackHandle = std::shared_ptr; +using EldbusMessageCallbackHandle = std::unique_ptr; +using EldbusObjectCallbackHandle = std::shared_ptr; +using EldbusProxyCallbackHandle = std::shared_ptr; + +namespace detail +{ + template struct signature; + template struct signature>; + template struct signature>; + template struct signature>; + template struct signature>>; + template struct signature>; + template struct signature, N>>; + template struct signature>; + template struct signature>; + + template struct signature::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 { \ + 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 { + 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 { + static std::string sig() + { + return "s"; + } + static void set(Eldbus_Message_Iter *iter, const std::string &v) + { + eldbus_message_iter_arguments_append(iter, sig().c_str(), v.c_str()); + } + static void set(Eldbus_Message_Iter *iter, const char *v) + { + eldbus_message_iter_arguments_append(iter, sig().c_str(), v); + } + static bool get(Eldbus_Message_Iter *iter, std::string &v) + { + const char *q; + if (!eldbus_message_iter_get_and_next(iter, 's', &q)) { + if (!eldbus_message_iter_get_and_next(iter, 'o', &q)) + return false; + } + v = q; + return true; + } + }; + template struct signature_tuple_element_type_helper { + using type = typename signature_tuple_element_type_helper < INDEX - 1, ARGS... >::type; + }; + template struct signature_tuple_element_type_helper<0, A, ARGS...> { + using type = A; + }; + + template struct signature_tuple_helper { + using current_type = typename signature_tuple_element_type_helper::type; + + static std::string sig() + { + return signature::sig() + signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::sig(); + } + static void set(Eldbus_Message_Iter *iter, const std::tuple &args) + { + signature::set(iter, std::get(args)); + signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::set(iter, args); + } + static bool get(Eldbus_Message_Iter *iter, std::tuple &args) + { + return signature::get(iter, std::get(args)) && + signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::get(iter, args); + } + }; + template struct signature_tuple_helper { + static std::string sig() + { + return ""; + } + static void set(Eldbus_Message_Iter *iter, const std::tuple &args) + { + } + static bool get(Eldbus_Message_Iter *iter, std::tuple &args) + { + return true; + } + }; + template struct signature> { + static std::string sig() + { + return "(" + signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig() + ")"; + } + static void set(Eldbus_Message_Iter *iter, const std::tuple &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) + { + 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 struct signature> { + static std::string sig() + { + return "(" + signature_tuple_helper<0, 2, A, B>::sig() + ")"; + } + static void set(Eldbus_Message_Iter *iter, const std::pair &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 &ab, char sg = 'r') + { + Eldbus_Message_Iter *entry; + if (!eldbus_message_iter_get_and_next(iter, sg, &entry)) return false; + std::tuple 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 struct signature> { + static std::string sig() + { + return "a" + signature::sig(); + } + static void set(Eldbus_Message_Iter *iter, const std::vector &v) + { + auto lst = eldbus_message_iter_container_new(iter, 'a', signature::sig().c_str()); + ASSERT(lst); + for (auto &a : v) { + signature::set(lst, a); + } + eldbus_message_iter_container_close(iter, lst); + } + static bool get(Eldbus_Message_Iter *iter, std::vector &v) + { + Eldbus_Message_Iter *s; + v.clear(); + if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false; + A a; + while (signature::get(s, a)) + v.push_back(std::move(a)); + return true; + } + }; + template struct signature> { + static std::string sig() + { + return "a" + signature::sig(); + } + static void set(Eldbus_Message_Iter *iter, const std::array &v) + { + auto lst = eldbus_message_iter_container_new(iter, 'a', signature::sig().c_str()); + assert(lst); + for (auto &a : v) { + signature::set(lst, a); + } + eldbus_message_iter_container_close(iter, lst); + } + static bool get(Eldbus_Message_Iter *iter, std::array &v) + { + Eldbus_Message_Iter *s; + if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) + return false; + for (auto &a : v) { + if (!signature::get(s, a)) + return false; + } + return true; + } + }; + template struct signature, N>> { + static std::string sig() + { + return "a" + signature>::sig(); + } + static void set(Eldbus_Message_Iter *iter, const std::array, N> &v) + { + auto lst = eldbus_message_iter_container_new(iter, 'a', signature>::sig().c_str()); + assert(lst); + for (auto &a : v) { + signature>::set(lst, a); + } + eldbus_message_iter_container_close(iter, lst); + } + static bool get(Eldbus_Message_Iter *iter, std::array, 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>::get(s, a, sig)) + return false; + } + return true; + } + }; + template struct signature>> { + static std::string sig() + { + return "a" + signature>::sig(); + } + static void set(Eldbus_Message_Iter *iter, const std::vector> &v) + { + auto lst = eldbus_message_iter_container_new(iter, 'a', signature>::sig().c_str()); + ASSERT(lst); + for (auto &a : v) { + signature>::set(lst, a); + } + eldbus_message_iter_container_close(iter, lst); + } + static bool get(Eldbus_Message_Iter *iter, std::vector> &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> + if (t && t[0] == '{') + sig = '{'; + free(t); + std::pair a; + while (signature>::get(s, a)) + v.push_back(std::move(a)); + return true; + } + }; + template struct signature> { + static std::string sig() + { + return "v"; + } + static void set(Eldbus_Message_Iter *iter, const EldbusVariant &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::sig().c_str()); + signature::set(var, v.value); + eldbus_message_iter_container_close(iter, var); + } + static bool get(Eldbus_Message_Iter *iter, EldbusVariant &v) + { + Eldbus_Message_Iter *s; + if (!eldbus_message_iter_get_and_next(iter, 'v', &s)) return false; + return signature::get(s, v.value); + } + }; + template struct signature> { + 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 &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>::set(lst, a, 'e'); + } + eldbus_message_iter_container_close(iter, lst); + } + static bool get(Eldbus_Message_Iter *iter, std::unordered_map &v) + { + Eldbus_Message_Iter *s; + v.clear(); + if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false; + std::pair a; + while (signature>::get(s, a)) + v.insert(std::move(a)); + return true; + } + }; + template struct signature> { + static std::string sig() + { + return "a{" + signature_tuple_helper<0, 2, A, B>::sig() + "}"; + } + static void set(Eldbus_Message_Iter *iter, const std::map &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>::set(lst, a, 'e'); + } + eldbus_message_iter_container_close(iter, lst); + } + static bool get(Eldbus_Message_Iter *iter, std::map &v) + { + Eldbus_Message_Iter *s; + v.clear(); + if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false; + std::pair a; + while (signature::get(s, a)) + v.insert(std::move(a)); + return true; + } + }; + template struct signature { + static std::string sig() + { + return signature::sig(); + } + static void set(Eldbus_Message_Iter *iter, const A &v) + { + signature::set(iter, v); + } + static void get(Eldbus_Message_Iter *iter, A &v) + { + signature::get(iter, v); + } + }; + template struct signature { + static std::string sig() + { + return signature::sig(); + } + static void set(Eldbus_Message_Iter *iter, const A &v) + { + signature::set(iter, v); + } + static void get(Eldbus_Message_Iter *iter, A &v) + { + signature::get(iter, v); + } + }; + template struct signature { + static std::string sig() + { + return signature::sig(); + } + static void set(Eldbus_Message_Iter *iter, const A &v) + { + signature::set(iter, v); + } + static void get(Eldbus_Message_Iter *iter, A &v) + { + signature::get(iter, v); + } + }; + template <> struct signature> { + using subtype = std::pair; + + static std::string sig() + { + return "(so)"; + } + static void set(Eldbus_Message_Iter *iter, const std::shared_ptr &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::set(iter, { bus, std::string{prefixPath} + path }); + g_free(path); + g_free(bus); + } else { + signature::set(iter, { {}, std::string{nullPath} }); + } + } + static bool get(Eldbus_Message_Iter *iter, std::shared_ptr &v); + }; + struct CallId { + static unsigned int LastId; + unsigned int id = ++LastId; + }; + template 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::get(iter, r)) + return true; + ERROR("call %d: signature mismatch, got %s, expected %s", + callId.id, + eldbus_message_signature_get(msg), + signature::sig().c_str()); + } else + ERROR("call %d: failed to get iter", callId.id); + return false; + } + }; + template struct unpackValues_Helper> { + bool operator()(CallId callId, std::tuple &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 bool unpackValues(CallId callId, R &r, const Eldbus_Message *msg) + { + return unpackValues_Helper()(callId, r, msg); + } + template void packValues(CallId callId, Eldbus_Message *msg, const R &r) + { + DEBUG("call %d: packing values with signature %s", callId.id, signature::sig().c_str()); + auto iter = eldbus_message_iter_get(msg); + signature::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 void packValues_helper(Eldbus_Message_Iter *iter, A &&a, ARGS &&... r) + { + signature::set(iter, std::forward(a)); + packValues_helper(iter, std::forward(r)...); + } + template 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(r)...); + DEBUG("call %d: signature is %s", callId.id, eldbus_message_signature_get(msg)); + } + + template struct seq {}; + template struct gens : gens < N - 1, N - 1, S... > {}; + template struct gens<0, S...> { + typedef seq type; + }; + + template struct apply_helper { + const C &c; + const std::tuple &args; + + template R apply_2(seq) + { + return c(std::get(args)...); + } + R apply_1() + { + return apply_2(typename gens::type()); + } + }; + template R apply(const C &c, const std::tuple &args) + { + apply_helper ah { c, args }; + return ah.apply_1(); + } + + struct EldbusProxyBase { + EldbusProxyBase() + { + eldbus_init(); + } + ~EldbusProxyBase() + { + eldbus_shutdown(); + } + }; +} + +namespace detail +{ + template struct Method; + template struct Method; + template struct Method; + template struct Listen; + template struct Listen; + + 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> *)data; + DEBUG("call %d: got reply", d->first.id); + d->second(msg); + } + static void pendingFreeCb(void *data, const void *) + { + auto d = (std::pair> *)data; + DEBUG("call %d: deleting", d->first.id); + delete d; + } + template + 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 + void asyncCall(CallId callId, ConnectionState connectionState, const std::string &func_name, + std::function 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, [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 *)data))(msg); + } + template void addListener(CallId callId, ConnectionState &connectionState, + const std::string &func_name, + std::vector*> &callbacks, + std::function callback) + { + auto tmp = new std::function { + [ 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 params; + if (detail::unpackValues(callId, params, msg)) + detail::apply, 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 + 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 + 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 using Method = detail::Method; + template using Listen = detail::Listen; + + template + Method method(std::string func_name) + { + return Method { connectionState, std::move(func_name), info }; + } + + template + Listen listen(std::string func_name) + { + return Listen { connectionState, callbacks, std::move(func_name), info }; + } + +private: + detail::ConnectionState connectionState; + std::vector*> callbacks; + std::string info; +}; + +template struct detail::Method { + ConnectionState connectionState; + std::string func_name; + std::string info; + + Optional 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(callId, r, reply.get())) + return r; + } + return {}; + } + void asyncCall(const ARGS &... args, std::function)> 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(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 struct detail::Method { + 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 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 struct detail::Listen { + detail::ConnectionState connectionState; + std::vector*> &callbacks; + std::string func_name; + std::string info; + + void add(std::function cc) + { + CallId callId; + detail::displayDebugCallInfo(callId, func_name, info); + detail::addListener(callId, connectionState, func_name, callbacks, std::move(cc)); + } +}; + +#endif diff --git a/src/NavigationInterface.cpp b/src/NavigationInterface.cpp index 88d30d8..b882f13 100644 --- a/src/NavigationInterface.cpp +++ b/src/NavigationInterface.cpp @@ -2,6 +2,7 @@ #include "UniversalSwitchLog.hpp" #include "Atspi.hpp" #include "UIElement.hpp" +#include "DBus.hpp" #include #include @@ -10,7 +11,7 @@ #include #include -constexpr static int ELDBUS_CALL_TIMEOUT = 1000; +using AtspiAccessiblePtr = std::shared_ptr; 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; -using EldbusMessageCallbackHandle = std::unique_ptr; -using EldbusObjectCallbackHandle = std::unique_ptr; -using EldbusProxyCallbackHandle = std::unique_ptr; -using AtspiAccessiblePtr = std::shared_ptr; - -namespace -{ - void eldbusParamSet(const EldbusMessageCallbackHandle &msg) - {} - - void eldbusParamSet(const EldbusMessageCallbackHandle &msg, int arg) - { - eldbus_message_arguments_append(msg.get(), "i", arg); - } - - template - void eldbusParamSet(const EldbusMessageCallbackHandle &msg, A &&arg, ARGS &&... args) - { - eldbusParamSet(msg, std::forward(arg)); - eldbusParamSet(msg, std::forward(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 bool eldbusParamGet(Eldbus_Message_Iter *iter, std::pair &v); - template bool eldbusParamGet(Eldbus_Message_Iter *iter, std::tuple &v); - template bool eldbusParamGet(Eldbus_Message_Iter *iter, std::vector &v); - - template - typename std::enable_if::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 - struct EldbusParamGetTuple { - static bool get(Eldbus_Message_Iter *iter, std::tuple &args) - { - if (!EldbusParamGetTuple < I - 1, ARGS... >::get(iter, args)) - return false; - return eldbusParamGet(iter, std::get(args)); - } - }; - - template - struct EldbusParamGetTuple<0, ARGS...> { - static bool get(Eldbus_Message_Iter *iter, std::tuple &args) - { - return eldbusParamGet(iter, std::get<0>(args)); - } - }; - - template bool eldbusParamGet(Eldbus_Message_Iter *iter, std::pair &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 bool eldbusParamGet(Eldbus_Message_Iter *iter, std::tuple &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 bool eldbusParamGet(Eldbus_Message_Iter *iter, std::vector &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 - bool eldbusParamGetFromMsg(const Eldbus_Message *msg, A &v) - { - auto iter = eldbus_message_iter_get(msg); - return eldbusParamGet(iter, v); - } - - template - bool eldbusParamGetFromMsg(const Eldbus_Message *msg, std::tuple &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 - Optional 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 std::enable_if::value, bool>::type call(const std::string &func_name, ARGS &&... args) - { - auto reply = _call(func_name, std::forward(args)...); - return bool(reply); - } - - template - typename std::enable_if < !std::is_same::value, Optional>::type call(const std::string &func_name, ARGS &&... args) - { - auto reply = _call(func_name, std::forward(args)...); - return eldbusParamsGet(reply.get()); - } - - template - void listen(const std::string &func_name, std::function 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>(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(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 - static R _invoke(std::function fnc, const std::tuple<> &t) - { - return fnc(); - } - - template - static R _invoke(std::function fnc, const std::tuple &t) - { - return fnc(std::get<0>(t)); - } - - template - static R _invoke(std::function fnc, const std::tuple &t) - { - return fnc(std::get<0>(t), std::get<1>(t)); - } - - template - static R _invoke(std::function fnc, const std::tuple &t) - { - return fnc(std::get<0>(t), std::get<1>(t), std::get<2>(t)); - } - - template - static R _invoke(std::function fnc, const std::tuple &t) - { - return fnc(std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t)); - } - - template - static R _invoke(std::function fnc, const std::tuple &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 *)data))(msg); - } - - template - 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)...); - 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 obj, std::vector> 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("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("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([=]() { emitCallback(CallbackType::n); })); } while(0) + do { navProxy.listen(#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([=]() { emitCallback(CallbackType::n); })); } while(0) + do { navProxy.listen(SignalName).add([=]() { emitCallback(CallbackType::n); }); } while (0) LISTEN(DashedRow, "SpecialRow"); LISTEN(DashedElementInRow, "SpecialElementInRow"); #undef LISTEN - - navProxy.listen("ContextChanged", std::function( - [this](AtspiAccessiblePtr obj, int x, int y, int width, int height) { - emitCallback(CallbackType::ContextChanged, std::make_shared(obj), x, y, width, height); - })); - navProxy.listen("BoxMoved", std::function( - [this](int x, int y, int w, int h, BoxPositionMode mode) { + navProxy.listen("ContextChanged").add( + [ = ](AtspiAccessiblePtr obj) { + emitCallback(CallbackType::ContextChanged, std::make_shared(obj)); + DEBUG("got element %s", Singleton::instance().getUniqueId(obj).c_str()); + }); + navProxy.listen("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>)>( - [this](AtspiAccessiblePtr root, std::vector> attrs) { + }); + navProxy.listen>)>("HackAttributesForRootAfterContextChanged").add( + [ = ](AtspiAccessiblePtr root, std::vector> attrs) { hack_setRootObjectAttributes(root, std::move(attrs)); - })); + }); + } ~NavigationImpl() = default; void resetPosition() override { - navProxy.call("ResetIndexes"); + navProxy.method("ResetIndexes").call(); } void nextRow() override { - navProxy.call("NextRow"); + navProxy.method("NextRow").call(); } void prevRow() override { - navProxy.call("PreviousRow"); + navProxy.method("PreviousRow").call(); } void nextElementInRow() override { - navProxy.call("NextElementInRow"); + navProxy.method("NextElementInRow").call(); } void prevElementInRow() override { - navProxy.call("PreviousElementInRow"); + navProxy.method("PreviousElementInRow").call(); } std::shared_ptr getCurrentElement() override { - auto current = navProxy.call("GetCurrentElement"); + auto current = navProxy.method("GetCurrentElement").call(); return std::make_shared(std::move(*current)); } std::shared_ptr getElementAtPoint(int x, int y) override { - auto elem = navProxy.call("GetElementAtPoint", x, y); + auto elem = navProxy.method("GetElementAtPoint").call(x, y); return std::make_shared(std::move(*elem), Point{x, y}); } @@ -456,7 +133,7 @@ private: } } - EldbusProxy navProxy; + DBus navProxy; }; template<>