Merge branch 'master' of git+ssh://git.codethink.co.uk/git/atspi-dbus
[platform/core/uifw/at-spi2-atk.git] / atk-adaptor / bridge.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, 2003 Sun Microsystems Inc.,
6  * Copyright 2001, 2002, 2003 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 #include "config.h"
25 #include "dbus/dbus-glib-lowlevel.h"
26
27 #include <X11/Xlib.h>
28 #include <X11/Xatom.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <atk/atk.h>
35 #include <atk/atkobject.h>
36 #include <atk/atknoopobject.h>
37 #include "accessible.h"
38 #include "bridge.h"
39 #include "atk-dbus.h"
40
41 #undef SPI_BRIDGE_DEBUG
42
43 #define DBG(a,b) if(_dbg>=(a))b
44
45 int _dbg = 0;
46 static const char *registry = NULL;
47 SpiAppData *this_app = NULL;
48 static gboolean registry_died = FALSE;
49 static gint toplevels = 0;
50 static gboolean exiting = FALSE;
51 static AtkMisc *misc = NULL;
52 static gboolean during_init_shutdown = TRUE;
53
54 static const char *spi_atk_bridge_get_registry (void);
55 static char *device_event_controller = NULL;
56 static void     spi_atk_bridge_register_application    (const char *registry);
57 static gchar   *spi_atk_bridge_get_registry_ior        (void);
58 static gboolean spi_atk_bridge_do_registration         (void);
59 static void     spi_atk_bridge_toplevel_added          (AtkObject             *object,
60                                                         guint                 index,
61                                                         AtkObject             *child);
62 static void     spi_atk_bridge_toplevel_removed        (AtkObject             *object,
63                                                         guint                 index,
64                                                         AtkObject             *child);
65
66 static void     spi_atk_bridge_exit_func               (void);
67 static void     spi_atk_tidy_windows                   (void);
68 static void     deregister_application                 (SpiAppData *app);
69 static void reinit_register_vars (void);
70
71 /* For automatic libgnome init */
72 extern void gnome_accessibility_module_init     (void);
73 extern void gnome_accessibility_module_shutdown (void);
74
75 static int     atk_bridge_initialized = FALSE;
76 static pid_t   atk_bridge_pid = 0;
77
78 /*
79  *   These exported symbols are hooked by gnome-program
80  * to provide automatic module initialization and shutdown.
81  */
82 extern void gnome_accessibility_module_init     (void);
83 extern void gnome_accessibility_module_shutdown (void);
84
85 void
86 spi_atk_register_event_listeners(void);
87
88 void
89 spi_atk_deregister_event_listeners (void);
90
91 static gboolean
92 post_init (gpointer data)
93 {
94   during_init_shutdown = FALSE;
95   return FALSE;
96 }
97
98 static DBusObjectPathVTable droute_vtable =
99 {
100   NULL,
101   &droute_message,
102   NULL, NULL, NULL, NULL
103 };
104
105 static gchar* atspi_dbus_name;
106 static gboolean atspi_no_register; 
107
108 static GOptionEntry atspi_option_entries[] = 
109 {
110   {"atspi-dbus-name", 0, 0, G_OPTION_ARG_STRING, &atspi_dbus_name, "D-Bus bus name to register as", NULL},
111   {"atspi-no-register", 0, 0, G_OPTION_ARG_NONE, &atspi_no_register, "Do not register with Registry Daemon", NULL},
112   {NULL}
113 };
114
115 static SpiAppData *
116 spi_app_init (AtkObject *root, gint *argc, gchar **argv[])
117 {
118   GOptionContext *opt;
119   SpiAppData *ad = g_new0(SpiAppData, 1);
120   GError *err = NULL;
121   DBusError error;
122   int i;
123
124   opt = g_option_context_new(NULL);
125   g_option_context_add_main_entries(opt, atspi_option_entries, NULL);
126   g_option_context_set_ignore_unknown_options(opt, TRUE);
127   if (!g_option_context_parse(opt, argc, argv, &err))
128       g_warning("Option parsing failed: %s\n", err->message);
129
130   dbus_error_init(&error);
131   ad->root = root;
132   ad->droute.bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
133   g_print("D-Bus unique name is : %s\n", dbus_bus_get_unique_name(ad->droute.bus));
134
135   if (!ad->droute.bus)
136   {
137     g_warning("Couldn't connect to dbus: %s\n", error.message);
138     free(ad);
139     return NULL;
140   }
141   if (atspi_dbus_name != NULL && dbus_bus_request_name(ad->droute.bus, 
142                                                        atspi_dbus_name,
143                                                        0,
144                                                        &error))
145   {
146     g_print("\nRecieved D-Bus name - %s\n", atspi_dbus_name);
147   }
148   spi_register_tree_object(ad->droute.bus, &ad->droute, "/org/freedesktop/atspi/tree");
149   if (!dbus_connection_try_register_fallback (ad->droute.bus, 
150                                               "/org/freedesktop/atspi/accessible", 
151                                               &droute_vtable, 
152                                               &ad->droute, 
153                                               &error))
154   {
155     g_warning("Couldn't register droute.\n");
156     free(ad);
157     return NULL;
158   }
159
160   dbus_connection_setup_with_g_main(ad->droute.bus, g_main_context_default());
161
162   atk_dbus_initialize (&ad->droute);
163   return ad;
164 }
165
166 static int
167 atk_bridge_init (gint *argc, gchar **argv[])
168 {
169   const char *debug_env_string = g_getenv ("AT_SPI_DEBUG");
170   gchar *fname;
171   gboolean success = FALSE;
172
173   if (atk_bridge_initialized)
174     {
175       return 0;
176     }
177   atk_bridge_initialized = TRUE;
178   atk_bridge_pid = getpid ();
179
180   misc = atk_misc_get_instance();
181
182   if (g_getenv ("ATK_BRIDGE_REDIRECT_LOG"))
183     {
184       fname = g_strconcat ("/tmp/", g_get_prgname (), ".at-spi-log", NULL);
185       /* make sure we're not being redirected - security issue */
186       if (!g_file_test (fname, G_FILE_TEST_IS_SYMLINK))
187           freopen (fname, "w", stderr);
188       g_free (fname);
189     }
190
191   if (debug_env_string) 
192       _dbg = (int) g_ascii_strtod (debug_env_string, NULL);
193
194   /* Connect to dbus */
195   this_app = spi_app_init (atk_get_root (), argc, argv);
196
197   /*
198    * We only want to enable the bridge for top level
199    * applications, we detect bonobo components by seeing
200    * if they were activated with the intention of extracting
201    * an impl. by IID - very solid.
202    */
203 #ifdef WITH_BONOBO
204   // TODO: Figure out if this is still needed
205   if (bonobo_activation_iid_get ())
206 #else
207   if (0)
208 #endif
209     {
210       DBG (1, g_message ("Found Bonobo component\n"));
211       g_signal_connect (atk_get_root (), 
212                         "children-changed::add",
213                         (GCallback) spi_atk_bridge_toplevel_added, 
214                         NULL);
215       g_signal_connect (atk_get_root (), 
216                         "children-changed::remove",
217                         (GCallback) spi_atk_bridge_toplevel_removed, 
218                         NULL);
219       /* in this case we redefine 'success' to mean 'registry is present' */
220       success = (spi_atk_bridge_get_registry () != NULL);
221     }
222   else
223     {
224       success = spi_atk_bridge_do_registration ();
225     }
226   /*
227    * we must emit events even if we are not registered as a
228    * full-fledged app; See bugzilla #400709.
229    */
230   if (success) 
231     {
232       spi_atk_register_event_listeners ();
233     }
234   else
235     {
236       atk_bridge_initialized = FALSE;
237     }
238   g_idle_add (post_init, NULL);
239
240   return 0;
241 }
242
243 static gboolean
244 spi_atk_bridge_do_registration (void)
245 {
246   if (spi_atk_bridge_get_registry () == NULL)
247     {
248       g_warning ("Could not locate registry");
249       return FALSE;
250     }
251
252   /* Create the accessible application server object */
253   if (this_app == NULL)
254     this_app = spi_app_init (atk_get_root (), 0, NULL);
255
256   DBG (1, g_message ("About to register application\n"));
257
258   spi_atk_bridge_register_application (spi_atk_bridge_get_registry ());
259   
260   g_atexit (spi_atk_bridge_exit_func);
261
262   DBG (1, g_message ("Application registered & listening\n"));
263   return TRUE;
264 }
265
266 static void
267 spi_atk_bridge_toplevel_added (AtkObject *object,
268                                guint     index,
269                                AtkObject *child)
270 {
271   if (toplevels == 0)
272     {
273       spi_atk_bridge_do_registration ();
274     }
275   toplevels++;
276 }
277
278 static void
279 spi_atk_bridge_toplevel_removed (AtkObject *object,
280                                  guint     index,
281                                  AtkObject *child)
282 {
283   toplevels--;
284   if (toplevels == 0)
285     {
286       deregister_application (this_app);
287       reinit_register_vars ();
288     }
289   if (toplevels < 0)
290     {
291       g_warning ("More toplevels removed than added\n");
292       toplevels = 0;
293     }
294 }
295
296 static void
297 spi_atk_bridge_register_application (const char *registry)
298 {
299   DBusMessage *message, *reply;
300   DBusError error;
301
302   message = dbus_message_new_signal (SPI_DBUS_PATH_REGISTRY, SPI_DBUS_INTERFACE_TREE, "registerApplication");
303   dbus_error_init (&error);
304   dbus_connection_send (this_app->droute.bus, message, NULL);
305   if (error.message) g_print (error.message);
306   if (message) dbus_message_unref (message);
307 }
308
309 /* 
310  * Returns a 'canonicalized' value for DISPLAY,
311  * with the screen number stripped off if present.
312  */
313 static const gchar*
314 spi_display_name (void)
315 {
316     static const char *canonical_display_name = NULL;
317     if (!canonical_display_name)
318       {
319         const gchar *display_env = g_getenv ("AT_SPI_DISPLAY");
320         if (!display_env)
321           {
322             display_env = g_getenv ("DISPLAY");
323             if (!display_env || !display_env[0]) 
324                 canonical_display_name = ":0";
325             else
326               {
327                 gchar *display_p, *screen_p;
328                 canonical_display_name = g_strdup (display_env);
329                 display_p = strrchr (canonical_display_name, ':');
330                 screen_p = strrchr (canonical_display_name, '.');
331                 if (screen_p && display_p && (screen_p > display_p))
332                   {
333                     *screen_p = '\0';
334                   }
335               }
336           }
337         else
338           {
339             canonical_display_name = display_env;
340           }
341       }
342     return canonical_display_name;
343 }
344
345 static     Display *bridge_display = NULL;
346
347 static gchar *
348 spi_atk_bridge_get_registry_ior (void)
349 {
350      
351      Atom AT_SPI_IOR;
352      Atom actual_type;
353      int actual_format;
354      unsigned char *data = NULL;  
355      unsigned long nitems;
356      unsigned long leftover;
357      if (!bridge_display) 
358        bridge_display = XOpenDisplay (spi_display_name ());
359
360      AT_SPI_IOR = XInternAtom (bridge_display, "AT_SPI_IOR", False); 
361      XGetWindowProperty(bridge_display, 
362                         XDefaultRootWindow (bridge_display),
363                         AT_SPI_IOR, 0L, 
364                         (long)BUFSIZ, False, 
365                         (Atom) 31, &actual_type, &actual_format,
366                         &nitems, &leftover, &data);
367      if (data == NULL)
368           g_warning (_("AT_SPI_REGISTRY was not started at session startup."));
369      
370      return (gchar *) data;
371      
372 }
373
374 static const char *
375 spi_atk_bridge_get_registry (void)
376 {
377   // TODO: check for registry dying, as the old code attempted to do
378   return "org.freedesktop.atspi.registry";
379 }
380
381 static const char *
382 spi_atk_bridge_get_dec (void)
383 {
384   return "/dec";
385 }
386
387 int
388 gtk_module_init (gint *argc, gchar **argv[])
389 {
390         //printf("Pointer to argc %x %x\n", (short) ((argc && 0xffff0000) >> 16), (short) (argc && 0xffff));
391         return atk_bridge_init (argc, argv);
392 }
393
394 static void
395 deregister_application (SpiAppData *app)
396 {
397   const char *registry = spi_atk_bridge_get_registry ();
398   // todo: deregister
399 }
400
401 static void
402 spi_atk_bridge_exit_func (void)
403 {
404   SpiAppData *app = (SpiAppData *) this_app;
405
406   DBG (1, g_message ("exiting bridge\n"));
407
408   if (!app)
409     {
410       return;
411     }
412   if (atk_bridge_pid != getpid ())
413     {
414       _exit (0);
415     }
416
417   during_init_shutdown = TRUE;
418   exiting = TRUE;
419   /*
420    * Check whether we still have windows which have not been deleted.
421    */
422   spi_atk_tidy_windows ();
423   /*
424    *  FIXME: this may be incorrect for apps that do their own bonobo
425    *  shutdown, until we can explicitly shutdown to get the ordering
426    *  right.
427    */
428   if (!registry_died)
429     deregister_application (this_app);
430   this_app = NULL;
431   DBG (1, g_message ("bridge exit func complete.\n"));
432
433   if (g_getenv ("AT_BRIDGE_SHUTDOWN"))
434     {
435     }
436   if (bridge_display)
437     XCloseDisplay (bridge_display);
438 }
439
440 void
441 gnome_accessibility_module_init (void)
442 {
443   atk_bridge_init (0, NULL);
444
445   if (g_getenv ("AT_BRIDGE_SHUTDOWN"))
446     {
447         g_print("Atk Accessibility bridge initialized\n");
448     }
449 }
450
451 void
452 gnome_accessibility_module_shutdown (void)
453 {
454   int     i;
455   
456   if (!atk_bridge_initialized)
457     {
458       return;
459     }
460   during_init_shutdown = TRUE;
461   atk_bridge_initialized = FALSE;
462
463   if (g_getenv ("AT_BRIDGE_SHUTDOWN"))
464     {
465         g_print("Atk Accessibility bridge shutdown\n");
466     }
467
468   spi_atk_deregister_event_listeners();
469
470   deregister_application (this_app);
471   this_app = NULL;
472
473   misc = NULL;
474 }
475
476 static void
477 reinit_register_vars (void)
478 {
479   registry = NULL;
480   device_event_controller = NULL;
481   this_app = NULL;
482 }