2001-12-07 Michael Meeks <michael@ximian.com>
[platform/core/uifw/at-spi2-atk.git] / cspi / spi_main.c
1 /*
2  *
3  * Basic SPI initialization and event loop function prototypes
4  *
5  */
6 #include <string.h>
7 #include <stdlib.h>
8 #include <cspi/spi-private.h>
9
10 #undef DEBUG_OBJECTS
11
12 static CORBA_Environment ev = { 0 };
13 static Accessibility_Registry registry = CORBA_OBJECT_NIL;
14 static SPIBoolean is_gnome_app = TRUE;
15 static GHashTable *live_refs = NULL;
16
17 static guint
18 spi_object_hash (gconstpointer key)
19 {
20   CORBA_Object object = (CORBA_Object) key;
21   guint        retval;
22   
23   retval = CORBA_Object_hash (object, 0, &ev);
24
25   return retval;
26 }
27
28 static gboolean
29 spi_object_equal (gconstpointer a, gconstpointer b)
30 {
31   CORBA_Object objecta = (CORBA_Object) a;
32   CORBA_Object objectb = (CORBA_Object) b;
33   gboolean     retval;
34
35   retval = CORBA_Object_is_equivalent (objecta, objectb, &ev);
36
37   return retval;
38 }
39
40 static void
41 spi_object_release (gpointer  value)
42 {
43   Accessible *a = (Accessible *) value;
44
45 #ifdef DEBUG_OBJECTS
46   g_print ("releasing %p => %p\n", a, a->objref);
47 #endif
48
49   bonobo_object_release_unref (a->objref, NULL);
50
51   memset (a, 0xaa, sizeof (Accessible));
52   a->ref_count = -1;
53
54 #ifndef DEBUG_OBJECTS
55   g_free (a);
56 #endif
57 }
58
59
60 SPIBoolean
61 cspi_accessible_is_a (Accessible *obj,
62                       const char *interface_name)
63 {
64   SPIBoolean        retval;
65   Bonobo_Unknown unknown;
66
67   if (obj == NULL)
68     {
69       return FALSE;
70     }
71
72   unknown = Bonobo_Unknown_queryInterface (CSPI_OBJREF (obj),
73                                            interface_name, cspi_ev ());
74
75   if (BONOBO_EX (cspi_ev ()))
76     {
77       g_error ("Exception '%s' checking if is '%s'",
78                bonobo_exception_get_text (cspi_ev ()),
79                interface_name);
80     }
81
82   if (unknown != CORBA_OBJECT_NIL)
83     {
84       retval = TRUE;
85       bonobo_object_release_unref (unknown, NULL);
86     }
87   else
88     {
89       retval = FALSE;
90     }
91
92   return retval;
93 }
94
95 static GHashTable *
96 get_live_refs (void)
97 {
98   if (!live_refs) 
99     {
100       live_refs = g_hash_table_new_full (spi_object_hash,
101                                          spi_object_equal,
102                                          NULL,
103                                          spi_object_release);
104     }
105   return live_refs;
106 }
107
108 CORBA_Environment *
109 cspi_ev (void)
110 {
111   /* This method is an ugly hack */
112   return &ev;
113 }
114
115 Accessibility_Registry
116 cspi_registry (void)
117 {
118   return registry;
119 }
120
121 SPIBoolean
122 cspi_is_gnome_app (void)
123 {
124   return is_gnome_app;  
125 }
126
127 SPIBoolean
128 cspi_exception (void)
129 {
130   SPIBoolean retval;
131
132   if (BONOBO_EX (&ev))
133     {
134       CORBA_exception_free (&ev);
135       retval = TRUE;
136     }
137   else
138     {
139       retval = FALSE;
140     }
141
142   return retval;
143 }
144
145 Accessible *
146 cspi_object_add (CORBA_Object corba_object)
147 {
148   Accessible *ref;
149
150   if (corba_object == CORBA_OBJECT_NIL)
151     {
152       ref = NULL;
153     }
154   else
155     {
156       if ((ref = g_hash_table_lookup (get_live_refs (), corba_object)))
157         {
158           g_assert (ref->ref_count > 0);
159           ref->ref_count++;
160           bonobo_object_release_unref (corba_object, NULL);
161 #ifdef DEBUG_OBJECTS
162           g_print ("returning cached %p => %p\n", ref, ref->objref);
163 #endif
164         }
165       else
166         {
167           ref = g_new (Accessible, 1);
168
169 #ifdef DEBUG_OBJECTS
170           g_print ("allocating %p => %p\n", ref, corba_object);
171 #endif
172
173           ref->objref = corba_object;
174           ref->ref_count = 1;
175
176           g_hash_table_insert (get_live_refs (), ref->objref, ref);
177         }
178     }
179
180   return ref;
181 }
182
183 Accessible *
184 cspi_object_add_check (CORBA_Object corba_object)
185 {
186   Accessible *retval;
187
188   if (ev._major == CORBA_USER_EXCEPTION &&
189       !strcmp (ev._id, ex_Accessibility_ChildGone))
190     {
191       retval = NULL;
192     }
193   else if (ev._major != CORBA_NO_EXCEPTION)
194     {
195       cspi_check_ev (cspi_ev (), "pre method check");
196       retval = NULL;
197     }
198   else
199     {
200       retval = cspi_object_add (corba_object);
201
202       cspi_check_ev (cspi_ev (), "post method check");
203     }
204
205   return retval;
206 }
207
208 void
209 cspi_object_ref (Accessible *accessible)
210 {
211   g_return_if_fail (accessible != NULL);
212
213   accessible->ref_count++;
214 }
215
216 void
217 cspi_object_unref (Accessible *accessible)
218 {
219   if (accessible == NULL)
220     {
221       return;
222     }
223
224   if (--accessible->ref_count == 0)
225     {
226       g_hash_table_remove (get_live_refs (), accessible->objref);
227     }
228 }
229
230 static void
231 cspi_cleanup (void)
232 {
233   GHashTable *refs;
234
235   refs = live_refs;
236   live_refs = NULL;
237   if (refs)
238     {
239       g_hash_table_destroy (refs);
240     }
241
242   if (registry != CORBA_OBJECT_NIL)
243     {
244       bonobo_object_release_unref (registry, NULL);
245       registry = CORBA_OBJECT_NIL;
246     }
247 }
248
249 static gboolean SPI_inited = FALSE;
250
251 /**
252  * SPI_init:
253  * @isGNOMEApp: a #SPIBoolean indicating whether the client of the SPI
254  *              will use the Gnome event loop or not.  Clients that have
255  *              their own GUIS will usually specify #TRUE here, and must
256  *              do so if they use Gnome GUI components.
257  *
258  * Connects to the accessibility registry and initializes the SPI.
259  *
260  * Returns: 0 on success, otherwise an integer error code.
261  **/
262 int
263 SPI_init (SPIBoolean isGNOMEApp)
264 {
265   int argc = 0;
266   char *obj_id;
267   is_gnome_app = isGNOMEApp;
268
269   if (SPI_inited)
270     {
271       return 1;
272     }
273
274   SPI_inited = TRUE;
275
276   CORBA_exception_init (&ev);
277
278   if (!bonobo_init (&argc, NULL))
279     {
280       g_error ("Could not initialize Bonobo");
281     }
282
283   obj_id = "OAFIID:Accessibility_Registry:proto0.1";
284
285   registry = bonobo_activation_activate_from_id (
286           obj_id, 0, NULL, cspi_ev ());
287
288   if (ev._major != CORBA_NO_EXCEPTION)
289     {
290       g_error ("AT-SPI error: during registry activation: %s\n",
291                bonobo_exception_get_text (cspi_ev ()));
292     }
293
294   if (registry == CORBA_OBJECT_NIL)
295     {
296       g_error ("Could not locate registry");
297     }
298
299   bonobo_activate ();
300
301   if (isGNOMEApp)
302     {
303       g_atexit (cspi_cleanup);
304     }
305   
306   return 0;
307 }
308
309 /**
310  * SPI_event_main:
311  *
312  * Starts/enters the main event loop for the SPI services.
313  *
314  * (NOTE: This method does not return control, it is exited via a call
315  * to SPI_exit() from within an event handler).
316  *
317  **/
318 void
319 SPI_event_main ()
320 {
321   if (cspi_is_gnome_app ())
322     {
323       bonobo_main ();
324     }
325   else
326     {
327       /* TODO: install signal handlers to do cleanup */
328       CORBA_ORB_run (bonobo_orb (), cspi_ev ());
329       fprintf (stderr, "orb loop exited...\n");
330     }
331 }
332
333 /**
334  * SPI_eventIsReady:
335  *
336  * Checks to see if an SPI event is waiting in the event queue.
337  * Used by clients that don't wish to use SPI_event_main().
338  *
339  * Not Yet Implemented.
340  *
341  * Returns: #TRUE if an event is waiting, otherwise #FALSE.
342  *
343  **/
344 SPIBoolean
345 SPI_eventIsReady ()
346 {
347   return FALSE;
348 }
349
350 /**
351  * SPI_nextEvent:
352  * @waitForEvent: a #SPIBoolean indicating whether to block or not.
353  *
354  * Gets the next event in the SPI event queue; blocks if no event
355  * is pending and @waitForEvent is #TRUE.
356  * Used by clients that don't wish to use SPI_event_main().
357  *
358  * Not Yet Implemented.
359  *
360  * Returns: the next #AccessibleEvent in the SPI event queue.
361  **/
362 AccessibleEvent *
363 SPI_nextEvent (SPIBoolean waitForEvent)
364 {
365   return NULL;
366 }
367
368 /**
369  * SPI_exit:
370  *
371  * Disconnects from the Accessibility Registry and releases 
372  * any floating resources.
373  **/
374 void
375 SPI_exit (void)
376 {
377   if (!SPI_inited)
378     {
379       return;
380     }
381
382   SPI_inited = FALSE;
383
384   cspi_cleanup ();
385
386   if (cspi_is_gnome_app ())
387     {
388       bonobo_main_quit ();
389     }
390   else
391     {
392       CORBA_ORB_shutdown (bonobo_orb (), TRUE, cspi_ev ());         
393     }
394   fprintf (stderr, "bye-bye!\n");
395 }
396