Mon Jun 2 15:35:29 2003 Jonathan Blandford <jrb@redhat.com>
[platform/core/uifw/at-spi2-atk.git] / cspi / spi_main.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 /*
25  *
26  * Basic SPI initialization and event loop function prototypes
27  *
28  */
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <cspi/spi-private.h>
34
35 #undef DEBUG_OBJECTS
36
37 static CORBA_Environment ev = { 0 };
38 static Accessibility_Registry registry = CORBA_OBJECT_NIL;
39 static GHashTable *live_refs = NULL;
40
41 static guint
42 cspi_object_hash (gconstpointer key)
43 {
44   CORBA_Object object = (CORBA_Object) key;
45
46   return CORBA_Object_hash (object, 0, &ev);
47 }
48
49 static gboolean
50 cspi_object_equal (gconstpointer a, gconstpointer b)
51 {
52   CORBA_Object objecta = (CORBA_Object) a;
53   CORBA_Object objectb = (CORBA_Object) b;
54
55   return CORBA_Object_is_equivalent (objecta, objectb, &ev);
56 }
57
58 static void
59 cspi_object_release (gpointer value)
60 {
61   Accessible *a = (Accessible *) value;
62
63 #ifdef DEBUG_OBJECTS
64   g_print ("releasing %p => %p\n", a, a->objref);
65 #endif
66
67   if (!a->on_loan)
68     {
69       cspi_release_unref (a->objref);
70     }
71
72   memset (a, 0xaa, sizeof (Accessible));
73   a->ref_count = -1;
74
75 #ifndef DEBUG_OBJECTS
76   free (a);
77 #endif
78 }
79
80 SPIBoolean
81 cspi_accessible_is_a (Accessible *accessible,
82                       const char *interface_name)
83 {
84   SPIBoolean        retval;
85   Bonobo_Unknown unknown;
86
87   if (accessible == NULL)
88     {
89       return FALSE;
90     }
91
92   unknown = Bonobo_Unknown_queryInterface (CSPI_OBJREF (accessible),
93                                            interface_name, cspi_ev ());
94
95   if (ev._major != CORBA_NO_EXCEPTION)
96     {
97       g_error ("Exception '%s' checking if is '%s'",
98                cspi_exception_get_text (),
99                interface_name);
100     }
101
102   if (unknown != CORBA_OBJECT_NIL)
103     {
104       retval = TRUE;
105       cspi_release_unref (unknown);
106     }
107   else
108     {
109       retval = FALSE;
110     }
111
112   return retval;
113 }
114
115 static GHashTable *
116 cspi_get_live_refs (void)
117 {
118   if (!live_refs) 
119     {
120       live_refs = g_hash_table_new_full (cspi_object_hash,
121                                          cspi_object_equal,
122                                          NULL,
123                                          cspi_object_release);
124     }
125   return live_refs;
126 }
127
128 CORBA_Environment *
129 cspi_ev (void)
130 {
131   CORBA_exception_init (&ev);
132   return &ev;
133 }
134
135 Accessibility_Registry
136 cspi_registry (void)
137 {
138   if (!cspi_ping (registry))
139     {
140       registry = cspi_init ();
141     }
142   return registry;
143 }
144
145 SPIBoolean
146 cspi_exception (void)
147 {
148   SPIBoolean retval;
149
150   if (ev._major != CORBA_NO_EXCEPTION)
151     {
152       CORBA_exception_free (&ev);
153       retval = TRUE;
154     }
155   else
156     {
157       retval = FALSE;
158     }
159
160   return retval;
161 }
162
163 /*
164  *   This method swallows the corba_object BonoboUnknown
165  * reference, and returns an Accessible associated with it.
166  * If the reference is loaned, it means it is only valid
167  * between a borrow / return pair.
168  */
169 static Accessible *
170 cspi_object_get_ref (CORBA_Object corba_object, gboolean on_loan)
171 {
172   Accessible *ref;
173
174   if (corba_object == CORBA_OBJECT_NIL)
175     {
176       ref = NULL;
177     }
178   else if (!cspi_check_ev ("pre method check: add"))
179     {
180       ref = NULL;
181     }
182   else
183     {
184       if ((ref = g_hash_table_lookup (cspi_get_live_refs (), corba_object)))
185         {
186           g_assert (ref->ref_count > 0);
187           ref->ref_count++;
188           if (!on_loan)
189             {
190               if (ref->on_loan) /* Convert to a permanant ref */
191                 {
192                   ref->on_loan = FALSE;
193                 }
194               else
195                 {
196                   cspi_release_unref (corba_object);
197                 }
198             }
199 #ifdef DEBUG_OBJECTS
200           g_print ("returning cached %p => %p\n", ref, ref->objref);
201 #endif
202         }
203       else
204         {
205           ref = malloc (sizeof (Accessible));
206           ref->objref = corba_object;
207           ref->ref_count = 1;
208           ref->on_loan = on_loan;
209 #ifdef DEBUG_OBJECTS
210           g_print ("allocated %p => %p\n", ref, corba_object);
211 #endif
212           g_hash_table_insert (cspi_get_live_refs (), ref->objref, ref);
213         }
214     }
215
216   return ref;
217 }
218
219 Accessible *
220 cspi_object_add (CORBA_Object corba_object)
221 {
222   return cspi_object_get_ref (corba_object, FALSE);
223 }
224
225 Accessible *
226 cspi_object_borrow (CORBA_Object corba_object)
227 {
228   return cspi_object_get_ref (corba_object, TRUE);
229 }
230
231 void
232 cspi_object_return (Accessible *accessible)
233 {
234   g_return_if_fail (accessible != NULL);
235
236   if (!accessible->on_loan ||
237       accessible->ref_count == 1)
238     {
239       cspi_object_unref (accessible);
240     }
241   else /* Convert to a permanant ref */
242     {
243       accessible->on_loan = FALSE;
244       accessible->objref = cspi_dup_ref (accessible->objref);
245       accessible->ref_count--;
246     }
247 }
248
249 Accessible *
250 cspi_object_take (CORBA_Object corba_object)
251 {
252   Accessible *accessible;
253   accessible = cspi_object_borrow (corba_object);
254
255   cspi_object_ref (accessible);
256   /* 
257    * if the remote object is dead, 
258    * cspi_object_return will throw an exception. 
259    * FIXME: what clears that exception context ever ?
260    */
261   cspi_object_return (accessible);
262   if (cspi_exception ()) 
263     {
264       cspi_object_unref (accessible);
265       accessible = NULL;
266     }
267   return accessible;
268 }
269
270 void
271 cspi_object_ref (Accessible *accessible)
272 {
273   g_return_if_fail (accessible != NULL);
274
275   accessible->ref_count++;
276 }
277
278 void
279 cspi_object_unref (Accessible *accessible)
280 {
281   if (accessible == NULL)
282     {
283       return;
284     }
285
286   if (--accessible->ref_count == 0)
287     {
288       g_hash_table_remove (cspi_get_live_refs (), accessible->objref);
289     }
290 }
291
292 static void
293 cspi_cleanup (void)
294 {
295   GHashTable *refs;
296
297   refs = live_refs;
298   live_refs = NULL;
299   if (refs)
300     {
301       g_hash_table_destroy (refs);
302     }
303
304   if (registry != CORBA_OBJECT_NIL)
305     {
306       cspi_release_unref (registry);
307       registry = CORBA_OBJECT_NIL;
308     }
309 }
310
311 static gboolean SPI_inited = FALSE;
312
313 /**
314  * SPI_init:
315  *
316  * Connects to the accessibility registry and initializes the SPI.
317  *
318  * Returns: 0 on success, otherwise an integer error code.
319  **/
320 int
321 SPI_init (void)
322 {
323   if (SPI_inited)
324     {
325       return 1;
326     }
327
328   SPI_inited = TRUE;
329
330   CORBA_exception_init (&ev);
331
332   registry = cspi_init ();
333
334   g_atexit (cspi_cleanup);
335   
336   return 0;
337 }
338
339 /**
340  * SPI_event_main:
341  *
342  * Starts/enters the main event loop for the SPI services.
343  *
344  * (NOTE: This method does not return control, it is exited via a call to
345  *  SPI_event_quit () from within an event handler).
346  *
347  **/
348 void
349 SPI_event_main (void)
350 {
351   cspi_main ();
352 }
353
354 /**
355  * SPI_event_quit:
356  *
357  * Quits the last main event loop for the SPI services,
358  * see SPI_event_main
359  **/
360 void
361 SPI_event_quit (void)
362 {
363   cspi_main_quit ();
364 }
365
366 /**
367  * SPI_eventIsReady:
368  *
369  * Checks to see if an SPI event is waiting in the event queue.
370  * Used by clients that don't wish to use SPI_event_main().
371  *
372  * Not Yet Implemented.
373  *
374  * Returns: #TRUE if an event is waiting, otherwise #FALSE.
375  *
376  **/
377 SPIBoolean
378 SPI_eventIsReady ()
379 {
380   return FALSE;
381 }
382
383 /**
384  * SPI_nextEvent:
385  * @waitForEvent: a #SPIBoolean indicating whether to block or not.
386  *
387  * Gets the next event in the SPI event queue; blocks if no event
388  * is pending and @waitForEvent is #TRUE.
389  * Used by clients that don't wish to use SPI_event_main().
390  *
391  * Not Yet Implemented.
392  *
393  * Returns: the next #AccessibleEvent in the SPI event queue.
394  **/
395 AccessibleEvent *
396 SPI_nextEvent (SPIBoolean waitForEvent)
397 {
398   return NULL;
399 }
400
401 static void
402 report_leaked_ref (gpointer key, gpointer val, gpointer user_data)
403 {
404   char *name, *role;
405   Accessible *a = (Accessible *) val;
406   
407   name = Accessible_getName (a);
408   if (cspi_exception ())
409     {
410       name = NULL;
411     }
412
413   role = Accessible_getRoleName (a);
414   if (cspi_exception ())
415     {
416       role = NULL;
417     }
418
419   fprintf (stderr, "leaked %d references to object %s, role %s %p\n",
420            a->ref_count, name ? name : "<?>", role ? role : "<?>", a);
421
422   SPI_freeString (name);
423 }
424
425
426 /**
427  * SPI_exit:
428  *
429  * Disconnects from the Accessibility Registry and releases 
430  * any floating resources. Call only once at exit.
431  *
432  * Returns: 0 if there were no leaks, otherwise non zero.
433  **/
434 int
435 SPI_exit (void)
436 {
437   int leaked;
438
439   if (!SPI_inited)
440     {
441       return 0;
442     }
443
444   SPI_inited = FALSE;
445
446   if (live_refs)
447     {
448       leaked = g_hash_table_size (live_refs);
449 #define PRINT_LEAKS
450 #ifdef PRINT_LEAKS
451       g_hash_table_foreach (live_refs, report_leaked_ref, NULL);
452 #endif
453     }
454   else
455     {
456       leaked = 0;
457     }
458
459 #ifdef DEBUG_OBJECTS
460   if (leaked)
461     {
462       fprintf (stderr, "Leaked %d SPI handles\n", leaked);
463     }
464 #endif
465
466   cspi_cleanup ();
467
468   fprintf (stderr, "bye-bye!\n");
469
470   return leaked;
471 }
472
473 /**
474  * SPI_freeString:
475  * @s: a character string returned from another at-spi call.
476  *
477  * Free a character string returned from an at-spi call.  Clients of
478  * at-spi should use this function instead of free () or g_free().
479  * A NULL string @s will be silently ignored.
480  * This API should not be used to free strings
481  * from other libraries or allocated by the client.
482  **/
483 void
484 SPI_freeString (char *s)
485 {
486   if (s)
487     {
488       CORBA_free (s);
489     }
490 }