Some work on event support; many bug fixes
[platform/upstream/at-spi2-core.git] / atspi / atspi-device-listener.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2002 Ximian Inc.
6  * Copyright 2002 Sun Microsystems, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include "atspi-private.h"
25
26 typedef struct
27 {
28   AtspiDeviceListenerCB    callback;
29   gpointer user_data;
30 } DeviceEventHandler;
31
32 GObjectClass *device_parent_class;
33
34 static guint32 _e_id = 0;
35
36 /*
37  * Misc. helpers.
38  */
39
40 static DeviceEventHandler *
41 device_event_handler_new (AtspiDeviceListenerCB callback, gpointer user_data)
42 {
43   DeviceEventHandler *eh = g_new0 (DeviceEventHandler, 1);
44
45   eh->callback = callback;
46   eh->user_data = user_data;
47
48   return eh;
49 }
50
51 static GList *
52 event_list_remove_by_cb (GList *list, AtspiDeviceListenerCB callback)
53 {
54   GList *l, *next;
55         
56   for (l = list; l; l = next)
57     {
58       DeviceEventHandler *eh = l->data;
59       next = l->next;
60
61       if (eh->callback == callback)
62       {
63         list = g_list_delete_link (list, l);
64         g_free (eh);
65       }
66     }
67
68   return list;
69 }
70
71 /*
72  * Standard event dispatcher
73  */
74
75 static guint listener_id = 0;
76 static GList *device_listeners = NULL;
77
78 static gboolean
79 id_is_free (guint id)
80 {
81   GList *l;
82
83   for (l = device_listeners; l; l = g_list_next (l))
84   {
85     AtspiDeviceListener *listener = l->data;
86     if (listener->id == id) return FALSE;
87   }
88   return TRUE;
89 }
90
91 static void
92 remove_listener (GObject *obj, gpointer data)
93 {
94   device_listeners = g_list_remove (device_listeners, obj);
95 }
96
97 /* 
98  * Device event handler
99  */
100 static gboolean
101 atspi_device_event_dispatch (AtspiDeviceListener               *listener,
102                    const AtspiDeviceEvent *event)
103 {
104   GList *l;
105   AtspiDeviceEvent anevent;
106   gboolean handled = FALSE;
107
108   /* FIXME: re-enterancy hazard on this list */
109   for (l = listener->callbacks; l; l = l->next)
110     {
111       DeviceEventHandler *eh = l->data;
112
113       if ((handled = eh->callback (&anevent, eh->user_data)))
114         {
115           break;
116         }
117     }
118
119   return handled;
120 }
121
122 static void
123 atspi_device_listener_init (AtspiDeviceListener *listener)
124 {
125   GList *new_list;
126
127   do
128   {
129     listener->id = listener_id++;
130   } while (!id_is_free (listener->id));
131   new_list = g_list_append (device_listeners, listener);
132   if (new_list) device_listeners = new_list;
133 }
134
135 static void
136 atspi_device_listener_finalize (GObject *object)
137 {
138   AtspiDeviceListener *listener = (AtspiDeviceListener *) object;
139   GList *l;
140   
141   for (l = listener->callbacks; l; l = l->next)
142     {
143       g_free (l->data);
144     }
145   
146   g_list_free (listener->callbacks);
147
148   device_parent_class->finalize (object);
149 }
150
151 static void
152 atspi_device_listener_class_init (AtspiDeviceListenerClass *klass)
153 {
154   GObjectClass *object_class = (GObjectClass *) klass;
155
156   device_parent_class = g_type_class_peek_parent (klass);
157   object_class->finalize = atspi_device_listener_finalize;
158
159   klass->device_event = atspi_device_event_dispatch;
160 }
161
162 G_DEFINE_TYPE (AtspiDeviceListener, atspi_device_listener,
163                           G_TYPE_OBJECT)
164
165 /**
166  * atspi_device_listener_new:
167  * @callback: (scope call): an #AtspiDeviceListenerCB callback function,
168  *            or NULL.
169  * @user_data: (closure): a pointer to data which will be passed to the
170  * callback when invoked.
171  *
172  * Create a new #AtspiDeviceListener with a specified callback function.
173  *
174  * Returns: a pointer to a newly-created #AtspiDeviceListener.
175  *
176  **/
177 AtspiDeviceListener *
178 atspi_device_listener_new (AtspiDeviceListenerCB callback)
179 {
180   AtspiDeviceListener *listener = g_object_new (atspi_device_listener_get_type (), NULL);
181
182   return listener;
183 }
184
185 /**
186  * atspi_device_listener_add_callback:
187  * @listener: the #AtspiDeviceListener instance to modify.
188  * @callback: (scope call): an #AtspiDeviceListenerCB function pointer.
189  * @user_data: (closure): a pointer to data which will be passed to the
190  *             callback when invoked.
191  *
192  * Add an in-process callback function to an existing #AtspiDeviceListener.
193  *
194  * Returns: #TRUE if successful, otherwise #FALSE.
195  *
196  **/
197 void
198 atspi_device_listener_add_callback (AtspiDeviceListener  *listener,
199                              AtspiDeviceListenerCB callback,
200                              void                      *user_data)
201 {
202   g_return_if_fail (ATSPI_IS_DEVICE_LISTENER (listener));
203
204   listener->callbacks = g_list_prepend (listener->callbacks,
205                                         device_event_handler_new ((void *)callback, user_data));
206 }
207
208 /**
209  * atspi_device_listener_remove_callback:
210  * @listener: the #AtspiDeviceListener instance to modify.
211  * @callback: (scope call): an #AtspiDeviceListenerCB function pointer.
212  *
213  * Remove an in-process callback function from an existing #AtspiDeviceListener.
214  *
215  * Returns: #TRUE if successful, otherwise #FALSE.
216  *
217  **/
218 void
219 atspi_device_listener_remove_callback (AtspiDeviceListener  *listener,
220                                 AtspiDeviceListenerCB callback)
221 {
222   g_return_if_fail (ATSPI_IS_DEVICE_LISTENER (listener));
223
224   listener->callbacks = event_list_remove_by_cb (listener->callbacks, (void *) callback);
225 }
226
227 static const char *device_event_type = "(uinnisb)";
228
229 static void
230 read_device_event_from_iter (DBusMessageIter *iter, AtspiDeviceEvent *event)
231 {
232   dbus_uint32_t type;
233   dbus_int32_t id;
234   dbus_int16_t hw_code;
235   dbus_int16_t modifiers;
236   dbus_int32_t timestamp;
237   char *event_string;
238   dbus_bool_t is_text;
239   DBusMessageIter iter_struct;
240
241   dbus_message_iter_recurse (iter, &iter_struct);
242
243   dbus_message_iter_get_basic (&iter_struct, &type);
244   event->type = type;
245   dbus_message_iter_next (&iter_struct);
246
247   dbus_message_iter_get_basic (&iter_struct, &id);
248   event->id = id;
249   dbus_message_iter_next (&iter_struct);
250
251   dbus_message_iter_get_basic (&iter_struct, &hw_code);
252   event->hw_code = hw_code;
253   dbus_message_iter_next (&iter_struct);
254
255   dbus_message_iter_get_basic (&iter_struct, &modifiers);
256   event->modifiers = modifiers;
257   dbus_message_iter_next (&iter_struct);
258
259   dbus_message_iter_get_basic (&iter_struct, &timestamp);
260   event->timestamp = timestamp;
261   dbus_message_iter_next (&iter_struct);
262
263   dbus_message_iter_get_basic (&iter_struct, &event->event_string);
264   dbus_message_iter_next (&iter_struct);
265
266   dbus_message_iter_get_basic (&iter_struct, &is_text);
267   event->is_text = is_text;
268 }
269
270 /*
271  * atspi_dbus_handle_DeviceEvent: (skip)
272  */
273 DBusHandlerResult
274 atspi_dbus_handle_DeviceEvent (DBusConnection *bus, DBusMessage *message, void *data)
275 {
276   const char *path = dbus_message_get_path (message);
277   int id;
278   AtspiDeviceEvent event;
279     AtspiDeviceListener *listener;
280   DBusMessageIter iter;
281   AtspiDeviceListenerClass *klass;
282   dbus_bool_t retval = FALSE;
283   GList *l;
284   DBusMessage *reply;
285   void *p = &event;
286
287   if (strcmp (dbus_message_get_signature (message), "(uinnisb)") != 0)
288   {
289     g_warning ("Atspi: Unknown signature for an event");
290     goto done;
291   }
292
293   if (sscanf (path, "/org/a11y/atspi/listeners/%d", &id) != 1)
294   {
295     g_warning ("Atspi: Bad listener path: %s\n", path);
296     goto done;
297   }
298
299   for (l = device_listeners; l; l = g_list_next (l))
300   {
301     listener = l->data;
302     if (listener->id == id) break;
303   }
304
305   if (!l)
306   {
307     goto done;
308   }
309   dbus_message_iter_init (message, &iter);
310   read_device_event_from_iter (&iter, &event);
311   klass = ATSPI_DEVICE_LISTENER_GET_CLASS (listener);
312   if (klass->device_event)
313   {
314     retval = (*klass->device_event) (listener, &event);
315   }
316 done:
317   reply = dbus_message_new_method_return (message);
318   if (reply)
319   {
320     dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &retval, DBUS_TYPE_INVALID);
321     dbus_connection_send (_atspi_bus(), reply, NULL);
322     dbus_message_unref (reply);
323   }
324   return DBUS_HANDLER_RESULT_HANDLED;
325 }
326
327 gchar *
328 _atspi_device_listener_get_path (AtspiDeviceListener *listener)
329 {
330   return g_strdup_printf ("/org/a11y/atspi/listeners/%d", listener->id);
331 }