← Back to team overview

lightdm-gtk-greeter-team team mailing list archive

[Merge] lp:~kalgasnik/lightdm-gtk-greeter/screenshot-r316 into lp:lightdm-gtk-greeter

 

Andrew P. has proposed merging lp:~kalgasnik/lightdm-gtk-greeter/screenshot-r316 into lp:lightdm-gtk-greeter.

Requested reviews:
  LightDM Gtk+ Greeter Development Team (lightdm-gtk-greeter-team)

For more details, see:
https://code.launchpad.net/~kalgasnik/lightdm-gtk-greeter/screenshot-r316/+merge/246953

1. Keyboard accelerators: controlled by greeter without passing them to GtkWindow
   Some old code removed, logic of map/unmap changed.

2. PrintScreen: taking screenshot.
   Option: max-screenshots-count = [integer_value]
value > 0: save only last [value] screenshots and remove old files
-1: allow any count of files in screenshots directory
 0: disable PrintScreen key

Default value: 10.

Path to save files: /var/tmp/lightdm-gtk-greeter-screenshots or %TMP/lightdm-gtk-greeter-screenshots (if %TMP defined).

TODO: libcanberra to play capture/error sound.
-- 
Your team LightDM Gtk+ Greeter Development Team is requested to review the proposed merge of lp:~kalgasnik/lightdm-gtk-greeter/screenshot-r316 into lp:lightdm-gtk-greeter.
=== modified file 'src/greeterbackground.c'
--- src/greeterbackground.c	2014-12-10 07:59:02 +0000
+++ src/greeterbackground.c	2015-01-19 21:41:07 +0000
@@ -58,7 +58,7 @@
 } BackgroundConfig;
 
 /* Transition configuration
-   Used to as part of <MonitorConfig> and <Monitor> */
+   Used as part of <MonitorConfig> and <Monitor> */
 typedef struct
 {
     /* Transition duration, in ms */
@@ -144,8 +144,6 @@
 
     /* Widget to display on active monitor */
     GtkWidget* child;
-    /* List of groups <GtkAccelGroup*> for greeter screens windows */
-    GSList* accel_groups;
 
     /* Mapping monitor name <gchar*> to its config <MonitorConfig*> */
     GHashTable* configs;
@@ -296,7 +294,7 @@
 static void set_surface_as_root                     (GdkScreen* screen,
                                                      cairo_surface_t* surface);
 static gdouble transition_func_linear               (gdouble x);
-static gdouble transition_func_easy_in_out          (gdouble x);
+static gdouble transition_func_ease_in_out          (gdouble x);
 
 /* Implemented in lightdm-gtk-greeter.c */
 gpointer greeter_save_focus(GtkWidget* widget);
@@ -317,7 +315,7 @@
     .transition =
     {
         .duration = 500,
-        .func = transition_func_easy_in_out,
+        .func = transition_func_ease_in_out,
         .draw = (TransitionDraw)monitor_transition_draw_alpha
     }
 };
@@ -345,7 +343,6 @@
     self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, GREETER_BACKGROUND_TYPE, GreeterBackgroundPrivate);
     self->priv->screen = NULL;
     self->priv->screen_monitors_changed_handler_id = 0;
-    self->priv->accel_groups = NULL;
 
     self->priv->configs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)monitor_config_free);
     self->priv->default_config = monitor_config_copy(&DEFAULT_MONITOR_CONFIG, NULL);
@@ -439,7 +436,7 @@
             priv->transition_types = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
             g_hash_table_insert(priv->transition_types, g_strdup("none"), NULL);
             g_hash_table_insert(priv->transition_types, g_strdup("linear"), transition_func_linear);
