2008-09-25 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.git] / registryd / desktop.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., 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 /* desktop.c: implements SpiDesktop.idl */
25
26 #include <config.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <atk/atkcomponent.h>
30 #include <gdk/gdkscreen.h>
31 #include <gdk/gdkx.h>
32
33 #include <spi-common/spi-dbus.h>
34
35 #include "desktop.h"
36 #include "registry.h"
37
38 G_DEFINE_TYPE(SpiDesktop, spi_desktop, G_TYPE_OBJECT)
39
40 /* SpiDesktop signals */
41 enum {
42   APPLICATION_ADDED,
43   APPLICATION_REMOVED,  
44 LAST_SIGNAL
45 };
46 static guint spi_desktop_signals[LAST_SIGNAL];
47
48
49 /* Our parent Gtk object type */
50 #define PARENT_TYPE SPI_ACCESSIBLE_TYPE
51
52 static gboolean exiting = FALSE;
53
54 /* A pointer to our parent object class */
55 static GObjectClass *parent_class;
56
57 #define SPI_TYPE_ATK_DESKTOP            (spi_atk_desktop_get_type ())
58 #define SPI_ATK_DESKTOP(o)              (G_TYPE_CHECK_INSTANCE_CAST ((o), SPI_TYPE_ATK_DESKTOP, SpiAtkDesktop))
59 #define SPI_IS_ATK_DESKTOP(o)           (G_TYPE_CHECK_INSTANCE_TYPE ((o), SPI_TYPE_ATK_DESKTOP))
60
61 typedef struct {
62         AtkObject parent;
63
64         GdkScreen *screen;
65 } SpiAtkDesktop;
66
67 typedef struct {
68         AtkObjectClass parent;
69 } SpiAtkDesktopClass;
70
71 static void spi_atk_desktop_init (SpiAtkDesktop *desktop);
72 static void atk_component_interface_init (AtkComponentIface *iface);
73 static void spi_atk_desktop_get_extents  (AtkComponent    *component,
74                                           gint            *x,
75                                           gint            *y,
76                                           gint            *width,
77                                           gint            *height,
78                                           AtkCoordType    coord_type);
79
80 static GType 
81 spi_atk_desktop_get_type (void)
82 {
83   static GType type = 0;
84
85   if (!type)
86     {
87       static const GTypeInfo typeInfo =
88       {
89         sizeof (SpiAtkDesktopClass),
90         (GBaseInitFunc) NULL,
91         (GBaseFinalizeFunc) NULL,
92         (GClassInitFunc) NULL,
93         (GClassFinalizeFunc) NULL,
94         NULL,
95         sizeof (SpiAtkDesktop),
96         0,
97         (GInstanceInitFunc) spi_atk_desktop_init,
98       } ;
99       static const GInterfaceInfo atk_component_info =
100         {
101         (GInterfaceInitFunc) atk_component_interface_init,
102         (GInterfaceFinalizeFunc) NULL,
103         NULL
104       };
105
106       type = g_type_register_static (ATK_TYPE_OBJECT,
107                                      "SpiAtkDesktop", &typeInfo, 0);
108       g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
109                                    &atk_component_info);
110     }
111   return type;
112 }
113
114 static void 
115 spi_atk_desktop_init (SpiAtkDesktop *desktop)
116 {
117   GdkDisplay *display;
118
119   atk_object_set_name (ATK_OBJECT (desktop), "main");
120   display = gdk_x11_lookup_xdisplay (GDK_DISPLAY ());
121   desktop->screen = gdk_display_get_default_screen (display);
122 }
123
124 static void
125 atk_component_interface_init (AtkComponentIface *iface)
126 {
127   g_return_if_fail (iface != NULL);
128
129   iface->get_extents = spi_atk_desktop_get_extents;
130 }
131
132 static void 
133 spi_atk_desktop_get_extents (AtkComponent *component,
134                              gint         *x,
135                              gint         *y,
136                              gint         *width,
137                              gint         *height,
138                              AtkCoordType coord_type)
139 {
140   SpiAtkDesktop *desktop;
141
142   g_return_if_fail (SPI_IS_ATK_DESKTOP (component));
143   desktop = SPI_ATK_DESKTOP (component);
144   *x = 0;
145   *y = 0;
146   *width = gdk_screen_get_width (desktop->screen);
147   *height = gdk_screen_get_height (desktop->screen);
148 }
149
150 static void
151 spi_desktop_init (SpiDesktop *desktop)
152 {
153   desktop->applications = NULL;
154 }
155
156 static void
157 spi_desktop_dispose (GObject *object)
158 {
159   SpiDesktop *desktop = (SpiDesktop *) object;
160
161   while (desktop->applications)
162     {
163       SpiDesktopApplication *app = desktop->applications->data;
164       g_assert (app != NULL);
165       spi_desktop_remove_application (desktop, app->bus_name);
166     }
167
168   G_OBJECT_CLASS (parent_class)->dispose (object); 
169 }
170
171 static dbus_bool_t
172 impl_desktop_get_child_count (const char *path, DBusMessageIter * iter,
173                      void *user_data)
174 {
175   SpiDesktop *desktop = SPI_REGISTRY(user_data)->desktop;
176
177   if (desktop->applications)
178     {
179       return droute_return_v_int32(iter, g_list_length (desktop->applications));
180     }
181   else
182     {
183       return droute_return_v_int32(iter, 0);
184     }
185 }
186
187 static DBusMessage *
188 impl_desktop_get_child_at_index (DBusConnection *bus, DBusMessage *message, void *user_data)
189 {
190   SpiDesktop *desktop = SPI_REGISTRY(user_data)->desktop;
191   DBusError error;
192   dbus_int32_t index;
193   SpiDesktopApplication *app;
194   const char *bus_name;
195   DBusMessage *reply;
196
197   dbus_error_init (&error);
198   if (!dbus_message_get_args (message, &error, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID))
199   {
200     return spi_dbus_general_error (message);
201   }
202   app = g_list_nth_data (desktop->applications, index);
203   bus_name = (app? app->bus_name: "");
204
205   reply = dbus_message_new_method_return (message);
206   if (reply)
207     {
208       dbus_message_append_args (reply, DBUS_TYPE_STRING, &bus_name, DBUS_TYPE_INVALID);
209     }
210
211   return reply;
212 }
213
214 static DBusMessage *
215 impl_desktop_get_children (DBusConnection *bus, DBusMessage *message, void *user_data)
216 {
217   SpiDesktop *desktop = SPI_REGISTRY(user_data)->desktop;
218   DBusError error;
219   gint count;
220   gint i;
221   SpiDesktopApplication *app;
222   DBusMessage *reply;
223   DBusMessageIter iter, iter_array;
224
225   reply = dbus_message_new_method_return (message);
226   if (!reply) return NULL;
227   dbus_message_iter_init_append (reply, &iter);
228   if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s", &iter_array))
229   {
230     goto oom;
231   }
232   count = g_list_length (desktop->applications);
233   for (i = 0; i < count; i++)
234   {
235     app = g_list_nth_data (desktop->applications, i);
236     if (!app)
237     {
238       g_warning ("Null app\n");
239       continue;
240     }
241     dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_STRING, &app->bus_name);
242   }
243   if (!dbus_message_iter_close_container (&iter, &iter_array))
244   {
245     goto oom;
246   }
247   return reply;
248 oom:
249   // TODO: Handle out of memory
250   return reply;
251 }
252
253 static dbus_bool_t
254 impl_get_name (const char *path, DBusMessageIter * iter, void *user_data)
255 {
256   if (strcmp (path, SPI_DBUS_PATH_DESKTOP) != 0)
257     return FALSE;
258   // TODO: call atk_object_get_name
259   return droute_return_v_string (iter, "main");
260 }
261
262 static void
263 spi_desktop_exiting (void)
264 {
265   exiting = TRUE;
266 }
267
268 static void
269 spi_desktop_class_init (SpiDesktopClass *klass)
270 {
271   GObjectClass * object_class = (GObjectClass *) klass;
272
273   object_class->dispose = spi_desktop_dispose;
274   
275   parent_class = g_type_class_ref (G_TYPE_OBJECT);
276
277   spi_desktop_signals[APPLICATION_ADDED] =
278     g_signal_new ("application_added",
279                   G_TYPE_FROM_CLASS (klass),
280                   G_SIGNAL_RUN_LAST,
281                   G_STRUCT_OFFSET (SpiDesktopClass, application_added),
282                   NULL, NULL,
283                   g_cclosure_marshal_VOID__UINT,
284                   G_TYPE_NONE,
285                   1, G_TYPE_UINT);
286   spi_desktop_signals[APPLICATION_REMOVED] =
287     g_signal_new ("application_removed",
288                   G_TYPE_FROM_CLASS (klass),
289                   G_SIGNAL_RUN_LAST,
290                   G_STRUCT_OFFSET (SpiDesktopClass, application_removed),
291                   NULL, NULL,
292                   g_cclosure_marshal_VOID__UINT,
293                   G_TYPE_NONE,
294                   1, G_TYPE_UINT);
295   g_atexit (spi_desktop_exiting);
296 }
297
298 SpiDesktop *
299 spi_desktop_new (void)
300 {
301   return g_object_new (SPI_DESKTOP_TYPE, NULL);
302 }
303
304 static void
305 abnormal_application_termination (gpointer object, SpiDesktopApplication *app)
306 {
307   g_return_if_fail (SPI_IS_DESKTOP (app->desktop));
308
309   if (!exiting)
310     spi_desktop_remove_application (app->desktop, app->bus_name);
311 }
312
313 void
314 spi_desktop_add_application (SpiDesktop *desktop,
315                              const char *application)
316 {
317   SpiDesktopApplication       *app;
318
319   g_return_if_fail (SPI_IS_DESKTOP (desktop));
320
321   app = g_new (SpiDesktopApplication, 1);
322   app->desktop = desktop;
323   app->bus_name = g_strdup (application);
324
325       desktop->applications = g_list_append (desktop->applications, app);
326
327   // TODO: Listen for termination, and call abnormal_application_termination
328
329   g_signal_emit (G_OBJECT (desktop),
330                  spi_desktop_signals[APPLICATION_ADDED], 0,
331                  g_list_index (desktop->applications, app));
332 }
333
334 void
335 spi_desktop_remove_application (SpiDesktop *desktop,
336                                 const char *bus_name)
337 {
338   guint idx;
339   GList *l;
340   SpiDesktopApplication *app;
341
342   g_return_if_fail (  bus_name != NULL);
343   g_return_if_fail (SPI_IS_DESKTOP (desktop));
344
345   idx = 0;
346   for (l = desktop->applications; l; l = l->next)
347     {
348       app = (SpiDesktopApplication *) l->data;
349
350       if (!strcmp(app->bus_name, bus_name))
351         {
352           break;
353         }
354       idx++;
355     }
356
357   if (!l) return;
358
359   g_signal_emit (G_OBJECT (desktop), spi_desktop_signals[APPLICATION_REMOVED], 0, idx);
360
361   desktop->applications = g_list_delete_link (desktop->applications, l);
362
363   g_free (app->bus_name);
364   g_free (app);
365 }
366
367 static DRouteMethod methods_desktop[] =
368 {
369   { impl_desktop_get_child_at_index, "getChildAtIndex" },
370   { impl_desktop_get_children, "getChildren" },
371   { NULL, NULL }
372 };
373
374 static DRouteProperty properties_desktop[] =
375 {
376   { impl_desktop_get_child_count, NULL, "getChildCount" },
377   { NULL, NULL, NULL }
378 };
379
380 static DRouteProperty properties_accessible[] =
381 {
382   { impl_get_name, "getName" },
383   { NULL, NULL }
384 };
385
386 void
387 spi_registry_initialize_desktop_interface (DRouteData * data)
388 {
389   droute_add_interface (data, SPI_DBUS_INTERFACE_DESKTOP, methods_desktop,
390                         properties_desktop, NULL, NULL);
391   droute_add_interface (data, SPI_DBUS_INTERFACE_ACCESSIBLE, NULL,
392                         properties_accessible, NULL, NULL);
393 };