[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[Pkg-xfce-devel] Getting orca started in lightdm



Samuel Thibault, le Tue 21 Oct 2014 00:07:41 +0200, a ?crit :
> Samuel Thibault, le Mon 20 Oct 2014 22:58:14 +0200, a ?crit :
> > Yves-Alexis Perez, le Mon 20 Oct 2014 22:47:41 +0200, a ?crit :
> > > On lun., 2014-10-20 at 22:42 +0200, Samuel Thibault wrote:
> > > > I forgot to answer this:
> > > > 
> > > > Yves-Alexis Perez, le Mon 20 Oct 2014 22:34:22 +0200, a ?crit :
> > > > > I guess that this is not about the user session but really
> > > > > about the greeter?
> > > > 
> > > > Yes, in order to be able to login non-blindly.
> > > 
> > > Ok. Then I think you need to ask upstream about that, because overriding
> > > the wrapper doesn't look like the best way to do it.
> > 
> > On the long run, I agree, but for Jessie I guess it is too late for
> > getting the source code change in?
> 
> Actually support was contributed upstream on
> 
> https://bugs.launchpad.net/lightdm-gtk-greeter/+bug/1319848
> 
> I'm having a look at backporting it to the Debian package.

Here is a backport of the upstream patch.  It is then a matter of the
orca package to define reader= (and thus allowing to start the reader by
just pressing F4), and the d-i to define a11y-states=+reader.

Do you feel like uploading this change for Jessie, or we'd rather go the
wrapper way for Jessie?

Samuel
-------------- next part --------------
=== modified file 'data/lightdm-gtk-greeter.conf'
Index: data/lightdm-gtk-greeter.conf
===================================================================
--- data/lightdm-gtk-greeter.conf.orig
+++ data/lightdm-gtk-greeter.conf
@@ -10,7 +10,9 @@
 # show-indicators = semi-colon ";" separated list of allowed indicator modules. Built-in indicators include "~a11y", "~language", "~session", "~power". Unity indicators can be represented by short name (e.g. "sound", "power"), service file name, or absolute path
 # show-clock (true or false)
 # clock-format = strftime-format string, e.g. %H:%M
-# keyboard = command to launch on-screen keyboard
+# keyboard = command to launch on-screen keyboard (e.g. onboard)
+# reader = command to launch screen reader (e.g. orca)
+# a11y-states = states of accessibility features: "name" - save state on exit, "-name" - disabled at start (default value for unlisted), "+name" - enabled at start. Allowed names: contrast, font, keyboard.
 # position = main window position: x y
 # default-user-image = Image used as default user icon, path or #icon-name
 # screensaver-timeout = Timeout (in seconds) until the screen blanks when the greeter is called as lockscreen
@@ -28,5 +30,6 @@ show-indicators=~language;~session;~powe
 #show-clock=
 #clock-format=
 #keyboard=
+#reader=
 #position=
 #screensaver-timeout=
Index: src/lightdm-gtk-greeter.c
===================================================================
--- src/lightdm-gtk-greeter.c.orig
+++ src/lightdm-gtk-greeter.c
@@ -54,8 +54,10 @@
 #include <src/lightdm-gtk-greeter-ui.h>
 
 static LightDMGreeter *greeter;
+
 static GKeyFile *state;
 static gchar *state_filename;
+static void save_state_file (void);
 
 /* Defaults */
 static gchar *default_font_name, *default_theme_name, *default_icon_theme_name;
@@ -67,7 +69,7 @@ static GtkWindow *panel_window;
 static GtkWidget *clock_label;
 static GtkWidget *menubar, *power_menuitem, *session_menuitem, *language_menuitem, *a11y_menuitem, *session_badge;
 static GtkWidget *suspend_menuitem, *hibernate_menuitem, *restart_menuitem, *shutdown_menuitem;
-static GtkWidget *keyboard_menuitem;
+static GtkWidget *contrast_menuitem, *font_menuitem, *keyboard_menuitem, *reader_menuitem;
 static GtkMenu *session_menu, *language_menu;
 
 /* Login Window Widgets */
@@ -80,10 +82,52 @@ static GtkInfoBar *info_bar;
 static GtkButton *cancel_button, *login_button;
 
 static gchar *clock_format;
-static gchar **a11y_keyboard_command;
-static GPid a11y_kbd_pid = 0;
-static GError *a11y_keyboard_error;
-static GtkWindow *onboard_window;
+
+typedef struct
+{
+    gint value;
+    /* +0 and -0 */
+    gint sign;
+    /* interpret 'value' as percentage of screen width/height */
+    gboolean percentage;
+    /* -1: left/top, 0: center, +1: right,bottom */
+    gint anchor;
+} DimensionPosition;
+
+typedef struct
+{
+    DimensionPosition x, y;
+} WindowPosition;
+
+/* Function translate user defined coordinates to absolute value */
+static gint get_absolute_position (const DimensionPosition *p, gint screen, gint window);
+static const WindowPosition CENTERED_WINDOW_POS = {.x = {50, +1, TRUE, 0}, .y = {50, +1, TRUE, 0}};
+static const WindowPosition ONBOARD_WINDOW_POS  = {.x = {50, +1, TRUE, 0}, .y = { 0, -1, FALSE, +1}};
+static const WindowPosition ONBOARD_WINDOW_SIZE = {.x = {610, 0, FALSE,0}, .y = {210, 0, FALSE, 0}};
+static WindowPosition main_window_pos;
+
+typedef struct
+{
+    gchar **argv;
+    gint argc;
+
+    GPid pid;
+    GtkWidget *menu_item;
+    GtkWidget *widget;
+} MenuCommand;
+
+static MenuCommand *menu_command_parse (const gchar *value, GtkWidget *menu_item);
+static MenuCommand *menu_command_parse_extended (const gchar *value, GtkWidget *menu_item,
+                                                       const gchar *xid_supported, const gchar *xid_arg,
+                                                       const WindowPosition *size);
+static gboolean menu_command_run (MenuCommand *command);
+static gboolean menu_command_stop (MenuCommand *command);
+static void command_terminated_cb (GPid pid, gint status, MenuCommand *command);
+
+static MenuCommand *a11y_keyboard_command;
+static MenuCommand *a11y_reader_command;
+
+static void a11y_menuitem_toggled_cb (GtkCheckMenuItem *item, const gchar* name);
 
 /* Pending Questions */
 static GSList *pending_questions = NULL;
@@ -119,27 +163,203 @@ typedef struct
   gchar *text;
 } PAMConversationMessage;
 
