diff -Nru power-profiles-daemon-0.20/.gitlab-ci.yml power-profiles-daemon-0.21/.gitlab-ci.yml --- power-profiles-daemon-0.20/.gitlab-ci.yml 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/.gitlab-ci.yml 2024-04-03 23:55:02.000000000 +0000 @@ -2,8 +2,6 @@ variables: DEPENDENCIES: gcc - gcovr - gtk-doc pkgconfig(udev) pkgconfig(systemd) pkgconfig(gio-2.0) @@ -13,12 +11,17 @@ systemd meson git - python3-gobject - python3-dbusmock python3-packaging - python3-pylint - umockdev - e2fsprogs + TEST_DEPENDENCIES: e2fsprogs + python3-dbusmock + python3-gobject + python3-pylint + umockdev + TEST_DEBUG_DEPENDENCIES: glib2 + glibc + libgudev + upower + polkit-libs workflow: rules: @@ -31,11 +34,17 @@ before_script: - echo 8096000 > /proc/sys/fs/inotify/max_user_instances - mkdir -m 700 $TMPDIR -p + - if command -v dnf5 &>/dev/null; then ln -sfv $(command -v dnf5) /usr/local/bin/dnf; fi - dnf update -y - - if [ -x /bin/dnf ]; then - dnf install -y $DEPENDENCIES $JOB_DEPS; - else - dnf5 install -y $DEPENDENCIES $JOB_DEPS; + - dnf install -y $DEPENDENCIES $JOB_DEPS + # dnf5 does not include debuginfo-install yet, so let's use dnf4 instead. + - if [ -n "$JOB_DEBUG_DEPS" ]; then + if ! command -v dnf4 &>/dev/null; then + dnf install -y dnf4; + dnf4 update -y; + fi; + dnf4 install -y "dnf-command(debuginfo-install)"; + dnf4 debuginfo-install -y $JOB_DEBUG_DEPS; fi pre_commit: @@ -48,7 +57,12 @@ script: - pre-commit run --all-files -build_stable: +build_and_test: + variables: + JOB_DEPS: $TEST_DEPENDENCIES + gcovr + python3-coverage + JOB_DEBUG_DEPS: $TEST_DEBUG_DEPENDENCIES extends: - .install-deps script: @@ -56,35 +70,73 @@ --werror --fatal-meson-warnings --warnlevel 2 - -Dgtk_doc=true - -Dpylint=true + -Dpylint=enabled -Db_coverage=true _build - meson test -C _build --print-errorlogs - .ci/fail_skipped_tests.py _build/meson-logs/testlog.junit.xml - ninja -C _build coverage + - python_coverage_data=_build/meson-logs/python.coverage + - coverage3 combine --data-file=$python_coverage_data + _build/python-coverage/* + - coverage3 xml --data-file=$python_coverage_data + -o $python_coverage_data.xml + - sed "s,_build/src/,src/,g" -i $python_coverage_data.xml + - coverage3 report --data-file=$python_coverage_data + - coverage3 html --data-file=$python_coverage_data + -d _build/meson-logs/python-coverage-html - cat _build/meson-logs/coverage.txt || true - - meson install -C _build - - ninja -C _build uninstall -v - - meson dist -C _build artifacts: when: always paths: - _build/meson-logs/*.txt + - _build/meson-logs/*.xml - _build/meson-dist/* - - _build/meson-logs/coveragereport/index.html + - _build/meson-logs/coveragereport/* + - _build/meson-logs/python-coverage-html/* reports: junit: - _build/meson-logs/testlog.junit.xml coverage_report: coverage_format: cobertura - path: _build/meson-logs/coverage.xml + path: _build/meson-logs/*coverage.xml coverage: '/^TOTAL.*\s+(\d+\%)$/' +dist_install: + variables: + JOB_DEPS: argparse-manpage + bash-completion-devel + python3-gobject + python3-shtab + extends: + - .install-deps + script: + - meson setup + --werror + --fatal-meson-warnings + --warnlevel 2 + -Dmanpage=enabled + -Dbashcomp=enabled + -Dzshcomp=enabled + -Dtests=false + _build + - meson install -C _build + - ninja -C _build uninstall -v + - meson dist -C _build + artifacts: + when: always + paths: + - _build/meson-logs/*.txt + rules: + - if: ($CI_PIPELINE_SOURCE == "merge_request_event" || + $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH) + address_sanitizer: variables: - JOB_DEPS: libasan + JOB_DEPS: $TEST_DEPENDENCIES + libasan libubsan + JOB_DEBUG_DEPS: $TEST_DEBUG_DEPENDENCIES extends: - .install-deps script: @@ -101,7 +153,9 @@ valgrind: variables: - JOB_DEPS: valgrind + JOB_DEPS: $TEST_DEPENDENCIES + valgrind + JOB_DEBUG_DEPS: $TEST_DEBUG_DEPENDENCIES extends: - .install-deps script: @@ -118,12 +172,63 @@ scan_build: variables: JOB_DEPS: clang-analyzer + which extends: - .install-deps script: - - meson setup _build - - env SCANBUILD=$(which scan-build) ninja -C _build scan-build + - meson setup _build -Dtests=false + - env SCANBUILD=$(which scan-build) ninja -C _build scan-build artifacts: when: on_failure paths: - _build/meson-logs + +docs: + variables: + JOB_DEPS: gtk-doc + extends: + - .install-deps + script: + - meson setup + -Dgtk_doc=true + -Dtests=false + _build-docs + - ninja -C _build-docs power-profiles-daemon-doc + artifacts: + expose_as: "HTML Documentation" + paths: + - _build-docs/docs/html + - _build-docs/docs/html/index.html + expire_in: 1 week + rules: + - if: ($CI_PIPELINE_SOURCE == "merge_request_event" || + $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH) + +pages: + image: alpine:latest + stage: deploy + needs: + - job: build_and_test + artifacts: true + - job: docs + artifacts: true + script: + - mv _build-docs/docs/html public + - mkdir public/coverage + - mv _build/meson-logs/coveragereport public/coverage/daemon + - mv _build/meson-logs/python-coverage-html public/coverage/tool + - echo ' + + Power Profiles Daemon Coverage + + + + ' > public/coverage/index.html + artifacts: + paths: + - public + rules: + - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH diff -Nru power-profiles-daemon-0.20/NEWS power-profiles-daemon-0.21/NEWS --- power-profiles-daemon-0.20/NEWS 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/NEWS 2024-04-03 23:55:02.000000000 +0000 @@ -1,3 +1,25 @@ +0.21 +---- + +Since this release power-profiles-daemon is battery-state aware and some drivers +use a more power efficient state when using the balanced profile on battery. +In particular both the AMD and Intel P-State drivers will use the +balance_power EPP profile, while for Intel one we also set the energy +performance bias to 8 (instead of 6). + +This release also contains various fixes for the powerprofilesctl command line +tool when using the launch or version commands. +The tool is now better documented as we generate a manual page for it (if +python3-argparse is installed) and bash completions. We're even generating the +ZSH completions, but the install path must be provided. + +The daemon command line interface has been improved for debugging, so use +--help-debug for further information. + +The systemd service lockdown settings have been restricted even more. + +Various code optimizations. + 0.20 ---- diff -Nru power-profiles-daemon-0.20/README.md power-profiles-daemon-0.21/README.md --- power-profiles-daemon-0.20/README.md 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/README.md 2024-04-03 23:55:02.000000000 +0000 @@ -95,7 +95,7 @@ If that doesn't work, please file an issue, attach the output of: ```sh -sudo G_MESSAGES_DEBUG=all /usr/libexec/power-profiles-daemon -r -v +sudo /usr/libexec/power-profiles-daemon -r -vv ``` ## Operations on Intel-based machines @@ -179,8 +179,8 @@ 1. Adding `amdgpu.abmlevel=0` to the kernel command line. This will disable abm value changes entirely. -2. By using `POWER_PROFILE_DAEMON_ACTION_BLOCK=amdgpu_panel_power` in the - `power-profiles-daemon` environment as described below. This will allow you to +2. By using `--block-action=amdgpu_panel_power` in the + `power-profiles-daemon` `ExecStart` command. This will allow you to still change values manually in sysfs but `power-profiles-daemon` will not change anything. @@ -188,8 +188,7 @@ Power-profiles daemon will load all supported drivers and actions by default. If you have a problem with a given driver or action, you can disable it by -populating the `POWER_PROFILE_DAEMON_DRIVER_BLOCK` or `POWER_PROFILE_DAEMON_ACTION_BLOCK` -environment variables with the name of the driver or action you want to disable +using `--block-action` or `--block-driver` with the name of the driver or action you want to disable in the environment that launches the daemon (such as the systemd unit file). For example to edit the unit: @@ -198,12 +197,11 @@ sudo systemctl edit power-profiles-daemon.service ``` -Then add to the drop-in file: +Then modify the Execstart line to the drop-in file: ```text [Service] -Environment=POWER_PROFILE_DAEMON_DRIVER_BLOCK=xxx -Environment=POWER_PROFILE_DAEMON_ACTION_BLOCK=yyy +ExecStart=/usr/libexec/power-profiles-daemon --block-action=FOO ``` Then restart the service: diff -Nru power-profiles-daemon-0.20/data/power-profiles-daemon.service.in power-profiles-daemon-0.21/data/power-profiles-daemon.service.in --- power-profiles-daemon-0.20/data/power-profiles-daemon.service.in 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/data/power-profiles-daemon.service.in 2024-04-03 23:55:02.000000000 +0000 @@ -6,22 +6,35 @@ [Service] Type=dbus BusName=org.freedesktop.UPower.PowerProfiles +# To enable debugging add a -vv to the ExecStart line ExecStart=@libexecdir@/power-profiles-daemon Restart=on-failure # This always corresponds to /var/lib/power-profiles-daemon StateDirectory=power-profiles-daemon -#Uncomment this to enable debug -#Environment="G_MESSAGES_DEBUG=all" # Lockdown +DevicePolicy=closed +KeyringMode=private +LockPersonality=yes +MemoryDenyWriteExecute=yes +NoNewPrivileges=yes +PrivateDevices=yes +PrivateTmp=yes +ProtectClock=yes +ProtectControlGroups=yes +ProtectHome=yes +ProtectHostname=yes +ProtectKernelLogs=yes +ProtectKernelModules=yes +ProtectProc=invisible ProtectSystem=strict -ProtectControlGroups=true -ProtectHome=true -ProtectKernelModules=true -PrivateTmp=true +RemoveIPC=yes RestrictAddressFamilies=AF_UNIX AF_LOCAL AF_NETLINK MemoryDenyWriteExecute=true RestrictRealtime=true +RestrictNamespaces=yes +RestrictRealtime=yes +RestrictSUIDSGID=yes [Install] WantedBy=graphical.target diff -Nru power-profiles-daemon-0.20/debian/changelog power-profiles-daemon-0.21/debian/changelog --- power-profiles-daemon-0.20/debian/changelog 2024-03-09 18:25:30.000000000 +0000 +++ power-profiles-daemon-0.21/debian/changelog 2024-04-04 00:19:26.000000000 +0000 @@ -1,17 +1,8 @@ -power-profiles-daemon (0.20-3ubuntu1) noble; urgency=medium +power-profiles-daemon (0.21-1) unstable; urgency=medium - * Backport some patches from upstream that make the amd-pstate driver - transition between EPP states for Battery and AC. These help runtime - power consumption on battery with minimal impact to performance and - can help more machines meet energy certifications. + * New upstream release - -- Mario Limonciello Sat, 09 Mar 2024 12:25:30 -0600 - -power-profiles-daemon (0.20-3build1) noble; urgency=medium - - * No-change rebuild against libglib2.0-0t64 - - -- Steve Langasek Fri, 08 Mar 2024 06:43:03 +0000 + -- Marco Trevisan (Treviño) Thu, 04 Apr 2024 02:19:26 +0200 power-profiles-daemon (0.20-3) unstable; urgency=medium diff -Nru power-profiles-daemon-0.20/debian/control power-profiles-daemon-0.21/debian/control --- power-profiles-daemon-0.20/debian/control 2024-03-08 06:43:03.000000000 +0000 +++ power-profiles-daemon-0.21/debian/control 2024-04-04 00:19:26.000000000 +0000 @@ -1,8 +1,7 @@ Source: power-profiles-daemon Section: admin Priority: optional -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Debian freedesktop.org maintainers +Maintainer: Debian freedesktop.org maintainers Uploaders: Sebastien Bacher , Marco Trevisan (Treviño) Build-Depends: debhelper-compat (= 13), dh-python, diff -Nru power-profiles-daemon-0.20/debian/gbp.conf power-profiles-daemon-0.21/debian/gbp.conf --- power-profiles-daemon-0.20/debian/gbp.conf 2024-02-15 17:53:27.000000000 +0000 +++ power-profiles-daemon-0.21/debian/gbp.conf 2024-04-04 00:19:26.000000000 +0000 @@ -7,6 +7,9 @@ [buildpackage] sign-tags = True +[import-orig] +postimport = dch -v%(version)s New upstream release; git add debian/changelog; debcommit + [dch] multimaint-merge = True diff -Nru power-profiles-daemon-0.20/debian/patches/Add-support-for-upower-into-the-daemon.patch power-profiles-daemon-0.21/debian/patches/Add-support-for-upower-into-the-daemon.patch --- power-profiles-daemon-0.20/debian/patches/Add-support-for-upower-into-the-daemon.patch 2024-03-09 18:25:14.000000000 +0000 +++ power-profiles-daemon-0.21/debian/patches/Add-support-for-upower-into-the-daemon.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,323 +0,0 @@ -From 71161808db6f438e935575cccacd6db5aebbe09d Mon Sep 17 00:00:00 2001 -From: Mario Limonciello -Date: Sat, 24 Feb 2024 17:30:44 -0600 -Subject: [PATCH 1/3] Add support for upower into the daemon - -The daemon can notify CPU driver, Platform driver and all actions -of system power state changes. ---- - src/power-profiles-daemon.c | 128 ++++++++++++++++++++++++++++++++++++ - src/ppd-action.c | 12 ++++ - src/ppd-action.h | 13 ++-- - src/ppd-driver.c | 12 ++++ - src/ppd-driver.h | 5 ++ - src/ppd-profile.h | 16 +++++ - 6 files changed, 182 insertions(+), 4 deletions(-) - -diff --git a/src/power-profiles-daemon.c b/src/power-profiles-daemon.c -index eb673cf..c2ee13d 100644 ---- a/src/power-profiles-daemon.c -+++ b/src/power-profiles-daemon.c -@@ -36,6 +36,10 @@ - - #define POWER_PROFILES_RESOURCES_PATH "/org/freedesktop/UPower/PowerProfiles" - -+#define UPOWER_DBUS_NAME "org.freedesktop.UPower" -+#define UPOWER_DBUS_PATH "/org/freedesktop/UPower" -+#define UPOWER_DBUS_INTERFACE "org.freedesktop.UPower" -+ - #ifndef POLKIT_HAS_AUTOPOINTERS - /* FIXME: Remove this once we're fine to depend on polkit 0.114 */ - G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthorizationResult, g_object_unref) -@@ -63,6 +67,10 @@ typedef struct { - GPtrArray *actions; - GHashTable *profile_holds; - GLogLevelFlags log_level; -+ -+ GDBusProxy *upower_proxy; -+ guint watcher_id; -+ gboolean on_battery; - } PpdApp; - - typedef struct { -@@ -996,6 +1004,114 @@ bus_acquired_handler (GDBusConnection *connection, - data->app->connection = g_object_ref (connection); - } - -+static void -+upower_properties_changed (GDBusProxy *proxy, -+ GVariant *changed_properties, -+ GStrv invalidated_properties, -+ PpdApp *data) -+{ -+ g_autoptr (GVariant) battery_val = NULL; -+ PpdPowerChangedReason reason; -+ guint i; -+ -+ if (proxy != NULL) -+ battery_val = g_dbus_proxy_get_cached_property (proxy, "OnBattery"); -+ -+ data->on_battery = battery_val ? g_variant_get_boolean (battery_val) : FALSE; -+ -+ if (!battery_val) -+ reason = PPD_POWER_CHANGED_REASON_UNKNOWN; -+ else if (data->on_battery) -+ reason = PPD_POWER_CHANGED_REASON_BATTERY; -+ else -+ reason = PPD_POWER_CHANGED_REASON_AC; -+ -+ for (i = 0; i < data->actions->len; i++) { -+ g_autoptr (GError) error = NULL; -+ PpdAction *action; -+ gboolean ret; -+ -+ action = g_ptr_array_index (data->actions, i); -+ -+ ret = ppd_action_power_changed (action, reason, &error); -+ if (!ret) { -+ g_warning ("failed to update action %s: %s", -+ ppd_action_get_action_name (action), -+ error->message); -+ continue; -+ } -+ } -+ -+ if (PPD_IS_DRIVER_CPU (data->cpu_driver)) { -+ g_autoptr (GError) error = NULL; -+ gboolean ret; -+ -+ ret = ppd_driver_power_changed (PPD_DRIVER (data->cpu_driver), reason, &error); -+ if (!ret) { -+ g_warning ("failed to update driver %s: %s", -+ ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)), -+ error->message); -+ } -+ } -+ -+ if (PPD_IS_DRIVER_PLATFORM (data->platform_driver)) { -+ g_autoptr (GError) error = NULL; -+ gboolean ret; -+ -+ ret = ppd_driver_power_changed (PPD_DRIVER (data->platform_driver), reason, &error); -+ if (!ret) { -+ g_warning ("failed to update driver %s: %s", -+ ppd_driver_get_driver_name (PPD_DRIVER (data->platform_driver)), -+ error->message); -+ } -+ -+ } -+} -+ -+static void -+upower_name_vanished (GDBusConnection *connection, -+ const gchar *name, -+ gpointer user_data) -+{ -+ PpdApp *data = user_data; -+ -+ g_debug ("%s vanished", UPOWER_DBUS_NAME); -+ -+ /* reset */ -+ g_clear_pointer (&data->upower_proxy, g_object_unref); -+ upower_properties_changed (NULL, NULL, NULL, data); -+} -+ -+static void -+upower_name_appeared (GDBusConnection *connection, -+ const gchar *name, -+ const gchar *name_owner, -+ gpointer user_data) -+{ -+ PpdApp *data = user_data; -+ g_autoptr (GError) error = NULL; -+ -+ g_debug ("%s appeared", UPOWER_DBUS_NAME); -+ data->upower_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, -+ G_DBUS_PROXY_FLAGS_NONE, -+ NULL, -+ UPOWER_DBUS_NAME, -+ UPOWER_DBUS_PATH, -+ UPOWER_DBUS_INTERFACE, -+ NULL, -+ &error); -+ if (data->upower_proxy == NULL) { -+ g_debug ("failed to connect to upower: %s", error->message); -+ return; -+ } -+ -+ g_signal_connect (data->upower_proxy, -+ "g-properties-changed", -+ G_CALLBACK(upower_properties_changed), -+ data); -+ upower_properties_changed (data->upower_proxy, NULL, NULL, data); -+} -+ - static gboolean - has_required_drivers (PpdApp *data) - { -@@ -1163,6 +1279,15 @@ start_profile_drivers (PpdApp *data) - if (!activate_target_profile (data, data->active_profile, PPD_PROFILE_ACTIVATION_REASON_RESET, &initial_error)) - g_warning ("Failed to activate initial profile: %s", initial_error->message); - -+ /* start watching for power changes */ -+ data->watcher_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, -+ UPOWER_DBUS_NAME, -+ G_BUS_NAME_WATCHER_FLAGS_NONE, -+ upower_name_appeared, -+ upower_name_vanished, -+ data, -+ NULL); -+ - send_dbus_event (data, PROP_ALL); - - data->was_started = TRUE; -@@ -1182,6 +1307,7 @@ restart_profile_drivers (void) - start_profile_drivers (ppd_app); - } - -+ - static void - name_acquired_handler (GDBusConnection *connection, - const gchar *name, -@@ -1259,6 +1385,8 @@ free_app_data (PpdApp *data) - g_clear_handle_id (&data->name_id, g_bus_unown_name); - g_clear_handle_id (&data->legacy_name_id, g_bus_unown_name); - -+ g_clear_handle_id (&data->watcher_id, g_bus_unwatch_name); -+ g_clear_pointer (&data->upower_proxy, g_object_unref); - g_clear_pointer (&data->config_path, g_free); - g_clear_pointer (&data->config, g_key_file_unref); - g_ptr_array_free (data->probed_drivers, TRUE); -diff --git a/src/ppd-action.c b/src/ppd-action.c -index e550bcc..39b425b 100644 ---- a/src/ppd-action.c -+++ b/src/ppd-action.c -@@ -149,6 +149,18 @@ ppd_action_activate_profile (PpdAction *action, - return PPD_ACTION_GET_CLASS (action)->activate_profile (action, profile, error); - } - -+gboolean ppd_action_power_changed (PpdAction *action, -+ PpdPowerChangedReason reason, -+ GError **error) -+{ -+ g_return_val_if_fail (PPD_IS_ACTION (action), FALSE); -+ -+ if (!PPD_ACTION_GET_CLASS (action)->power_changed) -+ return TRUE; -+ -+ return PPD_ACTION_GET_CLASS (action)->power_changed (action, reason, error); -+} -+ - const char * - ppd_action_get_action_name (PpdAction *action) - { -diff --git a/src/ppd-action.h b/src/ppd-action.h -index 9b74ce3..c7682ae 100644 ---- a/src/ppd-action.h -+++ b/src/ppd-action.h -@@ -28,14 +28,19 @@ struct _PpdActionClass - { - GObjectClass parent_class; - -- PpdProbeResult (* probe) (PpdAction *action); -- gboolean (* activate_profile) (PpdAction *action, -- PpdProfile profile, -- GError **error); -+ PpdProbeResult (* probe) (PpdAction *action); -+ gboolean (* activate_profile) (PpdAction *action, -+ PpdProfile profile, -+ GError **error); -+ gboolean (* power_changed) (PpdAction *action, -+ PpdPowerChangedReason reason, -+ GError **error); -+ - }; - - #ifndef __GTK_DOC_IGNORE__ - PpdProbeResult ppd_action_probe (PpdAction *action); - gboolean ppd_action_activate_profile (PpdAction *action, PpdProfile profile, GError **error); -+gboolean ppd_action_power_changed (PpdAction *action, PpdPowerChangedReason reason, GError **error); - const char *ppd_action_get_action_name (PpdAction *action); - #endif -diff --git a/src/ppd-driver.c b/src/ppd-driver.c -index 435e9e9..257ee27 100644 ---- a/src/ppd-driver.c -+++ b/src/ppd-driver.c -@@ -236,6 +236,18 @@ ppd_driver_activate_profile (PpdDriver *driver, - return PPD_DRIVER_GET_CLASS (driver)->activate_profile (driver, profile, reason, error); - } - -+gboolean ppd_driver_power_changed (PpdDriver *driver, -+ PpdPowerChangedReason reason, -+ GError **error) -+{ -+ g_return_val_if_fail (PPD_IS_DRIVER (driver), FALSE); -+ -+ if (!PPD_DRIVER_GET_CLASS (driver)->power_changed) -+ return TRUE; -+ -+ return PPD_DRIVER_GET_CLASS (driver)->power_changed (driver, reason, error); -+} -+ - const char * - ppd_driver_get_driver_name (PpdDriver *driver) - { -diff --git a/src/ppd-driver.h b/src/ppd-driver.h -index f02bd4d..c9f1deb 100644 ---- a/src/ppd-driver.h -+++ b/src/ppd-driver.h -@@ -45,6 +45,7 @@ typedef enum{ - * @parent_class: The parent class. - * @probe: Called by the daemon on startup. - * @activate_profile: Called by the daemon for every profile change. -+ * @power_changed: Called by the daemon when power adapter status changes - * - * New profile drivers should not derive from #PpdDriver. They should - * derive from the child from #PpdDriverCpu or #PpdDriverPlatform drivers -@@ -59,12 +60,16 @@ struct _PpdDriverClass - PpdProfile profile, - PpdProfileActivationReason reason, - GError **error); -+ gboolean (* power_changed) (PpdDriver *driver, -+ PpdPowerChangedReason reason, -+ GError **error); - }; - - #ifndef __GTK_DOC_IGNORE__ - PpdProbeResult ppd_driver_probe (PpdDriver *driver); - gboolean ppd_driver_activate_profile (PpdDriver *driver, - PpdProfile profile, PpdProfileActivationReason reason, GError **error); -+gboolean ppd_driver_power_changed (PpdDriver *driver, PpdPowerChangedReason reason, GError **error); - const char *ppd_driver_get_driver_name (PpdDriver *driver); - PpdProfile ppd_driver_get_profiles (PpdDriver *driver); - const char *ppd_driver_get_performance_degraded (PpdDriver *driver); -diff --git a/src/ppd-profile.h b/src/ppd-profile.h -index 4af46ad..b4c1dde 100644 ---- a/src/ppd-profile.h -+++ b/src/ppd-profile.h -@@ -45,6 +45,22 @@ typedef enum { - PPD_PROFILE_PERFORMANCE = 1 << 2 - } PpdProfile; - -+/** -+ * PpdPowerChangedReason: -+ * @PPD_POWER_CHANGED_REASON_UNKNOWN: the power state is now unknown. -+ * This can happen if the power notification service is no longer available. -+ * @PPD_POWER_CHANGED_REASON_AC: the power source is now AC. -+ * @PPD_POWER_CHANGED_REASON_BATTERY: the power source is battery. -+ -+ * Drivers or actions can use this information to decide what to do within a -+ * given profile. -+ */ -+typedef enum{ -+ PPD_POWER_CHANGED_REASON_UNKNOWN = 0, -+ PPD_POWER_CHANGED_REASON_AC, -+ PPD_POWER_CHANGED_REASON_BATTERY, -+} PpdPowerChangedReason; -+ - #define PPD_PROFILE_ALL (PPD_PROFILE_BALANCED | PPD_PROFILE_POWER_SAVER | PPD_PROFILE_PERFORMANCE) - #define PPD_PROFILE_UNSET (0) - --- -2.34.1 - diff -Nru power-profiles-daemon-0.20/debian/patches/Generate-bash-completion-and-zsh-completion-using-shtab.patch power-profiles-daemon-0.21/debian/patches/Generate-bash-completion-and-zsh-completion-using-shtab.patch --- power-profiles-daemon-0.20/debian/patches/Generate-bash-completion-and-zsh-completion-using-shtab.patch 2024-03-09 18:25:14.000000000 +0000 +++ power-profiles-daemon-0.21/debian/patches/Generate-bash-completion-and-zsh-completion-using-shtab.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,128 +0,0 @@ -From: Mario Limonciello -Date: Wed, 14 Feb 2024 22:39:55 -0600 -Subject: Generate bash-completion and zsh-completion using shtab - -Origin: https://gitlab.freedesktop.org/upower/power-profiles-daemon/commit/3d509fa ---- - meson.build | 17 +++++++++++++++-- - meson_options.txt | 8 ++++++++ - src/meson.build | 32 ++++++++++++++++++++++++++++++++ - src/powerprofilesctl | 7 +++++++ - 4 files changed, 62 insertions(+), 2 deletions(-) - -diff --git a/meson.build b/meson.build -index 05571c4..3a31aac 100644 ---- a/meson.build -+++ b/meson.build -@@ -67,6 +67,18 @@ address_sanitizer = get_option('b_sanitize') == 'address' or \ - get_option('b_sanitize') == 'address,undefined' or \ - get_option('b_sanitize') == 'leak' - -+python = import('python') -+python3 = python.find_installation('python3') -+ -+have_shtab = run_command( -+ [python3, '-c', 'import shtab'], check: false).returncode() == 0 -+if not have_shtab -+ warning('''python3-shtab not found''') -+endif -+ -+bashcomp = dependency('bash-completion', required: get_option('bashcomp').disable_auto_if(not have_shtab)) -+zshcomp = have_shtab and get_option('zshcomp') != '' -+ - subdir('src') - subdir('data') - -@@ -80,6 +92,9 @@ if get_option('gtk_doc') - subdir('docs') - endif - -+python = import('python') -+python3 = python.find_installation('python3') -+ - if get_option('tests') - # Python 3 required modules - python3_required_modules = ['dbusmock', 'gi'] -@@ -89,8 +104,6 @@ if get_option('tests') - 'UMockdev': '1.0', - } - -- python = import('python') -- python3 = python.find_installation('python3') - foreach p : python3_required_modules - # Source: https://docs.python.org/3/library/importlib.html#checking-if-a-module-can-be-imported - script = 'import importlib.util; import sys; exit(1) if importlib.util.find_spec(\''+ p +'\') is None else exit(0)' -diff --git a/meson_options.txt b/meson_options.txt -index 5f3252f..e5a31fd 100644 ---- a/meson_options.txt -+++ b/meson_options.txt -@@ -18,3 +18,11 @@ option('manpage', - description: 'generate powerprofilesctl man page', - type: 'feature', - value: 'auto') -+option('bashcomp', -+ description: 'generate bash completion', -+ type: 'feature', -+ value: 'auto') -+option('zshcomp', -+ description: 'path for zsh completion file', -+ type: 'string', -+ value: '') -diff --git a/src/meson.build b/src/meson.build -index 49d62a9..bf00f2d 100644 ---- a/src/meson.build -+++ b/src/meson.build -@@ -146,3 +146,35 @@ if argparse_manpage.found() - output: 'powerprofilesctl.1', - )) - endif -+ -+generate_completion = [python3, powerprofilesctl, '--print-completion'] -+if bashcomp.found() -+ completions_dir = bashcomp.get_variable(pkgconfig: 'completionsdir', -+ pkgconfig_define: bashcomp.version().version_compare('>= 2.10') ? -+ ['datadir', get_option('datadir')] : ['prefix', prefix], -+ ) -+ -+ custom_target('bash-completion', -+ output: 'powerprofilesctl', -+ command: [ -+ generate_completion, -+ 'bash', -+ ], -+ capture: true, -+ install: true, -+ install_dir: completions_dir, -+ ) -+endif -+ -+if zshcomp -+ custom_target('zsh-completion', -+ output: '_powerprofilesctl', -+ command: [ -+ generate_completion, -+ 'zsh', -+ ], -+ capture: true, -+ install: true, -+ install_dir: get_option('zshcomp'), -+ ) -+endif -diff --git a/src/powerprofilesctl b/src/powerprofilesctl -index a363bbc..768c658 100755 ---- a/src/powerprofilesctl -+++ b/src/powerprofilesctl -@@ -190,6 +190,13 @@ def get_parser(): - ) - parser_version.set_defaults(func=_version) - -+ try: -+ import shtab # pylint: disable=import-outside-toplevel -+ -+ shtab.add_argument_to(parser, ["-s", "--print-completion"]) # magic! -+ except ImportError: -+ pass -+ - return parser - - diff -Nru power-profiles-daemon-0.20/debian/patches/amd-pstate-Transition-between-balanced_power-and-bal.patch power-profiles-daemon-0.21/debian/patches/amd-pstate-Transition-between-balanced_power-and-bal.patch --- power-profiles-daemon-0.20/debian/patches/amd-pstate-Transition-between-balanced_power-and-bal.patch 2024-03-09 18:25:14.000000000 +0000 +++ power-profiles-daemon-0.21/debian/patches/amd-pstate-Transition-between-balanced_power-and-bal.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,209 +0,0 @@ -From bdc60171e57e17819e8b14552ebb9ab4e1ab4bd9 Mon Sep 17 00:00:00 2001 -From: Mario Limonciello -Date: Sat, 24 Feb 2024 19:17:00 -0600 -Subject: [PATCH 3/3] amd-pstate: Transition between balanced_power and - balanced_performance - -When the system is on AC and profile is balanced put the EPP value -of balanced_performance. -When the system is on battery and profile is balanced put the EPP value -of balanced_power. ---- - src/ppd-driver-amd-pstate.c | 40 ++++++++++++++++++--- - tests/integration_test.py | 70 +++++++++++++++++++++++++++++++++++++ - 2 files changed, 106 insertions(+), 4 deletions(-) - -diff --git a/src/ppd-driver-amd-pstate.c b/src/ppd-driver-amd-pstate.c -index b1f6b11..06fc8c9 100644 ---- a/src/ppd-driver-amd-pstate.c -+++ b/src/ppd-driver-amd-pstate.c -@@ -38,6 +38,7 @@ struct _PpdDriverAmdPstate - - PpdProfile activated_profile; - GList *epp_devices; /* GList of paths */ -+ gboolean on_battery; - }; - - G_DEFINE_TYPE (PpdDriverAmdPstate, ppd_driver_amd_pstate, PPD_TYPE_DRIVER_CPU) -@@ -166,7 +167,7 @@ profile_to_gov_pref (PpdProfile profile) - } - - static const char * --profile_to_epp_pref (PpdProfile profile) -+profile_to_epp_pref (PpdProfile profile, gboolean battery) - { - /* Note that we don't check "energy_performance_available_preferences" - * as all the values are always available */ -@@ -174,7 +175,7 @@ profile_to_epp_pref (PpdProfile profile) - case PPD_PROFILE_POWER_SAVER: - return "power"; - case PPD_PROFILE_BALANCED: -- return "balance_performance"; -+ return battery ? "balance_power" : "balance_performance"; - case PPD_PROFILE_PERFORMANCE: - return "performance"; - } -@@ -185,11 +186,15 @@ profile_to_epp_pref (PpdProfile profile) - static gboolean - apply_pref_to_devices (GList *devices, - PpdProfile profile, -+ gboolean battery, - GError **error) - { - gboolean ret = TRUE; - GList *l; - -+ if (profile == PPD_PROFILE_UNSET) -+ return TRUE; -+ - for (l = devices; l != NULL; l = l->next) { - const char *base = l->data; - g_autofree char *epp = NULL; -@@ -207,7 +212,7 @@ apply_pref_to_devices (GList *devices, - "energy_performance_preference", - NULL); - -- ret = ppd_utils_write (epp, profile_to_epp_pref (profile), error); -+ ret = ppd_utils_write (epp, profile_to_epp_pref (profile, battery), error); - if (!ret) - break; - } -@@ -227,12 +232,13 @@ ppd_driver_amd_pstate_activate_profile (PpdDriver *driver, - g_return_val_if_fail (pstate->epp_devices != NULL, FALSE); - - if (pstate->epp_devices) { -- ret = apply_pref_to_devices (pstate->epp_devices, profile, error); -+ ret = apply_pref_to_devices (pstate->epp_devices, profile, pstate->on_battery, error); - if (!ret && pstate->activated_profile != PPD_PROFILE_UNSET) { - g_autoptr(GError) error_local = NULL; - /* reset back to previous */ - if (!apply_pref_to_devices (pstate->epp_devices, - pstate->activated_profile, -+ pstate->on_battery, - &error_local)) - g_warning ("failed to restore previous profile: %s", error_local->message); - return ret; -@@ -245,6 +251,31 @@ ppd_driver_amd_pstate_activate_profile (PpdDriver *driver, - return ret; - } - -+static gboolean -+ppd_driver_amd_pstate_power_changed (PpdDriver *driver, -+ PpdPowerChangedReason reason, -+ GError **error) -+{ -+ PpdDriverAmdPstate *pstate = PPD_DRIVER_AMD_PSTATE (driver); -+ -+ switch (reason) { -+ case PPD_POWER_CHANGED_REASON_UNKNOWN: -+ case PPD_POWER_CHANGED_REASON_AC: -+ pstate->on_battery = FALSE; -+ break; -+ case PPD_POWER_CHANGED_REASON_BATTERY: -+ pstate->on_battery = TRUE; -+ break; -+ default: -+ g_assert_not_reached (); -+ } -+ -+ return apply_pref_to_devices (pstate->epp_devices, -+ pstate->activated_profile, -+ pstate->on_battery, -+ error); -+} -+ - static void - ppd_driver_amd_pstate_finalize (GObject *object) - { -@@ -268,6 +299,7 @@ ppd_driver_amd_pstate_class_init (PpdDriverAmdPstateClass *klass) - driver_class = PPD_DRIVER_CLASS (klass); - driver_class->probe = ppd_driver_amd_pstate_probe; - driver_class->activate_profile = ppd_driver_amd_pstate_activate_profile; -+ driver_class->power_changed = ppd_driver_amd_pstate_power_changed; - } - - static void -diff --git a/tests/integration_test.py b/tests/integration_test.py -index 8ec129b..694b879 100644 ---- a/tests/integration_test.py -+++ b/tests/integration_test.py -@@ -1280,6 +1280,76 @@ class Tests(dbusmock.DBusTestCase): - profiles = self.get_dbus_property("Profiles") - self.assertEqual(len(profiles), 2) - -+ def test_amd_pstate_upower(self): -+ """Switching between balance_power and balance_performance based on battery""" -+ # Create 2 CPUs with preferences -+ dir1 = os.path.join( -+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/" -+ ) -+ os.makedirs(dir1) -+ self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n") -+ self.write_file_contents( -+ os.path.join(dir1, "energy_performance_preference"), "performance\n" -+ ) -+ dir2 = os.path.join( -+ self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy1/" -+ ) -+ os.makedirs(dir2) -+ self.write_file_contents(os.path.join(dir2, "scaling_governor"), "powersave\n") -+ self.write_file_contents( -+ os.path.join(dir2, "energy_performance_preference"), "performance\n" -+ ) -+ -+ # Create AMD P-State configuration -+ pstate_dir = os.path.join( -+ self.testbed.get_root_dir(), "sys/devices/system/cpu/amd_pstate" -+ ) -+ os.makedirs(pstate_dir) -+ self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n") -+ -+ # desktop PM profile -+ dir3 = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/") -+ os.makedirs(dir3) -+ self.write_file_contents(os.path.join(dir3, "pm_profile"), "1\n") -+ -+ ( -+ upowerd, -+ garbage, # pylint: disable=unused-variable -+ ) = self.spawn_server_template( -+ "upower", -+ {"DaemonVersion": "0.99", "OnBattery": True}, -+ stdout=subprocess.PIPE, -+ ) -+ -+ self.start_daemon() -+ -+ profiles = self.get_dbus_property("Profiles") -+ self.assertEqual(len(profiles), 3) -+ -+ self.assertEqual(profiles[0]["Driver"], "multiple") -+ self.assertEqual(profiles[0]["CpuDriver"], "amd_pstate") -+ self.assertEqual(profiles[0]["Profile"], "power-saver") -+ -+ energy_prefs = os.path.join(dir2, "energy_performance_preference") -+ scaling_governor = os.path.join(dir2, "scaling_governor") -+ -+ self.assert_file_eventually_contains(energy_prefs, "balance_power") -+ self.assert_file_eventually_contains(scaling_governor, "powersave") -+ -+ upowerd.terminate() -+ upowerd.wait() -+ upowerd.stdout.close() -+ -+ self.assert_file_eventually_contains(energy_prefs, "balance_performance") -+ -+ upowerd, garbage = self.spawn_server_template( -+ "upower", -+ {"DaemonVersion": "0.99", "OnBattery": False}, -+ stdout=subprocess.PIPE, -+ ) -+ -+ self.assert_file_eventually_contains(energy_prefs, "balance_performance") -+ - def test_amdgpu_panel_power(self): - """Verify AMDGPU Panel power actions""" - amdgpu_panel_power_savings = "amdgpu/panel_power_savings" --- -2.34.1 - diff -Nru power-profiles-daemon-0.20/debian/patches/amdgpu-Use-the-upower-core-support-from-the-daemon.patch power-profiles-daemon-0.21/debian/patches/amdgpu-Use-the-upower-core-support-from-the-daemon.patch --- power-profiles-daemon-0.20/debian/patches/amdgpu-Use-the-upower-core-support-from-the-daemon.patch 2024-03-09 18:25:14.000000000 +0000 +++ power-profiles-daemon-0.21/debian/patches/amdgpu-Use-the-upower-core-support-from-the-daemon.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,168 +0,0 @@ -From ab0cef6ee23f3dad58b8248b43b26e1f3f03f70f Mon Sep 17 00:00:00 2001 -From: Mario Limonciello -Date: Sat, 24 Feb 2024 17:31:06 -0600 -Subject: [PATCH 2/3] amdgpu: Use the upower core support from the daemon - -No need to track in the action anymore. ---- - src/ppd-action-amdgpu-panel-power.c | 99 +++++++---------------------- - 1 file changed, 23 insertions(+), 76 deletions(-) - -diff --git a/src/ppd-action-amdgpu-panel-power.c b/src/ppd-action-amdgpu-panel-power.c -index c2e824b..3468fa0 100644 ---- a/src/ppd-action-amdgpu-panel-power.c -+++ b/src/ppd-action-amdgpu-panel-power.c -@@ -43,10 +43,9 @@ struct _PpdActionAmdgpuPanelPower - PpdProfile last_profile; - - GUdevClient *client; -- GDBusProxy *proxy; -- guint watcher_id; - - gint panel_power_saving; -+ gboolean valid_battery; - gboolean on_battery; - }; - -@@ -175,7 +174,7 @@ ppd_action_amdgpu_panel_power_activate_profile (PpdAction *action, - PpdActionAmdgpuPanelPower *self = PPD_ACTION_AMDGPU_PANEL_POWER (action); - self->last_profile = profile; - -- if (self->proxy == NULL) { -+ if (!self->valid_battery) { - g_debug ("upower not available; battery data might be stale"); - return TRUE; - } -@@ -183,28 +182,29 @@ ppd_action_amdgpu_panel_power_activate_profile (PpdAction *action, - return ppd_action_amdgpu_panel_update_target (self, error); - } - -- --static void --upower_properties_changed (GDBusProxy *proxy, -- GVariant *changed_properties, -- GStrv invalidated_properties, -- PpdActionAmdgpuPanelPower *self) -+static gboolean -+ppd_action_amdgpu_panel_power_power_changed (PpdAction *action, -+ PpdPowerChangedReason reason, -+ GError **error) - { -- g_autoptr (GVariant) battery_val = NULL; -- g_autoptr (GError) error = NULL; -- gboolean new_on_battery; -- -- if (proxy != NULL) -- battery_val = g_dbus_proxy_get_cached_property (proxy, "OnBattery"); -- new_on_battery = battery_val ? g_variant_get_boolean (battery_val) : FALSE; -+ PpdActionAmdgpuPanelPower *self = PPD_ACTION_AMDGPU_PANEL_POWER (action); - -- if (self->on_battery == new_on_battery) -- return; -+ switch (reason) { -+ case PPD_POWER_CHANGED_REASON_UNKNOWN: -+ self->valid_battery = FALSE; -+ return TRUE; -+ case PPD_POWER_CHANGED_REASON_AC: -+ self->on_battery = FALSE; -+ break; -+ case PPD_POWER_CHANGED_REASON_BATTERY: -+ self->on_battery = TRUE; -+ break; -+ default: -+ g_assert_not_reached (); -+ } - -- g_debug ("OnBattery: %d -> %d", self->on_battery, new_on_battery); -- self->on_battery = new_on_battery; -- if (!ppd_action_amdgpu_panel_update_target (self, &error)) -- g_warning ("failed to update target: %s", error->message); -+ self->valid_battery = TRUE; -+ return ppd_action_amdgpu_panel_update_target (self, error); - } - - static void -@@ -260,59 +260,13 @@ ppd_action_amdgpu_panel_power_probe (PpdAction *action) - return PPD_PROBE_RESULT_FAIL; - } - --static void --upower_name_vanished (GDBusConnection *connection, -- const gchar *name, -- gpointer user_data) --{ -- PpdActionAmdgpuPanelPower *self = user_data; -- -- g_debug ("%s vanished", UPOWER_DBUS_NAME); -- -- /* reset */ -- g_clear_pointer (&self->proxy, g_object_unref); -- upower_properties_changed (NULL, NULL, NULL, self); --} -- --static void --upower_name_appeared (GDBusConnection *connection, -- const gchar *name, -- const gchar *name_owner, -- gpointer user_data) --{ -- PpdActionAmdgpuPanelPower *self = user_data; -- g_autoptr (GError) error = NULL; -- -- g_debug ("%s appeared", UPOWER_DBUS_NAME); -- self->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, -- G_DBUS_PROXY_FLAGS_NONE, -- NULL, -- UPOWER_DBUS_NAME, -- UPOWER_DBUS_PATH, -- UPOWER_DBUS_INTERFACE, -- NULL, -- &error); -- if (self->proxy == NULL) { -- g_debug ("failed to connect to upower: %s", error->message); -- return; -- } -- -- g_signal_connect (self->proxy, -- "g-properties-changed", -- G_CALLBACK(upower_properties_changed), -- self); -- upower_properties_changed (self->proxy, NULL, NULL, self); --} -- - static void - ppd_action_amdgpu_panel_power_finalize (GObject *object) - { - PpdActionAmdgpuPanelPower *action; - - action = PPD_ACTION_AMDGPU_PANEL_POWER (object); -- g_clear_handle_id (&action->watcher_id, g_bus_unwatch_name); - g_clear_object (&action->client); -- g_clear_pointer (&action->proxy, g_object_unref); - G_OBJECT_CLASS (ppd_action_amdgpu_panel_power_parent_class)->finalize (object); - } - -@@ -329,6 +283,7 @@ ppd_action_amdgpu_panel_power_class_init (PpdActionAmdgpuPanelPowerClass *klass) - driver_class = PPD_ACTION_CLASS(klass); - driver_class->probe = ppd_action_amdgpu_panel_power_probe; - driver_class->activate_profile = ppd_action_amdgpu_panel_power_activate_profile; -+ driver_class->power_changed = ppd_action_amdgpu_panel_power_power_changed; - } - - static void -@@ -339,12 +294,4 @@ ppd_action_amdgpu_panel_power_init (PpdActionAmdgpuPanelPower *self) - self->client = g_udev_client_new (subsystem); - g_signal_connect (G_OBJECT (self->client), "uevent", - G_CALLBACK (udev_uevent_cb), self); -- -- self->watcher_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, -- UPOWER_DBUS_NAME, -- G_BUS_NAME_WATCHER_FLAGS_NONE, -- upower_name_appeared, -- upower_name_vanished, -- self, -- NULL); - } --- -2.34.1 - diff -Nru power-profiles-daemon-0.20/debian/patches/build-Bump-dependency-on-polkit-gobject-1-0.99.patch power-profiles-daemon-0.21/debian/patches/build-Bump-dependency-on-polkit-gobject-1-0.99.patch --- power-profiles-daemon-0.20/debian/patches/build-Bump-dependency-on-polkit-gobject-1-0.99.patch 2024-03-09 18:25:14.000000000 +0000 +++ power-profiles-daemon-0.21/debian/patches/build-Bump-dependency-on-polkit-gobject-1-0.99.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= -Date: Thu, 15 Feb 2024 06:10:18 +0100 -Subject: build: Bump dependency on polkit-gobject-1 0.99 - -It's still old enough to work on old distros, but it's what debian's -automatic dependencies system (based on library exported symbols) tells -us, so we should trust it! - -Origin: https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/commit/d225c19 ---- - meson.build | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/meson.build b/meson.build -index 1a40c22..05571c4 100644 ---- a/meson.build -+++ b/meson.build -@@ -41,7 +41,7 @@ gio_unix_dep = dependency('gio-unix-2.0') - gio_dep = dependency('gio-2.0') - gudev_dep = dependency('gudev-1.0', version: '>= 234') - upower_dep = dependency('upower-glib') --polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.91') -+polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.99') - polkit_policy_directory = polkit_gobject_dep.get_variable('policydir') - - gnome = import('gnome') diff -Nru power-profiles-daemon-0.20/debian/patches/build-Expose-powerprofilesctl-script-and-load-it-using-fi.patch power-profiles-daemon-0.21/debian/patches/build-Expose-powerprofilesctl-script-and-load-it-using-fi.patch --- power-profiles-daemon-0.20/debian/patches/build-Expose-powerprofilesctl-script-and-load-it-using-fi.patch 2024-03-09 18:25:14.000000000 +0000 +++ power-profiles-daemon-0.21/debian/patches/build-Expose-powerprofilesctl-script-and-load-it-using-fi.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= -Date: Thu, 15 Feb 2024 03:22:52 +0100 -Subject: build: Expose powerprofilesctl script and load it using files - -Origin: https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/commit/7cd764c ---- - src/meson.build | 7 +++---- - 1 file changed, 3 insertions(+), 4 deletions(-) - -diff --git a/src/meson.build b/src/meson.build -index 88b8ff6..3f59dd3 100644 ---- a/src/meson.build -+++ b/src/meson.build -@@ -104,15 +104,14 @@ executable('power-profiles-daemon', - install_dir: libexecdir - ) - --script = 'powerprofilesctl' --install_data(script, -+powerprofilesctl = files('powerprofilesctl') -+install_data(powerprofilesctl, - install_dir: get_option('bindir') - ) --script = join_paths(meson.current_source_dir(), script) - if get_option('pylint') - test('pylint-powerprofilesctl', - pylint, -- args: pylint_flags + [ script ], -+ args: pylint_flags + [ powerprofilesctl ], - env: nomalloc, - ) - endif diff -Nru power-profiles-daemon-0.20/debian/patches/fix-typo-in-meson_options.txt.patch power-profiles-daemon-0.21/debian/patches/fix-typo-in-meson_options.txt.patch --- power-profiles-daemon-0.20/debian/patches/fix-typo-in-meson_options.txt.patch 2024-03-09 18:25:14.000000000 +0000 +++ power-profiles-daemon-0.21/debian/patches/fix-typo-in-meson_options.txt.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -From: Mario Limonciello -Date: Wed, 14 Feb 2024 22:50:30 -0600 -Subject: fix typo in meson_options.txt - -Origin: https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/commit/9a3b379 ---- - meson_options.txt | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/meson_options.txt b/meson_options.txt -index 0de978c..5f3252f 100644 ---- a/meson_options.txt -+++ b/meson_options.txt -@@ -15,6 +15,6 @@ option('tests', - type: 'boolean', - value: true) - option('manpage', -- description: 'gemerate powerprofilesctl man page', -+ description: 'generate powerprofilesctl man page', - type: 'feature', - value: 'auto') diff -Nru power-profiles-daemon-0.20/debian/patches/powerprofilectl-Generate-manpage-using-argparse-manpage.patch power-profiles-daemon-0.21/debian/patches/powerprofilectl-Generate-manpage-using-argparse-manpage.patch --- power-profiles-daemon-0.20/debian/patches/powerprofilectl-Generate-manpage-using-argparse-manpage.patch 2024-03-09 18:25:14.000000000 +0000 +++ power-profiles-daemon-0.21/debian/patches/powerprofilectl-Generate-manpage-using-argparse-manpage.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,105 +0,0 @@ -From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= -Date: Thu, 15 Feb 2024 04:05:37 +0100 -Subject: powerprofilectl: Generate manpage using argparse-manpage - -Add an option to toggle this feature so that it can be either -required or not - -Origin: https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/commit/7858623 ---- - meson.build | 2 ++ - meson_options.txt | 4 ++++ - src/meson.build | 31 +++++++++++++++++++++++++++++++ - src/powerprofilesctl | 8 ++++++-- - 4 files changed, 43 insertions(+), 2 deletions(-) - -diff --git a/meson.build b/meson.build -index cd35078..1a40c22 100644 ---- a/meson.build -+++ b/meson.build -@@ -56,6 +56,8 @@ if get_option('pylint') - endif - xmllint = find_program('xmllint', required: false) - -+argparse_manpage = find_program('argparse-manpage', required: get_option('manpage')) -+ - bus_names = { - 'org.freedesktop.UPower.PowerProfiles': '/org/freedesktop/UPower/PowerProfiles', - 'net.hadess.PowerProfiles': '/net/hadess/PowerProfiles', -diff --git a/meson_options.txt b/meson_options.txt -index 5e9a7ce..0de978c 100644 ---- a/meson_options.txt -+++ b/meson_options.txt -@@ -14,3 +14,7 @@ option('tests', - description: 'Whether to run tests', - type: 'boolean', - value: true) -+option('manpage', -+ description: 'gemerate powerprofilesctl man page', -+ type: 'feature', -+ value: 'auto') -diff --git a/src/meson.build b/src/meson.build -index 3f59dd3..49d62a9 100644 ---- a/src/meson.build -+++ b/src/meson.build -@@ -115,3 +115,34 @@ if get_option('pylint') - env: nomalloc, - ) - endif -+ -+if argparse_manpage.found() -+ argparse_features = run_command(argparse_manpage, '--help', -+ check: true).stdout().strip() -+ -+ install_man(configure_file( -+ command: [ -+ argparse_manpage, -+ '--pyfile', powerprofilesctl, -+ '--function', 'get_parser', -+ argparse_features.contains('--author') ? -+ ['--author', 'Bastien Nocera', '--author', 'Mario Limonciello'] : [], -+ argparse_features.contains('--author-email') ? -+ ['--author-email', 'hadess@hadess.net', '--author-email', 'mario.limonciello@amd.com'] : [], -+ argparse_features.contains('--project-name') ? -+ ['--project-name', meson.project_name()] : [], -+ argparse_features.contains('--version') ? -+ ['--version', meson.project_version()] : [], -+ argparse_features.contains('--url') ? -+ ['--url', 'https://gitlab.freedesktop.org/upower/power-profiles-daemon'] : [], -+ argparse_features.contains('--description') ? -+ ['--manual-title', 'Power Profiles Daemon Control Program'] : [], -+ argparse_features.contains('--description') ? -+ ['--description', 'Command line utility to control Power Profiles Daemon'] : [], -+ argparse_features.contains('--format') ? -+ ['--format', 'single-commands-section'] : [], -+ ], -+ capture: true, -+ output: 'powerprofilesctl.1', -+ )) -+endif -diff --git a/src/powerprofilesctl b/src/powerprofilesctl -index acc0509..a363bbc 100755 ---- a/src/powerprofilesctl -+++ b/src/powerprofilesctl -@@ -138,7 +138,7 @@ def _launch(args): - return ret - - --def main(): -+def get_parser(): - parser = argparse.ArgumentParser( - epilog="Use “powerprofilesctl COMMAND --help” to get detailed help for individual commands", - ) -@@ -190,7 +190,11 @@ def main(): - ) - parser_version.set_defaults(func=_version) - -- args = parser.parse_args() -+ return parser -+ -+ -+def main(): -+ args = get_parser().parse_args() - # default behavior is to run list if no command is given - if not args.command: - args.func = _list diff -Nru power-profiles-daemon-0.20/debian/patches/powerprofilesctl-Avoid-uneeded-raise-this-will-happen-any.patch power-profiles-daemon-0.21/debian/patches/powerprofilesctl-Avoid-uneeded-raise-this-will-happen-any.patch --- power-profiles-daemon-0.20/debian/patches/powerprofilesctl-Avoid-uneeded-raise-this-will-happen-any.patch 2024-03-09 18:25:14.000000000 +0000 +++ power-profiles-daemon-0.21/debian/patches/powerprofilesctl-Avoid-uneeded-raise-this-will-happen-any.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,112 +0,0 @@ -From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= -Date: Thu, 15 Feb 2024 01:22:17 +0100 -Subject: powerprofilesctl: Avoid uneeded raise, this will happen anyways - -Origin: https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/commit/fce98c6 ---- - src/powerprofilesctl | 64 ++++++++++++++++------------------------------------ - 1 file changed, 19 insertions(+), 45 deletions(-) - -diff --git a/src/powerprofilesctl b/src/powerprofilesctl -index af4dfe1..acc0509 100755 ---- a/src/powerprofilesctl -+++ b/src/powerprofilesctl -@@ -13,14 +13,10 @@ PROPERTIES_IFACE = "org.freedesktop.DBus.Properties" - - - def get_proxy(): -- try: -- bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) -- proxy = Gio.DBusProxy.new_sync( -- bus, Gio.DBusProxyFlags.NONE, None, PP_NAME, PP_PATH, PROPERTIES_IFACE, None -- ) -- except: -- raise -- return proxy -+ bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) -+ return Gio.DBusProxy.new_sync( -+ bus, Gio.DBusProxyFlags.NONE, None, PP_NAME, PP_PATH, PROPERTIES_IFACE, None -+ ) - - - def command(func): -@@ -55,38 +51,23 @@ def _get(args): # pylint: disable=unused-argument - - @command - def _set(args): -- try: -- proxy = get_proxy() -- proxy.Set( -- "(ssv)", PP_IFACE, "ActiveProfile", GLib.Variant.new_string(args.profile[0]) -- ) -- except: -- raise -+ proxy = get_proxy() -+ proxy.Set( -+ "(ssv)", PP_IFACE, "ActiveProfile", GLib.Variant.new_string(args.profile[0]) -+ ) - - - def get_profiles_property(prop): -- try: -- proxy = get_proxy() -- except: -- raise -- -- profiles = None -- try: -- profiles = proxy.Get("(ss)", PP_IFACE, prop) -- except: -- raise -- return profiles -+ proxy = get_proxy() -+ return proxy.Get("(ss)", PP_IFACE, prop) - - - @command - def _list(args): # pylint: disable=unused-argument -- try: -- profiles = get_profiles_property("Profiles") -- reason = get_proxy().Get("(ss)", PP_IFACE, "PerformanceDegraded") -- degraded = reason != "" -- active = get_proxy().Get("(ss)", PP_IFACE, "ActiveProfile") -- except: -- raise -+ profiles = get_profiles_property("Profiles") -+ reason = get_proxy().Get("(ss)", PP_IFACE, "PerformanceDegraded") -+ degraded = reason != "" -+ active = get_proxy().Get("(ss)", PP_IFACE, "ActiveProfile") - - index = 0 - for profile in reversed(profiles): -@@ -106,10 +87,7 @@ def _list(args): # pylint: disable=unused-argument - - @command - def _list_holds(args): # pylint: disable=unused-argument -- try: -- holds = get_profiles_property("ActiveProfileHolds") -- except: -- raise -+ holds = get_profiles_property("ActiveProfileHolds") - - index = 0 - for hold in holds: -@@ -136,14 +114,10 @@ def _launch(args): - if not reason: - reason = f"Running {args.appid}" - ret = 0 -- try: -- bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) -- proxy = Gio.DBusProxy.new_sync( -- bus, Gio.DBusProxyFlags.NONE, None, PP_NAME, PP_PATH, PP_IFACE, None -- ) -- except: -- raise -- -+ bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) -+ proxy = Gio.DBusProxy.new_sync( -+ bus, Gio.DBusProxyFlags.NONE, None, PP_NAME, PP_PATH, PP_IFACE, None -+ ) - cookie = proxy.HoldProfile("(sss)", profile, reason, appid) - - # Kill child when we go away diff -Nru power-profiles-daemon-0.20/debian/patches/series power-profiles-daemon-0.21/debian/patches/series --- power-profiles-daemon-0.20/debian/patches/series 2024-03-09 18:25:14.000000000 +0000 +++ power-profiles-daemon-0.21/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -powerprofilesctl-Avoid-uneeded-raise-this-will-happen-any.patch -tests-Ensure-daemon-is-ready-before-launching-the-test-cl.patch -build-Expose-powerprofilesctl-script-and-load-it-using-fi.patch -powerprofilectl-Generate-manpage-using-argparse-manpage.patch -build-Bump-dependency-on-polkit-gobject-1-0.99.patch -fix-typo-in-meson_options.txt.patch -Generate-bash-completion-and-zsh-completion-using-shtab.patch -tests-Fix-failing-messages-to-use-f-strings-as-was-intend.patch -Add-support-for-upower-into-the-daemon.patch -amdgpu-Use-the-upower-core-support-from-the-daemon.patch -amd-pstate-Transition-between-balanced_power-and-bal.patch diff -Nru power-profiles-daemon-0.20/debian/patches/tests-Ensure-daemon-is-ready-before-launching-the-test-cl.patch power-profiles-daemon-0.21/debian/patches/tests-Ensure-daemon-is-ready-before-launching-the-test-cl.patch --- power-profiles-daemon-0.20/debian/patches/tests-Ensure-daemon-is-ready-before-launching-the-test-cl.patch 2024-03-09 18:25:14.000000000 +0000 +++ power-profiles-daemon-0.21/debian/patches/tests-Ensure-daemon-is-ready-before-launching-the-test-cl.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= -Date: Thu, 15 Feb 2024 01:30:26 +0100 -Subject: tests: Ensure daemon is ready before launching the test client - -Origin: https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/commit/1ca4696 ---- - tests/integration_test.py | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/tests/integration_test.py b/tests/integration_test.py -index f31d6c5..6ffe547 100644 ---- a/tests/integration_test.py -+++ b/tests/integration_test.py -@@ -1626,6 +1626,7 @@ class Tests(dbusmock.DBusTestCase): - def test_vanishing_hold(self): - self.create_platform_profile() - self.start_daemon() -+ self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile")) - - sourcedir = os.getenv("top_srcdir", ".") - tool_path = os.path.join(sourcedir, "src", "powerprofilesctl") diff -Nru power-profiles-daemon-0.20/debian/patches/tests-Fix-failing-messages-to-use-f-strings-as-was-intend.patch power-profiles-daemon-0.21/debian/patches/tests-Fix-failing-messages-to-use-f-strings-as-was-intend.patch --- power-profiles-daemon-0.20/debian/patches/tests-Fix-failing-messages-to-use-f-strings-as-was-intend.patch 2024-03-09 18:25:14.000000000 +0000 +++ power-profiles-daemon-0.21/debian/patches/tests-Fix-failing-messages-to-use-f-strings-as-was-intend.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= -Date: Thu, 15 Feb 2024 18:04:42 +0100 -Subject: tests: Fix failing messages to use f'strings as was intended to - -But then as per reformatting, they got lost... ---- - tests/integration_test.py | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/tests/integration_test.py b/tests/integration_test.py -index 6ffe547..02528e1 100644 ---- a/tests/integration_test.py -+++ b/tests/integration_test.py -@@ -396,7 +396,7 @@ class Tests(dbusmock.DBusTestCase): - lambda: self.read_file_contents(path) == encoded, - timeout=timeout, - message=f"file '{path}' does not contain '{contents}', " -- + "but '{self.read_file_contents(path)}'", -+ + f"but '{self.read_file_contents(path)}'", - ) - - def assert_sysfs_attr_eventually_is(self, device, attribute, contents, timeout=800): -@@ -406,7 +406,7 @@ class Tests(dbusmock.DBusTestCase): - lambda: self.read_sysfs_attr(device, attribute) == encoded, - timeout=timeout, - message=f"file {device} '{attribute}' does not contain '{contents}', " -- + "but '{self.read_sysfs_attr(device, attribute)}'", -+ + f"but '{self.read_sysfs_attr(device, attribute)}'", - ) - - def assert_dbus_property_eventually_is(self, prop, value, timeout=1200): -@@ -414,7 +414,8 @@ class Tests(dbusmock.DBusTestCase): - return self.assert_eventually( - lambda: self.get_dbus_property(prop) == value, - timeout=timeout, -- message=f"property '{prop}' is not '{value}', but '{self.get_dbus_property(prop)}'", -+ message=f"property '{prop}' is not '{value}', but " -+ + f"'{self.get_dbus_property(prop)}'", - ) - - # diff -Nru power-profiles-daemon-0.20/docs/power-profiles-daemon-docs.xml power-profiles-daemon-0.21/docs/power-profiles-daemon-docs.xml --- power-profiles-daemon-0.20/docs/power-profiles-daemon-docs.xml 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/docs/power-profiles-daemon-docs.xml 2024-04-03 23:55:02.000000000 +0000 @@ -9,20 +9,46 @@ Version &version; - Bastien - Nocera - -
- hadess@hadess.net -
-
+ Bastien + Nocera + +
+ hadess@hadess.net +
+
+
+ + Mario + Limonciello + +
+ superm1@gmail.com +
+
+ + Marco + Trevisan + +
+ mail@3v1n0.net +
+
+
2020 Red Hat, Inc. + + 2024 + Advanced Micro Devices, Inc + + + 2024 + Canonical Ltd + diff -Nru power-profiles-daemon-0.20/meson.build power-profiles-daemon-0.21/meson.build --- power-profiles-daemon-0.20/meson.build 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/meson.build 2024-04-03 23:55:02.000000000 +0000 @@ -1,5 +1,5 @@ project('power-profiles-daemon', [ 'c' ], - version: '0.20', + version: '0.21', license: 'GPLv3+', default_options: [ 'buildtype=debugoptimized', @@ -41,21 +41,34 @@ gio_dep = dependency('gio-2.0') gudev_dep = dependency('gudev-1.0', version: '>= 234') upower_dep = dependency('upower-glib') -polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.91') +polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.99') polkit_policy_directory = polkit_gobject_dep.get_variable('policydir') +python3_required_modules = [] +gi_required_modules = {} + +powerprofilesctl_required_gi_modules = { + 'GLib': '2.0', + 'Gio': '2.0', +} + gnome = import('gnome') add_global_arguments('-D_GNU_SOURCE=1', language: 'c') add_global_arguments(common_cflags, language: 'c') -if get_option('pylint') - pylint = find_program('pylint-3', 'pylint3', 'pylint', required: true) +pylint = find_program('pylint-3', 'pylint3', 'pylint', required: get_option('pylint')) +if pylint.found() nomalloc = environment({'MALLOC_PERTURB_': '0'}) pylint_flags = ['-d', 'C0116', '-d', 'C0114', '-d', 'W0707', '-d', 'W0706' ] endif xmllint = find_program('xmllint', required: false) +argparse_manpage = find_program('argparse-manpage', required: get_option('manpage')) +if argparse_manpage.found() + gi_required_modules += powerprofilesctl_required_gi_modules +endif + bus_names = { 'org.freedesktop.UPower.PowerProfiles': '/org/freedesktop/UPower/PowerProfiles', 'net.hadess.PowerProfiles': '/net/hadess/PowerProfiles', @@ -65,6 +78,52 @@ get_option('b_sanitize') == 'address,undefined' or \ get_option('b_sanitize') == 'leak' +bashcomp = dependency('bash-completion', required: get_option('bashcomp')) +zshcomp = get_option('zshcomp') != '' + +python = import('python') +python3 = python.find_installation('python3') + +if bashcomp.found() or zshcomp + python3_required_modules += 'shtab' + gi_required_modules += powerprofilesctl_required_gi_modules +endif + +if get_option('tests') + python3_required_modules += [ + 'dbusmock', + ] + gi_required_modules += powerprofilesctl_required_gi_modules + gi_required_modules += { + 'UMockdev': '1.0', + } +endif + +# Python 3 required modules +if gi_required_modules.keys().length() > 0 + python3_required_modules += 'gi' +endif + +foreach p : python3_required_modules + # Source: https://docs.python.org/3/library/importlib.html#checking-if-a-module-can-be-imported + script = 'import importlib.util; import sys; exit(1) if importlib.util.find_spec(\''+ p +'\') is None else exit(0)' + if run_command(python3, '-c', script, check: false).returncode() == 0 + message('Python3 module \'@0@\' found: YES'.format(p)) + else + error('Python3 module \'@0@\' required but not found'.format(p)) + endif +endforeach + +foreach module, version : gi_required_modules + script = 'import gi; gi.require_version("@0@", "@1@")'.format(module, version) + if run_command(python3, '-c', script, check: false).returncode() == 0 + message('Python3 module \'@0@\' found: YES @1@'.format(module, version)) + else + error('''GObject Introspection module '@0@' version @1@ required but not found'''.format( + module, version)) + endif +endforeach + subdir('src') subdir('data') @@ -79,32 +138,6 @@ endif if get_option('tests') - # Python 3 required modules - python3_required_modules = ['dbusmock', 'gi'] - gi_required_modules = { - 'GLib': '2.0', - 'Gio': '2.0', - 'UMockdev': '1.0', - } - - python = import('python') - python3 = python.find_installation('python3') - foreach p : python3_required_modules - # Source: https://docs.python.org/3/library/importlib.html#checking-if-a-module-can-be-imported - script = 'import importlib.util; import sys; exit(1) if importlib.util.find_spec(\''+ p +'\') is None else exit(0)' - if run_command(python3, '-c', script, check: false).returncode() != 0 - error('Python3 module \'' + p + '\' required for running tests but not found') - endif - endforeach - - foreach module, version : gi_required_modules - script = 'import gi; gi.require_version("@0@", "@1@")'.format(module, version) - if run_command(python3, '-c', script, check: false).returncode() != 0 - error('''GObject Introspection module '@0@' version @1@ required for running tests but not found'''.format( - module, version)) - endif - endforeach - subdir('tests') endif @@ -112,3 +145,12 @@ find_program('check-news.sh').full_path(), '@0@'.format(meson.project_version()) ) + +summary({ + 'tests': get_option('tests'), + 'bash-completion': bashcomp, + 'zsh-completion': zshcomp, + 'manpages': argparse_manpage.found(), + 'python linting': pylint.found(), + 'gtk_doc': get_option('gtk_doc'), +}) diff -Nru power-profiles-daemon-0.20/meson_options.txt power-profiles-daemon-0.21/meson_options.txt --- power-profiles-daemon-0.20/meson_options.txt 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/meson_options.txt 2024-04-03 23:55:02.000000000 +0000 @@ -7,10 +7,22 @@ value: false, description: 'Build docs') option('pylint', - type: 'boolean', - value: false, + type: 'feature', + value: 'auto', description: 'Run pylint checks, for developers only') option('tests', description: 'Whether to run tests', type: 'boolean', value: true) +option('manpage', + description: 'generate powerprofilesctl man page', + type: 'feature', + value: 'auto') +option('bashcomp', + description: 'generate bash completion', + type: 'feature', + value: 'auto') +option('zshcomp', + description: 'path for zsh completion file', + type: 'string', + value: '') diff -Nru power-profiles-daemon-0.20/src/completions/meson.build power-profiles-daemon-0.21/src/completions/meson.build --- power-profiles-daemon-0.20/src/completions/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ power-profiles-daemon-0.21/src/completions/meson.build 2024-04-03 23:55:02.000000000 +0000 @@ -0,0 +1,36 @@ +generate_completion = [python3, powerprofilesctl, '--print-completion'] + +completions_common = { + 'env': {'PPD_COMPLETIONS_GENERATION': '1'}, + 'capture': true, + 'install': true, +} + +if bashcomp.found() + completions_dir = bashcomp.get_variable(pkgconfig: 'completionsdir', + pkgconfig_define: bashcomp.version().version_compare('>= 2.10') ? + ['datadir', get_option('datadir')] : ['prefix', prefix], + ) + + custom_target('bash-completion', + output: 'powerprofilesctl', + command: [ + generate_completion, + 'bash', + ], + install_dir: completions_dir, + kwargs: completions_common, + ) +endif + +if zshcomp + custom_target('zsh-completion', + output: '_powerprofilesctl', + command: [ + generate_completion, + 'zsh', + ], + install_dir: get_option('zshcomp'), + kwargs: completions_common, + ) +endif diff -Nru power-profiles-daemon-0.20/src/meson.build power-profiles-daemon-0.21/src/meson.build --- power-profiles-daemon-0.20/src/meson.build 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/meson.build 2024-04-03 23:55:02.000000000 +0000 @@ -104,15 +104,52 @@ install_dir: libexecdir ) -script = 'powerprofilesctl' -install_data(script, - install_dir: get_option('bindir') +powerprofilesctl = configure_file( + input: files('powerprofilesctl'), + output: 'powerprofilesctl', + configuration: { + 'VERSION': meson.project_version(), + }, + install_dir: get_option('bindir') ) -script = join_paths(meson.current_source_dir(), script) -if get_option('pylint') + +if pylint.found() test('pylint-powerprofilesctl', pylint, - args: pylint_flags + [ script ], + args: pylint_flags + [ powerprofilesctl ], env: nomalloc, ) endif + +if argparse_manpage.found() + argparse_features = run_command(argparse_manpage, '--help', + check: true).stdout().strip() + + install_man(configure_file( + command: [ + argparse_manpage, + '--pyfile', powerprofilesctl, + '--function', 'get_parser', + argparse_features.contains('--author') ? + ['--author', 'Bastien Nocera', '--author', 'Mario Limonciello'] : [], + argparse_features.contains('--author-email') ? + ['--author-email', 'hadess@hadess.net', '--author-email', 'mario.limonciello@amd.com'] : [], + argparse_features.contains('--project-name') ? + ['--project-name', meson.project_name()] : [], + argparse_features.contains('--version') ? + ['--version', meson.project_version()] : [], + argparse_features.contains('--url') ? + ['--url', 'https://gitlab.freedesktop.org/upower/power-profiles-daemon'] : [], + argparse_features.contains('--description') ? + ['--manual-title', 'Power Profiles Daemon Control Program'] : [], + argparse_features.contains('--description') ? + ['--description', 'Command line utility to control Power Profiles Daemon'] : [], + argparse_features.contains('--format') ? + ['--format', 'single-commands-section'] : [], + ], + capture: true, + output: 'powerprofilesctl.1', + )) +endif + +subdir('completions') \ No newline at end of file diff -Nru power-profiles-daemon-0.20/src/power-profiles-daemon.c power-profiles-daemon-0.21/src/power-profiles-daemon.c --- power-profiles-daemon-0.20/src/power-profiles-daemon.c 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/power-profiles-daemon.c 2024-04-03 23:55:02.000000000 +0000 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "power-profiles-daemon-resources.h" @@ -36,6 +37,14 @@ #define POWER_PROFILES_RESOURCES_PATH "/org/freedesktop/UPower/PowerProfiles" +#define UPOWER_DBUS_NAME "org.freedesktop.UPower" +#define UPOWER_DBUS_PATH "/org/freedesktop/UPower" +#define UPOWER_DBUS_INTERFACE "org.freedesktop.UPower" + +#define LOGIND_DBUS_NAME "org.freedesktop.login1" +#define LOGIND_DBUS_PATH "/org/freedesktop/login1" +#define LOGIND_DBUS_INTERFACE "org.freedesktop.login1.Manager" + #ifndef POLKIT_HAS_AUTOPOINTERS /* FIXME: Remove this once we're fine to depend on polkit 0.114 */ G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthorizationResult, g_object_unref) @@ -43,8 +52,17 @@ #endif typedef struct { + GOptionGroup *group; + GLogLevelFlags log_level; + gboolean replace; + GStrv blocked_drivers; + GStrv blocked_actions; +} DebugOptions; + +typedef struct { GMainLoop *main_loop; GDBusConnection *connection; + GCancellable *cancellable; guint name_id; guint legacy_name_id; gboolean was_started; @@ -62,7 +80,16 @@ PpdDriverPlatform *platform_driver; GPtrArray *actions; GHashTable *profile_holds; - GLogLevelFlags log_level; + + GDBusProxy *upower_proxy; + gulong upower_watch_id; + gulong upower_properties_id; + PpdPowerChangedReason power_changed_reason; + + guint logind_sleep_signal_id; + + GStrv blocked_drivers; + GStrv blocked_actions; } PpdApp; typedef struct { @@ -74,6 +101,15 @@ } ProfileHold; static void +debug_options_free(DebugOptions *options) +{ + g_strfreev (options->blocked_drivers); + g_strfreev (options->blocked_actions); + g_free (options); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC (DebugOptions, debug_options_free) + +static void profile_hold_free (ProfileHold *hold) { if (hold == NULL) @@ -89,6 +125,7 @@ static void stop_profile_drivers (PpdApp *data); static void start_profile_drivers (PpdApp *data); +static void upower_battery_set_power_changed_reason (PpdApp *, PpdPowerChangedReason); /* profile drivers and actions */ #include "ppd-action-trickle-charge.h" @@ -284,12 +321,12 @@ GVariantBuilder props_builder; GVariant *props_changed = NULL; - g_assert (data->connection); + g_return_if_fail (data->connection); if (mask == 0) return; - g_assert ((mask & PROP_ALL) != 0); + g_return_if_fail ((mask & PROP_ALL) != 0); g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}")); @@ -429,12 +466,10 @@ for (i = 0; i < actions->len; i++) { g_autoptr(GError) error = NULL; PpdAction *action; - gboolean ret; action = g_ptr_array_index (actions, i); - ret = ppd_action_activate_profile (action, profile, &error); - if (!ret) + if (!ppd_action_activate_profile (action, profile, &error)) g_warning ("Failed to activate action '%s' to profile %s: %s", ppd_profile_to_str (profile), ppd_action_get_action_name (action), @@ -449,52 +484,43 @@ GError **error) { PpdProfile current_profile = data->active_profile; - gboolean cpu_set = TRUE; - gboolean platform_set = TRUE; - g_debug ("Setting active profile '%s' for reason '%s' (current: '%s')", + g_info ("Setting active profile '%s' for reason '%s' (current: '%s')", ppd_profile_to_str (target_profile), ppd_profile_activation_reason_to_str (reason), ppd_profile_to_str (current_profile)); /* Try CPU first */ - if (driver_profile_support (PPD_DRIVER (data->cpu_driver), target_profile)) - cpu_set = ppd_driver_activate_profile (PPD_DRIVER (data->cpu_driver), - target_profile, reason, error); - - if (!cpu_set) { + if (driver_profile_support (PPD_DRIVER (data->cpu_driver), target_profile) && + !ppd_driver_activate_profile (PPD_DRIVER (data->cpu_driver), + target_profile, reason, error)) { g_prefix_error (error, "Failed to activate CPU driver '%s': ", ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver))); return FALSE; } /* Then try platform */ - if (driver_profile_support (PPD_DRIVER (data->platform_driver), target_profile)) { - platform_set = ppd_driver_activate_profile (PPD_DRIVER (data->platform_driver), - target_profile, reason, error); - } + if (driver_profile_support (PPD_DRIVER (data->platform_driver), target_profile) && + !ppd_driver_activate_profile (PPD_DRIVER (data->platform_driver), + target_profile, reason, error)) { + g_autoptr(GError) recovery_error = NULL; - if (!platform_set) { g_prefix_error (error, "Failed to activate platform driver '%s': ", ppd_driver_get_driver_name (PPD_DRIVER (data->platform_driver))); - /* Try to recover */ - if (cpu_set) { - g_autoptr(GError) recovery_error = NULL; - - g_debug ("Reverting CPU driver '%s' to profile '%s'", - ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)), - ppd_profile_to_str (current_profile)); - - if (!ppd_driver_activate_profile (PPD_DRIVER (data->cpu_driver), - current_profile, PPD_PROFILE_ACTIVATION_REASON_INTERNAL, - &recovery_error)) { - g_prefix_error (error, "Failed to revert CPU driver '%s': ", - ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver))); - g_warning ("Failed to revert CPU driver '%s': %s", - ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)), - recovery_error->message); - } + if (!PPD_IS_DRIVER (data->cpu_driver)) + return FALSE; + + g_debug ("Reverting CPU driver '%s' to profile '%s'", + ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)), + ppd_profile_to_str (current_profile)); + + if (!ppd_driver_activate_profile (PPD_DRIVER (data->cpu_driver), + current_profile, PPD_PROFILE_ACTIVATION_REASON_INTERNAL, + &recovery_error)) { + g_warning ("Failed to revert CPU driver '%s': %s", + ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)), + recovery_error->message); } return FALSE; @@ -827,7 +853,7 @@ { PpdApp *data = user_data; - g_assert (data->connection); + g_return_val_if_fail (data->connection, NULL); if (g_strcmp0 (property_name, "ActiveProfile") == 0) return g_variant_new_string (get_active_profile (data)); @@ -861,7 +887,7 @@ PpdApp *data = user_data; const char *profile; - g_assert (data->connection); + g_return_val_if_fail (data->connection, FALSE); if (g_strcmp0 (property_name, "ActiveProfile") != 0) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, @@ -888,7 +914,7 @@ gpointer user_data) { PpdApp *data = user_data; - g_assert (data->connection); + g_return_if_fail (data->connection); if (!g_str_equal (interface_name, POWER_PROFILES_IFACE_NAME) && !g_str_equal (interface_name, POWER_PROFILES_LEGACY_IFACE_NAME)) { @@ -947,9 +973,9 @@ PpdBusOwnData *data = user_data; PpdApp *app = data->app; - g_debug ("power-profiles-daemon is already running, or it cannot own its D-Bus name. Verify installation."); + g_warning ("power-profiles-daemon is already running, or it cannot own its D-Bus name. Verify installation."); if (!app->was_started) - app->ret = 1; + app->ret = EXIT_FAILURE; g_main_loop_quit (app->main_loop); } @@ -996,6 +1022,190 @@ data->app->connection = g_object_ref (connection); } +static void +upower_battery_update_state_from_value (PpdApp *data, + GVariant *battery_val) +{ + PpdPowerChangedReason reason; + + if (!battery_val) + reason = PPD_POWER_CHANGED_REASON_UNKNOWN; + else if (g_variant_get_boolean (battery_val)) + reason = PPD_POWER_CHANGED_REASON_BATTERY; + else + reason = PPD_POWER_CHANGED_REASON_AC; + + upower_battery_set_power_changed_reason (data, reason); +} + +static void +upower_battery_update_state (PpdApp *data) +{ + g_autoptr(GVariant) battery_val = NULL; + + battery_val = g_dbus_proxy_get_cached_property (data->upower_proxy, "OnBattery"); + upower_battery_update_state_from_value (data, battery_val); +} + +static void +upower_properties_changed (GDBusProxy *proxy, + GVariant *changed_properties, + GStrv invalidated_properties, + PpdApp *data) +{ + g_auto(GVariantDict) props_dict = G_VARIANT_DICT_INIT (changed_properties); + g_autoptr(GVariant) battery_val = NULL; + + battery_val = g_variant_dict_lookup_value (&props_dict, "OnBattery", + G_VARIANT_TYPE_BOOLEAN); + + if (battery_val) + upower_battery_update_state_from_value (data, battery_val); +} + +static void +upower_battery_set_power_changed_reason (PpdApp *data, + PpdPowerChangedReason reason) +{ + if (data->power_changed_reason == reason) + return; + + data->power_changed_reason = reason; + g_info ("Power Changed because of reason %s", + ppd_power_changed_reason_to_str (reason)); + + for (guint i = 0; i < data->actions->len; i++) { + g_autoptr(GError) error = NULL; + PpdAction *action; + + action = g_ptr_array_index (data->actions, i); + + if (!ppd_action_power_changed (action, reason, &error)) { + g_warning ("failed to update action %s: %s", + ppd_action_get_action_name (action), + error->message); + g_clear_error (&error); + continue; + } + } + + if (PPD_IS_DRIVER_CPU (data->cpu_driver)) { + g_autoptr(GError) error = NULL; + + if (!ppd_driver_power_changed (PPD_DRIVER (data->cpu_driver), reason, &error)) { + g_warning ("failed to update driver %s: %s", + ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)), + error->message); + } + } + + if (PPD_IS_DRIVER_PLATFORM (data->platform_driver)) { + g_autoptr(GError) error = NULL; + + if (!ppd_driver_power_changed (PPD_DRIVER (data->platform_driver), reason, &error)) { + g_warning ("failed to update driver %s: %s", + ppd_driver_get_driver_name (PPD_DRIVER (data->platform_driver)), + error->message); + } + } +} + +static void +upower_name_owner_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + PpdApp *data = user_data; + GDBusProxy *upower_proxy = G_DBUS_PROXY (object); + g_autofree char *name_owner = NULL; + + name_owner = g_dbus_proxy_get_name_owner (upower_proxy); + + if (name_owner != NULL) { + g_debug ("%s appeared", UPOWER_DBUS_NAME); + upower_battery_update_state (data); + return; + } + + g_debug ("%s vanished", UPOWER_DBUS_NAME); + upower_battery_set_power_changed_reason (data, PPD_POWER_CHANGED_REASON_UNKNOWN); +} + +static void +on_upower_proxy_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PpdApp *data = user_data; + g_autoptr(GDBusProxy) upower_proxy = NULL; + g_autoptr(GError) error = NULL; + + upower_proxy = g_dbus_proxy_new_finish (res, &error); + + if (upower_proxy == NULL) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + g_warning ("failed to connect to upower: %s", error->message); + return; + } + + g_return_if_fail (data->upower_proxy == NULL); + data->upower_proxy = g_steal_pointer (&upower_proxy); + + data->upower_properties_id = g_signal_connect (data->upower_proxy, + "g-properties-changed", + G_CALLBACK (upower_properties_changed), + data); + + data->upower_watch_id = g_signal_connect (data->upower_proxy, + "notify::g-name-owner", + G_CALLBACK (upower_name_owner_changed), + data); + + upower_battery_update_state (data); +} + +static void +on_logind_prepare_for_sleep_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + PpdApp *data = user_data; + gboolean start; + + g_variant_get (parameters, "(b)", &start); + + if (start) + g_debug ("System preparing for suspend"); + else + g_debug ("System woke up from suspend"); + + if (PPD_IS_DRIVER_CPU (data->cpu_driver)) { + g_autoptr(GError) error = NULL; + + if (!ppd_driver_prepare_to_sleep (PPD_DRIVER (data->cpu_driver), start, &error)) { + g_warning ("failed to notify driver %s: %s", + ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)), + error->message); + } + } + + if (PPD_IS_DRIVER_PLATFORM (data->platform_driver)) { + g_autoptr(GError) error = NULL; + + if (!ppd_driver_prepare_to_sleep (PPD_DRIVER (data->platform_driver), start, &error)) { + g_warning ("failed to notify driver %s: %s", + ppd_driver_get_driver_name (PPD_DRIVER (data->platform_driver)), + error->message); + } + } +} + static gboolean has_required_drivers (PpdApp *data) { @@ -1010,52 +1220,97 @@ } static void +restart_profile_drivers (PpdApp *data) +{ + stop_profile_drivers (data); + start_profile_drivers (data); +} + +static void driver_probe_request_cb (PpdDriver *driver, gpointer user_data) { PpdApp *data = user_data; - stop_profile_drivers (data); - start_profile_drivers (data); + restart_profile_drivers (data); +} + +static void +disconnect_array_objects_signals_by_data (GPtrArray *array, + void *data) +{ + for (guint i = 0; i < array->len; ++i) { + GObject *object = g_ptr_array_index (array, i); + + g_signal_handlers_disconnect_by_data (object, data); + } +} + +static void +maybe_disconnect_object_by_data (void *object, + void *data) +{ + if (!G_IS_OBJECT (object)) + return; + + g_signal_handlers_disconnect_by_data (object, data); } static void stop_profile_drivers (PpdApp *data) { + if (data->logind_sleep_signal_id) { + g_dbus_connection_signal_unsubscribe (data->connection, data->logind_sleep_signal_id); + data->logind_sleep_signal_id = 0; + } + + upower_battery_set_power_changed_reason (data, PPD_POWER_CHANGED_REASON_UNKNOWN); release_all_profile_holds (data); + g_cancellable_cancel (data->cancellable); + disconnect_array_objects_signals_by_data (data->probed_drivers, data); g_ptr_array_set_size (data->probed_drivers, 0); + disconnect_array_objects_signals_by_data (data->actions, data); g_ptr_array_set_size (data->actions, 0); + g_clear_signal_handler (&data->upower_watch_id, data->upower_proxy); + g_clear_signal_handler (&data->upower_properties_id, data->upower_proxy); + g_clear_object (&data->cancellable); + maybe_disconnect_object_by_data (data->upower_proxy, data); + g_clear_object (&data->upower_proxy); + maybe_disconnect_object_by_data (data->cpu_driver, data); g_clear_object (&data->cpu_driver); + maybe_disconnect_object_by_data (data->platform_driver, data); g_clear_object (&data->platform_driver); } static gboolean -action_blocked (PpdAction *action) +action_blocked (PpdApp *app, PpdAction *action) { const gchar *action_name = ppd_action_get_action_name (action); - const gchar *env = g_getenv ("POWER_PROFILE_DAEMON_ACTION_BLOCK"); + gboolean blocked; - if (env == NULL) + if (app->blocked_actions == NULL || g_strv_length (app->blocked_actions) == 0) return FALSE; - g_auto(GStrv) actions = NULL; + blocked = g_strv_contains ((const gchar *const *) app->blocked_actions, action_name); - actions = g_strsplit (env, ",", -1); - return g_strv_contains ((const gchar *const *)actions, action_name); + if (blocked) + g_debug ("Action '%s' is blocked", action_name); + return blocked; } static gboolean -driver_blocked (PpdDriver *driver) +driver_blocked (PpdApp *app, PpdDriver *driver) { const gchar *driver_name = ppd_driver_get_driver_name (driver); - const gchar *env = g_getenv ("POWER_PROFILE_DAEMON_DRIVER_BLOCK"); + gboolean blocked; - if (env == NULL) + if (app->blocked_drivers == NULL || g_strv_length (app->blocked_drivers) == 0) return FALSE; - g_auto(GStrv) drivers = NULL; - drivers = g_strsplit (env, ",", -1); - return g_strv_contains ((const char **) drivers, driver_name); + blocked = g_strv_contains ((const gchar *const *) app->blocked_drivers, driver_name); + if (blocked) + g_debug ("Driver '%s' is blocked", driver_name); + return blocked; } static void @@ -1063,6 +1318,10 @@ { guint i; g_autoptr(GError) initial_error = NULL; + gboolean needs_battery_monitor = FALSE; + gboolean needs_suspend_monitor = FALSE; + + data->cancellable = g_cancellable_new (); for (i = 0; i < G_N_ELEMENTS (objects); i++) { g_autoptr(GObject) object = NULL; @@ -1075,7 +1334,7 @@ PpdProbeResult result; g_debug ("Handling driver '%s'", ppd_driver_get_driver_name (driver)); - if (driver_blocked (driver)) { + if (driver_blocked (data, driver)) { g_debug ("Driver '%s' is blocked, skipping", ppd_driver_get_driver_name (driver)); continue; } @@ -1121,7 +1380,14 @@ else if (PPD_IS_DRIVER_PLATFORM (driver)) g_set_object (&data->platform_driver, PPD_DRIVER_PLATFORM (driver)); else - g_assert_not_reached (); + g_return_if_reached (); + + if (PPD_DRIVER_GET_CLASS (driver)->power_changed != NULL) + needs_battery_monitor = TRUE; + + if (PPD_DRIVER_GET_CLASS (driver)->prepare_to_sleep != NULL) + needs_suspend_monitor = TRUE; + g_info ("Driver '%s' loaded", ppd_driver_get_driver_name (driver)); g_signal_connect (G_OBJECT (driver), "notify::performance-degraded", G_CALLBACK (driver_performance_degraded_changed_cb), data); @@ -1135,7 +1401,7 @@ g_debug ("Handling action '%s'", ppd_action_get_action_name (action)); - if (action_blocked (action)) { + if (action_blocked (data, action)) { g_debug ("Action '%s' is blocked, skipping", ppd_action_get_action_name (action)); continue; } @@ -1146,16 +1412,21 @@ continue; } + if (PPD_ACTION_GET_CLASS (action)->power_changed != NULL) + needs_battery_monitor = TRUE; + + g_info ("Action '%s' loaded", ppd_action_get_action_name (action)); g_ptr_array_add (data->actions, g_steal_pointer (&action)); continue; } - g_assert_not_reached (); + g_return_if_reached (); } if (!has_required_drivers (data)) { + data->ret = EXIT_FAILURE; g_warning ("Some non-optional profile drivers are missing, programmer error"); - goto bail; + g_main_loop_quit (data->main_loop); } /* Set initial state either from configuration, or using the currently selected profile */ @@ -1164,24 +1435,50 @@ g_warning ("Failed to activate initial profile: %s", initial_error->message); send_dbus_event (data, PROP_ALL); - data->was_started = TRUE; - return; + if (needs_battery_monitor) { + /* start watching for power changes */ + g_debug ("Battery state monitor required, connecting to upower..."); + g_dbus_proxy_new (data->connection, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + UPOWER_DBUS_NAME, + UPOWER_DBUS_PATH, + UPOWER_DBUS_INTERFACE, + data->cancellable, + on_upower_proxy_cb, + data); + } else { + g_debug ("No battery state monitor required by any driver, let's skip it"); + } -bail: - data->ret = 1; - g_debug ("Exiting because some non recoverable error occurred during startup"); - g_main_loop_quit (data->main_loop); + if (needs_suspend_monitor) { + g_debug ("Suspension state monitor required, monitoring logind..."); + data->logind_sleep_signal_id = + g_dbus_connection_signal_subscribe (data->connection, + LOGIND_DBUS_NAME, + LOGIND_DBUS_INTERFACE, + "PrepareForSleep", + LOGIND_DBUS_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_logind_prepare_for_sleep_cb, + data, + NULL); + } else { + g_debug ("No suspension monitor required by any driver, let's skip it"); + } } void -restart_profile_drivers (void) +restart_profile_drivers_for_default_app (void) { - stop_profile_drivers (ppd_app); - start_profile_drivers (ppd_app); + restart_profile_drivers (ppd_app); } + static void name_acquired_handler (GDBusConnection *connection, const gchar *name, @@ -1256,13 +1553,17 @@ if (data == NULL) return; + stop_profile_drivers (data); + g_clear_handle_id (&data->name_id, g_bus_unown_name); g_clear_handle_id (&data->legacy_name_id, g_bus_unown_name); + g_strfreev (data->blocked_drivers); + g_strfreev (data->blocked_actions); g_clear_pointer (&data->config_path, g_free); g_clear_pointer (&data->config, g_key_file_unref); - g_ptr_array_free (data->probed_drivers, TRUE); - g_ptr_array_free (data->actions, TRUE); + g_clear_pointer (&data->probed_drivers, g_ptr_array_unref); + g_clear_pointer (&data->actions, g_ptr_array_unref); g_clear_object (&data->cpu_driver); g_clear_object (&data->platform_driver); g_hash_table_destroy (data->profile_holds); @@ -1297,7 +1598,7 @@ const gchar *message, gpointer user_data) { - PpdApp *data = user_data; + DebugOptions *data = user_data; g_autoptr(GString) domain = NULL; gboolean use_color; gint color; @@ -1339,22 +1640,114 @@ return FALSE; } +static gboolean +verbose_arg_cb (const gchar *option_name, + const gchar *value, + gpointer user_data, + GError **error) +{ + DebugOptions *data = user_data; + + if (data->log_level == G_LOG_LEVEL_MESSAGE) { + data->log_level = G_LOG_LEVEL_INFO; + return TRUE; + } + if (data->log_level == G_LOG_LEVEL_INFO) { + data->log_level = G_LOG_LEVEL_DEBUG; + return TRUE; + } + g_set_error_literal (error, + G_OPTION_ERROR, + G_OPTION_ERROR_FAILED, + "No further debug level supported"); + return FALSE; +} + +static gboolean +debug_pre_parse_hook (GOptionContext *context, + GOptionGroup *group, + gpointer user_data, + GError **error) +{ + DebugOptions *data = user_data; + + const GOptionEntry options[] = { + { + "verbose", + 'v', + G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + (GOptionArgFunc)verbose_arg_cb, + "Show extra debugging information", + NULL, + }, + { + "replace", + 'r', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &data->replace, + "Replace the running instance of power-profiles-daemon", + NULL, + }, + { + "block-driver", + 0, + G_OPTION_FLAG_NONE, + G_OPTION_ARG_STRING_ARRAY, + &data->blocked_drivers, + "Block driver(s) from loading", + NULL, + }, + { + "block-action", + 0, + G_OPTION_FLAG_NONE, + G_OPTION_ARG_STRING_ARRAY, + &data->blocked_actions, + "Block action(s) from loading", + NULL, + }, + { NULL } + }; + g_option_group_add_entries (group, options); + + return TRUE; +} + +static gboolean +debug_post_parse_hook (GOptionContext *context, + GOptionGroup *group, + gpointer user_data, + GError **error) +{ + DebugOptions *debug = (DebugOptions *)user_data; + + g_log_set_default_handler (debug_handler_cb, debug); + + return TRUE; +} + int main (int argc, char **argv) { - g_autoptr (PpdApp) data = NULL; + g_autoptr(DebugOptions) debug_options = g_new0 (DebugOptions, 1); + g_autoptr(PpdApp) data = NULL; g_autoptr(GOptionContext) option_context = NULL; g_autoptr(GError) error = NULL; - gboolean verbose = FALSE; - gboolean replace = FALSE; - const GOptionEntry options[] = { - { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Show extra debugging information", NULL }, - { "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, "Replace the running instance of power-profiles-daemon", NULL }, - { NULL} - }; + + debug_options->log_level = G_LOG_LEVEL_MESSAGE; + debug_options->group = g_option_group_new ("debug", + "Debugging Options", + "Show debugging options", + debug_options, + NULL); + g_option_group_set_parse_hooks (debug_options->group, + debug_pre_parse_hook, + debug_post_parse_hook); setlocale (LC_ALL, ""); option_context = g_option_context_new (""); - g_option_context_add_main_entries (option_context, options, NULL); + g_option_context_add_group (option_context, debug_options->group); if (!g_option_context_parse (option_context, &argc, &argv, &error)) { g_print ("Failed to parse arguments: %s\n", error->message); @@ -1369,24 +1762,21 @@ data->profile_holds = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) profile_hold_free); data->active_profile = PPD_PROFILE_BALANCED; data->selected_profile = PPD_PROFILE_BALANCED; - data->log_level = verbose ? G_LOG_LEVEL_DEBUG : G_LOG_LEVEL_MESSAGE; + data->blocked_drivers = g_steal_pointer (&debug_options->blocked_drivers); + data->blocked_actions = g_steal_pointer (&debug_options->blocked_actions); g_unix_signal_add (SIGTERM, quit_signal_callback, data); g_unix_signal_add (SIGINT, quit_signal_callback, data); - /* redirect all domains */ - if (verbose && !g_log_writer_is_journald (fileno (stdout))) - g_log_set_default_handler (debug_handler_cb, data); - - g_debug ("Starting power-profiles-daemon version "VERSION); + g_info ("Starting power-profiles-daemon version "VERSION); load_configuration (data); ppd_app = data; /* Set up D-Bus */ - if (!setup_dbus (data, replace, &error)) { + if (!setup_dbus (data, debug_options->replace, &error)) { g_error ("Failed to start dbus: %s", error->message); - return 1; + return EXIT_FAILURE; } g_main_loop_run (data->main_loop); diff -Nru power-profiles-daemon-0.20/src/powerprofilesctl power-profiles-daemon-0.21/src/powerprofilesctl --- power-profiles-daemon-0.20/src/powerprofilesctl 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/powerprofilesctl 2024-04-03 23:55:02.000000000 +0000 @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import argparse +import os import signal import subprocess import sys @@ -13,14 +14,10 @@ def get_proxy(): - try: - bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) - proxy = Gio.DBusProxy.new_sync( - bus, Gio.DBusProxyFlags.NONE, None, PP_NAME, PP_PATH, PROPERTIES_IFACE, None - ) - except: - raise - return proxy + bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) + return Gio.DBusProxy.new_sync( + bus, Gio.DBusProxyFlags.NONE, None, PP_NAME, PP_PATH, PROPERTIES_IFACE, None + ) def command(func): @@ -40,14 +37,18 @@ @command -def _version(args): # pylint: disable=unused-argument - proxy = get_proxy() - ver = proxy.Get("(ss)", PP_IFACE, "Version") - print(ver) +def _version(_args): + client_version = "@VERSION@" + try: + proxy = get_proxy() + daemon_ver = proxy.Get("(ss)", PP_IFACE, "Version") + except GLib.Error: + daemon_ver = "unknown" + print(f"client: {client_version}\ndaemon: {daemon_ver}") @command -def _get(args): # pylint: disable=unused-argument +def _get(_args): proxy = get_proxy() profile = proxy.Get("(ss)", PP_IFACE, "ActiveProfile") print(profile) @@ -55,38 +56,23 @@ @command def _set(args): - try: - proxy = get_proxy() - proxy.Set( - "(ssv)", PP_IFACE, "ActiveProfile", GLib.Variant.new_string(args.profile[0]) - ) - except: - raise + proxy = get_proxy() + proxy.Set( + "(ssv)", PP_IFACE, "ActiveProfile", GLib.Variant.new_string(args.profile[0]) + ) def get_profiles_property(prop): - try: - proxy = get_proxy() - except: - raise - - profiles = None - try: - profiles = proxy.Get("(ss)", PP_IFACE, prop) - except: - raise - return profiles + proxy = get_proxy() + return proxy.Get("(ss)", PP_IFACE, prop) @command -def _list(args): # pylint: disable=unused-argument - try: - profiles = get_profiles_property("Profiles") - reason = get_proxy().Get("(ss)", PP_IFACE, "PerformanceDegraded") - degraded = reason != "" - active = get_proxy().Get("(ss)", PP_IFACE, "ActiveProfile") - except: - raise +def _list(_args): + profiles = get_profiles_property("Profiles") + reason = get_proxy().Get("(ss)", PP_IFACE, "PerformanceDegraded") + degraded = reason != "" + active = get_proxy().Get("(ss)", PP_IFACE, "ActiveProfile") index = 0 for profile in reversed(profiles): @@ -105,11 +91,8 @@ @command -def _list_holds(args): # pylint: disable=unused-argument - try: - holds = get_profiles_property("ActiveProfileHolds") - except: - raise +def _list_holds(_args): + holds = get_profiles_property("ActiveProfileHolds") index = 0 for hold in holds: @@ -136,35 +119,47 @@ if not reason: reason = f"Running {args.appid}" ret = 0 - try: - bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) - proxy = Gio.DBusProxy.new_sync( - bus, Gio.DBusProxyFlags.NONE, None, PP_NAME, PP_PATH, PP_IFACE, None - ) - except: - raise - + bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) + proxy = Gio.DBusProxy.new_sync( + bus, Gio.DBusProxyFlags.NONE, None, PP_NAME, PP_PATH, PP_IFACE, None + ) cookie = proxy.HoldProfile("(sss)", profile, reason, appid) - # Kill child when we go away - def receive_signal(_signum, _stack): - launched_app.terminate() - - signal.signal(signal.SIGTERM, receive_signal) - # print (f'Got {cookie} for {profile} hold') with subprocess.Popen(args.arguments) as launched_app: + # Redirect the same signal to the child + def receive_signal(signum, _stack): + launched_app.send_signal(signum) + + redirected_signals = [ + signal.SIGTERM, + signal.SIGINT, + signal.SIGABRT, + ] + + for sig in redirected_signals: + signal.signal(sig, receive_signal) + try: launched_app.wait() ret = launched_app.returncode except KeyboardInterrupt: ret = launched_app.returncode + + for sig in redirected_signals: + signal.signal(sig, signal.SIG_DFL) + proxy.ReleaseProfile("(u)", cookie) - return ret + if ret < 0: + # Use standard POSIX signal exit code. + os.kill(os.getpid(), -ret) + return + sys.exit(ret) -def main(): + +def get_parser(): parser = argparse.ArgumentParser( epilog="Use “powerprofilesctl COMMAND --help” to get detailed help for individual commands", ) @@ -216,10 +211,48 @@ ) parser_version.set_defaults(func=_version) - args = parser.parse_args() + if not os.getenv("PPD_COMPLETIONS_GENERATION"): + return parser + + try: + import shtab # pylint: disable=import-outside-toplevel + + shtab.add_argument_to(parser, ["--print-completion"]) # magic! + except ImportError: + pass + + return parser + + +def check_unknown_args(args, unknown_args, cmd): + if cmd != "launch": + return False + + for idx, unknown_arg in enumerate(unknown_args): + arg = args[idx] + if arg == cmd: + return True + if unknown_arg == arg: + return False + + return True + + +def main(): + parser = get_parser() + args, unknown = parser.parse_known_args() # default behavior is to run list if no command is given if not args.command: args.func = _list + + if check_unknown_args(sys.argv[1:], unknown, args.command): + args.arguments += unknown + unknown = [] + + if unknown: + msg = argparse._("unrecognized arguments: %s") + parser.error(msg % " ".join(unknown)) + args.func(args) diff -Nru power-profiles-daemon-0.20/src/ppd-action-amdgpu-panel-power.c power-profiles-daemon-0.21/src/ppd-action-amdgpu-panel-power.c --- power-profiles-daemon-0.20/src/ppd-action-amdgpu-panel-power.c 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-action-amdgpu-panel-power.c 2024-04-03 23:55:02.000000000 +0000 @@ -43,10 +43,9 @@ PpdProfile last_profile; GUdevClient *client; - GDBusProxy *proxy; - guint watcher_id; gint panel_power_saving; + gboolean valid_battery; gboolean on_battery; }; @@ -175,7 +174,7 @@ PpdActionAmdgpuPanelPower *self = PPD_ACTION_AMDGPU_PANEL_POWER (action); self->last_profile = profile; - if (self->proxy == NULL) { + if (!self->valid_battery) { g_debug ("upower not available; battery data might be stale"); return TRUE; } @@ -183,28 +182,29 @@ return ppd_action_amdgpu_panel_update_target (self, error); } +static gboolean +ppd_action_amdgpu_panel_power_power_changed (PpdAction *action, + PpdPowerChangedReason reason, + GError **error) +{ + PpdActionAmdgpuPanelPower *self = PPD_ACTION_AMDGPU_PANEL_POWER (action); -static void -upower_properties_changed (GDBusProxy *proxy, - GVariant *changed_properties, - GStrv invalidated_properties, - PpdActionAmdgpuPanelPower *self) -{ - g_autoptr (GVariant) battery_val = NULL; - g_autoptr (GError) error = NULL; - gboolean new_on_battery; - - if (proxy != NULL) - battery_val = g_dbus_proxy_get_cached_property (proxy, "OnBattery"); - new_on_battery = battery_val ? g_variant_get_boolean (battery_val) : FALSE; - - if (self->on_battery == new_on_battery) - return; + switch (reason) { + case PPD_POWER_CHANGED_REASON_UNKNOWN: + self->valid_battery = FALSE; + return TRUE; + case PPD_POWER_CHANGED_REASON_AC: + self->on_battery = FALSE; + break; + case PPD_POWER_CHANGED_REASON_BATTERY: + self->on_battery = TRUE; + break; + default: + g_assert_not_reached (); + } - g_debug ("OnBattery: %d -> %d", self->on_battery, new_on_battery); - self->on_battery = new_on_battery; - if (!ppd_action_amdgpu_panel_update_target (self, &error)) - g_warning ("failed to update target: %s", error->message); + self->valid_battery = TRUE; + return ppd_action_amdgpu_panel_update_target (self, error); } static void @@ -261,58 +261,12 @@ } static void -upower_name_vanished (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - PpdActionAmdgpuPanelPower *self = user_data; - - g_debug ("%s vanished", UPOWER_DBUS_NAME); - - /* reset */ - g_clear_pointer (&self->proxy, g_object_unref); - upower_properties_changed (NULL, NULL, NULL, self); -} - -static void -upower_name_appeared (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - gpointer user_data) -{ - PpdActionAmdgpuPanelPower *self = user_data; - g_autoptr (GError) error = NULL; - - g_debug ("%s appeared", UPOWER_DBUS_NAME); - self->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - UPOWER_DBUS_NAME, - UPOWER_DBUS_PATH, - UPOWER_DBUS_INTERFACE, - NULL, - &error); - if (self->proxy == NULL) { - g_debug ("failed to connect to upower: %s", error->message); - return; - } - - g_signal_connect (self->proxy, - "g-properties-changed", - G_CALLBACK(upower_properties_changed), - self); - upower_properties_changed (self->proxy, NULL, NULL, self); -} - -static void ppd_action_amdgpu_panel_power_finalize (GObject *object) { PpdActionAmdgpuPanelPower *action; action = PPD_ACTION_AMDGPU_PANEL_POWER (object); - g_clear_handle_id (&action->watcher_id, g_bus_unwatch_name); g_clear_object (&action->client); - g_clear_pointer (&action->proxy, g_object_unref); G_OBJECT_CLASS (ppd_action_amdgpu_panel_power_parent_class)->finalize (object); } @@ -329,6 +283,7 @@ driver_class = PPD_ACTION_CLASS(klass); driver_class->probe = ppd_action_amdgpu_panel_power_probe; driver_class->activate_profile = ppd_action_amdgpu_panel_power_activate_profile; + driver_class->power_changed = ppd_action_amdgpu_panel_power_power_changed; } static void @@ -337,14 +292,6 @@ const gchar * const subsystem[] = { "drm", NULL }; self->client = g_udev_client_new (subsystem); - g_signal_connect (G_OBJECT (self->client), "uevent", - G_CALLBACK (udev_uevent_cb), self); - - self->watcher_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, - UPOWER_DBUS_NAME, - G_BUS_NAME_WATCHER_FLAGS_NONE, - upower_name_appeared, - upower_name_vanished, - self, - NULL); + g_signal_connect_object (G_OBJECT (self->client), "uevent", + G_CALLBACK (udev_uevent_cb), self, 0); } diff -Nru power-profiles-daemon-0.20/src/ppd-action.c power-profiles-daemon-0.21/src/ppd-action.c --- power-profiles-daemon-0.20/src/ppd-action.c 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-action.c 2024-04-03 23:55:02.000000000 +0000 @@ -60,7 +60,7 @@ switch (property_id) { case PROP_ACTION_NAME: - g_assert (priv->action_name == NULL); + g_return_if_fail (priv->action_name == NULL); priv->action_name = g_value_dup_string (value); break; default: @@ -149,6 +149,18 @@ return PPD_ACTION_GET_CLASS (action)->activate_profile (action, profile, error); } +gboolean ppd_action_power_changed (PpdAction *action, + PpdPowerChangedReason reason, + GError **error) +{ + g_return_val_if_fail (PPD_IS_ACTION (action), FALSE); + + if (!PPD_ACTION_GET_CLASS (action)->power_changed) + return TRUE; + + return PPD_ACTION_GET_CLASS (action)->power_changed (action, reason, error); +} + const char * ppd_action_get_action_name (PpdAction *action) { diff -Nru power-profiles-daemon-0.20/src/ppd-action.h power-profiles-daemon-0.21/src/ppd-action.h --- power-profiles-daemon-0.20/src/ppd-action.h 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-action.h 2024-04-03 23:55:02.000000000 +0000 @@ -28,14 +28,19 @@ { GObjectClass parent_class; - PpdProbeResult (* probe) (PpdAction *action); - gboolean (* activate_profile) (PpdAction *action, - PpdProfile profile, - GError **error); + PpdProbeResult (* probe) (PpdAction *action); + gboolean (* activate_profile) (PpdAction *action, + PpdProfile profile, + GError **error); + gboolean (* power_changed) (PpdAction *action, + PpdPowerChangedReason reason, + GError **error); + }; #ifndef __GTK_DOC_IGNORE__ PpdProbeResult ppd_action_probe (PpdAction *action); gboolean ppd_action_activate_profile (PpdAction *action, PpdProfile profile, GError **error); +gboolean ppd_action_power_changed (PpdAction *action, PpdPowerChangedReason reason, GError **error); const char *ppd_action_get_action_name (PpdAction *action); #endif diff -Nru power-profiles-daemon-0.20/src/ppd-driver-amd-pstate.c power-profiles-daemon-0.21/src/ppd-driver-amd-pstate.c --- power-profiles-daemon-0.20/src/ppd-driver-amd-pstate.c 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-driver-amd-pstate.c 2024-04-03 23:55:02.000000000 +0000 @@ -37,7 +37,8 @@ PpdDriverCpu parent_instance; PpdProfile activated_profile; - GList *epp_devices; /* GList of paths */ + GPtrArray *epp_devices; /* Array of paths */ + gboolean on_battery; }; G_DEFINE_TYPE (PpdDriverAmdPstate, ppd_driver_amd_pstate, PPD_TYPE_DRIVER_CPU) @@ -76,29 +77,28 @@ g_autofree char *pm_profile_str = NULL; guint64 pm_profile; const char *dirname; - PpdProbeResult ret = PPD_PROBE_RESULT_FAIL; /* Verify that AMD P-State is running in active mode */ pstate_status_path = ppd_utils_get_sysfs_path (PSTATE_STATUS_PATH); if (!g_file_get_contents (pstate_status_path, &status, NULL, NULL)) - return ret; + return PPD_PROBE_RESULT_FAIL; status = g_strchomp (status); if (g_strcmp0 (status, "active") != 0) { g_debug ("AMD P-State is not running in active mode"); - return ret; + return PPD_PROBE_RESULT_FAIL; } policy_dir = ppd_utils_get_sysfs_path (CPUFREQ_POLICY_DIR); dir = g_dir_open (policy_dir, 0, NULL); if (!dir) { g_debug ("Could not open %s", policy_dir); - return ret; + return PPD_PROBE_RESULT_FAIL; } /* only run on things that we know aren't servers */ pm_profile_path = ppd_utils_get_sysfs_path (ACPI_PM_PROFILE); if (!g_file_get_contents (pm_profile_path, &pm_profile_str, NULL, NULL)) - return ret; + return PPD_PROBE_RESULT_FAIL; pm_profile = g_ascii_strtoull (pm_profile_str, NULL, 10); switch (pm_profile) { case PM_UNSPECIFIED: @@ -106,7 +106,7 @@ case PM_SOHO_SERVER: case PM_PERFORMANCE_SERVER: g_debug ("AMD-P-State not supported on PM profile %" G_GUINT64_FORMAT, pm_profile); - return ret; + return PPD_PROBE_RESULT_FAIL; default: break; } @@ -126,25 +126,26 @@ if (!g_file_test (path, G_FILE_TEST_EXISTS)) continue; - pstate->epp_devices = g_list_prepend (pstate->epp_devices, g_steal_pointer (&base)); - ret = PPD_PROBE_RESULT_SUCCESS; + if (!pstate->epp_devices) + pstate->epp_devices = g_ptr_array_new_with_free_func (g_free); + + g_ptr_array_add (pstate->epp_devices, g_steal_pointer (&base)); } - return ret; + if (pstate->epp_devices && pstate->epp_devices->len) + return PPD_PROBE_RESULT_SUCCESS; + + return PPD_PROBE_RESULT_FAIL; } static PpdProbeResult ppd_driver_amd_pstate_probe (PpdDriver *driver) { PpdDriverAmdPstate *pstate = PPD_DRIVER_AMD_PSTATE (driver); - PpdProbeResult ret = PPD_PROBE_RESULT_FAIL; + PpdProbeResult ret; ret = probe_epp (pstate); - if (ret != PPD_PROBE_RESULT_SUCCESS) - goto out; - -out: g_debug ("%s p-state settings", ret == PPD_PROBE_RESULT_SUCCESS ? "Found" : "Didn't find"); return ret; @@ -162,11 +163,11 @@ return "performance"; } - g_assert_not_reached (); + g_return_val_if_reached (NULL); } static const char * -profile_to_epp_pref (PpdProfile profile) +profile_to_epp_pref (PpdProfile profile, gboolean battery) { /* Note that we don't check "energy_performance_available_preferences" * as all the values are always available */ @@ -174,24 +175,31 @@ case PPD_PROFILE_POWER_SAVER: return "power"; case PPD_PROFILE_BALANCED: - return "balance_performance"; + return battery ? "balance_power" : "balance_performance"; case PPD_PROFILE_PERFORMANCE: return "performance"; } - g_assert_not_reached (); + g_return_val_if_reached (NULL); } static gboolean -apply_pref_to_devices (GList *devices, +apply_pref_to_devices (GPtrArray *devices, PpdProfile profile, + gboolean battery, GError **error) { - gboolean ret = TRUE; - GList *l; + const char *epp_pref; + const char *gov_pref; - for (l = devices; l != NULL; l = l->next) { - const char *base = l->data; + if (profile == PPD_PROFILE_UNSET) + return TRUE; + + epp_pref = profile_to_epp_pref (profile, battery); + gov_pref = profile_to_gov_pref (profile); + + for (guint i = 0; i < devices->len; ++i) { + const char *base = g_ptr_array_index (devices, i); g_autofree char *epp = NULL; g_autofree char *gov = NULL; @@ -199,44 +207,42 @@ "scaling_governor", NULL); - ret = ppd_utils_write (gov, profile_to_gov_pref (profile), error); - if (!ret) - break; + if (!ppd_utils_write (gov, gov_pref, error)) + return FALSE; epp = g_build_filename (base, "energy_performance_preference", NULL); - ret = ppd_utils_write (epp, profile_to_epp_pref (profile), error); - if (!ret) - break; + if (!ppd_utils_write (epp, epp_pref, error)) + return FALSE; } - return ret; + return TRUE; } static gboolean ppd_driver_amd_pstate_activate_profile (PpdDriver *driver, - PpdProfile profile, - PpdProfileActivationReason reason, - GError **error) + PpdProfile profile, + PpdProfileActivationReason reason, + GError **error) { PpdDriverAmdPstate *pstate = PPD_DRIVER_AMD_PSTATE (driver); gboolean ret = FALSE; g_return_val_if_fail (pstate->epp_devices != NULL, FALSE); + g_return_val_if_fail (pstate->epp_devices->len != 0, FALSE); - if (pstate->epp_devices) { - ret = apply_pref_to_devices (pstate->epp_devices, profile, error); - if (!ret && pstate->activated_profile != PPD_PROFILE_UNSET) { - g_autoptr(GError) error_local = NULL; - /* reset back to previous */ - if (!apply_pref_to_devices (pstate->epp_devices, - pstate->activated_profile, - &error_local)) - g_warning ("failed to restore previous profile: %s", error_local->message); - return ret; - } + ret = apply_pref_to_devices (pstate->epp_devices, profile, pstate->on_battery, error); + if (!ret && pstate->activated_profile != PPD_PROFILE_UNSET) { + g_autoptr(GError) error_local = NULL; + /* reset back to previous */ + if (!apply_pref_to_devices (pstate->epp_devices, + pstate->activated_profile, + pstate->on_battery, + &error_local)) + g_warning ("failed to restore previous profile: %s", error_local->message); + return ret; } if (ret) @@ -245,13 +251,38 @@ return ret; } +static gboolean +ppd_driver_amd_pstate_power_changed (PpdDriver *driver, + PpdPowerChangedReason reason, + GError **error) +{ + PpdDriverAmdPstate *pstate = PPD_DRIVER_AMD_PSTATE (driver); + + switch (reason) { + case PPD_POWER_CHANGED_REASON_UNKNOWN: + case PPD_POWER_CHANGED_REASON_AC: + pstate->on_battery = FALSE; + break; + case PPD_POWER_CHANGED_REASON_BATTERY: + pstate->on_battery = TRUE; + break; + default: + g_assert_not_reached (); + } + + return apply_pref_to_devices (pstate->epp_devices, + pstate->activated_profile, + pstate->on_battery, + error); +} + static void ppd_driver_amd_pstate_finalize (GObject *object) { PpdDriverAmdPstate *driver; driver = PPD_DRIVER_AMD_PSTATE (object); - g_clear_list (&driver->epp_devices, g_free); + g_clear_pointer (&driver->epp_devices, g_ptr_array_unref); G_OBJECT_CLASS (ppd_driver_amd_pstate_parent_class)->finalize (object); } @@ -268,6 +299,7 @@ driver_class = PPD_DRIVER_CLASS (klass); driver_class->probe = ppd_driver_amd_pstate_probe; driver_class->activate_profile = ppd_driver_amd_pstate_activate_profile; + driver_class->power_changed = ppd_driver_amd_pstate_power_changed; } static void diff -Nru power-profiles-daemon-0.20/src/ppd-driver-fake.c power-profiles-daemon-0.21/src/ppd-driver-fake.c --- power-profiles-daemon-0.20/src/ppd-driver-fake.c 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-driver-fake.c 2024-04-03 23:55:02.000000000 +0000 @@ -14,7 +14,7 @@ #include extern void main_loop_quit (void); -void restart_profile_drivers (void); +void restart_profile_drivers_for_default_app (void); struct _PpdDriverFake { @@ -88,7 +88,7 @@ break; case 'r': g_print ("Restarting profile drivers\n"); - restart_profile_drivers (); + restart_profile_drivers_for_default_app (); break; case 'q': case 'x': diff -Nru power-profiles-daemon-0.20/src/ppd-driver-intel-pstate.c power-profiles-daemon-0.21/src/ppd-driver-intel-pstate.c --- power-profiles-daemon-0.20/src/ppd-driver-intel-pstate.c 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-driver-intel-pstate.c 2024-04-03 23:55:02.000000000 +0000 @@ -30,11 +30,11 @@ PpdDriverCpu parent_instance; PpdProfile activated_profile; - GList *epp_devices; /* GList of paths */ - GList *epb_devices; /* GList of paths */ - GDBusProxy *logind_proxy; + GPtrArray *epp_devices; /* Array of paths */ + GPtrArray *epb_devices; /* Array of paths */ GFileMonitor *no_turbo_mon; char *no_turbo_path; + gboolean on_battery; }; G_DEFINE_TYPE (PpdDriverIntelPstate, ppd_driver_intel_pstate, PPD_TYPE_DRIVER_CPU) @@ -90,8 +90,12 @@ g_autofree char *path = NULL; path = g_file_get_path (file); - g_debug ("File monitor change happened for '%s'", path); - update_no_turbo (pstate); + g_debug ("File monitor change happened for '%s' (event type %d)", path, event_type); + + g_return_if_fail (event_type != G_FILE_MONITOR_EVENT_DELETED); + + if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT) + update_no_turbo (pstate); } static GFileMonitor * @@ -126,33 +130,28 @@ return has_turbo; } -static void -logind_proxy_signal_cb (GDBusProxy *proxy, - const char *sender_name, - const char *signal_name, - GVariant *parameters, - gpointer user_data) +static gboolean +ppd_driver_intel_pstate_prepare_for_sleep (PpdDriver *driver, + gboolean start, + GError **error) { - PpdDriverIntelPstate *pstate = user_data; - g_autoptr(GError) error = NULL; - gboolean start; - PpdProbeResult ret; - - if (g_strcmp0 (signal_name, "PrepareForSleep") != 0) - return; - g_variant_get (parameters, "(b)", &start); + PpdDriverIntelPstate *pstate = PPD_DRIVER_INTEL_PSTATE (driver); + g_autoptr(GError) local_error = NULL; + if (start) - return; + return TRUE; - g_debug ("System woke up from suspend, re-applying energy_perf_bias"); - ret = ppd_driver_intel_pstate_activate_profile (PPD_DRIVER (pstate), - pstate->activated_profile, - PPD_PROFILE_ACTIVATION_REASON_RESUME, - &error); - if (ret != PPD_PROBE_RESULT_SUCCESS) { - g_warning ("Could not reapply energy_perf_bias preference on resume: %s", - error->message); + g_debug ("Re-applying energy_perf_bias"); + if (!ppd_driver_intel_pstate_activate_profile (PPD_DRIVER (pstate), + pstate->activated_profile, + PPD_PROFILE_ACTIVATION_REASON_RESUME, + &local_error)) { + g_propagate_prefixed_error (error, g_steal_pointer (&local_error), + "Could not reapply energy_perf_bias preference on resume: "); + return FALSE; } + + return TRUE; } static PpdProbeResult @@ -161,14 +160,12 @@ g_autoptr(GDir) dir = NULL; g_autofree char *policy_dir = NULL; const char *dirname; - g_autoptr(GError) error = NULL; - PpdProbeResult ret = PPD_PROBE_RESULT_FAIL; policy_dir = ppd_utils_get_sysfs_path (CPU_DIR); dir = g_dir_open (policy_dir, 0, NULL); if (!dir) { g_debug ("Could not open %s", CPU_DIR); - return ret; + return PPD_PROBE_RESULT_FAIL; } while ((dirname = g_dir_read_name (dir)) != NULL) { @@ -182,28 +179,16 @@ if (!g_file_test (path, G_FILE_TEST_EXISTS)) continue; - pstate->epb_devices = g_list_prepend (pstate->epb_devices, g_steal_pointer (&path)); - ret = PPD_PROBE_RESULT_SUCCESS; - } + if (!pstate->epb_devices) + pstate->epb_devices = g_ptr_array_new_with_free_func (g_free); - pstate->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); - if (!pstate->logind_proxy) { - g_debug ("Could not create proxy for logind: %s", - error->message); - } else { - g_signal_connect (pstate->logind_proxy, "g-signal", - G_CALLBACK (logind_proxy_signal_cb), - pstate); + g_ptr_array_add (pstate->epb_devices, g_steal_pointer (&path)); } - return ret; + if (pstate->epb_devices && pstate->epb_devices->len) + return PPD_PROBE_RESULT_SUCCESS; + + return PPD_PROBE_RESULT_FAIL; } static PpdProbeResult @@ -214,23 +199,22 @@ g_autofree char *pstate_status_path = NULL; g_autofree char *status = NULL; const char *dirname; - PpdProbeResult ret = PPD_PROBE_RESULT_FAIL; /* Verify that Intel P-State is running in active mode */ pstate_status_path = ppd_utils_get_sysfs_path (PSTATE_STATUS_PATH); if (!g_file_get_contents (pstate_status_path, &status, NULL, NULL)) - return ret; + return PPD_PROBE_RESULT_FAIL; status = g_strchomp (status); if (g_strcmp0 (status, "active") != 0) { g_debug ("Intel P-State is running in passive mode"); - return ret; + return PPD_PROBE_RESULT_FAIL; } policy_dir = ppd_utils_get_sysfs_path (CPUFREQ_POLICY_DIR); dir = g_dir_open (policy_dir, 0, NULL); if (!dir) { g_debug ("Could not open %s", policy_dir); - return ret; + return PPD_PROBE_RESULT_FAIL; } while ((dirname = g_dir_read_name (dir)) != NULL) { @@ -255,11 +239,16 @@ continue; } - pstate->epp_devices = g_list_prepend (pstate->epp_devices, g_steal_pointer (&path)); - ret = PPD_PROBE_RESULT_SUCCESS; + if (!pstate->epp_devices) + pstate->epp_devices = g_ptr_array_new_with_free_func (g_free); + + g_ptr_array_add (pstate->epp_devices, g_steal_pointer (&path)); } - return ret; + if (pstate->epp_devices && pstate->epp_devices->len) + return PPD_PROBE_RESULT_SUCCESS; + + return PPD_PROBE_RESULT_FAIL; } static PpdProbeResult @@ -283,8 +272,8 @@ pstate->no_turbo_path = ppd_utils_get_sysfs_path (NO_TURBO_PATH); pstate->no_turbo_mon = monitor_no_turbo_prop (pstate->no_turbo_path); if (pstate->no_turbo_mon) { - g_signal_connect (G_OBJECT (pstate->no_turbo_mon), "changed", - G_CALLBACK (no_turbo_changed), pstate); + g_signal_connect_object (G_OBJECT (pstate->no_turbo_mon), "changed", + G_CALLBACK (no_turbo_changed), pstate, 0); } update_no_turbo (pstate); } @@ -303,7 +292,7 @@ } static const char * -profile_to_epp_pref (PpdProfile profile) +profile_to_epp_pref (PpdProfile profile, gboolean battery) { /* Note that we don't check "energy_performance_available_preferences" * as all the values are always available */ @@ -311,16 +300,16 @@ case PPD_PROFILE_POWER_SAVER: return "power"; case PPD_PROFILE_BALANCED: - return "balance_performance"; + return battery ? "balance_power" : "balance_performance"; case PPD_PROFILE_PERFORMANCE: return "performance"; } - g_assert_not_reached (); + g_return_val_if_reached (NULL); } static const char * -profile_to_epb_pref (PpdProfile profile) +profile_to_epb_pref (PpdProfile profile, gboolean battery) { /* From arch/x86/include/asm/msr-index.h * See ENERGY_PERF_BIAS_* */ @@ -328,61 +317,79 @@ case PPD_PROFILE_POWER_SAVER: return "15"; case PPD_PROFILE_BALANCED: - return "6"; + return battery ? "8" : "6"; case PPD_PROFILE_PERFORMANCE: return "0"; } - g_assert_not_reached (); + g_return_val_if_reached (NULL); } static gboolean -apply_pref_to_devices (GList *devices, - const char *pref, +apply_pref_to_devices (PpdDriver *driver, + PpdProfile profile, GError **error) { - gboolean ret = TRUE; - GList *l; + PpdDriverIntelPstate *pstate = PPD_DRIVER_INTEL_PSTATE (driver); - for (l = devices; l != NULL; l = l->next) { - const char *path = l->data; + if (profile == PPD_PROFILE_UNSET) + return TRUE; - ret = ppd_utils_write (path, pref, error); - if (!ret) - break; + g_return_val_if_fail (pstate->epp_devices != NULL || + pstate->epb_devices != NULL, FALSE); + g_return_val_if_fail ((pstate->epp_devices && pstate->epp_devices->len != 0) || + (pstate->epb_devices && pstate->epb_devices->len != 0), FALSE); + + if (pstate->epp_devices) { + const char *epp_pref = profile_to_epp_pref (profile, pstate->on_battery); + + if (!ppd_utils_write_files (pstate->epp_devices, epp_pref, error)) + return FALSE; } - return ret; + if (pstate->epb_devices) { + const char *epb_pref = profile_to_epb_pref (profile, pstate->on_battery); + + if (!ppd_utils_write_files (pstate->epb_devices, epb_pref, error)) + return FALSE; + } + + pstate->activated_profile = profile; + + return TRUE; } static gboolean -ppd_driver_intel_pstate_activate_profile (PpdDriver *driver, - PpdProfile profile, - PpdProfileActivationReason reason, - GError **error) +ppd_driver_intel_pstate_power_changed (PpdDriver *driver, + PpdPowerChangedReason reason, + GError **error) { PpdDriverIntelPstate *pstate = PPD_DRIVER_INTEL_PSTATE (driver); - gboolean ret = FALSE; - const char *pref; - g_return_val_if_fail (pstate->epp_devices != NULL || - pstate->epb_devices, FALSE); - - if (pstate->epp_devices) { - pref = profile_to_epp_pref (profile); - ret = apply_pref_to_devices (pstate->epp_devices, pref, error); - if (!ret) - return ret; - } - if (pstate->epb_devices) { - pref = profile_to_epb_pref (profile); - ret = apply_pref_to_devices (pstate->epb_devices, pref, error); + switch (reason) { + case PPD_POWER_CHANGED_REASON_UNKNOWN: + case PPD_POWER_CHANGED_REASON_AC: + pstate->on_battery = FALSE; + break; + case PPD_POWER_CHANGED_REASON_BATTERY: + pstate->on_battery = TRUE; + break; + default: + g_return_val_if_reached (FALSE); } - if (ret) - pstate->activated_profile = profile; + return apply_pref_to_devices (driver, + pstate->activated_profile, + error); +} - return ret; +static gboolean +ppd_driver_intel_pstate_activate_profile (PpdDriver *driver, + PpdProfile profile, + PpdProfileActivationReason reason, + GError **error) +{ + return apply_pref_to_devices (driver, profile, error); } static void @@ -391,11 +398,11 @@ PpdDriverIntelPstate *driver; driver = PPD_DRIVER_INTEL_PSTATE (object); - g_clear_list (&driver->epp_devices, g_free); - g_clear_list (&driver->epb_devices, g_free); + + g_clear_pointer (&driver->epp_devices, g_ptr_array_unref); + g_clear_pointer (&driver->epb_devices, g_ptr_array_unref); g_clear_pointer (&driver->no_turbo_path, g_free); g_clear_object (&driver->no_turbo_mon); - g_clear_object (&driver->logind_proxy); G_OBJECT_CLASS (ppd_driver_intel_pstate_parent_class)->finalize (object); } @@ -412,6 +419,8 @@ driver_class = PPD_DRIVER_CLASS (klass); driver_class->probe = ppd_driver_intel_pstate_probe; driver_class->activate_profile = ppd_driver_intel_pstate_activate_profile; + driver_class->prepare_to_sleep = ppd_driver_intel_pstate_prepare_for_sleep; + driver_class->power_changed = ppd_driver_intel_pstate_power_changed; } static void diff -Nru power-profiles-daemon-0.20/src/ppd-driver-platform-profile.c power-profiles-daemon-0.21/src/ppd-driver-platform-profile.c --- power-profiles-daemon-0.20/src/ppd-driver-platform-profile.c 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-driver-platform-profile.c 2024-04-03 23:55:02.000000000 +0000 @@ -31,7 +31,7 @@ gboolean has_low_power; GFileMonitor *lapmode_mon; GFileMonitor *acpi_platform_profile_mon; - guint acpi_platform_profile_changed_id; + gulong acpi_platform_profile_changed_id; }; G_DEFINE_TYPE (PpdDriverPlatformProfile, ppd_driver_platform_profile, PPD_TYPE_DRIVER_PLATFORM) @@ -71,7 +71,8 @@ return "performance"; } - g_assert_not_reached (); + g_debug ("Unhandled ACPI platform profile '%d'", profile); + g_return_val_if_reached (NULL); } static PpdProfile @@ -90,10 +91,9 @@ case 'p': return PPD_PROFILE_PERFORMANCE; default: - g_debug ("Got unsupported performance_profile value '%s'", str); + g_debug ("Unhandled ACPI platform profile '%c'", str[0]); + g_return_val_if_reached (PPD_PROFILE_UNSET); } - - return PPD_PROFILE_UNSET; } static PpdProfile @@ -197,8 +197,12 @@ gpointer user_data) { PpdDriverPlatformProfile *self = user_data; - g_debug (LAPMODE_SYSFS_NAME " attribute changed"); - update_dytc_lapmode_state (self); + + g_debug (LAPMODE_SYSFS_NAME " attribute changed (event: %d)", event_type); + g_return_if_fail (event_type != G_FILE_MONITOR_EVENT_DELETED); + + if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT) + update_dytc_lapmode_state (self); } static void @@ -209,12 +213,17 @@ gpointer user_data) { PpdDriverPlatformProfile *self = user_data; + g_debug (ACPI_PLATFORM_PROFILE_PATH " changed (%d)", event_type); if (self->probe_result == PPD_PROBE_RESULT_DEFER) { g_signal_emit_by_name (G_OBJECT (self), "probe-request", 0); return; } - update_acpi_platform_profile_state (self); + + g_return_if_fail (event_type != G_FILE_MONITOR_EVENT_DELETED); + + if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT) + update_acpi_platform_profile_state (self); } static gboolean @@ -224,6 +233,7 @@ GError **error) { PpdDriverPlatformProfile *self = PPD_DRIVER_PLATFORM_PROFILE (driver); + g_autoptr(GError) local_error = NULL; g_autofree char *platform_profile_path = NULL; const char *platform_profile_value; @@ -245,8 +255,11 @@ g_signal_handler_block (G_OBJECT (self->acpi_platform_profile_mon), self->acpi_platform_profile_changed_id); platform_profile_path = ppd_utils_get_sysfs_path (ACPI_PLATFORM_PROFILE_PATH); - if (!ppd_utils_write (platform_profile_path, profile_to_acpi_platform_profile_value (self, profile), error)) { - g_debug ("Failed to write to acpi_platform_profile: %s", (* error)->message); + if (!ppd_utils_write (platform_profile_path, + profile_to_acpi_platform_profile_value (self, profile), &local_error)) { + g_debug ("Failed to write to acpi_platform_profile: %s", local_error->message); + g_propagate_prefixed_error (error, g_steal_pointer (&local_error), + "Failed to write to acpi_platform_profile: "); g_signal_handler_unblock (G_OBJECT (self->acpi_platform_profile_mon), self->acpi_platform_profile_changed_id); return FALSE; } @@ -316,8 +329,8 @@ self->lapmode_mon = ppd_utils_monitor_sysfs_attr (self->device, LAPMODE_SYSFS_NAME, NULL); - g_signal_connect (G_OBJECT (self->lapmode_mon), "changed", - G_CALLBACK (lapmode_changed), self); + g_signal_connect_object (G_OBJECT (self->lapmode_mon), "changed", + G_CALLBACK (lapmode_changed), self, 0); update_dytc_lapmode_state (self); out: @@ -334,6 +347,8 @@ PpdDriverPlatformProfile *driver; driver = PPD_DRIVER_PLATFORM_PROFILE (object); + g_clear_signal_handler (&driver->acpi_platform_profile_changed_id, + driver->acpi_platform_profile_mon); g_clear_pointer (&driver->profile_choices, g_strfreev); g_clear_object (&driver->device); g_clear_object (&driver->lapmode_mon); diff -Nru power-profiles-daemon-0.20/src/ppd-driver.c power-profiles-daemon-0.21/src/ppd-driver.c --- power-profiles-daemon-0.20/src/ppd-driver.c 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-driver.c 2024-04-03 23:55:02.000000000 +0000 @@ -63,9 +63,11 @@ PpdDriver *driver = PPD_DRIVER (object); PpdDriverPrivate *priv = PPD_DRIVER_GET_PRIVATE (driver); + g_return_if_fail (PPD_IS_DRIVER (object)); + switch (property_id) { case PROP_DRIVER_NAME: - g_assert (priv->driver_name == NULL); + g_return_if_fail (priv->driver_name == NULL); priv->driver_name = g_value_dup_string (value); break; case PROP_PROFILES: @@ -236,6 +238,32 @@ return PPD_DRIVER_GET_CLASS (driver)->activate_profile (driver, profile, reason, error); } +gboolean +ppd_driver_power_changed (PpdDriver *driver, + PpdPowerChangedReason reason, + GError **error) +{ + g_return_val_if_fail (PPD_IS_DRIVER (driver), FALSE); + + if (!PPD_DRIVER_GET_CLASS (driver)->power_changed) + return TRUE; + + return PPD_DRIVER_GET_CLASS (driver)->power_changed (driver, reason, error); +} + +gboolean +ppd_driver_prepare_to_sleep (PpdDriver *driver, + gboolean start, + GError **error) +{ + g_return_val_if_fail (PPD_IS_DRIVER (driver), FALSE); + + if (!PPD_DRIVER_GET_CLASS (driver)->prepare_to_sleep) + return TRUE; + + return PPD_DRIVER_GET_CLASS (driver)->prepare_to_sleep (driver, start, error); +} + const char * ppd_driver_get_driver_name (PpdDriver *driver) { @@ -319,6 +347,6 @@ case PPD_PROFILE_ACTIVATION_REASON_PROGRAM_HOLD: return "program-hold"; default: - g_assert_not_reached (); + g_return_val_if_reached (NULL); } } diff -Nru power-profiles-daemon-0.20/src/ppd-driver.h power-profiles-daemon-0.21/src/ppd-driver.h --- power-profiles-daemon-0.20/src/ppd-driver.h 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-driver.h 2024-04-03 23:55:02.000000000 +0000 @@ -45,6 +45,7 @@ * @parent_class: The parent class. * @probe: Called by the daemon on startup. * @activate_profile: Called by the daemon for every profile change. + * @power_changed: Called by the daemon when power adapter status changes * * New profile drivers should not derive from #PpdDriver. They should * derive from the child from #PpdDriverCpu or #PpdDriverPlatform drivers @@ -59,12 +60,20 @@ PpdProfile profile, PpdProfileActivationReason reason, GError **error); + gboolean (* power_changed) (PpdDriver *driver, + PpdPowerChangedReason reason, + GError **error); + gboolean (* prepare_to_sleep) (PpdDriver *driver, + gboolean start, + GError **error); }; #ifndef __GTK_DOC_IGNORE__ PpdProbeResult ppd_driver_probe (PpdDriver *driver); gboolean ppd_driver_activate_profile (PpdDriver *driver, PpdProfile profile, PpdProfileActivationReason reason, GError **error); +gboolean ppd_driver_power_changed (PpdDriver *driver, PpdPowerChangedReason reason, GError **error); +gboolean ppd_driver_prepare_to_sleep (PpdDriver *driver, gboolean start, GError **error); const char *ppd_driver_get_driver_name (PpdDriver *driver); PpdProfile ppd_driver_get_profiles (PpdDriver *driver); const char *ppd_driver_get_performance_degraded (PpdDriver *driver); diff -Nru power-profiles-daemon-0.20/src/ppd-profile.c power-profiles-daemon-0.21/src/ppd-profile.c --- power-profiles-daemon-0.20/src/ppd-profile.c 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-profile.c 2024-04-03 23:55:02.000000000 +0000 @@ -13,31 +13,37 @@ const char * ppd_profile_to_str (PpdProfile profile) { - GFlagsClass *klass = g_type_class_ref (PPD_TYPE_PROFILE); + g_autoptr(GFlagsClass) klass = g_type_class_ref (PPD_TYPE_PROFILE); GFlagsValue *value = g_flags_get_first_value (klass, profile); const gchar *name = value ? value->value_nick : ""; - g_type_class_unref (klass); return name; } PpdProfile ppd_profile_from_str (const char *str) { - GFlagsClass *klass = g_type_class_ref (PPD_TYPE_PROFILE); + g_autoptr(GFlagsClass) klass = g_type_class_ref (PPD_TYPE_PROFILE); GFlagsValue *value = g_flags_get_value_by_nick (klass, str); PpdProfile profile = value ? value->value : PPD_PROFILE_UNSET; - g_type_class_unref (klass); return profile; } gboolean ppd_profile_has_single_flag (PpdProfile profile) { - GFlagsClass *klass = g_type_class_ref (PPD_TYPE_PROFILE); + g_autoptr(GFlagsClass) klass = g_type_class_ref (PPD_TYPE_PROFILE); GFlagsValue *value = g_flags_get_first_value (klass, profile); - gboolean ret = FALSE; if (value && value->value == profile) - ret = TRUE; - g_type_class_unref (klass); - return ret; + return TRUE; + + return FALSE; +} + +const char * +ppd_power_changed_reason_to_str (PpdPowerChangedReason reason) +{ + g_autoptr(GEnumClass) klass = g_type_class_ref (PPD_TYPE_POWER_CHANGED_REASON); + GEnumValue *value = g_enum_get_value (klass, reason); + const gchar *name = value ? value->value_nick : ""; + return name; } diff -Nru power-profiles-daemon-0.20/src/ppd-profile.h power-profiles-daemon-0.21/src/ppd-profile.h --- power-profiles-daemon-0.20/src/ppd-profile.h 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-profile.h 2024-04-03 23:55:02.000000000 +0000 @@ -45,9 +45,27 @@ PPD_PROFILE_PERFORMANCE = 1 << 2 } PpdProfile; +/** + * PpdPowerChangedReason: + * @PPD_POWER_CHANGED_REASON_UNKNOWN: the power state is now unknown. + * This can happen if the power notification service is no longer available. + * @PPD_POWER_CHANGED_REASON_AC: the power source is now AC. + * @PPD_POWER_CHANGED_REASON_BATTERY: the power source is battery. + + * Drivers or actions can use this information to decide what to do within a + * given profile. + */ +typedef enum{ + PPD_POWER_CHANGED_REASON_UNKNOWN = 0, + PPD_POWER_CHANGED_REASON_AC, + PPD_POWER_CHANGED_REASON_BATTERY, +} PpdPowerChangedReason; + #define PPD_PROFILE_ALL (PPD_PROFILE_BALANCED | PPD_PROFILE_POWER_SAVER | PPD_PROFILE_PERFORMANCE) #define PPD_PROFILE_UNSET (0) const char *ppd_profile_to_str (PpdProfile profile); PpdProfile ppd_profile_from_str (const char *str); gboolean ppd_profile_has_single_flag (PpdProfile profile); + +const char *ppd_power_changed_reason_to_str (PpdPowerChangedReason reason); diff -Nru power-profiles-daemon-0.20/src/ppd-utils.c power-profiles-daemon-0.21/src/ppd-utils.c --- power-profiles-daemon-0.20/src/ppd-utils.c 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-utils.c 2024-04-03 23:55:02.000000000 +0000 @@ -10,7 +10,9 @@ #define G_LOG_DOMAIN "Utils" #include "ppd-utils.h" +#include #include +#include #include #include @@ -26,39 +28,65 @@ return g_build_filename (root, filename, NULL); } -gboolean ppd_utils_write (const char *filename, - const char *value, - GError **error) +gboolean +ppd_utils_write (const char *filename, + const char *value, + GError **error) { - FILE *sysfsfp; - int ret; +#if GLIB_CHECK_VERSION (2, 76, 0) + g_autofd +#endif + int fd = -1; + size_t size; g_return_val_if_fail (filename, FALSE); g_return_val_if_fail (value, FALSE); g_debug ("Writing '%s' to '%s'", value, filename); - sysfsfp = fopen (filename, "w"); - if (sysfsfp == NULL) { + fd = g_open (filename, O_WRONLY | O_TRUNC | O_SYNC); + if (fd == -1) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "Could not open '%s' for writing", filename); g_debug ("Could not open for writing '%s'", filename); return FALSE; } - setbuf (sysfsfp, NULL); - ret = fprintf (sysfsfp, "%s", value); - if (ret <= 0) { - g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), - "Error writing '%s': %s", filename, g_strerror (errno)); - g_debug ("Error writing '%s': %s", filename, g_strerror (errno)); - return FALSE; + + size = strlen (value); + while (size) { + ssize_t written = write (fd, value, size); + + if (written == -1) { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), + "Error writing '%s': %s", filename, g_strerror (errno)); + g_debug ("Error writing '%s': %s", filename, g_strerror (errno)); +#if !GLIB_CHECK_VERSION (2, 76, 0) + g_close (fd, NULL); +#endif + return FALSE; + } + + g_return_val_if_fail (written <= size, FALSE); + size -= written; } - if (fclose (sysfsfp) != 0) { - g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), - "Error closing '%s': %s", filename, g_strerror (errno)); - g_debug ("Error closing '%s': %s", filename, g_strerror (errno)); - return FALSE; + + return TRUE; +} + +gboolean +ppd_utils_write_files (GPtrArray *filenames, + const char *value, + GError **error) +{ + g_return_val_if_fail (filenames != NULL, FALSE); + + for (guint i = 0; i < filenames->len; i++) { + const char *file = g_ptr_array_index (filenames, i); + + if (!ppd_utils_write (file, value, error)) + return FALSE; } + return TRUE; } diff -Nru power-profiles-daemon-0.20/src/ppd-utils.h power-profiles-daemon-0.21/src/ppd-utils.h --- power-profiles-daemon-0.20/src/ppd-utils.h 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/src/ppd-utils.h 2024-04-03 23:55:02.000000000 +0000 @@ -16,6 +16,9 @@ gboolean ppd_utils_write (const char *filename, const char *value, GError **error); +gboolean ppd_utils_write_files (GPtrArray *filenames, + const char *value, + GError **error); gboolean ppd_utils_write_sysfs (GUdevDevice *device, const char *attribute, const char *value, diff -Nru power-profiles-daemon-0.20/tests/integration_test.py power-profiles-daemon-0.21/tests/integration_test.py --- power-profiles-daemon-0.20/tests/integration_test.py 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/tests/integration_test.py 2024-04-03 23:55:02.000000000 +0000 @@ -21,6 +21,7 @@ import os import subprocess +import signal import sys import tempfile import time @@ -99,21 +100,27 @@ os.environ["G_DEBUG"] = "fatal_warnings" # set up a fake system D-BUS - cls.test_bus = Gio.TestDBus.new(Gio.TestDBusFlags.NONE) - cls.test_bus.up() - try: - del os.environ["DBUS_SESSION_BUS_ADDRESS"] - except KeyError: - pass - os.environ["DBUS_SYSTEM_BUS_ADDRESS"] = cls.test_bus.get_bus_address() - + cls.start_system_bus() cls.dbus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) - cls.dbus_con = cls.get_dbus(True) - @classmethod - def tearDownClass(cls): - cls.test_bus.down() - dbusmock.DBusTestCase.tearDownClass() + def start_dbus_template(self, template, parameters): + process, dbus_object = self.spawn_server_template( + template, parameters, stdout=subprocess.PIPE + ) + + def stop_template(): + process.stdout.close() + try: + process.kill() + except OSError: + pass + process.wait() + + self.addCleanup(stop_template) + self.assertTrue(process) + self.assertTrue(dbus_object) + + return process, dbus_object, stop_template def setUp(self): """Set up a local umockdev testbed. @@ -121,16 +128,11 @@ The testbed is initially empty. """ self.testbed = UMockdev.Testbed.new() - self.polkitd, self.obj_polkit = self.spawn_server_template( - "polkitd", {}, stdout=subprocess.PIPE - ) - self.obj_polkit.SetAllowed( - [ - "org.freedesktop.UPower.PowerProfiles.switch-profile", - "org.freedesktop.UPower.PowerProfiles.hold-profile", - ] - ) + def del_testbed(): + del self.testbed + + self.addCleanup(del_testbed) self.proxy = None self.props_proxy = None self.log = None @@ -140,6 +142,14 @@ # Used for dytc devices self.tp_acpi = None + self.polkitd, self.obj_polkit, _ = self.start_dbus_template("polkitd", {}) + self.obj_polkit.SetAllowed( + [ + "org.freedesktop.UPower.PowerProfiles.switch-profile", + "org.freedesktop.UPower.PowerProfiles.hold-profile", + ] + ) + def run(self, result=None): super().run(result) if not result or not self.log: @@ -150,31 +160,11 @@ sys.stderr.write(tmpf.read()) sys.stderr.write("------------------------------\n") - def tearDown(self): - del self.testbed - self.stop_daemon() - - if self.polkitd: - self.polkitd.stdout.close() - try: - self.polkitd.kill() - except OSError: - pass - self.polkitd.wait() - - self.obj_polkit = None - - del self.tp_acpi - try: - os.remove(self.testbed.get_root_dir() + "/" + "ppd_test_conf.ini") - except AttributeError: - pass - # # Daemon control and D-BUS I/O # - def start_daemon(self): + def start_daemon(self, args=None): """Start daemon and create DBus proxy. When done, this sets self.proxy as the Gio.DBusProxy for power-profiles-daemon. @@ -187,7 +177,9 @@ env["UMOCKDEV_DIR"] = self.testbed.get_root_dir() env["LD_PRELOAD"] = os.getenv("PPD_LD_PRELOAD") + " " + os.getenv("LD_PRELOAD") self.log = tempfile.NamedTemporaryFile() # pylint: disable=consider-using-with - daemon_path = [self.daemon_path, "-v"] + daemon_path = [self.daemon_path, "-vv"] + if args: + daemon_path += args if os.getenv("PPD_TEST_WRAPPER"): daemon_path = os.getenv("PPD_TEST_WRAPPER").split(" ") + daemon_path elif os.getenv("VALGRIND"): @@ -195,9 +187,9 @@ # pylint: disable=consider-using-with self.daemon = subprocess.Popen( - daemon_path, env=env, stdout=self.log, stderr=subprocess.STDOUT + daemon_path, env=env, stdout=self.log, stderr=sys.stderr ) - self.addCleanup(self.daemon.kill) + self.addCleanup(self.stop_daemon, delete_profile=True) def on_proxy_connected(_, res): try: @@ -220,10 +212,13 @@ ) # wait until the daemon gets online + wait_time = 20 if "valgrind" in daemon_path[0] else 5 self.assert_eventually( lambda: self.proxy and self.proxy.get_name_owner(), - timeout=10 * 1000, - message="daemon did not start in 10 seconds", + timeout=wait_time * 1000, + message=lambda: f"daemon did not start in {wait_time} seconds: " + + f"proxy is {self.proxy} and owner " + + f"{self.proxy.get_name_owner() if self.proxy else 'None'}", ) def properties_changed_cb(_, changed_properties, invalidated): @@ -250,7 +245,7 @@ None, ) - def stop_daemon(self): + def stop_daemon(self, delete_profile=False): """Stop the daemon if it is running.""" if self.daemon: @@ -260,6 +255,12 @@ pass self.assertEqual(self.daemon.wait(timeout=3000), 0) + if delete_profile: + try: + os.remove(self.testbed.get_root_dir() + "/" + "ppd_test_conf.ini") + except (AttributeError, FileNotFoundError): + pass + self.daemon = None self.proxy = None @@ -315,6 +316,7 @@ attr = "-" if enable: os.chmod(fname, 0o444) + self.addCleanup(self.change_immutable, fname, False) attr = "+" if os.geteuid() == 0: if not GLib.find_program_in_path("chattr"): @@ -332,6 +334,7 @@ ["dytc_lapmode", "0\n"], ["DEVPATH", "/devices/platform/thinkpad_acpi"], ) + self.addCleanup(self.testbed.remove_device, self.tp_acpi) def create_amd_apu(self): proc_dir = os.path.join(self.testbed.get_root_dir(), "proc/") @@ -365,14 +368,38 @@ os.remove(os.path.join(acpi_dir, "platform_profile")) os.removedirs(acpi_dir) - def assert_eventually(self, condition, message=None, timeout=5000): + def powerprofilesctl_path(self): + builddir = os.getenv("top_builddir", ".") + return os.path.join(builddir, "src", "powerprofilesctl") + + def python_coverage_commands(self): + coverage = os.getenv("PPD_PYTHON_COVERAGE") + if not coverage: + return [] + + builddir = os.getenv("top_builddir", ".") + data_file = os.path.join(builddir, "python-coverage", self.id() + ".coverage") + # We also may need to use "--parallel-mode" if running with + # meson test --repeat, but this is not a priority for now. + return [ + coverage, + "run", + f"--data-file={data_file}", + f"--include={builddir}/*", + ] + + def powerprofilesctl_command(self): + return self.python_coverage_commands() + [self.powerprofilesctl_path()] + + def assert_eventually(self, condition, message=None, timeout=5000, keep_checking=0): """Assert that condition function eventually returns True. Timeout is in milliseconds, defaulting to 5000 (5 seconds). message is printed on failure. """ - if condition(): - return + if not keep_checking: + if condition(): + return done = False @@ -384,37 +411,68 @@ while not done: if condition(): GLib.source_remove(source) + if keep_checking > 0: + self.assert_condition_persists( + condition, message, timeout=keep_checking + ) return GLib.MainContext.default().iteration(False) - self.fail(message or "timed out waiting for " + str(condition)) + self.fail(message() if message else f"timed out waiting for {condition}") - def assert_file_eventually_contains(self, path, contents, timeout=800): + def assert_condition_persists(self, condition, message=None, timeout=1000): + done = False + + def on_timeout_reached(): + nonlocal done + done = True + + source = GLib.timeout_add(timeout, on_timeout_reached) + while not done: + if not condition(): + GLib.source_remove(source) + self.fail( + message() if message else f"Condition is not persisting {condition}" + ) + GLib.MainContext.default().iteration(False) + + def assert_file_eventually_contains( + self, path, contents, timeout=800, keep_checking=0 + ): """Asserts that file contents eventually matches expectations""" encoded = contents.encode("utf-8") return self.assert_eventually( lambda: self.read_file_contents(path) == encoded, timeout=timeout, - message=f"file '{path}' does not contain '{contents}', " - + "but '{self.read_file_contents(path)}'", + keep_checking=keep_checking, + message=lambda: f"file '{path}' does not contain '{contents}', " + + f"but '{self.read_file_contents(path)}'", ) - def assert_sysfs_attr_eventually_is(self, device, attribute, contents, timeout=800): + # pylint: disable=too-many-arguments + def assert_sysfs_attr_eventually_is( + self, device, attribute, contents, timeout=800, keep_checking=0 + ): """Asserts that file contents eventually matches expectations""" encoded = contents.encode("utf-8") return self.assert_eventually( lambda: self.read_sysfs_attr(device, attribute) == encoded, timeout=timeout, - message=f"file {device} '{attribute}' does not contain '{contents}', " - + "but '{self.read_sysfs_attr(device, attribute)}'", + keep_checking=keep_checking, + message=lambda: f"file {device} '{attribute}' does not contain '{contents}', " + + f"but '{self.read_sysfs_attr(device, attribute)}'", ) - def assert_dbus_property_eventually_is(self, prop, value, timeout=1200): + def assert_dbus_property_eventually_is( + self, prop, value, timeout=1200, keep_checking=0 + ): """Asserts that a dbus property eventually is what expected""" return self.assert_eventually( lambda: self.get_dbus_property(prop) == value, timeout=timeout, - message=f"property '{prop}' is not '{value}', but '{self.get_dbus_property(prop)}'", + keep_checking=keep_checking, + message=lambda: f"property '{prop}' is not '{value}', but " + + f"'{self.get_dbus_property(prop)}'", ) # @@ -527,7 +585,10 @@ ) # Degraded DYTC - self.testbed.set_attribute(self.tp_acpi, "dytc_lapmode", "1\n") + lapmode = os.path.join( + self.testbed.get_root_dir(), "sys/devices/thinkpad_acpi/dytc_lapmode" + ) + self.write_file_contents(lapmode, "1\n") self.assert_eventually(lambda: self.have_text_in_log("dytc_lapmode is now on")) self.assertEqual( self.get_dbus_property("PerformanceDegraded"), @@ -549,7 +610,10 @@ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance") # Degraded - self.testbed.set_attribute(self.tp_acpi, "dytc_lapmode", "1\n") + lapmode = os.path.join( + self.testbed.get_root_dir(), "sys/devices/thinkpad_acpi/dytc_lapmode" + ) + self.write_file_contents(lapmode, "1\n") self.assert_eventually(lambda: self.have_text_in_log("dytc_lapmode is now on")) self.assertEqual(self.get_dbus_property("PerformanceDegraded"), "lap-detected") self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance") @@ -646,13 +710,10 @@ os.makedirs(pstate_dir) self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n") - upowerd, obj_upower = self.spawn_server_template( + self.start_dbus_template( "upower", {"DaemonVersion": "0.99", "OnBattery": False}, - stdout=subprocess.PIPE, ) - self.assertNotEqual(upowerd, None) - self.assertNotEqual(obj_upower, None) self.start_daemon() @@ -668,11 +729,50 @@ os.path.join(dir1, "energy_performance_preference"), "balance_performance" ) - self.stop_daemon() + def test_intel_pstate_reapply_on_resume_from_sleep(self): + # Create CPU with preference + dir1 = os.path.join( + self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/" + ) + os.makedirs(dir1) + gov_path = os.path.join(dir1, "scaling_governor") + self.write_file_contents(gov_path, "performance\n") + energy_prefs = os.path.join(dir1, "energy_performance_preference") + self.write_file_contents(energy_prefs, "performance\n") + pstate_dir = os.path.join( + self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate" + ) + os.makedirs(pstate_dir) + self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n") + + _, obj_logind, _ = self.start_dbus_template("logind", {}) + + self.start_daemon() + self.assert_dbus_property_eventually_is( + "ActiveProfile", "balanced", keep_checking=100 + ) + + # Simulate system changing to performance mode just before going to suspend + self.write_file_contents(energy_prefs, "performance\n") + self.assert_file_eventually_contains( + energy_prefs, "performance\n", keep_checking=500 + ) + + obj_logind.EmitSignal( + "org.freedesktop.login1.Manager", "PrepareForSleep", "b", [True] + ) + self.assert_file_eventually_contains( + energy_prefs, "performance\n", keep_checking=500 + ) + + # Check that on resume the value is reset to the expected one. + obj_logind.EmitSignal( + "org.freedesktop.login1.Manager", "PrepareForSleep", "b", [False] + ) - upowerd.terminate() - upowerd.wait() - upowerd.stdout.close() + self.assert_file_eventually_contains( + energy_prefs, "balance_performance", timeout=3000, keep_checking=100 + ) def test_intel_pstate_error(self): """Intel P-State driver in error state""" @@ -709,10 +809,6 @@ energy_prefs = os.path.join(dir1, "energy_performance_preference") self.assert_file_eventually_contains(energy_prefs, "balance_performance\n") - self.stop_daemon() - - self.change_immutable(pref_path, False) - def test_intel_pstate_passive(self): """Intel P-State in passive mode -> placeholder""" @@ -751,8 +847,6 @@ energy_prefs = os.path.join(dir1, "energy_performance_preference") self.assert_file_eventually_contains(energy_prefs, "performance\n") - self.stop_daemon() - def test_intel_pstate_passive_with_epb(self): """Intel P-State in passive mode (no HWP) with energy_perf_bias""" @@ -800,8 +894,6 @@ self.assert_file_eventually_contains(energy_perf_bias, "0") - self.stop_daemon() - def test_action_blocklist(self): """Test action blocklist works""" self.testbed.add_device( @@ -814,15 +906,13 @@ self.create_amd_apu() - self.spawn_server_template( + self.start_dbus_template( "upower", {"DaemonVersion": "0.99", "OnBattery": False}, - stdout=subprocess.PIPE, ) # Block panel_power action - os.environ["POWER_PROFILE_DAEMON_ACTION_BLOCK"] = "amdgpu_panel_power" - self.start_daemon() + self.start_daemon(["--block-action", "amdgpu_panel_power"]) self.assertNotIn("amdgpu_panel_power", self.get_dbus_property("Actions")) def test_driver_blocklist(self): @@ -870,10 +960,8 @@ self.write_file_contents(os.path.join(dir3, "pm_profile"), "1\n") # block platform profile - os.environ["POWER_PROFILE_DAEMON_DRIVER_BLOCK"] = "platform_profile" - + self.start_daemon(["--block-driver", "platform_profile"]) # Verify that only amd-pstate is loaded - self.start_daemon() profiles = self.get_dbus_property("Profiles") self.assertEqual(len(profiles), 3) self.assertEqual(profiles[0]["Driver"], "multiple") @@ -883,10 +971,10 @@ self.stop_daemon() # block both drivers - os.environ["POWER_PROFILE_DAEMON_DRIVER_BLOCK"] = "amd_pstate,platform_profile" - + self.start_daemon( + ["--block-driver", "amd_pstate", "--block-driver", "platform_profile"] + ) # Verify that only placeholder is loaded - self.start_daemon() profiles = self.get_dbus_property("Profiles") self.assertEqual(len(profiles), 2) self.assertEqual(profiles[0]["PlatformDriver"], "placeholder") @@ -984,9 +1072,6 @@ ), b"balance_performance", ) - self.change_immutable(profile, False) - - self.stop_daemon() # pylint: disable=too-many-statements def test_amd_pstate(self): @@ -1051,8 +1136,6 @@ self.assert_file_eventually_contains(energy_prefs, "power") self.assert_file_eventually_contains(scaling_governor, "powersave") - self.stop_daemon() - def test_amd_pstate_balance(self): """AMD P-State driver (balance)""" @@ -1077,13 +1160,10 @@ os.makedirs(dir2) self.write_file_contents(os.path.join(dir2, "pm_profile"), "1\n") - upowerd, obj_upower = self.spawn_server_template( + self.start_dbus_template( "upower", {"DaemonVersion": "0.99", "OnBattery": False}, - stdout=subprocess.PIPE, ) - self.assertTrue(upowerd) - self.assertTrue(obj_upower) self.start_daemon() @@ -1100,12 +1180,6 @@ scaling_governor = os.path.join(dir1, "scaling_governor") self.assert_file_eventually_contains(scaling_governor, "powersave") - self.stop_daemon() - - upowerd.terminate() - upowerd.wait() - upowerd.stdout.close() - def test_amd_pstate_error(self): """AMD P-State driver in error state""" @@ -1146,10 +1220,6 @@ energy_prefs = os.path.join(dir1, "energy_performance_preference") self.assert_file_eventually_contains(energy_prefs, "balance_performance\n") - self.stop_daemon() - - self.change_immutable(pref_path, False) - def test_amd_pstate_passive(self): """AMD P-State in passive mode -> placeholder""" @@ -1191,8 +1261,6 @@ self.assert_file_eventually_contains(energy_prefs, "performance\n") - self.stop_daemon() - def test_amd_pstate_server(self): # Create 2 CPUs with preferences dir1 = os.path.join( @@ -1231,8 +1299,6 @@ with self.assertRaises(KeyError): print(profiles[0]["CpuDriver"]) - self.stop_daemon() - def test_dytc_performance_driver(self): """Lenovo DYTC performance driver""" @@ -1251,12 +1317,15 @@ self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance") # lapmode detected - self.testbed.set_attribute(self.tp_acpi, "dytc_lapmode", "1\n") + lapmode = os.path.join( + self.testbed.get_root_dir(), "sys/devices/thinkpad_acpi/dytc_lapmode" + ) + self.write_file_contents(lapmode, "1\n") self.assert_dbus_property_eventually_is("PerformanceDegraded", "lap-detected") self.assertEqual(self.get_dbus_property("ActiveProfile"), "performance") # Reset lapmode - self.testbed.set_attribute(self.tp_acpi, "dytc_lapmode", "0\n") + self.write_file_contents(lapmode, "0\n") self.assert_dbus_property_eventually_is("PerformanceDegraded", "") # Performance mode didn't change @@ -1291,6 +1360,99 @@ profiles = self.get_dbus_property("Profiles") self.assertEqual(len(profiles), 2) + def test_amd_pstate_upower(self): + """Switching between balance_power and balance_performance based on battery""" + # Create 2 CPUs with preferences + dir1 = os.path.join( + self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/" + ) + os.makedirs(dir1) + self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n") + self.write_file_contents( + os.path.join(dir1, "energy_performance_preference"), "performance\n" + ) + dir2 = os.path.join( + self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy1/" + ) + os.makedirs(dir2) + self.write_file_contents(os.path.join(dir2, "scaling_governor"), "powersave\n") + self.write_file_contents( + os.path.join(dir2, "energy_performance_preference"), "performance\n" + ) + + # Create AMD P-State configuration + pstate_dir = os.path.join( + self.testbed.get_root_dir(), "sys/devices/system/cpu/amd_pstate" + ) + os.makedirs(pstate_dir) + self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n") + + # desktop PM profile + dir3 = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/") + os.makedirs(dir3) + self.write_file_contents(os.path.join(dir3, "pm_profile"), "1\n") + + _, _, stop_upowerd = self.start_dbus_template( + "upower", + {"DaemonVersion": "0.99", "OnBattery": True}, + ) + + self.start_daemon() + + profiles = self.get_dbus_property("Profiles") + self.assertEqual(len(profiles), 3) + + self.assertEqual(profiles[0]["Driver"], "multiple") + self.assertEqual(profiles[0]["CpuDriver"], "amd_pstate") + self.assertEqual(profiles[0]["Profile"], "power-saver") + + energy_prefs = os.path.join(dir2, "energy_performance_preference") + scaling_governor = os.path.join(dir2, "scaling_governor") + + self.assert_file_eventually_contains(energy_prefs, "balance_power") + self.assert_file_eventually_contains(scaling_governor, "powersave") + + stop_upowerd() + + self.assert_file_eventually_contains(energy_prefs, "balance_performance") + + _, upowerd_obj, stop_upowerd = self.start_dbus_template( + "upower", + {"DaemonVersion": "0.99", "OnBattery": False}, + ) + + self.assert_file_eventually_contains(energy_prefs, "balance_performance") + + upowerd_obj.Set("org.freedesktop.UPower", "OnBattery", True) + self.assert_file_eventually_contains(energy_prefs, "balance_power") + + # Ensure that changing some other property doesn't change the state. + upowerd_obj.Set("org.freedesktop.UPower", "LidIsClosed", True) + self.assert_file_eventually_contains( + energy_prefs, "balance_power", keep_checking=800 + ) + + upowerd_obj.Set("org.freedesktop.UPower", "OnBattery", False) + self.assert_file_eventually_contains(energy_prefs, "balance_performance") + + self.stop_daemon() + + # start upower after the daemon + stop_upowerd() + + self.start_daemon() + + self.assert_file_eventually_contains(energy_prefs, "balance_performance") + + _, upowerd_obj, _ = self.start_dbus_template( + "upower", + {"DaemonVersion": "0.99", "OnBattery": False}, + ) + self.assert_file_eventually_contains(energy_prefs, "balance_performance") + + upowerd_obj.Set("org.freedesktop.UPower", "OnBattery", True) + self.assert_file_eventually_contains(energy_prefs, "balance_power") + def test_amdgpu_panel_power(self): """Verify AMDGPU Panel power actions""" amdgpu_panel_power_savings = "amdgpu/panel_power_savings" @@ -1313,10 +1475,9 @@ # start upower and try again self.stop_daemon() - self.spawn_server_template( + self.start_dbus_template( "upower", {"DaemonVersion": "0.99", "OnBattery": True}, - stdout=subprocess.PIPE, ) self.start_daemon() @@ -1452,8 +1613,6 @@ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced") self.assertEqual(self.get_dbus_property("PerformanceDegraded"), "") - self.stop_daemon() - def test_hp_wmi(self): # Uses cool instead of low-power acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/") @@ -1486,8 +1645,6 @@ self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b"balanced" ) - self.stop_daemon() - def test_quiet(self): # Uses quiet instead of low-power acpi_dir = os.path.join(self.testbed.get_root_dir(), "sys/firmware/acpi/") @@ -1514,8 +1671,6 @@ self.read_sysfs_file("sys/firmware/acpi/platform_profile"), b"quiet" ) - self.stop_daemon() - def test_hold_release_profile(self): self.create_platform_profile() self.start_daemon() @@ -1543,9 +1698,9 @@ released_cookie = None - def signal_cb(_, sender, signal, params): + def signal_cb(_, sender, signal_name, params): nonlocal released_cookie - if signal == "ProfileReleased": + if signal_name == "ProfileReleased": released_cookie = params self.addCleanup( @@ -1621,17 +1776,97 @@ ) self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver") - self.stop_daemon() + def test_launch_arguments_redirection(self): + self.create_platform_profile() + self.start_daemon() + self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile")) - def test_vanishing_hold(self): + with tempfile.NamedTemporaryFile(mode="+rt") as tmpf: + subprocess.check_call( + self.powerprofilesctl_command() + + [ + "launch", + "sh", + "-c", + f'echo "$@" > {tmpf.name}', + "--", + "--foo", + "--bar", + "-v", + "arg", + ] + ) + self.assertEqual(tmpf.readlines(), ["--foo --bar -v arg\n"]) + + def test_unknown_action(self): self.create_platform_profile() self.start_daemon() + self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile")) + + with self.assertRaises(subprocess.CalledProcessError): + tool_cmd = self.powerprofilesctl_command() + subprocess.check_output( + tool_cmd + ["hopefully-invalid-action"], stderr=subprocess.PIPE + ) + + def test_unknown_list_argument(self): + self.create_platform_profile() + self.start_daemon() + self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile")) + + with self.assertRaises(subprocess.CalledProcessError): + subprocess.check_output( + self.powerprofilesctl_command() + ["list", "--invalid-argument"], + stderr=subprocess.PIPE, + ) + + def test_launch_arguments_invalid(self): + self.create_platform_profile() + self.start_daemon() + self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile")) + + with self.assertRaises(subprocess.CalledProcessError): + tool_cmd = self.powerprofilesctl_command() + subprocess.check_output( + tool_cmd + ["--foo-arg", "launch", "true"], stderr=subprocess.PIPE + ) + + def test_launch_with_command_failure(self): + self.create_platform_profile() + self.start_daemon() + self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile")) + + tool_cmd = self.powerprofilesctl_command() + cmd = subprocess.run(tool_cmd + ["launch", "false"], check=False) + self.assertEqual(cmd.returncode, 1) - sourcedir = os.getenv("top_srcdir", ".") - tool_path = os.path.join(sourcedir, "src", "powerprofilesctl") + cmd = subprocess.run(tool_cmd + ["launch", "sh", "-c", "exit 55"], check=False) + self.assertEqual(cmd.returncode, 55) + def test_launch_with_command_signaled(self): + self.create_platform_profile() + self.start_daemon() + self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile")) + + tool_cmd = self.powerprofilesctl_command() + cmd = subprocess.run( + tool_cmd + ["launch", "sh", "-c", f"kill -{signal.SIGKILL} $$"], check=False + ) + self.assertEqual(cmd.returncode, -signal.SIGKILL) + + cmd = subprocess.run( + tool_cmd + ["launch", "sh", "-c", f"kill -{signal.SIGINT} $$"], check=False + ) + self.assertEqual(cmd.returncode, -signal.SIGINT) + + def test_vanishing_hold(self): + self.create_platform_profile() + self.start_daemon() + self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile")) + + tool_cmd = self.powerprofilesctl_command() with subprocess.Popen( - [tool_path, "launch", "-p", "power-saver", "sleep", "3600"], + tool_cmd + ["launch", "-p", "power-saver", "sleep", "3600"], stdout=sys.stdout, stderr=sys.stderr, ) as launch_process: @@ -1644,12 +1879,37 @@ # Make sure to handle vanishing clients launch_process.terminate() - self.assertEqual(launch_process.wait(), 0) + retcode = launch_process.wait() + self.assertEqual(retcode, -signal.SIGTERM) holds = self.get_dbus_property("ActiveProfileHolds") self.assertEqual(len(holds), 0) - self.stop_daemon() + def test_launch_sigint_wrapper(self): + self.create_platform_profile() + self.start_daemon() + self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile")) + + with subprocess.Popen( + self.powerprofilesctl_command() + ["launch", "sleep", "3600"], + ) as launch_process: + time.sleep(1) + launch_process.send_signal(signal.SIGINT) + retcode = launch_process.wait() + self.assertEqual(retcode, -signal.SIGINT) + + def test_launch_sigabrt_wrapper(self): + self.create_platform_profile() + self.start_daemon() + self.assert_eventually(lambda: self.get_dbus_property("ActiveProfile")) + + with subprocess.Popen( + self.powerprofilesctl_command() + ["launch", "sleep", "3600"], + ) as launch_process: + time.sleep(1) + launch_process.send_signal(signal.SIGABRT) + retcode = launch_process.wait() + self.assertEqual(retcode, -signal.SIGABRT) def test_hold_priority(self): """power-saver should take priority over performance""" @@ -1717,8 +1977,6 @@ self.call_dbus_method("ReleaseProfile", GLib.Variant("(u)", powersaver_cookie)) self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced") - self.stop_daemon() - def test_save_profile(self): """save profile across runs""" @@ -1746,7 +2004,6 @@ self.start_daemon() self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver") - self.stop_daemon() def test_save_deferred_load(self): """save profile across runs, but kernel driver loaded after start""" @@ -1774,7 +2031,6 @@ ) self.assert_dbus_property_eventually_is("ActiveProfile", "power-saver") - self.stop_daemon() def test_not_allowed_profile(self): """Check that we get errors when trying to change a profile and not allowed""" @@ -1801,8 +2057,6 @@ ) self.assertIn("AccessDenied", str(error.exception)) - self.stop_daemon() - def test_not_allowed_hold(self): """Check that we get an error when trying to hold a profile and not allowed""" @@ -1819,13 +2073,83 @@ self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced") self.assertEqual(len(self.get_dbus_property("ActiveProfileHolds")), 0) - self.stop_daemon() - def test_get_version_prop(self): """Checks that the version property is advertised""" self.start_daemon() self.assertTrue(self.get_dbus_property("Version")) + def test_intel_pstate_upower(self): + # Create CPU with preference + dir1 = os.path.join( + self.testbed.get_root_dir(), "sys/devices/system/cpu/cpufreq/policy0/" + ) + os.makedirs(dir1) + self.write_file_contents(os.path.join(dir1, "scaling_governor"), "powersave\n") + self.write_file_contents( + os.path.join(dir1, "energy_performance_preference"), "performance\n" + ) + + # Create Intel P-State configuration + pstate_dir = os.path.join( + self.testbed.get_root_dir(), "sys/devices/system/cpu/intel_pstate" + ) + os.makedirs(pstate_dir) + self.write_file_contents(os.path.join(pstate_dir, "no_turbo"), "1\n") + self.write_file_contents(os.path.join(pstate_dir, "turbo_pct"), "0\n") + self.write_file_contents(os.path.join(pstate_dir, "status"), "active\n") + + _, _, stop_upowerd = self.start_dbus_template( + "upower", + {"DaemonVersion": "0.99", "OnBattery": True}, + ) + + self.start_daemon() + + profiles = self.get_dbus_property("Profiles") + self.assertEqual(len(profiles), 3) + self.assertEqual(self.get_dbus_property("PerformanceDegraded"), "") + + energy_prefs = os.path.join(dir1, "energy_performance_preference") + scaling_governor = os.path.join(dir1, "scaling_governor") + + self.assert_file_eventually_contains(energy_prefs, "balance_power") + self.assert_file_eventually_contains(scaling_governor, "powersave") + + stop_upowerd() + + self.assert_file_eventually_contains(energy_prefs, "balance_performance") + + _, upowerd_obj, stop_upowerd = self.start_dbus_template( + "upower", + {"DaemonVersion": "0.99", "OnBattery": False}, + ) + + self.assert_file_eventually_contains(energy_prefs, "balance_performance") + + upowerd_obj.Set("org.freedesktop.UPower", "OnBattery", True) + self.assert_file_eventually_contains(energy_prefs, "balance_power") + + upowerd_obj.Set("org.freedesktop.UPower", "OnBattery", False) + self.assert_file_eventually_contains(energy_prefs, "balance_performance") + + self.stop_daemon() + + # start upower after the daemon + stop_upowerd() + + self.start_daemon() + + self.assert_file_eventually_contains(energy_prefs, "balance_performance") + + _, upowerd_obj, _ = self.start_dbus_template( + "upower", + {"DaemonVersion": "0.99", "OnBattery": False}, + ) + self.assert_file_eventually_contains(energy_prefs, "balance_performance") + + upowerd_obj.Set("org.freedesktop.UPower", "OnBattery", True) + self.assert_file_eventually_contains(energy_prefs, "balance_power") + def test_intel_pstate_noturbo(self): """Intel P-State driver (balance)""" @@ -1854,17 +2178,12 @@ self.assertEqual(len(profiles), 3) self.assertEqual(self.get_dbus_property("PerformanceDegraded"), "") - self.stop_daemon() - def test_powerprofilesctl_version_command(self): """Check powerprofilesctl version command works""" self.start_daemon() - sourcedir = os.getenv("top_srcdir", ".") - tool_path = os.path.join(sourcedir, "src", "powerprofilesctl") - - cmd = subprocess.run([tool_path, "version"], check=True) + cmd = subprocess.run(self.powerprofilesctl_command() + ["version"], check=True) self.assertEqual(cmd.returncode, 0) def test_powerprofilesctl_list_command(self): @@ -1872,10 +2191,8 @@ self.start_daemon() - sourcedir = os.getenv("top_srcdir", ".") - tool_path = os.path.join(sourcedir, "src", "powerprofilesctl") - - cmd = subprocess.run([tool_path, "list"], capture_output=True, check=True) + tool_cmd = self.powerprofilesctl_command() + cmd = subprocess.run(tool_cmd + ["list"], capture_output=True, check=True) self.assertEqual(cmd.returncode, 0) self.assertIn("* balanced", cmd.stdout.decode("utf-8")) @@ -1884,47 +2201,43 @@ self.start_daemon() - sourcedir = os.getenv("top_srcdir", ".") - tool_path = os.path.join(sourcedir, "src", "powerprofilesctl") - self.assertEqual(self.get_dbus_property("ActiveProfile"), "balanced") - cmd = subprocess.run([tool_path, "get"], capture_output=True, check=True) + tool_cmd = self.powerprofilesctl_command() + cmd = subprocess.run(tool_cmd + ["get"], capture_output=True, check=True) self.assertEqual(cmd.returncode, 0) self.assertEqual(cmd.stdout, b"balanced\n") cmd = subprocess.run( - [tool_path, "set", "power-saver"], capture_output=True, check=True + tool_cmd + ["set", "power-saver"], capture_output=True, check=True ) self.assertEqual(cmd.returncode, 0) self.assertEqual(self.get_dbus_property("ActiveProfile"), "power-saver") - cmd = subprocess.run([tool_path, "get"], capture_output=True, check=True) + cmd = subprocess.run(tool_cmd + ["get"], capture_output=True, check=True) self.assertEqual(cmd.returncode, 0) self.assertEqual(cmd.stdout, b"power-saver\n") def test_powerprofilesctl_error(self): """Check that powerprofilesctl returns 1 rather than an exception on error""" - sourcedir = os.getenv("top_srcdir", ".") - tool_path = os.path.join(sourcedir, "src", "powerprofilesctl") - + tool_cmd = self.powerprofilesctl_command() with self.assertRaises(subprocess.CalledProcessError) as error: subprocess.check_output( - [tool_path, "list"], stderr=subprocess.PIPE, universal_newlines=True + tool_cmd + ["list"], stderr=subprocess.PIPE, universal_newlines=True ) self.assertNotIn("Traceback", error.exception.stderr) with self.assertRaises(subprocess.CalledProcessError) as error: subprocess.check_output( - [tool_path, "get"], stderr=subprocess.PIPE, universal_newlines=True + tool_cmd + ["get"], stderr=subprocess.PIPE, universal_newlines=True ) self.assertNotIn("Traceback", error.exception.stderr) with self.assertRaises(subprocess.CalledProcessError) as error: subprocess.check_output( - [tool_path, "set", "not-a-profile"], + tool_cmd + ["set", "not-a-profile"], stderr=subprocess.PIPE, universal_newlines=True, ) @@ -1932,7 +2245,7 @@ with self.assertRaises(subprocess.CalledProcessError) as error: subprocess.check_output( - [tool_path, "list-holds"], + tool_cmd + ["list-holds"], stderr=subprocess.PIPE, universal_newlines=True, ) @@ -1940,7 +2253,7 @@ with self.assertRaises(subprocess.CalledProcessError) as error: subprocess.check_output( - [tool_path, "launch", "-p", "power-saver", "sleep", "1"], + tool_cmd + ["launch", "-p", "power-saver", "sleep", "1"], stderr=subprocess.PIPE, universal_newlines=True, ) @@ -1949,12 +2262,11 @@ self.start_daemon() with self.assertRaises(subprocess.CalledProcessError) as error: subprocess.check_output( - [tool_path, "set", "not-a-profile"], + tool_cmd + ["set", "not-a-profile"], stderr=subprocess.PIPE, universal_newlines=True, ) self.assertNotIn("Traceback", error.exception.stderr) - self.stop_daemon() # # Helper methods diff -Nru power-profiles-daemon-0.20/tests/meson.build power-profiles-daemon-0.21/tests/meson.build --- power-profiles-daemon-0.20/tests/meson.build 2024-02-14 23:36:59.000000000 +0000 +++ power-profiles-daemon-0.21/tests/meson.build 2024-04-03 23:55:02.000000000 +0000 @@ -1,7 +1,6 @@ envs = environment() envs.set('PPD_TEST_VERBOSE', 'true') envs.set ('top_builddir', meson.project_build_root()) -envs.set ('top_srcdir', meson.project_source_root()) unittest_inspector = find_program('unittest_inspector.py') integration_tests = files('integration_test.py') @@ -65,6 +64,16 @@ envs.set('PPD_LD_PRELOAD', ' '.join(ppd_tests_ld_preload)) +coverage_args = [] +python3_coverage = find_program([ + 'python3-coverage', + 'coverage3', + 'coverage', + ], required: false) +if python3_coverage.found() + envs.set('PPD_PYTHON_COVERAGE', python3_coverage.full_path()) +endif + foreach ut: unit_tests test(ut, python3, @@ -76,7 +85,7 @@ ) endforeach -if get_option('pylint') +if pylint.found() integration_pylint_flags = ['-d', 'W0511', '-d', 'C0302'] + pylint_flags test('pylint-integration-tests', pylint,