2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2001 Sun Microsystems Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
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 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
26 #include <libbonobo.h>
27 #include <orbit/orbit.h>
29 #include <atk/atkobject.h>
30 #include <atk/atknoopobject.h>
31 #include <libspi/Accessibility.h>
32 #include "accessible.h"
33 #include "application.h"
35 #include <bonobo-activation/bonobo-activation-register.h>
37 #undef SPI_BRIDGE_DEBUG
39 static CORBA_Environment ev;
40 static Accessibility_Registry registry = NULL;
41 static SpiApplication *this_app = NULL;
42 static gboolean registry_died = FALSE;
44 static Accessibility_Registry spi_atk_bridge_get_registry (void);
45 static void spi_atk_bridge_exit_func (void);
46 static void spi_atk_register_event_listeners (void);
47 static void spi_atk_bridge_focus_tracker (AtkObject *object);
48 static void spi_atk_bridge_register_application (Accessibility_Registry registry);
49 static gboolean spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
51 const GValue *param_values,
54 spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint,
56 const GValue *param_values,
59 spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
61 const GValue *param_values,
63 static gboolean spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint,
65 const GValue *param_values,
67 static gint spi_atk_bridge_key_listener (AtkKeyEventStruct *event,
70 /* For automatic libgnome init */
71 extern void gnome_accessibility_module_init (void);
72 extern void gnome_accessibility_module_shutdown (void);
74 static int atk_bridge_initialized = FALSE;
75 static guint atk_bridge_focus_tracker_id = 0;
76 static guint atk_bridge_key_event_listener_id = 0;
77 static GArray *listener_ids = NULL;
80 * These exported symbols are hooked by gnome-program
81 * to provide automatic module initialization and shutdown.
83 extern void gnome_accessibility_module_init (void);
84 extern void gnome_accessibility_module_shutdown (void);
87 atk_bridge_init (gint *argc, gchar **argv[])
91 if (atk_bridge_initialized)
95 atk_bridge_initialized = TRUE;
97 if (!bonobo_init (argc, argv ? *argv : NULL))
99 g_error ("Could not initialize Bonobo");
103 * We only want to enable the bridge for top level
104 * applications, we detect bonobo components by seeing
105 * if they were activated with the intention of extracting
106 * an impl. by IID - very solid.
108 if (bonobo_activation_iid_get ())
111 CORBA_exception_init(&ev);
113 if (spi_atk_bridge_get_registry () == CORBA_OBJECT_NIL)
115 g_error ("Could not locate registry");
120 /* Create the accessible application server object */
122 this_app = spi_application_new (atk_get_root ());
124 fprintf (stderr, "About to register application\n");
126 spi_atk_bridge_register_application (spi_atk_bridge_get_registry ());
128 g_atexit (spi_atk_bridge_exit_func);
130 fprintf (stderr, "Application registered & listening\n");
136 spi_atk_bridge_register_application (Accessibility_Registry registry)
138 Accessibility_Registry_registerApplication (spi_atk_bridge_get_registry (),
139 BONOBO_OBJREF (this_app),
141 spi_atk_register_event_listeners ();
144 static Accessibility_Registry
145 spi_atk_bridge_get_registry ()
147 CORBA_Environment ev;
149 if (registry_died || (registry == NULL)) {
150 CORBA_exception_init (&ev);
151 if (registry_died) g_warning ("registry died! restarting...");
152 registry = bonobo_activation_activate_from_id (
153 "OAFIID:Accessibility_Registry:1.0", 0, NULL, &ev);
155 if (ev._major != CORBA_NO_EXCEPTION)
157 g_error ("Accessibility app error: exception during "
158 "registry activation from id: %s\n",
159 CORBA_exception_id (&ev));
160 CORBA_exception_free (&ev);
163 if (registry_died && registry) {
164 registry_died = FALSE;
165 spi_atk_bridge_register_application (registry);
172 gtk_module_init (gint *argc, gchar **argv[])
174 return atk_bridge_init (argc, argv);
178 add_signal_listener (const char *signal_name)
182 id = atk_add_global_event_listener (
183 spi_atk_bridge_signal_listener, signal_name);
185 g_array_append_val (listener_ids, id);
189 spi_atk_register_event_listeners (void)
192 * kludge to make sure the Atk interface types are registered, otherwise
193 * the AtkText signal handlers below won't get registered
196 GObject *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
197 AtkObject *bo = atk_no_op_object_new (ao);
199 /* Register for focus event notifications, and register app with central registry */
201 listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
203 atk_bridge_focus_tracker_id = atk_add_focus_tracker (spi_atk_bridge_focus_tracker);
205 id = atk_add_global_event_listener (spi_atk_bridge_property_event_listener,
206 "Gtk:AtkObject:property-change");
207 g_array_append_val (listener_ids, id);
208 id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
210 g_array_append_val (listener_ids, id);
211 id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
213 g_array_append_val (listener_ids, id);
214 id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
216 g_array_append_val (listener_ids, id);
217 id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
219 g_array_append_val (listener_ids, id);
220 id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
222 g_array_append_val (listener_ids, id);
223 id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
225 g_array_append_val (listener_ids, id);
226 id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
227 "window:deactivate");
228 g_array_append_val (listener_ids, id);
229 id = atk_add_global_event_listener (spi_atk_bridge_state_event_listener,
230 "Gtk:AtkObject:state-change");
231 g_array_append_val (listener_ids, id);
233 add_signal_listener ("Gtk:AtkObject:children-changed");
234 add_signal_listener ("Gtk:AtkObject:visible-data-changed");
235 add_signal_listener ("Gtk:AtkSelection:selection-changed");
236 add_signal_listener ("Gtk:AtkText:text-selection-changed");
237 add_signal_listener ("Gtk:AtkText:text-changed");
238 add_signal_listener ("Gtk:AtkText:text-caret-moved");
239 add_signal_listener ("Gtk:AtkTable:row-inserted");
240 add_signal_listener ("Gtk:AtkTable:row-reordered");
241 add_signal_listener ("Gtk:AtkTable:row-deleted");
242 add_signal_listener ("Gtk:AtkTable:column-inserted");
243 add_signal_listener ("Gtk:AtkTable:column-reordered");
244 add_signal_listener ("Gtk:AtkTable:column-deleted");
245 add_signal_listener ("Gtk:AtkTable:model-changed");
247 * May add the following listeners to implement preemptive key listening for GTK+
249 * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
250 * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
252 atk_bridge_key_event_listener_id = atk_add_key_event_listener (
253 spi_atk_bridge_key_listener, NULL);
255 g_object_unref (G_OBJECT (bo));
260 deregister_application (BonoboObject *app)
262 Accessibility_Registry registry = spi_atk_bridge_get_registry ();
263 Accessibility_Registry_deregisterApplication (registry, BONOBO_OBJREF (app), &ev);
265 registry = bonobo_object_release_unref (registry, &ev);
267 app = bonobo_object_unref (app);
271 spi_atk_bridge_exit_func (void)
273 BonoboObject *app = (BonoboObject *) this_app;
275 fprintf (stderr, "exiting bridge\n");
284 * FIXME: this may be incorrect for apps that do their own bonobo
285 * shutdown, until we can explicitly shutdown to get the ordering
288 if (!bonobo_is_initialized ())
290 fprintf (stderr, "Re-initializing bonobo\n");
291 g_assert (bonobo_init (0, NULL));
292 g_assert (bonobo_activate ());
295 deregister_application (app);
297 fprintf (stderr, "bridge exit func complete.\n");
299 if (g_getenv ("AT_BRIDGE_SHUTDOWN"))
301 g_assert (!bonobo_debug_shutdown ());
306 gnome_accessibility_module_init (void)
308 atk_bridge_init (NULL, NULL);
310 g_print("Atk Accessibilty bridge initialized\n");
314 gnome_accessibility_module_shutdown (void)
316 BonoboObject *app = (BonoboObject *) this_app;
318 GArray *ids = listener_ids;
320 if (!atk_bridge_initialized)
324 atk_bridge_initialized = FALSE;
327 g_print("Atk Accessibilty bridge shutdown\n");
330 atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
332 for (i = 0; ids && i < ids->len; i++)
334 atk_remove_global_event_listener (g_array_index (ids, guint, i));
337 atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
339 deregister_application (app);
343 spi_atk_bridge_focus_tracker (AtkObject *object)
345 SpiAccessible *source;
346 Accessibility_Event e;
348 source = spi_accessible_new (object);
351 e.source = BONOBO_OBJREF (source);
355 Accessibility_Registry_notifyEvent (spi_atk_bridge_get_registry (), &e, &ev);
356 if (BONOBO_EX (&ev)) registry_died = TRUE;
358 Accessibility_Accessible_unref (e.source, &ev);
360 CORBA_exception_free (&ev);
364 spi_atk_emit_eventv (GObject *gobject,
365 unsigned long detail1,
366 unsigned long detail2,
367 const char *format, ...)
370 Accessibility_Event e;
371 SpiAccessible *source;
373 #ifdef SPI_BRIDGE_DEBUG
377 va_start (args, format);
379 if (ATK_IS_IMPLEMENTOR (gobject))
381 aobject = atk_implementor_ref_accessible (ATK_IMPLEMENTOR (gobject));
382 source = spi_accessible_new (aobject);
383 g_object_unref (G_OBJECT (aobject));
385 else if (ATK_IS_OBJECT (gobject))
387 aobject = ATK_OBJECT (gobject);
388 source = spi_accessible_new (aobject);
394 g_error ("received property-change event from non-AtkImplementor");
399 e.type = g_strdup_vprintf (format, args);
400 e.source = BONOBO_OBJREF (source);
404 #ifdef SPI_BRIDGE_DEBUG
405 s = Accessibility_Accessible__get_name (BONOBO_OBJREF (source), &ev);
406 g_warning ("Emitting event '%s' (%lu, %lu) on %s",
407 e.type, e.detail1, e.detail2, s);
411 Accessibility_Registry_notifyEvent (spi_atk_bridge_get_registry (), &e, &ev);
412 if (BONOBO_EX (&ev)) registry_died = TRUE;
413 Accessibility_Accessible_unref (e.source, &ev);
415 CORBA_exception_free (&ev);
425 spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
426 guint n_param_values,
427 const GValue *param_values,
430 AtkPropertyValues *values;
433 #ifdef SPI_BRIDGE_DEBUG
434 GSignalQuery signal_query;
438 g_signal_query (signal_hint->signal_id, &signal_query);
439 name = signal_query.signal_name;
441 s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
442 s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
443 values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
444 fprintf (stderr, "Received (property) signal %s:%s:%s from object %s (gail %s)\n",
445 g_type_name (signal_query.itype), name, values->property_name, s, s2);
449 gobject = g_value_get_object (param_values + 0);
450 values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
452 spi_atk_emit_eventv (gobject, 0, 0, "object:property-change:%s", values->property_name);
458 spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
459 guint n_param_values,
460 const GValue *param_values,
464 gchar *property_name;
466 unsigned long detail1;
467 #ifdef SPI_BRIDGE_DEBUG
468 GSignalQuery signal_query;
471 g_signal_query (signal_hint->signal_id, &signal_query);
472 name = signal_query.signal_name;
473 fprintf (stderr, "Received (state) signal %s:%s\n",
474 g_type_name (signal_query.itype), name);
477 gobject = g_value_get_object (param_values + 0);
478 property_name = g_strdup (g_value_get_string (param_values + 1));
479 detail1 = (g_value_get_boolean (param_values + 2))
481 type = g_strdup_printf ("object:state-changed:%s", property_name);
482 spi_atk_emit_eventv (gobject,
486 g_free (property_name);
493 spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent *keystroke,
494 AtkKeyEventStruct *event)
499 g_print ("event %c (%d)\n", (int) event->keyval, (int) event->keycode);
505 g_print ("WARNING: NULL key event!");
508 keystroke->id = (CORBA_long) event->keyval;
509 keystroke->hw_code = (CORBA_short) event->keycode;
510 keystroke->timestamp = (CORBA_unsigned_long) event->timestamp;
511 keystroke->modifiers = (CORBA_unsigned_short) (event->state & 0xFFFF);
514 keystroke->event_string = CORBA_string_dup (event->string);
515 keystroke->is_text = CORBA_TRUE;
519 keystroke->event_string = CORBA_string_dup ("");
520 keystroke->is_text = CORBA_FALSE;
524 case (ATK_KEY_EVENT_PRESS):
525 keystroke->type = Accessibility_KEY_PRESSED_EVENT;
527 case (ATK_KEY_EVENT_RELEASE):
528 keystroke->type = Accessibility_KEY_RELEASED_EVENT;
535 g_print ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
536 (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
537 (int) keystroke->modifiers,
538 keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
543 spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data)
545 CORBA_boolean result;
546 Accessibility_DeviceEvent key_event;
547 Accessibility_DeviceEventController controller;
550 g_warning ("failure: pre-listener get dec\n");
553 Accessibility_Registry_getDeviceEventController (
554 spi_atk_bridge_get_registry (), &ev);
558 g_warning ("failure: no deviceeventcontroller found\n");
559 CORBA_exception_free (&ev);
560 registry_died = TRUE;
566 spi_init_keystroke_from_atk_key_event (&key_event, event);
568 result = Accessibility_DeviceEventController_notifyListenersSync (
569 controller, &key_event, &ev);
571 CORBA_exception_free (&ev);
578 spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint,
579 guint n_param_values,
580 const GValue *param_values,
584 GSignalQuery signal_query;
586 gint detail1 = 0, detail2 = 0;
587 #ifdef SPI_BRIDGE_DEBUG
591 g_signal_query (signal_hint->signal_id, &signal_query);
593 name = signal_query.signal_name;
595 #ifdef SPI_BRIDGE_DEBUG
596 s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
597 s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
598 fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n",
599 g_type_name (signal_query.itype), name, s ? s : "<NULL>" , s2);
602 gobject = g_value_get_object (param_values + 0);
603 if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT)
604 detail1 = g_value_get_int (param_values + 1);
605 if (G_VALUE_TYPE (param_values + 2) == G_TYPE_INT)
606 detail2 = g_value_get_int (param_values + 2);
608 spi_atk_emit_eventv (gobject, detail1, detail2, "object:%s", name);
616 spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint,
617 guint n_param_values,
618 const GValue *param_values,
622 GSignalQuery signal_query;
624 #ifdef SPI_BRIDGE_DEBUG
628 g_signal_query (signal_hint->signal_id, &signal_query);
630 name = signal_query.signal_name;
632 #ifdef SPI_BRIDGE_DEBUG
633 s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
634 s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
635 fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n",
636 g_type_name (signal_query.itype), name, s ? s : "<NULL>" , s2);
639 gobject = g_value_get_object (param_values + 0);
640 spi_atk_emit_eventv (gobject, 0, 0, "window:%s", name);