-typedef struct
+GdkPixbuf* default_user_pixbuf = NULL;
+gchar* default_user_icon = "avatar-default";
+
+
+static void
+save_state_file (void)
 {
-    gint value;
-    /* +0 and -0 */
-    gint sign;
-    /* interpret 'value' as percentage of screen width/height */
-    gboolean percentage;
-    /* -1: left/top, 0: center, +1: right,bottom */
-    gint anchor;
-} DimensionPosition;
+    GError *error = NULL;
+    gsize data_length = 0;
+    gchar *data = g_key_file_to_data (state, &data_length, &error);
 
-typedef struct
+    if (error)
+    {
+        g_warning ("Failed to save state file: %s", error->message);
+        g_clear_error (&error);
+    }
+
+    if (data)
+    {
+        g_file_set_contents (state_filename, data, data_length, &error);
+        if (error)
+        {
+            g_warning ("Failed to save state file: %s", error->message);
+            g_clear_error (&error);
+        }
+        g_free (data);
+    }
+}
+
+/* MenuCommand */
+
+static MenuCommand*
+menu_command_parse (const gchar *value, GtkWidget *menu_item)
 {
-    DimensionPosition x, y;
-} WindowPosition;
+    return menu_command_parse_extended (value, menu_item, NULL, NULL, NULL);
+}
 
-const WindowPosition CENTERED_WINDOW_POS = { .x = {50, +1, TRUE, 0}, .y = {50, +1, TRUE, 0} };
-WindowPosition main_window_pos;
+static MenuCommand*
+menu_command_parse_extended (const gchar *value, GtkWidget *menu_item,
+                             const gchar *xid_supported, const gchar *xid_arg,
+                             const WindowPosition *size)
+{
+    if (!value)
+        return NULL;
 
-GdkPixbuf* default_user_pixbuf = NULL;
-gchar* default_user_icon = "avatar-default";
+    GError *error = NULL;
+    gchar **argv;
+    gint argc = 0;
+
+    if (!g_shell_parse_argv (value, &argc, &argv, &error))
+    {
+        if (error)
+            g_warning ("Failed to parse command line: %s", error->message);
+        g_clear_error (&error);
+        return NULL;
+    }
+
+    MenuCommand *command = g_new0 (MenuCommand, 1);
+    command->menu_item = menu_item;
+    command->argc = argc;
+    command->argv = argv;
+
+    if (g_strcmp0 (argv[0], xid_supported) == 0)
+    {
+        gboolean have_xid_arg = FALSE;
+        gint i;
+        for (i = 1; i < argc; ++i)
+            if (g_strcmp0 (argv[i], xid_arg) == 0)
+            {
+                have_xid_arg = TRUE;
+                break;
+            }
+        if (!have_xid_arg)
+        {
+            gchar *new_value = g_strdup_printf ("%s %s", value, xid_arg);
+
+            if (g_shell_parse_argv (new_value, &argc, &argv, &error))
+            {
+                g_strfreev (command->argv);
+                command->argc = argc;
+                command->argv = argv;
+                have_xid_arg = TRUE;
+            }
+            else
+            {
+                if (error)
+                    g_warning ("Failed to parse command line: %s", error->message);
+                g_clear_error (&error);
+            }
+            g_free (new_value);
+        }
+
+        if (have_xid_arg)
+        {
+            GdkRectangle screen;
+            command->widget = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+            gdk_screen_get_monitor_geometry (gdk_screen_get_default (),
+                                             /* must be replaced with background->active_monitor */
+                                             gdk_screen_get_primary_monitor (gdk_screen_get_default ()),
+                                             &screen);
+            gtk_widget_set_size_request (command->widget,
+                                         get_absolute_position (&size->x, screen.width, 0),
+                                         get_absolute_position (&size->y, screen.height, 0));
+        }
+    }
+    return command;
+}
+
+static gboolean
+menu_command_run (MenuCommand *command)
+{
+    g_return_val_if_fail (command && g_strv_length (command->argv), FALSE);
+
+    GError *error = NULL;
+    gboolean spawned = FALSE;
+    if (command->widget)
+    {
+        GtkSocket* socket = NULL;
+        gint out_fd = 0;
+
+        if (g_spawn_async_with_pipes (NULL, command->argv, NULL, G_SPAWN_SEARCH_PATH,
+                                      NULL, NULL, &command->pid, NULL, &out_fd, NULL, &error))
+        {
+            gchar* text = NULL;
+            GIOChannel* out_channel = g_io_channel_unix_new (out_fd);
+            if (g_io_channel_read_line (out_channel, &text, NULL, NULL, &error) == G_IO_STATUS_NORMAL)
+            {
+                gchar* end_ptr = NULL;
+
+                text = g_strstrip (text);
+                gint id = g_ascii_strtoll (text, &end_ptr, 0);
+
+                if (id != 0 && end_ptr > text)
+                {
+                    socket = GTK_SOCKET (gtk_socket_new ());
+                    gtk_container_add (GTK_CONTAINER (command->widget), GTK_WIDGET (socket));
+                    gtk_socket_add_id (socket, id);
+                    gtk_widget_show_all (GTK_WIDGET (command->widget));
+                    spawned = TRUE;
+                }
+                else
+                    g_warning ("Failed to get '%s' socket: unrecognized output", command->argv[0]);
+
+                g_free(text);
+            }
+        }
+    }
+    else
+    {
+        spawned = g_spawn_async (NULL, command->argv, NULL,
+                                 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+                                 NULL, NULL, &command->pid, &error);
+        if (spawned)
+            g_child_watch_add (command->pid, (GChildWatchFunc)command_terminated_cb, command);
+    }
+
+    if(!spawned)
+    {
+        if (error)
+            g_warning ("Command spawning error: '%s'", error->message);
+        command->pid = 0;
+        gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (command->menu_item), FALSE);
+    }
+    g_clear_error(&error);
+    return spawned;
+}
+
+static gboolean
+menu_command_stop (MenuCommand *command)
+{
+    g_return_val_if_fail (command, FALSE);
+
+    if (command->pid)
+    {
+        kill (command->pid, SIGTERM);
+        g_spawn_close_pid (command->pid);
+        command->pid = 0;
+        if (command->menu_item)
+            gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (command->menu_item), FALSE);
+        if (command->widget)
+            gtk_widget_hide (command->widget);
+    }
+    return TRUE;
+}
+
+static void
+command_terminated_cb (GPid pid, gint status, MenuCommand *command)
+{
+    menu_command_stop (command);
+}
+
+static void
+a11y_menuitem_toggled_cb (GtkCheckMenuItem *item, const gchar* name)
+{
+    g_key_file_set_boolean (state, "a11y-states", name, gtk_check_menu_item_get_active (item));
+    save_state_file ();
+}
 
 static void
 pam_message_finalize (PAMConversationMessage *message)
