1 #include "atspi/atspi.h"
9 #define SAFE_BUFFER_SIZE 255
10 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
13 static unsigned indent_width = 2;
15 static const char* atspi_state_names[] = {
16 [ATSPI_STATE_INVALID] = "INVALID",
17 [ATSPI_STATE_ACTIVE] = "ACTIVE",
18 [ATSPI_STATE_ARMED] = "ARMED",
19 [ATSPI_STATE_BUSY] = "BUSY",
20 [ATSPI_STATE_CHECKED] = "CHECKED",
21 [ATSPI_STATE_COLLAPSED] = "COLLAPSED",
22 [ATSPI_STATE_DEFUNCT] = "DEFUNCT",
23 [ATSPI_STATE_EDITABLE] = "EDITABLE",
24 [ATSPI_STATE_ENABLED] = "ENABLED",
25 [ATSPI_STATE_EXPANDABLE] = "EXPANDABLE",
26 [ATSPI_STATE_EXPANDED] = "EXPANDED",
27 [ATSPI_STATE_FOCUSABLE] = "FOCUSABLE",
28 [ATSPI_STATE_FOCUSED] = "FOCUSED",
29 [ATSPI_STATE_HAS_TOOLTIP] = "HAS_TOOLTIP",
30 [ATSPI_STATE_HORIZONTAL] = "HORIZONTAL",
31 [ATSPI_STATE_ICONIFIED] = "ICONIFIED",
32 [ATSPI_STATE_MULTI_LINE] = "MULTI_LINE",
33 [ATSPI_STATE_MULTISELECTABLE] = "MULTISELECTABLE",
34 [ATSPI_STATE_OPAQUE] = "OPAQUE",
35 [ATSPI_STATE_PRESSED] = "PRESSED",
36 [ATSPI_STATE_RESIZABLE] = "RESIZABLE",
37 [ATSPI_STATE_SELECTABLE] = "SELECTABLE",
38 [ATSPI_STATE_SELECTED] = "SELECTED",
39 [ATSPI_STATE_SENSITIVE] = "SENSITIVE",
40 [ATSPI_STATE_SHOWING] = "SHOWING",
41 [ATSPI_STATE_SINGLE_LINE] = "SINGLE_LINE",
42 [ATSPI_STATE_STALE] = "STALE",
43 [ATSPI_STATE_TRANSIENT] = "TRANSIENT",
44 [ATSPI_STATE_VERTICAL] = "VERTICAL",
45 [ATSPI_STATE_VISIBLE] = "VISIBLE",
46 [ATSPI_STATE_MANAGES_DESCENDANTS] = "MANAGES_DESCENDANTS",
47 [ATSPI_STATE_INDETERMINATE] = "INDETERMINATE",
48 [ATSPI_STATE_REQUIRED] = "REQUIRED",
49 [ATSPI_STATE_TRUNCATED] = "TRUNCATED",
50 [ATSPI_STATE_ANIMATED] = "ANIMATED",
51 [ATSPI_STATE_INVALID_ENTRY] = "INVALID_ENTRY",
52 [ATSPI_STATE_SUPPORTS_AUTOCOMPLETION] = "SUPPORTS_AUTOCOMPLETION",
53 [ATSPI_STATE_SELECTABLE_TEXT] = "SELECTABLE_TEXT",
54 [ATSPI_STATE_IS_DEFAULT] = "IS_DEFAULT",
55 [ATSPI_STATE_VISITED] = "VISITED",
56 [ATSPI_STATE_CHECKABLE] = "CHECKABLE",
57 [ATSPI_STATE_MODAL] = "MODAL",
58 [ATSPI_STATE_HIGHLIGHTED] = "HIGHLIGHTED",
59 [ATSPI_STATE_HIGHLIGHTABLE] = "HIGHLIGHTABLE",
60 [ATSPI_STATE_HAS_POPUP] = "HAS_POPUP",
61 [ATSPI_STATE_READ_ONLY] = "READ_ONLY",
62 [ATSPI_STATE_LAST_DEFINED] = "LAST_DEFINED"
65 typedef struct _StringAccumulator {
70 char *strdup(const char *c) {
71 char *result = (char *)calloc(1, strlen(c) + 1);
72 return result ? strcpy(result, c) : result;
75 static char *multiply_string(char c, unsigned number) {
76 char *result = (char *)calloc(1, number + 1);
81 memset(result, c, number);
86 static void _print_atspi_states_legend() {
87 int array_len = ARRAY_SIZE(atspi_state_names);
88 int line_count = (array_len + COLUMN_NO - 1)/COLUMN_NO;
89 for (int line_idx = 0; line_idx < line_count; line_idx++) {
90 if ((line_idx < line_count - 1) || (array_len % 3 == 0)) {
91 printf("[%d]\t%-24s[%d]\t%-24s[%d]\t%s\n", line_idx * 3, atspi_state_names[line_idx * 3], (line_idx * 3) + 1, atspi_state_names[(line_idx * 3) + 1], (line_idx * 3) + 2, atspi_state_names[(line_idx * 3) + 2]);
92 } else if (array_len % 3 == 2) {
93 printf("[%d]\t%-24s[%d]\t%s\n", line_idx * 3, atspi_state_names[line_idx * 3], (line_idx * 3) + 1, atspi_state_names[(line_idx * 3) + 1]);
95 printf("[%d]\t%s\n", line_idx * 3, atspi_state_names[line_idx * 3]);
100 static void set_indent(int indent_size)
102 if (indent_size < 0) {
103 fprintf(stderr, "WARNING: Invalid indent size. Default value retained.\n");
107 indent_width = indent_size;
110 static int _int_sort_function(gconstpointer a, gconstpointer b)
112 int int_a = GPOINTER_TO_INT (a);
113 int int_b = GPOINTER_TO_INT (b);
115 return int_a - int_b;
118 static int _accumulate_string(const char *string, StringAccumulator *accumulator) {
123 int length = accumulator->length + strlen(string);
124 char *rebuffer = realloc(accumulator->string, length + 1);
126 strcpy(rebuffer + accumulator->length, string);
127 accumulator->string = rebuffer;
128 accumulator->length = length;
136 static char *get_info(AtspiAccessible *node) {
141 int node_height = -1;
142 char *node_name = atspi_accessible_get_name(node, NULL);
143 char *node_role_name = atspi_accessible_get_role_name(node, NULL);
144 AtspiComponent *component = atspi_accessible_get_component_iface(node);
146 AtspiRect *extent = NULL;
147 if (component != NULL){
148 extent = atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL);
150 if (extent != NULL) {
151 node_width = extent->width;
152 node_height = extent->height;
156 AtspiStateSet *node_state_set = atspi_accessible_get_state_set(node);
157 GArray *states = atspi_state_set_get_states(node_state_set);
158 g_array_sort(states, _int_sort_function);
160 AtspiStateType state;
161 StringAccumulator state_accumulator = {};
162 for (int i = 0; states && (i < states->len); i++) {
163 state = g_array_index(states, AtspiStateType, i);
164 char node_state_str[8];
165 sprintf(node_state_str, "[%d]", state);
166 _accumulate_string(node_state_str, &state_accumulator);
168 g_array_free(states, 0);
170 StringAccumulator attribute_accumulator = {};
171 GHashTable *attributes = NULL;
172 attributes = atspi_accessible_get_attributes(node, NULL);
174 GHashTableIter attributes_iter;
175 gpointer attr_key, attr_value;
176 g_hash_table_iter_init (&attributes_iter, attributes);
177 while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) {
178 _accumulate_string("[", &attribute_accumulator);
179 _accumulate_string((char*)attr_key, &attribute_accumulator);
180 _accumulate_string("=", &attribute_accumulator);
181 _accumulate_string((char*)attr_value, &attribute_accumulator);
182 _accumulate_string("]", &attribute_accumulator);
184 g_hash_table_unref(attributes);
187 char result[SAFE_BUFFER_SIZE + 1];
188 snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%d,%d],[%s],[%s]]\n", node_role_name, attribute_accumulator.string, node_width, node_height, node_name, state_accumulator.string);
191 free(node_role_name);
193 if (state_accumulator.string)
194 g_free(state_accumulator.string);
196 if (attribute_accumulator.string)
197 g_free(attribute_accumulator.string);
200 g_object_unref(component);
206 g_object_unref(node_state_set);
208 return strdup(result);
211 static void test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessible *parent_candidate, int parent_candidate_index)
213 int parent_index = atspi_accessible_get_index_in_parent(obj, NULL);
214 AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
215 if (parent_index != parent_candidate_index || parent_candidate != parent) {
216 printf("[FAIL<%d,", parent_candidate_index);
221 printf("%d", parent_index);
230 static int expand_r(int indent_number, AtspiAccessible *object, bool check_integrity)
232 char *indent = multiply_string(' ', indent_number*indent_width);
233 if (indent != NULL) {
234 printf("%s", indent);
238 char *node_info = get_info(object);
239 if (node_info != NULL) {
240 printf("%s", node_info);
244 int count = atspi_accessible_get_child_count(object, NULL);
245 for (int i = 0; i < count; i++) {
246 AtspiAccessible *child = atspi_accessible_get_child_at_index(object, i, NULL);
249 test_atspi_parent_child_relation(child, object, i);
251 expand_r(indent_number + 1, child, check_integrity);
259 printf("AT-SPI2-CORE-UTIL\n\n");
260 printf("USAGE: at_spi2_tool [OPTION] [PARAMETER] ...\n\n");
261 printf("OPTION LIST:\n");
262 printf("-h, --help\t\tshow this message\n");
263 printf("-g, --show-legend\tprint AT-SPI state legend\n");
264 printf("-l, --list-apps\t\tlist all applications of desktop\n");
265 printf("-d, --tree-dump\t\tdump tree for selected application\n");
266 printf("-c, --tree-check\tcheck tree for selected application\n");
267 printf("-i, --indent\t\tset indentation size\n");
268 printf("\nEXAMPLE:\n");
269 printf("\tat_spi2_tool -i3 -d quickpanel\n");
270 printf("\t show AT-SPI tree for node \"quickpanel\" using three-space indentation\n");
271 printf("\tat_spi2_tool -i1 -c starter\n");
272 printf("\t show AT-SPI tree with integrity test for node \"starter\" using one-space indentation\n");
275 static void atspi_tree_traverse(AtspiAccessible *desktop, const char *app_name, bool dump, bool check)
277 int count = atspi_accessible_get_child_count(desktop, NULL);
278 for (int i = 0; i < count; i++) {
279 AtspiAccessible *child = atspi_accessible_get_child_at_index(desktop, i, NULL);
281 char *name = atspi_accessible_get_name(child, NULL);
284 printf("%s\n", name);
286 if (dump && name && !strcmp(name, app_name)) {
287 expand_r(0, child, false);
291 if (check && name && !strcmp(name, app_name)) {
292 test_atspi_parent_child_relation(child, desktop, i);
293 expand_r(0, child, true);
300 static void run_command(int argc, char *argv[], AtspiAccessible *desktop)
302 struct option long_options[] = {
303 {"help", no_argument, 0, 'h'},
304 {"show-legend", no_argument, 0, 'g'},
305 {"list-apps", no_argument, 0, 'l'},
306 {"tree-dump", required_argument, 0, 'd'},
307 {"tree-check", required_argument, 0, 'c'},
308 {"indent", required_argument, 0, 'i'},
312 int option_index = 0;
315 command = getopt_long(argc, argv, "hgld:c:i:", long_options, &option_index);
317 if (command == ERROR_STATE)
326 _print_atspi_states_legend();
330 atspi_tree_traverse(desktop, NULL, false, false);
334 atspi_tree_traverse(desktop, optarg, true, false);
338 atspi_tree_traverse(desktop, optarg, false, true);
342 set_indent(atoi(optarg));
354 int main(int argc, char *argv[])
357 printf("Arguments required. Type %s --help.\n", argv[0]);
361 AtspiAccessible *desktop = atspi_get_desktop(0);
364 fprintf(stderr, "atspi_get_desktop failed\n");
370 run_command(argc, argv, desktop);