diff options
author | Bastien Nocera <hadess@hadess.net> | 2021-07-16 13:40:10 +0200 |
---|---|---|
committer | Bastien Nocera <hadess@hadess.net> | 2021-07-22 22:02:30 +0200 |
commit | 3ff8b95c333689cbae4a0aa868cc214b118e0b3a (patch) | |
tree | bec420026a918373fa58e1e7f0121a98895315af | |
parent | 25374412a610ab473a937562e34c48ea0992c60a (diff) | |
download | gnome-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.in | 5 | ||||
-rw-r--r-- | plugins/power/gsd-power-manager.c | 128 | ||||
-rwxr-xr-x | plugins/power/test.py | 32 |
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)) |