@@ -915,10 +1135,6 @@ background_window_expose (GtkWidget    *
 static void
 start_authentication (const gchar *username)
 {
-    gchar *data;
-    gsize data_length;
-    GError *error = NULL;
-
     cancelling = FALSE;
     prompted = FALSE;
     password_prompted = FALSE;
@@ -931,18 +1147,7 @@ start_authentication (const gchar *usern
     }
 
     g_key_file_set_value (state, "greeter", "last-user", username);
-    data = g_key_file_to_data (state, &data_length, &error);
-    if (error)
-        g_warning ("Failed to save state file: %s", error->message);
-    g_clear_error (&error);
-    if (data)
-    {
-        g_file_set_contents (state_filename, data, data_length, &error);
-        if (error)
-            g_warning ("Failed to save state file: %s", error->message);
-        g_clear_error (&error);
-    }
-    g_free (data);
+    save_state_file ();
 
     if (g_strcmp0 (username, "*other") == 0)
     {
@@ -1025,9 +1230,6 @@ start_session (void)
 {
     gchar *language;
     gchar *session;
-    gchar *data;
-    gsize data_length;
-    GError *error = NULL;
 
     language = get_language ();
     if (language)
@@ -1038,19 +1240,7 @@ start_session (void)
 
     /* Remember last choice */
     g_key_file_set_value (state, "greeter", "last-session", session);
-
-    data = g_key_file_to_data (state, &data_length, &error);
-    if (error)
-        g_warning ("Failed to save state file: %s", error->message);
-    g_clear_error (&error);
-    if (data)
-    {
-        g_file_set_contents (state_filename, data, data_length, &error);
-        if (error)
-            g_warning ("Failed to save state file: %s", error->message);
-        g_clear_error (&error);
-    }
-    g_free (data);
+    save_state_file ();
 
     if (!lightdm_greeter_start_session_sync (greeter, session, NULL))
     {
@@ -1711,82 +1901,26 @@ a11y_contrast_cb (GtkCheckMenuItem *item
     }
 }
 
-static void
-keyboard_terminated_cb (GPid pid, gint status, gpointer user_data)
+void a11y_keyboard_cb (GtkCheckMenuItem *item, gpointer user_data);
+G_MODULE_EXPORT
+void
+a11y_keyboard_cb (GtkCheckMenuItem *item, gpointer user_data)
 {
-    gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (keyboard_menuitem), FALSE);
+    if (gtk_check_menu_item_get_active (item))
+        menu_command_run (a11y_keyboard_command);
+    else
+        menu_command_stop (a11y_keyboard_command);
 }
 
-void a11y_keyboard_cb (GtkCheckMenuItem *item);
+void a11y_reader_cb (GtkCheckMenuItem *item, gpointer user_data);
 G_MODULE_EXPORT
 void
-a11y_keyboard_cb (GtkCheckMenuItem *item)
+a11y_reader_cb (GtkCheckMenuItem *item, gpointer user_data)
 {
     if (gtk_check_menu_item_get_active (item))
-    {
-        gboolean spawned = FALSE;
-        if (onboard_window)
-        {
-            GtkSocket* socket = NULL;
-            gint out_fd = 0;
-
-            if (g_spawn_async_with_pipes (NULL, a11y_keyboard_command, NULL, G_SPAWN_SEARCH_PATH,
-                                          NULL, NULL, &a11y_kbd_pid, NULL, &out_fd, NULL,
-                                          &a11y_keyboard_error))
-            {
-                gchar* text = NULL;
-                GIOChannel* out_channel = g_io_channel_unix_new (out_fd);
-                if (g_io_channel_read_line(out_channel, &text, NULL, NULL, &a11y_keyboard_error) == G_IO_STATUS_NORMAL)
-                {
-                    gchar* end_ptr = NULL;
-
-                    text = g_strstrip (text);
-                    gint id = g_ascii_strtoll (text, &end_ptr, 0);
-
-                    if (id != 0 && end_ptr > text)
-                    {
-                        socket = GTK_SOCKET (gtk_socket_new ());
-                        gtk_container_add (GTK_CONTAINER (onboard_window), GTK_WIDGET (socket));
-                        gtk_socket_add_id (socket, id);
-                        gtk_widget_show_all (GTK_WIDGET (onboard_window));
-                        spawned = TRUE;
-                    }
-                    else
-                        g_debug ("onboard keyboard command error : 'unrecognized output'");
-
-                    g_free(text);
-                }
-            }
-        }
-        else
-        {
-            spawned = g_spawn_async (NULL, a11y_keyboard_command, NULL,
-                                     G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
-                                     NULL, NULL, &a11y_kbd_pid, &a11y_keyboard_error);
-            if (spawned)
-                g_child_watch_add (a11y_kbd_pid, keyboard_terminated_cb, NULL);
-        }
-
-        if(!spawned)
-        {
-            if (a11y_keyboard_error)
-                g_debug ("a11y keyboard command error : '%s'", a11y_keyboard_error->message);
-            a11y_kbd_pid = 0;
-            g_clear_error(&a11y_keyboard_error);
-            gtk_check_menu_item_set_active (item, FALSE);
-        }
-    }
+        menu_command_run (a11y_reader_command);
     else
-    {
-        if (a11y_kbd_pid != 0)
-        {
-            kill (a11y_kbd_pid, SIGTERM);
-            g_spawn_close_pid (a11y_kbd_pid);
-            a11y_kbd_pid = 0;
-            if (onboard_window)
-                gtk_widget_hide (GTK_WIDGET(onboard_window));
-        }
-    }
+        menu_command_stop (a11y_reader_command);
 }
 
 static void
@@ -2180,7 +2314,8 @@ static GdkFilterReturn
 focus_upon_map (GdkXEvent *gxevent, GdkEvent *event, gpointer  data)
 {
     XEvent* xevent = (XEvent*)gxevent;
-    GdkWindow* keyboard_win = onboard_window ? gtk_widget_get_window (GTK_WIDGET (onboard_window)) : NULL;
+    GdkWindow* keyboard_win = a11y_keyboard_command && a11y_keyboard_command->widget ?
+                                    gtk_widget_get_window (GTK_WIDGET (a11y_keyboard_command->widget)) : NULL;
     if (xevent->type == MapNotify)
     {
         Window xwin = xevent->xmap.window;
@@ -2203,11 +2338,8 @@ focus_upon_map (GdkXEvent *gxevent, GdkE
         {
             gdk_window_focus (win, GDK_CURRENT_TIME);
             /* Make sure to keep keyboard above */
-            if (onboard_window)
-            {
-                if (keyboard_win)
-                    gdk_window_raise (keyboard_win);
-            }
+            if (keyboard_win)
+                gdk_window_raise (keyboard_win);
         }
     }
     else if (xevent->type == UnmapNotify)
@@ -2220,11 +2352,8 @@ focus_upon_map (GdkXEvent *gxevent, GdkE
         {
             gdk_window_focus (gtk_widget_get_window (GTK_WIDGET (login_window)), GDK_CURRENT_TIME);
             /* Make sure to keep keyboard above */
-            if (onboard_window)
-            {
-                if (keyboard_win)
-                    gdk_window_raise (keyboard_win);
-            }
+            if (keyboard_win)
+                gdk_window_raise (keyboard_win);
         }
     }
     return GDK_FILTER_CONTINUE;
@@ -2239,7 +2368,7 @@ main (int argc, char **argv)
     const GList *items, *item;
     GtkCellRenderer *renderer;
     GtkWidget *image, *infobar_compat, *content_area;
-    gchar *value, *state_dir;
+    gchar *value, **values, *state_dir;
 #if GTK_CHECK_VERSION (3, 0, 0)
     GdkRGBA background_color;
     GtkIconTheme *icon_theme;
@@ -2415,15 +2544,7 @@ main (int argc, char **argv)
     if (value)
         g_object_set (gtk_settings_get_default (), "gtk-xft-rgba", value, NULL);
     g_free (value);
-    
-    /* Get a11y on screen keyboard command*/
-    gint argp;
-    value = g_key_file_get_value (config, "greeter", "keyboard", NULL);
-    g_debug ("a11y keyboard command is '%s'", value);
-    /* Set NULL to blank to avoid warnings */
-    if (!value) { value = g_strdup(""); }
-    g_shell_parse_argv (value, &argp, &a11y_keyboard_command, NULL);
-    g_free (value);
+
 
     builder = gtk_builder_new ();
     if (!gtk_builder_add_from_string (builder, lightdm_gtk_greeter_ui,
@@ -2453,8 +2574,6 @@ main (int argc, char **argv)
     gtk_style_context_add_provider (GTK_STYLE_CONTEXT(gtk_widget_get_style_context(GTK_WIDGET(menubar))), GTK_STYLE_PROVIDER (css_provider), 800);
 #endif
     
-    keyboard_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "keyboard_menuitem"));
-
     /* Login window */
     login_window = GTK_WINDOW (gtk_builder_get_object (builder, "login_window"));
     user_image = GTK_IMAGE (gtk_builder_get_object (builder, "user_image"));
@@ -2508,11 +2627,17 @@ main (int argc, char **argv)
     session_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "session_menuitem"));
     language_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "language_menuitem"));
     a11y_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "a11y_menuitem"));
