Implements screen-reader alike navigation for Universal Switch 12/148312/15
authorRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Mon, 28 Aug 2017 15:15:01 +0000 (17:15 +0200)
committerRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Fri, 20 Oct 2017 09:16:11 +0000 (11:16 +0200)
Change-Id: I6db525ec2c9aab25822d4a72b47a258294c455e1

20 files changed:
src/Atspi.cpp
src/Atspi.hpp
src/DBus.cpp
src/Geometry.cpp
src/Geometry.hpp
src/NavigationInterface.cpp
src/NavigationInterface.hpp
src/PointScanner.cpp
src/QueryBuilder.cpp
src/QueryBuilder.hpp
src/RowScanner.cpp
src/SQLiteConfiguration.cpp
src/ScreenScanner.hpp
src/ScreenScannerManager.cpp
src/SwitchManager.cpp
src/UIElement.cpp
src/UniversalSwitch.cpp
src/UniversalSwitch.hpp
src/Window.cpp
utils/setVconfKeys.sh

index 84b8b49..fc7b54d 100644 (file)
@@ -38,9 +38,6 @@ namespace
        }
 }
 
-static AtspiAccessiblePtr root_object;
-static std::vector<std::pair<std::string, std::string>> root_object_attributes;
-
 template <typename T> static std::string getBusNameImpl(T *o)
 {
        if (!o) return "";
@@ -84,7 +81,7 @@ template <typename T> struct InterfaceNameFromType {
        {                                                                                                     \
                getInterface<Atspi ## TYPE>(obj, std::move(callback));                                            \
        }                                                                                                     \
-       std::string Atspi::getUniqueId(const std::shared_ptr<Atspi ## TYPE> &obj) const                       \
+       std::string Atspi::getUniqueId(const std::shared_ptr<Atspi ## TYPE> &obj)                             \
        {                                                                                                     \
                if (!obj)                                                                                         \
                        return "(null)";                                                                              \
@@ -96,7 +93,7 @@ template <typename T> struct InterfaceNameFromType {
                g_free(v);                                                                                        \
                return std::move(z);                                                                              \
        }                                                                                                     \
-       AtspiAccessiblePtr Atspi::getBase(                                                      \
+       AtspiAccessiblePtr Atspi::getBase(                                                                    \
                        const std::shared_ptr<Atspi ## TYPE> &obj) const                                              \
        {                                                                                                     \
                if (!obj) return {};                                                                              \
@@ -112,12 +109,6 @@ ADD_INTROSPECTION_FUNCTIONS(EditableText, EDITABLE_TEXT);
 
 #undef ADD_INTROSPECTION_FUNCTIONS
 
-void hack_setRootObjectAttributes(AtspiAccessiblePtr obj, std::vector<std::pair<std::string, std::string>> attrs)
-{
-       root_object = std::move(obj);
-       root_object_attributes = std::move(attrs);
-}
-
 Atspi::Atspi()
        : eldbus(), connection(efl::eldbus::session)
 {
@@ -244,9 +235,11 @@ Optional<AtspiRole> Atspi::getRole(const AtspiAccessiblePtr &accessibleObj) cons
        return role;
 }
 
-Atspi::StateSet Atspi::getStateSet(const AtspiAccessiblePtr &accessibleObj) const
+Optional<Atspi::StateSet> Atspi::getStateSet(const AtspiAccessiblePtr &accessibleObj) const
 {
        auto states = atspi_accessible_get_state_set(accessibleObj.get());
+       if (!states)
+               return {};
        StateSet result;
        auto v = states->states;
        g_object_unref(states);
@@ -425,6 +418,13 @@ void Atspi::getAttributes(const AtspiAccessiblePtr &accessibleObj,
        );
 }
 
+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),
+                       getBusName(accessibleObj));
+}
+
 void Atspi::doActionName(const AtspiActionPtr &accessibleObj, const std::string &action, AsyncCallback<bool> callback) const
 {
        callFunction<bool(std::string)>(
@@ -538,7 +538,7 @@ void Atspi::getStateSet(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<S
 void Atspi::getChildren(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::vector<AtspiAccessiblePtr>> callback) const
 {
        getChildrenCount(accessibleObj,
-       [accessibleObj, callback](ValueOrError<size_t> childrenCount) {
+       [accessibleObj, callback, this](ValueOrError<size_t> childrenCount) {
                if (!childrenCount) {
                        callback(Error{ childrenCount.getError() });
                } else {
@@ -567,7 +567,7 @@ void Atspi::getChildren(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<s
                                        if (state->todoCount == 0)
                                                state->callback(std::move(state->children));
                                };
-                               Singleton<Atspi>::instance().getChildAtIndex(accessibleObj, index, std::move(cback));
+                               getChildAtIndex(accessibleObj, index, std::move(cback));
                        }
                }
        });
@@ -795,13 +795,8 @@ void Atspi::getAllAcceptedObjects(const AtspiAccessiblePtr &root, AsyncCallback<
        );
 }
 
-std::vector<std::pair<std::string, std::string>> Atspi::getAttributes(const AtspiAccessiblePtr &accessibleObj) const
+std::vector<std::pair<std::string, std::string>> Atspi::getAttributes(const std::shared_ptr<AtspiAccessible> &accessibleObj) const
 {
-       ASSERT(accessibleObj == root_object);
-       return root_object_attributes;
-#if 0
-       // do not delete - root_* hack is temporary
-
        EXIT_IF_NULLPTR(accessibleObj);
        GError *error = nullptr;
        GHashTable *attr = atspi_accessible_get_attributes(accessibleObj.get(), nullptr);
@@ -822,7 +817,6 @@ std::vector<std::pair<std::string, std::string>> Atspi::getAttributes(const Atsp
        g_hash_table_unref(attr);
 
        return attributes;
-#endif
 }
 
 AtspiActionPtr Atspi::getActionInterface(const AtspiAccessiblePtr &accessibleObj) const
@@ -1007,7 +1001,7 @@ Optional<std::vector<AtspiAccessiblePtr>> Atspi::getChildren(const AtspiAccessib
        return std::move(tmp);
 }
 
-AtspiAccessiblePtr Atspi::make(AtspiAccessible *obj, bool increase_reference) const
+AtspiAccessiblePtr Atspi::make(AtspiAccessible *obj, bool increase_reference)
 {
        if (!obj) return {};
        if (increase_reference)
@@ -1015,7 +1009,7 @@ AtspiAccessiblePtr Atspi::make(AtspiAccessible *obj, bool increase_reference) co
        return { obj, g_object_unref };
 }
 
-AtspiAccessiblePtr Atspi::make(const std::string &bus, const std::string &path) const
+AtspiAccessiblePtr Atspi::make(const std::string &bus, const std::string &path)
 {
        return { ref_accessible(bus.c_str(), path.c_str()), g_object_unref };
 }
@@ -1056,3 +1050,50 @@ bool Atspi::selectChild(const AtspiSelectionPtr &accessibleObj, size_t index) co
        PRINT_ERROR_AND_FREE(error);
        return isSuccessful;
 }
+
+using VisibleWindowsReplyType = std::tuple <
+                                                               uint32_t, // resourceId
+                                                               int32_t, int32_t, int32_t, int32_t, // position
+                                                               bool, // alpha
+                                                               int32_t, // visibility
+                                                               bool, // focused
+                                                               int32_t, // pid
+                                                               int32_t, // parentPid
+                                                               int32_t, // ancestorPid;
+                                                               int32_t // notificationLevel
+                                                               >;
+static void parseReply(std::vector<Atspi::VisibleWindowInfo> &dst, std::vector<VisibleWindowsReplyType> &src)
+{
+       dst.clear();
+       dst.reserve(src.size());
+       for (auto &v : src) {
+               dst.push_back({});
+               auto &r = dst.back();
+               r.resourceId = std::get<0>(v);
+               r.position.position.x = std::get<1>(v);
+               r.position.position.y = std::get<2>(v);
+               r.position.size.width = std::get<3>(v);
+               r.position.size.height = std::get<4>(v);
+               r.alpha = std::get<5>(v);
+               r.visibility = (Atspi::WindowVisibility)std::get<6>(v);
+               r.focused = std::get<7>(v);
+               r.pid = std::get<8>(v);
+               r.ancestorPid = std::get<9>(v);
+               r.notificationLevel = std::get<10>(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::ValueOrError<std::vector<VisibleWindowsReplyType>> wins) {
+               if (wins) {
+                       std::vector<VisibleWindowInfo> res;
+                       parseReply(res, std::get<0>(wins));
+                       callback(std::move(res));
+               } else {
+                       callback({});
+               }
+       });
+}
index 156d4f3..1e8357d 100644 (file)
@@ -33,8 +33,8 @@ public:
        Atspi();
        ~Atspi();
 
-       AtspiAccessiblePtr make(const std::string &bus, const std::string &path) const;
-       AtspiAccessiblePtr make(AtspiAccessible *obj, bool increase_reference) const;
+       static AtspiAccessiblePtr make(const std::string &bus, const std::string &path);
+       static AtspiAccessiblePtr make(AtspiAccessible *obj, bool increase_reference);
        AtspiAccessiblePtr getObjectInRelation(const AtspiAccessiblePtr &accessibleObj, AtspiRelationType searchType) const;
        std::vector<std::pair<std::string, std::string>> getAttributes(const AtspiAccessiblePtr &accessibleObj) const;
 
@@ -58,7 +58,7 @@ public:
        Optional<std::vector<AtspiAccessiblePtr>> getChildren(const AtspiAccessiblePtr &accessibleObj) const;
        Optional<AtspiRole> getRole(const AtspiAccessiblePtr &accessibleObj) const;
        Optional<std::string> getName(const AtspiAccessiblePtr &accessibleObj) const;
-       StateSet getStateSet(const AtspiAccessiblePtr &accessibleObj) const;
+       Optional<StateSet> getStateSet(const AtspiAccessiblePtr &accessibleObj) const;
 
        AtspiAccessiblePtr getParent(const AtspiAccessiblePtr &accessibleObj) const;
        Optional<size_t> getIndexInParent(const AtspiAccessiblePtr &accessibleObj) const;
@@ -141,6 +141,7 @@ public:
        void getIndexInParent(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<int> callback) const;
        void selectChild(const AtspiSelectionPtr &accessibleObj, int index, AsyncCallback<void> callback) const;
        void getStateSet(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<StateSet> callback) const;
+       void getProcessId(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<unsigned int> callback) const;
        struct AcceptedObjectInfo {
                AtspiAccessiblePtr object;
                Rectangle pos;
@@ -161,10 +162,27 @@ public:
        void getMaximumValue(const std::shared_ptr<AtspiValue> &valueInterface, AsyncCallback<double> callback) const;
        void getMinimumValue(const std::shared_ptr<AtspiValue> &valueInterface, AsyncCallback<double> callback) const;
 
+       enum class WindowVisibility : int32_t {
+               unknown = -1,
+               unobscured = 0,
+               partially_obscured = 1,
+               fully_obscured = 2
+       };
+       struct VisibleWindowInfo {
+               uint32_t resourceId;
+               Rectangle position;
+               bool alpha;
+               WindowVisibility visibility;
+               bool focused;
+               int32_t pid, parentPid, ancestorPid;
+               int32_t notificationLevel;
+       };
+       void getVisibleWindowInfos(AsyncCallback<std::vector<VisibleWindowInfo>> callback);
+
 #define ADD_INTROSPECTION_FUNCTIONS(TYPE, NAME)                                                   \
        void get ## TYPE ## Interface(const AtspiAccessiblePtr &obj,                                  \
                                                                  AsyncCallback<std::shared_ptr<Atspi ## TYPE>> callback) const;  \
-       std::string getUniqueId(const std::shared_ptr<Atspi ## TYPE> &obj) const;                     \
+       static std::string getUniqueId(const std::shared_ptr<Atspi ## TYPE> &obj);                    \
        AtspiAccessiblePtr getBase(const std::shared_ptr<Atspi ## TYPE> &) const;                     \
        static std::string getBusName(const std::shared_ptr<Atspi ## TYPE> &o);                       \
        static std::string getPath(const std::shared_ptr<Atspi ## TYPE> &o);
index 39d4e29..934108b 100644 (file)
@@ -134,7 +134,7 @@ bool DBus::detail::signature<std::shared_ptr<AtspiAccessible>>::get(Eldbus_Messa
 {
        subtype s;
        if (!signature<subtype>::get(iter, s)) return false;
-       v = Singleton<Atspi>::instance().make(s.first.c_str(), s.second.c_str());
+       v = Atspi::make(s.first.c_str(), s.second.c_str());
        return true;
 }
 
index 768a469..9a63a51 100644 (file)
@@ -9,6 +9,24 @@ static constexpr int DEFAULT_LINE_THICKNESS = 10;
 #define DEFAULT_LINE_COLOUR                    255, 0, 0, 255
 #define DEFAULT_RECTANGLE_COLOUR       0, 255, 0, 255
 
+std::string Point::toString() const
+{
+       std::ostringstream o;
+       o << "x=" << std::setw(4) << x << " y=" << std::setw(4) << y;
+       return o.str();
+}
+
+std::string Size::toString() const
+{
+       std::ostringstream o;
+       o << "w=" << std::setw(4) << width << " h=" << std::setw(4) << height;
+       return o.str();
+}
+
+std::string Rectangle::toString() const
+{
+       return position.toString() + " " + size.toString();
+}
 
 Rectangle Rectangle::intersect(const Rectangle first, const Rectangle second)
 {
index 3f1c8eb..fe3ace0 100644 (file)
@@ -5,21 +5,30 @@
 
 #include <memory>
 #include <vector>
+#include <string>
+#include <sstream>
+#include <iomanip>
 
 struct Point {
        int x{};
        int y{};
+
+       std::string toString() const;
 };
 
 struct Size {
        int width{};
        int height{};
+
+       std::string toString() const;
 };
 
 struct Rectangle {
        Point position;
        Size size;
 
+       std::string toString() const;
+
        static Rectangle intersect(const Rectangle first, const Rectangle second);
        static Rectangle sum(const Rectangle first, const Rectangle second);
 };
index 8300f52..1ed84c5 100644 (file)
@@ -1,8 +1,10 @@
 #include "NavigationInterface.hpp"
 #include "UniversalSwitchLog.hpp"
+#include "UniversalSwitch.hpp"
 #include "Atspi.hpp"
 #include "UIElement.hpp"
 #include "DBus.hpp"
+#include "utils.hpp"
 
 #include <glib.h>
 #include <glib-object.h>
@@ -12,6 +14,7 @@
 #include <functional>
 
 using AtspiAccessiblePtr = std::shared_ptr<AtspiAccessible>;
+static unsigned int contextChangeIdent = 0;
 
 NavigationInterface::~NavigationInterface()
 {
@@ -20,6 +23,11 @@ NavigationInterface::~NavigationInterface()
        callbacks.clear();
 }
 
+NavigationInterface::CallbackHandleBase::~CallbackHandleBase()
+{
+       Singleton<UniversalSwitch>::instance().getNavigationInterface()->unregisterCb(this);
+}
+
 void NavigationInterface::unregisterCb(CallbackHandleBase *cb)
 {
        if (!cb->registered)
@@ -35,113 +43,588 @@ void NavigationInterface::unregisterCb(CallbackHandleBase *cb)
        }
 }
 
-void hack_setRootObjectAttributes(std::shared_ptr<AtspiAccessible> obj, std::vector<std::pair<std::string, std::string>> attrs);
-
 class NavigationImpl : public NavigationInterface
 {
 public:
        NavigationImpl()
        {
-               DBus::DBusClient proxy{"org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus"};
-               auto addr = proxy.method<std::string()>("GetAddress").call();
-               if (addr) {
-                       DEBUG("got dbus %s", std::get<0>(addr).c_str());
-                       DBus::EldbusConnectionCallbackHandle connection { eldbus_address_connection_get(std::get<0>(addr).c_str()), eldbus_connection_unref };
-                       navProxy = { "org.tizen.ScreenNavigator", "/org/tizen/ScreenNavigator", "org.tizen.ScreenNavigator", std::move(connection) };
-                       navProxyServer = { connection };
-               }
-
-               if (!navProxy)
-                       return;
-
-#define LISTEN(n) \
-       do { navProxy.addSignal<void()>(#n, [=]() { emitCallback<NavigationCallbackType::n>(); }); } while (0)
-               LISTEN(FirstRow);
-               LISTEN(LastRow);
-               LISTEN(FirstElementInRow);
-               LISTEN(LastElementInRow);
-#undef LISTEN
-
-#define LISTEN(n, SignalName) \
-       do { navProxy.addSignal<void()>(SignalName, [=]() { emitCallback<NavigationCallbackType::n>(); }); } while (0)
-               LISTEN(DashedRow, "SpecialRow");
-               LISTEN(DashedElementInRow, "SpecialElementInRow");
-#undef LISTEN
-               navProxy.addSignal<void(AtspiAccessiblePtr, int, int, int, int)>("ContextChanged",
-               [ = ](AtspiAccessiblePtr obj, int x, int y, int w, int h) {
-                       emitCallback<NavigationCallbackType::ContextChanged>(std::make_shared<UIElement>(obj), x, y, w, h);
-                       DEBUG("got element %s", Singleton<Atspi>::instance().getUniqueId(obj).c_str());
-               });
-               navProxy.addSignal<void(int, int, int, int, BoxPositionMode)>("BoxMoved",
-               [ = ](int x, int y, int w, int h, BoxPositionMode mode) {
-                       emitCallback<NavigationCallbackType::BoxMoved>(x, y, w, h, mode);
-               });
-               navProxy.addSignal<void(AtspiAccessiblePtr, std::vector<std::pair<std::string, std::string>>)>("HackAttributesForRootAfterContextChanged",
-               [ = ](AtspiAccessiblePtr root, std::vector<std::pair<std::string, std::string>> attrs) {
-                       hack_setRootObjectAttributes(root, std::move(attrs));
-               });
-
+               initializeContextSwitchTracking();
        }
 
        ~NavigationImpl() = default;
 
        void resetPosition() override
        {
-               navProxy.method<void()>("ResetIndexes").call();
+               resetIndexes();
        }
 
        void nextRow() override
        {
-               navProxy.method<void()>("NextRow").call();
+               handleNextPrevRow(true);
        }
 
        void prevRow() override
        {
-               navProxy.method<void()>("PreviousRow").call();
+               handleNextPrevRow(false);
        }
 
        void nextElementInRow() override
        {
-               navProxy.method<void()>("NextElementInRow").call();
+               handleNextPrevElementInRow(true);
        }
 
        void prevElementInRow() override
        {
-               navProxy.method<void()>("PreviousElementInRow").call();
+               handleNextPrevElementInRow(false);
        }
 
-       std::shared_ptr<UIElement> getCurrentElement() override
+       bool isSingleElementRow() override
        {
-               auto current = navProxy.method<AtspiAccessiblePtr()>("GetCurrentElement").call();
-               return std::make_shared<UIElement>(std::move(std::get<0>(current)));
+               auto cols = current_rows.getColumnCountInRow(current_row_index);
+               return cols && *cols == 1;
        }
 
-       std::shared_ptr<UIElement> getElementAtPoint(int x, int y) override
+       std::shared_ptr<AtspiAccessible> getCurrentElement() override
        {
-               auto elem = navProxy.method<AtspiAccessiblePtr(int, int)>("GetElementAtPoint").call(x, y);
-               return std::make_shared<UIElement>(std::move(std::get<0>(elem)), Point{x, y});
+               auto elem = current_rows.getElement(current_row_index, current_column_index);
+               if (!elem) return {};
+               return (*elem).obj;
+       }
+       void getElementAtPoint(Point pt, std::function<void(DBus::ValueOrError<std::shared_ptr<AtspiAccessible>>)> callback) override
+       {
+               if (rootObject) {
+                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                       if (!atspi)
+                               callback(DBus::Error("no atspi"));
+                       else
+                               atspi->getAtPoint(pt, Atspi::CoordType::Screen, rootObject, std::move(callback));
+               } else {
+                       callback(DBus::Error("no root object"));
+               }
        }
 
 private:
-       template <NavigationCallbackType type, typename ... ARGS>
-       void emitCallback(ARGS ... args)
-       {
-               for (auto a : callbacks) {
-                       if (a->type == type) {
-                               using CallbackType = typename NavigationCallbackTraits<type>::CallbackFunctionType;
-                               auto b = static_cast<typename NavigationInterface::CallbackHandleImpl<CallbackType>*>(a);
-                               b->callback(args...);
+       std::shared_ptr<AtspiAccessible> lastActiveWindow, topWindow, rootObject;
+       std::vector<std::shared_ptr<AtspiAccessible>> modalWindows;
+
+       struct GObjectUnref {
+               template <typename T> void operator()(T *t)
+               {
+                       if (t) g_object_unref(t);
+               }
+       };
+       std::unique_ptr<AtspiEventListener, GObjectUnref> eventListener;
+
+       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 val = m.call();
+               if (!val) {
+                       ERROR("failed to get pid");
+                       return {};
+               }
+               if (std::get<0>(val) < 0) {
+                       ERROR("invalid pid value");
+                       return {};
+               }
+               DEBUG("got pid %d", std::get<0>(val));
+               return std::get<0>(val);
+       }
+
+       struct row_info {
+               unsigned int first, last;
+               Rectangle pos;
+       };
+
+       struct object_info {
+               std::shared_ptr<AtspiAccessible> obj;
+               Rectangle pos;
+       };
+
+       enum class row_box_mode {
+               ROW_BOX_NONE = 0,
+               ROW_BOX_NORMAL,
+               ROW_BOX_SPECIAL,
+       };
+
+       struct navigation_rows {
+               std::vector<object_info> object_infos;
+               std::vector<row_info> row_infos;
+               Rectangle root_pos;
+
+               unsigned int getRowCount()
+               {
+                       return (unsigned int)row_infos.size();
+               }
+               Optional<unsigned int> getColumnCountInRow(unsigned int row)
+               {
+                       if (row == 0 || row > getRowCount()) {
+                               return {};
+                       }
+                       row_info &ri = row_infos[row - 1];
+                       return ri.last - ri.first + 1;
+               }
+               Optional<object_info> getElement(unsigned int row, unsigned int col)
+               {
+                       if (row == 0 || col == 0 || row > getRowCount()) return {};
+                       auto &ri = row_infos[row - 1];
+                       if (col > ri.last - ri.first + 1) return {};
+                       auto &oi = object_infos[ri.first + col - 1];
+                       return oi;
+               }
+               Optional<Rectangle> getRowSize(unsigned int row)
+               {
+                       if (row == 0) {
+                               DEBUG("root pos is %s", root_pos.toString().c_str());
+                               return root_pos;
+                       }
+                       if (row <= getRowCount()) {
+                               auto &ri = row_infos[row - 1];
+                               DEBUG("row %d pos is %s", row, ri.pos.toString().c_str());
+                               return ri.pos;
+                       }
+                       return {};
+               }
+               bool findElement(unsigned int &row, unsigned int &col, const std::shared_ptr<AtspiAccessible> &obj)
+               {
+                       for (unsigned int r = 0; r < row_infos.size(); ++r) {
+                               row_info &ri = row_infos[r];
+                               for (unsigned int c = ri.first; c <= ri.last; ++c) {
+                                       object_info &e = object_infos[c];
+                                       if (e.obj == obj) {
+                                               row = r + 1;
+                                               col = c - ri.first + 1;
+                                               return true;
+                                       }
+                               }
+                       }
+                       return false;
+               }
+       };
+
+       navigation_rows 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)
+       {
+               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());
                        }
                }
        }
 
-       DBus::DBusClient navProxy;
-       DBus::DBusServer navProxyServer;
+       void createNavigationRows(const std::shared_ptr<AtspiAccessible> &root, std::function<void(Optional<navigation_rows>)> callback)
+       {
+               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+               if (!atspi) {
+                       callback({});
+                       return;
+               }
+               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;
+                                       }
+                                       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;
+                                       navState.row_infos.push_back(rowInfo);
+                               }
+                               navigation_rows_debug_dump(navState);
+                               callback(std::move(navState));
+                       } else {
+                               ERROR("failed %s", value_.getError().message.c_str());
+                               callback({});
+                       }
+               });
+       }
+
+       void resetIndexes(void)
+       {
+               DEBUG("resetting current position");
+               current_row_index = current_column_index = 0;
+       }
+
+       void screenNavigationContextChanged(unsigned int currentContextChangeIdent, const std::shared_ptr<AtspiAccessible> &root)
+       {
+               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;
+                       }
+                       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::BoxMoved>(Rectangle{}, BoxPositionMode::NONE);
+                       DEBUG("context has %d rows", current_rows_count);
+               });
+       }
+
+       void handleNextPrevRow(bool nextRow)
+       {
+               unsigned int row_count = current_rows_count + 1;
+               unsigned int row = (current_row_index + (nextRow ? 1 : row_count - 1)) % row_count;
+               DEBUG("new row is %d", row);
+               current_column_index = 0;
+               if (row == 0) {
+                       emitCallback<NavigationCallbackType::DashedRow>();
+               } else {
+                       if (!nextRow && row == 1)
+                               emitCallback<NavigationCallbackType::FirstRow>();
+                       if (!nextRow && row == row_count - 1)
+                               emitCallback<NavigationCallbackType::LastRow>();
+                       if (nextRow && row == row_count - 1)
+                               emitCallback<NavigationCallbackType::LastRow>();
+                       if (nextRow && row == 1)
+                               emitCallback<NavigationCallbackType::FirstRow>();
+               }
+               current_row_index = row;
+               auto count = current_rows.getColumnCountInRow(current_row_index);
+               current_row_column_count = count ? *count : 0;
+               auto pos = current_rows.getRowSize(current_row_index);
+               if (!pos) {
+                       DEBUG("invalid row %d", current_row_index);
+               } else {
+                       DEBUG("navigation_rows_get_row_size: %s", (*pos).toString().c_str());
+                       emitCallback<NavigationCallbackType::BoxMoved>(*pos, current_row_index > 0 ? BoxPositionMode::NORMAL : BoxPositionMode::DASHED);
+               }
+       }
+
+       void handleNextPrevElementInRow(bool nextElement)
+       {
+               unsigned int col_count = current_row_column_count + 1;
+               unsigned int col = (current_column_index + (nextElement ? 1 : col_count - 1)) % col_count;
+               DEBUG("new column is %d", col);
+               if (col == 0) {
+                       emitCallback<NavigationCallbackType::DashedElementInRow>();
+               } else {
+                       if (!nextElement && col == 1)
+                               emitCallback<NavigationCallbackType::FirstElementInRow>();
+                       if (!nextElement && col == col_count - 1)
+                               emitCallback<NavigationCallbackType::LastElementInRow>();
+                       if (nextElement && col == col_count - 1)
+                               emitCallback<NavigationCallbackType::LastElementInRow>();
+                       if (nextElement && col == 1)
+                               emitCallback<NavigationCallbackType::FirstElementInRow>();
+               }
+               current_column_index = col;
+
+               Rectangle pos = {};
+               if (current_column_index) {
+                       auto e = current_rows.getElement(current_row_index, current_column_index);
+                       if (e)
+                               pos = (*e).pos;
+               } else {
+                       auto e = current_rows.getRowSize(current_row_index);
+                       if (e)
+                               pos = *e;
+               }
+
+               emitCallback<NavigationCallbackType::BoxMoved>(pos, current_column_index > 0 ? BoxPositionMode::NORMAL : BoxPositionMode::DASHED);
+       }
+
+       ecore::Timer restartTimer;
+
+       void rebuildContextFromZero()
+       {
+               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);
+
+               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;
+                                                       }
+                                                       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);
+                                               });
+                                       });
+                               }
+                       });
+               };
+               auto processProcess = [ = ](AtspiAccessiblePtr app) {
+                       DEBUG("process %s", Atspi::getUniqueId(app).c_str());
+                       if (!app) return;
+
+                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                       if (!atspi) return;
+
+                       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 {
+                                       atspi->getMatchedElements(
+                                               std::get<0>(col),
+                                               ATSPI_Collection_SORT_ORDER_CANONICAL, 5,
+                                               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));
+                                               }
+                                       });
+                               }
+                       });
+               };
+               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");
+                               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;
+                                       });
+                               }
+                               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->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;
+                                       }
+                                       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)
+       {
+               if (root == rootObject) {
+                       DEBUG("no context switch, as object %s is already the root", Atspi::getUniqueId(root).c_str());
+                       return;
+               }
+               rootObject = root;
+               screenNavigationContextChanged(contextChangeIdent, root);
+               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() : "");
+                       });
+               }
+       }
+
+       ecore::Timer rebuildContextTimer;
+
+       void initializeRebuildingContext()
+       {
+               rebuildContextTimer.reset(0.2f,
+               [ = ]() {
+                       auto nav = std::dynamic_pointer_cast<NavigationImpl>(Singleton<UniversalSwitch>::instance().getNavigationInterface());
+                       if (nav)
+                               nav->rebuildContextFromZero();
+                       return ecore::TimerRepetitionPolicy::cancel;
+               });
+       }
+
+       void onAtspiWindow(const AtspiEvent *event)
+       {
+               using namespace std::literals::string_literals;
+
+               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();
+                       }
+               }
+       }
+
+       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 = {};
+               }
+       }
 };
 
