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