}
}
-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 "";
{ \
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)"; \
g_free(v); \
return std::move(z); \
} \
- AtspiAccessiblePtr Atspi::getBase( \
+ AtspiAccessiblePtr Atspi::getBase( \
const std::shared_ptr<Atspi ## TYPE> &obj) const \
{ \
if (!obj) return {}; \
#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)
{
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);
);
}
+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)>(
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 {
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));
}
}
});
);
}
-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);
g_hash_table_unref(attr);
return attributes;
-#endif
}
AtspiActionPtr Atspi::getActionInterface(const AtspiAccessiblePtr &accessibleObj) const
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)
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 };
}
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({});
+ }
+ });
+}
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;
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;
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;
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);
{
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;
}
#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)
{
#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);
};
#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>
#include <functional>
using AtspiAccessiblePtr = std::shared_ptr<AtspiAccessible>;
+static unsigned int contextChangeIdent = 0;
NavigationInterface::~NavigationInterface()
{
callbacks.clear();
}
+NavigationInterface::CallbackHandleBase::~CallbackHandleBase()
+{
+ Singleton<UniversalSwitch>::instance().getNavigationInterface()->unregisterCb(this);
+}
+
void NavigationInterface::unregisterCb(CallbackHandleBase *cb)
{
if (!cb->registered)
}
}
-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>();
}
#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,
class NavigationInterface
{
public:
- struct BoxPosition {
- int x, y, w, h;
- };
-
enum class BoxPositionMode {
NONE = 0,
NORMAL,
: type(type)
{}
- virtual ~CallbackHandleBase()
- {
- Singleton<NavigationInterface>::instance().unregisterCb(this);
- }
+ virtual ~CallbackHandleBase();
};
using CallbackHandle = std::unique_ptr<CallbackHandleBase>;
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)
return ptr;
}
+
protected:
template <typename T>
struct CallbackHandleImpl : public CallbackHandleBase {
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) \
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
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();
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()
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();
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()
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 (";
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();
{
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);
}
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;
{
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 ]() {
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())
moveRow(lastRowsDirection);
return;
}
- boxMoved({{x, y}, {w, h}}, mode);
+ boxMoved(pos, mode);
playFeedback();
}));
}
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) {
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()
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");
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;
}
}
{
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;
}
}
return ecore::TimerRepetitionPolicy::cancel;
}
- return ecore::TimerRepetitionPolicy::renew;
+ return timer.isSet() ? ecore::TimerRepetitionPolicy::renew : ecore::TimerRepetitionPolicy::cancel;
}
void RowScannerImpl::countInactivityAfterCompletedLoop()
path = (resource ? resource : "") + dbName;
free(resource);
- } else
+ } else {
path = dbName;
+ }
if (sqlite3_open(path.c_str(), &dbHandler) != SQLITE_OK) {
ERROR("sqlite3_open() failed");
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))
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;
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());
}
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);
}
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");
}
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());
//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) {
DEBUG("Universal-switch active mode");
atspi = std::make_shared<Atspi>();
+ navigationInterface = createNavigationImpl();
auto activityFactory = ActivityFactory::getInstance();
switchManager = SwitchManager::create<SwitchManager>(compositeSwitchProvider, configuration, activityFactory);
switchManager.reset();
}
textToSpeech.reset();
+ navigationInterface.reset();
atspi.reset();
}
return screenScannerManager;
}
+std::shared_ptr<NavigationInterface> UniversalSwitch::getNavigationInterface() const
+{
+ return navigationInterface;
+}
std::shared_ptr<SwitchManager> UniversalSwitch::getSwitchManager() const
{
class SwitchManager;
class TextToSpeech;
class Window;
+class NavigationInterface;
class UniversalSwitch
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);
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;
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");
// 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);
$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