summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libupower-glib/up-client.c42
-rw-r--r--libupower-glib/up-client.h3
-rw-r--r--src/org.freedesktop.UPower.xml78
-rw-r--r--src/up-daemon.c180
-rw-r--r--src/up-daemon.h2
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,