Add navigation helper functions for screen-reader and friends 41/136141/1
authorRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Mon, 20 Mar 2017 07:27:18 +0000 (08:27 +0100)
committerShinwoo Kim <cinoo.kim@samsung.com>
Wed, 28 Jun 2017 08:08:40 +0000 (17:08 +0900)
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
atk-adaptor/object.c
atk-adaptor/object.h

index 058b11698a30ebdabe2745d52ae4c5bd7439b856..a84052a03325df746fc06972de65419aff551e8f 100644 (file)
 #include "atspi/atspi.h"
 #include "spi-dbus.h"
 #include "accessible-stateset.h"
+#include "accessible-register.h"
 #include "object.h"
 #include "introspection.h"
 #include <string.h>
+#include <stdint.h>
+
+
+
+
+
+
+
+
+
+
+
+
+
+// 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);
 };
index bdc1625bebcf849c72fe57b0058c57b285d54fff..f053f24e4d6de272ea1edf3803d53cdaa2942bee 100644 (file)
@@ -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)
 {
index 5f283bce0e3e5d2abf747b6281551ef8ffccee7e..14f0fa54e789b3d6f9ac20c8982b84cb56fa05f0 100644 (file)
@@ -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);