2.34.0
[platform/upstream/at-spi2-core.git] / atspi / atspi-device-listener.c
index 31bb270..8e04c41 100644 (file)
@@ -4,50 +4,76 @@
  *
  * Copyright 2002 Ximian Inc.
  * Copyright 2002 Sun Microsystems, Inc.
+ * Copyright 2010, 2011 Novell, Inc.
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 
 #include "atspi-private.h"
+#include <stdio.h>
 
 typedef struct
 {
   AtspiDeviceListenerCB    callback;
   gpointer user_data;
+  GDestroyNotify callback_destroyed;
 } DeviceEventHandler;
 
 GObjectClass *device_parent_class;
 
-static guint32 _e_id = 0;
-
 /*
  * Misc. helpers.
  */
 
 static DeviceEventHandler *
-device_event_handler_new (AtspiDeviceListenerCB callback, gpointer user_data)
+device_event_handler_new (AtspiDeviceListenerCB callback,
+                          GDestroyNotify callback_destroyed,
+                          gpointer user_data)
 {
   DeviceEventHandler *eh = g_new0 (DeviceEventHandler, 1);
 
   eh->callback = callback;
+  eh->callback_destroyed = callback_destroyed;
   eh->user_data = user_data;
 
   return eh;
 }
 
+static gboolean
+device_remove_datum (const AtspiDeviceEvent *event, void *user_data)
+{
+  AtspiDeviceListenerSimpleCB cb = user_data;
+  return cb (event);
+}
+  
+static void
+device_event_handler_free (DeviceEventHandler *eh)
+{
+#if 0
+  /* TODO; Test this; it will probably crash with pyatspi for unknown reasons */
+  if (eh->callback_destroyed)
+  {
+    gpointer rea_callback = (eh->callback == device_remove_datum ?
+                            eh->user_data : eh->callback);
+    (*eh->callback_destroyed) (real_callback);
+  }
+#endif
+  g_free (eh);
+}
+
 static GList *
 event_list_remove_by_cb (GList *list, AtspiDeviceListenerCB callback)
 {
@@ -61,7 +87,7 @@ event_list_remove_by_cb (GList *list, AtspiDeviceListenerCB callback)
       if (eh->callback == callback)
       {
         list = g_list_delete_link (list, l);
-       g_free (eh);
+        device_event_handler_free (eh);
       }
     }
 
@@ -88,10 +114,27 @@ id_is_free (guint id)
   return TRUE;
 }
 
-static void
-remove_listener (GObject *obj, gpointer data)
+static AtspiDeviceEvent *
+atspi_device_event_copy (const AtspiDeviceEvent *src)
+{
+  AtspiDeviceEvent *dst = g_new0 (AtspiDeviceEvent, 1);
+  dst->type = src->type;
+  dst->id = src->id;
+  dst->hw_code = src->hw_code;
+  dst->modifiers = src->modifiers;
+  dst->timestamp = src->timestamp;
+  if (src->event_string)
+    dst->event_string = g_strdup (src->event_string);
+  dst->is_text = src->is_text;
+  return dst;
+}
+
+void
+atspi_device_event_free (AtspiDeviceEvent *event)
 {
-  device_listeners = g_list_remove (device_listeners, obj);
+  if (event->event_string)
+    g_free (event->event_string);
+  g_free (event);
 }
 
 /* 
@@ -102,7 +145,6 @@ atspi_device_event_dispatch (AtspiDeviceListener               *listener,
                   const AtspiDeviceEvent *event)
 {
   GList *l;
-  AtspiDeviceEvent anevent;
   gboolean handled = FALSE;
 
   /* FIXME: re-enterancy hazard on this list */
