* AT-SPI - Assistive Technology Service Provider Interface
* (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
*
+ * Copyright 2008, Codethink Ltd.
* Copyright 2001, 2002 Sun Microsystems Inc.,
* Copyright 2001, 2002 Ximian, Inc.
*
* Boston, MA 02111-1307, USA.
*/
-/* registry.c: the main accessibility service registry implementation */
-
-#undef SPI_LISTENER_DEBUG
-#undef SPI_DEBUG
-#undef SPI_QUEUE_DEBUG
-
#include <config.h>
-#ifdef SPI_DEBUG
-# include <stdio.h>
-#endif
-
#include <spi-common/spi-dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
#include "registry.h"
-#include "dbus/dbus-glib-lowlevel.h"
-
-/* Our parent GObject type */
-#define PARENT_TYPE G_OBJECT_TYPE
-
-int _dbg = 0;
-
-typedef enum {
- ETYPE_FOCUS,
- ETYPE_OBJECT,
- ETYPE_PROPERTY,
- ETYPE_WINDOW,
- ETYPE_TOOLKIT,
- ETYPE_KEYBOARD,
- ETYPE_MOUSE,
- ETYPE_LAST_DEFINED
-} EventTypeCategory;
-
-typedef struct {
- const char *event_name;
- EventTypeCategory type_cat;
- GQuark major; /* from string segment[1] */
- GQuark minor; /* from string segment[1]+segment[2] */
- GQuark detail; /* from string segment[3] (not concatenated) */
-} EventTypeStruct;
+
+enum
+{
+ REGISTRY_APPLICATION_REMOVE = 0,
+ REGISTRY_APPLICATION_ADD = 1
+};
+
+/*---------------------------------------------------------------------------*/
G_DEFINE_TYPE(SpiRegistry, spi_registry, G_TYPE_OBJECT)
static void
-spi_registry_set_debug (const char *debug_flag_string)
+spi_registry_class_init (SpiRegistryClass *klass)
+{
+ GObjectClass * object_class = (GObjectClass *) klass;
+
+ spi_registry_parent_class = g_type_class_ref (G_TYPE_OBJECT);
+}
+
+static void
+spi_registry_init (SpiRegistry *registry)
{
- if (debug_flag_string)
- _dbg = (int) g_ascii_strtod (debug_flag_string, NULL);
+ registry->apps = g_sequence_new (g_free);
}
-static void emit(SpiRegistry *registry, const char *name, int first_type, ...)
+/*---------------------------------------------------------------------------*/
+
+static void emit(SpiRegistry *reg, const char *itf, const char *name, int ftype, ...)
{
va_list arg;
- va_start(arg, first_type);
- spi_dbus_emit_valist(registry->droute.bus, SPI_DBUS_PATH_REGISTRY, SPI_DBUS_INTERFACE_REGISTRY, name, first_type, arg);
+ va_start(arg, ftype);
+ spi_dbus_emit_valist(reg->bus, SPI_DBUS_PATH_DEC, itf, name, ftype, arg);
va_end(arg);
}
-static void
-desktop_add_application (SpiDesktop *desktop,
- guint index, gpointer data)
+/*---------------------------------------------------------------------------*/
+
+static gint
+data_str_cmp (gpointer a, gpointer b, gpointer data)
{
- SpiRegistry *registry = SPI_REGISTRY (data);
- const SpiDesktopApplication *app = g_list_nth_data(desktop->applications, index);
-
- emit(registry, "ApplicationAdd", DBUS_TYPE_UINT32, &index, DBUS_TYPE_STRING, &app->path, DBUS_TYPE_INVALID);
+ return g_strcmp0(a, b);
}
-
-
-static void
-desktop_remove_application (SpiDesktop *desktop,
- guint index, gpointer data)
+static gboolean
+seq_add_string (GSequence *seq, gchar *str)
{
- SpiRegistry *registry = SPI_REGISTRY (data);
- const char *name = g_list_nth_data(desktop->applications, index);
-
- emit(registry, "ApplicationRemove", DBUS_TYPE_UINT32, &index, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
+ GSequenceIter *iter;
+ gchar *item;
+ gboolean res = FALSE;
+
+ iter = g_sequence_search (seq, str, (GCompareDataFunc) data_str_cmp, NULL);
+ iter = g_sequence_iter_prev (iter);
+
+ if (!g_sequence_iter_is_end (iter))
+ {
+ item = g_sequence_get (iter);
+ if (g_strcmp0 (item, str))
+ {
+ g_sequence_insert_sorted (seq, g_strdup(str), (GCompareDataFunc) data_str_cmp, NULL);
+ res = TRUE;
+ }
+ }
+ else
+ {
+ g_sequence_insert_sorted (seq, g_strdup(str), (GCompareDataFunc) data_str_cmp, NULL);
+ res = TRUE;
+ }
+
+ return res;
}
+static gboolean
+seq_remove_string (GSequence *seq, gchar *str)
+{
+ GSequenceIter *iter;
+ gchar *item;
+ gboolean res = FALSE;
+
+ iter = g_sequence_search (seq, str, (GCompareDataFunc) data_str_cmp, NULL);
+ iter = g_sequence_iter_prev (iter);
+
+ if (!g_sequence_iter_is_end (iter))
+ {
+ item = g_sequence_get (iter);
+ if (!g_strcmp0 (item, str))
+ {
+ g_sequence_remove (iter);
+ res = TRUE;
+ }
+ }
+ return res;
+}
static void
-spi_registry_object_finalize (GObject *object)
+add_application (DBusConnection *bus, SpiRegistry *reg, gchar *app)
{
- DBG (1, g_warning ("spi_registry_object_finalize called\n"));
- g_object_unref (SPI_REGISTRY (object)->de_controller);
-
- G_OBJECT_CLASS (spi_registry_parent_class)->finalize (object);
+ guint add = REGISTRY_APPLICATION_ADD;
+
+ if (seq_add_string (reg->apps, app))
+ {
+ emit (reg,
+ SPI_DBUS_INTERFACE_REGISTRY,
+ "updateApplications",
+ DBUS_TYPE_INT32,
+ &add,
+ DBUS_TYPE_STRING,
+ &app,
+ DBUS_TYPE_INVALID);
+ }
}
-/**
- * registerApplication:
- * @application: a reference to the requesting @Application
- * return values: void
- *
- * Register a new application with the accessibility broker.
- *
- **/
-static DBusMessage *
-impl_accessibility_registry_register_application (DBusConnection *bus, DBusMessage *message, void *user_data)
+static void
+remove_application (DBusConnection *bus, SpiRegistry *reg, gchar *app)
{
- SpiRegistry *registry = SPI_REGISTRY (user_data);
- const char *application = dbus_message_get_sender (message);
-
-#ifdef SPI_DEBUG
- fprintf (stderr, "registering app %s\n", application);
-#endif
- spi_desktop_add_application (registry->desktop, application);
-
- /*
- * TODO: change the implementation below to a WM-aware one;
- * e.g. don't add all apps to the SpiDesktop
- */
- return dbus_message_new_method_return (message);
+ guint remove = REGISTRY_APPLICATION_REMOVE;
+
+ if (seq_remove_string (reg->apps, app))
+ {
+ /*TODO spi_remove_device_listeners (registry->de_controller, old);*/
+ emit (reg,
+ SPI_DBUS_INTERFACE_REGISTRY,
+ "updateApplications",
+ DBUS_TYPE_INT32,
+ &remove,
+ DBUS_TYPE_STRING,
+ &app,
+ DBUS_TYPE_INVALID);
+ }
}
-/**
- * deregisterApplication:
- * @application: a reference to the @Application
- * to be deregistered.
- * return values: void
- *
- * De-register an application previously registered with the broker.
- *
- **/
-static DBusMessage *
-impl_accessibility_registry_deregister_application (DBusConnection *bus, DBusMessage *message, void *user_data)
-{
- SpiRegistry *registry = SPI_REGISTRY (user_data);
- const char *application = dbus_message_get_sender (message);
+/*---------------------------------------------------------------------------*/
- spi_desktop_remove_application (registry->desktop, application);
+static void
+add_bus_name_cb (gpointer item, gpointer data)
+{
+ DBusMessageIter *iter_array = (DBusMessageIter *) data;
-#ifdef SPI_DEBUG
- fprintf (stderr, "de-registered app %s\n", application);
-#endif
- return dbus_message_new_method_return (message);
+ dbus_message_iter_append_basic (iter_array, DBUS_TYPE_STRING, (gchar **) &item);
}
-/**
- * getDesktopCount:
- * return values: a short integer indicating the current number of
- * @Desktops.
- *
- * Get the current number of desktops.
- *
- **/
static DBusMessage *
-impl_accessibility_registry_get_desktop_count (DBusConnection *bus, DBusMessage *message, void *user_data)
+impl_getApplications (DBusConnection *bus, DBusMessage *message, void *user_data)
{
- dbus_int16_t n_desktops = 1;
DBusMessage *reply;
+ DBusMessageIter iter, iter_array;
+ SpiRegistry *reg = SPI_REGISTRY (user_data);
- /* TODO: implement support for multiple virtual desktops */
reply = dbus_message_new_method_return (message);
- if (reply)
- {
- dbus_message_append_args (reply, DBUS_TYPE_INT16, &n_desktops, DBUS_TYPE_INVALID);
- }
+
+ dbus_message_iter_init_append (reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &iter_array);
+ g_sequence_foreach (reg->apps, add_bus_name_cb, &iter_array);
+ dbus_message_iter_close_container(&iter, &iter_array);
return reply;
}
-/**
- * getDesktop:
- * @n: the index of the requested @Desktop.
- * return values: a reference to the requested @Desktop.
- *
- * Get the nth accessible desktop.
- *
- **/
-static DBusMessage *
-impl_accessibility_registry_get_desktop (DBusConnection *bus, DBusMessage *message, void *user_data)
+/*---------------------------------------------------------------------------*/
+
+static void
+impl_registerApplication (DBusConnection *bus, DBusMessage *message, void *user_data)
{
- DBusError error;
- dbus_int16_t n;
- const char *path;
- DBusMessage *reply;
+ gchar *app_name;
+ SpiRegistry *reg = SPI_REGISTRY (user_data);
- /* TODO: implement support for multiple virtual desktops */
- dbus_error_init (&error);
- if (!dbus_message_get_args (message, &error, DBUS_TYPE_INT16, &n, DBUS_TYPE_INVALID))
- {
- return spi_dbus_general_error (message);
- }
- path = (n == 0? SPI_DBUS_PATH_DESKTOP: "/");
- reply = dbus_message_new_method_return (message);
- if (reply)
- {
- dbus_message_append_args (reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID);
- }
- return reply;
+ if (dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &app_name, DBUS_TYPE_INVALID))
+ add_application(bus, reg, app_name);
}
-
-/**
- * getDesktopList:
- * return values: a sequence containing references to
- * the @Desktops.
- *
- * Get a list of accessible desktops.
- *
- **/
-static DBusMessage *
-impl_accessibility_registry_get_desktop_list (DBusConnection *bus, DBusMessage *message, void *user_data)
+static void
+impl_deregisterApplication (DBusConnection *bus, DBusMessage *message, void *user_data)
{
- DBusMessage *reply;
- DBusMessageIter iter, iter_array;
- const char *path = SPI_DBUS_PATH_DESKTOP;
+ gchar *app_name;
+ SpiRegistry *reg = SPI_REGISTRY (user_data);
- reply = dbus_message_new_method_return (message);
- if (!reply) return NULL;
- dbus_message_iter_init_append(reply, &iter);
- if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "o", &iter_array)) goto oom;
- dbus_message_iter_append_basic(&iter_array, DBUS_TYPE_STRING, &path);
- if (!dbus_message_iter_close_container (&iter, &iter_array)) goto oom;
- return reply;
-oom:
- // TODO: handle out-of-memory
- return reply;
+ if (dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &app_name, DBUS_TYPE_INVALID))
+ remove_application(bus, reg, app_name);
}
+/*---------------------------------------------------------------------------*/
-static DBusMessage *
-impl_accessibility_registry_get_device_event_controller (DBusConnection *bus, DBusMessage *message, void *user_data)
+static DBusHandlerResult
+message_handler (DBusConnection *bus, DBusMessage *message, void *user_data)
{
- DBusMessage *reply;
- const char *path = SPI_DBUS_PATH_DEC;
+ DBusMessage *reply = NULL;
+ guint res = DBUS_HANDLER_RESULT_HANDLED;
+
+
+ int mtype;
+ const char *itf;
+ const char *name;
+
+ mtype = dbus_message_get_type (message);
+ itf = dbus_message_get_interface (message);
+ name = dbus_message_get_member (message);
+
+ if (dbus_message_is_method_call (message, SPI_DBUS_INTERFACE_REGISTRY, "getApplications"))
+ reply = impl_getApplications (bus, message, user_data);
+ else if (dbus_message_is_method_call (message, SPI_DBUS_INTERFACE_REGISTRY, "registerApplication"))
+ impl_registerApplication (bus, message, user_data);
+ else if (dbus_message_is_method_call (message, SPI_DBUS_INTERFACE_REGISTRY, "deregisterApplication"))
+ impl_deregisterApplication (bus, message, user_data);
+ else
+ res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- reply = dbus_message_new_method_return (message);
if (reply)
- {
- dbus_message_append_args (reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID);
- }
- return reply;
+ {
+ dbus_connection_send (bus, reply, NULL);
+ dbus_message_unref (reply);
+ }
+ return res;
}
+/*---------------------------------------------------------------------------*/
static void
-spi_registry_class_init (SpiRegistryClass *klass)
+handle_disconnection (DBusConnection *bus, SpiRegistry *reg, DBusMessage *message)
{
- GObjectClass * object_class = (GObjectClass *) klass;
+ char *name, *old, *new;
+
+ if (dbus_message_get_args (message, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old,
+ DBUS_TYPE_STRING, &new,
+ DBUS_TYPE_INVALID))
+ {
+ if (*old != '\0' && *new == '\0')
+ {
+ remove_application(bus, reg, old);
+ }
+ }
+}
- spi_registry_parent_class = g_type_class_ref (G_TYPE_OBJECT);
-
- object_class->finalize = spi_registry_object_finalize;
+static DBusHandlerResult
+signal_handler (DBusConnection *bus, DBusMessage *message, void *user_data)
+{
+ SpiRegistry *registry = SPI_REGISTRY (user_data);
+ guint res = DBUS_HANDLER_RESULT_HANDLED;
+ const char *iface = dbus_message_get_interface (message);
+ const char *member = dbus_message_get_member (message);
+
+ if (!g_strcmp0(iface, DBUS_INTERFACE_DBUS) && !g_strcmp0(member, "NameOwnerChanged"))
+ handle_disconnection (bus, registry, message);
+ else
+ res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ return res;
}
-static DBusObjectPathVTable droute_vtable =
+/*---------------------------------------------------------------------------*/
+
+static gchar *app_reg_sig_match = "type='signal', interface='org.freedesktop.atspi.Tree', member='registerApplication'";
+static gchar *app_dereg_sig_match = "type='signal', interface='org.freedesktop.atspi.Tree', member='deregisterApplication'";
+
+static gchar *app_sig_match_blank = "";
+
+static DBusObjectPathVTable reg_vtable =
{
NULL,
- &droute_message,
+ &message_handler,
NULL, NULL, NULL, NULL
};
-static void
-spi_registry_init (SpiRegistry *registry)
-{
- DBusError error;
-
- spi_registry_set_debug (g_getenv ("AT_SPI_DEBUG"));
- /*
- * TODO: FIXME, this module makes the foolish assumptions that
- * registryd uses the same display as the apps, and that the
- * DISPLAY environment variable is set.
- */
- gdk_init (NULL, NULL);
-
- registry->exit_notify_timeout = 200;
- registry->queue_handler_id = 0;
-
- dbus_error_init (&error);
- registry->droute.bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
- if (!registry->droute.bus)
- {
- g_warning("Couldn't connect to dbus: %s\n", error.message);
- return;
- }
- registry->droute.user_data = registry;
- spi_registry_initialize_registry_interface (®istry->droute);
- spi_registry_initialize_desktop_interface (®istry->droute);
- spi_registry_initialize_dec_interface (®istry->droute);
- // todo: initialize accessible and component interfaces, for desktop?
- if (!dbus_connection_try_register_fallback (registry->droute.bus, "/org/freedesktop/atspi", &droute_vtable, ®istry->droute, &error))
- {
- g_warning("Couldn't register droute.\n");
- }
- dbus_connection_setup_with_g_main(registry->droute.bus, g_main_context_default());
-
- // TODO: decide whether focus_object is still relevant
- registry->desktop = spi_desktop_new ();
- /* Register callback notification for application addition and removal */
- g_signal_connect (G_OBJECT (registry->desktop),
- "application_added",
- G_CALLBACK (desktop_add_application),
- registry);
-
- g_signal_connect (G_OBJECT (registry->desktop),
- "application_removed",
- G_CALLBACK (desktop_remove_application),
- registry);
-
- registry->de_controller = spi_device_event_controller_new (registry);
-}
-
SpiRegistry *
-spi_registry_new (void)
+spi_registry_new (DBusConnection *bus)
{
- SpiRegistry *retval = g_object_new (SPI_REGISTRY_TYPE, NULL);
- return retval;
-}
+ SpiRegistry *reg = g_object_new (SPI_REGISTRY_TYPE, NULL);
-static DRouteMethod methods[] =
-{
- { impl_accessibility_registry_register_application , "registerApplication" },
- { impl_accessibility_registry_deregister_application, "deregisterApplication" },
- { impl_accessibility_registry_get_desktop_count, "getDesktopCount" },
- { impl_accessibility_registry_get_desktop, "getDesktop" },
- { impl_accessibility_registry_get_desktop_list, "getDesktopList" },
- { impl_accessibility_registry_get_device_event_controller, "getDeviceEventController" },
- { NULL, NULL }
-};
+ reg->bus = bus;
-void
-spi_registry_initialize_registry_interface (DRouteData * data)
-{
- droute_add_interface (data, "org.freedesktop.atspi.Registry", methods,
- NULL, NULL, NULL);
-};
+ dbus_connection_register_object_path(bus, SPI_DBUS_PATH_REGISTRY, ®_vtable, reg);
+
+ //dbus_bus_add_match (bus, app_reg_sig_match, NULL);
+ //dbus_bus_add_match (bus, app_dereg_sig_match, NULL);
+ dbus_bus_add_match (bus, app_sig_match_blank, NULL);
+ dbus_connection_add_filter (bus, signal_handler, reg, NULL);
+
+ return reg;
+}