[gnome-settings-daemon/f18] Make lid-close handling work with recent systemd

Matthias Clasen mclasen at fedoraproject.org
Tue Oct 2 17:01:51 UTC 2012


commit e5220e49c97fad0b08c8494f7d96a51c0b2d3e2a
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Tue Oct 2 13:01:47 2012 -0400

    Make lid-close handling work with recent systemd
    
    A lot of discussion and testing in
    https://bugzilla.gnome.org/show_bug.cgi?id=680689

 0001-Clean-up-gsd_power_stop.patch                 |  100 ++
 ...edia-keys-Use-logind-for-suspending-and-r.patch | 1464 ++++++++++++++++++++
 2 files changed, 1564 insertions(+), 0 deletions(-)
---
diff --git a/0001-Clean-up-gsd_power_stop.patch b/0001-Clean-up-gsd_power_stop.patch
new file mode 100644
index 0000000..c6b1fe1
--- /dev/null
+++ b/0001-Clean-up-gsd_power_stop.patch
@@ -0,0 +1,100 @@
+From d5638d181649b567292b033fc6d26b449632fd50 Mon Sep 17 00:00:00 2001
+From: Matthias Clasen <mclasen at redhat.com>
+Date: Mon, 1 Oct 2012 13:22:31 -0400
+Subject: [PATCH 1/2] Clean up gsd_power_stop
+
+Using g_clear_object makes this a bit nicer.
+---
+ plugins/power/gsd-power-manager.c | 59 ++++++++++-----------------------------
+ 1 file changed, 14 insertions(+), 45 deletions(-)
+
+diff --git a/plugins/power/gsd-power-manager.c b/plugins/power/gsd-power-manager.c
+index d4bf937..0e06495 100644
+--- a/plugins/power/gsd-power-manager.c
++++ b/plugins/power/gsd-power-manager.c
+@@ -3798,68 +3798,37 @@ gsd_power_manager_stop (GsdPowerManager *manager)
+                 manager->priv->introspection_data = NULL;
+         }
+ 
+-        if (manager->priv->connection != NULL) {
+-                g_object_unref (manager->priv->connection);
+-                manager->priv->connection = NULL;
+-        }
+-
+         kill_lid_close_safety_timer (manager);
+ 
+         g_signal_handlers_disconnect_by_data (manager->priv->up_client, manager);
+ 
+-        g_object_unref (manager->priv->session);
+-        g_object_unref (manager->priv->settings);
+-        g_object_unref (manager->priv->settings_screensaver);
+-        g_object_unref (manager->priv->up_client);
+-        manager->priv->session = NULL;
+-        manager->priv->settings = NULL;
+-        manager->priv->settings_screensaver = NULL;
+-        manager->priv->up_client = NULL;
+-
+-        if (manager->priv->x11_screen != NULL) {
+-                g_object_unref (manager->priv->x11_screen);
+-                manager->priv->x11_screen = NULL;
+-        }
++        g_clear_object (&manager->priv->connection);
++        g_clear_object (&manager->priv->session);
++        g_clear_object (&manager->priv->settings);
++        g_clear_object (&manager->priv->settings_screensaver);
++        g_clear_object (&manager->priv->up_client);
++        g_clear_object (&manager->priv->x11_screen);
+ 
+         g_ptr_array_unref (manager->priv->devices_array);
+-        g_object_unref (manager->priv->phone);
+-        g_object_unref (manager->priv->device_composite);
+         manager->priv->devices_array = NULL;
+-        manager->priv->phone = NULL;
+-        manager->priv->device_composite = NULL;
+-
+-        if (manager->priv->previous_icon != NULL) {
+-                g_object_unref (manager->priv->previous_icon);
+-                manager->priv->previous_icon = NULL;
+-        }
++        g_clear_object (&manager->priv->phone);
++        g_clear_object (&manager->priv->device_composite);
++        g_clear_object (&manager->priv->previous_icon);
+ 
+         g_free (manager->priv->previous_summary);
+         manager->priv->previous_summary = NULL;
+ 
+-        if (manager->priv->upower_proxy != NULL) {
+-                g_object_unref (manager->priv->upower_proxy);
+-                manager->priv->upower_proxy = NULL;
+-        }
+-
+-        if (manager->priv->session_proxy != NULL) {
+-                g_object_unref (manager->priv->session_proxy);
+-                manager->priv->session_proxy = NULL;
+-        }
+-
+-        if (manager->priv->session_presence_proxy != NULL) {
+-                g_object_unref (manager->priv->session_presence_proxy);
+-                manager->priv->session_presence_proxy = NULL;
+-        }
++        g_clear_object (&manager->priv->upower_proxy);
++        g_clear_object (&manager->priv->session_proxy);
++        g_clear_object (&manager->priv->session_presence_proxy);
+ 
+         if (manager->priv->critical_alert_timeout_id > 0) {
+                 g_source_remove (manager->priv->critical_alert_timeout_id);
+                 manager->priv->critical_alert_timeout_id = 0;
+         }
+ 
+-        g_object_unref (manager->priv->idletime);
+-        g_object_unref (manager->priv->status_icon);
+-        manager->priv->idletime = NULL;
+-        manager->priv->status_icon = NULL;
++        g_clear_object (&manager->priv->idletime);
++        g_clear_object (&manager->priv->status_icon);
+ }
+ 
+ static void
+-- 
+1.7.12.1
+
diff --git a/0001-power-and-media-keys-Use-logind-for-suspending-and-r.patch b/0001-power-and-media-keys-Use-logind-for-suspending-and-r.patch
new file mode 100644
index 0000000..2d7bb65
--- /dev/null
+++ b/0001-power-and-media-keys-Use-logind-for-suspending-and-r.patch
@@ -0,0 +1,1464 @@
+From 9db5c300ed17bb667c99234db27914b57bbdaf9e Mon Sep 17 00:00:00 2001
+From: Richard Hughes <richard at hughsie.com>
+Date: Fri, 21 Sep 2012 11:56:53 +0100
+Subject: [PATCH] power and media-keys: Use logind for suspending and
+ rebooting the system
+
+Use the new logind features to suspend and resume but making sure we opt out
+of logind handling the sleep and power keys, and also inhibiting for lid close
+auto-suspend if there is an external monitor connected.
+
+Also use a delay inihibit for logind so that we can do actions on suspend like
+blanking the screen using the screensaver and also poking the screensaver on
+resume.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=680689
+---
+ plugins/common/Makefile.am                  |   4 +-
+ plugins/common/gsd-power-helper.c           | 203 --------
+ plugins/common/gsd-power-helper.h           |  35 --
+ plugins/media-keys/gsd-media-keys-manager.c | 156 +++++--
+ plugins/power/gsd-power-manager.c           | 691 +++++++++++++++++++---------
+ 5 files changed, 587 insertions(+), 502 deletions(-)
+ delete mode 100644 plugins/common/gsd-power-helper.c
+ delete mode 100644 plugins/common/gsd-power-helper.h
+
+diff --git a/plugins/common/Makefile.am b/plugins/common/Makefile.am
+index 7e50db4..b0e907c 100644
+--- a/plugins/common/Makefile.am
++++ b/plugins/common/Makefile.am
+@@ -6,9 +6,7 @@ libcommon_la_SOURCES = \
+ 	gsd-keygrab.c		\
+ 	gsd-keygrab.h		\
+ 	gsd-input-helper.c	\
+-	gsd-input-helper.h	\
+-	gsd-power-helper.c	\
+-	gsd-power-helper.h
++	gsd-input-helper.h
+ 
+ libcommon_la_CPPFLAGS = \
+ 	$(AM_CPPFLAGS)
+diff --git a/plugins/common/gsd-power-helper.c b/plugins/common/gsd-power-helper.c
+deleted file mode 100644
+index 27d0eda..0000000
+--- a/plugins/common/gsd-power-helper.c
++++ /dev/null
+@@ -1,203 +0,0 @@
+-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+- *
+- * Copyright (C) 2012 Bastien Nocera <hadess at hadess.net>
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+- *
+- */
+-
+-#include "config.h"
+-
+-#include "gsd-power-helper.h"
+-
+-#define SYSTEMD_DBUS_NAME                       "org.freedesktop.login1"
+-#define SYSTEMD_DBUS_PATH                       "/org/freedesktop/login1"
+-#define SYSTEMD_DBUS_INTERFACE                  "org.freedesktop.login1.Manager"
+-
+-#define CONSOLEKIT_DBUS_NAME                    "org.freedesktop.ConsoleKit"
+-#define CONSOLEKIT_DBUS_PATH_MANAGER            "/org/freedesktop/ConsoleKit/Manager"
+-#define CONSOLEKIT_DBUS_INTERFACE_MANAGER       "org.freedesktop.ConsoleKit.Manager"
+-
+-#ifdef HAVE_SYSTEMD
+-static void
+-systemd_stop (void)
+-{
+-        GDBusConnection *bus;
+-
+-        bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
+-        g_dbus_connection_call (bus,
+-                                SYSTEMD_DBUS_NAME,
+-                                SYSTEMD_DBUS_PATH,
+-                                SYSTEMD_DBUS_INTERFACE,
+-                                "PowerOff",
+-                                g_variant_new ("(b)", FALSE),
+-                                NULL, 0, G_MAXINT, NULL, NULL, NULL);
+-        g_object_unref (bus);
+-}
+-
+-static void
+-systemd_suspend (void)
+-{
+-        GDBusConnection *bus;
+-
+-        bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
+-        g_dbus_connection_call (bus,
+-                                SYSTEMD_DBUS_NAME,
+-                                SYSTEMD_DBUS_PATH,
+-                                SYSTEMD_DBUS_INTERFACE,
+-                                "Suspend",
+-                                g_variant_new ("(b)", TRUE),
+-                                NULL, 0, G_MAXINT, NULL, NULL, NULL);
+-        g_object_unref (bus);
+-}
+-
+-static void
+-systemd_hibernate (void)
+-{
+-        GDBusConnection *bus;
+-
+-        bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
+-        g_dbus_connection_call (bus,
+-                                SYSTEMD_DBUS_NAME,
+-                                SYSTEMD_DBUS_PATH,
+-                                SYSTEMD_DBUS_INTERFACE,
+-                                "Hibernate",
+-                                g_variant_new ("(b)", TRUE),
+-                                NULL, 0, G_MAXINT, NULL, NULL, NULL);
+-        g_object_unref (bus);
+-}
+-
+-#else /* HAVE_SYSTEMD */
+-
+-static void
+-consolekit_stop_cb (GObject *source_object,
+-                    GAsyncResult *res,
+-                    gpointer user_data)
+-{
+-        GVariant *result;
+-        GError *error = NULL;
+-
+-        result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
+-                                           res,
+-                                           &error);
+-        if (result == NULL) {
+-                g_warning ("couldn't stop using ConsoleKit: %s",
+-                           error->message);
+-                g_error_free (error);
+-        } else {
+-                g_variant_unref (result);
+-        }
+-}
+-
+-static void
+-consolekit_stop (void)
+-{
+-        GError *error = NULL;
+-        GDBusProxy *proxy;
+-
+-        /* power down the machine in a safe way */
+-        proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+-                                               G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+-                                               NULL,
+-                                               CONSOLEKIT_DBUS_NAME,
+-                                               CONSOLEKIT_DBUS_PATH_MANAGER,
+-                                               CONSOLEKIT_DBUS_INTERFACE_MANAGER,
+-                                               NULL, &error);
+-        if (proxy == NULL) {
+-                g_warning ("cannot connect to ConsoleKit: %s",
+-                           error->message);
+-                g_error_free (error);
+-                return;
+-        }
+-        g_dbus_proxy_call (proxy,
+-                           "Stop",
+-                           NULL,
+-                           G_DBUS_CALL_FLAGS_NONE,
+-                           -1, NULL,
+-                           consolekit_stop_cb, NULL);
+-        g_object_unref (proxy);
+-}
+-static void
+-upower_sleep_cb (GObject *source_object,
+-                 GAsyncResult *res,
+-                 gpointer user_data)
+-{
+-        GVariant *result;
+-        GError *error = NULL;
+-
+-        result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
+-                                           res,
+-                                           &error);
+-        if (result == NULL) {
+-                g_warning ("couldn't sleep using UPower: %s",
+-                           error->message);
+-                g_error_free (error);
+-        } else {
+-                g_variant_unref (result);
+-        }
+-}
+-
+-static void
+-upower_suspend (GDBusProxy *upower_proxy)
+-{
+-        g_dbus_proxy_call (upower_proxy,
+-                           "Suspend",
+-                           NULL,
+-                           G_DBUS_CALL_FLAGS_NONE,
+-                           -1, NULL,
+-                           upower_sleep_cb, NULL);
+-}
+-
+-static void
+-upower_hibernate (GDBusProxy *upower_proxy)
+-{
+-        g_dbus_proxy_call (upower_proxy,
+-                           "Hibernate",
+-                           NULL,
+-                           G_DBUS_CALL_FLAGS_NONE,
+-                           -1, NULL,
+-                           upower_sleep_cb, NULL);
+-}
+-#endif /* HAVE_SYSTEMD */
+-
+-void
+-gsd_power_suspend (GDBusProxy *upower_proxy)
+-{
+-#ifdef HAVE_SYSTEMD
+-	systemd_suspend ();
+-#else
+-	upower_suspend (upower_proxy);
+-#endif
+-}
+-
+-void
+-gsd_power_poweroff (void)
+-{
+-#ifdef HAVE_SYSTEMD
+-	systemd_stop ();
+-#else
+-	consolekit_stop ();
+-#endif
+-}
+-
+-void
+-gsd_power_hibernate (GDBusProxy *upower_proxy)
+-{
+-#ifdef HAVE_SYSTEMD
+-	systemd_hibernate ();
+-#else
+-	upower_hibernate (upower_proxy);
+-#endif
+-}
+diff --git a/plugins/common/gsd-power-helper.h b/plugins/common/gsd-power-helper.h
+deleted file mode 100644
+index e3be14f..0000000
+--- a/plugins/common/gsd-power-helper.h
++++ /dev/null
+@@ -1,35 +0,0 @@
+-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+- *
+- * Copyright (C) 2012 Bastien Nocera <hadess at hadess.net>
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+- */
+-
+-#ifndef __GSD_POWER_HELPER_H
+-#define __GSD_POWER_HELPER_H
+-
+-#include <glib.h>
+-
+-G_BEGIN_DECLS
+-
+-#include <gio/gio.h>
+-
+-void gsd_power_suspend   (GDBusProxy *upower_proxy);
+-void gsd_power_hibernate (GDBusProxy *upower_proxy);
+-void gsd_power_poweroff  (void);
+-
+-G_END_DECLS
+-
+-#endif /* __GSD_POWER_HELPER_H */
+diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c
+index 9c84d7f..a2f277e 100644
+--- a/plugins/media-keys/gsd-media-keys-manager.c
++++ b/plugins/media-keys/gsd-media-keys-manager.c
+@@ -39,6 +39,7 @@
+ #include <gdk/gdkx.h>
+ #include <gtk/gtk.h>
+ #include <gio/gdesktopappinfo.h>
++#include <gio/gunixfdlist.h>
+ 
+ #ifdef HAVE_GUDEV
+ #include <gudev/gudev.h>
+@@ -51,7 +52,6 @@
+ #include "shortcuts-list.h"
+ #include "gsd-osd-window.h"
+ #include "gsd-input-helper.h"
+-#include "gsd-power-helper.h"
+ #include "gsd-enums.h"
+ 
+ #include <canberra.h>
+@@ -105,6 +105,10 @@ static const gchar introspection_xml[] =
+ #define KEY_CURRENT_INPUT_SOURCE "current"
+ #define KEY_INPUT_SOURCES        "sources"
+ 
++#define SYSTEMD_DBUS_NAME                       "org.freedesktop.login1"
++#define SYSTEMD_DBUS_PATH                       "/org/freedesktop/login1"
++#define SYSTEMD_DBUS_INTERFACE                  "org.freedesktop.login1.Manager"
++
+ #define GSD_MEDIA_KEYS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_MEDIA_KEYS_MANAGER, GsdMediaKeysManagerPrivate))
+ 
+ typedef struct {
+@@ -148,10 +152,13 @@ struct GsdMediaKeysManagerPrivate
+ 
+         /* Power stuff */
+         GSettings       *power_settings;
+-        GDBusProxy      *upower_proxy;
+         GDBusProxy      *power_screen_proxy;
+         GDBusProxy      *power_keyboard_proxy;
+ 
++        /* systemd stuff */
++        GDBusProxy      *logind_proxy;
++        gint             inhibit_keys_fd;
++
+         /* Multihead stuff */
+         GdkScreen       *current_screen;
+         GSList          *screens;
+@@ -1618,6 +1625,38 @@ do_toggle_contrast_action (GsdMediaKeysManager *manager)
+ }
+ 
+ static void
++power_action_suspend (GsdMediaKeysManager *manager)
++{
++#ifndef HAVE_SYSTEMD
++        g_warning ("no systemd support");
++        return;
++#endif
++        g_dbus_proxy_call (manager->priv->logind_proxy,
++                           "Suspend",
++                           g_variant_new ("(b)", TRUE),
++                           G_DBUS_CALL_FLAGS_NONE,
++                           G_MAXINT,
++                           manager->priv->bus_cancellable,
++                           NULL, NULL);
++}
++
++static void
++power_action_hibernate (GsdMediaKeysManager *manager)
++{
++#ifndef HAVE_SYSTEMD
++        g_warning ("no systemd support");
++        return;
++#endif
++        g_dbus_proxy_call (manager->priv->logind_proxy,
++                           "Hibernate",
++                           g_variant_new ("(b)", TRUE),
++                           G_DBUS_CALL_FLAGS_NONE,
++                           G_MAXINT,
++                           manager->priv->bus_cancellable,
++                           NULL, NULL);
++}
++
++static void
+ do_config_power_action (GsdMediaKeysManager *manager,
+                         const gchar *config_key)
+ {
+@@ -1627,14 +1666,14 @@ do_config_power_action (GsdMediaKeysManager *manager,
+                                            config_key);
+         switch (action_type) {
+         case GSD_POWER_ACTION_SUSPEND:
+-                gsd_power_suspend (manager->priv->upower_proxy);
++                power_action_suspend (manager);
+                 break;
+         case GSD_POWER_ACTION_INTERACTIVE:
+         case GSD_POWER_ACTION_SHUTDOWN:
+                 gnome_session_shutdown (manager);
+                 break;
+         case GSD_POWER_ACTION_HIBERNATE:
+-                gsd_power_hibernate (manager->priv->upower_proxy);
++                power_action_hibernate (manager);
+                 break;
+         case GSD_POWER_ACTION_BLANK:
+         case GSD_POWER_ACTION_NOTHING:
+@@ -2248,6 +2287,7 @@ gsd_media_keys_manager_stop (GsdMediaKeysManager *manager)
+         }
+ #endif /* HAVE_GUDEV */
+ 
++        g_clear_object (&priv->logind_proxy);
+         if (priv->settings) {
+                 g_object_unref (priv->settings);
+                 priv->settings = NULL;
+@@ -2268,11 +2308,6 @@ gsd_media_keys_manager_stop (GsdMediaKeysManager *manager)
+                 priv->power_keyboard_proxy = NULL;
+         }
+ 
+-        if (priv->upower_proxy) {
+-                g_object_unref (priv->upower_proxy);
+-                priv->upower_proxy = NULL;
+-        }
+-
+         if (priv->cancellable != NULL) {
+                 g_cancellable_cancel (priv->cancellable);
+                 g_object_unref (priv->cancellable);
+@@ -2363,9 +2398,85 @@ gsd_media_keys_manager_class_init (GsdMediaKeysManagerClass *klass)
+ }
+ 
+ static void
++inhibit_done (GObject      *source,
++              GAsyncResult *result,
++              gpointer      user_data)
++{
++        GDBusProxy *proxy = G_DBUS_PROXY (source);
++        GsdMediaKeysManager *manager = GSD_MEDIA_KEYS_MANAGER (user_data);
++        GError *error = NULL;
++        GVariant *res;
++        GUnixFDList *fd_list = NULL;
++        gint idx;
++
++        res = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, &fd_list, result, &error);
++        if (res == NULL) {
++                g_warning ("Unable to inhibit keypresses: %s", error->message);
++                g_error_free (error);
++        } else {
++                g_variant_get (res, "(h)", &idx);
++                manager->priv->inhibit_keys_fd = g_unix_fd_list_get (fd_list, idx, &error);
++                if (manager->priv->inhibit_keys_fd == -1) {
++                        g_warning ("Failed to receive system inhibitor fd: %s", error->message);
++                        g_error_free (error);
++                }
++                g_debug ("System inhibitor fd is %d", manager->priv->inhibit_keys_fd);
++                g_object_unref (fd_list);
++                g_variant_unref (res);
++        }
++}
++
++static void
+ gsd_media_keys_manager_init (GsdMediaKeysManager *manager)
+ {
++        GError *error;
++        GDBusConnection *bus;
++
++        error = NULL;
+         manager->priv = GSD_MEDIA_KEYS_MANAGER_GET_PRIVATE (manager);
++
++        bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
++        if (bus == NULL) {
++                g_warning ("Failed to connect to system bus: %s",
++                           error->message);
++                g_error_free (error);
++                return;
++        }
++
++        manager->priv->logind_proxy =
++                g_dbus_proxy_new_sync (bus,
++                                       0,
++                                       NULL,
++                                       SYSTEMD_DBUS_NAME,
++                                       SYSTEMD_DBUS_PATH,
++                                       SYSTEMD_DBUS_INTERFACE,
++                                       NULL,
++                                       &error);
++
++        if (manager->priv->logind_proxy == NULL) {
++                g_warning ("Failed to connect to systemd: %s",
++                           error->message);
++                g_error_free (error);
++        }
++
++        g_object_unref (bus);
++
++        g_debug ("Adding system inhibitors for power keys");
++        manager->priv->inhibit_keys_fd = -1;
++        g_dbus_proxy_call_with_unix_fd_list (manager->priv->logind_proxy,
++                                             "Inhibit",
++                                             g_variant_new ("(ssss)",
++                                                            "handle-power-key:handle-suspend-key:handle-hibernate-key",
++                                                            g_get_user_name (),
++                                                            "GNOME handling keypresses",
++                                                            "block"),
++                                             0,
++                                             G_MAXINT,
++                                             NULL,
++                                             NULL,
++                                             inhibit_done,
++                                             manager);
++
+ }
+ 
+ static void
+@@ -2382,6 +2493,8 @@ gsd_media_keys_manager_finalize (GObject *object)
+ 
+         if (media_keys_manager->priv->start_idle_id != 0)
+                 g_source_remove (media_keys_manager->priv->start_idle_id);
++        if (media_keys_manager->priv->inhibit_keys_fd != -1)
++                close (media_keys_manager->priv->inhibit_keys_fd);
+ 
+         G_OBJECT_CLASS (gsd_media_keys_manager_parent_class)->finalize (object);
+ }
+@@ -2401,21 +2514,6 @@ xrandr_ready_cb (GObject             *source_object,
+ }
+ 
+ static void
+-upower_ready_cb (GObject             *source_object,
+-                 GAsyncResult        *res,
+-                 GsdMediaKeysManager *manager)
+-{
+-        GError *error = NULL;
+-
+-        manager->priv->upower_proxy = g_dbus_proxy_new_finish (res, &error);
+-        if (manager->priv->upower_proxy == NULL) {
+-                g_warning ("Failed to get proxy for upower: %s",
+-                           error->message);
+-                g_error_free (error);
+-        }
+-}
+-
+-static void
+ power_screen_ready_cb (GObject             *source_object,
+                        GAsyncResult        *res,
+                        GsdMediaKeysManager *manager)
+@@ -2517,16 +2615,6 @@ register_manager (GsdMediaKeysManager *manager)
+                    manager->priv->bus_cancellable,
+                    (GAsyncReadyCallback) on_bus_gotten,
+                    manager);
+-
+-        g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+-                                  G_DBUS_PROXY_FLAGS_NONE,
+-                                  NULL,
+-                                  "org.freedesktop.UPower",
+-                                  "/org/freedesktop/UPower",
+-                                  "org.freedesktop.UPower",
+-                                  NULL,
+-                                  (GAsyncReadyCallback) upower_ready_cb,
+-                                  manager);
+ }
+ 
+ GsdMediaKeysManager *
+diff --git a/plugins/power/gsd-power-manager.c b/plugins/power/gsd-power-manager.c
+index 0e06495..a931061 100644
+--- a/plugins/power/gsd-power-manager.c
++++ b/plugins/power/gsd-power-manager.c
+@@ -1,7 +1,7 @@
+ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+  *
+  * Copyright (C) 2007 William Jon McCann <mccann at jhu.edu>
+- * Copyright (C) 2011 Richard Hughes <richard at hughsie.com>
++ * Copyright (C) 2011-2012 Richard Hughes <richard at hughsie.com>
+  * Copyright (C) 2011 Ritesh Khadgaray <khadgaray at gmail.com>
+  *
+  * This program is free software; you can redistribute it and/or modify
+@@ -31,6 +31,7 @@
+ #include <libupower-glib/upower.h>
+ #include <libnotify/notify.h>
+ #include <canberra-gtk.h>
++#include <gio/gunixfdlist.h>
+ 
+ #define GNOME_DESKTOP_USE_UNSTABLE_API
+ #include <libgnome-desktop/gnome-rr.h>
+@@ -42,7 +43,6 @@
+ #include "gnome-settings-session.h"
+ #include "gsd-enums.h"
+ #include "gsd-power-manager.h"
+-#include "gsd-power-helper.h"
+ 
+ #define GNOME_SESSION_DBUS_NAME                 "org.gnome.SessionManager"
+ #define GNOME_SESSION_DBUS_PATH                 "/org/gnome/SessionManager"
+@@ -77,6 +77,10 @@
+ #define GSD_POWER_MANAGER_RECALL_DELAY                  30 /* seconds */
+ #define GSD_POWER_MANAGER_LID_CLOSE_SAFETY_TIMEOUT      30 /* seconds */
+ 
++#define SYSTEMD_DBUS_NAME                       "org.freedesktop.login1"
++#define SYSTEMD_DBUS_PATH                       "/org/freedesktop/login1"
++#define SYSTEMD_DBUS_INTERFACE                  "org.freedesktop.login1.Manager"
++
+ /* Keep this in sync with gnome-shell */
+ #define SCREENSAVER_FADE_TIME                           10 /* seconds */
+ 
+@@ -190,13 +194,17 @@ struct GsdPowerManagerPrivate
+         ca_context              *canberra_context;
+         ca_proplist             *critical_alert_loop_props;
+         guint32                  critical_alert_timeout_id;
+-        GDBusProxy              *screensaver_proxy;
+         GDBusProxy              *session_proxy;
+         GDBusProxy              *session_presence_proxy;
+         GpmIdletime             *idletime;
+         GsdPowerIdleMode         current_idle_mode;
+-        guint                    lid_close_safety_timer_id;
+         GtkStatusIcon           *status_icon;
++
++        /* systemd stuff */
++        GDBusProxy              *logind_proxy;
++        gint                     inhibit_lid_switch_fd;
++        gint                     inhibit_suspend_fd;
++        guint                    inhibit_lid_switch_timer_id;
+ };
+ 
+ enum {
+@@ -213,8 +221,8 @@ static GIcon    *engine_get_icon (GsdPowerManager *manager);
+ static gchar    *engine_get_summary (GsdPowerManager *manager);
+ static void      do_power_action_type (GsdPowerManager *manager, GsdPowerActionType action_type);
+ static void      do_lid_closed_action (GsdPowerManager *manager);
+-static void      lock_screensaver (GsdPowerManager *manager);
+-static void      kill_lid_close_safety_timer (GsdPowerManager *manager);
++static void      uninhibit_lid_switch (GsdPowerManager *manager);
++static gboolean  external_monitor_is_connected (GnomeRRScreen *screen);
+ 
+ G_DEFINE_TYPE (GsdPowerManager, gsd_power_manager, G_TYPE_OBJECT)
+ 
+@@ -2044,6 +2052,57 @@ gnome_session_shutdown (void)
+ }
+ 
+ static void
++action_poweroff (GsdPowerManager *manager)
++{
++        if (manager->priv->logind_proxy == NULL) {
++                g_warning ("no systemd support");
++                return;
++        }
++        g_dbus_proxy_call (manager->priv->logind_proxy,
++                           "PowerOff",
++                           g_variant_new ("(b)", FALSE),
++                           G_DBUS_CALL_FLAGS_NONE,
++                           G_MAXINT,
++                           NULL,
++                           NULL,
++                           NULL);
++}
++
++static void
++action_suspend (GsdPowerManager *manager)
++{
++        if (manager->priv->logind_proxy == NULL) {
++                g_warning ("no systemd support");
++                return;
++        }
++        g_dbus_proxy_call (manager->priv->logind_proxy,
++                           "Suspend",
++                           g_variant_new ("(b)", FALSE),
++                           G_DBUS_CALL_FLAGS_NONE,
++                           G_MAXINT,
++                           NULL,
++                           NULL,
++                           NULL);
++}
++
++static void
++action_hibernate (GsdPowerManager *manager)
++{
++        if (manager->priv->logind_proxy == NULL) {
++                g_warning ("no systemd support");
++                return;
++        }
++        g_dbus_proxy_call (manager->priv->logind_proxy,
++                           "Hibernate",
++                           g_variant_new ("(b)", FALSE),
++                           G_DBUS_CALL_FLAGS_NONE,
++                           G_MAXINT,
++                           NULL,
++                           NULL,
++                           NULL);
++}
++
++static void
+ do_power_action_type (GsdPowerManager *manager,
+                       GsdPowerActionType action_type)
+ {
+@@ -2052,19 +2111,19 @@ do_power_action_type (GsdPowerManager *manager,
+ 
+         switch (action_type) {
+         case GSD_POWER_ACTION_SUSPEND:
+-                gsd_power_suspend (manager->priv->upower_proxy);
++                action_suspend (manager);
+                 break;
+         case GSD_POWER_ACTION_INTERACTIVE:
+                 gnome_session_shutdown ();
+                 break;
+         case GSD_POWER_ACTION_HIBERNATE:
+-                gsd_power_hibernate (manager->priv->upower_proxy);
++                action_hibernate (manager);
+                 break;
+         case GSD_POWER_ACTION_SHUTDOWN:
+                 /* this is only used on critically low battery where
+                  * hibernate is not available and is marginally better
+                  * than just powering down the computer mid-write */
+-                gsd_power_poweroff ();
++                action_poweroff (manager);
+                 break;
+         case GSD_POWER_ACTION_BLANK:
+                 ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
+@@ -2136,85 +2195,20 @@ upower_kbd_toggle (GsdPowerManager *manager,
+         return ret;
+ }
+ 
+-static void
+-do_lid_open_action (GsdPowerManager *manager)
+-{
+-        gboolean ret;
+-        GError *error = NULL;
+-
+-        /* play a sound, using sounds from the naming spec */
+-        ca_context_play (manager->priv->canberra_context, 0,
+-                         CA_PROP_EVENT_ID, "lid-open",
+-                         /* TRANSLATORS: this is the sound description */
+-                         CA_PROP_EVENT_DESCRIPTION, _("Lid has been opened"),
+-                         NULL);
+-
+-        /* ensure we turn the panel back on after lid open */
+-        ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
+-                                             GNOME_RR_DPMS_ON,
+-                                             &error);
+-        if (!ret) {
+-                g_warning ("failed to turn the panel on after lid open: %s",
+-                           error->message);
+-                g_clear_error (&error);
+-        }
+-
+-        /* only toggle keyboard if present and already toggled off */
+-        if (manager->priv->upower_kdb_proxy != NULL &&
+-            manager->priv->kbd_brightness_old != -1) {
+-                ret = upower_kbd_toggle (manager, &error);
+-                if (!ret) {
+-                        g_warning ("failed to turn the kbd backlight on: %s",
+-                                   error->message);
+-                        g_error_free (error);
+-                }
+-        }
+-
+-        kill_lid_close_safety_timer (manager);
+-}
+-
+ static gboolean
+-is_on (GnomeRROutput *output)
++inhibit_lid_switch_timer_cb (GsdPowerManager *manager)
+ {
+-	GnomeRRCrtc *crtc;
+-
+-	crtc = gnome_rr_output_get_crtc (output);
+-	if (!crtc)
+-		return FALSE;
+-	return gnome_rr_crtc_get_current_mode (crtc) != NULL;
+-}
+-
+-static gboolean
+-non_laptop_outputs_are_all_off (GnomeRRScreen *screen)
+-{
+-        GnomeRROutput **outputs;
+-        int i;
+-
+-        outputs = gnome_rr_screen_list_outputs (screen);
+-        for (i = 0; outputs[i] != NULL; i++) {
+-                if (gnome_rr_output_is_laptop (outputs[i]))
+-                        continue;
+-
+-                if (is_on (outputs[i]))
+-                        return FALSE;
++        if (!external_monitor_is_connected (manager->priv->x11_screen) ||
++            g_settings_get_boolean (manager->priv->settings,
++                                    "lid-close-suspend-with-external-monitor")) {
++                g_debug ("no external monitors for a while; uninhibiting lid close");
++                uninhibit_lid_switch (manager);
++                manager->priv->inhibit_lid_switch_timer_id = 0;
++                return G_SOURCE_REMOVE;
+         }
+ 
+-        return TRUE;
+-}
+-
+-/* Timeout callback used to check conditions when the laptop's lid is closed but
+- * the machine is not suspended yet.  We try to suspend again, so that the laptop
+- * won't overheat if placed in a backpack.
+- */
+-static gboolean
+-lid_close_safety_timer_cb (GsdPowerManager *manager)
+-{
+-        manager->priv->lid_close_safety_timer_id = 0;
+-
+-        g_debug ("lid has been closed for a while; trying to suspend again");
+-        do_lid_closed_action (manager);
+-
+-        return FALSE;
++        g_debug ("external monitor still there; trying again later");
++        return G_SOURCE_CONTINUE;
+ }
+ 
+ /* Sets up a timer to be triggered some seconds after closing the laptop lid
+@@ -2222,82 +2216,73 @@ lid_close_safety_timer_cb (GsdPowerManager *manager)
+  * again in the timeout handler to see if we can suspend then.
+  */
+ static void
+-setup_lid_close_safety_timer (GsdPowerManager *manager)
++setup_inhibit_lid_switch_timer (GsdPowerManager *manager)
+ {
+-        if (manager->priv->lid_close_safety_timer_id != 0)
++        if (manager->priv->inhibit_lid_switch_timer_id != 0) {
++                g_debug ("lid close safety timer already set up");
+                 return;
++        }
++
++        g_debug ("setting up lid close safety timer");
+ 
+-        manager->priv->lid_close_safety_timer_id = g_timeout_add_seconds (GSD_POWER_MANAGER_LID_CLOSE_SAFETY_TIMEOUT,
+-                                                                          (GSourceFunc) lid_close_safety_timer_cb,
++        manager->priv->inhibit_lid_switch_timer_id = g_timeout_add_seconds (GSD_POWER_MANAGER_LID_CLOSE_SAFETY_TIMEOUT,
++                                                                          (GSourceFunc) inhibit_lid_switch_timer_cb,
+                                                                           manager);
+-        g_source_set_name_by_id (manager->priv->lid_close_safety_timer_id, "[GsdPowerManager] lid close safety timer");
++        g_source_set_name_by_id (manager->priv->inhibit_lid_switch_timer_id, "[GsdPowerManager] lid close safety timer");
+ }
+ 
+ static void
+-kill_lid_close_safety_timer (GsdPowerManager *manager)
++restart_inhibit_lid_switch_timer (GsdPowerManager *manager)
+ {
+-        if (manager->priv->lid_close_safety_timer_id != 0) {
+-                g_source_remove (manager->priv->lid_close_safety_timer_id);
+-                manager->priv->lid_close_safety_timer_id = 0;
++        if (manager->priv->inhibit_lid_switch_timer_id != 0) {
++                g_debug ("restarting lid close safety timer");
++                g_source_remove (manager->priv->inhibit_lid_switch_timer_id);
++                manager->priv->inhibit_lid_switch_timer_id = 0;
++                setup_inhibit_lid_switch_timer (manager);
+         }
+ }
+ 
+ static void
+-suspend_with_lid_closed (GsdPowerManager *manager)
++do_lid_open_action (GsdPowerManager *manager)
+ {
+         gboolean ret;
+         GError *error = NULL;
+-        GsdPowerActionType action_type;
+ 
+-        /* maybe lock the screen if the lid is closed */
+-        lock_screensaver (manager);
+-
+-        /* we have different settings depending on AC state */
+-        if (up_client_get_on_battery (manager->priv->up_client)) {
+-                action_type = g_settings_get_enum (manager->priv->settings,
+-                                                   "lid-close-battery-action");
+-        } else {
+-                action_type = g_settings_get_enum (manager->priv->settings,
+-                                                   "lid-close-ac-action");
+-        }
+-
+-        /* check we won't melt when the lid is closed */
+-        if (action_type != GSD_POWER_ACTION_SUSPEND &&
+-            action_type != GSD_POWER_ACTION_HIBERNATE) {
+-                if (up_client_get_lid_force_sleep (manager->priv->up_client)) {
+-                        g_warning ("to prevent damage, now forcing suspend");
+-                        do_power_action_type (manager, GSD_POWER_ACTION_SUSPEND);
+-                        return;
+-                }
+-        }
++        /* play a sound, using sounds from the naming spec */
++        ca_context_play (manager->priv->canberra_context, 0,
++                         CA_PROP_EVENT_ID, "lid-open",
++                         /* TRANSLATORS: this is the sound description */
++                         CA_PROP_EVENT_DESCRIPTION, _("Lid has been opened"),
++                         NULL);
+ 
+-        /* ensure we turn the panel back on after resume */
++        /* ensure we turn the panel back on after lid open */
+         ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
+-                                             GNOME_RR_DPMS_OFF,
++                                             GNOME_RR_DPMS_ON,
+                                              &error);
+         if (!ret) {
+-                g_warning ("failed to turn the panel off after lid close: %s",
++                g_warning ("failed to turn the panel on after lid open: %s",
+                            error->message);
+-                g_error_free (error);
++                g_clear_error (&error);
+         }
+ 
+-        /* only toggle keyboard if present and not already toggled */
+-        if (manager->priv->upower_kdb_proxy &&
+-            manager->priv->kbd_brightness_old == -1) {
++        /* only toggle keyboard if present and already toggled off */
++        if (manager->priv->upower_kdb_proxy != NULL &&
++            manager->priv->kbd_brightness_old != -1) {
+                 ret = upower_kbd_toggle (manager, &error);
+                 if (!ret) {
+-                        g_warning ("failed to turn the kbd backlight off: %s",
++                        g_warning ("failed to turn the kbd backlight on: %s",
+                                    error->message);
+                         g_error_free (error);
+                 }
+         }
+-
+-        do_power_action_type (manager, action_type);
+ }
+ 
+ static void
+ do_lid_closed_action (GsdPowerManager *manager)
+ {
++        gboolean ret;
++        GError *error = NULL;
++
+         /* play a sound, using sounds from the naming spec */
+         ca_context_play (manager->priv->canberra_context, 0,
+                          CA_PROP_EVENT_ID, "lid-close",
+@@ -2305,21 +2290,22 @@ do_lid_closed_action (GsdPowerManager *manager)
+                          CA_PROP_EVENT_DESCRIPTION, _("Lid has been closed"),
+                          NULL);
+ 
++        /* turn the panel off if the lid is closed (mainly for Dells...) */
++        ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
++                                             GNOME_RR_DPMS_OFF,
++                                             &error);
++        if (!ret) {
++                g_warning ("failed to turn the panel off after lid close: %s",
++                           error->message);
++                g_error_free (error);
++        }
++
+         /* refresh RANDR so we get an accurate view of what monitors are plugged in when the lid is closed */
+         gnome_rr_screen_refresh (manager->priv->x11_screen, NULL); /* NULL-GError */
+ 
+-        /* perform policy action */
+-        if (g_settings_get_boolean (manager->priv->settings, "lid-close-suspend-with-external-monitor")
+-            || non_laptop_outputs_are_all_off (manager->priv->x11_screen)) {
+-                g_debug ("lid is closed; suspending or hibernating");
+-                suspend_with_lid_closed (manager);
+-        } else {
+-                g_debug ("lid is closed; not suspending nor hibernating since some external monitor outputs are still active");
+-                setup_lid_close_safety_timer (manager);
+-        }
++        restart_inhibit_lid_switch_timer (manager);
+ }
+ 
+-
+ static void
+ up_client_changed_cb (UpClient *client, GsdPowerManager *manager)
+ {
+@@ -2339,6 +2325,7 @@ up_client_changed_cb (UpClient *client, GsdPowerManager *manager)
+         if (manager->priv->lid_is_closed == tmp)
+                 return;
+         manager->priv->lid_is_closed = tmp;
++        g_debug ("up changed: lid is now %s", tmp ? "closed" : "open");
+ 
+         /* fake a keypress */
+         if (tmp)
+@@ -3290,30 +3277,6 @@ gsd_power_manager_class_init (GsdPowerManagerClass *klass)
+ }
+ 
+ static void
+-sleep_cb_screensaver_proxy_ready_cb (GObject *source_object,
+-                            GAsyncResult *res,
+-                            gpointer user_data)
+-{
+-        GError *error = NULL;
+-        GsdPowerManager *manager = GSD_POWER_MANAGER (user_data);
+-
+-        manager->priv->screensaver_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+-        if (manager->priv->screensaver_proxy == NULL) {
+-                g_warning ("Could not connect to gnome-screensaver: %s",
+-                           error->message);
+-                g_error_free (error);
+-                return;
+-        }
+-
+-        /* Finish the upower_notify_sleep_cb() call by locking the screen */
+-        g_debug ("gnome-screensaver activated, doing gnome-screensaver lock");
+-        g_dbus_proxy_call (manager->priv->screensaver_proxy,
+-                           "Lock",
+-                           NULL, G_DBUS_CALL_FLAGS_NONE, -1,
+-                           NULL, NULL, NULL);
+-}
+-
+-static void
+ idle_dbus_signal_cb (GDBusProxy *proxy,
+                      const gchar *sender_name,
+                      const gchar *signal_name,
+@@ -3465,75 +3428,38 @@ out:
+ }
+ 
+ static void
+-lock_screensaver (GsdPowerManager *manager)
++lock_screensaver (GsdPowerManager *manager,
++                  GSourceFunc      done_cb)
+ {
+         gboolean do_lock;
+ 
+         do_lock = g_settings_get_boolean (manager->priv->settings_screensaver,
+                                           "lock-enabled");
+-        if (!do_lock)
++        if (!do_lock && done_cb) {
++                done_cb (manager);
+                 return;
+-
+-        if (manager->priv->screensaver_proxy != NULL) {
+-                g_debug ("doing gnome-screensaver lock");
+-                g_dbus_proxy_call (manager->priv->screensaver_proxy,
+-                                   "Lock",
+-                                   NULL, G_DBUS_CALL_FLAGS_NONE, -1,
+-                                   NULL, NULL, NULL);
+-        } else {
+-                /* connect to the screensaver first */
+-                g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+-                                          G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+-                                          NULL,
+-                                          GS_DBUS_NAME,
+-                                          GS_DBUS_PATH,
+-                                          GS_DBUS_INTERFACE,
+-                                          NULL,
+-                                          sleep_cb_screensaver_proxy_ready_cb,
+-                                          manager);
+         }
+-}
+-
+-static void
+-upower_notify_sleep_cb (UpClient *client,
+-                        UpSleepKind sleep_kind,
+-                        GsdPowerManager *manager)
+-{
+-        lock_screensaver (manager);
+-}
+-
+-static void
+-upower_notify_resume_cb (UpClient *client,
+-                         UpSleepKind sleep_kind,
+-                         GsdPowerManager *manager)
+-{
+-        gboolean ret;
+-        GError *error = NULL;
+-
+-        /* this displays the unlock dialogue so the user doesn't have
+-         * to move the mouse or press any key before the window comes up */
+-        if (manager->priv->screensaver_proxy != NULL) {
+-                g_dbus_proxy_call (manager->priv->screensaver_proxy,
+-                                   "SimulateUserActivity",
+-                                   NULL,
+-                                   G_DBUS_CALL_FLAGS_NONE,
+-                                   -1, NULL, NULL, NULL);
+-        }
+-
+-        /* close existing notifications on resume, the system power
+-         * state is probably different now */
+-        notify_close_if_showing (manager->priv->notification_low);
+-        notify_close_if_showing (manager->priv->notification_discharging);
+ 
+-        /* ensure we turn the panel back on after resume */
+-        ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
+-                                             GNOME_RR_DPMS_ON,
+-                                             &error);
+-        if (!ret) {
+-                g_warning ("failed to turn the panel on after resume: %s",
+-                           error->message);
+-                g_error_free (error);
+-        }
++        g_dbus_connection_call (manager->priv->connection,
++                                GS_DBUS_NAME,
++                                GS_DBUS_PATH,
++                                GS_DBUS_INTERFACE,
++                                "Lock",
++                                NULL, NULL,
++                                G_DBUS_CALL_FLAGS_NONE, -1,
++                                NULL, NULL, NULL);
++
++        /* Wait until gnome-shell shield animation is done
++         *
++         * FIXME: the shell should mark the lock as active
++         * when the shield is down, then we could wait for
++         * that. This would also fix the problem that we wait
++         * needlessly when the shell has already locked the
++         * screen because it is initiating the suspend.
++         *
++         * https://bugzilla.gnome.org/show_bug.cgi?id=685053
++         */
++        g_timeout_add (500, done_cb, manager);
+ }
+ 
+ static void
+@@ -3621,6 +3547,283 @@ engine_session_active_changed_cb (GnomeSettingsSession *session,
+         idle_set_mode (manager, GSD_POWER_IDLE_MODE_NORMAL);
+ }
+ 
++static void
++inhibit_lid_switch_done (GObject      *source,
++                         GAsyncResult *result,
++                         gpointer      user_data)
++{
++        GDBusProxy *proxy = G_DBUS_PROXY (source);
++        GsdPowerManager *manager = GSD_POWER_MANAGER (user_data);
++        GError *error = NULL;
++        GVariant *res;
++        GUnixFDList *fd_list = NULL;
++        gint idx;
++
++        res = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, &fd_list, result, &error);
++        if (res == NULL) {
++                g_warning ("Unable to inhibit lid switch: %s", error->message);
++                g_error_free (error);
++        } else {
++                g_variant_get (res, "(h)", &idx);
++                manager->priv->inhibit_lid_switch_fd = g_unix_fd_list_get (fd_list, idx, &error);
++                if (manager->priv->inhibit_lid_switch_fd == -1) {
++                        g_warning ("Failed to receive system inhibitor fd: %s", error->message);
++                        g_error_free (error);
++                }
++                g_debug ("System inhibitor fd is %d", manager->priv->inhibit_lid_switch_fd);
++                g_object_unref (fd_list);
++                g_variant_unref (res);
++        }
++}
++
++static void
++inhibit_lid_switch (GsdPowerManager *manager)
++{
++        GVariant *params;
++
++        if (manager->priv->inhibit_lid_switch_fd != -1) {
++                g_debug ("already inhibited lid-switch");
++                return;
++        }
++        g_debug ("Adding lid switch system inhibitor");
++
++        params = g_variant_new ("(ssss)",
++                                "handle-lid-switch",
++                                g_get_user_name (),
++                                "Multiple displays attached",
++                                "block");
++        g_dbus_proxy_call_with_unix_fd_list (manager->priv->logind_proxy,
++                                             "Inhibit",
++                                             params,
++                                             0,
++                                             G_MAXINT,
++                                             NULL,
++                                             NULL,
++                                             inhibit_lid_switch_done,
++                                             manager);
++}
++
++static void
++uninhibit_lid_switch (GsdPowerManager *manager)
++{
++        if (manager->priv->inhibit_lid_switch_fd == -1) {
++                g_debug ("no lid-switch inhibitor");
++                return;
++        }
++        g_debug ("Removing lid switch system inhibitor");
++        close (manager->priv->inhibit_lid_switch_fd);
++        manager->priv->inhibit_lid_switch_fd = -1;
++}
++
++static void
++inhibit_suspend_done (GObject      *source,
++                      GAsyncResult *result,
++                      gpointer      user_data)
++{
++        GDBusProxy *proxy = G_DBUS_PROXY (source);
++        GsdPowerManager *manager = GSD_POWER_MANAGER (user_data);
++        GError *error = NULL;
++        GVariant *res;
++        GUnixFDList *fd_list = NULL;
++        gint idx;
++
++        res = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, &fd_list, result, &error);
++        if (res == NULL) {
++                g_warning ("Unable to inhibit suspend: %s", error->message);
++                g_error_free (error);
++        } else {
++                g_variant_get (res, "(h)", &idx);
++                manager->priv->inhibit_suspend_fd = g_unix_fd_list_get (fd_list, idx, &error);
++                if (manager->priv->inhibit_suspend_fd == -1) {
++                        g_warning ("Failed to receive system inhibitor fd: %s", error->message);
++                        g_error_free (error);
++                }
++                g_debug ("System inhibitor fd is %d", manager->priv->inhibit_suspend_fd);
++                g_object_unref (fd_list);
++                g_variant_unref (res);
++        }
++}
++
++/* We take a delay inhibitor here, which causes logind to send a
++ * PrepareToSleep signal, which gives us a chance to lock the screen
++ * and do some other preparations.
++ */
++static void
++inhibit_suspend (GsdPowerManager *manager)
++{
++        if (manager->priv->inhibit_suspend_fd != -1) {
++                g_debug ("already inhibited lid-switch");
++                return;
++        }
++        g_debug ("Adding suspend delay inhibitor");
++        g_dbus_proxy_call_with_unix_fd_list (manager->priv->logind_proxy,
++                                             "Inhibit",
++                                             g_variant_new ("(ssss)",
++                                                            "sleep",
++                                                            g_get_user_name (),
++                                                            "GNOME needs to lock the screen",
++                                                            "delay"),
++                                             0,
++                                             G_MAXINT,
++                                             NULL,
++                                             NULL,
++                                             inhibit_suspend_done,
++                                             manager);
++}
++
++static void
++uninhibit_suspend (GsdPowerManager *manager)
++{
++        if (manager->priv->inhibit_suspend_fd == -1) {
++                g_debug ("no suspend delay inhibitor");
++                return;
++        }
++        g_debug ("Removing suspend delay inhibitor");
++        close (manager->priv->inhibit_suspend_fd);
++        manager->priv->inhibit_suspend_fd = -1;
++}
++
++static gboolean
++randr_output_is_on (GnomeRROutput *output)
++{
++	GnomeRRCrtc *crtc;
++
++	crtc = gnome_rr_output_get_crtc (output);
++	if (!crtc)
++		return FALSE;
++	return gnome_rr_crtc_get_current_mode (crtc) != NULL;
++}
++
++static gboolean
++external_monitor_is_connected (GnomeRRScreen *screen)
++{
++        GnomeRROutput **outputs;
++        guint i;
++
++        if (g_file_test ("/tmp/external_connected", G_FILE_TEST_EXISTS))
++                return TRUE;
++
++        /* see if we have more than one screen plugged in */
++        outputs = gnome_rr_screen_list_outputs (screen);
++        for (i = 0; outputs[i] != NULL; i++) {
++                if (randr_output_is_on (outputs[i]) &&
++                    !gnome_rr_output_is_laptop (outputs[i]))
++                        return TRUE;
++        }
++
++        return FALSE;
++}
++
++static void
++on_randr_event (GnomeRRScreen *screen, gpointer user_data)
++{
++        GsdPowerManager *manager = GSD_POWER_MANAGER (user_data);
++
++        /* when a second monitor is plugged in, we take the
++         * handle-lid-switch inhibitor lock of logind to prevent
++         * it from suspending.
++         *
++         * Uninhibiting is done in the inhibit_lid_switch_timer,
++         * since we want to give users a few seconds when unplugging
++         * and replugging an external monitor, not suspend right away.
++         */
++        if (external_monitor_is_connected (screen) &&
++            !g_settings_get_boolean (manager->priv->settings,
++                                     "lid-close-suspend-with-external-monitor")) {
++                inhibit_lid_switch (manager);
++                setup_inhibit_lid_switch_timer (manager);
++        }
++        else {
++                restart_inhibit_lid_switch_timer (manager);
++        }
++}
++
++static gboolean
++screen_lock_done_cb (gpointer data)
++{
++        GsdPowerManager *manager = data;
++
++        /* lift the delay inhibit, so logind can proceed */
++        uninhibit_suspend (manager);
++
++        return FALSE;
++}
++
++static void
++handle_suspend_actions (GsdPowerManager *manager)
++{
++        gboolean ret;
++        GError *error = NULL;
++
++        /* ensure we turn the panel back on after resume */
++        ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
++                                             GNOME_RR_DPMS_ON,
++                                             &error);
++        if (!ret) {
++                g_warning ("failed to turn the panel on after resume: %s",
++                           error->message);
++                g_error_free (error);
++        }
++
++        lock_screensaver (manager, screen_lock_done_cb);
++}
++
++static void
++handle_resume_actions (GsdPowerManager *manager)
++{
++        gboolean ret;
++        GError *error = NULL;
++
++        /* this displays the unlock dialogue so the user doesn't have
++         * to move the mouse or press any key before the window comes up */
++        g_dbus_connection_call (manager->priv->connection,
++                                GS_DBUS_NAME,
++                                GS_DBUS_PATH,
++                                GS_DBUS_INTERFACE,
++                                "SimulateUserActivity",
++                                NULL, NULL,
++                                G_DBUS_CALL_FLAGS_NONE, -1,
++                                NULL, NULL, NULL);
++
++        /* close existing notifications on resume, the system power
++         * state is probably different now */
++        notify_close_if_showing (manager->priv->notification_low);
++        notify_close_if_showing (manager->priv->notification_discharging);
++
++        /* ensure we turn the panel back on after resume */
++        ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
++                                             GNOME_RR_DPMS_ON,
++                                             &error);
++        if (!ret) {
++                g_warning ("failed to turn the panel on after resume: %s",
++                           error->message);
++                g_error_free (error);
++        }
++
++        /* set up the delay again */
++        inhibit_suspend (manager);
++}
++
++static void
++logind_proxy_signal_cb (GDBusProxy  *proxy,
++                        const gchar *sender_name,
++                        const gchar *signal_name,
++                        GVariant    *parameters,
++                        gpointer     user_data)
++{
++        GsdPowerManager *manager = GSD_POWER_MANAGER (user_data);
++        gboolean is_about_to_suspend;
++
++        if (g_strcmp0 (signal_name, "PrepareForSleep") != 0)
++                return;
++        g_variant_get (parameters, "(b)", &is_about_to_suspend);
++        if (is_about_to_suspend) {
++                handle_suspend_actions (manager);
++        } else {
++                handle_resume_actions (manager);
++        }
++}
++
+ gboolean
+ gsd_power_manager_start (GsdPowerManager *manager,
+                          GError **error)
+@@ -3630,6 +3833,25 @@ gsd_power_manager_start (GsdPowerManager *manager,
+         g_debug ("Starting power manager");
+         gnome_settings_profile_start (NULL);
+ 
++        manager->priv->logind_proxy =
++                g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
++                                               0,
++                                               NULL,
++                                               SYSTEMD_DBUS_NAME,
++                                               SYSTEMD_DBUS_PATH,
++                                               SYSTEMD_DBUS_INTERFACE,
++                                               NULL,
++                                               error);
++        if (manager->priv->logind_proxy == NULL) {
++                g_warning ("no systemd support");
++                return FALSE;
++        }
++        g_signal_connect (manager->priv->logind_proxy, "g-signal",
++                          G_CALLBACK (logind_proxy_signal_cb),
++                          manager);
++        /* Set up a delay inhibitor to be informed about suspend attempts */
++        inhibit_suspend (manager);
++
+         /* track the active session */
+         manager->priv->session = gnome_settings_session_new ();
+         g_signal_connect (manager->priv->session, "notify::state",
+@@ -3644,10 +3866,6 @@ gsd_power_manager_start (GsdPowerManager *manager,
+                           G_CALLBACK (engine_settings_key_changed_cb), manager);
+         manager->priv->settings_screensaver = g_settings_new ("org.gnome.desktop.screensaver");
+         manager->priv->up_client = up_client_new ();
+-        g_signal_connect (manager->priv->up_client, "notify-sleep",
+-                          G_CALLBACK (upower_notify_sleep_cb), manager);
+-        g_signal_connect (manager->priv->up_client, "notify-resume",
+-                          G_CALLBACK (upower_notify_resume_cb), manager);
+         manager->priv->lid_is_closed = up_client_get_lid_is_closed (manager->priv->up_client);
+         g_signal_connect (manager->priv->up_client, "device-added",
+                           G_CALLBACK (engine_device_added_cb), manager);
+@@ -3761,6 +3979,9 @@ gsd_power_manager_start (GsdPowerManager *manager,
+         manager->priv->x11_screen = gnome_rr_screen_new (gdk_screen_get_default (), error);
+         if (manager->priv->x11_screen == NULL)
+                 return FALSE;
++        g_signal_connect (manager->priv->x11_screen, "changed", G_CALLBACK (on_randr_event), manager);
++        /* set up initial state */
++        on_randr_event (manager->priv->x11_screen, manager);
+ 
+         /* ensure the default dpms timeouts are cleared */
+         ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
+@@ -3787,6 +4008,11 @@ gsd_power_manager_stop (GsdPowerManager *manager)
+ {
+         g_debug ("Stopping power manager");
+ 
++        if (manager->priv->inhibit_lid_switch_timer_id != 0) {
++                g_source_remove (manager->priv->inhibit_lid_switch_timer_id);
++                manager->priv->inhibit_lid_switch_timer_id = 0;
++        }
++
+         if (manager->priv->bus_cancellable != NULL) {
+                 g_cancellable_cancel (manager->priv->bus_cancellable);
+                 g_object_unref (manager->priv->bus_cancellable);
+@@ -3798,8 +4024,6 @@ gsd_power_manager_stop (GsdPowerManager *manager)
+                 manager->priv->introspection_data = NULL;
+         }
+ 
+-        kill_lid_close_safety_timer (manager);
+-
+         g_signal_handlers_disconnect_by_data (manager->priv->up_client, manager);
+ 
+         g_clear_object (&manager->priv->connection);
+@@ -3807,6 +4031,17 @@ gsd_power_manager_stop (GsdPowerManager *manager)
+         g_clear_object (&manager->priv->settings);
+         g_clear_object (&manager->priv->settings_screensaver);
+         g_clear_object (&manager->priv->up_client);
++
++        if (manager->priv->inhibit_lid_switch_fd != -1) {
++                close (manager->priv->inhibit_lid_switch_fd);
++                manager->priv->inhibit_lid_switch_fd = -1;
++        }
++        if (manager->priv->inhibit_suspend_fd != -1) {
++                close (manager->priv->inhibit_suspend_fd);
++                manager->priv->inhibit_suspend_fd = -1;
++        }
++
++        g_clear_object (&manager->priv->logind_proxy);
+         g_clear_object (&manager->priv->x11_screen);
+ 
+         g_ptr_array_unref (manager->priv->devices_array);
+@@ -3835,6 +4070,8 @@ static void
+ gsd_power_manager_init (GsdPowerManager *manager)
+ {
+         manager->priv = GSD_POWER_MANAGER_GET_PRIVATE (manager);
++        manager->priv->inhibit_lid_switch_fd = -1;
++        manager->priv->inhibit_suspend_fd = -1;
+ }
+ 
+ static void
+-- 
+1.7.12.1
+


More information about the scm-commits mailing list