*/
#include <cspi/spi-private.h>
-#include <cspi/bonobo/cspi-bonobo-listener.h>
static GSList *_cspi_event_queue = NULL;
static char *
cspi_internal_event_get_text (const InternalEvent *e)
{
- CORBA_any *any;
g_return_val_if_fail (e, NULL);
- g_return_val_if_fail (e->data, NULL);
- any = (CORBA_any *) e->data;
- if (CORBA_TypeCode_equivalent (any->_type, TC_CORBA_string, NULL))
+ if (e->event.v_type == EVENT_DATA_STRING)
{
- return CORBA_string_dup (* (char **) any->_value);
- }
- else
- {
-#ifdef EVENT_CONTEXT_DEBUG
- fprintf (stderr, "requested string, TC is not TC_CORBA_string! (%u)\n",
- (unsigned) any->_type);
-#endif
- return NULL;
+ return g_strdup (e->event.v.text? e->event.v.text: "");
}
+ return NULL;
}
static Accessible *
cspi_internal_event_get_object (const InternalEvent *e)
{
- CORBA_any *any;
+ g_return_val_if_fail (e, NULL);
+ if (e->event.v_type == EVENT_DATA_OBJECT)
+ {
+ cspi_object_ref (e->event.v.accessible);
+ return e->event.v.accessible;
+ }
+ return NULL;
+}
+static SPIRect *
+cspi_internal_event_get_rect (const InternalEvent *e)
+{
g_return_val_if_fail (e, NULL);
- g_return_val_if_fail (e->data, NULL);
+ if (e->event.v_type == EVENT_DATA_RECT)
+ {
+ SPIRect *rect = g_new (SPIRect, 1);
+ if (rect) memcpy (rect, &e->event.v.rect, sizeof(*rect));
+ return rect;
+ }
+ return NULL;
+}
- any = (CORBA_any *) e->data;
- if (CORBA_TypeCode_equal (any->_type, TC_CORBA_Object, cspi_ev()))
- return cspi_object_take (* (CORBA_Object *) any->_value);
- else
- return NULL;
+/**
+ * AccessibleEvent_getSourceName:
+ * @e: an #AccessibleEvent to be queried.
+ *
+ * Get the 'accessible-name' of the object emitting the event.
+ *
+ * Returns: The name of the event source, or NULL if the event source cannot be identified
+ * or does not report a name.
+ */
+char* AccessibleEvent_getSourceName (const AccessibleEvent *e)
+{
+ if (e && e->source)
+ {
+ return Accessible_getName (e->source);
+ }
+ return NULL;
+}
+
+/**
+ * AccessibleEvent_getSourceRole:
+ * @e: an #AccessibleEvent to be queried.
+ *
+ * Get the #AccessibleRole of the object emitting the event.
+ *
+ * Returns: #AccessibleRole of the event source, or SPI_ROLE_UNKNOWN
+ * if the event source's role is unknown or unspecified.
+ * (Some kinds of events, such as 'mouse:' events or
+ * toolkit events, don't have associated object roles.)
+ */
+AccessibleRole AccessibleEvent_getSourceRole (const AccessibleEvent *e)
+{
+ if (e && e->source)
+ {
+ return Accessible_getRole (e->source);
+ }
+ return SPI_ROLE_UNKNOWN;
+}
+
+/**
+ * AccessibleEvent_getSourceApplication:
+ * @e: an #AccessibleEvent to be queried.
+ *
+ * Get the #Application hosting the object which emitted the event.
+ *
+ * Returns: A pointer to the host #Application contining the event source
+ * component.
+ */
+#if 0
+AccessibleApplication* AccessibleEvent_getSourceApplication (const AccessibleEvent *e)
+{
+xyzzy
+ InternalEvent *ie = (InternalEvent *)e;
+ CORBA_any *any = ((ie && ie->data) ? (CORBA_any *)ie->data : NULL);
+ if (any &&
+ CORBA_TypeCode_equivalent (any->_type,
+ TC_Accessibility_EventDetails, NULL))
+ {
+ Accessibility_EventDetails *details = (Accessibility_EventDetails *) any->_value;
+ return cspi_object_take (details->host_application);
+ }
+ else
+ return NULL;
+}
+#endif
+
+/**
+ * AccessibleEvent_getSourceDetails:
+ * @e: an #AccessibleEvent to be queried.
+ * @name: a pointer to a character string which will point to the name of the event source
+ * on successful completion of the call.
+ * @role: a pointer to an #AccessibleRole which will point to the role of the event source
+ * on successful completion of the call.
+ * @app: A pointer to an #AccessibleApplication which points to the host application for this event
+ * on successful completion of the call.
+ *
+ * Get the host #Application, "accessible name", and #AccessibleRole
+ * of the object which emitted the event.
+ *
+ * Returns: TRUE if the source details were successfully retrieved,
+ * FALSE if they were not, either due to error, incomplete data,
+ * or the fact that the event did not encapsulate the required data.
+ */
+#if 0
+SPIBoolean AccessibleEvent_getSourceDetails (const AccessibleEvent *e,
+ char **name, AccessibleRole *role,
+ AccessibleApplication **app)
+{
+ InternalEvent *ie = (InternalEvent *)e;
+ CORBA_any *any = ((ie && ie->data) ? (CORBA_any *)ie->data : NULL);
+ if (any &&
+ CORBA_TypeCode_equivalent (any->_type,
+ TC_Accessibility_EventDetails, NULL))
+ {
+ Accessibility_EventDetails *details = (Accessibility_EventDetails *) any->_value;
+ *name = CORBA_string_dup (details->source_name);
+ *role = cspi_role_from_spi_role (details->source_role);
+ *app = cspi_object_take (details->host_application);
+ return TRUE;
+ }
+ else
+ {
+ *name = NULL;
+ *role = SPI_ROLE_UNKNOWN;
+ *app = NULL;
+ return FALSE;
+ }
}
+#endif
/**
* AccessibleTextChangedEvent_getChangeString:
return cspi_internal_event_get_text (foo);
}
+/**
+ * AccessibleBoundsChangedEvent_getNewBounds:
+ * @e: a pointer to the #AccessibleEvent being queried.
+ *
+ * Queries an #AccessibleEvent of type "object:bounds-changed",
+ * returning a pointer to an SPIRect structure containing the
+ * new bounds, or NULL on error.
+ * The returned structure should be freed with SPI_freeRect when
+ * the caller has finished referencing it.
+ *
+ * @Since: AT-SPI 1.6
+ *
+ * Returns: a pointer to an SPIRect defining the new object bounds.
+ **/
+SPIRect *
+AccessibleBoundsChangedEvent_getNewBounds (const AccessibleEvent *e)
+{
+ const InternalEvent *foo = (InternalEvent *) e;
+ /* TODO: check the event type. */
+ return cspi_internal_event_get_rect (foo);
+}
+
static gint
cspi_event_compare (gconstpointer p1, gconstpointer p2)
{
if (event->ref_count < 1)
{
cspi_internal_event_remove (event);
- g_free (e->type);
+ g_free ((gpointer)e->type);
Accessible_unref (e->source);
- CORBA_free (event->data);
- g_free (e);
+ if (event->event.v_type == EVENT_DATA_OBJECT)
+ {
+ Accessible_unref (event->event.v.accessible);
+ }
+ g_free ((gpointer)e);
}
}
}
}
+
+typedef struct
+{
+ CSpiEventListener *listener;
+ char *event;
+ char *detail;
+} CSpiEventListenerEntry;
+
+static GList *event_listeners = NULL;
+
+static dbus_bool_t
+demarshal_rect (DBusMessageIter *iter, SPIRect *rect)
+{
+ dbus_int32_t x, y, width, height;
+ DBusMessageIter iter_struct;
+
+ dbus_message_iter_recurse (iter, &iter_struct);
+ if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
+ dbus_message_iter_get_basic (&iter_struct, &x);
+ dbus_message_iter_next (&iter_struct);
+ if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
+ dbus_message_iter_get_basic (&iter_struct, &y);
+ dbus_message_iter_next (&iter_struct);
+ if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
+ dbus_message_iter_get_basic (&iter_struct, &width);
+ dbus_message_iter_next (&iter_struct);
+ if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
+ dbus_message_iter_get_basic (&iter_struct, &height);
+ rect->x = x;
+ rect->y = y;
+ rect->width = width;
+ rect->height = height;
+ return TRUE;
+}
+
+static gboolean
+parse_eventType (const char *eventType, char **type, char **detail, char **matchrule)
+{
+ char *p, *q;
+ char *t, *d;
+
+ p = strchr (eventType, ':');
+ if (p) p = strchr (p + 1, ':');
+ if (!p) p = eventType + strlen (eventType);
+ t = g_malloc (p - eventType + 1);
+ if (t)
+ {
+ memcpy (t, eventType, p - eventType);
+ t[p - eventType] = '\0';
+ if (!strchr (t, ':'))
+ {
+ char *q = g_strconcat (t, ":", NULL);
+ if (1)
+ {
+ g_free (t);
+ t = q;
+ }
+ }
+ }
+ else return FALSE;
+ if (*p == ':')
+ {
+ d = g_strdup (p + 1);
+ if (!d)
+ {
+ g_free (t);
+ return FALSE;
+ }
+ }
+ else d = NULL;
+ if ((p = strchr (t, ':')))
+ {
+ *p = (p[1] == '\0'? '\0': '_');
+ }
+ while ((p = strchr (t, '-'))) *p = '_';
+ if (matchrule)
+ {
+ *matchrule = g_strdup_printf ("type='signal',interface='%s',member='%s'", spi_interface_accessible, t);
+ if (!*matchrule)
+ {
+ g_free (t);
+ if (d) g_free (d);
+ return FALSE;
+ }
+ }
+ if (type) *type = t;
+ if (detail) *detail = d;
+ return TRUE;
+}
+
+static void listener_data_free (CSpiEventListenerEntry *e)
+{
+ g_free (e->event);
+ if (e->detail) g_free (e->detail);
+ g_free (e);
+}
+
+/**
+ * SPI_registerGlobalEventListener:
+ * @listener: the #AccessibleEventListener to be registered against an
+ * event type.
+ * @eventType: a character string indicating the type of events for which
+ * notification is requested. Format is
+ * EventClass:major_type:minor_type:detail
+ * where all subfields other than EventClass are optional.
+ * EventClasses include "object", "window", "mouse",
+ * and toolkit events (e.g. "Gtk", "AWT").
+ * Examples: "focus:", "Gtk:GtkWidget:button_press_event".
+ *
+ * Legal object event types:
+ *
+ * (property change events)
+ *
+ * object:property-change
+ * object:property-change:accessible-name
+ * object:property-change:accessible-description
+ * object:property-change:accessible-parent
+ * object:property-change:accessible-value
+ * object:property-change:accessible-role
+ * object:property-change:accessible-table-caption
+ * object:property-change:accessible-table-column-description
+ * object:property-change:accessible-table-column-header
+ * object:property-change:accessible-table-row-description
+ * object:property-change:accessible-table-row-header
+ * object:property-change:accessible-table-summary
+ *
+ * (other object events)
+ *
+ * object:state-changed
+ * object:children-changed
+ * object:visible-data-changed
+ * object:selection-changed
+ * object:text-selection-changed
+ * object:text-changed
+ * object:text-caret-moved
+ * object:row-inserted
+ * object:row-reordered
+ * object:row-deleted
+ * object:column-inserted
+ * object:column-reordered
+ * object:column-deleted
+ * object:model-changed
+ * object:active-descendant-changed
+ *
+ * (window events)
+ *
+ * window:minimize
+ * window:maximize
+ * window:restore
+ * window:close
+ * window:create
+ * window:reparent
+ * window:desktop-create
+ * window:desktop-destroy
+ * window:activate
+ * window:deactivate
+ * window:raise
+ * window:lower
+ * window:move
+ * window:resize
+ * window:shade
+ * window:unshade
+ * window:restyle
+ *
+ * (other events)
+ *
+ * focus:
+ * mouse:abs
+ * mouse:rel
+ * mouse:b1p
+ * mouse:b1r
+ * mouse:b2p
+ * mouse:b2r
+ * mouse:b3p
+ * mouse:b3r
+ *
+ * NOTE: this string may be UTF-8, but should not contain byte value 56
+ * (ascii ':'), except as a delimiter, since non-UTF-8 string
+ * delimiting functions are used internally.
+ * In general, listening to
+ * toolkit-specific events is not recommended.
+ *
+ * Add an in-process callback function to an existing AccessibleEventListener.
+ *
+ * Returns: #TRUE if successful, otherwise #FALSE.
+ **/
+SPIBoolean
+SPI_registerGlobalEventListener (AccessibleEventListener *listener,
+ const char *eventType)
+{
+ CSpiEventListenerEntry *e;
+ char *matchrule;
+ DBusError error;
+ GList *new_list;
+
+ if (!listener)
+ {
+ return FALSE;
+ }
+
+ e = g_new (CSpiEventListenerEntry, 1);
+ if (!e) return FALSE;
+ e->listener = listener;
+ if (!parse_eventType (eventType, &e->event, &e->detail, &matchrule))
+ {
+ g_free (e);
+ return FALSE;
+ }
+ new_list = g_list_prepend (event_listeners, e);
+ if (!new_list)
+ {
+ listener_data_free (e);
+ return FALSE;
+ }
+ event_listeners = new_list;
+ dbus_error_init (&error);
+ dbus_bus_add_match (cspi_bus(), matchrule, &error);
+ if (error.message)
+ {
+ g_warning ("Adding match: %s", error.message);
+ }
+ return TRUE;
+}
+
+/**
+ * SPI_deregisterGlobalEventListenerAll:
+ * @listener: the #AccessibleEventListener to be registered against
+ * an event type.
+ *
+ * deregisters an AccessibleEventListener from the registry, for all
+ * event types it may be listening to. Use
+ * AccessibleEventListener_unref to release the
+ * listener reference.
+ *
+ * Returns: #TRUE if successful, otherwise #FALSE.
+ **/
+SPIBoolean
+SPI_deregisterGlobalEventListenerAll (AccessibleEventListener *listener)
+{
+ GList *l;
+
+ if (!listener)
+ {
+ return FALSE;
+ }
+
+ for (l = event_listeners; l;)
+ {
+ CSpiEventListenerEntry *e = l->data;
+ if (e->listener == listener)
+ {
+ listener_data_free (e);
+ l = g_list_remove (l, e);
+ }
+ else l = g_list_next (l);
+ }
+ return TRUE;
+}
+
+/**
+ * SPI_deregisterGlobalEventListener:
+ * @listener: the #AccessibleEventListener registered against an event type.
+ * @eventType: a string specifying the event type for which this
+ * listener is to be deregistered.
+ *
+ * deregisters an AccessibleEventListener from the registry, for a specific
+ * event type.
+ *
+ * Returns: #TRUE if successful, otherwise #FALSE.
+ **/
+SPIBoolean
+SPI_deregisterGlobalEventListener (AccessibleEventListener *listener,
+ const char *eventType)
+{
+ char *type, *detail, *matchrule;
+ GList *l;
+
+ if (!parse_eventType (eventType, &type, &detail, &matchrule))
+ {
+ return FALSE;
+ }
+ if (!listener)
+ {
+ return FALSE;
+ }
+
+ for (l = event_listeners; l;)
+ {
+ CSpiEventListenerEntry *e = l->data;
+ if (e->listener == listener && !strcmp (e->event, type) && (e->detail == detail || !strcmp (e->detail, detail)))
+ {
+ DBusError error;
+ listener_data_free (e);
+ l = g_list_remove (l, e);
+ dbus_error_init (&error);
+ dbus_bus_remove_match (cspi_bus(), matchrule, &error);
+ }
+ else l = g_list_next (l);
+ }
+ g_free (type);
+ if (detail) g_free (detail);
+ g_free (matchrule);
+ return TRUE;
+}
+
+void
+cspi_dispatch_event (AccessibleEvent *e)
+{
+ char *event, *detail;
+ GList *l;
+
+ if (!parse_eventType (e->type, &event, &detail, NULL))
+ {
+ g_warning ("Couldn't parse event: %s\n", e->type);
+ return;
+ }
+ for (l = event_listeners; l; l = g_list_next (l))
+ {
+ CSpiEventListenerEntry *entry = l->data;
+ if (!strcmp (event, entry->event) &&
+ (entry->detail == NULL || !strcmp (detail, entry->detail)))
+ {
+ CSpiEventListenerClass *klass = CSPI_EVENT_LISTENER_GET_CLASS (entry->listener);
+ if (klass->event) (*klass->event)(entry->listener, e);
+ }
+ }
+ if (detail) g_free (detail);
+ g_free (event);
+}
+
+DBusHandlerResult
+cspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
+{
+ char *detail = NULL;
+ const char *event = dbus_message_get_member (message);
+ DBusMessageIter iter, iter_variant;
+ dbus_message_iter_init (message, &iter);
+ AccessibleEvent e;
+ dbus_int32_t detail1, detail2;
+ char *p;
+
+ g_return_if_fail (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING);
+ dbus_message_iter_get_basic (&iter, &detail);
+ dbus_message_iter_next (&iter);
+ g_return_if_fail (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_INT32);
+ dbus_message_iter_get_basic (&iter, &detail1);
+ e.detail1 = detail1;
+ dbus_message_iter_next (&iter);
+ g_return_if_fail (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_INT32);
+ dbus_message_iter_get_basic (&iter, &detail2);
+ e.detail2 = detail2;
+ dbus_message_iter_next (&iter);
+ e.type = g_strdup (event);
+ p = strchr (e.type, '_');
+ if (p) *p = ':';
+ if (detail[0] != '\0')
+ {
+ p = g_strconcat (e.type, ":", detail, NULL);
+ if (p)
+ {
+ g_free (e.type);
+ e.type = p;
+ }
+ }
+ while ((p = strchr (e.type, '_'))) *p = '-';
+ e.source = cspi_ref_accessible (dbus_message_get_sender(message), dbus_message_get_path(message));
+ dbus_message_iter_recurse (&iter, &iter_variant);
+ switch (dbus_message_iter_get_arg_type (&iter_variant))
+ {
+ case DBUS_TYPE_OBJECT_PATH:
+ {
+ dbus_message_iter_get_basic (&iter_variant, &p);
+ e.v_type = EVENT_DATA_OBJECT;
+ e.v.accessible = cspi_ref_accessible (dbus_message_get_sender(message), p);
+ break;
+ }
+ case DBUS_TYPE_STRING:
+ {
+ dbus_message_iter_get_basic (&iter_variant, &p);
+ e.v_type = EVENT_DATA_STRING;
+ e.v.text = g_strdup (p);
+ break;
+ }
+ case DBUS_TYPE_STRUCT:
+ {
+ if (demarshal_rect (&iter_variant, &e.v.rect))
+ {
+ e.v_type = EVENT_DATA_RECT;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ cspi_dispatch_event (&e);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}