+static void _print_horizontal_line_in_relations_table() {
+ for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) {
+ for (int j = 0; j < RELATION_TABLE_COLUMN_WIDTH; j++)
+ printf("-");
+ printf("+");
+ }
+ printf("\n");
+}
+
+static char *_get_unique_id_of_object_in_relation(AtspiRelation *relation) {
+ if (!relation)
+ return NULL;
+
+ gint last_index = atspi_relation_get_n_targets(relation) - 1;
+ AtspiAccessible *target = atspi_relation_get_target(relation, last_index);
+ return atspi_accessible_get_unique_id(target, NULL);
+}
+
+static void _print_relations_for_object(AtspiAccessible *node) {
+ GArray *relations = atspi_accessible_get_relation_set(node, NULL);
+ if (!relations)
+ return;
+
+ if (!relations->len) {
+ g_array_free(relations, FALSE);
+ return;
+ }
+
+ char **table = calloc(RELATION_TABLE_COLUMN_COUNT, sizeof(char *));
+ if (!table) {
+ fprintf(stderr, "Calloc failed. Can't alloc memory for object relations string\n");
+ return;
+ }
+
+ table[0] = atspi_accessible_get_unique_id(node, NULL);
+ for (int i = 0; i < relations->len; i++) {
+ AtspiRelation *relation = g_array_index(relations, AtspiRelation *, i);
+ AtspiRelationType type = atspi_relation_get_relation_type(relation);
+ int idx;
+ switch (type) {
+ case ATSPI_RELATION_CONTROLLER_FOR: idx = 1; break;
+ case ATSPI_RELATION_CONTROLLED_BY: idx = 2; break;
+ case ATSPI_RELATION_FLOWS_TO: idx = 3; break;
+ case ATSPI_RELATION_FLOWS_FROM: idx = 4; break;
+ case ATSPI_RELATION_DESCRIBED_BY: idx = 5; break;
+ default: idx = 0;
+ }
+
+ if (idx > 0)
+ table[idx] = _get_unique_id_of_object_in_relation(relation);
+ else {
+ char buf[16];
+ snprintf(buf, sizeof(buf), " [%d]", type);
+ _combine_strings(&table[RELATION_TABLE_COLUMN_COUNT - 1], buf);
+ }
+ }
+
+ for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) {
+ printf("%*s|", RELATION_TABLE_COLUMN_WIDTH, table[i] ? table[i] : "");
+ free(table[i]);
+ }
+ free(table);
+
+ printf("\n");
+ _print_horizontal_line_in_relations_table();
+ if (relations)
+ g_array_free(relations, TRUE);
+}
+
+typedef void (*print_information_about_object_function)(AtspiAccessible *);
+
+static void _iterate_over_tree(print_information_about_object_function func, AtspiAccessible *node) {
+ func(node);
+
+ int count = atspi_accessible_get_child_count(node, NULL);
+ for (int i = 0; i < count; i++) {
+ AtspiAccessible *child = atspi_accessible_get_child_at_index(node, i, NULL);
+ if (child)
+ _iterate_over_tree(func, child);
+ }
+}
+
+static void _print_relations_for_objects_in_tree(AtspiAccessible *node) {
+ _iterate_over_tree(_print_relations_for_object, node);
+}
+
+static void _print_header_for_relation_table() {
+ char *table[] = {"OBJECT", "CONTROLLER_FOR", "CONTROLLED_BY", "FLOWS_TO", "FLOWS_FROM", "DESCRIBED_BY", "OTHER RELATION [ID]"};
+ assert(ARRAY_SIZE(table) == RELATION_TABLE_COLUMN_COUNT);
+
+ _print_horizontal_line_in_relations_table();
+
+ for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++)
+ printf("%*s|", RELATION_TABLE_COLUMN_WIDTH , table[i]);
+
+ printf("\n");
+ _print_horizontal_line_in_relations_table();
+}
+
+static void _print_relations_table(AtspiAccessible *node) {
+ printf("\nRELATIONS TABLE\n");
+ _print_header_for_relation_table();
+ _print_relations_for_objects_in_tree(node);
+}
+
+static void _print_horizontal_line_in_attributes_table() {
+ int size_factor = 1;
+ for (int i = 0; i < ATTRIBUTE_TABLE_COLUMN_COUNT; i++) {
+ if (i == ATTRIBUTE_TABLE_COLUMN_COUNT - 1)
+ size_factor = 4;
+ for (int j = 0; j < ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * size_factor; j++)
+ printf("-");
+ printf("+");
+ }
+ printf("\n");
+}
+
+static void _print_attributes_for_object(AtspiAccessible *node) {
+ GHashTable *attributes = atspi_accessible_get_attributes(node, NULL);
+
+ if (!attributes)
+ return;
+
+ char *unique_id = atspi_accessible_get_unique_id(node, NULL);
+ GHashTableIter attributes_iter;
+ gpointer attr_key;
+ gpointer attr_value;
+
+ g_hash_table_iter_init (&attributes_iter, attributes);
+ while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) {
+ printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH, unique_id ? unique_id : "");
+ printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH, attr_key ? (char *) attr_key : "");
+ printf("%*s|\n", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * 4, attr_value ? (char *) attr_value : "");
+ }
+
+ if (g_hash_table_size (attributes))
+ _print_horizontal_line_in_attributes_table();
+
+ g_hash_table_unref(attributes);
+ free(unique_id);
+}
+
+static void _print_attributes_for_objects_in_tree(AtspiAccessible *node) {
+ _iterate_over_tree(_print_attributes_for_object, node);
+}
+
+static void _print_header_for_attributes_table() {
+ char *table[] = {"OBJECT", "ATTRIBUTE KEY", "ATTRIBUTE VALUE"};
+ assert(ARRAY_SIZE(table) == ATTRIBUTE_TABLE_COLUMN_COUNT);
+
+ _print_horizontal_line_in_attributes_table();
+
+ int size_factor = 1;
+ for (int i = 0; i < ATTRIBUTE_TABLE_COLUMN_COUNT; i++) {
+ if (i == ATTRIBUTE_TABLE_COLUMN_COUNT - 1)
+ size_factor = 4;
+ printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * size_factor , table[i]);
+ }
+
+ printf("\n");
+ _print_horizontal_line_in_attributes_table();
+}
+
+static void _print_attributes_table(AtspiAccessible *node) {
+ printf("\nATTRIBUTES TABLE\n");
+ _print_header_for_attributes_table();
+ _print_attributes_for_objects_in_tree(node);
+}
+
+static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bool first_match, int length_limit)