-            g_hash_table_insert(priv->transition_types, g_strdup("easy-in-out"), transition_func_easy_in_out);
+            g_hash_table_insert(priv->transition_types, g_strdup("ease-in-out"), transition_func_ease_in_out);
         }
         if(!g_hash_table_lookup_extended(priv->transition_types, transition_type, NULL, (gpointer*)&config->transition.func))
         {
@@ -574,10 +571,6 @@
         gtk_style_context_add_class(gtk_widget_get_style_context(GTK_WIDGET(monitor->window)), "lightdm-gtk-greeter");
         g_free(window_name);
 
-        GSList* item;
-        for(item = priv->accel_groups; item != NULL; item = g_slist_next(item))
-            gtk_window_add_accel_group(monitor->window, item->data);
-
         if(priv->follow_cursor)
             g_signal_connect(G_OBJECT(monitor->window), "enter-notify-event",
                              G_CALLBACK(monitor_window_enter_notify_cb), monitor);
@@ -1031,25 +1024,6 @@
     return priv->active_monitor ? &priv->active_monitor->geometry : NULL;
 }
 
-void
-greeter_background_add_accel_group(GreeterBackground* background,
-                                   GtkAccelGroup* group)
-{
-    g_return_if_fail(GREETER_IS_BACKGROUND(background));
-    g_return_if_fail(group != NULL);
-    GreeterBackgroundPrivate* priv = background->priv;
-
-    if(priv->monitors)
-    {
-        gint i;
-        for(i = 0; i < priv->monitors_size; ++i)
-            if(priv->monitors[i].window)
-                gtk_window_add_accel_group(priv->monitors[i].window, group);
-    }
-
-    priv->accel_groups = g_slist_append(priv->accel_groups, group);
-}
-
 static gboolean
 background_config_initialize(BackgroundConfig* config,
                              const gchar* value)
@@ -1633,7 +1607,7 @@
 }
 
 static gdouble
-transition_func_easy_in_out(gdouble x)
+transition_func_ease_in_out(gdouble x)
 {
     return (1 - cos(M_PI*x))/2;
 }

=== modified file 'src/greeterbackground.h'
--- src/greeterbackground.h	2014-11-03 10:39:58 +0000
+++ src/greeterbackground.h	2015-01-19 21:41:07 +0000
@@ -38,8 +38,6 @@
                                                      const gchar* path);
 void greeter_background_save_xroot                  (GreeterBackground* background);
 const GdkRectangle* greeter_background_get_active_monitor_geometry(GreeterBackground* background);
-void greeter_background_add_accel_group             (GreeterBackground* background,
-                                                     GtkAccelGroup* group);
 
 G_END_DECLS
 

=== modified file 'src/lightdm-gtk-greeter-fallback.css'
--- src/lightdm-gtk-greeter-fallback.css	2014-05-22 19:52:34 +0000
+++ src/lightdm-gtk-greeter-fallback.css	2015-01-19 21:41:07 +0000
@@ -1,12 +1,23 @@
 #layout_menuitem>GtkLabel
 {
-  border: 1px solid alpha(@menu_fg_color, 0.8);
-  border-radius: 0.5em;
-  padding: 0px 0.25em;
-  background: alpha(@menu_fg_color, 0.2);
+    border: 1px solid alpha(@menu_fg_color, 0.8);
+    border-radius: 0.5em;
+    padding: 0px 0.25em;
+    background: alpha(@menu_fg_color, 0.2);
 }
 
 #layout_menuitem
 {
-  padding: 1px;
+    padding: 1px;
+}
+
+/* Used as "screen-capture" effect */
+#capture-splash.capture-triggered
+{
+    background: alpha(@theme_base_color, 0.5);
+}
+
+#screen
+{
+    background: transparent;
 }

=== modified file 'src/lightdm-gtk-greeter.c'
--- src/lightdm-gtk-greeter.c	2015-01-17 13:45:58 +0000
+++ src/lightdm-gtk-greeter.c	2015-01-19 21:41:07 +0000
@@ -68,6 +68,7 @@
 
 /* Screen window */
 static GtkOverlay *screen_overlay;
+static GtkAccelGroup *greeter_accel_group;
 
 /* Login window */
 static GtkWidget    *login_window;
@@ -81,9 +82,10 @@
 /* Panel */
 static GtkWidget    *panel_window, *menubar;
 static GtkWidget    *power_menuitem, *session_menuitem, *language_menuitem, *a11y_menuitem,
-                    *layout_menuitem, *clock_menuitem, *host_menuitem;
+                    *layout_menuitem, *clock_menuitem, *host_menuitem, *tools_menuitem;
 static GtkWidget    *suspend_menuitem, *hibernate_menuitem, *restart_menuitem, *shutdown_menuitem;
 static GtkWidget    *contrast_menuitem, *font_menuitem, *keyboard_menuitem, *reader_menuitem;