-template<>
-template<>
-std::unique_ptr<NavigationInterface> Singleton<NavigationInterface>::createImplementation<NavigationInterface>()
+std::shared_ptr<NavigationInterface> createNavigationImpl()
 {
-       return std::unique_ptr<NavigationInterface> { new NavigationImpl };
+       return std::make_shared<NavigationImpl>();
 }
index f210d18..f2b4f73 100644 (file)
@@ -4,11 +4,14 @@
 #include "Singleton.hpp"
 #include "UIElement.hpp"
 #include "Optional.hpp"
+#include "Geometry.hpp"
 
 #include <atspi/atspi.h>
 #include <vector>
 #include <memory>
 
+class Atspi;
+
 enum class NavigationCallbackType {
        FirstRow, LastRow, DashedRow,
        FirstElementInRow, LastElementInRow, DashedElementInRow,
@@ -25,10 +28,6 @@ struct NavigationCallbackTraits {
 class NavigationInterface
 {
 public:
-       struct BoxPosition {
-               int x, y, w, h;
-       };
-
        enum class BoxPositionMode {
                NONE = 0,
                NORMAL,
@@ -43,10 +42,7 @@ public:
                        : type(type)
                {}
 
-               virtual ~CallbackHandleBase()
-               {
-                       Singleton<NavigationInterface>::instance().unregisterCb(this);
-               }
+               virtual ~CallbackHandleBase();
        };
        using CallbackHandle = std::unique_ptr<CallbackHandleBase>;
 
@@ -57,9 +53,9 @@ public:
        virtual void prevRow() = 0;
        virtual void nextElementInRow() = 0;
        virtual void prevElementInRow() = 0;
-
-       virtual std::shared_ptr<UIElement> getCurrentElement() = 0;
-       virtual std::shared_ptr<UIElement> getElementAtPoint(int x, int y) = 0;
+       virtual bool isSingleElementRow() = 0;
+       virtual std::shared_ptr<AtspiAccessible> getCurrentElement() = 0;
+       virtual void getElementAtPoint(Point pt, std::function<void(DBus::ValueOrError<std::shared_ptr<AtspiAccessible>>)> callback) = 0;
 
        template <NavigationCallbackType type>
        CallbackHandle registerCb(std::function<typename NavigationCallbackTraits<type>::CallbackFunctionType> callback)
@@ -71,6 +67,7 @@ public:
                return ptr;
        }
 
+
 protected:
        template <typename T>
        struct CallbackHandleImpl : public CallbackHandleBase {
@@ -84,6 +81,17 @@ protected:
        void unregisterCb(CallbackHandleBase *cb);
 
        std::vector<CallbackHandleBase *> callbacks;
+
+       template <NavigationCallbackType type, typename ... ARGS> void emitCallback(ARGS &&... args)
+       {
+               for (auto a : callbacks) {
+                       if (a->type == type) {
+                               using T = typename NavigationCallbackTraits<type>::CallbackFunctionType;
+                               auto b = static_cast<typename NavigationInterface::CallbackHandleImpl<T>*>(a);
+                               b->callback(args...);
+                       }
+               }
+       }
 };
 
 #define SPECIALIZE_CALLBACK_TRAITS(Item, FunctionType) \
@@ -93,8 +101,10 @@ protected:
                using CallbackFunctionType = FunctionType; \
        };
 
-SPECIALIZE_CALLBACK_TRAITS(ContextChanged, void(std::shared_ptr<UIElement>, int, int, int, int));
-SPECIALIZE_CALLBACK_TRAITS(BoxMoved, void(int, int, int, int, NavigationInterface::BoxPositionMode));
+SPECIALIZE_CALLBACK_TRAITS(ContextChanged, void(std::shared_ptr<UIElement>, Rectangle));
+SPECIALIZE_CALLBACK_TRAITS(BoxMoved, void(Rectangle, NavigationInterface::BoxPositionMode));
 #undef SPECIALIZE_CALLBACK_TRAITS
 
+std::shared_ptr<NavigationInterface> createNavigationImpl();
+
 #endif
index 109c6d8..aaf01ba 100644 (file)
@@ -20,14 +20,14 @@ class PointScannerImpl : public ScreenScanner
 public:
        PointScannerImpl(const ScanningProperties &properties);
        ~PointScannerImpl() override;
-       Optional<std::shared_ptr<UIElement>> acceptAutoscanningPhase() override;
+       bool 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};
 
