* AT-SPI - Assistive Technology Service Provider Interface
* (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
*
+ * Copyright 2011, F123 Consulting & Mais Diferenças
* Copyright 2008, 2009, Codethink Ltd.
* Copyright 2001, 2002, 2003 Sun Microsystems Inc.,
* Copyright 2001, 2002, 2003 Ximian, Inc.
#include "bridge.h"
#include "accessible-register.h"
-#include "common/spi-dbus.h"
+#include "spi-dbus.h"
#include "event.h"
#include "object.h"
-#include "dbus/dbus-glib-lowlevel.h"
static GArray *listener_ids = NULL;
typedef struct _SpiReentrantCallClosure
{
+ DBusConnection *bus;
GMainLoop *loop;
DBusMessage *reply;
+ guint timeout;
} SpiReentrantCallClosure;
static void
switch_main_context (GMainContext *cnx)
{
-/* This code won't work on dbus-glib earlier than 0.9.0 because of FDO#30574 */
- if (spi_global_app_data->app_bus_addr [0] == '\0')
- return;
-
GList *list;
+#ifndef DISABLE_P2P
atspi_dbus_server_setup_with_g_main (spi_global_app_data->server, cnx);
+#endif
atspi_dbus_connection_setup_with_g_main (spi_global_app_data->bus, cnx);
for (list = spi_global_app_data->direct_connections; list; list = list->next)
atspi_dbus_connection_setup_with_g_main (list->data, cnx);
g_main_loop_quit (closure->loop);
}
+static gboolean
+timeout_reply (void *data)
+{
+ SpiReentrantCallClosure *closure = data;
+
+ if (!dbus_connection_get_is_connected (closure->bus))
+ g_main_loop_quit (closure->loop);
+ closure->timeout = -1;
+ return FALSE;
+}
+
static DBusMessage *
send_and_allow_reentry (DBusConnection * bus, DBusMessage * message)
{
DBusPendingCall *pending;
SpiReentrantCallClosure closure;
GMainContext *main_context;
+ GSource *source;
main_context = (g_getenv ("AT_SPI_CLIENT") ? NULL :
spi_global_app_data->main_context);
+ closure.bus = bus;
closure.loop = g_main_loop_new (main_context, FALSE);
+ closure.reply = NULL;
switch_main_context (main_context);
- if (!dbus_connection_send_with_reply (bus, message, &pending, -1) || !pending)
+ if (!dbus_connection_send_with_reply (bus, message, &pending, 9000) || !pending)
{
switch_main_context (NULL);
return NULL;
}
dbus_pending_call_set_notify (pending, set_reply, (void *) &closure, NULL);
+ source = g_timeout_source_new (500);
+ g_source_set_callback (source, timeout_reply, &closure, NULL);
+ closure.timeout = g_source_attach (source, main_context);
+ g_source_unref (source);
g_main_loop_run (closure.loop);
+ if (closure.timeout != -1)
+ g_source_destroy (source);
g_main_loop_unref (closure.loop);
return closure.reply;
static gboolean
Accessibility_DeviceEventController_NotifyListenersSync (const
- Accessibility_DeviceEvent
+ AtspiDeviceEvent
* key_event)
{
DBusMessage *message;
message =
dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY,
- SPI_DBUS_PATH_DEC,
- SPI_DBUS_INTERFACE_DEC,
+ ATSPI_DBUS_PATH_DEC,
+ ATSPI_DBUS_INTERFACE_DEC,
"NotifyListenersSync");
dbus_error_init (&error);
}
static void
-spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent * keystroke,
+spi_init_keystroke_from_atk_key_event (AtspiDeviceEvent * keystroke,
AtkKeyEventStruct * event)
{
keystroke->id = (dbus_int32_t) event->keyval;
switch (event->type)
{
case (ATK_KEY_EVENT_PRESS):
- keystroke->type = Accessibility_KEY_PRESSED_EVENT;
+ keystroke->type = ATSPI_KEY_PRESSED;
break;
case (ATK_KEY_EVENT_RELEASE):
- keystroke->type = Accessibility_KEY_RELEASED_EVENT;
+ keystroke->type = ATSPI_KEY_RELEASED;
break;
default:
keystroke->type = 0;
spi_atk_bridge_key_listener (AtkKeyEventStruct * event, gpointer data)
{
gboolean result;
- Accessibility_DeviceEvent key_event;
+ AtspiDeviceEvent key_event;
spi_init_keystroke_from_atk_key_event (&key_event, event);
}
static const void *
-replace_null (const gint type,
+validate_for_dbus (const gint type,
const void *val)
{
switch (type)
case DBUS_TYPE_OBJECT_PATH:
if (!val)
return "";
+ else if (!g_utf8_validate (val, -1, NULL))
+ {
+ g_warning ("atk-bridge: Received bad UTF-8 string when emitting event");
+ return "";
+ }
else
return val;
default:
dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, type, &sub);
- val = replace_null ((int) *type, val);
+ val = validate_for_dbus ((int) *type, val);
dbus_message_iter_append_basic(&sub, (int) *type, &val);
dbus_message_iter_close_container(iter, &sub);
data [2] = ensure_proper_format (minor);
data [3] = NULL;
+ /* Hack: Always pass events that update the cache.
+ * TODO: FOr 2.2, have at-spi2-core define a special "cache listener" for
+ * this instead, so that we don't send these if no one is listening */
+ if (!g_strcmp0 (data [1], "ChildrenChanged") ||
+ !g_strcmp0 (data [1], "PropertyChange") ||
+ !g_strcmp0 (data [1], "StateChanged"))
+ {
+ g_free (data [2]);
+ g_free (data [1]);
+ g_free (data [0]);
+ return TRUE;
+ }
+
/* Hack: events such as "object::text-changed::insert:system" as
generated by Gecko */
data [2][strcspn (data [2], ":")] = '\0';
}
}
+#if 0
+ g_print("event: %s %s %s: %d\n", data[0], data[1], data[2], ret);
+#endif
g_free (data [2]);
g_free (data [1]);
g_free (data [0]);
return ret;
}
+/* Convert a : to a / so that listeners can use arg0path to match only
+ * * the prefix */
+static char *
+adapt_minor_for_dbus (const char *source)
+{
+ gchar *ret = g_strdup (source);
+ int i = strcspn (ret, ":");
+ if (ret[i] == ':')
+ ret[i] = '/';
+ return ret;
+}
+
/*
* Emits an AT-SPI event.
* AT-SPI events names are split into three parts:
{
DBusConnection *bus = spi_global_app_data->bus;
const char *path;
+ char *minor_dbus;
gchar *cname, *t;
DBusMessage *sig;
return;
path = spi_register_object_to_path (spi_global_register, G_OBJECT (obj));
+ g_return_if_fail (path != NULL);
/*
* This is very annoying, but as '-' isn't a legal signal
dbus_message_iter_init_append(sig, &iter);
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor);
+ minor_dbus = adapt_minor_for_dbus (minor);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor_dbus);
+ g_free (minor_dbus);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2);
append_variant (&iter, type, val);
guint detail1;
accessible = ATK_OBJECT (g_value_get_object (¶m_values[0]));
- pname = g_strdup (g_value_get_string (¶m_values[1]));
+ pname = g_value_get_string (¶m_values[1]);
- /* TODO - Possibly ignore a change to the 'defunct' state.
- * This is because without reference counting defunct objects should be removed.
- */
detail1 = (g_value_get_boolean (¶m_values[2])) ? 1 : 0;
emit_event (accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0,
DBUS_TYPE_INT32_AS_STRING, 0, append_basic);
- g_free (pname);
+
+ if (!g_strcmp0 (pname, "defunct") && detail1)
+ spi_register_deregister_object (spi_global_register, G_OBJECT (accessible),
+ TRUE);
return TRUE;
}
AtkObject *accessible;
GSignalQuery signal_query;
const gchar *name, *minor;
- gint detail1;
+ gint detail1 = 0;
g_signal_query (signal_hint->signal_id, &signal_query);
name = signal_query.signal_name;
GSignalQuery signal_query;
const gchar *name, *minor;
gchar *selected;
- gint detail1, detail2;
+ gint detail1 = 0, detail2 = 0;
g_signal_query (signal_hint->signal_id, &signal_query);
name = signal_query.signal_name;
emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2,
DBUS_TYPE_STRING_AS_STRING, selected, append_basic);
+ g_free (selected);
+
return TRUE;
}
GSignalQuery signal_query;
const gchar *name;
gchar *minor, *text;
- gint detail1, detail2;
+ gint detail1 = 0, detail2 = 0;
accessible = ATK_OBJECT (g_value_get_object (¶m_values[0]));
/* Get signal name for 'Gtk:AtkText:text-changed' so
GSignalQuery signal_query;
const gchar *name;
gchar *minor, *text;
- gint detail1, detail2;
+ gint detail1 = 0, detail2 = 0;
accessible = ATK_OBJECT (g_value_get_object (¶m_values[0]));
/* Get signal name for 'Gtk:AtkText:text-changed' so
AtkObject *accessible;
GSignalQuery signal_query;
const gchar *name, *minor;
- gint detail1, detail2;
+ gint detail1 = 0, detail2 = 0;
g_signal_query (signal_hint->signal_id, &signal_query);
name = signal_query.signal_name;
{
GSignalQuery signal_query;
const gchar *name, *minor;
- gint detail1, detail2 = 0;
+ gint detail1 = 0, detail2 = 0;
AtkObject *accessible, *ao=NULL;
gpointer child;
* and stores the signal id returned so that the function may be
* de-registered later.
*/
-static void
+static guint
add_signal_listener (GSignalEmissionHook listener, const char *signal_name)
{
guint id;
id = atk_add_global_event_listener (listener, signal_name);
- g_array_append_val (listener_ids, id);
+
+ if (id > 0) /* id == 0 is a failure */
+ g_array_append_val (listener_ids, id);
+
+ return id;
}
/*
*/
GObject *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
AtkObject *bo = atk_no_op_object_new (ao);
+ guint id = 0;
g_object_unref (G_OBJECT (bo));
g_object_unref (ao);
+ if (listener_ids)
+ {
+ g_warning ("atk_bridge: spi_atk-register_event_listeners called multiple times");
+ return;
+ }
+
/* Register for focus event notifications, and register app with central registry */
listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
add_signal_listener (property_event_listener,
"Gtk:AtkObject:property-change");
- add_signal_listener (window_event_listener, "window:create");
- add_signal_listener (window_event_listener, "window:destroy");
- add_signal_listener (window_event_listener, "window:minimize");
- add_signal_listener (window_event_listener, "window:maximize");
- add_signal_listener (window_event_listener, "window:restore");
- add_signal_listener (window_event_listener, "window:activate");
- add_signal_listener (window_event_listener, "window:deactivate");
+
+ /* window events: we tentative try to register using the old format */
+ id = add_signal_listener (window_event_listener, "window:create");
+
+ if (id != 0)
+ {
+ /* If we are able to register using the old format, we assume
+ * that the ATK implementor is managing window events without
+ * AtkWindow. We can't use the opposite test because after
+ * including AtkWindow on ATK you would be able to register to
+ * that event, although the ATK implementor could or not use it.
+ */
+
+ add_signal_listener (window_event_listener, "window:destroy");
+ add_signal_listener (window_event_listener, "window:minimize");
+ add_signal_listener (window_event_listener, "window:maximize");
+ add_signal_listener (window_event_listener, "window:restore");
+ add_signal_listener (window_event_listener, "window:activate");
+ add_signal_listener (window_event_listener, "window:deactivate");
+ }
+ else
+ {
+ add_signal_listener (window_event_listener, "Atk:AtkWindow:create");
+ add_signal_listener (window_event_listener, "Atk:AtkWindow:destroy");
+ add_signal_listener (window_event_listener, "Atk:AtkWindow:minimize");
+ add_signal_listener (window_event_listener, "Atk:AtkWindow:maximize");
+ add_signal_listener (window_event_listener, "Atk:AtkWindow:restore");
+ add_signal_listener (window_event_listener, "Atk:AtkWindow:activate");
+ add_signal_listener (window_event_listener, "Atk:AtkWindow:deactivate");
+ }
+
add_signal_listener (document_event_listener,
"Gtk:AtkDocument:load-complete");
add_signal_listener (document_event_listener, "Gtk:AtkDocument:reload");
"Gtk:AtkTable:column-reordered");
add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-deleted");
add_signal_listener (generic_event_listener, "Gtk:AtkTable:model-changed");
-
- /* Children signal listeners */
- atk_add_global_event_listener (children_changed_event_listener,
- "Gtk:AtkObject:children-changed");
+ add_signal_listener (children_changed_event_listener, "Gtk:AtkObject:children-changed");
#if 0
g_signal_connect (G_OBJECT (spi_global_app_data->root),
listener_ids = NULL;
if (atk_bridge_focus_tracker_id)
+ {
atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
+ atk_bridge_focus_tracker_id = 0;
+ }
- for (i = 0; ids && i < ids->len; i++)
+ if (ids)
{
- atk_remove_global_event_listener (g_array_index (ids, guint, i));
+ for (i = 0; i < ids->len; i++)
+ {
+ atk_remove_global_event_listener (g_array_index (ids, guint, i));
+ }
+ g_array_free (ids, TRUE);
}
if (atk_bridge_key_event_listener_id)
+ {
atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
+ atk_bridge_key_event_listener_id = 0;
+ }
}
/*---------------------------------------------------------------------------*/