1 #include "atspi/atspi.h"
11 #define ERROR_STATE -1
12 #define SAFE_BUFFER_SIZE 2048
13 #define CHECK_OUTPUT_WIDTH 42
14 #define MINIMAL_MODULE_WIDTH 3
16 #define NUMBER_WIDTH 6
17 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
23 #define RELATION_TABLE_COLUMN_COUNT 7
24 #define ATTRIBUTE_TABLE_COLUMN_COUNT 3
25 #define ACTIONS_TABLE_COLUMN_COUNT 2
26 #define RELATION_TABLE_COLUMN_WIDTH 20
27 #define ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH 20
28 #define ACTIONS_TABLE_BASE_COLUMN_WIDTH 20
31 static unsigned indent_width = 2;
32 static int module_name_limit = -1;
34 static const char* atspi_state_names[] = {
35 [ATSPI_STATE_INVALID] = "INVALID",
36 [ATSPI_STATE_ACTIVE] = "ACTIVE",
37 [ATSPI_STATE_ARMED] = "ARMED",
38 [ATSPI_STATE_BUSY] = "BUSY",
39 [ATSPI_STATE_CHECKED] = "CHECKED",
40 [ATSPI_STATE_COLLAPSED] = "COLLAPSED",
41 [ATSPI_STATE_DEFUNCT] = "DEFUNCT",
42 [ATSPI_STATE_EDITABLE] = "EDITABLE",
43 [ATSPI_STATE_ENABLED] = "ENABLED",
44 [ATSPI_STATE_EXPANDABLE] = "EXPANDABLE",
45 [ATSPI_STATE_EXPANDED] = "EXPANDED",
46 [ATSPI_STATE_FOCUSABLE] = "FOCUSABLE",
47 [ATSPI_STATE_FOCUSED] = "FOCUSED",
48 [ATSPI_STATE_HAS_TOOLTIP] = "HAS_TOOLTIP",
49 [ATSPI_STATE_HORIZONTAL] = "HORIZONTAL",
50 [ATSPI_STATE_ICONIFIED] = "ICONIFIED",
51 [ATSPI_STATE_MULTI_LINE] = "MULTI_LINE",
52 [ATSPI_STATE_MULTISELECTABLE] = "MULTISELECTABLE",
53 [ATSPI_STATE_OPAQUE] = "OPAQUE",
54 [ATSPI_STATE_PRESSED] = "PRESSED",
55 [ATSPI_STATE_RESIZABLE] = "RESIZABLE",
56 [ATSPI_STATE_SELECTABLE] = "SELECTABLE",
57 [ATSPI_STATE_SELECTED] = "SELECTED",
58 [ATSPI_STATE_SENSITIVE] = "SENSITIVE",
59 [ATSPI_STATE_SHOWING] = "SHOWING",
60 [ATSPI_STATE_SINGLE_LINE] = "SINGLE_LINE",
61 [ATSPI_STATE_STALE] = "STALE",
62 [ATSPI_STATE_TRANSIENT] = "TRANSIENT",
63 [ATSPI_STATE_VERTICAL] = "VERTICAL",
64 [ATSPI_STATE_VISIBLE] = "VISIBLE",
65 [ATSPI_STATE_MANAGES_DESCENDANTS] = "MANAGES_DESCENDANTS",
66 [ATSPI_STATE_INDETERMINATE] = "INDETERMINATE",
67 [ATSPI_STATE_REQUIRED] = "REQUIRED",
68 [ATSPI_STATE_TRUNCATED] = "TRUNCATED",
69 [ATSPI_STATE_ANIMATED] = "ANIMATED",
70 [ATSPI_STATE_INVALID_ENTRY] = "INVALID_ENTRY",
71 [ATSPI_STATE_SUPPORTS_AUTOCOMPLETION] = "SUPPORTS_AUTOCOMPLETION",
72 [ATSPI_STATE_SELECTABLE_TEXT] = "SELECTABLE_TEXT",
73 [ATSPI_STATE_IS_DEFAULT] = "IS_DEFAULT",
74 [ATSPI_STATE_VISITED] = "VISITED",
75 [ATSPI_STATE_CHECKABLE] = "CHECKABLE",
76 [ATSPI_STATE_MODAL] = "MODAL",
77 [ATSPI_STATE_HIGHLIGHTED] = "HIGHLIGHTED",
78 [ATSPI_STATE_HIGHLIGHTABLE] = "HIGHLIGHTABLE",
79 [ATSPI_STATE_HAS_POPUP] = "HAS_POPUP",
80 [ATSPI_STATE_READ_ONLY] = "READ_ONLY",
81 [ATSPI_STATE_LAST_DEFINED] = "LAST_DEFINED"
84 typedef struct _Box_Size {
91 static char *_multiply_string(char c, unsigned number)
93 char *result = (char *)calloc(1, number + 1);
98 memset(result, c, number);
103 static void _print_module_legend()
105 printf("\n[[node],[node role name],[attributes list],[x,y,width,height],[node name],[states list][relations]\n\n");
108 static void _print_atspi_states_legend()
110 _print_module_legend();
112 int array_len = ARRAY_SIZE(atspi_state_names);
113 int line_count = (array_len + COLUMN_NO - 1)/COLUMN_NO;
114 for (int line_idx = 0; line_idx < line_count; line_idx++) {
115 if ((line_idx < line_count - 1) || (array_len % COLUMN_NO == 0)) {
116 printf("[%d]\t%-24s[%d]\t%-24s[%d]\t%s\n",
117 line_idx * COLUMN_NO,
118 atspi_state_names[line_idx * COLUMN_NO],
119 (line_idx * COLUMN_NO) + 1,
120 atspi_state_names[(line_idx * COLUMN_NO) + 1],
121 (line_idx * COLUMN_NO) + 2,
122 atspi_state_names[(line_idx * COLUMN_NO) + 2]);
123 } else if (array_len % COLUMN_NO == 2) {
124 printf("[%d]\t%-24s[%d]\t%s\n",
125 line_idx * COLUMN_NO,
126 atspi_state_names[line_idx * COLUMN_NO],
127 (line_idx * COLUMN_NO) + 1,
128 atspi_state_names[(line_idx * COLUMN_NO) + 1]);
130 printf("[%d]\t%s\n", line_idx * COLUMN_NO, atspi_state_names[line_idx * COLUMN_NO]);
135 static void _set_indent(int indent_size)
137 if (indent_size < 0) {
138 fprintf(stderr, "WARNING: Invalid indent size. Default value retained.\n");
142 indent_width = indent_size;
145 static void _set_module_length_limit(int limit)
147 if (limit < MINIMAL_MODULE_WIDTH) {
148 fprintf(stderr, "WARNING: Invalid maximum field size. Default value retained.\n");
152 module_name_limit = limit;
155 static int _int_sort_function(gconstpointer a, gconstpointer b)
157 int int_a = GPOINTER_TO_INT (a);
158 int int_b = GPOINTER_TO_INT (b);
160 return int_a - int_b;
163 static void _truncate_string(char *string, int length_limit)
165 if (length_limit < MINIMAL_MODULE_WIDTH || !string)
168 int len = strlen(string);
170 if (len > length_limit)
171 memcpy(string + length_limit - 3, "...", 4);
174 static size_t _combine_strings(char **destination, const char *source)
176 if (!source || !*source || !destination)
179 size_t old_len = *destination ? strlen(*destination) : 0;
180 size_t source_len = strlen(source);
181 size_t len = old_len + source_len;
182 char *buf = realloc(*destination, (len + 1) * sizeof(char));
185 fprintf(stderr, "_combine_strings: allocation failed");
189 memcpy(buf + old_len, source, source_len + 1);
196 static Box_Size *_get_box_size(AtspiAccessible *node)
198 Box_Size *box_size = (Box_Size *)malloc(sizeof(Box_Size));
199 if (box_size == NULL)
207 AtspiComponent *component = atspi_accessible_get_component_iface(node);
208 AtspiRect *extent = component ? atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL) : NULL;
210 if (extent == NULL) {
211 _combine_strings(&x, "_");
212 _combine_strings(&y, "_");
213 _combine_strings(&width, "_");
214 _combine_strings(&height, "_");
216 char temp_buff[NUMBER_WIDTH];
218 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->x);
219 _combine_strings(&x, temp_buff);
221 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->y);
222 _combine_strings(&y, temp_buff);
224 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->width);
225 _combine_strings(&width, temp_buff);
227 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->height);
228 _combine_strings(&height, temp_buff);
230 g_object_unref(component);
236 box_size->width = width;
237 box_size->height = height;
242 static char *_get_states(AtspiAccessible *node, int length_limit)
244 AtspiStateSet *node_state_set = atspi_accessible_get_state_set(node);
245 GArray *states = atspi_state_set_get_states(node_state_set);
247 g_clear_object(&node_state_set);
250 g_array_sort(states, _int_sort_function);
252 AtspiStateType state_type;
253 char *state_string = NULL;
255 for (int i = 0; i < states->len; i++) {
256 state_type = g_array_index(states, AtspiStateType, i);
258 char node_state_str[27] = "";
259 strncat(node_state_str, "(", sizeof(node_state_str) - strlen(node_state_str) - 1);
260 strncat(node_state_str, atspi_state_names[state_type], sizeof(node_state_str) - strlen(node_state_str) - 1);
261 strncat(node_state_str, ")", sizeof(node_state_str) - strlen(node_state_str) - 1);
263 _combine_strings(&state_string, node_state_str);
266 g_array_free(states, 0);
267 g_clear_object(&node_state_set);
269 _truncate_string(state_string, length_limit);
274 static char *_get_attributes(AtspiAccessible *node, int length_limit, bool *attributes_are_too_long)
276 GHashTable *attributes = atspi_accessible_get_attributes(node, NULL);
281 GHashTableIter attributes_iter;
284 g_hash_table_iter_init (&attributes_iter, attributes);
286 while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) {
287 char attributes_string[SAFE_BUFFER_SIZE];
288 int ret = snprintf(attributes_string, SAFE_BUFFER_SIZE, "(%s=%s)", (char *)attr_key, (char *)attr_value);
289 if (ret >= SAFE_BUFFER_SIZE)
290 fprintf(stderr, "\n%s, %s %d: generated string is too long. Buffer overflow\n", __FILE__, __FUNCTION__, __LINE__);
292 _combine_strings(&result, attributes_string);
294 g_hash_table_unref(attributes);
296 int real_truncate_size = (length_limit < ATTR_WIDTH && length_limit > MINIMAL_MODULE_WIDTH) ? length_limit : ATTR_WIDTH;
297 if (result && attributes_are_too_long && strlen(result) > real_truncate_size)
298 *attributes_are_too_long = true;
300 _truncate_string(result, real_truncate_size);
304 static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes_are_too_long, bool *app_has_relations, bool *app_has_actions)
309 char *node_name = atspi_accessible_get_name(node, NULL);
310 char *node_role_name = atspi_accessible_get_role_name(node, NULL);
311 char *unique_id = atspi_accessible_get_unique_id(node, NULL);
312 char *path = atspi_accessible_get_path(node, NULL);
313 unsigned long long eo_ptr = 0;
314 sscanf(path, "%llu", &eo_ptr);
316 char *attributes = _get_attributes(node, length_limit, attributes_are_too_long);
317 Box_Size *box_size = _get_box_size(node);
318 char *states = _get_states(node, length_limit);
320 GArray *relations = atspi_accessible_get_relation_set(node, NULL);
321 bool current_node_has_relations = (relations && relations->len);
323 bool current_node_has_actions = false;
325 AtspiAction* action_if = atspi_accessible_get_action_iface (node);
328 gint no_actions = atspi_action_get_n_actions (action_if, NULL);
329 if (no_actions > 0) current_node_has_actions = true;
332 char result[SAFE_BUFFER_SIZE];
333 int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s(%p)],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s]]",
334 unique_id, (uintptr_t)eo_ptr,
337 box_size ? box_size->x : "nil",
338 box_size ? box_size->y : "nil",
339 box_size ? box_size->width : "nil",
340 box_size ? box_size->height : "nil",
343 current_node_has_relations ? "*" : "");
345 if (ret >= SAFE_BUFFER_SIZE)
346 fprintf(stderr, "\n%s, %s %d: generated string is too long. Buffer overflow\n", __FILE__, __FUNCTION__, __LINE__);
348 if (current_node_has_relations)
349 *app_has_relations = true;
351 if (current_node_has_actions)
352 *app_has_actions = true;
355 free(node_role_name);
360 free(box_size->width);
361 free(box_size->height);
366 g_array_free(relations, TRUE);
368 return g_strdup(result);
371 static void _test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessible *parent_candidate, int parent_candidate_index)
373 int parent_index = atspi_accessible_get_index_in_parent(obj, NULL);
374 AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
376 char output[CHECK_OUTPUT_WIDTH];
377 if (parent_index != parent_candidate_index || parent_candidate != parent) {
378 char parent_status[NUMBER_WIDTH];
381 strncpy(parent_status, "_", NUMBER_WIDTH);
383 snprintf(parent_status, NUMBER_WIDTH, "%d", parent_index);
385 char *parent_unique_id = atspi_accessible_get_unique_id(parent, NULL);
386 char *parent_candidate_unique_id = atspi_accessible_get_unique_id(parent_candidate, NULL);
387 snprintf(output, CHECK_OUTPUT_WIDTH, "[FAIL<%d,%s><%s,%s>]", parent_candidate_index, parent_status,
388 parent_candidate_unique_id, parent_unique_id);
389 free(parent_unique_id);
390 free(parent_candidate_unique_id);
393 snprintf(output, CHECK_OUTPUT_WIDTH, "[OK]");
396 printf("%-*s\t", CHECK_OUTPUT_WIDTH, output);
399 static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible *object, bool check_integrity, int length_limit,
400 bool *attributes_are_too_long, bool *app_has_relations, bool *app_has_actions)
402 char *indent = _multiply_string(' ', indent_number*indent_width);
403 if (indent != NULL) {
404 printf("%s", indent);
408 char *node_info = _get_info(object, length_limit, attributes_are_too_long, app_has_relations, app_has_actions);
409 if (node_info != NULL) {
410 printf("%s\n", node_info);
414 int count = atspi_accessible_get_child_count(object, NULL);
415 for (int i = 0; i < count; i++) {
416 AtspiAccessible *child = atspi_accessible_get_child_at_index(object, i, NULL);
419 _test_atspi_parent_child_relation(child, object, i);
421 _print_atspi_tree_verify_maybe_r(indent_number + 1, child, check_integrity, length_limit, attributes_are_too_long, app_has_relations, app_has_actions);
429 printf("AT-SPI2-CORE-UTIL\n\n");
430 printf("USAGE: at_spi2_tool [OPTION] [PARAMETER] ...\n\n");
431 printf("OPTION LIST:\n");
432 printf("-h, --help\t\tshow this message\n");
433 printf("-v, --version\t\tshow actual version of tool\n");
434 printf("-g, --show-legend\tprint AT-SPI state legend\n");
435 printf("-l, --list-apps\t\tlist all applications of desktop\n");
436 printf("-a, --at-spi-client <true|false>\tenable/disable org.a11y.Status.IsEnabled property\n");
437 printf("-s, --sleep <N>\tsleep N seconds\n");
438 printf("-d, --tree-dump\t\tdump tree for selected application\n");
439 printf("-c, --tree-check\tcheck tree for selected application\n");
440 printf("-f, --first-match\tperform dump or check only for first matching application\n");
441 printf("-i, --indent\t\tset indentation size, default value stands at 2\n");
442 printf("-t, --truncate\t\tset maximum single field size, default value stands at 30\n");
443 printf("\nEXAMPLE:\n");
444 printf("\tat_spi2_tool -i3 -d quickpanel\n");
445 printf("\t show AT-SPI tree for node \"quickpanel\" using three-space indentation\n");
446 printf("\tat_spi2_tool -i1 -c starter\n");
447 printf("\t show AT-SPI tree with integrity test for node \"starter\" using one-space indentation\n");
450 void _print_version()
452 printf("AT-SPI2-CORE-UTIL v%s\n", VERSION);
455 static void _print_horizontal_line_in_relations_table() {
456 for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) {
457 for (int j = 0; j < RELATION_TABLE_COLUMN_WIDTH; j++)
464 static char *_get_unique_id_of_object_in_relation(AtspiRelation *relation) {
468 gint last_index = atspi_relation_get_n_targets(relation) - 1;
469 AtspiAccessible *target = atspi_relation_get_target(relation, last_index);
470 return atspi_accessible_get_unique_id(target, NULL);
473 static void _print_relations_for_object(AtspiAccessible *node) {
474 GArray *relations = atspi_accessible_get_relation_set(node, NULL);
478 if (!relations->len) {
479 g_array_free(relations, FALSE);
483 char **table = calloc(RELATION_TABLE_COLUMN_COUNT, sizeof(char *));
485 fprintf(stderr, "Calloc failed. Can't alloc memory for object relations string\n");
489 table[0] = atspi_accessible_get_unique_id(node, NULL);
490 for (int i = 0; i < relations->len; i++) {
491 AtspiRelation *relation = g_array_index(relations, AtspiRelation *, i);
492 AtspiRelationType type = atspi_relation_get_relation_type(relation);
495 case ATSPI_RELATION_CONTROLLER_FOR: idx = 1; break;
496 case ATSPI_RELATION_CONTROLLED_BY: idx = 2; break;
497 case ATSPI_RELATION_FLOWS_TO: idx = 3; break;
498 case ATSPI_RELATION_FLOWS_FROM: idx = 4; break;
499 case ATSPI_RELATION_DESCRIBED_BY: idx = 5; break;
504 table[idx] = _get_unique_id_of_object_in_relation(relation);
507 snprintf(buf, sizeof(buf), " [%d]", type);
508 _combine_strings(&table[RELATION_TABLE_COLUMN_COUNT - 1], buf);
512 for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) {
513 printf("%*s|", RELATION_TABLE_COLUMN_WIDTH, table[i] ? table[i] : "");
519 _print_horizontal_line_in_relations_table();
521 g_array_free(relations, TRUE);
524 typedef void (*print_information_about_object_function)(AtspiAccessible *);
526 static void _iterate_over_tree(print_information_about_object_function func, AtspiAccessible *node) {
529 int count = atspi_accessible_get_child_count(node, NULL);
530 for (int i = 0; i < count; i++) {
531 AtspiAccessible *child = atspi_accessible_get_child_at_index(node, i, NULL);
533 _iterate_over_tree(func, child);
537 static void _print_relations_for_objects_in_tree(AtspiAccessible *node) {
538 _iterate_over_tree(_print_relations_for_object, node);
541 static void _print_header_for_relation_table() {
542 char *table[] = {"OBJECT", "CONTROLLER_FOR", "CONTROLLED_BY", "FLOWS_TO", "FLOWS_FROM", "DESCRIBED_BY", "OTHER RELATION [ID]"};
543 assert(ARRAY_SIZE(table) == RELATION_TABLE_COLUMN_COUNT);
545 _print_horizontal_line_in_relations_table();
547 for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++)
548 printf("%*s|", RELATION_TABLE_COLUMN_WIDTH , table[i]);
551 _print_horizontal_line_in_relations_table();
554 static void _print_relations_table(AtspiAccessible *node) {
555 printf("\nRELATIONS TABLE\n");
556 _print_header_for_relation_table();
557 _print_relations_for_objects_in_tree(node);
560 static void _print_horizontal_line_in_actions_table() {
562 for (int i = 0; i < ACTIONS_TABLE_COLUMN_COUNT; i++) {
563 if (i == ACTIONS_TABLE_COLUMN_COUNT - 1)
565 for (int j = 0; j < ACTIONS_TABLE_BASE_COLUMN_WIDTH * size_factor; j++)
572 static void _print_actions_for_object(AtspiAccessible *node) {
573 char *unique_id = atspi_accessible_get_unique_id(node, NULL);
574 AtspiAction* action_if = atspi_accessible_get_action_iface (node);
576 gint no_actions = atspi_action_get_n_actions (action_if, NULL);
577 for (int i=0; i< no_actions; i++) {
578 gchar * ith_action = atspi_action_get_action_name (action_if, i, NULL);
579 printf("%*s|", ACTIONS_TABLE_BASE_COLUMN_WIDTH, unique_id ? unique_id : "");
580 printf("%*s|\n", ACTIONS_TABLE_BASE_COLUMN_WIDTH * 4, ith_action ? (char *) ith_action : "");
583 _print_horizontal_line_in_actions_table();
588 static void _print_actions_for_objects_in_tree(AtspiAccessible *node) {
589 _iterate_over_tree(_print_actions_for_object, node);
592 static void _print_header_for_actions_table() {
593 char *table[] = {"OBJECT", "SUPPORTED ACTION"};
594 assert(ARRAY_SIZE(table) == ACTIONS_TABLE_COLUMN_COUNT);
596 _print_horizontal_line_in_actions_table();
599 for (int i = 0; i < ACTIONS_TABLE_COLUMN_COUNT; i++) {
600 if (i == ACTIONS_TABLE_COLUMN_COUNT - 1)
602 printf("%*s|", ACTIONS_TABLE_BASE_COLUMN_WIDTH * size_factor , table[i]);
606 _print_horizontal_line_in_actions_table();
609 static void _print_actions_table(AtspiAccessible *node) {
610 printf("\nSUPPPORTED ACTIONS TABLE\n");
611 _print_header_for_actions_table();
612 _print_actions_for_objects_in_tree(node);
615 static void _print_horizontal_line_in_attributes_table() {
617 for (int i = 0; i < ATTRIBUTE_TABLE_COLUMN_COUNT; i++) {
618 if (i == ATTRIBUTE_TABLE_COLUMN_COUNT - 1)
620 for (int j = 0; j < ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * size_factor; j++)
627 static void _print_attributes_for_object(AtspiAccessible *node) {
628 GHashTable *attributes = atspi_accessible_get_attributes(node, NULL);
633 char *unique_id = atspi_accessible_get_unique_id(node, NULL);
634 GHashTableIter attributes_iter;
638 g_hash_table_iter_init (&attributes_iter, attributes);
639 while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) {
640 printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH, unique_id ? unique_id : "");
641 printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH, attr_key ? (char *) attr_key : "");
642 printf("%*s|\n", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * 4, attr_value ? (char *) attr_value : "");
645 if (g_hash_table_size (attributes))
646 _print_horizontal_line_in_attributes_table();
648 g_hash_table_unref(attributes);
652 static void _print_attributes_for_objects_in_tree(AtspiAccessible *node) {
653 _iterate_over_tree(_print_attributes_for_object, node);
656 static void _print_header_for_attributes_table() {
657 char *table[] = {"OBJECT", "ATTRIBUTE KEY", "ATTRIBUTE VALUE"};
658 assert(ARRAY_SIZE(table) == ATTRIBUTE_TABLE_COLUMN_COUNT);
660 _print_horizontal_line_in_attributes_table();
663 for (int i = 0; i < ATTRIBUTE_TABLE_COLUMN_COUNT; i++) {
664 if (i == ATTRIBUTE_TABLE_COLUMN_COUNT - 1)
666 printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * size_factor , table[i]);
670 _print_horizontal_line_in_attributes_table();
673 static void _print_attributes_table(AtspiAccessible *node) {
674 printf("\nATTRIBUTES TABLE\n");
675 _print_header_for_attributes_table();
676 _print_attributes_for_objects_in_tree(node);
679 static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bool first_match, int length_limit)
682 AtspiAccessible *desktop = atspi_get_desktop(0);
684 fprintf(stderr, "atspi_get_desktop failed\n");
688 int count = atspi_accessible_get_child_count(desktop, NULL);
689 bool app_name_matched = false;
691 for (int i = 0; i < count; i++) {
692 AtspiAccessible *child = atspi_accessible_get_child_at_index(desktop, i, NULL);
694 fprintf(stderr, "\n%s, %s %d: Null application occured. Results may be misleading.\n", __FILE__, __FUNCTION__, __LINE__);
698 char *name = atspi_accessible_get_name(child, NULL);
699 bool attributes_are_too_long = false;
700 bool app_has_relations = false;
701 bool app_has_actions = false;
704 printf("%s\n", name);
706 if ((check || dump) && name && app_name && !strcmp(name, app_name)) {
707 app_name_matched = true;
709 _print_module_legend();
712 _test_atspi_parent_child_relation(child, desktop, i);
714 _print_atspi_tree_verify_maybe_r(0, child, check, length_limit, &attributes_are_too_long, &app_has_relations, &app_has_actions);
716 if (app_has_relations)
717 _print_relations_table(child);
719 if (attributes_are_too_long)
720 _print_attributes_table(child);
723 _print_actions_table(child);
735 if (!app_name_matched && (dump || check))
736 fprintf(stderr, "There is no application with name: %s. Try again.\n", app_name);
738 g_object_unref(desktop);
741 static void _at_spi_client_enable(gboolean enabled)
743 static GDBusProxy *proxy = NULL; //we keep proxy (dbus connection) until program exits
745 GVariant *enabled_variant;
746 GError *error = NULL;
747 GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_NONE;
751 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
753 NULL, /* GDBusInterfaceInfo */
756 "org.freedesktop.DBus.Properties",
757 NULL, /* GCancellable */
760 fprintf(stderr, "Failed to create proxy object for '/org/a11y/bus': %s\n", error->message);
766 enabled_variant = g_variant_new_boolean(enabled);
767 result = g_dbus_proxy_call_sync(proxy,
769 g_variant_new ("(ssv)", "org.a11y.Status", "IsEnabled", enabled_variant),
770 G_DBUS_CALL_FLAGS_NONE,
775 g_variant_unref(enabled_variant);
777 g_variant_unref(result);
780 fprintf(stderr, "Fail to call org.freedesktop.DBus.Properties.Set: %s\n", error->message);
785 static void _run_command(int argc, char *argv[])
787 struct option long_options[] = {
788 {"help", no_argument, 0, 'h'},
789 {"version", no_argument, 0, 'v'},
790 {"show-legend", no_argument, 0, 'g'},
791 {"list-apps", no_argument, 0, 'l'},
792 {"at-spi-client", no_argument, 0, 'a'},
793 {"sleep", required_argument, 0, 's'},
794 {"tree-dump", required_argument, 0, 'd'},
795 {"tree-check", required_argument, 0, 'c'},
796 {"first-match", no_argument, 0, 'f'},
797 {"indent", required_argument, 0, 'i'},
798 {"truncate", required_argument, 0, 't'},
802 int option_index = 0;
803 bool traverse_flags[FLAG_NO] = {false};
804 char *app_name = NULL;
805 gboolean enable_at_spi_client;
808 command = getopt_long(argc, argv, "hvgla:s:d:c:ft:i:", long_options, &option_index);
810 if (command == ERROR_STATE)
823 _print_atspi_states_legend();
827 _atspi_tree_traverse(NULL, false, false, false, module_name_limit);
831 enable_at_spi_client = TRUE;
832 if (optarg[0] == 'f' || optarg[0] == '0')
833 enable_at_spi_client = FALSE;
835 _at_spi_client_enable(enable_at_spi_client);
843 traverse_flags[DUMP] = true;
848 traverse_flags[CHECK] = true;
853 traverse_flags[FIRST_MATCH] = true;
857 _set_indent(atoi(optarg));
861 _set_module_length_limit(atoi(optarg));
865 fprintf(stderr, "Invalid parameter. Use: \"--help\"\n");
873 if (traverse_flags[DUMP] || traverse_flags[CHECK])
874 _atspi_tree_traverse(app_name, traverse_flags[DUMP], traverse_flags[CHECK], traverse_flags[FIRST_MATCH], module_name_limit);
877 int main(int argc, char *argv[])
880 printf("Arguments required. Type %s --help.\n", argv[0]);
884 int result = atspi_init();
887 fprintf(stderr, "Unable to initilize AT-SPI infrastructure, atspi_init failed\n");
891 _run_command(argc, argv);