Navigation implementation without external dependencies 91/154591/26
authorRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Tue, 10 Oct 2017 08:36:08 +0000 (10:36 +0200)
committerRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Fri, 20 Oct 2017 12:50:09 +0000 (14:50 +0200)
This patch implements navigation without adding any new dependencies to
third party at-spi libraries. Everything is calculated using native
at-spi calls.

Change-Id: Ie536f014a6baa781046e16026e87b6200d5609b4

src/Atspi.cpp
src/Atspi.hpp
src/Geometry.hpp
src/NavigationInterface.cpp
src/PointScanner.cpp
src/RowScanner.cpp
src/ScreenScanner.hpp
src/ScreenScannerManager.cpp
src/UniversalSwitch.cpp
src/dbusLocators.hpp

index fc7b54d..1b15b4f 100644 (file)
@@ -1,5 +1,6 @@
 #include "Atspi.hpp"
 #include "Singleton.hpp"
+#include "dbusLocators.hpp"
 #include <memory>
 #include <functional>
 #include <limits>
@@ -104,6 +105,7 @@ ADD_INTROSPECTION_FUNCTIONS(Accessible, ACCESSIBLE);
 ADD_INTROSPECTION_FUNCTIONS(Action, ACTION);
 ADD_INTROSPECTION_FUNCTIONS(Selection, SELECTION);
 ADD_INTROSPECTION_FUNCTIONS(Collection, COLLECTION);
+ADD_INTROSPECTION_FUNCTIONS(Component, COMPONENT);
 ADD_INTROSPECTION_FUNCTIONS(Value, VALUE);
 ADD_INTROSPECTION_FUNCTIONS(EditableText, EDITABLE_TEXT);
 
@@ -114,21 +116,92 @@ Atspi::Atspi()
 {
        ConnectAtClient();
 
-       DBusClient proxy{"org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus"};
-       auto addr = proxy.method<std::string()>("GetAddress").call();
+       DBusClient proxy{ dbusLocators::atspi::BUS, dbusLocators::atspi::OBJ_PATH, dbusLocators::atspi::BUS_INTERFACE };
+       auto addr = proxy.method<std::string()>(dbusLocators::atspi::GET_ADDRESS).call();
 
        if (!addr) {
                ERROR("failed");
        } else {
-               eldbusConnection = DBus::EldbusConnectionCallbackHandle{ eldbus_address_connection_get(std::get<0>(addr).c_str()), eldbus_connection_unref };
+               eldbusConnection = DBus::EldbusConnectionCallbackHandle{
+                       eldbus_address_connection_get(std::get<0>(addr).c_str()),
+                       eldbus_connection_unref };
        }
+
+       eventListener = atspi_event_listener_new(onAtspiEventCb, this, nullptr);
+       ASSERT(eventListener);
+       atspi_event_listener_register(eventListener, "window:activate", NULL);
+       atspi_event_listener_register(eventListener, "object:state-changed:visible", NULL);
+       atspi_event_listener_register(eventListener, "object:state-changed:showing", NULL);
+       atspi_event_listener_register(eventListener, "object:bounds-changed", NULL);
+       atspi_event_listener_register(eventListener, "object:state-changed:defunct", NULL);
 }
 
 Atspi::~Atspi()
 {
+       atspi_event_listener_deregister(eventListener, "window:activate", NULL);
+       atspi_event_listener_deregister(eventListener, "object:state-changed:visible", NULL);
+       atspi_event_listener_deregister(eventListener, "object:state-changed:showing", NULL);
+       atspi_event_listener_deregister(eventListener, "object:bounds-changed", NULL);
+       atspi_event_listener_deregister(eventListener, "object:state-changed:defunct", NULL);
+       g_object_unref(eventListener);
+
        DisconnectAtClient();
 }
 
+void Atspi::onAtspiEventCb(AtspiEvent *event, void *data)
+{
+       static_cast<Atspi *>(data)->onAtspiEvent(event);
+}
+
+Atspi::WatchHandler::~WatchHandler()
+{
+       atspi->removeWatcher(this);
+}
+
+void Atspi::removeWatcher(WatchHandler *wh)
+{
+       if (wh->atspi) {
+               ASSERT(wh->atspi == this);
+               watchedCallbacks[wh->index] = {};
+               ASSERT(!watchedCallbacks[wh->index]);
+               wh->atspi = nullptr;
+
+               while (!watchedCallbacks.empty() && !watchedCallbacks.back())
+                       watchedCallbacks.pop_back();
+       }
+}
+
+std::unique_ptr<Atspi::WatchHandler> Atspi::registerWatcherForAllObjects(WatchCallback callback)
+{
+       std::unique_ptr<Atspi::WatchHandler> wh { new WatchHandler };
+       wh->atspi = this;
+       wh->index = watchedCallbacks.size();
+       watchedCallbacks.push_back(std::move(callback));
+       return std::move(wh);
+}
+
+void Atspi::onAtspiEvent(AtspiEvent *event)
+{
+       using namespace std::literals::string_literals;
+
+       auto source = make(event->source, true);
+       auto id = getUniqueId(source);
+       DEBUG("Event: detail1 %d detail2 %d source %20s event %s", event->detail1, event->detail2, id.c_str(), event->type);
+       Optional<WatchEvent> watchEventType;
+
+       if (event->type == "object:state-changed:showing"s || event->type == "object:state-changed:visible"s)
+               watchEventType = event->detail1 ? WatchEvent::shown : WatchEvent::hidden;
+       else if (event->type == "window:activate"s) watchEventType = WatchEvent::activated;
+       else if (event->type == "object:bounds-changed"s) watchEventType = WatchEvent::moved;
+       else if (event->type == "object:state-changed:defunct"s) watchEventType = WatchEvent::defunct;
+
+       if (watchEventType) {
+               for (size_t i = 0; i < watchedCallbacks.size(); ++i) {
+                       if (watchedCallbacks[i]) watchedCallbacks[i](source, *watchEventType);
+               }
+       }
+}
+
 bool Atspi::ConnectAtClient()
 {
        auto error = atspi_init();
@@ -139,7 +212,8 @@ bool Atspi::ConnectAtClient()
                INFO("Atspi has been already initialized");
        }
 
