1
2 /*
3 * AT-SPI - Assistive Technology Service Provider Interface
4 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 *
6 * Copyright 2001, 2002 Sun Microsystems Inc.,
7 * Copyright 2001, 2002 Ximian, 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 /* atspi_registry.c: Global functions wrapping the registry */
26
27 #include "atspi-private.h"
28
29 typedef struct
30 {
31 AtspiDeviceListener *listener;
32 GArray *key_set;
33 AtspiKeyMaskType modmask;
34 AtspiKeyEventMask event_types;
35 gint sync_type;
36 } DeviceListenerEntry;
37
38 static GList *device_listeners;
39
40 /**
41 * atspi_get_desktop_count:
42 *
43 * Gets the number of virtual desktops.
44 * NOTE: multiple virtual desktops are not implemented yet; as a
45 * consequence, this function always returns 1.
46 *
47 * Returns: a #gint indicating the number of active virtual desktops.
48 **/
49 gint
atspi_get_desktop_count()50 atspi_get_desktop_count ()
51 {
52 return 1;
53 }
54
55 /**
56 * atspi_get_desktop:
57 * @i: a #gint indicating which of the accessible desktops is to be returned.
58 *
59 * Gets the virtual desktop indicated by index @i.
60 * NOTE: currently multiple virtual desktops are not implemented;
61 * as a consequence, any @i value different from 0 will not return a
62 * virtual desktop - instead it will return NULL.
63 *
64 * Returns: (transfer full): a pointer to the @i-th virtual desktop's
65 * #AtspiAccessible representation.
66 **/
67 AtspiAccessible*
atspi_get_desktop(gint i)68 atspi_get_desktop (gint i)
69 {
70 if (i != 0) return NULL;
71 return _atspi_ref_accessible (atspi_bus_registry, atspi_path_root);
72 }
73
74 /**
75 * atspi_get_desktop_list:
76 *
77 * Gets the list of virtual desktops. On return, @list will point
78 * to a newly-created, NULL terminated array of virtual desktop
79 * pointers.
80 * It is the responsibility of the caller to free this array when
81 * it is no longer needed.
82 * NOTE: currently multiple virtual desktops are not implemented;
83 * this implementation always returns a #Garray with a single
84 * #AtspiAccessible desktop.
85 *
86 * Returns: (element-type AtspiAccessible*) (transfer full): a #GArray of
87 * desktops.
88 **/
89 GArray *
atspi_get_desktop_list()90 atspi_get_desktop_list ()
91 {
92 GArray *array = g_array_new (TRUE, TRUE, sizeof (AtspiAccessible *));
93 AtspiAccessible *desktop;
94
95 desktop = _atspi_ref_accessible (atspi_bus_registry, atspi_path_root);
96 if (array)
97 g_array_index (array, AtspiAccessible *, 0) = desktop;
98 return array;
99 }
100
101 static gboolean
notify_keystroke_listener(DeviceListenerEntry * e)102 notify_keystroke_listener (DeviceListenerEntry *e)
103 {
104 gchar *path = _atspi_device_listener_get_path (e->listener);
105 dbus_uint32_t d_modmask = e->modmask;
106 dbus_uint32_t d_event_types = e->event_types;
107 AtspiEventListenerMode listener_mode;
108 gboolean retval = FALSE;
109 DBusError d_error;
110
111 listener_mode.synchronous =
112 (dbus_bool_t) ((e->sync_type & ATSPI_KEYLISTENER_SYNCHRONOUS)!=0);
113 listener_mode.preemptive =
114 (dbus_bool_t) ((e->sync_type & ATSPI_KEYLISTENER_CANCONSUME)!=0);
115 listener_mode.global =
116 (dbus_bool_t) ((e->sync_type & ATSPI_KEYLISTENER_ALL_WINDOWS)!=0);
117
118 dbus_error_init (&d_error);
119 dbind_method_call_reentrant (_atspi_bus(), atspi_bus_registry,
120 atspi_path_dec, atspi_interface_dec,
121 "RegisterKeystrokeListener", &d_error,
122 "oa(iisi)uu(bbb)=>b", path, e->key_set,
123 d_modmask, d_event_types, &listener_mode,
124 &retval);
125 if (dbus_error_is_set (&d_error))
126 {
127 g_warning ("RegisterKeystrokeListener failed: %s", d_error.message);
128 dbus_error_free (&d_error);
129 }
130
131 g_free (path);
132
133 return retval;
134 }
135
136 static void
device_listener_entry_free(DeviceListenerEntry * e)137 device_listener_entry_free (DeviceListenerEntry *e)
138 {
139 g_array_free (e->key_set, TRUE);
140 g_free (e);
141 }
142
143 static void
unregister_listener(gpointer data,GObject * object)144 unregister_listener (gpointer data, GObject *object)
145 {
146 GList *l;
147 AtspiDeviceListener *listener = ATSPI_DEVICE_LISTENER (object);
148
149 for (l = device_listeners; l;)
150 {
151 DeviceListenerEntry *e = l->data;
152 if (e->listener == listener)
153 {
154 GList *next = l->next;
155 device_listener_entry_free (e);
156 device_listeners = g_list_delete_link (device_listeners, l);
157 l = next;
158 }
159 else
160 l = l->next;
161 }
162 }
163
164 /**
165 * atspi_register_keystroke_listener:
166 * @listener: a pointer to the #AtspiDeviceListener for which
167 * keystroke events are requested.
168 * @key_set: (element-type AtspiKeyDefinition) (allow-none): a pointer to the
169 * #AtspiKeyDefinition array indicating which keystroke events are
170 * requested, or NULL
171 * to indicate that all keycodes and keyvals for the specified
172 * modifier set are to be included.
173 * @modmask: an #AtspiKeyMaskType mask indicating which
174 * key event modifiers must be set in combination with @keys,
175 * events will only be reported for key events for which all
176 * modifiers in @modmask are set. If you wish to listen for
177 * events with multiple modifier combinations, you must call
178 * #atspi_register_keystroke_listener once for each
179 * combination.
180 * @event_types: an #AtspiKeyMaskType mask indicating which
181 * types of key events are requested (%ATSPI_KEY_PRESSED etc.).
182 * @sync_type: an #AtspiKeyListenerSyncType parameter indicating
183 * the behavior of the notification/listener transaction.
184 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
185 *
186 * Registers a listener for keystroke events, either pre-emptively for
187 * all windows (%ATSPI_KEYLISTENER_ALL_WINDOWS),
188 * non-preemptively (%ATSPI_KEYLISTENER_NOSYNC), or
189 * pre-emptively at the toolkit level (%ATSPI_KEYLISTENER_CANCONSUME).
190 * If ALL_WINDOWS or CANCONSUME are used, the event is consumed
191 * upon receipt if one of @listener's callbacks returns %TRUE
192 * (other sync_type values may be available in the future).
193 *
194 * Returns: %TRUE if successful, otherwise %FALSE.
195 **/
196 gboolean
atspi_register_keystroke_listener(AtspiDeviceListener * listener,GArray * key_set,AtspiKeyMaskType modmask,AtspiKeyEventMask event_types,AtspiKeyListenerSyncType sync_type,GError ** error)197 atspi_register_keystroke_listener (AtspiDeviceListener *listener,
198 GArray *key_set,
199 AtspiKeyMaskType modmask,
200 AtspiKeyEventMask event_types,
201 AtspiKeyListenerSyncType sync_type,
202 GError **error)
203 {
204 DeviceListenerEntry *e;
205
206 g_return_val_if_fail (listener != NULL, FALSE);
207
208 e = g_new0 (DeviceListenerEntry, 1);
209 e->listener = listener;
210 e->modmask = modmask;
211 e->event_types = event_types;
212 e->sync_type = sync_type;
213 if (key_set)
214 {
215 gint i;
216 e->key_set = g_array_sized_new (FALSE, TRUE, sizeof (AtspiKeyDefinition),
217 key_set->len);
218 e->key_set->len = key_set->len;
219 for (i = 0; i < key_set->len; i++)
220 {
221 AtspiKeyDefinition *kd = ((AtspiKeyDefinition *) key_set->data) + i;
222 AtspiKeyDefinition *d_kd = ((AtspiKeyDefinition *) e->key_set->data) + i;
223 d_kd->keycode = kd->keycode;
224 d_kd->keysym = kd->keysym;
225 if (kd->keystring)
226 {
227 d_kd->keystring = kd->keystring;
228 }
229 else
230 {
231 d_kd->keystring = "";
232 }
233 }
234 }
235 else
236 {
237 e->key_set = g_array_sized_new (FALSE, TRUE, sizeof (AtspiKeyDefinition), 0);
238 }
239
240 g_object_weak_ref (G_OBJECT (listener), unregister_listener, NULL);
241 device_listeners = g_list_prepend (device_listeners, e);
242 return notify_keystroke_listener (e);
243 }
244
245 /**
246 * atspi_deregister_keystroke_listener:
247 * @listener: a pointer to the #AtspiDeviceListener for which
248 * keystroke events are requested.
249 * @key_set: (element-type AtspiKeyDefinition) (allow-none): a pointer to the
250 * #AtspiKeyDefinition array indicating which keystroke events are
251 * requested, or %NULL
252 * to indicate that all keycodes and keyvals for the specified
253 * modifier set are to be included.
254 * @modmask: the key modifier mask for which this listener is to be
255 * 'deregistered' (of type #AtspiKeyMaskType).
256 * @event_types: an #AtspiKeyMaskType mask indicating which
257 * types of key events were requested (%ATSPI_KEY_PRESSED, etc.).
258 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
259 *
260 * Removes a keystroke event listener from the registry's listener queue,
261 * ceasing notification of events with modifiers matching @modmask.
262 *
263 * Returns: %TRUE if successful, otherwise %FALSE.
264 **/
265 gboolean
atspi_deregister_keystroke_listener(AtspiDeviceListener * listener,GArray * key_set,AtspiKeyMaskType modmask,AtspiKeyEventMask event_types,GError ** error)266 atspi_deregister_keystroke_listener (AtspiDeviceListener *listener,
267 GArray *key_set,
268 AtspiKeyMaskType modmask,
269 AtspiKeyEventMask event_types,
270 GError **error)
271 {
272 GArray *d_key_set;
273 gchar *path = _atspi_device_listener_get_path (listener);
274 gint i;
275 dbus_uint32_t d_modmask = modmask;
276 dbus_uint32_t d_event_types = event_types;
277 DBusError d_error;
278 GList *l;
279
280 dbus_error_init (&d_error);
281 if (!listener)
282 {
283 return FALSE;
284 }
285
286 /* copy the keyval filter values from the C api into the DBind KeySet */
287 if (key_set)
288 {
289 d_key_set = g_array_sized_new (FALSE, TRUE, sizeof (AtspiKeyDefinition), key_set->len);
290 d_key_set->len = key_set->len;
291 for (i = 0; i < key_set->len; ++i)
292 {
293 AtspiKeyDefinition *kd = ((AtspiKeyDefinition *) key_set->data) + i;
294 AtspiKeyDefinition *d_kd = ((AtspiKeyDefinition *) d_key_set->data) + i;
295 d_kd->keycode = kd->keycode;
296 d_kd->keysym = kd->keysym;
297 if (kd->keystring)
298 {
299 d_kd->keystring = kd->keystring;
300 }
301 else
302 {
303 d_kd->keystring = "";
304 }
305 }
306 }
307 else
308 {
309 d_key_set = g_array_sized_new (FALSE, TRUE, sizeof (AtspiKeyDefinition), 0);
310 }
311
312 dbind_method_call_reentrant (_atspi_bus(), atspi_bus_registry,
313 atspi_path_dec, atspi_interface_dec,
314 "DeregisterKeystrokeListener", &d_error,
315 "oa(iisi)uu", path, d_key_set, d_modmask,
316 d_event_types);
317 if (dbus_error_is_set (&d_error))
318 {
319 g_warning ("DeregisterKeystrokeListener failed: %s", d_error.message);
320 dbus_error_free (&d_error);
321 }
322
323 unregister_listener (listener, NULL);
324 for (l = device_listeners; l;)
325 {
326 /* TODO: This code is all wrong / doesn't match what is in
327 * deviceeventcontroller.c. It would be nice to deprecate these methods
328 * in favor of methods that return an ID for the registration that can
329 * be passed to a deregister function, for instance. */
330 DeviceListenerEntry *e = l->data;
331 if (e->modmask == modmask && e->event_types == event_types)
332 {
333 GList *next = l->next;
334 device_listener_entry_free (e);
335 device_listeners = g_list_delete_link (device_listeners, l);
336 l = next;
337 }
338 else
339 l = l->next;
340 }
341 g_array_free (d_key_set, TRUE);
342 g_free (path);
343 return TRUE;
344 }
345
346 /**
347 * atspi_register_device_event_listener:
348 * @listener: a pointer to the #AtspiDeviceListener which requests
349 * the events.
350 * @event_types: an #AtspiDeviceEventMask mask indicating which
351 * types of key events are requested (%ATSPI_KEY_PRESSED, etc.).
352 * @filter: (allow-none): Unused parameter.
353 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
354 *
355 * Registers a listener for device events, for instance button events.
356 *
357 * Returns: %TRUE if successful, otherwise %FALSE.
358 **/
359 gboolean
atspi_register_device_event_listener(AtspiDeviceListener * listener,AtspiDeviceEventMask event_types,void * filter,GError ** error)360 atspi_register_device_event_listener (AtspiDeviceListener *listener,
361 AtspiDeviceEventMask event_types,
362 void *filter, GError **error)
363 {
364 gboolean retval = FALSE;
365 dbus_uint32_t d_event_types = event_types;
366 gchar *path = _atspi_device_listener_get_path (listener);
367 DBusError d_error;
368
369 dbus_error_init (&d_error);
370 if (!listener)
371 {
372 return retval;
373 }
374
375 dbind_method_call_reentrant (_atspi_bus(), atspi_bus_registry, atspi_path_dec, atspi_interface_dec, "RegisterDeviceEventListener", &d_error, "ou=>b", path, d_event_types, &retval);
376 if (dbus_error_is_set (&d_error))
377 {
378 g_warning ("RegisterDeviceEventListener failed: %s", d_error.message);
379 dbus_error_free (&d_error);
380 }
381
382 g_free (path);
383 return retval;
384 }
385
386 /**
387 * atspi_deregister_device_event_listener:
388 * @listener: a pointer to the #AtspiDeviceListener for which
389 * device events are requested.
390 * @filter: (allow-none): Unused parameter.
391 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
392 *
393 * Removes a device event listener from the registry's listener queue,
394 * ceasing notification of events of the specified type.
395 *
396 * Returns: %TRUE if successful, otherwise %FALSE.
397 **/
398 gboolean
atspi_deregister_device_event_listener(AtspiDeviceListener * listener,void * filter,GError ** error)399 atspi_deregister_device_event_listener (AtspiDeviceListener *listener,
400 void *filter, GError **error)
401 {
402 dbus_uint32_t event_types = 0;
403 gchar *path = _atspi_device_listener_get_path (listener);
404 DBusError d_error;
405
406 dbus_error_init (&d_error);
407
408 if (!listener)
409 {
410 return FALSE;
411 }
412
413 event_types |= (1 << ATSPI_BUTTON_PRESSED_EVENT);
414 event_types |= (1 << ATSPI_BUTTON_RELEASED_EVENT);
415
416 dbind_method_call_reentrant (_atspi_bus(), atspi_bus_registry, atspi_path_dec, atspi_interface_dec, "DeregisterDeviceEventListener", &d_error, "ou", path, event_types);
417 if (dbus_error_is_set (&d_error))
418 {
419 g_warning ("DeregisterDeviceEventListener failed: %s", d_error.message);
420 dbus_error_free (&d_error);
421 }
422
423 g_free (path);
424 return TRUE;
425 }
426
427 static gboolean
using_mutter()428 using_mutter ()
429 {
430 return (g_getenv ("WAYLAND_DISPLAY") != NULL);
431 }
432
433 /**
434 * atspi_generate_keyboard_event:
435 * @keyval: a #gint indicating the keycode or keysym or modifier mask of the
436 * key event being synthesized.
437 * @keystring: (allow-none): an (optional) UTF-8 string which, if
438 * @synth_type is %ATSPI_KEY_STRING, indicates a 'composed'
439 * keyboard input string being synthesized; this type of
440 * keyboard event synthesis does not emulate hardware
441 * keypresses but injects the string as though a composing
442 * input method (such as XIM) were used.
443 * @synth_type: an #AtspiKeySynthType flag indicating whether @keyval
444 * is to be interpreted as a keysym rather than a keycode
445 * (%ATSPI_KEY_SYM) or a string (%ATSPI_KEY_STRING) or a modifier
446 * mask (%ATSPI_KEY_LOCKMODIFIERS and %ATSPI_KEY_UNLOCKMODIFIERS), or
447 * whether to synthesize %ATSPI_KEY_PRESS,
448 * %ATSPI_KEY_RELEASE, or both (%ATSPI_KEY_PRESSRELEASE).
449 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
450 *
451 * Synthesizes a keyboard event (as if a hardware keyboard event occurred in the
452 * current UI context).
453 *
454 * Returns: %TRUE if successful, otherwise %FALSE.
455 **/
456 gboolean
atspi_generate_keyboard_event(glong keyval,const gchar * keystring,AtspiKeySynthType synth_type,GError ** error)457 atspi_generate_keyboard_event (glong keyval,
458 const gchar *keystring,
459 AtspiKeySynthType synth_type, GError **error)
460 {
461 dbus_uint32_t d_synth_type = synth_type;
462 dbus_int32_t d_keyval = keyval;
463 DBusError d_error;
464
465 if (using_mutter ())
466 {
467 if (_atspi_mutter_generate_keyboard_event (keyval, keystring, synth_type, error))
468 return TRUE;
469 }
470
471 dbus_error_init (&d_error);
472 if (!keystring)
473 keystring = "";
474 dbind_method_call_reentrant (_atspi_bus(), atspi_bus_registry, atspi_path_dec, atspi_interface_dec, "GenerateKeyboardEvent", &d_error, "isu", d_keyval, keystring, d_synth_type);
475 if (dbus_error_is_set (&d_error))
476 {
477 g_warning ("GenerateKeyboardEvent failed: %s", d_error.message);
478 dbus_error_free (&d_error);
479 }
480
481 return TRUE;
482 }
483
484 /**
485 * atspi_generate_mouse_event:
486 * @x: a #glong indicating the screen x coordinate of the mouse event.
487 * @y: a #glong indicating the screen y coordinate of the mouse event.
488 * @name: a string indicating which mouse event to be synthesized
489 * (e.g. "b1p", "b1c", "b2r", "rel", "abs").
490 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
491 *
492 * Synthesizes a mouse event at a specific screen coordinate.
493 * Most AT clients should use the #AccessibleAction interface when
494 * tempted to generate mouse events, rather than this method.
495 * Event names: b1p = button 1 press; b2r = button 2 release;
496 * b3c = button 3 click; b2d = button 2 double-click;
497 * abs = absolute motion; rel = relative motion.
498 *
499 * Returns: %TRUE if successful, otherwise %FALSE.
500 **/
501 gboolean
atspi_generate_mouse_event(glong x,glong y,const gchar * name,GError ** error)502 atspi_generate_mouse_event (glong x, glong y, const gchar *name, GError **error)
503 {
504 dbus_int32_t d_x = x, d_y = y;
505 DBusError d_error;
506
507 g_return_val_if_fail (name != NULL, FALSE);
508
509 if (using_mutter ())
510 {
511 if (_atspi_mutter_generate_mouse_event (x, y, name, error))
512 return TRUE;
513 }
514
515 dbus_error_init (&d_error);
516 dbind_method_call_reentrant (_atspi_bus(), atspi_bus_registry,
517 atspi_path_dec, atspi_interface_dec,
518 "GenerateMouseEvent", &d_error, "iis",
519 d_x, d_y, name);
520 if (dbus_error_is_set (&d_error))
521 {
522 g_warning ("GenerateMouseEvent failed: %s", d_error.message);
523 dbus_error_free (&d_error);
524 }
525
526 return TRUE;
527 }
528
529 /**
530 * atspi_set_reference_window:
531 * @accessible: the #AtspiAccessible corresponding to the window to select.
532 * should be a top-level window with a role of
533 * ATSPI_ROLE_APPLICATION.
534 *
535 * Sets the reference window that will be used when atspi_generate_mouse_event
536 * is called. Coordinates will be assumed to be relative to this window. This
537 * is needed because, due to Wayland's security model, it is not currently
538 * possible to retrieve global coordinates.
539 * If NULL is passed, then AT-SPI will use the window that has focus at the
540 * time that atspi_generate_mouse_event is called.
541 */
542 void
atspi_set_reference_window(AtspiAccessible * accessible)543 atspi_set_reference_window (AtspiAccessible *accessible)
544 {
545 if (using_mutter ())
546 _atspi_mutter_set_reference_window (accessible);
547 }
548
549 AtspiKeyDefinition *
atspi_key_definition_copy(AtspiKeyDefinition * src)550 atspi_key_definition_copy (AtspiKeyDefinition *src)
551 {
552 AtspiKeyDefinition *dst;
553
554 dst = g_new0 (AtspiKeyDefinition, 1);
555 dst->keycode = src->keycode;
556 dst->keysym = src->keysym;
557 if (src->keystring)
558 dst->keystring = g_strdup (src->keystring);
559 dst->unused = src->unused;
560 return dst;
561 }
562
563 void
atspi_key_definition_free(AtspiKeyDefinition * kd)564 atspi_key_definition_free (AtspiKeyDefinition *kd)
565 {
566 if (kd->keystring)
567 g_free (kd->keystring);
568 g_free (kd);
569 }
570
571 void
_atspi_reregister_device_listeners()572 _atspi_reregister_device_listeners ()
573 {
574 GList *l;
575 DeviceListenerEntry *e;
576
577 for (l = device_listeners; l; l = l->next)
578 {
579 e = l->data;
580 notify_keystroke_listener (e);
581 }
582 }
583 G_DEFINE_BOXED_TYPE (AtspiKeyDefinition, atspi_key_definition, atspi_key_definition_copy, atspi_key_definition_free)
584