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