source: trunk/third/at-spi/registryd/registry.c @ 18422

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