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