summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Nocera <hadess@hadess.net>2021-07-16 13:40:10 +0200
committerBastien Nocera <hadess@hadess.net>2021-07-22 22:02:30 +0200
commit3ff8b95c333689cbae4a0aa868cc214b118e0b3a (patch)
treebec420026a918373fa58e1e7f0121a98895315af
parent25374412a610ab473a937562e34c48ea0992c60a (diff)
downloadgnome-settings-daemon-3ff8b95c333689cbae4a0aa868cc214b118e0b3a.tar.gz
power: Enable power-saver profile when low on battery
When low on battery, and if the feature is enabled, hold the power profile to "power-saver" until the battery is sufficiently recharged.
-rw-r--r--data/org.gnome.settings-daemon.plugins.power.gschema.xml.in5
-rw-r--r--plugins/power/gsd-power-manager.c128
-rwxr-xr-xplugins/power/test.py32
3 files changed, 165 insertions, 0 deletions
diff --git a/data/org.gnome.settings-daemon.plugins.power.gschema.xml.in b/data/org.gnome.settings-daemon.plugins.power.gschema.xml.in
index 93c704e9..04b287bd 100644
--- a/data/org.gnome.settings-daemon.plugins.power.gschema.xml.in
+++ b/data/org.gnome.settings-daemon.plugins.power.gschema.xml.in
@@ -41,5 +41,10 @@
<summary>Power button action</summary>
<description>The action to take when the system power button is pressed. This action is hard-coded (and the setting ignored) on virtual machines (power off) and tablets (suspend).</description>
</key>
+ <key name="power-saver-profile-on-low-battery" type="b">
+ <default>true</default>
+ <summary>Enable power-saver profile when battery is low</summary>
+ <description>Automatically enable the "power-saver" profile using power-profiles-daemon if the battery is low.</description>
+ </key>
</schema>
</schemalist>
diff --git a/plugins/power/gsd-power-manager.c b/plugins/power/gsd-power-manager.c
index 95cec9c3..1f125a6f 100644
--- a/plugins/power/gsd-power-manager.c
+++ b/plugins/power/gsd-power-manager.c
@@ -59,6 +59,10 @@
#define UPOWER_DBUS_INTERFACE "org.freedesktop.UPower"
#define UPOWER_DBUS_INTERFACE_KBDBACKLIGHT "org.freedesktop.UPower.KbdBacklight"
+#define PPD_DBUS_NAME "net.hadess.PowerProfiles"
+#define PPD_DBUS_PATH "/net/hadess/PowerProfiles"
+#define PPD_DBUS_INTERFACE "net.hadess.PowerProfiles"
+
#define GSD_POWER_SETTINGS_SCHEMA "org.gnome.settings-daemon.plugins.power"
#define GSD_POWER_DBUS_NAME GSD_DBUS_NAME ".Power"
@@ -185,6 +189,10 @@ struct _GsdPowerManager
gdouble ambient_last_absolute;
gint64 ambient_last_time;
+ /* Power Profiles */
+ GDBusProxy *power_profiles_proxy;
+ guint32 power_saver_cookie;
+
/* Sound */
guint32 critical_alert_timeout_id;
@@ -1928,6 +1936,67 @@ idle_configure (GsdPowerManager *manager)
}
static void
+hold_profile_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GsdPowerManager *manager = user_data;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GVariant) result = NULL;
+
+ result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
+ res,
+ &error);
+ if (result == NULL) {
+ g_warning ("Couldn't hold power-saver profile: %s", error->message);
+ return;
+ }
+
+ if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(u)"))) {
+ g_variant_get (result, "(u)", &manager->power_saver_cookie);
+ g_debug ("Holding power-saver profile with cookie %u", manager->power_saver_cookie);
+ } else {
+ g_warning ("Calling HoldProfile() did not return a uint32");
+ }
+}
+
+static void
+enable_power_saver (GsdPowerManager *manager)
+{
+ if (!manager->power_profiles_proxy)
+ return;
+ if (!g_settings_get_boolean (manager->settings, "power-saver-profile-on-low-battery"))
+ return;
+
+ g_debug ("Starting hold of power-saver profile");
+
+ g_dbus_proxy_call (manager->power_profiles_proxy,
+ "HoldProfile",
+ g_variant_new("(sss)",
+ "power-saver",
+ "Power saver profile when low on battery",
+ GSD_POWER_DBUS_NAME),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, manager->cancellable, hold_profile_cb, manager);
+}
+
+static void
+disable_power_saver (GsdPowerManager *manager)
+{
+ if (!manager->power_profiles_proxy || manager->power_saver_cookie == 0)
+ return;
+
+ g_debug ("Releasing power-saver profile");
+
+ g_dbus_proxy_call (manager->power_profiles_proxy,
+ "ReleaseProfile",
+ g_variant_new ("(u)", manager->power_saver_cookie),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, dbus_call_log_error, "ReleaseProfile failed");
+ manager->power_saver_cookie = 0;
+}
+
+static void
main_battery_or_ups_low_changed (GsdPowerManager *manager,
gboolean is_low)
{
@@ -1935,6 +2004,10 @@ main_battery_or_ups_low_changed (GsdPowerManager *manager,
return;
manager->battery_is_low = is_low;
idle_configure (manager);
+ if (is_low)
+ enable_power_saver (manager);
+ else
+ disable_power_saver (manager);
}
static gboolean
@@ -2079,6 +2152,39 @@ screensaver_signal_cb (GDBusProxy *proxy,
}
static void
+power_profiles_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);
+
+ if (g_strcmp0 (signal_name, "ProfileReleased") != 0)
+ return;
+ manager->power_saver_cookie = 0;
+}
+
+static void
+power_profiles_proxy_ready_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr(GError) error = NULL;
+ GsdPowerManager *manager = GSD_POWER_MANAGER (user_data);
+
+ manager->power_profiles_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+ if (manager->power_profiles_proxy == NULL) {
+ g_debug ("Could not connect to power-profiles-daemon: %s", error->message);
+ return;
+ }
+
+ g_signal_connect (manager->power_profiles_proxy, "g-signal",
+ G_CALLBACK (power_profiles_proxy_signal_cb),
+ manager);
+}
+
+static void
power_keyboard_proxy_ready_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
@@ -2289,6 +2395,14 @@ engine_settings_key_changed_cb (GSettings *settings,
idle_configure (manager);
return;
}
+ if (g_str_equal (key, "power-saver-profile-on-low-battery")) {
+ if (manager->battery_is_low &&
+ g_settings_get_boolean (settings, key))
+ enable_power_saver (manager);
+ else
+ disable_power_saver (manager);
+ return;
+ }
}
static void
@@ -2599,6 +2713,17 @@ on_rr_screen_acquired (GObject *object,
g_signal_connect (manager->up_client, "notify::on-battery",
G_CALLBACK (up_client_on_battery_cb), manager);
+ /* connect to power-profiles-daemon */
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ PPD_DBUS_NAME,
+ PPD_DBUS_PATH,
+ PPD_DBUS_INTERFACE,
+ manager->cancellable,
+ power_profiles_proxy_ready_cb,
+ manager);
+
/* connect to UPower for keyboard backlight control */
manager->kbd_brightness_now = -1;
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
@@ -2862,6 +2987,9 @@ gsd_power_manager_stop (GsdPowerManager *manager)
g_clear_object (&manager->screensaver_proxy);
+ disable_power_saver (manager);
+ g_clear_object (&manager->power_profiles_proxy);
+
play_loop_stop (&manager->critical_alert_timeout_id);
g_clear_object (&manager->idle_monitor);
diff --git a/plugins/power/test.py b/plugins/power/test.py
index d98efc0a..e1d7d5e6 100755
--- a/plugins/power/test.py
+++ b/plugins/power/test.py
@@ -80,6 +80,13 @@ class PowerPluginBase(gsdtestcase.GSDTestCase):
'gnome_screensaver')
self.addCleanup(self.stop_process, self.screensaver)
+ # start mock power-profiles-daemon
+ try:
+ (self.ppd, self.obj_ppd) = self.spawn_server_template('power_profiles_daemon')
+ self.addCleanup(self.stop_process, self.ppd)
+ except ModuleNotFoundError:
+ self.ppd = None
+
self.start_session()
self.addCleanup(self.stop_session)
@@ -1235,5 +1242,30 @@ class PowerPluginTest8(PowerPluginBase):
self.assertEqual(exc.exception.get_dbus_message(), 'No usable backlight could be found!')
+ def test_power_saver_on_low_battery(self):
+ '''Check that the power-saver profile gets held when low on battery'''
+
+ if not self.ppd:
+ self.skipTest("power-profiles-daemon dbusmock support is not available")
+
+ obj_props = dbus.Interface(self.obj_ppd, dbus.PROPERTIES_IFACE)
+
+ self.set_composite_battery_discharging()
+ time.sleep(0.5)
+ holds = obj_props.Get('net.hadess.PowerProfiles', 'ActiveProfileHolds')
+ self.assertEqual(len(holds), 0)
+
+ self.set_composite_battery_critical()
+ time.sleep(0.5)
+ holds = obj_props.Get('net.hadess.PowerProfiles', 'ActiveProfileHolds')
+ self.assertEqual(len(holds), 1)
+ self.assertEqual(holds[0]['Profile'], 'power-saver')
+ self.assertEqual(holds[0]['ApplicationId'], 'org.gnome.SettingsDaemon.Power')
+
+ self.set_composite_battery_discharging()
+ time.sleep(0.5)
+ holds = obj_props.Get('net.hadess.PowerProfiles', 'ActiveProfileHolds')
+ self.assertEqual(len(holds), 0)
+
# avoid writing to stderr
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))