diff --git a/plugins/media-keys/csd-media-keys-manager.c b/plugins/media-keys/csd-media-keys-manager.c index 02930a3..7c1c519 100644 --- a/plugins/media-keys/csd-media-keys-manager.c +++ b/plugins/media-keys/csd-media-keys-manager.c @@ -39,6 +39,7 @@ #include #include #include +#include #ifdef HAVE_GUDEV #include @@ -121,6 +122,10 @@ static const gchar kb_introspection_xml[] = #define VOLUME_STEP 5 /* percents for one volume button press */ #define MAX_VOLUME 65536.0 +#define SYSTEMD_DBUS_NAME "org.freedesktop.login1" +#define SYSTEMD_DBUS_PATH "/org/freedesktop/login1" +#define SYSTEMD_DBUS_INTERFACE "org.freedesktop.login1.Manager" + #define CSD_MEDIA_KEYS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CSD_TYPE_MEDIA_KEYS_MANAGER, CsdMediaKeysManagerPrivate)) typedef struct { @@ -167,6 +172,10 @@ struct CsdMediaKeysManagerPrivate GDBusProxy *power_screen_proxy; GDBusProxy *power_keyboard_proxy; + /* systemd stuff */ + GDBusProxy *logind_proxy; + gint inhibit_keys_fd; + /* Multihead stuff */ GdkScreen *current_screen; GSList *screens; @@ -2213,6 +2222,11 @@ csd_media_keys_manager_stop (CsdMediaKeysManager *manager) } #endif /* HAVE_GUDEV */ + if (priv->logind_proxy) { + g_object_unref (priv->logind_proxy); + priv->logind_proxy = NULL; + } + if (priv->settings) { g_object_unref (priv->settings); priv->settings = NULL; @@ -2356,9 +2370,85 @@ csd_media_keys_manager_class_init (CsdMediaKeysManagerClass *klass) } static void +inhibit_done (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (source); + CsdMediaKeysManager *manager = CSD_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 csd_media_keys_manager_init (CsdMediaKeysManager *manager) { + GError *error; + GDBusConnection *bus; + + error = NULL; manager->priv = CSD_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 (), + "Cinnamon handling keypresses", + "block"), + 0, + G_MAXINT, + NULL, + NULL, + inhibit_done, + manager); + } static void @@ -2375,6 +2465,8 @@ csd_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 (csd_media_keys_manager_parent_class)->finalize (object); } diff --git a/plugins/power/csd-power-manager.c b/plugins/power/csd-power-manager.c index b54cb5b..b9c5429 100644 --- a/plugins/power/csd-power-manager.c +++ b/plugins/power/csd-power-manager.c @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -79,6 +80,10 @@ #define CSD_POWER_MANAGER_CRITICAL_ALERT_TIMEOUT 5 /* seconds */ #define CSD_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 */ @@ -203,6 +208,13 @@ struct CsdPowerManagerPrivate GtkStatusIcon *status_icon; guint xscreensaver_watchdog_timer_id; gboolean is_virtual_machine; + + /* systemd stuff */ + GDBusProxy *logind_proxy; + gint inhibit_lid_switch_fd; + gboolean inhibit_lid_switch_taken; + gint inhibit_suspend_fd; + gboolean inhibit_suspend_taken; }; enum { @@ -3350,30 +3362,6 @@ lock_screensaver (CsdPowerManager *manager) if (!do_lock) return; - /* 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, - CsdPowerManager *manager) -{ - gboolean do_lock; - - do_lock = g_settings_get_boolean (manager->priv->settings, - "lock-on-suspend"); - if (!do_lock) - return; - /* connect to the screensaver first */ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, @@ -3384,46 +3372,6 @@ upower_notify_sleep_cb (UpClient *client, NULL, sleep_cb_screensaver_proxy_ready_cb, manager); - -} - -static void -upower_notify_resume_cb (UpClient *client, - UpSleepKind sleep_kind, - CsdPowerManager *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); - } - - if (manager->priv->screensaver_proxy != NULL) { - g_object_unref (manager->priv->screensaver_proxy); - manager->priv->screensaver_proxy = 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); - } } static void @@ -3582,6 +3530,219 @@ disable_builtin_screensaver (gpointer unused) return TRUE; } +static void +inhibit_lid_switch_done (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (source); + CsdPowerManager *manager = CSD_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 (CsdPowerManager *manager) +{ + GVariant *params; + + if (manager->priv->inhibit_lid_switch_taken) { + g_debug ("already inhibited lid-switch"); + return; + } + g_debug ("Adding lid switch system inhibitor"); + manager->priv->inhibit_lid_switch_taken = TRUE; + + 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 +inhibit_suspend_done (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (source); + CsdPowerManager *manager = CSD_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 (CsdPowerManager *manager) +{ + if (manager->priv->inhibit_suspend_taken) { + g_debug ("already inhibited lid-switch"); + return; + } + g_debug ("Adding suspend delay inhibitor"); + manager->priv->inhibit_suspend_taken = TRUE; + g_dbus_proxy_call_with_unix_fd_list (manager->priv->logind_proxy, + "Inhibit", + g_variant_new ("(ssss)", + "sleep", + g_get_user_name (), + "Cinnamon needs to lock the screen", + "delay"), + 0, + G_MAXINT, + NULL, + NULL, + inhibit_suspend_done, + manager); +} + +static void +uninhibit_suspend (CsdPowerManager *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; + manager->priv->inhibit_suspend_taken = FALSE; +} + +static void +handle_suspend_actions (CsdPowerManager *manager) +{ + gboolean do_lock; + + do_lock = g_settings_get_boolean (manager->priv->settings, + "lock-on-suspend"); + if (do_lock) + lock_screensaver (manager); + + /* lift the delay inhibit, so logind can proceed */ + uninhibit_suspend (manager); +} + +static void +handle_resume_actions (CsdPowerManager *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 +upower_notify_sleep_cb (UpClient *client, + UpSleepKind sleep_kind, + CsdPowerManager *manager) +{ + handle_suspend_actions (manager); +} + +static void +upower_notify_resume_cb (UpClient *client, + UpSleepKind sleep_kind, + CsdPowerManager *manager) +{ + handle_resume_actions (manager); +} + +static void +logind_proxy_signal_cb (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + CsdPowerManager *manager = CSD_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); + } +} + static gboolean is_hardware_a_virtual_machine (void) { @@ -3647,6 +3808,26 @@ csd_power_manager_start (CsdPowerManager *manager, if (manager->priv->x11_screen == NULL) return FALSE; + /* Set up the logind proxy */ + 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); + 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); + + /* Disable logind's lid handling while g-s-d is active */ + inhibit_lid_switch (manager); + /* track the active session */ manager->priv->session = cinnamon_settings_session_new (); g_signal_connect (manager->priv->session, "notify::state", @@ -3856,6 +4037,22 @@ csd_power_manager_stop (CsdPowerManager *manager) manager->priv->up_client = NULL; } + if (manager->priv->inhibit_lid_switch_fd != -1) { + close (manager->priv->inhibit_lid_switch_fd); + manager->priv->inhibit_lid_switch_fd = -1; + manager->priv->inhibit_lid_switch_taken = FALSE; + } + if (manager->priv->inhibit_suspend_fd != -1) { + close (manager->priv->inhibit_suspend_fd); + manager->priv->inhibit_suspend_fd = -1; + manager->priv->inhibit_suspend_taken = FALSE; + } + + if (manager->priv->logind_proxy != NULL) { + g_object_unref (manager->priv->logind_proxy); + manager->priv->logind_proxy = NULL; + } + if (manager->priv->x11_screen != NULL) { g_object_unref (manager->priv->x11_screen); manager->priv->x11_screen = NULL; @@ -3928,6 +4125,8 @@ static void csd_power_manager_init (CsdPowerManager *manager) { manager->priv = CSD_POWER_MANAGER_GET_PRIVATE (manager); + manager->priv->inhibit_lid_switch_fd = -1; + manager->priv->inhibit_suspend_fd = -1; } static void