+static GtkWidget    *screenshot_menuitem;
 static GtkWidget    *clock_label, *session_badge;
 static GtkMenu      *session_menu, *language_menu, *layout_menu;
 
@@ -277,7 +279,19 @@
 void a11y_keyboard_cb (GtkCheckMenuItem *item, gpointer user_data);
 void a11y_reader_cb (GtkCheckMenuItem *item, gpointer user_data);
 
-/* Power indciator */
+/* Screen capture */
+static const gint SCREENSHOT_BLINK_DURATION = 300;  /* Duration (in ms) of "screen capture" effect */
+static const gint SCREENSHOT_DEFAULT_MAX_COUNT = 10;
+
+static GtkWidget *screenshot_splash_widget = NULL;
+static guint screenshot_splash_finish_id = 0;
+
+static gint screenshot_max_count;
+void tools_screenshot_cb (GtkWidget *menuitem, gpointer userdata);
+static gint compare_file_info_dates (GFileInfo *a_info, GFileInfo *b_info);
+static gboolean take_screenshot (gint max_files_count);
+
+/* Power indicator */
 static void power_menu_cb (GtkWidget *menuitem, gpointer userdata);
 void suspend_cb (GtkWidget *widget, LightDMGreeter *greeter);
 void hibernate_cb (GtkWidget *widget, LightDMGreeter *greeter);
@@ -1382,6 +1396,7 @@
     g_hash_table_insert (builtin_items, "~layout", layout_menuitem);
     g_hash_table_insert (builtin_items, "~host", host_menuitem);
     g_hash_table_insert (builtin_items, "~clock", clock_menuitem);
+    g_hash_table_insert (builtin_items, "~tools", tools_menuitem);
 
     g_hash_table_iter_init (&iter, builtin_items);
     while (g_hash_table_iter_next (&iter, NULL, &iter_value))
@@ -1756,6 +1771,192 @@
         menu_command_stop (a11y_reader_command);
 }
 
