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_Accessible_unref (a, &ev);
115 Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
117 Accessibility_Desktop_unref (e.source, &ev);
118 CORBA_exception_free (&ev);
124 desktop_remove_application (SpiDesktop *desktop,
125 guint index, gpointer data)
127 BonoboObject *registry = BONOBO_OBJECT (data);
128 Accessibility_Event e;
129 Accessibility_Accessible a;
130 CORBA_Environment ev;
132 CORBA_exception_init (&ev);
134 e.type = "object:children-changed:remove";
135 e.source = BONOBO_OBJREF (desktop);
138 a = Accessibility_Accessible_getChildAtIndex (BONOBO_OBJREF (desktop),
141 spi_init_any_object (&e.any_data, a);
143 spi_init_any_nil (&e.any_data);
144 Accessibility_Accessible_unref (a, &ev);
145 Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
147 Accessibility_Desktop_unref (e.source, &ev);
148 CORBA_exception_free (&ev);
153 spi_registry_object_finalize (GObject *object)
155 DBG (1, g_warning ("spi_registry_object_finalize called\n"));
157 /* TODO: unref deviceeventcontroller, which disconnects key listener */
158 G_OBJECT_CLASS (spi_registry_parent_class)->finalize (object);
162 _get_unique_id (void)
170 * registerApplication:
171 * @application: a reference to the requesting @Application
172 * return values: void
174 * Register a new application with the accessibility broker.
178 impl_accessibility_registry_register_application (PortableServer_Servant servant,
179 const Accessibility_Application application,
180 CORBA_Environment * ev)
182 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
185 fprintf (stderr, "registering app %p\n", application);
187 spi_desktop_add_application (registry->desktop, application);
189 Accessibility_Application__set_id (application, _get_unique_id (), ev);
192 * TODO: change the implementation below to a WM-aware one;
193 * e.g. don't add all apps to the SpiDesktop
197 #ifdef USE_A_HASH_IN_FUTURE
199 compare_corba_objects (gconstpointer p1, gconstpointer p2)
201 CORBA_Environment ev;
205 fprintf (stderr, "comparing %p to %p\n",
209 retval = !CORBA_Object_is_equivalent ((CORBA_Object) p1, (CORBA_Object) p2, &ev);
215 register_with_toolkits (SpiRegistry *spi_registry_bonobo_object, EventTypeStruct *etype, CORBA_Environment *ev)
220 Accessibility_Desktop desktop;
221 Accessibility_Application app;
222 Accessibility_Registry registry;
223 registry = BONOBO_OBJREF (spi_registry_bonobo_object);
225 /* for each app in each desktop, call ...Application_registerToolkitEventListener */
227 n_desktops = Accessibility_Registry_getDesktopCount (registry, ev);
229 for (i=0; i<n_desktops; ++i)
231 desktop = Accessibility_Registry_getDesktop (registry, i, ev);
232 n_apps = Accessibility_Desktop__get_childCount (desktop, ev);
233 for (j=0; j<n_apps; ++j)
235 app = (Accessibility_Application) Accessibility_Desktop_getChildAtIndex (desktop,
238 Accessibility_Application_registerToolkitEventListener (app,
240 CORBA_string_dup (etype->event_name),
246 #ifdef USE_A_HASH_IN_FUTURE
249 compare_listener_quarks (gconstpointer p1, gconstpointer p2)
251 return (((SpiListenerStruct *)p2)->event_type_quark !=
252 ((SpiListenerStruct *)p1)->event_type_quark);
256 compare_listener_corbaref (gconstpointer p1, gconstpointer p2)
258 return compare_corba_objects (((SpiListenerStruct *)p2)->listener,
259 ((SpiListenerStruct *)p1)->listener);
264 parse_event_type (EventTypeStruct *etype, const char *event_name)
266 gchar **split_string;
269 split_string = g_strsplit (event_name, ":", 4);
270 etype->event_name = event_name;
272 if (!g_ascii_strncasecmp (event_name, "focus:", 6))
274 etype->type_cat = ETYPE_FOCUS;
276 else if (!g_ascii_strncasecmp (event_name, "mouse:", 6))
278 etype->type_cat = ETYPE_MOUSE;
280 else if (!g_ascii_strncasecmp (event_name, "object:", 7))
282 etype->type_cat = ETYPE_OBJECT;
284 else if (!g_ascii_strncasecmp (event_name, "window:", 7))
286 etype->type_cat = ETYPE_WINDOW;
288 else if (!g_ascii_strncasecmp (event_name, "keyboard:", 9))
290 etype->type_cat = ETYPE_KEYBOARD;
294 etype->type_cat = ETYPE_TOOLKIT;
299 etype->major = g_quark_from_string (split_string[1]);
302 etype->minor = g_quark_from_string (s = g_strconcat (split_string[1], split_string[2], NULL));
306 etype->detail = g_quark_from_string (split_string[3]);
310 etype->detail = g_quark_from_static_string ("");
315 etype->minor = etype->major;
316 etype->detail = g_quark_from_static_string (""); //etype->major;
321 etype->major = g_quark_from_static_string ("");
322 etype->minor = etype->major;
323 etype->detail = etype->major;
326 g_strfreev (split_string);
330 * deregisterApplication:
331 * @application: a reference to the @Application
332 * to be deregistered.
333 * return values: void
335 * De-register an application previously registered with the broker.
339 impl_accessibility_registry_deregister_application (PortableServer_Servant servant,
340 const Accessibility_Application application,
341 CORBA_Environment * ev)
343 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
345 spi_desktop_remove_application (registry->desktop, application);
348 fprintf (stderr, "de-registered app %p\n", application);
353 get_listener_list (SpiRegistry *registry,
354 EventTypeCategory cat)
363 ret = ®istry->object_listeners;
366 ret = ®istry->window_listeners;
370 ret = ®istry->toolkit_listeners;
381 * CORBA Accessibility::Registry::registerGlobalEventListener method implementation
384 impl_accessibility_registry_register_global_event_listener (
385 PortableServer_Servant servant,
386 Accessibility_EventListener listener,
387 const CORBA_char *event_name,
388 CORBA_Environment *ev)
390 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
391 SpiListenerStruct *ls = spi_listener_struct_new (listener, ev);
392 EventTypeStruct etype;
395 #ifdef SPI_LISTENER_DEBUG
396 fprintf (stderr, "registering for events of type %s\n", event_name);
399 /* parse, check major event type and add listener accordingly */
400 parse_event_type (&etype, event_name);
401 ls->event_type_quark = etype.minor;
402 ls->event_type_cat = etype.type_cat;
404 list = get_listener_list (registry, etype.type_cat);
408 *list = g_list_prepend (*list, ls);
410 if (etype.type_cat == ETYPE_TOOLKIT)
412 register_with_toolkits (registry, &etype, ev);
417 spi_listener_struct_free (ls, ev);
421 static SpiReEntrantContinue
422 remove_listener_cb (GList * const *list, gpointer user_data)
424 SpiListenerStruct *ls = (SpiListenerStruct *) (*list)->data;
425 CORBA_Environment ev;
426 Accessibility_EventListener listener = user_data;
428 CORBA_exception_init (&ev);
430 if (CORBA_Object_is_equivalent (ls->listener, listener, &ev))
432 spi_re_entrant_list_delete_link (list);
433 spi_listener_struct_free (ls, &ev);
436 CORBA_exception_free (&ev);
438 return SPI_RE_ENTRANT_CONTINUE;
442 * CORBA Accessibility::Registry::deregisterGlobalEventListenerAll method implementation
445 impl_accessibility_registry_deregister_global_event_listener_all (
446 PortableServer_Servant servant,
447 Accessibility_EventListener listener,
448 CORBA_Environment *ev)
452 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
454 lists[0] = ®istry->object_listeners;
455 lists[1] = ®istry->window_listeners;
456 lists[2] = ®istry->toolkit_listeners;
458 for (i = 0; i < sizeof (lists) / sizeof (lists[0]); i++)
460 spi_re_entrant_list_foreach (lists [i], remove_listener_cb, listener);
466 * CORBA Accessibility::Registry::deregisterGlobalEventListener method implementation
469 impl_accessibility_registry_deregister_global_event_listener (
470 PortableServer_Servant servant,
471 Accessibility_EventListener listener,
472 const CORBA_char *event_name,
473 CORBA_Environment *ev)
475 SpiRegistry *registry;
476 EventTypeStruct etype;
478 registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
480 parse_event_type (&etype, (char *) event_name);
482 spi_re_entrant_list_foreach (get_listener_list (registry, etype.type_cat),
483 remove_listener_cb, listener);
489 * return values: a short integer indicating the current number of
492 * Get the current number of desktops.
496 impl_accessibility_registry_get_desktop_count (PortableServer_Servant servant,
497 CORBA_Environment * ev)
499 /* TODO: implement support for multiple virtual desktops */
500 CORBA_short n_desktops;
501 n_desktops = (CORBA_short) 1;
508 * @n: the index of the requested @Desktop.
509 * return values: a reference to the requested @Desktop.
511 * Get the nth accessible desktop.
514 static Accessibility_Desktop
515 impl_accessibility_registry_get_desktop (PortableServer_Servant servant,
517 CORBA_Environment * ev)
519 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
521 /* TODO: implement support for multiple virtual desktops */
524 return (Accessibility_Desktop)
525 bonobo_object_dup_ref (BONOBO_OBJREF (registry->desktop), ev);
529 return (Accessibility_Desktop) CORBA_OBJECT_NIL;
536 * return values: a sequence containing references to
539 * Get a list of accessible desktops.
542 static Accessibility_DesktopSeq *
543 impl_accessibility_registry_get_desktop_list (PortableServer_Servant servant,
544 CORBA_Environment * ev)
546 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
547 Accessibility_DesktopSeq *desktops;
549 desktops = Accessibility_DesktopSeq__alloc ();
550 desktops->_length = desktops->_maximum = 1;
551 desktops->_buffer = Accessibility_DesktopSeq_allocbuf (desktops->_length);
552 desktops->_buffer [0] = bonobo_object_dup_ref (BONOBO_OBJREF (registry->desktop), ev);
558 static Accessibility_DeviceEventController
559 impl_accessibility_registry_get_device_event_controller (PortableServer_Servant servant,
560 CORBA_Environment *ev)
562 SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
564 if (!registry->de_controller)
566 registry->de_controller = spi_device_event_controller_new (registry);
569 return bonobo_object_dup_ref (BONOBO_OBJREF (registry->de_controller), ev);
573 CORBA_Environment *ev;
574 Bonobo_Unknown source;
575 EventTypeStruct etype;
576 Accessibility_Event e_out;
579 static SpiReEntrantContinue
580 notify_listeners_cb (GList * const *list, gpointer user_data)
582 SpiListenerStruct *ls;
583 NotifyContext *ctx = user_data;
590 #ifdef SPI_LISTENER_DEBUG
591 fprintf (stderr, "event quarks: %lx %lx %lx\n", ls->event_type_quark, ctx->etype.major, ctx->etype.minor);
592 fprintf (stderr, "event name: %s\n", ctx->etype.event_name);
595 if ((ls->event_type_quark == ctx->etype.major) ||
596 (ls->event_type_quark == ctx->etype.minor))
599 fprintf (stderr, "notifying listener %d\n", 0);
600 /* g_list_index (list, l->data)); */
601 s = Accessibility_Accessible__get_name (ctx->source, ctx->ev);
602 fprintf (stderr, "event source name %s\n", s);
606 ctx->e_out.source = CORBA_Object_duplicate (ctx->source, ctx->ev);
607 if (BONOBO_EX (ctx->ev))
609 return SPI_RE_ENTRANT_CONTINUE;
612 if ((*list) && (*list)->data == ls)
614 Accessibility_EventListener_notifyEvent (
615 (Accessibility_EventListener) ls->listener, &ctx->e_out, ctx->ev);
616 if (ctx->ev->_major != CORBA_NO_EXCEPTION)
618 DBG (1, g_warning ("Accessibility app error: exception during "
619 "event notification: %s\n",
620 CORBA_exception_id (ctx->ev)));
621 if (ctx->ev->_major == CORBA_SYSTEM_EXCEPTION)
622 CORBA_exception_init (ctx->ev);
623 /* clear system exception on notify, it means listener is dead but
624 * that's no concern of the event source :-) */
627 else /* dup re-entered */
629 CORBA_Object_release (ctx->e_out.source, ctx->ev);
633 return SPI_RE_ENTRANT_CONTINUE;
637 impl_registry_notify_event (PortableServer_Servant servant,
638 const Accessibility_Event *e,
639 CORBA_Environment *ev)
641 SpiRegistry *registry;
645 registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
647 parse_event_type (&ctx.etype, e->type);
649 list = get_listener_list (registry, ctx.etype.type_cat);
655 CORBA_any__copy (&ctx.e_out.any_data, &e->any_data);
656 ctx.source = e->source;
657 spi_re_entrant_list_foreach (list, notify_listeners_cb, &ctx);
662 spi_registry_class_init (SpiRegistryClass *klass)
664 GObjectClass * object_class = (GObjectClass *) klass;
665 POA_Accessibility_Registry__epv *epv = &klass->epv;
667 spi_registry_parent_class = g_type_class_ref (SPI_LISTENER_TYPE);
669 object_class->finalize = spi_registry_object_finalize;
671 klass->parent_class.epv.notifyEvent = impl_registry_notify_event;
673 epv->registerApplication = impl_accessibility_registry_register_application;
674 epv->deregisterApplication = impl_accessibility_registry_deregister_application;
675 epv->registerGlobalEventListener = impl_accessibility_registry_register_global_event_listener;
676 epv->deregisterGlobalEventListener = impl_accessibility_registry_deregister_global_event_listener;
677 epv->deregisterGlobalEventListenerAll = impl_accessibility_registry_deregister_global_event_listener_all;
678 epv->getDeviceEventController = impl_accessibility_registry_get_device_event_controller;
679 epv->getDesktopCount = impl_accessibility_registry_get_desktop_count;
680 epv->getDesktop = impl_accessibility_registry_get_desktop;
681 epv->getDesktopList = impl_accessibility_registry_get_desktop_list;
685 spi_registry_init (SpiRegistry *registry)
687 spi_registry_set_debug (g_getenv ("AT_SPI_DEBUG"));
688 registry->object_listeners = NULL;
689 registry->window_listeners = NULL;
690 registry->toolkit_listeners = NULL;
691 registry->desktop = spi_desktop_new ();
692 /* Register callback notification for application addition and removal */
693 g_signal_connect (G_OBJECT (registry->desktop),
695 G_CALLBACK (desktop_add_application),
698 g_signal_connect (G_OBJECT (registry->desktop),
699 "application_removed",
700 G_CALLBACK (desktop_remove_application),
703 registry->de_controller = spi_device_event_controller_new (registry);
706 BONOBO_TYPE_FUNC_FULL (SpiRegistry,
707 Accessibility_Registry,
712 spi_registry_new (void)
714 SpiRegistry *retval = g_object_new (SPI_REGISTRY_TYPE, NULL);
715 bonobo_object_set_immortal (BONOBO_OBJECT (retval), TRUE);