Bugfix for keygrab regression (registryd was holding keygrabs after they should have...
[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 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 /* registry.c: the main accessibility service registry implementation */
24
25 #undef SPI_LISTENER_DEBUG
26
27 #include <config.h>
28 #ifdef SPI_DEBUG
29 #  include <stdio.h>
30 #endif
31
32 #include <bonobo/bonobo-exception.h>
33 #include "../libspi/spi-private.h"
34 #include "registry.h"
35
36 /* Our parent GObject type  */
37 #define PARENT_TYPE SPI_LISTENER_TYPE
38
39 /* A pointer to our parent object class */
40 static SpiListenerClass *spi_registry_parent_class;
41
42 typedef enum {
43   ETYPE_FOCUS,
44   ETYPE_OBJECT,
45   ETYPE_PROPERTY,
46   ETYPE_WINDOW,
47   ETYPE_TOOLKIT,
48   ETYPE_KEYBOARD,
49   
50   ETYPE_LAST_DEFINED
51 } EventTypeCategory;
52
53 typedef struct {
54   char *event_name;
55   EventTypeCategory type_cat;
56   GQuark major;  /* from string segment[1] */
57   GQuark minor;  /* from string segment[1]+segment[2] */
58   GQuark detail; /* from string segment[3] (not concatenated) */
59 } EventTypeStruct;
60
61 typedef struct {
62   Accessibility_EventListener listener;
63   GQuark            event_type_quark;
64   EventTypeCategory event_type_cat;
65 } SpiListenerStruct;
66
67
68 SpiListenerStruct *
69 spi_listener_struct_new (Accessibility_EventListener listener, CORBA_Environment *ev)
70 {
71   SpiListenerStruct *retval = g_malloc (sizeof (SpiListenerStruct));
72   retval->listener = bonobo_object_dup_ref (listener, ev);
73   return retval;
74 }
75
76
77 void
78 spi_listener_struct_free (SpiListenerStruct *ls, CORBA_Environment *ev)
79 {
80   bonobo_object_release_unref (ls->listener, ev);
81   g_free (ls);
82 }
83
84
85 static void
86 spi_registry_object_finalize (GObject *object)
87 {
88   printf ("spi_registry_object_finalize called\n");
89
90   /* TODO: unref deviceeventcontroller, which disconnects key listener */
91   G_OBJECT_CLASS (spi_registry_parent_class)->finalize (object);
92 }
93
94 static long
95 _get_unique_id (void)
96 {
97   static long id = 0;
98
99   return ++id;
100 }
101
102 /**
103  * registerApplication:
104  * @application: a reference to the requesting @Application
105  * return values: void
106  *
107  * Register a new application with the accessibility broker.
108  *
109  **/
110 static void
111 impl_accessibility_registry_register_application (PortableServer_Servant servant,
112                                                   const Accessibility_Application application,
113                                                   CORBA_Environment * ev)
114 {
115   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
116
117 #ifdef SPI_DEBUG
118   fprintf (stderr, "registering app %p\n", application);
119 #endif
120   spi_desktop_add_application (registry->desktop, application);
121
122   Accessibility_Application__set_id (application, _get_unique_id (), ev);
123
124   /*
125    * TODO: change the implementation below to a WM-aware one;
126    * e.g. don't add all apps to the SpiDesktop
127    */
128 }
129
130 #ifdef USE_A_HASH_IN_FUTURE
131 static gint
132 compare_corba_objects (gconstpointer p1, gconstpointer p2)
133 {
134   CORBA_Environment ev;
135   gint retval;
136
137 #ifdef SPI_DEBUG
138   fprintf (stderr, "comparing %p to %p\n",
139            p1, p2);
140 #endif
141   
142   retval = !CORBA_Object_is_equivalent ((CORBA_Object) p1, (CORBA_Object) p2, &ev);
143   return retval;  
144 }
145 #endif
146
147 static void
148 register_with_toolkits (SpiRegistry *spi_registry_bonobo_object, EventTypeStruct *etype, CORBA_Environment *ev)
149 {
150   gint n_desktops;
151   gint n_apps;
152   gint i, j;
153   Accessibility_Desktop desktop;
154   Accessibility_Application app;
155   Accessibility_Registry registry;
156   registry  = BONOBO_OBJREF (spi_registry_bonobo_object);
157
158   /* for each app in each desktop, call ...Application_registerToolkitEventListener */
159
160   n_desktops = Accessibility_Registry_getDesktopCount (registry, ev);
161
162   for (i=0; i<n_desktops; ++i)
163     {
164       desktop = Accessibility_Registry_getDesktop (registry, i, ev);
165       n_apps = Accessibility_Desktop__get_childCount (desktop, ev);
166       for (j=0; j<n_apps; ++j)
167         {
168           app = (Accessibility_Application) Accessibility_Desktop_getChildAtIndex (desktop,
169                                                                                    j,
170                                                                                    ev);
171           Accessibility_Application_registerToolkitEventListener (app,
172                                                                   registry,
173                                                                   CORBA_string_dup (etype->event_name),
174                                                                   ev);
175         }
176     }
177 }
178
179 #ifdef USE_A_HASH_IN_FUTURE
180
181 static gint
182 compare_listener_quarks (gconstpointer p1, gconstpointer p2)
183 {
184         return (((SpiListenerStruct *)p2)->event_type_quark !=
185                 ((SpiListenerStruct *)p1)->event_type_quark);
186 }
187
188 static gint
189 compare_listener_corbaref (gconstpointer p1, gconstpointer p2)
190 {
191   return compare_corba_objects (((SpiListenerStruct *)p2)->listener,
192                                 ((SpiListenerStruct *)p1)->listener);
193 }
194 #endif
195
196 static void
197 parse_event_type (EventTypeStruct *etype, const char *event_name)
198 {
199   gchar **split_string;
200   gchar *s;
201
202   split_string = g_strsplit (event_name, ":", 4);
203   etype->event_name = g_strdup (event_name);
204
205   if (!g_ascii_strncasecmp (event_name, "focus:", 6))
206     {
207       etype->type_cat = ETYPE_FOCUS;
208     }
209   else if (!g_ascii_strncasecmp (event_name, "object:", 7))
210     {
211       etype->type_cat = ETYPE_OBJECT;
212     }
213   else if (!g_ascii_strncasecmp (event_name, "window:", 7))
214     {
215       etype->type_cat = ETYPE_WINDOW;
216     }
217   else
218     {
219       etype->type_cat = ETYPE_TOOLKIT;
220     }
221
222   if (split_string[1])
223     {
224       if (split_string[2])
225         {
226           etype->minor = g_quark_from_string (s = g_strconcat (split_string[1], split_string[2], NULL));
227           g_free (s);
228           if (split_string[3])
229             {
230               etype->detail = g_quark_from_string (split_string[3]);
231               s = g_strconcat (split_string[1], split_string[2], split_string[3], NULL);
232               etype->major = g_quark_from_string (s);
233               g_free (s);
234             }
235           else
236             {
237               etype->detail = g_quark_from_static_string ("");
238               s = g_strconcat (split_string[1], split_string[2], NULL);
239               etype->major = g_quark_from_string (s);
240               g_free (s);
241             }
242         }
243       else
244         {
245           etype->major = g_quark_from_string (split_string[1]);
246           etype->minor = etype->major;
247           etype->detail = etype->major;
248         }
249     }
250   else
251     {
252       etype->major = g_quark_from_static_string ("");
253       etype->minor = etype->major;
254       etype->detail = etype->major;
255     }
256
257   g_strfreev (split_string);
258 }
259
260 /**
261  * deregisterApplication:
262  * @application: a reference to the @Application
263  * to be deregistered.
264  * return values: void
265  *
266  * De-register an application previously registered with the broker.
267  *
268  **/
269 static void
270 impl_accessibility_registry_deregister_application (PortableServer_Servant servant,
271                                                     const Accessibility_Application application,
272                                                     CORBA_Environment * ev)
273 {
274   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
275
276   spi_desktop_remove_application (registry->desktop, application);
277
278 #ifdef SPI_DEBUG
279   fprintf (stderr, "de-registered app %p\n", application);
280 #endif
281 }
282
283 static GList **
284 get_listener_list (SpiRegistry      *registry,
285                    EventTypeCategory cat)
286 {
287   GList **ret;
288   
289   switch (cat)
290     {
291       case ETYPE_OBJECT:
292       case ETYPE_PROPERTY:
293       case ETYPE_FOCUS:
294         ret = &registry->object_listeners;
295         break;
296       case ETYPE_WINDOW:
297         ret = &registry->window_listeners;
298         break;
299       case ETYPE_TOOLKIT:
300         ret = &registry->toolkit_listeners;
301         break;
302       case ETYPE_KEYBOARD:
303       default:
304         ret = NULL;
305         break;
306     }
307   return ret;
308 }
309
310 /*
311  * CORBA Accessibility::Registry::registerGlobalEventListener method implementation
312  */
313 static void
314 impl_accessibility_registry_register_global_event_listener (
315         PortableServer_Servant      servant,
316         Accessibility_EventListener listener,
317         const CORBA_char           *event_name,
318         CORBA_Environment          *ev)
319 {
320   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
321   SpiListenerStruct *ls = spi_listener_struct_new (listener, ev);
322   EventTypeStruct etype;
323   GList          **list;
324
325 #ifdef SPI_DEBUG
326   fprintf (stderr, "registering for events of type %s\n", event_name);
327 #endif
328
329   /* parse, check major event type and add listener accordingly */
330   parse_event_type (&etype, event_name);
331   ls->event_type_quark = etype.major;
332   ls->event_type_cat = etype.type_cat;
333
334   list = get_listener_list (registry, etype.type_cat);
335
336   if (list)
337     {
338       *list = g_list_prepend (*list, ls);
339
340       if (etype.type_cat == ETYPE_TOOLKIT)
341         {
342           register_with_toolkits (registry, &etype, ev);
343         }
344     }
345   else
346     {
347       spi_listener_struct_free (ls, ev);
348     }
349 }
350
351 static SpiReEntrantContinue
352 remove_listener_cb (GList * const *list, gpointer user_data)
353 {
354   SpiListenerStruct *ls = (SpiListenerStruct *) (*list)->data;
355   CORBA_Environment  ev;
356   Accessibility_EventListener listener = user_data;
357
358   CORBA_exception_init (&ev);
359         
360   if (CORBA_Object_is_equivalent (ls->listener, listener, &ev))
361     {
362        spi_re_entrant_list_delete_link (list);
363        spi_listener_struct_free (ls, &ev);
364     }
365
366   CORBA_exception_free (&ev);
367
368   return SPI_RE_ENTRANT_CONTINUE;
369 }
370
371 /*
372  * CORBA Accessibility::Registry::deregisterGlobalEventListenerAll method implementation
373  */
374 static void
375 impl_accessibility_registry_deregister_global_event_listener_all (
376         PortableServer_Servant      servant,
377         Accessibility_EventListener listener,
378         CORBA_Environment          *ev)
379 {
380   int i;
381   GList **lists[2];
382   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
383
384   lists[0] = &registry->object_listeners;
385   lists[1] = &registry->window_listeners;
386   lists[2] = &registry->toolkit_listeners;
387
388   for (i = 0; i < sizeof (lists) / sizeof (lists[0]); i++)
389     {
390       spi_re_entrant_list_foreach (lists [i], remove_listener_cb, listener);
391     }
392 }
393
394
395 /*
396  * CORBA Accessibility::Registry::deregisterGlobalEventListener method implementation
397  */
398 static void
399 impl_accessibility_registry_deregister_global_event_listener (
400         PortableServer_Servant      servant,
401         Accessibility_EventListener listener,
402         const CORBA_char           *event_name,
403         CORBA_Environment          *ev)
404 {
405   SpiRegistry    *registry;
406   EventTypeStruct etype;
407
408   registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
409
410   parse_event_type (&etype, (char *) event_name);
411
412   spi_re_entrant_list_foreach (get_listener_list (registry, etype.type_cat),
413                                 remove_listener_cb, listener);
414 }
415
416
417 /**
418  * getDesktopCount:
419  * return values: a short integer indicating the current number of
420  * @Desktops.
421  *
422  * Get the current number of desktops.
423  *
424  **/
425 static short
426 impl_accessibility_registry_get_desktop_count (PortableServer_Servant servant,
427                                                CORBA_Environment * ev)
428 {
429   /* TODO: implement support for multiple virtual desktops */
430   CORBA_short n_desktops;
431   n_desktops = (CORBA_short) 1;
432   return n_desktops;
433 }
434
435
436 /**
437  * getDesktop:
438  * @n: the index of the requested @Desktop.
439  * return values: a reference to the requested @Desktop.
440  *
441  * Get the nth accessible desktop.
442  *
443  **/
444 static Accessibility_Desktop
445 impl_accessibility_registry_get_desktop (PortableServer_Servant servant,
446                                          const CORBA_short n,
447                                          CORBA_Environment * ev)
448 {
449   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
450
451   /* TODO: implement support for multiple virtual desktops */
452   if (n == 0)
453     {
454       return (Accessibility_Desktop)
455         bonobo_object_dup_ref (BONOBO_OBJREF (registry->desktop), ev);
456     }
457   else
458     {
459       return (Accessibility_Desktop) CORBA_OBJECT_NIL;
460     }
461 }
462
463
464 /**
465  * getDesktopList:
466  * return values: a sequence containing references to
467  * the @Desktops.
468  *
469  * Get a list of accessible desktops.
470  *
471  **/
472 static Accessibility_DesktopSeq *
473 impl_accessibility_registry_get_desktop_list (PortableServer_Servant servant,
474                                               CORBA_Environment * ev)
475 {
476   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
477   Accessibility_DesktopSeq *desktops;
478
479   desktops = Accessibility_DesktopSeq__alloc ();
480   desktops->_length = desktops->_maximum = 1;
481   desktops->_buffer = Accessibility_DesktopSeq_allocbuf (desktops->_length);
482   desktops->_buffer [0] = bonobo_object_dup_ref (BONOBO_OBJREF (registry->desktop), ev);
483
484   return desktops;
485 }
486
487
488 static Accessibility_DeviceEventController
489 impl_accessibility_registry_get_device_event_controller (PortableServer_Servant servant,
490                                                          CORBA_Environment     *ev)
491 {
492   SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
493
494   if (!registry->de_controller)
495     {
496       registry->de_controller = spi_device_event_controller_new (registry);
497     }
498
499   return bonobo_object_dup_ref (BONOBO_OBJREF (registry->de_controller), ev);
500 }
501
502 typedef struct {
503   CORBA_Environment  *ev;
504   Bonobo_Unknown      source;
505   EventTypeStruct     etype;
506   Accessibility_Event e_out;
507 } NotifyContext;
508
509 static SpiReEntrantContinue
510 notify_listeners_cb (GList * const *list, gpointer user_data)
511 {
512   SpiListenerStruct *ls;
513   NotifyContext     *ctx = user_data;
514 #ifdef SPI_DEBUG
515   CORBA_string       s;
516 #endif
517
518   ls = (*list)->data;
519
520 #ifdef SPI_LISTENER_DEBUG
521   fprintf (stderr, "event quarks: %lx %lx %lx\n", ls->event_type_quark, etype.major, etype.minor);
522   fprintf (stderr, "event name: %s\n", etype.event_name);
523 #endif
524
525   if ((ls->event_type_quark == ctx->etype.major) ||
526       (ls->event_type_quark == ctx->etype.minor))
527     {
528 #ifdef SPI_DEBUG
529       fprintf (stderr, "notifying listener %d\n", g_list_index (listeners, l->data));
530       s = Accessibility_Accessible__get_name (ctx->source, ev);
531       fprintf (stderr, "event source name %s\n", s);
532       CORBA_free (s);
533 #endif
534       
535       ctx->e_out.source = bonobo_object_dup_ref (ctx->source, ctx->ev);
536       if (BONOBO_EX (ctx->ev))
537         {
538           return SPI_RE_ENTRANT_CONTINUE;;
539         }
540
541       if ((*list) && (*list)->data == ls)
542         {
543           Accessibility_EventListener_notifyEvent (
544             (Accessibility_EventListener) ls->listener, &ctx->e_out, ctx->ev);
545         if (ctx->ev->_major != CORBA_NO_EXCEPTION)
546           {
547             g_warning ("Accessibility app error: exception during "
548                        "event notification: %s\n",
549                        CORBA_exception_id (ctx->ev));
550           }
551         }
552       else /* dup re-entered */
553         {
554           bonobo_object_release_unref (ctx->e_out.source, ctx->ev);
555         }
556     }  
557
558   return SPI_RE_ENTRANT_CONTINUE;
559 }
560
561 static void
562 impl_registry_notify_event (PortableServer_Servant     servant,
563                             const Accessibility_Event *e,
564                             CORBA_Environment         *ev)
565 {
566   SpiRegistry  *registry;
567   GList       **list;
568   NotifyContext ctx;
569
570   registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
571
572   parse_event_type (&ctx.etype, e->type);
573
574   list = get_listener_list (registry, ctx.etype.type_cat);
575
576   if (list)
577     {
578       ctx.ev = ev;
579       ctx.e_out = *e;
580       ctx.source = e->source;
581       parse_event_type (&ctx.etype, e->type);
582
583       spi_re_entrant_list_foreach (list, notify_listeners_cb, &ctx);
584     }
585
586   if (e->source != CORBA_OBJECT_NIL)
587     {
588       Accessibility_Accessible_unref (e->source, ev);
589     }
590 }
591
592
593 static void
594 spi_registry_class_init (SpiRegistryClass *klass)
595 {
596   GObjectClass * object_class = (GObjectClass *) klass;
597   POA_Accessibility_Registry__epv *epv = &klass->epv;
598
599   spi_registry_parent_class = g_type_class_ref (SPI_LISTENER_TYPE);
600   
601   object_class->finalize = spi_registry_object_finalize;
602
603   klass->parent_class.epv.notifyEvent   = impl_registry_notify_event;
604   
605   epv->registerApplication              = impl_accessibility_registry_register_application;
606   epv->deregisterApplication            = impl_accessibility_registry_deregister_application;
607   epv->registerGlobalEventListener      = impl_accessibility_registry_register_global_event_listener;
608   epv->deregisterGlobalEventListener    = impl_accessibility_registry_deregister_global_event_listener;
609   epv->deregisterGlobalEventListenerAll = impl_accessibility_registry_deregister_global_event_listener_all;
610   epv->getDeviceEventController         = impl_accessibility_registry_get_device_event_controller;
611   epv->getDesktopCount                  = impl_accessibility_registry_get_desktop_count;
612   epv->getDesktop                       = impl_accessibility_registry_get_desktop;
613   epv->getDesktopList                   = impl_accessibility_registry_get_desktop_list;
614 }
615
616 static void
617 spi_registry_init (SpiRegistry *registry)
618 {
619   registry->object_listeners = NULL;
620   registry->window_listeners = NULL;
621   registry->toolkit_listeners = NULL;
622   registry->desktop = spi_desktop_new ();
623   registry->de_controller = NULL;
624 }
625
626 BONOBO_TYPE_FUNC_FULL (SpiRegistry,
627                        Accessibility_Registry,
628                        PARENT_TYPE,
629                        spi_registry);
630
631 SpiRegistry *
632 spi_registry_new (void)
633 {
634   SpiRegistry *retval = g_object_new (SPI_REGISTRY_TYPE, NULL);
635   bonobo_object_set_immortal (BONOBO_OBJECT (retval), TRUE);
636   return retval;
637 }