2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2001, 2002 Sun Microsystems Inc.,
6 * Copyright 2001, 2002 Ximian, Inc.
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.
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.
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.
24 /* registry.c: the main accessibility service registry implementation */
26 #undef SPI_LISTENER_DEBUG
34 #include <bonobo/bonobo-exception.h>
35 #include "../libspi/spi-private.h"
38 /* Our parent GObject type */
39 #define PARENT_TYPE SPI_LISTENER_TYPE
41 /* A pointer to our parent object class */
42 static SpiListenerClass *spi_registry_parent_class;
58 const char *event_name;
59 EventTypeCategory type_cat;
60 GQuark major; /* from string segment[1] */
61 GQuark minor; /* from string segment[1]+segment[2] */
62 GQuark detail; /* from string segment[3] (not concatenated) */
66 Accessibility_EventListener listener;
67 GQuark event_type_quark;
68 EventTypeCategory event_type_cat;
72 spi_registry_set_debug (const char *debug_flag_string)
74 if (debug_flag_string)
75 _dbg = (int) g_ascii_strtod (debug_flag_string, NULL);
79 spi_listener_struct_new (Accessibility_EventListener listener, CORBA_Environment *ev)
81 SpiListenerStruct *retval = g_malloc (sizeof (SpiListenerStruct));
82 retval->listener = bonobo_object_dup_ref (listener, ev);
88 spi_listener_struct_free (SpiListenerStruct *ls, CORBA_Environment *ev)
90 bonobo_object_release_unref (ls->listener, ev);
95 desktop_add_application (SpiDesktop *desktop,
96 guint index, gpointer data)
98 BonoboObject *registry = BONOBO_OBJECT (data);
99 Accessibility_Event e;
100 CORBA_Environment ev;
101 Accessibility_Accessible a;
103 CORBA_exception_init (&ev);
104 e.type = "object:children-changed:add";
105 e.source = BONOBO_OBJREF (desktop);
108 a = Accessibility_Accessible_getChildAtIndex (BONOBO_OBJREF (desktop),
111 spi_init_any_object (&e.any_data, a);
113 spi_init_any_nil (&e.any_data);
114 Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
116 bonobo_object_release_unref (a, &ev);
117 CORBA_exception_free (&ev);
123 desktop_remove_application (SpiDesktop *desktop,
124 guint index, gpointer data)
126 BonoboObject *registry = BONOBO_OBJECT (data);
127 Accessibility_Event e;
128 Accessibility_Accessible a;
129 CORBA_Environment ev;
131 CORBA_exception_init (&ev);
133 e.type = "object:children-changed:remove";
134 e.source = BONOBO_OBJREF (desktop);
137 a = Accessibility_Accessible_getChildAtIndex (BONOBO_OBJREF (desktop),
140 spi_init_any_object (&e.any_data, a);
142 spi_init_any_nil (&e.any_data);
143 Accessibility_Accessible_unref (a, &ev);
144 Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
146 Accessibility_Desktop_unref (e.source, &ev);
147 CORBA_exception_free (&ev);
152 spi_registry_object_finalize (GObject *object)
154 DBG (1, g_warning ("spi_registry_object_finalize called\n"));
156 /* TODO: unref deviceeventcontroller, which disconnects key listener */
157 G_OBJECT_CLASS (spi_registry_parent_class)->finalize (object);
161 _get_unique_id (void)
169 * registerApplication:
170 * @application: a reference to the requesting @Application
171 * return values: void
173 * Register a new application with the accessibility broker.
177 impl_accessibility_registry_register_application (PortableServer_Servant servant,
178 const Accessibility_Application application,
179 CORBA_Environment * ev)
181 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
184 fprintf (stderr, "registering app %p\n", application);
186 spi_desktop_add_application (registry->desktop, application);
188 Accessibility_Application__set_id (application, _get_unique_id (), ev);
191 * TODO: change the implementation below to a WM-aware one;
192 * e.g. don't add all apps to the SpiDesktop
196 #ifdef USE_A_HASH_IN_FUTURE
198 compare_corba_objects (gconstpointer p1, gconstpointer p2)
200 CORBA_Environment ev;
204 fprintf (stderr, "comparing %p to %p\n",
208 retval = !CORBA_Object_is_equivalent ((CORBA_Object) p1, (CORBA_Object) p2, &ev);
214 register_with_toolkits (SpiRegistry *spi_registry_bonobo_object, EventTypeStruct *etype, CORBA_Environment *ev)
219 Accessibility_Desktop desktop;
220 Accessibility_Application app;
221 Accessibility_Registry registry;
222 registry = BONOBO_OBJREF (spi_registry_bonobo_object);
224 /* for each app in each desktop, call ...Application_registerToolkitEventListener */
226 n_desktops = Accessibility_Registry_getDesktopCount (registry, ev);
228 for (i=0; i<n_desktops; ++i)
230 desktop = Accessibility_Registry_getDesktop (registry, i, ev);
231 n_apps = Accessibility_Desktop__get_childCount (desktop, ev);
232 for (j=0; j<n_apps; ++j)
234 app = (Accessibility_Application) Accessibility_Desktop_getChildAtIndex (desktop,
237 Accessibility_Application_registerToolkitEventListener (app,
239 CORBA_string_dup (etype->event_name),
245 #ifdef USE_A_HASH_IN_FUTURE
248 compare_listener_quarks (gconstpointer p1, gconstpointer p2)
250 return (((SpiListenerStruct *)p2)->event_type_quark !=
251 ((SpiListenerStruct *)p1)->event_type_quark);
255 compare_listener_corbaref (gconstpointer p1, gconstpointer p2)
257 return compare_corba_objects (((SpiListenerStruct *)p2)->listener,
258 ((SpiListenerStruct *)p1)->listener);
263 parse_event_type (EventTypeStruct *etype, const char *event_name)
265 gchar **split_string;
268 split_string = g_strsplit (event_name, ":", 4);
269 etype->event_name = event_name;
271 if (!g_ascii_strncasecmp (event_name, "focus:", 6))
273 etype->type_cat = ETYPE_FOCUS;
275 else if (!g_ascii_strncasecmp (event_name, "mouse:", 6))
277 etype->type_cat = ETYPE_MOUSE;
279 else if (!g_ascii_strncasecmp (event_name, "object:", 7))
281 etype->type_cat = ETYPE_OBJECT;
283 else if (!g_ascii_strncasecmp (event_name, "window:", 7))
285 etype->type_cat = ETYPE_WINDOW;
287 else if (!g_ascii_strncasecmp (event_name, "keyboard:", 9))
289 etype->type_cat = ETYPE_KEYBOARD;
293 etype->type_cat = ETYPE_TOOLKIT;
298 etype->major = g_quark_from_string (split_string[1]);
301 etype->minor = g_quark_from_string (s = g_strconcat (split_string[1], split_string[2], NULL));
305 etype->detail = g_quark_from_string (split_string[3]);
309 etype->detail = g_quark_from_static_string ("");
314 etype->minor = etype->major;
315 etype->detail = g_quark_from_static_string (""); //etype->major;
320 etype->major = g_quark_from_static_string ("");
321 etype->minor = etype->major;
322 etype->detail = etype->major;
325 g_strfreev (split_string);
329 * deregisterApplication:
330 * @application: a reference to the @Application
331 * to be deregistered.
332 * return values: void
334 * De-register an application previously registered with the broker.
338 impl_accessibility_registry_deregister_application (PortableServer_Servant servant,
339 const Accessibility_Application application,
340 CORBA_Environment * ev)
342 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
344 spi_desktop_remove_application (registry->desktop, application);
347 fprintf (stderr, "de-registered app %p\n", application);
352 get_listener_list (SpiRegistry *registry,
353 EventTypeCategory cat)
363 ret = ®istry->object_listeners;
366 ret = ®istry->window_listeners;
370 ret = ®istry->toolkit_listeners;
380 * CORBA Accessibility::Registry::registerGlobalEventListener method implementation
383 impl_accessibility_registry_register_global_event_listener (
384 PortableServer_Servant servant,
385 Accessibility_EventListener listener,
386 const CORBA_char *event_name,
387 CORBA_Environment *ev)
389 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
390 SpiListenerStruct *ls = spi_listener_struct_new (listener, ev);
391 EventTypeStruct etype;
394 #ifdef SPI_LISTENER_DEBUG
395 fprintf (stderr, "registering for events of type %s\n", event_name);
398 /* parse, check major event type and add listener accordingly */
399 parse_event_type (&etype, event_name);
400 ls->event_type_quark = etype.minor;
401 ls->event_type_cat = etype.type_cat;
403 list = get_listener_list (registry, etype.type_cat);
407 *list = g_list_prepend (*list, ls);
409 if (etype.type_cat == ETYPE_TOOLKIT)
411 register_with_toolkits (registry, &etype, ev);
416 spi_listener_struct_free (ls, ev);
420 static SpiReEntrantContinue
421 remove_listener_cb (GList * const *list, gpointer user_data)
423 SpiListenerStruct *ls = (SpiListenerStruct *) (*list)->data;
424 CORBA_Environment ev;
425 Accessibility_EventListener listener = user_data;
427 CORBA_exception_init (&ev);
429 if (CORBA_Object_is_equivalent (ls->listener, listener, &ev))
431 spi_re_entrant_list_delete_link (list);
432 spi_listener_struct_free (ls, &ev);
435 CORBA_exception_free (&ev);
437 return SPI_RE_ENTRANT_CONTINUE;
441 * CORBA Accessibility::Registry::deregisterGlobalEventListenerAll method implementation
444 impl_accessibility_registry_deregister_global_event_listener_all (
445 PortableServer_Servant servant,
446 Accessibility_EventListener listener,
447 CORBA_Environment *ev)
451 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
453 lists[0] = ®istry->object_listeners;
454 lists[1] = ®istry->window_listeners;
455 lists[2] = ®istry->toolkit_listeners;
457 for (i = 0; i < sizeof (lists) / sizeof (lists[0]); i++)
459 spi_re_entrant_list_foreach (lists [i], remove_listener_cb, listener);
465 * CORBA Accessibility::Registry::deregisterGlobalEventListener method implementation
468 impl_accessibility_registry_deregister_global_event_listener (
469 PortableServer_Servant servant,
470 Accessibility_EventListener listener,
471 const CORBA_char *event_name,
472 CORBA_Environment *ev)
474 SpiRegistry *registry;
475 EventTypeStruct etype;
477 registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
479 parse_event_type (&etype, (char *) event_name);
481 spi_re_entrant_list_foreach (get_listener_list (registry, etype.type_cat),
482 remove_listener_cb, listener);
488 * return values: a short integer indicating the current number of
491 * Get the current number of desktops.
495 impl_accessibility_registry_get_desktop_count (PortableServer_Servant servant,
496 CORBA_Environment * ev)
498 /* TODO: implement support for multiple virtual desktops */
499 CORBA_short n_desktops;
500 n_desktops = (CORBA_short) 1;
507 * @n: the index of the requested @Desktop.
508 * return values: a reference to the requested @Desktop.
510 * Get the nth accessible desktop.
513 static Accessibility_Desktop
514 impl_accessibility_registry_get_desktop (PortableServer_Servant servant,
516 CORBA_Environment * ev)
518 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
520 /* TODO: implement support for multiple virtual desktops */
523 return (Accessibility_Desktop)
524 bonobo_object_dup_ref (BONOBO_OBJREF (registry->desktop), ev);
528 return (Accessibility_Desktop) CORBA_OBJECT_NIL;
535 * return values: a sequence containing references to
538 * Get a list of accessible desktops.
541 static Accessibility_DesktopSeq *
542 impl_accessibility_registry_get_desktop_list (PortableServer_Servant servant,
543 CORBA_Environment * ev)
545 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
546 Accessibility_DesktopSeq *desktops;
548 desktops = Accessibility_DesktopSeq__alloc ();
549 desktops->_length = desktops->_maximum = 1;
550 desktops->_buffer = Accessibility_DesktopSeq_allocbuf (desktops->_length);
551 desktops->_buffer [0] = bonobo_object_dup_ref (BONOBO_OBJREF (registry->desktop), ev);
557 static Accessibility_DeviceEventController
558 impl_accessibility_registry_get_device_event_controller (PortableServer_Servant servant,
559 CORBA_Environment *ev)
561 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
563 if (!registry->de_controller)
565 registry->de_controller = spi_device_event_controller_new (registry);
568 return bonobo_object_dup_ref (BONOBO_OBJREF (registry->de_controller), ev);
572 CORBA_Environment *ev;
573 Bonobo_Unknown source;
574 EventTypeStruct etype;
575 Accessibility_Event e_out;
578 static SpiReEntrantContinue
579 notify_listeners_cb (GList * const *list, gpointer user_data)
581 SpiListenerStruct *ls;
582 NotifyContext *ctx = user_data;
589 #ifdef SPI_LISTENER_DEBUG
590 fprintf (stderr, "event quarks: %lx %lx %lx\n", ls->event_type_quark, ctx->etype.major, ctx->etype.minor);
591 fprintf (stderr, "event name: %s\n", ctx->etype.event_name);
594 if ((ls->event_type_quark == ctx->etype.major) ||
595 (ls->event_type_quark == ctx->etype.minor))
598 fprintf (stderr, "notifying listener %d\n", 0);
599 /* g_list_index (list, l->data)); */
600 s = Accessibility_Accessible__get_name (ctx->source, ctx->ev);
601 fprintf (stderr, "event source name %s\n", s);
605 ctx->e_out.source = CORBA_Object_duplicate (ctx->source, ctx->ev);
606 if (BONOBO_EX (ctx->ev))
608 return SPI_RE_ENTRANT_CONTINUE;
611 if ((*list) && (*list)->data == ls)
613 Accessibility_EventListener_notifyEvent (
614 (Accessibility_EventListener) ls->listener, &ctx->e_out, ctx->ev);
615 if (ctx->ev->_major != CORBA_NO_EXCEPTION)
617 DBG (1, g_warning ("Accessibility app error: exception during "
618 "event notification: %s\n",
619 CORBA_exception_id (ctx->ev)));
620 if (ctx->ev->_major == CORBA_SYSTEM_EXCEPTION)
621 CORBA_exception_init (ctx->ev);
622 /* clear system exception on notify, it means listener is dead but
623 * that's no concern of the event source :-) */
626 else /* dup re-entered */
628 CORBA_Object_release (ctx->e_out.source, ctx->ev);
632 return SPI_RE_ENTRANT_CONTINUE;
636 impl_registry_notify_event (PortableServer_Servant servant,
637 const Accessibility_Event *e,
638 CORBA_Environment *ev)
640 SpiRegistry *registry;
644 registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
646 parse_event_type (&ctx.etype, e->type);
648 list = get_listener_list (registry, ctx.etype.type_cat);
654 CORBA_any__copy (&ctx.e_out.any_data, &e->any_data);
655 ctx.source = e->source;
656 spi_re_entrant_list_foreach (list, notify_listeners_cb, &ctx);
661 spi_registry_class_init (SpiRegistryClass *klass)
663 GObjectClass * object_class = (GObjectClass *) klass;
664 POA_Accessibility_Registry__epv *epv = &klass->epv;
666 spi_registry_parent_class = g_type_class_ref (SPI_LISTENER_TYPE);
668 object_class->finalize = spi_registry_object_finalize;
670 klass->parent_class.epv.notifyEvent = impl_registry_notify_event;
672 epv->registerApplication = impl_accessibility_registry_register_application;
673 epv->deregisterApplication = impl_accessibility_registry_deregister_application;
674 epv->registerGlobalEventListener = impl_accessibility_registry_register_global_event_listener;
675 epv->deregisterGlobalEventListener = impl_accessibility_registry_deregister_global_event_listener;
676 epv->deregisterGlobalEventListenerAll = impl_accessibility_registry_deregister_global_event_listener_all;
677 epv->getDeviceEventController = impl_accessibility_registry_get_device_event_controller;
678 epv->getDesktopCount = impl_accessibility_registry_get_desktop_count;
679 epv->getDesktop = impl_accessibility_registry_get_desktop;
680 epv->getDesktopList = impl_accessibility_registry_get_desktop_list;
684 spi_registry_init (SpiRegistry *registry)
686 spi_registry_set_debug (g_getenv ("AT_SPI_DEBUG"));
687 registry->object_listeners = NULL;
688 registry->window_listeners = NULL;
689 registry->toolkit_listeners = NULL;
690 registry->desktop = spi_desktop_new ();
691 /* Register callback notification for application addition and removal */
692 g_signal_connect (G_OBJECT (registry->desktop),
694 G_CALLBACK (desktop_add_application),
697 g_signal_connect (G_OBJECT (registry->desktop),
698 "application_removed",
699 G_CALLBACK (desktop_remove_application),
702 registry->de_controller = spi_device_event_controller_new (registry);
705 BONOBO_TYPE_FUNC_FULL (SpiRegistry,
706 Accessibility_Registry,
711 spi_registry_new (void)
713 SpiRegistry *retval = g_object_new (SPI_REGISTRY_TYPE, NULL);
714 bonobo_object_set_immortal (BONOBO_OBJECT (retval), TRUE);