+/* Taking screenshot */
+
+static gboolean
+screenshot_splash_timeout_cb (gpointer user_data)
+{
+    gtk_widget_hide (screenshot_splash_widget);
+    gtk_style_context_remove_class (gtk_widget_get_style_context (screenshot_splash_widget), "capture-triggered");
+    screenshot_splash_finish_id = 0;
+    return G_SOURCE_REMOVE;
+}
+
+void
+tools_screenshot_cb (GtkWidget *menuitem, gpointer userdata)
+{
+    if (screenshot_max_count == 0)
+        return;
+
+    if (take_screenshot (screenshot_max_count))
+    {
+        /* TODO: Playing "capture" sound */
+
+        if (!screenshot_splash_widget)
+        {
+            screenshot_splash_widget = gtk_event_box_new ();
+            gtk_widget_set_name (screenshot_splash_widget, "capture-splash");
+            gtk_event_box_set_visible_window (GTK_EVENT_BOX (screenshot_splash_widget), TRUE);
+            gtk_overlay_add_overlay (screen_overlay, screenshot_splash_widget);
+        }
+
+        if (!screenshot_splash_finish_id)
+        {
+            gtk_widget_show (screenshot_splash_widget);
+            gtk_style_context_add_class (gtk_widget_get_style_context (screenshot_splash_widget), "capture-triggered");
+            screenshot_splash_finish_id = g_timeout_add (SCREENSHOT_BLINK_DURATION, screenshot_splash_timeout_cb, NULL);
+        }
+    }
+    else
+    {
+        /* TODO: Playing "error" sound */
+    }
+}
+
+static gint
+compare_file_info_dates (GFileInfo *a_info, GFileInfo *b_info)
+{
+    guint64 a = g_file_info_get_attribute_uint64 (a_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+    guint64 b = g_file_info_get_attribute_uint64 (b_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+    return a < b ? -1 : a == b ? 0 : +1;
+}
+
+static gboolean
+take_screenshot (gint max_files_count)
+{
+    if (max_files_count == 0)
+    {
+        g_debug ("[Screenshot] Taking screenshot: aborted, max_files_count == 0");
+        return FALSE;
+    }
+
+    g_debug ("[Screenshot] Taking screenshot...");
+
+    const gchar *tmp_dir_variable = g_getenv ("TMPDIR");
+    gchar *output_dir;
+    GError *error = NULL;
+    gboolean failed = FALSE;
+
+    const gint SCREENSHOT_DIR_MODE = 0700;
+    const gchar *SCREENSHOT_DIR_NAME = "lightdm-gtk-greeter-screenshots";
+
+    if (tmp_dir_variable)
+        output_dir = g_build_filename (tmp_dir_variable, SCREENSHOT_DIR_NAME, NULL);
+    else
+        /* "/var/tmp" is usually more persistent than "/tmp" */
+        output_dir = g_build_filename (G_DIR_SEPARATOR_S, "var", "tmp", SCREENSHOT_DIR_NAME, NULL);
+
+    if (g_mkdir_with_parents (output_dir, SCREENSHOT_DIR_MODE) == -1)
+    {
+        g_warning ("[Screenshot] Failed to create directory '%s': %s", output_dir, g_strerror (errno));
+        g_free (output_dir);
+        return FALSE;
+    }
+
+    GDateTime *now = g_date_time_new_now_local ();
+    gchar *output_name = now ? g_date_time_format (now, "screenshot_%F_%T.png") : g_strdup ("screenshot.png");
+    g_date_time_unref (now);
+
+    if (max_files_count > 0)
+    {
+        GFileEnumerator *enumerator;
+        GFile *parent = g_file_new_for_path (output_dir);
+        enumerator = g_file_enumerate_children (parent,
+                                                G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+                                                G_FILE_ATTRIBUTE_STANDARD_NAME ","
+                                                G_FILE_ATTRIBUTE_TIME_MODIFIED,
+                                                G_FILE_QUERY_INFO_NONE,
+                                                NULL, &error);
+
+        if (!enumerator)
+        {
+            g_warning ("[Screenshot] Failed to enumerate screenshots directory: %s", error ? error->message : "unknown error");
+            g_clear_error (&error);
+            g_object_unref (parent);
+            g_free (output_name);
+            g_free (output_dir);
+            return FALSE;
+        }
+
+        gint files_count = 0;
+        GList *files = NULL;
+        GFileInfo *info;
+
+        while ((info = g_file_enumerator_next_file (enumerator, NULL, &error)))
+        {
+            if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR &&
+                g_strcmp0 (g_file_info_get_name (info), output_name) != 0)
+            {
+                files = g_list_prepend (files, info);
+                files_count++;
+            }
+            else
+                g_object_unref (info);
+        }
+        g_file_enumerator_close (enumerator, NULL, NULL);
+        g_object_unref (enumerator);
+
+        g_debug ("[Screenshot] Directory items count: %d, limit: %d", files_count, max_files_count);
+
+        if (files_count >= max_files_count)
+        {
+            GList *iter;
+            files = g_list_sort (files, (GCompareFunc)compare_file_info_dates);
+
+            for (iter = g_list_nth (files, files_count - max_files_count); iter && !failed; iter = g_list_previous (iter))
+            {
+                GFile *file = g_file_get_child (parent, g_file_info_get_name (iter->data));
+                if (!g_file_delete (file, NULL, &error))
+                {
+                    g_warning ("[Screenshot] Failed to delete old screenshot '%s': %s",
+                               g_file_info_get_name (iter->data),
+                               error ? error->message : "unknown error");
+                    g_clear_error (&error);
+                    failed = TRUE;
+                }
+                g_object_unref (file);
+            }
+        }
+        g_list_free_full (files, g_object_unref);
+        g_object_unref (parent);
+
+        if (failed)
+        {
+            g_free (output_name);
+            g_free (output_dir);
+            return FALSE;
+        }
+    }
+
+    /* Finally, taking screenshot */
+
+    GdkWindow *root = gdk_get_default_root_window ();
+    GdkPixbuf *screenshot = gdk_pixbuf_get_from_window (root, 0, 0, gdk_window_get_width (root), gdk_window_get_height (root));
+
+    if (!screenshot)
+        g_warning ("[Screenshot] gdk_pixbuf_get_from_window(root) failed");
+    else
+    {
+        gchar *output_path = g_build_filename (output_dir, output_name, NULL);
+        if (!gdk_pixbuf_save (screenshot, output_path, "png", &error, NULL))
+        {
+            g_warning ("[Screenshot] Failed to save image as '%s': %s", output_path, error ? error->message : "unknown error");
+            g_clear_error (&error);
+            failed = TRUE;
+        }
+        else
+            g_debug ("[Screenshot] Image saved as '%s'", output_path);
+
+        g_object_unref (screenshot);
+        g_free (output_path);
+    }
+
+    g_free (output_name);
+    g_free (output_dir);
+
+    return screenshot && !failed;
+}
+
 /* Power indicator */
 
 static void
@@ -2462,32 +2663,25 @@
 }
 
 static GdkFilterReturn
