ApplicationAdd -> applicationAdd
[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 SpiDesktopApplication *app = g_list_nth_data(desktop->applications, index);
88   
89   emit(registry, "applicationAdd", DBUS_TYPE_UINT32, &index, DBUS_TYPE_STRING, &app->bus_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   SpiDesktopApplication *app = g_list_nth_data(desktop->applications, index);
100   
101   spi_dbus_remove_disconnect_match (registry->droute.bus, app->bus_name);
102   emit(registry, "applicationRemove", DBUS_TYPE_UINT32, &index, DBUS_TYPE_STRING, &app->bus_name, DBUS_TYPE_INVALID);
103 }
104
105
106 static void
107 spi_registry_object_finalize (GObject *object)
108 {
109   DBG (1, g_warning ("spi_registry_object_finalize called\n"));
110   g_object_unref (SPI_REGISTRY (object)->de_controller);
111
112   G_OBJECT_CLASS (spi_registry_parent_class)->finalize (object);
113 }
114
115 /**
116  * registerApplication:
117  * @application: a reference to the requesting @Application
118  * return values: void
119  *
120  * Register a new application with the accessibility broker.
121  *
122  **/
123 static DBusMessage *
124 impl_accessibility_registry_register_application (DBusConnection *bus, DBusMessage *message, void *user_data)
125 {
126   SpiRegistry *registry = SPI_REGISTRY (user_data);
127   const char *application = dbus_message_get_sender (message);
128
129 #ifdef SPI_DEBUG
130   fprintf (stderr, "registering app %s\n", application);
131 #endif
132   spi_desktop_add_application (registry->desktop, application);
133
134   spi_dbus_add_disconnect_match (registry->droute.bus, application);
135
136   /*
137    * TODO: change the implementation below to a WM-aware one;
138    * e.g. don't add all apps to the SpiDesktop
139    */
140   return dbus_message_new_method_return (message);
141 }
142
143 /**
144  * deregisterApplication:
145  * @application: a reference to the @Application
146  * to be deregistered.
147  * return values: void
148  *
149  * De-register an application previously registered with the broker.
150  *
151  **/
152 static DBusMessage *
153 impl_accessibility_registry_deregister_application (DBusConnection *bus, DBusMessage *message, void *user_data)
154 {
155   SpiRegistry *registry = SPI_REGISTRY (user_data);
156   const char *application = dbus_message_get_sender (message);
157
158   spi_desktop_remove_application (registry->desktop, application);
159
160 #ifdef SPI_DEBUG
161   fprintf (stderr, "de-registered app %s\n", application);
162 #endif
163   return dbus_message_new_method_return (message);
164 }
165
166 /**
167  * getDesktopCount:
168  * return values: a short integer indicating the current number of
169  * @Desktops.
170  *
171  * Get the current number of desktops.
172  *
173  **/
174 static DBusMessage *
175 impl_accessibility_registry_get_desktop_count (DBusConnection *bus, DBusMessage *message, void *user_data)
176 {
177   dbus_int16_t n_desktops = 1;
178   DBusMessage *reply;
179
180   /* TODO: implement support for multiple virtual desktops */
181   reply = dbus_message_new_method_return (message);
182   if (reply)
183   {
184     dbus_message_append_args (reply, DBUS_TYPE_INT16, &n_desktops, DBUS_TYPE_INVALID);
185   }
186   return reply;
187 }
188
189 /**
190  * getDesktop:
191  * @n: the index of the requested @Desktop.
192  * return values: a reference to the requested @Desktop.
193  *
194  * Get the nth accessible desktop.
195  *
196  **/
197 static DBusMessage *
198 impl_accessibility_registry_get_desktop (DBusConnection *bus, DBusMessage *message, void *user_data)
199 {
200   DBusError error;
201   dbus_int16_t n;
202   const char *path;
203   DBusMessage *reply;
204
205   /* TODO: implement support for multiple virtual desktops */
206   dbus_error_init (&error);
207   if (!dbus_message_get_args (message, &error, DBUS_TYPE_INT16, &n, DBUS_TYPE_INVALID))
208   {
209     return spi_dbus_general_error (message);
210   }
211   path = (n == 0? SPI_DBUS_PATH_DESKTOP: "/");
212   reply = dbus_message_new_method_return (message);
213   if (reply)
214   {
215     dbus_message_append_args (reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID);
216   }
217   return reply;
218 }
219
220
221 /**
222  * getDesktopList:
223  * return values: a sequence containing references to
224  * the @Desktops.
225  *
226  * Get a list of accessible desktops.
227  *
228  **/
229 static DBusMessage *
230 impl_accessibility_registry_get_desktop_list (DBusConnection *bus, DBusMessage *message, void *user_data)
231 {
232   DBusMessage *reply;
233   DBusMessageIter iter, iter_array;
234   const char *path = SPI_DBUS_PATH_DESKTOP;
235
236   reply = dbus_message_new_method_return (message);
237   if (!reply) return NULL;
238   dbus_message_iter_init_append(reply, &iter);
239   if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "o", &iter_array)) goto oom;
240   dbus_message_iter_append_basic(&iter_array, DBUS_TYPE_OBJECT_PATH, &path);
241   if (!dbus_message_iter_close_container (&iter, &iter_array)) goto oom;
242   return reply;
243 oom:
244   // TODO: handle out-of-memory
245   return reply;
246 }
247
248
249 static DBusMessage *
250 impl_accessibility_registry_get_device_event_controller (DBusConnection *bus, DBusMessage *message, void *user_data)
251 {
252   DBusMessage *reply;
253   const char *path = SPI_DBUS_PATH_DEC;
254
255   reply = dbus_message_new_method_return (message);
256   if (reply)
257   {
258     dbus_message_append_args (reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID);
259   }
260   return reply;
261 }
262
263
264 static void
265 spi_registry_class_init (SpiRegistryClass *klass)
266 {
267   GObjectClass * object_class = (GObjectClass *) klass;
268
269   spi_registry_parent_class = g_type_class_ref (G_TYPE_OBJECT);
270   
271   object_class->finalize = spi_registry_object_finalize;
272 }
273
274 static DBusObjectPathVTable droute_vtable =
275 {
276   NULL,
277   &droute_message,
278   NULL, NULL, NULL, NULL
279 };
280
281 DBusHandlerResult
282 disconnect_watch (DBusConnection *bus, DBusMessage *message, void *user_data)
283 {
284   SpiRegistry *registry = SPI_REGISTRY (user_data);
285   const char *name, *old, *new;
286
287   if (!dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged"))
288   {
289     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
290   }
291   if (!dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID))
292   {
293     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
294   }
295   if (*old != '\0' && *new == '\0')
296   {
297     spi_desktop_remove_application (registry->desktop, old);
298     spi_remove_device_listeners (registry->de_controller, old);
299   }
300   return DBUS_HANDLER_RESULT_HANDLED;
301 }
302
303 static void
304 spi_registry_init (SpiRegistry *registry)
305 {
306   DBusError error;
307
308   spi_registry_set_debug (g_getenv ("AT_SPI_DEBUG"));
309   /*
310    * TODO: FIXME, this module makes the foolish assumptions that
311    * registryd uses the same display as the apps, and that the
312    * DISPLAY environment variable is set.
313    */
314   gdk_init (NULL, NULL);
315
316   registry->exit_notify_timeout = 200;
317   registry->queue_handler_id  = 0;
318
319   dbus_error_init (&error);
320   registry->droute.bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
321   if (!registry->droute.bus)
322   {
323     g_warning("Couldn't connect to dbus: %s\n", error.message);
324     return;
325   }
326   registry->droute.user_data = registry;
327   spi_registry_initialize_registry_interface (&registry->droute);
328   spi_registry_initialize_desktop_interface (&registry->droute);
329   spi_registry_initialize_dec_interface (&registry->droute);
330   // todo: initialize accessible and component interfaces, for desktop?
331   if (!dbus_connection_try_register_fallback (registry->droute.bus, "/org/freedesktop/atspi", &droute_vtable, &registry->droute, &error))
332   {
333     g_warning("Couldn't register droute.\n");
334   }
335   dbus_connection_setup_with_g_main(registry->droute.bus, g_main_context_default());
336
337   // TODO: decide whether focus_object is still relevant
338   registry->desktop = spi_desktop_new ();
339   /* Register callback notification for application addition and removal */
340   g_signal_connect (G_OBJECT (registry->desktop),
341                     "application_added",
342                     G_CALLBACK (desktop_add_application),
343                     registry);
344
345   g_signal_connect (G_OBJECT (registry->desktop),
346                     "application_removed",
347                     G_CALLBACK (desktop_remove_application),
348                     registry);
349
350   registry->de_controller = spi_device_event_controller_new (registry);
351
352   dbus_connection_add_filter (registry->droute.bus, disconnect_watch, registry, NULL);
353 }
354
355 SpiRegistry *
356 spi_registry_new (void)
357 {
358   SpiRegistry *retval = g_object_new (SPI_REGISTRY_TYPE, NULL);
359   return retval;
360 }
361
362 static DRouteMethod methods[] =
363 {
364   { impl_accessibility_registry_register_application , "registerApplication" },
365   { impl_accessibility_registry_deregister_application, "deregisterApplication" },
366   { impl_accessibility_registry_get_desktop_count, "getDesktopCount" },
367   { impl_accessibility_registry_get_desktop, "getDesktop" },
368   { impl_accessibility_registry_get_desktop_list, "getDesktopList" },
369   { impl_accessibility_registry_get_device_event_controller, "getDeviceEventController" },
370   { NULL, NULL }
371 };
372
373 void
374 spi_registry_initialize_registry_interface (DRouteData * data)
375 {
376   droute_add_interface (data, "org.freedesktop.atspi.Registry", methods,
377                         NULL, NULL, NULL);
378 };