-       Optional<std::shared_ptr<UIElement>> acceptEventTransition();
+       bool 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;
 }
 
-Optional<std::shared_ptr<UIElement>> PointScannerImpl::acceptAutoscanningPhase()
+bool PointScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
 {
-       return acceptEventTransition();
+       return acceptEventTransition(std::move(callback));
 }
 
 void PointScannerImpl::next()
@@ -108,13 +108,13 @@ void PointScannerImpl::prev()
        DEBUG("'prev' operation not supported in PointScanner");
 }
 
-Optional<std::shared_ptr<UIElement>> PointScannerImpl::acceptEventTransition()
+bool PointScannerImpl::acceptEventTransition(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
 {
        switch (state) {
        case State::START:
                startHorizontalLine();
                state = State::HORIZONTAL;
-               return {};
+               return false;
        case State::HORIZONTAL:
                stopHorizontalLine();
                startVerticalLine();
@@ -137,11 +137,16 @@ Optional<std::shared_ptr<UIElement>> PointScannerImpl::acceptEventTransition()
                state = State::END;
 
                DEBUG("Scanning complete");
-               return Singleton<NavigationInterface>::instance().getElementAtPoint(intersectionPoint.x, intersectionPoint.y);
+               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));
+               });
+               return true;
+
        case State::END:
                ERROR("State::END case should not be reached");
        }
