From fe9f99e73477e139716655ac657eef387d8524d1 Mon Sep 17 00:00:00 2001 From: Radoslaw Cybulski Date: Thu, 9 Mar 2017 18:09:34 +0100 Subject: [PATCH] Add navigation helper functions for screen-reader and friends (part 2) Change-Id: I67eb6da5262a9de721dc2cb569255079dc221ec9 --- atspi/atspi-accessible.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++- atspi/atspi-accessible.h | 2 + atspi/atspi-constants.h | 5 +++ 3 files changed, 120 insertions(+), 1 deletion(-) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index a579254..71fd816 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -341,7 +341,7 @@ atspi_accessible_get_navigable_at_point (AtspiAccessible *root, AtspiAccessible *return_value = NULL; unsigned char recurse = 0; - g_return_val_if_fail (root != NULL, FALSE); + g_return_val_if_fail (root != NULL, NULL); do { reply = _atspi_dbus_call_partial (root, atspi_interface_accessible, "GetNavigableAtPoint", error, "iiu", d_x, d_y, d_ctype); @@ -354,6 +354,118 @@ atspi_accessible_get_navigable_at_point (AtspiAccessible *root, return return_value; } +static unsigned char are_objects_on_the_same_bus(AtspiAccessible *obj1, AtspiAccessible *obj2) +{ + const char *bus_name_1 = obj1->parent.app->bus_name; + const char *bus_name_2 = obj2->parent.app->bus_name; + return strcmp(bus_name_1, bus_name_2) == 0; +} + +static unsigned char object_is_valid(AtspiAccessible *obj) +{ + if (!obj) + return 0; + AtspiStateSet *ss = atspi_accessible_get_state_set(obj); + if (!ss) + return 0; + unsigned char valid = atspi_state_set_contains(ss, ATSPI_STATE_DEFUNCT) == 0; + g_object_unref(ss); + return valid; +} + +typedef enum { + NEIGHBOR_SEARCH_MODE_NORMAL = 0, + NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT = 1, + NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING = 2, +} GetNeighborSearchMode; +/** + * atspi_accessible_get_neighbor: + * @root: a pointer to a #AtspiAccessible, which represents current root of subtree to search + * @start: a pointer to the #AtspiAccessible to start search from (can be null, which means start from root) + * @direction: direction, in which search (forward or backward) + * + * Calculates next (or previous) accessible element in logical order or null if none found. + * + * Returns: (nullable) (transfer full): a pointer to an + * #AtspiAccessible element, which is next (previous) in logical order or null if none found. + **/ +AtspiAccessible * +atspi_accessible_get_neighbor (AtspiAccessible *root, + AtspiAccessible *start, + AtspiNeighborSearchDirection direction, + GError **error) +{ + g_return_val_if_fail (object_is_valid(root), NULL); + if (!object_is_valid(start)) + start = root; + const char *root_path = ATSPI_OBJECT(root)->path; + AtspiAccessible *return_value = NULL; + g_object_ref(start); + unsigned char recurse; + GetNeighborSearchMode search_mode = NEIGHBOR_SEARCH_MODE_NORMAL; + GQueue *children_root_stack = g_queue_new(); + + while(1) { + const char *path = are_objects_on_the_same_bus(root, start) ? root_path : ""; + DBusMessage *reply = _atspi_dbus_call_partial (start, atspi_interface_accessible, "GetNeighbor", error, "sii", path, (int)direction, (int)search_mode); + AtspiAccessible *ret = _atspi_dbus_return_accessible_and_recurse_info_from_message (reply, &recurse); + // got return value and request for recursive search, it means ret is on another bridge, than start + // thus we're recursing. should the recurse failed to find anything it will end with + if (ret && recurse) { + g_object_unref(G_OBJECT(start)); + g_queue_push_tail(children_root_stack, ret); + start = ret; + g_object_ref(start); + search_mode = NEIGHBOR_SEARCH_MODE_RECURSE_FROM_ROOT; + continue; + } + // found the one we've been looking for + if (ret) { + g_object_unref(G_OBJECT(start)); + return_value = ret; + break; + } + + // we've stepped into different bridges previously and now we're going back to the last one + // and continuing search where we left + if (!g_queue_is_empty(children_root_stack)) { + g_object_unref(G_OBJECT(start)); + start = g_queue_pop_tail(children_root_stack); + search_mode = NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING; + continue; + } + // there's no more bridges to check, but we might have started from one + // in that case there might be bridges "below" start, which we yet have to visit + if (!are_objects_on_the_same_bus(root, start)) { + unsigned char continue_loop = 1; + while(continue_loop) { + AtspiAccessible *parent = atspi_accessible_get_parent(start, NULL); + continue_loop = parent ? are_objects_on_the_same_bus(start, parent) : 0; + g_object_unref(G_OBJECT(start)); + start = parent; + } + // going up thru parents put us in weird place (we didnt meet root on the way) + // so we bail out + if (!start) + break; + + // start object now points to different bridge and must be treated as "resume after recursing" + search_mode = NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING; + continue; + } + + // nothing found + g_object_unref(start); + return_value = NULL; + break; + } + while(!g_queue_is_empty(children_root_stack)) + g_object_unref(g_queue_pop_tail(children_root_stack)); + g_queue_free(children_root_stack); + + return return_value; +} + /** * atspi_accessible_get_description: * @obj: a pointer to the #AtspiAccessible object on which to operate. diff --git a/atspi/atspi-accessible.h b/atspi/atspi-accessible.h index 82b779e..293946b 100644 --- a/atspi/atspi-accessible.h +++ b/atspi/atspi-accessible.h @@ -83,6 +83,8 @@ gchar * atspi_accessible_get_unique_id (AtspiAccessible *obj, GError **error); AtspiAccessible *atspi_accessible_get_navigable_at_point (AtspiAccessible *root, gint x, gint y, AtspiCoordType ctype, GError **error); +AtspiAccessible *atspi_accessible_get_neighbor (AtspiAccessible *root, AtspiAccessible *start, AtspiNeighborSearchDirection direction, GError **error); + AtspiAccessible * atspi_accessible_get_parent (AtspiAccessible *obj, GError **error); gint atspi_accessible_get_child_count (AtspiAccessible *obj, GError **error); diff --git a/atspi/atspi-constants.h b/atspi/atspi-constants.h index dd10679..24f3852 100644 --- a/atspi/atspi-constants.h +++ b/atspi/atspi-constants.h @@ -136,6 +136,11 @@ typedef enum { ATSPI_COORD_TYPE_WINDOW, } AtspiCoordType; +typedef enum { + ATSPI_NEIGHBOR_SEARCH_FORWARD = 1, + ATSPI_NEIGHBOR_SEARCH_BACKWARD = 2, +} AtspiNeighborSearchDirection; + /** * ATSPI_COORD_TYPE_COUNT: * -- 2.7.4