-focus_upon_map (GdkXEvent *gxevent, GdkEvent *event, gpointer  data)
+wm_window_filter (GdkXEvent *gxevent, GdkEvent *event, gpointer  data)
 {
     XEvent* xevent = (XEvent*)gxevent;
-    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;
-        Window keyboard_xid = 0;
-        GdkDisplay* display = gdk_x11_lookup_xdisplay (xevent->xmap.display);
-        GdkWindow* win = gdk_x11_window_foreign_new_for_display (display, xwin);
+        GdkDisplay *display = gdk_x11_lookup_xdisplay (xevent->xmap.display);
+        GdkWindow *win = gdk_x11_window_foreign_new_for_display (display, xevent->xmap.window);
         GdkWindowTypeHint win_type = gdk_window_get_type_hint (win);
 
-        /* Check to see if this window is our onboard window, since we don't want to focus it. */
-        if (keyboard_win)
-            keyboard_xid = gdk_x11_window_get_xid (keyboard_win);
-
-        if (xwin != keyboard_xid
-            && win_type != GDK_WINDOW_TYPE_HINT_TOOLTIP
-            && win_type != GDK_WINDOW_TYPE_HINT_NOTIFICATION)
-        {
+/*        if (win_type != GDK_WINDOW_TYPE_HINT_COMBO &&
+            win_type != GDK_WINDOW_TYPE_HINT_MENU &&
+            win_type != GDK_WINDOW_TYPE_HINT_POPUP_MENU &&
+            win_type != GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU &&
+            win_type != GDK_WINDOW_TYPE_HINT_TOOLTIP &&
+            win_type != GDK_WINDOW_TYPE_HINT_NOTIFICATION)
+*/
+        if (win_type == GDK_WINDOW_TYPE_HINT_DESKTOP ||
+            win_type == GDK_WINDOW_TYPE_HINT_DIALOG)
             gdk_window_focus (win, GDK_CURRENT_TIME);
-            /* Make sure to keep keyboard above */
-            if (keyboard_win)
-                gdk_window_raise (keyboard_win);
-        }
     }
     else if (xevent->type == UnmapNotify)
     {
@@ -2496,12 +2690,25 @@
         XGetInputFocus (xevent->xunmap.display, &xwin, &revert_to);
 
         if (revert_to == RevertToNone)
-        {
-            gdk_window_focus (gtk_widget_get_window (GTK_WIDGET (login_window)), GDK_CURRENT_TIME);
-            /* Make sure to keep keyboard above */
-            if (keyboard_win)
-                gdk_window_raise (keyboard_win);
-        }
+            gdk_window_focus (gtk_widget_get_window (GTK_WIDGET (screen_overlay)), GDK_CURRENT_TIME);
+    }
+    return GDK_FILTER_CONTINUE;
+}
+
+static GdkFilterReturn
+accelerators_window_filter (GdkXEvent *gxevent, GdkEvent *event, gpointer data)
+{
+    XKeyEvent* keyevent = (XKeyEvent*)gxevent;
+    if (keyevent->type == KeyPress)
+    {
+        KeySym accel_key = XLookupKeysym (keyevent, 0);
+        GdkModifierType accel_mods = keyevent->state & ~(Mod2Mask | Mod5Mask | LockMask);
+        gchar *accel_name = gtk_accelerator_name (accel_key, (accel_mods & gtk_accelerator_get_default_mod_mask ()));
+        GQuark accel_quark = g_quark_from_string (accel_name);
+        g_free (accel_name);
+
+        if (gtk_accel_group_activate (greeter_accel_group, accel_quark, G_OBJECT (screen_overlay), accel_key, accel_mods))
+            return GDK_FILTER_REMOVE;
     }
     return GDK_FILTER_CONTINUE;
 }
