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