From 94dd5699432c821fac1552c8a7b80d3c2ce6c63f Mon Sep 17 00:00:00 2001 From: Radoslaw Cybulski Date: Mon, 20 Mar 2017 08:27:18 +0100 Subject: [PATCH] Add navigation helper functions for screen-reader and friends This patch adds GetNavigableAtPoint accessibility interface - the function finds accessibility object at given coordinates. This massively (10-15 times) reduce amount of IPC calls (and time spent) in typical screen-reader scenario. Change-Id: I73a99818cb9d65f0367866d40705c804d5509a20 --- atk-adaptor/adaptors/accessible-adaptor.c | 326 +++++++++++++++++++++- atk-adaptor/object.c | 21 +- atk-adaptor/object.h | 3 + 3 files changed, 345 insertions(+), 5 deletions(-) diff --git a/atk-adaptor/adaptors/accessible-adaptor.c b/atk-adaptor/adaptors/accessible-adaptor.c index 058b116..a84052a 100644 --- a/atk-adaptor/adaptors/accessible-adaptor.c +++ b/atk-adaptor/adaptors/accessible-adaptor.c @@ -29,9 +29,326 @@ #include "atspi/atspi.h" #include "spi-dbus.h" #include "accessible-stateset.h" +#include "accessible-register.h" #include "object.h" #include "introspection.h" #include +#include + + + + + + + + + + + + + +// TIZEN_ONLY(20170310) - implementation of get object under coordinates for accessibility +static AtspiRelationType spi_relation_type_from_atk_relation_type (AtkRelationType type); + +static AtspiRole _object_get_role_impl(void *ptr) +{ + AtkObject *obj = (AtkObject*)ptr; + AtkRole role = atk_object_get_role (obj); + AtspiRole rv = spi_accessible_role_from_atk_role (role); + return rv; +} + +static uint64_t _object_get_state_set_impl(void *ptr) +{ + dbus_uint32_t array[2]; + spi_atk_state_to_dbus_array((AtkObject*)ptr, array); + uint64_t res = array[0] | ((uint64_t)array[1] << 32); + return res; +} + +static void *_get_object_in_relation_by_type_impl(void *ptr, AtspiRelationType expected_relation_type) +{ + if (ptr) { + g_return_val_if_fail (ATK_IS_OBJECT (ptr), NULL); + AtkRelationSet *set = atk_object_ref_relation_set (ptr); + if (!set) + return NULL; + gint count = atk_relation_set_get_n_relations (set), i; + for (i = 0; i < count; i++) + { + AtkRelation *r = atk_relation_set_get_relation (set, i); + if (!r) + continue; + AtkRelationType rt = atk_relation_get_relation_type (r); + AtspiRelationType type = spi_relation_type_from_atk_relation_type (rt); + if (expected_relation_type == type) + { + GPtrArray *target = atk_relation_get_target (r); + if (target && target->len > 0) + return target->pdata[0]; + } + } + g_object_unref (set); + } + return NULL; +} + +static unsigned char _object_is_zero_size_impl(void *ptr) +{ + AtkObject *obj = (AtkObject*)obj; + g_return_val_if_fail(ATK_IS_COMPONENT (obj), 0); + gint x, y, w, h; + atk_component_get_extents (ATK_COMPONENT(obj), &x, &y, &w, &h, ATSPI_COORD_TYPE_SCREEN); + return w == 0 || h == 0; +} + +unsigned char _object_is_scrollable_impl(void *ptr) +{ + AtspiRole role = _object_get_role_impl(ptr); + switch (role) { + case ATSPI_ROLE_SCROLL_PANE: //scroller + case ATSPI_ROLE_LIST: //genlist, list + case ATSPI_ROLE_TREE_TABLE: //gengrid + case ATSPI_ROLE_TOOL_BAR: //toolbar + return 1; + default: + break; + } + return 0; +} + +void *_get_parent_impl(void *ptr) +{ + return atk_object_get_parent((AtkObject*)ptr); +} + +void *_object_at_point_get_impl(void *ptr, int x, int y, unsigned char coordinates_are_screen_based) +{ + AtkObject *obj = (AtkObject*)ptr; + g_return_val_if_fail(ATK_IS_COMPONENT (obj), 0); + return atk_component_ref_accessible_at_point (ATK_COMPONENT(obj), x, y, + coordinates_are_screen_based ? ATSPI_COORD_TYPE_SCREEN : ATSPI_COORD_TYPE_WINDOW); +} + +unsigned char _object_contains_impl(void *ptr, int x, int y, unsigned char coordinates_are_screen_based) +{ + AtkObject *obj = (AtkObject*)ptr; + g_return_val_if_fail(ATK_IS_COMPONENT (obj), 0); + + return atk_component_contains (ATK_COMPONENT(obj), x, y, + coordinates_are_screen_based ? ATSPI_COORD_TYPE_SCREEN : ATSPI_COORD_TYPE_WINDOW); +} + +unsigned char _object_is_proxy_impl(void *ptr) +{ + return 0; +} + +typedef struct { + AtspiRole (*object_get_role)(void *ptr); + uint64_t (*object_get_state_set)(void *ptr); + void *(*get_object_in_relation_by_type)(void *ptr, AtspiRelationType type); + unsigned char (*object_is_zero_size)(void *ptr); + void *(*get_parent)(void *ptr); + unsigned char (*object_is_scrollable)(void *ptr); + void *(*object_at_point_get)(void *ptr, int x, int y, unsigned char coordinates_are_screen_based); + unsigned char (*object_contains)(void *ptr, int x, int y, unsigned char coordinates_are_screen_based); + unsigned char (*object_is_proxy)(void *ptr); +} accessibility_navigation_pointer_table; + +static unsigned char _accept_object_check_role(accessibility_navigation_pointer_table *table, void *obj) +{ + AtspiRole role = table->object_get_role(obj); + switch (role) + { + case ATSPI_ROLE_APPLICATION: + case ATSPI_ROLE_FILLER: + case ATSPI_ROLE_SCROLL_PANE: + case ATSPI_ROLE_SPLIT_PANE: + case ATSPI_ROLE_WINDOW: + case ATSPI_ROLE_IMAGE: + case ATSPI_ROLE_LIST: + case ATSPI_ROLE_ICON: + case ATSPI_ROLE_TOOL_BAR: + case ATSPI_ROLE_REDUNDANT_OBJECT: + case ATSPI_ROLE_COLOR_CHOOSER: + case ATSPI_ROLE_PANEL: + case ATSPI_ROLE_TREE_TABLE: + case ATSPI_ROLE_PAGE_TAB_LIST: + case ATSPI_ROLE_PAGE_TAB: + case ATSPI_ROLE_SPIN_BUTTON: + case ATSPI_ROLE_INPUT_METHOD_WINDOW: + case ATSPI_ROLE_EMBEDDED: + case ATSPI_ROLE_INVALID: + case ATSPI_ROLE_NOTIFICATION: + return 0; + default: + break; + } + return 1; +} + +static unsigned char _state_set_is_set(uint64_t state_set, AtspiStateType state) +{ + return (state_set & ((uint64_t)1 << (unsigned int)state)) != 0; +} + +static unsigned char _object_is_item(accessibility_navigation_pointer_table *table, void *obj) +{ + AtspiRole role = table->object_get_role(obj); + return role == ATSPI_ROLE_LIST_ITEM || role == ATSPI_ROLE_MENU_ITEM; +} + +static unsigned char _object_is_highlightable(accessibility_navigation_pointer_table *table, void *obj) +{ + uint64_t state_set = table->object_get_state_set(obj); + return _state_set_is_set(state_set, ATSPI_STATE_HIGHLIGHTABLE); +} + +static unsigned char _object_is_showing(accessibility_navigation_pointer_table *table, void *obj) +{ + uint64_t state_set = table->object_get_state_set(obj); + return _state_set_is_set(state_set, ATSPI_STATE_SHOWING); +} + +static unsigned char _object_is_collapsed(accessibility_navigation_pointer_table *table, void *obj) +{ + uint64_t state_set = table->object_get_state_set(obj); + return + _state_set_is_set(state_set, ATSPI_STATE_EXPANDABLE) && + !_state_set_is_set(state_set, ATSPI_STATE_EXPANDED); +} + +static unsigned char _object_has_modal_state(accessibility_navigation_pointer_table *table, void *obj) +{ + uint64_t state_set = table->object_get_state_set(obj); + return _state_set_is_set(state_set, ATSPI_STATE_MODAL); +} + +static unsigned char _object_is_zero_size(accessibility_navigation_pointer_table *table, void *obj) +{ + return table->object_is_zero_size(obj); +} + +static void *_get_scrollable_parent(accessibility_navigation_pointer_table *table, void *obj) +{ + while(obj) + { + obj = table->get_parent(obj); + if (obj && table->object_is_scrollable(obj)) return obj; + } + return NULL; +} +static unsigned char _accept_object(accessibility_navigation_pointer_table *table, void *obj) +{ + if (!obj) return 0; + if (!_accept_object_check_role(table, obj)) return 0; + if (table->get_object_in_relation_by_type(obj, ATSPI_RELATION_CONTROLLED_BY) != NULL) return 0; + if (!_object_is_highlightable(table, obj)) return 0; + + if (_get_scrollable_parent(table, obj) != NULL) + { + void *parent = table->get_parent(obj); + + if (parent) + { + return !_object_is_item(table, obj) || !_object_is_collapsed(table, parent); + } + } + else + { + if (_object_is_zero_size(table, obj)) return 0; + if (!_object_is_showing(table, obj)) return 0; + } + return 1; +} + +static void *_calculate_navigable_accessible_at_point_impl(accessibility_navigation_pointer_table *table, + void *root, int x, int y, unsigned char coordinates_are_screen_based) +{ + if (!root) return NULL; + + void *return_value = NULL; + while (1) + { + void *target = table->object_at_point_get(root, x, y, coordinates_are_screen_based); + if (!target) break; + + // always return proxy, so atspi lib can call on it again + if (table->object_is_proxy(target)) return target; + + root = target; + void *relation_obj = table->get_object_in_relation_by_type(root, ATSPI_RELATION_CONTROLLED_BY); + unsigned char contains = 0; + if (relation_obj) + { + contains = table->object_contains(relation_obj, x, y, coordinates_are_screen_based); + if (contains) root = relation_obj; + } + + if (_accept_object(table, root)) + { + return_value = root; + if (contains) break; + } + } + + if (return_value && _object_has_modal_state(table, return_value)) return_value = NULL; + return return_value; +} + +accessibility_navigation_pointer_table construct_accessibility_navigation_pointer_table(void) +{ + accessibility_navigation_pointer_table table; +#define INIT(n) table.n = _## n ## _impl + INIT(object_get_role); + INIT(object_get_state_set); + INIT(get_object_in_relation_by_type); + INIT(object_is_zero_size); + INIT(get_parent); + INIT(object_is_scrollable); + INIT(object_at_point_get); + INIT(object_contains); + INIT(object_is_proxy); +#undef INIT + return table; +} +static AtkObject *_calculate_navigable_accessible_at_point(AtkObject *root, unsigned char coord_type, int x, int y) +{ + accessibility_navigation_pointer_table table = construct_accessibility_navigation_pointer_table(); + AtkObject *result = (AtkObject*)_calculate_navigable_accessible_at_point_impl(&table, root, x, y, coord_type ? 1 : 0); + return result; +} + +static DBusMessage * +impl_GetNavigableAtPoint (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + dbus_int32_t x, y; + dbus_uint32_t coord_type; + DBusMessage *reply; + AtkObject *child; + + g_return_val_if_fail (ATK_IS_COMPONENT (object), + droute_not_yet_handled_error (message)); + + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, + DBUS_TYPE_UINT32, &coord_type, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + child = (AtkObject*) + _calculate_navigable_accessible_at_point (object, coord_type == ATSPI_COORD_TYPE_SCREEN, x, y); + + reply = spi_object_return_reference_and_recurse_flag (message, child, 0); + if (child) + g_object_unref (child); + + return reply; +} +// static dbus_bool_t impl_get_Name (DBusMessageIter * iter, void *user_data) @@ -151,7 +468,7 @@ impl_GetChildAtIndex (DBusConnection * bus, g_return_val_if_fail (ATK_IS_OBJECT (user_data), droute_not_yet_handled_error (message)); - if (!dbus_message_get_args + if (!dbus_message_get_args (message, NULL, DBUS_TYPE_INT32, &i, DBUS_TYPE_INVALID)) { return droute_invalid_arguments_error (message); @@ -210,7 +527,7 @@ impl_GetChildren (DBusConnection * bus, for (i = 0; i < count; i++) { AtkObject *child = atk_object_ref_accessible_child (object, i); - spi_object_append_reference (&iter_array, child); + spi_object_append_reference (&iter_array, child); if (child) g_object_unref (child); } @@ -524,6 +841,9 @@ impl_GetInterfaces (DBusConnection * bus, } static DRouteMethod methods[] = { +// TIZEN_ONLY(20170310) - implementation of get object under coordinates for accessibility + {impl_GetNavigableAtPoint, "GetNavigableAtPoint"}, +// {impl_GetChildAtIndex, "GetChildAtIndex"}, {impl_GetChildren, "GetChildren"}, {impl_GetIndexInParent, "GetIndexInParent"}, @@ -553,6 +873,6 @@ spi_initialize_accessible (DRoutePath * path) { spi_atk_add_interface (path, ATSPI_DBUS_INTERFACE_ACCESSIBLE, - spi_org_a11y_atspi_Accessible, + spi_org_a11y_atspi_Accessible, methods, properties); }; diff --git a/atk-adaptor/object.c b/atk-adaptor/object.c index bdc1625..f053f24 100644 --- a/atk-adaptor/object.c +++ b/atk-adaptor/object.c @@ -114,7 +114,7 @@ spi_object_append_reference (DBusMessageIter * iter, AtkObject * obj) dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name); dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_close_container (iter, &iter_struct); - + g_free (path); } @@ -145,7 +145,7 @@ spi_hyperlink_append_reference (DBusMessageIter * iter, AtkHyperlink * obj) dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name); dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_close_container (iter, &iter_struct); - + g_free (path); } @@ -190,6 +190,23 @@ spi_object_return_reference (DBusMessage * msg, AtkObject * obj) return reply; } +DBusMessage * +spi_object_return_reference_and_recurse_flag (DBusMessage * msg, AtkObject * obj, unsigned char recurse) +{ + DBusMessage *reply; + + reply = dbus_message_new_method_return (msg); + if (reply) + { + DBusMessageIter iter; + dbus_message_iter_init_append (reply, &iter); + spi_object_append_reference (&iter, obj); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_BYTE, &recurse); + } + + return reply; +} + DBusMessage * spi_hyperlink_return_reference (DBusMessage * msg, AtkHyperlink * obj) { diff --git a/atk-adaptor/object.h b/atk-adaptor/object.h index 5f283bc..14f0fa5 100644 --- a/atk-adaptor/object.h +++ b/atk-adaptor/object.h @@ -48,6 +48,9 @@ spi_object_append_null_reference (DBusMessageIter * iter); DBusMessage * spi_object_return_reference (DBusMessage * msg, AtkObject * obj); +DBusMessage * +spi_object_return_reference_and_recurse_flag (DBusMessage * msg, AtkObject * obj, unsigned char recurse); + DBusMessage * spi_hyperlink_return_reference (DBusMessage * msg, AtkHyperlink * obj); -- 2.34.1