@@ -2704,6 +2911,7 @@
 
     /* Screen window */
     screen_overlay = GTK_OVERLAY (gtk_builder_get_object (builder, "screen_overlay"));
+    greeter_accel_group = GTK_ACCEL_GROUP (gtk_builder_get_object (builder, "greeter_accel_group"));
 
     /* Login window */
     login_window = GTK_WIDGET (gtk_builder_get_object (builder, "login_window"));
@@ -2719,18 +2927,27 @@
     /* Panel window*/
     panel_window = GTK_WIDGET (gtk_builder_get_object (builder, "panel_window"));
     menubar = GTK_WIDGET (gtk_builder_get_object (builder, "menubar"));
+
     session_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "session_menuitem"));
     session_menu = GTK_MENU (gtk_builder_get_object (builder, "session_menu"));
+
     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"));
+
+    tools_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "tools_menuitem"));
+    screenshot_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "screenshot_menuitem"));
+
     clock_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "clock_menuitem"));
     host_menuitem = GTK_WIDGET (gtk_builder_get_object (builder, "host_menuitem"));
 
@@ -2751,6 +2968,7 @@
     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);
+    gtk_accel_map_add_entry ("<Login>/tools/screenshot", GDK_KEY_Print, 0);
 
     init_indicators (config);
 
@@ -2953,6 +3171,17 @@
         gdk_threads_add_timeout (1000, (GSourceFunc) clock_timeout_thread, NULL);
     }
 
+    /* Screen capture */
+    screenshot_max_count = g_key_file_get_integer (config, "greeter", "max-screenshots-count", &error);
+    if (error)
+    {
+        if (error->code == G_KEY_FILE_ERROR_INVALID_VALUE)
+            g_warning ("[Configuration] Invalid '%s' value, using default: %d", "max-screenshots-count", SCREENSHOT_DEFAULT_MAX_COUNT);
+        screenshot_max_count = SCREENSHOT_DEFAULT_MAX_COUNT;
+        g_clear_error (&error);
+    }
+    gtk_widget_set_visible (screenshot_menuitem, screenshot_max_count != 0);
+
     /* A bit of CSS */
     GdkRGBA lightdm_gtk_greeter_override_defaults;
     css_provider = gtk_css_provider_new ();
@@ -3018,9 +3247,6 @@
     }
     g_strfreev (config_groups);
 
-    greeter_background_add_accel_group (greeter_background, GTK_ACCEL_GROUP (gtk_builder_get_object (builder, "a11y_accelgroup")));
-    greeter_background_add_accel_group (greeter_background, GTK_ACCEL_GROUP (gtk_builder_get_object (builder, "power_accelgroup")));
-
     greeter_background_connect (greeter_background, gdk_screen_get_default ());
 
     if (lightdm_greeter_get_hide_users_hint (greeter))
@@ -3089,10 +3315,12 @@
     }
     g_strfreev (values);
 
-    /* focus fix (source: unity-greeter) */
-    GdkWindow* root_window = gdk_get_default_root_window ();
+    GdkWindow *root_window = gdk_get_default_root_window ();
     gdk_window_set_events (root_window, gdk_window_get_events (root_window) | GDK_SUBSTRUCTURE_MASK);
-    gdk_window_add_filter (root_window, focus_upon_map, NULL);
+    /* There is no window manager, so we need to implement some of its functionality */
+    gdk_window_add_filter (root_window, wm_window_filter, NULL);
+    /* Use "manual" accelerators control instead of passing accel groups to GreeterBackground's windows */
+    gdk_window_add_filter (gtk_widget_get_window (GTK_WIDGET (screen_overlay)), accelerators_window_filter, NULL);
 
     gtk_widget_show (GTK_WIDGET (screen_overlay));
 