-       return {};
+       return false;
 }
 
 ecore::TimerRepetitionPolicy PointScannerImpl::timeEventTransition()
index 6bd8c6a..8be4687 100644 (file)
@@ -50,6 +50,12 @@ QueryBuilder &QueryBuilder::insertInto(const std::string &s)
        return *this;
 }
 
+QueryBuilder &QueryBuilder::replaceInto(const std::string &s)
+{
+       query << "replace into " << s << " ";
+       return *this;
+}
+
 QueryBuilder &QueryBuilder::values(const std::initializer_list<std::string> &list)
 {
        query << "values (";
index 9795b21..1e6edbd 100644 (file)
@@ -15,11 +15,14 @@ public:
        QueryBuilder &where(const std::string &lv, const std::string &op, const std::string &rv);
        QueryBuilder &set(const std::initializer_list<std::pair<std::string, std::string>> &pairs);
        QueryBuilder &insertInto(const std::string &s);
+       QueryBuilder &replaceInto(const std::string &s);
        QueryBuilder &values(const std::initializer_list<std::string> &list);
        QueryBuilder &update(const std::string &s);
        QueryBuilder &deleteFrom(const std::string &s);
        QueryBuilder &qAnd(const std::string &lv, const std::string &op, const std::string &rv);
        QueryBuilder &qOr(const std::string &lv, const std::string &op, const std::string &rv);
+       QueryBuilder &onDuplicateUpdate(const std::initializer_list<std::string> &argNames);
+       QueryBuilder &argumentNames(const std::initializer_list<std::string> &argNames);
 
        std::string build();
 
index aecc3b4..f686ffe 100644 (file)
@@ -21,8 +21,9 @@ namespace
        {
                auto tts = Singleton<UniversalSwitch>::instance().getTextToSpeech();
                if (tts->isEnabled()) {
-                       auto elem = Singleton<NavigationInterface>::instance().getCurrentElement();
-                       Singleton<UniversalSwitch>::instance().getTextToSpeech()->speak(elem);
+                       auto elem = Singleton<UniversalSwitch>::instance().getNavigationInterface()->getCurrentElement();
+                       auto uiElem = std::make_shared<UIElement>(std::move(elem));
+                       Singleton<UniversalSwitch>::instance().getTextToSpeech()->speak(std::move(uiElem));
                }
                Sound::playSoundFeedback(Sound::ID::NAVIGATION_ITERATED);
        }
@@ -33,7 +34,7 @@ class RowScannerImpl : public ScreenScanner
 public:
        RowScannerImpl(const ScanningProperties &properties);
        ~RowScannerImpl() override;
-       Optional<std::shared_ptr<UIElement>> acceptAutoscanningPhase() override;
+       bool acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback) override;
        void next() override;
        void prev() override;
 
