X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=cspi%2Fspi_event.c;h=d20161a5ff06643f023e8bd3771c784a7d3eaea7;hb=dea123f383eb81663ea33463363f5c27f4ac0bcd;hp=808b4dd50e2bdabdcf246818dc1ed786a0361b3a;hpb=a0661d5d4128769f2544d24430c80dc440bfdbba;p=platform%2Fcore%2Fuifw%2Fat-spi2-atk.git diff --git a/cspi/spi_event.c b/cspi/spi_event.c index 808b4dd..d20161a 100644 --- a/cspi/spi_event.c +++ b/cspi/spi_event.c @@ -2,7 +2,8 @@ * AT-SPI - Assistive Technology Service Provider Interface * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) * - * Copyright 2001 Sun Microsystems Inc. + * Copyright 2001, 2002, 2003 Sun Microsystems Inc., + * Copyright 2001, 2002, 2003 Ximian, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,11 +22,95 @@ */ #include -#include + +static GSList *_cspi_event_queue = NULL; + +/** + * SPI_freeAccessibleKeySet: + * @keyset: An AccessibleKeyset to free. + * + * Release the memory used by an AccessibleKeySet. + * + **/ +void +SPI_freeAccessibleKeySet (AccessibleKeySet *keyset) +{ + int i = 0; + g_free (keyset->keysyms); + g_free (keyset->keycodes); + while (keyset->keystrings [i]) + { + g_free (keyset->keystrings [i++]); + } + g_free (keyset->keystrings); + g_free (keyset); +} + +/** + * SPI_createAccessibleKeySet: + * @len: the number of key values in the key set. + * @keysyms: a UTF-8 string containing symbolic key values to be matched, or NULL if + * matching is performed against other key values instead. + * @keycodes: an array of unsigned short values which are the hardware keycodes + * to be matched, or NULL if the keyset is specified solely by keysyms + * and/or keystrings. + * @keystrings: an array of null-terminated character strings which specify key + * name values to match, or NULL if the keyset is specified solely by + * keycodes and/or keysyms. + * + * Create a new #AccessibleKeySet of a specified length. + * A KeySet is used typically to match key event values, and a matches are made + * using the following criteria: a match exists with a key event if all non-null + * i-th members of the keyset match the key event. + * If both keystring and keysym values are NULL, a keycode value match is + * forced, thus the match for keysym=0, keycode=0, keystring=NULL is + * keycode 0. + * + * Returns: a pointer to a newly-created #AccessibleKeySet. + * + **/ +AccessibleKeySet * +SPI_createAccessibleKeySet (int len, const char *keysyms, short *keycodes, + const char **keystrings) +{ + AccessibleKeySet *keyset = g_new0 (AccessibleKeySet, 1); + int i, keysym_len = 0; + const char *keysym_ptr = keysyms; + keyset->len = len; + keyset->keysyms = g_new0 (unsigned long, len); + keyset->keycodes = g_new0 (unsigned short, len); + keyset->keystrings = g_new0 (char *, len); + if (keysyms) + { + keysym_len = g_utf8_strlen (keysyms, -1); + } + for (i = 0; i < len; ++i) + { + if (i < keysym_len) + { + keyset->keysyms [i] = (unsigned long) g_utf8_get_char (keysym_ptr); + keysym_ptr = g_utf8_find_next_char (keysym_ptr, NULL); + } + else + { + keyset->keysyms [i] = 0; + } + if (keycodes) + { + keyset->keycodes [i] = keycodes [i]; + } + if (keystrings) + { + keyset->keystrings [i] = g_strdup (keystrings [i]); + } + } + return keyset; +} /** - * createAccessibleEventListener: + * SPI_createAccessibleEventListener: * @callback : an #AccessibleEventListenerCB callback function, or NULL. + * @user_data: a pointer to data which will be passed to the callback when invoked. * * Create a new #AccessibleEventListener with a specified (in-process) callback function. * @@ -33,12 +118,13 @@ * **/ AccessibleEventListener * -createAccessibleEventListener (AccessibleEventListenerCB callback) +SPI_createAccessibleEventListener (AccessibleEventListenerCB callback, + void *user_data) { AccessibleEventListener *listener = cspi_event_listener_new (); if (callback) { - AccessibleEventListener_addCallback (listener, callback); + AccessibleEventListener_addCallback (listener, callback, user_data); } return listener; } @@ -47,6 +133,7 @@ createAccessibleEventListener (AccessibleEventListenerCB callback) * AccessibleEventListener_addCallback: * @listener: the #AccessibleEventListener instance to modify. * @callback: an #AccessibleEventListenerCB function pointer. + * @user_data: a pointer to data which will be passed to the callback when invoked. * * Add an in-process callback function to an existing AccessibleEventListener. * Note that the callback function must live in the same address @@ -58,15 +145,28 @@ createAccessibleEventListener (AccessibleEventListenerCB callback) * Returns: #TRUE if successful, otherwise #FALSE. * **/ -boolean +SPIBoolean AccessibleEventListener_addCallback (AccessibleEventListener *listener, - AccessibleEventListenerCB callback) + AccessibleEventListenerCB callback, + void *user_data) { - cspi_event_listener_add_callback (listener, callback); + cspi_event_listener_add_cb (listener, callback, user_data); return TRUE; } /** + * AccessibleEventListener_unref: + * @listener: a pointer to the #AccessibleEventListener being operated on. + * + * Decrements an #AccessibleEventListener's reference count. + **/ +void +AccessibleEventListener_unref (AccessibleEventListener *listener) +{ + cspi_event_listener_unref (listener); +} + +/** * AccessibleEventListener_removeCallback: * @listener: the #AccessibleEventListener instance to modify. * @callback: an #AccessibleEventListenerCB function pointer. @@ -76,17 +176,18 @@ AccessibleEventListener_addCallback (AccessibleEventListener *listener, * Returns: #TRUE if successful, otherwise #FALSE. * **/ -boolean +SPIBoolean AccessibleEventListener_removeCallback (AccessibleEventListener *listener, AccessibleEventListenerCB callback) { - cspi_event_listener_remove_callback (listener, callback); + cspi_event_listener_remove_cb (listener, callback); return TRUE; } /** - * createAccessibleKeystrokeListener: + * SPI_createAccessibleKeystrokeListener: * @callback : an #AccessibleKeystrokeListenerCB callback function, or NULL. + * @user_data: a pointer to data which will be passed to the callback when invoked. * * Create a new #AccessibleKeystrokeListener with a specified callback function. * @@ -94,31 +195,34 @@ AccessibleEventListener_removeCallback (AccessibleEventListener *listener, * **/ AccessibleKeystrokeListener * -createAccessibleKeystrokeListener (AccessibleKeystrokeListenerCB callback) +SPI_createAccessibleKeystrokeListener (AccessibleKeystrokeListenerCB callback, + void *user_data) { - CSpiKeystrokeListener *listener = cspi_keystroke_listener_new (); + AccessibleDeviceListener *listener = cspi_device_listener_new (); if (callback) { - AccessibleKeystrokeListener_addCallback (listener, callback); + AccessibleDeviceListener_addCallback (listener, callback, user_data); } - return (AccessibleKeystrokeListener *)listener; + return listener; } /** * AccessibleKeystrokeListener_addCallback: * @listener: the #AccessibleKeystrokeListener instance to modify. * @callback: an #AccessibleKeystrokeListenerCB function pointer. + * @user_data: a pointer to data which will be passed to the callback when invoked. * * Add an in-process callback function to an existing #AccessibleKeystrokeListener. * * Returns: #TRUE if successful, otherwise #FALSE. * **/ -boolean +SPIBoolean AccessibleKeystrokeListener_addCallback (AccessibleKeystrokeListener *listener, - AccessibleKeystrokeListenerCB callback) + AccessibleKeystrokeListenerCB callback, + void *user_data) { - cspi_keystroke_listener_add_callback (listener, callback); + cspi_device_listener_add_cb (listener, callback, user_data); return TRUE; } @@ -132,23 +236,1011 @@ AccessibleKeystrokeListener_addCallback (AccessibleKeystrokeListener *listener, * Returns: #TRUE if successful, otherwise #FALSE. * **/ -boolean +SPIBoolean AccessibleKeystrokeListener_removeCallback (AccessibleKeystrokeListener *listener, AccessibleKeystrokeListenerCB callback) { - cspi_keystroke_listener_remove_callback (listener, callback); + cspi_device_listener_remove_cb (listener, callback); return TRUE; } - /** * AccessibleKeystrokeListener_unref: * @listener: a pointer to the #AccessibleKeystrokeListener being operated on. * * Decrements an #AccessibleKeystrokeListener's reference count. **/ -void AccessibleKeystrokeListener_unref (AccessibleKeystrokeListener *listener) +void +AccessibleKeystrokeListener_unref (AccessibleKeystrokeListener *listener) +{ + cspi_device_listener_unref (listener); +} + +/** + * SPI_createAccessibleDeviceListener: + * @callback : an #AccessibleDeviceListenerCB callback function, or NULL. + * @user_data: a pointer to data which will be passed to the callback when invoked. + * + * Create a new #AccessibleDeviceListener with a specified callback function. + * + * Returns: a pointer to a newly-created #AccessibleDeviceListener. + * + **/ +AccessibleDeviceListener * +SPI_createAccessibleDeviceListener (AccessibleDeviceListenerCB callback, + void *user_data) +{ + AccessibleDeviceListener *listener = cspi_device_listener_new (); + if (callback) + { + AccessibleDeviceListener_addCallback (listener, callback, user_data); + } + return listener; +} + +/** + * AccessibleDeviceListener_addCallback: + * @listener: the #AccessibleDeviceListener instance to modify. + * @callback: an #AccessibleDeviceListenerCB function pointer. + * @user_data: a pointer to data which will be passed to the callback when invoked. + * + * Add an in-process callback function to an existing #AccessibleDeviceListener. + * + * Returns: #TRUE if successful, otherwise #FALSE. + * + **/ +SPIBoolean +AccessibleDeviceListener_addCallback (AccessibleDeviceListener *listener, + AccessibleDeviceListenerCB callback, + void *user_data) +{ + cspi_device_listener_add_cb (listener, callback, user_data); + return TRUE; +} + +/** + * AccessibleDeviceListener_removeCallback: + * @listener: the #AccessibleDeviceListener instance to modify. + * @callback: an #AccessibleDeviceListenerCB function pointer. + * + * Remove an in-process callback function from an existing #AccessibleDeviceListener. + * + * Returns: #TRUE if successful, otherwise #FALSE. + * + **/ +SPIBoolean +AccessibleDeviceListener_removeCallback (AccessibleDeviceListener *listener, + AccessibleDeviceListenerCB callback) { - /* Would prefer this not to be bonobo api */ - bonobo_object_unref (BONOBO_OBJECT (listener)); + cspi_device_listener_remove_cb (listener, callback); + return TRUE; +} + +/** + * AccessibleDeviceListener_unref: + * @listener: a pointer to the #AccessibleDeviceListener being operated on. + * + * Decrements an #AccessibleDeviceListener's reference count. + **/ +void +AccessibleDeviceListener_unref (AccessibleDeviceListener *listener) +{ + cspi_device_listener_unref (listener); +} + +static char * +cspi_internal_event_get_text (const InternalEvent *e) +{ + g_return_val_if_fail (e, NULL); + if (e->event.v_type == EVENT_DATA_STRING) + { + return g_strdup (e->event.v.text? e->event.v.text: ""); + } + return NULL; +} + +static Accessible * +cspi_internal_event_get_object (const InternalEvent *e) +{ + g_return_val_if_fail (e, NULL); + if (e->event.v_type == EVENT_DATA_OBJECT) + { + cspi_object_ref (e->event.v.accessible); + return e->event.v.accessible; + } + return NULL; +} + +static SPIRect * +cspi_internal_event_get_rect (const InternalEvent *e) +{ + g_return_val_if_fail (e, NULL); + if (e->event.v_type == EVENT_DATA_RECT) + { + SPIRect *rect = g_new (SPIRect, 1); + if (rect) memcpy (rect, &e->event.v.rect, sizeof(*rect)); + return rect; + } + return NULL; +} + +/** + * AccessibleEvent_getSourceName: + * @e: an #AccessibleEvent to be queried. + * + * Get the 'accessible-name' of the object emitting the event. + * + * Returns: The name of the event source, or NULL if the event source cannot be identified + * or does not report a name. + */ +char* AccessibleEvent_getSourceName (const AccessibleEvent *e) +{ + if (e && e->source) + { + return Accessible_getName (e->source); + } + return NULL; +} + +/** + * AccessibleEvent_getSourceRole: + * @e: an #AccessibleEvent to be queried. + * + * Get the #AccessibleRole of the object emitting the event. + * + * Returns: #AccessibleRole of the event source, or SPI_ROLE_UNKNOWN + * if the event source's role is unknown or unspecified. + * (Some kinds of events, such as 'mouse:' events or + * toolkit events, don't have associated object roles.) + */ +AccessibleRole AccessibleEvent_getSourceRole (const AccessibleEvent *e) +{ + if (e && e->source) + { + return Accessible_getRole (e->source); + } + return SPI_ROLE_UNKNOWN; +} + +/** + * AccessibleEvent_getSourceApplication: + * @e: an #AccessibleEvent to be queried. + * + * Get the #Application hosting the object which emitted the event. + * + * Returns: A pointer to the host #Application contining the event source + * component. + */ +#if 0 +AccessibleApplication* AccessibleEvent_getSourceApplication (const AccessibleEvent *e) +{ +xyzzy + InternalEvent *ie = (InternalEvent *)e; + CORBA_any *any = ((ie && ie->data) ? (CORBA_any *)ie->data : NULL); + if (any && + CORBA_TypeCode_equivalent (any->_type, + TC_Accessibility_EventDetails, NULL)) + { + Accessibility_EventDetails *details = (Accessibility_EventDetails *) any->_value; + return cspi_object_take (details->host_application); + } + else + return NULL; +} +#endif + +/** + * AccessibleEvent_getSourceDetails: + * @e: an #AccessibleEvent to be queried. + * @name: a pointer to a character string which will point to the name of the event source + * on successful completion of the call. + * @role: a pointer to an #AccessibleRole which will point to the role of the event source + * on successful completion of the call. + * @app: A pointer to an #AccessibleApplication which points to the host application for this event + * on successful completion of the call. + * + * Get the host #Application, "accessible name", and #AccessibleRole + * of the object which emitted the event. + * + * Returns: TRUE if the source details were successfully retrieved, + * FALSE if they were not, either due to error, incomplete data, + * or the fact that the event did not encapsulate the required data. + */ +#if 0 +SPIBoolean AccessibleEvent_getSourceDetails (const AccessibleEvent *e, + char **name, AccessibleRole *role, + AccessibleApplication **app) +{ + InternalEvent *ie = (InternalEvent *)e; + CORBA_any *any = ((ie && ie->data) ? (CORBA_any *)ie->data : NULL); + if (any && + CORBA_TypeCode_equivalent (any->_type, + TC_Accessibility_EventDetails, NULL)) + { + Accessibility_EventDetails *details = (Accessibility_EventDetails *) any->_value; + *name = CORBA_string_dup (details->source_name); + *role = cspi_role_from_spi_role (details->source_role); + *app = cspi_object_take (details->host_application); + return TRUE; + } + else + { + *name = NULL; + *role = SPI_ROLE_UNKNOWN; + *app = NULL; + return FALSE; + } +} +#endif + +/** + * AccessibleTextChangedEvent_getChangeString: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type "object:text-changed", + * returning the text inserted or deleted. + * + * Returns: a UTF-8 text string indicating the text inserted, + * deleted, or substituted by this event. + **/ +char * +AccessibleTextChangedEvent_getChangeString (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + /* TODO: check the event type. */ + return cspi_internal_event_get_text (foo); +} + +/** + * AccessibleTextSelectionChangedEvent_getSelectionString: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type "object:text-selection-changed", + * returning the newly added, removed, or modified selection string. + * + * Returns: a UTF-8 text string indicating the recently changed selection. + **/ +char * +AccessibleTextSelectionChangedEvent_getSelectionString (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + /* TODO: check the event type. */ + return cspi_internal_event_get_text (foo); +} + +/** + * AccessibleWindowEvent_getTitleString: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type "window:", + * returning the window title. + * + * Returns: a UTF-8 text string representing the title of the + * recently changed window. + **/ +char * +AccessibleWindowEvent_getTitleString (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + /* TODO: check the event type. */ + return cspi_internal_event_get_text (foo); +} + +/** + * AccessibleChildChangedEvent_getChildAccessible: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type "object:children_changed" + * to get a reference to the changed #Accessible. + * Note that context #Accessibles are not guaranteed to outlive + * event delivery, in which case this call may return %NULL + * even if the object existed at the time of dispatch. + * + * Returns: the context #Accessible for the event, or %NULL if + * there is no longer a valid context #Accessible + * object for the event. + **/ +Accessible * +AccessibleChildChangedEvent_getChildAccessible (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + return (Accessible *) cspi_internal_event_get_object (foo); +} + +/** + * AccessibleParentChangedEvent_getParentAccessible: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type "object:property-change:accessible-parent" + * to get a reference to the changed #Accessible. + * Note that context #Accessibles are not guaranteed to outlive + * event delivery, in which case this call may return %NULL + * even if the object existed at the time of dispatch. + * + * Returns: an #Accessible pointer representing the new parent object. + **/ +Accessible * +AccessibleParentChangedEvent_getParentAccessible (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + return (Accessible *) cspi_internal_event_get_object (foo); +} + +/** + * AccessibleActiveDescendantChangedEvent_getActiveDescendant: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type "object:active-descendant-changed" + * to get a reference to the changed #Accessible. + * Note that context #Accessibles are not guaranteed to outlive + * event delivery, in which case this call may return %NULL + * even if the object existed at the time of dispatch. + * + * Returns: an #Accessible pointer representing the new active descendant. + **/ +Accessible * +AccessibleActiveDescendantChangedEvent_getActiveDescendant (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + return (Accessible *) cspi_internal_event_get_object (foo); +} + +/** + * AccessibleTableSummaryChangedEvent_getSummaryAccessible: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type "object:property-changed:accessible-table-summary" + * to get a reference to the changed #Accessible. + * Note that context #Accessibles are not guaranteed to outlive + * event delivery, in which case this call may return %NULL + * even if the object existed at the time of dispatch. + * + * Returns: an #Accessible pointer representing the new table summary. + **/ +Accessible * +AccessibleTableSummaryChangedEvent_getSummaryAccessible (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + return (Accessible *) cspi_internal_event_get_object (foo); +} + +/** + * AccessibleTableHeaderChangedEvent_getHeaderAccessible: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type + * "object:property-changed:accessible-table-row-header" or + * "object:property-changed:accessible-table-column-header" + * to get a reference to the changed #Accessible. + * Note that context #Accessibles are not guaranteed to outlive + * event delivery, in which case this call may return %NULL + * even if the object existed at the time of dispatch. + * + * Returns: an #Accessible pointer representing the new table header. + **/ +Accessible * +AccessibleTableHeaderChangedEvent_getHeaderAccessible (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + return (Accessible *) cspi_internal_event_get_object (foo); +} + + +/** + * AccessibleTableCaptionChangedEvent_getCaptionString: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type + * "object:property-changed:accessible-table-caption-object" + * returning the text in the caption, if present. + * + * Returns: a UTF-8 text string indicating the text in the caption. + **/ +char * +AccessibleTableCaptionChangedEvent_getCaptionString (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + /* TODO: check the event type. */ + return cspi_internal_event_get_text (foo); +} + +/** + * AccessibleTableRowDescriptionChangedEvent_getDescriptionString: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type + * "object:property-changed:accessible-table-row-description" + * returning the new table row description. + * + * Returns: a UTF-8 text string representing the recently changed + * table row description + **/ +char * +AccessibleTableRowDescriptionChangedEvent_getDescriptionString (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + /* TODO: check the event type. */ + return cspi_internal_event_get_text (foo); +} + +/** + * AccessibleTableColumnDescriptionChangedEvent_getDescriptionString: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type + * "object:property-changed:accessible-table-column-description" + * returning the new table column description. + * + * Returns: a UTF-8 text string representing the recently changed + * table column description + **/ +char * +AccessibleTableColumnDescriptionChangedEvent_getDescriptionString (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + /* TODO: check the event type. */ + return cspi_internal_event_get_text (foo); +} + +/** + * AccessibleDescriptionChangedEvent_getDescriptionString: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type + * "object:property-changed:accessible-description" + * returning the new description. + * + * Returns: a UTF-8 text string representing the recently changed + * description + **/ +char * +AccessibleDescriptionChangedEvent_getDescriptionString (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + /* TODO: check the event type. */ + return cspi_internal_event_get_text (foo); +} + +/** + * AccessibleBoundsChangedEvent_getNewBounds: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type "object:bounds-changed", + * returning a pointer to an SPIRect structure containing the + * new bounds, or NULL on error. + * The returned structure should be freed with SPI_freeRect when + * the caller has finished referencing it. + * + * @Since: AT-SPI 1.6 + * + * Returns: a pointer to an SPIRect defining the new object bounds. + **/ +SPIRect * +AccessibleBoundsChangedEvent_getNewBounds (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + /* TODO: check the event type. */ + return cspi_internal_event_get_rect (foo); +} + +static gint +cspi_event_compare (gconstpointer p1, gconstpointer p2) +{ + const InternalEvent *e1 = p1, *e2 = p2; + return (gint) ((long) e2->id - (long) e1->id); +} + +static InternalEvent * +cspi_internal_event_lookup (const InternalEvent *e) +{ + InternalEvent *internal = NULL; + GSList *p = + g_slist_find_custom (_cspi_event_queue, e, cspi_event_compare); + if (p) + internal = p->data; + return internal; +} + +static const InternalEvent * +cspi_internal_event_check (const AccessibleEvent *e) +{ + InternalEvent *internal = (InternalEvent *) e; + if (internal->magic == SPI_INTERNAL_EVENT_MAGIC) + return internal; + else + return NULL; +} + +static InternalEvent * +cspi_internal_event_add (const InternalEvent *e) +{ + _cspi_event_queue = g_slist_prepend (_cspi_event_queue, (gpointer) e); + return (InternalEvent *) e; +} + +static void +cspi_internal_event_remove (const InternalEvent *e) +{ + GSList *link = g_slist_find_custom (_cspi_event_queue, e, cspi_event_compare); + if (link) + _cspi_event_queue = g_slist_remove_link (_cspi_event_queue, link); +} + +/** + * AccessibleNameChangedEvent_getNameString: + * @e: a pointer to the #AccessibleEvent being queried. + * + * Queries an #AccessibleEvent of type "object:property-change:accessible_name:", + * returning the name. + * + * Returns: a UTF-8 text string representing the name of the + * object which recently changed. + **/ +char * +AccessibleNameChangedEvent_getNameString (const AccessibleEvent *e) +{ + const InternalEvent *foo = (InternalEvent *) e; + return cspi_internal_event_get_text (foo); +} + +/** + * AccessibleEvent_ref: + * @e: a pointer to the #AccessibleEvent being referenced. + * + * Increments by 1 the reference count of the event + * + * Returns: TRUE if the function succeeded; FALSE if the pointer is not a + * valid event. + **/ +SPIBoolean +AccessibleEvent_ref (const AccessibleEvent *e) +{ + const InternalEvent *private = cspi_internal_event_check (e); + if (private) + { + InternalEvent *event = cspi_internal_event_lookup (private); + /* + * put event in the cache if it's not there already, + * and increment refcount + */ + if (!event) + { + event = cspi_internal_event_add (private); + } + event->ref_count++; + return TRUE; + } + else + return FALSE; +} + +/** + * AccessibleEvent_unref: + * @e: a pointer to the #AccessibleEvent being referenced. + * + * Decrements by 1 the reference count of the event. The event is destroyed + * when the reference count recahes zero. + * + **/ +void +AccessibleEvent_unref (const AccessibleEvent *e) +{ + const InternalEvent *private = cspi_internal_event_check (e); + /* decrement refcount and remove if appropriate */ + if (private) + { + InternalEvent *event = cspi_internal_event_lookup (private); + if (event) + { + event->ref_count--; + if (event->ref_count < 1) + { + cspi_internal_event_remove (event); + g_free ((gpointer)e->type); + Accessible_unref (e->source); + if (event->event.v_type == EVENT_DATA_OBJECT) + { + Accessible_unref (event->event.v.accessible); + } + g_free ((gpointer)e); + } + } + } +} + +typedef struct +{ + CSpiEventListener *listener; + char *event; + char *detail; +} CSpiEventListenerEntry; + +static GList *event_listeners = NULL; + +static dbus_bool_t +demarshal_rect (DBusMessageIter *iter, SPIRect *rect) +{ + dbus_int32_t x, y, width, height; + DBusMessageIter iter_struct; + + dbus_message_iter_recurse (iter, &iter_struct); + if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE; + dbus_message_iter_get_basic (&iter_struct, &x); + dbus_message_iter_next (&iter_struct); + if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE; + dbus_message_iter_get_basic (&iter_struct, &y); + dbus_message_iter_next (&iter_struct); + if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE; + dbus_message_iter_get_basic (&iter_struct, &width); + dbus_message_iter_next (&iter_struct); + if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE; + dbus_message_iter_get_basic (&iter_struct, &height); + rect->x = x; + rect->y = y; + rect->width = width; + rect->height = height; + return TRUE; +} + +static gboolean +parse_eventType (const char *eventType, char **type, char **detail, char **matchrule) +{ + char *p, *q; + char *t, *d; + + p = strchr (eventType, ':'); + if (p) p = strchr (p + 1, ':'); + if (!p) p = eventType + strlen (eventType); + t = g_malloc (p - eventType + 1); + if (t) + { + memcpy (t, eventType, p - eventType); + t[p - eventType] = '\0'; + if (!strchr (t, ':')) + { + char *q = g_strconcat (t, ":", NULL); + if (1) + { + g_free (t); + t = q; + } + } + } + else return FALSE; + if (*p == ':') + { + d = g_strdup (p + 1); + if (!d) + { + g_free (t); + return FALSE; + } + } + else d = NULL; + if ((p = strchr (t, ':'))) + { + *p = (p[1] == '\0'? '\0': '_'); + } + while ((p = strchr (t, '-'))) *p = '_'; + if (matchrule) + { + *matchrule = g_strdup_printf ("type='signal',interface='%s',member='%s'", spi_interface_accessible, t); + if (!*matchrule) + { + g_free (t); + if (d) g_free (d); + return FALSE; + } + } + if (type) *type = t; + if (detail) *detail = d; + return TRUE; +} + +static void listener_data_free (CSpiEventListenerEntry *e) +{ + g_free (e->event); + if (e->detail) g_free (e->detail); + g_free (e); +} + +/** + * SPI_registerGlobalEventListener: + * @listener: the #AccessibleEventListener to be registered against an + * event type. + * @eventType: a character string indicating the type of events for which + * notification is requested. Format is + * EventClass:major_type:minor_type:detail + * where all subfields other than EventClass are optional. + * EventClasses include "object", "window", "mouse", + * and toolkit events (e.g. "Gtk", "AWT"). + * Examples: "focus:", "Gtk:GtkWidget:button_press_event". + * + * Legal object event types: + * + * (property change events) + * + * object:property-change + * object:property-change:accessible-name + * object:property-change:accessible-description + * object:property-change:accessible-parent + * object:property-change:accessible-value + * object:property-change:accessible-role + * object:property-change:accessible-table-caption + * object:property-change:accessible-table-column-description + * object:property-change:accessible-table-column-header + * object:property-change:accessible-table-row-description + * object:property-change:accessible-table-row-header + * object:property-change:accessible-table-summary + * + * (other object events) + * + * object:state-changed + * object:children-changed + * object:visible-data-changed + * object:selection-changed + * object:text-selection-changed + * object:text-changed + * object:text-caret-moved + * object:row-inserted + * object:row-reordered + * object:row-deleted + * object:column-inserted + * object:column-reordered + * object:column-deleted + * object:model-changed + * object:active-descendant-changed + * + * (window events) + * + * window:minimize + * window:maximize + * window:restore + * window:close + * window:create + * window:reparent + * window:desktop-create + * window:desktop-destroy + * window:activate + * window:deactivate + * window:raise + * window:lower + * window:move + * window:resize + * window:shade + * window:unshade + * window:restyle + * + * (other events) + * + * focus: + * mouse:abs + * mouse:rel + * mouse:b1p + * mouse:b1r + * mouse:b2p + * mouse:b2r + * mouse:b3p + * mouse:b3r + * + * NOTE: this string may be UTF-8, but should not contain byte value 56 + * (ascii ':'), except as a delimiter, since non-UTF-8 string + * delimiting functions are used internally. + * In general, listening to + * toolkit-specific events is not recommended. + * + * Add an in-process callback function to an existing AccessibleEventListener. + * + * Returns: #TRUE if successful, otherwise #FALSE. + **/ +SPIBoolean +SPI_registerGlobalEventListener (AccessibleEventListener *listener, + const char *eventType) +{ + CSpiEventListenerEntry *e; + char *matchrule; + DBusError error; + GList *new_list; + + if (!listener) + { + return FALSE; + } + + e = g_new (CSpiEventListenerEntry, 1); + if (!e) return FALSE; + e->listener = listener; + if (!parse_eventType (eventType, &e->event, &e->detail, &matchrule)) + { + g_free (e); + return FALSE; + } + new_list = g_list_prepend (event_listeners, e); + if (!new_list) + { + listener_data_free (e); + return FALSE; + } + event_listeners = new_list; + dbus_error_init (&error); + dbus_bus_add_match (cspi_bus(), matchrule, &error); + if (error.message) + { + g_warning ("Adding match: %s", error.message); + } + return TRUE; +} + +/** + * SPI_deregisterGlobalEventListenerAll: + * @listener: the #AccessibleEventListener to be registered against + * an event type. + * + * deregisters an AccessibleEventListener from the registry, for all + * event types it may be listening to. Use + * AccessibleEventListener_unref to release the + * listener reference. + * + * Returns: #TRUE if successful, otherwise #FALSE. + **/ +SPIBoolean +SPI_deregisterGlobalEventListenerAll (AccessibleEventListener *listener) +{ + GList *l; + + if (!listener) + { + return FALSE; + } + + for (l = event_listeners; l;) + { + CSpiEventListenerEntry *e = l->data; + if (e->listener == listener) + { + listener_data_free (e); + l = g_list_remove (l, e); + } + else l = g_list_next (l); + } + return TRUE; +} + +/** + * SPI_deregisterGlobalEventListener: + * @listener: the #AccessibleEventListener registered against an event type. + * @eventType: a string specifying the event type for which this + * listener is to be deregistered. + * + * deregisters an AccessibleEventListener from the registry, for a specific + * event type. + * + * Returns: #TRUE if successful, otherwise #FALSE. + **/ +SPIBoolean +SPI_deregisterGlobalEventListener (AccessibleEventListener *listener, + const char *eventType) +{ + char *type, *detail, *matchrule; + GList *l; + + if (!parse_eventType (eventType, &type, &detail, &matchrule)) + { + return FALSE; + } + if (!listener) + { + return FALSE; + } + + for (l = event_listeners; l;) + { + CSpiEventListenerEntry *e = l->data; + if (e->listener == listener && !strcmp (e->event, type) && (e->detail == detail || !strcmp (e->detail, detail))) + { + DBusError error; + listener_data_free (e); + l = g_list_remove (l, e); + dbus_error_init (&error); + dbus_bus_remove_match (cspi_bus(), matchrule, &error); + } + else l = g_list_next (l); + } + g_free (type); + if (detail) g_free (detail); + g_free (matchrule); + return TRUE; +} + +void +cspi_dispatch_event (AccessibleEvent *e) +{ + char *event, *detail; + GList *l; + + if (!parse_eventType (e->type, &event, &detail, NULL)) + { + g_warning ("Couldn't parse event: %s\n", e->type); + return; + } + for (l = event_listeners; l; l = g_list_next (l)) + { + CSpiEventListenerEntry *entry = l->data; + if (!strcmp (event, entry->event) && + (entry->detail == NULL || !strcmp (detail, entry->detail))) + { + CSpiEventListenerClass *klass = CSPI_EVENT_LISTENER_GET_CLASS (entry->listener); + if (klass->event) (*klass->event)(entry->listener, e); + } + } + if (detail) g_free (detail); + g_free (event); +} + +DBusHandlerResult +cspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data) +{ + char *detail = NULL; + const char *event = dbus_message_get_member (message); + DBusMessageIter iter, iter_variant; + dbus_message_iter_init (message, &iter); + AccessibleEvent e; + dbus_int32_t detail1, detail2; + char *p; + + g_return_if_fail (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic (&iter, &detail); + dbus_message_iter_next (&iter); + g_return_if_fail (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_INT32); + dbus_message_iter_get_basic (&iter, &detail1); + e.detail1 = detail1; + dbus_message_iter_next (&iter); + g_return_if_fail (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_INT32); + dbus_message_iter_get_basic (&iter, &detail2); + e.detail2 = detail2; + dbus_message_iter_next (&iter); + e.type = g_strdup (event); + p = strchr (e.type, '_'); + if (p) *p = ':'; + if (detail[0] != '\0') + { + p = g_strconcat (e.type, ":", detail, NULL); + if (p) + { + g_free (e.type); + e.type = p; + } + } + while ((p = strchr (e.type, '_'))) *p = '-'; + e.source = cspi_ref_accessible (dbus_message_get_sender(message), dbus_message_get_path(message)); + dbus_message_iter_recurse (&iter, &iter_variant); + switch (dbus_message_iter_get_arg_type (&iter_variant)) + { + case DBUS_TYPE_OBJECT_PATH: + { + dbus_message_iter_get_basic (&iter_variant, &p); + e.v_type = EVENT_DATA_OBJECT; + e.v.accessible = cspi_ref_accessible (dbus_message_get_sender(message), p); + break; + } + case DBUS_TYPE_STRING: + { + dbus_message_iter_get_basic (&iter_variant, &p); + e.v_type = EVENT_DATA_STRING; + e.v.text = g_strdup (p); + break; + } + case DBUS_TYPE_STRUCT: + { + if (demarshal_rect (&iter_variant, &e.v.rect)) + { + e.v_type = EVENT_DATA_RECT; + } + break; + } + default: + break; + } + cspi_dispatch_event (&e); + return DBUS_HANDLER_RESULT_HANDLED; }