X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=bus%2Fat-spi-bus-launcher.c;h=afd4edbd7802e653ce677728580ce10311c98a97;hb=refs%2Ftags%2Faccepted%2Ftizen%2Funified%2F20200221.093534;hp=89860b6619b7ca34fb78928a52ecae4db3391f19;hpb=4a435a0629752f9ee5a8a1a86566c3e48029dd20;p=platform%2Fupstream%2Fat-spi2-core.git diff --git a/bus/at-spi-bus-launcher.c b/bus/at-spi-bus-launcher.c index 89860b6..afd4edb 100644 --- a/bus/at-spi-bus-launcher.c +++ b/bus/at-spi-bus-launcher.c @@ -2,22 +2,22 @@ * * at-spi-bus-launcher: Manage the a11y bus as a child process * - * Copyright 2011 Red Hat, Inc. + * Copyright 2011-2018 Red Hat, Inc. * * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public + * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include "config.h" @@ -25,6 +25,11 @@ #include #include #include +#ifdef __linux__ +#include +#include +#include +#endif #include #include #include @@ -35,7 +40,14 @@ #include #endif +//TODO: move to vconf/vconf-internal-setting-keys.h? +#define VCONFKEY_SETAPPL_ACCESSIBILITY_UNIVERSAL_SWITCH_CONFIGURATION_SERVICE "db/setting/accessibility/universal-switch/configuration-service" +#define VCONFKEY_SETAPPL_ACCESSIBILITY_UNIVERSAL_SWITCH_INTERACTION_SERVICE "db/setting/accessibility/universal-switch/interaction-service" + +#define MAX_NUMBER_OF_KEYS_PER_CLIENT 2 + #define APP_CONTROL_OPERATION_SCREEN_READ "http://tizen.org/appcontrol/operation/read_screen" +#define APP_CONTROL_OPERATION_UNIVERSAL_SWITCH "http://tizen.org/appcontrol/operation/universal_switch" #include #include @@ -64,6 +76,7 @@ FILE *log_file; #define LOGD(arg...) do {if (log_file) {fprintf(log_file, ##arg);fprintf(log_file, "\n"); fflush(log_file);}} while(0) #endif +static gboolean _launch_process_repeat_until_success(gpointer user_data); typedef enum { A11Y_BUS_STATE_IDLE = 0, @@ -73,24 +86,40 @@ typedef enum { } A11yBusState; typedef struct { + const char * name; + const char * app_control_operation; + const char * vconf_key[MAX_NUMBER_OF_KEYS_PER_CLIENT]; + int number_of_keys; + int launch_repeats; + int pid; +} A11yBusClient; + +typedef struct { GMainLoop *loop; gboolean launch_immediately; gboolean a11y_enabled; gboolean screen_reader_enabled; + GHashTable *client_watcher_id; GDBusConnection *session_bus; GSettings *a11y_schema; GSettings *interface_schema; + int name_owner_id; - int launch_screen_reader_repeats; - gboolean screen_reader_needed; - int pid; + A11yBusClient screen_reader; + A11yBusClient universal_switch; + + GDBusProxy *client_proxy; A11yBusState state; /* -1 == error, 0 == pending, > 0 == running */ int a11y_bus_pid; char *a11y_bus_address; +#ifdef HAVE_X11 + gboolean x11_prop_set; +#endif int pipefd[2]; + int listenfd; char *a11y_launch_error_message; } A11yBusLauncher; @@ -111,20 +140,141 @@ static const gchar introspection_xml[] = static GDBusNodeInfo *introspection_data = NULL; static void -setup_bus_child (gpointer data) +respond_to_end_session (GDBusProxy *proxy) { - A11yBusLauncher *app = data; - (void) app; + GVariant *parameters; - close (app->pipefd[0]); - dup2 (app->pipefd[1], 3); - close (app->pipefd[1]); + parameters = g_variant_new ("(bs)", TRUE, ""); - /* On Linux, tell the bus process to exit if this process goes away */ -#ifdef __linux -#include - prctl (PR_SET_PDEATHSIG, 15); -#endif + g_dbus_proxy_call (proxy, + "EndSessionResponse", parameters, + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, NULL, NULL); +} + +static void +g_signal_cb (GDBusProxy *proxy, + gchar *sender_name, + gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + A11yBusLauncher *app = user_data; + + if (g_strcmp0 (signal_name, "QueryEndSession") == 0) + respond_to_end_session (proxy); + else if (g_strcmp0 (signal_name, "EndSession") == 0) + respond_to_end_session (proxy); + else if (g_strcmp0 (signal_name, "Stop") == 0) + g_main_loop_quit (app->loop); +} + +static void +client_proxy_ready_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + A11yBusLauncher *app = user_data; + GError *error = NULL; + + app->client_proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + + if (error != NULL) + { + g_warning ("Failed to get a client proxy: %s", error->message); + g_error_free (error); + + return; + } + + g_signal_connect (app->client_proxy, "g-signal", + G_CALLBACK (g_signal_cb), app); +} + +static void +register_client (A11yBusLauncher *app) +{ + GDBusProxyFlags flags; + GDBusProxy *sm_proxy; + GError *error; + const gchar *app_id; + const gchar *autostart_id; + gchar *client_startup_id; + GVariant *parameters; + GVariant *variant; + gchar *object_path; + + flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS; + + error = NULL; + sm_proxy = g_dbus_proxy_new_sync (app->session_bus, flags, NULL, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + NULL, &error); + + if (error != NULL) + { + g_warning ("Failed to get session manager proxy: %s", error->message); + g_error_free (error); + + return; + } + + app_id = "at-spi-bus-launcher"; + autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID"); + + if (autostart_id != NULL) + { + client_startup_id = g_strdup (autostart_id); + g_unsetenv ("DESKTOP_AUTOSTART_ID"); + } + else + { + client_startup_id = g_strdup (""); + } + + parameters = g_variant_new ("(ss)", app_id, client_startup_id); + g_free (client_startup_id); + + error = NULL; + variant = g_dbus_proxy_call_sync (sm_proxy, + "RegisterClient", parameters, + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &error); + + g_object_unref (sm_proxy); + + if (error != NULL) + { + g_warning ("Failed to register client: %s", error->message); + g_error_free (error); + + return; + } + + g_variant_get (variant, "(o)", &object_path); + g_variant_unref (variant); + + flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES; + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, flags, NULL, + "org.gnome.SessionManager", object_path, + "org.gnome.SessionManager.ClientPrivate", + NULL, client_proxy_ready_cb, app); + + g_free (object_path); +} + +static void +name_appeared_handler (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + A11yBusLauncher *app = user_data; + + register_client (app); } /** @@ -139,7 +289,7 @@ unix_read_all_fd_to_string (int fd, { ssize_t bytes_read; - while (max_bytes > 1 && (bytes_read = read (fd, buf, MAX (4096, max_bytes - 1)))) + while (max_bytes > 1 && (bytes_read = read (fd, buf, MIN (4096, max_bytes - 1)))) { if (bytes_read < 0) return FALSE; @@ -171,27 +321,50 @@ on_bus_exited (GPid pid, g_main_loop_quit (app->loop); } +#ifdef DBUS_DAEMON +static void +setup_bus_child_daemon (gpointer data) +{ + A11yBusLauncher *app = data; + (void) app; + + close (app->pipefd[0]); + dup2 (app->pipefd[1], 3); + close (app->pipefd[1]); + + /* On Linux, tell the bus process to exit if this process goes away */ +#ifdef __linux__ + prctl (PR_SET_PDEATHSIG, 15); +#endif +} + static gboolean -ensure_a11y_bus (A11yBusLauncher *app) +ensure_a11y_bus_daemon (A11yBusLauncher *app, char *config_path) { + char *argv[] = { DBUS_DAEMON, config_path, "--nofork", "--print-address", "3", NULL }; GPid pid; - char *argv[] = { DBUS_DAEMON, NULL, "--nofork", "--print-address", "3", NULL }; char addr_buf[2048]; GError *error = NULL; if (app->a11y_bus_pid != 0) return FALSE; - argv[1] = g_strdup_printf ("--config-file=%s/at-spi2/accessibility.conf", SYSCONFDIR); + argv[1] = (char*)config_path; if (pipe (app->pipefd) < 0) - g_error ("Failed to create pipe: %s", strerror (errno)); + { + char buf[4096] = { 0 }; + strerror_r (errno, buf, sizeof(buf)); + g_error ("Failed to create pipe: %s", buf); + } + + g_clear_pointer (&app->a11y_launch_error_message, g_free); if (!g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, - setup_bus_child, + setup_bus_child_daemon, app, &pid, &error)) @@ -212,8 +385,11 @@ ensure_a11y_bus (A11yBusLauncher *app) LOGD("Launched a11y bus, child is %ld", (long) pid); if (!unix_read_all_fd_to_string (app->pipefd[0], addr_buf, sizeof (addr_buf))) { - app->a11y_launch_error_message = g_strdup_printf ("Failed to read address: %s", strerror (errno)); + char buf[4096] = { 0 }; + strerror_r (errno, buf, sizeof(buf)); + app->a11y_launch_error_message = g_strdup_printf ("Failed to read address: %s", buf); kill (app->a11y_bus_pid, SIGTERM); + app->a11y_bus_pid = -1; goto error; } close (app->pipefd[0]); @@ -221,38 +397,179 @@ ensure_a11y_bus (A11yBusLauncher *app) app->state = A11Y_BUS_STATE_RUNNING; /* Trim the trailing newline */ + if (app->a11y_bus_address) g_free(app->a11y_bus_address); app->a11y_bus_address = g_strchomp (g_strdup (addr_buf)); LOGD("a11y bus address: %s", app->a11y_bus_address); -#ifdef HAVE_X11 - { - Display *display = XOpenDisplay (NULL); - if (display) - { - Atom bus_address_atom = XInternAtom (display, "AT_SPI_BUS", False); - XChangeProperty (display, - XDefaultRootWindow (display), - bus_address_atom, - XA_STRING, 8, PropModeReplace, - (guchar *) app->a11y_bus_address, strlen (app->a11y_bus_address)); - XFlush (display); - XCloseDisplay (display); - } - } + return TRUE; + +error: + if (app->pipefd[0] > 0) close (app->pipefd[0]); + if (app->pipefd[1] > 0) close (app->pipefd[1]); + app->state = A11Y_BUS_STATE_ERROR; + + return FALSE; +} +#else +static gboolean +ensure_a11y_bus_daemon (A11yBusLauncher *app, char *config_path) +{ + return FALSE; +} #endif - if (argv[1]) g_free(argv[1]); +#ifdef DBUS_BROKER +static void +setup_bus_child_broker (gpointer data) +{ + A11yBusLauncher *app = data; + gchar *pid_str; + (void) app; + + dup2 (app->listenfd, 3); + close (app->listenfd); + g_setenv("LISTEN_FDS", "1", TRUE); + + pid_str = g_strdup_printf("%u", getpid()); + g_setenv("LISTEN_PID", pid_str, TRUE); + g_free(pid_str); + + /* Tell the bus process to exit if this process goes away */ + prctl (PR_SET_PDEATHSIG, SIGTERM); +} + +static gboolean +ensure_a11y_bus_broker (A11yBusLauncher *app, char *config_path) +{ + char *argv[] = { DBUS_BROKER, config_path, "--scope", "user", NULL }; + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + socklen_t addr_len = sizeof(addr); + GPid pid; + GError *error = NULL; + + if ((app->listenfd = socket (PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) < 0) + { + char buf[4096] = { 0 }; + strerror_r (errno, buf, sizeof(buf)); + g_error ("Failed to create listening socket: %s", buf); + } + + if (bind (app->listenfd, (struct sockaddr *)&addr, sizeof(sa_family_t)) < 0) + { + char buf[4096] = { 0 }; + strerror_r (errno, buf, sizeof(buf)); + g_error ("Failed to bind listening socket: %s", buf); + } + + if (getsockname (app->listenfd, (struct sockaddr *)&addr, &addr_len) < 0) + { + char buf[4096] = { 0 }; + strerror_r (errno, buf, sizeof(buf)); + g_error ("Failed to get socket name for listening socket: %s", buf); + } + + if (listen (app->listenfd, 1024) < 0) + { + char buf[4096] = { 0 }; + strerror_r (errno, buf, sizeof(buf)); + g_error ("Failed to listen on socket: %s", buf); + } + + g_clear_pointer (&app->a11y_launch_error_message, g_free); + + if (!g_spawn_async (NULL, + argv, + NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, + setup_bus_child_broker, + app, + &pid, + &error)) + { + app->a11y_bus_pid = -1; + app->a11y_launch_error_message = g_strdup (error->message); + g_clear_error (&error); + goto error; + } + + close (app->listenfd); + app->listenfd = -1; + + g_child_watch_add (pid, on_bus_exited, app); + app->a11y_bus_pid = pid; + g_debug ("Launched a11y bus, child is %ld", (long) pid); + app->state = A11Y_BUS_STATE_RUNNING; + + app->a11y_bus_address = g_strconcat("unix:abstract=", addr.sun_path + 1, NULL); + g_debug ("a11y bus address: %s", app->a11y_bus_address); return TRUE; - error: - if (argv[1]) g_free(argv[1]); - close (app->pipefd[0]); - close (app->pipefd[1]); +error: + close (app->listenfd); app->state = A11Y_BUS_STATE_ERROR; return FALSE; } +#else +static gboolean +ensure_a11y_bus_broker (A11yBusLauncher *app, char *config_path) +{ + return FALSE; +} +#endif + +static gboolean +ensure_a11y_bus (A11yBusLauncher *app) +{ + char *config_path = NULL; + gboolean success = FALSE; + + if (app->a11y_bus_pid != 0) + return FALSE; + + if (g_file_test (SYSCONFDIR"/at-spi2/accessibility.conf", G_FILE_TEST_EXISTS)) + config_path = "--config-file="SYSCONFDIR"/at-spi2/accessibility.conf"; + else + config_path = "--config-file="DATADIR"/defaults/at-spi2/accessibility.conf"; + +#ifdef WANT_DBUS_BROKER + success = ensure_a11y_bus_broker (app, config_path); + if (!success) + { + if (!ensure_a11y_bus_daemon (app, config_path)) + return FALSE; + } +#else + success = ensure_a11y_bus_daemon (app, config_path); + if (!success) + { + if (!ensure_a11y_bus_broker (app, config_path)) + return FALSE; + } +#endif + +#ifdef HAVE_X11 + if (g_getenv ("DISPLAY") != NULL && g_getenv ("WAYLAND_DISPLAY") == NULL) + { + Display *display = XOpenDisplay (NULL); + if (display) + { + Atom bus_address_atom = XInternAtom (display, "AT_SPI_BUS", False); + XChangeProperty (display, + XDefaultRootWindow (display), + bus_address_atom, + XA_STRING, 8, PropModeReplace, + (guchar *) app->a11y_bus_address, strlen (app->a11y_bus_address)); + XFlush (display); + XCloseDisplay (display); + app->x11_prop_set = TRUE; + } + } +#endif + + return TRUE; +} static void handle_method_call (GDBusConnection *connection, @@ -329,6 +646,9 @@ handle_a11y_enabled_change (A11yBusLauncher *app, gboolean enabled, &builder, &invalidated_builder), NULL); + + g_variant_builder_clear (&builder); + g_variant_builder_clear (&invalidated_builder); } static void @@ -341,11 +661,6 @@ handle_screen_reader_enabled_change (A11yBusLauncher *app, gboolean enabled, if (enabled == app->screen_reader_enabled) return; - /* If the screen reader is being enabled, we should enable accessibility - * if it isn't enabled already */ - if (enabled) - handle_a11y_enabled_change (app, enabled, notify_gsettings); - app->screen_reader_enabled = enabled; if (notify_gsettings && app->a11y_schema) @@ -367,6 +682,63 @@ handle_screen_reader_enabled_change (A11yBusLauncher *app, gboolean enabled, &builder, &invalidated_builder), NULL); + g_variant_builder_clear (&builder); + g_variant_builder_clear (&invalidated_builder); +} + +static gboolean +is_client_connected(A11yBusLauncher *app) +{ + guint watchers = g_hash_table_size(app->client_watcher_id); + LOGD("clients connected: %d", watchers); + return watchers > 0; +} + +static void +remove_client_watch(A11yBusLauncher *app, + const gchar *sender) +{ + LOGD("Remove client watcher for %s", sender); + guint watcher_id = GPOINTER_TO_UINT(g_hash_table_lookup(app->client_watcher_id, sender)); + if (watcher_id) + g_bus_unwatch_name(watcher_id); + + g_hash_table_remove(app->client_watcher_id, sender); + if (!is_client_connected(app)) + handle_a11y_enabled_change (app, FALSE, TRUE); +} + +static void +on_client_name_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + A11yBusLauncher *app = user_data; + remove_client_watch(app, name); +} + +static void +add_client_watch(A11yBusLauncher *app, + const gchar *sender) +{ + LOGD("Add client watcher for %s", sender); + + if (g_hash_table_contains(app->client_watcher_id, sender)) + { + LOGI("Watcher for %s already registered", sender); + return; + } + + guint watcher_id = g_bus_watch_name(G_BUS_TYPE_SESSION, + sender, + G_BUS_NAME_WATCHER_FLAGS_NONE, + NULL, + on_client_name_vanished, + app, + NULL); + + g_hash_table_insert(app->client_watcher_id, g_strdup(sender), GUINT_TO_POINTER(watcher_id)); + handle_a11y_enabled_change (app, TRUE, TRUE); } static gboolean @@ -394,7 +766,10 @@ handle_set_property (GDBusConnection *connection, if (g_strcmp0 (property_name, "IsEnabled") == 0) { - handle_a11y_enabled_change (app, enabled, TRUE); + if (enabled) + add_client_watch(app, sender); + else + remove_client_watch(app, sender); return TRUE; } else if (g_strcmp0 (property_name, "ScreenReaderEnabled") == 0) @@ -440,16 +815,6 @@ on_bus_acquired (GDBusConnection *connection, } app->session_bus = connection; - if (app->launch_immediately) - { - ensure_a11y_bus (app); - if (app->state == A11Y_BUS_STATE_ERROR) - { - g_main_loop_quit (app->loop); - return; - } - } - error = NULL; registration_id = g_dbus_connection_register_object (connection, "/org/a11y/bus", @@ -492,7 +857,22 @@ on_name_acquired (GDBusConnection *connection, gpointer user_data) { A11yBusLauncher *app = user_data; - (void) app; + + if (app->launch_immediately) + { + ensure_a11y_bus (app); + if (app->state == A11Y_BUS_STATE_ERROR) + { + g_main_loop_quit (app->loop); + return; + } + } + + g_bus_watch_name (G_BUS_TYPE_SESSION, + "org.gnome.SessionManager", + G_BUS_NAME_WATCHER_FLAGS_NONE, + name_appeared_handler, NULL, + user_data, NULL); } static int sigterm_pipefd[2]; @@ -521,7 +901,11 @@ init_sigterm_handling (A11yBusLauncher *app) GIOChannel *sigterm_channel; if (pipe (sigterm_pipefd) < 0) - g_error ("Failed to create pipe: %s", strerror (errno)); + { + char buf[4096] = { 0 }; + strerror_r (errno, buf, sizeof(buf)); + g_error ("Failed to create pipe: %s", buf); + } signal (SIGTERM, sigterm_handler); sigterm_channel = g_io_channel_unix_new (sigterm_pipefd[0]); @@ -531,53 +915,20 @@ init_sigterm_handling (A11yBusLauncher *app) app); } -static gboolean -already_running () -{ -#ifdef HAVE_X11 - Atom AT_SPI_BUS; - Atom actual_type; - Display *bridge_display; - int actual_format; - unsigned char *data = NULL; - unsigned long nitems; - unsigned long leftover; - gboolean result = FALSE; - - bridge_display = XOpenDisplay (NULL); - if (!bridge_display) - return FALSE; - - AT_SPI_BUS = XInternAtom (bridge_display, "AT_SPI_BUS", False); - XGetWindowProperty (bridge_display, - XDefaultRootWindow (bridge_display), - AT_SPI_BUS, 0L, - (long) BUFSIZ, False, - (Atom) 31, &actual_type, &actual_format, - &nitems, &leftover, &data); - - if (data) - { - GDBusConnection *bus; - bus = g_dbus_connection_new_for_address_sync ((const gchar *)data, 0, - NULL, NULL, NULL); - if (bus != NULL) - { - result = TRUE; - g_object_unref (bus); - } - } - - XCloseDisplay (bridge_display); - return result; -#else - return FALSE; -#endif -} - static GSettings * get_schema (const gchar *name) { +#if GLIB_CHECK_VERSION (2, 32, 0) + GSettingsSchemaSource *source = g_settings_schema_source_get_default (); + if (!source) return NULL; + + GSettingsSchema *schema = g_settings_schema_source_lookup (source, name, FALSE); + + if (schema == NULL) + return NULL; + + return g_settings_new_full (schema, NULL, NULL); +#else const char * const *schemas = NULL; gint i; @@ -589,13 +940,13 @@ get_schema (const gchar *name) } return NULL; +#endif } static void gsettings_key_changed (GSettings *gsettings, const gchar *key, void *user_data) { gboolean new_val = g_settings_get_boolean (gsettings, key); - A11yBusLauncher *app = user_data; if (!strcmp (key, "toolkit-accessibility")) handle_a11y_enabled_change (_global_app, new_val, FALSE); @@ -603,11 +954,44 @@ gsettings_key_changed (GSettings *gsettings, const gchar *key, void *user_data) handle_screen_reader_enabled_change (_global_app, new_val, FALSE); } +static int +_process_dead_tracker (int pid, void *data) +{ + A11yBusLauncher *app = data; + + if (app->screen_reader.pid > 0 && pid == app->screen_reader.pid) + { + LOGE("screen reader is dead, pid: %d, restarting", pid); + app->screen_reader.pid = 0; + g_timeout_add_seconds (2, _launch_process_repeat_until_success, &app->screen_reader); + } + + if (app->universal_switch.pid > 0 && pid == app->universal_switch.pid) + { + LOGE("universal switch is dead, pid: %d, restarting", pid); + app->universal_switch.pid = 0; + g_timeout_add_seconds (2, _launch_process_repeat_until_success, &app->universal_switch); + } + return 0; +} + +static void +_register_process_dead_tracker () +{ + if(_global_app->screen_reader.pid > 0 || _global_app->universal_switch.pid > 0) { + LOGD("registering process dead tracker"); + aul_listen_app_dead_signal(_process_dead_tracker, _global_app); + } else { + LOGD("unregistering process dead tracker"); + aul_listen_app_dead_signal(NULL, NULL); + } +} + + static gboolean -_launch_screen_reader(gpointer user_data, gboolean by_vconf_change) +_launch_client(A11yBusClient *client, gboolean by_vconf_change) { - A11yBusLauncher *bl = user_data; - LOGD("Launching screen reader"); + LOGD("Launching %s", client->name); bundle *kb = NULL; gboolean ret = FALSE; @@ -628,115 +1012,169 @@ _launch_screen_reader(gpointer user_data, gboolean by_vconf_change) } } - int operation_error = appsvc_set_operation(kb, APP_CONTROL_OPERATION_SCREEN_READ); + int operation_error = appsvc_set_operation(kb, client->app_control_operation); LOGD("appsvc_set_operation: %i", operation_error); - bl->pid = appsvc_run_service(kb, 0, NULL, NULL); + client->pid = appsvc_run_service(kb, 0, NULL, NULL); - if (bl->pid > 0) + if (client->pid > 0) { - LOGD("Screen reader launched with pid: %i", bl->pid); + LOGD("Process launched with pid: %i", client->pid); + _register_process_dead_tracker(); ret = TRUE; } else { - LOGD("Can't start screen-reader - error code: %i", bl->pid); + LOGD("Can't start %s - error code: %i", client->name, client->pid); } - bundle_free(kb); return ret; } static gboolean -_launch_screen_reader_repeat_until_success(gpointer user_data) { - A11yBusLauncher *bl = user_data; +_launch_process_repeat_until_success(gpointer user_data) { + A11yBusClient *client = user_data; - if (bl->launch_screen_reader_repeats > 100 || bl->pid > 0) + if (client->launch_repeats > 100 || client->pid > 0) { //do not try anymore return FALSE; } - gboolean ret = _launch_screen_reader(user_data, FALSE); + gboolean ret = _launch_client(client, FALSE); if (ret) { //we managed to - bl->launch_screen_reader_repeats = 0; + client->launch_repeats = 0; return FALSE; } + client->launch_repeats++; //try again return TRUE; } static gboolean -_terminate_screen_reader(A11yBusLauncher *bl) +_terminate_process(int pid) { - LOGD("Terminating screen reader"); int ret; int ret_aul; - if (bl->pid <= 0) + if (pid <= 0) return FALSE; - - int status = aul_app_get_status_bypid(bl->pid); + int status = aul_app_get_status_bypid(pid); if (status < 0) { - LOGD("App with pid %d already terminated", bl->pid); - bl->pid = 0; + LOGD("App with pid %d already terminated", pid); return TRUE; } - LOGD("terminate process with pid %d", bl->pid); - ret_aul = aul_terminate_pid(bl->pid); + LOGD("terminate process with pid %d", pid); + ret_aul = aul_terminate_pid(pid); if (ret_aul >= 0) { LOGD("Terminating with aul_terminate_pid: return is %d", ret_aul); - bl->pid = 0; return TRUE; } else LOGD("aul_terminate_pid failed: return is %d", ret_aul); LOGD("Unable to terminate process using aul api. Sending SIGTERM signal"); - ret = kill(bl->pid, SIGTERM); + ret = kill(pid, SIGTERM); if (!ret) { - bl->pid = 0; return TRUE; } - LOGD("Unable to terminate process: %d with api or signal.", bl->pid); + LOGD("Unable to terminate process: %d with api or signal.", pid); return FALSE; } -void screen_reader_cb(keynode_t *node, void *user_data) +static gboolean +_terminate_client(A11yBusClient *client) +{ + LOGD("Terminating %s", client->name); + int pid = client->pid; + client->pid = 0; + _register_process_dead_tracker(); + gboolean ret = _terminate_process(pid); + return ret; +} + +void vconf_client_cb(keynode_t *node, void *user_data) { - A11yBusLauncher *bl = user_data; - int ret; + A11yBusClient *client = user_data; + + gboolean client_needed = FALSE; + int i; + for (i = 0; i < client->number_of_keys; i++) { + int status = 0; + int ret =vconf_get_bool(client->vconf_key[i], &status); + if (ret != 0) + { + LOGD("Could not read %s key value.\n", client->vconf_key[i]); + return; + } + LOGD("vconf_keynode_get_bool(node): %i", status); + if (status < 0) + return; - ret = vconf_keynode_get_bool(node); - LOGD("vconf_keynode_get_bool(node): %i", ret); - if (ret < 0) - return; + if (status == 1) { + client_needed = TRUE; + break; + } + } //check if process really exists (e.g didn't crash) - if (bl->pid > 0) + if (client->pid > 0) { - int err = kill(bl->pid,0); + int err = kill(client->pid,0); //process doesn't exist if (err == ESRCH) - bl->pid = 0; + client->pid = 0; } - bl->screen_reader_needed = ret; - LOGD("bl->screen_reader_needed: %i, bl->pid: %i", ret, bl->pid); - if (!bl->screen_reader_needed && (bl->pid > 0)) - _terminate_screen_reader(bl); - else if (bl->screen_reader_needed && (bl->pid <= 0)) - _launch_screen_reader(bl, TRUE); + LOGD("client_needed: %i, client->pid: %i", client_needed, client->pid); + if (!client_needed && (client->pid > 0)) + _terminate_client(client); + else if (client_needed && (client->pid <= 0)) + _launch_client(client, TRUE); +} + + +static gboolean register_executable(A11yBusClient *client) +{ + gboolean client_needed = FALSE; + + int i; + for (i = 0; i < client->number_of_keys; i++) { + if (!client->vconf_key[i]) { + LOGE("Vconf_key missing for client: %d \n", i); + return FALSE; + } + + int status = 0; + int ret = vconf_get_bool(client->vconf_key[i], &status); + if (ret != 0) + { + LOGD("Could not read %s key value.\n", client->vconf_key[i]); + return FALSE; + } + ret = vconf_notify_key_changed(client->vconf_key[i], vconf_client_cb, client); + if (ret != 0) + { + LOGD("Could not add information level callback\n"); + return FALSE; + } + if (status) + client_needed = TRUE; + } + + if (client_needed) + g_timeout_add_seconds(2,_launch_process_repeat_until_success, client); + return TRUE; } int @@ -748,32 +1186,34 @@ main (int argc, #endif LOGD("Starting atspi bus launcher"); - GError *error = NULL; - GMainLoop *loop; - GDBusConnection *session_bus; - int name_owner_id; + gboolean a11y_set = FALSE; gboolean screen_reader_set = FALSE; gint i; - if (already_running ()) - { - LOGD("atspi bus launcher is already running"); - return 0; - } - _global_app = g_slice_new0 (A11yBusLauncher); _global_app->loop = g_main_loop_new (NULL, FALSE); - _global_app->launch_screen_reader_repeats = 0; + _global_app->client_watcher_id = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + + _global_app->screen_reader.name = "screen-reader"; + _global_app->screen_reader.app_control_operation = APP_CONTROL_OPERATION_SCREEN_READ; + _global_app->screen_reader.vconf_key[0] = VCONFKEY_SETAPPL_ACCESSIBILITY_TTS; + _global_app->screen_reader.number_of_keys = 1; + + _global_app->universal_switch.name = "universal-switch"; + _global_app->universal_switch.app_control_operation = APP_CONTROL_OPERATION_UNIVERSAL_SWITCH; + _global_app->universal_switch.vconf_key[0] = VCONFKEY_SETAPPL_ACCESSIBILITY_UNIVERSAL_SWITCH_CONFIGURATION_SERVICE; + _global_app->universal_switch.vconf_key[1] = VCONFKEY_SETAPPL_ACCESSIBILITY_UNIVERSAL_SWITCH_INTERACTION_SERVICE; + _global_app->universal_switch.number_of_keys = 2; for (i = 1; i < argc; i++) { if (!strcmp (argv[i], "--launch-immediately")) _global_app->launch_immediately = TRUE; - else if (sscanf (argv[i], "--a11y=%d", &_global_app->a11y_enabled) == 2) + else if (sscanf (argv[i], "--a11y=%d", &_global_app->a11y_enabled) == 1) a11y_set = TRUE; else if (sscanf (argv[i], "--screen-reader=%d", - &_global_app->screen_reader_enabled) == 2) + &_global_app->screen_reader_enabled) == 1) screen_reader_set = TRUE; else g_error ("usage: %s [--launch-immediately] [--a11y=0|1] [--screen-reader=0|1]", argv[0]); @@ -811,7 +1251,7 @@ main (int argc, introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); g_assert (introspection_data != NULL); - name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + g_bus_own_name (G_BUS_TYPE_SESSION, "org.a11y.Bus", G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, on_bus_acquired, @@ -820,20 +1260,8 @@ main (int argc, _global_app, NULL); - int ret = vconf_get_bool(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, &_global_app->screen_reader_needed); - if (ret != 0) - { - LOGD("Could not read VCONFKEY_SETAPPL_ACCESSIBILITY_TTS key value.\n"); - return FALSE; - } - ret = vconf_notify_key_changed(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, screen_reader_cb, _global_app); - if(ret != 0) - { - LOGD("Could not add information level callback\n"); - return FALSE; - } - if (_global_app->screen_reader_needed) - g_timeout_add_seconds(2,_launch_screen_reader_repeat_until_success, _global_app); + register_executable (&_global_app->screen_reader); + register_executable (&_global_app->universal_switch); g_main_loop_run (_global_app->loop); @@ -845,19 +1273,20 @@ main (int argc, * we don't want early login processes to pick up the stale address. */ #ifdef HAVE_X11 - { - Display *display = XOpenDisplay (NULL); - if (display) - { - Atom bus_address_atom = XInternAtom (display, "AT_SPI_BUS", False); - XDeleteProperty (display, - XDefaultRootWindow (display), - bus_address_atom); + if (_global_app->x11_prop_set) + { + Display *display = XOpenDisplay (NULL); + if (display) + { + Atom bus_address_atom = XInternAtom (display, "AT_SPI_BUS", False); + XDeleteProperty (display, + XDefaultRootWindow (display), + bus_address_atom); - XFlush (display); - XCloseDisplay (display); - } - } + XFlush (display); + XCloseDisplay (display); + } + } #endif if (_global_app->a11y_launch_error_message)