+    contrast_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "high_contrast_menuitem"));
+    font_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "large_font_menuitem"));
+    keyboard_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "keyboard_menuitem"));
+    reader_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "reader_menuitem"));
+
     power_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "power_menuitem"));
 
     gtk_accel_map_add_entry ("<Login>/a11y/font", GDK_KEY_F1, 0);
     gtk_accel_map_add_entry ("<Login>/a11y/contrast", GDK_KEY_F2, 0);
     gtk_accel_map_add_entry ("<Login>/a11y/keyboard", GDK_KEY_F3, 0);
+    gtk_accel_map_add_entry ("<Login>/a11y/reader", GDK_KEY_F4, 0);
     gtk_accel_map_add_entry ("<Login>/power/shutdown", GDK_KEY_F4, GDK_MOD1_MASK);
 
 #ifdef START_INDICATOR_SERVICES
@@ -2630,6 +2755,20 @@ main (int argc, char **argv)
         gtk_container_add (GTK_CONTAINER (a11y_menuitem), image);
     }
 
+    value = g_key_file_get_value (config, "greeter", "keyboard", NULL);
+    a11y_keyboard_command = menu_command_parse_extended (value, keyboard_menuitem, "onboard", "--xid",
+                                                         &ONBOARD_WINDOW_SIZE);
+    g_free (value);
+
+    gtk_widget_set_visible (keyboard_menuitem, a11y_keyboard_command != NULL);
+    if (a11y_keyboard_command)
+        g_signal_connect (a11y_keyboard_command->widget, "size-allocate", G_CALLBACK (center_window), (gpointer)&ONBOARD_WINDOW_POS);
+
+    value = g_key_file_get_value (config, "greeter", "reader", NULL);
+    a11y_reader_command = menu_command_parse (value, reader_menuitem);
+    gtk_widget_set_visible (reader_menuitem, a11y_reader_command != NULL);
+    g_free (value);
+
     /* Power menu */
     if (gtk_widget_get_visible (power_menuitem))
     {
@@ -2745,22 +2884,44 @@ main (int argc, char **argv)
     gtk_widget_show (GTK_WIDGET (login_window));
     gdk_window_focus (gtk_widget_get_window (GTK_WIDGET (login_window)), GDK_CURRENT_TIME);
 
-    if (a11y_keyboard_command)
+    values = g_key_file_get_string_list (config, "greeter", "a11y-states", NULL, NULL);
+    if (values && *values)
     {
-        /* If command is onboard, position the application at the bottom-center of the screen */
-        if (g_strcmp0(a11y_keyboard_command[0], "onboard") == 0)
+        GHashTable *items = g_hash_table_new (g_str_hash, g_str_equal);
+        g_hash_table_insert (items, "contrast", contrast_menuitem);
+        g_hash_table_insert (items, "font", font_menuitem);
+        g_hash_table_insert (items, "keyboard", keyboard_menuitem);
+        g_hash_table_insert (items, "reader", reader_menuitem);
+
+        gpointer item;
+        gchar **values_iter;
+        for (values_iter = values; *values_iter; ++values_iter)
         {
-            gint argp;
-            value = "onboard --xid";
-            g_debug ("a11y keyboard command is now '%s'", value);
-            g_shell_parse_argv (value, &argp, &a11y_keyboard_command, NULL);
-            onboard_window = GTK_WINDOW (gtk_window_new(GTK_WINDOW_TOPLEVEL));
-            gtk_widget_set_size_request (GTK_WIDGET (onboard_window), 605, 205);
-            gtk_window_move (onboard_window, (monitor_geometry.width - 605)/2, monitor_geometry.height - 205);
+            value = *values_iter;
+            switch (value[0])
+            {
+            case '-':
+                continue;
+            case '+':
+                if (g_hash_table_lookup_extended (items, &value[1], NULL, &item) &&
+                    gtk_widget_get_visible (GTK_WIDGET (item)))
+                        gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
+                break;
+            case '~':
+                value++;
+            default:
+                if (g_hash_table_lookup_extended (items, value, NULL, &item) &&
+                    gtk_widget_get_visible (GTK_WIDGET (item)))
+                {
+                    gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
+                                                    g_key_file_get_boolean (state, "a11y-states", value, NULL));
+                    g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (a11y_menuitem_toggled_cb), g_strdup (value));
+                }
+            }
         }
