add attributes table to at_spi2_tool
[platform/upstream/at-spi2-core.git] / test / at_spi2_tool.c
1 #include "atspi/atspi.h"
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <getopt.h>
6 #include <stdbool.h>
7 #include <gio/gio.h>
8 #include <assert.h>
9
10 #define ERROR_STATE -1
11 #define SAFE_BUFFER_SIZE 2048
12 #define CHECK_OUTPUT_WIDTH 42
13 #define MINIMAL_MODULE_WIDTH 3
14 #define ATTR_WIDTH 50
15 #define NUMBER_WIDTH 6
16 #define ARRAY_SIZE(x)  (sizeof(x) / sizeof((x)[0]))
17 #define COLUMN_NO 3
18 #define FLAG_NO 3
19 #define DUMP 0
20 #define CHECK 1
21 #define FIRST_MATCH 2
22 #define RELATION_TABLE_COLUMN_COUNT 7
23 #define ATTRIBUTE_TABLE_COLUMN_COUNT 3
24 #define RELATION_TABLE_COLUMN_WIDTH 20
25 #define ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH 20
26 #define VERSION "1.1"
27
28 static unsigned indent_width = 2;
29 static int module_name_limit = -1;
30
31 static const char* atspi_state_names[] = {
32         [ATSPI_STATE_INVALID] = "INVALID",
33         [ATSPI_STATE_ACTIVE] = "ACTIVE",
34         [ATSPI_STATE_ARMED] = "ARMED",
35         [ATSPI_STATE_BUSY] =  "BUSY",
36         [ATSPI_STATE_CHECKED] = "CHECKED",
37         [ATSPI_STATE_COLLAPSED] = "COLLAPSED",
38         [ATSPI_STATE_DEFUNCT] = "DEFUNCT",
39         [ATSPI_STATE_EDITABLE] = "EDITABLE",
40         [ATSPI_STATE_ENABLED] = "ENABLED",
41         [ATSPI_STATE_EXPANDABLE] = "EXPANDABLE",
42         [ATSPI_STATE_EXPANDED] = "EXPANDED",
43         [ATSPI_STATE_FOCUSABLE] = "FOCUSABLE",
44         [ATSPI_STATE_FOCUSED] = "FOCUSED",
45         [ATSPI_STATE_HAS_TOOLTIP] = "HAS_TOOLTIP",
46         [ATSPI_STATE_HORIZONTAL] = "HORIZONTAL",
47         [ATSPI_STATE_ICONIFIED] = "ICONIFIED",
48         [ATSPI_STATE_MULTI_LINE] = "MULTI_LINE",
49         [ATSPI_STATE_MULTISELECTABLE] = "MULTISELECTABLE",
50         [ATSPI_STATE_OPAQUE] = "OPAQUE",
51         [ATSPI_STATE_PRESSED] = "PRESSED",
52         [ATSPI_STATE_RESIZABLE] = "RESIZABLE",
53         [ATSPI_STATE_SELECTABLE] = "SELECTABLE",
54         [ATSPI_STATE_SELECTED] = "SELECTED",
55         [ATSPI_STATE_SENSITIVE] = "SENSITIVE",
56         [ATSPI_STATE_SHOWING] = "SHOWING",
57         [ATSPI_STATE_SINGLE_LINE] = "SINGLE_LINE",
58         [ATSPI_STATE_STALE] = "STALE",
59         [ATSPI_STATE_TRANSIENT] = "TRANSIENT",
60         [ATSPI_STATE_VERTICAL] = "VERTICAL",
61         [ATSPI_STATE_VISIBLE] = "VISIBLE",
62         [ATSPI_STATE_MANAGES_DESCENDANTS] = "MANAGES_DESCENDANTS",
63         [ATSPI_STATE_INDETERMINATE] = "INDETERMINATE",
64         [ATSPI_STATE_REQUIRED] = "REQUIRED",
65         [ATSPI_STATE_TRUNCATED] = "TRUNCATED",
66         [ATSPI_STATE_ANIMATED] = "ANIMATED",
67         [ATSPI_STATE_INVALID_ENTRY] = "INVALID_ENTRY",
68         [ATSPI_STATE_SUPPORTS_AUTOCOMPLETION] = "SUPPORTS_AUTOCOMPLETION",
69         [ATSPI_STATE_SELECTABLE_TEXT] = "SELECTABLE_TEXT",
70         [ATSPI_STATE_IS_DEFAULT] = "IS_DEFAULT",
71         [ATSPI_STATE_VISITED] = "VISITED",
72         [ATSPI_STATE_CHECKABLE] = "CHECKABLE",
73         [ATSPI_STATE_MODAL] = "MODAL",
74         [ATSPI_STATE_HIGHLIGHTED] = "HIGHLIGHTED",
75         [ATSPI_STATE_HIGHLIGHTABLE] = "HIGHLIGHTABLE",
76         [ATSPI_STATE_HAS_POPUP] = "HAS_POPUP",
77         [ATSPI_STATE_READ_ONLY] = "READ_ONLY",
78         [ATSPI_STATE_LAST_DEFINED] = "LAST_DEFINED"
79 };
80
81 typedef struct _Box_Size {
82         char *x;
83         char *y;
84         char *height;
85         char *width;
86 } Box_Size;
87
88 static char *_multiply_string(char c, unsigned number)
89 {
90         char *result = (char *)calloc(1, number + 1);
91
92         if (result == NULL)
93                 return result;
94
95         memset(result, c, number);
96
97         return result;
98 }
99
100 static void _print_module_legend()
101 {
102         printf("\n[[node],[node role name],[attributes list],[x,y,width,height],[node name],[states list][relations]\n\n");
103 }
104
105 static void _print_atspi_states_legend()
106 {
107         _print_module_legend();
108
109         int array_len = ARRAY_SIZE(atspi_state_names);
110         int line_count = (array_len + COLUMN_NO - 1)/COLUMN_NO;
111         for (int line_idx = 0; line_idx < line_count; line_idx++) {
112                 if ((line_idx < line_count - 1) || (array_len % COLUMN_NO == 0)) {
113                         printf("[%d]\t%-24s[%d]\t%-24s[%d]\t%s\n",
114                                         line_idx * COLUMN_NO,
115                                         atspi_state_names[line_idx * COLUMN_NO],
116                                         (line_idx * COLUMN_NO) + 1,
117                                         atspi_state_names[(line_idx * COLUMN_NO) + 1],
118                                         (line_idx * COLUMN_NO) + 2,
119                                         atspi_state_names[(line_idx * COLUMN_NO) + 2]);
120                 } else if (array_len % COLUMN_NO == 2) {
121                         printf("[%d]\t%-24s[%d]\t%s\n",
122                                         line_idx * COLUMN_NO,
123                                         atspi_state_names[line_idx * COLUMN_NO],
124                                         (line_idx * COLUMN_NO) + 1,
125                                         atspi_state_names[(line_idx * COLUMN_NO) + 1]);
126                 } else {
127                         printf("[%d]\t%s\n", line_idx * COLUMN_NO, atspi_state_names[line_idx * COLUMN_NO]);
128                 }
129         }
130 }
131
132 static void _set_indent(int indent_size)
133 {
134         if (indent_size < 0) {
135                 fprintf(stderr, "WARNING: Invalid indent size. Default value retained.\n");
136                 return;
137         }
138
139         indent_width = indent_size;
140 }
141
142 static void _set_module_length_limit(int limit)
143 {
144         if (limit < MINIMAL_MODULE_WIDTH) {
145                 fprintf(stderr, "WARNING: Invalid maximum field size. Default value retained.\n");
146                 return;
147         }
148
149         module_name_limit = limit;
150 }
151
152 static int _int_sort_function(gconstpointer a, gconstpointer b)
153 {
154         int int_a = GPOINTER_TO_INT (a);
155         int int_b = GPOINTER_TO_INT (b);
156
157         return int_a - int_b;
158 }
159
160 static void _truncate_string(char *string, int length_limit)
161 {
162         if (length_limit < MINIMAL_MODULE_WIDTH || !string)
163                 return;
164
165         int len = strlen(string);
166
167         if (len > length_limit)
168                 memcpy(string + length_limit - 3, "...", 4);
169 }
170
171 static size_t _combine_strings(char **destination, const char *source)
172 {
173         if (!source || !*source || !destination)
174                 return 0;
175
176         size_t old_len = *destination ? strlen(*destination) : 0;
177         size_t source_len = strlen(source);
178         size_t len = old_len + source_len;
179         char *buf = realloc(*destination, (len + 1) * sizeof(char));
180
181         if (!buf) {
182                 fprintf(stderr, "_combine_strings: allocation failed");
183                 return old_len;
184         }
185
186         memcpy(buf + old_len, source, source_len + 1);
187
188         *destination = buf;
189
190         return len;
191 }
192
193 static Box_Size *_get_box_size(AtspiAccessible *node)
194 {
195         Box_Size *box_size = (Box_Size *)malloc(sizeof(Box_Size));
196         if (box_size == NULL)
197                 return NULL;
198
199         char *x = NULL;
200         char *y = NULL;
201         char *width = NULL;
202         char *height = NULL;
203
204         AtspiComponent *component = atspi_accessible_get_component_iface(node);
205         AtspiRect *extent = component ? atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL) : NULL;
206
207         if (extent == NULL) {
208                 _combine_strings(&x, "_");
209                 _combine_strings(&y, "_");
210                 _combine_strings(&width, "_");
211                 _combine_strings(&height, "_");
212         } else {
213                 char temp_buff[NUMBER_WIDTH];
214
215                 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->x);
216                 _combine_strings(&x, temp_buff);
217
218                 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->y);
219                 _combine_strings(&y, temp_buff);
220
221                 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->width);
222                 _combine_strings(&width, temp_buff);
223
224                 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->height);
225                 _combine_strings(&height, temp_buff);
226
227                 g_object_unref(component);
228                 g_free(extent);
229         }
230
231         box_size->x = x;
232         box_size->y = y;
233         box_size->width = width;
234         box_size->height = height;
235
236         return box_size;
237 }
238
239 static char *_get_states(AtspiAccessible *node, int length_limit)
240 {
241         AtspiStateSet *node_state_set = atspi_accessible_get_state_set(node);
242         GArray *states = atspi_state_set_get_states(node_state_set);
243         if (!states) return NULL;
244         g_array_sort(states, _int_sort_function);
245
246         AtspiStateType state_type;
247         char *state_string = NULL;
248
249         for (int i = 0; i < states->len; i++) {
250                 state_type = g_array_index(states, AtspiStateType, i);
251
252                 char node_state_str[8];
253                 snprintf(node_state_str, 8, "(%d)", state_type);
254                 _combine_strings(&state_string, node_state_str);
255         }
256
257         g_array_free(states, 0);
258         g_object_unref(node_state_set);
259
260         _truncate_string(state_string, length_limit);
261
262         return state_string;
263 }
264
265 static char *_get_attributes(AtspiAccessible *node, int length_limit, bool *attributes_are_too_long)
266 {
267         GHashTable *attributes = atspi_accessible_get_attributes(node, NULL);
268
269         if (!attributes)
270                 return NULL;
271
272         GHashTableIter attributes_iter;
273         gpointer attr_key;
274         gpointer attr_value;
275         g_hash_table_iter_init (&attributes_iter, attributes);
276         char *result = NULL;
277         while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) {
278                 char attributes_string[SAFE_BUFFER_SIZE];
279                 int ret = snprintf(attributes_string, SAFE_BUFFER_SIZE, "(%s=%s)", (char *)attr_key, (char *)attr_value);
280                 if (ret >= SAFE_BUFFER_SIZE)
281                         fprintf(stderr, "\n%s, %s %d: generated string is too long. Buffer overflow\n", __FILE__, __FUNCTION__, __LINE__);
282
283                 _combine_strings(&result, attributes_string);
284         }
285         g_hash_table_unref(attributes);
286
287         int real_truncate_size = (length_limit < ATTR_WIDTH && length_limit > MINIMAL_MODULE_WIDTH) ? length_limit : ATTR_WIDTH;
288         if (result && attributes_are_too_long && strlen(result) > real_truncate_size)
289                 *attributes_are_too_long = true;
290
291         _truncate_string(result, real_truncate_size);
292         return result;
293 }
294
295 static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes_are_too_long, bool *app_has_relations)
296 {
297         if (!node)
298                 return NULL;
299
300         char *node_name = atspi_accessible_get_name(node, NULL);
301         char *node_role_name = atspi_accessible_get_role_name(node, NULL);
302         char *unique_id = atspi_accessible_get_unique_id(node, NULL);
303
304         char *attributes = _get_attributes(node, length_limit, attributes_are_too_long);
305         Box_Size *box_size = _get_box_size(node);
306         char *states = _get_states(node, length_limit);
307
308         GArray *relations = atspi_accessible_get_relation_set(node, NULL);
309         bool current_node_has_relations = (relations && relations->len);
310
311         char result[SAFE_BUFFER_SIZE];
312         int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s]]",
313                                                 unique_id,
314                                                 node_role_name,
315                                                 attributes,
316                                                 box_size->x,
317                                                 box_size->y,
318                                                 box_size->width,
319                                                 box_size->height,
320                                                 node_name,
321                                                 states,
322                                                 current_node_has_relations ? "*" : "");
323
324         if (ret >= SAFE_BUFFER_SIZE)
325                 fprintf(stderr, "\n%s, %s %d: generated string is too long. Buffer overflow\n", __FILE__, __FUNCTION__, __LINE__);
326
327         if (current_node_has_relations)
328                 *app_has_relations = true;
329
330         free(node_name);
331         free(node_role_name);
332         free(unique_id);
333         free(attributes);
334         if (box_size) {
335                 free(box_size->width);
336                 free(box_size->height);
337                 free(box_size);
338         }
339         free(states);
340         g_array_free(relations, TRUE);
341
342         return g_strdup(result);
343 }
344
345 static void _test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessible *parent_candidate, int parent_candidate_index)
346 {
347         int parent_index = atspi_accessible_get_index_in_parent(obj, NULL);
348         AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
349
350         char output[CHECK_OUTPUT_WIDTH];
351         if (parent_index != parent_candidate_index || parent_candidate != parent) {
352                 char parent_status[NUMBER_WIDTH];
353
354                 if (parent == NULL)
355                         strncpy(parent_status, "_", NUMBER_WIDTH);
356                 else
357                         snprintf(parent_status, NUMBER_WIDTH, "%d", parent_index);
358
359                 char *parent_unique_id = atspi_accessible_get_unique_id(parent, NULL);
360                 char *parent_candidate_unique_id = atspi_accessible_get_unique_id(parent_candidate, NULL);
361                 snprintf(output, CHECK_OUTPUT_WIDTH, "[FAIL<%d,%s><%s,%s>]", parent_candidate_index, parent_status,
362                                 parent_candidate_unique_id, parent_unique_id);
363                 free(parent_unique_id);
364                 free(parent_candidate_unique_id);
365
366         } else {
367                 snprintf(output, CHECK_OUTPUT_WIDTH, "[OK]");
368         }
369
370         printf("%-*s\t", CHECK_OUTPUT_WIDTH, output);
371 }
372
373 static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible *object, bool check_integrity, int length_limit,
374                                                                                         bool *attributes_are_too_long, bool *app_has_relations)
375 {
376         char *indent = _multiply_string(' ', indent_number*indent_width);
377         if (indent != NULL) {
378                 printf("%s", indent);
379                 free(indent);
380         }
381
382         char *node_info = _get_info(object, length_limit, attributes_are_too_long, app_has_relations);
383         if (node_info != NULL) {
384                 printf("%s\n", node_info);
385                 free(node_info);
386         }
387
388         int count = atspi_accessible_get_child_count(object, NULL);
389         for (int i = 0; i < count; i++) {
390                 AtspiAccessible *child = atspi_accessible_get_child_at_index(object, i, NULL);
391                 if (child) {
392                         if (check_integrity)
393                                 _test_atspi_parent_child_relation(child, object, i);
394
395                         _print_atspi_tree_verify_maybe_r(indent_number + 1, child, check_integrity, length_limit, attributes_are_too_long, app_has_relations);
396                 }
397         }
398         return 0;
399 }
400
401 void _print_help()
402 {
403         printf("AT-SPI2-CORE-UTIL\n\n");
404         printf("USAGE: at_spi2_tool [OPTION] [PARAMETER] ...\n\n");
405         printf("OPTION LIST:\n");
406         printf("-h, --help\t\tshow this message\n");
407         printf("-v, --version\t\tshow actual version of tool\n");
408         printf("-g, --show-legend\tprint AT-SPI state legend\n");
409         printf("-l, --list-apps\t\tlist all applications of desktop\n");
410         printf("-a, --at-spi-client <true|false>\tenable/disable org.a11y.Status.IsEnabled property\n");
411         printf("-s, --sleep <N>\tsleep N seconds\n");
412         printf("-d, --tree-dump\t\tdump tree for selected application\n");
413         printf("-c, --tree-check\tcheck tree for selected application\n");
414         printf("-f, --first-match\tperform dump or check only for first matching application\n");
415         printf("-i, --indent\t\tset indentation size, default value stands at 2\n");
416         printf("-t, --truncate\t\tset maximum single field size, default value stands at 30\n");
417         printf("\nEXAMPLE:\n");
418         printf("\tat_spi2_tool -i3 -d quickpanel\n");
419         printf("\t  show AT-SPI tree for node \"quickpanel\" using three-space indentation\n");
420         printf("\tat_spi2_tool -i1 -c starter\n");
421         printf("\t  show AT-SPI tree with integrity test for node \"starter\" using one-space indentation\n");
422 }
423
424 void _print_version()
425 {
426         printf("AT-SPI2-CORE-UTIL v%s\n", VERSION);
427 }
428
429 static void _print_horizontal_line_in_relations_table() {
430         for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) {
431                 for (int j = 0; j < RELATION_TABLE_COLUMN_WIDTH; j++)
432                         printf("-");
433                 printf("+");
434         }
435         printf("\n");
436 }
437
438 static char *_get_unique_id_of_object_in_relation(AtspiRelation *relation) {
439         if (!relation)
440                 return NULL;
441
442         gint last_index = atspi_relation_get_n_targets(relation) - 1;
443         AtspiAccessible *target = atspi_relation_get_target(relation, last_index);
444         return atspi_accessible_get_unique_id(target, NULL);
445 }
446
447 static void _print_relations_for_object(AtspiAccessible *node) {
448         GArray *relations = atspi_accessible_get_relation_set(node, NULL);
449         if (!relations)
450                 return;
451
452         if (!relations->len) {
453                 g_array_free(relations, FALSE);
454                 return;
455         }
456
457         char **table = calloc(RELATION_TABLE_COLUMN_COUNT, sizeof(char *));
458         if (!table) {
459                 fprintf(stderr, "Calloc failed. Can't alloc memory for object relations string\n");
460                 return;
461         }
462
463         table[0] = atspi_accessible_get_unique_id(node, NULL);
464         for (int i = 0; i < relations->len; i++) {
465                 AtspiRelation *relation = g_array_index(relations, AtspiRelation *, i);
466                 AtspiRelationType type = atspi_relation_get_relation_type(relation);
467                 int idx;
468                 switch (type) {
469                         case ATSPI_RELATION_CONTROLLER_FOR:     idx = 1; break;
470                         case ATSPI_RELATION_CONTROLLED_BY:      idx = 2; break;
471                         case ATSPI_RELATION_FLOWS_TO:           idx = 3; break;
472                         case ATSPI_RELATION_FLOWS_FROM:         idx = 4; break;
473                         case ATSPI_RELATION_DESCRIBED_BY:       idx = 5; break;
474                         default: idx = 0;
475                 }
476
477                 if (idx > 0)
478                         table[idx] = _get_unique_id_of_object_in_relation(relation);
479                 else {
480                         char buf[16];
481                         snprintf(buf, sizeof(buf), " [%d]", type);
482                         _combine_strings(&table[RELATION_TABLE_COLUMN_COUNT - 1], buf);
483                 }
484         }
485
486         for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) {
487                 printf("%*s|", RELATION_TABLE_COLUMN_WIDTH, table[i] ? table[i] : "");
488                 free(table[i]);
489         }
490         free(table);
491
492         printf("\n");
493         _print_horizontal_line_in_relations_table();
494         g_array_free(relations, TRUE);
495 }
496
497 typedef void (*print_information_about_object_function)(AtspiAccessible *);
498
499 static void _iterate_over_tree(print_information_about_object_function func, AtspiAccessible *node) {
500         func(node);
501
502         int count = atspi_accessible_get_child_count(node, NULL);
503         for (int i = 0; i < count; i++) {
504                 AtspiAccessible *child = atspi_accessible_get_child_at_index(node, i, NULL);
505                 if (child)
506                         _iterate_over_tree(func, child);
507         }
508 }
509
510 static void _print_relations_for_objects_in_tree(AtspiAccessible *node) {
511         _iterate_over_tree(_print_relations_for_object, node);
512 }
513
514 static void _print_header_for_relation_table() {
515         char *table[] = {"OBJECT", "CONTROLLER_FOR", "CONTROLLED_BY", "FLOWS_TO", "FLOWS_FROM", "DESCRIBED_BY", "OTHER RELATION [ID]"};
516         assert(ARRAY_SIZE(table) == RELATION_TABLE_COLUMN_COUNT);
517
518         _print_horizontal_line_in_relations_table();
519
520         for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++)
521                 printf("%*s|", RELATION_TABLE_COLUMN_WIDTH , table[i]);
522
523         printf("\n");
524         _print_horizontal_line_in_relations_table();
525 }
526
527 static void _print_relations_table(AtspiAccessible *node) {
528         printf("\nRELATIONS TABLE\n");
529         _print_header_for_relation_table();
530         _print_relations_for_objects_in_tree(node);
531 }
532
533 static void _print_horizontal_line_in_attributes_table() {
534         int size_factor = 1;
535         for (int i = 0; i < ATTRIBUTE_TABLE_COLUMN_COUNT; i++) {
536                 if (i == ATTRIBUTE_TABLE_COLUMN_COUNT - 1)
537                         size_factor = 4;
538                 for (int j = 0; j < ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * size_factor; j++)
539                         printf("-");
540                 printf("+");
541         }
542         printf("\n");
543 }
544
545 static void _print_attributes_for_object(AtspiAccessible *node) {
546         GHashTable *attributes = atspi_accessible_get_attributes(node, NULL);
547
548         if (!attributes)
549                 return;
550
551         char *unique_id = atspi_accessible_get_unique_id(node, NULL);
552         GHashTableIter attributes_iter;
553         gpointer attr_key;
554         gpointer attr_value;
555
556         g_hash_table_iter_init (&attributes_iter, attributes);
557         while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) {
558                 printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH, unique_id ? unique_id : "");
559                 printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH, attr_key ? (char *) attr_key : "");
560                 printf("%*s|\n", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * 4, attr_value ? (char *) attr_value : "");
561         }
562
563         if (g_hash_table_size (attributes))
564                 _print_horizontal_line_in_attributes_table();
565
566         g_hash_table_unref(attributes);
567         free(unique_id);
568 }
569
570 static void _print_attributes_for_objects_in_tree(AtspiAccessible *node) {
571         _iterate_over_tree(_print_attributes_for_object, node);
572 }
573
574 static void _print_header_for_attributes_table() {
575         char *table[] = {"OBJECT", "ATTRIBUTE KEY", "ATTRIBUTE VALUE"};
576         assert(ARRAY_SIZE(table) == ATTRIBUTE_TABLE_COLUMN_COUNT);
577
578         _print_horizontal_line_in_attributes_table();
579
580         int size_factor = 1;
581         for (int i = 0; i < ATTRIBUTE_TABLE_COLUMN_COUNT; i++) {
582                 if (i == ATTRIBUTE_TABLE_COLUMN_COUNT - 1)
583                         size_factor = 4;
584                 printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * size_factor , table[i]);
585         }
586
587         printf("\n");
588         _print_horizontal_line_in_attributes_table();
589 }
590
591 static void _print_attributes_table(AtspiAccessible *node) {
592         printf("\nATTRIBUTES TABLE\n");
593         _print_header_for_attributes_table();
594         _print_attributes_for_objects_in_tree(node);
595 }
596
597 static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bool first_match, int length_limit)
598 {
599
600         AtspiAccessible *desktop = atspi_get_desktop(0);
601         if (!desktop) {
602                 fprintf(stderr, "atspi_get_desktop failed\n");
603                 return;
604         }
605
606         int count = atspi_accessible_get_child_count(desktop, NULL);
607         bool app_name_matched = false;
608
609         for (int i = 0; i < count; i++) {
610                 AtspiAccessible *child = atspi_accessible_get_child_at_index(desktop, i, NULL);
611                 if (child == NULL) {
612                         fprintf(stderr, "\n%s, %s %d: Null application occured. Results may be misleading.\n", __FILE__, __FUNCTION__, __LINE__);
613                         continue;
614                 }
615
616                 char *name = atspi_accessible_get_name(child, NULL);
617                 bool attributes_are_too_long = false;
618                 bool app_has_relations = false;
619
620                 if (!dump && !check)
621                         printf("%s\n", name);
622
623                 if ((check || dump) && name && !strcmp(name, app_name)) {
624                         app_name_matched = true;
625
626                         _print_module_legend();
627
628                         if (check)
629                                 _test_atspi_parent_child_relation(child, desktop, i);
630
631                         _print_atspi_tree_verify_maybe_r(0, child, check, length_limit, &attributes_are_too_long, &app_has_relations);
632
633                         if (app_has_relations)
634                                 _print_relations_table(child);
635
636                         if (attributes_are_too_long)
637                                 _print_attributes_table(child);
638
639                         if (first_match) {
640                                 free(name);
641                                 break;
642                         } else {
643                                 printf("\n");
644                         }
645
646                         free(name);
647                 }
648         }
649
650         if (!app_name_matched && (dump || check))
651                 fprintf(stderr, "There is no application with name: %s. Try again.\n", app_name);
652
653         g_object_unref(desktop);
654 }
655
656 static void _at_spi_client_enable(gboolean enabled)
657 {
658         static GDBusProxy *proxy = NULL; //we keep proxy (dbus connection) until program exits
659         GVariant *result;
660         GError *error = NULL;
661         GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_NONE;
662
663
664         if (!proxy) {
665                 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
666                                         flags,
667                                         NULL, /* GDBusInterfaceInfo */
668                                         "org.a11y.Bus",
669                                         "/org/a11y/bus",
670                                         "org.freedesktop.DBus.Properties",
671                                         NULL, /* GCancellable */
672                                         &error);
673                 if (error) {
674                         fprintf(stderr, "Failed to create proxy object for '/org/a11y/bus': %s\n", error->message);
675                         g_error_free(error);
676                         return;
677                 }
678         }
679
680         result = g_dbus_proxy_call_sync(proxy,
681                                         "Set",
682                                         g_variant_new ("(ssv)",  "org.a11y.Status", "IsEnabled", g_variant_new_boolean(enabled)),
683                                         G_DBUS_CALL_FLAGS_NONE,
684                                         -1,
685                                         NULL,
686                                         &error);
687         if (result)
688                 g_variant_unref(result);
689
690         if (error) {
691                 fprintf(stderr, "Fail to call org.freedesktop.DBus.Properties.Set: %s\n", error->message);
692                 g_error_free(error);
693         }
694 }
695
696 static void _run_command(int argc, char *argv[])
697 {
698         struct option long_options[] = {
699                 {"help", no_argument, 0, 'h'},
700                 {"version", no_argument, 0, 'v'},
701                 {"show-legend", no_argument, 0, 'g'},
702                 {"list-apps", no_argument, 0, 'l'},
703                 {"at-spi-client", no_argument, 0, 'a'},
704                 {"sleep", required_argument, 0, 's'},
705                 {"tree-dump", required_argument, 0, 'd'},
706                 {"tree-check", required_argument, 0, 'c'},
707                 {"first-match", no_argument, 0, 'f'},
708                 {"indent", required_argument, 0, 'i'},
709                 {"truncate", required_argument, 0, 't'},
710         };
711
712         int command = 0;
713         int option_index = 0;
714         bool traverse_flags[FLAG_NO] = {false};
715         char *app_name = NULL;
716         gboolean enable_at_spi_client;
717
718         while (TRUE) {
719                 command = getopt_long(argc, argv, "hvgla:s:d:c:ft:i:", long_options, &option_index);
720
721                 if (command == ERROR_STATE)
722                         break;
723
724                 switch (command) {
725                 case 'h':
726                         _print_help();
727                         break;
728
729                 case 'v':
730                         _print_version();
731                         break;
732
733                 case 'g':
734                         _print_atspi_states_legend();
735                         break;
736
737                 case 'l':
738                         _atspi_tree_traverse(NULL, false, false, false, module_name_limit);
739                         break;
740
741                 case 'a':
742                         enable_at_spi_client = TRUE;
743                         if (optarg[0] == 'f' || optarg[0] == '0')
744                                 enable_at_spi_client = FALSE;
745
746                         _at_spi_client_enable(enable_at_spi_client);
747                         break;
748
749                 case 's':
750                         sleep(atoi(optarg));
751                         break;
752
753                 case 'd':
754                         traverse_flags[DUMP] = true;
755                         app_name = optarg;
756                         break;
757
758                 case 'c':
759                         traverse_flags[CHECK] = true;
760                         app_name = optarg;
761                         break;
762
763                 case 'f':
764                         traverse_flags[FIRST_MATCH] = true;
765                         break;
766
767                 case 'i':
768                         _set_indent(atoi(optarg));
769                         break;
770
771                 case 't':
772                         _set_module_length_limit(atoi(optarg));
773                         break;
774
775                 case '?':
776                         fprintf(stderr, "Invalid parameter. Use: \"--help\"\n");
777                         break;
778
779                 default:
780                         abort();
781                 }
782         }
783
784         if (traverse_flags[DUMP] || traverse_flags[CHECK])
785                 _atspi_tree_traverse(app_name, traverse_flags[DUMP], traverse_flags[CHECK], traverse_flags[FIRST_MATCH], module_name_limit);
786 }
787
788 int main(int argc, char *argv[])
789 {
790         if (argc < 2) {
791                 printf("Arguments required. Type %s --help.\n", argv[0]);
792                 return -1;
793         }
794
795         int result = atspi_init();
796         if (result != 0)
797         {
798                 fprintf(stderr, "Unable to initilize AT-SPI infrastructure, atspi_init failed\n");
799                 return -1;
800         }
801
802         _run_command(argc, argv);
803
804         return 0;
805 }