diff options
-rw-r--r-- | libupower-glib/up-client.c | 42 | ||||
-rw-r--r-- | libupower-glib/up-client.h | 3 | ||||
-rw-r--r-- | src/org.freedesktop.UPower.xml | 78 | ||||
-rw-r--r-- | src/up-daemon.c | 180 | ||||
-rw-r--r-- | src/up-daemon.h | 2 |
5 files changed, 283 insertions, 22 deletions
diff --git a/libupower-glib/up-client.c b/libupower-glib/up-client.c index b60510a..e0727eb 100644 --- a/libupower-glib/up-client.c +++ b/libupower-glib/up-client.c @@ -240,6 +240,48 @@ out: } /** + * up_client_about_to_sleep_sync: + * @client: a #UpClient instance. + * @cancellable: a #GCancellable or %NULL + * @error: a #GError, or %NULL. + * + * Tells UPower that we are soon to reqest either Suspend() or Hibernate() + * and that session and system components should be notified of this. + * + * Return value: TRUE if system suspended okay, FALSE other wise. + * + * Since: 0.9.1 + **/ +gboolean +up_client_about_to_sleep_sync (UpClient *client, GCancellable *cancellable, GError **error) +{ + gboolean ret; + GError *error_local = NULL; + + g_return_val_if_fail (UP_IS_CLIENT (client), FALSE); + g_return_val_if_fail (client->priv->proxy != NULL, FALSE); + + ret = dbus_g_proxy_call (client->priv->proxy, "AboutToSleep", &error_local, + G_TYPE_INVALID, G_TYPE_INVALID); + if (!ret) { + /* DBus might time out, which is okay */ + if (g_error_matches (error_local, DBUS_GERROR, DBUS_GERROR_NO_REPLY)) { + g_debug ("DBUS timed out, but recovering"); + ret = TRUE; + goto out; + } + + /* an actual error */ + g_warning ("Couldn't sent that we were about to sleep: %s", error_local->message); + g_set_error (error, 1, 0, "%s", error_local->message); + } +out: + if (error_local != NULL) + g_error_free (error_local); + return ret; +} + +/** * up_client_get_properties_sync: * @client: a #UpClient instance. * @cancellable: a #GCancellable or %NULL diff --git a/libupower-glib/up-client.h b/libupower-glib/up-client.h index 54b55b1..995c5fb 100644 --- a/libupower-glib/up-client.h +++ b/libupower-glib/up-client.h @@ -86,6 +86,9 @@ gboolean up_client_enumerate_devices_sync (UpClient *client, gboolean up_client_suspend_sync (UpClient *client, GCancellable *cancellable, GError **error); +gboolean up_client_about_to_sleep_sync (UpClient *client, + GCancellable *cancellable, + GError **error); gboolean up_client_hibernate_sync (UpClient *client, GCancellable *cancellable, GError **error); diff --git a/src/org.freedesktop.UPower.xml b/src/org.freedesktop.UPower.xml index ca8d63c..cf32edc 100644 --- a/src/org.freedesktop.UPower.xml +++ b/src/org.freedesktop.UPower.xml @@ -110,6 +110,66 @@ method return sender=:1.386 -> dest=:1.451 reply_serial=2 </doc:description> </doc:doc> </signal> + + <!-- ************************************************************ --> + + <signal name="Sleeping"> + <doc:doc> + <doc:description> + <doc:para> + This signal is sent when the session is about to be suspended or + hibernated. + Session and system programs have one second to do anything required + before the sleep action is taken (such as sending out Avahi or + Jabber messages). + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <!-- ************************************************************ --> + + <signal name="Resuming"> + <doc:doc> + <doc:description> + <doc:para> + This signal is sent when the session has just returned from + Suspend() or Hibernate(). + Session and system programs can then do anything required (such as + sending out Avahi or Jabber messages). + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <!-- ************************************************************ --> + + <method name="AboutToSleep"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <doc:doc> + <doc:description> + <doc:para> + This method tells UPower that the Suspend() or Hibernate() method + is about to be called. + This allows UPower to emit the Suspending signal whilst + session activities are happening that have to be done before the + suspend process is started. + </doc:para> + <doc:para> + This method would typically be called by the session power + management daemon, before it locks the screen and waits for the + screen to fade to black. + The session power management component would then call Suspend() or + Hibernate() when these syncronous tasks have completed. + </doc:para> + <doc:para> + If this method is not called than nothing bad will happen and + Suspend() or Hibernate() will block for the required second. + </doc:para> + </doc:description> + </doc:doc> + </method> + <!-- ************************************************************ --> <method name="Suspend"> @@ -120,6 +180,15 @@ method return sender=:1.386 -> dest=:1.451 reply_serial=2 Suspends the computer into a low power state. System state is not preserved if the power is lost. </doc:para> + <doc:para> + If AboutToRequestSleep() has not been called then UPower will send + the Sleeping() signal and block for one second. + </doc:para> + <doc:para> + If AboutToRequestSleep() has been called less than one second + before this method is called then UPower will block for the + remaining time to complete one second of delay. + </doc:para> </doc:description> </doc:doc> </method> @@ -134,6 +203,15 @@ method return sender=:1.386 -> dest=:1.451 reply_serial=2 Hibernates the computer into a low power state. System state is preserved if the power is lost. </doc:para> + <doc:para> + If AboutToRequestSleep() has not been called then UPower will send + the Sleeping() signal and block for one second. + </doc:para> + <doc:para> + If AboutToRequestSleep() has been called less than one second + before this method is called then UPower will block for the + remaining time to complete one second of delay. + </doc:para> </doc:description> </doc:doc> </method> diff --git a/src/up-daemon.c b/src/up-daemon.c index b2f90a0..ac6e937 100644 --- a/src/up-daemon.c +++ b/src/up-daemon.c @@ -62,6 +62,8 @@ enum SIGNAL_DEVICE_REMOVED, SIGNAL_DEVICE_CHANGED, SIGNAL_CHANGED, + SIGNAL_SLEEPING, + SIGNAL_RESUMING, SIGNAL_LAST, }; @@ -85,6 +87,8 @@ struct UpDaemonPrivate gboolean during_coldplug; guint battery_poll_id; guint battery_poll_count; + GTimer *about_to_sleep_timer; + guint about_to_sleep_id; }; static void up_daemon_finalize (GObject *object); @@ -492,17 +496,134 @@ up_daemon_enumerate_devices (UpDaemon *daemon, DBusGMethodInvocation *context) } /** - * up_daemon_suspend: + * up_daemon_about_to_sleep: **/ gboolean -up_daemon_suspend (UpDaemon *daemon, DBusGMethodInvocation *context) +up_daemon_about_to_sleep (UpDaemon *daemon, DBusGMethodInvocation *context) +{ + PolkitSubject *subject = NULL; + GError *error; + + egg_debug ("emitting sleeping"); + g_signal_emit (daemon, signals[SIGNAL_SLEEPING], 0); + g_timer_start (daemon->priv->about_to_sleep_timer); + + /* already requested */ + if (daemon->priv->about_to_sleep_id != 0) { + error = g_error_new (UP_DAEMON_ERROR, + UP_DAEMON_ERROR_GENERAL, + "Sleep has already been requested and is pending"); + dbus_g_method_return_error (context, error); + goto out; + } + + subject = up_polkit_get_subject (daemon->priv->polkit, context); + if (subject == NULL) + goto out; + + /* TODO: use another PolicyKit context? */ + if (!up_polkit_check_auth (daemon->priv->polkit, subject, "org.freedesktop.upower.suspend", context)) + goto out; + + dbus_g_method_return (context, NULL); +out: + if (subject != NULL) + g_object_unref (subject); + return TRUE; +} + +/* temp object for deferred callback */ +typedef struct { + UpDaemon *daemon; + DBusGMethodInvocation *context; + gchar *command; +} UpDaemonDeferredSleep; + +/** + * up_daemon_deferred_sleep_cb: + **/ +static gboolean +up_daemon_deferred_sleep_cb (UpDaemonDeferredSleep *sleep) { - gboolean ret; GError *error; GError *error_local = NULL; - PolkitSubject *subject = NULL; gchar *stdout = NULL; gchar *stderr = NULL; + gboolean ret; + UpDaemon *daemon = sleep->daemon; + + /* run the command */ + ret = g_spawn_command_line_sync (sleep->command, &stdout, &stderr, NULL, &error_local); + if (!ret) { + error = g_error_new (UP_DAEMON_ERROR, + UP_DAEMON_ERROR_GENERAL, + "Failed to spawn: %s, stdout:%s, stderr:%s", error_local->message, stdout, stderr); + g_error_free (error_local); + dbus_g_method_return_error (sleep->context, error); + goto out; + } + + /* emit signal for session components */ + egg_debug ("emitting resuming"); + g_signal_emit (daemon, signals[SIGNAL_RESUMING], 0); + + /* reset the about-to-sleep logic */ + g_timer_reset (daemon->priv->about_to_sleep_timer); + g_timer_stop (daemon->priv->about_to_sleep_timer); + + /* actually return from the DBus call now */ + dbus_g_method_return (sleep->context, NULL); + +out: + /* clear timer */ + daemon->priv->about_to_sleep_id = 0; + + g_free (stdout); + g_free (stderr); + + /* delete temp object */ + g_object_unref (sleep->daemon); + g_free (sleep->command); + g_free (sleep); + + return FALSE; +} + +/** + * up_daemon_deferred_sleep: + **/ +static void +up_daemon_deferred_sleep (UpDaemon *daemon, const gchar *command, DBusGMethodInvocation *context) +{ + UpDaemonDeferredSleep *sleep; + gfloat elapsed; + + /* create callback object */ + sleep = g_new0 (UpDaemonDeferredSleep, 1); + sleep->daemon = g_object_ref (daemon); + sleep->context = context; + sleep->command = g_strdup (command); + + /* about to sleep */ + elapsed = g_timer_elapsed (daemon->priv->about_to_sleep_timer, NULL); + egg_debug ("between AboutToSleep() and %s was %fs", sleep->command, elapsed); + if (elapsed < 1.0f) { + /* we have to wait for a little bit */ + daemon->priv->about_to_sleep_id = g_timeout_add (1000 - (elapsed * 1000), (GSourceFunc) up_daemon_deferred_sleep_cb, sleep); + } else { + /* we can do this straight away */ + daemon->priv->about_to_sleep_id = g_idle_add ((GSourceFunc) up_daemon_deferred_sleep_cb, sleep); + } +} + +/** + * up_daemon_suspend: + **/ +gboolean +up_daemon_suspend (UpDaemon *daemon, DBusGMethodInvocation *context) +{ + GError *error; + PolkitSubject *subject = NULL; /* no kernel support */ if (!daemon->priv->kernel_can_suspend) { @@ -520,19 +641,18 @@ up_daemon_suspend (UpDaemon *daemon, DBusGMethodInvocation *context) if (!up_polkit_check_auth (daemon->priv->polkit, subject, "org.freedesktop.upower.suspend", context)) goto out; - ret = g_spawn_command_line_sync ("/usr/sbin/pm-suspend", &stdout, &stderr, NULL, &error_local); - if (!ret) { + /* already requested */ + if (daemon->priv->about_to_sleep_id != 0) { error = g_error_new (UP_DAEMON_ERROR, UP_DAEMON_ERROR_GENERAL, - "Failed to spawn: %s, stdout:%s, stderr:%s", error_local->message, stdout, stderr); - g_error_free (error_local); + "Sleep has already been requested and is pending"); dbus_g_method_return_error (context, error); goto out; } - dbus_g_method_return (context, NULL); + + /* do this deferred action */ + up_daemon_deferred_sleep (daemon, "/usr/sbin/pm-suspend", context); out: - g_free (stdout); - g_free (stderr); if (subject != NULL) g_object_unref (subject); return TRUE; @@ -544,12 +664,8 @@ out: gboolean up_daemon_hibernate (UpDaemon *daemon, DBusGMethodInvocation *context) { - gboolean ret; GError *error; - GError *error_local = NULL; PolkitSubject *subject = NULL; - gchar *stdout = NULL; - gchar *stderr = NULL; /* no kernel support */ if (!daemon->priv->kernel_can_hibernate) { @@ -585,19 +701,18 @@ up_daemon_hibernate (UpDaemon *daemon, DBusGMethodInvocation *context) if (!up_polkit_check_auth (daemon->priv->polkit, subject, "org.freedesktop.upower.hibernate", context)) goto out; - ret = g_spawn_command_line_sync ("/usr/sbin/pm-hibernate", &stdout, &stderr, NULL, &error_local); - if (!ret) { + /* already requested */ + if (daemon->priv->about_to_sleep_id != 0) { error = g_error_new (UP_DAEMON_ERROR, UP_DAEMON_ERROR_GENERAL, - "Failed to spawn: %s, stdout:%s, stderr:%s", error_local->message, stdout, stderr); - g_error_free (error_local); + "Sleep has already been requested and is pending"); dbus_g_method_return_error (context, error); goto out; } - dbus_g_method_return (context, NULL); + + /* do this deferred action */ + up_daemon_deferred_sleep (daemon, "/usr/sbin/pm-hibernate", context); out: - g_free (stdout); - g_free (stderr); if (subject != NULL) g_object_unref (subject); return TRUE; @@ -899,6 +1014,7 @@ up_daemon_init (UpDaemon *daemon) daemon->priv->during_coldplug = FALSE; daemon->priv->battery_poll_id = 0; daemon->priv->battery_poll_count = 0; + daemon->priv->about_to_sleep_id = 0; daemon->priv->backend = up_backend_new (); g_signal_connect (daemon->priv->backend, "device-added", @@ -906,6 +1022,10 @@ up_daemon_init (UpDaemon *daemon) g_signal_connect (daemon->priv->backend, "device-removed", G_CALLBACK (up_daemon_device_removed_cb), daemon); + /* use a timer for the about-to-sleep logic */ + daemon->priv->about_to_sleep_timer = g_timer_new (); + g_timer_stop (daemon->priv->about_to_sleep_timer); + /* watch when these properties change */ g_signal_connect (daemon, "notify::lid-is-present", G_CALLBACK (up_daemon_properties_changed_cb), daemon); @@ -1080,6 +1200,21 @@ up_daemon_class_init (UpDaemonClass *klass) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + signals[SIGNAL_SLEEPING] = + g_signal_new ("sleeping", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[SIGNAL_RESUMING] = + g_signal_new ("resuming", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); g_object_class_install_property (object_class, PROP_DAEMON_VERSION, @@ -1167,6 +1302,7 @@ up_daemon_finalize (GObject *object) g_object_unref (daemon->priv->power_devices); g_object_unref (daemon->priv->polkit); g_object_unref (daemon->priv->backend); + g_timer_destroy (daemon->priv->about_to_sleep_timer); G_OBJECT_CLASS (up_daemon_parent_class)->finalize (object); } diff --git a/src/up-daemon.h b/src/up-daemon.h index 9361ae8..c919a91 100644 --- a/src/up-daemon.h +++ b/src/up-daemon.h @@ -83,6 +83,8 @@ gboolean up_daemon_get_low_battery (UpDaemon *daemon, DBusGMethodInvocation *context); gboolean up_daemon_suspend (UpDaemon *daemon, DBusGMethodInvocation *context); +gboolean up_daemon_about_to_sleep (UpDaemon *daemon, + DBusGMethodInvocation *context); gboolean up_daemon_hibernate (UpDaemon *daemon, DBusGMethodInvocation *context); gboolean up_daemon_can_suspend (UpDaemon *daemon, |