+        g_hash_table_unref (items);
     }
-    gtk_widget_set_sensitive (keyboard_menuitem, a11y_keyboard_command != NULL);
-    gtk_widget_set_visible (keyboard_menuitem, a11y_keyboard_command != NULL);
+    g_strfreev (values);
     gdk_threads_add_timeout (1000, (GSourceFunc) clock_timeout_thread, NULL);
 
     /* focus fix (source: unity-greeter) */
Index: src/lightdm-gtk-greeter.glade
===================================================================
--- src/lightdm-gtk-greeter.glade.orig
+++ src/lightdm-gtk-greeter.glade
@@ -136,6 +136,17 @@
                         <signal name="toggled" handler="a11y_keyboard_cb" swapped="no"/>
                       </object>
                     </child>
+                    <child>
+                      <object class="GtkCheckMenuItem" id="reader_menuitem">
+                        <property name="use_action_appearance">False</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="accel_path">&lt;Login&gt;/a11y/reader</property>
+                        <property name="label" translatable="yes">Screen Reader</property>
+                        <property name="use_underline">True</property>
+                        <signal name="toggled" handler="a11y_reader_cb" swapped="no"/>
+                      </object>
+                    </child>
                   </object>
                 </child>
               </object>
Index: src/lightdm-gtk-greeter-ui.h
===================================================================
--- src/lightdm-gtk-greeter-ui.h.orig
+++ src/lightdm-gtk-greeter-ui.h
@@ -80,118 +80,124 @@ static const char lightdm_gtk_greeter_ui
   "ty><property name=\"label\" translatable=\"yes\">On Screen Keyboard</pr"
   "operty><property name=\"use_underline\">True</property><signal name=\"t"
   "oggled\" handler=\"a11y_keyboard_cb\" swapped=\"no\"/></object></child>"
