Fixed bug 85980.
[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   guint        retval;
43   
44   retval = CORBA_Object_hash (object, 0, &ev);
45
46   return retval;
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   gboolean     retval;
55
56   retval = CORBA_Object_is_equivalent (objecta, objectb, &ev);
57
58   return retval;
59 }
60
61 static void
62 cspi_object_release (gpointer  value)
63 {
64   Accessible *a = (Accessible *) value;
65
66 #ifdef DEBUG_OBJECTS
67   g_print ("releasing %p => %p\n", a, a->objref);
68 #endif
69
70   cspi_release_unref (a->objref);
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 *obj,
82                       const char *interface_name)
83 {
84   SPIBoolean        retval;
85   Bonobo_Unknown unknown;
86
87   if (obj == NULL)
88     {
89       return FALSE;
90     }
91
92   unknown = Bonobo_Unknown_queryInterface (CSPI_OBJREF (obj),
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   return &ev;
132 }
133
134 Accessibility_Registry
135 cspi_registry (void)
136 {
137   if (!cspi_ping (registry))
138           registry = cspi_init ();
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 Accessible *
161 cspi_object_new (CORBA_Object corba_object)
162 {
163   Accessible *ref;
164
165   if (corba_object == CORBA_OBJECT_NIL)
166     {
167       ref = NULL;
168     }
169   else if (!cspi_check_ev ("pre method check: add"))
170     {
171       ref = NULL;
172     }
173   else
174     {
175       ref = malloc (sizeof (Accessible));
176 //    ref->objref = CORBA_Object_duplicate (corba_object, cspi_ev ());
177       ref->objref = corba_object;
178       ref->ref_count = 1;
179     }
180
181   return ref;
182 }
183
184 Accessible *
185 cspi_object_add (CORBA_Object corba_object)
186 {
187         return cspi_object_add_ref (corba_object, FALSE);
188 }
189
190 Accessible *
191 cspi_object_add_ref (CORBA_Object corba_object, gboolean do_ref)
192 {
193   Accessible *ref;
194
195   if (corba_object == CORBA_OBJECT_NIL)
196     {
197       ref = NULL;
198     }
199   else if (!cspi_check_ev ("pre method check: add"))
200     {
201       ref = NULL;
202     }
203   else
204     {
205       if ((ref = g_hash_table_lookup (cspi_get_live_refs (), corba_object)))
206         {
207           g_assert (ref->ref_count > 0);
208           ref->ref_count++;
209           if (!do_ref) 
210                 cspi_release_unref (corba_object);
211 #ifdef DEBUG_OBJECTS
212           g_print ("returning cached %p => %p\n", ref, ref->objref);
213 #endif
214         }
215       else
216         {
217           ref = cspi_object_new (corba_object);
218
219 #ifdef DEBUG_OBJECTS
220           g_print ("allocated %p => %p\n", ref, corba_object);
221 #endif
222           if (do_ref) {
223 #ifdef JAVA_BRIDGE_BUG_IS_FIXED
224                   g_assert (CORBA_Object_is_a (corba_object,
225                                                 "IDL:Bonobo/Unknown:1.0", cspi_ev ()));
226 #endif
227                   Bonobo_Unknown_ref (corba_object, cspi_ev ());
228           }
229           g_hash_table_insert (cspi_get_live_refs (), ref->objref, ref);
230         }
231     }
232
233   return ref;
234 }
235
236 void
237 cspi_object_ref (Accessible *accessible)
238 {
239   g_return_if_fail (accessible != NULL);
240
241   if (g_hash_table_lookup (cspi_get_live_refs (), accessible->objref) == NULL) {
242           accessible->objref = cspi_dup_ref (accessible->objref);
243           g_hash_table_insert (cspi_get_live_refs (), accessible->objref, accessible);
244   } else {
245           accessible->ref_count++;
246   }
247 }
248
249 void
250 cspi_object_unref (Accessible *accessible)
251 {
252   if (accessible == NULL)
253     {
254       return;
255     }
256
257   if (--accessible->ref_count == 0)
258     {
259       g_hash_table_remove (cspi_get_live_refs (), accessible->objref);
260     }
261 }
262
263 static void
264 cspi_cleanup (void)
265 {
266   GHashTable *refs;
267
268   refs = live_refs;
269   live_refs = NULL;
270   if (refs)
271     {
272       g_hash_table_destroy (refs);
273     }
274
275   if (registry != CORBA_OBJECT_NIL)
276     {
277       cspi_release_unref (registry);
278       registry = CORBA_OBJECT_NIL;
279     }
280 }
281
282 static gboolean SPI_inited = FALSE;
283
284 /**
285  * SPI_init:
286  *
287  * Connects to the accessibility registry and initializes the SPI.
288  *
289  * Returns: 0 on success, otherwise an integer error code.
290  **/
291 int
292 SPI_init (void)
293 {
294   if (SPI_inited)
295     {
296       return 1;
297     }
298
299   SPI_inited = TRUE;
300
301   CORBA_exception_init (&ev);
302
303   registry = cspi_init ();
304
305   g_atexit (cspi_cleanup);
306   
307   return 0;
308 }
309
310 /**
311  * SPI_event_main:
312  *
313  * Starts/enters the main event loop for the SPI services.
314  *
315  * (NOTE: This method does not return control, it is exited via a call to
316  *  SPI_event_quit () from within an event handler).
317  *
318  **/
319 void
320 SPI_event_main (void)
321 {
322   cspi_main ();
323 }
324
325 /**
326  * SPI_event_quit:
327  *
328  * Quits the last main event loop for the SPI services,
329  * see SPI_event_main
330  **/
331 void
332 SPI_event_quit (void)
333 {
334   cspi_main_quit ();
335 }
336
337 /**
338  * SPI_eventIsReady:
339  *
340  * Checks to see if an SPI event is waiting in the event queue.
341  * Used by clients that don't wish to use SPI_event_main().
342  *
343  * Not Yet Implemented.
344  *
345  * Returns: #TRUE if an event is waiting, otherwise #FALSE.
346  *
347  **/
348 SPIBoolean
349 SPI_eventIsReady ()
350 {
351   return FALSE;
352 }
353
354 /**
355  * SPI_nextEvent:
356  * @waitForEvent: a #SPIBoolean indicating whether to block or not.
357  *
358  * Gets the next event in the SPI event queue; blocks if no event
359  * is pending and @waitForEvent is #TRUE.
360  * Used by clients that don't wish to use SPI_event_main().
361  *
362  * Not Yet Implemented.
363  *
364  * Returns: the next #AccessibleEvent in the SPI event queue.
365  **/
366 AccessibleEvent *
367 SPI_nextEvent (SPIBoolean waitForEvent)
368 {
369   return NULL;
370 }
371
372 static void
373 report_leaked_ref (gpointer key, gpointer val, gpointer user_data)
374 {
375         Accessible *a = (Accessible *) val;
376         char *name, *role;
377         name = Accessible_getName (a);
378         if (cspi_exception ()) name = NULL;
379         role = Accessible_getRoleName (a);
380         if (cspi_exception ()) role = NULL;
381         fprintf (stderr, "leaked object %s, role %s\n", (name) ? name : "<?>",
382                  (role) ? role : "<?>");
383         if (name) SPI_freeString (name);
384 }
385
386
387 /**
388  * SPI_exit:
389  *
390  * Disconnects from the Accessibility Registry and releases 
391  * any floating resources. Call only once at exit.
392  *
393  * Returns: 0 if there were no leaks, otherwise non zero.
394  **/
395 int
396 SPI_exit (void)
397 {
398   int leaked;
399
400   if (!SPI_inited)
401     {
402       return 0;
403     }
404
405   SPI_inited = FALSE;
406
407   if (live_refs)
408     {
409       leaked = g_hash_table_size (live_refs);
410 #define PRINT_LEAKS
411 #ifdef PRINT_LEAKS
412       g_hash_table_foreach (live_refs, report_leaked_ref, NULL);
413 #endif
414     }
415   else
416     {
417       leaked = 0;
418     }
419
420 #ifdef DEBUG_OBJECTS
421   if (leaked)
422     {
423       fprintf (stderr, "Leaked %d SPI handles\n", leaked);
424     }
425 #endif
426
427   cspi_cleanup ();
428
429   fprintf (stderr, "bye-bye!\n");
430
431   return leaked;
432 }
433
434 /**
435  * SPI_freeString:
436  * @s: a character string returned from another at-spi call.
437  *
438  * Free a character string returned from an at-spi call.  Clients of
439  * at-spi should use this function instead of free () or g_free().
440  * This API should not be used to free strings
441  * from other libraries or allocated by the client.
442  **/
443 void
444 SPI_freeString (char *s)
445 {
446   CORBA_free (s);
447 }