70bb7f577585f130ec7476f11bc8b3cc0536a9ec
[platform/core/uifw/at-spi2-atk.git] / registryd / registry.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 /* registry.c: the main accessibility service registry implementation */
25
26 #undef SPI_LISTENER_DEBUG
27 #undef SPI_DEBUG
28 #undef SPI_QUEUE_DEBUG
29
30 #include <config.h>
31 #ifdef SPI_DEBUG
32 #  include <stdio.h>
33 #endif
34
35 #include <bonobo/bonobo-exception.h>
36 #include "../libspi/spi-private.h"
37 #include "registry.h"
38
39 /* Our parent GObject type  */
40 #define PARENT_TYPE SPI_LISTENER_TYPE
41
42 /* A pointer to our parent object class */
43 static SpiListenerClass *spi_registry_parent_class;
44
45 static GQuark _deactivate_quark = 0;
46 static GQuark _activate_quark = 0;
47 static GQuark _state_quark = 0;
48
49 int _dbg = 0;
50
51 typedef enum {
52   ETYPE_FOCUS,
53   ETYPE_OBJECT,
54   ETYPE_PROPERTY,
55   ETYPE_WINDOW,
56   ETYPE_TOOLKIT,
57   ETYPE_KEYBOARD,
58   ETYPE_MOUSE,
59   ETYPE_LAST_DEFINED
60 } EventTypeCategory;
61
62 typedef struct {
63   const char *event_name;
64   EventTypeCategory type_cat;
65   GQuark major;  /* from string segment[1] */
66   GQuark minor;  /* from string segment[1]+segment[2] */
67   GQuark detail; /* from string segment[3] (not concatenated) */
68 } EventTypeStruct;
69
70 typedef struct {
71   Accessibility_EventListener listener;
72   GQuark            event_type_quark;
73   EventTypeCategory event_type_cat;
74 } SpiListenerStruct;
75
76 static void
77 spi_registry_set_debug (const char *debug_flag_string)
78 {
79   if (debug_flag_string) 
80     _dbg = (int) g_ascii_strtod (debug_flag_string, NULL);
81 }
82
83 SpiListenerStruct *
84 spi_listener_struct_new (Accessibility_EventListener listener, CORBA_Environment *ev)
85 {
86   SpiListenerStruct *retval = g_malloc (sizeof (SpiListenerStruct));
87   retval->listener = bonobo_object_dup_ref (listener, ev);
88   return retval;
89 }
90
91
92 void
93 spi_listener_struct_free (SpiListenerStruct *ls, CORBA_Environment *ev)
94 {
95   bonobo_object_release_unref (ls->listener, ev);
96   g_free (ls);
97 }
98
99 static void
100 desktop_add_application (SpiDesktop *desktop,
101                          guint index, gpointer data)
102 {
103   BonoboObject *registry = BONOBO_OBJECT (data);
104   Accessibility_Event e;
105   CORBA_Environment ev;
106   Accessibility_Accessible a;
107   
108   CORBA_exception_init (&ev);
109   e.type = "object:children-changed:add";
110   e.source = BONOBO_OBJREF (desktop);
111   e.detail1 = index;
112   e.detail2 = 0;
113   a = Accessibility_Accessible_getChildAtIndex (BONOBO_OBJREF (desktop), 
114                                                 index, &ev);
115   /* FIXME
116   spi_init_any_object (&e.any_data, a);
117   */
118   spi_init_any_nil (&e.any_data);
119   Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
120                                       &e, &ev);
121   bonobo_object_release_unref (a, &ev);
122   CORBA_exception_free (&ev);
123 }
124
125
126
127 static void
128 desktop_remove_application (SpiDesktop *desktop,
129                             guint index, gpointer data)
130 {
131   BonoboObject *registry = BONOBO_OBJECT (data);
132   Accessibility_Event e;
133   Accessibility_Accessible a;
134   CORBA_Environment ev;
135   
136   CORBA_exception_init (&ev);
137
138   e.type = "object:children-changed:remove";
139   e.source = BONOBO_OBJREF (desktop);
140   e.detail1 = index;
141   e.detail2 = 0;
142   a = Accessibility_Accessible_getChildAtIndex (BONOBO_OBJREF (desktop), 
143                                                 index, &ev);
144   /* FIXME
145   spi_init_any_object (&e.any_data, a);
146   */
147   spi_init_any_nil (&e.any_data);
148   Accessibility_Accessible_unref (a, &ev);
149   Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
150                                       &e, &ev);
151   Accessibility_Desktop_unref (e.source, &ev);
152   CORBA_exception_free (&ev);
153 }
154
155
156 static void
157 spi_registry_object_finalize (GObject *object)
158 {
159   DBG (1, g_warning ("spi_registry_object_finalize called\n"));
160   g_object_unref (SPI_REGISTRY (object)->de_controller);
161
162   G_OBJECT_CLASS (spi_registry_parent_class)->finalize (object);
163 }
164
165 static long
166 _get_unique_id (void)
167 {
168   static long id = 0;
169
170   return ++id;
171 }
172
173 /**
174  * registerApplication:
175  * @application: a reference to the requesting @Application
176  * return values: void
177  *
178  * Register a new application with the accessibility broker.
179  *
180  **/
181 static void
182 impl_accessibility_registry_register_application (PortableServer_Servant servant,
183                                                   const Accessibility_Application application,
184                                                   CORBA_Environment * ev)
185 {
186   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
187
188 #ifdef SPI_DEBUG
189   fprintf (stderr, "registering app %p\n", application);
190 #endif
191   spi_desktop_add_application (registry->desktop, application);
192
193   Accessibility_Application__set_id (application, _get_unique_id (), ev);
194
195   /*
196    * TODO: change the implementation below to a WM-aware one;
197    * e.g. don't add all apps to the SpiDesktop
198    */
199 }
200
201 #ifdef USE_A_HASH_IN_FUTURE
202 static gint
203 compare_corba_objects (gconstpointer p1, gconstpointer p2)
204 {
205   CORBA_Environment ev;
206   gint retval;
207
208 #ifdef SPI_DEBUG
209   fprintf (stderr, "comparing %p to %p\n",
210            p1, p2);
211 #endif
212   
213   retval = !CORBA_Object_is_equivalent ((CORBA_Object) p1, (CORBA_Object) p2, &ev);
214   return retval;  
215 }
216 #endif
217
218 static void
219 register_with_toolkits (SpiRegistry *spi_registry_bonobo_object, EventTypeStruct *etype, CORBA_Environment *ev)
220 {
221   gint n_desktops;
222   gint n_apps;
223   gint i, j;
224   Accessibility_Desktop desktop;
225   Accessibility_Application app;
226   Accessibility_Registry registry;
227   registry  = BONOBO_OBJREF (spi_registry_bonobo_object);
228
229   /* for each app in each desktop, call ...Application_registerToolkitEventListener */
230
231   n_desktops = Accessibility_Registry_getDesktopCount (registry, ev);
232
233   for (i=0; i<n_desktops; ++i)
234     {
235       desktop = Accessibility_Registry_getDesktop (registry, i, ev);
236       n_apps = Accessibility_Desktop__get_childCount (desktop, ev);
237       for (j=0; j<n_apps; ++j)
238         {
239           app = (Accessibility_Application) Accessibility_Desktop_getChildAtIndex (desktop,
240                                                                                    j,
241                                                                                    ev);
242           Accessibility_Application_registerToolkitEventListener (app,
243                                                                   registry,
244                                                                   CORBA_string_dup (etype->event_name),
245                                                                   ev);
246         }
247     }
248 }
249
250 #ifdef USE_A_HASH_IN_FUTURE
251
252 static gint
253 compare_listener_quarks (gconstpointer p1, gconstpointer p2)
254 {
255         return (((SpiListenerStruct *)p2)->event_type_quark !=
256                 ((SpiListenerStruct *)p1)->event_type_quark);
257 }
258
259 static gint
260 compare_listener_corbaref (gconstpointer p1, gconstpointer p2)
261 {
262   return compare_corba_objects (((SpiListenerStruct *)p2)->listener,
263                                 ((SpiListenerStruct *)p1)->listener);
264 }
265 #endif
266
267 static void
268 parse_event_type (EventTypeStruct *etype, const char *event_name)
269 {
270   gchar **split_string;
271   gchar *s;
272
273   split_string = g_strsplit (event_name, ":", 4);
274   etype->event_name = event_name;
275
276   if (!g_ascii_strncasecmp (event_name, "focus:", 6))
277     {
278       etype->type_cat = ETYPE_FOCUS;
279     }
280   else if (!g_ascii_strncasecmp (event_name, "mouse:", 6))
281     {
282       etype->type_cat = ETYPE_MOUSE;
283     }
284   else if (!g_ascii_strncasecmp (event_name, "object:", 7))
285     {
286       etype->type_cat = ETYPE_OBJECT;
287     }
288   else if (!g_ascii_strncasecmp (event_name, "window:", 7))
289     {
290       etype->type_cat = ETYPE_WINDOW;
291     }
292   else if (!g_ascii_strncasecmp (event_name, "keyboard:", 9))
293     {
294       etype->type_cat = ETYPE_KEYBOARD;
295     }
296   else
297     {
298       etype->type_cat = ETYPE_TOOLKIT;
299     }
300
301   if (split_string[1])
302     {
303       etype->major = g_quark_from_string (split_string[1]);
304       if (split_string[2])
305         {
306           etype->minor = g_quark_from_string (s = g_strconcat (split_string[1], split_string[2], NULL));
307           g_free (s);
308           if (split_string[3])
309             {
310               etype->detail = g_quark_from_string (split_string[3]);
311             }
312           else
313             {
314               etype->detail = g_quark_from_static_string ("");
315             }
316         }
317       else
318         {
319           etype->minor = etype->major;
320           etype->detail = g_quark_from_static_string (""); //etype->major;
321         }
322     }
323   else
324     {
325       etype->major = g_quark_from_static_string ("");
326       etype->minor = etype->major;
327       etype->detail = etype->major;
328     }
329
330   g_strfreev (split_string);
331 }
332
333 /**
334  * deregisterApplication:
335  * @application: a reference to the @Application
336  * to be deregistered.
337  * return values: void
338  *
339  * De-register an application previously registered with the broker.
340  *
341  **/
342 static void
343 impl_accessibility_registry_deregister_application (PortableServer_Servant servant,
344                                                     const Accessibility_Application application,
345                                                     CORBA_Environment * ev)
346 {
347   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
348
349   spi_desktop_remove_application (registry->desktop, application);
350
351 #ifdef SPI_DEBUG
352   fprintf (stderr, "de-registered app %p\n", application);
353 #endif
354 }
355
356 static GList **
357 get_listener_list (SpiRegistry      *registry,
358                    EventTypeCategory cat)
359 {
360   GList **ret;
361   
362   switch (cat)
363     {
364       case ETYPE_OBJECT:
365       case ETYPE_PROPERTY:
366       case ETYPE_FOCUS:
367       case ETYPE_KEYBOARD:
368         ret = &registry->object_listeners;
369         break;
370       case ETYPE_WINDOW:
371         ret = &registry->window_listeners;
372         break;
373       case ETYPE_MOUSE:
374       case ETYPE_TOOLKIT:
375         ret = &registry->toolkit_listeners;
376         break;
377       default:
378         ret = NULL;
379         break;
380     }
381   return ret;
382 }
383
384 /*
385  * CORBA Accessibility::Registry::registerGlobalEventListener method implementation
386  */
387 static void
388 impl_accessibility_registry_register_global_event_listener (
389         PortableServer_Servant      servant,
390         Accessibility_EventListener listener,
391         const CORBA_char           *event_name,
392         CORBA_Environment          *ev)
393 {
394   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); 
395   SpiListenerStruct *ls = spi_listener_struct_new (listener, ev);
396   EventTypeStruct etype;
397   GList          **list;
398
399 #ifdef SPI_LISTENER_DEBUG
400   fprintf (stderr, "registering for events of type %s\n", event_name);
401 #endif
402
403   /* parse, check major event type and add listener accordingly */
404   parse_event_type (&etype, event_name);
405   ls->event_type_quark = etype.minor;
406   ls->event_type_cat = etype.type_cat;
407
408   list = get_listener_list (registry, etype.type_cat);
409
410   if (list)
411     {
412       *list = g_list_prepend (*list, ls);
413
414       if (etype.type_cat == ETYPE_TOOLKIT)
415         {
416           register_with_toolkits (registry, &etype, ev);
417         }
418     }
419   else
420     {
421       spi_listener_struct_free (ls, ev);
422     }
423 }
424
425 static SpiReEntrantContinue
426 remove_listener_cb (GList * const *list, gpointer user_data)
427 {
428   SpiListenerStruct *ls = (SpiListenerStruct *) (*list)->data;
429   CORBA_Environment  ev;
430   Accessibility_EventListener listener = user_data;
431
432   CORBA_exception_init (&ev);
433         
434   if (CORBA_Object_is_equivalent (ls->listener, listener, &ev))
435     {
436        spi_re_entrant_list_delete_link (list);
437        spi_listener_struct_free (ls, &ev);
438     }
439
440   CORBA_exception_free (&ev);
441
442   return SPI_RE_ENTRANT_CONTINUE;
443 }
444
445 /*
446  * CORBA Accessibility::Registry::deregisterGlobalEventListenerAll method implementation
447  */
448 static void
449 impl_accessibility_registry_deregister_global_event_listener_all (
450         PortableServer_Servant      servant,
451         Accessibility_EventListener listener,
452         CORBA_Environment          *ev)
453 {
454   int i;
455   GList **lists[3];
456   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
457
458   lists[0] = &registry->object_listeners;
459   lists[1] = &registry->window_listeners;
460   lists[2] = &registry->toolkit_listeners;
461
462   for (i = 0; i < sizeof (lists) / sizeof (lists[0]); i++)
463     {
464       spi_re_entrant_list_foreach (lists [i], remove_listener_cb, listener);
465     }
466 }
467
468
469 /*
470  * CORBA Accessibility::Registry::deregisterGlobalEventListener method implementation
471  */
472 static void
473 impl_accessibility_registry_deregister_global_event_listener (
474         PortableServer_Servant      servant,
475         Accessibility_EventListener listener,
476         const CORBA_char           *event_name,
477         CORBA_Environment          *ev)
478 {
479   SpiRegistry    *registry;
480   EventTypeStruct etype;
481
482   registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
483
484   parse_event_type (&etype, (char *) event_name);
485
486   spi_re_entrant_list_foreach (get_listener_list (registry, etype.type_cat),
487                                 remove_listener_cb, listener);
488 }
489
490
491 /**
492  * getDesktopCount:
493  * return values: a short integer indicating the current number of
494  * @Desktops.
495  *
496  * Get the current number of desktops.
497  *
498  **/
499 static short
500 impl_accessibility_registry_get_desktop_count (PortableServer_Servant servant,
501                                                CORBA_Environment * ev)
502 {
503   /* TODO: implement support for multiple virtual desktops */
504   CORBA_short n_desktops;
505   n_desktops = (CORBA_short) 1;
506   return n_desktops;
507 }
508
509
510 /**
511  * getDesktop:
512  * @n: the index of the requested @Desktop.
513  * return values: a reference to the requested @Desktop.
514  *
515  * Get the nth accessible desktop.
516  *
517  **/
518 static Accessibility_Desktop
519 impl_accessibility_registry_get_desktop (PortableServer_Servant servant,
520                                          const CORBA_short n,
521                                          CORBA_Environment * ev)
522 {
523   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
524
525   /* TODO: implement support for multiple virtual desktops */
526   if (n == 0)
527     {
528       return (Accessibility_Desktop)
529         bonobo_object_dup_ref (BONOBO_OBJREF (registry->desktop), ev);
530     }
531   else
532     {
533       return (Accessibility_Desktop) CORBA_OBJECT_NIL;
534     }
535 }
536
537
538 /**
539  * getDesktopList:
540  * return values: a sequence containing references to
541  * the @Desktops.
542  *
543  * Get a list of accessible desktops.
544  *
545  **/
546 static Accessibility_DesktopSeq *
547 impl_accessibility_registry_get_desktop_list (PortableServer_Servant servant,
548                                               CORBA_Environment * ev)
549 {
550   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
551   Accessibility_DesktopSeq *desktops;
552
553   desktops = Accessibility_DesktopSeq__alloc ();
554   desktops->_length = desktops->_maximum = 1;
555   desktops->_buffer = Accessibility_DesktopSeq_allocbuf (desktops->_length);
556   desktops->_buffer [0] = bonobo_object_dup_ref (BONOBO_OBJREF (registry->desktop), ev);
557
558   return desktops;
559 }
560
561
562 static Accessibility_DeviceEventController
563 impl_accessibility_registry_get_device_event_controller (PortableServer_Servant servant,
564                                                          CORBA_Environment     *ev)
565 {
566   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
567
568   if (!registry->de_controller)
569     {
570       registry->de_controller = spi_device_event_controller_new (registry);
571     }
572
573   return bonobo_object_dup_ref (BONOBO_OBJREF (registry->de_controller), ev);
574 }
575
576 typedef struct {
577   CORBA_Environment  *ev;
578   Bonobo_Unknown      source;
579   EventTypeStruct     etype;
580   Accessibility_Event e_out;
581 } NotifyContext;
582
583 static SpiReEntrantContinue
584 notify_listeners_cb (GList * const *list, gpointer user_data)
585 {
586   SpiListenerStruct *ls;
587   NotifyContext     *ctx = user_data;
588
589   ls = (*list)->data;
590
591 #ifdef SPI_LISTENER_DEBUG
592   fprintf (stderr, "event quarks: %lx %lx %lx\n", ls->event_type_quark, ctx->etype.major, ctx->etype.minor);
593   fprintf (stderr, "event name: %s\n", ctx->etype.event_name);
594 #endif
595   if ((ls->event_type_quark == ctx->etype.major) ||
596       (ls->event_type_quark == ctx->etype.minor))
597     {
598 #ifdef SPI_DEBUG
599       CORBA_string s;
600       fprintf (stderr, "notifying listener %d\n", 0);
601 /* g_list_index (list, l->data)); */
602       s = Accessibility_Accessible__get_name (ctx->source, ctx->ev);
603       fprintf (stderr, "event source name %s\n", s);
604       CORBA_free (s);
605       if (BONOBO_EX (ctx->ev))
606         {
607           CORBA_exception_free (ctx->ev);
608           return SPI_RE_ENTRANT_CONTINUE;
609         }
610 #endif
611       
612       ctx->e_out.source = ctx->source;
613       
614       if ((*list) && (*list)->data == ls)
615         {
616           Accessibility_EventListener_notifyEvent (
617             (Accessibility_EventListener) ls->listener, &ctx->e_out, ctx->ev);
618           if (ctx->ev->_major != CORBA_NO_EXCEPTION)
619             {
620               DBG (1, g_warning ("Accessibility app error: exception during "
621                         "event notification: %s\n",
622                         CORBA_exception_id (ctx->ev)));
623               CORBA_exception_free (ctx->ev);
624               /* FIXME: check that this item is removed from the list
625                * on system exception by a 'broken' listener */
626             }
627         }
628     }  
629
630   return SPI_RE_ENTRANT_CONTINUE;
631 }
632
633 static void
634 registry_emit_event (SpiRegistry *registry, NotifyContext *ctx)
635 {
636   GList       **list = get_listener_list (registry, ctx->etype.type_cat);
637
638   if (list && *list)
639     {
640     
641       spi_re_entrant_list_foreach (list, notify_listeners_cb, ctx);
642     }
643 }
644
645 static NotifyContext*
646 registry_clone_notify_context (NotifyContext *ctx)
647 {
648   NotifyContext *new_ctx = g_new0 (NotifyContext, 1);
649
650   new_ctx->ev = NULL;
651   new_ctx->source = bonobo_object_dup_ref (ctx->source, NULL); 
652   new_ctx->etype.event_name = CORBA_string_dup (ctx->etype.event_name);
653   new_ctx->etype.type_cat = ctx->etype.type_cat;
654   new_ctx->etype.major = ctx->etype.major;
655   new_ctx->etype.minor = ctx->etype.minor;
656   new_ctx->etype.detail = ctx->etype.detail;
657   new_ctx->e_out.type = CORBA_string_dup (ctx->e_out.type);
658   new_ctx->e_out.source = ctx->e_out.source;
659   new_ctx->e_out.detail1 = ctx->e_out.detail1;
660   new_ctx->e_out.detail2 = ctx->e_out.detail2;
661   CORBA_any__copy (&(new_ctx->e_out.any_data), &(ctx->e_out.any_data));
662   return new_ctx;
663 }
664
665 static void
666 registry_flush_event_queue (SpiRegistry       *registry,
667                             gboolean      discard,
668                             CORBA_Environment *ev)
669 {
670   NotifyContext *q_ctx;
671   while (!g_queue_is_empty (registry->deferred_event_queue)) {
672     q_ctx = g_queue_pop_tail (registry->deferred_event_queue);
673 #ifdef SPI_QUEUE_DEBUG
674     fprintf (stderr, "%s! %s [n=%d] %p\n", (discard ? "discard" : "pop"), 
675              q_ctx->etype.event_name, 
676              (int) registry->deferred_event_queue->length, q_ctx);
677 #endif
678     if (!discard)  {
679       q_ctx->ev = ev;
680       registry_emit_event (registry, q_ctx);
681     }
682     bonobo_object_release_unref (q_ctx->source, NULL);
683     CORBA_free ((void *)q_ctx->etype.event_name);
684     CORBA_free ((void *)q_ctx->e_out.type);
685     g_free (q_ctx);
686   }
687   registry->is_queueing = FALSE;
688 }
689
690 static gboolean
691 registry_timeout_flush_queue (gpointer data)
692 {
693   SpiRegistry *registry = data;
694   CORBA_Environment ev;
695 #ifdef SPI_QUEUE_DEBUG
696   fprintf (stderr, "timeout! flushing queue...\n");
697 #endif
698   CORBA_exception_init (&ev);
699   registry_flush_event_queue (registry, FALSE, &ev);
700   return FALSE;
701 }
702
703 static gboolean
704 registry_discard_on_event (SpiRegistry *registry, NotifyContext *ctx)
705 {
706   gboolean retval = FALSE;
707   NotifyContext *q_ctx = g_queue_peek_tail (registry->deferred_event_queue);
708   if ((q_ctx != NULL) &&
709       (ctx->etype.type_cat == ETYPE_WINDOW) && 
710       (ctx->etype.major == _activate_quark)) {
711     if (CORBA_Object_is_equivalent (ctx->source, q_ctx->source, NULL)) {
712       retval = TRUE;
713     }
714   }
715   return retval;
716 }
717
718 static gboolean
719 registry_reset_on_event (SpiRegistry *registry, NotifyContext *ctx)
720 {
721   return (ctx->etype.type_cat == ETYPE_WINDOW) ? TRUE : FALSE;
722 }
723
724 static void
725 registry_start_queue (SpiRegistry *registry)
726 {
727     g_timeout_add_full (G_PRIORITY_HIGH_IDLE, 
728                         registry->exit_notify_timeout,
729                         registry_timeout_flush_queue, registry, 
730                         NULL);
731     registry->is_queueing = 1;
732 }
733
734 static gboolean
735 registry_defer_on_event (SpiRegistry *registry, NotifyContext *ctx)
736 {
737   gboolean defer = FALSE;
738   if ((ctx->etype.type_cat == ETYPE_WINDOW) && 
739       (ctx->etype.major == _deactivate_quark)) {
740     defer = TRUE;
741     registry_start_queue (registry);
742   }
743   /* defer all object:state-change events after a window:deactivate */
744   else if ((ctx->etype.type_cat == ETYPE_FOCUS) ||
745            ((ctx->etype.type_cat == ETYPE_OBJECT) && 
746            (ctx->etype.major == _state_quark))) {
747     defer = TRUE;
748   }
749   return defer;
750 }
751
752 static void
753 registry_queue_event (SpiRegistry *registry, NotifyContext *ctx)
754 {
755   NotifyContext *q_ctx = registry_clone_notify_context (ctx);
756 #ifdef SPI_QUEUE_DEBUG
757     if (q_ctx->etype.type_cat != ETYPE_MOUSE)
758       fprintf (stderr, "push! %s %p\n", q_ctx->etype.event_name, q_ctx);
759 #endif    
760   g_queue_push_head (registry->deferred_event_queue, q_ctx);
761 }
762
763 /**
764  * Dispose of event in one of several ways:
765  * 1) discard;
766  * 2) initiate queuing and push onto queue (below)
767  * 3) push on existing queue to either pop on timeout or on subsequent event
768  * 4) pass-through immediately
769  * 5) pass-through, discarding queued events
770  * 6) emit queued events and then pass through
771  **/
772 static gboolean
773 registry_filter_event (SpiRegistry *registry, NotifyContext *ctx,
774                        CORBA_Environment *ev)
775 {
776   g_assert (ctx != NULL);
777
778   /* case #1 is not yet used */
779   if (registry_defer_on_event (registry, ctx)) { /* #2, #3 */
780     if (registry->is_queueing) {
781       registry_queue_event (registry, ctx);
782       return FALSE;
783     }
784     else { /* #4a */
785       return TRUE;
786     }
787   } 
788   else if (registry_reset_on_event (registry, ctx)) { /* #5, #6 */
789     gboolean discard = registry_discard_on_event (registry, ctx);
790 #ifdef SPI_QUEUE_DEBUG
791     fprintf (stderr, "event %s caused reset, discard=%d\n",
792              ctx->etype.event_name, (int) discard);
793 #endif    
794     registry_flush_event_queue (registry, discard, ev);
795     return (discard ? FALSE : TRUE);
796   }
797   else { /* #4b */
798     return TRUE;
799   }
800 }
801
802 static void
803 impl_registry_notify_event (PortableServer_Servant     servant,
804                             const Accessibility_Event *e,
805                             CORBA_Environment         *ev)
806 {
807   SpiRegistry  *registry;
808   NotifyContext ctx;
809   static int level = 0;
810
811   level++;
812   registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
813
814   parse_event_type (&ctx.etype, e->type);
815
816   ctx.ev = ev;
817   ctx.e_out = *e;
818   ctx.source = e->source;
819
820   if (registry_filter_event (registry, &ctx, ev)) {
821 #ifdef SPI_QUEUE_DEBUG
822     if (ctx.etype.type_cat != ETYPE_MOUSE)
823 {
824       fprintf (stderr, "emit! %s level: %d\n", ctx.etype.event_name, level);
825       fprintf (stderr, "emit! %p %x\n", ctx.e_out, ctx.e_out.type);
826 }
827 #endif    
828     registry_emit_event (registry, &ctx);
829   }
830   level--;
831 }
832
833 static void
834 spi_registry_class_init (SpiRegistryClass *klass)
835 {
836   GObjectClass * object_class = (GObjectClass *) klass;
837   POA_Accessibility_Registry__epv *epv = &klass->epv;
838
839   spi_registry_parent_class = g_type_class_ref (SPI_LISTENER_TYPE);
840   
841   object_class->finalize = spi_registry_object_finalize;
842
843   klass->parent_class.epv.notifyEvent   = impl_registry_notify_event;
844   
845   epv->registerApplication              = impl_accessibility_registry_register_application;
846   epv->deregisterApplication            = impl_accessibility_registry_deregister_application;
847   epv->registerGlobalEventListener      = impl_accessibility_registry_register_global_event_listener;
848   epv->deregisterGlobalEventListener    = impl_accessibility_registry_deregister_global_event_listener;
849   epv->deregisterGlobalEventListenerAll = impl_accessibility_registry_deregister_global_event_listener_all;
850   epv->getDeviceEventController         = impl_accessibility_registry_get_device_event_controller;
851   epv->getDesktopCount                  = impl_accessibility_registry_get_desktop_count;
852   epv->getDesktop                       = impl_accessibility_registry_get_desktop;
853   epv->getDesktopList                   = impl_accessibility_registry_get_desktop_list;
854   _deactivate_quark = g_quark_from_static_string ("deactivate");
855   _activate_quark = g_quark_from_static_string ("activate");
856   _state_quark = g_quark_from_static_string ("state-changed");
857 }
858
859 static void
860 spi_registry_init (SpiRegistry *registry)
861 {
862   spi_registry_set_debug (g_getenv ("AT_SPI_DEBUG"));
863   /*
864    * TODO: FIXME, this module makes the foolish assumptions that
865    * registryd uses the same display as the apps, and that the
866    * DISPLAY environment variable is set.
867    */
868   gdk_init (NULL, NULL);
869
870   registry->object_listeners = NULL;
871   registry->window_listeners = NULL;
872   registry->toolkit_listeners = NULL;
873   registry->deferred_event_queue = g_queue_new ();
874   registry->exit_notify_timeout = 100;
875   registry->desktop = spi_desktop_new ();
876   /* Register callback notification for application addition and removal */
877   g_signal_connect (G_OBJECT (registry->desktop),
878                     "application_added",
879                     G_CALLBACK (desktop_add_application),
880                     registry);
881
882   g_signal_connect (G_OBJECT (registry->desktop),
883                     "application_removed",
884                     G_CALLBACK (desktop_remove_application),
885                     registry);
886
887   registry->de_controller = spi_device_event_controller_new (registry);
888 }
889
890 BONOBO_TYPE_FUNC_FULL (SpiRegistry,
891                        Accessibility_Registry,
892                        PARENT_TYPE,
893                        spi_registry)
894
895 SpiRegistry *
896 spi_registry_new (void)
897 {
898   SpiRegistry *retval = g_object_new (SPI_REGISTRY_TYPE, NULL);
899   bonobo_object_set_immortal (BONOBO_OBJECT (retval), TRUE);
900   return retval;
901 }