atspi.h \
atspi-accessible.c \
atspi-accessible.h \
+ atspi-accessible-private.h \
atspi-action.c \
atspi-action.h \
atspi-application.c \
*/
#include "atspi-private.h"
+#include "atspi-accessible-private.h"
#include <string.h>
static gboolean enable_caching = FALSE;
}
G_DEFINE_TYPE_WITH_CODE (AtspiAccessible, atspi_accessible, ATSPI_TYPE_OBJECT,
+ G_ADD_PRIVATE (AtspiAccessible)
G_IMPLEMENT_INTERFACE (ATSPI_TYPE_ACTION, atspi_action_interface_init)
G_IMPLEMENT_INTERFACE (ATSPI_TYPE_COLLECTION, atspi_collection_interface_init)
G_IMPLEMENT_INTERFACE (ATSPI_TYPE_COMPONENT, atspi_component_interface_init)
g_hash_table_insert (_atspi_get_live_refs (), accessible, NULL);
g_print("at-spi: init: %d objects\n", accessible_count);
#endif
+
+ accessible->priv = atspi_accessible_get_instance_private (accessible);
}
static void
g_return_val_if_fail (obj != NULL, NULL);
+ if (obj->priv->cache)
+ {
+ GValue *val = g_hash_table_lookup (obj->priv->cache, "Attributes");
+ if (val)
+ return g_value_dup_boxed (val);
+ }
+
if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_ATTRIBUTES))
{
message = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
g_return_val_if_fail (obj != NULL, NULL);
- if (_atspi_accessible_get_cache_mask (obj) & ATSPI_CACHE_ATTRIBUTES)
+ if (obj->priv->cache)
{
- GArray *array = g_array_new (TRUE, TRUE, sizeof (gchar *));
- GHashTable *attributes = atspi_accessible_get_attributes (obj, error);
- if (!attributes)
- return NULL;
- g_hash_table_foreach (attributes, add_to_attribute_array, &array);
- g_hash_table_unref (attributes);
- return array;
+ GValue *val = g_hash_table_lookup (obj->priv->cache, "Attributes");
+ if (val)
+ {
+ GArray *array = g_array_new (TRUE, TRUE, sizeof (gchar *));
+ GHashTable *attributes = g_value_get_boxed (val);
+ g_hash_table_foreach (attributes, add_to_attribute_array, &array);
+ return array;
+ }
}
message = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetAttributes", error, "");
AtspiCache result = accessible->cached_properties & mask & flag;
if (accessible->states && atspi_state_set_contains (accessible->states, ATSPI_STATE_TRANSIENT))
return FALSE;
- return (result != 0 && (atspi_main_loop || enable_caching) &&
+ return (result != 0 && (atspi_main_loop || enable_caching ||
+ flag == ATSPI_CACHE_INTERFACES) &&
!atspi_no_cache);
}
}
return locale;
}
+
+void
+free_value (gpointer data)
+{
+ GValue *value = data;
+
+ g_value_unset (value);
+ g_free (value);
+}
+
+GHashTable *
+_atspi_accessible_ref_cache (AtspiAccessible *accessible)
+{
+ AtspiAccessiblePrivate *priv = accessible->priv;
+
+ priv->cache_ref_count++;
+ if (priv->cache)
+ return g_hash_table_ref (priv->cache);
+ priv->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ free_value);
+ return priv->cache;
+}
+
+void
+_atspi_accessible_unref_cache (AtspiAccessible *accessible)
+{
+ AtspiAccessiblePrivate *priv = accessible->priv;
+
+ if (priv->cache)
+ {
+ g_hash_table_unref (priv->cache);
+ if (--priv->cache_ref_count == 0)
+ priv->cache = NULL;
+ }
+}
#define ATSPI_IS_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ATSPI_TYPE_ACCESSIBLE))
#define ATSPI_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ATSPI_TYPE_ACCESSIBLE, AtspiAccessibleClass))
+typedef struct _AtspiAccessiblePrivate AtspiAccessiblePrivate;
+
struct _AtspiAccessible
{
AtspiObject parent;
AtspiStateSet *states;
GHashTable *attributes;
guint cached_properties;
+ AtspiAccessiblePrivate *priv;
};
typedef struct _AtspiAccessibleClass AtspiAccessibleClass;
*/
#include "atspi-private.h"
+#include "atspi-accessible-private.h"
void
atspi_rect_free (AtspiRect *rect)
{
dbus_uint32_t d_ctype = ctype;
AtspiRect bbox;
+ AtspiAccessible *accessible;
bbox.x = bbox.y = bbox.width = bbox.height = -1;
g_return_val_if_fail (obj != NULL, atspi_rect_copy (&bbox));
+ accessible = ATSPI_ACCESSIBLE (obj);
+ if (accessible->priv->cache && ctype == ATSPI_COORD_TYPE_SCREEN)
+ {
+ GValue *val = g_hash_table_lookup (accessible->priv->cache, "Component.ScreenExtents");
+ if (val)
+ {
+ return g_value_dup_boxed (val);
+ }
+ }
+
_atspi_dbus_call (obj, atspi_interface_component, "GetExtents", error, "u=>(iiii)", d_ctype, &bbox);
return atspi_rect_copy (&bbox);
}
*/
#include "atspi-private.h"
+#include "atspi-accessible-private.h"
#include <string.h>
#include <ctype.h>
char *category;
char *name;
char *detail;
+ GArray *properties;
} EventListenerEntry;
G_DEFINE_TYPE (AtspiEventListener, atspi_event_listener, G_TYPE_OBJECT)
event_type, error);
}
+/**
+ * atspi_event_listener_register_full:
+ * @listener: The #AtspiEventListener to register against an event type.
+ * @event_type: a character string indicating the type of events for which
+ * notification is requested. See #atspi_event_listener_register
+ * for a description of the format and legal event types.
+* @properties: (element-type gchar*) (transfer none) (allow-none): a list of
+ * properties that should be sent along with the event. The
+ * properties are valued for the duration of the event callback.k
+ * TODO: Document.
+ *
+ * Adds an in-process callback function to an existing #AtspiEventListener.
+ *
+ * Returns: #TRUE if successful, otherwise #FALSE.
+ **/
+gboolean
+atspi_event_listener_register_full (AtspiEventListener *listener,
+ const gchar *event_type,
+ GArray *properties,
+ GError **error)
+{
+ /* TODO: Keep track of which events have been registered, so that we
+ * deregister all of them when the event listener is destroyed */
+
+ return atspi_event_listener_register_from_callback_full (listener->callback,
+ listener->user_data,
+ listener->cb_destroyed,
+ event_type,
+ properties,
+ error);
+}
+
static gboolean
notify_event_registered (EventListenerEntry *e)
{
- DBusMessage *message, *reply;
- message = dbus_message_new_method_call (atspi_bus_registry,
- atspi_path_registry,
- atspi_interface_registry,
- "RegisterEvent");
- if (!message)
- return FALSE;
- dbus_message_append_args (message, DBUS_TYPE_STRING, &e->event_type, DBUS_TYPE_INVALID);
- reply = _atspi_dbus_send_with_reply_and_block (message, NULL);
+ dbind_method_call_reentrant (_atspi_bus (), atspi_bus_registry,
+ atspi_path_registry,
+ atspi_interface_registry,
+ "RegisterEvent",
+ NULL, "sas", e->event_type,
+ e->properties);
- if (reply)
- dbus_message_unref (reply);
return TRUE;
}
const gchar *event_type,
GError **error)
{
+ return atspi_event_listener_register_from_callback_full (callback,
+ user_data,
+ callback_destroyed,
+ event_type, NULL,
+ error);
+}
+
+static GArray *
+copy_event_properties (GArray *src)
+{
+ gint i;
+
+ GArray *dst = g_array_new (FALSE, FALSE, sizeof (char *));
+
+ if (!src)
+ return dst;
+ for (i = 0; i < src->len; i++)
+ {
+ gchar *dup = g_strdup (g_array_index (src, char *, i));
+ g_array_append_val (dst, dup);
+ }
+ return dst;
+}
+
+gboolean
+atspi_event_listener_register_from_callback_full (AtspiEventListenerCB callback,
+ void *user_data,
+ GDestroyNotify callback_destroyed,
+ const gchar *event_type,
+ GArray *properties,
+ GError **error)
+{
EventListenerEntry *e;
DBusError d_error;
GPtrArray *matchrule_array;
g_free (e);
return FALSE;
}
+ e->properties = copy_event_properties (properties);
event_listeners = g_list_prepend (event_listeners, e);
for (i = 0; i < matchrule_array->len; i++)
{
AtspiEvent e;
dbus_int32_t detail1, detail2;
char *p;
+ GHashTable *cache = NULL;
- if (strcmp (signature, "siiv(so)") != 0)
+ if (strcmp (signature, "siiv(so)") != 0 &&
+ strcmp (signature, "siiva{sv}") != 0)
{
g_warning ("Got invalid signature %s for signal %s from interface %s\n", signature, member, category);
return DBUS_HANDLER_RESULT_HANDLED;
break;
}
+ dbus_message_iter_next (&iter);
+ if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY)
+ {
+ /* new form -- parse properties sent with event */
+ _atspi_dbus_update_cache_from_dict (e.source, &iter);
+ }
+
if (!strncmp (e.type, "object:children-changed", 23))
{
cache_process_children_changed (&e);
_atspi_send_event (&e);
+ if (cache)
+ _atspi_accessible_unref_cache (e.source);
+
g_free (converted_type);
g_free (name);
g_free (detail);
GError **error);
gboolean
+atspi_event_listener_register_full (AtspiEventListener *listener,
+ const gchar *event_type,
+ GArray *properties,
+ GError **error);
+
+gboolean
atspi_event_listener_register_from_callback (AtspiEventListenerCB callback,
void *user_data,
GDestroyNotify callback_destroyed,
GError **error);
gboolean
+atspi_event_listener_register_from_callback_full (AtspiEventListenerCB callback,
+ void *user_data,
+ GDestroyNotify callback_destroyed,
+ const gchar *event_type,
+ GArray *properties,
+ GError **error);
+
+gboolean
atspi_event_listener_register_no_data (AtspiEventListenerSimpleCB callback,
GDestroyNotify callback_destroyed,
const gchar *event_type,
AtspiMatchRule *rule = g_object_new (ATSPI_TYPE_MATCH_RULE, NULL);
int i;
- if (!rule)
- return NULL;
-
if (states)
rule->states = g_object_ref (states);
rule->statematchtype = statematchtype;
if (!dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "{ss}",
&iter_dict))
return FALSE;
- g_hash_table_foreach (rule->attributes, append_entry, &iter_dict);
+ if (rule->attributes)
+ g_hash_table_foreach (rule->attributes, append_entry, &iter_dict);
dbus_message_iter_close_container (&iter_struct, &iter_dict);
dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &d_attributematchtype);
typedef enum
{
ATSPI_ERROR_APPLICATION_GONE,
- ATSPI_ERROR_IPC
+ ATSPI_ERROR_IPC,
+ ATSPI_ERROR_SYNC_NOT_ALLOWED,
} AtspiError;
extern GMainLoop *atspi_main_loop;
GHashTable *_atspi_get_live_refs ();
gchar *_atspi_name_compat (gchar *in);
+
+void _atspi_dbus_update_cache_from_dict (AtspiAccessible *accessible, DBusMessageIter *iter);
+
+gboolean _atspi_get_allow_sync ();
+
+gboolean _atspi_set_allow_sync (gboolean val);
+
+void _atspi_set_error_no_sync (GError **error);
G_END_DECLS
#endif /* _ATSPI_MISC_PRIVATE_H_ */
static GHashTable *live_refs = NULL;
static gint method_call_timeout = 800;
static gint app_startup_time = 15000;
+static gboolean allow_sync = TRUE;
GMainLoop *atspi_main_loop;
GMainContext *atspi_main_context;
if (!check_app (aobj->app, error))
return FALSE;
+ if (!allow_sync)
+ {
+ _atspi_set_error_no_sync (error);
+ return FALSE;
+ }
+
va_start (args, type);
dbus_error_init (&err);
set_timeout (aobj->app);
if (!check_app (aobj->app, error))
return FALSE;
+ if (!allow_sync)
+ {
+ _atspi_set_error_no_sync (error);
+ return FALSE;
+ }
+
message = dbus_message_new_method_call (aobj->app->bus_name,
aobj->path,
"org.freedesktop.DBus.Properties",
DBusMessageIter iter_array;
accessible->interfaces = 0;
+ if (strcmp (dbus_message_iter_get_signature (iter), "as") != 0)
+ {
+ g_warning ("_atspi_dbus_set_interfaces: Passed iterator with invalid signature %s", dbus_message_iter_get_signature (iter));
+ return;
+ }
dbus_message_iter_recurse (iter, &iter_array);
while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
{
return NULL;
}
+
+void
+_atspi_dbus_update_cache_from_dict (AtspiAccessible *accessible, DBusMessageIter *iter)
+{
+ GHashTable *cache = _atspi_accessible_ref_cache (accessible);
+ DBusMessageIter iter_dict, iter_dict_entry, iter_struct, iter_variant;
+
+ dbus_message_iter_recurse (iter, &iter_dict);
+ while (dbus_message_iter_get_arg_type (&iter_dict) != DBUS_TYPE_INVALID)
+ {
+ const char *key;
+ GValue *val = NULL;
+ dbus_message_iter_recurse (&iter_dict, &iter_dict_entry);
+ dbus_message_iter_get_basic (&iter_dict_entry, &key);
+ dbus_message_iter_next (&iter_dict_entry);
+ dbus_message_iter_recurse (&iter_dict_entry, &iter_variant);
+ if (!strcmp (key, "interfaces"))
+ {
+ _atspi_dbus_set_interfaces (accessible, &iter_variant);
+ }
+ else if (!strcmp (key, "Attributes"))
+ {
+ val = g_new0 (GValue, 1);;
+ g_value_init (val, G_TYPE_HASH_TABLE);
+ if (strcmp (dbus_message_iter_get_signature (&iter_variant),
+ "a{ss}") != 0)
+ break;
+ g_value_take_boxed (val, _atspi_dbus_hash_from_iter (&iter_variant));
+ }
+ else if (!strcmp (key, "Component.ScreenExtents"))
+ {
+ dbus_int32_t d_int;
+ AtspiRect extents;
+ val = g_new0 (GValue, 1);;
+ g_value_init (val, ATSPI_TYPE_RECT);
+ if (strcmp (dbus_message_iter_get_signature (&iter_variant),
+ "(iiii)") != 0)
+ break;
+ dbus_message_iter_recurse (&iter_variant, &iter_struct);
+ dbus_message_iter_get_basic (&iter_struct, &d_int);
+ extents.x = d_int;
+ dbus_message_iter_next (&iter_struct);
+ dbus_message_iter_get_basic (&iter_struct, &d_int);
+ extents.y = d_int;
+ dbus_message_iter_next (&iter_struct);
+ dbus_message_iter_get_basic (&iter_struct, &d_int);
+ extents.width = d_int;
+ dbus_message_iter_next (&iter_struct);
+ dbus_message_iter_get_basic (&iter_struct, &d_int);
+ extents.height = d_int;
+ g_value_set_boxed (val, &extents);
+ }
+ if (val)
+ g_hash_table_insert (cache, g_strdup (key), val);
+ dbus_message_iter_next (&iter_dict);
+ }
+}
+
+gboolean
+_atspi_get_allow_sync ()
+{
+ return allow_sync;
+}
+
+gboolean
+_atspi_set_allow_sync (gboolean val)
+{
+ gboolean ret = allow_sync;
+
+ allow_sync = val;
+ return ret;
+}
+
+void
+_atspi_set_error_no_sync (GError **error)
+{
+ g_set_error_literal (error, ATSPI_ERROR, ATSPI_ERROR_SYNC_NOT_ALLOWED,
+ _("Attempted synchronous call where prohibited"));
+}
#include "glib/gi18n.h"
#include "atspi.h"
+#include "atspi-accessible-private.h"
G_BEGIN_DECLS
void _atspi_reregister_device_listeners ();
{
gchar *bus_name;
gchar **data;
+ GSList *properties;
};
static void
{
g_strfreev (evdata->data);
g_free (evdata->bus_name);
+ g_slist_free_full (evdata->properties, g_free);
g_free (evdata);
registry->events = g_list_remove (registry->events, evdata);
}
gchar **data;
DBusMessage *signal;
const char *sender = dbus_message_get_sender (message);
+ DBusMessageIter iter, iter_array;
+ const char *signature = dbus_message_get_signature (message);
- if (!dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &orig_name,
- DBUS_TYPE_INVALID))
+ if (strcmp (signature, "sas") != 0)
+ {
+ g_warning ("got RegisterEvent with invalid signature '%s'", signature);
return NULL;
+ }
+ dbus_message_iter_init (message, &iter);
+ dbus_message_iter_get_basic (&iter, &orig_name);
+ dbus_message_iter_next (&iter);
+ dbus_message_iter_recurse (&iter, &iter_array);
name = ensure_proper_format (orig_name);
- evdata = (event_data *) g_malloc (sizeof (*evdata));
- if (!evdata)
- return NULL;
+ evdata = g_new0 (event_data, 1);
data = g_strsplit (name, ":", 3);
evdata->bus_name = g_strdup (sender);
evdata->data = data;
+
+ while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
+ {
+ const char *property;
+ dbus_message_iter_get_basic (&iter_array, &property);
+ evdata->properties = g_slist_append (evdata->properties,
+ g_strdup (property));
+ dbus_message_iter_next (&iter_array);
+ }
registry->events = g_list_append (registry->events, evdata);
if (needs_mouse_poll (evdata->data))
signal = dbus_message_new_signal (SPI_DBUS_PATH_REGISTRY,
SPI_DBUS_INTERFACE_REGISTRY,
"EventListenerRegistered");
- dbus_message_append_args (signal, DBUS_TYPE_STRING, &sender,
- DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
- dbus_connection_send (bus, signal, NULL);
- dbus_message_unref (signal);
+ if (signal)
+ {
+ GSList *ls = evdata->properties;
+ dbus_message_iter_init_append (signal, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &sender);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &name);
+ dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s", &iter_array);
+ while (ls)
+ {
+ dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_STRING, &ls->data);
+ ls = g_slist_next (ls);
+ }
+ dbus_message_iter_close_container (&iter, &iter_array);
+ dbus_connection_send (bus, signal, NULL);
+ dbus_message_unref (signal);
+ }
g_free (name);
return dbus_message_new_method_return (message);