1 /* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
3 * at-spi-bus-launcher: Manage the a11y bus as a child process
5 * Copyright 2011 Red Hat, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
35 #include <X11/Xatom.h>
38 //TODO: move to vconf/vconf-internal-setting-keys.h?
39 #define VCONFKEY_SETAPPL_ACCESSIBILITY_UNIVERSAL_SWITCH "db/setting/accessibility/universal-switch"
41 #define APP_CONTROL_OPERATION_SCREEN_READ "http://tizen.org/appcontrol/operation/read_screen"
42 #define APP_CONTROL_OPERATION_UNIVERSAL_SWITCH "http://tizen.org/appcontrol/operation/universal_switch"
46 //uncomment if you want debug
47 //#ifndef TIZEN_ENGINEER_MODE
48 //#define TIZEN_ENGINEER_MODE
54 #define LOG_TAG "ATSPI_BUS_LAUNCHER"
59 //uncomment this if you want log suring startup
60 //seems like dlog is not working at startup time
61 #define ATSPI_BUS_LAUNCHER_LOG_TO_FILE
63 #ifdef ATSPI_BUS_LAUNCHER_LOG_TO_FILE
68 #define LOGD(arg...) do {if (log_file) {fprintf(log_file, ##arg);fprintf(log_file, "\n"); fflush(log_file);}} while(0)
71 static gboolean _launch_process_repeat_until_success(gpointer user_data);
74 A11Y_BUS_STATE_IDLE = 0,
75 A11Y_BUS_STATE_READING_ADDRESS,
76 A11Y_BUS_STATE_RUNNING,
82 const char * app_control_operation;
83 const char * vconf_key;
90 gboolean launch_immediately;
91 gboolean a11y_enabled;
92 gboolean screen_reader_enabled;
93 GHashTable *client_watcher_id;
94 GDBusConnection *session_bus;
95 GSettings *a11y_schema;
96 GSettings *interface_schema;
98 A11yBusClient screen_reader;
99 A11yBusClient universal_switch;
101 GDBusProxy *client_proxy;
105 /* -1 == error, 0 == pending, > 0 == running */
107 char *a11y_bus_address;
109 char *a11y_launch_error_message;
112 static A11yBusLauncher *_global_app = NULL;
114 static const gchar introspection_xml[] =
116 " <interface name='org.a11y.Bus'>"
117 " <method name='GetAddress'>"
118 " <arg type='s' name='address' direction='out'/>"
121 "<interface name='org.a11y.Status'>"
122 "<property name='IsEnabled' type='b' access='readwrite'/>"
123 "<property name='ScreenReaderEnabled' type='b' access='readwrite'/>"
126 static GDBusNodeInfo *introspection_data = NULL;
129 respond_to_end_session (GDBusProxy *proxy)
131 GVariant *parameters;
133 parameters = g_variant_new ("(bs)", TRUE, "");
135 g_dbus_proxy_call (proxy,
136 "EndSessionResponse", parameters,
137 G_DBUS_CALL_FLAGS_NONE,
138 -1, NULL, NULL, NULL);
142 g_signal_cb (GDBusProxy *proxy,
145 GVariant *parameters,
148 A11yBusLauncher *app = user_data;
150 if (g_strcmp0 (signal_name, "QueryEndSession") == 0)
151 respond_to_end_session (proxy);
152 else if (g_strcmp0 (signal_name, "EndSession") == 0)
153 respond_to_end_session (proxy);
154 else if (g_strcmp0 (signal_name, "Stop") == 0)
155 g_main_loop_quit (app->loop);
159 client_proxy_ready_cb (GObject *source_object,
163 A11yBusLauncher *app = user_data;
164 GError *error = NULL;
166 app->client_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
170 g_warning ("Failed to get a client proxy: %s", error->message);
171 g_error_free (error);
176 g_signal_connect (app->client_proxy, "g-signal",
177 G_CALLBACK (g_signal_cb), app);
181 register_client (A11yBusLauncher *app)
183 GDBusProxyFlags flags;
184 GDBusProxy *sm_proxy;
187 const gchar *autostart_id;
188 gchar *client_startup_id;
189 GVariant *parameters;
193 flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
194 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS;
197 sm_proxy = g_dbus_proxy_new_sync (app->session_bus, flags, NULL,
198 "org.gnome.SessionManager",
199 "/org/gnome/SessionManager",
200 "org.gnome.SessionManager",
205 g_warning ("Failed to get session manager proxy: %s", error->message);
206 g_error_free (error);
211 app_id = "at-spi-bus-launcher";
212 autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
214 if (autostart_id != NULL)
216 client_startup_id = g_strdup (autostart_id);
217 g_unsetenv ("DESKTOP_AUTOSTART_ID");
221 client_startup_id = g_strdup ("");
224 parameters = g_variant_new ("(ss)", app_id, client_startup_id);
225 g_free (client_startup_id);
228 variant = g_dbus_proxy_call_sync (sm_proxy,
229 "RegisterClient", parameters,
230 G_DBUS_CALL_FLAGS_NONE,
233 g_object_unref (sm_proxy);
237 g_warning ("Failed to register client: %s", error->message);
238 g_error_free (error);
243 g_variant_get (variant, "(o)", &object_path);
244 g_variant_unref (variant);
246 flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES;
247 g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, flags, NULL,
248 "org.gnome.SessionManager", object_path,
249 "org.gnome.SessionManager.ClientPrivate",
250 NULL, client_proxy_ready_cb, app);
252 g_free (object_path);
256 name_appeared_handler (GDBusConnection *connection,
258 const gchar *name_owner,
261 A11yBusLauncher *app = user_data;
263 register_client (app);
267 setup_bus_child (gpointer data)
269 A11yBusLauncher *app = data;
272 close (app->pipefd[0]);
273 dup2 (app->pipefd[1], 3);
274 close (app->pipefd[1]);
276 /* On Linux, tell the bus process to exit if this process goes away */
278 #include <sys/prctl.h>
279 prctl (PR_SET_PDEATHSIG, 15);
284 * unix_read_all_fd_to_string:
286 * Read all data from a file descriptor to a C string buffer.
289 unix_read_all_fd_to_string (int fd,
295 while (max_bytes > 1 && (bytes_read = read (fd, buf, MAX (4096, max_bytes - 1))))
300 max_bytes -= bytes_read;
307 on_bus_exited (GPid pid,
311 A11yBusLauncher *app = data;
313 app->a11y_bus_pid = -1;
314 app->state = A11Y_BUS_STATE_ERROR;
315 if (app->a11y_launch_error_message == NULL)
317 if (WIFEXITED (status))
318 app->a11y_launch_error_message = g_strdup_printf ("Bus exited with code %d", WEXITSTATUS (status));
319 else if (WIFSIGNALED (status))
320 app->a11y_launch_error_message = g_strdup_printf ("Bus killed by signal %d", WTERMSIG (status));
321 else if (WIFSTOPPED (status))
322 app->a11y_launch_error_message = g_strdup_printf ("Bus stopped by signal %d", WSTOPSIG (status));
324 g_main_loop_quit (app->loop);
328 ensure_a11y_bus (A11yBusLauncher *app)
331 char *argv[] = { DBUS_DAEMON, NULL, "--nofork", "--print-address", "3", NULL };
333 GError *error = NULL;
334 const char *config_path = NULL;
336 if (app->a11y_bus_pid != 0)
339 if (g_file_test (SYSCONFDIR"/at-spi2/accessibility.conf", G_FILE_TEST_EXISTS))
340 config_path = "--config-file="SYSCONFDIR"/at-spi2/accessibility.conf";
342 config_path = "--config-file="DATADIR"/defaults/at-spi2/accessibility.conf";
344 argv[1] = (char*)config_path;
346 if (pipe (app->pipefd) < 0)
347 g_error ("Failed to create pipe: %s", strerror (errno));
349 if (!g_spawn_async (NULL,
352 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
358 app->a11y_bus_pid = -1;
359 app->a11y_launch_error_message = g_strdup (error->message);
360 g_clear_error (&error);
364 close (app->pipefd[1]);
367 g_child_watch_add (pid, on_bus_exited, app);
369 app->state = A11Y_BUS_STATE_READING_ADDRESS;
370 app->a11y_bus_pid = pid;
371 LOGD("Launched a11y bus, child is %ld", (long) pid);
372 if (!unix_read_all_fd_to_string (app->pipefd[0], addr_buf, sizeof (addr_buf)))
374 app->a11y_launch_error_message = g_strdup_printf ("Failed to read address: %s", strerror (errno));
375 kill (app->a11y_bus_pid, SIGTERM);
378 close (app->pipefd[0]);
380 app->state = A11Y_BUS_STATE_RUNNING;
382 /* Trim the trailing newline */
383 app->a11y_bus_address = g_strchomp (g_strdup (addr_buf));
384 LOGD("a11y bus address: %s", app->a11y_bus_address);
388 Display *display = XOpenDisplay (NULL);
391 Atom bus_address_atom = XInternAtom (display, "AT_SPI_BUS", False);
392 XChangeProperty (display,
393 XDefaultRootWindow (display),
395 XA_STRING, 8, PropModeReplace,
396 (guchar *) app->a11y_bus_address, strlen (app->a11y_bus_address));
398 XCloseDisplay (display);
406 close (app->pipefd[0]);
407 close (app->pipefd[1]);
408 app->state = A11Y_BUS_STATE_ERROR;
414 handle_method_call (GDBusConnection *connection,
416 const gchar *object_path,
417 const gchar *interface_name,
418 const gchar *method_name,
419 GVariant *parameters,
420 GDBusMethodInvocation *invocation,
423 A11yBusLauncher *app = user_data;
425 if (g_strcmp0 (method_name, "GetAddress") == 0)
427 ensure_a11y_bus (app);
428 if (app->a11y_bus_pid > 0)
429 g_dbus_method_invocation_return_value (invocation,
430 g_variant_new ("(s)", app->a11y_bus_address));
432 g_dbus_method_invocation_return_dbus_error (invocation,
433 "org.a11y.Bus.Error",
434 app->a11y_launch_error_message);
439 handle_get_property (GDBusConnection *connection,
441 const gchar *object_path,
442 const gchar *interface_name,
443 const gchar *property_name,
447 A11yBusLauncher *app = user_data;
449 if (g_strcmp0 (property_name, "IsEnabled") == 0)
450 return g_variant_new ("b", app->a11y_enabled);
451 else if (g_strcmp0 (property_name, "ScreenReaderEnabled") == 0)
452 return g_variant_new ("b", app->screen_reader_enabled);
458 handle_a11y_enabled_change (A11yBusLauncher *app, gboolean enabled,
459 gboolean notify_gsettings)
461 GVariantBuilder builder;
462 GVariantBuilder invalidated_builder;
464 if (enabled == app->a11y_enabled)
467 app->a11y_enabled = enabled;
469 if (notify_gsettings && app->interface_schema)
471 g_settings_set_boolean (app->interface_schema, "toolkit-accessibility",
476 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
477 g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
478 g_variant_builder_add (&builder, "{sv}", "IsEnabled",
479 g_variant_new_boolean (enabled));
481 g_dbus_connection_emit_signal (app->session_bus, NULL, "/org/a11y/bus",
482 "org.freedesktop.DBus.Properties",
484 g_variant_new ("(sa{sv}as)", "org.a11y.Status",
486 &invalidated_builder),
489 g_variant_builder_clear (&builder);
490 g_variant_builder_clear (&invalidated_builder);
494 handle_screen_reader_enabled_change (A11yBusLauncher *app, gboolean enabled,
495 gboolean notify_gsettings)
497 GVariantBuilder builder;
498 GVariantBuilder invalidated_builder;
500 if (enabled == app->screen_reader_enabled)
503 app->screen_reader_enabled = enabled;
505 if (notify_gsettings && app->a11y_schema)
507 g_settings_set_boolean (app->a11y_schema, "screen-reader-enabled",
512 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
513 g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
514 g_variant_builder_add (&builder, "{sv}", "ScreenReaderEnabled",
515 g_variant_new_boolean (enabled));
517 g_dbus_connection_emit_signal (app->session_bus, NULL, "/org/a11y/bus",
518 "org.freedesktop.DBus.Properties",
520 g_variant_new ("(sa{sv}as)", "org.a11y.Status",
522 &invalidated_builder),
524 g_variant_builder_clear (&builder);
525 g_variant_builder_clear (&invalidated_builder);
529 is_client_connected(A11yBusLauncher *app)
531 guint watchers = g_hash_table_size(app->client_watcher_id);
532 LOGD("clients connected: %d", watchers);
537 remove_client_watch(A11yBusLauncher *app,
540 LOGD("Remove client watcher for %s", sender);
541 guint watcher_id = GPOINTER_TO_UINT(g_hash_table_lookup(app->client_watcher_id, sender));
543 g_bus_unwatch_name(watcher_id);
545 g_hash_table_remove(app->client_watcher_id, sender);
546 if (!is_client_connected(app))
547 handle_a11y_enabled_change (app, FALSE, TRUE);
551 on_client_name_vanished (GDBusConnection *connection,
555 A11yBusLauncher *app = user_data;
556 remove_client_watch(app, name);
560 add_client_watch(A11yBusLauncher *app,
563 LOGD("Add client watcher for %s", sender);
565 if (g_hash_table_contains(app->client_watcher_id, sender))
567 LOGI("Watcher for %s already registered", sender);
571 guint watcher_id = g_bus_watch_name(G_BUS_TYPE_SESSION,
573 G_BUS_NAME_WATCHER_FLAGS_NONE,
575 on_client_name_vanished,
579 g_hash_table_insert(app->client_watcher_id, g_strdup(sender), GUINT_TO_POINTER(watcher_id));
580 handle_a11y_enabled_change (app, TRUE, TRUE);
584 handle_set_property (GDBusConnection *connection,
586 const gchar *object_path,
587 const gchar *interface_name,
588 const gchar *property_name,
593 A11yBusLauncher *app = user_data;
594 const gchar *type = g_variant_get_type_string (value);
597 if (g_strcmp0 (type, "b") != 0)
599 g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
600 "org.a11y.Status.%s expects a boolean but got %s", property_name, type);
604 enabled = g_variant_get_boolean (value);
606 if (g_strcmp0 (property_name, "IsEnabled") == 0)
609 add_client_watch(app, sender);
611 remove_client_watch(app, sender);
614 else if (g_strcmp0 (property_name, "ScreenReaderEnabled") == 0)
616 handle_screen_reader_enabled_change (app, enabled, TRUE);
621 g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
622 "Unknown property '%s'", property_name);
627 static const GDBusInterfaceVTable bus_vtable =
630 NULL, /* handle_get_property, */
631 NULL /* handle_set_property */
634 static const GDBusInterfaceVTable status_vtable =
636 NULL, /* handle_method_call */
642 on_bus_acquired (GDBusConnection *connection,
646 A11yBusLauncher *app = user_data;
648 guint registration_id;
650 if (connection == NULL)
652 g_main_loop_quit (app->loop);
655 app->session_bus = connection;
657 if (app->launch_immediately)
659 ensure_a11y_bus (app);
660 if (app->state == A11Y_BUS_STATE_ERROR)
662 g_main_loop_quit (app->loop);
668 registration_id = g_dbus_connection_register_object (connection,
670 introspection_data->interfaces[0],
675 if (registration_id == 0)
677 g_error ("%s", error->message);
678 g_clear_error (&error);
681 g_dbus_connection_register_object (connection,
683 introspection_data->interfaces[1],
691 on_name_lost (GDBusConnection *connection,
695 A11yBusLauncher *app = user_data;
696 if (app->session_bus == NULL
697 && connection == NULL
698 && app->a11y_launch_error_message == NULL)
699 app->a11y_launch_error_message = g_strdup ("Failed to connect to session bus");
700 g_main_loop_quit (app->loop);
704 on_name_acquired (GDBusConnection *connection,
708 g_bus_watch_name (G_BUS_TYPE_SESSION,
709 "org.gnome.SessionManager",
710 G_BUS_NAME_WATCHER_FLAGS_NONE,
711 name_appeared_handler, NULL,
715 static int sigterm_pipefd[2];
718 sigterm_handler (int signum)
720 write (sigterm_pipefd[1], "X", 1);
724 on_sigterm_pipe (GIOChannel *channel,
725 GIOCondition condition,
728 A11yBusLauncher *app = data;
730 g_main_loop_quit (app->loop);
736 init_sigterm_handling (A11yBusLauncher *app)
738 GIOChannel *sigterm_channel;
740 if (pipe (sigterm_pipefd) < 0)
741 g_error ("Failed to create pipe: %s", strerror (errno));
742 signal (SIGTERM, sigterm_handler);
744 sigterm_channel = g_io_channel_unix_new (sigterm_pipefd[0]);
745 g_io_add_watch (sigterm_channel,
746 G_IO_IN | G_IO_ERR | G_IO_HUP,
757 Display *bridge_display;
759 unsigned char *data = NULL;
760 unsigned long nitems;
761 unsigned long leftover;
762 gboolean result = FALSE;
764 bridge_display = XOpenDisplay (NULL);
768 AT_SPI_BUS = XInternAtom (bridge_display, "AT_SPI_BUS", False);
769 XGetWindowProperty (bridge_display,
770 XDefaultRootWindow (bridge_display),
772 (long) BUFSIZ, False,
773 (Atom) 31, &actual_type, &actual_format,
774 &nitems, &leftover, &data);
778 GDBusConnection *bus;
779 bus = g_dbus_connection_new_for_address_sync ((const gchar *)data, 0,
784 g_object_unref (bus);
788 XCloseDisplay (bridge_display);
796 get_schema (const gchar *name)
798 const char * const *schemas = NULL;
801 schemas = g_settings_list_schemas ();
802 for (i = 0; schemas[i]; i++)
804 if (!strcmp (schemas[i], name))
805 return g_settings_new (schemas[i]);
812 gsettings_key_changed (GSettings *gsettings, const gchar *key, void *user_data)
814 gboolean new_val = g_settings_get_boolean (gsettings, key);
816 if (!strcmp (key, "toolkit-accessibility"))
817 handle_a11y_enabled_change (_global_app, new_val, FALSE);
818 else if (!strcmp (key, "screen-reader-enabled"))
819 handle_screen_reader_enabled_change (_global_app, new_val, FALSE);
823 _process_dead_tracker (int pid, void *data)
825 A11yBusLauncher *app = data;
827 if (app->screen_reader.pid > 0 && pid == app->screen_reader.pid)
829 LOGE("screen reader is dead, pid: %d, restarting", pid);
830 app->screen_reader.pid = 0;
831 g_timeout_add_seconds (2, _launch_process_repeat_until_success, &app->screen_reader);
834 if (app->universal_switch.pid > 0 && pid == app->universal_switch.pid)
836 LOGE("universal switch is dead, pid: %d, restarting", pid);
837 app->universal_switch.pid = 0;
838 g_timeout_add_seconds (2, _launch_process_repeat_until_success, &app->universal_switch);
844 _register_process_dead_tracker ()
846 if(_global_app->screen_reader.pid > 0 || _global_app->universal_switch.pid > 0) {
847 LOGD("registering process dead tracker");
848 aul_listen_app_dead_signal(_process_dead_tracker, _global_app);
850 LOGD("unregistering process dead tracker");
851 aul_listen_app_dead_signal(NULL, NULL);
857 _launch_client(A11yBusClient *client, gboolean by_vconf_change)
859 LOGD("Launching %s", client->name);
862 gboolean ret = FALSE;
864 kb = bundle_create();
868 LOGD("Can't create bundle");
874 if (bundle_add_str(kb, "by_vconf_change", "yes") != BUNDLE_ERROR_NONE)
876 LOGD("Can't add information to bundle");
880 int operation_error = appsvc_set_operation(kb, client->app_control_operation);
881 LOGD("appsvc_set_operation: %i", operation_error);
883 client->pid = appsvc_run_service(kb, 0, NULL, NULL);
887 LOGD("Process launched with pid: %i", client->pid);
888 _register_process_dead_tracker();
893 LOGD("Can't start %s - error code: %i", client->name, client->pid);
901 _launch_process_repeat_until_success(gpointer user_data) {
902 A11yBusClient *client = user_data;
904 if (client->launch_repeats > 100 || client->pid > 0)
910 gboolean ret = _launch_client(client, FALSE);
915 client->launch_repeats = 0;
918 client->launch_repeats++;
924 _terminate_process(int pid)
931 int status = aul_app_get_status_bypid(pid);
935 LOGD("App with pid %d already terminated", pid);
939 LOGD("terminate process with pid %d", pid);
940 ret_aul = aul_terminate_pid(pid);
943 LOGD("Terminating with aul_terminate_pid: return is %d", ret_aul);
947 LOGD("aul_terminate_pid failed: return is %d", ret_aul);
949 LOGD("Unable to terminate process using aul api. Sending SIGTERM signal");
950 ret = kill(pid, SIGTERM);
956 LOGD("Unable to terminate process: %d with api or signal.", pid);
961 _terminate_client(A11yBusClient *client)
963 LOGD("Terminating %s", client->name);
964 int pid = client->pid;
966 _register_process_dead_tracker();
967 gboolean ret = _terminate_process(pid);
971 void vconf_client_cb(keynode_t *node, void *user_data)
973 A11yBusClient *client = user_data;
974 int client_needed = vconf_keynode_get_bool(node);
975 LOGD("vconf_keynode_get_bool(node): %i", client_needed);
976 if (client_needed < 0)
979 //check if process really exists (e.g didn't crash)
982 int err = kill(client->pid,0);
983 //process doesn't exist
988 LOGD("client_needed: %i, client->pid: %i", client_needed, client->pid);
989 if (!client_needed && (client->pid > 0))
990 _terminate_client(client);
991 else if (client_needed && (client->pid <= 0))
992 _launch_client(client, TRUE);
996 static gboolean register_executable(A11yBusClient *client)
998 gboolean client_needed = FALSE;
1000 if(!client->vconf_key) {
1001 LOGE("Vconf_key missing for client: %s \n", client->vconf_key);
1005 int ret = vconf_get_bool(client->vconf_key, &client_needed);
1008 LOGD("Could not read %s key value.\n", client->vconf_key);
1011 ret = vconf_notify_key_changed(client->vconf_key, vconf_client_cb, client);
1014 LOGD("Could not add information level callback\n");
1019 g_timeout_add_seconds(2,_launch_process_repeat_until_success, client);
1027 #ifdef ATSPI_BUS_LAUNCHER_LOG_TO_FILE
1028 log_file = fopen("/tmp/at-spi-bus-launcher.log", "a");
1031 LOGD("Starting atspi bus launcher");
1032 gboolean a11y_set = FALSE;
1033 gboolean screen_reader_set = FALSE;
1036 if (already_running ())
1038 LOGD("atspi bus launcher is already running");
1042 _global_app = g_slice_new0 (A11yBusLauncher);
1043 _global_app->loop = g_main_loop_new (NULL, FALSE);
1044 _global_app->client_watcher_id = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1046 _global_app->screen_reader.name = "screen-reader";
1047 _global_app->screen_reader.app_control_operation = APP_CONTROL_OPERATION_SCREEN_READ;
1048 _global_app->screen_reader.vconf_key = VCONFKEY_SETAPPL_ACCESSIBILITY_TTS;
1050 _global_app->universal_switch.name = "universal-switch";
1051 _global_app->universal_switch.app_control_operation = APP_CONTROL_OPERATION_UNIVERSAL_SWITCH;
1052 _global_app->universal_switch.vconf_key = VCONFKEY_SETAPPL_ACCESSIBILITY_UNIVERSAL_SWITCH;
1054 for (i = 1; i < argc; i++)
1056 if (!strcmp (argv[i], "--launch-immediately"))
1057 _global_app->launch_immediately = TRUE;
1058 else if (sscanf (argv[i], "--a11y=%d", &_global_app->a11y_enabled) == 1)
1060 else if (sscanf (argv[i], "--screen-reader=%d",
1061 &_global_app->screen_reader_enabled) == 1)
1062 screen_reader_set = TRUE;
1064 g_error ("usage: %s [--launch-immediately] [--a11y=0|1] [--screen-reader=0|1]", argv[0]);
1067 _global_app->interface_schema = get_schema ("org.gnome.desktop.interface");
1068 _global_app->a11y_schema = get_schema ("org.gnome.desktop.a11y.applications");
1072 _global_app->a11y_enabled = _global_app->interface_schema
1073 ? g_settings_get_boolean (_global_app->interface_schema, "toolkit-accessibility")
1074 : _global_app->launch_immediately;
1077 if (!screen_reader_set)
1079 _global_app->screen_reader_enabled = _global_app->a11y_schema
1080 ? g_settings_get_boolean (_global_app->a11y_schema, "screen-reader-enabled")
1084 if (_global_app->interface_schema)
1085 g_signal_connect (_global_app->interface_schema,
1086 "changed::toolkit-accessibility",
1087 G_CALLBACK (gsettings_key_changed), _global_app);
1089 if (_global_app->a11y_schema)
1090 g_signal_connect (_global_app->a11y_schema,
1091 "changed::screen-reader-enabled",
1092 G_CALLBACK (gsettings_key_changed), _global_app);
1094 init_sigterm_handling (_global_app);
1096 introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
1097 g_assert (introspection_data != NULL);
1099 g_bus_own_name (G_BUS_TYPE_SESSION,
1101 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
1108 register_executable (&_global_app->screen_reader);
1109 register_executable (&_global_app->universal_switch);
1111 g_main_loop_run (_global_app->loop);
1113 if (_global_app->a11y_bus_pid > 0)
1114 kill (_global_app->a11y_bus_pid, SIGTERM);
1116 /* Clear the X property if our bus is gone; in the case where e.g.
1117 * GDM is launching a login on an X server it was using before,
1118 * we don't want early login processes to pick up the stale address.
1122 Display *display = XOpenDisplay (NULL);
1125 Atom bus_address_atom = XInternAtom (display, "AT_SPI_BUS", False);
1126 XDeleteProperty (display,
1127 XDefaultRootWindow (display),
1131 XCloseDisplay (display);
1136 if (_global_app->a11y_launch_error_message)
1138 g_printerr ("Failed to launch bus: %s", _global_app->a11y_launch_error_message);