4065745f2550d6fd3b57dd999fbc93cb3c01a204
[platform/core/uifw/at-spi2-atk.git] / atk-bridge / bridge.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2001, 2002, 2003 Sun Microsystems Inc.,
6  * Copyright 2001, 2002, 2003 Ximian, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include <string.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <libbonobo.h>
29 #include <orbit/orbit.h>
30 #include <atk/atk.h>
31 #include <atk/atkobject.h>
32 #include <atk/atknoopobject.h>
33 #include <libspi/Accessibility.h>
34 #include <libspi/spi-private.h>
35 #include "accessible.h"
36 #include "application.h"
37
38 #include <bonobo-activation/bonobo-activation-register.h>
39
40 #undef SPI_BRIDGE_DEBUG
41
42 #define DBG(a,b) if(_dbg>=(a))b
43
44 static int _dbg = 0;
45 static CORBA_Environment ev;
46 static Accessibility_Registry registry = CORBA_OBJECT_NIL;
47 static Accessibility_DeviceEventController device_event_controller = CORBA_OBJECT_NIL;
48 static SpiApplication *this_app = NULL;
49 static gboolean registry_died = FALSE;
50 static gboolean atk_listeners_registered = FALSE;
51 static gint toplevels = 0;
52
53 static guint atk_signal_text_changed;
54 static guint atk_signal_children_changed;
55 static guint atk_signal_active_descendant_changed;
56 static guint atk_signal_text_selection_changed;
57
58 /* NOT YET USED
59    static guint atk_signal_row_reordered;
60    static guint atk_signal_row_inserted;
61    static guint atk_signal_row_deleted;
62    static guint atk_signal_column_reordered;
63    static guint atk_signal_column_inserted;
64    static guint atk_signal_column_deleted;
65 */
66
67 static guint atk_signal_link_selected;
68
69 static Accessibility_Registry spi_atk_bridge_get_registry (void);
70 static void     spi_atk_bridge_do_registration         (void);
71 static void     spi_atk_bridge_toplevel_added          (AtkObject             *object,
72                                                         guint                 index,
73                                                         AtkObject             *child);
74 static void     spi_atk_bridge_toplevel_removed        (AtkObject             *object,
75                                                         guint                 index,
76                                                         AtkObject             *child);
77
78 static void     spi_atk_bridge_exit_func               (void);
79 static void     spi_atk_register_event_listeners       (void);
80 static void     spi_atk_deregister_event_listeners     (void);
81 static void     spi_atk_bridge_focus_tracker           (AtkObject             *object);
82 static void     spi_atk_bridge_register_application    (Accessibility_Registry registry);
83 static gboolean spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
84                                                         guint                  n_param_values,
85                                                         const GValue          *param_values,
86                                                         gpointer               data);
87 static gboolean
88 spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint,
89                                 guint n_param_values,
90                                 const GValue *param_values,
91                                 gpointer data);
92 static gboolean
93 spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
94                                      guint n_param_values,
95                                      const GValue *param_values,
96                                      gpointer data);
97 static gboolean spi_atk_bridge_signal_listener         (GSignalInvocationHint *signal_hint,
98                                                         guint                  n_param_values,
99                                                         const GValue          *param_values,
100                                                         gpointer               data);
101 static gint     spi_atk_bridge_key_listener            (AtkKeyEventStruct     *event,
102                                                         gpointer               data);
103 static void     spi_atk_tidy_windows                   (void);
104 static void     deregister_application                 (BonoboObject          *app);
105 static void     reinit_register_vars                   (void);
106
107 /* For automatic libgnome init */
108 extern void gnome_accessibility_module_init     (void);
109 extern void gnome_accessibility_module_shutdown (void);
110
111 static int     atk_bridge_initialized = FALSE;
112 static guint   atk_bridge_focus_tracker_id = 0;
113 static guint   atk_bridge_key_event_listener_id = 0;
114 static GArray *listener_ids = NULL;
115
116 /*
117  *   These exported symbols are hooked by gnome-program
118  * to provide automatic module initialization and shutdown.
119  */
120 extern void gnome_accessibility_module_init     (void);
121 extern void gnome_accessibility_module_shutdown (void);
122
123 static void
124 spi_atk_bridge_init_event_type_consts ()
125 {
126   atk_signal_children_changed = g_signal_lookup ("children_changed", 
127                                               ATK_TYPE_OBJECT);
128   atk_signal_text_changed = g_signal_lookup ("text_changed", 
129                                              ATK_TYPE_TEXT);
130   atk_signal_active_descendant_changed = 
131          g_signal_lookup ("active_descendant_changed", 
132                           ATK_TYPE_OBJECT);
133   atk_signal_link_selected = g_signal_lookup ("link_selected", 
134                                               ATK_TYPE_HYPERTEXT);
135   atk_signal_text_selection_changed = g_signal_lookup ("text_selection_changed", 
136                                               ATK_TYPE_TEXT);
137 }
138
139 static int
140 atk_bridge_init (gint *argc, gchar **argv[])
141 {
142   const char *debug_env_string = g_getenv ("AT_SPI_DEBUG");
143
144   if (atk_bridge_initialized)
145     {
146       return 0;
147     }
148   atk_bridge_initialized = TRUE;
149
150   if (debug_env_string)
151     _dbg = (int) g_ascii_strtod (debug_env_string, NULL);
152
153   if (!bonobo_init (argc, argv ? *argv : NULL))
154     {
155       g_error ("Could not initialize Bonobo");
156     }
157
158   /*
159    * We only want to enable the bridge for top level
160    * applications, we detect bonobo components by seeing
161    * if they were activated with the intention of extracting
162    * an impl. by IID - very solid.
163    */
164   if (bonobo_activation_iid_get ())
165     {
166       DBG (1, g_message ("Found Bonobo component\n"));
167       g_signal_connect (atk_get_root (), 
168                         "children-changed::add",
169                         (GCallback) spi_atk_bridge_toplevel_added, 
170                         NULL);
171       g_signal_connect (atk_get_root (), 
172                         "children-changed::remove",
173                         (GCallback) spi_atk_bridge_toplevel_removed, 
174                         NULL);
175     }
176   else
177     {
178       spi_atk_bridge_do_registration ();
179     }
180  
181   spi_atk_bridge_init_event_type_consts ();
182
183   return 0;
184 }
185
186
187 static void
188 spi_atk_bridge_do_registration (void)
189 {
190   CORBA_Environment ev;
191
192   CORBA_exception_init(&ev);
193
194   if (spi_atk_bridge_get_registry () == CORBA_OBJECT_NIL)
195     {
196       g_error ("Could not locate registry");
197     }
198
199   bonobo_activate ();
200
201   /* Create the accessible application server object */
202
203   this_app = spi_application_new (atk_get_root ());
204
205   DBG (1, g_message ("About to register application\n"));
206
207   spi_atk_bridge_register_application (spi_atk_bridge_get_registry ());
208   
209   g_atexit (spi_atk_bridge_exit_func);
210
211   DBG (1, g_message ("Application registered & listening\n"));
212
213 }
214
215 static void
216 spi_atk_bridge_toplevel_added (AtkObject *object,
217                                guint     index,
218                                AtkObject *child)
219 {
220   if (toplevels == 0)
221     spi_atk_bridge_do_registration ();
222   toplevels++;
223 }
224
225 static void
226 spi_atk_bridge_toplevel_removed (AtkObject *object,
227                                  guint     index,
228                                  AtkObject *child)
229 {
230   BonoboObject *app = (BonoboObject *) this_app;
231
232   toplevels--;
233   if (toplevels == 0)
234     {
235       deregister_application (app);
236       spi_atk_deregister_event_listeners ();
237       reinit_register_vars ();
238     }
239   if (toplevels < 0)
240     {
241       g_warning ("More toplevels removed than added\n");
242       toplevels = 0;
243     }
244 }
245
246 static void
247 spi_atk_bridge_register_application (Accessibility_Registry registry)
248 {
249   Accessibility_Registry_registerApplication (spi_atk_bridge_get_registry (),
250                                               BONOBO_OBJREF (this_app),
251                                               &ev);
252   spi_atk_register_event_listeners ();
253 }
254
255 static Accessibility_Registry
256 spi_atk_bridge_get_registry (void)
257 {
258   CORBA_Environment ev;
259
260   if (registry_died || (registry == CORBA_OBJECT_NIL)) {
261           CORBA_exception_init (&ev);
262           if (registry_died) 
263             DBG (1, g_warning ("registry died! restarting..."));
264           registry = bonobo_activation_activate_from_id (
265                   "OAFIID:Accessibility_Registry:1.0", 0, NULL, &ev);
266           
267           if (ev._major != CORBA_NO_EXCEPTION)
268           {
269                   g_error ("Accessibility app error: exception during "
270                            "registry activation from id: %s\n",
271                            CORBA_exception_id (&ev));
272                   CORBA_exception_free (&ev);
273           }
274           
275           if (registry_died && registry) {
276                   registry_died = FALSE;
277                   spi_atk_bridge_register_application (registry);
278           }
279   }
280   return registry;
281 }
282
283 static Accessibility_DeviceEventController
284 spi_atk_bridget_get_dec (void)
285 {
286   CORBA_Environment ev;
287
288   if (device_event_controller != CORBA_OBJECT_NIL)
289     {
290       if (ORBit_small_get_connection_status (device_event_controller)
291           == ORBIT_CONNECTION_CONNECTED)
292         return device_event_controller;
293     }
294
295   CORBA_exception_init (&ev);
296
297   device_event_controller =
298     Accessibility_Registry_getDeviceEventController (
299             spi_atk_bridge_get_registry (), &ev);
300
301   if (BONOBO_EX (&ev))
302     {
303       g_warning ("failure: no deviceeventcontroller found\n");
304       registry_died = TRUE;
305       device_event_controller = CORBA_OBJECT_NIL;
306     }
307
308   return device_event_controller;
309 }
310
311 int
312 gtk_module_init (gint *argc, gchar **argv[])
313 {
314         return atk_bridge_init (argc, argv);
315 }
316
317 static void
318 add_signal_listener (const char *signal_name)
319 {
320   guint id;
321
322   id = atk_add_global_event_listener (
323     spi_atk_bridge_signal_listener, signal_name);
324
325   g_array_append_val (listener_ids, id);
326 }
327
328 static void
329 spi_atk_register_event_listeners (void)
330 {
331   /*
332    * kludge to make sure the Atk interface types are registered, otherwise
333    * the AtkText signal handlers below won't get registered
334    */
335   guint      id;
336   GObject   *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
337   AtkObject *bo = atk_no_op_object_new (ao);
338
339
340   if (atk_listeners_registered) return;
341
342   atk_listeners_registered = TRUE;
343
344   /* Register for focus event notifications, and register app with central registry  */
345
346   listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
347
348   atk_bridge_focus_tracker_id = atk_add_focus_tracker (spi_atk_bridge_focus_tracker);
349
350   id = atk_add_global_event_listener (spi_atk_bridge_property_event_listener,
351                                       "Gtk:AtkObject:property-change");
352   g_array_append_val (listener_ids, id);
353   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
354                                       "window:create");
355   g_array_append_val (listener_ids, id);
356   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
357                                       "window:destroy");
358   g_array_append_val (listener_ids, id);
359   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
360                                       "window:minimize");
361   g_array_append_val (listener_ids, id);
362   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
363                                       "window:maximize");
364   g_array_append_val (listener_ids, id);
365   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
366                                       "window:restore");
367   g_array_append_val (listener_ids, id);
368   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
369                                       "window:activate");
370   g_array_append_val (listener_ids, id);
371   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
372                                       "window:deactivate");
373   g_array_append_val (listener_ids, id);
374   id = atk_add_global_event_listener (spi_atk_bridge_state_event_listener,
375                                       "Gtk:AtkObject:state-change");
376   g_array_append_val (listener_ids, id);
377
378   add_signal_listener ("Gtk:AtkObject:children-changed");
379   add_signal_listener ("Gtk:AtkObject:visible-data-changed");
380   add_signal_listener ("Gtk:AtkObject:active-descendant-changed");
381   add_signal_listener ("Gtk:AtkSelection:selection-changed");
382   add_signal_listener ("Gtk:AtkText:text-selection-changed");
383   add_signal_listener ("Gtk:AtkText:text-changed");
384   add_signal_listener ("Gtk:AtkText:text-caret-moved");
385   add_signal_listener ("Gtk:AtkTable:row-inserted");
386   add_signal_listener ("Gtk:AtkTable:row-reordered");
387   add_signal_listener ("Gtk:AtkTable:row-deleted");
388   add_signal_listener ("Gtk:AtkTable:column-inserted");
389   add_signal_listener ("Gtk:AtkTable:column-reordered");
390   add_signal_listener ("Gtk:AtkTable:column-deleted");
391   add_signal_listener ("Gtk:AtkTable:model-changed");
392   add_signal_listener ("Gtk:AtkHypertext:link-selected");
393 /*
394  * May add the following listeners to implement preemptive key listening for GTK+
395  *
396  * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
397  * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
398  */
399   atk_bridge_key_event_listener_id = atk_add_key_event_listener (
400     spi_atk_bridge_key_listener, NULL);
401   
402   g_object_unref (G_OBJECT (bo));
403   g_object_unref (ao);
404 }
405
406 static void
407 spi_atk_deregister_event_listeners (void)
408 {
409   gint i;
410   guint id;
411
412   if (!atk_listeners_registered)
413     return;
414
415   atk_listeners_registered = FALSE;
416
417   for (i = 0; i < listener_ids->len; i++)
418     {
419       id = g_array_index (listener_ids, guint, i);
420       atk_remove_global_event_listener (id);   
421     }
422   g_array_free (listener_ids, TRUE);
423   listener_ids = NULL;
424
425   atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
426   atk_bridge_focus_tracker_id = 0;
427
428   atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
429   atk_bridge_key_event_listener_id = 0;
430 }
431
432 static void
433 deregister_application (BonoboObject *app)
434 {
435   Accessibility_Registry registry = spi_atk_bridge_get_registry ();     
436   Accessibility_Registry_deregisterApplication (registry, BONOBO_OBJREF (app), &ev);
437
438   device_event_controller = bonobo_object_release_unref (device_event_controller, &ev);
439   registry = bonobo_object_release_unref (registry, &ev);
440   
441   app = bonobo_object_unref (app);
442 }
443
444 static void
445 spi_atk_bridge_exit_func (void)
446 {
447   BonoboObject *app = (BonoboObject *) this_app;
448
449   DBG (1, g_message ("exiting bridge\n"));
450
451   if (!app)
452     {
453       return;
454     }
455   this_app = NULL;
456
457   /*
458    * Check whether we still have windows which have not been deleted.
459    */
460   spi_atk_tidy_windows ();
461   /*
462    *  FIXME: this may be incorrect for apps that do their own bonobo
463    *  shutdown, until we can explicitly shutdown to get the ordering
464    *  right.
465    */
466   if (!bonobo_is_initialized ())
467     {
468       DBG (1, g_warning ("Re-initializing bonobo\n"));
469       g_assert (bonobo_init (0, NULL));
470       g_assert (bonobo_activate ());
471     }
472   
473   deregister_application (app);
474
475   DBG (1, g_message ("bridge exit func complete.\n"));
476
477   if (g_getenv ("AT_BRIDGE_SHUTDOWN"))
478     {
479       g_assert (!bonobo_debug_shutdown ());
480     }
481 }
482
483 void
484 gnome_accessibility_module_init (void)
485 {
486   atk_bridge_init (NULL, NULL);
487
488   g_print("Atk Accessibilty bridge initialized\n");
489 }
490
491 void
492 gnome_accessibility_module_shutdown (void)
493 {
494   BonoboObject *app = (BonoboObject *) this_app;
495   int     i;
496   GArray *ids = listener_ids;
497   
498   if (!atk_bridge_initialized)
499     {
500       return;
501     }
502   atk_bridge_initialized = FALSE;
503   this_app = NULL;
504
505   g_print("Atk Accessibilty bridge shutdown\n");
506
507   listener_ids = NULL;
508   atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
509   
510   for (i = 0; ids && i < ids->len; i++)
511   {
512           atk_remove_global_event_listener (g_array_index (ids, guint, i));
513   }
514   
515   atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
516
517   deregister_application (app);
518 }
519
520 static void
521 spi_atk_bridge_focus_tracker (AtkObject *object)
522 {
523   SpiAccessible *source;
524   Accessibility_Event e;
525
526   source = spi_accessible_new (object);
527
528   e.type = "focus:";
529   e.source = BONOBO_OBJREF (source);
530   e.detail1 = 0;
531   e.detail2 = 0;
532   spi_init_any_nil (&e.any_data);
533
534   CORBA_exception_init (&ev);
535   Accessibility_Registry_notifyEvent (spi_atk_bridge_get_registry (), &e, &ev);
536   if (BONOBO_EX (&ev))
537     registry_died = TRUE;
538
539   bonobo_object_unref (source);
540   
541   CORBA_exception_free (&ev);
542 }
543
544 static void
545 spi_atk_emit_eventv (const GObject         *gobject,
546                      long                   detail1,
547                      long                   detail2,
548                      CORBA_any             *any,
549                      const char            *format, ...)
550 {
551   va_list             args;
552   Accessibility_Event e;
553   SpiAccessible      *source;
554   AtkObject          *aobject;
555 #ifdef SPI_BRIDGE_DEBUG
556   CORBA_string s;
557 #endif
558   
559   va_start (args, format);
560   
561   if (ATK_IS_IMPLEMENTOR (gobject))
562     {
563       aobject = atk_implementor_ref_accessible (ATK_IMPLEMENTOR (gobject));
564       source  = spi_accessible_new (aobject);
565       g_object_unref (G_OBJECT (aobject));
566     }
567   else if (ATK_IS_OBJECT (gobject))
568     {
569       aobject = ATK_OBJECT (gobject);
570       source  = spi_accessible_new (aobject);
571     }
572   else
573     {
574       aobject = NULL;
575       source  = NULL;
576       DBG (0, g_warning ("received property-change event from non-AtkImplementor"));
577     }
578
579   if (source) 
580     {
581       e.type = g_strdup_vprintf (format, args);
582       e.source = BONOBO_OBJREF (source);
583       e.detail1 = detail1;
584       e.detail2 = detail2;
585       if (any) e.any_data = *any;
586       else spi_init_any_nil (&e.any_data);
587
588 #ifdef SPI_BRIDGE_DEBUG
589       s = Accessibility_Accessible__get_name (BONOBO_OBJREF (source), &ev);
590       g_warning ("Emitting event '%s' (%lu, %lu) on %s",
591                  e.type, e.detail1, e.detail2, s);
592       CORBA_free (s);
593 #endif
594       CORBA_exception_init (&ev);
595       Accessibility_Registry_notifyEvent (spi_atk_bridge_get_registry (), 
596                                           &e, &ev);
597 #ifdef SPI_BRIDGE_DEBUG
598       if (ev._major != CORBA_NO_EXCEPTION)
599         g_warning ("error emitting event %s, (%d) %s",
600                    e.type,
601                    ev._major,
602                    CORBA_exception_id(&ev));
603 #endif        
604       if (BONOBO_EX (&ev)) registry_died = TRUE;
605       Accessibility_Accessible_unref (e.source, &ev);
606       
607       CORBA_exception_free (&ev);
608       
609       g_free (e.type);
610     }
611
612   va_end (args);
613
614 }
615
616 static gboolean
617 spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
618                                         guint n_param_values,
619                                         const GValue *param_values,
620                                         gpointer data)
621 {
622   AtkPropertyValues *values;
623   GObject *gobject;
624   const gchar *prop_name;
625   CORBA_any any;
626   const gchar *sp = NULL;
627   AtkObject *ao;
628   SpiAccessible *s_ao = NULL;
629   CORBA_Object c_obj;
630   gint i;
631
632 #ifdef SPI_BRIDGE_DEBUG
633   GSignalQuery signal_query;
634   const gchar *name;
635   const gchar *s, *s2;
636   
637   g_signal_query (signal_hint->signal_id, &signal_query);
638   name = signal_query.signal_name;
639
640   s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
641   s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
642   values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
643   DBG (2, g_message ("Received (property) signal %s:%s:%s from object %s (gail %s)\n",
644            g_type_name (signal_query.itype), name, values->property_name, s, s2));
645   
646 #endif
647
648   gobject = g_value_get_object (param_values + 0);
649   values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
650
651   prop_name = values->property_name;
652   if (strcmp (prop_name, "accessible-name") == 0)
653     {
654       sp = atk_object_get_name (ATK_OBJECT (gobject));
655       spi_init_any_string (&any, (gchar **)&sp);
656     }
657   else if (strcmp (prop_name, "accessible-description") == 0)
658     {
659       sp = atk_object_get_description (ATK_OBJECT (gobject));
660       spi_init_any_string (&any, (gchar **)&sp);
661     }
662   else if (strcmp (prop_name, "accessible-parent") == 0)
663     {
664       ao = atk_object_get_parent (ATK_OBJECT (gobject));
665       if (ao) 
666         {
667           s_ao = spi_accessible_new (ao);
668           c_obj = BONOBO_OBJREF (s_ao);
669           spi_init_any_object (&any, &c_obj);
670         }
671       else
672         {
673           spi_init_any_nil (&any);
674         }
675     }
676   else if (strcmp (prop_name, "accessible-table-summary") == 0)
677     {
678       ao = atk_table_get_summary (ATK_TABLE (gobject));
679       if (ao) 
680         {
681           s_ao = spi_accessible_new (ao);
682           c_obj = BONOBO_OBJREF (s_ao);
683           spi_init_any_object (&any, &c_obj);
684         }
685       else
686         {
687           spi_init_any_nil (&any);
688         }
689     }
690   else if (strcmp (prop_name, "accessible-table-column-header") == 0)
691     {
692       i = g_value_get_int (&(values->new_value));
693       ao = atk_table_get_column_header (ATK_TABLE (gobject), i);
694       if (ao) 
695         {
696           s_ao = spi_accessible_new (ao);
697           c_obj = BONOBO_OBJREF (s_ao);
698           spi_init_any_object (&any, &c_obj);
699         }
700       else
701         {
702           spi_init_any_nil (&any);
703         }
704     }
705   else if (strcmp (prop_name, "accessible-table-row-header") == 0)
706     {
707       i = g_value_get_int (&(values->new_value));
708       ao = atk_table_get_row_header (ATK_TABLE (gobject), i);
709       if (ao) 
710         {
711           s_ao = spi_accessible_new (ao);
712           c_obj = BONOBO_OBJREF (s_ao);
713           spi_init_any_object (&any, &c_obj);
714         }
715       else
716         {
717           spi_init_any_nil (&any);
718         }
719     }
720   else if (strcmp (prop_name, "accessible-table-row-description") == 0)
721     {
722       i = g_value_get_int (&(values->new_value));
723       sp = atk_table_get_row_description (ATK_TABLE (gobject), i);
724       spi_init_any_string (&any, (gchar **)&sp);
725     }
726   else if (strcmp (prop_name, "accessible-table-column-description") == 0)
727     {
728       i = g_value_get_int (&(values->new_value));
729       sp = atk_table_get_column_description (ATK_TABLE (gobject), i);
730       spi_init_any_string (&any, (gchar **)&sp);
731     }
732   else if (strcmp (prop_name, "accessible-table-caption-object") == 0)
733     {
734       ao = atk_table_get_caption (ATK_TABLE (gobject));
735       sp = atk_object_get_name (ao);
736       spi_init_any_string (&any, (gchar **)&sp);
737     }
738   else
739     {
740       spi_init_any_nil (&any);
741     }
742
743   spi_atk_emit_eventv (gobject, 0, 0, &any,
744                        "object:property-change:%s", prop_name);
745
746
747   return TRUE;
748 }
749
750 static gboolean
751 spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
752                                      guint n_param_values,
753                                      const GValue *param_values,
754                                      gpointer data)
755 {
756   GObject *gobject;
757   gchar *property_name;
758   gchar *type;
759   unsigned long detail1;
760 #ifdef SPI_BRIDGE_DEBUG
761   GSignalQuery signal_query;
762   const gchar *name;
763   
764   g_signal_query (signal_hint->signal_id, &signal_query);
765   name = signal_query.signal_name;
766   fprintf (stderr, "Received (state) signal %s:%s\n",
767            g_type_name (signal_query.itype), name);
768 #endif
769
770   gobject = g_value_get_object (param_values + 0);
771   property_name = g_strdup (g_value_get_string (param_values + 1));
772   detail1 = (g_value_get_boolean (param_values + 2))
773     ? 1 : 0;
774   type = g_strdup_printf ("object:state-changed:%s", property_name);
775   spi_atk_emit_eventv (gobject, 
776                        detail1,
777                        0,
778                        NULL,
779                        type);
780   g_free (property_name);
781   g_free (type);
782   return TRUE;
783 }
784
785 static void
786 spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent  *keystroke,
787                                        AtkKeyEventStruct          *event)
788 {
789 #ifdef SPI_DEBUG
790   if (event)
791     {
792       g_print ("event %c (%d)\n", (int) event->keyval, (int) event->keycode);
793     }
794   else
795 #endif
796   if (!event)
797     {
798       g_print ("WARNING: NULL key event!");
799     }
800   
801   keystroke->id        = (CORBA_long) event->keyval;
802   keystroke->hw_code   = (CORBA_short) event->keycode;
803   keystroke->timestamp = (CORBA_unsigned_long) event->timestamp;
804   keystroke->modifiers = (CORBA_unsigned_short) (event->state & 0xFFFF);
805   if (event->string)
806     {
807       gunichar c;
808
809       keystroke->event_string = CORBA_string_dup (event->string);
810       c = g_utf8_get_char_validated (event->string, -1);
811       if (c > 0 && g_unichar_isprint (c))
812         keystroke->is_text = CORBA_TRUE;
813       else
814         keystroke->is_text = CORBA_FALSE;
815     }
816   else
817     {
818       keystroke->event_string = CORBA_string_dup ("");
819       keystroke->is_text = CORBA_FALSE;
820     }
821   switch (event->type)
822     {
823     case (ATK_KEY_EVENT_PRESS):
824       keystroke->type = Accessibility_KEY_PRESSED_EVENT;
825       break;
826     case (ATK_KEY_EVENT_RELEASE):
827       keystroke->type = Accessibility_KEY_RELEASED_EVENT;
828       break;
829     default:
830       keystroke->type = 0;
831       break;
832     }
833 #if 0  
834   g_print ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
835            (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
836            (int) keystroke->modifiers,
837            keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
838 #endif
839 }
840
841 static gint
842 spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data)
843 {
844   CORBA_boolean             result;
845   Accessibility_DeviceEvent key_event;
846
847   CORBA_exception_init (&ev);
848
849   spi_init_keystroke_from_atk_key_event (&key_event, event);
850
851   result = Accessibility_DeviceEventController_notifyListenersSync (
852           spi_atk_bridget_get_dec (), &key_event, &ev);
853
854   CORBA_exception_free (&ev);
855
856   return result;
857 }
858
859 static gboolean
860 spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint,
861                                 guint n_param_values,
862                                 const GValue *param_values,
863                                 gpointer data)
864 {
865   GObject *gobject;
866   GSignalQuery signal_query;
867   const gchar *name;
868   const gchar *detail;
869   CORBA_any any;
870   CORBA_Object c_obj;
871   char *sp = NULL;
872   AtkObject *ao;
873   AtkText *text;
874   gint detail1 = 0, detail2 = 0;
875   SpiAccessible *s_ao = NULL;
876 #ifdef SPI_BRIDGE_DEBUG
877   const gchar *s, *s2;
878 #endif 
879   
880   g_signal_query (signal_hint->signal_id, &signal_query);
881
882   name = signal_query.signal_name;
883   if (signal_hint->detail)
884     detail = g_quark_to_string (signal_hint->detail);
885   else
886     detail = NULL;
887
888 #ifdef SPI_BRIDGE_DEBUG
889   s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
890   s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
891   fprintf (stderr, "Received signal %s:%s detail: %s from object %s (gail %s)\n",
892            g_type_name (signal_query.itype), name, 
893            detail ? detail : "<NULL>", s ? s : "<NULL>" , s2);
894 #endif
895   
896   gobject = g_value_get_object (param_values + 0);
897
898   if (signal_query.signal_id == atk_signal_active_descendant_changed)
899     {
900       gpointer child = g_value_get_pointer (param_values + 1);
901
902       g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE);
903
904       ao = ATK_OBJECT (child);
905
906       detail1 = atk_object_get_index_in_parent (ao);
907       s_ao = spi_accessible_new (ao);
908       c_obj = BONOBO_OBJREF (s_ao);
909       spi_init_any_object (&any, &c_obj);
910     }
911   else if (signal_query.signal_id == atk_signal_link_selected)
912     {
913       if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT)
914         detail1 = g_value_get_int (param_values + 1);
915       spi_init_any_nil (&any);
916     }
917   else if ((signal_query.signal_id == atk_signal_children_changed) && gobject)
918     {
919       ao = atk_object_ref_accessible_child (ATK_OBJECT (gobject), 
920                                             detail1);
921       if (ao) 
922         {
923           s_ao = spi_accessible_new (ao);
924           c_obj = BONOBO_OBJREF (s_ao);
925           spi_init_any_object (&any, &c_obj);
926           g_object_unref (ao);
927         }
928       else
929         {
930           spi_init_any_nil (&any);
931         }
932     }
933   else
934     {
935       if (n_param_values >= 2)
936         {
937           if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT)
938             detail1 = g_value_get_int (param_values + 1);
939           if (n_param_values >= 3)
940             {
941               if (G_VALUE_TYPE (param_values + 2) == G_TYPE_INT)
942                 detail2 = g_value_get_int (param_values + 2);
943             }
944         }
945
946       if (signal_query.signal_id == atk_signal_text_changed)
947         {
948           sp = atk_text_get_text (ATK_TEXT (gobject),
949                                   detail1,
950                                   detail1+detail2);
951           spi_init_any_string (&any, &sp);
952         }
953       else if (signal_query.signal_id == atk_signal_text_selection_changed)
954         {
955           text = ATK_TEXT (gobject);
956  
957           /* Return NULL as the selected string */
958           spi_init_any_nil (&any);
959         }
960       else
961         {
962           spi_init_any_nil (&any);
963         }
964     }
965
966   if (detail)
967     spi_atk_emit_eventv (gobject, detail1, detail2, &any,
968                          "object:%s:%s", name, detail);
969   else
970     spi_atk_emit_eventv (gobject, detail1, detail2, &any,
971                          "object:%s", name);
972
973   if (sp)
974     g_free (sp);
975
976   return TRUE;
977 }
978
979 static gboolean
980 spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint,
981                                       guint n_param_values,
982                                       const GValue *param_values,
983                                       gpointer data)
984 {
985   GObject *gobject;
986   GSignalQuery signal_query;
987   CORBA_any any;
988   const gchar *name, *s;
989 #ifdef SPI_BRIDGE_DEBUG
990   const gchar *s2;
991 #endif
992   
993   g_signal_query (signal_hint->signal_id, &signal_query);
994
995   name = signal_query.signal_name;
996
997 #ifdef SPI_BRIDGE_DEBUG
998   s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
999   s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
1000   fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n",
1001            g_type_name (signal_query.itype), name, s ? s : "<NULL>" , s2);
1002 #endif
1003   
1004   gobject = g_value_get_object (param_values + 0);
1005
1006   s = atk_object_get_name (ATK_OBJECT (gobject));
1007   spi_init_any_string (&any, (char **) &s);
1008   
1009   spi_atk_emit_eventv (gobject, 0, 0, &any,
1010                        "window:%s", name);
1011   return TRUE;
1012 }
1013
1014 static void
1015 spi_atk_tidy_windows (void)
1016 {
1017   AtkObject *root;
1018   gint n_children;
1019   gint i;
1020
1021   root = atk_get_root ();
1022   n_children = atk_object_get_n_accessible_children (root);
1023   for (i = 0; i < n_children; i++)
1024     {
1025       AtkObject *child;
1026       AtkStateSet *stateset;
1027       CORBA_any any;
1028       const gchar *name;
1029      
1030       child = atk_object_ref_accessible_child (root, i);
1031       stateset = atk_object_ref_state_set (child);
1032       
1033       name = atk_object_get_name (child);
1034       spi_init_any_string (&any, (char**) &name);
1035       if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE))
1036         {
1037           spi_atk_emit_eventv (G_OBJECT (child), 0, 0, &any, "window:deactivate");
1038         }
1039       g_free (stateset);
1040
1041       spi_atk_emit_eventv (G_OBJECT (child), 0, 0, &any, "window:destroy");
1042       g_object_unref (child);
1043     }
1044 }
1045
1046 static void
1047 reinit_register_vars (void)
1048 {
1049   registry = CORBA_OBJECT_NIL;
1050   device_event_controller = CORBA_OBJECT_NIL;
1051   this_app = NULL;
1052 }
1053