← Back to team overview

lightdm-gtk-greeter-team team mailing list archive

[Merge] lp:~lightdm-gtk-greeter-team/lightdm-gtk-greeter/lp-1319848-states-and-orca into lp:lightdm-gtk-greeter

 

Andrew P. has proposed merging lp:~lightdm-gtk-greeter-team/lightdm-gtk-greeter/lp-1319848-states-and-orca into lp:lightdm-gtk-greeter.

Requested reviews:
  LightDM Gtk+ Greeter Development Team (lightdm-gtk-greeter-team)
Related bugs:
  Bug #1319848 in LightDM GTK+ Greeter: "Key to toggle Orca screen reader"
  https://bugs.launchpad.net/lightdm-gtk-greeter/+bug/1319848

For more details, see:
https://code.launchpad.net/~lightdm-gtk-greeter-team/lightdm-gtk-greeter/lp-1319848-states-and-orca/+merge/225811

1. New option:
reader=screen-reader-program

Adds new item to a11y menu: Screen Reader (F4 key).

2. New option:
a11y-states=list-of-a11y-features

Controls state of a11y tools at startup. Allowed items: contrast;font;keyboard;reader.

"name" - save state on exit and restore it at start
"-name" - always disabled at start (default value)
"+name" - always enabled at start

Start screen reader at start and use saved state of "contrast" menu item:
a11y-states=+reader;contrast

-- 
https://code.launchpad.net/~lightdm-gtk-greeter-team/lightdm-gtk-greeter/lp-1319848-states-and-orca/+merge/225811
Your team LightDM Gtk+ Greeter Development Team is requested to review the proposed merge of lp:~lightdm-gtk-greeter-team/lightdm-gtk-greeter/lp-1319848-states-and-orca into lp:lightdm-gtk-greeter.
=== modified file 'data/lightdm-gtk-greeter.conf'
--- data/lightdm-gtk-greeter.conf	2014-02-09 20:03:35 +0000
+++ data/lightdm-gtk-greeter.conf	2014-07-07 11:37:57 +0000
@@ -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)
+# keyboard = 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-clock=
 #clock-format=
 #keyboard=
+#reader=
 #position=
 #screensaver-timeout=

=== modified file 'src/lightdm-gtk-greeter.c'
--- src/lightdm-gtk-greeter.c	2014-07-05 16:46:13 +0000
+++ src/lightdm-gtk-greeter.c	2014-07-07 11:37:57 +0000
@@ -64,8 +64,10 @@
 #endif
 
 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;
@@ -78,7 +80,8 @@
 static GtkWidget *power_menuitem, *session_menuitem, *language_menuitem, *a11y_menuitem,
                  *layout_menuitem, *session_badge;
 static GtkWidget *suspend_menuitem, *hibernate_menuitem, *restart_menuitem, *shutdown_menuitem;
-static GtkWidget *keyboard_menuitem, *clock_menuitem, *clock_label, *host_menuitem;
+static GtkWidget *clock_menuitem, *clock_label, *host_menuitem;
+static GtkWidget *contrast_menuitem, *font_menuitem, *keyboard_menuitem, *reader_menuitem;
 static GtkMenu *session_menu, *language_menu, *layout_menu;
 
 /* Login Window Widgets */
@@ -91,10 +94,52 @@
 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;
@@ -130,25 +175,6 @@
   gchar *text;
 } PAMConversationMessage;
 
-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;
-
-static const WindowPosition CENTERED_WINDOW_POS = { .x = {50, +1, TRUE, 0}, .y = {50, +1, TRUE, 0} };
-static WindowPosition main_window_pos;
-
 static GdkPixbuf* default_user_pixbuf = NULL;
 static gchar* default_user_icon = "avatar-default";
 
@@ -197,6 +223,201 @@
 static const gchar *INDICATOR_DATA_MENUITEMS      = "indicator-data-menuitems";
 #endif
 
+
+static void
+save_state_file (void)
+{
+    GError *error = NULL;
+    gsize data_length = 0;
+    gchar *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);
+    }
+}
+
+/* MenuCommand */
+
+static MenuCommand*
+menu_command_parse (const gchar *value, GtkWidget *menu_item)
+{
+    return menu_command_parse_extended (value, menu_item, NULL, NULL, NULL);
+}
+
+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;
+
+    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)
 {
@@ -734,9 +955,7 @@
     for (menu_iter = menu_items; menu_iter != NULL; menu_iter = g_list_next(menu_iter))
     {
         if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_iter->data)))
