X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=registryd%2Fregistry.c;h=31dbb81d0debfe7493111326aed39d3c6230262d;hb=99f0f510b665fb2298bc8cdd6613e384e445406b;hp=0224e8a0e85147301d54cfb9133cd337d2a376bd;hpb=2f49c85d0c1ab9db3a47fa74d53c6acdfe44e9bd;p=platform%2Fcore%2Fuifw%2Fat-spi2-atk.git diff --git a/registryd/registry.c b/registryd/registry.c index 0224e8a..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 @@ -23,6 +24,8 @@ /* registry.c: the main accessibility service registry implementation */ #undef SPI_LISTENER_DEBUG +#undef SPI_DEBUG +#undef SPI_QUEUE_DEBUG #include #ifdef SPI_DEBUG @@ -39,6 +42,13 @@ /* A pointer to our parent object class */ static SpiListenerClass *spi_registry_parent_class; +static GQuark _deactivate_quark = 0; +static GQuark _activate_quark = 0; +static GQuark _state_quark = 0; +static GQuark _state_changed_focused_quark = 0; + +int _dbg = 0; + typedef enum { ETYPE_FOCUS, ETYPE_OBJECT, @@ -46,12 +56,12 @@ typedef enum { ETYPE_WINDOW, ETYPE_TOOLKIT, ETYPE_KEYBOARD, - + ETYPE_MOUSE, ETYPE_LAST_DEFINED } EventTypeCategory; typedef struct { - char *event_name; + const char *event_name; EventTypeCategory type_cat; GQuark major; /* from string segment[1] */ GQuark minor; /* from string segment[1]+segment[2] */ @@ -64,6 +74,12 @@ typedef struct { EventTypeCategory event_type_cat; } SpiListenerStruct; +static void +spi_registry_set_debug (const char *debug_flag_string) +{ + if (debug_flag_string) + _dbg = (int) g_ascii_strtod (debug_flag_string, NULL); +} SpiListenerStruct * spi_listener_struct_new (Accessibility_EventListener listener, CORBA_Environment *ev) @@ -81,13 +97,69 @@ spi_listener_struct_free (SpiListenerStruct *ls, CORBA_Environment *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 void spi_registry_object_finalize (GObject *object) { - printf ("spi_registry_object_finalize called\n"); + DBG (1, g_warning ("spi_registry_object_finalize called\n")); + g_object_unref (SPI_REGISTRY (object)->de_controller); - /* TODO: unref deviceeventcontroller, which disconnects key listener */ G_OBJECT_CLASS (spi_registry_parent_class)->finalize (object); } @@ -200,12 +272,16 @@ parse_event_type (EventTypeStruct *etype, const char *event_name) gchar *s; split_string = g_strsplit (event_name, ":", 4); - etype->event_name = g_strdup (event_name); + 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; @@ -214,6 +290,10 @@ parse_event_type (EventTypeStruct *etype, const 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; @@ -221,6 +301,7 @@ parse_event_type (EventTypeStruct *etype, const char *event_name) if (split_string[1]) { + etype->major = g_quark_from_string (split_string[1]); if (split_string[2]) { etype->minor = g_quark_from_string (s = g_strconcat (split_string[1], split_string[2], NULL)); @@ -228,23 +309,16 @@ parse_event_type (EventTypeStruct *etype, const char *event_name) if (split_string[3]) { etype->detail = g_quark_from_string (split_string[3]); - s = g_strconcat (split_string[1], split_string[2], split_string[3], NULL); - etype->major = g_quark_from_string (s); - g_free (s); } else { etype->detail = g_quark_from_static_string (""); - s = g_strconcat (split_string[1], split_string[2], NULL); - etype->major = g_quark_from_string (s); - g_free (s); } } else { - etype->major = g_quark_from_string (split_string[1]); etype->minor = etype->major; - etype->detail = etype->major; + etype->detail = g_quark_from_static_string (""); //etype->major; } } else @@ -291,15 +365,16 @@ get_listener_list (SpiRegistry *registry, 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; - case ETYPE_KEYBOARD: default: ret = NULL; break; @@ -317,18 +392,18 @@ impl_accessibility_registry_register_global_event_listener ( const CORBA_char *event_name, CORBA_Environment *ev) { - SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); + SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); SpiListenerStruct *ls = spi_listener_struct_new (listener, ev); EventTypeStruct etype; GList **list; -#ifdef SPI_DEBUG +#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_quark = etype.major; + ls->event_type_quark = etype.minor; ls->event_type_cat = etype.type_cat; list = get_listener_list (registry, etype.type_cat); @@ -378,7 +453,7 @@ impl_accessibility_registry_deregister_global_event_listener_all ( CORBA_Environment *ev) { int i; - GList **lists[2]; + GList **lists[3]; SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); lists[0] = ®istry->object_listeners; @@ -511,47 +586,45 @@ notify_listeners_cb (GList * const *list, gpointer user_data) { SpiListenerStruct *ls; NotifyContext *ctx = user_data; -#ifdef SPI_DEBUG - CORBA_string s; -#endif ls = (*list)->data; #ifdef SPI_LISTENER_DEBUG - fprintf (stderr, "event quarks: %lx %lx %lx\n", ls->event_type_quark, etype.major, etype.minor); - fprintf (stderr, "event name: %s\n", etype.event_name); + 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 - fprintf (stderr, "notifying listener %d\n", g_list_index (listeners, l->data)); - s = Accessibility_Accessible__get_name (ctx->source, ev); + 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); -#endif - - ctx->e_out.source = bonobo_object_dup_ref (ctx->source, ctx->ev); if (BONOBO_EX (ctx->ev)) { - return SPI_RE_ENTRANT_CONTINUE;; + 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) - { - g_warning ("Accessibility app error: exception during " - "event notification: %s\n", - CORBA_exception_id (ctx->ev)); - } - } - else /* dup re-entered */ - { - bonobo_object_release_unref (ctx->e_out.source, 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 */ + } } } @@ -559,36 +632,279 @@ notify_listeners_cb (GList * const *list, gpointer user_data) } static void -impl_registry_notify_event (PortableServer_Servant servant, - const Accessibility_Event *e, - CORBA_Environment *ev) +registry_emit_event (SpiRegistry *registry, NotifyContext *ctx) { - SpiRegistry *registry; - GList **list; - NotifyContext ctx; + GList **list = get_listener_list (registry, ctx->etype.type_cat); - registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); + if (list && *list) + { + + spi_re_entrant_list_foreach (list, notify_listeners_cb, ctx); + } +} - parse_event_type (&ctx.etype, e->type); +static NotifyContext* +registry_clone_notify_context (NotifyContext *ctx) +{ + 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_flush_event_queue (SpiRegistry *registry, + gboolean discard, + CORBA_Environment *ev) +{ + 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; +} - list = get_listener_list (registry, ctx.etype.type_cat); +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; +} - if (list) - { - ctx.ev = ev; - ctx.e_out = *e; - ctx.source = e->source; - parse_event_type (&ctx.etype, e->type); +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 - spi_re_entrant_list_foreach (list, notify_listeners_cb, &ctx); +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) + { + 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 + 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; +} - if (e->source != CORBA_OBJECT_NIL) +static gboolean +registry_queue_event (SpiRegistry *registry, NotifyContext *ctx) +{ + 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) { - Accessibility_Accessible_unref (e->source, ev); + 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 +impl_registry_notify_event (PortableServer_Servant servant, + const Accessibility_Event *e, + CORBA_Environment *ev) +{ + 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--; +} static void spi_registry_class_init (SpiRegistryClass *klass) @@ -611,22 +927,56 @@ spi_registry_class_init (SpiRegistryClass *klass) 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 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->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 (); - registry->de_controller = NULL; + /* 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); } BONOBO_TYPE_FUNC_FULL (SpiRegistry, Accessibility_Registry, PARENT_TYPE, - spi_registry); + spi_registry) SpiRegistry * spi_registry_new (void)