-       auto dbusStatus = setPropertyBool("org.a11y.Bus", "/org/a11y/bus", "org.a11y.Status", "IsEnabled", true);
+       auto dbusStatus = setPropertyBool(dbusLocators::atspi::BUS, dbusLocators::atspi::OBJ_PATH, dbusLocators::atspi::STATUS_INTERFACE,
+                                                                         dbusLocators::atspi::IS_ENABLED, true);
        if (!dbusStatus) {
                ERROR("IsEnabled flag set to true procedure failed");
                return false;
@@ -152,7 +226,8 @@ bool Atspi::ConnectAtClient()
 bool Atspi::DisconnectAtClient()
 {
        DEBUG("shutting down");
-       auto dbusStatus = setPropertyBool("org.a11y.Bus", "/org/a11y/bus", "org.a11y.Status", "IsEnabled", false);
+       auto dbusStatus = setPropertyBool(dbusLocators::atspi::BUS, dbusLocators::atspi::OBJ_PATH, dbusLocators::atspi::STATUS_INTERFACE,
+                                                                         dbusLocators::atspi::IS_ENABLED, false);
        if (!dbusStatus)
                ERROR("IsEnabled flag set to false procedure failed");
 
@@ -176,14 +251,16 @@ bool Atspi::setPropertyBool(const char *bus,
                ERROR("Failed to create eldbus object");
                return false;
        }
-       std::unique_ptr<Eldbus_Proxy, void (*)(Eldbus_Proxy *)> proxy{eldbus_proxy_get(dobj.get(), "org.freedesktop.DBus.Properties"), eldbus_proxy_unref};
+       std::unique_ptr<Eldbus_Proxy, void (*)(Eldbus_Proxy *)> proxy{
+               eldbus_proxy_get(dobj.get(), dbusLocators::freeDesktop::PROPERTIES_INTERFACE), eldbus_proxy_unref};
        if (!proxy) {
-               ERROR("Failed to create proxy object for 'org.freedesktop.DBus.Properties'");
+               ERROR("Failed to create proxy object for '%s'", dbusLocators::freeDesktop::PROPERTIES_INTERFACE);
                return false;
        }
-       std::unique_ptr<Eldbus_Message, void (*)(Eldbus_Message *)> req{eldbus_proxy_method_call_new(proxy.get(), "Set"), eldbus_message_unref};
+       std::unique_ptr<Eldbus_Message, void (*)(Eldbus_Message *)> req{
+               eldbus_proxy_method_call_new(proxy.get(), dbusLocators::freeDesktop::SET), eldbus_message_unref};
        if (!req) {
-               ERROR("Failed to create method call on org.freedesktop.DBus.Properties.Set");
+               ERROR("Failed to create method call on %s.%s", dbusLocators::freeDesktop::PROPERTIES_INTERFACE, dbusLocators::freeDesktop::SET);
                return false;
        }
        eldbus_message_ref(req.get());
@@ -356,7 +433,7 @@ template <typename CallType, typename InterfaceType, typename ... ARGS> void get
 )
 {
        callFunction<typename PropertyCallback<CallType>::CallType>(
-               eldbusConnection, DBUS_INTERFACE_PROPERTIES, obj, "Get",
+               eldbusConnection, dbusLocators::freeDesktop::PROPERTIES_INTERFACE, obj, dbusLocators::freeDesktop::GET,
        [callback](typename PropertyCallback<CallType>::VariantReturnType val) {
                if (!val) {
                        callback(Error { val.getError() });
@@ -391,7 +468,7 @@ template <typename CallType, typename InterfaceType, typename Value> void setPro
 {
        typename PropertySetterCallback<CallType>::VariantSetterType variantValue { std::move(value) };
        callFunction<typename PropertySetterCallback<CallType>::CallType>(
-               eldbusConnection, DBUS_INTERFACE_PROPERTIES, obj, "Set",
+               eldbusConnection, dbusLocators::freeDesktop::PROPERTIES_INTERFACE, obj, dbusLocators::freeDesktop::SET,
                std::move(callback), interface, func_name, std::move(variantValue));
 }
 
@@ -413,15 +490,17 @@ void Atspi::getAttributes(const AtspiAccessiblePtr &accessibleObj,
        callFunction<std::unordered_map<std::string, std::string>()>(
                eldbusConnection,
                accessibleObj,
-               "GetAttributes",
+               dbusLocators::atspi::GET_ATTRIBUTES,
                std::move(callback)
        );
 }
 
 void Atspi::getProcessId(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<unsigned int> callback) const
 {
-       DBus::DBusClient dbus { "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", eldbusConnection };
-       dbus.method<unsigned int(std::string)>("GetConnectionUnixProcessID").asyncCall(std::move(callback),
+       DBus::DBusClient dbus {
+               dbusLocators::freeDesktop::BUS, dbusLocators::freeDesktop::OBJ_PATH, dbusLocators::freeDesktop::INTERFACE,
+               eldbusConnection };
+       dbus.method<unsigned int(std::string)>(dbusLocators::freeDesktop::GET_CONNECTION_UNIX_PROCESS_ID).asyncCall(std::move(callback),
                        getBusName(accessibleObj));
 }
 
@@ -430,7 +509,7 @@ void Atspi::doActionName(const AtspiActionPtr &accessibleObj, const std::string
        callFunction<bool(std::string)>(
                eldbusConnection,
                accessibleObj,
-               "DoActionName",
+               dbusLocators::atspi::DO_ACTION_NAME,
                std::move(callback),
                action
        );
@@ -441,7 +520,7 @@ void Atspi::getParent(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<Ats
        getProperty<AtspiAccessiblePtr()>(
                eldbusConnection,
                accessibleObj,
-               "Parent",
+               dbusLocators::atspi::PARENT,
                std::move(callback)
        );
 }
@@ -451,7 +530,7 @@ void Atspi::getIndexInParent(const AtspiAccessiblePtr &accessibleObj, AsyncCallb
        callFunction<int()>(
                eldbusConnection,
                accessibleObj,
-               "GetIndexInParent",
+               dbusLocators::atspi::GET_INDEX_IN_PARENT,
                std::move(callback)
        );
 }
@@ -461,7 +540,7 @@ void Atspi::selectChild(const AtspiSelectionPtr &accessibleObj, int index, Async
        callFunction<void(int)>(
                eldbusConnection,
                accessibleObj,
-               "SelectChild",
+               dbusLocators::atspi::SELECT_CHILD,
                std::move(callback),
                index
        );
@@ -472,7 +551,7 @@ void Atspi::getName(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::
        getProperty<std::string()>(
                eldbusConnection,
                accessibleObj,
-               "Name",
+               dbusLocators::atspi::NAME,
                std::move(callback)
        );
 }
@@ -482,7 +561,7 @@ void Atspi::getRole(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<Atspi
        callFunction<uint32_t()>(
                eldbusConnection,
                accessibleObj,
-               "GetRole",
+               dbusLocators::atspi::GET_ROLE,
        [callback](ValueOrError<uint32_t> val) {
                if (!val) callback(Error{ val.getError() });
                else callback((AtspiRole)std::get<0>(val));
@@ -494,7 +573,7 @@ void Atspi::getChildrenCount(const AtspiAccessiblePtr &accessibleObj, AsyncCallb
        getProperty<int()>(
                eldbusConnection,
                accessibleObj,
-               "ChildCount",
+               dbusLocators::atspi::CHILD_COUNT,
        [callback](ValueOrError<int> val) {
                if (!val) callback(Error{ val.getError() });
                else callback((size_t)std::get<0>(val));
@@ -507,7 +586,7 @@ void Atspi::getChildAtIndex(const AtspiAccessiblePtr &accessibleObj, size_t inde
        callFunction<AtspiAccessiblePtr(int)>(
                eldbusConnection,
                accessibleObj,
-               "GetChildAtIndex",
+               dbusLocators::atspi::GET_CHILD_AT_INDEX,
                std::move(callback),
                (int)index
        );
@@ -518,7 +597,7 @@ void Atspi::getStateSet(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<S
        callFunction<std::array<uint32_t, 2>()>(
                eldbusConnection,
                accessibleObj,
-               "GetState",
+               dbusLocators::atspi::GET_STATE,
        [callback](ValueOrError<std::array<uint32_t, 2>> data) {
                if (!data) {
                        callback(Error{ data.getError() });
@@ -573,15 +652,15 @@ void Atspi::getChildren(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<s
        });
 }
 
-void Atspi::getRelationSet(const std::shared_ptr<AtspiAccessible> &accessibleObj,
+void Atspi::getRelationSet(const AtspiAccessiblePtr &accessibleObj,
                                                   AsyncCallback<std::vector<AccessibilityRelation>> callback) const
 {
-       using RelationSet = std::vector<std::tuple<uint32_t, std::vector<std::shared_ptr<AtspiAccessible>>>>;
+       using RelationSet = std::vector<std::tuple<uint32_t, std::vector<AtspiAccessiblePtr>>>;
 
        callFunction<RelationSet()>(
                eldbusConnection,
                accessibleObj,
-               "GetRelationSet",
+               dbusLocators::atspi::GET_RELATION_SET,
        [callback](ValueOrError<RelationSet> relationSet) {
                if (!relationSet) {
                        ERROR("dbus getRelationSet failed");
@@ -597,8 +676,25 @@ void Atspi::getRelationSet(const std::shared_ptr<AtspiAccessible> &accessibleObj
        });
 }
 
-void Atspi::getObjectInRelation(const std::shared_ptr<AtspiAccessible> &accessibleObj, AtspiRelationType searchType,
-                                                               AsyncCallback<std::shared_ptr<AtspiAccessible>> callback) const
+void Atspi::getScreenPosition(const AtspiComponentPtr &accessibleObj, AsyncCallback<Rectangle> callback) const
+{
+       callFunction<DBus::ValueOrError<std::tuple<int32_t, int32_t, int32_t, int32_t>>(uint32_t)>(
+                               eldbusConnection,
+                               accessibleObj,
+                               dbusLocators::atspi::GET_EXTENTS,
+       [ = ](DBus::ValueOrError<std::tuple<int32_t, int32_t, int32_t, int32_t>> val) {
+               if (!val) {
+                       callback(val.getError());
+               } else {
+                       auto &p = std::get<0>(val);
+                       callback(Rectangle{ { std::get<0>(p), std::get<1>(p) }, { std::get<2>(p), std::get<3>(p) } });
+               }
+       }, (uint32_t)ATSPI_COORD_TYPE_SCREEN);
+
+}
+
+void Atspi::getObjectInRelation(const AtspiAccessiblePtr &accessibleObj, AtspiRelationType searchType,
+                                                               AsyncCallback<AtspiAccessiblePtr> callback) const
 {
        auto obj = accessibleObj;
        getRelationSet(accessibleObj,
@@ -609,11 +705,8 @@ void Atspi::getObjectInRelation(const std::shared_ptr<AtspiAccessible> &accessib
                        return;
                }
 
-               DEBUG("Relations found: %d", std::get<0>(relations).size());
                for (auto r : std::get<0>(relations)) {
-                       DEBUG("Relation type: %d", r. type);
                        if (r.type == searchType) {
-                               DEBUG("Searched relation found");
                                auto objInRelation = r.targets[0];
                                callback(std::move(objInRelation));
                                return;
@@ -625,7 +718,7 @@ void Atspi::getObjectInRelation(const std::shared_ptr<AtspiAccessible> &accessib
 
 namespace
 {
-       void getValueTemplateFunction(const EldbusConnectionCallbackHandle &eldbusConnection, const std::shared_ptr<AtspiValue> &valueInterface, const std::string paramName,
+       void getValueTemplateFunction(const EldbusConnectionCallbackHandle &eldbusConnection, const AtspiValuePtr &valueInterface, const std::string paramName,
                                                                  AsyncCallback<double> callback)
        {
                getProperty<double()>(
@@ -636,30 +729,30 @@ namespace
        }
 }
 
-void Atspi::getCurrentValue(const std::shared_ptr<AtspiValue> &valueInterface, AsyncCallback<double> callback) const
+void Atspi::getCurrentValue(const AtspiValuePtr &valueInterface, AsyncCallback<double> callback) const
 {
-       getValueTemplateFunction(eldbusConnection, valueInterface, "CurrentValue", std::move(callback));
+       getValueTemplateFunction(eldbusConnection, valueInterface, dbusLocators::atspi::CURRENT_VALUE, std::move(callback));
 }
 
-void Atspi::setCurrentValue(const std::shared_ptr<AtspiValue> &valueInterface, double newValue, AsyncCallback<void> callback) const
+void Atspi::setCurrentValue(const AtspiValuePtr &valueInterface, double newValue, AsyncCallback<void> callback) const
 {
        setProperty<void(double)>(
                eldbusConnection,
                valueInterface,
-               "CurrentValue",
+               dbusLocators::atspi::CURRENT_VALUE,
                std::move(callback),
                newValue
        );
 }
 
-void Atspi::getMaximumValue(const std::shared_ptr<AtspiValue> &valueInterface, AsyncCallback<double> callback) const
+void Atspi::getMaximumValue(const AtspiValuePtr &valueInterface, AsyncCallback<double> callback) const
 {
-       getValueTemplateFunction(eldbusConnection, valueInterface, "MaximumValue", std::move(callback));
+       getValueTemplateFunction(eldbusConnection, valueInterface, dbusLocators::atspi::MAXIMUM_VALUE, std::move(callback));
 }
 
-void Atspi::getMinimumValue(const std::shared_ptr<AtspiValue> &valueInterface, AsyncCallback<double> callback) const
+void Atspi::getMinimumValue(const AtspiValuePtr &valueInterface, AsyncCallback<double> callback) const
 {
-       getValueTemplateFunction(eldbusConnection, valueInterface, "MinimumValue", std::move(callback));
+       getValueTemplateFunction(eldbusConnection, valueInterface, dbusLocators::atspi::MINIMUM_VALUE, std::move(callback));
 }
 
 template <typename T> void Atspi::getInterface(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::shared_ptr<T>> callback) const
@@ -667,7 +760,7 @@ template <typename T> void Atspi::getInterface(const AtspiAccessiblePtr &accessi
        callFunction<std::vector<std::string>()>(
                eldbusConnection,
                accessibleObj,
-               "GetInterfaces",
+               dbusLocators::atspi::GET_INTERFACES,
        [this, callback, accessibleObj](ValueOrError<std::vector<std::string>> val) {
                const auto interfaceName = InterfaceNameFromType<T>::interfaceName;
                if (!val) {
@@ -696,7 +789,7 @@ void Atspi::getMatchedElements(const AtspiCollectionPtr &rootObj, AtspiCollectio
 
        auto pr = DBusClient { bus, path, ATSPI_DBUS_INTERFACE_COLLECTION, eldbusConnection };
        std::get<8>(m.value) = reverse;
-       auto mm = pr.method<std::vector<AtspiAccessiblePtr>(decltype(Matcher::value), uint32_t, int32_t, bool)>("GetMatches");
+       auto mm = pr.method<std::vector<AtspiAccessiblePtr>(decltype(Matcher::value), uint32_t, int32_t, bool)>(dbusLocators::atspi::GET_MATCHES);
        mm.asyncCall(std::move(callback),
                                 std::move(m.value),
                                 (uint32_t)sortOrder,
@@ -743,7 +836,7 @@ void Atspi::getAtPoint(Point pt, CoordType type, const AtspiAccessiblePtr &root,
                                callFunction<ReturnType(int32_t, int32_t, uint32_t)>(
                                        self->atspi->eldbusConnection,
                                        ptr,
-                                       "GetNavigableAtPoint",
+                                       dbusLocators::atspi::GET_NAVIGABLE_AT_POINT,
                                [self](ReturnType value) {
                                        process(std::move(self), std::move(value));
                                },
@@ -755,47 +848,14 @@ void Atspi::getAtPoint(Point pt, CoordType type, const AtspiAccessiblePtr &root,
        callFunction<ReturnType(int32_t, int32_t, uint32_t)>(
                eldbusConnection,
                root,
-               "GetNavigableAtPoint",
+               dbusLocators::atspi::GET_NAVIGABLE_AT_POINT,
        [h](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 = ValueOrError<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",
-       [ = ](ReturnType value) {
-               if (!value) {
-                       callback(Error { value.getError() });
-               } else {
-                       std::tuple<std::vector<AcceptedObjectInfo>, Rectangle> res;
-                       auto &ois = std::get<0>(res);
-                       std::get<1>(res) = {
-                               { std::get<0>(value), std::get<1>(value) },
-                               { std::get<2>(value), std::get<3>(value) }
-                       };
-                       auto v = std::get<4>(value);
-                       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 std::shared_ptr<AtspiAccessible> &accessibleObj) const
+std::vector<std::pair<std::string, std::string>> Atspi::getAttributes(const AtspiAccessiblePtr &accessibleObj) const
 {
        EXIT_IF_NULLPTR(accessibleObj);
        GError *error = nullptr;
@@ -1085,8 +1145,10 @@ static void parseReply(std::vector<Atspi::VisibleWindowInfo> &dst, std::vector<V
 
 void Atspi::getVisibleWindowInfos(AsyncCallback<std::vector<VisibleWindowInfo>> callback)
 {
-       DBus::DBusClient dbus{ "org.enlightenment.wm", "/org/enlightenment/wm", "org.enlightenment.wm.proc", DBus::ConnectionType::SYSTEM };
-       dbus.method<std::vector<VisibleWindowsReplyType>()>("GetVisibleWinInfo").asyncCall(
+       DBus::DBusClient dbus{
+               dbusLocators::windowManager::BUS, dbusLocators::windowManager::OBJ_PATH, dbusLocators::windowManager::INTERFACE,
+               DBus::ConnectionType::SYSTEM };
+       dbus.method<std::vector<VisibleWindowsReplyType>()>(dbusLocators::windowManager::GET_VISIBLE_WIN_INFO).asyncCall(
        [ = ](DBus::ValueOrError<std::vector<VisibleWindowsReplyType>> wins) {
                if (wins) {
                        std::vector<VisibleWindowInfo> res;
index 1e8357d..55ce0dc 100644 (file)
 #include <vector>
 #include <bitset>
 #include <array>
+#include <map>
 
 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 AtspiComponentPtr = std::shared_ptr<AtspiComponent>;
 using AtspiValuePtr = std::shared_ptr<AtspiValue>;
 using AtspiEditableTextPtr = std::shared_ptr<AtspiEditableText>;
 template <typename ... T> using AsyncCallback = typename std::function<void(DBus::ValueOrError<T...>)>;
@@ -142,25 +144,21 @@ public:
        void selectChild(const AtspiSelectionPtr &accessibleObj, int index, AsyncCallback<void> callback) const;
        void getStateSet(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<StateSet> callback) const;
        void getProcessId(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<unsigned int> 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;
 
        struct AccessibilityRelation {
                uint32_t type;
-               std::vector<std::shared_ptr<AtspiAccessible>> targets;
+               std::vector<AtspiAccessiblePtr> targets;
        };
-       void getObjectInRelation(const std::shared_ptr<AtspiAccessible> &accessibleObj, AtspiRelationType searchType,
-                                                        AsyncCallback<std::shared_ptr<AtspiAccessible>> callback) const;
-       void getRelationSet(const std::shared_ptr<AtspiAccessible> &accessibleObj,
+       void getObjectInRelation(const AtspiAccessiblePtr &accessibleObj, AtspiRelationType searchType,
+                                                        AsyncCallback<AtspiAccessiblePtr> callback) const;
+       void getRelationSet(const AtspiAccessiblePtr &accessibleObj,
                                                AsyncCallback<std::vector<AccessibilityRelation>> callback) const;
-       void getCurrentValue(const std::shared_ptr<AtspiValue> &valueInterface, AsyncCallback<double> callback) const;
-       void setCurrentValue(const std::shared_ptr<AtspiValue> &valueInterface, double newValue, AsyncCallback<void> callback) const;
-       void getMaximumValue(const std::shared_ptr<AtspiValue> &valueInterface, AsyncCallback<double> callback) const;
-       void getMinimumValue(const std::shared_ptr<AtspiValue> &valueInterface, AsyncCallback<double> callback) const;
+       void getCurrentValue(const AtspiValuePtr &valueInterface, AsyncCallback<double> callback) const;
+       void setCurrentValue(const AtspiValuePtr &valueInterface, double newValue, AsyncCallback<void> callback) const;
+       void getMaximumValue(const AtspiValuePtr &valueInterface, AsyncCallback<double> callback) const;
+       void getMinimumValue(const AtspiValuePtr &valueInterface, AsyncCallback<double> callback) const;
+       void getScreenPosition(const AtspiComponentPtr &obj, AsyncCallback<Rectangle> callback) const;
 
        enum class WindowVisibility : int32_t {
                unknown = -1,
@@ -192,9 +190,29 @@ public:
        ADD_INTROSPECTION_FUNCTIONS(Selection, SELECTION);
        ADD_INTROSPECTION_FUNCTIONS(Collection, COLLECTION);
        ADD_INTROSPECTION_FUNCTIONS(Value, VALUE);
+       ADD_INTROSPECTION_FUNCTIONS(Component, COMPONENT);
        ADD_INTROSPECTION_FUNCTIONS(EditableText, EDITABLE_TEXT);
 #undef ADD_INTROSPECTION_FUNCTIONS
 
+       enum class WatchEvent {
+               activated, shown, hidden, moved, defunct,
+       };
+       using WatchCallback = std::function<void(const AtspiAccessiblePtr &obj, WatchEvent ev)>;
+       class WatchHandler
+       {
+               friend class Atspi;
+               friend class std::default_delete<WatchHandler>;
+               Atspi *atspi;
+               size_t index = 0;
+
+               WatchHandler() = default;
+               ~WatchHandler();
+               WatchHandler(const WatchHandler &) = delete;
+               WatchHandler(WatchHandler &&) = delete;
+               void operator = (const WatchHandler &) = delete;
+               void operator = (WatchHandler &&) = delete;
+       };
+       std::unique_ptr<WatchHandler> registerWatcherForAllObjects(WatchCallback callback);
 private:
        bool ConnectAtClient();
        bool DisconnectAtClient();
@@ -204,6 +222,12 @@ private:
        efl::eldbus::eldbus_init eldbus;
        efl::eldbus::connection connection;
        DBus::EldbusConnectionCallbackHandle eldbusConnection;
+       AtspiEventListener *eventListener;
+       std::vector<WatchCallback> watchedCallbacks;
+       void onAtspiEvent(AtspiEvent *event);
+       static void onAtspiEventCb(AtspiEvent *event, void *data);
+       void removeWatcher(WatchHandler *);
+
        template <typename T> DBus::DBusClient getProxy(const std::shared_ptr<T> &obj, const std::string &interface) const;
        template <typename T> void getInterface(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::shared_ptr<T>> callback) const;
        void getInterface(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<AtspiAccessiblePtr> callback) const
index fe3ace0..098a01b 100644 (file)
@@ -14,6 +14,15 @@ struct Point {
        int y{};
 
        std::string toString() const;
+
+       bool operator == (Point p) const
+       {
+               return x == p.x && y == p.y;
+       }
+       bool operator != (Point p) const
+       {
+               return !(*this == p);
+       }
 };
 
 struct Size {
@@ -21,6 +30,19 @@ struct Size {
        int height{};
 
        std::string toString() const;
+       bool isPositive() const
+       {
+               return width > 0 && height > 0;
+       }
+
+       bool operator == (Size p) const
+       {
+               return width == p.width && height == p.height;
+       }
+       bool operator != (Size p) const
+       {
+               return !(*this == p);
+       }
 };
 
 struct Rectangle {
@@ -28,9 +50,22 @@ struct Rectangle {
        Size size;
 
        std::string toString() const;
+       bool hasPositiveSize() const
+       {
+               return size.isPositive();
+       }
 
        static Rectangle intersect(const Rectangle first, const Rectangle second);
        static Rectangle sum(const Rectangle first, const Rectangle second);
+
+       bool operator == (Rectangle p) const
+       {
+               return position == p.position && size == p.size;
+       }
+       bool operator != (Rectangle p) const
+       {
+               return !(*this == p);
+       }
 };
 
 namespace evas
index 1ed84c5..fe8b862 100644 (file)
@@ -2,9 +2,12 @@
 #include "UniversalSwitchLog.hpp"
 #include "UniversalSwitch.hpp"
 #include "Atspi.hpp"
+#include "dbusLocators.hpp"
 #include "UIElement.hpp"
 #include "DBus.hpp"
 #include "utils.hpp"
+#include "Window.hpp"
+#include "UniversalSwitchLog.hpp"
 
 #include <glib.h>
 #include <glib-object.h>
 #include <tuple>
 #include <memory>
 #include <functional>
+#include <algorithm>
+#include <unordered_set>
+#include <limits>
 
 using AtspiAccessiblePtr = std::shared_ptr<AtspiAccessible>;
-static unsigned int contextChangeIdent = 0;
+static constexpr int MaximumAllowedElements = 1000;
+
+static std::initializer_list<AtspiRole> ignoredAtspiRoles = {
+       ATSPI_ROLE_APPLICATION,
+       ATSPI_ROLE_FILLER,
+       ATSPI_ROLE_SCROLL_PANE,
+       ATSPI_ROLE_SPLIT_PANE,
+       ATSPI_ROLE_WINDOW,
+       ATSPI_ROLE_IMAGE,
+       ATSPI_ROLE_LIST,
+       ATSPI_ROLE_ICON,
+       ATSPI_ROLE_TOOL_BAR,
+       ATSPI_ROLE_REDUNDANT_OBJECT,
+       ATSPI_ROLE_COLOR_CHOOSER,
+       ATSPI_ROLE_TREE_TABLE,
+       ATSPI_ROLE_PAGE_TAB_LIST,
+       ATSPI_ROLE_PAGE_TAB,
+       ATSPI_ROLE_SPIN_BUTTON,
+       ATSPI_ROLE_INPUT_METHOD_WINDOW,
+       ATSPI_ROLE_EMBEDDED,
+       ATSPI_ROLE_INVALID,
+       ATSPI_ROLE_NOTIFICATION,
+       ATSPI_ROLE_DATE_EDITOR,
+       ATSPI_ROLE_CALENDAR,
+};
 
 NavigationInterface::~NavigationInterface()
 {
@@ -48,7 +78,16 @@ class NavigationImpl : public NavigationInterface
 public:
        NavigationImpl()
        {
-               initializeContextSwitchTracking();
+#ifdef DEBUG_DEBUGS_WANTED
+               DBus::setDebugPrinter([](auto str, auto strLen) {
+                       DEBUG("%s", std::string(str, strLen).c_str());
+               });
+#endif
+               watchHandle = Singleton<UniversalSwitch>::instance().getAtspi()->registerWatcherForAllObjects(
+               [this](const auto & src, auto eventType) {
+                       this->onAtspiEvent(src, eventType);
+               });
+               initializeRebuildingContext(true);
        }
 
        ~NavigationImpl() = default;
@@ -92,20 +131,21 @@ public:
        }
        void getElementAtPoint(Point pt, std::function<void(DBus::ValueOrError<std::shared_ptr<AtspiAccessible>>)> callback) override
        {
-               if (rootObject) {
+               if (rootVisibleObject) {
                        auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
                        if (!atspi)
                                callback(DBus::Error("no atspi"));
                        else
-                               atspi->getAtPoint(pt, Atspi::CoordType::Screen, rootObject, std::move(callback));
+                               atspi->getAtPoint(pt, Atspi::CoordType::Screen, rootVisibleObject, std::move(callback));
                } else {
                        callback(DBus::Error("no root object"));
                }
        }
 
 private:
-       std::shared_ptr<AtspiAccessible> lastActiveWindow, topWindow, rootObject;
+       std::shared_ptr<AtspiAccessible> rootObject, rootVisibleObject;
        std::vector<std::shared_ptr<AtspiAccessible>> modalWindows;
+       std::unique_ptr<Atspi::WatchHandler> watchHandle;
 
        struct GObjectUnref {
                template <typename T> void operator()(T *t)
@@ -117,8 +157,11 @@ private:
 
        static Optional<unsigned int> getFocusProcessPid()
        {
-               auto ed = DBus::DBusClient{ "org.enlightenment.wm", "/org/enlightenment/wm", "org.enlightenment.wm.proc", DBus::ConnectionType::SYSTEM };
-               auto m = ed.method<int()>("GetFocusProc");
+               auto ed = DBus::DBusClient{
+                       dbusLocators::windowManager::BUS, dbusLocators::windowManager::OBJ_PATH, dbusLocators::windowManager::INTERFACE,
+                       DBus::ConnectionType::SYSTEM };
+
+               auto m = ed.method<int()>(dbusLocators::windowManager::GET_FOCUS_PROC);
                auto val = m.call();
                if (!val) {
                        ERROR("failed to get pid");
@@ -132,40 +175,84 @@ private:
                return std::get<0>(val);
        }
 
-       struct row_info {
+       struct RowInfo {
                unsigned int first, last;
                Rectangle pos;
        };
 
-       struct object_info {
+       struct ObjectInfo {
                std::shared_ptr<AtspiAccessible> obj;
                Rectangle pos;
        };
 
-       enum class row_box_mode {
-               ROW_BOX_NONE = 0,
-               ROW_BOX_NORMAL,
-               ROW_BOX_SPECIAL,
+       struct AcceptedObjectInfo {
+               AtspiAccessiblePtr object;
+               Rectangle pos;
        };
 
-       struct navigation_rows {
-               std::vector<object_info> object_infos;
-               std::vector<row_info> row_infos;
-               Rectangle root_pos;
+       class NavigationRows
+       {
+               std::vector<ObjectInfo> object_infos;
+               std::vector<RowInfo> row_infos;
+               std::unordered_set<AtspiAccessiblePtr> knownObjects;
+               Rectangle root_pos = { { 0, 0 }, {1, 1 } };
+       public:
+               NavigationRows() = default;
+               NavigationRows(const std::vector<AcceptedObjectInfo> &objects, Rectangle root_pos) : root_pos(root_pos)
+               {
+                       object_infos.reserve(objects.size());
+                       row_infos.clear();
+
+                       RowInfo rowInfo;
+                       unsigned char new_line = 1;
+                       int prev_obj_x = 0;
 
-               unsigned int getRowCount()
+                       for (unsigned int i = 0; i < objects.size(); ++i) {
+                               auto &a = objects[i];
+                               knownObjects.insert(a.object);
+                               object_infos.push_back({});
+                               auto &objInfo = object_infos.back();
+                               objInfo.obj = std::move(a.object);
+                               objInfo.pos = a.pos;
+
+                               if (!new_line && (objInfo.pos.position.x < prev_obj_x || rowInfo.pos.position.y + rowInfo.pos.size.height <= objInfo.pos.position.y)) {
+                                       // new row
+                                       rowInfo.last = i - 1;
+                                       row_infos.push_back(rowInfo);
+                                       new_line = 1;
+                               }
+                               prev_obj_x = objInfo.pos.position.x;
+                               if (new_line) {
+                                       new_line = 0;
+                                       rowInfo.first = i;
+                                       rowInfo.pos = objInfo.pos;
+                               } else {
+                                       rowInfo.pos = Rectangle::sum(rowInfo.pos, objInfo.pos);
+                               }
+                       }
+                       if (!new_line) {
+                               rowInfo.last = (unsigned int)objects.size() - 1;
+                               row_infos.push_back(rowInfo);
+                       }
+                       debugDump();
+               }
+               bool hasObject(const AtspiAccessiblePtr &p) const
+               {
+                       return knownObjects.find(p) != knownObjects.end();
+               }
+               unsigned int getRowCount() const
                {
                        return (unsigned int)row_infos.size();
                }
-               Optional<unsigned int> getColumnCountInRow(unsigned int row)
+               Optional<unsigned int> getColumnCountInRow(unsigned int row) const
                {
                        if (row == 0 || row > getRowCount()) {
                                return {};
                        }
-                       row_info &ri = row_infos[row - 1];
+                       auto &ri = row_infos[row - 1];
                        return ri.last - ri.first + 1;
                }
-               Optional<object_info> getElement(unsigned int row, unsigned int col)
+               Optional<ObjectInfo> getElement(unsigned int row, unsigned int col) const
                {
                        if (row == 0 || col == 0 || row > getRowCount()) return {};
                        auto &ri = row_infos[row - 1];
@@ -173,7 +260,7 @@ private:
                        auto &oi = object_infos[ri.first + col - 1];
                        return oi;
                }
-               Optional<Rectangle> getRowSize(unsigned int row)
+               Optional<Rectangle> getRowSize(unsigned int row) const
                {
                        if (row == 0) {
                                DEBUG("root pos is %s", root_pos.toString().c_str());
@@ -186,12 +273,12 @@ private:
                        }
                        return {};
                }
-               bool findElement(unsigned int &row, unsigned int &col, const std::shared_ptr<AtspiAccessible> &obj)
+               bool findElement(unsigned int &row, unsigned int &col, const std::shared_ptr<AtspiAccessible> &obj) const
                {
                        for (unsigned int r = 0; r < row_infos.size(); ++r) {
-                               row_info &ri = row_infos[r];
+                               auto &ri = row_infos[r];
                                for (unsigned int c = ri.first; c <= ri.last; ++c) {
-                                       object_info &e = object_infos[c];
+                                       auto &e = object_infos[c];
                                        if (e.obj == obj) {
                                                row = r + 1;
                                                col = c - ri.first + 1;
@@ -201,77 +288,406 @@ private:
                        }
                        return false;
                }
+               friend bool operator == (const NavigationRows &l, const NavigationRows &r)
+               {
+                       if (l.getRowCount() != r.getRowCount()) return false;
+                       for (size_t y = 1; y <= l.getRowCount(); ++y) {
+                               if (*l.getColumnCountInRow(y) != *r.getColumnCountInRow(y)) return false;
+                               auto s = *l.getColumnCountInRow(y);
+                               for (size_t x = 1; x <= s; ++x) {
+                                       auto a = *l.getElement(y, x);
+                                       auto b = *r.getElement(y, x);
+                                       if (a.obj != b.obj) return false;
+                                       if (a.pos != b.pos) return false;
+                               }
+                       }
+                       return true;
+               }
+               friend bool operator != (const NavigationRows &l, const NavigationRows &r)
+               {
+                       return !(l == r);
+               }
+               void debugDump(const char *prefix = "")
+               {
+                       DEBUG("%sobjects %d", prefix, (unsigned int)object_infos.size());
+                       for (unsigned int r = 0; r < row_infos.size(); ++r) {
+                               auto &ri = row_infos[r];
+                               DEBUG("%srow %3d of %3d (size %s)", prefix, r + 1, row_infos.size(), ri.pos.toString().c_str());
+                               for (unsigned int c = ri.first; c <= ri.last; ++c) {
+                                       auto &e = object_infos[c];
+                                       DEBUG("%s    element %3d %3d = %s %s", prefix, c, c - ri.first + 1, e.pos.toString().c_str(),
+                                                 Atspi::getUniqueId(e.obj).c_str());
+                               }
+                       }
+               }
        };
 
-       navigation_rows current_rows;
+       static void filterRows(std::vector<AcceptedObjectInfo> &objects)
+       {
+               auto mainWindow = Singleton<UniversalSwitch>::instance().getMainWindow();
+               auto mainWindowDims = mainWindow->getDimensions();
+
+               size_t src = 0, dst = 0;
+               for (; src < objects.size(); ++src) {
+                       auto d = Rectangle::intersect(mainWindowDims, objects[src].pos);
+                       if (!d.hasPositiveSize()) continue;
+                       if (src + 1 < objects.size() && objects[src].pos == objects[src + 1].pos) continue;
+                       objects[dst++] = std::move(objects[src]);
+               }
+               objects.resize(dst);
+       }
+
+       static void sortWidgets(std::vector<AcceptedObjectInfo> &objects)
+       {
+               auto sort_vertically = [](const AcceptedObjectInfo & l, const AcceptedObjectInfo & r) {
+                       return l.pos.position.y < r.pos.position.y;
+               };
+               auto sort_horizontally = [](const AcceptedObjectInfo & l, const AcceptedObjectInfo & r) {
+                       return l.pos.position.x < r.pos.position.x;
+               };
+               std::sort(objects.begin(), objects.end(), sort_vertically);
+               for (size_t b = 0; b < objects.size();) {
+                       size_t e = b + 1;
+                       const auto yl = objects[b].pos.position.y;
+                       const auto hl = objects[b].pos.size.height;
+                       const auto maxPositionYForObject = yl + (hl + 3) / 4;
+                       while (e < objects.size() && objects[e].pos.position.y <= maxPositionYForObject) ++e;
+                       std::sort(objects.data() + b, objects.data() + e, sort_horizontally);
+                       b = e;
+               }
+       }
+
+       NavigationRows reconstructNavigationRows(std::vector<AcceptedObjectInfo> objects, Rectangle root_pos)
+       {
+               filterRows(objects);
+               sortWidgets(objects);
+               return NavigationRows { objects, root_pos };
+       }
+
+       NavigationRows current_rows;
        unsigned int current_row_index = 0, current_column_index = 0, current_rows_count = 0, current_row_column_count = 0;
 
-       void navigation_rows_debug_dump(const navigation_rows &nr)
+       static bool checkIfStillRunning()
        {
-               DEBUG("objects %d", (unsigned int)nr.object_infos.size());
-               for (unsigned int r = 0; r < nr.row_infos.size(); ++r) {
-                       auto &ri = nr.row_infos[r];
-                       DEBUG("row %3d of %3d (size %s)", r + 1, nr.row_infos.size(), ri.pos.toString().c_str());
-                       for (unsigned int c = ri.first; c <= ri.last; ++c) {
-                               auto &e = nr.object_infos[c];
-                               DEBUG("    element %3d %3d = %s %s", c, c - ri.first + 1, e.pos.toString().c_str(),
-                                         Atspi::getUniqueId(e.obj).c_str());
-                       }
+               auto a = Singleton<UniversalSwitch>::instance().getAtspi();
+               if (!a) {
+                       ERROR("no atspi");
+                       return false;
                }
+               auto n = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
+               if (!n) {
+                       ERROR("no navigation");
+                       return false;
+               }
+               return true;
        }
 
-       void createNavigationRows(const std::shared_ptr<AtspiAccessible> &root, std::function<void(Optional<navigation_rows>)> callback)
+       template <typename Callback> static bool checkIfStillRunningOtherwiseCallCallback(const std::function<Callback> &callback)
        {
-               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-               if (!atspi) {
+               if (!checkIfStillRunning()) {
                        callback({});
-                       return;
+                       return false;
+               }
+               return true;
+       }
+
+       template <typename Callback, typename ... Elem> static bool checkIfStillRunningOtherwiseCallCallback(const std::function<Callback> &callback, const DBus::ValueOrError<Elem...> &val)
+       {
+               if (!val) {
+                       ERROR("failed %s", val.getError().message.c_str());
+                       callback({});
+                       return false;
+               }
+               return checkIfStillRunningOtherwiseCallCallback(callback);
+       }
+
+       struct Empty { };
+       template <typename Elem, typename ALL = Empty> struct ParallerExecHelper {
+       public:
+               using CallbackType = std::function<void(ParallerExecHelper<Elem, ALL> &)>;
+
+               std::vector<Elem> elems;
+               ALL all;
+
+               ParallerExecHelper() = delete;
+               ParallerExecHelper(CallbackType callback, size_t size = 0) : elems(size), callback(std::move(callback)) { }
+               ~ParallerExecHelper()
+               {
+                       callback(*this);
                }
-               atspi->getAllAcceptedObjects(root,
-               [ = ](DBus::ValueOrError<std::tuple<std::vector<Atspi::AcceptedObjectInfo>, Rectangle>> value_) {
-                       if (value_) {
-                               navigation_rows navState;
-                               auto value = std::get<0>(value_);
-                               auto &objects = std::get<0>(value);
-                               navState.root_pos = std::get<1>(value);
-                               navState.object_infos.reserve(objects.size());
-                               navState.row_infos.clear();
-
-                               row_info rowInfo;
-                               unsigned char new_line = 1;
-                               int prev_obj_x = 0;
-
-                               for (unsigned int i = 0; i < objects.size(); ++i) {
-                                       auto &a = objects[objects.size() - 1 - i];
-                                       navState.object_infos.push_back({});
-                                       auto &objInfo = navState.object_infos.back();
-                                       objInfo.obj = std::move(a.object);
-                                       objInfo.pos = a.pos;
-
-                                       if (!new_line && (objInfo.pos.position.x < prev_obj_x || rowInfo.pos.position.y + rowInfo.pos.size.height <= objInfo.pos.position.y)) {
-                                               // new row
-                                               rowInfo.last = i - 1;
-                                               navState.row_infos.push_back(rowInfo);
-                                               new_line = 1;
+       private:
+               CallbackType callback;
+       };
+
+       template <typename ... ARGS> bool continueProcessingImpl(const char *filename, int fileline, const DBus::ValueOrError<ARGS...> &args)
+       {
+               if (!args) {
+                       ERROR("in %s:%d: %s", filename, fileline, args.getError().message.c_str());
+                       return false;
+               }
+               return checkIfStillRunning();
+       }
+#define continueProcessing(args) continueProcessingImpl(__FILE__, __LINE__, args)
+
+       void createNavigationRowsFromElements(
+               const AtspiAccessiblePtr &root,
+               const AtspiAccessiblePtr &visibleRoot,
+               AtspiCollectionPtr collection,
+               std::vector<AtspiAccessiblePtr> &elements,
+               std::function<void(Optional<NavigationRows>)> callback)
+       {
+               DEBUG("my root is %s", Atspi::getUniqueId(root).c_str());
+               DEBUG("got %d elements", elements.size());
+               for (size_t i = 0; i < elements.size(); ++i)
+                       DEBUG("got element %2d     %s", (unsigned int)i, Atspi::getUniqueId(elements[i]).c_str());
+               struct Elem {
+                       AtspiAccessiblePtr obj, parent;
+                       Rectangle pos;
+                       bool hasRelation = false, isItem = false, isCollapsed = false, isDisabled = false;
+               };
+               struct All {
+                       std::vector<AtspiAccessiblePtr> expandableElems, expandableAndExpandedElems;
+                       std::unordered_map<AtspiAccessiblePtr, size_t> ptrToIndex;
+                       Optional<Rectangle> rootPos, rootPos2;
+
+                       bool hasError = false;
+               };
+               using ExecHelper = ParallerExecHelper<Elem, All>;
+
+               auto updateIsCollapsed = [](ExecHelper & result) {
+                       // parent is not collapsed, when it's expandbable, but not expanded, so it must be in
+                       // expandableElems (e1), but not in expandableAndExpandedElems (e2)
+                       auto &e1 = result.all.expandableElems;
+                       auto &e2 = result.all.expandableAndExpandedElems;
+
+                       std::unordered_map<AtspiAccessiblePtr, size_t> parentPtrToIndex;
+                       for (size_t i = 0; i < result.elems.size(); ++i) {
+                               auto &e = result.elems[i];
+                               if (e.parent) parentPtrToIndex[e.parent] = i;
+                       }
+                       std::sort(result.all.expandableElems.begin(), result.all.expandableElems.end());
+                       std::sort(result.all.expandableAndExpandedElems.begin(), result.all.expandableAndExpandedElems.end());
+                       for (size_t i1 = 0, i2 = 0; i1 < e1.size(); ++i1) {
+                               while (i2 < e2.size() && e1[i1] > e2[i2]) ++i2;
+                               if (i2 == e2.size() || e1[i1] != e2[i2]) {
+                                       auto it = parentPtrToIndex.find(e1[i1]);
+                                       if (it != parentPtrToIndex.end())
+                                               result.elems[it->second].isCollapsed = true;
+                               }
+                       }
+               };
+               auto clb = [callback, updateIsCollapsed](ExecHelper & result) {
+                       auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
+                       if (nav) {
+                               DEBUG("finalizing navigation");
+                               updateIsCollapsed(result);
+                               for (auto &inf : result.elems) {
+                                       DEBUG("elem %30s pos %s relation %d item %d collapsed %d", Atspi::getUniqueId(inf.obj).c_str(),
+                                                 inf.pos.toString().c_str(), inf.hasRelation ? 1 : 0, inf.isItem ? 1 : 0, inf.isCollapsed ? 1 : 0);
+                               }
+                               std::vector<AcceptedObjectInfo> objects;
+                               objects.reserve(result.elems.size());
+                               for (size_t i = 0; i < result.elems.size(); ++i) {
+                                       if (result.elems[i].obj && result.elems[i].pos.size.width > 0 && result.elems[i].pos.size.height > 0)
+                                               objects.push_back({ result.elems[i].obj, result.elems[i].pos });
+                               }
+                               auto &r = result.all.rootPos ? *result.all.rootPos : *result.all.rootPos2;
+                               callback(nav->reconstructNavigationRows(objects, r));
+                       } else {
+                               DEBUG("no navigation");
+                               callback({});
+                       }
+               };
+               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+
+               auto sizes = std::make_shared<ExecHelper>(clb, elements.size());
+               auto queryChildWithGivenRoleAndChildrenCount = [ = ](const AtspiAccessiblePtr & parent, size_t index, size_t expectedCount,
+               AtspiRole expectedRole, std::function<void(AtspiAccessiblePtr elem)> callback) {
+                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                       if (atspi) {
+                               atspi->getChildrenCount(parent, [ = ](DBus::ValueOrError<size_t> count) {
+                                       if (continueProcessing(count)) {
+                                               if (std::get<0>(count) != expectedCount) {
+                                                       callback(nullptr);
+                                               } else {
+                                                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                                                       atspi->getChildAtIndex(parent, index, [ = ](DBus::ValueOrError<AtspiAccessiblePtr> child) {
+                                                               if (continueProcessing(child)) {
+                                                                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                                                                       atspi->getRole(std::get<0>(child), [ = ](DBus::ValueOrError<AtspiRole> role) {
+                                                                               if (continueProcessing(role)) {
+                                                                                       if (std::get<0>(role) != expectedRole)
+                                                                                               callback(nullptr);
+                                                                                       else
+                                                                                               callback(std::get<0>(child));
+                                                                               } else {
+                                                                                       sizes->all.hasError = true;
+                                                                               }
+                                                                       });
+                                                               } else {
+                                                                       sizes->all.hasError = true;
+                                                               }
+                                                       });
+                                               }
+                                       } else {
+                                               sizes->all.hasError = true;
                                        }
-                                       prev_obj_x = objInfo.pos.position.x;
-                                       if (new_line) {
-                                               new_line = 0;
-                                               rowInfo.first = i;
-                                               rowInfo.pos = objInfo.pos;
+                               });
+                       }
+               };
+               auto updateTotalSize = [ = ](AtspiAccessiblePtr elem, bool first) {
+                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                       if (atspi) {
+                               atspi->getComponentInterface(elem, [ = ](DBus::ValueOrError<AtspiComponentPtr> col) {
+                                       if (continueProcessing(col)) {
+                                               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                                               atspi->getScreenPosition(std::get<0>(col), [ = ](DBus::ValueOrError<Rectangle> pos) {
+                                                       if (continueProcessing(pos)) {
+                                                               if (first)
+                                                                       sizes->all.rootPos = std::get<0>(pos);
+                                                               else
+                                                                       sizes->all.rootPos2 = std::get<0>(pos);
+                                                       } else {
+                                                               sizes->all.hasError = true;
+                                                       }
+                                               });
                                        } else {
-                                               rowInfo.pos = Rectangle::sum(rowInfo.pos, objInfo.pos);
+                                               sizes->all.hasError = true;
+                                       }
+                               });
+                       } else {
+                               DEBUG("no atspi");
+                       }
+               };
+               atspi->getRole(visibleRoot, [ = ](DBus::ValueOrError<AtspiRole> role) {
+                       if (continueProcessing(role)) {
+                               auto callback = [ = ](AtspiAccessiblePtr c1) {
+                                       if (c1) {
+                                               queryChildWithGivenRoleAndChildrenCount(c1, 0, 1, ATSPI_ROLE_NOTIFICATION, [ = ](AtspiAccessiblePtr c2) {
+                                                       if (c2) {
+                                                               queryChildWithGivenRoleAndChildrenCount(c2, 1, 2, ATSPI_ROLE_FILLER, [ = ](AtspiAccessiblePtr c3) {
+                                                                       if (c3) {
+                                                                               updateTotalSize(c3, true);
+                                                                       }
+                                                               });
+                                                       }
+                                               });
                                        }
+                               };
+                               if (std::get<0>(role) == ATSPI_ROLE_WINDOW) {
+                                       queryChildWithGivenRoleAndChildrenCount(visibleRoot, 0, 1, ATSPI_ROLE_DIALOG, std::move(callback));
+                               } else {
+                                       callback(visibleRoot);
                                }
-                               if (!new_line) {
-                                       rowInfo.last = (unsigned int)objects.size() - 1;
-                                       navState.row_infos.push_back(rowInfo);
+                       } else {
+                               sizes->all.hasError = true;
+                       }
+               });
+               updateTotalSize(visibleRoot, false);
+
+               for (size_t i = 0; i < elements.size(); ++i) {
+                       auto &e = elements[i];
+                       sizes->elems[i].obj = e;
+                       sizes->all.ptrToIndex[e] = i;
+               }
+
+               atspi->getMatchedElements(collection, ATSPI_Collection_SORT_ORDER_CANONICAL, MaximumAllowedElements,
+                                                                 Atspi::Matcher().states({ ATSPI_STATE_VISIBLE, ATSPI_STATE_SHOWING, ATSPI_STATE_HIGHLIGHTABLE, ATSPI_STATE_ENABLED, ATSPI_STATE_EXPANDABLE }, ATSPI_Collection_MATCH_ALL).
+                                                                 roles(ignoredAtspiRoles, ATSPI_Collection_MATCH_NONE),
+               false, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elems) {
+                       if (continueProcessing(elems))
+                               sizes->all.expandableElems = std::move(std::get<0>(elems));
+                       else
+                               sizes->all.hasError = true;
+               });
+               atspi->getMatchedElements(collection, ATSPI_Collection_SORT_ORDER_CANONICAL, MaximumAllowedElements,
+                                                                 Atspi::Matcher().states({ ATSPI_STATE_VISIBLE, ATSPI_STATE_SHOWING, ATSPI_STATE_HIGHLIGHTABLE, ATSPI_STATE_ENABLED, ATSPI_STATE_EXPANDABLE, ATSPI_STATE_EXPANDED }, ATSPI_Collection_MATCH_ALL).
+                                                                 roles(ignoredAtspiRoles, ATSPI_Collection_MATCH_NONE),
+               false, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elems) {
+                       if (continueProcessing(elems))
+                               sizes->all.expandableAndExpandedElems = std::move(std::get<0>(elems));
+                       else
+                               sizes->all.hasError = true;
+               });
+               atspi->getMatchedElements(collection, ATSPI_Collection_SORT_ORDER_CANONICAL, MaximumAllowedElements,
+                                                                 Atspi::Matcher().states({ ATSPI_STATE_VISIBLE, ATSPI_STATE_SHOWING, ATSPI_STATE_HIGHLIGHTABLE, ATSPI_STATE_ENABLED }, ATSPI_Collection_MATCH_ALL).
+                                                                 roles({ ATSPI_ROLE_LIST_ITEM, ATSPI_ROLE_MENU_ITEM }, ATSPI_Collection_MATCH_ANY),
+               false, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elems) {
+                       if (continueProcessing(elems)) {
+                               for (auto &e : std::get<0>(elems)) {
+                                       auto indexIt = sizes->all.ptrToIndex.find(e);
+                                       if (indexIt != sizes->all.ptrToIndex.end())
+                                               sizes->elems[indexIt->second].isItem = true;
                                }
-                               navigation_rows_debug_dump(navState);
-                               callback(std::move(navState));
                        } else {
-                               ERROR("failed %s", value_.getError().message.c_str());
-                               callback({});
+                               sizes->all.hasError = true;
+                       }
+               });
+
+               for (size_t i = 0; i < elements.size(); ++i) {
+                       auto &e = elements[i];
+                       atspi->getComponentInterface(e, [ = ](DBus::ValueOrError<AtspiComponentPtr> com) {
+                               if (continueProcessing(com)) {
+                                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                                       atspi->getScreenPosition(std::get<0>(com), [ = ](DBus::ValueOrError<Rectangle> val) {
+                                               if (continueProcessing(val))
+                                                       sizes->elems[i].pos = std::get<0>(val);
+                                               else
+                                                       sizes->all.hasError = true;
+                                       });
+                               } else {
+                                       sizes->all.hasError = true;
+                               }
+                       });
+                       atspi->getObjectInRelation(e, ATSPI_RELATION_CONTROLLED_BY, [ = ](DBus::ValueOrError<AtspiAccessiblePtr> obj) {
+                               if (continueProcessing(obj))
+                                       sizes->elems[i].hasRelation = std::get<0>(obj) != nullptr;
+                               else
+                                       sizes->all.hasError = true;
+                       });
+                       atspi->getParent(e, [ = ](DBus::ValueOrError<AtspiAccessiblePtr> parent) {
+                               if (continueProcessing(parent))
+                                       sizes->elems[i].parent = std::move(std::get<0>(parent));
+                               else
+                                       sizes->all.hasError = true;
+                       });
+               }
+       }
+
+       void createNavigationRowsNew(const std::shared_ptr<AtspiAccessible> &root, const std::shared_ptr<AtspiAccessible> &visibleRoot, std::function<void(Optional<NavigationRows>)> callback)
+       {
+               if (!checkIfStillRunningOtherwiseCallCallback(callback)) return;
+               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+               atspi->getCollectionInterface(root, [ = ](DBus::ValueOrError<AtspiCollectionPtr> col) {
+                       if (checkIfStillRunningOtherwiseCallCallback(callback, col)) {
+                               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                               atspi->getMatchedElements(std::get<0>(col), ATSPI_Collection_SORT_ORDER_CANONICAL, MaximumAllowedElements,
+                                                                                 Atspi::Matcher()
+                                                                                 .states({ ATSPI_STATE_VISIBLE, ATSPI_STATE_SHOWING, ATSPI_STATE_HIGHLIGHTABLE, ATSPI_STATE_ENABLED }, ATSPI_Collection_MATCH_ALL)
+                                                                                 .roles(ignoredAtspiRoles, ATSPI_Collection_MATCH_NONE)
+                                                                                 ,
+                               false, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elems) {
+                                       if (!checkIfStillRunningOtherwiseCallCallback(callback, elems)) return;
+                                       auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
+                                       nav->createNavigationRowsFromElements(root, visibleRoot, std::get<0>(col), std::get<0>(elems), callback);
+                               });
+                       }
+               });
+       }
+
+       void createNavigationRows(const std::shared_ptr<AtspiAccessible> &root, const std::shared_ptr<AtspiAccessible> &visibleRoot, std::function<void(Optional<NavigationRows>)> callback)
+       {
+               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+               if (!atspi) {
+                       callback({});
+                       return;
+               }
+               createNavigationRowsNew(root, visibleRoot,
+               [ = ](Optional<NavigationRows> nr) {
+                       if (!nr) {
+                               ERROR("failed");
+                       } else {
+                               callback(std::move(nr));
                        }
                });
        }
@@ -282,29 +698,24 @@ private:
                current_row_index = current_column_index = 0;
        }
 
-       void screenNavigationContextChanged(unsigned int currentContextChangeIdent, const std::shared_ptr<AtspiAccessible> &root)
+       void screenNavigationContextChanged(const AtspiAccessiblePtr &root, const AtspiAccessiblePtr &visibleRoot)
        {
                DEBUG("changing context");
 
                current_rows_count = current_row_column_count = 0;
                resetIndexes();
 
-               createNavigationRows(root,
-               [ = ](Optional<navigation_rows> rows) {
-                       if (currentContextChangeIdent != contextChangeIdent) {
-                               DEBUG("ignoring, as something else took over - rootObject has changed");
-                               return;
-                       }
+               createNavigationRows(root, visibleRoot,
+               [ = ](Optional<NavigationRows> rows) {
                        if (rows) {
                                current_rows = std::move(*rows);
                                current_rows_count = current_rows.getRowCount();
                        } else {
                                ERROR("failed to get navigation rows");
-                               current_rows.root_pos = { { 0, 0 }, {1, 1 } };
                        }
 
                        auto ui = std::make_shared<UIElement>(root);
-                       emitCallback<NavigationCallbackType::ContextChanged>(ui, current_rows.root_pos);
+                       emitCallback<NavigationCallbackType::ContextChanged>(ui, *current_rows.getRowSize(0));
 
                        emitCallback<NavigationCallbackType::BoxMoved>(Rectangle{}, BoxPositionMode::NONE);
                        DEBUG("context has %d rows", current_rows_count);
@@ -376,252 +787,274 @@ private:
 
        ecore::Timer restartTimer;
 
-       void rebuildContextFromZero()
+       void rebuildContextFromZero(bool force)
        {
-               auto rebuildContextCallback = [](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elems) {
-                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-                       if (!atspi) return;
-                       ASSERT(elems);
-                       auto &el = std::get<0>(elems);
-                       ASSERT(!el.empty());
-                       DEBUG("got %d elements", (unsigned int)el.size());
-                       for (auto i = 0u; i < el.size(); ++i) {
-                               auto n = atspi->getName(el[i]);
-                               DEBUG("got element %u = %s %s", i,
-                                         Atspi::getUniqueId(el[i]).c_str(),
-                                         (n ? (*n).c_str() : "")
-                                        );
-                       }
-                       auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
-                       if (nav) {
-                               nav->buildContextForRoot(el[el.size() - 1]);
-                       }
-               };
-
                auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-               if (!atspi) return;
-
-               auto currentPid = getpid();
-               auto focusedPid = getFocusProcessPid();
-               auto desktop = atspi->getDesktop();
-               ASSERT(desktop);
+               if (!atspi) {
+                       DEBUG("no atspi");
+                       return;
+               }
 
                auto checkIfHasChildren = [ = ](AtspiAccessiblePtr root, std::function<void(bool)> callback) {
                        auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-                       if (!atspi) return;
                        atspi->getChildrenCount(root, [ = ](DBus::ValueOrError<size_t> count) {
-                               if (!count) {
-                                       ERROR("failed %s", count.getError().message.c_str());
-                                       return;
-                               }
-                               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-                               if (!atspi) return;
-                               DEBUG("elem %s has %d children", Atspi::getUniqueId(root).c_str(), count ? (int)std::get<0>(count) : -1);
-                               if (std::get<0>(count) == 0) {
-                                       callback(false);
-                               } else {
-                                       atspi->getChildAtIndex(root, 0, [ = ](DBus::ValueOrError<AtspiAccessiblePtr> child) {
-                                               if (!child) {
-                                                       ERROR("failed %s", count.getError().message.c_str());
-                                                       return;
-                                               }
-                                               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-                                               if (!atspi) return;
-
-                                               atspi->getChildrenCount(std::get<0>(child), [ = ](DBus::ValueOrError<size_t> count) {
-                                                       if (!count) {
-                                                               ERROR("failed %s", count.getError().message.c_str());
-                                                               return;
+                               if (continueProcessing(count)) {
+                                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                                       DEBUG("elem %s has %d children", Atspi::getUniqueId(root).c_str(), count ? (int)std::get<0>(count) : -1);
+                                       if (std::get<0>(count) == 0) {
+                                               callback(false);
+                                       } else {
+                                               atspi->getChildAtIndex(root, 0, [ = ](DBus::ValueOrError<AtspiAccessiblePtr> child) {
+                                                       if (continueProcessing(child)) {
+                                                               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                                                               atspi->getChildrenCount(std::get<0>(child), [ = ](DBus::ValueOrError<size_t> count) {
+                                                                       if (continueProcessing(count)) {
+                                                                               DEBUG("elem %s has %d children", Atspi::getUniqueId(root).c_str(), count ? (int)std::get<0>(count) : -1);
+                                                                               callback(std::get<0>(count) > 0);
+                                                                       } else {
+                                                                               callback(false);
+                                                                       }
+                                                               });
+                                                       } else {
+                                                               callback(false);
                                                        }
-                                                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-                                                       if (!atspi) return;
-                                                       DEBUG("elem %s has %d children", Atspi::getUniqueId(root).c_str(), count ? (int)std::get<0>(count) : -1);
-                                                       callback(std::get<0>(count) > 0);
                                                });
-                                       });
+                                       }
+                               } else {
+                                       callback(false);
                                }
                        });
                };
                auto processProcess = [ = ](AtspiAccessiblePtr app) {
-                       DEBUG("process %s", Atspi::getUniqueId(app).c_str());
-                       if (!app) return;
-
                        auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-                       if (!atspi) return;
-
+                       ASSERT(atspi);
                        atspi->getCollectionInterface(app, [ = ](DBus::ValueOrError<AtspiCollectionPtr> col) {
-                               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-                               if (!atspi) return;
-                               if (!col) {
-                                       ERROR("failed %s", col.getError().message.c_str());
-                               } else {
+                               if (continueProcessing(col)) {
+                                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
                                        atspi->getMatchedElements(
                                                std::get<0>(col),
-                                               ATSPI_Collection_SORT_ORDER_CANONICAL, 5,
+                                               ATSPI_Collection_SORT_ORDER_CANONICAL, MaximumAllowedElements,
                                                Atspi::Matcher().states({ATSPI_STATE_MODAL, ATSPI_STATE_SHOWING, ATSPI_STATE_VISIBLE}, ATSPI_Collection_MATCH_ALL),
                                                false,
                                        [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elems) {
-                                               if (!elems) {
-                                                       ERROR("error %s", elems.getError().message.c_str());
-                                                       return;
-                                               }
-                                               if (std::get<0>(elems).empty()) {
-                                                       DEBUG("no modal window found");
-                                                       atspi->getMatchedElements(
-                                                               std::get<0>(col),
-                                                               ATSPI_Collection_SORT_ORDER_CANONICAL, 5,
-                                                               Atspi::Matcher().states({ATSPI_STATE_SHOWING, ATSPI_STATE_VISIBLE}, ATSPI_Collection_MATCH_ALL).
-                                                               roles({ ATSPI_ROLE_DIALOG, ATSPI_ROLE_FRAME, ATSPI_ROLE_WINDOW,  /*, ATSPI_ROLE_POPUP_MENU ? */ }, ATSPI_Collection_MATCH_ANY),
-                                                               false,
-                                                               rebuildContextCallback);
-                                               } else {
-                                                       DEBUG("modal window found");
-                                                       rebuildContextCallback(std::move(elems));
+                                               if (continueProcessing(elems)) {
+                                                       auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
+                                                       ASSERT(nav);
+                                                       if (std::get<0>(elems).empty()) {
+                                                               DEBUG("no modal window found");
+
+                                                               atspi->getMatchedElements(
+                                                                       std::get<0>(col),
+                                                                       ATSPI_Collection_SORT_ORDER_CANONICAL, 5,
+                                                                       Atspi::Matcher().states({ATSPI_STATE_SHOWING, ATSPI_STATE_VISIBLE}, ATSPI_Collection_MATCH_ALL).
+                                                                       roles({ ATSPI_ROLE_DIALOG, ATSPI_ROLE_FRAME, ATSPI_ROLE_WINDOW, ATSPI_ROLE_PAGE_TAB, /*, ATSPI_ROLE_POPUP_MENU ? */ }, ATSPI_Collection_MATCH_ANY),
+                                                                       false,
+                                                               [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elems) {
+                                                                       if (continueProcessing(elems)) {
+                                                                               auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
+                                                                               ASSERT(nav);
+                                                                               ASSERT(!std::get<0>(elems).empty());
+                                                                               nav->buildContextForRoot(app, std::get<0>(elems)[0], force);
+                                                                       }
+                                                               });
+                                                       } else {
+                                                               auto &el = std::get<0>(elems);
+                                                               auto modal = el[el.size() - 1];
+                                                               DEBUG("modal window %s selected", Atspi::getUniqueId(modal).c_str());
+
+                                                               struct ElementNames {
+                                                                       std::vector<AtspiAccessiblePtr> elems;
+                                                                       std::vector<std::string> names;
+
+                                                                       ~ElementNames()
+                                                                       {
+                                                                               DEBUG("got %d modal windows", (unsigned int)elems.size());
+                                                                               for (unsigned int i = 0; i < elems.size(); ++i)
+                                                                                       DEBUG("got modal window %u = %s %s", i, Atspi::getUniqueId(elems[i]).c_str(), names[i].c_str());
+                                                                       }
+                                                               };
+                                                               auto t = std::make_shared<ElementNames>();
+                                                               t->elems = std::move(std::get<0>(elems));
+                                                               t->names.resize(t->elems.size());
+                                                               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                                                               for (auto i = 0u; i < t->elems.size(); ++i) {
+                                                                       atspi->getName(t->elems[i], [ = ](DBus::ValueOrError<std::string> name) {
+                                                                               if (name) t->names[i] = std::move(std::get<0>(name));
+                                                                       });
+                                                               }
+
+                                                               nav->buildContextForRoot(modal, modal, force);
+                                                       }
                                                }
                                        });
                                }
                        });
                };
-               atspi->getChildren(desktop,
-               [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> desktopChildren) {
-                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-                       if (!atspi) return;
-
-                       if (!desktopChildren) {
-                               DEBUG("failed to get children, trying again soon");
+               struct All {
+                       std::unordered_map<int32_t, AtspiAccessiblePtr> pidToAtspi;
+                       std::unordered_map<AtspiAccessiblePtr, int32_t> atspiToPid;
+                       std::unordered_map<AtspiAccessiblePtr, std::string> names;
+                       int32_t ourPid;
+                       std::vector<int32_t> pids;
+                       bool weHaveChildren = false;
+               };
+               using ExecHelper = ParallerExecHelper<Empty, All>;
+               auto windowsParser = [ = ](ExecHelper & wins) {
+                       for (auto it : wins.all.names) {
+                               auto id = Atspi::getUniqueId(it.first);
+                               auto pid = wins.all.atspiToPid[it.first];
+                               DEBUG("got %6d   %s %s", pid, id.c_str(), it.second.c_str());
+                       }
+                       for (auto pid : wins.all.pids) {
+                               DEBUG("got pid %d", pid);
+                       }
+                       AtspiAccessiblePtr root;
+                       for (auto pid : wins.all.pids) {
+                               DEBUG("checking pid %d", pid);
+                               auto ptr = wins.all.pidToAtspi[pid];
+                               auto name = wins.all.names[ptr];
+                               if ((pid == wins.all.ourPid && wins.all.weHaveChildren) || name == "quickpanel" || name == "volume" || pid == wins.all.pids.back()) {
+                                       root = std::move(ptr);
+                                       break;
+                               }
+                       }
+                       if (root) {
+                               processProcess(root);
+                       } else {
                                auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
                                if (nav) {
-                                       nav->restartTimer.reset(5.0f, [] {
-                                               auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
-                                               if (nav)
-                                               {
-                                                       nav->rebuildContextFromZero();
-                                               }
-                                               return ecore::TimerRepetitionPolicy::cancel;
-                                       });
+                                       DEBUG("restarting context navigation, as selected window was lost");
+                                       nav->initializeRebuildingContext(force, 0.2f);
+                               } else {
+                                       DEBUG("no navigation");
                                }
-                               return;
                        }
-                       struct Data {
-                               Optional<AtspiAccessiblePtr> us, focused;
-                               size_t todo;
-                       };
-                       auto data = std::make_shared<Data>();
-                       auto processData = [ = ]() {
-                               if (data->us) {
-                                       checkIfHasChildren(*data->us, [ = ](bool weHaveChildren) {
-                                               processProcess(weHaveChildren ? *data->us : (data->focused ? *data->focused : nullptr));
-                                       });
-                               } else if (data->focused)
-                                       processProcess(*data->focused);
-                       };
-                       data->todo = std::get<0>(desktopChildren).size();
-                       for (auto app : std::get<0>(desktopChildren)) {
-                               if (!app) {
-                                       --data->todo;
-                                       if (data->todo == 0)
-                                               processData();
-                                       continue;
+               };
+               atspi->getVisibleWindowInfos([ = ](DBus::ValueOrError<std::vector<Atspi::VisibleWindowInfo>> wins_) {
+                       if (continueProcessing(wins_)) {
+                               auto &wins = std::get<0>(wins_);
+                               std::vector<int> pids;
+                               {
+                                       bool foundFocused = false;
+                                       for (auto &w : wins) {
+                                               bool visibility =
+                                                       w.visibility == Atspi::WindowVisibility::unobscured ||
+                                                       w.visibility == Atspi::WindowVisibility::partially_obscured ||
+                                                       w.focused;
+                                               DEBUG("got pid %6d visibility %d (%2d %d)", w.pid, visibility ? 1 : 0, (int)w.visibility, (int)w.focused);
+                                               if (visibility) {
+                                                       if (!foundFocused)
+                                                               pids.push_back(w.pid);
+                                                       if (w.focused) {
+                                                               foundFocused = true;
+                                                       }
+                                               }
+                                       }
                                }
-                               atspi->getProcessId(app,
-                               [ = ](DBus::ValueOrError<unsigned int> pid) {
-                                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-                                       if (!atspi) return;
-                                       if (!pid) {
-                                               DEBUG("error %s with root %s", pid.getError().message.c_str(), Atspi::getUniqueId(app).c_str());
-                                               return;
+
+                               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                               auto desktop = atspi->getDesktop();
+                               ASSERT(desktop);
+                               atspi->getChildren(desktop,
+                               [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> desktopChildren) {
+                                       if (continueProcessing(desktopChildren)) {
+                                               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+
+                                               auto ptr = std::make_shared<ExecHelper>(std::move(windowsParser), pids.size());
+                                               ptr->all.pids = std::move(pids);
+                                               ptr->all.ourPid = getpid();
+
+                                               for (auto app : std::get<0>(desktopChildren)) {
+                                                       if (!app) continue;
+                                                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                                                       atspi->getProcessId(app,
+                                                       [ = ](DBus::ValueOrError<unsigned int> pid) {
+                                                               if (continueProcessing(pid)) {
+                                                                       auto p = (int32_t)std::get<0>(pid);
+                                                                       ptr->all.atspiToPid[app] = p;
+                                                                       ptr->all.pidToAtspi[p] = app;
+                                                                       if (p == ptr->all.ourPid) {
+                                                                               checkIfHasChildren(app, [ = ](bool hasChildren) {
+                                                                                       ptr->all.weHaveChildren = hasChildren;
+                                                                               });
+                                                                       }
+                                                               }
+                                                       });
+                                                       atspi->getName(app, [ = ](DBus::ValueOrError<std::string> name) {
+                                                               if (continueProcessing(name))
+                                                                       ptr->all.names[app] = std::move(std::get<0>(name));
+                                                       });
+                                               }
+                                       } else {
+                                               auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
+                                               ASSERT(nav);
+                                               nav->initializeRebuildingContext(force, 5.0f);
                                        }
-                                       if (focusedPid && std::get<0>(pid) == *focusedPid) data->focused = app;
-                                       else if (std::get<0>(pid) == currentPid) data->us = app;
-                                       --data->todo;
-                                       if (data->todo == 0)
-                                               processData();
                                });
                        }
                });
        }
 
-       void buildContextForRoot(const std::shared_ptr<AtspiAccessible> &root)
+       void buildContextForRoot(const std::shared_ptr<AtspiAccessible> &root, const std::shared_ptr<AtspiAccessible> &visibleRoot, bool force)
        {
                if (root == rootObject) {
-                       DEBUG("no context switch, as object %s is already the root", Atspi::getUniqueId(root).c_str());
-                       return;
+                       if (!force) {
+                               DEBUG("no context switch, as object %s is already the root", Atspi::getUniqueId(root).c_str());
+                               return;
+                       }
+                       DEBUG("object %s is already the root, but context switch is forced", Atspi::getUniqueId(root).c_str());
                }
                rootObject = root;
-               screenNavigationContextChanged(contextChangeIdent, root);
+               rootVisibleObject = visibleRoot;
+               screenNavigationContextChanged(root, visibleRoot);
                auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
                if (atspi) {
                        atspi->getName(root,
                        [ = ](DBus::ValueOrError<std::string> name) {
                                DEBUG("new CONTEXT is %s %s", Atspi::getUniqueId(root).c_str(), name ? std::get<0>(name).c_str() : "");
                        });
+               } else {
+                       DEBUG("no atspi");
                }
        }
 
        ecore::Timer rebuildContextTimer;
 
-       void initializeRebuildingContext()
+       void initializeRebuildingContext(bool force, float delay = 0.2f)
        {
-               rebuildContextTimer.reset(0.2f,
+               DEBUG("requesting context rebuild");
+               rebuildContextTimer.reset(delay,
                [ = ]() {
                        auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
                        if (nav)
-                               nav->rebuildContextFromZero();
+                               nav->rebuildContextFromZero(force);
                        return ecore::TimerRepetitionPolicy::cancel;
                });
        }
 
-       void onAtspiWindow(const AtspiEvent *event)
+       void onAtspiEvent(const AtspiAccessiblePtr &src, Atspi::WatchEvent event)
        {
                using namespace std::literals::string_literals;
+               bool rebuild = false;
 
-               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
-               if (atspi) {
-                       auto source = atspi->make(event->source, true);
-                       auto id = atspi->getUniqueId(source);
-                       bool update = false;
-                       DEBUG("Event: %s: %s", event->type, id.c_str());
-
-                       if (event->type == "window:activate"s ||
-                                       event->type == "object:state-changed:visible"s ||
-                                       event->type == "object:state-changed:showing"s ||
-                                       event->type == "object:bounds-changed"s ||
-                                       event->type == "object:state-changed:defunct"s ||
-                                       event->type == "object:children-changed"s) {
-                               initializeRebuildingContext();
-                       }
+               switch (event) {
+               case Atspi::WatchEvent::activated:
+               case Atspi::WatchEvent::shown:
+                       rebuild = true;
+                       break;
+               case Atspi::WatchEvent::defunct:
+               case Atspi::WatchEvent::hidden:
+               case Atspi::WatchEvent::moved:
+                       if (current_rows.hasObject(src)) rebuild = true;
+                       break;
+               default:
+                       DEBUG("unknown event %d", (int)event);
+                       ASSERT(0);
                }
+               if (rebuild)
+                       initializeRebuildingContext(true);
        }
 
-       static void onAtspiWindowCb(AtspiEvent *event, void *data)
-       {
-               ((NavigationImpl *)data)->onAtspiWindow(event);
-       }
-
-       void initializeContextSwitchTracking()
-       {
-               eventListener.reset(atspi_event_listener_new(onAtspiWindowCb, this, nullptr));
-               atspi_event_listener_register(eventListener.get(), "window:activate", NULL);
-               atspi_event_listener_register(eventListener.get(), "object:state-changed:visible", NULL);
-               atspi_event_listener_register(eventListener.get(), "object:state-changed:showing", NULL);
-               atspi_event_listener_register(eventListener.get(), "object:bounds-changed", NULL);
-               atspi_event_listener_register(eventListener.get(), "object:state-changed:defunct", NULL);
-               atspi_event_listener_register(eventListener.get(), "object:children-changed", NULL);
-
-               initializeRebuildingContext();
-       }
-       void terminateContextSwitchTracking()
-       {
-               if (eventListener) {
-                       atspi_event_listener_deregister(eventListener.get(), "window:activate", NULL);
-                       atspi_event_listener_deregister(eventListener.get(), "object:state-changed:visible", NULL);
-                       atspi_event_listener_deregister(eventListener.get(), "object:state-changed:showing", NULL);
-                       atspi_event_listener_register(eventListener.get(), "object:bounds-changed", NULL);
-                       atspi_event_listener_register(eventListener.get(), "object:state-changed:defunct", NULL);
-                       atspi_event_listener_register(eventListener.get(), "object:children-changed", NULL);
-                       eventListener = {};
-               }
-       }
 };
 
 std::shared_ptr<NavigationInterface> createNavigationImpl()
index aaf01ba..9cb7f02 100644 (file)
@@ -20,14 +20,14 @@ class PointScannerImpl : public ScreenScanner
 public:
        PointScannerImpl(const ScanningProperties &properties);
        ~PointScannerImpl() override;
-       bool acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback) override;
+       void acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback) override;
        void next() override;
        void prev() override;
 
 private:
        enum class State {END, VERTICAL_REST, HORIZONTAL_DASHED, VERTICAL_FIRST, HORIZONTAL, START};
 
-       bool acceptEventTransition(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback);
+       void acceptEventTransition(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback);
        ecore::TimerRepetitionPolicy timeEventTransition();
 
        void disableScanner();
@@ -94,9 +94,9 @@ void PointScannerImpl::resetGraphics()
        state = State::START;
 }
 
-bool PointScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
+void PointScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
 {
-       return acceptEventTransition(std::move(callback));
+       acceptEventTransition(std::move(callback));
 }
 
 void PointScannerImpl::next()
@@ -108,13 +108,13 @@ void PointScannerImpl::prev()
        DEBUG("'prev' operation not supported in PointScanner");
 }
 
-bool PointScannerImpl::acceptEventTransition(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
+void PointScannerImpl::acceptEventTransition(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
 {
        switch (state) {
        case State::START:
                startHorizontalLine();
                state = State::HORIZONTAL;
-               return false;
+               return;
        case State::HORIZONTAL:
                stopHorizontalLine();
                startVerticalLine();
@@ -123,13 +123,13 @@ bool PointScannerImpl::acceptEventTransition(std::function<void(Optional<std::sh
                stateMachineTimer.reset(2.0 * screenSweepTime, [this]() {
                        return timeEventTransition();
                });
-               return {};
+               return;
        case State::HORIZONTAL_DASHED:
                stateMachineTimer.reset();
                dashedLine.clear();
                startHorizontalLine();
                state = State::HORIZONTAL;
-               return {};
+               return;
        case State::VERTICAL_FIRST:
        case State::VERTICAL_REST:
                stopVerticalLine();
@@ -139,14 +139,14 @@ bool PointScannerImpl::acceptEventTransition(std::function<void(Optional<std::sh
                DEBUG("Scanning complete");
                Singleton<UniversalSwitch>::instance().getNavigationInterface()->getElementAtPoint(intersectionPoint,
                [ = ](DBus::ValueOrError<std::shared_ptr<AtspiAccessible>> elem) {
-                       callback(std::make_shared<UIElement>(std::move(std::get<0>(elem)), intersectionPoint));
+                       callback(std::make_shared<UIElement>(elem ? std::move(std::get<0>(elem)) : nullptr, intersectionPoint));
                });
-               return true;
+               return;
 
        case State::END:
                ERROR("State::END case should not be reached");
        }
-       return false;
+       return;
 }
 
 ecore::TimerRepetitionPolicy PointScannerImpl::timeEventTransition()
index f686ffe..8cca5a9 100644 (file)
@@ -34,7 +34,7 @@ class RowScannerImpl : public ScreenScanner
 public:
        RowScannerImpl(const ScanningProperties &properties);
        ~RowScannerImpl() override;
-       bool acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback) override;
+       void acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback) override;
        void next() override;
        void prev() override;
 
@@ -135,7 +135,7 @@ void RowScannerImpl::boxMoved(Rectangle dimensions, NavigationInterface::BoxPosi
        ASSERT(0, "unknown mode %d", (unsigned int)mode);
 }
 
-bool RowScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
+void RowScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
 {
        bool continueScanning = false;
        if (possibleMoveUpInHierarchy) {
@@ -155,11 +155,16 @@ bool RowScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::sh
        case State::START:
                state = State::ROWS;
                startScanning(continueScanning);
-               return false;
+               return;
        case State::ROWS:
                state = State::ITEMS;
-               startScanning(continueScanning);
-               return false;
+               if (!Singleton<UniversalSwitch>::instance().getNavigationInterface()->isSingleElementRow()) {
+                       startScanning(continueScanning);
+                       return;
+               }
+               DEBUG("Single element row");
+               Singleton<UniversalSwitch>::instance().getNavigationInterface()->nextElementInRow();
+       // continue as case ITEMS
        case State::ITEMS:
                state = State::END;
                stopScanning();
@@ -170,7 +175,7 @@ bool RowScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::sh
                state = State::ITEMS;
                DEBUG("Scanner will continue previous scanning");
                startScanning(true);
-               return false;
+               return;
        }
        state = State::END;
        DEBUG("Scanning complete");
@@ -178,7 +183,7 @@ bool RowScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::sh
        stopScanning();
        if (!elem) callback({});
        else callback(std::make_shared<UIElement>(std::move(elem)));
-       return true;
+       return;
 }
 
 void RowScannerImpl::next()
index db6ea4b..01eb165 100644 (file)
@@ -16,7 +16,7 @@ public:
 
        virtual ~ScreenScanner() = default;
 
-       virtual bool acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback) = 0;
+       virtual void acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback) = 0;
        virtual void next() = 0;
        virtual void prev() = 0;
 
index 8acf401..2d92157 100644 (file)
@@ -143,17 +143,12 @@ void ScreenScannerManager::acceptAutoscanning()
 {
        Optional<std::shared_ptr<UIElement>> element;
        auto handle = [](Optional<std::shared_ptr<UIElement>> elem) {
-               DEBUG(".");
                if (elem) {
-                       DEBUG(".");
                        auto self = Singleton<UniversalSwitch>::instance().getScreenScannerManager();
                        if (self) {
-                               DEBUG(".");
                                self->stopAutoscanning();
                                Singleton<UniversalSwitch>::instance().getTextToSpeech()->speak(*elem);
-                               DEBUG(".");
                                self->notify(*elem);
-                               DEBUG(".");
                        }
                }
        };
index 4dae9be..e266428 100644 (file)
@@ -25,25 +25,6 @@ void UniversalSwitch::initialize()
        //TODO: temporarily memory database is used (change to file database)
        auto configuration = std::make_shared<SQLiteConfiguration>(true);
 
-       {
-               //TODO: remove after integration with settings app
-               std::string switchId = "AccessoriesSwitchProvider_x";
-               auto switch_ = compositeSwitchProvider->findSwitchById(switchId);
-               std::string activityType = "SELECT";
-               auto item = std::make_shared<SwitchConfigurationItem>(switch_->getId()->getGlobalId(), activityType);
-               DEBUG("getGlobalId %s", switch_->getId()->getGlobalId().c_str());
-               configuration->add(item);
-       }
-       {
-               //TODO: remove after integration with settings app
-               std::string switchId = "ScreenSwitchProvider_touch";
-               auto switch_ = compositeSwitchProvider->findSwitchById(switchId);
-               std::string activityType = "SELECT";
-               auto item = std::make_shared<SwitchConfigurationItem>(switch_->getId()->getGlobalId(), activityType);
-               DEBUG("getGlobalId %s", switch_->getId()->getGlobalId().c_str());
-               configuration->add(item);
-       }
-
        setCompositeSwitchProvider(compositeSwitchProvider);
        setConfiguration(configuration);
        setDBusInterface(DBusInterface::create());
index b5c84f6..b781543 100644 (file)
@@ -28,6 +28,56 @@ namespace dbusLocators
                static constexpr const char *BACK_BUTTON_DOWN_SIGNAL = "BackButtonDown";
                static constexpr const char *BACK_BUTTON_UP_SIGNAL = "BackButtonUp";
        }
+
+       namespace freeDesktop
+       {
+               static constexpr const char *BUS = "org.freedesktop.DBus";
+               static constexpr const char *OBJ_PATH = "/org/freedesktop/DBus";
+               static constexpr const char *INTERFACE = "org.freedesktop.DBus";
+               static constexpr const char *PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties";
+               static constexpr const char *GET_CONNECTION_UNIX_PROCESS_ID = "GetConnectionUnixProcessID";
+               static constexpr const char *SET = "Set";
+               static constexpr const char *GET = "Get";
+       }
+
+       namespace windowManager
+       {
+               static constexpr const char *BUS = "org.enlightenment.wm";
+               static constexpr const char *OBJ_PATH = "/org/enlightenment/wm";
+               static constexpr const char *INTERFACE = "org.enlightenment.wm.proc";
+               static constexpr const char *GET_VISIBLE_WIN_INFO = "GetVisibleWinInfo";
+               static constexpr const char *GET_FOCUS_PROC = "GetFocusProc";
+       }
+
+       namespace atspi
+       {
+               static constexpr const char *BUS = "org.a11y.Bus";
+               static constexpr const char *OBJ_PATH = "/org/a11y/bus";
+               static constexpr const char *BUS_INTERFACE = "org.a11y.Bus";
+               static constexpr const char *STATUS_INTERFACE = "org.a11y.Status";
+
+               static constexpr const char *GET_ADDRESS = "GetAddress";
+               static constexpr const char *IS_ENABLED = "IsEnabled";
+               static constexpr const char *GET_ATTRIBUTES = "GetAttributes";
+               static constexpr const char *DO_ACTION_NAME = "DoActionName";
+               static constexpr const char *PARENT = "Parent";
+               static constexpr const char *GET_MATCHES = "GetMatches";
+               static constexpr const char *GET_INDEX_IN_PARENT = "GetIndexInParent";
+               static constexpr const char *SELECT_CHILD = "SelectChild";
+               static constexpr const char *NAME = "Name";
+               static constexpr const char *GET_ROLE = "GetRole";
+               static constexpr const char *CHILD_COUNT = "ChildCount";
+               static constexpr const char *GET_CHILD_AT_INDEX = "GetChildAtIndex";
+               static constexpr const char *GET_STATE = "GetState";
+               static constexpr const char *GET_RELATION_SET = "GetRelationSet";
+               static constexpr const char *GET_EXTENTS = "GetExtents";
+               static constexpr const char *CURRENT_VALUE = "CurrentValue";
+               static constexpr const char *MAXIMUM_VALUE = "MaximumValue";
+               static constexpr const char *MINIMUM_VALUE = "MinimumValue";
+               static constexpr const char *GET_INTERFACES = "GetInterfaces";
+               static constexpr const char *GET_NAVIGABLE_AT_POINT = "GetNavigableAtPoint";
+       }
 }
 
 #endif
+