2008-05-16 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.git] / registryd / registry.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2001, 2002 Sun Microsystems Inc.,
6  * Copyright 2001, 2002 Ximian, Inc.
7  *
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.
12  *
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.
17  *
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.
22  */
23
24 /* registry.c: the main accessibility service registry implementation */
25
26 #undef SPI_LISTENER_DEBUG
27 #undef SPI_DEBUG
28 #undef SPI_QUEUE_DEBUG
29
30 #include <config.h>
31 #ifdef SPI_DEBUG
32 #  include <stdio.h>
33 #endif
34
35 #include <spi-common/spi-dbus.h>
36
37 #include "registry.h"
38 #include "dbus/dbus-glib-lowlevel.h"
39
40 /* Our parent GObject type  */
41 #define PARENT_TYPE G_OBJECT_TYPE
42
43 int _dbg = 0;
44
45 typedef enum {
46   ETYPE_FOCUS,
47   ETYPE_OBJECT,
48   ETYPE_PROPERTY,
49   ETYPE_WINDOW,
50   ETYPE_TOOLKIT,
51   ETYPE_KEYBOARD,
52   ETYPE_MOUSE,
53   ETYPE_LAST_DEFINED
54 } EventTypeCategory;
55
56 typedef struct {
57   const char *event_name;
58   EventTypeCategory type_cat;
59   GQuark major;  /* from string segment[1] */
60   GQuark minor;  /* from string segment[1]+segment[2] */
61   GQuark detail; /* from string segment[3] (not concatenated) */
62 } EventTypeStruct;
63
64 G_DEFINE_TYPE(SpiRegistry, spi_registry, G_TYPE_OBJECT)
65
66 static void
67 spi_registry_set_debug (const char *debug_flag_string)
68 {
69   if (debug_flag_string) 
70     _dbg = (int) g_ascii_strtod (debug_flag_string, NULL);
71 }
72
73 static void emit(SpiRegistry *registry, const char *name, int first_type, ...)
74 {
75   va_list arg;
76
77   va_start(arg, first_type);
78   spi_dbus_emit_valist(registry->droute.bus, SPI_DBUS_PATH_REGISTRY, SPI_DBUS_INTERFACE_REGISTRY, name, first_type, arg);
79   va_end(arg);
80 }
81
82 static void
83 desktop_add_application (SpiDesktop *desktop,
84                          guint index, gpointer data)
85 {
86   SpiRegistry *registry = SPI_REGISTRY (data);
87   const char *name = g_list_nth_data(desktop->applications, index);
88   
89   emit(registry, "ApplicationAdd", DBUS_TYPE_UINT32, &index, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
90 }
91
92
93
94 static void
95 desktop_remove_application (SpiDesktop *desktop,
96                             guint index, gpointer data)
97 {
98   SpiRegistry *registry = SPI_REGISTRY (data);
99   const char *name = g_list_nth_data(desktop->applications, index);
100   
101   emit(registry, "ApplicationRemove", DBUS_TYPE_UINT32, &index, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
102 }
103
104
105 static void
106 spi_registry_object_finalize (GObject *object)
107 {
108   DBG (1, g_warning ("spi_registry_object_finalize called\n"));
109   g_object_unref (SPI_REGISTRY (object)->de_controller);
110
111   G_OBJECT_CLASS (spi_registry_parent_class)->finalize (object);
112 }
113
114 /**
115  * registerApplication:
116  * @application: a reference to the requesting @Application
117  * return values: void
118  *
119  * Register a new application with the accessibility broker.
120  *
121  **/
122 static DBusMessage *
123 impl_accessibility_registry_register_application (DBusConnection *bus, DBusMessage *message, void *user_data)
124 {
125   SpiRegistry *registry = SPI_REGISTRY (user_data);
126   const char *application = dbus_message_get_sender (message);
127
128 #ifdef SPI_DEBUG
129   fprintf (stderr, "registering app %s\n", application);
130 #endif
131   spi_desktop_add_application (registry->desktop, application);
132
133   /*
134    * TODO: change the implementation below to a WM-aware one;
135    * e.g. don't add all apps to the SpiDesktop
136    */
137   return dbus_message_new_method_return (message);
138 }
139
140 /**
141  * deregisterApplication:
142  * @application: a reference to the @Application
143  * to be deregistered.
144  * return values: void
145  *
146  * De-register an application previously registered with the broker.
147  *
148  **/
149 static DBusMessage *
150 impl_accessibility_registry_deregister_application (DBusConnection *bus, DBusMessage *message, void *user_data)
151 {
152   SpiRegistry *registry = SPI_REGISTRY (user_data);
153   const char *application = dbus_message_get_sender (message);
154
155   spi_desktop_remove_application (registry->desktop, application);
156
157 #ifdef SPI_DEBUG
158   fprintf (stderr, "de-registered app %s\n", application);
159 #endif
160   return dbus_message_new_method_return (message);
161 }
162
163 /**
164  * getDesktopCount:
165  * return values: a short integer indicating the current number of
166  * @Desktops.
167  *
168  * Get the current number of desktops.
169  *
170  **/
171 static DBusMessage *
172 impl_accessibility_registry_get_desktop_count (DBusConnection *bus, DBusMessage *message, void *user_data)
173 {
174   dbus_int16_t n_desktops = 1;
175   DBusMessage *reply;
176
177   /* TODO: implement support for multiple virtual desktops */
178   reply = dbus_message_new_method_return (message);
179   if (reply)
180   {
181     dbus_message_append_args (reply, DBUS_TYPE_INT16, &n_desktops, DBUS_TYPE_INVALID);
182   }
183   return reply;
184 }
185
186 /**
187  * getDesktop:
188  * @n: the index of the requested @Desktop.
189  * return values: a reference to the requested @Desktop.
190  *
191  * Get the nth accessible desktop.
192  *
193  **/
194 static DBusMessage *
195 impl_accessibility_registry_get_desktop (DBusConnection *bus, DBusMessage *message, void *user_data)
196 {
197   DBusError error;
198   dbus_int16_t n;
199   const char *path;
200   DBusMessage *reply;
201
202   /* TODO: implement support for multiple virtual desktops */
203   dbus_error_init (&error);
204   if (!dbus_message_get_args (message, &error, DBUS_TYPE_INT16, &n, DBUS_TYPE_INVALID))
205   {
206     return spi_dbus_general_error (message);
207   }
208   path = (n == 0? SPI_DBUS_PATH_DESKTOP: "/");
209   reply = dbus_message_new_method_return (message);
210   if (reply)
211   {
212     dbus_message_append_args (reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID);
213   }
214   return reply;
215 }
216
217
218 /**
219  * getDesktopList:
220  * return values: a sequence containing references to
221  * the @Desktops.
222  *
223  * Get a list of accessible desktops.
224  *
225  **/
226 static DBusMessage *
227 impl_accessibility_registry_get_desktop_list (DBusConnection *bus, DBusMessage *message, void *user_data)
228 {
229   DBusMessage *reply;
230   DBusMessageIter iter, iter_array;
231   const char *path = SPI_DBUS_PATH_DESKTOP;
232
233   reply = dbus_message_new_method_return (message);
234   if (!reply) return NULL;
235   dbus_message_iter_init_append(reply, &iter);
236   if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "o", &iter_array)) goto oom;
237   dbus_message_iter_append_basic(&iter_array, DBUS_TYPE_STRING, &path);
238   if (!dbus_message_iter_close_container (&iter, &iter_array)) goto oom;
239   return reply;
240 oom:
241   // TODO: handle out-of-memory
242   return reply;
243 }
244
245
246 static DBusMessage *
247 impl_accessibility_registry_get_device_event_controller (DBusConnection *bus, DBusMessage *message, void *user_data)
248 {
249   DBusMessage *reply;
250   const char *path = SPI_DBUS_PATH_DEC;
251
252   reply = dbus_message_new_method_return (message);
253   if (reply)
254   {
255     dbus_message_append_args (reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID);
256   }
257   return reply;
258 }
259
260
261 static void
262 spi_registry_class_init (SpiRegistryClass *klass)
263 {
264   GObjectClass * object_class = (GObjectClass *) klass;
265
266   spi_registry_parent_class = g_type_class_ref (G_TYPE_OBJECT);
267   
268   object_class->finalize = spi_registry_object_finalize;
269 }
270
271 static DBusObjectPathVTable droute_vtable =
272 {
273   NULL,
274   &droute_message,
275   NULL, NULL, NULL, NULL
276 };
277
278 static void
279 spi_registry_init (SpiRegistry *registry)
280 {
281   DBusError error;
282
283   spi_registry_set_debug (g_getenv ("AT_SPI_DEBUG"));
284   /*
285    * TODO: FIXME, this module makes the foolish assumptions that
286    * registryd uses the same display as the apps, and that the
287    * DISPLAY environment variable is set.
288    */
289   gdk_init (NULL, NULL);
290
291   registry->exit_notify_timeout = 200;
292   registry->queue_handler_id  = 0;
293
294   dbus_error_init (&error);
295   registry->droute.bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
296   if (!registry->droute.bus)
297   {
298     g_warning("Couldn't connect to dbus: %s\n", error.message);
299     return;
300   }
301   spi_registry_initialize_registry_interface (&registry->droute);
302   spi_registry_initialize_desktop_interface (&registry->droute);
303   spi_registry_initialize_dec_interface (&registry->droute);
304   // todo: initialize accessible and component interfaces, for desktop?
305   if (!dbus_connection_try_register_fallback (registry->droute.bus, "/org/freedesktop/atspi", &droute_vtable, &registry->droute, &error))
306   {
307     g_warning("Couldn't register droute.\n");
308   }
309   dbus_connection_setup_with_g_main(registry->droute.bus, g_main_context_default());
310
311   // TODO: decide whether focus_object is still relevant
312   registry->desktop = spi_desktop_new ();
313   /* Register callback notification for application addition and removal */
314   g_signal_connect (G_OBJECT (registry->desktop),
315                     "application_added",
316                     G_CALLBACK (desktop_add_application),
317                     registry);
318
319   g_signal_connect (G_OBJECT (registry->desktop),
320                     "application_removed",
321                     G_CALLBACK (desktop_remove_application),
322                     registry);
323
324   registry->de_controller = spi_device_event_controller_new (registry);
325 }
326
327 SpiRegistry *
328 spi_registry_new (void)
329 {
330   SpiRegistry *retval = g_object_new (SPI_REGISTRY_TYPE, NULL);
331   return retval;
332 }
333
334 static DRouteMethod methods[] =
335 {
336   { impl_accessibility_registry_register_application , "registerApplication" },
337   { impl_accessibility_registry_deregister_application, "deregisterApplication" },
338   { impl_accessibility_registry_get_desktop_count, "getDesktopCount" },
339   { impl_accessibility_registry_get_desktop, "getDesktop" },
340   { impl_accessibility_registry_get_desktop_list, "getDesktopList" },
341   { impl_accessibility_registry_get_device_event_controller, "getDeviceEventController" },
342   { NULL, NULL }
343 };
344
345 void
346 spi_registry_initialize_registry_interface (DRouteData * data)
347 {
348   droute_add_interface (data, "org.freedesktop.atspi.Registry", methods,
349                         NULL, NULL, NULL);
350 };