@@ -110,7 +152,7 @@ atspi_device_event_dispatch (AtspiDeviceListener               *listener,
     {
       DeviceEventHandler *eh = l->data;
 
-      if ((handled = eh->callback (&anevent, eh->user_data)))
+      if ((handled = eh->callback (atspi_device_event_copy (event), eh->user_data)))
         {
          break;
        }
@@ -122,14 +164,12 @@ atspi_device_event_dispatch (AtspiDeviceListener               *listener,
 static void
 atspi_device_listener_init (AtspiDeviceListener *listener)
 {
-  GList *new_list;
 
   do
   {
     listener->id = listener_id++;
   } while (!id_is_free (listener->id));
-  new_list = g_list_append (device_listeners, listener);
-  if (new_list) device_listeners = new_list;
+  device_listeners = g_list_append (device_listeners, listener);
 }
 
 static void
@@ -140,7 +180,7 @@ atspi_device_listener_finalize (GObject *object)
   
   for (l = listener->callbacks; l; l = l->next)
     {
-      g_free (l->data);
+      device_event_handler_free (l->data);
     }
   
   g_list_free (listener->callbacks);
@@ -164,45 +204,77 @@ G_DEFINE_TYPE (AtspiDeviceListener, atspi_device_listener,
 
 /**
  * atspi_device_listener_new:
- * @callback: (scope call): an #AtspiDeviceListenerCB callback function,
+ * @callback: (scope notified): an #AtspiDeviceListenerCB callback function,
  *            or NULL.
  * @user_data: (closure): a pointer to data which will be passed to the
  * callback when invoked.
+ * @callback_destroyed: A #GDestroyNotify called when the listener is freed
+ * and data associated with the callback should be freed. It can be NULL.
  *
- * Create a new #AtspiDeviceListener with a specified callback function.
+ * Creates a new #AtspiDeviceListener with a specified callback function.
  *
- * Returns: a pointer to a newly-created #AtspiDeviceListener.
+ * Returns: (transfer full): a pointer to a newly-created #AtspiDeviceListener.
  *
  **/
 AtspiDeviceListener *
-atspi_device_listener_new (AtspiDeviceListenerCB callback)
+atspi_device_listener_new (AtspiDeviceListenerCB callback,
+                           void *user_data,
+                           GDestroyNotify callback_destroyed)
 {
   AtspiDeviceListener *listener = g_object_new (atspi_device_listener_get_type (), NULL);
 
+  if (callback)
+    atspi_device_listener_add_callback (listener, callback, callback_destroyed,
+                                       user_data);
   return listener;
 }
 
 /**
+ * atspi_device_listener_new_simple: (skip)
+ * @callback: (scope notified): an #AtspiDeviceListenerCB callback function,
+ *            or NULL.
+ * @callback_destroyed: A #GDestroyNotify called when the listener is freed
+ * and data associated with the callback should be freed.  It an be NULL.
+ *
+ * Creates a new #AtspiDeviceListener with a specified callback function.
+ * This method is similar to #atspi_device_listener_new, but callback
+ * takes no user data.
+ *
+ * Returns: a pointer to a newly-created #AtspiDeviceListener.
+ *
+ **/
+AtspiDeviceListener *
+atspi_device_listener_new_simple (AtspiDeviceListenerSimpleCB callback,
+                           GDestroyNotify callback_destroyed)
+{
+  return atspi_device_listener_new (device_remove_datum, callback, callback_destroyed);
+}
+
+/**
  * atspi_device_listener_add_callback:
  * @listener: the #AtspiDeviceListener instance to modify.
- * @callback: (scope call): an #AtspiDeviceListenerCB function pointer.
+ * @callback: (scope notified): an #AtspiDeviceListenerCB function pointer.
+ * @callback_destroyed: A #GDestroyNotify called when the listener is freed
+ * and data associated with the callback should be freed. It can be NULL.
  * @user_data: (closure): a pointer to data which will be passed to the
- *             callback when invoked.
- *
- * Add an in-process callback function to an existing #AtspiDeviceListener.
+ *             callback when invoked. 
  *
- * Returns: #TRUE if successful, otherwise #FALSE.
+ * Adds an in-process callback function to an existing #AtspiDeviceListener.
  *
  **/
 void
 atspi_device_listener_add_callback (AtspiDeviceListener  *listener,
                             AtspiDeviceListenerCB callback,
+                            GDestroyNotify callback_destroyed,
                             void                      *user_data)
 {
   g_return_if_fail (ATSPI_IS_DEVICE_LISTENER (listener));
+  DeviceEventHandler *new_handler;
 
-  listener->callbacks = g_list_prepend (listener->callbacks,
-                                       device_event_handler_new ((void *)callback, user_data));
+  new_handler = device_event_handler_new (callback,
+                                          callback_destroyed, user_data);
+
+  listener->callbacks = g_list_prepend (listener->callbacks, new_handler);
 }
 
 /**
@@ -210,9 +282,8 @@ atspi_device_listener_add_callback (AtspiDeviceListener  *listener,
  * @listener: the #AtspiDeviceListener instance to modify.
  * @callback: (scope call): an #AtspiDeviceListenerCB function pointer.
  *
- * Remove an in-process callback function from an existing #AtspiDeviceListener.
- *
- * Returns: #TRUE if successful, otherwise #FALSE.
+ * Removes an in-process callback function from an existing 
+ * #AtspiDeviceListener.
  *
  **/
 void
@@ -224,17 +295,14 @@ atspi_device_listener_remove_callback (AtspiDeviceListener  *listener,
   listener->callbacks = event_list_remove_by_cb (listener->callbacks, (void *) callback);
 }
 
-static const char *device_event_type = "(uinnisb)";
-
 static void
 read_device_event_from_iter (DBusMessageIter *iter, AtspiDeviceEvent *event)
 {
   dbus_uint32_t type;
   dbus_int32_t id;
-  dbus_int16_t hw_code;
-  dbus_int16_t modifiers;
+  dbus_int32_t hw_code;
+  dbus_int32_t modifiers;
   dbus_int32_t timestamp;
-  char *event_string;
   dbus_bool_t is_text;
   DBusMessageIter iter_struct;
 
@@ -248,12 +316,13 @@ read_device_event_from_iter (DBusMessageIter *iter, AtspiDeviceEvent *event)
   event->id = id;
   dbus_message_iter_next (&iter_struct);
 
+  /* TODO: Remove cast from next two on ABI break */
   dbus_message_iter_get_basic (&iter_struct, &hw_code);
-  event->hw_code = hw_code;
+  event->hw_code = (gushort) hw_code;
   dbus_message_iter_next (&iter_struct);
 
   dbus_message_iter_get_basic (&iter_struct, &modifiers);
-  event->modifiers = modifiers;
+  event->modifiers = (gushort) modifiers;
   dbus_message_iter_next (&iter_struct);
 
   dbus_message_iter_get_basic (&iter_struct, &timestamp);
@@ -267,11 +336,8 @@ read_device_event_from_iter (DBusMessageIter *iter, AtspiDeviceEvent *event)
   event->is_text = is_text;
 }
 
-/*
- * atspi_dbus_handle_DeviceEvent: (skip)
- */
 DBusHandlerResult
-atspi_dbus_handle_DeviceEvent (DBusConnection *bus, DBusMessage *message, void *data)
+_atspi_dbus_handle_DeviceEvent (DBusConnection *bus, DBusMessage *message, void *data)
 {
   const char *path = dbus_message_get_path (message);
   int id;
@@ -282,17 +348,16 @@ atspi_dbus_handle_DeviceEvent (DBusConnection *bus, DBusMessage *message, void *
   dbus_bool_t retval = FALSE;
   GList *l;
   DBusMessage *reply;
-  void *p = &event;
 
-  if (strcmp (dbus_message_get_signature (message), "(uinnisb)") != 0)
+  if (strcmp (dbus_message_get_signature (message), "(uiuuisb)") != 0)
   {
-    g_warning ("Atspi: Unknown signature for an event");
+    g_warning ("AT-SPI: Unknown signature for an event");
     goto done;
   }
 
   if (sscanf (path, "/org/a11y/atspi/listeners/%d", &id) != 1)
   {
-    g_warning ("Atspi: Bad listener path: %s\n", path);
+    g_warning ("AT-SPI: Bad listener path: %s\n", path);
     goto done;
   }
 
@@ -312,6 +377,11 @@ atspi_dbus_handle_DeviceEvent (DBusConnection *bus, DBusMessage *message, void *
   if (klass->device_event)
   {
     retval = (*klass->device_event) (listener, &event);
+    if (retval != 0 && retval != 1)
+    {
+      g_warning ("AT-SPI: device event handler returned %d; should be 0 or 1", retval);
+      retval = 0;
+    }
   }
 done:
   reply = dbus_message_new_method_return (message);
@@ -326,6 +396,10 @@ done:
 
 gchar *
 _atspi_device_listener_get_path (AtspiDeviceListener *listener)
-{
-  return g_strdup_printf ("/org/a11y/atspi/listeners/%d", listener->id);
+{  return g_strdup_printf ("/org/a11y/atspi/listeners/%d", listener->id);
 }
+
+G_DEFINE_BOXED_TYPE (AtspiDeviceEvent,
+                     atspi_device_event,
+                     atspi_device_event_copy,
+                     atspi_device_event_free)