API revisions: tweaks to key event API, added some reserved slots for
[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 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 #undef SPI_BRIDGE_DEBUG
36
37 static CORBA_Environment ev;
38 static Accessibility_Registry registry;
39 static SpiApplication *this_app = NULL;
40
41 static gboolean spi_atk_bridge_idle_init (gpointer user_data);
42 static void spi_atk_bridge_focus_tracker (AtkObject *object);
43 static void spi_atk_bridge_exit_func (void);
44 static void spi_atk_register_event_listeners (void);
45 static gboolean spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
46                                                         guint n_param_values,
47                                                         const GValue *param_values,
48                                                         gpointer data);
49 static gboolean spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
50                                                      guint n_param_values,
51                                                      const GValue *param_values,
52                                                      gpointer data);
53 static gboolean spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint,
54                                                 guint n_param_values,
55                                                 const GValue *param_values,
56                                                 gpointer data);
57 static gint spi_atk_bridge_key_listener (AtkKeyEventStruct *event,
58                                          gpointer data);
59
60 int
61 gtk_module_init (gint *argc, gchar **argv[])
62 {
63   CORBA_Environment ev;
64
65   if (!bonobo_init (argc, *argv))
66     {
67       g_error ("Could not initialize Bonobo");
68     }
69
70   CORBA_exception_init(&ev);
71
72   registry = bonobo_activation_activate_from_id (
73           "OAFIID:Accessibility_Registry:proto0.1", 0, NULL, &ev);
74   
75   if (ev._major != CORBA_NO_EXCEPTION)
76     {
77       g_error ("Accessibility app error: exception during "
78                "registry activation from id: %s\n",
79                CORBA_exception_id (&ev));
80       CORBA_exception_free (&ev);
81     }
82
83   if (CORBA_Object_is_nil (registry, &ev))
84     {
85       g_error ("Could not locate registry");
86     }
87
88   bonobo_activate ();
89
90   /* Create the accessible application server object */
91
92   this_app = spi_application_new (atk_get_root ());
93
94   fprintf (stderr, "About to register application\n");
95
96   Accessibility_Registry_registerApplication (registry,
97                                               BONOBO_OBJREF (this_app),
98                                               &ev);
99
100   g_atexit (spi_atk_bridge_exit_func);
101
102   g_idle_add (spi_atk_bridge_idle_init, NULL);
103
104   return 0;
105 }
106
107 static gboolean
108 spi_atk_bridge_idle_init (gpointer user_data)
109 {
110   spi_atk_register_event_listeners ();
111
112   fprintf (stderr, "Application registered & listening\n");
113
114   return FALSE;
115 }
116
117 static void
118 spi_atk_register_event_listeners (void)
119 {
120   /*
121    * kludge to make sure the Atk interface types are registered, otherwise
122    * the AtkText signal handlers below won't get registered
123    */
124   GObject   *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
125   AtkObject *bo = atk_no_op_object_new (ao);
126   
127   /* Register for focus event notifications, and register app with central registry  */
128
129   atk_add_focus_tracker (spi_atk_bridge_focus_tracker);
130   atk_add_global_event_listener (spi_atk_bridge_property_event_listener, "Gtk:AtkObject:property-change");
131   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkObject:children-changed");
132   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkObject:visible-data-changed");
133   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkSelection:selection-changed");
134   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkText:text-selection-changed");
135   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkText:text-changed");
136   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkText:text-caret-moved");
137   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkTable:row-inserted");
138   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkTable:row-reordered");
139   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkTable:row-deleted");
140   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkTable:column-inserted");
141   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkTable:column-reordered");
142   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkTable:column-deleted");
143   atk_add_global_event_listener (spi_atk_bridge_signal_listener, "Gtk:AtkTable:model-changed");
144 /*
145  * May add the following listeners to implement preemptive key listening for GTK+
146  *
147  * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
148  * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
149  */
150   atk_add_key_event_listener    (spi_atk_bridge_key_listener, NULL);
151   
152   g_object_unref (G_OBJECT (bo));
153   g_object_unref (ao);
154 }
155
156 static void
157 spi_atk_bridge_exit_func (void)
158 {
159   BonoboObject *app = (BonoboObject *) this_app;
160
161   fprintf (stderr, "exiting bridge\n");
162
163   if (!app)
164     {
165       return;
166     }
167   this_app = NULL;
168
169   /*
170    *  FIXME: this may be incorrect for apps that do their own bonobo
171    *  shutdown, until we can explicitly shutdown to get the ordering
172    *  right.
173    */
174   if (!bonobo_is_initialized ())
175     {
176       fprintf (stderr, "Re-initializing bonobo\n");
177       g_assert (bonobo_init (0, NULL));
178       g_assert (bonobo_activate ());
179     }
180   
181   Accessibility_Registry_deregisterApplication (
182           registry, BONOBO_OBJREF (app), &ev);
183
184   bonobo_object_release_unref (registry, &ev);
185   
186   bonobo_object_unref (app);
187   
188   fprintf (stderr, "bridge exit func complete.\n");
189
190   if (g_getenv ("AT_BRIDGE_SHUTDOWN"))
191     {
192       g_assert (!bonobo_debug_shutdown ());
193     }
194 }
195
196 static void
197 spi_atk_bridge_focus_tracker (AtkObject *object)
198 {
199   SpiAccessible *source;
200   Accessibility_Event e;
201
202   source = spi_accessible_new (object);
203
204   e.type = "focus:";
205   e.source = BONOBO_OBJREF (source);
206   e.detail1 = 0;
207   e.detail2 = 0;
208
209   Accessibility_Registry_notifyEvent (registry, &e, &ev);
210
211   CORBA_exception_free (&ev);
212 }
213
214 static void
215 spi_atk_emit_eventv (GObject      *gobject,
216                      unsigned long detail1,
217                      unsigned long detail2,
218                      const char   *format, ...)
219 {
220   va_list             args;
221   Accessibility_Event e;
222   SpiAccessible      *source;
223   AtkObject          *aobject;
224 #ifdef SPI_BRIDGE_DEBUG
225   CORBA_string s;
226 #endif
227   
228   va_start (args, format);
229   
230   if (ATK_IS_IMPLEMENTOR (gobject))
231     {
232       aobject = atk_implementor_ref_accessible (ATK_IMPLEMENTOR (gobject));
233       source  = spi_accessible_new (aobject);
234       g_object_unref (G_OBJECT (aobject));
235     }
236   else if (ATK_IS_OBJECT (gobject))
237     {
238       aobject = ATK_OBJECT (gobject);
239       source  = spi_accessible_new (aobject);
240     }
241   else
242     {
243       aobject = NULL;
244       source  = NULL;
245       g_error ("received property-change event from non-AtkImplementor");
246     }
247
248   if (source != NULL)
249     {
250       e.type = g_strdup_vprintf (format, args);
251       e.source = BONOBO_OBJREF (source);
252       e.detail1 = detail1;
253       e.detail2 = detail2;
254
255 #ifdef SPI_BRIDGE_DEBUG
256       s = Accessibility_Accessible__get_name (BONOBO_OBJREF (source), &ev);
257       g_warning ("Emitting event '%s' (%lu, %lu) on %s",
258                  e.type, e.detail1, e.detail2, s);
259       CORBA_free (s);
260 #endif
261
262       Accessibility_Registry_notifyEvent (registry, &e, &ev);
263
264       CORBA_exception_free (&ev);
265
266       g_free (e.type);
267     }
268
269   va_end (args);
270 }
271
272 static gboolean
273 spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
274                                         guint n_param_values,
275                                         const GValue *param_values,
276                                         gpointer data)
277 {
278   AtkPropertyValues *values;
279   GObject *gobject;
280
281 #ifdef SPI_BRIDGE_DEBUG
282   GSignalQuery signal_query;
283   const gchar *name;
284   gchar *s, *s2;
285   
286   g_signal_query (signal_hint->signal_id, &signal_query);
287   name = signal_query.signal_name;
288
289   s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
290   s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
291   fprintf (stderr, "Received (property) signal %s:%s from object %s (gail %s)\n",
292            g_type_name (signal_query.itype), name, s, s2);
293 #endif
294
295   gobject = g_value_get_object (param_values + 0);
296   values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
297
298   spi_atk_emit_eventv (gobject, 0, 0, "object:property-change:%s", values->property_name);
299
300   return TRUE;
301 }
302
303 static gboolean
304 spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
305                                      guint n_param_values,
306                                      const GValue *param_values,
307                                      gpointer data)
308 {
309   GObject *gobject;
310   AtkPropertyValues *values;
311 #ifdef SPI_BRIDGE_DEBUG
312   GSignalQuery signal_query;
313   const gchar *name;
314   
315   g_signal_query (signal_hint->signal_id, &signal_query);
316   name = signal_query.signal_name;
317   fprintf (stderr, "Received (state) signal %s:%s\n",
318            g_type_name (signal_query.itype), name);
319 #endif
320
321   gobject = g_value_get_object (param_values + 0);
322   values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
323
324   spi_atk_emit_eventv (gobject, 
325                        (unsigned long) values->old_value.data[0].v_ulong,
326                        (unsigned long) values->new_value.data[0].v_ulong,
327                        "object:%s:?", values->property_name);
328   
329   return TRUE;
330 }
331
332 static void
333 spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent  *keystroke,
334                                        AtkKeyEventStruct          *event)
335 {
336 #ifdef SPI_DEBUG
337   if (event)
338     {
339       g_print ("event %c (%d)\n", (int) event->keyval, (int) event->keycode);
340     }
341   else
342 #endif
343   if (!event)
344     {
345       g_print ("WARNING: NULL key event!");
346     }
347   
348   keystroke->id        = (CORBA_long) event->keyval;
349   keystroke->hw_code   = (CORBA_short) event->keycode;
350   keystroke->timestamp = (CORBA_unsigned_long) event->timestamp;
351   keystroke->modifiers = (CORBA_unsigned_short) (event->state & 0xFFFF);
352   if (event->string)
353     {
354       keystroke->event_string = CORBA_string_dup (event->string);
355       keystroke->is_text = CORBA_TRUE;
356     }
357   else
358     {
359       keystroke->event_string = CORBA_string_dup ("");
360       keystroke->is_text = CORBA_FALSE;
361     }
362   switch (event->type)
363     {
364     case (ATK_KEY_EVENT_PRESS):
365       keystroke->type = Accessibility_KEY_PRESSED;
366       break;
367     case (ATK_KEY_EVENT_RELEASE):
368       keystroke->type = Accessibility_KEY_RELEASED;
369       break;
370     default:
371       keystroke->type = 0;
372       break;
373     }
374 #if 0  
375   g_print ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
376            (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
377            (int) keystroke->modifiers,
378            keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
379 #endif
380 }
381
382 static gint
383 spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data)
384 {
385   CORBA_boolean             result;
386   Accessibility_DeviceEvent key_event;
387   Accessibility_DeviceEventController controller =
388     Accessibility_Registry_getDeviceEventController (registry, &ev);
389
390   if (BONOBO_EX (&ev))
391     {
392       g_warning ("failure: no deviceeventcontroller found\n");
393       CORBA_exception_free (&ev);
394       result = FALSE;
395     }
396   else
397     {
398
399       spi_init_keystroke_from_atk_key_event (&key_event, event);
400
401       result = Accessibility_DeviceEventController_notifyListenersSync (
402         controller, &key_event, &ev);
403
404       CORBA_exception_free (&ev);
405     }
406
407   return result;
408 }
409
410 static gboolean
411 spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint,
412                                 guint n_param_values,
413                                 const GValue *param_values,
414                                 gpointer data)
415 {
416   GObject *gobject;
417   GSignalQuery signal_query;
418   const gchar *name;
419   gint detail1 = 0, detail2 = 0;
420 #ifdef SPI_BRIDGE_DEBUG
421   gchar *s, *s2;
422 #endif
423   
424   g_signal_query (signal_hint->signal_id, &signal_query);
425
426   name = signal_query.signal_name;
427
428 #ifdef SPI_BRIDGE_DEBUG
429   s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
430   s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
431   fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n",
432            g_type_name (signal_query.itype), name, s, s2);
433 #endif
434
435   gobject = g_value_get_object (param_values + 0);
436   if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT)
437     detail1 = g_value_get_int (param_values + 1);
438   if (G_VALUE_TYPE (param_values + 2) == G_TYPE_INT)
439     detail2 = g_value_get_int (param_values + 2);
440   
441   spi_atk_emit_eventv (gobject, detail1, detail2, "object:%s", name);
442
443   return TRUE;
444 }
445
446
447
448
449
450
451