-        {
             return g_strdup(g_object_get_data (G_OBJECT (menu_iter->data), LANGUAGE_DATA_CODE));
-        }
     }
 
     return NULL;
@@ -792,7 +1011,9 @@
         {
             if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_iter->data)))
             {
-                gtk_menu_item_set_label (GTK_MENU_ITEM (language_menuitem), g_strdup (g_object_get_data (G_OBJECT (menu_iter->data), LANGUAGE_DATA_CODE)));
+                gtk_menu_item_set_label (GTK_MENU_ITEM (language_menuitem),
+                                         g_strdup (g_object_get_data (G_OBJECT (menu_iter->data),
+                                                                      LANGUAGE_DATA_CODE)));
                 break;
             }
         }
@@ -870,6 +1091,7 @@
 
     if (username)
         user = lightdm_user_list_get_user_by_name (lightdm_user_list_get_instance (), username);
+
     if (user)
     {
         path = lightdm_user_get_image (user);
@@ -896,7 +1118,6 @@
         gtk_image_set_from_icon_name (GTK_IMAGE (user_image), default_user_icon, GTK_ICON_SIZE_DIALOG);
 }
 
-/* Function translate user defined coordinates to absolute value */
 static gint
 get_absolute_position (const DimensionPosition *p, gint screen, gint window)
 {
@@ -987,7 +1208,7 @@
 
     region = gdk_region_rectangle (&rect);
 
-    while(x >= y)
+    while (x >= y)
     {
 
         rect.x = -x + radius;
@@ -1002,12 +1223,12 @@
         rect.width = y - radius + width - rect.x;
         rect.height =  x - radius + height - rect.y;
 
-        gdk_region_union_with_rect(region, &rect);
+        gdk_region_union_with_rect (region, &rect);
 
         y++;
         radiusError += yChange;
         yChange += 2;
-        if(((radiusError << 1) + xChange) > 0)
+        if (((radiusError << 1) + xChange) > 0)
         {
             x--;
             radiusError += xChange;
@@ -1025,9 +1246,10 @@
 
     GdkWindow *window = gtk_widget_get_window (widget);
     if (window_region)
-        gdk_region_destroy(window_region);
+        gdk_region_destroy (window_region);
     window_region = cairo_region_from_rectangle (allocation->width, allocation->height, radius);
-    if (window) {
+    if (window)
+    {
         gdk_window_shape_combine_region(window, window_region, 0, 0);
         gdk_window_input_shape_combine_region(window, window_region, 0, 0);
     }
@@ -1053,10 +1275,6 @@
 static void
 start_authentication (const gchar *username)
 {
-    gchar *data;
-    gsize data_length;
-    GError *error = NULL;
-
     cancelling = FALSE;
     prompted = FALSE;
     password_prompted = FALSE;
@@ -1069,18 +1287,7 @@
     }
 
     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)
     {
@@ -1163,9 +1370,6 @@
 {
     gchar *language;
     gchar *session;
-    gchar *data;
-    gsize data_length;
-    GError *error = NULL;
 
     language = get_language ();
     if (language)
@@ -1176,19 +1380,7 @@
 
     /* 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))
     {
@@ -1851,82 +2043,26 @@
     }
 }
 
-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
@@ -2320,7 +2456,8 @@
 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;
@@ -2343,11 +2480,8 @@
         {
             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)
@@ -2360,11 +2494,8 @@
         {
             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;
@@ -2553,7 +2684,7 @@
     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;
@@ -2734,15 +2865,7 @@
     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,
@@ -2816,16 +2939,20 @@
     language_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "language_menuitem"));
     language_menu = GTK_MENU(gtk_builder_get_object (builder, "language_menu"));
     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"));
     layout_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "layout_menuitem"));
     layout_menu = GTK_MENU(gtk_builder_get_object (builder, "layout_menu"));
-    keyboard_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "keyboard_menuitem"));
     clock_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "clock_menuitem"));
     host_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "host_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
@@ -2934,6 +3061,20 @@
         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))
     {
@@ -3115,22 +3256,44 @@
     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);
 
     /* focus fix (source: unity-greeter) */
     GdkWindow* root_window = gdk_get_default_root_window ();

=== modified file 'src/lightdm-gtk-greeter.glade'
--- src/lightdm-gtk-greeter.glade	2014-06-09 16:14:37 +0000
+++ src/lightdm-gtk-greeter.glade	2014-07-07 11:37:57 +0000
@@ -108,6 +108,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>


Follow ups