=== modified file 'src/lightdm-gtk-greeter.glade'
--- src/lightdm-gtk-greeter.glade	2014-12-10 07:59:02 +0000
+++ src/lightdm-gtk-greeter.glade	2015-01-19 21:41:07 +0000
@@ -1,10 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- Generated with glade 3.18.3 -->
 <interface>
-  <requires lib="gtk+" version="3.4"/>
+  <requires lib="gtk+" version="3.8"/>
   <requires lib="greeter_menu_bar" version="1.0"/>
-  <object class="GtkAccelGroup" id="a11y_accelgroup"/>
-  <object class="GtkAccelGroup" id="power_accelgroup"/>
+  <object class="GtkAccelGroup" id="greeter_accel_group"/>
   <object class="GtkEventBox" id="panel_window">
     <property name="name">panel_window</property>
     <property name="visible">True</property>
@@ -26,12 +25,13 @@
               <object class="GtkMenu" id="power_menu">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="accel_group">power_accelgroup</property>
+                <property name="accel_group">greeter_accel_group</property>
                 <child>
                   <object class="GtkMenuItem" id="suspend_menuitem">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="label" translatable="yes">Suspend</property>
+                    <property name="use_underline">True</property>
                     <signal name="activate" handler="suspend_cb" swapped="no"/>
                   </object>
                 </child>
@@ -40,6 +40,7 @@
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="label" translatable="yes">Hibernate</property>
+                    <property name="use_underline">True</property>
                     <signal name="activate" handler="hibernate_cb" swapped="no"/>
                   </object>
                 </child>
@@ -48,6 +49,7 @@
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="label" translatable="yes">Restart...</property>
+                    <property name="use_underline">True</property>
                     <signal name="activate" handler="restart_cb" swapped="no"/>
                   </object>
                 </child>
@@ -58,6 +60,7 @@
                     <property name="can_focus">False</property>
                     <property name="accel_path">&lt;Login&gt;/power/shutdown</property>
                     <property name="label" translatable="yes">Shut Down...</property>
+                    <property name="use_underline">True</property>
                     <signal name="activate" handler="shutdown_cb" swapped="no"/>
                   </object>
                 </child>
@@ -74,7 +77,7 @@
               <object class="GtkMenu" id="a11y_menu">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="accel_group">a11y_accelgroup</property>
+                <property name="accel_group">greeter_accel_group</property>
                 <child>
                   <object class="GtkCheckMenuItem" id="large_font_menuitem">
                     <property name="use_action_appearance">False</property>
@@ -82,6 +85,7 @@
                     <property name="can_focus">False</property>
                     <property name="accel_path">&lt;Login&gt;/a11y/font</property>
                     <property name="label" translatable="yes">Large Font</property>
+                    <property name="use_underline">True</property>
                     <signal name="toggled" handler="a11y_font_cb" swapped="no"/>
                   </object>
                 </child>
@@ -92,6 +96,7 @@
                     <property name="can_focus">False</property>
                     <property name="accel_path">&lt;Login&gt;/a11y/contrast</property>
                     <property name="label" translatable="yes">High Contrast</property>
+                    <property name="use_underline">True</property>
                     <signal name="toggled" handler="a11y_contrast_cb" swapped="no"/>
                   </object>
                 </child>
@@ -163,6 +168,30 @@
           </object>
         </child>
         <child>
+          <object class="GtkMenuItem" id="tools_menuitem">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="use_underline">True</property>
+            <child type="submenu">
+              <object class="GtkMenu" id="tools_menu">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="accel_group">greeter_accel_group</property>
+                <child>
+                  <object class="GtkMenuItem" id="screenshot_menuitem">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="accel_path">&lt;Login&gt;/tools/screenshot</property>
+                    <property name="label" translatable="yes">Take screenshot</property>
+                    <property name="use_underline">True</property>
+                    <signal name="activate" handler="tools_screenshot_cb" swapped="no"/>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
           <object class="GtkSeparatorMenuItem" id="clock_menuitem">
             <property name="name">clock_menuitem</property>
             <property name="can_focus">False</property>


Follow ups