Add relation-dump option 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 <assert.h>
8
9 #define ERROR_STATE -1
10 #define SAFE_BUFFER_SIZE 2048
11 #define CHECK_OUTPUT_WIDTH 42
12 #define MINIMAL_MODULE_WIDTH 3
13 #define ATTR_WIDTH 50
14 #define NUMBER_WIDTH 6
15 #define ARRAY_SIZE(x)  (sizeof(x) / sizeof((x)[0]))
16 #define COLUMN_NO 3
17 #define FLAG_NO 3
18 #define DUMP 0
19 #define CHECK 1
20 #define FIRST_MATCH 2
21 #define RELATION_TABLE_COLUMN_COUNT 7
22 #define RELATION_COLUMN_WIDTH 20
23 #define VERSION "1.1"
24
25 static unsigned indent_width = 2;
26 static int module_name_limit = -1;
27
28 static const char* atspi_state_names[] = {
29         [ATSPI_STATE_INVALID] = "INVALID",
30         [ATSPI_STATE_ACTIVE] = "ACTIVE",
31         [ATSPI_STATE_ARMED] = "ARMED",
32         [ATSPI_STATE_BUSY] =  "BUSY",
33         [ATSPI_STATE_CHECKED] = "CHECKED",
34         [ATSPI_STATE_COLLAPSED] = "COLLAPSED",
35         [ATSPI_STATE_DEFUNCT] = "DEFUNCT",
36         [ATSPI_STATE_EDITABLE] = "EDITABLE",
37         [ATSPI_STATE_ENABLED] = "ENABLED",
38         [ATSPI_STATE_EXPANDABLE] = "EXPANDABLE",
39         [ATSPI_STATE_EXPANDED] = "EXPANDED",
40         [ATSPI_STATE_FOCUSABLE] = "FOCUSABLE",
41         [ATSPI_STATE_FOCUSED] = "FOCUSED",
42         [ATSPI_STATE_HAS_TOOLTIP] = "HAS_TOOLTIP",
43         [ATSPI_STATE_HORIZONTAL] = "HORIZONTAL",
44         [ATSPI_STATE_ICONIFIED] = "ICONIFIED",
45         [ATSPI_STATE_MULTI_LINE] = "MULTI_LINE",
46         [ATSPI_STATE_MULTISELECTABLE] = "MULTISELECTABLE",
47         [ATSPI_STATE_OPAQUE] = "OPAQUE",
48         [ATSPI_STATE_PRESSED] = "PRESSED",
49         [ATSPI_STATE_RESIZABLE] = "RESIZABLE",
50         [ATSPI_STATE_SELECTABLE] = "SELECTABLE",
51         [ATSPI_STATE_SELECTED] = "SELECTED",
52         [ATSPI_STATE_SENSITIVE] = "SENSITIVE",
53         [ATSPI_STATE_SHOWING] = "SHOWING",
54         [ATSPI_STATE_SINGLE_LINE] = "SINGLE_LINE",
55         [ATSPI_STATE_STALE] = "STALE",
56         [ATSPI_STATE_TRANSIENT] = "TRANSIENT",
57         [ATSPI_STATE_VERTICAL] = "VERTICAL",
58         [ATSPI_STATE_VISIBLE] = "VISIBLE",
59         [ATSPI_STATE_MANAGES_DESCENDANTS] = "MANAGES_DESCENDANTS",
60         [ATSPI_STATE_INDETERMINATE] = "INDETERMINATE",
61         [ATSPI_STATE_REQUIRED] = "REQUIRED",
62         [ATSPI_STATE_TRUNCATED] = "TRUNCATED",
63         [ATSPI_STATE_ANIMATED] = "ANIMATED",
64         [ATSPI_STATE_INVALID_ENTRY] = "INVALID_ENTRY",
65         [ATSPI_STATE_SUPPORTS_AUTOCOMPLETION] = "SUPPORTS_AUTOCOMPLETION",
66         [ATSPI_STATE_SELECTABLE_TEXT] = "SELECTABLE_TEXT",
67         [ATSPI_STATE_IS_DEFAULT] = "IS_DEFAULT",
68         [ATSPI_STATE_VISITED] = "VISITED",
69         [ATSPI_STATE_CHECKABLE] = "CHECKABLE",
70         [ATSPI_STATE_MODAL] = "MODAL",
71         [ATSPI_STATE_HIGHLIGHTED] = "HIGHLIGHTED",
72         [ATSPI_STATE_HIGHLIGHTABLE] = "HIGHLIGHTABLE",
73         [ATSPI_STATE_HAS_POPUP] = "HAS_POPUP",
74         [ATSPI_STATE_READ_ONLY] = "READ_ONLY",
75         [ATSPI_STATE_LAST_DEFINED] = "LAST_DEFINED"
76 };
77
78 typedef struct _Box_Size {
79         char *x;
80         char *y;
81         char *height;
82         char *width;
83 } Box_Size;
84
85 static char *_multiply_string(char c, unsigned number)
86 {
87         char *result = (char *)calloc(1, number + 1);
88
89         if (result == NULL)
90                 return result;
91
92         memset(result, c, number);
93
94         return result;
95 }
96
97 static void _print_module_legend()
98 {
99         printf("\n[[node],[node role name],[attributes list],[x,y,width,height],[node name],[states list][relations]\n\n");
100 }
101
102 static void _print_atspi_states_legend()
103 {
104         _print_module_legend();
105
106         int array_len = ARRAY_SIZE(atspi_state_names);
107         int line_count = (array_len + COLUMN_NO - 1)/COLUMN_NO;
108         for (int line_idx = 0; line_idx < line_count; line_idx++) {
109                 if ((line_idx < line_count - 1) || (array_len % COLUMN_NO == 0)) {
110                         printf("[%d]\t%-24s[%d]\t%-24s[%d]\t%s\n",
111                                         line_idx * COLUMN_NO,
112                                         atspi_state_names[line_idx * COLUMN_NO],
113                                         (line_idx * COLUMN_NO) + 1,
114                                         atspi_state_names[(line_idx * COLUMN_NO) + 1],
115                                         (line_idx * COLUMN_NO) + 2,
116                                         atspi_state_names[(line_idx * COLUMN_NO) + 2]);
117                 } else if (array_len % COLUMN_NO == 2) {
118                         printf("[%d]\t%-24s[%d]\t%s\n",
119                                         line_idx * COLUMN_NO,
120                                         atspi_state_names[line_idx * COLUMN_NO],
121                                         (line_idx * COLUMN_NO) + 1,
122                                         atspi_state_names[(line_idx * COLUMN_NO) + 1]);
123                 } else {
124                         printf("[%d]\t%s\n", line_idx * COLUMN_NO, atspi_state_names[line_idx * COLUMN_NO]);
125                 }
126         }
127 }
128
129 static void _set_indent(int indent_size)
130 {
131         if (indent_size < 0) {
132                 fprintf(stderr, "WARNING: Invalid indent size. Default value retained.\n");
133                 return;
134         }
135
136         indent_width = indent_size;
137 }
138
139 static void _set_module_length_limit(int limit)
140 {
141         if (limit < MINIMAL_MODULE_WIDTH) {
142                 fprintf(stderr, "WARNING: Invalid maximum field size. Default value retained.\n");
143                 return;
144         }
145
146         module_name_limit = limit;
147 }
148
149 static int _int_sort_function(gconstpointer a, gconstpointer b)
150 {
151         int int_a = GPOINTER_TO_INT (a);
152         int int_b = GPOINTER_TO_INT (b);
153
154         return int_a - int_b;
155 }
156
157 static void _truncate_string(char *string, int length_limit)
158 {
159         if (length_limit < MINIMAL_MODULE_WIDTH || !string)
160                 return;
161
162         int len = strlen(string);
163
164         if (len > length_limit)
165                 memcpy(string + length_limit - 3, "...", 4);
166 }
167
168 static size_t _combine_strings(char **destination, const char *source)
169 {
170         if (!source || !*source || !destination)
171                 return 0;
172
173         size_t old_len = *destination ? strlen(*destination) : 0;
174         size_t source_len = strlen(source);
175         size_t len = old_len + source_len;
176         char *buf = realloc(*destination, (len + 1) * sizeof(char));
177
178         if (!buf) {
179                 fprintf(stderr, "_combine_strings: allocation failed");
180                 return old_len;
181         }
182
183         memcpy(buf + old_len, source, source_len + 1);
184
185         *destination = buf;
186
187         return len;
188 }
189
190 static Box_Size *_get_box_size(AtspiAccessible *node)
191 {
192         Box_Size *box_size = (Box_Size *)malloc(sizeof(Box_Size));
193         if (box_size == NULL)
194                 return NULL;
195
196         char *x = NULL;
197         char *y = NULL;
198         char *width = NULL;
199         char *height = NULL;
200
201         AtspiComponent *component = atspi_accessible_get_component_iface(node);
202         AtspiRect *extent = component ? atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL) : NULL;
203
204         if (extent == NULL) {
205                 _combine_strings(&x, "_");
206                 _combine_strings(&y, "_");
207                 _combine_strings(&width, "_");
208                 _combine_strings(&height, "_");
209         } else {
210                 char temp_buff[NUMBER_WIDTH];
211
212                 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->x);
213                 _combine_strings(&x, temp_buff);
214
215                 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->y);
216                 _combine_strings(&y, temp_buff);
217
218                 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->width);
219                 _combine_strings(&width, temp_buff);
220
221                 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->height);
222                 _combine_strings(&height, temp_buff);
223
224                 g_object_unref(component);
225                 g_free(extent);
226         }
227
228         box_size->x = x;
229         box_size->y = y;
230         box_size->width = width;
231         box_size->height = height;
232
233         return box_size;
234 }
235
236 static char *_get_states(AtspiAccessible *node, int length_limit)
237 {
238         AtspiStateSet *node_state_set = atspi_accessible_get_state_set(node);
239         GArray *states = atspi_state_set_get_states(node_state_set);
240         if (!states) return NULL;
241         g_array_sort(states, _int_sort_function);
242
243         AtspiStateType state_type;
244         char *state_string = NULL;
245
246         for (int i = 0; i < states->len; i++) {
247                 state_type = g_array_index(states, AtspiStateType, i);
248
249                 char node_state_str[8];
250                 snprintf(node_state_str, 8, "(%d)", state_type);
251                 _combine_strings(&state_string, node_state_str);
252         }
253
254         g_array_free(states, 0);
255         g_object_unref(node_state_set);
256
257         _truncate_string(state_string, length_limit);
258
259         return state_string;
260 }
261
262 static char *_get_attributes(AtspiAccessible *node, int length_limit)
263 {
264         GHashTable *attributes = atspi_accessible_get_attributes(node, NULL);
265
266         if (!attributes)
267                 return NULL;
268
269         GHashTableIter attributes_iter;
270         gpointer attr_key;
271         gpointer attr_value;
272         g_hash_table_iter_init (&attributes_iter, attributes);
273         char *result = NULL;
274         while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) {
275                 char attributes_string[ATTR_WIDTH];
276                 int ret = snprintf(attributes_string, ATTR_WIDTH, "(%s=%s)", (char *)attr_key, (char *)attr_value);
277                 if (ret >= ATTR_WIDTH)
278                         fprintf(stderr, "\n_get_attributes: generated string is too long. Buffer overflow\n");
279
280                 _combine_strings(&result, attributes_string);
281         }
282         g_hash_table_unref(attributes);
283
284         _truncate_string(result, length_limit);
285         return result;
286 }
287
288 static char *_get_info(AtspiAccessible *node, int length_limit)
289 {
290         if (!node)
291                 return NULL;
292
293         char *node_name = atspi_accessible_get_name(node, NULL);
294         char *node_role_name = atspi_accessible_get_role_name(node, NULL);
295         char *path = atspi_accessible_get_path(node, NULL);
296
297         char *attributes = _get_attributes(node, length_limit);
298         Box_Size *box_size = _get_box_size(node);
299         char *states = _get_states(node, length_limit);
300
301         GArray *relations = atspi_accessible_get_relation_set(node, NULL);
302
303         char result[SAFE_BUFFER_SIZE];
304         int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s]]",
305                                                 path,
306                                                 node_role_name,
307                                                 attributes,
308                                                 box_size->x,
309                                                 box_size->y,
310                                                 box_size->width,
311                                                 box_size->height,
312                                                 node_name,
313                                                 states,
314                                                 (relations && relations->len) ? "*" : "");
315
316         if (ret >= SAFE_BUFFER_SIZE)
317                 fprintf(stderr, "\n%s, %s %s: generated string is too long. Buffer overflow\n", __FILE__, __FUNCTION__, __LINE__);
318
319         free(node_name);
320         free(node_role_name);
321         free(path);
322         free(attributes);
323         if (box_size) {
324                 free(box_size->width);
325                 free(box_size->height);
326                 free(box_size);
327         }
328         free(states);
329         g_array_free(relations, TRUE);
330
331         return g_strdup(result);
332 }
333
334 static void _test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessible *parent_candidate, int parent_candidate_index)
335 {
336         int parent_index = atspi_accessible_get_index_in_parent(obj, NULL);
337         AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
338
339         char output[CHECK_OUTPUT_WIDTH];
340         if (parent_index != parent_candidate_index || parent_candidate != parent) {
341                 char parent_status[NUMBER_WIDTH];
342
343                 if (parent == NULL)
344                         strncpy(parent_status, NUMBER_WIDTH, "_");
345                 else
346                         snprintf(parent_status, NUMBER_WIDTH, "%d", parent_index);
347
348                 char *parent_path = atspi_accessible_get_path(parent, NULL);
349                 char *parent_candidate_path = atspi_accessible_get_path(parent_candidate, NULL);
350                 snprintf(output, CHECK_OUTPUT_WIDTH, "[FAIL<%d,%s><%s,%s>]", parent_candidate_index, parent_status,
351                                 parent_candidate_path, parent_path);
352                 free(parent_path);
353                 free(parent_candidate_path);
354
355         } else {
356                 snprintf(output, CHECK_OUTPUT_WIDTH, "[OK]");
357         }
358
359         printf("%-*s\t", CHECK_OUTPUT_WIDTH, output);
360 }
361
362 static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible *object, bool check_integrity, int length_limit)
363 {
364         char *indent = _multiply_string(' ', indent_number*indent_width);
365         if (indent != NULL) {
366                 printf("%s", indent);
367                 free(indent);
368         }
369
370         char *node_info = _get_info(object, length_limit);
371         if (node_info != NULL) {
372                 printf("%s\n", node_info);
373                 free(node_info);
374         }
375
376         int count = atspi_accessible_get_child_count(object, NULL);
377         for (int i = 0; i < count; i++) {
378                 AtspiAccessible *child = atspi_accessible_get_child_at_index(object, i, NULL);
379                 if (child) {
380                         if (check_integrity)
381                                 _test_atspi_parent_child_relation(child, object, i);
382
383                         _print_atspi_tree_verify_maybe_r(indent_number + 1, child, check_integrity, length_limit);
384                 }
385         }
386         return 0;
387 }
388
389 void _print_help()
390 {
391         printf("AT-SPI2-CORE-UTIL\n\n");
392         printf("USAGE: at_spi2_tool [OPTION] [PARAMETER] ...\n\n");
393         printf("OPTION LIST:\n");
394         printf("-h, --help\t\tshow this message\n");
395         printf("-v, --version\t\tshow actual version of tool\n");
396         printf("-g, --show-legend\tprint AT-SPI state legend\n");
397         printf("-l, --list-apps\t\tlist all applications of desktop\n");
398         printf("-d, --tree-dump\t\tdump tree for selected application\n");
399         printf("-c, --tree-check\tcheck tree for selected application\n");
400         printf("-f, --first-match\tperform dump or check only for first matching application\n");
401         printf("-i, --indent\t\tset indentation size, default value stands at 2\n");
402         printf("-t, --truncate\t\tset maximum single field size, default value stands at 30\n");
403         printf("\nEXAMPLE:\n");
404         printf("\tat_spi2_tool -i3 -d quickpanel\n");
405         printf("\t  show AT-SPI tree for node \"quickpanel\" using three-space indentation\n");
406         printf("\tat_spi2_tool -i1 -c starter\n");
407         printf("\t  show AT-SPI tree with integrity test for node \"starter\" using one-space indentation\n");
408 }
409
410 void _print_version()
411 {
412         printf("AT-SPI2-CORE-UTIL v%s\n", VERSION);
413 }
414
415 static void _print_horizontal_line_in_relation_table() {
416         for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) {
417                 for (int j = 0; j < RELATION_COLUMN_WIDTH; j++)
418                         printf("-");
419                 printf("+");
420         }
421         printf("\n");
422 }
423
424 static void _print_legend_for_relation_table() {
425         char *table[] = {"OBJECT", "CONTROLLER_FOR", "CONTROLLED_BY", "FLOWS_TO", "FLOWS_FROM", "DESCRIBED_BY", "OTHER RELATION [ID]"};
426         assert(sizeof(table) / sizeof(table[0]) == RELATION_TABLE_COLUMN_COUNT);
427         for(int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++)
428                 printf("%*s|", RELATION_COLUMN_WIDTH , table[i]);
429         printf("\n");
430         _print_horizontal_line_in_relation_table();
431 }
432
433 static char *_get_path_to_object_in_relation(AtspiRelation *relation) {
434         if (!relation)
435                 return NULL;
436         gint last_index = atspi_relation_get_n_targets(relation) - 1;
437         AtspiAccessible *target = atspi_relation_get_target(relation, last_index);
438         return atspi_accessible_get_path(target, NULL);
439 }
440
441 static void _print_relations_for_object(AtspiAccessible *node) {
442         GArray *relations = atspi_accessible_get_relation_set(node, NULL);
443         if (!relations)
444                 return;
445
446         if (relations->len) {
447                 int size = RELATION_TABLE_COLUMN_COUNT * sizeof(char *);
448                 char **table = malloc(size);
449                 memset(table, 0, size);
450                 table[0] = atspi_accessible_get_path(node, NULL);
451                 char buf[8] = "";
452                 for (int i = 0; i < relations->len; i++) {
453                         AtspiRelation *relation = g_array_index(relations, AtspiRelation *, i);
454                         AtspiRelationType type = atspi_relation_get_relation_type(relation);
455                         switch (type) {
456                                 case ATSPI_RELATION_CONTROLLER_FOR:
457                                         table[1] = _get_path_to_object_in_relation(relation);
458                                         break;
459                                 case ATSPI_RELATION_CONTROLLED_BY:
460                                         table[2] = _get_path_to_object_in_relation(relation);
461                                         break;
462                                 case ATSPI_RELATION_FLOWS_TO:
463                                         table[3] = _get_path_to_object_in_relation(relation);
464                                         break;
465                                 case ATSPI_RELATION_FLOWS_FROM:
466                                         table[4] = _get_path_to_object_in_relation(relation);
467                                         break;
468                                 case ATSPI_RELATION_DESCRIBED_BY:
469                                         table[5] = _get_path_to_object_in_relation(relation);
470                                         break;
471                                 default:
472                                         snprintf(buf, 8, " [%d]", type);
473                                         _combine_strings(&table[RELATION_TABLE_COLUMN_COUNT - 1], buf);
474                         }
475                 }
476
477                 for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) {
478                         printf("%*s|", RELATION_COLUMN_WIDTH, table[i] ? table[i] : "");
479                         free(table[i]);
480                 }
481                 free(table);
482
483                 printf("\n");
484                 _print_horizontal_line_in_relation_table();
485         }
486         g_array_free(relations, TRUE);
487
488         int count = atspi_accessible_get_child_count(node, NULL);
489         for (int i = 0; i < count; i++) {
490                 AtspiAccessible *child = atspi_accessible_get_child_at_index(node, i, NULL);
491                 if (child)
492                         _print_relations_for_object(child);
493         }
494 }
495
496 static void _atspi_tree_traverse(AtspiAccessible *desktop, const char *app_name, bool dump, bool check, bool first_match, int length_limit)
497 {
498         int count = atspi_accessible_get_child_count(desktop, NULL);
499         bool app_name_matched = false;
500         for (int i = 0; i < count; i++) {
501                 AtspiAccessible *child = atspi_accessible_get_child_at_index(desktop, i, NULL);
502                 if (child == NULL) {
503                         fprintf(stderr, "\n%s, %s %s: Null child occured. Results may be misleading.\n", __FILE__, __FUNCTION__, __LINE__);
504                         continue;
505                 }
506
507                 char *name = atspi_accessible_get_name(child, NULL);
508
509                 if (!dump && !check)
510                         printf("%s\n", name);
511
512                 if ((check || dump) && name && !strcmp(name, app_name)) {
513                         app_name_matched = true;
514
515                         _print_module_legend();
516
517                         if (check)
518                                 _test_atspi_parent_child_relation(child, desktop, i);
519
520                         _print_atspi_tree_verify_maybe_r(0, child, check, length_limit);
521
522                         if (first_match) {
523                                 free(name);
524                                 break;
525                         } else {
526                                 printf("\n");
527                                 _print_legend_for_relation_table();
528                                 _print_relations_for_object(child);
529                         }
530
531                         free(name);
532                 }
533         }
534
535         if (!app_name_matched && (dump || check))
536                 fprintf(stderr, "There is no application with name: %s. Try again.\n", app_name);
537 }
538
539 static void _run_command(int argc, char *argv[], AtspiAccessible *desktop)
540 {
541         struct option long_options[] = {
542                 {"help", no_argument, 0, 'h'},
543                 {"version", no_argument, 0, 'v'},
544                 {"show-legend", no_argument, 0, 'g'},
545                 {"list-apps", no_argument, 0, 'l'},
546                 {"tree-dump", required_argument, 0, 'd'},
547                 {"tree-check", required_argument, 0, 'c'},
548                 {"first-match", no_argument, 0, 'f'},
549                 {"indent", required_argument, 0, 'i'},
550                 {"truncate", required_argument, 0, 't'},
551         };
552
553         int command = 0;
554         int option_index = 0;
555         bool traverse_flags[FLAG_NO] = {false};
556         char *app_name = NULL;
557
558         while (TRUE) {
559                 command = getopt_long(argc, argv, "hvgld:c:ft:i:", long_options, &option_index);
560
561                 if (command == ERROR_STATE)
562                         break;
563
564                 switch (command) {
565                 case 'h':
566                         _print_help();
567                         break;
568
569                 case 'v':
570                         _print_version();
571                         break;
572
573                 case 'g':
574                         _print_atspi_states_legend();
575                         break;
576
577                 case 'l':
578                         _atspi_tree_traverse(desktop, NULL, false, false, false, module_name_limit);
579                         break;
580
581                 case 'd':
582                         traverse_flags[DUMP] = true;
583                         app_name = optarg;
584                         break;
585
586                 case 'c':
587                         traverse_flags[CHECK] = true;
588                         app_name = optarg;
589                         break;
590
591                 case 'f':
592                         traverse_flags[FIRST_MATCH] = true;
593                         break;
594
595                 case 'i':
596                         _set_indent(atoi(optarg));
597                         break;
598
599                 case 't':
600                         _set_module_length_limit(atoi(optarg));
601                         break;
602
603                 case '?':
604                         fprintf(stderr, "Invalid parameter. Use: \"--help\"\n");
605                         break;
606
607                 default:
608                         abort();
609                 }
610         }
611
612         if (traverse_flags[DUMP] || traverse_flags[CHECK])
613                 _atspi_tree_traverse(desktop, app_name, traverse_flags[DUMP], traverse_flags[CHECK], traverse_flags[FIRST_MATCH], module_name_limit);
614 }
615
616 int main(int argc, char *argv[])
617 {
618         if (argc < 2) {
619                 printf("Arguments required. Type %s --help.\n", argv[0]);
620                 return -1;
621         }
622
623         int result = atspi_init();
624         if (result != 0)
625         {
626                 fprintf(stderr, "Unable to initilize AT-SPI infrastructure, atspi_init failed\n");
627                 return -1;
628         }
629
630         AtspiAccessible *desktop = atspi_get_desktop(0);
631         if (!desktop) {
632                 fprintf(stderr, "atspi_get_desktop failed\n");
633                 return -1;
634         }
635
636         _run_command(argc, argv, desktop);
637
638         return 0;
639 }