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