+
+ dbus_message_iter_init (reply, &iter);
+
+ /* get attributes */
+ reading_material->attributes = _atspi_dbus_hash_from_iter (&iter);
+ dbus_message_iter_next (&iter);
+
+ /* get name */
+ dbus_message_iter_get_basic (&iter, &name);
+ reading_material->name = g_strdup (name);
+ dbus_message_iter_next (&iter);
+
+ /* get name of relation LABELED_BY */
+ dbus_message_iter_get_basic (&iter, &name);
+ reading_material->labeled_by_name = g_strdup (name);
+ dbus_message_iter_next (&iter);
+
+ /* get name of text interface */
+ dbus_message_iter_get_basic (&iter, &name);
+ reading_material->text_interface_name = g_strdup (name);
+ dbus_message_iter_next (&iter);
+
+ /* get role */
+ dbus_message_iter_get_basic (&iter, &role);
+ reading_material->role = role;
+ dbus_message_iter_next (&iter);
+
+ /* get state set */
+ dbus_message_iter_recurse (&iter, &iter_array);
+ dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
+ val = ((guint64)states [1]) << 32;
+ val += states [0];
+ reading_material->states = val;
+ dbus_message_iter_next (&iter);
+
+ /* get localized role name */
+ dbus_message_iter_get_basic (&iter, &name);
+ reading_material->localized_role_name = g_strdup (name);
+ dbus_message_iter_next (&iter);
+
+ /* get child count */
+ dbus_message_iter_get_basic (&iter, &child_count);
+ reading_material->child_count = child_count;
+ dbus_message_iter_next (&iter);
+
+ /* get current value */
+ dbus_message_iter_get_basic (&iter, ¤t_value);
+ reading_material->value = current_value;
+ dbus_message_iter_next (&iter);
+
+ /* get minimum increment */
+ dbus_message_iter_get_basic (&iter, ¤t_value);
+ reading_material->increment = current_value;
+ dbus_message_iter_next (&iter);
+
+ /* get maximum value */
+ dbus_message_iter_get_basic (&iter, ¤t_value);
+ reading_material->upper = current_value;
+ dbus_message_iter_next (&iter);
+
+ /* get minimum value */
+ dbus_message_iter_get_basic (&iter, ¤t_value);
+ reading_material->lower = current_value;
+ dbus_message_iter_next (&iter);
+
+ /* get description */
+ dbus_message_iter_get_basic (&iter, &name);
+ reading_material->description = g_strdup (name);
+ dbus_message_iter_next (&iter);
+
+ /* get index in parent */
+ dbus_message_iter_get_basic (&iter, &index_in_parent);
+ reading_material->index_in_parent = index_in_parent;
+ dbus_message_iter_next (&iter);
+
+ /* get selected in parent */
+ dbus_message_iter_get_basic (&iter, &is_selected);
+ reading_material->is_selected_in_parent = is_selected;
+ dbus_message_iter_next (&iter);
+
+ /* get has checkbox child */
+ dbus_message_iter_get_basic (&iter, &is_selected);
+ reading_material->has_checkbox_child = is_selected;
+ dbus_message_iter_next (&iter);
+
+ /* get list children count */
+ dbus_message_iter_get_basic (&iter, &child_count);
+ reading_material->list_children_count = child_count;
+ dbus_message_iter_next (&iter);
+
+ /* get first selected child index */
+ dbus_message_iter_get_basic (&iter, &index_in_parent);
+ reading_material->first_selected_child_index = index_in_parent;
+ dbus_message_iter_next (&iter);
+
+ ////////////////
+ /* get parent */
+ parent = _atspi_dbus_return_accessible_from_iter (&iter);
+ reading_material->parent = parent;
+
+ /* parent states */
+ dbus_message_iter_recurse (&iter, &iter_array);
+ dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
+ val = ((guint64)states [1]) << 32;
+ val += states [0];
+ reading_material->parent_states = val;
+ dbus_message_iter_next (&iter);
+
+ /* get parent child count */
+ dbus_message_iter_get_basic (&iter, &child_count);
+ reading_material->parent_child_count = child_count;
+ dbus_message_iter_next (&iter);
+
+ /* get parent role */
+ dbus_message_iter_get_basic (&iter, &role);
+ reading_material->parent_role = role;
+ dbus_message_iter_next (&iter);
+
+ /* get parent selected child count */
+ dbus_message_iter_get_basic (&iter, &child_count);
+ reading_material->parent_selected_child_count = child_count;
+ dbus_message_iter_next (&iter);
+
+ ////////////////////////////////////////
+ /* get relation object - DESCRIBED_BY */
+ parent = _atspi_dbus_return_accessible_from_iter (&iter);
+ reading_material->described_by_accessible = parent;
+
+ return reading_material;
+}
+
+/**
+ * atspi_accessible_get_default_label_info:
+ * @obj: a pointer to the #AtspiAccessible object would be window.
+ *
+ * Gets default label information
+ *
+ * Returns: default label information to be used screen-reader side.
+ * This is not stable. And this depends on toolkit side UI definition.
+ * The candidate of default label object could be changed by UI definition.
+ * You have to handle all alocated memory as below on screen-reader side.
+ *
+ * AtspiAccessibleDefaultLabelInfo *dli
+ * g_hash_table_unref(dli->attributes);
+
+ * g_object_unref(dli->obj);
+ * free(dli);
+ **/
+AtspiAccessibleDefaultLabelInfo *
+atspi_accessible_get_default_label_info (AtspiAccessible *obj, GError **error)
+{
+ AtspiAccessibleDefaultLabelInfo *default_label_info = NULL;
+ AtspiAccessible *default_label_object;
+ dbus_uint32_t role;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ g_return_val_if_fail (obj != NULL, NULL);
+
+ reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetDefaultLabelInfo", error, "");
+
+ _ATSPI_DBUS_CHECK_SIG (reply, "(so)ua{ss}", NULL, NULL);
+
+ default_label_info = calloc(1, sizeof(AtspiAccessibleDefaultLabelInfo));
+ if (!default_label_info)
+ {
+ return default_label_info;
+ }
+
+ dbus_message_iter_init (reply, &iter);
+
+ default_label_object = _atspi_dbus_return_accessible_from_iter (&iter);
+ default_label_info->obj = default_label_object;
+
+ dbus_message_iter_get_basic (&iter, &role);
+ default_label_info->role = role;
+ dbus_message_iter_next (&iter);
+
+ default_label_info->attributes = _atspi_dbus_hash_from_iter (&iter);
+ dbus_message_iter_next (&iter);
+
+ return default_label_info;
+}
+
+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,
+ NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE = 3,
+} 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();
+ DBusMessageIter iter;
+
+ 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);
+ // call failed, error is set, so we bail out
+ if (!reply) break;
+
+ _ATSPI_DBUS_CHECK_SIG (reply, "(so)y", error, NULL);
+ dbus_message_iter_init (reply, &iter);
+ AtspiAccessible *ret = _atspi_dbus_return_accessible_from_iter (&iter);
+
+ unsigned char value = 0;
+ dbus_message_iter_get_basic (&iter, &value);
+ dbus_message_iter_next (&iter);
+ recurse = (value != 0);
+
+ dbus_message_unref(reply);
+
+ // 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));
+ start = ret;
+ g_object_ref(start);
+ if (are_objects_on_the_same_bus(root, ret))
+ {
+ search_mode = NEIGHBOR_SEARCH_MODE_RECURSE_TO_OUTSIDE;
+ }
+ else
+ {
+ g_queue_push_tail(children_root_stack, ret);
+ 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);
+ 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;