-  "</object></child></object></child><child><object class=\"GtkMenuItem\" "
-  "id=\"language_menuitem\"><property name=\"visible\">True</property><pro"
-  "perty name=\"can_focus\">True</property><property name=\"label\">[langu"
-  "age_code]</property><child type=\"submenu\"><object class=\"GtkMenu\" i"
-  "d=\"language_menu\"><property name=\"visible\">True</property><property"
-  " name=\"can_focus\">False</property></object></child></object></child><"
-  "child><object class=\"GtkMenuItem\" id=\"session_menuitem\"><property n"
-  "ame=\"visible\">True</property><property name=\"can_focus\">True</prope"
-  "rty><child type=\"submenu\"><object class=\"GtkMenu\" id=\"session_menu"
-  "\"><property name=\"visible\">True</property><property name=\"can_focus"
-  "\">False</property></object></child></object></child></object><packing>"
-  "<property name=\"expand\">False</property><property name=\"fill\">True<"
-  "/property><property name=\"pack_type\">end</property><property name=\"p"
-  "osition\">2</property></packing></child></object></child></object><obje"
-  "ct class=\"GtkListStore\" id=\"user_liststore\"><columns><column type=\""
-  "gchararray\"/><column type=\"gchararray\"/><column type=\"gint\"/></col"
-  "umns></object><object class=\"GtkWindow\" id=\"login_window\"><property"
-  " name=\"name\">login_window</property><property name=\"can_focus\">Fals"
-  "e</property><property name=\"resizable\">False</property><property name"
-  "=\"decorated\">False</property><accel-groups><group name=\"a11y_accelgr"
-  "oup\"/><group name=\"power_accelgroup\"/></accel-groups><signal name=\""
-  "key-press-event\" handler=\"login_window_key_press_cb\" swapped=\"no\"/"
-  "><child><object class=\"GtkVBox\" id=\"vbox2\"><property name=\"visible"
-  "\">True</property><property name=\"can_focus\">False</property><child><"
-  "object class=\"GtkFrame\" id=\"content_frame\"><property name=\"name\">"
-  "content_frame</property><property name=\"visible\">True</property><prop"
-  "erty name=\"can_focus\">False</property><property name=\"label_xalign\""
-  ">0</property><property name=\"shadow_type\">none</property><child><obje"
-  "ct class=\"GtkTable\" id=\"table1\"><property name=\"visible\">True</pr"
-  "operty><property name=\"can_focus\">False</property><property name=\"ma"
-  "rgin_left\">24</property><property name=\"margin_right\">24</property><"
-  "property name=\"margin_top\">24</property><property name=\"n_rows\">3</"
-  "property><property name=\"n_columns\">2</property><property name=\"colu"
-  "mn_spacing\">18</property><property name=\"row_spacing\">6</property><c"
-  "hild><object class=\"GtkComboBox\" id=\"user_combobox\"><property name="
-  "\"name\">user_combobox</property><property name=\"width_request\">200</"
-  "property><property name=\"can_focus\">False</property><property name=\""
-  "model\">user_liststore</property><signal name=\"changed\" handler=\"use"
-  "r_combobox_active_changed_cb\" swapped=\"no\"/></object><packing><prope"
-  "rty name=\"left_attach\">1</property><property name=\"right_attach\">2<"
-  "/property><property name=\"y_options\"/></packing></child><child><objec"
-  "t class=\"GtkEntry\" id=\"password_entry\"><property name=\"name\">prom"
-  "pt_entry</property><property name=\"width_request\">200</property><prop"
-  "erty name=\"visible\">True</property><property name=\"can_focus\">True<"
-  "/property><property name=\"visibility\">False</property><property name="
-  "\"invisible_char\">\342\200\242</property><property name=\"primary_icon"
-  "_activatable\">False</property><property name=\"secondary_icon_activata"
-  "ble\">False</property><signal name=\"activate\" handler=\"login_cb\" sw"
-  "apped=\"no\"/><signal name=\"key-press-event\" handler=\"password_key_p"
-  "ress_cb\" swapped=\"no\"/></object><packing><property name=\"left_attac"
-  "h\">1</property><property name=\"right_attach\">2</property><property n"
-  "ame=\"top_attach\">2</property><property name=\"bottom_attach\">3</prop"
-  "erty><property name=\"y_options\"/></packing></child><child><object cla"
-  "ss=\"GtkEntry\" id=\"username_entry\"><property name=\"name\">prompt_en"
-  "try</property><property name=\"can_focus\">True</property><property nam"
-  "e=\"no_show_all\">True</property><property name=\"invisible_char\">\342"
-  "\200\242</property><signal name=\"focus-out-event\" handler=\"username_"
-  "focus_out_cb\" swapped=\"no\"/><signal name=\"key-press-event\" handler"
-  "=\"username_key_press_cb\" swapped=\"no\"/></object><packing><property "
-  "name=\"left_attach\">1</property><property name=\"right_attach\">2</pro"
-  "perty><property name=\"top_attach\">1</property><property name=\"bottom"
-  "_attach\">2</property></packing></child><child><object class=\"GtkFrame"
-  "\" id=\"user_image_border\"><property name=\"name\">user_image_border</"
-  "property><property name=\"visible\">True</property><property name=\"can"
-  "_focus\">False</property><property name=\"label_xalign\">0</property><p"
-  "roperty name=\"shadow_type\">none</property><child><object class=\"GtkI"
-  "mage\" id=\"user_image\"><property name=\"name\">user_image</property><"
-  "property name=\"visible\">True</property><property name=\"can_focus\">F"
-  "alse</property><property name=\"pixel_size\">80</property><property nam"
-  "e=\"icon_name\">avatar-default</property></object></child></object><pac"
-  "king><property name=\"bottom_attach\">3</property><property name=\"x_op"
-  "tions\"/><property name=\"y_options\"/></packing></child></object></chi"
-  "ld><child type=\"label_item\"><placeholder/></child></object><packing><"
-  "property name=\"expand\">True</property><property name=\"fill\">True</p"
-  "roperty><property name=\"position\">0</property></packing></child><chil"
-  "d><object class=\"GtkAlignment\" id=\"infobar_compat\"><property name=\""
-  "visible\">True</property><property name=\"can_focus\">False</property><"
-  "child><object class=\"GtkLabel\" id=\"message_label\"><property name=\""
+  "<child><object class=\"GtkCheckMenuItem\" id=\"reader_menuitem\"><prope"
+  "rty name=\"use_action_appearance\">False</property><property name=\"vis"
+  "ible\">True</property><property name=\"can_focus\">False</property><pro"
+  "perty name=\"accel_path\">&lt;Login&gt;/a11y/reader</property><property"
+  " name=\"label\" translatable=\"yes\">Screen Reader</property><property "
+  "name=\"use_underline\">True</property><signal name=\"toggled\" handler="
+  "\"a11y_reader_cb\" swapped=\"no\"/></object></child></object></child></"
+  "object></child><child><object class=\"GtkMenuItem\" id=\"language_menui"
+  "tem\"><property name=\"visible\">True</property><property name=\"can_fo"
+  "cus\">True</property><property name=\"label\">[language_code]</property"
+  "><child type=\"submenu\"><object class=\"GtkMenu\" id=\"language_menu\""
+  "><property name=\"visible\">True</property><property name=\"can_focus\""
+  ">False</property></object></child></object></child><child><object class"
+  "=\"GtkMenuItem\" id=\"session_menuitem\"><property name=\"visible\">Tru"
+  "e</property><property name=\"can_focus\">True</property><child type=\"s"
+  "ubmenu\"><object class=\"GtkMenu\" id=\"session_menu\"><property name=\""
   "visible\">True</property><property name=\"can_focus\">False</property><"
