4647011a88381fa3fa03c9b9fdea71aef98c6fec
[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 Sun Microsystems Inc.,
6  * Copyright 2001, 2002 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 <stdio.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <libbonobo.h>
28 #include <orbit/orbit.h>
29 #include <atk/atk.h>
30 #include <atk/atkobject.h>
31 #include <atk/atknoopobject.h>
32 #include <libspi/Accessibility.h>
33 #include "accessible.h"
34 #include "application.h"
35
36 #include <bonobo-activation/bonobo-activation-register.h>
37
38 #undef SPI_BRIDGE_DEBUG
39
40 static CORBA_Environment ev;
41 static Accessibility_Registry registry = NULL;
42 static SpiApplication *this_app = NULL;
43 static gboolean registry_died = FALSE;
44 static guint toplevel_handler;
45
46 /* NOT YET USED
47    static GQuark atk_quark_property_changed_name;
48    static GQuark atk_quark_property_changed_description;
49    static GQuark atk_quark_property_changed_parent;
50    static GQuark atk_quark_property_changed_role;
51    static GQuark atk_quark_property_changed_table_caption;
52    static GQuark atk_quark_property_changed_table_column_description;
53    static GQuark atk_quark_property_changed_table_row_description;
54    static guint atk_signal_property_changed;
55 */
56
57 static guint atk_signal_text_changed;
58 static guint atk_signal_child_changed;
59
60 /* NOT YET USED
61    static guint atk_signal_text_selection_changed;
62    static guint atk_signal_active_descendant_changed;
63    static guint atk_signal_row_reordered;
64    static guint atk_signal_row_inserted;
65    static guint atk_signal_row_deleted;
66    static guint atk_signal_column_reordered;
67    static guint atk_signal_column_inserted;
68    static guint atk_signal_column_deleted;
69 */
70
71 #define ATK_BRIDGE_RESERVED_CONTEXT_SIZE 16
72
73 typedef enum {
74   ATK_BRIDGE_CONTEXT_TYPE_NONE = 0,
75   ATK_BRIDGE_CONTEXT_TYPE_STRING,
76   ATK_BRIDGE_CONTEXT_TYPE_OBJECT
77 } AtkBridgeEventContextType;
78
79 typedef union {
80   gchar       *string;
81   AtkObject   *object;
82   gpointer    *foo;
83 } AtkBridgeEventContextData;
84
85 typedef struct {
86   AtkBridgeEventContextType _type;
87   AtkBridgeEventContextData _data;
88 } AtkBridgeEventContext;
89
90 static Accessibility_Registry spi_atk_bridge_get_registry (void);
91 static void     spi_atk_bridge_do_registration         (void);
92 static void     spi_atk_bridge_toplevel_added          (AtkObject             *object,
93                                                         guint                 index,
94                                                         AtkObject             *child);
95
96 static void     spi_atk_bridge_exit_func               (void);
97 static void     spi_atk_register_event_listeners       (void);
98 static void     spi_atk_bridge_focus_tracker           (AtkObject             *object);
99 static void     spi_atk_bridge_register_application    (Accessibility_Registry registry);
100 static gboolean spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
101                                                         guint                  n_param_values,
102                                                         const GValue          *param_values,
103                                                         gpointer               data);
104 static gboolean
105 spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint,
106                                 guint n_param_values,
107                                 const GValue *param_values,
108                                 gpointer data);
109 static gboolean
110 spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
111                                      guint n_param_values,
112                                      const GValue *param_values,
113                                      gpointer data);
114 static gboolean spi_atk_bridge_signal_listener         (GSignalInvocationHint *signal_hint,
115                                                         guint                  n_param_values,
116                                                         const GValue          *param_values,
117                                                         gpointer               data);
118 static gint     spi_atk_bridge_key_listener            (AtkKeyEventStruct     *event,
119                                                         gpointer               data);
120
121 /* For automatic libgnome init */
122 extern void gnome_accessibility_module_init     (void);
123 extern void gnome_accessibility_module_shutdown (void);
124
125 static int     atk_bridge_initialized = FALSE;
126 static guint   atk_bridge_focus_tracker_id = 0;
127 static guint   atk_bridge_key_event_listener_id = 0;
128 static GArray *listener_ids = NULL;
129
130 /*
131  *   These exported symbols are hooked by gnome-program
132  * to provide automatic module initialization and shutdown.
133  */
134 extern void gnome_accessibility_module_init     (void);
135 extern void gnome_accessibility_module_shutdown (void);
136
137 static void
138 atk_bridge_init_event_type_consts ()
139 {
140   atk_signal_child_changed = g_signal_lookup ("child_changed", 
141                                               ATK_TYPE_OBJECT);
142   atk_signal_text_changed = g_signal_lookup ("text_changed", 
143                                              ATK_TYPE_TEXT);
144 }
145
146 static int
147 atk_bridge_init (gint *argc, gchar **argv[])
148 {
149   if (atk_bridge_initialized)
150     {
151       return 0;
152     }
153   atk_bridge_initialized = TRUE;
154
155   if (!bonobo_init (argc, argv ? *argv : NULL))
156     {
157       g_error ("Could not initialize Bonobo");
158     }
159
160   /*
161    * We only want to enable the bridge for top level
162    * applications, we detect bonobo components by seeing
163    * if they were activated with the intention of extracting
164    * an impl. by IID - very solid.
165    */
166   if (bonobo_activation_iid_get ())
167     {
168       fprintf (stderr, "Found Bonobo component\n");
169       toplevel_handler = g_signal_connect (atk_get_root (), 
170                                            "children-changed::add",
171                                            (GCallback) spi_atk_bridge_toplevel_added, 
172                                            NULL);
173     }
174   else
175     {
176       spi_atk_bridge_do_registration ();
177     }
178  
179   atk_bridge_init_event_type_consts ();
180
181   return 0;
182 }
183
184 static void
185 spi_atk_bridge_do_registration (void)
186 {
187   CORBA_Environment ev;
188
189   CORBA_exception_init(&ev);
190
191   if (spi_atk_bridge_get_registry () == CORBA_OBJECT_NIL)
192     {
193       g_error ("Could not locate registry");
194     }
195
196   bonobo_activate ();
197
198   /* Create the accessible application server object */
199
200   this_app = spi_application_new (atk_get_root ());
201
202   fprintf (stderr, "About to register application\n");
203
204   spi_atk_bridge_register_application (spi_atk_bridge_get_registry ());
205   
206   g_atexit (spi_atk_bridge_exit_func);
207
208   fprintf (stderr, "Application registered & listening\n");
209
210 }
211
212 static void
213 spi_atk_bridge_toplevel_added (AtkObject *object,
214                                guint     index,
215                                AtkObject *child)
216 {
217   g_signal_handler_disconnect (object, toplevel_handler);
218   spi_atk_bridge_do_registration ();
219 }
220
221 static void
222 spi_atk_bridge_register_application (Accessibility_Registry registry)
223 {
224   Accessibility_Registry_registerApplication (spi_atk_bridge_get_registry (),
225                                               BONOBO_OBJREF (this_app),
226                                               &ev);
227   spi_atk_register_event_listeners ();
228 }
229
230 static Accessibility_Registry
231 spi_atk_bridge_get_registry ()
232 {
233   CORBA_Environment ev;
234
235   if (registry_died || (registry == NULL)) {
236           CORBA_exception_init (&ev);
237           if (registry_died) g_warning ("registry died! restarting...");
238           registry = bonobo_activation_activate_from_id (
239                   "OAFIID:Accessibility_Registry:1.0", 0, NULL, &ev);
240           
241           if (ev._major != CORBA_NO_EXCEPTION)
242           {
243                   g_error ("Accessibility app error: exception during "
244                            "registry activation from id: %s\n",
245                            CORBA_exception_id (&ev));
246                   CORBA_exception_free (&ev);
247           }
248           
249           if (registry_died && registry) {
250                   registry_died = FALSE;
251                   spi_atk_bridge_register_application (registry);
252           }
253   }
254   return registry;
255 }
256
257 int
258 gtk_module_init (gint *argc, gchar **argv[])
259 {
260         return atk_bridge_init (argc, argv);
261 }
262
263 static void
264 add_signal_listener (const char *signal_name)
265 {
266   guint id;
267
268   id = atk_add_global_event_listener (
269     spi_atk_bridge_signal_listener, signal_name);
270
271   g_array_append_val (listener_ids, id);
272 }
273
274 static void
275 spi_atk_register_event_listeners (void)
276 {
277   /*
278    * kludge to make sure the Atk interface types are registered, otherwise
279    * the AtkText signal handlers below won't get registered
280    */
281   guint      id;
282   GObject   *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
283   AtkObject *bo = atk_no_op_object_new (ao);
284   
285   /* Register for focus event notifications, and register app with central registry  */
286
287   listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
288
289   atk_bridge_focus_tracker_id = atk_add_focus_tracker (spi_atk_bridge_focus_tracker);
290
291   id = atk_add_global_event_listener (spi_atk_bridge_property_event_listener,
292                                       "Gtk:AtkObject:property-change");
293   g_array_append_val (listener_ids, id);
294   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
295                                       "window:create");
296   g_array_append_val (listener_ids, id);
297   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
298                                       "window:destroy");
299   g_array_append_val (listener_ids, id);
300   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
301                                       "window:minimize");
302   g_array_append_val (listener_ids, id);
303   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
304                                       "window:maximize");
305   g_array_append_val (listener_ids, id);
306   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
307                                       "window:restore");
308   g_array_append_val (listener_ids, id);
309   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
310                                       "window:activate");
311   g_array_append_val (listener_ids, id);
312   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
313                                       "window:deactivate");
314   g_array_append_val (listener_ids, id);
315   id = atk_add_global_event_listener (spi_atk_bridge_state_event_listener,
316                                       "Gtk:AtkObject:state-change");
317   g_array_append_val (listener_ids, id);
318
319   add_signal_listener ("Gtk:AtkObject:children-changed");
320   add_signal_listener ("Gtk:AtkObject:visible-data-changed");
321   add_signal_listener ("Gtk:AtkSelection:selection-changed");
322   add_signal_listener ("Gtk:AtkText:text-selection-changed");
323   add_signal_listener ("Gtk:AtkText:text-changed");
324   add_signal_listener ("Gtk:AtkText:text-caret-moved");
325   add_signal_listener ("Gtk:AtkTable:row-inserted");
326   add_signal_listener ("Gtk:AtkTable:row-reordered");
327   add_signal_listener ("Gtk:AtkTable:row-deleted");
328   add_signal_listener ("Gtk:AtkTable:column-inserted");
329   add_signal_listener ("Gtk:AtkTable:column-reordered");
330   add_signal_listener ("Gtk:AtkTable:column-deleted");
331   add_signal_listener ("Gtk:AtkTable:model-changed");
332 /*
333  * May add the following listeners to implement preemptive key listening for GTK+
334  *
335  * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
336  * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
337  */
338   atk_bridge_key_event_listener_id = atk_add_key_event_listener (
339     spi_atk_bridge_key_listener, NULL);
340   
341   g_object_unref (G_OBJECT (bo));
342   g_object_unref (ao);
343 }
344
345 static void
346 deregister_application (BonoboObject *app)
347 {
348   Accessibility_Registry registry = spi_atk_bridge_get_registry ();     
349   Accessibility_Registry_deregisterApplication (registry, BONOBO_OBJREF (app), &ev);
350
351   registry = bonobo_object_release_unref (registry, &ev);
352   
353   app = bonobo_object_unref (app);
354 }
355
356 static void
357 spi_atk_bridge_exit_func (void)
358 {
359   BonoboObject *app = (BonoboObject *) this_app;
360
361   fprintf (stderr, "exiting bridge\n");
362
363   if (!app)
364     {
365       return;
366     }
367   this_app = NULL;
368
369   /*
370    *  FIXME: this may be incorrect for apps that do their own bonobo
371    *  shutdown, until we can explicitly shutdown to get the ordering
372    *  right.
373    */
374   if (!bonobo_is_initialized ())
375     {
376       fprintf (stderr, "Re-initializing bonobo\n");
377       g_assert (bonobo_init (0, NULL));
378       g_assert (bonobo_activate ());
379     }
380   
381   deregister_application (app);
382
383   fprintf (stderr, "bridge exit func complete.\n");
384
385   if (g_getenv ("AT_BRIDGE_SHUTDOWN"))
386     {
387       g_assert (!bonobo_debug_shutdown ());
388     }
389 }
390
391 void
392 gnome_accessibility_module_init (void)
393 {
394   atk_bridge_init (NULL, NULL);
395
396   g_print("Atk Accessibilty bridge initialized\n");
397 }
398
399 void
400 gnome_accessibility_module_shutdown (void)
401 {
402   BonoboObject *app = (BonoboObject *) this_app;
403   int     i;
404   GArray *ids = listener_ids;
405   
406   if (!atk_bridge_initialized)
407     {
408       return;
409     }
410   atk_bridge_initialized = FALSE;
411   this_app = NULL;
412
413   g_print("Atk Accessibilty bridge shutdown\n");
414
415   listener_ids = NULL;
416   atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
417   
418   for (i = 0; ids && i < ids->len; i++)
419   {
420           atk_remove_global_event_listener (g_array_index (ids, guint, i));
421   }
422   
423   atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
424
425   deregister_application (app);
426 }
427
428 static void
429 atk_bridge_event_context_init (CORBA_any *any, 
430                                AtkBridgeEventContext *ctx)
431 {
432   SpiAccessible *accessible;
433   if (ctx) 
434     {
435       switch (ctx->_type) 
436         {
437           /* FIXME      
438             case ATK_BRIDGE_CONTEXT_TYPE_OBJECT:
439                 accessible = spi_accessible_new (ctx->_data.object);    
440                 spi_init_any_object (any, BONOBO_OBJREF (accessible));
441                 break;
442           */
443         case ATK_BRIDGE_CONTEXT_TYPE_STRING:
444           spi_init_any_string (any, &ctx->_data.string);
445           break;
446         default:
447           spi_init_any_nil (any); 
448         } 
449     }
450   else
451     {
452       spi_init_any_nil (any); 
453     }
454
455
456 static void
457 spi_atk_bridge_focus_tracker (AtkObject *object)
458 {
459   SpiAccessible *source;
460   Accessibility_Event e;
461
462   source = spi_accessible_new (object);
463
464   e.type = "focus:";
465   e.source = BONOBO_OBJREF (source);
466   e.detail1 = 0;
467   e.detail2 = 0;
468   spi_init_any_nil (&e.any_data);
469   Accessibility_Registry_notifyEvent (spi_atk_bridge_get_registry (), &e, &ev);
470   if (BONOBO_EX (&ev)) registry_died = TRUE;
471   
472   Accessibility_Accessible_unref (e.source, &ev);
473   
474   CORBA_exception_free (&ev);
475 }
476
477 static
478 AtkBridgeEventContext *
479 spi_atk_bridge_event_context_create (GObject *gobject, 
480                                      long detail1, 
481                                      long detail2, 
482                                      GSignalQuery *signal_query, 
483                                      const gchar *detail)
484 {
485   AtkBridgeEventContext *ctx = g_new0 (AtkBridgeEventContext, 1);
486   /*
487   if (signal_query->signal_id == atk_signal_child_changed) 
488     {  
489       ctx->_type = ATK_BRIDGE_CONTEXT_TYPE_OBJECT;
490       ctx->_data.object = atk_object_ref_accessible_child (ATK_OBJECT (gobject),
491                                                            (gint) detail1);
492     }
493   else */ if (signal_query->signal_id == atk_signal_text_changed)
494     {
495       ctx->_type = ATK_BRIDGE_CONTEXT_TYPE_STRING;
496       ctx->_data.string = atk_text_get_text (ATK_TEXT (gobject),
497                                              (gint) detail1,
498                                              (gint) detail1+detail2);
499     }
500   else
501     {
502       ctx->_type = ATK_BRIDGE_CONTEXT_TYPE_NONE;
503     }
504   return ctx;
505 }
506
507 static void
508 spi_atk_bridge_event_context_free (AtkBridgeEventContext *ctx)
509 {
510   if (ctx->_type == ATK_BRIDGE_CONTEXT_TYPE_OBJECT)
511     g_object_unref (ctx->_data.object);
512   g_free (ctx);
513 }
514
515 static void
516 spi_atk_emit_eventv (GObject               *gobject,
517                      unsigned long          detail1,
518                      unsigned long          detail2,
519                      AtkBridgeEventContext *context,
520                      const char   *format, ...)
521 {
522   va_list             args;
523   Accessibility_Event e;
524   SpiAccessible      *source;
525   AtkObject          *aobject;
526 #ifdef SPI_BRIDGE_DEBUG
527   CORBA_string s;
528 #endif
529   
530   va_start (args, format);
531   
532   if (ATK_IS_IMPLEMENTOR (gobject))
533     {
534       aobject = atk_implementor_ref_accessible (ATK_IMPLEMENTOR (gobject));
535       source  = spi_accessible_new (aobject);
536       g_object_unref (G_OBJECT (aobject));
537     }
538   else if (ATK_IS_OBJECT (gobject))
539     {
540       aobject = ATK_OBJECT (gobject);
541       source  = spi_accessible_new (aobject);
542     }
543   else
544     {
545       aobject = NULL;
546       source  = NULL;
547       g_error ("received property-change event from non-AtkImplementor");
548     }
549
550   if (source != NULL)
551     {
552       e.type = g_strdup_vprintf (format, args);
553       e.source = BONOBO_OBJREF (source);
554       e.detail1 = detail1;
555       e.detail2 = detail2;
556 #ifdef SPI_BRIDGE_DEBUG
557       s = Accessibility_Accessible__get_name (BONOBO_OBJREF (source), &ev);
558       g_warning ("Emitting event '%s' (%lu, %lu) on %s",
559                  e.type, e.detail1, e.detail2, s);
560       CORBA_free (s);
561 #endif
562       CORBA_exception_init (&ev);
563       atk_bridge_event_context_init (&e.any_data, context); 
564       Accessibility_Registry_notifyEvent (spi_atk_bridge_get_registry (), &e, &ev);
565       /* I haven't freed any_data._value when it's a char*, does it leak ? */
566 #ifdef SPI_BRIDGE_DEBUG
567       if (ev._major != CORBA_NO_EXCEPTION)
568               g_warning ("error emitting event %s, (%d) %s",
569                          e.type,
570                          ev._major,
571                          CORBA_exception_id(&ev));
572 #endif        
573       if (BONOBO_EX (&ev)) registry_died = TRUE;
574       Accessibility_Accessible_unref (e.source, &ev);
575
576       CORBA_exception_free (&ev);
577
578       g_free (e.type);
579     }
580
581   va_end (args);
582
583 }
584
585 static gboolean
586 spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
587                                         guint n_param_values,
588                                         const GValue *param_values,
589                                         gpointer data)
590 {
591   AtkPropertyValues *values;
592   GObject *gobject;
593
594 #ifdef SPI_BRIDGE_DEBUG
595   GSignalQuery signal_query;
596   const gchar *name;
597   const gchar *s, *s2;
598   
599   g_signal_query (signal_hint->signal_id, &signal_query);
600   name = signal_query.signal_name;
601
602   s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
603   s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
604   values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
605   fprintf (stderr, "Received (property) signal %s:%s:%s from object %s (gail %s)\n",
606            g_type_name (signal_query.itype), name, values->property_name, s, s2);
607   
608 #endif
609
610   gobject = g_value_get_object (param_values + 0);
611   values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
612
613   spi_atk_emit_eventv (gobject, 0, 0, NULL,
614                        "object:property-change:%s", values->property_name);
615
616   return TRUE;
617 }
618
619 static gboolean
620 spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
621                                      guint n_param_values,
622                                      const GValue *param_values,
623                                      gpointer data)
624 {
625   GObject *gobject;
626   gchar *property_name;
627   gchar *type;
628   unsigned long detail1;
629 #ifdef SPI_BRIDGE_DEBUG
630   GSignalQuery signal_query;
631   const gchar *name;
632   
633   g_signal_query (signal_hint->signal_id, &signal_query);
634   name = signal_query.signal_name;
635   fprintf (stderr, "Received (state) signal %s:%s\n",
636            g_type_name (signal_query.itype), name);
637 #endif
638
639   gobject = g_value_get_object (param_values + 0);
640   property_name = g_strdup (g_value_get_string (param_values + 1));
641   detail1 = (g_value_get_boolean (param_values + 2))
642     ? 1 : 0;
643   type = g_strdup_printf ("object:state-changed:%s", property_name);
644   spi_atk_emit_eventv (gobject, 
645                        detail1,
646                        0,
647                        NULL,
648                        type);
649   g_free (property_name);
650   g_free (type);
651   return TRUE;
652 }
653
654
655 static void
656 spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent  *keystroke,
657                                        AtkKeyEventStruct          *event)
658 {
659 #ifdef SPI_DEBUG
660   if (event)
661     {
662       g_print ("event %c (%d)\n", (int) event->keyval, (int) event->keycode);
663     }
664   else
665 #endif
666   if (!event)
667     {
668       g_print ("WARNING: NULL key event!");
669     }
670   
671   keystroke->id        = (CORBA_long) event->keyval;
672   keystroke->hw_code   = (CORBA_short) event->keycode;
673   keystroke->timestamp = (CORBA_unsigned_long) event->timestamp;
674   keystroke->modifiers = (CORBA_unsigned_short) (event->state & 0xFFFF);
675   if (event->string)
676     {
677       keystroke->event_string = CORBA_string_dup (event->string);
678       keystroke->is_text = CORBA_TRUE;
679     }
680   else
681     {
682       keystroke->event_string = CORBA_string_dup ("");
683       keystroke->is_text = CORBA_FALSE;
684     }
685   switch (event->type)
686     {
687     case (ATK_KEY_EVENT_PRESS):
688       keystroke->type = Accessibility_KEY_PRESSED_EVENT;
689       break;
690     case (ATK_KEY_EVENT_RELEASE):
691       keystroke->type = Accessibility_KEY_RELEASED_EVENT;
692       break;
693     default:
694       keystroke->type = 0;
695       break;
696     }
697 #if 0  
698   g_print ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
699            (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
700            (int) keystroke->modifiers,
701            keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
702 #endif
703 }
704
705 static gint
706 spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data)
707 {
708   CORBA_boolean             result;
709   Accessibility_DeviceEvent key_event;
710   Accessibility_DeviceEventController controller;
711         
712   if (BONOBO_EX (&ev))
713         g_warning ("failure: pre-listener get dec\n");
714
715   controller =
716     Accessibility_Registry_getDeviceEventController (
717             spi_atk_bridge_get_registry (), &ev);
718
719   if (BONOBO_EX (&ev))
720     {
721       g_warning ("failure: no deviceeventcontroller found\n");
722       CORBA_exception_free (&ev);
723       registry_died = TRUE;
724       result = FALSE;
725     }
726   else
727     {
728
729       spi_init_keystroke_from_atk_key_event (&key_event, event);
730
731       result = Accessibility_DeviceEventController_notifyListenersSync (
732         controller, &key_event, &ev);
733
734       bonobo_object_release_unref (controller, &ev);
735       CORBA_exception_free (&ev);
736     }
737
738   return result;
739 }
740
741 static gboolean
742 spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint,
743                                 guint n_param_values,
744                                 const GValue *param_values,
745                                 gpointer data)
746 {
747   GObject *gobject;
748   GSignalQuery signal_query;
749   const gchar *name;
750   const gchar *detail;
751   AtkBridgeEventContext *ctx = NULL;
752   
753   gint detail1 = 0, detail2 = 0;
754 #ifdef SPI_BRIDGE_DEBUG
755   const gchar *s, *s2;
756 #endif
757   
758   g_signal_query (signal_hint->signal_id, &signal_query);
759
760   name = signal_query.signal_name;
761   if (signal_hint->detail)
762     detail = g_quark_to_string (signal_hint->detail);
763   else
764     detail = NULL;
765
766 #ifdef SPI_BRIDGE_DEBUG
767   s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
768   s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
769   fprintf (stderr, "Received signal %s:%s detail: %s from object %s (gail %s)\n",
770            g_type_name (signal_query.itype), name, 
771                         detail ? detail : "<NULL>", s ? s : "<NULL>" , s2);
772 #endif
773
774   gobject = g_value_get_object (param_values + 0);
775   if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT)
776     detail1 = g_value_get_int (param_values + 1);
777   if (G_VALUE_TYPE (param_values + 2) == G_TYPE_INT)
778     detail2 = g_value_get_int (param_values + 2);
779
780   /* build some event context data, depending on the type */
781   ctx = spi_atk_bridge_event_context_create (gobject, 
782                                              detail1, detail2, 
783                                              &signal_query, 
784                                              detail);
785
786   if (detail)
787     spi_atk_emit_eventv (gobject, detail1, detail2, ctx,
788                          "object:%s:%s", name, detail);
789   else
790     spi_atk_emit_eventv (gobject, detail1, detail2, ctx,
791                          "object:%s", name);
792
793   if (ctx) 
794     spi_atk_bridge_event_context_free (ctx);
795
796   return TRUE;
797 }
798
799 static gboolean
800 spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint,
801                                 guint n_param_values,
802                                 const GValue *param_values,
803                                 gpointer data)
804 {
805   GObject *gobject;
806   GSignalQuery signal_query;
807   AtkBridgeEventContext ctx;
808
809   const gchar *name, *s;
810 #ifdef SPI_BRIDGE_DEBUG
811   const gchar *s2;
812 #endif
813   
814   g_signal_query (signal_hint->signal_id, &signal_query);
815
816   name = signal_query.signal_name;
817
818 #ifdef SPI_BRIDGE_DEBUG
819   s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
820   s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
821   fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n",
822            g_type_name (signal_query.itype), name, s ? s : "<NULL>" , s2);
823 #endif
824   
825   gobject = g_value_get_object (param_values + 0);
826   ctx._type = ATK_BRIDGE_CONTEXT_TYPE_STRING;
827   s = atk_object_get_name (ATK_OBJECT (gobject));
828   ctx._data.string = (gchar *) s;
829   /* cast from const silences compiler */
830   spi_atk_emit_eventv (gobject, 0, 0, &ctx, "window:%s", name);
831   /* don't free the context, it's on the stack */
832   return TRUE;
833 }