Initial commit, not even close to being usable yet
[platform/upstream/at-spi2-core.git] / atspi / atspi-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   union
29     {
30       AtspiEventListenerCB     event;
31       AtspiDeviceListenerCB    device_event;
32       gpointer                      method;
33     } cb;
34   gpointer user_data;
35 } EventHandler;
36
37 GObjectClass *event_parent_class;
38 GObjectClass *device_parent_class;
39
40 static guint32 _e_id = 0;
41
42 /*
43  * Misc. helpers.
44  */
45
46 static EventHandler *
47 atspi_event_handler_new (gpointer method, gpointer user_data)
48 {
49   EventHandler *eh = g_new0 (EventHandler, 1);
50
51   eh->cb.method = method;
52   eh->user_data = user_data;
53
54   return eh;
55 }
56
57 static GList *
58 event_list_remove_by_cb (GList *list, gpointer callback)
59 {
60   GList *l, *next;
61         
62   for (l = list; l; l = next)
63     {
64       EventHandler *eh = l->data;
65       next = l->next;
66
67       if (eh->cb.method == callback)
68       {
69         list = g_list_delete_link (list, l);
70         g_free (eh);
71       }
72     }
73
74   return list;
75 }
76
77 /*
78  * Standard event dispatcher
79  */
80
81 G_DEFINE_TYPE (AtspiEventListener, atspi_event_listener,
82                           G_TYPE_OBJECT)
83
84 static void
85 atspi_event_dispatch (AtspiEventListener    *listener,
86             const AtspiEvent *event)
87 {
88   GList *l;
89   
90   /* FIXME: re-entrancy hazard on this list */
91   for (l = listener->callbacks; l; l = l->next)
92     {
93       EventHandler *eh = l->data;
94       /* cast hides our private stuff from client handlers */
95       eh->cb.event (event, eh->user_data);
96     }
97 }
98
99 static guint listener_id = 0;
100 static GList *device_listeners = NULL;
101
102 static gboolean
103 id_is_free (guint id)
104 {
105   GList *l;
106
107   for (l = device_listeners; l; l = g_list_next (l))
108   {
109     AtspiDeviceListener *listener = l->data;
110     if (listener->id == id) return FALSE;
111   }
112   return TRUE;
113 }
114
115 static void
116 remove_listener (GObject *obj, gpointer data)
117 {
118   device_listeners = g_list_remove (device_listeners, obj);
119 }
120
121 static void
122 atspi_event_listener_init (AtspiEventListener *listener)
123 {
124 }
125
126 static void
127 atspi_event_listener_finalize (GObject *object)
128 {
129   AtspiEventListener *listener = (AtspiEventListener *) object;
130   GList *l;
131   
132   for (l = listener->callbacks; l; l = l->next)
133     {
134       g_free (l->data);
135     }
136   
137   g_list_free (listener->callbacks);
138
139   event_parent_class->finalize (object);
140 }
141
142 static void
143 atspi_event_listener_class_init (AtspiEventListenerClass *klass)
144 {
145   GObjectClass *object_class = (GObjectClass *) klass;
146
147   event_parent_class = g_type_class_peek_parent (klass);
148   object_class->finalize = atspi_event_listener_finalize;
149
150   klass->event = atspi_event_dispatch;
151 }
152
153 AtspiEventListener *
154 atspi_event_listener_new (void)
155 {
156   AtspiEventListener *listener;
157
158   listener = g_object_new (atspi_event_listener_get_type (), NULL);
159
160   return listener;
161 }
162
163 void
164 atspi_event_listener_add_cb (AtspiEventListener  *listener,
165                             AtspiEventListenerCB callback,
166                             void                     *user_data)
167 {
168   g_return_if_fail (ATSPI_IS_EVENT_LISTENER (listener));
169
170   listener->callbacks = g_list_prepend (listener->callbacks,
171                                         atspi_event_handler_new ((void *) callback, user_data));
172 }
173
174 void
175 atspi_event_listener_remove_cb (AtspiEventListener  *listener,
176                                AtspiEventListenerCB callback)
177 {
178   g_return_if_fail (ATSPI_IS_EVENT_LISTENER (listener));
179
180   listener->callbacks = event_list_remove_by_cb (listener->callbacks, (void *) callback);
181 }
182
183 /* 
184  * Device event handler
185  */
186 static gboolean
187 atspi_device_event_dispatch (AtspiDeviceListener               *listener,
188                    const AtspiDeviceEvent *event)
189 {
190   GList *l;
191   AtspiDeviceEvent anevent;
192   gboolean handled = FALSE;
193
194   /* FIXME: re-enterancy hazard on this list */
195   for (l = listener->callbacks; l; l = l->next)
196     {
197       EventHandler *eh = l->data;
198
199       if ((handled = eh->cb.device_event (&anevent, eh->user_data)))
200         {
201           break;
202         }
203     }
204
205   return handled;
206 }
207
208 static void
209 atspi_device_listener_init (AtspiDeviceListener *listener)
210 {
211   GList *new_list;
212
213   do
214   {
215     listener->id = listener_id++;
216   } while (!id_is_free (listener->id));
217   new_list = g_list_append (device_listeners, listener);
218   if (new_list) device_listeners = new_list;
219 }
220
221 static void
222 atspi_device_listener_finalize (GObject *object)
223 {
224   AtspiDeviceListener *listener = (AtspiDeviceListener *) object;
225   GList *l;
226   
227   for (l = listener->callbacks; l; l = l->next)
228     {
229       g_free (l->data);
230     }
231   
232   g_list_free (listener->callbacks);
233
234   device_parent_class->finalize (object);
235 }
236
237 static void
238 atspi_device_listener_class_init (AtspiDeviceListenerClass *klass)
239 {
240   GObjectClass *object_class = (GObjectClass *) klass;
241
242   device_parent_class = g_type_class_peek_parent (klass);
243   object_class->finalize = atspi_device_listener_finalize;
244
245   klass->device_event = atspi_device_event_dispatch;
246 }
247
248 G_DEFINE_TYPE (AtspiDeviceListener, atspi_device_listener,
249                           G_TYPE_OBJECT)
250
251 AtspiDeviceListener *
252 atspi_device_listener_new (void)
253 {
254   AtspiDeviceListener *listener = g_object_new (atspi_device_listener_get_type (), NULL);
255
256   return listener;
257 }
258
259 void
260 atspi_device_listener_add_cb (AtspiDeviceListener  *listener,
261                              AtspiDeviceListenerCB callback,
262                              void                      *user_data)
263 {
264   g_return_if_fail (ATSPI_IS_DEVICE_LISTENER (listener));
265
266   listener->callbacks = g_list_prepend (listener->callbacks,
267                                         atspi_event_handler_new ((void *)callback, user_data));
268 }
269
270 void
271 atspi_device_listener_remove_cb (AtspiDeviceListener  *listener,
272                                 AtspiDeviceListenerCB callback)
273 {
274   g_return_if_fail (ATSPI_IS_DEVICE_LISTENER (listener));
275
276   listener->callbacks = event_list_remove_by_cb (listener->callbacks, (void *) callback);
277 }
278
279 static const char *device_event_type = "(uinnisb)";
280
281 static void
282 read_device_event_from_iter (DBusMessageIter *iter, AtspiDeviceEvent *event)
283 {
284   dbus_uint32_t type;
285   dbus_int32_t id;
286   dbus_int16_t hw_code;
287   dbus_int16_t modifiers;
288   dbus_int32_t timestamp;
289   char *event_string;
290   dbus_bool_t is_text;
291   DBusMessageIter iter_struct;
292
293   dbus_message_iter_recurse (iter, &iter_struct);
294
295   dbus_message_iter_get_basic (&iter_struct, &type);
296   event->type = type;
297   dbus_message_iter_next (&iter_struct);
298
299   dbus_message_iter_get_basic (&iter_struct, &id);
300   event->id = id;
301   dbus_message_iter_next (&iter_struct);
302
303   dbus_message_iter_get_basic (&iter_struct, &hw_code);
304   event->hw_code = hw_code;
305   dbus_message_iter_next (&iter_struct);
306
307   dbus_message_iter_get_basic (&iter_struct, &modifiers);
308   event->modifiers = modifiers;
309   dbus_message_iter_next (&iter_struct);
310
311   dbus_message_iter_get_basic (&iter_struct, &timestamp);
312   event->timestamp = timestamp;
313   dbus_message_iter_next (&iter_struct);
314
315   dbus_message_iter_get_basic (&iter_struct, &event->event_string);
316   dbus_message_iter_next (&iter_struct);
317
318   dbus_message_iter_get_basic (&iter_struct, &is_text);
319   event->is_text = is_text;
320 }
321
322 /*
323  * atspi_dbus_handle_DeviceEvent: (skip)
324  */
325 DBusHandlerResult
326 atspi_dbus_handle_DeviceEvent (DBusConnection *bus, DBusMessage *message, void *data)
327 {
328   const char *path = dbus_message_get_path (message);
329   int id;
330   AtspiDeviceEvent event;
331     AtspiDeviceListener *listener;
332   DBusMessageIter iter;
333   AtspiDeviceListenerClass *klass;
334   dbus_bool_t retval = FALSE;
335   GList *l;
336   DBusMessage *reply;
337   void *p = &event;
338
339   if (strcmp (dbus_message_get_signature (message), "(uinnisb)") != 0)
340   {
341     g_warning ("Atspi: Unknown signature for an event");
342     goto done;
343   }
344
345   if (sscanf (path, "/org/a11y/atspi/listeners/%d", &id) != 1)
346   {
347     g_warning ("Atspi: Bad listener path: %s\n", path);
348     goto done;
349   }
350
351   for (l = device_listeners; l; l = g_list_next (l))
352   {
353     listener = l->data;
354     if (listener->id == id) break;
355   }
356
357   if (!l)
358   {
359     goto done;
360   }
361   dbus_message_iter_init (message, &iter);
362   read_device_event_from_iter (&iter, &event);
363   klass = ATSPI_DEVICE_LISTENER_GET_CLASS (listener);
364   if (klass->device_event)
365   {
366     retval = (*klass->device_event) (listener, &event);
367   }
368 done:
369   reply = dbus_message_new_method_return (message);
370   if (reply)
371   {
372     dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &retval, DBUS_TYPE_INVALID);
373     dbus_connection_send (_atspi_bus(), reply, NULL);
374     dbus_message_unref (reply);
375   }
376   return DBUS_HANDLER_RESULT_HANDLED;
377 }
378
379 gchar *
380 _atspi_device_listener_get_path (AtspiDeviceListener *listener)
381 {
382   return g_strdup_printf ("/org/a11y/atspi/listeners/%d", listener->id);
383 }