1/* gtkatspicontext.c: AT-SPI GtkATContext implementation
2 *
3 * Copyright 2020 GNOME Foundation
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "config.h"
22
23#include "gtkatspicontextprivate.h"
24
25#include "gtkaccessibleprivate.h"
26
27#include "gtkatspiactionprivate.h"
28#include "gtkatspieditabletextprivate.h"
29#include "gtkatspiprivate.h"
30#include "gtkatspirootprivate.h"
31#include "gtkatspiselectionprivate.h"
32#include "gtkatspitextprivate.h"
33#include "gtkatspiutilsprivate.h"
34#include "gtkatspivalueprivate.h"
35#include "gtkatspicomponentprivate.h"
36#include "a11y/atspi/atspi-accessible.h"
37#include "a11y/atspi/atspi-action.h"
38#include "a11y/atspi/atspi-editabletext.h"
39#include "a11y/atspi/atspi-text.h"
40#include "a11y/atspi/atspi-value.h"
41#include "a11y/atspi/atspi-selection.h"
42#include "a11y/atspi/atspi-component.h"
43
44#include "gtkdebug.h"
45#include "gtkeditable.h"
46#include "gtkentryprivate.h"
47#include "gtkroot.h"
48#include "gtkstack.h"
49#include "gtktextview.h"
50#include "gtktypebuiltins.h"
51#include "gtkwindow.h"
52
53#include <gio/gio.h>
54
55#include <locale.h>
56
57#if defined(GDK_WINDOWING_WAYLAND)
58# include <gdk/wayland/gdkwaylanddisplay.h>
59#endif
60#if defined(GDK_WINDOWING_X11)
61# include <gdk/x11/gdkx11display.h>
62# include <gdk/x11/gdkx11property.h>
63#endif
64
65/* We create a GtkAtSpiContext object for (almost) every widget.
66 *
67 * Each context implements a number of Atspi interfaces on a D-Bus
68 * object. The context objects are connected into a tree by the
69 * Parent property and GetChildAtIndex method of the Accessible
70 * interface.
71 *
72 * The tree is an almost perfect mirror image of the widget tree,
73 * with a few notable exceptions:
74 *
75 * - We don't create contexts for the GtkText widgets inside
76 * entry wrappers, since the Text functionality is represented
77 * on the entry contexts.
78 *
79 * - We insert non-widget backed context objects for each page
80 * of a stack. The main purpose of these extra context is to
81 * hold the TAB_PANEL role and be the target of the CONTROLS
82 * relation with their corresponding tabs (in the stack
83 * switcher or notebook).
84 */
85
86struct _GtkAtSpiContext
87{
88 GtkATContext parent_instance;
89
90 /* The root object, used as a entry point */
91 GtkAtSpiRoot *root;
92
93 /* The object path of the ATContext on the bus */
94 char *context_path;
95
96 /* Just a pointer; the connection is owned by the GtkAtSpiRoot
97 * associated to the GtkATContext
98 */
99 GDBusConnection *connection;
100
101 /* Accerciser refuses to work unless we implement a GetInterface
102 * call that returns a list of all implemented interfaces. We
103 * collect the answer here.
104 */
105 GVariant *interfaces;
106
107 guint registration_ids[20];
108 guint n_registered_objects;
109};
110
111G_DEFINE_TYPE (GtkAtSpiContext, gtk_at_spi_context, GTK_TYPE_AT_CONTEXT)
112
113/* {{{ State handling */
114static void
115set_atspi_state (guint64 *states,
116 AtspiStateType state)
117{
118 *states |= (G_GUINT64_CONSTANT (1) << state);
119}
120
121static void
122unset_atspi_state (guint64 *states,
123 AtspiStateType state)
124{
125 *states &= ~(G_GUINT64_CONSTANT (1) << state);
126}
127
128static void
129collect_states (GtkAtSpiContext *self,
130 GVariantBuilder *builder)
131{
132 GtkATContext *ctx = GTK_AT_CONTEXT (ptr: self);
133 GtkAccessibleValue *value;
134 GtkAccessible *accessible;
135 guint64 states = 0;
136
137 accessible = gtk_at_context_get_accessible (self: ctx);
138
139 set_atspi_state (states: &states, state: ATSPI_STATE_VISIBLE);
140
141 if (ctx->accessible_role == GTK_ACCESSIBLE_ROLE_WINDOW)
142 {
143 set_atspi_state (states: &states, state: ATSPI_STATE_SHOWING);
144 if (gtk_accessible_get_platform_state (self: accessible, state: GTK_ACCESSIBLE_PLATFORM_STATE_ACTIVE))
145 set_atspi_state (states: &states, state: ATSPI_STATE_ACTIVE);
146 }
147
148 if (ctx->accessible_role == GTK_ACCESSIBLE_ROLE_TEXT_BOX ||
149 ctx->accessible_role == GTK_ACCESSIBLE_ROLE_SEARCH_BOX ||
150 ctx->accessible_role == GTK_ACCESSIBLE_ROLE_SPIN_BUTTON)
151 set_atspi_state (states: &states, state: ATSPI_STATE_EDITABLE);
152
153 if (gtk_at_context_has_accessible_property (self: ctx, property: GTK_ACCESSIBLE_PROPERTY_READ_ONLY))
154 {
155 value = gtk_at_context_get_accessible_property (self: ctx, property: GTK_ACCESSIBLE_PROPERTY_READ_ONLY);
156 if (gtk_boolean_accessible_value_get (value))
157 {
158 set_atspi_state (states: &states, state: ATSPI_STATE_READ_ONLY);
159 unset_atspi_state (states: &states, state: ATSPI_STATE_EDITABLE);
160 }
161 }
162
163 if (gtk_accessible_get_platform_state (self: accessible, state: GTK_ACCESSIBLE_PLATFORM_STATE_FOCUSABLE))
164 set_atspi_state (states: &states, state: ATSPI_STATE_FOCUSABLE);
165
166 if (gtk_accessible_get_platform_state (self: accessible, state: GTK_ACCESSIBLE_PLATFORM_STATE_FOCUSED))
167 set_atspi_state (states: &states, state: ATSPI_STATE_FOCUSED);
168
169 if (gtk_at_context_has_accessible_property (self: ctx, property: GTK_ACCESSIBLE_PROPERTY_ORIENTATION))
170 {
171 value = gtk_at_context_get_accessible_property (self: ctx, property: GTK_ACCESSIBLE_PROPERTY_ORIENTATION);
172 if (gtk_orientation_accessible_value_get (value) == GTK_ORIENTATION_HORIZONTAL)
173 set_atspi_state (states: &states, state: ATSPI_STATE_HORIZONTAL);
174 else
175 set_atspi_state (states: &states, state: ATSPI_STATE_VERTICAL);
176 }
177
178 if (gtk_at_context_has_accessible_property (self: ctx, property: GTK_ACCESSIBLE_PROPERTY_MODAL))
179 {
180 value = gtk_at_context_get_accessible_property (self: ctx, property: GTK_ACCESSIBLE_PROPERTY_MODAL);
181 if (gtk_boolean_accessible_value_get (value))
182 set_atspi_state (states: &states, state: ATSPI_STATE_MODAL);
183 }
184
185 if (gtk_at_context_has_accessible_property (self: ctx, property: GTK_ACCESSIBLE_PROPERTY_MULTI_LINE))
186 {
187 value = gtk_at_context_get_accessible_property (self: ctx, property: GTK_ACCESSIBLE_PROPERTY_MULTI_LINE);
188 if (gtk_boolean_accessible_value_get (value))
189 set_atspi_state (states: &states, state: ATSPI_STATE_MULTI_LINE);
190 }
191
192 if (gtk_at_context_has_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_BUSY))
193 {
194 value = gtk_at_context_get_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_BUSY);
195 if (gtk_boolean_accessible_value_get (value))
196 set_atspi_state (states: &states, state: ATSPI_STATE_BUSY);
197 }
198
199 if (gtk_at_context_has_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_CHECKED))
200 {
201 value = gtk_at_context_get_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_CHECKED);
202 switch (gtk_tristate_accessible_value_get (value))
203 {
204 case GTK_ACCESSIBLE_TRISTATE_TRUE:
205 set_atspi_state (states: &states, state: ATSPI_STATE_CHECKED);
206 break;
207 case GTK_ACCESSIBLE_TRISTATE_MIXED:
208 set_atspi_state (states: &states, state: ATSPI_STATE_INDETERMINATE);
209 break;
210 case GTK_ACCESSIBLE_TRISTATE_FALSE:
211 default:
212 break;
213 }
214 }
215
216 if (gtk_at_context_has_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_DISABLED))
217 {
218 value = gtk_at_context_get_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_DISABLED);
219 if (!gtk_boolean_accessible_value_get (value))
220 set_atspi_state (states: &states, state: ATSPI_STATE_SENSITIVE);
221 }
222 else
223 set_atspi_state (states: &states, state: ATSPI_STATE_SENSITIVE);
224
225 if (gtk_at_context_has_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_EXPANDED))
226 {
227 value = gtk_at_context_get_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_EXPANDED);
228 if (value->value_class->type == GTK_ACCESSIBLE_VALUE_TYPE_BOOLEAN)
229 {
230 set_atspi_state (states: &states, state: ATSPI_STATE_EXPANDABLE);
231 if (gtk_boolean_accessible_value_get (value))
232 set_atspi_state (states: &states, state: ATSPI_STATE_EXPANDED);
233 }
234 }
235
236 if (gtk_at_context_has_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_INVALID))
237 {
238 value = gtk_at_context_get_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_INVALID);
239 switch (gtk_invalid_accessible_value_get (value))
240 {
241 case GTK_ACCESSIBLE_INVALID_TRUE:
242 case GTK_ACCESSIBLE_INVALID_GRAMMAR:
243 case GTK_ACCESSIBLE_INVALID_SPELLING:
244 set_atspi_state (states: &states, state: ATSPI_STATE_INVALID);
245 break;
246 case GTK_ACCESSIBLE_INVALID_FALSE:
247 default:
248 break;
249 }
250 }
251
252 if (gtk_at_context_has_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_PRESSED))
253 {
254 value = gtk_at_context_get_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_PRESSED);
255 switch (gtk_tristate_accessible_value_get (value))
256 {
257 case GTK_ACCESSIBLE_TRISTATE_TRUE:
258 set_atspi_state (states: &states, state: ATSPI_STATE_PRESSED);
259 break;
260 case GTK_ACCESSIBLE_TRISTATE_MIXED:
261 set_atspi_state (states: &states, state: ATSPI_STATE_INDETERMINATE);
262 break;
263 case GTK_ACCESSIBLE_TRISTATE_FALSE:
264 default:
265 break;
266 }
267 }
268
269 if (gtk_at_context_has_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_SELECTED))
270 {
271 value = gtk_at_context_get_accessible_state (self: ctx, state: GTK_ACCESSIBLE_STATE_SELECTED);
272 if (value->value_class->type == GTK_ACCESSIBLE_VALUE_TYPE_BOOLEAN)
273 {
274 set_atspi_state (states: &states, state: ATSPI_STATE_SELECTABLE);
275 if (gtk_boolean_accessible_value_get (value))
276 set_atspi_state (states: &states, state: ATSPI_STATE_SELECTED);
277 }
278 }
279
280 g_variant_builder_add (builder, format_string: "u", (guint32) (states & 0xffffffff));
281 g_variant_builder_add (builder, format_string: "u", (guint32) (states >> 32));
282}
283/* }}} */
284/* {{{ Relation handling */
285static void
286collect_relations (GtkAtSpiContext *self,
287 GVariantBuilder *builder)
288{
289 GtkATContext *ctx = GTK_AT_CONTEXT (ptr: self);
290 struct {
291 GtkAccessibleRelation r;
292 AtspiRelationType s;
293 } map[] = {
294 { GTK_ACCESSIBLE_RELATION_LABELLED_BY, ATSPI_RELATION_LABELLED_BY },
295 { GTK_ACCESSIBLE_RELATION_CONTROLS, ATSPI_RELATION_CONTROLLER_FOR },
296 { GTK_ACCESSIBLE_RELATION_DESCRIBED_BY, ATSPI_RELATION_DESCRIBED_BY },
297 { GTK_ACCESSIBLE_RELATION_FLOW_TO, ATSPI_RELATION_FLOWS_TO},
298 };
299 GtkAccessibleValue *value;
300 GList *list, *l;
301 GtkATContext *target_ctx;
302 int i;
303
304 for (i = 0; i < G_N_ELEMENTS (map); i++)
305 {
306 if (!gtk_at_context_has_accessible_relation (self: ctx, relation: map[i].r))
307 continue;
308
309 GVariantBuilder b = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a(so)"));
310
311 value = gtk_at_context_get_accessible_relation (self: ctx, relation: map[i].r);
312 list = gtk_reference_list_accessible_value_get (value);
313
314 for (l = list; l; l = l->next)
315 {
316 target_ctx = gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: l->data));
317
318 /* Realize the ATContext of the target, so we can ask for its ref */
319 gtk_at_context_realize (self: target_ctx);
320
321 g_variant_builder_add (builder: &b, format_string: "@(so)",
322 gtk_at_spi_context_to_ref (self: GTK_AT_SPI_CONTEXT (ptr: target_ctx)));
323 }
324
325 g_variant_builder_add (builder, format_string: "(ua(so))", map[i].s, &b);
326 }
327}
328/* }}} */
329/* {{{ Accessible implementation */
330static int
331get_index_in_parent (GtkWidget *widget)
332{
333 GtkWidget *parent = gtk_widget_get_parent (widget);
334 GtkWidget *child;
335 int idx;
336
337 if (parent == NULL)
338 return -1;
339
340 idx = 0;
341 for (child = gtk_widget_get_first_child (widget: parent);
342 child;
343 child = gtk_widget_get_next_sibling (widget: child))
344 {
345 if (child == widget)
346 return idx;
347
348 if (!gtk_accessible_should_present (self: GTK_ACCESSIBLE (ptr: child)))
349 continue;
350
351 idx++;
352 }
353
354 return -1;
355}
356
357static int
358get_index_in_toplevels (GtkWidget *widget)
359{
360 GListModel *toplevels = gtk_window_get_toplevels ();
361 guint n_toplevels = g_list_model_get_n_items (list: toplevels);
362 GtkWidget *window;
363 int idx;
364
365 idx = 0;
366 for (guint i = 0; i < n_toplevels; i++)
367 {
368 window = g_list_model_get_item (list: toplevels, position: i);
369
370 g_object_unref (object: window);
371
372 if (window == widget)
373 return idx;
374
375 if (!gtk_accessible_should_present (self: GTK_ACCESSIBLE (ptr: window)))
376 continue;
377
378 idx += 1;
379 }
380
381 return -1;
382}
383
384static GVariant *
385get_parent_context_ref (GtkAccessible *accessible)
386{
387 GVariant *res = NULL;
388
389 if (GTK_IS_WIDGET (accessible))
390 {
391 GtkWidget *widget = GTK_WIDGET (accessible);
392 GtkWidget *parent = gtk_widget_get_parent (widget);
393
394 if (parent == NULL)
395 {
396 GtkATContext *context = gtk_accessible_get_at_context (self: accessible);
397 GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ptr: context);
398
399 res = gtk_at_spi_root_to_ref (self: self->root);
400 }
401 else if (GTK_IS_STACK (parent))
402 {
403 GtkStackPage *page =
404 gtk_stack_get_page (GTK_STACK (parent), child: widget);
405 GtkATContext *parent_context =
406 gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: page));
407
408 if (parent_context != NULL)
409 {
410 gtk_at_context_realize (self: parent_context);
411
412 res = gtk_at_spi_context_to_ref (self: GTK_AT_SPI_CONTEXT (ptr: parent_context));
413 }
414 }
415 else
416 {
417 GtkATContext *parent_context =
418 gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: parent));
419
420 if (parent_context != NULL)
421 {
422 /* XXX: This realize() is needed otherwise opening a GtkPopover will
423 * emit a warning when getting the context's reference
424 */
425 gtk_at_context_realize (self: parent_context);
426
427 res = gtk_at_spi_context_to_ref (self: GTK_AT_SPI_CONTEXT (ptr: parent_context));
428 }
429 }
430 }
431 else if (GTK_IS_STACK_PAGE (accessible))
432 {
433 GtkWidget *parent =
434 gtk_widget_get_parent (widget: gtk_stack_page_get_child (GTK_STACK_PAGE (accessible)));
435 GtkATContext *parent_context =
436 gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: parent));
437
438 if (parent_context != NULL)
439 {
440 gtk_at_context_realize (self: parent_context);
441
442 res = gtk_at_spi_context_to_ref (self: GTK_AT_SPI_CONTEXT (ptr: parent_context));
443 }
444 }
445
446 if (res == NULL)
447 res = gtk_at_spi_null_ref ();
448
449 return res;
450}
451
452static void
453handle_accessible_method (GDBusConnection *connection,
454 const gchar *sender,
455 const gchar *object_path,
456 const gchar *interface_name,
457 const gchar *method_name,
458 GVariant *parameters,
459 GDBusMethodInvocation *invocation,
460 gpointer user_data)
461{
462 GtkAtSpiContext *self = user_data;
463
464 GTK_NOTE (A11Y, g_message ("handling %s on %s", method_name, object_path));
465
466 if (g_strcmp0 (str1: method_name, str2: "GetRole") == 0)
467 {
468 guint atspi_role = gtk_atspi_role_for_context (context: GTK_AT_CONTEXT (ptr: self));
469
470 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(u)", atspi_role));
471 }
472 else if (g_strcmp0 (str1: method_name, str2: "GetRoleName") == 0)
473 {
474 GtkAccessibleRole role = gtk_at_context_get_accessible_role (self: GTK_AT_CONTEXT (ptr: self));
475 const char *name = gtk_accessible_role_to_name (role, NULL);
476 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(s)", name));
477 }
478 else if (g_strcmp0 (str1: method_name, str2: "GetLocalizedRoleName") == 0)
479 {
480 GtkAccessibleRole role = gtk_at_context_get_accessible_role (self: GTK_AT_CONTEXT (ptr: self));
481 const char *name = gtk_accessible_role_to_name (role, GETTEXT_PACKAGE);
482 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(s)", name));
483 }
484 else if (g_strcmp0 (str1: method_name, str2: "GetState") == 0)
485 {
486 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(au)"));
487
488 g_variant_builder_open (builder: &builder, G_VARIANT_TYPE ("au"));
489 collect_states (self, builder: &builder);
490 g_variant_builder_close (builder: &builder);
491
492 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_builder_end (builder: &builder));
493 }
494 else if (g_strcmp0 (str1: method_name, str2: "GetAttributes") == 0)
495 {
496 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(a{ss})"));
497
498 g_variant_builder_open (builder: &builder, G_VARIANT_TYPE ("a{ss}"));
499 g_variant_builder_add (builder: &builder, format_string: "{ss}", "toolkit", "GTK");
500
501 if (gtk_at_context_has_accessible_property (self: GTK_AT_CONTEXT (ptr: self), property: GTK_ACCESSIBLE_PROPERTY_PLACEHOLDER))
502 {
503 GtkAccessibleValue *value;
504
505 value = gtk_at_context_get_accessible_property (self: GTK_AT_CONTEXT (ptr: self), property: GTK_ACCESSIBLE_PROPERTY_PLACEHOLDER);
506
507 g_variant_builder_add (builder: &builder, format_string: "{ss}",
508 "placeholder-text", gtk_string_accessible_value_get (value));
509 }
510
511 g_variant_builder_close (builder: &builder);
512
513 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_builder_end (builder: &builder));
514 }
515 else if (g_strcmp0 (str1: method_name, str2: "GetApplication") == 0)
516 {
517 g_dbus_method_invocation_return_value (invocation,
518 parameters: g_variant_new (format_string: "(@(so))", gtk_at_spi_root_to_ref (self: self->root)));
519 }
520 else if (g_strcmp0 (str1: method_name, str2: "GetChildAtIndex") == 0)
521 {
522 GtkATContext *context = NULL;
523 GtkAccessible *accessible;
524 int idx, real_idx = 0;
525
526 g_variant_get (value: parameters, format_string: "(i)", &idx);
527
528 accessible = gtk_at_context_get_accessible (self: GTK_AT_CONTEXT (ptr: self));
529
530 if (GTK_IS_STACK_PAGE (accessible))
531 {
532 if (idx == 0)
533 {
534 GtkWidget *child;
535
536 child = gtk_stack_page_get_child (GTK_STACK_PAGE (accessible));
537 if (gtk_accessible_should_present (self: GTK_ACCESSIBLE (ptr: child)))
538 context = gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: child));
539 }
540 }
541 else if (GTK_IS_WIDGET (accessible))
542 {
543 GtkWidget *widget = GTK_WIDGET (accessible);
544 GtkWidget *child;
545
546 real_idx = 0;
547 for (child = gtk_widget_get_first_child (widget);
548 child;
549 child = gtk_widget_get_next_sibling (widget: child))
550 {
551 if (!gtk_accessible_should_present (self: GTK_ACCESSIBLE (ptr: child)))
552 continue;
553
554 if (real_idx == idx)
555 break;
556
557 real_idx += 1;
558 }
559
560 if (child)
561 {
562 if (GTK_IS_STACK (accessible))
563 context = gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: gtk_stack_get_page (GTK_STACK (accessible), child)));
564 else
565 context = gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: child));
566 }
567 }
568
569 if (context == NULL)
570 {
571 g_dbus_method_invocation_return_error (invocation,
572 G_IO_ERROR,
573 code: G_IO_ERROR_INVALID_ARGUMENT,
574 format: "No child with index %d", idx);
575 return;
576 }
577
578 /* Realize the child ATContext in order to get its ref */
579 gtk_at_context_realize (self: context);
580
581 GVariant *ref = gtk_at_spi_context_to_ref (self: GTK_AT_SPI_CONTEXT (ptr: context));
582
583 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(@(so))", ref));
584 }
585 else if (g_strcmp0 (str1: method_name, str2: "GetChildren") == 0)
586 {
587 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a(so)"));
588
589 GtkAccessible *accessible = gtk_at_context_get_accessible (self: GTK_AT_CONTEXT (ptr: self));
590 if (GTK_IS_WIDGET (accessible))
591 {
592 GtkWidget *widget = GTK_WIDGET (accessible);
593 GtkWidget *child;
594
595 for (child = gtk_widget_get_first_child (widget);
596 child;
597 child = gtk_widget_get_next_sibling (widget: child))
598 {
599 if (!gtk_accessible_should_present (self: GTK_ACCESSIBLE (ptr: child)))
600 continue;
601
602 GtkATContext *context = gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: child));
603
604 /* Realize the child ATContext in order to get its ref */
605 gtk_at_context_realize (self: context);
606
607 GVariant *ref = gtk_at_spi_context_to_ref (self: GTK_AT_SPI_CONTEXT (ptr: context));
608
609 if (ref != NULL)
610 g_variant_builder_add (builder: &builder, format_string: "@(so)", ref);
611 }
612 }
613 else if (GTK_IS_STACK_PAGE (accessible))
614 {
615 GtkWidget *child = gtk_stack_page_get_child (GTK_STACK_PAGE (accessible));
616
617 if (gtk_accessible_should_present (self: GTK_ACCESSIBLE (ptr: child)))
618 {
619 GtkATContext *context = gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: child));
620
621 /* Realize the child ATContext in order to get its ref */
622 gtk_at_context_realize (self: context);
623
624 GVariant *ref = gtk_at_spi_context_to_ref (self: GTK_AT_SPI_CONTEXT (ptr: context));
625
626 if (ref != NULL)
627 g_variant_builder_add (builder: &builder, format_string: "@(so)", ref);
628 }
629 }
630
631 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(a(so))", &builder));
632 }
633 else if (g_strcmp0 (str1: method_name, str2: "GetIndexInParent") == 0)
634 {
635 int idx = gtk_at_spi_context_get_index_in_parent (self);
636
637 if (idx == -1)
638 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_FAILED, format: "Not found");
639 else
640 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(i)", idx));
641 }
642 else if (g_strcmp0 (str1: method_name, str2: "GetRelationSet") == 0)
643 {
644 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a(ua(so))"));
645 collect_relations (self, builder: &builder);
646 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(a(ua(so)))", &builder));
647 }
648 else if (g_strcmp0 (str1: method_name, str2: "GetInterfaces") == 0)
649 {
650 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(@as)", self->interfaces));
651 }
652
653}
654
655static GVariant *
656handle_accessible_get_property (GDBusConnection *connection,
657 const gchar *sender,
658 const gchar *object_path,
659 const gchar *interface_name,
660 const gchar *property_name,
661 GError **error,
662 gpointer user_data)
663{
664 GtkAtSpiContext *self = user_data;
665 GVariant *res = NULL;
666
667 GtkAccessible *accessible = gtk_at_context_get_accessible (self: GTK_AT_CONTEXT (ptr: self));
668
669 GTK_NOTE (A11Y, g_message ("handling GetProperty %s on %s", property_name, object_path));
670
671 if (g_strcmp0 (str1: property_name, str2: "Name") == 0)
672 {
673 char *label = gtk_at_context_get_name (self: GTK_AT_CONTEXT (ptr: self));
674 res = g_variant_new_string (string: label ? label : "");
675 g_free (mem: label);
676 }
677 else if (g_strcmp0 (str1: property_name, str2: "Description") == 0)
678 {
679 char *label = gtk_at_context_get_description (self: GTK_AT_CONTEXT (ptr: self));
680 res = g_variant_new_string (string: label ? label : "");
681 g_free (mem: label);
682 }
683 else if (g_strcmp0 (str1: property_name, str2: "Locale") == 0)
684 res = g_variant_new_string (string: setlocale (LC_MESSAGES, NULL));
685 else if (g_strcmp0 (str1: property_name, str2: "AccessibleId") == 0)
686 res = g_variant_new_string (string: "");
687 else if (g_strcmp0 (str1: property_name, str2: "Parent") == 0)
688 res = get_parent_context_ref (accessible);
689 else if (g_strcmp0 (str1: property_name, str2: "ChildCount") == 0)
690 res = g_variant_new_int32 (value: gtk_at_spi_context_get_child_count (self));
691 else
692 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED,
693 format: "Unknown property '%s'", property_name);
694
695 return res;
696}
697
698static const GDBusInterfaceVTable accessible_vtable = {
699 handle_accessible_method,
700 handle_accessible_get_property,
701 NULL,
702};
703/* }}} */
704/* {{{ Change notification */
705static void
706emit_text_changed (GtkAtSpiContext *self,
707 const char *kind,
708 int start,
709 int end,
710 const char *text)
711{
712 if (self->connection == NULL)
713 return;
714
715 g_dbus_connection_emit_signal (connection: self->connection,
716 NULL,
717 object_path: self->context_path,
718 interface_name: "org.a11y.atspi.Event.Object",
719 signal_name: "TextChanged",
720 parameters: g_variant_new (format_string: "(siiva{sv})",
721 kind, start, end, g_variant_new_string (string: text), NULL),
722 NULL);
723}
724
725static void
726emit_text_selection_changed (GtkAtSpiContext *self,
727 const char *kind,
728 int cursor_position)
729{
730 if (self->connection == NULL)
731 return;
732
733 if (strcmp (s1: kind, s2: "text-caret-moved") == 0)
734 g_dbus_connection_emit_signal (connection: self->connection,
735 NULL,
736 object_path: self->context_path,
737 interface_name: "org.a11y.atspi.Event.Object",
738 signal_name: "TextCaretMoved",
739 parameters: g_variant_new (format_string: "(siiva{sv})",
740 "", cursor_position, 0, g_variant_new_string (string: ""), NULL),
741 NULL);
742 else
743 g_dbus_connection_emit_signal (connection: self->connection,
744 NULL,
745 object_path: self->context_path,
746 interface_name: "org.a11y.atspi.Event.Object",
747 signal_name: "TextSelectionChanged",
748 parameters: g_variant_new (format_string: "(siiva{sv})",
749 "", 0, 0, g_variant_new_string (string: ""), NULL),
750 NULL);
751}
752
753static void
754emit_selection_changed (GtkAtSpiContext *self,
755 const char *kind)
756{
757 if (self->connection == NULL)
758 return;
759
760 g_dbus_connection_emit_signal (connection: self->connection,
761 NULL,
762 object_path: self->context_path,
763 interface_name: "org.a11y.atspi.Event.Object",
764 signal_name: "SelectionChanged",
765 parameters: g_variant_new (format_string: "(siiva{sv})",
766 "", 0, 0, g_variant_new_string (string: ""), NULL),
767 NULL);
768}
769
770static void
771emit_state_changed (GtkAtSpiContext *self,
772 const char *name,
773 gboolean enabled)
774{
775 if (self->connection == NULL)
776 return;
777
778 g_dbus_connection_emit_signal (connection: self->connection,
779 NULL,
780 object_path: self->context_path,
781 interface_name: "org.a11y.atspi.Event.Object",
782 signal_name: "StateChanged",
783 parameters: g_variant_new (format_string: "(siiva{sv})",
784 name, enabled, 0, g_variant_new_string (string: "0"), NULL),
785 NULL);
786}
787
788static void
789emit_defunct (GtkAtSpiContext *self)
790{
791 if (self->connection == NULL)
792 return;
793
794 g_dbus_connection_emit_signal (connection: self->connection,
795 NULL,
796 object_path: self->context_path,
797 interface_name: "org.a11y.atspi.Event.Object",
798 signal_name: "StateChanged",
799 parameters: g_variant_new (format_string: "(siiva{sv})", "defunct", TRUE, 0, g_variant_new_string (string: "0"), NULL),
800 NULL);
801}
802
803static void
804emit_property_changed (GtkAtSpiContext *self,
805 const char *name,
806 GVariant *value)
807{
808 if (self->connection == NULL)
809 return;
810
811 g_dbus_connection_emit_signal (connection: self->connection,
812 NULL,
813 object_path: self->context_path,
814 interface_name: "org.a11y.atspi.Event.Object",
815 signal_name: "PropertyChange",
816 parameters: g_variant_new (format_string: "(siiva{sv})",
817 name, 0, 0, value, NULL),
818 NULL);
819}
820
821static void
822emit_bounds_changed (GtkAtSpiContext *self,
823 int x,
824 int y,
825 int width,
826 int height)
827{
828 if (self->connection == NULL)
829 return;
830
831 g_dbus_connection_emit_signal (connection: self->connection,
832 NULL,
833 object_path: self->context_path,
834 interface_name: "org.a11y.atspi.Event.Object",
835 signal_name: "BoundsChanged",
836 parameters: g_variant_new (format_string: "(siiva{sv})",
837 "", 0, 0, g_variant_new (format_string: "(iiii)", x, y, width, height), NULL),
838 NULL);
839}
840
841static void
842emit_children_changed (GtkAtSpiContext *self,
843 GtkAtSpiContext *child_context,
844 int idx,
845 GtkAccessibleChildState state)
846{
847 /* If we don't have a connection on either contexts, we cannot emit a signal */
848 if (self->connection == NULL || child_context->connection == NULL)
849 return;
850
851 GVariant *context_ref = gtk_at_spi_context_to_ref (self);
852 GVariant *child_ref = gtk_at_spi_context_to_ref (self: child_context);
853
854 gtk_at_spi_emit_children_changed (connection: self->connection,
855 path: self->context_path,
856 state,
857 idx,
858 child_ref,
859 sender_ref: context_ref);
860}
861
862static void
863emit_focus (GtkAtSpiContext *self,
864 gboolean focus_in)
865{
866 if (self->connection == NULL)
867 return;
868
869 if (focus_in)
870 g_dbus_connection_emit_signal (connection: self->connection,
871 NULL,
872 object_path: self->context_path,
873 interface_name: "org.a11y.atspi.Event.Focus",
874 signal_name: "Focus",
875 parameters: g_variant_new (format_string: "(siiva{sv})",
876 "", 0, 0, g_variant_new_string (string: "0"), NULL),
877 NULL);
878}
879
880static void
881emit_window_event (GtkAtSpiContext *self,
882 const char *event_type)
883{
884 if (self->connection == NULL)
885 return;
886
887 g_dbus_connection_emit_signal (connection: self->connection,
888 NULL,
889 object_path: self->context_path,
890 interface_name: "org.a11y.atspi.Event.Window",
891 signal_name: event_type,
892 parameters: g_variant_new (format_string: "(siiva{sv})",
893 "", 0, 0,
894 g_variant_new_string(string: "0"),
895 NULL),
896 NULL);
897}
898
899static void
900gtk_at_spi_context_state_change (GtkATContext *ctx,
901 GtkAccessibleStateChange changed_states,
902 GtkAccessiblePropertyChange changed_properties,
903 GtkAccessibleRelationChange changed_relations,
904 GtkAccessibleAttributeSet *states,
905 GtkAccessibleAttributeSet *properties,
906 GtkAccessibleAttributeSet *relations)
907{
908 GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ptr: ctx);
909 GtkAccessible *accessible = gtk_at_context_get_accessible (self: ctx);
910 GtkAccessibleValue *value;
911
912 if (GTK_IS_WIDGET (accessible) && !gtk_widget_get_realized (GTK_WIDGET (accessible)))
913 return;
914
915 if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_HIDDEN)
916 {
917 GtkWidget *parent;
918 GtkATContext *context;
919 GtkAccessibleChildChange change;
920
921 value = gtk_accessible_attribute_set_get_value (self: states, state: GTK_ACCESSIBLE_STATE_HIDDEN);
922 if (gtk_boolean_accessible_value_get (value))
923 change = GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED;
924 else
925 change = GTK_ACCESSIBLE_CHILD_CHANGE_ADDED;
926
927 if (GTK_IS_ROOT (ptr: accessible))
928 {
929 gtk_at_spi_root_child_changed (self: self->root, change, child: accessible);
930 emit_state_changed (self, name: "showing", enabled: gtk_boolean_accessible_value_get (value));
931 }
932 else
933 {
934 if (GTK_IS_WIDGET (accessible))
935 parent = gtk_widget_get_parent (GTK_WIDGET (accessible));
936 else if (GTK_IS_STACK_PAGE (accessible))
937 parent = gtk_widget_get_parent (widget: gtk_stack_page_get_child (GTK_STACK_PAGE (accessible)));
938 else
939 g_assert_not_reached ();
940
941 context = gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: parent));
942 gtk_at_context_child_changed (self: context, change, child: accessible);
943 }
944 }
945
946 if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_BUSY)
947 {
948 value = gtk_accessible_attribute_set_get_value (self: states, state: GTK_ACCESSIBLE_STATE_BUSY);
949 emit_state_changed (self, name: "busy", enabled: gtk_boolean_accessible_value_get (value));
950 }
951
952 if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_CHECKED)
953 {
954 value = gtk_accessible_attribute_set_get_value (self: states, state: GTK_ACCESSIBLE_STATE_CHECKED);
955
956 if (value->value_class->type == GTK_ACCESSIBLE_VALUE_TYPE_TRISTATE)
957 {
958 switch (gtk_tristate_accessible_value_get (value))
959 {
960 case GTK_ACCESSIBLE_TRISTATE_TRUE:
961 emit_state_changed (self, name: "checked", TRUE);
962 emit_state_changed (self, name: "indeterminate", FALSE);
963 break;
964 case GTK_ACCESSIBLE_TRISTATE_MIXED:
965 emit_state_changed (self, name: "checked", FALSE);
966 emit_state_changed (self, name: "indeterminate", TRUE);
967 break;
968 case GTK_ACCESSIBLE_TRISTATE_FALSE:
969 emit_state_changed (self, name: "checked", FALSE);
970 emit_state_changed (self, name: "indeterminate", FALSE);
971 break;
972 default:
973 break;
974 }
975 }
976 else
977 {
978 emit_state_changed (self, name: "checked", FALSE);
979 emit_state_changed (self, name: "indeterminate", TRUE);
980 }
981 }
982
983 if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_DISABLED)
984 {
985 value = gtk_accessible_attribute_set_get_value (self: states, state: GTK_ACCESSIBLE_STATE_DISABLED);
986 emit_state_changed (self, name: "sensitive", enabled: !gtk_boolean_accessible_value_get (value));
987 }
988
989 if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_EXPANDED)
990 {
991 value = gtk_accessible_attribute_set_get_value (self: states, state: GTK_ACCESSIBLE_STATE_EXPANDED);
992 if (value->value_class->type == GTK_ACCESSIBLE_VALUE_TYPE_BOOLEAN)
993 {
994 emit_state_changed (self, name: "expandable", TRUE);
995 emit_state_changed (self, name: "expanded",enabled: gtk_boolean_accessible_value_get (value));
996 }
997 else
998 emit_state_changed (self, name: "expandable", FALSE);
999 }
1000
1001 if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_INVALID)
1002 {
1003 value = gtk_accessible_attribute_set_get_value (self: states, state: GTK_ACCESSIBLE_STATE_INVALID);
1004 switch (gtk_invalid_accessible_value_get (value))
1005 {
1006 case GTK_ACCESSIBLE_INVALID_TRUE:
1007 case GTK_ACCESSIBLE_INVALID_GRAMMAR:
1008 case GTK_ACCESSIBLE_INVALID_SPELLING:
1009 emit_state_changed (self, name: "invalid", TRUE);
1010 break;
1011 case GTK_ACCESSIBLE_INVALID_FALSE:
1012 emit_state_changed (self, name: "invalid", FALSE);
1013 break;
1014 default:
1015 break;
1016 }
1017 }
1018
1019 if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_PRESSED)
1020 {
1021 value = gtk_accessible_attribute_set_get_value (self: states, state: GTK_ACCESSIBLE_STATE_PRESSED);
1022
1023 if (value->value_class->type == GTK_ACCESSIBLE_VALUE_TYPE_TRISTATE)
1024 {
1025 switch (gtk_tristate_accessible_value_get (value))
1026 {
1027 case GTK_ACCESSIBLE_TRISTATE_TRUE:
1028 emit_state_changed (self, name: "pressed", TRUE);
1029 emit_state_changed (self, name: "indeterminate", FALSE);
1030 break;
1031 case GTK_ACCESSIBLE_TRISTATE_MIXED:
1032 emit_state_changed (self, name: "pressed", FALSE);
1033 emit_state_changed (self, name: "indeterminate", TRUE);
1034 break;
1035 case GTK_ACCESSIBLE_TRISTATE_FALSE:
1036 emit_state_changed (self, name: "pressed", FALSE);
1037 emit_state_changed (self, name: "indeterminate", FALSE);
1038 break;
1039 default:
1040 break;
1041 }
1042 }
1043 else
1044 {
1045 emit_state_changed (self, name: "pressed", FALSE);
1046 emit_state_changed (self, name: "indeterminate", TRUE);
1047 }
1048 }
1049
1050 if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_SELECTED)
1051 {
1052 value = gtk_accessible_attribute_set_get_value (self: states, state: GTK_ACCESSIBLE_STATE_SELECTED);
1053 if (value->value_class->type == GTK_ACCESSIBLE_VALUE_TYPE_BOOLEAN)
1054 {
1055 emit_state_changed (self, name: "selectable", TRUE);
1056 emit_state_changed (self, name: "selected",enabled: gtk_boolean_accessible_value_get (value));
1057 }
1058 else
1059 emit_state_changed (self, name: "selectable", FALSE);
1060 }
1061
1062 if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_READ_ONLY)
1063 {
1064 gboolean readonly;
1065
1066 value = gtk_accessible_attribute_set_get_value (self: properties, state: GTK_ACCESSIBLE_PROPERTY_READ_ONLY);
1067 readonly = gtk_boolean_accessible_value_get (value);
1068
1069 emit_state_changed (self, name: "read-only", enabled: readonly);
1070 if (ctx->accessible_role == GTK_ACCESSIBLE_ROLE_TEXT_BOX)
1071 emit_state_changed (self, name: "editable", enabled: !readonly);
1072 }
1073
1074 if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_ORIENTATION)
1075 {
1076 value = gtk_accessible_attribute_set_get_value (self: properties, state: GTK_ACCESSIBLE_PROPERTY_ORIENTATION);
1077 if (gtk_orientation_accessible_value_get (value) == GTK_ORIENTATION_HORIZONTAL)
1078 {
1079 emit_state_changed (self, name: "horizontal", TRUE);
1080 emit_state_changed (self, name: "vertical", FALSE);
1081 }
1082 else
1083 {
1084 emit_state_changed (self, name: "horizontal", FALSE);
1085 emit_state_changed (self, name: "vertical", TRUE);
1086 }
1087 }
1088
1089 if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_MODAL)
1090 {
1091 value = gtk_accessible_attribute_set_get_value (self: properties, state: GTK_ACCESSIBLE_PROPERTY_MODAL);
1092 emit_state_changed (self, name: "modal", enabled: gtk_boolean_accessible_value_get (value));
1093 }
1094
1095 if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_MULTI_LINE)
1096 {
1097 value = gtk_accessible_attribute_set_get_value (self: properties, state: GTK_ACCESSIBLE_PROPERTY_MULTI_LINE);
1098 emit_state_changed (self, name: "multi-line", enabled: gtk_boolean_accessible_value_get (value));
1099 }
1100
1101 if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_LABEL)
1102 {
1103 char *label = gtk_at_context_get_name (self: GTK_AT_CONTEXT (ptr: self));
1104 GVariant *v = g_variant_new_take_string (string: label);
1105 emit_property_changed (self, name: "accessible-name", value: v);
1106 }
1107
1108 if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_DESCRIPTION)
1109 {
1110 char *label = gtk_at_context_get_description (self: GTK_AT_CONTEXT (ptr: self));
1111 GVariant *v = g_variant_new_take_string (string: label);
1112 emit_property_changed (self, name: "accessible-description", value: v);
1113 }
1114}
1115
1116static void
1117gtk_at_spi_context_platform_change (GtkATContext *ctx,
1118 GtkAccessiblePlatformChange changed_platform)
1119{
1120 GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ptr: ctx);
1121 GtkAccessible *accessible = gtk_at_context_get_accessible (self: ctx);
1122 GtkWidget *widget;
1123
1124 if (!GTK_IS_WIDGET (accessible))
1125 return;
1126
1127 widget = GTK_WIDGET (accessible);
1128 if (!gtk_widget_get_realized (widget))
1129 return;
1130
1131 if (changed_platform & GTK_ACCESSIBLE_PLATFORM_CHANGE_FOCUSABLE)
1132 {
1133 gboolean state = gtk_accessible_get_platform_state (self: GTK_ACCESSIBLE (ptr: widget),
1134 state: GTK_ACCESSIBLE_PLATFORM_STATE_FOCUSABLE);
1135 emit_state_changed (self, name: "focusable", enabled: state);
1136 }
1137
1138 if (changed_platform & GTK_ACCESSIBLE_PLATFORM_CHANGE_FOCUSED)
1139 {
1140 gboolean state = gtk_accessible_get_platform_state (self: GTK_ACCESSIBLE (ptr: widget),
1141 state: GTK_ACCESSIBLE_PLATFORM_STATE_FOCUSED);
1142 emit_state_changed (self, name: "focused", enabled: state);
1143 emit_focus (self, focus_in: state);
1144 }
1145
1146 if (changed_platform & GTK_ACCESSIBLE_PLATFORM_CHANGE_ACTIVE)
1147 {
1148 gboolean state = gtk_accessible_get_platform_state (self: GTK_ACCESSIBLE (ptr: widget),
1149 state: GTK_ACCESSIBLE_PLATFORM_STATE_ACTIVE);
1150 emit_state_changed (self, name: "active", enabled: state);
1151
1152 /* Orca tracks the window:activate and window:deactivate events on top
1153 * levels to decide whether to track other AT-SPI events
1154 */
1155 if (gtk_accessible_get_accessible_role (self: accessible) == GTK_ACCESSIBLE_ROLE_WINDOW)
1156 {
1157 if (state)
1158 emit_window_event (self, event_type: "activate");
1159 else
1160 emit_window_event (self, event_type: "deactivate");
1161 }
1162 }
1163}
1164
1165static void
1166gtk_at_spi_context_bounds_change (GtkATContext *ctx)
1167{
1168 GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ptr: ctx);
1169 GtkAccessible *accessible = gtk_at_context_get_accessible (self: ctx);
1170 GtkWidget *widget;
1171 GtkWidget *parent;
1172 double x, y;
1173 int width, height;
1174
1175 if (!GTK_IS_WIDGET (accessible))
1176 return;
1177
1178 widget = GTK_WIDGET (accessible);
1179 if (!gtk_widget_get_realized (widget))
1180 return;
1181
1182 parent = gtk_widget_get_parent (widget);
1183
1184 if (parent)
1185 gtk_widget_translate_coordinates (src_widget: widget, dest_widget: parent, src_x: 0., src_y: 0., dest_x: &x, dest_y: &y);
1186 else
1187 x = y = 0.;
1188
1189 width = gtk_widget_get_width (widget);
1190 height = gtk_widget_get_height (widget);
1191
1192 emit_bounds_changed (self, x: (int)x, y: (int)y, width, height);
1193}
1194
1195static void
1196gtk_at_spi_context_child_change (GtkATContext *ctx,
1197 GtkAccessibleChildChange change,
1198 GtkAccessible *child)
1199{
1200 GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ptr: ctx);
1201 GtkAccessible *accessible = gtk_at_context_get_accessible (self: ctx);
1202 GtkATContext *child_context = gtk_accessible_get_at_context (self: child);
1203 GtkWidget *parent_widget;
1204 GtkWidget *child_widget;
1205 int idx = 0;
1206
1207 if (!GTK_IS_WIDGET (accessible))
1208 return;
1209
1210 if (child_context == NULL)
1211 return;
1212
1213 /* handle the stack page special case */
1214 if (GTK_IS_WIDGET (child) &&
1215 GTK_IS_STACK (gtk_widget_get_parent (GTK_WIDGET (child))))
1216 {
1217 GtkWidget *stack;
1218 GtkStackPage *page;
1219 GListModel *pages;
1220
1221 stack = gtk_widget_get_parent (GTK_WIDGET (child));
1222 page = gtk_stack_get_page (GTK_STACK (stack), GTK_WIDGET (child));
1223 pages = G_LIST_MODEL (ptr: gtk_stack_get_pages (GTK_STACK (stack)));
1224 idx = 0;
1225 for (guint i = 0; i < g_list_model_get_n_items (list: pages); i++)
1226 {
1227 GtkStackPage *item = g_list_model_get_item (list: pages, position: i);
1228
1229 g_object_unref (object: item);
1230
1231 if (!gtk_accessible_should_present (self: GTK_ACCESSIBLE (ptr: item)))
1232 continue;
1233
1234 if (item == page)
1235 break;
1236
1237 idx++;
1238 }
1239 g_object_unref (object: pages);
1240
1241 if (change & GTK_ACCESSIBLE_CHILD_CHANGE_ADDED)
1242 {
1243 emit_children_changed (self: GTK_AT_SPI_CONTEXT (ptr: gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: stack))),
1244 child_context: GTK_AT_SPI_CONTEXT (ptr: gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: page))),
1245 idx,
1246 state: GTK_ACCESSIBLE_CHILD_STATE_ADDED);
1247
1248 emit_children_changed (self: GTK_AT_SPI_CONTEXT (ptr: gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: page))),
1249 child_context: GTK_AT_SPI_CONTEXT (ptr: gtk_accessible_get_at_context (self: child)),
1250 idx: 0,
1251 state: GTK_ACCESSIBLE_CHILD_STATE_ADDED);
1252 }
1253
1254 if (change & GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED)
1255 {
1256 emit_children_changed (self: GTK_AT_SPI_CONTEXT (ptr: gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: page))),
1257 child_context: GTK_AT_SPI_CONTEXT (ptr: gtk_accessible_get_at_context (self: child)),
1258 idx: 0,
1259 state: GTK_ACCESSIBLE_CHILD_STATE_REMOVED);
1260 emit_children_changed (self: GTK_AT_SPI_CONTEXT (ptr: gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: stack))),
1261 child_context: GTK_AT_SPI_CONTEXT (ptr: gtk_accessible_get_at_context (self: GTK_ACCESSIBLE (ptr: page))),
1262 idx,
1263 state: GTK_ACCESSIBLE_CHILD_STATE_REMOVED);
1264
1265 }
1266
1267 return;
1268 }
1269
1270 parent_widget = GTK_WIDGET (accessible);
1271
1272 if (GTK_IS_STACK_PAGE (child))
1273 child_widget = gtk_stack_page_get_child (GTK_STACK_PAGE (child));
1274 else
1275 child_widget = GTK_WIDGET (child);
1276
1277 if (gtk_widget_get_parent (widget: child_widget) != parent_widget)
1278 {
1279 idx = 0;
1280 }
1281 else
1282 {
1283 for (GtkWidget *iter = gtk_widget_get_first_child (widget: parent_widget);
1284 iter != NULL;
1285 iter = gtk_widget_get_next_sibling (widget: iter))
1286 {
1287 if (!gtk_accessible_should_present (self: GTK_ACCESSIBLE (ptr: iter)))
1288 continue;
1289
1290 if (iter == child_widget)
1291 break;
1292
1293 idx += 1;
1294 }
1295 }
1296
1297 if (change & GTK_ACCESSIBLE_CHILD_CHANGE_ADDED)
1298 emit_children_changed (self,
1299 child_context: GTK_AT_SPI_CONTEXT (ptr: child_context),
1300 idx,
1301 state: GTK_ACCESSIBLE_CHILD_STATE_ADDED);
1302 else if (change & GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED)
1303 emit_children_changed (self,
1304 child_context: GTK_AT_SPI_CONTEXT (ptr: child_context),
1305 idx,
1306 state: GTK_ACCESSIBLE_CHILD_STATE_REMOVED);
1307}
1308/* }}} */
1309/* {{{ D-Bus Registration */
1310static void
1311gtk_at_spi_context_register_object (GtkAtSpiContext *self)
1312{
1313 GtkAccessible *accessible = gtk_at_context_get_accessible (self: GTK_AT_CONTEXT (ptr: self));
1314 GVariantBuilder interfaces = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_STRING_ARRAY);
1315 const GDBusInterfaceVTable *vtable;
1316
1317 g_variant_builder_add (builder: &interfaces, format_string: "s", atspi_accessible_interface.name);
1318 self->registration_ids[self->n_registered_objects] =
1319 g_dbus_connection_register_object (self->connection,
1320 self->context_path,
1321 (GDBusInterfaceInfo *) &atspi_accessible_interface,
1322 &accessible_vtable,
1323 self,
1324 NULL,
1325 NULL);
1326 self->n_registered_objects++;
1327
1328 vtable = gtk_atspi_get_component_vtable (accessible);
1329 if (vtable)
1330 {
1331 g_variant_builder_add (builder: &interfaces, format_string: "s", atspi_component_interface.name);
1332 self->registration_ids[self->n_registered_objects] =
1333 g_dbus_connection_register_object (self->connection,
1334 self->context_path,
1335 (GDBusInterfaceInfo *) &atspi_component_interface,
1336 vtable,
1337 self,
1338 NULL,
1339 NULL);
1340 self->n_registered_objects++;
1341 }
1342
1343 vtable = gtk_atspi_get_text_vtable (accessible);
1344 if (vtable)
1345 {
1346 g_variant_builder_add (builder: &interfaces, format_string: "s", atspi_text_interface.name);
1347 self->registration_ids[self->n_registered_objects] =
1348 g_dbus_connection_register_object (self->connection,
1349 self->context_path,
1350 (GDBusInterfaceInfo *) &atspi_text_interface,
1351 vtable,
1352 self,
1353 NULL,
1354 NULL);
1355 self->n_registered_objects++;
1356 }
1357
1358 vtable = gtk_atspi_get_editable_text_vtable (accessible);
1359 if (vtable)
1360 {
1361 g_variant_builder_add (builder: &interfaces, format_string: "s", atspi_editable_text_interface.name);
1362 self->registration_ids[self->n_registered_objects] =
1363 g_dbus_connection_register_object (self->connection,
1364 self->context_path,
1365 (GDBusInterfaceInfo *) &atspi_editable_text_interface,
1366 vtable,
1367 self,
1368 NULL,
1369 NULL);
1370 self->n_registered_objects++;
1371 }
1372 vtable = gtk_atspi_get_value_vtable (accessible);
1373 if (vtable)
1374 {
1375 g_variant_builder_add (builder: &interfaces, format_string: "s", atspi_value_interface.name);
1376 self->registration_ids[self->n_registered_objects] =
1377 g_dbus_connection_register_object (self->connection,
1378 self->context_path,
1379 (GDBusInterfaceInfo *) &atspi_value_interface,
1380 vtable,
1381 self,
1382 NULL,
1383 NULL);
1384 self->n_registered_objects++;
1385 }
1386
1387 /* Calling gtk_accessible_get_accessible_role() in here will recurse,
1388 * so pass the role in explicitly.
1389 */
1390 vtable = gtk_atspi_get_selection_vtable (accessible,
1391 role: GTK_AT_CONTEXT (ptr: self)->accessible_role);
1392 if (vtable)
1393 {
1394 g_variant_builder_add (builder: &interfaces, format_string: "s", atspi_selection_interface.name);
1395 self->registration_ids[self->n_registered_objects] =
1396 g_dbus_connection_register_object (self->connection,
1397 self->context_path,
1398 (GDBusInterfaceInfo *) &atspi_selection_interface,
1399 vtable,
1400 self,
1401 NULL,
1402 NULL);
1403 self->n_registered_objects++;
1404 }
1405
1406 vtable = gtk_atspi_get_action_vtable (accessible);
1407 if (vtable)
1408 {
1409 g_variant_builder_add (builder: &interfaces, format_string: "s", atspi_action_interface.name);
1410 self->registration_ids[self->n_registered_objects] =
1411 g_dbus_connection_register_object (self->connection,
1412 self->context_path,
1413 (GDBusInterfaceInfo *) &atspi_action_interface,
1414 vtable,
1415 self,
1416 NULL,
1417 NULL);
1418 self->n_registered_objects++;
1419 }
1420
1421 self->interfaces = g_variant_ref_sink (value: g_variant_builder_end (builder: &interfaces));
1422
1423 GTK_NOTE (A11Y, g_message ("Registered %d interfaces on object path '%s'",
1424 self->n_registered_objects,
1425 self->context_path));
1426}
1427
1428static void
1429gtk_at_spi_context_unregister_object (GtkAtSpiContext *self)
1430{
1431 while (self->n_registered_objects > 0)
1432 {
1433 self->n_registered_objects--;
1434 g_dbus_connection_unregister_object (connection: self->connection,
1435 registration_id: self->registration_ids[self->n_registered_objects]);
1436 self->registration_ids[self->n_registered_objects] = 0;
1437 }
1438
1439 g_clear_pointer (&self->interfaces, g_variant_unref);
1440}
1441/* }}} */
1442/* {{{ GObject boilerplate */
1443static void
1444gtk_at_spi_context_finalize (GObject *gobject)
1445{
1446 GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ptr: gobject);
1447
1448 gtk_at_spi_context_unregister_object (self);
1449
1450 g_clear_object (&self->root);
1451
1452 g_free (mem: self->context_path);
1453
1454 G_OBJECT_CLASS (gtk_at_spi_context_parent_class)->finalize (gobject);
1455}
1456
1457static void
1458gtk_at_spi_context_constructed (GObject *gobject)
1459{
1460 GtkAtSpiContext *self G_GNUC_UNUSED = GTK_AT_SPI_CONTEXT (ptr: gobject);
1461
1462 G_OBJECT_CLASS (gtk_at_spi_context_parent_class)->constructed (gobject);
1463}
1464
1465static const char *get_bus_address (GdkDisplay *display);
1466
1467static void
1468register_object (GtkAtSpiRoot *root,
1469 GtkAtSpiContext *context)
1470{
1471 GtkAccessible *accessible = gtk_at_context_get_accessible (self: GTK_AT_CONTEXT (ptr: context));
1472
1473 gtk_atspi_connect_text_signals (accessible,
1474 text_changed: (GtkAtspiTextChangedCallback *)emit_text_changed,
1475 selection_changed: (GtkAtspiTextSelectionCallback *)emit_text_selection_changed,
1476 data: context);
1477 gtk_atspi_connect_selection_signals (accessible,
1478 selection_changed: (GtkAtspiSelectionCallback *)emit_selection_changed,
1479 data: context);
1480 gtk_at_spi_context_register_object (self: context);
1481}
1482
1483static void
1484gtk_at_spi_context_realize (GtkATContext *context)
1485{
1486 GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ptr: context);
1487 GdkDisplay *display = gtk_at_context_get_display (self: context);
1488
1489 /* Every GTK application has a single root AT-SPI object, which
1490 * handles all the global state, including the cache of accessible
1491 * objects. We use the GdkDisplay to store it, so it's guaranteed
1492 * to be a unique per-display connection
1493 */
1494 self->root =
1495 g_object_get_data (G_OBJECT (display), key: "-gtk-atspi-root");
1496
1497 if (self->root == NULL)
1498 {
1499 self->root = gtk_at_spi_root_new (bus_address: get_bus_address (display));
1500 g_object_set_data_full (G_OBJECT (display), key: "-gtk-atspi-root",
1501 g_object_ref (self->root),
1502 destroy: g_object_unref);
1503 }
1504 else
1505 {
1506 g_object_ref (self->root);
1507 }
1508
1509 /* UUIDs use '-' as the separator, but that's not a valid character
1510 * for a DBus object path
1511 */
1512 char *uuid = g_uuid_string_random ();
1513 size_t len = strlen (s: uuid);
1514 for (size_t i = 0; i < len; i++)
1515 {
1516 if (uuid[i] == '-')
1517 uuid[i] = '_';
1518 }
1519
1520 self->context_path =
1521 g_strconcat (string1: gtk_at_spi_root_get_base_path (self: self->root), "/", uuid, NULL);
1522
1523 g_free (mem: uuid);
1524
1525 self->connection = gtk_at_spi_root_get_connection (self: self->root);
1526 if (self->connection == NULL)
1527 return;
1528
1529#ifdef G_ENABLE_DEBUG
1530 if (GTK_DEBUG_CHECK (A11Y))
1531 {
1532 GtkAccessible *accessible = gtk_at_context_get_accessible (self: context);
1533 GtkAccessibleRole role = gtk_at_context_get_accessible_role (self: context);
1534 char *role_name = g_enum_to_string (g_enum_type: GTK_TYPE_ACCESSIBLE_ROLE, value: role);
1535 g_message ("Realizing ATSPI context “%s” for accessible “%s”, with role: “%s”",
1536 self->context_path,
1537 G_OBJECT_TYPE_NAME (accessible),
1538 role_name);
1539 g_free (mem: role_name);
1540 }
1541#endif
1542
1543 gtk_at_spi_root_queue_register (self: self->root, context: self, func: register_object);
1544}
1545
1546static void
1547gtk_at_spi_context_unrealize (GtkATContext *context)
1548{
1549 GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ptr: context);
1550 GtkAccessible *accessible = gtk_at_context_get_accessible (self: context);
1551
1552 GTK_NOTE (A11Y, g_message ("Unrealizing ATSPI context at '%s' for accessible '%s'",
1553 self->context_path,
1554 G_OBJECT_TYPE_NAME (accessible)));
1555
1556 /* Notify ATs that the accessible object is going away */
1557 emit_defunct (self);
1558 gtk_at_spi_root_unregister (self: self->root, context: self);
1559
1560 gtk_atspi_disconnect_text_signals (accessible);
1561 gtk_atspi_disconnect_selection_signals (accessible);
1562 gtk_at_spi_context_unregister_object (self);
1563
1564 g_clear_pointer (&self->context_path, g_free);
1565 g_clear_object (&self->root);
1566}
1567
1568static void
1569gtk_at_spi_context_class_init (GtkAtSpiContextClass *klass)
1570{
1571 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1572 GtkATContextClass *context_class = GTK_AT_CONTEXT_CLASS (ptr: klass);
1573
1574 gobject_class->constructed = gtk_at_spi_context_constructed;
1575 gobject_class->finalize = gtk_at_spi_context_finalize;
1576
1577 context_class->realize = gtk_at_spi_context_realize;
1578 context_class->unrealize = gtk_at_spi_context_unrealize;
1579 context_class->state_change = gtk_at_spi_context_state_change;
1580 context_class->platform_change = gtk_at_spi_context_platform_change;
1581 context_class->bounds_change = gtk_at_spi_context_bounds_change;
1582 context_class->child_change = gtk_at_spi_context_child_change;
1583}
1584
1585static void
1586gtk_at_spi_context_init (GtkAtSpiContext *self)
1587{
1588}
1589/* }}} */
1590/* {{{ Bus address discovery */
1591#ifdef GDK_WINDOWING_X11
1592static char *
1593get_bus_address_x11 (GdkDisplay *display)
1594{
1595 GTK_NOTE (A11Y, g_message ("Acquiring a11y bus via X11..."));
1596
1597 Display *xdisplay = gdk_x11_display_get_xdisplay (display);
1598 Atom type_return;
1599 int format_return;
1600 gulong nitems_return;
1601 gulong bytes_after_return;
1602 guchar *data = NULL;
1603 char *address = NULL;
1604
1605 gdk_x11_display_error_trap_push (display);
1606 XGetWindowProperty (xdisplay, DefaultRootWindow (xdisplay),
1607 gdk_x11_get_xatom_by_name_for_display (display, atom_name: "AT_SPI_BUS"),
1608 0L, BUFSIZ, False,
1609 (Atom) 31,
1610 &type_return, &format_return, &nitems_return,
1611 &bytes_after_return, &data);
1612 gdk_x11_display_error_trap_pop_ignored (display);
1613
1614 address = g_strdup (str: (char *) data);
1615
1616 XFree (data);
1617
1618 return address;
1619}
1620#endif
1621
1622#if defined(GDK_WINDOWING_WAYLAND) || defined(GDK_WINDOWING_X11)
1623static char *
1624get_bus_address_dbus (GdkDisplay *display)
1625{
1626 GTK_NOTE (A11Y, g_message ("Acquiring a11y bus via DBus..."));
1627
1628 GError *error = NULL;
1629 GDBusConnection *connection = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, error: &error);
1630
1631 if (error != NULL)
1632 {
1633 GTK_NOTE (A11Y, g_message ("Unable to acquire session bus: %s", error->message));
1634 g_error_free (error);
1635 return NULL;
1636 }
1637
1638 GVariant *res =
1639 g_dbus_connection_call_sync (connection, bus_name: "org.a11y.Bus",
1640 object_path: "/org/a11y/bus",
1641 interface_name: "org.a11y.Bus",
1642 method_name: "GetAddress",
1643 NULL, NULL,
1644 flags: G_DBUS_CALL_FLAGS_NONE,
1645 timeout_msec: -1,
1646 NULL,
1647 error: &error);
1648 if (error != NULL)
1649 {
1650 GTK_NOTE (A11Y, g_message ("Unable to acquire the address of the accessibility bus: %s",
1651 error->message));
1652 g_error_free (error);
1653 }
1654
1655 char *address = NULL;
1656 if (res != NULL)
1657 {
1658 g_variant_get (value: res, format_string: "(s)", &address);
1659 g_variant_unref (value: res);
1660 }
1661
1662 g_object_unref (object: connection);
1663
1664 return address;
1665}
1666#endif
1667
1668static const char *
1669get_bus_address (GdkDisplay *display)
1670{
1671 const char *bus_address;
1672
1673 bus_address = g_object_get_data (G_OBJECT (display), key: "-gtk-atspi-bus-address");
1674 if (bus_address != NULL)
1675 return bus_address;
1676
1677 /* The bus address environment variable takes precedence; this is the
1678 * mechanism used by Flatpak to handle the accessibility bus portal
1679 * between the sandbox and the outside world
1680 */
1681 bus_address = g_getenv (variable: "AT_SPI_BUS_ADDRESS");
1682 if (bus_address != NULL && *bus_address != '\0')
1683 {
1684 GTK_NOTE (A11Y, g_message ("Using ATSPI bus address from environment: %s", bus_address));
1685 g_object_set_data_full (G_OBJECT (display), key: "-gtk-atspi-bus-address",
1686 data: g_strdup (str: bus_address),
1687 destroy: g_free);
1688 goto out;
1689 }
1690
1691#if defined(GDK_WINDOWING_WAYLAND)
1692 if (bus_address == NULL)
1693 {
1694 if (GDK_IS_WAYLAND_DISPLAY (display))
1695 {
1696 char *addr = get_bus_address_dbus (display);
1697
1698 GTK_NOTE (A11Y, g_message ("Using ATSPI bus address from D-Bus: %s", addr));
1699 g_object_set_data_full (G_OBJECT (display), key: "-gtk-atspi-bus-address",
1700 data: addr,
1701 destroy: g_free);
1702
1703 bus_address = addr;
1704 }
1705 }
1706#endif
1707#if defined(GDK_WINDOWING_X11)
1708 if (bus_address == NULL)
1709 {
1710 if (GDK_IS_X11_DISPLAY (display))
1711 {
1712 char *addr = get_bus_address_dbus (display);
1713
1714 if (addr == NULL)
1715 {
1716 addr = get_bus_address_x11 (display);
1717 GTK_NOTE (A11Y, g_message ("Using ATSPI bus address from X11: %s", addr));
1718 }
1719 else
1720 {
1721 GTK_NOTE (A11Y, g_message ("Using ATSPI bus address from D-Bus: %s", addr));
1722 }
1723
1724 g_object_set_data_full (G_OBJECT (display), key: "-gtk-atspi-bus-address",
1725 data: addr,
1726 destroy: g_free);
1727
1728 bus_address = addr;
1729 }
1730 }
1731#endif
1732
1733out:
1734 return bus_address;
1735}
1736
1737/* }}} */
1738/* {{{ API */
1739GtkATContext *
1740gtk_at_spi_create_context (GtkAccessibleRole accessible_role,
1741 GtkAccessible *accessible,
1742 GdkDisplay *display)
1743{
1744 const char *bus_address;
1745
1746 g_return_val_if_fail (GTK_IS_ACCESSIBLE (accessible), NULL);
1747 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1748
1749 bus_address = get_bus_address (display);
1750
1751 if (bus_address == NULL)
1752 bus_address = "";
1753
1754 if (*bus_address == '\0')
1755 return NULL;
1756
1757#if defined(GDK_WINDOWING_WAYLAND)
1758 if (GDK_IS_WAYLAND_DISPLAY (display))
1759 return g_object_new (GTK_TYPE_AT_SPI_CONTEXT,
1760 first_property_name: "accessible-role", accessible_role,
1761 "accessible", accessible,
1762 "display", display,
1763 NULL);
1764#endif
1765#if defined(GDK_WINDOWING_X11)
1766 if (GDK_IS_X11_DISPLAY (display))
1767 return g_object_new (GTK_TYPE_AT_SPI_CONTEXT,
1768 first_property_name: "accessible-role", accessible_role,
1769 "accessible", accessible,
1770 "display", display,
1771 NULL);
1772#endif
1773
1774 return NULL;
1775}
1776
1777const char *
1778gtk_at_spi_context_get_context_path (GtkAtSpiContext *self)
1779{
1780 g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), NULL);
1781
1782 return self->context_path;
1783}
1784
1785/*< private >
1786 * gtk_at_spi_context_to_ref:
1787 * @self: a `GtkAtSpiContext`
1788 *
1789 * Returns an ATSPI object reference for the `GtkAtSpiContext`.
1790 *
1791 * Returns: (transfer floating): a `GVariant` with the reference
1792 */
1793GVariant *
1794gtk_at_spi_context_to_ref (GtkAtSpiContext *self)
1795{
1796 g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), NULL);
1797
1798 if (self->context_path == NULL)
1799 return gtk_at_spi_null_ref ();
1800
1801 const char *name = g_dbus_connection_get_unique_name (connection: self->connection);
1802
1803 return g_variant_new (format_string: "(so)", name, self->context_path);
1804}
1805
1806GVariant *
1807gtk_at_spi_context_get_interfaces (GtkAtSpiContext *self)
1808{
1809 g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), NULL);
1810
1811 return self->interfaces;
1812}
1813
1814GVariant *
1815gtk_at_spi_context_get_states (GtkAtSpiContext *self)
1816{
1817 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("au"));
1818
1819 collect_states (self, builder: &builder);
1820
1821 return g_variant_builder_end (builder: &builder);
1822}
1823
1824GVariant *
1825gtk_at_spi_context_get_parent_ref (GtkAtSpiContext *self)
1826{
1827 g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), NULL);
1828
1829 GtkAccessible *accessible = gtk_at_context_get_accessible (self: GTK_AT_CONTEXT (ptr: self));
1830
1831 return get_parent_context_ref (accessible);
1832}
1833
1834GtkAtSpiRoot *
1835gtk_at_spi_context_get_root (GtkAtSpiContext *self)
1836{
1837 g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), NULL);
1838
1839 return self->root;
1840}
1841
1842int
1843gtk_at_spi_context_get_index_in_parent (GtkAtSpiContext *self)
1844{
1845 g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), -1);
1846
1847 GtkAccessible *accessible = gtk_at_context_get_accessible (self: GTK_AT_CONTEXT (ptr: self));
1848 int idx;
1849
1850 if (GTK_IS_ROOT (ptr: accessible))
1851 idx = get_index_in_toplevels (GTK_WIDGET (accessible));
1852 else if (GTK_IS_STACK_PAGE (accessible))
1853 idx = get_index_in_parent (widget: gtk_stack_page_get_child (GTK_STACK_PAGE (accessible)));
1854 else if (GTK_IS_STACK (gtk_widget_get_parent (GTK_WIDGET (accessible))))
1855 idx = 1;
1856 else
1857 idx = get_index_in_parent (GTK_WIDGET (accessible));
1858
1859 return idx;
1860}
1861
1862int
1863gtk_at_spi_context_get_child_count (GtkAtSpiContext *self)
1864{
1865 g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), -1);
1866
1867 GtkAccessible *accessible = gtk_at_context_get_accessible (self: GTK_AT_CONTEXT (ptr: self));
1868 int n_children = -1;
1869
1870 if (GTK_IS_WIDGET (accessible))
1871 {
1872 GtkWidget *child;
1873
1874 n_children = 0;
1875 for (child = gtk_widget_get_first_child (GTK_WIDGET (accessible));
1876 child;
1877 child = gtk_widget_get_next_sibling (widget: child))
1878 {
1879 if (!gtk_accessible_should_present (self: GTK_ACCESSIBLE (ptr: child)))
1880 continue;
1881
1882 n_children++;
1883 }
1884 }
1885 else if (GTK_IS_STACK_PAGE (accessible))
1886 {
1887 n_children = 1;
1888 }
1889
1890 return n_children;
1891}
1892/* }}} */
1893
1894/* vim:set foldmethod=marker expandtab: */
1895

source code of gtk/gtk/a11y/gtkatspicontext.c