Hooked up StreamableContent. Fix for #78890.
[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_ev (void)
161 {
162   CORBA_exception_init (&ev);
163   return &ev;
164 }
165
166 Accessibility_Registry
167 cspi_registry (void)
168 {
169   if (!cspi_ping (registry))
170     {
171       registry = cspi_init ();
172     }
173   return registry;
174 }
175
176 SPIBoolean
177 cspi_exception (void)
178 {
179   SPIBoolean retval;
180
181   if (ev._major != CORBA_NO_EXCEPTION)
182     {
183       CORBA_exception_free (&ev);
184       retval = TRUE;
185     }
186   else
187     {
188       retval = FALSE;
189     }
190
191   return retval;
192 }
193
194 /*
195  *   This method swallows the corba_object BonoboUnknown
196  * reference, and returns an Accessible associated with it.
197  * If the reference is loaned, it means it is only valid
198  * between a borrow / return pair.
199  */
200 static Accessible *
201 cspi_object_get_ref (CORBA_Object corba_object, gboolean on_loan)
202 {
203   Accessible *ref;
204
205   if (corba_object == CORBA_OBJECT_NIL)
206     {
207       ref = NULL;
208     }
209   else if (!cspi_check_ev ("pre method check: add"))
210     {
211       ref = NULL;
212     }
213   else
214     {
215       if ((ref = g_hash_table_lookup (cspi_get_live_refs (), corba_object)))
216         {
217           g_assert (ref->ref_count > 0);
218           ref->ref_count++;
219           if (!on_loan)
220             {
221               if (ref->on_loan) /* Convert to a permanant ref */
222                 {
223                   ref->on_loan = FALSE;
224                 }
225               else
226                 {
227                   cspi_release_unref (corba_object);
228                 }
229             }
230 #ifdef DEBUG_OBJECTS
231           g_print ("returning cached %p => %p\n", ref, ref->objref);
232 #endif
233         }
234       else
235         {
236           ref = malloc (sizeof (Accessible));
237           ref->objref = corba_object;
238           ref->ref_count = 1;
239           ref->on_loan = on_loan;
240 #ifdef DEBUG_OBJECTS
241           g_print ("allocated %p => %p\n", ref, corba_object);
242 #endif
243           g_hash_table_insert (cspi_get_live_refs (), ref->objref, ref);
244         }
245     }
246
247   return ref;
248 }
249
250 Accessible *
251 cspi_object_add (CORBA_Object corba_object)
252 {
253   return cspi_object_get_ref (corba_object, FALSE);
254 }
255
256 Accessible *
257 cspi_object_borrow (CORBA_Object corba_object)
258 {
259   return cspi_object_get_ref (corba_object, TRUE);
260 }
261
262 void
263 cspi_object_return (Accessible *accessible)
264 {
265   g_return_if_fail (accessible != NULL);
266
267   if (!accessible->on_loan ||
268       accessible->ref_count == 1)
269     {
270       cspi_object_unref (accessible);
271     }
272   else /* Convert to a permanant ref */
273     {
274       accessible->on_loan = FALSE;
275       accessible->objref = cspi_dup_ref (accessible->objref);
276       accessible->ref_count--;
277     }
278 }
279
280 Accessible *
281 cspi_object_take (CORBA_Object corba_object)
282 {
283   Accessible *accessible;
284   accessible = cspi_object_borrow (corba_object);
285
286   cspi_object_ref (accessible);
287   /* 
288    * if the remote object is dead, 
289    * cspi_object_return will throw an exception. 
290    * FIXME: what clears that exception context ever ?
291    */
292   cspi_object_return (accessible);
293   if (cspi_exception ()) 
294     {
295       cspi_object_unref (accessible);
296       accessible = NULL;
297     }
298   return accessible;
299 }
300
301 void
302 cspi_object_ref (Accessible *accessible)
303 {
304   g_return_if_fail (accessible != NULL);
305
306   accessible->ref_count++;
307 }
308
309 void
310 cspi_object_unref (Accessible *accessible)
311 {
312   if (accessible == NULL)
313     {
314       return;
315     }
316
317   if (--accessible->ref_count == 0)
318     {
319       g_hash_table_remove (cspi_get_live_refs (), accessible->objref);
320     }
321 }
322
323 static void
324 cspi_cleanup (void)
325 {
326   GHashTable *refs;
327
328   cspi_streams_close_all ();
329
330   refs = live_refs;
331   live_refs = NULL;
332   if (refs)
333     {
334       g_hash_table_destroy (refs);
335     }
336
337   if (registry != CORBA_OBJECT_NIL)
338     {
339       cspi_release_unref (registry);
340       registry = CORBA_OBJECT_NIL;
341     }
342 }
343
344 static gboolean SPI_inited = FALSE;
345
346 /**
347  * SPI_init:
348  *
349  * Connects to the accessibility registry and initializes the SPI.
350  *
351  * Returns: 0 on success, otherwise an integer error code.
352  **/
353 int
354 SPI_init (void)
355 {
356   if (SPI_inited)
357     {
358       return 1;
359     }
360
361   SPI_inited = TRUE;
362
363   CORBA_exception_init (&ev);
364
365   registry = cspi_init ();
366
367   g_atexit (cspi_cleanup);
368   
369   return 0;
370 }
371
372 /**
373  * SPI_event_main:
374  *
375  * Starts/enters the main event loop for the SPI services.
376  *
377  * (NOTE: This method does not return control, it is exited via a call to
378  *  SPI_event_quit () from within an event handler).
379  *
380  **/
381 void
382 SPI_event_main (void)
383 {
384   cspi_main ();
385 }
386
387 /**
388  * SPI_event_quit:
389  *
390  * Quits the last main event loop for the SPI services,
391  * see SPI_event_main
392  **/
393 void
394 SPI_event_quit (void)
395 {
396   cspi_main_quit ();
397 }
398
399 /**
400  * SPI_eventIsReady:
401  *
402  * Checks to see if an SPI event is waiting in the event queue.
403  * Used by clients that don't wish to use SPI_event_main().
404  *
405  * Not Yet Implemented.
406  *
407  * Returns: #TRUE if an event is waiting, otherwise #FALSE.
408  *
409  **/
410 SPIBoolean
411 SPI_eventIsReady ()
412 {
413   return FALSE;
414 }
415
416 /**
417  * SPI_nextEvent:
418  * @waitForEvent: a #SPIBoolean indicating whether to block or not.
419  *
420  * Gets the next event in the SPI event queue; blocks if no event
421  * is pending and @waitForEvent is #TRUE.
422  * Used by clients that don't wish to use SPI_event_main().
423  *
424  * Not Yet Implemented.
425  *
426  * Returns: the next #AccessibleEvent in the SPI event queue.
427  **/
428 AccessibleEvent *
429 SPI_nextEvent (SPIBoolean waitForEvent)
430 {
431   return NULL;
432 }
433
434 static void
435 report_leaked_ref (gpointer key, gpointer val, gpointer user_data)
436 {
437   char *name, *role;
438   Accessible *a = (Accessible *) val;
439   
440   name = Accessible_getName (a);
441   if (cspi_exception ())
442     {
443       name = NULL;
444     }
445
446   role = Accessible_getRoleName (a);
447   if (cspi_exception ())
448     {
449       role = NULL;
450     }
451
452   fprintf (stderr, "leaked %d references to object %s, role %s %p\n",
453            a->ref_count, name ? name : "<?>", role ? role : "<?>", a);
454
455   SPI_freeString (name);
456 }
457
458
459 /**
460  * SPI_exit:
461  *
462  * Disconnects from the Accessibility Registry and releases 
463  * any floating resources. Call only once at exit.
464  *
465  * Returns: 0 if there were no leaks, otherwise non zero.
466  **/
467 int
468 SPI_exit (void)
469 {
470   int leaked;
471
472   if (!SPI_inited)
473     {
474       return 0;
475     }
476
477   SPI_inited = FALSE;
478
479   if (live_refs)
480     {
481       leaked = g_hash_table_size (live_refs);
482 #define PRINT_LEAKS
483 #ifdef PRINT_LEAKS
484       g_hash_table_foreach (live_refs, report_leaked_ref, NULL);
485 #endif
486     }
487   else
488     {
489       leaked = 0;
490     }
491
492 #ifdef DEBUG_OBJECTS
493   if (leaked)
494     {
495       fprintf (stderr, "Leaked %d SPI handles\n", leaked);
496     }
497 #endif
498
499   cspi_cleanup ();
500
501   fprintf (stderr, "bye-bye!\n");
502
503   return leaked;
504 }
505
506 /**
507  * SPI_freeString:
508  * @s: a character string returned from another at-spi call.
509  *
510  * Free a character string returned from an at-spi call.  Clients of
511  * at-spi should use this function instead of free () or g_free().
512  * A NULL string @s will be silently ignored.
513  * This API should not be used to free strings
514  * from other libraries or allocated by the client.
515  **/
516 void
517 SPI_freeString (char *s)
518 {
519   if (s)
520     {
521       CORBA_free (s);
522     }
523 }
524
525 /**
526  * DOCUMENT_ME!
527  **/
528 char *
529 SPI_dupString (char *s)
530 {
531   if (s)
532     {
533       return CORBA_string_dup (s);
534     }
535   else 
536     return NULL;
537 }
538
539 /**
540  * DOCUMENT_ME!
541  **/
542 SPIBoolean SPI_exceptionHandlerPush (SPIExceptionHandler *handler)
543 {
544   if (!exception_handlers)
545     exception_handlers = g_queue_new ();
546   g_queue_push_head (exception_handlers, handler);
547   return TRUE;
548 }
549
550 /**
551  * DOCUMENT_ME!
552  **/
553 SPIExceptionHandler* SPI_exceptionHandlerPop (void)
554 {
555   return (SPIExceptionHandler *) g_queue_pop_head (exception_handlers);
556 }
557
558 /**
559  * DOCUMENT_ME!
560  **/
561 SPIExceptionType SPIException_getSourceType (SPIException *err)
562 {
563   if (err)
564     return err->type;
565   else
566     return SPI_EXCEPTION_UNSPECIFIED;
567 }
568
569 /**
570  * DOCUMENT_ME!
571  **/
572 SPIExceptionCode SPIException_getExceptionCode (SPIException *err)
573 {  
574   return err->code;
575 }
576
577 /**
578  * DOCUMENT_ME!
579  **/
580 Accessible* SPIAccessibleException_getSource (SPIException *err)
581 {
582   if (err->type == SPI_EXCEPTION_SOURCE_ACCESSIBLE)
583     return cspi_object_get_ref (err->source, FALSE);
584   return NULL;
585 }
586
587 /**
588  * DOCUMENT_ME!
589  **/
590 char* SPIException_getDescription (SPIException *err)
591 {
592   /* TODO: friendlier error messages? */
593   if (err->ev)
594     return CORBA_exception_id (err->ev);
595   return NULL;
596 }