@@ -83,7 +84,7 @@ RowScannerImpl::RowScannerImpl(const ScanningProperties &properties)
 {
        ASSERT(properties.getAutoScanInterval() > 0, "autoScanInterval has to be bigger than 0");
 
-       auto &navigation = Singleton<NavigationInterface>::instance();
+       auto &navigation = *Singleton<UniversalSwitch>::instance().getNavigationInterface();
        navigation.resetPosition();
        using CT = NavigationCallbackType;
        navigationHandles.push_back(navigation.registerCb<CT::DashedRow>([ this ]() {
@@ -96,7 +97,7 @@ RowScannerImpl::RowScannerImpl(const ScanningProperties &properties)
                countInactivityAfterCompletedLoop();
        }));
        navigationHandles.push_back(navigation.registerCb<CT::BoxMoved>(
-       [ this ](int x, int y, int w, int h, NavigationInterface::BoxPositionMode mode) {
+       [ this ](Rectangle pos, NavigationInterface::BoxPositionMode mode) {
                if (skipFrameDrawing) {
                        skipFrameDrawing = false;
                        if (tryResetState())
@@ -104,7 +105,7 @@ RowScannerImpl::RowScannerImpl(const ScanningProperties &properties)
                        moveRow(lastRowsDirection);
                        return;
                }
-               boxMoved({{x, y}, {w, h}}, mode);
+               boxMoved(pos, mode);
                playFeedback();
        }));
 }
@@ -134,7 +135,7 @@ void RowScannerImpl::boxMoved(Rectangle dimensions, NavigationInterface::BoxPosi
        ASSERT(0, "unknown mode %d", (unsigned int)mode);
 }
 
-Optional<std::shared_ptr<UIElement>> RowScannerImpl::acceptAutoscanningPhase()
+bool RowScannerImpl::acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback)
 {
        bool continueScanning = false;
        if (possibleMoveUpInHierarchy) {
@@ -154,24 +155,30 @@ Optional<std::shared_ptr<UIElement>> RowScannerImpl::acceptAutoscanningPhase()
        case State::START:
                state = State::ROWS;
                startScanning(continueScanning);
-               return {};
+               return false;
        case State::ROWS:
                state = State::ITEMS;
                startScanning(continueScanning);
-               return {};
+               return false;
        case State::ITEMS:
                state = State::END;
                stopScanning();
                playFeedback();
                DEBUG("Scanning complete");
-               return Singleton<NavigationInterface>::instance().getCurrentElement();
+               break;
        case State::END:
                state = State::ITEMS;
                DEBUG("Scanner will continue previous scanning");
                startScanning(true);
-               return {};
+               return false;
        }
-       return {};
+       state = State::END;
+       DEBUG("Scanning complete");
+       auto elem = Singleton<UniversalSwitch>::instance().getNavigationInterface()->getCurrentElement();
+       stopScanning();
+       if (!elem) callback({});
+       else callback(std::make_shared<UIElement>(std::move(elem)));
+       return true;
 }
 
 void RowScannerImpl::next()
