source: trunk/third/at-spi/atk-bridge/bridge.c @ 18422

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