X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=registryd%2Fregistry.c;h=31dbb81d0debfe7493111326aed39d3c6230262d;hb=99f0f510b665fb2298bc8cdd6613e384e445406b;hp=ce196c5136c0da3c9da179b38b159c16664b91c7;hpb=336746297d374424847b92b8109cd43d00dde230;p=platform%2Fcore%2Fuifw%2Fat-spi2-atk.git diff --git a/registryd/registry.c b/registryd/registry.c index ce196c5..31dbb81 100644 --- a/registryd/registry.c +++ b/registryd/registry.c @@ -2,7 +2,8 @@ * AT-SPI - Assistive Technology Service Provider Interface * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) * - * Copyright 2001 Sun Microsystems Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -20,43 +21,33 @@ * Boston, MA 02111-1307, USA. */ -/* - * registry.c: the main accessibility service registry implementation - */ +/* registry.c: the main accessibility service registry implementation */ + +#undef SPI_LISTENER_DEBUG +#undef SPI_DEBUG +#undef SPI_QUEUE_DEBUG +#include #ifdef SPI_DEBUG -#include +# include #endif -#include -#include -/* - * This pulls the CORBA definitions for the "Accessibility::Registry" server - */ -#include +#include +#include "../libspi/spi-private.h" +#include "registry.h" -/* - * We'd like to replace the dependance on X-isms with a wrapper layer, - * to the extent that it can't be done with pure GDK. - * Anyone want to help? - */ -#include -#include +/* Our parent GObject type */ +#define PARENT_TYPE SPI_LISTENER_TYPE -/* - * This pulls the definition for the BonoboObject (GType) - */ -#include "registry.h" +/* A pointer to our parent object class */ +static SpiListenerClass *spi_registry_parent_class; -/* - * Our parent GObject type - */ -#define PARENT_TYPE LISTENER_TYPE +static GQuark _deactivate_quark = 0; +static GQuark _activate_quark = 0; +static GQuark _state_quark = 0; +static GQuark _state_changed_focused_quark = 0; -/* - * A pointer to our parent object class - */ -static ListenerClass *registry_parent_class; +int _dbg = 0; typedef enum { ETYPE_FOCUS, @@ -64,45 +55,120 @@ typedef enum { ETYPE_PROPERTY, ETYPE_WINDOW, ETYPE_TOOLKIT, + ETYPE_KEYBOARD, + ETYPE_MOUSE, ETYPE_LAST_DEFINED } EventTypeCategory; typedef struct { - char *event_name; + const char *event_name; EventTypeCategory type_cat; - char * major; - char * minor; - char * detail; - guint hash; + GQuark major; /* from string segment[1] */ + GQuark minor; /* from string segment[1]+segment[2] */ + GQuark detail; /* from string segment[3] (not concatenated) */ } EventTypeStruct; typedef struct { Accessibility_EventListener listener; - guint event_type_hash; + GQuark event_type_quark; EventTypeCategory event_type_cat; -} ListenerStruct; +} SpiListenerStruct; -/* static function prototypes */ -static void _registry_notify_listeners ( GList *listeners, - const Accessibility_Event *e, - CORBA_Environment *ev); +static void +spi_registry_set_debug (const char *debug_flag_string) +{ + if (debug_flag_string) + _dbg = (int) g_ascii_strtod (debug_flag_string, NULL); +} -static long _get_unique_id(); +SpiListenerStruct * +spi_listener_struct_new (Accessibility_EventListener listener, CORBA_Environment *ev) +{ + SpiListenerStruct *retval = g_malloc (sizeof (SpiListenerStruct)); + retval->listener = bonobo_object_dup_ref (listener, ev); + return retval; +} + + +void +spi_listener_struct_free (SpiListenerStruct *ls, CORBA_Environment *ev) +{ + bonobo_object_release_unref (ls->listener, ev); + g_free (ls); +} + +static void +desktop_add_application (SpiDesktop *desktop, + guint index, gpointer data) +{ + BonoboObject *registry = BONOBO_OBJECT (data); + Accessibility_Event e; + CORBA_Environment ev; + Accessibility_Accessible a; + + CORBA_exception_init (&ev); + e.type = "object:children-changed:add"; + e.source = BONOBO_OBJREF (desktop); + e.detail1 = index; + e.detail2 = 0; + a = Accessibility_Accessible_getChildAtIndex (BONOBO_OBJREF (desktop), + index, &ev); + /* FIXME + spi_init_any_object (&e.any_data, a); + */ + spi_init_any_nil (&e.any_data); + Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry), + &e, &ev); + bonobo_object_release_unref (a, &ev); + CORBA_exception_free (&ev); +} + + + +static void +desktop_remove_application (SpiDesktop *desktop, + guint index, gpointer data) +{ + BonoboObject *registry = BONOBO_OBJECT (data); + Accessibility_Event e; + Accessibility_Accessible a; + CORBA_Environment ev; + + CORBA_exception_init (&ev); + + e.type = "object:children-changed:remove"; + e.source = BONOBO_OBJREF (desktop); + e.detail1 = index; + e.detail2 = 0; + a = Accessibility_Accessible_getChildAtIndex (BONOBO_OBJREF (desktop), + index, &ev); + /* FIXME + spi_init_any_object (&e.any_data, a); + */ + spi_init_any_nil (&e.any_data); + Accessibility_Accessible_unref (a, &ev); + Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry), + &e, &ev); + Accessibility_Desktop_unref (e.source, &ev); + CORBA_exception_free (&ev); +} -static gboolean _device_event_controller_hook (gpointer source); -/* - * Implemented GObject::finalize - */ static void -registry_object_finalize (GObject *object) +spi_registry_object_finalize (GObject *object) { -/* Registry *registry = REGISTRY (object); */ - GObjectClass *object_class = G_OBJECT_GET_CLASS( object); + DBG (1, g_warning ("spi_registry_object_finalize called\n")); + g_object_unref (SPI_REGISTRY (object)->de_controller); - printf("registry_object_finalize called\n"); + G_OBJECT_CLASS (spi_registry_parent_class)->finalize (object); +} + +static long +_get_unique_id (void) +{ + static long id = 0; - object_class->finalize (object); + return ++id; } /** @@ -118,23 +184,22 @@ impl_accessibility_registry_register_application (PortableServer_Servant servant const Accessibility_Application application, CORBA_Environment * ev) { - Registry *registry = REGISTRY (bonobo_object_from_servant (servant)); + SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); #ifdef SPI_DEBUG fprintf (stderr, "registering app %p\n", application); #endif - registry->desktop->applications = g_list_append (registry->desktop->applications, - bonobo_object_dup_ref (application, ev)); + spi_desktop_add_application (registry->desktop, application); - /* TODO: create unique string here (with libuuid call ?) and hash ? */ - Accessibility_Application__set_id (application, _get_unique_id(), ev); + Accessibility_Application__set_id (application, _get_unique_id (), ev); /* * TODO: change the implementation below to a WM-aware one; - * e.g. don't add all apps to the Desktop + * e.g. don't add all apps to the SpiDesktop */ } +#ifdef USE_A_HASH_IN_FUTURE static gint compare_corba_objects (gconstpointer p1, gconstpointer p2) { @@ -149,9 +214,10 @@ compare_corba_objects (gconstpointer p1, gconstpointer p2) retval = !CORBA_Object_is_equivalent ((CORBA_Object) p1, (CORBA_Object) p2, &ev); return retval; } +#endif static void -register_with_toolkits (Registry *registry_bonobo_object, EventTypeStruct *etype, CORBA_Environment *ev) +register_with_toolkits (SpiRegistry *spi_registry_bonobo_object, EventTypeStruct *etype, CORBA_Environment *ev) { gint n_desktops; gint n_apps; @@ -159,7 +225,7 @@ register_with_toolkits (Registry *registry_bonobo_object, EventTypeStruct *etype Accessibility_Desktop desktop; Accessibility_Application app; Accessibility_Registry registry; - registry = BONOBO_OBJREF (registry_bonobo_object); + registry = BONOBO_OBJREF (spi_registry_bonobo_object); /* for each app in each desktop, call ...Application_registerToolkitEventListener */ @@ -177,38 +243,45 @@ register_with_toolkits (Registry *registry_bonobo_object, EventTypeStruct *etype Accessibility_Application_registerToolkitEventListener (app, registry, CORBA_string_dup (etype->event_name), - ev); } } } +#ifdef USE_A_HASH_IN_FUTURE + static gint -compare_listener_hash (gconstpointer p1, gconstpointer p2) +compare_listener_quarks (gconstpointer p1, gconstpointer p2) { - return (((ListenerStruct *)p2)->event_type_hash - ((ListenerStruct *)p1)->event_type_hash); + return (((SpiListenerStruct *)p2)->event_type_quark != + ((SpiListenerStruct *)p1)->event_type_quark); } static gint compare_listener_corbaref (gconstpointer p1, gconstpointer p2) { - return compare_corba_objects (((ListenerStruct *)p2)->listener, - ((ListenerStruct *)p1)->listener); + return compare_corba_objects (((SpiListenerStruct *)p2)->listener, + ((SpiListenerStruct *)p1)->listener); } +#endif static void -parse_event_type (EventTypeStruct *etype, char *event_name) +parse_event_type (EventTypeStruct *etype, const char *event_name) { - guint nbytes = 0; gchar **split_string; + gchar *s; - split_string = g_strsplit(event_name, ":", 4); - etype->event_name = g_strndup(event_name, 255); + split_string = g_strsplit (event_name, ":", 4); + etype->event_name = event_name; if (!g_ascii_strncasecmp (event_name, "focus:", 6)) { etype->type_cat = ETYPE_FOCUS; } + else if (!g_ascii_strncasecmp (event_name, "mouse:", 6)) + { + etype->type_cat = ETYPE_MOUSE; + } else if (!g_ascii_strncasecmp (event_name, "object:", 7)) { etype->type_cat = ETYPE_OBJECT; @@ -217,6 +290,10 @@ parse_event_type (EventTypeStruct *etype, char *event_name) { etype->type_cat = ETYPE_WINDOW; } + else if (!g_ascii_strncasecmp (event_name, "keyboard:", 9)) + { + etype->type_cat = ETYPE_KEYBOARD; + } else { etype->type_cat = ETYPE_TOOLKIT; @@ -224,36 +301,34 @@ parse_event_type (EventTypeStruct *etype, char *event_name) if (split_string[1]) { - etype->major = split_string[1]; + etype->major = g_quark_from_string (split_string[1]); if (split_string[2]) { - etype->minor = split_string[2]; + etype->minor = g_quark_from_string (s = g_strconcat (split_string[1], split_string[2], NULL)); + g_free (s); if (split_string[3]) { - etype->detail = split_string[3]; - etype->hash = g_str_hash ( g_strconcat (split_string[1], split_string[2], split_string[3], NULL)); + etype->detail = g_quark_from_string (split_string[3]); } else { - etype->detail = g_strdup (""); - etype->hash = g_str_hash ( g_strconcat (split_string[1], split_string[2], NULL)); + etype->detail = g_quark_from_static_string (""); } } else { - etype->minor = g_strdup (""); - etype->hash = g_str_hash ( split_string[1]); + etype->minor = etype->major; + etype->detail = g_quark_from_static_string (""); //etype->major; } } else { - etype->major = g_strdup (""); - etype->minor = g_strdup (""); - etype->detail = g_strdup (""); - etype->hash = g_str_hash (""); + etype->major = g_quark_from_static_string (""); + etype->minor = etype->major; + etype->detail = etype->major; } - /* TODO: don't forget to free the strings from caller when done ! */ + g_strfreev (split_string); } /** @@ -270,30 +345,41 @@ impl_accessibility_registry_deregister_application (PortableServer_Servant serva const Accessibility_Application application, CORBA_Environment * ev) { - Registry *registry = REGISTRY (bonobo_object_from_servant (servant)); - GList *list = g_list_find_custom (registry->desktop->applications, &application, compare_corba_objects); + SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); + + spi_desktop_remove_application (registry->desktop, application); #ifdef SPI_DEBUG - gint i; + fprintf (stderr, "de-registered app %p\n", application); #endif +} - if (list) +static GList ** +get_listener_list (SpiRegistry *registry, + EventTypeCategory cat) +{ + GList **ret; + + switch (cat) { -#ifdef SPI_DEBUG - fprintf (stderr, "deregistering application %p\n", application); -#endif - registry->desktop->applications = g_list_delete_link (registry->desktop->applications, list); -#ifdef SPI_DEBUG - fprintf (stderr, "there are now %d apps registered.\n", g_list_length (registry->desktop->applications)); - for (i = 0; i < g_list_length (registry->desktop->applications); ++i) { - fprintf (stderr, "getting application %d\n", i); - fprintf (stderr, "object address %p\n", - g_list_nth_data (registry->desktop->applications, i)); - } -#endif + case ETYPE_OBJECT: + case ETYPE_PROPERTY: + case ETYPE_FOCUS: + case ETYPE_KEYBOARD: + ret = ®istry->object_listeners; + break; + case ETYPE_WINDOW: + ret = ®istry->window_listeners; + break; + case ETYPE_MOUSE: + case ETYPE_TOOLKIT: + ret = ®istry->toolkit_listeners; + break; + default: + ret = NULL; + break; } - else - fprintf (stderr, "could not deregister application\n"); + return ret; } /* @@ -301,44 +387,60 @@ impl_accessibility_registry_deregister_application (PortableServer_Servant serva */ static void impl_accessibility_registry_register_global_event_listener ( - PortableServer_Servant servant, - Accessibility_EventListener listener, - const CORBA_char *event_name, - CORBA_Environment *ev) + PortableServer_Servant servant, + Accessibility_EventListener listener, + const CORBA_char *event_name, + CORBA_Environment *ev) { - Registry *registry = REGISTRY (bonobo_object_from_servant (servant)); - ListenerStruct *ls = g_malloc (sizeof (ListenerStruct)); + SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); + SpiListenerStruct *ls = spi_listener_struct_new (listener, ev); EventTypeStruct etype; - gboolean is_toolkit_specific = TRUE; + GList **list; - fprintf(stderr, "registering for events of type %s\n", event_name); +#ifdef SPI_LISTENER_DEBUG + fprintf (stderr, "registering for events of type %s\n", event_name); +#endif /* parse, check major event type and add listener accordingly */ parse_event_type (&etype, event_name); - ls->event_type_hash = etype.hash; + ls->event_type_quark = etype.minor; ls->event_type_cat = etype.type_cat; - switch (etype.type_cat) + list = get_listener_list (registry, etype.type_cat); + + if (list) + { + *list = g_list_prepend (*list, ls); + + if (etype.type_cat == ETYPE_TOOLKIT) + { + register_with_toolkits (registry, &etype, ev); + } + } + else + { + spi_listener_struct_free (ls, ev); + } +} + +static SpiReEntrantContinue +remove_listener_cb (GList * const *list, gpointer user_data) +{ + SpiListenerStruct *ls = (SpiListenerStruct *) (*list)->data; + CORBA_Environment ev; + Accessibility_EventListener listener = user_data; + + CORBA_exception_init (&ev); + + if (CORBA_Object_is_equivalent (ls->listener, listener, &ev)) { - case (ETYPE_FOCUS) : - case (ETYPE_OBJECT) : - case (ETYPE_PROPERTY) : - ls->listener = CORBA_Object_duplicate (listener, ev); - registry->object_listeners = - g_list_append (registry->object_listeners, ls); - break; - case (ETYPE_WINDOW) : - /* Support for Window Manager Events is not yet implemented */ - break; - case (ETYPE_TOOLKIT) : - ls->listener = CORBA_Object_duplicate (listener, ev); - registry->toolkit_listeners = - g_list_append (registry->toolkit_listeners, ls); - register_with_toolkits (registry, &etype, ev); - break; - default: - break; + spi_re_entrant_list_delete_link (list); + spi_listener_struct_free (ls, &ev); } + + CORBA_exception_free (&ev); + + return SPI_RE_ENTRANT_CONTINUE; } /* @@ -346,80 +448,44 @@ impl_accessibility_registry_register_global_event_listener ( */ static void impl_accessibility_registry_deregister_global_event_listener_all ( - PortableServer_Servant servant, - Accessibility_EventListener listener, - CORBA_Environment *ev) + PortableServer_Servant servant, + Accessibility_EventListener listener, + CORBA_Environment *ev) { - Registry *registry = REGISTRY (bonobo_object_from_servant (servant)); - ListenerStruct *ls = g_malloc (sizeof (ListenerStruct)); - GList *list; - ls->listener = listener; - list = g_list_find_custom (registry->object_listeners, ls, - compare_listener_corbaref); + int i; + GList **lists[3]; + SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); - /* - * TODO : de-register with toolkit if the last instance of a listener - * to a particular toolkit event type has been deregistered. - */ + lists[0] = ®istry->object_listeners; + lists[1] = ®istry->window_listeners; + lists[2] = ®istry->toolkit_listeners; - while (list) + for (i = 0; i < sizeof (lists) / sizeof (lists[0]); i++) { - fprintf (stderr, "deregistering listener\n"); - registry->object_listeners = g_list_delete_link (registry->object_listeners, list); - list = g_list_find_custom (registry->object_listeners, ls, compare_listener_corbaref); - } - list = g_list_find_custom (registry->toolkit_listeners, ls, compare_listener_corbaref); - while (list) - { - fprintf (stderr, "deregistering listener\n"); - registry->toolkit_listeners = g_list_delete_link (registry->toolkit_listeners, list); - list = g_list_find_custom (registry->toolkit_listeners, ls, compare_listener_corbaref); + spi_re_entrant_list_foreach (lists [i], remove_listener_cb, listener); } } + /* * CORBA Accessibility::Registry::deregisterGlobalEventListener method implementation */ static void impl_accessibility_registry_deregister_global_event_listener ( - PortableServer_Servant servant, - Accessibility_EventListener listener, - const CORBA_char * event_name, - CORBA_Environment *ev) + PortableServer_Servant servant, + Accessibility_EventListener listener, + const CORBA_char *event_name, + CORBA_Environment *ev) { - Registry *registry = REGISTRY (bonobo_object_from_servant (servant)); - ListenerStruct ls; + SpiRegistry *registry; EventTypeStruct etype; - GList *list; - GList **listeners; - parse_event_type (&etype, event_name); - switch (etype.type_cat) - { - case (ETYPE_OBJECT) : - case (ETYPE_PROPERTY) : - case (ETYPE_FOCUS) : - listeners = ®istry->object_listeners; - break; - case (ETYPE_WINDOW) : - /* Support for Window Manager Events is not yet implemented */ - break; - case (ETYPE_TOOLKIT) : - listeners = ®istry->toolkit_listeners; - break; - default: - break; - } + registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); - ls.event_type_hash = etype.hash; - list = g_list_find_custom (*listeners, &ls, compare_listener_hash); + parse_event_type (&etype, (char *) event_name); - while (list) - { - fprintf (stderr, "deregistering listener\n"); - *listeners = g_list_delete_link (*listeners, list); - list = g_list_find_custom (*listeners, &ls, compare_listener_hash); - } + spi_re_entrant_list_foreach (get_listener_list (registry, etype.type_cat), + remove_listener_cb, listener); } @@ -441,6 +507,7 @@ impl_accessibility_registry_get_desktop_count (PortableServer_Servant servant, return n_desktops; } + /** * getDesktop: * @n: the index of the requested @Desktop. @@ -454,13 +521,13 @@ impl_accessibility_registry_get_desktop (PortableServer_Servant servant, const CORBA_short n, CORBA_Environment * ev) { - Registry *registry = REGISTRY (bonobo_object_from_servant (servant)); + SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); /* TODO: implement support for multiple virtual desktops */ if (n == 0) { return (Accessibility_Desktop) - CORBA_Object_duplicate (BONOBO_OBJREF (registry->desktop), ev); + bonobo_object_dup_ref (BONOBO_OBJREF (registry->desktop), ev); } else { @@ -468,6 +535,7 @@ impl_accessibility_registry_get_desktop (PortableServer_Servant servant, } } + /** * getDesktopList: * return values: a sequence containing references to @@ -480,182 +548,440 @@ static Accessibility_DesktopSeq * impl_accessibility_registry_get_desktop_list (PortableServer_Servant servant, CORBA_Environment * ev) { - /* TODO: implement support for multiple virtual desktops */ - return (Accessibility_DesktopSeq *) NULL; + SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); + Accessibility_DesktopSeq *desktops; + + desktops = Accessibility_DesktopSeq__alloc (); + desktops->_length = desktops->_maximum = 1; + desktops->_buffer = Accessibility_DesktopSeq_allocbuf (desktops->_length); + desktops->_buffer [0] = bonobo_object_dup_ref (BONOBO_OBJREF (registry->desktop), ev); + + return desktops; } + static Accessibility_DeviceEventController impl_accessibility_registry_get_device_event_controller (PortableServer_Servant servant, - CORBA_Environment * ev) + CORBA_Environment *ev) { - Registry *registry = REGISTRY (bonobo_object_from_servant (servant)); - if (!registry->device_event_controller) - registry->device_event_controller = g_object_new (DEVICE_EVENT_CONTROLLER_TYPE, NULL); - return CORBA_Object_duplicate (BONOBO_OBJREF (registry->device_event_controller), ev); + SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); + + if (!registry->de_controller) + { + registry->de_controller = spi_device_event_controller_new (registry); + } + + return bonobo_object_dup_ref (BONOBO_OBJREF (registry->de_controller), ev); } -static void -impl_registry_notify_event (PortableServer_Servant servant, - const Accessibility_Event *e, - CORBA_Environment *ev) +typedef struct { + CORBA_Environment *ev; + Bonobo_Unknown source; + EventTypeStruct etype; + Accessibility_Event e_out; +} NotifyContext; + +static SpiReEntrantContinue +notify_listeners_cb (GList * const *list, gpointer user_data) { - Registry *registry = REGISTRY (bonobo_object_from_servant (servant)); - EventTypeStruct etype; + SpiListenerStruct *ls; + NotifyContext *ctx = user_data; + + ls = (*list)->data; + +#ifdef SPI_LISTENER_DEBUG + fprintf (stderr, "event quarks: %lx %lx %lx\n", ls->event_type_quark, ctx->etype.major, ctx->etype.minor); + fprintf (stderr, "event name: %s\n", ctx->etype.event_name); +#endif + if ((ls->event_type_quark == ctx->etype.major) || + (ls->event_type_quark == ctx->etype.minor)) + { +#ifdef SPI_DEBUG + CORBA_string s; + fprintf (stderr, "notifying listener %d\n", 0); +/* g_list_index (list, l->data)); */ + s = Accessibility_Accessible__get_name (ctx->source, ctx->ev); + fprintf (stderr, "event source name %s\n", s); + CORBA_free (s); + if (BONOBO_EX (ctx->ev)) + { + CORBA_exception_free (ctx->ev); + return SPI_RE_ENTRANT_CONTINUE; + } +#endif + + ctx->e_out.source = ctx->source; + + if ((*list) && (*list)->data == ls) + { + Accessibility_EventListener_notifyEvent ( + (Accessibility_EventListener) ls->listener, &ctx->e_out, ctx->ev); + if (ctx->ev->_major != CORBA_NO_EXCEPTION) + { + DBG (1, g_warning ("Accessibility app error: exception during " + "event notification: %s\n", + CORBA_exception_id (ctx->ev))); + CORBA_exception_free (ctx->ev); + /* FIXME: check that this item is removed from the list + * on system exception by a 'broken' listener */ + } + } + } + + return SPI_RE_ENTRANT_CONTINUE; +} - parse_event_type (&etype, e->type); +static void +registry_emit_event (SpiRegistry *registry, NotifyContext *ctx) +{ + GList **list = get_listener_list (registry, ctx->etype.type_cat); - switch (etype.type_cat) + if (list && *list) { - case (ETYPE_OBJECT) : - case (ETYPE_PROPERTY) : - case (ETYPE_FOCUS) : - _registry_notify_listeners (registry->object_listeners, e, ev); - break; - case (ETYPE_WINDOW) : - _registry_notify_listeners (registry->window_listeners, e, ev); - break; - case (ETYPE_TOOLKIT) : - _registry_notify_listeners (registry->toolkit_listeners, e, ev); - break; - default: - break; + + spi_re_entrant_list_foreach (list, notify_listeners_cb, ctx); } - /* Accessibility_Accessible_unref (e->source, ev);*/ /* This should be here! */ } -static long -_get_unique_id () +static NotifyContext* +registry_clone_notify_context (NotifyContext *ctx) { - static long id = 0; - return ++id; + NotifyContext *new_ctx = g_new0 (NotifyContext, 1); + + new_ctx->ev = NULL; + new_ctx->source = bonobo_object_dup_ref (ctx->source, NULL); + new_ctx->etype.event_name = CORBA_string_dup (ctx->etype.event_name); + new_ctx->etype.type_cat = ctx->etype.type_cat; + new_ctx->etype.major = ctx->etype.major; + new_ctx->etype.minor = ctx->etype.minor; + new_ctx->etype.detail = ctx->etype.detail; + new_ctx->e_out.type = CORBA_string_dup (ctx->e_out.type); + new_ctx->e_out.source = ctx->e_out.source; + new_ctx->e_out.detail1 = ctx->e_out.detail1; + new_ctx->e_out.detail2 = ctx->e_out.detail2; + CORBA_any__copy (&(new_ctx->e_out.any_data), &(ctx->e_out.any_data)); + return new_ctx; } static void -_registry_notify_listeners ( GList *listeners, - const Accessibility_Event *e, - CORBA_Environment *ev) +registry_flush_event_queue (SpiRegistry *registry, + gboolean discard, + CORBA_Environment *ev) { - int n; - int len; - ListenerStruct *ls; - EventTypeStruct etype; - guint minor_hash; - parse_event_type (&etype, e->type); - minor_hash = g_str_hash (g_strconcat (etype.major, etype.minor, NULL)); - len = g_list_length (listeners); + NotifyContext *q_ctx; + while (!g_queue_is_empty (registry->deferred_event_queue)) { + q_ctx = g_queue_pop_tail (registry->deferred_event_queue); +#ifdef SPI_QUEUE_DEBUG + fprintf (stderr, "%s! %s [n=%d] %p\n", (discard ? "discard" : "pop"), + q_ctx->etype.event_name, + (int) registry->deferred_event_queue->length, q_ctx); +#endif + if (!discard) { + q_ctx->ev = ev; + registry_emit_event (registry, q_ctx); + } + if (discard && + (q_ctx->etype.type_cat == ETYPE_OBJECT) && + (q_ctx->etype.major == _state_quark) && + (q_ctx->etype.minor == _state_changed_focused_quark)) { + registry->focus_object = q_ctx->source; +#ifdef SPI_QUEUE_DEBUG + fprintf (stderr, "discard!: set focus_object %p\n", registry->focus_object); +#endif + } + else { + bonobo_object_release_unref (q_ctx->source, NULL); + } + CORBA_free ((void *)q_ctx->etype.event_name); + CORBA_free ((void *)q_ctx->e_out.type); + g_free (q_ctx); + } + registry->is_queueing = FALSE; +} - for (n=0; nevent_type_hash, etype.hash, minor_hash); - fprintf(stderr, "event name: %s\n", etype.event_name); +static gboolean +registry_timeout_flush_queue (gpointer data) +{ + SpiRegistry *registry = data; + CORBA_Environment ev; +#ifdef SPI_QUEUE_DEBUG + fprintf (stderr, "timeout! flushing queue...\n"); +#endif + CORBA_exception_init (&ev); + registry->queue_handler_id = 0; + registry_flush_event_queue (registry, FALSE, &ev); + return FALSE; +} + +static gboolean +registry_discard_on_event (SpiRegistry *registry, NotifyContext *ctx) +{ + gboolean retval = FALSE; + NotifyContext *q_ctx = g_queue_peek_tail (registry->deferred_event_queue); + if ((q_ctx != NULL) && + (ctx->etype.type_cat == ETYPE_WINDOW) && + (ctx->etype.major == _activate_quark)) { + if (CORBA_Object_is_equivalent (ctx->source, q_ctx->source, NULL)) { + retval = TRUE; + } + } + return retval; +} + +static gboolean +registry_reset_on_event (SpiRegistry *registry, NotifyContext *ctx) +{ + return (ctx->etype.type_cat == ETYPE_WINDOW) ? TRUE : FALSE; +} + +#ifdef SPI_QUEUE_DEBUG +#include #endif - if ((ls->event_type_hash == etype.hash) || (ls->event_type_hash == minor_hash)) + +static void +registry_start_queue (SpiRegistry *registry) +{ +#ifdef SPI_QUEUE_DEBUG + struct timeval tp; + gettimeofday (&tp, NULL); + fprintf (stderr, "start queueing at %i.%.6i\n", tp.tv_sec, tp.tv_usec); +#endif + if (registry->queue_handler_id != 0) + g_source_remove (registry->queue_handler_id); + + if (registry->focus_object) + { +#ifdef SPI_QUEUE_DEBUG + fprintf (stderr, "registry_start_queue: release focus_object %p\n", registry->focus_object); +#endif + bonobo_object_release_unref (registry->focus_object, NULL); + registry->focus_object = NULL; + } + registry->queue_handler_id = g_timeout_add_full (G_PRIORITY_HIGH_IDLE, + registry->exit_notify_timeout, + registry_timeout_flush_queue, registry, + NULL); + registry->is_queueing = TRUE; +} + +static gboolean +registry_discard_event (SpiRegistry *registry, NotifyContext *ctx) +{ + gboolean ret = FALSE; + + if (ctx->etype.type_cat == ETYPE_FOCUS) + { + if (registry->focus_object) { -#ifdef SPI_DEBUG - fprintf(stderr, "notifying listener #%d\n", n); - fprintf(stderr, "event source name %s\n", Accessibility_Accessible__get_name(e->source, ev)); + if (CORBA_Object_is_equivalent (registry->focus_object, ctx->source, NULL)) + { + ret = TRUE; + } +#ifdef SPI_QUEUE_DEBUG + fprintf (stderr, "registry_discard_event: release focus_object %p\n", registry->focus_object); #endif - e->source = CORBA_Object_duplicate (e->source, ev); - Accessibility_Accessible_ref ( e->source, ev); - Accessibility_EventListener_notifyEvent ((Accessibility_EventListener) ls->listener, - e, - ev); - if (ev->_major != CORBA_NO_EXCEPTION) { - fprintf(stderr, - ("Accessibility app error: exception during event notification: %s\n"), - CORBA_exception_id(ev)); - exit(-1); - } + bonobo_object_release_unref (registry->focus_object, NULL); + registry->focus_object = NULL; } } + return ret; +} + +static gboolean +registry_defer_on_event (SpiRegistry *registry, NotifyContext *ctx) +{ + gboolean defer = FALSE; + if ((ctx->etype.type_cat == ETYPE_WINDOW) && + (ctx->etype.major == _deactivate_quark)) { + defer = TRUE; + registry_start_queue (registry); + } + /* defer all object:state-change events after a window:deactivate */ + else if ((ctx->etype.type_cat == ETYPE_FOCUS) || + ((ctx->etype.type_cat == ETYPE_OBJECT) && + (ctx->etype.major == _state_quark))) { + defer = TRUE; + } + return defer; } -static gboolean _device_event_controller_hook (gpointer p) +static gboolean +registry_queue_event (SpiRegistry *registry, NotifyContext *ctx) { - Registry *registry = (Registry *)p; - DeviceEventController *controller = registry->device_event_controller; - if (controller) - device_event_controller_check_key_event (controller); + NotifyContext *q_ctx = registry_clone_notify_context (ctx); +#ifdef SPI_QUEUE_DEBUG + if (q_ctx->etype.type_cat != ETYPE_MOUSE) + fprintf (stderr, "push! %s %p\n", q_ctx->etype.event_name, q_ctx); +#endif + if (registry->is_queueing) + { + g_queue_push_head (registry->deferred_event_queue, q_ctx); + + return FALSE; + } + else + { + bonobo_object_release_unref (q_ctx->source, NULL); + CORBA_free ((void *)q_ctx->etype.event_name); + CORBA_free ((void *)q_ctx->e_out.type); + g_free (q_ctx); + return TRUE; + } +} + +/** + * Dispose of event in one of several ways: + * 1) discard; + * 2) initiate queuing and push onto queue (below) + * 3) push on existing queue to either pop on timeout or on subsequent event + * 4) pass-through immediately + * 5) pass-through, discarding queued events + * 6) emit queued events and then pass through + **/ +static gboolean +registry_filter_event (SpiRegistry *registry, NotifyContext *ctx, + CORBA_Environment *ev) +{ + g_assert (ctx != NULL); + + if (registry_discard_event (registry, ctx)) + return FALSE; + + if (registry_defer_on_event (registry, ctx)) { /* #2, #3 */ + if (registry->is_queueing) { + return registry_queue_event (registry, ctx); + } + else { /* #4a */ + return TRUE; + } + } + else if (registry_reset_on_event (registry, ctx)) { /* #5, #6 */ + gboolean discard = registry_discard_on_event (registry, ctx); +#ifdef SPI_QUEUE_DEBUG + fprintf (stderr, "event %s caused reset, discard=%d\n", + ctx->etype.event_name, (int) discard); + { + struct timeval tp; + gettimeofday (&tp, NULL); + fprintf (stderr, "event at %i.%.6i\n", tp.tv_sec, tp.tv_usec); + } +#endif + registry_flush_event_queue (registry, discard, ev); + return (discard ? FALSE : TRUE); + } + else { /* #4b */ return TRUE; + } } static void -registry_class_init (RegistryClass *klass) +impl_registry_notify_event (PortableServer_Servant servant, + const Accessibility_Event *e, + CORBA_Environment *ev) { - GObjectClass * object_class = (GObjectClass *) klass; - POA_Accessibility_Registry__epv *epv = &klass->epv; - - registry_parent_class = g_type_class_ref (LISTENER_TYPE); + SpiRegistry *registry; + NotifyContext ctx; + static int level = 0; + + level++; + registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); + + parse_event_type (&ctx.etype, e->type); + + ctx.ev = ev; + ctx.e_out = *e; + ctx.source = e->source; + +#ifdef SPI_QUEUE_DEBUG + if (ctx.etype.type_cat != ETYPE_MOUSE) + fprintf (stderr, "filter! %s level: %d\n", ctx.etype.event_name, level); +#endif + if (registry_filter_event (registry, &ctx, ev)) { +#ifdef SPI_QUEUE_DEBUG + if (ctx.etype.type_cat != ETYPE_MOUSE) + fprintf (stderr, "emit! %s level: %d\n", ctx.etype.event_name, level); +#endif + registry_emit_event (registry, &ctx); + } + level--; +} - object_class->finalize = registry_object_finalize; +static void +spi_registry_class_init (SpiRegistryClass *klass) +{ + GObjectClass * object_class = (GObjectClass *) klass; + POA_Accessibility_Registry__epv *epv = &klass->epv; - epv->registerApplication = impl_accessibility_registry_register_application; - epv->deregisterApplication = impl_accessibility_registry_deregister_application; - epv->registerGlobalEventListener = impl_accessibility_registry_register_global_event_listener; - epv->deregisterGlobalEventListener = impl_accessibility_registry_deregister_global_event_listener; - epv->deregisterGlobalEventListenerAll = impl_accessibility_registry_deregister_global_event_listener_all; - epv->getDeviceEventController = impl_accessibility_registry_get_device_event_controller; - epv->getDesktopCount = impl_accessibility_registry_get_desktop_count; - epv->getDesktop = impl_accessibility_registry_get_desktop; - epv->getDesktopList = impl_accessibility_registry_get_desktop_list; + spi_registry_parent_class = g_type_class_ref (SPI_LISTENER_TYPE); + + object_class->finalize = spi_registry_object_finalize; - ((ListenerClass *) klass)->epv.notifyEvent = impl_registry_notify_event; + klass->parent_class.epv.notifyEvent = impl_registry_notify_event; + + epv->registerApplication = impl_accessibility_registry_register_application; + epv->deregisterApplication = impl_accessibility_registry_deregister_application; + epv->registerGlobalEventListener = impl_accessibility_registry_register_global_event_listener; + epv->deregisterGlobalEventListener = impl_accessibility_registry_deregister_global_event_listener; + epv->deregisterGlobalEventListenerAll = impl_accessibility_registry_deregister_global_event_listener_all; + epv->getDeviceEventController = impl_accessibility_registry_get_device_event_controller; + epv->getDesktopCount = impl_accessibility_registry_get_desktop_count; + epv->getDesktop = impl_accessibility_registry_get_desktop; + epv->getDesktopList = impl_accessibility_registry_get_desktop_list; + _deactivate_quark = g_quark_from_static_string ("deactivate"); + _activate_quark = g_quark_from_static_string ("activate"); + _state_quark = g_quark_from_static_string ("state-changed"); + _state_changed_focused_quark = g_quark_from_static_string ("state-changedfocused"); } static void -registry_init (Registry *registry) +spi_registry_init (SpiRegistry *registry) { + spi_registry_set_debug (g_getenv ("AT_SPI_DEBUG")); + /* + * TODO: FIXME, this module makes the foolish assumptions that + * registryd uses the same display as the apps, and that the + * DISPLAY environment variable is set. + */ + gdk_init (NULL, NULL); + registry->object_listeners = NULL; registry->window_listeners = NULL; registry->toolkit_listeners = NULL; - registry->applications = NULL; - registry->desktop = desktop_new(); - registry->device_event_controller = NULL; - registry->kbd_event_hook = _device_event_controller_hook; -} - -GType -registry_get_type (void) -{ - static GType type = 0; - - if (!type) { - static const GTypeInfo tinfo = { - sizeof (RegistryClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) registry_class_init, - (GClassFinalizeFunc) NULL, - NULL, /* class data */ - sizeof (Registry), - 0, /* n preallocs */ - (GInstanceInitFunc) registry_init, - NULL /* value table */ - }; - /* - * Here we use bonobo_type_unique instead of - * gtk_type_unique, this auto-generates a load of - * CORBA structures for us. All derived types must - * use bonobo_type_unique. - */ - type = bonobo_type_unique ( - PARENT_TYPE, - POA_Accessibility_Registry__init, - NULL, - G_STRUCT_OFFSET (RegistryClass, epv), - &tinfo, - "Registry"); - } - - return type; + registry->deferred_event_queue = g_queue_new (); + registry->exit_notify_timeout = 200; + registry->queue_handler_id = 0; + /* + * The focus_object is set when a state-change:focused event is discarded + * after a window:deactivate event is received because a window:activate + * event has been received for the same window within the timeout period. + * The focus object is used to suppress a focus event for that object. + * It is released when a window:deactivate event is received. + */ + registry->focus_object = NULL; + registry->desktop = spi_desktop_new (); + /* Register callback notification for application addition and removal */ + g_signal_connect (G_OBJECT (registry->desktop), + "application_added", + G_CALLBACK (desktop_add_application), + registry); + + g_signal_connect (G_OBJECT (registry->desktop), + "application_removed", + G_CALLBACK (desktop_remove_application), + registry); + + registry->de_controller = spi_device_event_controller_new (registry); } -Registry * -registry_new (void) +BONOBO_TYPE_FUNC_FULL (SpiRegistry, + Accessibility_Registry, + PARENT_TYPE, + spi_registry) + +SpiRegistry * +spi_registry_new (void) { - Registry *retval = - REGISTRY (g_object_new (registry_get_type (), NULL)); - return retval; + SpiRegistry *retval = g_object_new (SPI_REGISTRY_TYPE, NULL); + bonobo_object_set_immortal (BONOBO_OBJECT (retval), TRUE); + return retval; }