@@ -227,7 +234,7 @@ bool RowScannerImpl::tryResetState()
        if (inactiveLoopCounter >= properties.getLoopLimitToInaction()) {
                stopScanning();
                eraseFrame();
-               Singleton<NavigationInterface>::instance().resetPosition();
+               Singleton<UniversalSwitch>::instance().getNavigationInterface()->resetPosition();
                state = State::START;
                inactiveLoopCounter = 0;
                DEBUG("Scanner state set to START. Timmer disabled");
@@ -282,10 +289,10 @@ void RowScannerImpl::moveRow(VerticalScanningDirection direction)
        lastRowsDirection = direction;
        switch (direction) {
        case VerticalScanningDirection::TO_BOTTOM:
-               Singleton<NavigationInterface>::instance().nextRow();
+               Singleton<UniversalSwitch>::instance().getNavigationInterface()->nextRow();
                return;
        case VerticalScanningDirection::TO_TOP:
-               Singleton<NavigationInterface>::instance().prevRow();
+               Singleton<UniversalSwitch>::instance().getNavigationInterface()->prevRow();
                return;
        }
 }
@@ -302,10 +309,10 @@ void RowScannerImpl::moveElementInRow(HorizontalScanningDirection direction)
 {
        switch (direction) {
        case HorizontalScanningDirection::TO_RIGHT:
-               Singleton<NavigationInterface>::instance().nextElementInRow();
+               Singleton<UniversalSwitch>::instance().getNavigationInterface()->nextElementInRow();
                return;
        case HorizontalScanningDirection::TO_LEFT:
-               Singleton<NavigationInterface>::instance().prevElementInRow();
+               Singleton<UniversalSwitch>::instance().getNavigationInterface()->prevElementInRow();
                return;
        }
 }
