Fix merging errors
[platform/upstream/at-spi2-core.git] / atspi / atspi-registry.c
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
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*
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 *
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
102 //TIZEN_ONLY(20211206) Provide GetActiveWindow
103 /**
104  * atspi_get_active_window:
105  *
106  * Gets recently activated window.
107  * NOTE: the Tizen only interface for multiple AT-clients
108  *
109  * Returns: (transfer full): a pointer to active window's
110  * #AtspiAccessible representation.
111  **/
112 AtspiAccessible*
113 atspi_get_active_window ()
114 {
115   DBusMessage *message, *reply;
116   DBusMessageIter iter;
117   AtspiAccessible* active_window;
118
119   message = dbus_message_new_method_call (ATSPI_DBUS_NAME_REGISTRY,
120                                           ATSPI_DBUS_PATH_REGISTRY,
121                                           ATSPI_DBUS_INTERFACE_REGISTRY,
122                                           "GetActiveWindow");
123   if (!message)
124     return NULL;
125
126   reply = _atspi_dbus_send_with_reply_and_block (message, NULL);
127   if (!reply)
128     return NULL;
129
130   if (strcmp (dbus_message_get_signature (reply), "(so)") != 0)
131   {
132     dbus_message_unref (reply);
133     return NULL;
134   }
135
136   dbus_message_iter_init (reply, &iter);
137   active_window = _atspi_dbus_return_accessible_from_iter (&iter);
138
139   dbus_message_unref (reply);
140
141   if (!active_window)
142     return NULL;
143
144   return g_object_ref (active_window);
145 }
146
147 /**
148  * atspi_get_foreground_windows:
149  *
150  * Gets the array of foreground windows  On return, @array will point
151  *     to a newly-created, NULL terminated array of foreground window
152  *     pointers.
153  *     It is the responsibility of the caller to free this array when
154  *     it is no longer needed.
155  *
156  * Returns: (element-type AtspiAccessible*) (transfer full): a #GArray of
157  * foreground windows.
158  **/
159 GArray *
160 atspi_get_foreground_windows ()
161 {
162   DBusMessage *message, *reply;
163   DBusMessageIter iter, iter_array, iter_dict;
164   GArray *ret;
165
166   message = dbus_message_new_method_call (ATSPI_DBUS_NAME_REGISTRY,
167                                           ATSPI_DBUS_PATH_REGISTRY,
168                                           ATSPI_DBUS_INTERFACE_REGISTRY,
169                                           "GetForegroundWindows");
170   if (!message)
171     return NULL;
172
173   reply = _atspi_dbus_send_with_reply_and_block (message, NULL);
174   if (!reply)
175     return NULL;
176
177   if (strcmp (dbus_message_get_signature (reply), "a(so)") != 0)
178     {
179       dbus_message_unref (reply);
180       return NULL;
181     }
182
183   dbus_message_iter_init (reply, &iter);
184   ret = g_array_new (TRUE, TRUE, sizeof (AtspiAccessible *));
185
186   dbus_message_iter_recurse (&iter, &iter_array);
187   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
188     {
189       const char *name, *path;
190       AtspiAccessible *accessible;
191       dbus_message_iter_recurse (&iter_array, &iter_dict);
192       dbus_message_iter_get_basic (&iter_dict, &name);
193       dbus_message_iter_next (&iter_dict);
194       dbus_message_iter_get_basic (&iter_dict, &path);
195       accessible = ref_accessible(name, path);
196       ret = g_array_append_val (ret, accessible);
197       dbus_message_iter_next (&iter_array);
198     }
199   dbus_message_unref (reply);
200
201   return ret;
202 }
203 //
204
205 static gboolean
206 notify_keystroke_listener (DeviceListenerEntry *e)
207 {
208   gchar *path = _atspi_device_listener_get_path (e->listener);
209   dbus_uint32_t d_modmask = e->modmask;
210   dbus_uint32_t d_event_types = e->event_types;
211   AtspiEventListenerMode     listener_mode;
212   gboolean                          retval = FALSE;
213   DBusError d_error;
214
215   listener_mode.synchronous =
216           (dbus_bool_t) ((e->sync_type & ATSPI_KEYLISTENER_SYNCHRONOUS)!=0);
217   listener_mode.preemptive =
218           (dbus_bool_t) ((e->sync_type & ATSPI_KEYLISTENER_CANCONSUME)!=0);
219   listener_mode.global =
220           (dbus_bool_t) ((e->sync_type & ATSPI_KEYLISTENER_ALL_WINDOWS)!=0);
221
222   dbus_error_init (&d_error);
223   dbind_method_call_reentrant (_atspi_bus(), atspi_bus_registry,
224                                atspi_path_dec, atspi_interface_dec,
225                                "RegisterKeystrokeListener", &d_error,
226                                "oa(iisi)uu(bbb)=>b", path, e->key_set,
227                                d_modmask, d_event_types, &listener_mode,
228                                &retval);
229   if (dbus_error_is_set (&d_error))
230     {
231       g_warning ("RegisterKeystrokeListener failed: %s", d_error.message);
232       dbus_error_free (&d_error);
233     }
234
235   g_free (path);
236
237   return retval;
238 }
239
240 static void
241 device_listener_entry_free (DeviceListenerEntry *e)
242 {
243   g_array_free (e->key_set, TRUE);
244   g_free (e);
245 }
246
247 static void
248 unregister_listener (gpointer data, GObject *object)
249 {
250   GList *l;
251   AtspiDeviceListener *listener = ATSPI_DEVICE_LISTENER (object);
252
253   for (l = device_listeners; l;)
254     {
255       DeviceListenerEntry *e = l->data;
256       if (e->listener == listener)
257         {
258           GList *next = l->next;
259           device_listener_entry_free (e);
260           device_listeners = g_list_delete_link (device_listeners, l);
261           l = next;
262         }
263       else
264         l = l->next;
265     }
266 }
267
268 /**
269  * atspi_register_keystroke_listener:
270  * @listener:  a pointer to the #AtspiDeviceListener for which
271  *             keystroke events are requested.
272  * @key_set: (element-type AtspiKeyDefinition) (allow-none): a pointer to the
273  *        #AtspiKeyDefinition array indicating which keystroke events are
274  *        requested, or NULL
275  *        to indicate that all keycodes and keyvals for the specified
276  *        modifier set are to be included.
277  * @modmask:   an #AtspiKeyMaskType mask indicating which
278  *             key event modifiers must be set in combination with @keys,
279  *             events will only be reported for key events for which all
280  *             modifiers in @modmask are set.  If you wish to listen for
281  *             events with multiple modifier combinations, you must call
282  *             #atspi_register_keystroke_listener once for each
283  *             combination.
284  * @event_types: an #AtspiKeyMaskType mask indicating which
285  *             types of key events are requested (%ATSPI_KEY_PRESSED etc.).
286  * @sync_type: an #AtspiKeyListenerSyncType parameter indicating
287  *             the behavior of the notification/listener transaction.
288  * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
289  *             
290  * Registers a listener for keystroke events, either pre-emptively for
291  *             all windows (%ATSPI_KEYLISTENER_ALL_WINDOWS),
292  *             non-preemptively (%ATSPI_KEYLISTENER_NOSYNC), or
293  *             pre-emptively at the toolkit level (%ATSPI_KEYLISTENER_CANCONSUME).
294  *             If ALL_WINDOWS or CANCONSUME are used, the event is consumed
295  *             upon receipt if one of @listener's callbacks returns %TRUE 
296  *             (other sync_type values may be available in the future).
297  *
298  * Returns: %TRUE if successful, otherwise %FALSE.
299  **/
300 gboolean
301 atspi_register_keystroke_listener (AtspiDeviceListener  *listener,
302                                          GArray             *key_set,
303                                          AtspiKeyMaskType         modmask,
304                                          AtspiKeyEventMask        event_types,
305                                          AtspiKeyListenerSyncType sync_type,
306                                          GError **error)
307 {
308   DeviceListenerEntry *e;
309
310   g_return_val_if_fail (listener != NULL, FALSE);
311
312   e = g_new0 (DeviceListenerEntry, 1);
313   e->listener = listener;
314   e->modmask = modmask;
315   e->event_types = event_types;
316   e->sync_type = sync_type;
317   if (key_set)
318     {
319       gint i;
320       e->key_set = g_array_sized_new (FALSE, TRUE, sizeof (AtspiKeyDefinition),
321                                       key_set->len);
322       e->key_set->len = key_set->len;
323       for (i = 0; i < key_set->len; i++)
324         {
325           AtspiKeyDefinition *kd =  ((AtspiKeyDefinition *) key_set->data) + i;
326           AtspiKeyDefinition *d_kd =  ((AtspiKeyDefinition *) e->key_set->data) + i;
327           d_kd->keycode = kd->keycode;
328           d_kd->keysym = kd->keysym;
329           if (kd->keystring)
330             {
331               d_kd->keystring = kd->keystring;
332             } 
333           else 
334             {
335               d_kd->keystring = "";
336             }
337         }
338     }
339   else
340     {
341       e->key_set = g_array_sized_new (FALSE, TRUE, sizeof (AtspiKeyDefinition), 0);
342     }
343
344   g_object_weak_ref (G_OBJECT (listener), unregister_listener, NULL);
345   device_listeners = g_list_prepend (device_listeners, e);
346   return notify_keystroke_listener (e);
347 }
348
349 /**
350  * atspi_deregister_keystroke_listener:
351  * @listener: a pointer to the #AtspiDeviceListener for which
352  *            keystroke events are requested.
353  * @key_set: (element-type AtspiKeyDefinition) (allow-none): a pointer to the
354  *        #AtspiKeyDefinition array indicating which keystroke events are
355  *        requested, or %NULL
356  *        to indicate that all keycodes and keyvals for the specified
357  *        modifier set are to be included.
358  * @modmask:  the key modifier mask for which this listener is to be
359  *            'deregistered' (of type #AtspiKeyMaskType).
360  * @event_types: an #AtspiKeyMaskType mask indicating which
361  *             types of key events were requested (%ATSPI_KEY_PRESSED, etc.).
362  * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
363  *
364  * Removes a keystroke event listener from the registry's listener queue,
365  *            ceasing notification of events with modifiers matching @modmask.
366  *
367  * Returns: %TRUE if successful, otherwise %FALSE.
368  **/
369 gboolean
370 atspi_deregister_keystroke_listener (AtspiDeviceListener *listener,
371                                      GArray              *key_set,
372                                      AtspiKeyMaskType     modmask,
373                                      AtspiKeyEventMask    event_types,
374                                      GError             **error)
375 {
376   GArray *d_key_set;
377   gchar *path;
378   gint i;
379   dbus_uint32_t d_modmask = modmask;
380   dbus_uint32_t d_event_types = event_types;
381   DBusError d_error;
382   GList *l;
383
384   if (!listener)
385     {
386       return FALSE;
387     }
388
389   dbus_error_init (&d_error);
390
391   path = _atspi_device_listener_get_path (listener);
392
393   /* copy the keyval filter values from the C api into the DBind KeySet */
394   if (key_set)
395     {
396       d_key_set = g_array_sized_new (FALSE, TRUE, sizeof (AtspiKeyDefinition), key_set->len);
397       d_key_set->len = key_set->len;
398       for (i = 0; i < key_set->len; ++i)
399         {
400           AtspiKeyDefinition *kd =  ((AtspiKeyDefinition *) key_set->data) + i;
401           AtspiKeyDefinition *d_kd =  ((AtspiKeyDefinition *) d_key_set->data) + i;
402           d_kd->keycode = kd->keycode;
403           d_kd->keysym = kd->keysym;
404           if (kd->keystring)
405             {
406               d_kd->keystring = kd->keystring;
407             } 
408           else 
409             {
410               d_kd->keystring = "";
411             }
412         }
413     }
414   else
415     {
416       d_key_set = g_array_sized_new (FALSE, TRUE, sizeof (AtspiKeyDefinition), 0);
417     }
418
419   dbind_method_call_reentrant (_atspi_bus(), atspi_bus_registry,
420                                atspi_path_dec, atspi_interface_dec,
421                                "DeregisterKeystrokeListener", &d_error,
422                                "oa(iisi)uu", path, d_key_set, d_modmask,
423                                d_event_types);
424   if (dbus_error_is_set (&d_error))
425     {
426       g_warning ("DeregisterKeystrokeListener failed: %s", d_error.message);
427       dbus_error_free (&d_error);
428     }
429
430   unregister_listener (listener, NULL);
431   for (l = device_listeners; l;)
432     {
433       /* TODO: This code is all wrong / doesn't match what is in
434  *       deviceeventcontroller.c. It would be nice to deprecate these methods
435  *       in favor of methods that return an ID for the registration that can
436  *       be passed to a deregister function, for instance. */
437       DeviceListenerEntry *e = l->data;
438       if (e->modmask == modmask && e->event_types == event_types)
439         {
440           GList *next = l->next;
441           device_listener_entry_free (e);
442           device_listeners = g_list_delete_link (device_listeners, l);
443           l = next;
444         }
445       else
446         l = l->next;
447     }
448   g_array_free (d_key_set, TRUE);
449   g_free (path);
450   return TRUE;
451 }
452
453 /**
454  * atspi_register_device_event_listener:
455  * @listener:  a pointer to the #AtspiDeviceListener which requests
456  *             the events.
457  * @event_types: an #AtspiDeviceEventMask mask indicating which
458  *             types of key events are requested (%ATSPI_KEY_PRESSED, etc.).
459  * @filter: (allow-none): Unused parameter.
460  * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
461  *             
462  * Registers a listener for device events, for instance button events.
463  *
464  * Returns: %TRUE if successful, otherwise %FALSE.
465  **/
466 gboolean
467 atspi_register_device_event_listener (AtspiDeviceListener  *listener,
468                                  AtspiDeviceEventMask  event_types,
469                                  void                      *filter, GError **error)
470 {
471   gboolean                          retval = FALSE;
472   dbus_uint32_t d_event_types = event_types;
473   gchar *path;
474   DBusError d_error;
475
476   if (!listener)
477     {
478       return retval;
479     }
480
481   dbus_error_init (&d_error);
482
483   path = _atspi_device_listener_get_path (listener);
484
485     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);
486     if (dbus_error_is_set (&d_error))
487       {
488         g_warning ("RegisterDeviceEventListener failed: %s", d_error.message);
489         dbus_error_free (&d_error);
490       }
491
492   g_free (path);
493   return retval;
494 }
495
496 /**
497  * atspi_deregister_device_event_listener:
498  * @listener: a pointer to the #AtspiDeviceListener for which
499  *            device events are requested.
500  * @filter: (allow-none): Unused parameter.
501  * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
502  *
503  * Removes a device event listener from the registry's listener queue,
504  *            ceasing notification of events of the specified type.
505  *
506  * Returns: %TRUE if successful, otherwise %FALSE.
507  **/
508 gboolean
509 atspi_deregister_device_event_listener (AtspiDeviceListener *listener,
510                                    void                     *filter, GError **error)
511 {
512   dbus_uint32_t event_types = 0;
513   gchar *path;
514   DBusError d_error;
515
516   if (!listener)
517     {
518       return FALSE;
519     }
520
521   dbus_error_init (&d_error);
522
523   path = _atspi_device_listener_get_path (listener);
524
525   event_types |= (1 << ATSPI_BUTTON_PRESSED_EVENT);
526   event_types |= (1 << ATSPI_BUTTON_RELEASED_EVENT);
527
528   dbind_method_call_reentrant (_atspi_bus(), atspi_bus_registry, atspi_path_dec, atspi_interface_dec, "DeregisterDeviceEventListener", &d_error, "ou", path, event_types);
529   if (dbus_error_is_set (&d_error))
530     {
531       g_warning ("DeregisterDeviceEventListener failed: %s", d_error.message);
532       dbus_error_free (&d_error);
533     }
534
535   g_free (path);
536   return TRUE;
537 }
538
539 static gboolean
540 using_mutter ()
541 {
542   return (g_getenv ("WAYLAND_DISPLAY") != NULL);
543 }
544
545 /**
546  * atspi_generate_keyboard_event:
547  * @keyval: a #gint indicating the keycode or keysym or modifier mask of the
548  *           key event being synthesized.
549  * @keystring: (allow-none): an (optional) UTF-8 string which, if
550  *           @synth_type is %ATSPI_KEY_STRING, indicates a 'composed'
551  *           keyboard input string being synthesized; this type of
552  *           keyboard event synthesis does not emulate hardware
553  *           keypresses but injects the string as though a composing
554  *           input method (such as XIM) were used.
555  * @synth_type: an #AtspiKeySynthType flag indicating whether @keyval
556  *           is to be interpreted as a keysym rather than a keycode
557  *           (%ATSPI_KEY_SYM) or a string (%ATSPI_KEY_STRING) or a modifier
558  *           mask (%ATSPI_KEY_LOCKMODIFIERS and %ATSPI_KEY_UNLOCKMODIFIERS), or
559  *           whether to synthesize %ATSPI_KEY_PRESS,
560  *           %ATSPI_KEY_RELEASE, or both (%ATSPI_KEY_PRESSRELEASE).
561  * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
562  *
563  * Synthesizes a keyboard event (as if a hardware keyboard event occurred in the
564  * current UI context).
565  *
566  * Returns: %TRUE if successful, otherwise %FALSE.
567  **/
568 gboolean
569 atspi_generate_keyboard_event (glong keyval,
570                            const gchar *keystring,
571                            AtspiKeySynthType synth_type, GError **error)
572 {
573   dbus_uint32_t d_synth_type = synth_type;
574   dbus_int32_t d_keyval = keyval;
575   DBusError d_error;
576
577   if (using_mutter ())
578   {
579     if (_atspi_mutter_generate_keyboard_event (keyval, keystring, synth_type, error))
580       return TRUE;
581   }
582
583   dbus_error_init (&d_error);
584   if (!keystring)
585     keystring = "";
586   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);
587   if (dbus_error_is_set (&d_error))
588     {
589       g_warning ("GenerateKeyboardEvent failed: %s", d_error.message);
590       dbus_error_free (&d_error);
591     }
592
593   return TRUE;
594 }
595
596 /**
597  * atspi_generate_mouse_event:
598  * @x: a #glong indicating the screen x coordinate of the mouse event.
599  * @y: a #glong indicating the screen y coordinate of the mouse event.
600  * @name: a string indicating which mouse event to be synthesized
601  *        (e.g. "b1p", "b1c", "b2r", "rel", "abs").
602  * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
603  *
604  * Synthesizes a mouse event at a specific screen coordinate.
605  * Most AT clients should use the #AccessibleAction interface when
606  * tempted to generate mouse events, rather than this method.
607  * Event names: b1p = button 1 press; b2r = button 2 release;
608  *              b3c = button 3 click; b2d = button 2 double-click;
609  *              abs = absolute motion; rel = relative motion.
610  *
611  * Returns: %TRUE if successful, otherwise %FALSE.
612  **/
613 gboolean
614 atspi_generate_mouse_event (glong x, glong y, const gchar *name, GError **error)
615 {
616   dbus_int32_t d_x = x, d_y = y;
617   DBusError d_error;
618
619   g_return_val_if_fail (name != NULL, FALSE);
620
621   if (using_mutter ())
622   {
623     if (_atspi_mutter_generate_mouse_event (x, y, name, error))
624       return TRUE;
625   }
626
627   dbus_error_init (&d_error);
628   dbind_method_call_reentrant (_atspi_bus(), atspi_bus_registry,
629                                atspi_path_dec, atspi_interface_dec,
630                                "GenerateMouseEvent", &d_error, "iis",
631                                d_x, d_y, name);
632   if (dbus_error_is_set (&d_error))
633     {
634       g_warning ("GenerateMouseEvent failed: %s", d_error.message);
635       dbus_error_free (&d_error);
636     }
637
638   return TRUE;
639 }
640
641 /**
642  * atspi_set_reference_window:
643  * @accessible: the #AtspiAccessible corresponding to the window to select.
644  *              should be a top-level window with a role of
645  *              ATSPI_ROLE_APPLICATION.
646  *
647  * Sets the reference window that will be used when atspi_generate_mouse_event
648  * is called. Coordinates will be assumed to be relative to this window. This
649  * is needed because, due to Wayland's security model, it is not currently
650  * possible to retrieve global coordinates.
651  * If NULL is passed, then AT-SPI will use the window that has focus at the
652  * time that atspi_generate_mouse_event is called.
653  */
654 void
655 atspi_set_reference_window (AtspiAccessible *accessible)
656 {
657   if (using_mutter ())
658     _atspi_mutter_set_reference_window (accessible);
659 }
660
661 AtspiKeyDefinition *
662 atspi_key_definition_copy (AtspiKeyDefinition *src)
663 {
664   AtspiKeyDefinition *dst;
665
666   dst = g_new0 (AtspiKeyDefinition, 1);
667   dst->keycode = src->keycode;
668   dst->keysym = src->keysym;
669   if (src->keystring)
670     dst->keystring = g_strdup (src->keystring);
671   dst->modifiers = src->modifiers;
672   return dst;
673 }
674
675 void
676 atspi_key_definition_free (AtspiKeyDefinition *kd)
677 {
678   if (kd->keystring)
679     g_free (kd->keystring);
680   g_free (kd);
681 }
682
683 void
684 _atspi_reregister_device_listeners ()
685 {
686   GList *l;
687   DeviceListenerEntry *e;
688
689   for (l = device_listeners; l; l = l->next)
690     {
691       e = l->data;
692       notify_keystroke_listener (e);
693     }
694 }
695 G_DEFINE_BOXED_TYPE (AtspiKeyDefinition, atspi_key_definition, atspi_key_definition_copy, atspi_key_definition_free)