-  "property name=\"label\" comments=\"This is a placeholder string and wil"
-  "l be replaced with a message from PAM\">[message]</property></object></"
-  "child></object><packing><property name=\"expand\">True</property><prope"
-  "rty name=\"fill\">True</property><property name=\"position\">1</propert"
-  "y></packing></child><child><object class=\"GtkFrame\" id=\"buttonbox_fr"
-  "ame\"><property name=\"name\">buttonbox_frame</property><property name="
-  "\"visible\">True</property><property name=\"can_focus\">False</property"
-  "><property name=\"label_xalign\">0</property><property name=\"shadow_ty"
-  "pe\">none</property><child><object class=\"GtkHBox\" id=\"hbox2\"><prop"
-  "erty name=\"visible\">True</property><property name=\"can_focus\">False"
-  "</property><property name=\"margin_left\">24</property><property name=\""
-  "margin_right\">24</property><property name=\"margin_bottom\">24</proper"
-  "ty><child><object class=\"GtkButton\" id=\"login_button\"><property nam"
-  "e=\"label\" translatable=\"yes\">Log In</property><property name=\"name"
-  "\">login_button</property><property name=\"visible\">True</property><pr"
-  "operty name=\"can_focus\">True</property><property name=\"receives_defa"
-  "ult\">True</property><signal name=\"clicked\" handler=\"login_cb\" swap"
-  "ped=\"no\"/></object><packing><property name=\"expand\">False</property"
-  "><property name=\"fill\">True</property><property name=\"pack_type\">en"
-  "d</property><property name=\"position\">0</property></packing></child><"
-  "child><object class=\"GtkButton\" id=\"cancel_button\"><property name=\""
-  "label\" translatable=\"yes\">Cancel</property><property name=\"name\">c"
-  "ancel_button</property><property name=\"visible\">True</property><prope"
-  "rty name=\"can_focus\">True</property><property name=\"receives_default"
-  "\">True</property><signal name=\"clicked\" handler=\"cancel_cb\" swappe"
-  "d=\"no\"/></object><packing><property name=\"expand\">False</property><"
-  "property name=\"fill\">True</property><property name=\"position\">1</pr"
-  "operty></packing></child></object></child><child type=\"label_item\"><p"
-  "laceholder/></child></object><packing><property name=\"expand\">True</p"
-  "roperty><property name=\"fill\">True</property><property name=\"positio"
-  "n\">2</property></packing></child></object></child></object></interface"
-  ">"
+  "/object></child></object></child></object><packing><property name=\"exp"
+  "and\">False</property><property name=\"fill\">True</property><property "
+  "name=\"pack_type\">end</property><property name=\"position\">2</propert"
+  "y></packing></child></object></child></object><object class=\"GtkListSt"
+  "ore\" id=\"user_liststore\"><columns><column type=\"gchararray\"/><colu"
+  "mn type=\"gchararray\"/><column type=\"gint\"/></columns></object><obje"
+  "ct class=\"GtkWindow\" id=\"login_window\"><property name=\"name\">logi"
+  "n_window</property><property name=\"can_focus\">False</property><proper"
+  "ty name=\"resizable\">False</property><property name=\"decorated\">Fals"
+  "e</property><accel-groups><group name=\"a11y_accelgroup\"/><group name="
+  "\"power_accelgroup\"/></accel-groups><signal name=\"key-press-event\" h"
+  "andler=\"login_window_key_press_cb\" swapped=\"no\"/><child><object cla"
+  "ss=\"GtkVBox\" id=\"vbox2\"><property name=\"visible\">True</property><"
+  "property name=\"can_focus\">False</property><child><object class=\"GtkF"
+  "rame\" id=\"content_frame\"><property name=\"name\">content_frame</prop"
+  "erty><property name=\"visible\">True</property><property name=\"can_foc"
+  "us\">False</property><property name=\"label_xalign\">0</property><prope"
+  "rty name=\"shadow_type\">none</property><child><object class=\"GtkTable"
+  "\" id=\"table1\"><property name=\"visible\">True</property><property na"
+  "me=\"can_focus\">False</property><property name=\"margin_left\">24</pro"
+  "perty><property name=\"margin_right\">24</property><property name=\"mar"
+  "gin_top\">24</property><property name=\"n_rows\">3</property><property "
+  "name=\"n_columns\">2</property><property name=\"column_spacing\">18</pr"
+  "operty><property name=\"row_spacing\">6</property><child><object class="
+  "\"GtkComboBox\" id=\"user_combobox\"><property name=\"name\">user_combo"
+  "box</property><property name=\"width_request\">200</property><property "
+  "name=\"can_focus\">False</property><property name=\"model\">user_listst"
+  "ore</property><signal name=\"changed\" handler=\"user_combobox_active_c"
+  "hanged_cb\" swapped=\"no\"/></object><packing><property name=\"left_att"
+  "ach\">1</property><property name=\"right_attach\">2</property><property"
+  " name=\"y_options\"/></packing></child><child><object class=\"GtkEntry\""
+  " id=\"password_entry\"><property name=\"name\">prompt_entry</property><"
+  "property name=\"width_request\">200</property><property name=\"visible\""
+  ">True</property><property name=\"can_focus\">True</property><property n"
+  "ame=\"visibility\">False</property><property name=\"invisible_char\">\342"
+  "\200\242</property><property name=\"primary_icon_activatable\">False</p"
+  "roperty><property name=\"secondary_icon_activatable\">False</property><"
+  "signal name=\"activate\" handler=\"login_cb\" swapped=\"no\"/><signal n"
+  "ame=\"key-press-event\" handler=\"password_key_press_cb\" swapped=\"no\""
+  "/></object><packing><property name=\"left_attach\">1</property><propert"
+  "y name=\"right_attach\">2</property><property name=\"top_attach\">2</pr"
+  "operty><property name=\"bottom_attach\">3</property><property name=\"y_"
+  "options\"/></packing></child><child><object class=\"GtkEntry\" id=\"use"
+  "rname_entry\"><property name=\"name\">prompt_entry</property><property "
+  "name=\"can_focus\">True</property><property name=\"no_show_all\">True</"
+  "property><property name=\"invisible_char\">\342\200\242</property><sign"
+  "al name=\"focus-out-event\" handler=\"username_focus_out_cb\" swapped=\""
+  "no\"/><signal name=\"key-press-event\" handler=\"username_key_press_cb\""
+  " swapped=\"no\"/></object><packing><property name=\"left_attach\">1</pr"
+  "operty><property name=\"right_attach\">2</property><property name=\"top"
+  "_attach\">1</property><property name=\"bottom_attach\">2</property></pa"
+  "cking></child><child><object class=\"GtkFrame\" id=\"user_image_border\""
+  "><property name=\"name\">user_image_border</property><property name=\"v"
+  "isible\">True</property><property name=\"can_focus\">False</property><p"
+  "roperty name=\"label_xalign\">0</property><property name=\"shadow_type\""
+  ">none</property><child><object class=\"GtkImage\" id=\"user_image\"><pr"
+  "operty name=\"name\">user_image</property><property name=\"visible\">Tr"
+  "ue</property><property name=\"can_focus\">False</property><property nam"
+  "e=\"pixel_size\">80</property><property name=\"icon_name\">avatar-defau"
+  "lt</property></object></child></object><packing><property name=\"bottom"
+  "_attach\">3</property><property name=\"x_options\"/><property name=\"y_"
+  "options\"/></packing></child></object></child><child type=\"label_item\""
+  "><placeholder/></child></object><packing><property name=\"expand\">True"
+  "</property><property name=\"fill\">True</property><property name=\"posi"
+  "tion\">0</property></packing></child><child><object class=\"GtkAlignmen"
+  "t\" id=\"infobar_compat\"><property name=\"visible\">True</property><pr"
+  "operty name=\"can_focus\">False</property><child><object class=\"GtkLab"
+  "el\" id=\"message_label\"><property name=\"visible\">True</property><pr"
+  "operty name=\"can_focus\">False</property><property name=\"label\" comm"
+  "ents=\"This is a placeholder string and will be replaced with a message"
+  " from PAM\">[message]</property></object></child></object><packing><pro"
+  "perty name=\"expand\">True</property><property name=\"fill\">True</prop"
+  "erty><property name=\"position\">1</property></packing></child><child><"
+  "object class=\"GtkFrame\" id=\"buttonbox_frame\"><property name=\"name\""
+  ">buttonbox_frame</property><property name=\"visible\">True</property><p"
+  "roperty name=\"can_focus\">False</property><property name=\"label_xalig"
+  "n\">0</property><property name=\"shadow_type\">none</property><child><o"
+  "bject class=\"GtkHBox\" id=\"hbox2\"><property name=\"visible\">True</p"
+  "roperty><property name=\"can_focus\">False</property><property name=\"m"
+  "argin_left\">24</property><property name=\"margin_right\">24</property>"
+  "<property name=\"margin_bottom\">24</property><child><object class=\"Gt"
+  "kButton\" id=\"login_button\"><property name=\"label\" translatable=\"y"
+  "es\">Log In</property><property name=\"name\">login_button</property><p"
+  "roperty name=\"visible\">True</property><property name=\"can_focus\">Tr"
+  "ue</property><property name=\"receives_default\">True</property><signal"
+  " name=\"clicked\" handler=\"login_cb\" swapped=\"no\"/></object><packin"
+  "g><property name=\"expand\">False</property><property name=\"fill\">Tru"
+  "e</property><property name=\"pack_type\">end</property><property name=\""
+  "position\">0</property></packing></child><child><object class=\"GtkButt"
+  "on\" id=\"cancel_button\"><property name=\"label\" translatable=\"yes\""
+  ">Cancel</property><property name=\"name\">cancel_button</property><prop"
+  "erty name=\"visible\">True</property><property name=\"can_focus\">True<"
+  "/property><property name=\"receives_default\">True</property><signal na"
+  "me=\"clicked\" handler=\"cancel_cb\" swapped=\"no\"/></object><packing>"
+  "<property name=\"expand\">False</property><property name=\"fill\">True<"
+  "/property><property name=\"position\">1</property></packing></child></o"
+  "bject></child><child type=\"label_item\"><placeholder/></child></object"
+  "><packing><property name=\"expand\">True</property><property name=\"fil"
+  "l\">True</property><property name=\"position\">2</property></packing></"
+  "child></object></child></object></interface>"
 };
 
-static const unsigned lightdm_gtk_greeter_ui_length = 12254u;
+static const unsigned lightdm_gtk_greeter_ui_length = 12708u;
 


Reply to: