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