Fix most compiler warnings and various miscellaneous problems
[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 #include <stdio.h>
26
27 typedef struct
28 {
29   AtspiDeviceListenerCB    callback;
30   gpointer user_data;
31   GDestroyNotify callback_destroyed;
32 } DeviceEventHandler;
33
34 GObjectClass *device_parent_class;
35
36 /*
37  * Misc. helpers.
38  */
39
40 static DeviceEventHandler *
41 device_event_handler_new (AtspiDeviceListenerCB callback,
42                           GDestroyNotify callback_destroyed,
43                           gpointer user_data)
44 {
45   DeviceEventHandler *eh = g_new0 (DeviceEventHandler, 1);
46
47   eh->callback = callback;
48   eh->callback_destroyed = callback_destroyed;
49   eh->user_data = user_data;
50
51   return eh;
52 }
53
54 static gboolean
55 device_remove_datum (const AtspiDeviceEvent *event, void *user_data)
56 {
57   AtspiDeviceListenerSimpleCB cb = user_data;
58   return cb (event);
59 }
60   
61 static void
62 device_event_handler_free (DeviceEventHandler *eh)
63 {
64 #if 0
65   /* TODO; Test this; it will probably crash with pyatspi for unknown reasons */
66   if (eh->callback_destroyed)
67   {
68     gpointer rea_callback = (eh->callback == device_remove_datum ?
69                             eh->user_data : eh->callback);
70     (*eh->callback_destroyed) (real_callback);
71   }
72 #endif
73   g_free (eh);
74 }
75
76 static GList *
77 event_list_remove_by_cb (GList *list, AtspiDeviceListenerCB callback)
78 {
79   GList *l, *next;
80         
81   for (l = list; l; l = next)
82     {
83       DeviceEventHandler *eh = l->data;
84       next = l->next;
85
86       if (eh->callback == callback)
87       {
88         list = g_list_delete_link (list, l);
89         device_event_handler_free (eh);
90       }
91     }
92
93   return list;
94 }
95
96 /*
97  * Standard event dispatcher
98  */
99
100 static guint listener_id = 0;
101 static GList *device_listeners = NULL;
102
103 static gboolean
104 id_is_free (guint id)
105 {
106   GList *l;
107
108   for (l = device_listeners; l; l = g_list_next (l))
109   {
110     AtspiDeviceListener *listener = l->data;
111     if (listener->id == id) return FALSE;
112   }
113   return TRUE;
114 }
115
116 static AtspiDeviceEvent *
117 atspi_device_event_copy (AtspiDeviceEvent *src)
118 {
119   AtspiDeviceEvent *dst = g_new0 (AtspiDeviceEvent, 1);
120   if (!dst)
121     return NULL;
122   dst->type = src->type;
123   dst->id = src->id;
124   dst->hw_code = src->hw_code;
125   dst->modifiers = src->modifiers;
126   dst->timestamp = src->timestamp;
127   if (src->event_string)
128     dst->event_string = g_strdup (src->event_string);
129   dst->is_text = src->is_text;
130   return dst;
131 }
132
133 void
134 atspi_device_event_free (AtspiDeviceEvent *event)
135 {
136   if (event->event_string)
137     g_free (event->event_string);
138   g_free (event);
139 }
140
141 /* 
142  * Device event handler
143  */
144 static gboolean
145 atspi_device_event_dispatch (AtspiDeviceListener               *listener,
146                    const AtspiDeviceEvent *event)
147 {
148   GList *l;
149   gboolean handled = FALSE;
150
151   /* FIXME: re-enterancy hazard on this list */
152   for (l = listener->callbacks; l; l = l->next)
153     {
154       DeviceEventHandler *eh = l->data;
155
156       if ((handled = eh->callback (atspi_device_event_copy (event), eh->user_data)))
157         {
158           break;
159         }
160     }
161
162   return handled;
163 }
164
165 static void
166 atspi_device_listener_init (AtspiDeviceListener *listener)
167 {
168   GList *new_list;
169
170   do
171   {
172     listener->id = listener_id++;
173   } while (!id_is_free (listener->id));
174   new_list = g_list_append (device_listeners, listener);
175   if (new_list) device_listeners = new_list;
176 }
177
178 static void
179 atspi_device_listener_finalize (GObject *object)
180 {
181   AtspiDeviceListener *listener = (AtspiDeviceListener *) object;
182   GList *l;
183   
184   for (l = listener->callbacks; l; l = l->next)
185     {
186       device_event_handler_free (l->data);
187     }
188   
189   g_list_free (listener->callbacks);
190
191   device_parent_class->finalize (object);
192 }
193
194 static void
195 atspi_device_listener_class_init (AtspiDeviceListenerClass *klass)
196 {
197   GObjectClass *object_class = (GObjectClass *) klass;
198
199   device_parent_class = g_type_class_peek_parent (klass);
200   object_class->finalize = atspi_device_listener_finalize;
201
202   klass->device_event = atspi_device_event_dispatch;
203 }
204
205 G_DEFINE_TYPE (AtspiDeviceListener, atspi_device_listener,
206                           G_TYPE_OBJECT)
207
208 /**
209  * atspi_device_listener_new:
210  * @callback: (scope notify): an #AtspiDeviceListenerCB callback function,
211  *            or NULL.
212  * @callback_destroyed: A #GDestroyNotify called when the listener is freed
213  * and data associated with the callback should be freed.  Can be NULL.
214  * @user_data: (closure): a pointer to data which will be passed to the
215  * callback when invoked.
216  *
217  * Create a new #AtspiDeviceListener with a specified callback function.
218  *
219  * Returns: a pointer to a newly-created #AtspiDeviceListener.
220  *
221  **/
222 AtspiDeviceListener *
223 atspi_device_listener_new (AtspiDeviceListenerCB callback,
224                            GDestroyNotify callback_destroyed,
225                            void *user_data)
226 {
227   AtspiDeviceListener *listener = g_object_new (atspi_device_listener_get_type (), NULL);
228
229   if (callback)
230     atspi_device_listener_add_callback (listener, callback, callback_destroyed,
231                                        user_data);
232   return listener;
233 }
234
235 /**
236  * atspi_device_listener_new_simple:
237  * @callback: (scope notify): an #AtspiDeviceListenerCB callback function,
238  *            or NULL.
239  * @callback_destroyed: A #GDestroyNotify called when the listener is freed
240  * and data associated with the callback should be freed.  Can be NULL.
241  *
242  * Create a new #AtspiDeviceListener with a specified callback function.
243  * Like atspi_device_listener_new, but callback takes no user data.
244  *
245  * Returns: a pointer to a newly-created #AtspiDeviceListener.
246  *
247  **/
248 AtspiDeviceListener *
249 atspi_device_listener_new_simple (AtspiDeviceListenerSimpleCB callback,
250                            GDestroyNotify callback_destroyed)
251 {
252   return atspi_device_listener_new (device_remove_datum, callback_destroyed, callback);
253 }
254
255 /**
256  * atspi_device_listener_add_callback:
257  * @listener: the #AtspiDeviceListener instance to modify.
258  * @callback: (scope notify): an #AtspiDeviceListenerCB function pointer.
259  * @user_data: (closure): a pointer to data which will be passed to the
260  *             callback when invoked.
261  * @callback_destroyed: A #GDestroyNotify called when the listener is freed
262  * and data associated with the callback should be freed.  Can be NULL.
263  *
264  * Add an in-process callback function to an existing #AtspiDeviceListener.
265  *
266  * Returns: #TRUE if successful, otherwise #FALSE.
267  *
268  **/
269 void
270 atspi_device_listener_add_callback (AtspiDeviceListener  *listener,
271                              AtspiDeviceListenerCB callback,
272                              GDestroyNotify callback_destroyed,
273                              void                      *user_data)
274 {
275   g_return_if_fail (ATSPI_IS_DEVICE_LISTENER (listener));
276   DeviceEventHandler *new_handler;
277
278   new_handler = device_event_handler_new (callback,
279                                           callback_destroyed, user_data);
280
281   if (new_handler)
282   {
283     GList *new_list;
284     new_list = g_list_prepend (listener->callbacks, new_handler);
285     if (new_list)
286       listener->callbacks = new_list;
287   }
288 }
289
290 /**
291  * atspi_device_listener_remove_callback:
292  * @listener: the #AtspiDeviceListener instance to modify.
293  * @callback: (scope call): an #AtspiDeviceListenerCB function pointer.
294  *
295  * Remove an in-process callback function from an existing #AtspiDeviceListener.
296  *
297  * Returns: #TRUE if successful, otherwise #FALSE.
298  *
299  **/
300 void
301 atspi_device_listener_remove_callback (AtspiDeviceListener  *listener,
302                                 AtspiDeviceListenerCB callback)
303 {
304   g_return_if_fail (ATSPI_IS_DEVICE_LISTENER (listener));
305
306   listener->callbacks = event_list_remove_by_cb (listener->callbacks, (void *) callback);
307 }
308
309 static void
310 read_device_event_from_iter (DBusMessageIter *iter, AtspiDeviceEvent *event)
311 {
312   dbus_uint32_t type;
313   dbus_int32_t id;
314   dbus_int16_t hw_code;
315   dbus_int16_t modifiers;
316   dbus_int32_t timestamp;
317   dbus_bool_t is_text;
318   DBusMessageIter iter_struct;
319
320   dbus_message_iter_recurse (iter, &iter_struct);
321
322   dbus_message_iter_get_basic (&iter_struct, &type);
323   event->type = type;
324   dbus_message_iter_next (&iter_struct);
325
326   dbus_message_iter_get_basic (&iter_struct, &id);
327   event->id = id;
328   dbus_message_iter_next (&iter_struct);
329
330   dbus_message_iter_get_basic (&iter_struct, &hw_code);
331   event->hw_code = hw_code;
332   dbus_message_iter_next (&iter_struct);
333
334   dbus_message_iter_get_basic (&iter_struct, &modifiers);
335   event->modifiers = modifiers;
336   dbus_message_iter_next (&iter_struct);
337
338   dbus_message_iter_get_basic (&iter_struct, &timestamp);
339   event->timestamp = timestamp;
340   dbus_message_iter_next (&iter_struct);
341
342   dbus_message_iter_get_basic (&iter_struct, &event->event_string);
343   dbus_message_iter_next (&iter_struct);
344
345   dbus_message_iter_get_basic (&iter_struct, &is_text);
346   event->is_text = is_text;
347 }
348
349 /*
350  * atspi_dbus_handle_DeviceEvent: (skip)
351  */
352 DBusHandlerResult
353 atspi_dbus_handle_DeviceEvent (DBusConnection *bus, DBusMessage *message, void *data)
354 {
355   const char *path = dbus_message_get_path (message);
356   int id;
357   AtspiDeviceEvent event;
358     AtspiDeviceListener *listener;
359   DBusMessageIter iter;
360   AtspiDeviceListenerClass *klass;
361   dbus_bool_t retval = FALSE;
362   GList *l;
363   DBusMessage *reply;
364
365   if (strcmp (dbus_message_get_signature (message), "(uinnisb)") != 0)
366   {
367     g_warning ("Atspi: Unknown signature for an event");
368     goto done;
369   }
370
371   if (sscanf (path, "/org/a11y/atspi/listeners/%d", &id) != 1)
372   {
373     g_warning ("Atspi: Bad listener path: %s\n", path);
374     goto done;
375   }
376
377   for (l = device_listeners; l; l = g_list_next (l))
378   {
379     listener = l->data;
380     if (listener->id == id) break;
381   }
382
383   if (!l)
384   {
385     goto done;
386   }
387   dbus_message_iter_init (message, &iter);
388   read_device_event_from_iter (&iter, &event);
389   klass = ATSPI_DEVICE_LISTENER_GET_CLASS (listener);
390   if (klass->device_event)
391   {
392     retval = (*klass->device_event) (listener, &event);
393   }
394 done:
395   reply = dbus_message_new_method_return (message);
396   if (reply)
397   {
398     dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &retval, DBUS_TYPE_INVALID);
399     dbus_connection_send (_atspi_bus(), reply, NULL);
400     dbus_message_unref (reply);
401   }
402   return DBUS_HANDLER_RESULT_HANDLED;
403 }
404
405 gchar *
406 _atspi_device_listener_get_path (AtspiDeviceListener *listener)
407 {
408   return g_strdup_printf ("/org/a11y/atspi/listeners/%d", listener->id);
409 }
410
411 G_DEFINE_BOXED_TYPE (AtspiDeviceEvent,
412                      atspi_device_event,
413                      atspi_device_event_copy,
414                      atspi_device_event_free)