@@ -354,7 +361,7 @@ ecore::TimerRepetitionPolicy RowScannerImpl::iterateToNextScanningElement()
                return ecore::TimerRepetitionPolicy::cancel;
        }
 
-       return ecore::TimerRepetitionPolicy::renew;
+       return timer.isSet() ? ecore::TimerRepetitionPolicy::renew : ecore::TimerRepetitionPolicy::cancel;
 }
 
 void RowScannerImpl::countInactivityAfterCompletedLoop()
index e171b83..81c45b8 100644 (file)
@@ -29,8 +29,9 @@ SQLiteConfiguration::SQLiteConfiguration(bool createInMemory)
 
                path = (resource ? resource : "") + dbName;
                free(resource);
-       } else
+       } else {
                path = dbName;
+       }
 
        if (sqlite3_open(path.c_str(), &dbHandler) != SQLITE_OK) {
                ERROR("sqlite3_open() failed");
@@ -49,10 +50,10 @@ SQLiteConfiguration::~SQLiteConfiguration()
 
 void SQLiteConfiguration::add(const std::shared_ptr<SwitchConfigurationItem> &item)
 {
-       auto query = QueryBuilder().insertInto(tableName).values({item->getSwitchId(),
+       auto query = QueryBuilder().replaceInto(tableName).values({item->getSwitchId(),
                                 item->getUserName(),
                                 item->getActivityType()
-                                                                                                                        }).build();
+                                                                                                                         }).build();
 
        item->setChangeType(ChangeType::ADD);
        if (executeQuery(query))
index 43d9e9c..db6ea4b 100644 (file)
@@ -16,7 +16,7 @@ public:
 
        virtual ~ScreenScanner() = default;
 
-       virtual Optional<std::shared_ptr<UIElement>> acceptAutoscanningPhase() = 0;
+       virtual bool acceptAutoscanningPhase(std::function<void(Optional<std::shared_ptr<UIElement>>)> callback) = 0;
        virtual void next() = 0;
        virtual void prev() = 0;
 
index 3b6b456..8acf401 100644 (file)
@@ -15,11 +15,13 @@ ScreenScannerManager::ScreenScannerManager()
        scanningMethod = properties.getScanningMethod();
 
        //TODO: register for screen orientation change (probably better to do it in class Window)
-       contextChangedHandle = Singleton<NavigationInterface>::instance().registerCb<NavigationCallbackType::ContextChanged>(
-       [this](std::shared_ptr<UIElement> obj, int x, int y, int width, int height) {
-               auto rect = Rectangle {{x, y}, {width, height}};
-               properties.setScanningField(rect);
-               onContextChanged(obj);
+       contextChangedHandle = Singleton<UniversalSwitch>::instance().getNavigationInterface()->registerCb<NavigationCallbackType::ContextChanged>(
+       [](std::shared_ptr<UIElement> obj, Rectangle rect) {
+               auto self = Singleton<UniversalSwitch>::instance().getScreenScannerManager();
+               if (self) {
+                       self->properties.setScanningField(rect);
+                       self->onContextChanged(obj);
+               }
        });
        properties.setScanningField(mainWindow->getDimensions());
 }
@@ -139,17 +141,25 @@ void ScreenScannerManager::prev()
 
 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(".");
+                       }
+               }
+       };
        if (!screenScanner) {
                ERROR("scanner type not selected");
                return;
        }
