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;
41 static SpiApplication *this_app = NULL;
43 static void spi_atk_bridge_exit_func (void);
44 static void spi_atk_register_event_listeners (void);
45 static gboolean spi_atk_bridge_idle_init (gpointer user_data);
46 static void spi_atk_bridge_focus_tracker (AtkObject *object);
47 static gboolean spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
49 const GValue *param_values,
52 spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint,
54 const GValue *param_values,
57 spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
59 const GValue *param_values,
61 static gboolean spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint,
63 const GValue *param_values,
65 static gint spi_atk_bridge_key_listener (AtkKeyEventStruct *event,
68 /* For automatic libgnome init */
69 extern void gnome_accessibility_module_init (void);
70 extern void gnome_accessibility_module_shutdown (void);
72 static int atk_bridge_initialized = FALSE;
73 static guint atk_bridge_focus_tracker_id = 0;
74 static guint atk_bridge_key_event_listener_id = 0;
75 static guint idle_init_id = 0;
76 static GArray *listener_ids = NULL;
79 * These exported symbols are hooked by gnome-program
80 * to provide automatic module initialization and shutdown.
82 extern void gnome_accessibility_module_init (void);
83 extern void gnome_accessibility_module_shutdown (void);
86 atk_bridge_init (gint *argc, gchar **argv[])
90 if (atk_bridge_initialized)
94 atk_bridge_initialized = TRUE;
96 if (!bonobo_init (argc, argv ? *argv : NULL))
98 g_error ("Could not initialize Bonobo");
102 * We only want to enable the bridge for top level
103 * applications, we detect bonobo components by seeing
104 * if they were activated with the intention of extracting
105 * an impl. by IID - very solid.
107 if (bonobo_activation_iid_get ())
110 CORBA_exception_init(&ev);
112 registry = bonobo_activation_activate_from_id (
113 "OAFIID:Accessibility_Registry:proto0.1", 0, NULL, &ev);
115 if (ev._major != CORBA_NO_EXCEPTION)
117 g_error ("Accessibility app error: exception during "
118 "registry activation from id: %s\n",
119 CORBA_exception_id (&ev));
120 CORBA_exception_free (&ev);
123 if (registry == CORBA_OBJECT_NIL)
125 g_error ("Could not locate registry");
130 /* Create the accessible application server object */
132 this_app = spi_application_new (atk_get_root ());
134 fprintf (stderr, "About to register application\n");
136 Accessibility_Registry_registerApplication (registry,
137 BONOBO_OBJREF (this_app),
140 g_atexit (spi_atk_bridge_exit_func);
142 idle_init_id = g_idle_add (spi_atk_bridge_idle_init, NULL);
148 gtk_module_init (gint *argc, gchar **argv[])
150 return atk_bridge_init (argc, argv);
154 spi_atk_bridge_idle_init (gpointer user_data)
158 spi_atk_register_event_listeners ();
160 fprintf (stderr, "Application registered & listening\n");
166 add_signal_listener (const char *signal_name)
170 id = atk_add_global_event_listener (
171 spi_atk_bridge_signal_listener, signal_name);
173 g_array_append_val (listener_ids, id);
177 spi_atk_register_event_listeners (void)
180 * kludge to make sure the Atk interface types are registered, otherwise
181 * the AtkText signal handlers below won't get registered
184 GObject *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
185 AtkObject *bo = atk_no_op_object_new (ao);
187 /* Register for focus event notifications, and register app with central registry */
189 listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
191 atk_bridge_focus_tracker_id = atk_add_focus_tracker (spi_atk_bridge_focus_tracker);
193 id = atk_add_global_event_listener (spi_atk_bridge_property_event_listener,
194 "Gtk:AtkObject:property-change");
195 g_array_append_val (listener_ids, id);
196 id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
198 g_array_append_val (listener_ids, id);
199 id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
201 g_array_append_val (listener_ids, id);
202 id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
204 g_array_append_val (listener_ids, id);
205 id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
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,
215 "window:deactivate");
216 g_array_append_val (listener_ids, id);
217 id = atk_add_global_event_listener (spi_atk_bridge_state_event_listener,
218 "Gtk:AtkObject:state-change");
219 g_array_append_val (listener_ids, id);
221 add_signal_listener ("Gtk:AtkObject:children-changed");
222 add_signal_listener ("Gtk:AtkObject:visible-data-changed");
223 add_signal_listener ("Gtk:AtkSelection:selection-changed");
224 add_signal_listener ("Gtk:AtkText:text-selection-changed");
225 add_signal_listener ("Gtk:AtkText:text-changed");
226 add_signal_listener ("Gtk:AtkText:text-caret-moved");
227 add_signal_listener ("Gtk:AtkTable:row-inserted");
228 add_signal_listener ("Gtk:AtkTable:row-reordered");
229 add_signal_listener ("Gtk:AtkTable:row-deleted");
230 add_signal_listener ("Gtk:AtkTable:column-inserted");
231 add_signal_listener ("Gtk:AtkTable:column-reordered");
232 add_signal_listener ("Gtk:AtkTable:column-deleted");
233 add_signal_listener ("Gtk:AtkTable:model-changed");
235 * May add the following listeners to implement preemptive key listening for GTK+
237 * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
238 * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
240 atk_bridge_key_event_listener_id = atk_add_key_event_listener (
241 spi_atk_bridge_key_listener, NULL);
243 g_object_unref (G_OBJECT (bo));
248 deregister_application (BonoboObject *app)
250 Accessibility_Registry_deregisterApplication (
251 registry, BONOBO_OBJREF (app), &ev);
253 registry = bonobo_object_release_unref (registry, &ev);
255 app = bonobo_object_unref (app);
259 spi_atk_bridge_exit_func (void)
261 BonoboObject *app = (BonoboObject *) this_app;
263 fprintf (stderr, "exiting bridge\n");
272 * FIXME: this may be incorrect for apps that do their own bonobo
273 * shutdown, until we can explicitly shutdown to get the ordering
276 if (!bonobo_is_initialized ())
278 fprintf (stderr, "Re-initializing bonobo\n");
279 g_assert (bonobo_init (0, NULL));
280 g_assert (bonobo_activate ());
283 deregister_application (app);
285 fprintf (stderr, "bridge exit func complete.\n");
287 if (g_getenv ("AT_BRIDGE_SHUTDOWN"))
289 g_assert (!bonobo_debug_shutdown ());
294 gnome_accessibility_module_init (void)
296 atk_bridge_init (NULL, NULL);
298 g_print("Atk Accessibilty bridge initialized\n");
302 gnome_accessibility_module_shutdown (void)
304 BonoboObject *app = (BonoboObject *) this_app;
306 if (!atk_bridge_initialized)
310 atk_bridge_initialized = FALSE;
313 g_print("Atk Accessibilty bridge shutdown\n");
317 g_source_remove (idle_init_id);
323 GArray *ids = listener_ids;
326 atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
328 for (i = 0; ids && i < ids->len; i++)
330 atk_remove_global_event_listener (g_array_index (ids, guint, i));
333 atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
336 deregister_application (app);
340 spi_atk_bridge_focus_tracker (AtkObject *object)
342 SpiAccessible *source;
343 Accessibility_Event e;
345 source = spi_accessible_new (object);
348 e.source = BONOBO_OBJREF (source);
352 Accessibility_Registry_notifyEvent (registry, &e, &ev);
354 CORBA_exception_free (&ev);
358 spi_atk_emit_eventv (GObject *gobject,
359 unsigned long detail1,
360 unsigned long detail2,
361 const char *format, ...)
364 Accessibility_Event e;
365 SpiAccessible *source;
367 #ifdef SPI_BRIDGE_DEBUG
371 va_start (args, format);
373 if (ATK_IS_IMPLEMENTOR (gobject))
375 aobject = atk_implementor_ref_accessible (ATK_IMPLEMENTOR (gobject));
376 source = spi_accessible_new (aobject);
377 g_object_unref (G_OBJECT (aobject));
379 else if (ATK_IS_OBJECT (gobject))
381 aobject = ATK_OBJECT (gobject);
382 source = spi_accessible_new (aobject);
388 g_error ("received property-change event from non-AtkImplementor");
393 e.type = g_strdup_vprintf (format, args);
394 e.source = BONOBO_OBJREF (source);
398 #ifdef SPI_BRIDGE_DEBUG
399 s = Accessibility_Accessible__get_name (BONOBO_OBJREF (source), &ev);
400 g_warning ("Emitting event '%s' (%lu, %lu) on %s",
401 e.type, e.detail1, e.detail2, s);
405 Accessibility_Registry_notifyEvent (registry, &e, &ev);
407 CORBA_exception_free (&ev);
416 spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
417 guint n_param_values,
418 const GValue *param_values,
421 AtkPropertyValues *values;
424 #ifdef SPI_BRIDGE_DEBUG
425 GSignalQuery signal_query;
429 g_signal_query (signal_hint->signal_id, &signal_query);
430 name = signal_query.signal_name;
432 s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
433 s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
434 values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
435 fprintf (stderr, "Received (property) signal %s:%s:%s from object %s (gail %s)\n",
436 g_type_name (signal_query.itype), name, values->property_name, s, s2);
440 gobject = g_value_get_object (param_values + 0);
441 values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
443 spi_atk_emit_eventv (gobject, 0, 0, "object:property-change:%s", values->property_name);
449 spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
450 guint n_param_values,
451 const GValue *param_values,
455 gchar *property_name;
457 unsigned long detail1;
458 #ifdef SPI_BRIDGE_DEBUG
459 GSignalQuery signal_query;
462 g_signal_query (signal_hint->signal_id, &signal_query);
463 name = signal_query.signal_name;
464 fprintf (stderr, "Received (state) signal %s:%s\n",
465 g_type_name (signal_query.itype), name);
468 gobject = g_value_get_object (param_values + 0);
469 property_name = g_strdup (g_value_get_string (param_values + 1));
470 detail1 = (g_value_get_boolean (param_values + 2))
472 type = g_strdup_printf ("object:state-change:%s", property_name);
473 spi_atk_emit_eventv (gobject,
477 g_free (property_name);
484 spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent *keystroke,
485 AtkKeyEventStruct *event)
490 g_print ("event %c (%d)\n", (int) event->keyval, (int) event->keycode);
496 g_print ("WARNING: NULL key event!");
499 keystroke->id = (CORBA_long) event->keyval;
500 keystroke->hw_code = (CORBA_short) event->keycode;
501 keystroke->timestamp = (CORBA_unsigned_long) event->timestamp;
502 keystroke->modifiers = (CORBA_unsigned_short) (event->state & 0xFFFF);
505 keystroke->event_string = CORBA_string_dup (event->string);
506 keystroke->is_text = CORBA_TRUE;
510 keystroke->event_string = CORBA_string_dup ("");
511 keystroke->is_text = CORBA_FALSE;
515 case (ATK_KEY_EVENT_PRESS):
516 keystroke->type = Accessibility_KEY_PRESSED;
518 case (ATK_KEY_EVENT_RELEASE):
519 keystroke->type = Accessibility_KEY_RELEASED;
526 g_print ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
527 (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
528 (int) keystroke->modifiers,
529 keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
534 spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data)
536 CORBA_boolean result;
537 Accessibility_DeviceEvent key_event;
538 Accessibility_DeviceEventController controller =
539 Accessibility_Registry_getDeviceEventController (registry, &ev);
543 g_warning ("failure: no deviceeventcontroller found\n");
544 CORBA_exception_free (&ev);
550 spi_init_keystroke_from_atk_key_event (&key_event, event);
552 result = Accessibility_DeviceEventController_notifyListenersSync (
553 controller, &key_event, &ev);
555 CORBA_exception_free (&ev);
562 spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint,
563 guint n_param_values,
564 const GValue *param_values,
568 GSignalQuery signal_query;
570 gint detail1 = 0, detail2 = 0;
571 #ifdef SPI_BRIDGE_DEBUG
575 g_signal_query (signal_hint->signal_id, &signal_query);
577 name = signal_query.signal_name;
579 #ifdef SPI_BRIDGE_DEBUG
580 s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
581 s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
582 fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n",
583 g_type_name (signal_query.itype), name, s ? s : "<NULL>" , s2);
586 gobject = g_value_get_object (param_values + 0);
587 if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT)
588 detail1 = g_value_get_int (param_values + 1);
589 if (G_VALUE_TYPE (param_values + 2) == G_TYPE_INT)
590 detail2 = g_value_get_int (param_values + 2);
592 spi_atk_emit_eventv (gobject, detail1, detail2, "object:%s", name);
600 spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint,
601 guint n_param_values,
602 const GValue *param_values,
606 GSignalQuery signal_query;
608 #ifdef SPI_BRIDGE_DEBUG
612 g_signal_query (signal_hint->signal_id, &signal_query);
614 name = signal_query.signal_name;
616 #ifdef SPI_BRIDGE_DEBUG
617 s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
618 s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
619 fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n",
620 g_type_name (signal_query.itype), name, s ? s : "<NULL>" , s2);
623 gobject = g_value_get_object (param_values + 0);
624 spi_atk_emit_eventv (gobject, 0, 0, "window:%s", name);