Fix for bug #147648.
[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 #include "spi.h"
35
36 #undef DEBUG_OBJECTS
37
38 static CORBA_Environment ev = { 0 };
39 static Accessibility_Registry registry = CORBA_OBJECT_NIL;
40 static GHashTable *live_refs = NULL;
41 static GQueue *exception_handlers = NULL;
42
43 static guint
44 cspi_object_hash (gconstpointer key)
45 {
46   CORBA_Object object = (CORBA_Object) key;
47
48   return CORBA_Object_hash (object, 0, &ev);
49 }
50
51 static gboolean
52 cspi_object_equal (gconstpointer a, gconstpointer b)
53 {
54   CORBA_Object objecta = (CORBA_Object) a;
55   CORBA_Object objectb = (CORBA_Object) b;
56
57   return CORBA_Object_is_equivalent (objecta, objectb, cspi_ev ());
58 }
59
60 static void
61 cspi_object_release (gpointer value)
62 {
63   Accessible *a = (Accessible *) value;
64
65 #ifdef DEBUG_OBJECTS
66   g_print ("releasing %p => %p\n", a, a->objref);
67 #endif
68
69   if (!a->on_loan)
70     {
71       cspi_release_unref (a->objref);
72     }
73
74   memset (a, 0xaa, sizeof (Accessible));
75   a->ref_count = -1;
76
77 #ifndef DEBUG_OBJECTS
78   free (a);
79 #endif
80 }
81
82 gboolean
83 _cspi_exception_throw (CORBA_Environment *ev, char *desc_prefix)
84 {
85   SPIExceptionHandler *handler = NULL;
86   SPIException ex;
87   if (exception_handlers) handler = g_queue_peek_head (exception_handlers);
88
89   ex.type = SPI_EXCEPTION_SOURCE_UNSPECIFIED;
90   ex.source = CORBA_OBJECT_NIL; /* can we get this from here? */
91   ex.ev = CORBA_exception__copy (ev);
92   switch (ev->_major) {
93   case CORBA_SYSTEM_EXCEPTION:
94     ex.code = SPI_EXCEPTION_UNSPECIFIED;
95     break;
96   case CORBA_USER_EXCEPTION: /* help! how to interpret this? */
97     ex.code = SPI_EXCEPTION_UNSPECIFIED;
98     break;
99   default:
100     ex.code = SPI_EXCEPTION_UNSPECIFIED;
101     break;
102   }
103   
104   if (handler)
105     return (*handler) (&ex, FALSE);
106   else
107     return FALSE; /* means exception was not handled */
108 }
109
110 SPIBoolean
111 cspi_accessible_is_a (Accessible *accessible,
112                       const char *interface_name)
113 {
114   SPIBoolean        retval;
115   Bonobo_Unknown unknown;
116
117   if (accessible == NULL)
118     {
119       return FALSE;
120     }
121
122   unknown = Bonobo_Unknown_queryInterface (CSPI_OBJREF (accessible),
123                                            interface_name, cspi_ev ());
124
125   if (ev._major != CORBA_NO_EXCEPTION)
126     {
127       g_warning ("Exception '%s' checking if is '%s'",
128                  cspi_exception_get_text (),
129                  interface_name);
130       retval = FALSE;
131     }
132
133   else if (unknown != CORBA_OBJECT_NIL)
134     {
135       retval = TRUE;
136       cspi_release_unref (unknown);
137     }
138   else
139     {
140       retval = FALSE;
141     }
142
143   return retval;
144 }
145
146 static GHashTable *
147 cspi_get_live_refs (void)
148 {
149   if (!live_refs) 
150     {
151       live_refs = g_hash_table_new_full (cspi_object_hash,
152                                          cspi_object_equal,
153                                          NULL,
154                                          cspi_object_release);
155     }
156   return live_refs;
157 }
158
159 CORBA_Environment *
160 cspi_peek_ev (void)
161 {
162   return &ev;
163 }
164
165 CORBA_Environment *
166 cspi_ev (void)
167 {
168   CORBA_exception_init (&ev);
169   return &ev;
170 }
171
172 Accessibility_Registry
173 cspi_registry (void)
174 {
175   if (!cspi_ping (registry))
176     {
177       registry = cspi_init ();
178     }
179   return registry;
180 }
181
182 SPIBoolean
183 cspi_exception (void)
184 {
185   SPIBoolean retval;
186
187   if (ev._major != CORBA_NO_EXCEPTION)
188     {
189       CORBA_exception_free (&ev);
190       retval = TRUE;
191     }
192   else
193     {
194       retval = FALSE;
195     }
196
197   return retval;
198 }
199
200 /*
201  *   This method swallows the corba_object BonoboUnknown
202  * reference, and returns an Accessible associated with it.
203  * If the reference is loaned, it means it is only valid
204  * between a borrow / return pair.
205  */
206 static Accessible *
207 cspi_object_get_ref (CORBA_Object corba_object, gboolean on_loan)
208 {
209   Accessible *ref;
210
211   if (corba_object == CORBA_OBJECT_NIL)
212     {
213       ref = NULL;
214     }
215   else if (!cspi_check_ev ("pre method check: add"))
216     {
217       ref = NULL;
218     }
219   else
220     {
221       if ((ref = g_hash_table_lookup (cspi_get_live_refs (), corba_object)))
222         {
223           g_assert (ref->ref_count > 0);
224           ref->ref_count++;
225           if (!on_loan)
226             {
227               if (ref->on_loan) /* Convert to a permanant ref */
228                 {
229                   ref->on_loan = FALSE;
230                 }
231               else
232                 {
233                   cspi_release_unref (corba_object);
234                 }
235             }
236 #ifdef DEBUG_OBJECTS
237           g_print ("returning cached %p => %p\n", ref, ref->objref);
238 #endif
239         }
240       else
241         {
242           ref = malloc (sizeof (Accessible));
243           ref->objref = corba_object;
244           ref->ref_count = 1;
245           ref->on_loan = on_loan;
246 #ifdef DEBUG_OBJECTS
247           g_print ("allocated %p => %p\n", ref, corba_object);
248 #endif
249           g_hash_table_insert (cspi_get_live_refs (), ref->objref, ref);
250         }
251     }
252
253   return ref;
254 }
255
256 Accessible *
257 cspi_object_add (CORBA_Object corba_object)
258 {
259   return cspi_object_get_ref (corba_object, FALSE);
260 }
261
262 Accessible *
263 cspi_object_borrow (CORBA_Object corba_object)
264 {
265   return cspi_object_get_ref (corba_object, TRUE);
266 }
267
268 void
269 cspi_object_return (Accessible *accessible)
270 {
271   int old_ref_count;
272   g_return_if_fail (accessible != NULL);
273
274   if (!accessible->on_loan ||
275       accessible->ref_count == 1)
276     {
277       cspi_object_unref (accessible);
278     }
279   else /* Convert to a permanant ref */
280     {
281       accessible->on_loan = FALSE;
282       old_ref_count = accessible->ref_count;
283       accessible->objref = cspi_dup_ref (accessible->objref);
284       if (old_ref_count != accessible->ref_count &&
285           accessible->ref_count == 1)
286         {
287             cspi_object_unref (accessible);
288         }
289       else    
290         {
291           accessible->ref_count--;
292         }
293     }
294 }
295
296 Accessible *
297 cspi_object_take (CORBA_Object corba_object)
298 {
299   Accessible *accessible;
300   accessible = cspi_object_borrow (corba_object);
301
302   cspi_object_ref (accessible);
303   /* 
304    * if the remote object is dead, 
305    * cspi_object_return will throw an exception. 
306    * FIXME: what clears that exception context ever ?
307    */
308   cspi_object_return (accessible);
309   if (cspi_exception ()) 
310     {
311       cspi_object_unref (accessible);
312       accessible = NULL;
313     }
314   return accessible;
315 }
316
317 void
318 cspi_object_ref (Accessible *accessible)
319 {
320   g_return_if_fail (accessible != NULL);
321
322   accessible->ref_count++;
323 }
324
325 void
326 cspi_object_unref (Accessible *accessible)
327 {
328   if (accessible == NULL)
329     {
330       return;
331     }
332
333   g_return_if_fail (accessible->ref_count > 0);
334   if (--accessible->ref_count == 0)
335     {
336       g_hash_table_remove (cspi_get_live_refs (), accessible->objref);
337     }
338 }
339
340 static void
341 cspi_cleanup (void)
342 {
343   GHashTable *refs;
344
345   cspi_streams_close_all ();
346
347   refs = live_refs;
348   live_refs = NULL;
349   if (refs)
350     {
351       g_hash_table_destroy (refs);
352     }
353
354   if (registry != CORBA_OBJECT_NIL)
355     {
356       cspi_release_unref (registry);
357       registry = CORBA_OBJECT_NIL;
358     }
359 }
360
361 static gboolean SPI_inited = FALSE;
362
363 /**
364  * SPI_init:
365  *
366  * Connects to the accessibility registry and initializes the SPI.
367  *
368  * Returns: 0 on success, otherwise an integer error code.  
369  **/
370 int
371 SPI_init (void)
372 {
373   if (SPI_inited)
374     {
375       return 1;
376     }
377
378   SPI_inited = TRUE;
379
380   CORBA_exception_init (&ev);
381
382   registry = cspi_init ();
383
384   g_atexit (cspi_cleanup);
385
386   /* fprintf (stderr, "registry=%x\n", (int) registry); */
387
388   if ((registry != CORBA_OBJECT_NIL) && (cspi_ping (registry)))
389       return 0;
390   else
391       return 2;
392 }
393
394 /**
395  * SPI_event_main:
396  *
397  * Starts/enters the main event loop for the SPI services.
398  *
399  * (NOTE: This method does not return control, it is exited via a call to
400  *  SPI_event_quit () from within an event handler).
401  *
402  **/
403 void
404 SPI_event_main (void)
405 {
406   cspi_main ();
407 }
408
409 /**
410  * SPI_event_quit:
411  *
412  * Quits the last main event loop for the SPI services,
413  * see SPI_event_main
414  **/
415 void
416 SPI_event_quit (void)
417 {
418   cspi_main_quit ();
419 }
420
421 /**
422  * SPI_eventIsReady:
423  *
424  * Checks to see if an SPI event is waiting in the event queue.
425  * Used by clients that don't wish to use SPI_event_main().
426  *
427  * Not Yet Implemented.
428  *
429  * Returns: #TRUE if an event is waiting, otherwise #FALSE.
430  *
431  **/
432 SPIBoolean
433 SPI_eventIsReady ()
434 {
435   return FALSE;
436 }
437
438 /**
439  * SPI_nextEvent:
440  * @waitForEvent: a #SPIBoolean indicating whether to block or not.
441  *
442  * Gets the next event in the SPI event queue; blocks if no event
443  * is pending and @waitForEvent is #TRUE.
444  * Used by clients that don't wish to use SPI_event_main().
445  *
446  * Not Yet Implemented.
447  *
448  * Returns: the next #AccessibleEvent in the SPI event queue.
449  **/
450 AccessibleEvent *
451 SPI_nextEvent (SPIBoolean waitForEvent)
452 {
453   return NULL;
454 }
455
456 static void
457 report_leaked_ref (gpointer key, gpointer val, gpointer user_data)
458 {
459   char *name, *role;
460   Accessible *a = (Accessible *) val;
461   
462   name = Accessible_getName (a);
463   if (cspi_exception ())
464     {
465       name = NULL;
466     }
467
468   role = Accessible_getRoleName (a);
469   if (cspi_exception ())
470     {
471       role = NULL;
472     }
473
474   fprintf (stderr, "leaked %d references to object %s, role %s %p\n",
475            a->ref_count, name ? name : "<?>", role ? role : "<?>", a);
476
477   SPI_freeString (name);
478 }
479
480
481 /**
482  * SPI_exit:
483  *
484  * Disconnects from the Accessibility Registry and releases 
485  * any floating resources. Call only once at exit.
486  *
487  * Returns: 0 if there were no leaks, otherwise non zero.
488  **/
489 int
490 SPI_exit (void)
491 {
492   int leaked;
493
494   if (!SPI_inited)
495     {
496       return 0;
497     }
498
499   SPI_inited = FALSE;
500
501   if (live_refs)
502     {
503       leaked = g_hash_table_size (live_refs);
504 #ifdef DEBUG_OBJECTS
505       fprintf (stderr, "Leaked %d SPI handles\n", leaked);
506
507 #define PRINT_LEAKS
508 #ifdef PRINT_LEAKS
509       g_hash_table_foreach (live_refs, report_leaked_ref, NULL);
510 #endif
511
512 #endif
513     }
514   else
515     {
516       leaked = 0;
517     }
518
519   cspi_cleanup ();
520
521   return leaked;
522 }
523
524 /**
525  * SPI_freeString:
526  * @s: a character string returned from another at-spi call.
527  *
528  * Free a character string returned from an at-spi call.  Clients of
529  * at-spi should use this function instead of free () or g_free().
530  * A NULL string @s will be silently ignored.
531  * This API should not be used to free strings
532  * from other libraries or allocated by the client.
533  **/
534 void
535 SPI_freeString (char *s)
536 {
537   if (s)
538     {
539       CORBA_free (s);
540     }
541 }
542
543 /**
544  * SPI_freeRect:
545  * @r: a pointer to an SPIRect returned from another at-spi call.
546  *
547  * Free a SPIRect structure returned from an at-spi call.  Clients of
548  * at-spi should use this function instead of free () or g_free().
549  * A NULL rect @r will be silently ignored.
550  * This API should not be used to free data
551  * from other libraries or allocated by the client.
552  **/
553 void
554 SPI_freeRect (SPIRect *r)
555 {
556   if (r)
557     {
558       /* err, okay, in this case the client _could_ 
559          have called g_free, but we don't want to guarantee it */
560       g_free (r);
561     }
562 }
563
564 /**
565  * DOCUMENT_ME!
566  **/
567 char *
568 SPI_dupString (char *s)
569 {
570   if (s)
571     {
572       return CORBA_string_dup (s);
573     }
574   else 
575     return NULL;
576 }
577
578 /**
579  * DOCUMENT_ME!
580  **/
581 SPIBoolean SPI_exceptionHandlerPush (SPIExceptionHandler *handler)
582 {
583   if (!exception_handlers)
584     exception_handlers = g_queue_new ();
585   g_queue_push_head (exception_handlers, handler);
586   return TRUE;
587 }
588
589 /**
590  * DOCUMENT_ME!
591  **/
592 SPIExceptionHandler* SPI_exceptionHandlerPop (void)
593 {
594   return (SPIExceptionHandler *) g_queue_pop_head (exception_handlers);
595 }
596
597 /**
598  * DOCUMENT_ME!
599  **/
600 SPIExceptionType SPIException_getSourceType (SPIException *err)
601 {
602   if (err)
603     return err->type;
604   else
605     return SPI_EXCEPTION_SOURCE_UNSPECIFIED;
606 }
607
608 /**
609  * DOCUMENT_ME!
610  **/
611 SPIExceptionCode SPIException_getExceptionCode (SPIException *err)
612 {  
613   return err->code;
614 }
615
616 /**
617  * DOCUMENT_ME!
618  **/
619 Accessible* SPIAccessibleException_getSource (SPIException *err)
620 {
621   if (err->type == SPI_EXCEPTION_SOURCE_ACCESSIBLE)
622     return cspi_object_get_ref (err->source, FALSE);
623   return NULL;
624 }
625
626 /**
627  * DOCUMENT_ME!
628  **/
629 char* SPIException_getDescription (SPIException *err)
630 {
631   /* TODO: friendlier error messages? */
632   if (err->ev)
633     return CORBA_exception_id (err->ev);
634   return NULL;
635 }