-       auto element = screenScanner->acceptAutoscanningPhase();
-
-       if (!element) {
-               DEBUG("Element not found");
-               return;
-       }
-
-       Singleton<UniversalSwitch>::instance().getTextToSpeech()->speak(*element);
-       notify(*element);
+       screenScanner->acceptAutoscanningPhase(handle);
 }
index 35c5142..23dfc7a 100644 (file)
@@ -146,6 +146,7 @@ static void onButtonClickCb(void *data, Evas_Object *obj, void *event_info)
 
 void SwitchManager::onDisconnection()
 {
+       DEBUG("adding window");
        auto windowHandler = elm_win_add(NULL, "universal-switch-popup", ELM_WIN_NOTIFICATION);
        if (!windowHandler) {
                ERROR("Window cannot be created");
index ff85778..ea7d84e 100644 (file)
@@ -29,6 +29,7 @@ void UIElement::getAttributesAsync(std::function<void()> callback)
                }
 
                ptr->attributes = std::move(std::get<0>(data));
+               ASSERT(ptr->attributes);
                for (const auto &a : *ptr->attributes)
                        DEBUG("%s = %s", std::get<0>(a).c_str(), std::get<1>(a).c_str());
 
index 6c19c61..4dae9be 100644 (file)
@@ -25,10 +25,30 @@ 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());
 
+
        callbackHandle = Singleton<VConfInterface>::instance().registerAndGet<bool>(VCONF_KEY_SCANNING,
                                         false,
        [this](auto enabled) {
@@ -42,6 +62,7 @@ void UniversalSwitch::startScanning()
        DEBUG("Universal-switch active mode");
 
        atspi = std::make_shared<Atspi>();
+       navigationInterface = createNavigationImpl();
 
        auto activityFactory = ActivityFactory::getInstance();
        switchManager = SwitchManager::create<SwitchManager>(compositeSwitchProvider, configuration, activityFactory);
@@ -61,6 +82,7 @@ void UniversalSwitch::stopScanning()
                switchManager.reset();
        }
        textToSpeech.reset();
+       navigationInterface.reset();
        atspi.reset();
 }
 
@@ -69,6 +91,10 @@ std::shared_ptr<ScreenScannerManager> UniversalSwitch::getScreenScannerManager()
        return screenScannerManager;
 }
 
+std::shared_ptr<NavigationInterface> UniversalSwitch::getNavigationInterface() const
+{
+       return navigationInterface;
+}
 
 std::shared_ptr<SwitchManager> UniversalSwitch::getSwitchManager() const
 {
index aaf4b4f..551edd6 100644 (file)
@@ -15,6 +15,7 @@ class ScreenScannerManager;
 class SwitchManager;
 class TextToSpeech;
 class Window;
+class NavigationInterface;
 
 
 class UniversalSwitch
@@ -29,6 +30,7 @@ public:
        std::shared_ptr<Atspi> getAtspi() const;
        std::shared_ptr<Window> getMainWindow();
        std::shared_ptr<TextToSpeech> getTextToSpeech() const;
+       std::shared_ptr<NavigationInterface> getNavigationInterface() const;
 
        void setSwitchManager(const std::shared_ptr<SwitchManager> &sm);
        void setCompositeSwitchProvider(const std::shared_ptr<CompositeSwitchProvider> &csp);
@@ -47,6 +49,7 @@ private:
        std::shared_ptr<Configuration> configuration;
        std::shared_ptr<DBusInterface> dbusInterface;
        std::shared_ptr<Atspi> atspi;
+       std::shared_ptr<NavigationInterface> navigationInterface;
        std::shared_ptr<TextToSpeech> textToSpeech;
 
        std::shared_ptr<ScreenScannerManager> screenScannerManager;
index 4071878..9cd5422 100644 (file)
@@ -6,6 +6,7 @@
 Window::Window()
        : handler(nullptr, evas_object_del), screenWidth(0), screenHeight(0)
 {
+       DEBUG("adding window");
        auto windowHandler = elm_win_add(NULL, "universal-switch", ELM_WIN_NOTIFICATION);
        if (!windowHandler) {
                ERROR("Window cannot be created");
@@ -14,6 +15,7 @@ Window::Window()
 
        // TODO: name setter should be removed after reaching full functionality by navigation interface
        elm_atspi_accessible_name_set(windowHandler, "UniversalSwitch Main Window");
+
        efl_util_set_notification_window_level(windowHandler, EFL_UTIL_NOTIFICATION_LEVEL_3);
        elm_win_override_set(windowHandler, EINA_TRUE);
        elm_win_alpha_set(windowHandler, EINA_TRUE);
index 072f558..4ee9c6f 100755 (executable)
@@ -49,7 +49,7 @@ $VCONFTOOL double "${VCONF_PROJECT_PREFIX}PAUSE_ON_FIRST_ELEMENT_TIME" 1.0
 $VCONFTOOL bool "${VCONF_PROJECT_PREFIX}scanning" 0
 $VCONFTOOL int "${VCONF_PROJECT_PREFIX}SCAN_DIRECTION_VERTICAL" 1                      # 1 = SD_TOP_BOTTOM, 2 = SD_BOTTOM_TOP
 $VCONFTOOL int "${VCONF_PROJECT_PREFIX}SCAN_DIRECTION_HORIZONTAL" 1                    # 1 = SD_LEFT_RIGHT, 2 = SD_RIGHT_LEFT
-$VCONFTOOL int "${VCONF_PROJECT_PREFIX}SCAN_METHOD" 1                                          # 1 = POINT, 2 = ROW
+$VCONFTOOL int "${VCONF_PROJECT_PREFIX}SCAN_METHOD" 2                                          # 1 = POINT, 2 = ROW
 $VCONFTOOL int "${VCONF_PROJECT_PREFIX}SCAN_PT_SPEED" 30                                       # speed for PointScanner, jump time for RowScanner
 
 $VCONFTOOL double "${VCONF_PROJECT_PREFIX}SGL_INACTION_INT_VALUE" 0.0