2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2001, 2002, 2003 Sun Microsystems Inc.,
6 * Copyright 2001, 2002, 2003 Ximian, Inc.
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.
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.
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.
24 #include <cspi/spi-private.h>
26 static GSList *_cspi_event_queue = NULL;
29 * SPI_freeAccessibleKeySet:
30 * @keyset: An AccessibleKeyset to free.
32 * Release the memory used by an AccessibleKeySet.
36 SPI_freeAccessibleKeySet (AccessibleKeySet *keyset)
39 g_free (keyset->keysyms);
40 g_free (keyset->keycodes);
41 while (keyset->keystrings [i])
43 g_free (keyset->keystrings [i++]);
45 g_free (keyset->keystrings);
50 * SPI_createAccessibleKeySet:
51 * @len: the number of key values in the key set.
52 * @keysyms: a UTF-8 string containing symbolic key values to be matched, or NULL if
53 * matching is performed against other key values instead.
54 * @keycodes: an array of unsigned short values which are the hardware keycodes
55 * to be matched, or NULL if the keyset is specified solely by keysyms
57 * @keystrings: an array of null-terminated character strings which specify key
58 * name values to match, or NULL if the keyset is specified solely by
59 * keycodes and/or keysyms.
61 * Create a new #AccessibleKeySet of a specified length.
62 * A KeySet is used typically to match key event values, and a matches are made
63 * using the following criteria: a match exists with a key event if all non-null
64 * i-th members of the keyset match the key event.
65 * If both keystring and keysym values are NULL, a keycode value match is
66 * forced, thus the match for keysym=0, keycode=0, keystring=NULL is
69 * Returns: a pointer to a newly-created #AccessibleKeySet.
73 SPI_createAccessibleKeySet (int len, const char *keysyms, short *keycodes,
74 const char **keystrings)
76 AccessibleKeySet *keyset = g_new0 (AccessibleKeySet, 1);
77 int i, keysym_len = 0;
78 const char *keysym_ptr = keysyms;
80 keyset->keysyms = g_new0 (unsigned long, len);
81 keyset->keycodes = g_new0 (unsigned short, len);
82 keyset->keystrings = g_new0 (char *, len);
85 keysym_len = g_utf8_strlen (keysyms, -1);
87 for (i = 0; i < len; ++i)
91 keyset->keysyms [i] = (unsigned long) g_utf8_get_char (keysym_ptr);
92 keysym_ptr = g_utf8_find_next_char (keysym_ptr, NULL);
96 keyset->keysyms [i] = 0;
100 keyset->keycodes [i] = keycodes [i];
104 keyset->keystrings [i] = g_strdup (keystrings [i]);
111 * SPI_createAccessibleEventListener:
112 * @callback : an #AccessibleEventListenerCB callback function, or NULL.
113 * @user_data: a pointer to data which will be passed to the callback when invoked.
115 * Create a new #AccessibleEventListener with a specified (in-process) callback function.
117 * Returns: a pointer to a newly-created #AccessibleEventListener.
120 AccessibleEventListener *
121 SPI_createAccessibleEventListener (AccessibleEventListenerCB callback,
124 AccessibleEventListener *listener = cspi_event_listener_new ();
127 AccessibleEventListener_addCallback (listener, callback, user_data);
133 * AccessibleEventListener_addCallback:
134 * @listener: the #AccessibleEventListener instance to modify.
135 * @callback: an #AccessibleEventListenerCB function pointer.
136 * @user_data: a pointer to data which will be passed to the callback when invoked.
138 * Add an in-process callback function to an existing AccessibleEventListener.
139 * Note that the callback function must live in the same address
140 * space as the AccessibleEventListener implementation code, thus one should not
141 * use this function to attach callbacks to a 'remote' event listener
142 * (that is, one that was not created by a client call to
143 * createAccessibleEventListener ();
145 * Returns: #TRUE if successful, otherwise #FALSE.
149 AccessibleEventListener_addCallback (AccessibleEventListener *listener,
150 AccessibleEventListenerCB callback,
153 cspi_event_listener_add_cb (listener, callback, user_data);
158 * AccessibleEventListener_unref:
159 * @listener: a pointer to the #AccessibleEventListener being operated on.
161 * Decrements an #AccessibleEventListener's reference count.
164 AccessibleEventListener_unref (AccessibleEventListener *listener)
166 cspi_event_listener_unref (listener);
170 * AccessibleEventListener_removeCallback:
171 * @listener: the #AccessibleEventListener instance to modify.
172 * @callback: an #AccessibleEventListenerCB function pointer.
174 * Remove an in-process callback function from an existing AccessibleEventListener.
176 * Returns: #TRUE if successful, otherwise #FALSE.
180 AccessibleEventListener_removeCallback (AccessibleEventListener *listener,
181 AccessibleEventListenerCB callback)
183 cspi_event_listener_remove_cb (listener, callback);
188 * SPI_createAccessibleKeystrokeListener:
189 * @callback : an #AccessibleKeystrokeListenerCB callback function, or NULL.
190 * @user_data: a pointer to data which will be passed to the callback when invoked.
192 * Create a new #AccessibleKeystrokeListener with a specified callback function.
194 * Returns: a pointer to a newly-created #AccessibleKeystrokeListener.
197 AccessibleKeystrokeListener *
198 SPI_createAccessibleKeystrokeListener (AccessibleKeystrokeListenerCB callback,
201 AccessibleDeviceListener *listener = cspi_device_listener_new ();
204 AccessibleDeviceListener_addCallback (listener, callback, user_data);
210 * AccessibleKeystrokeListener_addCallback:
211 * @listener: the #AccessibleKeystrokeListener instance to modify.
212 * @callback: an #AccessibleKeystrokeListenerCB function pointer.
213 * @user_data: a pointer to data which will be passed to the callback when invoked.
215 * Add an in-process callback function to an existing #AccessibleKeystrokeListener.
217 * Returns: #TRUE if successful, otherwise #FALSE.
221 AccessibleKeystrokeListener_addCallback (AccessibleKeystrokeListener *listener,
222 AccessibleKeystrokeListenerCB callback,
225 cspi_device_listener_add_cb (listener, callback, user_data);
230 * AccessibleKeystrokeListener_removeCallback:
231 * @listener: the #AccessibleKeystrokeListener instance to modify.
232 * @callback: an #AccessibleKeystrokeListenerCB function pointer.
234 * Remove an in-process callback function from an existing #AccessibleKeystrokeListener.
236 * Returns: #TRUE if successful, otherwise #FALSE.
240 AccessibleKeystrokeListener_removeCallback (AccessibleKeystrokeListener *listener,
241 AccessibleKeystrokeListenerCB callback)
243 cspi_device_listener_remove_cb (listener, callback);
248 * AccessibleKeystrokeListener_unref:
249 * @listener: a pointer to the #AccessibleKeystrokeListener being operated on.
251 * Decrements an #AccessibleKeystrokeListener's reference count.
254 AccessibleKeystrokeListener_unref (AccessibleKeystrokeListener *listener)
256 cspi_device_listener_unref (listener);
260 * SPI_createAccessibleDeviceListener:
261 * @callback : an #AccessibleDeviceListenerCB callback function, or NULL.
262 * @user_data: a pointer to data which will be passed to the callback when invoked.
264 * Create a new #AccessibleDeviceListener with a specified callback function.
266 * Returns: a pointer to a newly-created #AccessibleDeviceListener.
269 AccessibleDeviceListener *
270 SPI_createAccessibleDeviceListener (AccessibleDeviceListenerCB callback,
273 AccessibleDeviceListener *listener = cspi_device_listener_new ();
276 AccessibleDeviceListener_addCallback (listener, callback, user_data);
282 * AccessibleDeviceListener_addCallback:
283 * @listener: the #AccessibleDeviceListener instance to modify.
284 * @callback: an #AccessibleDeviceListenerCB function pointer.
285 * @user_data: a pointer to data which will be passed to the callback when invoked.
287 * Add an in-process callback function to an existing #AccessibleDeviceListener.
289 * Returns: #TRUE if successful, otherwise #FALSE.
293 AccessibleDeviceListener_addCallback (AccessibleDeviceListener *listener,
294 AccessibleDeviceListenerCB callback,
297 cspi_device_listener_add_cb (listener, callback, user_data);
302 * AccessibleDeviceListener_removeCallback:
303 * @listener: the #AccessibleDeviceListener instance to modify.
304 * @callback: an #AccessibleDeviceListenerCB function pointer.
306 * Remove an in-process callback function from an existing #AccessibleDeviceListener.
308 * Returns: #TRUE if successful, otherwise #FALSE.
312 AccessibleDeviceListener_removeCallback (AccessibleDeviceListener *listener,
313 AccessibleDeviceListenerCB callback)
315 cspi_device_listener_remove_cb (listener, callback);
320 * AccessibleDeviceListener_unref:
321 * @listener: a pointer to the #AccessibleDeviceListener being operated on.
323 * Decrements an #AccessibleDeviceListener's reference count.
326 AccessibleDeviceListener_unref (AccessibleDeviceListener *listener)
328 cspi_device_listener_unref (listener);
332 cspi_internal_event_get_text (const InternalEvent *e)
334 g_return_val_if_fail (e, NULL);
335 if (e->event.v_type == EVENT_DATA_STRING)
337 return g_strdup (e->event.v.text? e->event.v.text: "");
343 cspi_internal_event_get_object (const InternalEvent *e)
345 g_return_val_if_fail (e, NULL);
346 if (e->event.v_type == EVENT_DATA_OBJECT)
348 cspi_object_ref (e->event.v.accessible);
349 return e->event.v.accessible;
355 cspi_internal_event_get_rect (const InternalEvent *e)
357 g_return_val_if_fail (e, NULL);
358 if (e->event.v_type == EVENT_DATA_RECT)
360 SPIRect *rect = g_new (SPIRect, 1);
361 if (rect) memcpy (rect, &e->event.v.rect, sizeof(*rect));
368 * AccessibleEvent_getSourceName:
369 * @e: an #AccessibleEvent to be queried.
371 * Get the 'accessible-name' of the object emitting the event.
373 * Returns: The name of the event source, or NULL if the event source cannot be identified
374 * or does not report a name.
376 char* AccessibleEvent_getSourceName (const AccessibleEvent *e)
380 return Accessible_getName (e->source);
386 * AccessibleEvent_getSourceRole:
387 * @e: an #AccessibleEvent to be queried.
389 * Get the #AccessibleRole of the object emitting the event.
391 * Returns: #AccessibleRole of the event source, or SPI_ROLE_UNKNOWN
392 * if the event source's role is unknown or unspecified.
393 * (Some kinds of events, such as 'mouse:' events or
394 * toolkit events, don't have associated object roles.)
396 AccessibleRole AccessibleEvent_getSourceRole (const AccessibleEvent *e)
400 return Accessible_getRole (e->source);
402 return SPI_ROLE_UNKNOWN;
406 * AccessibleEvent_getSourceApplication:
407 * @e: an #AccessibleEvent to be queried.
409 * Get the #Application hosting the object which emitted the event.
411 * Returns: A pointer to the host #Application contining the event source
415 AccessibleApplication* AccessibleEvent_getSourceApplication (const AccessibleEvent *e)
418 InternalEvent *ie = (InternalEvent *)e;
419 CORBA_any *any = ((ie && ie->data) ? (CORBA_any *)ie->data : NULL);
421 CORBA_TypeCode_equivalent (any->_type,
422 TC_Accessibility_EventDetails, NULL))
424 Accessibility_EventDetails *details = (Accessibility_EventDetails *) any->_value;
425 return cspi_object_take (details->host_application);
433 * AccessibleEvent_getSourceDetails:
434 * @e: an #AccessibleEvent to be queried.
435 * @name: a pointer to a character string which will point to the name of the event source
436 * on successful completion of the call.
437 * @role: a pointer to an #AccessibleRole which will point to the role of the event source
438 * on successful completion of the call.
439 * @app: A pointer to an #AccessibleApplication which points to the host application for this event
440 * on successful completion of the call.
442 * Get the host #Application, "accessible name", and #AccessibleRole
443 * of the object which emitted the event.
445 * Returns: TRUE if the source details were successfully retrieved,
446 * FALSE if they were not, either due to error, incomplete data,
447 * or the fact that the event did not encapsulate the required data.
450 SPIBoolean AccessibleEvent_getSourceDetails (const AccessibleEvent *e,
451 char **name, AccessibleRole *role,
452 AccessibleApplication **app)
454 InternalEvent *ie = (InternalEvent *)e;
455 CORBA_any *any = ((ie && ie->data) ? (CORBA_any *)ie->data : NULL);
457 CORBA_TypeCode_equivalent (any->_type,
458 TC_Accessibility_EventDetails, NULL))
460 Accessibility_EventDetails *details = (Accessibility_EventDetails *) any->_value;
461 *name = CORBA_string_dup (details->source_name);
462 *role = cspi_role_from_spi_role (details->source_role);
463 *app = cspi_object_take (details->host_application);
469 *role = SPI_ROLE_UNKNOWN;
477 * AccessibleTextChangedEvent_getChangeString:
478 * @e: a pointer to the #AccessibleEvent being queried.
480 * Queries an #AccessibleEvent of type "object:text-changed",
481 * returning the text inserted or deleted.
483 * Returns: a UTF-8 text string indicating the text inserted,
484 * deleted, or substituted by this event.
487 AccessibleTextChangedEvent_getChangeString (const AccessibleEvent *e)
489 const InternalEvent *foo = (InternalEvent *) e;
490 /* TODO: check the event type. */
491 return cspi_internal_event_get_text (foo);
495 * AccessibleTextSelectionChangedEvent_getSelectionString:
496 * @e: a pointer to the #AccessibleEvent being queried.
498 * Queries an #AccessibleEvent of type "object:text-selection-changed",
499 * returning the newly added, removed, or modified selection string.
501 * Returns: a UTF-8 text string indicating the recently changed selection.
504 AccessibleTextSelectionChangedEvent_getSelectionString (const AccessibleEvent *e)
506 const InternalEvent *foo = (InternalEvent *) e;
507 /* TODO: check the event type. */
508 return cspi_internal_event_get_text (foo);
512 * AccessibleWindowEvent_getTitleString:
513 * @e: a pointer to the #AccessibleEvent being queried.
515 * Queries an #AccessibleEvent of type "window:",
516 * returning the window title.
518 * Returns: a UTF-8 text string representing the title of the
519 * recently changed window.
522 AccessibleWindowEvent_getTitleString (const AccessibleEvent *e)
524 const InternalEvent *foo = (InternalEvent *) e;
525 /* TODO: check the event type. */
526 return cspi_internal_event_get_text (foo);
530 * AccessibleChildChangedEvent_getChildAccessible:
531 * @e: a pointer to the #AccessibleEvent being queried.
533 * Queries an #AccessibleEvent of type "object:children_changed"
534 * to get a reference to the changed #Accessible.
535 * Note that context #Accessibles are not guaranteed to outlive
536 * event delivery, in which case this call may return %NULL
537 * even if the object existed at the time of dispatch.
539 * Returns: the context #Accessible for the event, or %NULL if
540 * there is no longer a valid context #Accessible
541 * object for the event.
544 AccessibleChildChangedEvent_getChildAccessible (const AccessibleEvent *e)
546 const InternalEvent *foo = (InternalEvent *) e;
547 return (Accessible *) cspi_internal_event_get_object (foo);
551 * AccessibleParentChangedEvent_getParentAccessible:
552 * @e: a pointer to the #AccessibleEvent being queried.
554 * Queries an #AccessibleEvent of type "object:property-change:accessible-parent"
555 * to get a reference to the changed #Accessible.
556 * Note that context #Accessibles are not guaranteed to outlive
557 * event delivery, in which case this call may return %NULL
558 * even if the object existed at the time of dispatch.
560 * Returns: an #Accessible pointer representing the new parent object.
563 AccessibleParentChangedEvent_getParentAccessible (const AccessibleEvent *e)
565 const InternalEvent *foo = (InternalEvent *) e;
566 return (Accessible *) cspi_internal_event_get_object (foo);
570 * AccessibleActiveDescendantChangedEvent_getActiveDescendant:
571 * @e: a pointer to the #AccessibleEvent being queried.
573 * Queries an #AccessibleEvent of type "object:active-descendant-changed"
574 * to get a reference to the changed #Accessible.
575 * Note that context #Accessibles are not guaranteed to outlive
576 * event delivery, in which case this call may return %NULL
577 * even if the object existed at the time of dispatch.
579 * Returns: an #Accessible pointer representing the new active descendant.
582 AccessibleActiveDescendantChangedEvent_getActiveDescendant (const AccessibleEvent *e)
584 const InternalEvent *foo = (InternalEvent *) e;
585 return (Accessible *) cspi_internal_event_get_object (foo);
589 * AccessibleTableSummaryChangedEvent_getSummaryAccessible:
590 * @e: a pointer to the #AccessibleEvent being queried.
592 * Queries an #AccessibleEvent of type "object:property-changed:accessible-table-summary"
593 * to get a reference to the changed #Accessible.
594 * Note that context #Accessibles are not guaranteed to outlive
595 * event delivery, in which case this call may return %NULL
596 * even if the object existed at the time of dispatch.
598 * Returns: an #Accessible pointer representing the new table summary.
601 AccessibleTableSummaryChangedEvent_getSummaryAccessible (const AccessibleEvent *e)
603 const InternalEvent *foo = (InternalEvent *) e;
604 return (Accessible *) cspi_internal_event_get_object (foo);
608 * AccessibleTableHeaderChangedEvent_getHeaderAccessible:
609 * @e: a pointer to the #AccessibleEvent being queried.
611 * Queries an #AccessibleEvent of type
612 * "object:property-changed:accessible-table-row-header" or
613 * "object:property-changed:accessible-table-column-header"
614 * to get a reference to the changed #Accessible.
615 * Note that context #Accessibles are not guaranteed to outlive
616 * event delivery, in which case this call may return %NULL
617 * even if the object existed at the time of dispatch.
619 * Returns: an #Accessible pointer representing the new table header.
622 AccessibleTableHeaderChangedEvent_getHeaderAccessible (const AccessibleEvent *e)
624 const InternalEvent *foo = (InternalEvent *) e;
625 return (Accessible *) cspi_internal_event_get_object (foo);
630 * AccessibleTableCaptionChangedEvent_getCaptionString:
631 * @e: a pointer to the #AccessibleEvent being queried.
633 * Queries an #AccessibleEvent of type
634 * "object:property-changed:accessible-table-caption-object"
635 * returning the text in the caption, if present.
637 * Returns: a UTF-8 text string indicating the text in the caption.
640 AccessibleTableCaptionChangedEvent_getCaptionString (const AccessibleEvent *e)
642 const InternalEvent *foo = (InternalEvent *) e;
643 /* TODO: check the event type. */
644 return cspi_internal_event_get_text (foo);
648 * AccessibleTableRowDescriptionChangedEvent_getDescriptionString:
649 * @e: a pointer to the #AccessibleEvent being queried.
651 * Queries an #AccessibleEvent of type
652 * "object:property-changed:accessible-table-row-description"
653 * returning the new table row description.
655 * Returns: a UTF-8 text string representing the recently changed
656 * table row description
659 AccessibleTableRowDescriptionChangedEvent_getDescriptionString (const AccessibleEvent *e)
661 const InternalEvent *foo = (InternalEvent *) e;
662 /* TODO: check the event type. */
663 return cspi_internal_event_get_text (foo);
667 * AccessibleTableColumnDescriptionChangedEvent_getDescriptionString:
668 * @e: a pointer to the #AccessibleEvent being queried.
670 * Queries an #AccessibleEvent of type
671 * "object:property-changed:accessible-table-column-description"
672 * returning the new table column description.
674 * Returns: a UTF-8 text string representing the recently changed
675 * table column description
678 AccessibleTableColumnDescriptionChangedEvent_getDescriptionString (const AccessibleEvent *e)
680 const InternalEvent *foo = (InternalEvent *) e;
681 /* TODO: check the event type. */
682 return cspi_internal_event_get_text (foo);
686 * AccessibleDescriptionChangedEvent_getDescriptionString:
687 * @e: a pointer to the #AccessibleEvent being queried.
689 * Queries an #AccessibleEvent of type
690 * "object:property-changed:accessible-description"
691 * returning the new description.
693 * Returns: a UTF-8 text string representing the recently changed
697 AccessibleDescriptionChangedEvent_getDescriptionString (const AccessibleEvent *e)
699 const InternalEvent *foo = (InternalEvent *) e;
700 /* TODO: check the event type. */
701 return cspi_internal_event_get_text (foo);
705 * AccessibleBoundsChangedEvent_getNewBounds:
706 * @e: a pointer to the #AccessibleEvent being queried.
708 * Queries an #AccessibleEvent of type "object:bounds-changed",
709 * returning a pointer to an SPIRect structure containing the
710 * new bounds, or NULL on error.
711 * The returned structure should be freed with SPI_freeRect when
712 * the caller has finished referencing it.
716 * Returns: a pointer to an SPIRect defining the new object bounds.
719 AccessibleBoundsChangedEvent_getNewBounds (const AccessibleEvent *e)
721 const InternalEvent *foo = (InternalEvent *) e;
722 /* TODO: check the event type. */
723 return cspi_internal_event_get_rect (foo);
727 cspi_event_compare (gconstpointer p1, gconstpointer p2)
729 const InternalEvent *e1 = p1, *e2 = p2;
730 return (gint) ((long) e2->id - (long) e1->id);
733 static InternalEvent *
734 cspi_internal_event_lookup (const InternalEvent *e)
736 InternalEvent *internal = NULL;
738 g_slist_find_custom (_cspi_event_queue, e, cspi_event_compare);
744 static const InternalEvent *
745 cspi_internal_event_check (const AccessibleEvent *e)
747 InternalEvent *internal = (InternalEvent *) e;
748 if (internal->magic == SPI_INTERNAL_EVENT_MAGIC)
754 static InternalEvent *
755 cspi_internal_event_add (const InternalEvent *e)
757 _cspi_event_queue = g_slist_prepend (_cspi_event_queue, (gpointer) e);
758 return (InternalEvent *) e;
762 cspi_internal_event_remove (const InternalEvent *e)
764 GSList *link = g_slist_find_custom (_cspi_event_queue, e, cspi_event_compare);
766 _cspi_event_queue = g_slist_remove_link (_cspi_event_queue, link);
770 * AccessibleNameChangedEvent_getNameString:
771 * @e: a pointer to the #AccessibleEvent being queried.
773 * Queries an #AccessibleEvent of type "object:property-change:accessible_name:",
774 * returning the name.
776 * Returns: a UTF-8 text string representing the name of the
777 * object which recently changed.
780 AccessibleNameChangedEvent_getNameString (const AccessibleEvent *e)
782 const InternalEvent *foo = (InternalEvent *) e;
783 return cspi_internal_event_get_text (foo);
787 * AccessibleEvent_ref:
788 * @e: a pointer to the #AccessibleEvent being referenced.
790 * Increments by 1 the reference count of the event
792 * Returns: TRUE if the function succeeded; FALSE if the pointer is not a
796 AccessibleEvent_ref (const AccessibleEvent *e)
798 const InternalEvent *private = cspi_internal_event_check (e);
801 InternalEvent *event = cspi_internal_event_lookup (private);
803 * put event in the cache if it's not there already,
804 * and increment refcount
808 event = cspi_internal_event_add (private);
818 * AccessibleEvent_unref:
819 * @e: a pointer to the #AccessibleEvent being referenced.
821 * Decrements by 1 the reference count of the event. The event is destroyed
822 * when the reference count recahes zero.
826 AccessibleEvent_unref (const AccessibleEvent *e)
828 const InternalEvent *private = cspi_internal_event_check (e);
829 /* decrement refcount and remove if appropriate */
832 InternalEvent *event = cspi_internal_event_lookup (private);
836 if (event->ref_count < 1)
838 cspi_internal_event_remove (event);
839 g_free ((gpointer)e->type);
840 Accessible_unref (e->source);
841 if (event->event.v_type == EVENT_DATA_OBJECT)
843 Accessible_unref (event->event.v.accessible);
845 g_free ((gpointer)e);
853 CSpiEventListener *listener;
857 } CSpiEventListenerEntry;
859 static GList *event_listeners = NULL;
862 demarshal_rect (DBusMessageIter *iter, SPIRect *rect)
864 dbus_int32_t x, y, width, height;
865 DBusMessageIter iter_struct;
867 dbus_message_iter_recurse (iter, &iter_struct);
868 if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
869 dbus_message_iter_get_basic (&iter_struct, &x);
870 dbus_message_iter_next (&iter_struct);
871 if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
872 dbus_message_iter_get_basic (&iter_struct, &y);
873 dbus_message_iter_next (&iter_struct);
874 if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
875 dbus_message_iter_get_basic (&iter_struct, &width);
876 dbus_message_iter_next (&iter_struct);
877 if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
878 dbus_message_iter_get_basic (&iter_struct, &height);
882 rect->height = height;
887 parse_eventType (const char *eventType, char **categoryp, char **namep, char **detailp, char **matchrule)
889 char *tmp = g_strdup (eventType);
890 char *category = NULL, *name = NULL, *detail = NULL;
891 char *saveptr = NULL;
894 if (tmp == NULL) return FALSE;
895 while ((p = strchr (tmp, '-'))) *p = '_';
896 category = strtok_r (tmp, ":", &saveptr);
897 if (category) category = g_strdup (category);
898 if (!category) goto oom;
899 name = strtok_r (NULL, ":", &saveptr);
902 name = g_strdup (name);
904 detail = strtok_r (NULL, ":", &saveptr);
905 if (detail) detail = g_strdup (detail);
909 name = g_strdup (category);
914 *matchrule = g_strdup_printf ("type='signal',interface='org.freedesktop.atspi.event.%c%s',member='%s'", toupper(category[0]), category + 1, name);
915 if (!*matchrule) goto oom;
917 if (categoryp) *categoryp = category;
918 else g_free (category);
919 if (namep) *namep = name;
920 else if (name) g_free (name);
921 if (detailp) *detailp = detail;
922 else if (detail) g_free (detail);
926 if (tmp) g_free (tmp);
927 if (category) g_free (category);
928 if (name) g_free (name);
929 if (detail) g_free (detail);
933 static void listener_data_free (CSpiEventListenerEntry *e)
935 g_free (e->category);
937 if (e->detail) g_free (e->detail);
942 * SPI_registerGlobalEventListener:
943 * @listener: the #AccessibleEventListener to be registered against an
945 * @eventType: a character string indicating the type of events for which
946 * notification is requested. Format is
947 * EventClass:major_type:minor_type:detail
948 * where all subfields other than EventClass are optional.
949 * EventClasses include "object", "window", "mouse",
950 * and toolkit events (e.g. "Gtk", "AWT").
951 * Examples: "focus:", "Gtk:GtkWidget:button_press_event".
953 * Legal object event types:
955 * (property change events)
957 * object:property-change
958 * object:property-change:accessible-name
959 * object:property-change:accessible-description
960 * object:property-change:accessible-parent
961 * object:property-change:accessible-value
962 * object:property-change:accessible-role
963 * object:property-change:accessible-table-caption
964 * object:property-change:accessible-table-column-description
965 * object:property-change:accessible-table-column-header
966 * object:property-change:accessible-table-row-description
967 * object:property-change:accessible-table-row-header
968 * object:property-change:accessible-table-summary
970 * (other object events)
972 * object:state-changed
973 * object:children-changed
974 * object:visible-data-changed
975 * object:selection-changed
976 * object:text-selection-changed
977 * object:text-changed
978 * object:text-caret-moved
979 * object:row-inserted
980 * object:row-reordered
982 * object:column-inserted
983 * object:column-reordered
984 * object:column-deleted
985 * object:model-changed
986 * object:active-descendant-changed
996 * window:desktop-create
997 * window:desktop-destroy
1020 * NOTE: this string may be UTF-8, but should not contain byte value 56
1021 * (ascii ':'), except as a delimiter, since non-UTF-8 string
1022 * delimiting functions are used internally.
1023 * In general, listening to
1024 * toolkit-specific events is not recommended.
1026 * Add an in-process callback function to an existing AccessibleEventListener.
1028 * Returns: #TRUE if successful, otherwise #FALSE.
1031 SPI_registerGlobalEventListener (AccessibleEventListener *listener,
1032 const char *eventType)
1034 CSpiEventListenerEntry *e;
1044 e = g_new (CSpiEventListenerEntry, 1);
1045 if (!e) return FALSE;
1046 e->listener = listener;
1047 if (!parse_eventType (eventType, &e->category, &e->name, &e->detail, &matchrule))
1052 new_list = g_list_prepend (event_listeners, e);
1055 listener_data_free (e);
1058 event_listeners = new_list;
1059 dbus_error_init (&error);
1060 dbus_bus_add_match (SPI_bus(), matchrule, &error);
1063 g_warning ("Adding match: %s", error.message);
1069 * SPI_deregisterGlobalEventListenerAll:
1070 * @listener: the #AccessibleEventListener to be registered against
1073 * deregisters an AccessibleEventListener from the registry, for all
1074 * event types it may be listening to. Use
1075 * AccessibleEventListener_unref to release the
1076 * listener reference.
1078 * Returns: #TRUE if successful, otherwise #FALSE.
1081 SPI_deregisterGlobalEventListenerAll (AccessibleEventListener *listener)
1090 for (l = event_listeners; l;)
1092 CSpiEventListenerEntry *e = l->data;
1093 if (e->listener == listener)
1095 listener_data_free (e);
1096 l = g_list_remove (l, e);
1098 else l = g_list_next (l);
1104 * SPI_deregisterGlobalEventListener:
1105 * @listener: the #AccessibleEventListener registered against an event type.
1106 * @eventType: a string specifying the event type for which this
1107 * listener is to be deregistered.
1109 * deregisters an AccessibleEventListener from the registry, for a specific
1112 * Returns: #TRUE if successful, otherwise #FALSE.
1115 SPI_deregisterGlobalEventListener (AccessibleEventListener *listener,
1116 const char *eventType)
1118 char *category, *name, *detail, *matchrule;
1121 if (!parse_eventType (eventType, &category, &name, &detail, &matchrule))
1130 for (l = event_listeners; l;)
1132 CSpiEventListenerEntry *e = l->data;
1133 if (e->listener == listener && !strcmp (e->category, category) && !strcmp (e->name, name) && (e->detail == detail || !strcmp (e->detail, detail)))
1136 listener_data_free (e);
1137 l = g_list_remove (l, e);
1138 dbus_error_init (&error);
1139 dbus_bus_remove_match (SPI_bus(), matchrule, &error);
1141 else l = g_list_next (l);
1145 if (detail) g_free (detail);
1151 cspi_dispatch_event (AccessibleEvent *e)
1153 char *category, *name, *detail;
1156 if (!parse_eventType (e->type, &category, &name, &detail, NULL))
1158 g_warning ("Couldn't parse event: %s\n", e->type);
1161 for (l = event_listeners; l; l = g_list_next (l))
1163 CSpiEventListenerEntry *entry = l->data;
1164 if (!strcmp (category, entry->category) &&
1165 (entry->name == NULL || !strcmp (name, entry->name)) &&
1166 (entry->detail == NULL || !strcmp (detail, entry->detail)))
1168 CSpiEventListenerClass *klass = CSPI_EVENT_LISTENER_GET_CLASS (entry->listener);
1169 if (klass->event) (*klass->event)(entry->listener, e);
1172 if (detail) g_free (detail);
1178 cspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
1180 char *detail = NULL;
1181 const char *category = dbus_message_get_interface (message);
1182 const char *name = dbus_message_get_member (message);
1183 DBusMessageIter iter, iter_variant;
1184 dbus_message_iter_init (message, &iter);
1186 dbus_int32_t detail1, detail2;
1191 category = strrchr (category, '.');
1192 if (category == NULL)
1195 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1199 g_return_if_fail (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING);
1200 dbus_message_iter_get_basic (&iter, &detail);
1201 dbus_message_iter_next (&iter);
1202 g_return_if_fail (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_INT32);
1203 dbus_message_iter_get_basic (&iter, &detail1);
1204 e.detail1 = detail1;
1205 dbus_message_iter_next (&iter);
1206 g_return_if_fail (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_INT32);
1207 dbus_message_iter_get_basic (&iter, &detail2);
1208 e.detail2 = detail2;
1209 dbus_message_iter_next (&iter);
1210 e.type = g_strdup_printf ("%c%s:", tolower (category[0]), category + 1);
1211 if (strcasecmp (category, name) != 0)
1213 p = g_strconcat (e.type, ":", name, NULL);
1220 if (detail[0] != '\0')
1222 p = g_strconcat (e.type, ":", detail, NULL);
1229 while ((p = strchr (e.type, '_'))) *p = '-';
1230 e.source = cspi_ref_accessible (dbus_message_get_sender(message), dbus_message_get_path(message));
1231 dbus_message_iter_recurse (&iter, &iter_variant);
1232 switch (dbus_message_iter_get_arg_type (&iter_variant))
1234 case DBUS_TYPE_OBJECT_PATH:
1236 dbus_message_iter_get_basic (&iter_variant, &p);
1237 e.v_type = EVENT_DATA_OBJECT;
1238 e.v.accessible = cspi_ref_accessible (dbus_message_get_sender(message), p);
1241 case DBUS_TYPE_STRING:
1243 dbus_message_iter_get_basic (&iter_variant, &p);
1244 e.v_type = EVENT_DATA_STRING;
1245 e.v.text = g_strdup (p);
1248 case DBUS_TYPE_STRUCT:
1250 if (demarshal_rect (&iter_variant, &e.v.rect))
1252 e.v_type = EVENT_DATA_RECT;
1259 cspi_dispatch_event (&e);
1260 return DBUS_HANDLER_RESULT_HANDLED;