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