Fix return value of spi_check_ev and have it call spi_throw_exception if an exception...
[platform/core/uifw/at-spi2-atk.git] / cspi / spi_event.c
index 695f1a2..d20161a 100644 (file)
@@ -22,7 +22,6 @@
  */
 
 #include <cspi/spi-private.h>
-#include <cspi/bonobo/cspi-bonobo-listener.h>
 
 static GSList *_cspi_event_queue = NULL;
 
@@ -332,38 +331,147 @@ AccessibleDeviceListener_unref (AccessibleDeviceListener *listener)
 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:
@@ -593,6 +701,28 @@ AccessibleDescriptionChangedEvent_getDescriptionString (const AccessibleEvent *e
   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)
 {
@@ -708,9 +838,409 @@ AccessibleEvent_unref (const AccessibleEvent *e)
              cspi_internal_event_remove (event);
               g_free ((gpointer)e->type);
               Accessible_unref (e->source);
-              CORBA_free (event->data);
+             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;
+}