From 5a4e7411c722b8c96280f7a04152744d4a529e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 20 Nov 2025 19:50:33 +0100 Subject: [PATCH] Use gnome-shell interface for brightness control Related: https://issues.redhat.com/browse/RHEL-124242 --- gnome-settings-daemon.spec | 5 +- use-shell-brightness.patch | 3651 ++++++++++++++++++++++++++++++++++++ 2 files changed, 3654 insertions(+), 2 deletions(-) create mode 100644 use-shell-brightness.patch diff --git a/gnome-settings-daemon.spec b/gnome-settings-daemon.spec index 6ab02c8..c3e47ed 100644 --- a/gnome-settings-daemon.spec +++ b/gnome-settings-daemon.spec @@ -37,6 +37,9 @@ Patch1: subscription-manager.patch # https://issues.redhat.com/browse/RHEL-127784 Patch2: subman-delay-startup-notification-by-30min.patch +# https://issues.redhat.com/browse/RHEL-124242 +Patch3: use-shell-brightness.patch + BuildRequires: gcc BuildRequires: gettext BuildRequires: meson >= 0.49.0 @@ -145,8 +148,6 @@ cp %{SOURCE1} $RPM_BUILD_ROOT%{_datadir}/glib-2.0/schemas %{_sysconfdir}/xdg/autostart/org.gnome.SettingsDaemon.MediaKeys.desktop %{_datadir}/glib-2.0/schemas/org.gnome.settings-daemon.plugins.media-keys.gschema.xml -%{_libexecdir}/gsd-backlight-helper -%{_datadir}/polkit-1/actions/org.gnome.settings-daemon.plugins.power.policy %{_libexecdir}/gsd-power %{_sysconfdir}/xdg/autostart/org.gnome.SettingsDaemon.Power.desktop %{_datadir}/glib-2.0/schemas/org.gnome.settings-daemon.plugins.power.gschema.xml diff --git a/use-shell-brightness.patch b/use-shell-brightness.patch new file mode 100644 index 0000000..468da6a --- /dev/null +++ b/use-shell-brightness.patch @@ -0,0 +1,3651 @@ +From 1b3dcc365e167c031c0ac0eef7527319c530d8b8 Mon Sep 17 00:00:00 2001 +From: Sebastian Wick +Date: Thu, 5 Jun 2025 00:57:03 +0200 +Subject: [PATCH 01/10] power: Mock mutter with a custom dbusmock template + +The mutter we spawn for the tests is only used for Xsettings which is +hard to mock. The rest of the APIs we need from mutter are available as +dbus interfaces that we can mock. + +For now there isn't a lot of benefit, but in the next commit we will +start to rely on the mutter backlight API and having it mocked means we +don't have to figure out how to get backlights on a headless mutter +instance. + +Part-of: +--- + meson.build | 3 +- + plugins/power/dbusmock-templates/mutter.py | 138 +++++++++++++++++++++ + plugins/power/meson.build | 1 + + plugins/power/test.py | 12 ++ + 4 files changed, 153 insertions(+), 1 deletion(-) + create mode 100644 plugins/power/dbusmock-templates/mutter.py + +diff --git a/meson.build b/meson.build +index a1d58a62..989f580f 100644 +--- a/meson.build ++++ b/meson.build +@@ -2,7 +2,7 @@ project( + 'gnome-settings-daemon', 'c', + version: '47.2', + license: [ 'GPL2+', 'LGPLv2+' ], +- meson_version: '>= 0.57.0' ++ meson_version: '>= 0.64.0' + ) + + # Make sure the version always has a trailing N.0 so that +@@ -252,6 +252,7 @@ endif + gnome = import('gnome') + i18n = import('i18n') + pkg = import('pkgconfig') ++fs = import('fs') + + po_dir = join_paths(meson.project_source_root(), 'po') + +diff --git a/plugins/power/dbusmock-templates/mutter.py b/plugins/power/dbusmock-templates/mutter.py +new file mode 100644 +index 00000000..e092fe67 +--- /dev/null ++++ b/plugins/power/dbusmock-templates/mutter.py +@@ -0,0 +1,138 @@ ++'''mutter proxy mock template ++''' ++ ++# This program is free software; you can redistribute it and/or modify it under ++# the terms of the GNU Lesser General Public License as published by the Free ++# Software Foundation; either version 3 of the License, or (at your option) any ++# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text ++# of the license. ++ ++ ++__author__ = 'Sebastian Wick' ++__copyright__ = '(c) 2025 Red Hat Inc.' ++ ++ ++import dbus ++from dbusmock import MOCK_IFACE, mockobject ++ ++ ++BUS_NAME = 'org.gnome.Mutter.DisplayConfig' ++MAIN_OBJ = '/org/gnome/Mutter/DisplayConfig' ++MAIN_IFACE = 'org.gnome.Mutter.DisplayConfig' ++MOCK_IFACE = 'org.gnome.Mutter.DisplayConfig.Mock' ++SYSTEM_BUS = False ++ ++ ++def _wrap_in_dbus_variant(value): ++ dbus_types = [ ++ dbus.types.ByteArray, ++ dbus.types.Int16, ++ dbus.types.ObjectPath, ++ dbus.types.Struct, ++ dbus.types.UInt64, ++ dbus.types.Boolean, ++ dbus.types.Dictionary, ++ dbus.types.Int32, ++ dbus.types.Signature, ++ dbus.types.UInt16, ++ dbus.types.UnixFd, ++ dbus.types.Byte, ++ dbus.types.Double, ++ dbus.types.Int64, ++ dbus.types.String, ++ dbus.types.UInt32, ++ ] ++ if isinstance(value, dbus.String): ++ return dbus.String(str(value), variant_level=1) ++ if isinstance(value, dbus.types.Array) or isinstance(value, dbus.types.Struct): ++ return value ++ if type(value) in dbus_types: ++ return type(value)(value.conjugate(), variant_level=1) ++ if isinstance(value, str): ++ return dbus.String(value, variant_level=1) ++ raise dbus.exceptions.DBusException(f"could not wrap type {type(value)}") ++ ++ ++# this is a fixed up version of the dbusmock method which works for Struct types ++def UpdateProperties(mock, interface, properties): ++ changed_props = {} ++ ++ for name, value in properties.items(): ++ if not interface: ++ interface = mock.interface ++ if name not in mock.props.get(interface, {}): ++ raise dbus.exceptions.DBusException(f"property {name} not found", name=interface + ".NoSuchProperty") ++ ++ mock._set_property(interface, name, value) ++ changed_props[name] = _wrap_in_dbus_variant(value) ++ ++ mock.EmitSignal(dbus.PROPERTIES_IFACE, "PropertiesChanged", "sa{sv}as", [interface, changed_props, []]) ++ ++ ++def load(mock, parameters): ++ mock.serial = 1 ++ mock.backlights = {} ++ ++ mock.AddProperty(MAIN_IFACE, 'Backlight', backlights_to_dbus({})) ++ mock.AddProperty(MAIN_IFACE, 'PowerSaveMode', dbus.Int32(0)) ++ mock.AddProperty(MAIN_IFACE, 'HasExternalMonitor', True) ++ ++ ++def backlights_to_dbus(backlights): ++ out = dbus.Array([], signature='a{sv}') ++ for connector, backlight in backlights.items(): ++ b = dbus.Dictionary(backlight.copy(), signature='sv') ++ b['connector'] = dbus.String(connector) ++ out.append(b) ++ return out ++ ++ ++@dbus.service.method( ++ MAIN_IFACE, ++ in_signature='usi', ++ out_signature='' ++) ++def SetBacklight(self, serial, connector, value): ++ if serial != self.serial: ++ raise dbus.exceptions.DBusException("Invalid backlight serial") ++ ++ if not connector in self.backlights: ++ raise dbus.exceptions.DBusException("Unknown backlight") ++ ++ backlight = self.backlights[connector] ++ ++ if value < backlight['min'] or value > backlight['max']: ++ raise dbus.exceptions.DBusException("Invalid backlight value") ++ ++ backlight['value'] = value ++ ++ UpdateProperties(self, MAIN_IFACE, { ++ 'Backlight': dbus.Struct( ++ (dbus.UInt32(self.serial), backlights_to_dbus(self.backlights)), ++ signature='uaa{sv}', ++ variant_level=1 ++ ), ++ }) ++ ++ ++@dbus.service.method( ++ MOCK_IFACE, ++ in_signature='sbiii', ++ out_signature='', ++) ++def MockSetBacklight(self, connector, active, min, max, value): ++ self.backlights[connector] = { ++ 'active': dbus.Boolean(active), ++ 'min': dbus.Int32(min), ++ 'max': dbus.Int32(max), ++ 'value': dbus.Int32(value), ++ } ++ ++ self.serial += 1 ++ UpdateProperties(self, MAIN_IFACE, { ++ 'Backlight': dbus.Struct( ++ (dbus.UInt32(self.serial), backlights_to_dbus(self.backlights)), ++ signature='uaa{sv}', ++ variant_level=1 ++ ), ++ }) +diff --git a/plugins/power/meson.build b/plugins/power/meson.build +index 187976b8..3416f15d 100644 +--- a/plugins/power/meson.build ++++ b/plugins/power/meson.build +@@ -122,6 +122,7 @@ gsdpowerenums_py = custom_target( + ) + + test_py = find_program('test.py') ++fs.copyfile('dbusmock-templates/mutter.py') + + envs = environment() + #envs.prepend('G_DEBUG', 'fatal-warnings') +diff --git a/plugins/power/test.py b/plugins/power/test.py +index 64d79ad0..e4159280 100755 +--- a/plugins/power/test.py ++++ b/plugins/power/test.py +@@ -13,6 +13,7 @@ import math + import os + import os.path + import signal ++from pathlib import Path + + project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + builddir = os.environ.get('BUILDDIR', os.path.dirname(__file__)) +@@ -97,6 +98,17 @@ class PowerPluginBase(gsdtestcase.GSDTestCase): + self.addCleanup(self.cleanup_testbed) + os.environ['UMOCKDEV_DIR'] = self.testbed.get_root_dir() + ++ # start mock mutter ++ template = ( ++ Path(os.path.realpath(__file__)).parent ++ / "dbusmock-templates" ++ / "mutter.py" ++ ) ++ assert template.exists() ++ assert template.suffix == ".py" ++ (self.mockmutter, self.obj_mockmutter) = self.spawn_server_template(template.absolute().as_posix()) ++ self.addCleanup(self.stop_process, self.mockmutter) ++ + # Create a mock backlight device + # Note that this function creates a different or even no backlight + # device based on the name of the test. +-- +2.51.1 + + +From 1b45d43982f8d6acae1a158c7d252daf613bbe19 Mon Sep 17 00:00:00 2001 +From: Sebastian Wick +Date: Thu, 5 Jun 2025 00:58:46 +0200 +Subject: [PATCH 02/10] power: Only support mutter backlight control + +Mutter has taken over controlling the backlight of monitors entirely. +The other backends in g-s-d are now obsolete and it has to call into +mutter for every backlight change. + +The change here is not meant to be a functional change. + +Part-of: +--- + plugins/power/gsd-backlight-helper.c | 149 ----- + plugins/power/gsd-backlight.c | 611 +++--------------- + plugins/power/meson.build | 42 -- + ...settings-daemon.plugins.power.policy.in.in | 32 - + plugins/power/test-backlight-helper | 5 - + plugins/power/test.py | 150 +---- + 6 files changed, 93 insertions(+), 896 deletions(-) + delete mode 100644 plugins/power/gsd-backlight-helper.c + delete mode 100644 plugins/power/org.gnome.settings-daemon.plugins.power.policy.in.in + delete mode 100755 plugins/power/test-backlight-helper + +diff --git a/plugins/power/gsd-backlight-helper.c b/plugins/power/gsd-backlight-helper.c +deleted file mode 100644 +index f0fbbb59..00000000 +--- a/plugins/power/gsd-backlight-helper.c ++++ /dev/null +@@ -1,149 +0,0 @@ +-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- +- * +- * Copyright (C) 2018 Benjamin Berg +- * +- * Licensed under the GNU General Public License Version 2 +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +-#include "config.h" +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define GSD_BACKLIGHT_HELPER_EXIT_CODE_SUCCESS 0 +-#define GSD_BACKLIGHT_HELPER_EXIT_CODE_FAILED 1 +-#define GSD_BACKLIGHT_HELPER_EXIT_CODE_ARGUMENTS_INVALID 3 +-#define GSD_BACKLIGHT_HELPER_EXIT_CODE_INVALID_USER 4 +- +-#ifndef __linux__ +-#error "gsd-backlight-helper does not work on non-Linux" +-#endif +- +-static void +-usage(int argc, char *argv[]) +-{ +- fprintf (stderr, "Usage: %s device brightness\n", argv[0]); +- fprintf (stderr, " device: The backlight directory starting with \"/sys/class/backlight/\"\n"); +- fprintf (stderr, " brightness: The new brightness to write\n"); +-} +- +-int +-main (int argc, char *argv[]) +-{ +- char tmp[512]; +- char *device = NULL; +- int fd, len, res; +- int uid, euid; +- int brightness; +- int result = GSD_BACKLIGHT_HELPER_EXIT_CODE_FAILED; +- DIR *dp = NULL; +- struct dirent *ep; +- +- /* check calling UID */ +- uid = getuid (); +- euid = geteuid (); +- if (uid != 0 || euid != 0) { +- fprintf (stderr, "This program can only be used by the root user\n"); +- result = GSD_BACKLIGHT_HELPER_EXIT_CODE_INVALID_USER; +- goto done; +- } +- +- if (argc != 3) { +- fprintf (stderr, "Error: Need to be called with exactly two arguments\n"); +- usage (argc, argv); +- result = GSD_BACKLIGHT_HELPER_EXIT_CODE_ARGUMENTS_INVALID; +- goto done; +- } +- +- errno = 0; +- brightness = strtol (argv[2], NULL, 0); +- if (errno) { +- fprintf (stderr, "Error: Invalid brightness argument (%d: %s)\n", errno, strerror (errno)); +- usage (argc, argv); +- goto done; +- } +- +- dp = opendir ("/sys/class/backlight"); +- if (dp == NULL) { +- fprintf (stderr, "Error: Could not open /sys/class/backlight (%d: %s)\n", errno, strerror (errno)); +- result = GSD_BACKLIGHT_HELPER_EXIT_CODE_FAILED; +- goto done; +- } +- +- /* May be NULL if the path cannot be resolved */ +- device = realpath (argv[1], NULL); +- +- while ((ep = readdir (dp))) { +- char *path; +- +- if (ep->d_name[0] == '.') +- continue; +- +- /* Leave room for "/brightness" */ +- snprintf (tmp, sizeof(tmp) - 11, "/sys/class/backlight/%s", ep->d_name); +- path = realpath (tmp, NULL); +- if (path && device && strcmp (path, device) == 0) { +- free (path); +- strcat (tmp, "/brightness"); +- +- fd = open (tmp, O_WRONLY); +- if (fd < 0) { +- fprintf (stderr, "Error: Could not open brightness sysfs file (%d: %s)\n", errno, strerror(errno)); +- result = GSD_BACKLIGHT_HELPER_EXIT_CODE_FAILED; +- goto done; +- } +- +- len = snprintf (tmp, sizeof(tmp), "%d", brightness); +- if ((res = write (fd, tmp, len)) != len) { +- if (res == -1) +- fprintf (stderr, "Error: Writing to file (%d: %s)\n", errno, strerror(errno)); +- else +- fprintf (stderr, "Error: Wrote the wrong length (%d of %d bytes)!\n", res, len); +- +- close (fd); +- result = GSD_BACKLIGHT_HELPER_EXIT_CODE_FAILED; +- goto done; +- } +- close (fd); +- +- result = GSD_BACKLIGHT_HELPER_EXIT_CODE_SUCCESS; +- goto done; +- } else { +- free (path); +- } +- } +- +- result = GSD_BACKLIGHT_HELPER_EXIT_CODE_FAILED; +- fprintf (stderr, "Error: Could not find the specified backlight \"%s\"\n", argv[1]); +- +-done: +- if (device) +- free (device); +- if (dp) +- closedir (dp); +- +- return result; +-} +- +diff --git a/plugins/power/gsd-backlight.c b/plugins/power/gsd-backlight.c +index 70d259a3..a1fa2efb 100644 +--- a/plugins/power/gsd-backlight.c ++++ b/plugins/power/gsd-backlight.c +@@ -27,17 +27,6 @@ + #include "gsd-power-constants.h" + #include "gsd-power-manager.h" + +-#ifdef __linux__ +-#include +-#endif /* __linux__ */ +- +-typedef enum +-{ +- BACKLIGHT_BACKEND_NONE = 0, +- BACKLIGHT_BACKEND_UDEV, +- BACKLIGHT_BACKEND_MUTTER, +-} BacklightBackend; +- + struct _GsdBacklight + { + GObject object; +@@ -51,19 +40,7 @@ struct _GsdBacklight + uint32_t backlight_serial; + char *backlight_connector; + +- BacklightBackend backend; +- +-#ifdef __linux__ +- GDBusProxy *logind_proxy; +- +- GUdevClient *udev; +- GUdevDevice *udev_device; +- +- GTask *active_task; +- GQueue tasks; +- +- gint idle_update; +-#endif /* __linux__ */ ++ gboolean initialized; + + gboolean builtin_display_disabled; + }; +@@ -73,10 +50,6 @@ enum { + PROP_LAST, + }; + +-#define SYSTEMD_DBUS_NAME "org.freedesktop.login1" +-#define SYSTEMD_DBUS_PATH "/org/freedesktop/login1/session/auto" +-#define SYSTEMD_DBUS_INTERFACE "org.freedesktop.login1.Session" +- + #define MUTTER_DBUS_NAME "org.gnome.Mutter.DisplayConfig" + #define MUTTER_DBUS_PATH "/org/gnome/Mutter/DisplayConfig" + #define MUTTER_DBUS_INTERFACE "org.gnome.Mutter.DisplayConfig" +@@ -88,342 +61,9 @@ static gboolean gsd_backlight_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error); + +- + G_DEFINE_TYPE_EXTENDED (GsdBacklight, gsd_backlight, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, +- gsd_backlight_initable_iface_init);) +- +-#ifdef __linux__ +-static GUdevDevice* +-gsd_backlight_udev_get_type (GList *devices, const gchar *type) +-{ +- const gchar *type_tmp; +- GList *d; +- +- for (d = devices; d != NULL; d = d->next) { +- type_tmp = g_udev_device_get_sysfs_attr (d->data, "type"); +- if (g_strcmp0 (type_tmp, type) == 0) +- return G_UDEV_DEVICE (g_object_ref (d->data)); +- } +- return NULL; +-} +- +-/* +- * Search for a raw backlight interface, raw backlight interfaces registered +- * by the drm driver will have the drm-connector as their parent, check the +- * drm-connector's enabled sysfs attribute so that we pick the right LCD-panel +- * connector on laptops with hybrid-gfx. Fall back to just picking the first +- * raw backlight interface if no enabled interface is found. +- */ +-static GUdevDevice* +-gsd_backlight_udev_get_raw (GList *devices) +-{ +- GUdevDevice *parent; +- const gchar *attr; +- GList *d; +- +- for (d = devices; d != NULL; d = d->next) { +- attr = g_udev_device_get_sysfs_attr (d->data, "type"); +- if (g_strcmp0 (attr, "raw") != 0) +- continue; +- +- parent = g_udev_device_get_parent (d->data); +- if (!parent) +- continue; +- +- attr = g_udev_device_get_sysfs_attr (parent, "enabled"); +- if (!attr || g_strcmp0 (attr, "enabled") != 0) +- continue; +- +- return G_UDEV_DEVICE (g_object_ref (d->data)); +- } +- +- return gsd_backlight_udev_get_type (devices, "raw"); +-} +- +-static void +-gsd_backlight_udev_resolve (GsdBacklight *backlight) +-{ +- g_autolist(GUdevDevice) devices = NULL; +- +- g_assert (backlight->udev != NULL); +- +- devices = g_udev_client_query_by_subsystem (backlight->udev, "backlight"); +- if (devices == NULL) +- return; +- +- /* Search the backlight devices and prefer the types: +- * firmware -> platform -> raw */ +- backlight->udev_device = gsd_backlight_udev_get_type (devices, "firmware"); +- if (backlight->udev_device != NULL) +- return; +- +- backlight->udev_device = gsd_backlight_udev_get_type (devices, "platform"); +- if (backlight->udev_device != NULL) +- return; +- +- backlight->udev_device = gsd_backlight_udev_get_raw (devices); +- if (backlight->udev_device != NULL) +- return; +-} +- +-static gboolean +-gsd_backlight_udev_idle_update_cb (GsdBacklight *backlight) +-{ +- g_autoptr(GError) error = NULL; +- gint brightness; +- g_autofree gchar *path = NULL; +- g_autofree gchar *contents = NULL; +- backlight->idle_update = 0; +- +- /* If we are active again now, just stop. */ +- if (backlight->active_task) +- return FALSE; +- +- path = g_build_filename (g_udev_device_get_sysfs_path (backlight->udev_device), "brightness", NULL); +- if (!g_file_get_contents (path, &contents, NULL, &error)) { +- g_warning ("Could not get brightness from sysfs: %s", error->message); +- return FALSE; +- } +- brightness = g_ascii_strtoll (contents, NULL, 0); +- +- /* e.g. brightness lower than our minimum. */ +- brightness = CLAMP (brightness, backlight->brightness_min, backlight->brightness_max); +- +- /* Only notify if brightness has changed. */ +- if (brightness == backlight->brightness_val) +- return FALSE; +- +- backlight->brightness_val = brightness; +- backlight->brightness_target = brightness; +- g_object_notify_by_pspec (G_OBJECT (backlight), props[PROP_BRIGHTNESS]); +- +- return FALSE; +-} +- +-static void +-gsd_backlight_udev_idle_update (GsdBacklight *backlight) +-{ +- if (backlight->idle_update) +- return; +- +- backlight->idle_update = g_idle_add ((GSourceFunc) gsd_backlight_udev_idle_update_cb, backlight); +-} +- +- +-static void +-gsd_backlight_udev_uevent (GUdevClient *client, const gchar *action, GUdevDevice *device, gpointer user_data) +-{ +- GsdBacklight *backlight = GSD_BACKLIGHT (user_data); +- +- if (g_strcmp0 (action, "change") != 0) +- return; +- +- /* We are going to update our state after processing the tasks anyway. */ +- if (!g_queue_is_empty (&backlight->tasks)) +- return; +- +- if (g_strcmp0 (g_udev_device_get_sysfs_path (device), +- g_udev_device_get_sysfs_path (backlight->udev_device)) != 0) +- return; +- +- g_debug ("GsdBacklight: Got uevent"); +- +- gsd_backlight_udev_idle_update (backlight); +-} +- +- +-static gboolean +-gsd_backlight_udev_init (GsdBacklight *backlight) +-{ +- const gchar* const subsystems[] = {"backlight", NULL}; +- gint brightness_val; +- +- backlight->udev = g_udev_client_new (subsystems); +- gsd_backlight_udev_resolve (backlight); +- if (backlight->udev_device == NULL) +- return FALSE; +- +- backlight->brightness_max = g_udev_device_get_sysfs_attr_as_int (backlight->udev_device, +- "max_brightness"); +- backlight->brightness_min = MAX (1, backlight->brightness_max * 0.01); +- +- /* If the interface has less than 100 possible values, and it is of type +- * raw, then assume that 0 does not turn off the backlight completely. */ +- if (backlight->brightness_max < 99 && +- g_strcmp0 (g_udev_device_get_sysfs_attr (backlight->udev_device, "type"), "raw") == 0) +- backlight->brightness_min = 0; +- +- /* Ignore a backlight which has no steps. */ +- if (backlight->brightness_min >= backlight->brightness_max) { +- g_warning ("Resolved kernel backlight has an unusable maximum brightness (%d)", backlight->brightness_max); +- g_clear_object (&backlight->udev_device); +- return FALSE; +- } +- +- brightness_val = g_udev_device_get_sysfs_attr_as_int (backlight->udev_device, +- "brightness"); +- backlight->brightness_val = CLAMP (brightness_val, +- backlight->brightness_min, +- backlight->brightness_max); +- g_debug ("Using udev device with brightness from %i to %i. Current brightness is %i.", +- backlight->brightness_min, backlight->brightness_max, backlight->brightness_val); +- +- g_signal_connect_object (backlight->udev, "uevent", +- G_CALLBACK (gsd_backlight_udev_uevent), +- backlight, 0); +- +- backlight->backend = BACKLIGHT_BACKEND_UDEV; +- +- return TRUE; +-} +- +- +-typedef struct { +- int value; +- char *value_str; +-} BacklightHelperData; +- +-static void gsd_backlight_process_taskqueue (GsdBacklight *backlight); +- +-static void +-backlight_task_data_destroy (gpointer data) +-{ +- BacklightHelperData *task_data = (BacklightHelperData*) data; +- +- g_free (task_data->value_str); +- g_free (task_data); +-} +- +-static void +-gsd_backlight_set_helper_return (GsdBacklight *backlight, GTask *task, gint result, const GError *error) +-{ +- GTask *finished_task; +- gint percent = ABS_TO_PERCENTAGE (backlight->brightness_min, backlight->brightness_max, result); +- +- if (error) +- g_warning ("Error executing backlight helper: %s", error->message); +- +- /* If the queue will be empty then update the current value. */ +- if (task == g_queue_peek_tail (&backlight->tasks)) { +- if (error == NULL) { +- g_assert (backlight->brightness_target == result); +- +- backlight->brightness_val = backlight->brightness_target; +- g_debug ("New brightness value is in effect %i (%i..%i)", +- backlight->brightness_val, backlight->brightness_min, backlight->brightness_max); +- g_object_notify_by_pspec (G_OBJECT (backlight), props[PROP_BRIGHTNESS]); +- } +- +- /* The udev handler won't read while a write is pending, so queue an +- * update in case we have missed some events. */ +- gsd_backlight_udev_idle_update (backlight); +- } +- +- /* Return all the pending tasks up and including the one we actually +- * processed. */ +- do { +- finished_task = g_queue_pop_head (&backlight->tasks); +- +- if (error) +- g_task_return_error (finished_task, g_error_copy (error)); +- else +- g_task_return_int (finished_task, percent); +- +- g_object_unref (finished_task); +- } while (finished_task != task); +-} +- +-static void +-gsd_backlight_set_helper_finish (GObject *obj, GAsyncResult *res, gpointer user_data) +-{ +- g_autoptr(GSubprocess) proc = G_SUBPROCESS (obj); +- GTask *task = G_TASK (user_data); +- BacklightHelperData *data = g_task_get_task_data (task); +- GsdBacklight *backlight = g_task_get_source_object (task); +- g_autoptr(GError) error = NULL; +- +- g_assert (task == backlight->active_task); +- backlight->active_task = NULL; +- +- g_subprocess_wait_check_finish (proc, res, &error); +- +- if (error) +- goto done; +- +-done: +- gsd_backlight_set_helper_return (backlight, task, data->value, error); +- /* Start processing any tasks that were added in the meantime. */ +- gsd_backlight_process_taskqueue (backlight); +-} +- +-static void +-gsd_backlight_run_set_helper (GsdBacklight *backlight, GTask *task) +-{ +- GSubprocess *proc = NULL; +- BacklightHelperData *data = g_task_get_task_data (task); +- const gchar *gsd_backlight_helper = NULL; +- GError *error = NULL; +- +- g_assert (backlight->active_task == NULL); +- backlight->active_task = task; +- +- if (data->value_str == NULL) +- data->value_str = g_strdup_printf ("%d", data->value); +- +- /* This is solely for use by the test environment. If given, execute +- * this helper instead of the internal helper using pkexec */ +- gsd_backlight_helper = g_getenv ("GSD_BACKLIGHT_HELPER"); +- if (!gsd_backlight_helper) { +- proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE, +- &error, +- "pkexec", +- LIBEXECDIR "/gsd-backlight-helper", +- g_udev_device_get_sysfs_path (backlight->udev_device), +- data->value_str, NULL); +- } else { +- proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE, +- &error, +- gsd_backlight_helper, +- g_udev_device_get_sysfs_path (backlight->udev_device), +- data->value_str, NULL); +- } +- +- if (proc == NULL) { +- gsd_backlight_set_helper_return (backlight, task, -1, error); +- return; +- } +- +- g_subprocess_wait_check_async (proc, g_task_get_cancellable (task), +- gsd_backlight_set_helper_finish, +- task); +-} +- +-static void +-gsd_backlight_process_taskqueue (GsdBacklight *backlight) +-{ +- GTask *to_run; +- +- /* There is already a task active, nothing to do. */ +- if (backlight->active_task) +- return; +- +- /* Get the last added task, thereby compressing the updates into one. */ +- to_run = G_TASK (g_queue_peek_tail (&backlight->tasks)); +- if (to_run == NULL) +- return; +- +- /* And run it! */ +- gsd_backlight_run_set_helper (backlight, to_run); +-} +-#endif /* __linux__ */ +- +-static const char * +-gsd_backlight_mutter_find_monitor (GsdBacklight *backlight, +- gboolean controllable) +-{ +- return backlight->backlight_connector; +-} ++ gsd_backlight_initable_iface_init)) + + /** + * gsd_backlight_get_brightness +@@ -448,12 +88,37 @@ gsd_backlight_get_brightness (GsdBacklight *backlight, gint *target) + if (backlight->builtin_display_disabled) + return -1; + +- if (target) +- *target = ABS_TO_PERCENTAGE (backlight->brightness_min, backlight->brightness_max, backlight->brightness_target); ++ return ABS_TO_PERCENTAGE (backlight->brightness_min, ++ backlight->brightness_max, ++ backlight->brightness_val); ++} ++ ++/** ++ * gsd_backlight_get_target_brightness ++ * @backlight: a #GsdBacklight ++ * ++ * This returns the target value of the pending operations, which might differ ++ * from the last known stable value, but is identical to the last value set in ++ * the async setter. ++ * ++ * If the internal display is detected as disabled, then the function will ++ * instead return -1. ++ * ++ * Returns: The current target backlight value or -1 if the internal display is disabled. ++ **/ ++gint ++gsd_backlight_get_target_brightness (GsdBacklight *backlight) ++{ ++ if (backlight->builtin_display_disabled) ++ return -1; + +- return ABS_TO_PERCENTAGE (backlight->brightness_min, backlight->brightness_max, backlight->brightness_val); ++ return ABS_TO_PERCENTAGE (backlight->brightness_min, ++ backlight->brightness_max, ++ backlight->brightness_target); + } + ++static void update_mutter_backlight (GsdBacklight *backlight); ++ + static void + gsd_backlight_set_brightness_val_async (GsdBacklight *backlight, + int value, +@@ -462,9 +127,10 @@ gsd_backlight_set_brightness_val_async (GsdBacklight *backlight, + gpointer user_data) + { + GError *error = NULL; +- GTask *task = NULL; ++ g_autoptr(GTask) task = NULL; + const char *monitor; +- gint percent; ++ GsdDisplayConfig *display_config = ++ gnome_settings_bus_get_display_config_proxy (); + + value = MIN(backlight->brightness_max, value); + value = MAX(backlight->brightness_min, value); +@@ -472,80 +138,47 @@ gsd_backlight_set_brightness_val_async (GsdBacklight *backlight, + backlight->brightness_target = value; + + task = g_task_new (backlight, cancellable, callback, user_data); +- if (backlight->backend == BACKLIGHT_BACKEND_NONE) { ++ if (!backlight->initialized) { + g_task_return_new_error (task, GSD_POWER_MANAGER_ERROR, + GSD_POWER_MANAGER_ERROR_FAILED, + "No backend initialized yet"); +- g_object_unref (task); + return; + } + +-#ifdef __linux__ +- if (backlight->backend == BACKLIGHT_BACKEND_UDEV) { +- BacklightHelperData *task_data; +- +- if (backlight->logind_proxy) { +- g_dbus_proxy_call (backlight->logind_proxy, +- "SetBrightness", +- g_variant_new ("(ssu)", +- "backlight", +- g_udev_device_get_name (backlight->udev_device), +- backlight->brightness_target), +- G_DBUS_CALL_FLAGS_NONE, +- -1, NULL, +- NULL, NULL); +- +- percent = ABS_TO_PERCENTAGE (backlight->brightness_min, +- backlight->brightness_max, +- backlight->brightness_target); +- g_task_return_int (task, percent); +- } else { +- task_data = g_new0 (BacklightHelperData, 1); +- task_data->value = backlight->brightness_target; +- g_task_set_task_data (task, task_data, backlight_task_data_destroy); +- +- /* Task is set up now. Queue it and ensure we are working something. */ +- g_queue_push_tail (&backlight->tasks, task); +- gsd_backlight_process_taskqueue (backlight); +- } ++ monitor = backlight->backlight_connector; ++ if (!monitor) { ++ g_assert_not_reached (); + ++ g_task_return_new_error (task, GSD_POWER_MANAGER_ERROR, ++ GSD_POWER_MANAGER_ERROR_FAILED, ++ "No method to set brightness!"); + return; + } +-#endif /* __linux__ */ +- +- /* Fallback to setting via DisplayConfig (mutter) */ +- g_assert (backlight->backend == BACKLIGHT_BACKEND_MUTTER); +- +- monitor = gsd_backlight_mutter_find_monitor (backlight, TRUE); +- if (monitor) { +- GsdDisplayConfig *display_config = +- gnome_settings_bus_get_display_config_proxy (); +- +- if (!gsd_display_config_call_set_backlight_sync (display_config, +- backlight->backlight_serial, +- monitor, +- value, +- NULL, +- &error)) { ++ ++ while (TRUE) { ++ uint32_t serial = backlight->backlight_serial; ++ ++ g_clear_error (&error); ++ ++ if (gsd_display_config_call_set_backlight_sync (display_config, ++ serial, ++ monitor, ++ value, ++ NULL, ++ &error)) ++ break; ++ ++ update_mutter_backlight (backlight); ++ if (backlight->backlight_serial == serial) { + g_task_return_error (task, error); +- g_object_unref (task); + return; + } +- +- backlight->brightness_val = value; +- g_object_notify_by_pspec (G_OBJECT (backlight), props[PROP_BRIGHTNESS]); +- g_task_return_int (task, gsd_backlight_get_brightness (backlight, NULL)); +- g_object_unref (task); +- +- return; + } + +- g_assert_not_reached (); ++ backlight->brightness_val = value; ++ g_object_notify_by_pspec (G_OBJECT (backlight), props[PROP_BRIGHTNESS]); ++ g_task_return_int (task, gsd_backlight_get_brightness (backlight)); + +- g_task_return_new_error (task, GSD_POWER_MANAGER_ERROR, +- GSD_POWER_MANAGER_ERROR_FAILED, +- "No method to set brightness!"); +- g_object_unref (task); + } + + void +@@ -557,7 +190,9 @@ gsd_backlight_set_brightness_async (GsdBacklight *backlight, + { + /* Overflow/underflow is handled by gsd_backlight_set_brightness_val_async. */ + gsd_backlight_set_brightness_val_async (backlight, +- PERCENTAGE_TO_ABS (backlight->brightness_min, backlight->brightness_max, percent), ++ PERCENTAGE_TO_ABS (backlight->brightness_min, ++ backlight->brightness_max, ++ percent), + cancellable, + callback, + user_data); +@@ -695,17 +330,18 @@ gsd_backlight_cycle_up_async (GsdBacklight *backlight, + GAsyncReadyCallback callback, + gpointer user_data) + { +- if (backlight->brightness_target < backlight->brightness_max) ++ if (backlight->brightness_target < backlight->brightness_max) { + gsd_backlight_step_up_async (backlight, + cancellable, + callback, + user_data); +- else ++ } else { + gsd_backlight_set_brightness_val_async (backlight, + backlight->brightness_min, + cancellable, + callback, + user_data); ++ } + } + + /** +@@ -780,7 +416,6 @@ update_mutter_backlight (GsdBacklight *backlight) + g_autoptr(GVariant) monitors = NULL; + g_autoptr(GVariant) monitor = NULL; + gboolean monitor_active = FALSE; +- gboolean have_backlight = FALSE; + + backlights = gsd_display_config_get_backlight (display_config); + g_return_if_fail (backlights != NULL); +@@ -802,26 +437,16 @@ update_mutter_backlight (GsdBacklight *backlight) + g_variant_lookup (monitor, "active", "b", &monitor_active); + backlight->builtin_display_disabled = !monitor_active; + +- switch (backlight->backend) { +- case BACKLIGHT_BACKEND_NONE: +- case BACKLIGHT_BACKEND_MUTTER: +- if (g_variant_lookup (monitor, "value", "i", &backlight->brightness_val)) { +- g_variant_lookup (monitor, "min", "i", &backlight->brightness_min); +- g_variant_lookup (monitor, "max", "i", &backlight->brightness_max); +- have_backlight = TRUE; +- backlight->backend = BACKLIGHT_BACKEND_MUTTER; +- } +- break; +- case BACKLIGHT_BACKEND_UDEV: +- break; ++ if (g_variant_lookup (monitor, "value", "i", &backlight->brightness_val)) { ++ g_variant_lookup (monitor, "min", "i", &backlight->brightness_min); ++ g_variant_lookup (monitor, "max", "i", &backlight->brightness_max); ++ backlight->initialized = TRUE; ++ } else { ++ backlight->brightness_val = -1; ++ backlight->brightness_min = -1; ++ backlight->brightness_max = -1; + } + } +- +- if (!have_backlight && backlight->backend == BACKLIGHT_BACKEND_MUTTER) { +- backlight->brightness_val = -1; +- backlight->brightness_min = -1; +- backlight->brightness_max = -1; +- } + } + + static void +@@ -857,7 +482,6 @@ gsd_backlight_initable_init (GInitable *initable, + GsdBacklight *backlight = GSD_BACKLIGHT (initable); + GsdDisplayConfig *display_config = + gnome_settings_bus_get_display_config_proxy (); +- GError *logind_error = NULL; + + if (cancellable != NULL) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, +@@ -884,92 +508,16 @@ gsd_backlight_initable_init (GInitable *initable, + G_CONNECT_SWAPPED); + maybe_update_mutter_backlight (backlight); + +-#ifdef __linux__ +- backlight->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, &logind_error); +- if (backlight->logind_proxy) { +- /* Check that the SetBrightness method does exist */ +- g_dbus_proxy_call_sync (backlight->logind_proxy, +- "SetBrightness", NULL, +- G_DBUS_CALL_FLAGS_NONE, -1, +- NULL, &logind_error); +- +- if (g_error_matches (logind_error, G_DBUS_ERROR, +- G_DBUS_ERROR_INVALID_ARGS)) { +- /* We are calling the method with no arguments, so +- * this is expected. +- */ +- g_clear_error (&logind_error); +- } else if (g_error_matches (logind_error, G_DBUS_ERROR, +- G_DBUS_ERROR_UNKNOWN_METHOD)) { +- /* systemd version is too old, so ignore. +- */ +- g_clear_error (&logind_error); +- g_clear_object (&backlight->logind_proxy); +- } else { +- /* Fail on anything else */ +- g_clear_object (&backlight->logind_proxy); +- } +- } +- +- if (logind_error) { +- g_warning ("No logind found: %s", logind_error->message); +- g_error_free (logind_error); +- } +- +- /* Try finding a udev device. */ +- if (gsd_backlight_udev_init (backlight)) { +- g_assert (backlight->backend == BACKLIGHT_BACKEND_UDEV); +- goto found; +- } +-#endif /* __linux__ */ +- +- if (backlight->backend == BACKLIGHT_BACKEND_MUTTER) { +- g_debug ("Using DisplayConfig (mutter) for backlight."); +- goto found; +- } +- +- g_debug ("No usable backlight found."); +- +- g_set_error_literal (error, GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_NO_BACKLIGHT, +- "No usable backlight could be found!"); +- +- return FALSE; +- +-found: + backlight->brightness_target = backlight->brightness_val; +- backlight->brightness_step = MAX(backlight->brightness_step, BRIGHTNESS_STEP_AMOUNT(backlight->brightness_max - backlight->brightness_min + 1)); ++ backlight->brightness_step = ++ MAX(backlight->brightness_step, ++ BRIGHTNESS_STEP_AMOUNT(backlight->brightness_max - backlight->brightness_min + 1)); + + g_debug ("Step size for backlight is %i.", backlight->brightness_step); + + return TRUE; + } + +-static void +-gsd_backlight_finalize (GObject *object) +-{ +- GsdBacklight *backlight = GSD_BACKLIGHT (object); +- +-#ifdef __linux__ +- g_assert (backlight->active_task == NULL); +- g_assert (g_queue_is_empty (&backlight->tasks)); +- g_clear_object (&backlight->logind_proxy); +- g_clear_pointer (&backlight->backlight_connector, g_free); +- g_clear_object (&backlight->udev); +- g_clear_object (&backlight->udev_device); +- if (backlight->idle_update) { +- g_source_remove (backlight->idle_update); +- backlight->idle_update = 0; +- } +-#endif /* __linux__ */ +-} +- + static void + gsd_backlight_initable_iface_init (GInitableIface *iface) + { +@@ -981,7 +529,6 @@ gsd_backlight_class_init (GsdBacklightClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + +- object_class->finalize = gsd_backlight_finalize; + object_class->get_property = gsd_backlight_get_property; + + props[PROP_BRIGHTNESS] = g_param_spec_int ("brightness", "The display brightness", +@@ -992,7 +539,6 @@ gsd_backlight_class_init (GsdBacklightClass *klass) + g_object_class_install_properties (object_class, PROP_LAST, props); + } + +- + static void + gsd_backlight_init (GsdBacklight *backlight) + { +@@ -1001,11 +547,6 @@ gsd_backlight_init (GsdBacklight *backlight) + backlight->brightness_max = -1; + backlight->brightness_val = -1; + backlight->brightness_step = 1; +- +-#ifdef __linux__ +- backlight->active_task = NULL; +- g_queue_init (&backlight->tasks); +-#endif /* __linux__ */ + } + + GsdBacklight * +diff --git a/plugins/power/meson.build b/plugins/power/meson.build +index 3416f15d..e44f8ce9 100644 +--- a/plugins/power/meson.build ++++ b/plugins/power/meson.build +@@ -66,41 +66,6 @@ gsd_power_enums_update = executable( + native: true + ) + +-if host_is_linux +- policy = 'org.gnome.settings-daemon.plugins.power.policy' +- +- policy_in = configure_file( +- input: policy + '.in.in', +- output: policy + '.in', +- configuration: plugins_conf +- ) +- +- i18n.merge_file( +- input: policy_in, +- output: policy, +- po_dir: po_dir, +- install: true, +- install_dir: join_paths(gsd_datadir, 'polkit-1', 'actions') +- ) +- +- sources = files( +- 'gsd-backlight-helper.c', +- ) +- +- deps = [ +- ] +- +- executable( +- 'gsd-backlight-helper', +- sources, +- include_directories: top_inc, +- dependencies: deps, +- install: true, +- install_rpath: gsd_pkglibdir, +- install_dir: gsd_libexecdir +- ) +-endif +- + output = 'gsdpowerconstants.py' + + gsdpowerconstants_py = custom_target( +@@ -128,14 +93,7 @@ envs = environment() + #envs.prepend('G_DEBUG', 'fatal-warnings') + envs.set('BUILDDIR', meson.current_build_dir()) + envs.set('TOP_BUILDDIR', meson.project_build_root()) +-envs.set('LD_PRELOAD', 'libumockdev-preload.so.0') + envs.set('NO_AT_BRIDGE', '1') +-envs.set('HAVE_SYSFS_BACKLIGHT', host_is_linux ? '1' : '0') +- +-if get_option('b_sanitize').split(',').contains('address') +- # libasan needs to be loaded first; so we need to explicitly preload it +- envs.set('POWER_LD_PRELOAD', 'libasan.so.5') +-endif + + tests = [ + 'Screensaver', +diff --git a/plugins/power/org.gnome.settings-daemon.plugins.power.policy.in.in b/plugins/power/org.gnome.settings-daemon.plugins.power.policy.in.in +deleted file mode 100644 +index f16300f8..00000000 +--- a/plugins/power/org.gnome.settings-daemon.plugins.power.policy.in.in ++++ /dev/null +@@ -1,32 +0,0 @@ +- +- +- +- +- +- +- GNOME Settings Daemon +- http://git.gnome.org/browse/gnome-settings-daemon +- battery +- +- +- +- Modify the laptop brightness +- Authentication is required to modify the laptop brightness +- +- no +- no +- yes +- +- @libexecdir@/gsd-backlight-helper +- +- +- +- +diff --git a/plugins/power/test-backlight-helper b/plugins/power/test-backlight-helper +deleted file mode 100755 +index 2dceacd8..00000000 +--- a/plugins/power/test-backlight-helper ++++ /dev/null +@@ -1,5 +0,0 @@ +-#!/bin/sh +- +-# Simulate a slow call and just write the given brightness value to the device +-sleep 0.2 +-echo "$2" >"$1/brightness" +diff --git a/plugins/power/test.py b/plugins/power/test.py +index e4159280..4cfd9184 100755 +--- a/plugins/power/test.py ++++ b/plugins/power/test.py +@@ -32,12 +32,10 @@ DBusGMainLoop(set_as_default=True) + + import gi + gi.require_version('UPowerGlib', '1.0') +-gi.require_version('UMockdev', '1.0') + + from gi.repository import Gio + from gi.repository import GLib + from gi.repository import UPowerGlib +-from gi.repository import UMockdev + + # There must be a better way to do a version comparison ... but this works + mutter_version = subprocess.run(['mutter', '--version'], stdout=subprocess.PIPE).stdout.decode().strip() +@@ -93,11 +91,6 @@ class PowerPluginBase(gsdtestcase.GSDTestCase): + self.start_logind() + self.addCleanup(self.stop_logind) + +- # Setup umockdev testbed +- self.testbed = UMockdev.Testbed.new() +- self.addCleanup(self.cleanup_testbed) +- os.environ['UMOCKDEV_DIR'] = self.testbed.get_root_dir() +- + # start mock mutter + template = ( + Path(os.path.realpath(__file__)).parent +@@ -109,15 +102,10 @@ class PowerPluginBase(gsdtestcase.GSDTestCase): + (self.mockmutter, self.obj_mockmutter) = self.spawn_server_template(template.absolute().as_posix()) + self.addCleanup(self.stop_process, self.mockmutter) + +- # Create a mock backlight device +- # Note that this function creates a different or even no backlight +- # device based on the name of the test. +- self.add_backlight() +- +- if 'HAVE_SYSFS_BACKLIGHT' in os.environ and os.environ['HAVE_SYSFS_BACKLIGHT'] == '1': +- self.skip_sysfs_backlight = False ++ if 'legacy_brightness' in self.id(): ++ self.obj_mockmutter.MockSetBacklight('dp-1', True, 0, 15, 15) + else: +- self.skip_sysfs_backlight = True ++ self.obj_mockmutter.MockSetBacklight('dp-1', True, 0, 100, 50) + + # start mock upowerd + (self.upowerd, self.obj_upower) = self.spawn_server_template( +@@ -187,9 +175,6 @@ class PowerPluginBase(gsdtestcase.GSDTestCase): + + self.p_notify_log.clear() + +- def cleanup_testbed(self): +- del self.testbed +- + def delete_external_monitor_file(self): + try: + os.unlink(self.mock_external_monitor_file) +@@ -219,44 +204,17 @@ class PowerPluginBase(gsdtestcase.GSDTestCase): + def get_status(self): + return self.obj_session_presence_props.Get('org.gnome.SessionManager.Presence', 'status') + +- def backlight_defaults(self): +- # Hack to modify the brightness defaults before starting gsd-power. +- # The alternative would be to create two separate test files. +- if 'no_backlight' in self.id(): +- return None, None +- elif 'legacy_brightness' in self.id(): +- return 15, 15 +- else: +- return 100, 50 +- +- def add_backlight(self, _type="raw"): +- max_brightness, brightness = self.backlight_defaults() +- +- if max_brightness is None: +- self.backlight = None +- return +- +- # Undo mangling done in GSD +- if max_brightness >= 99: +- max_brightness += 1 +- brightness += 1 +- +- # This needs to be done before starting gsd-power! +- self.backlight = self.testbed.add_device('backlight', 'mock_backlight', None, +- ['type', _type, +- 'max_brightness', str(max_brightness), +- 'brightness', str(brightness)], +- []) +- + def get_brightness(self): +- max_brightness = int(open(os.path.join(self.testbed.get_root_dir() + self.backlight, 'max_brightness')).read()) ++ (serial, bl) = self.obj_mockmutter.Get('org.gnome.Mutter.DisplayConfig', 'Backlight') ++ assert bl ++ bl = bl[0] ++ assert bl ++ assert bl['active'] == True + +- # self.backlight contains the leading slash, so os.path.join doesn't quite work +- res = int(open(os.path.join(self.testbed.get_root_dir() + self.backlight, 'brightness')).read()) +- # Undo mangling done in GSD +- if max_brightness >= 99: +- res -= 1 +- return res ++ max_brightness = bl['max'] ++ value = bl['value'] ++ ++ return value + + def set_has_external_monitor(self, external): + if external: +@@ -464,8 +422,8 @@ class PowerPluginTestScreensaver(PowerPluginBase): + time.sleep(0.5) + self.reset_idle_timer() + self.check_unblank(2) +- if not self.skip_sysfs_backlight: +- self.assertTrue(self.get_brightness() == gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS , 'incorrect unblanked brightness (%d != %d)' % (self.get_brightness(), gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS)) ++ ++ self.assertTrue(self.get_brightness() == gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS , 'incorrect unblanked brightness (%d != %d)' % (self.get_brightness(), gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS)) + + # Check for no blank before the normal blank timeout + self.check_no_blank(gsdpowerconstants.SCREENSAVER_TIMEOUT_BLANK - 4) +@@ -731,9 +689,8 @@ class PowerPluginTestDim(PowerPluginBase): + self.check_dim(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY + 1) + # Give time for the brightness to change + time.sleep(2) +- if not self.skip_sysfs_backlight: +- level = self.get_brightness(); +- self.assertTrue(level == dim_level, 'incorrect dim brightness (%d != %d)' % (level, dim_level)) ++ level = self.get_brightness(); ++ self.assertTrue(level == dim_level, 'incorrect dim brightness (%d != %d)' % (level, dim_level)) + + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) + +@@ -753,8 +710,7 @@ class PowerPluginTestDim(PowerPluginBase): + time.sleep(1) + + # And check that we have the pre-dim brightness +- if not self.skip_sysfs_backlight: +- self.assertTrue(self.get_brightness() == gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS , 'incorrect unblanked brightness (%d != %d)' % (self.get_brightness(), gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS)) ++ self.assertTrue(self.get_brightness() == gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS , 'incorrect unblanked brightness (%d != %d)' % (self.get_brightness(), gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS)) + + def test_lid_close_inhibition(self): + '''Check that we correctly inhibit suspend with an external monitor''' +@@ -1103,15 +1059,10 @@ class PowerPluginTestBrightnessStep(PowerPluginBase): + def test_brightness_stepping(self): + '''Check that stepping the backlight works as expected''' + +- if self.skip_sysfs_backlight: +- self.skipTest("sysfs backlight support required for test") +- + obj_gsd_power = self.session_bus_con.get_object( + 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') + obj_gsd_power_screen_iface = dbus.Interface(obj_gsd_power, 'org.gnome.SettingsDaemon.Power.Screen') + +- # Each of the step calls will only return when the value was written +- start = time.time() + # We start at 50% and step by 5% each time + obj_gsd_power_screen_iface.StepUp() + self.assertEqual(self.get_brightness(), 55) +@@ -1121,10 +1072,6 @@ class PowerPluginTestBrightnessStep(PowerPluginBase): + self.assertEqual(self.get_brightness(), 65) + obj_gsd_power_screen_iface.StepUp() + self.assertEqual(self.get_brightness(), 70) +- stop = time.time() +- # This needs to take more than 0.8 seconds as each write is delayed by +- # 0.2 seconds by the test backlight helper +- self.assertGreater(stop - start, 0.8) + + # Now, the same thing should work fine if we step multiple times, + # even if we are so quick that compression will happen. +@@ -1162,9 +1109,6 @@ class PowerPluginTestBrightnessStep(PowerPluginBase): + def test_brightness_compression(self): + '''Check that compression also happens when setting the property''' + +- if self.skip_sysfs_backlight: +- self.skipTest("sysfs backlight support required for test") +- + # Now test that the compression works correctly. + # NOTE: Relies on the implementation detail, that the property setter + # returns immediately rather than waiting for the brightness to +@@ -1186,33 +1130,7 @@ class PowerPluginTestBrightnessStep(PowerPluginBase): + time.sleep(2.0) + self.assertEqual(self.get_brightness(), 90) + +- def test_brightness_uevent(self): +- if self.skip_sysfs_backlight: +- self.skipTest("sysfs backlight support required for test") +- +- obj_gsd_power = self.session_bus_con.get_object( +- 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') +- obj_gsd_power_prop_iface = dbus.Interface(obj_gsd_power, dbus.PROPERTIES_IFACE) +- +- brightness = obj_gsd_power_prop_iface.Get('org.gnome.SettingsDaemon.Power.Screen', 'Brightness') +- self.assertEqual(50, brightness) +- +- # Check that the brightness is updated if it was changed through some +- # other mechanism (e.g. firmware). +- # Set to 80+1 because of the GSD offset (see add_backlight). +- self.testbed.set_attribute(self.backlight, 'brightness', '81') +- self.testbed.uevent(self.backlight, 'change') +- +- self.check_plugin_log('GsdBacklight: Got uevent', 1, 'gsd-power did not process uevent') +- time.sleep(0.2) +- +- brightness = obj_gsd_power_prop_iface.Get('org.gnome.SettingsDaemon.Power.Screen', 'Brightness') +- self.assertEqual(80, brightness) +- + def test_brightness_step(self): +- if self.skip_sysfs_backlight: +- self.skipTest("sysfs backlight support required for test") +- + for l in self.plugin_startup_msgs: + if b'Step size for backlight is 5.' in l: + break +@@ -1220,9 +1138,6 @@ class PowerPluginTestBrightnessStep(PowerPluginBase): + self.fail('Step size is not 5') + + def test_legacy_brightness_step(self): +- if self.skip_sysfs_backlight: +- self.skipTest("sysfs backlight support required for test") +- + for l in self.plugin_startup_msgs: + if b'Step size for backlight is 1.' in l: + break +@@ -1230,9 +1145,6 @@ class PowerPluginTestBrightnessStep(PowerPluginBase): + self.fail('Step size is not 1') + + def test_legacy_brightness_rounding(self): +- if self.skip_sysfs_backlight: +- self.skipTest("sysfs backlight support required for test") +- + obj_gsd_power = self.session_bus_con.get_object( + 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') + obj_gsd_power_prop_iface = dbus.Interface(obj_gsd_power, dbus.PROPERTIES_IFACE) +@@ -1265,34 +1177,6 @@ class PowerPluginTestBrightnessStep(PowerPluginBase): + time.sleep(0.6) + self.assertEqual(self.get_brightness(), 15) + +- def test_no_backlight(self): +- '''Check that backlight brightness DBus api without a backlight''' +- +- obj_gsd_power = self.session_bus_con.get_object( +- 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') +- obj_gsd_power_props = dbus.Interface(obj_gsd_power, dbus.PROPERTIES_IFACE) +- obj_gsd_power_screen = dbus.Interface(obj_gsd_power, 'org.gnome.SettingsDaemon.Power.Screen') +- +- # We expect -1 to be returned +- brightness = obj_gsd_power_props.Get('org.gnome.SettingsDaemon.Power.Screen', 'Brightness') +- self.assertEqual(brightness, -1) +- +- # Trying to set the brightness +- with self.assertRaises(dbus.DBusException) as exc: +- obj_gsd_power_props.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 1) +- +- self.assertEqual(exc.exception.get_dbus_message(), 'No usable backlight could be found!') +- +- with self.assertRaises(dbus.DBusException) as exc: +- obj_gsd_power_screen.StepUp() +- +- self.assertEqual(exc.exception.get_dbus_message(), 'No usable backlight could be found!') +- +- with self.assertRaises(dbus.DBusException) as exc: +- obj_gsd_power_screen.StepDown() +- +- self.assertEqual(exc.exception.get_dbus_message(), 'No usable backlight could be found!') +- + def test_power_saver_on_low_battery(self): + '''Check that the power-saver profile gets held when low on battery''' + +-- +2.51.1 + + +From a88c53c8a11d01be4925b49b9c71de69a3306581 Mon Sep 17 00:00:00 2001 +From: Sebastian Wick +Date: Fri, 13 Jun 2025 00:44:31 +0200 +Subject: [PATCH 03/10] media-keys: Remove screen brightness media keys + +They are now handled on the gnome-shell side. +--- + ...s-daemon.plugins.media-keys.gschema.xml.in | 32 ----------- + plugins/media-keys/gsd-media-keys-manager.c | 55 +------------------ + plugins/media-keys/media-keys.h | 3 - + plugins/media-keys/shortcuts-list.h | 3 - + 4 files changed, 3 insertions(+), 90 deletions(-) + +diff --git a/data/org.gnome.settings-daemon.plugins.media-keys.gschema.xml.in b/data/org.gnome.settings-daemon.plugins.media-keys.gschema.xml.in +index ef5be625..9a3f0edc 100644 +--- a/data/org.gnome.settings-daemon.plugins.media-keys.gschema.xml.in ++++ b/data/org.gnome.settings-daemon.plugins.media-keys.gschema.xml.in +@@ -385,22 +385,6 @@ + Binding to suspend the machine. + + +- +- [''] +- Screen brightness up +- Binding to increase the screen brightness. +- +- +- [''] +- Screen brightness down +- Binding to decrease the screen brightness. +- +- +- [''] +- Screen brightness cycle +- Binding to cycle the screen brightness. +- +- + + [''] + Keyboard brightness up +@@ -622,22 +606,6 @@ + Static binding to suspend the machine. + + +- +- ['XF86MonBrightnessUp'] +- Screen brightness up +- Static binding to increase the screen brightness. +- +- +- ['XF86MonBrightnessDown'] +- Screen brightness down +- Static binding to decrease the screen brightness. +- +- +- ['XF86MonBrightnessCycle'] +- Screen brightness cycle +- Static binding to cycle the screen brightness. +- +- + + ['XF86KbdBrightnessUp'] + Keyboard brightness up +diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c +index 60ff2ced..aaea5ec8 100644 +--- a/plugins/media-keys/gsd-media-keys-manager.c ++++ b/plugins/media-keys/gsd-media-keys-manager.c +@@ -180,7 +180,6 @@ typedef struct + /* Power stuff */ + GSettings *power_settings; + GDBusProxy *power_proxy; +- GDBusProxy *power_screen_proxy; + GDBusProxy *power_keyboard_proxy; + UpDevice *composite_device; + char *chassis_type; +@@ -2086,22 +2085,15 @@ update_brightness_cb (GObject *source_object, + GVariant *variant; + GsdMediaKeysManager *manager = GSD_MEDIA_KEYS_MANAGER (user_data); + GsdMediaKeysManagerPrivate *priv = GSD_MEDIA_KEYS_MANAGER_GET_PRIVATE (manager); +- const char *icon, *debug; ++ const char *icon; + char *connector = NULL; + +- /* update the dialog with the new value */ +- if (G_DBUS_PROXY (source_object) == priv->power_keyboard_proxy) { +- debug = "keyboard"; +- } else { +- debug = "screen"; +- } +- + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), + res, &error); + if (variant == NULL) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) +- g_warning ("Failed to set new %s percentage: %s", +- debug, error->message); ++ g_warning ("Failed to set new keyboard percentage: %s", ++ error->message); + g_error_free (error); + return; + } +@@ -2133,11 +2125,6 @@ do_brightness_action (GsdMediaKeysManager *manager, + case KEYBOARD_BRIGHTNESS_TOGGLE_KEY: + proxy = priv->power_keyboard_proxy; + break; +- case SCREEN_BRIGHTNESS_UP_KEY: +- case SCREEN_BRIGHTNESS_DOWN_KEY: +- case SCREEN_BRIGHTNESS_CYCLE_KEY: +- proxy = priv->power_screen_proxy; +- break; + default: + g_assert_not_reached (); + } +@@ -2150,19 +2137,14 @@ do_brightness_action (GsdMediaKeysManager *manager, + + switch (type) { + case KEYBOARD_BRIGHTNESS_UP_KEY: +- case SCREEN_BRIGHTNESS_UP_KEY: + cmd = "StepUp"; + break; + case KEYBOARD_BRIGHTNESS_DOWN_KEY: +- case SCREEN_BRIGHTNESS_DOWN_KEY: + cmd = "StepDown"; + break; + case KEYBOARD_BRIGHTNESS_TOGGLE_KEY: + cmd = "Toggle"; + break; +- case SCREEN_BRIGHTNESS_CYCLE_KEY: +- cmd = "Cycle"; +- break; + default: + g_assert_not_reached (); + } +@@ -2467,9 +2449,6 @@ do_action (GsdMediaKeysManager *manager, + case HIBERNATE_KEY: + do_config_power_action (manager, GSD_POWER_ACTION_HIBERNATE, power_action_noninteractive); + break; +- case SCREEN_BRIGHTNESS_UP_KEY: +- case SCREEN_BRIGHTNESS_DOWN_KEY: +- case SCREEN_BRIGHTNESS_CYCLE_KEY: + case KEYBOARD_BRIGHTNESS_UP_KEY: + case KEYBOARD_BRIGHTNESS_DOWN_KEY: + case KEYBOARD_BRIGHTNESS_TOGGLE_KEY: +@@ -3079,7 +3058,6 @@ gsd_media_keys_manager_stop (GsdMediaKeysManager *manager) + g_clear_object (&priv->sound_settings); + g_clear_object (&priv->power_settings); + g_clear_object (&priv->power_proxy); +- g_clear_object (&priv->power_screen_proxy); + g_clear_object (&priv->power_keyboard_proxy); + g_clear_object (&priv->composite_device); + g_clear_object (&priv->mpris_controller); +@@ -3438,23 +3416,6 @@ power_ready_cb (GObject *source_object, + } + } + +-static void +-power_screen_ready_cb (GObject *source_object, +- GAsyncResult *res, +- GsdMediaKeysManager *manager) +-{ +- GsdMediaKeysManagerPrivate *priv = GSD_MEDIA_KEYS_MANAGER_GET_PRIVATE (manager); +- GError *error = NULL; +- +- priv->power_screen_proxy = g_dbus_proxy_new_finish (res, &error); +- if (priv->power_screen_proxy == NULL) { +- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) +- g_warning ("Failed to get proxy for power (screen): %s", +- error->message); +- g_error_free (error); +- } +-} +- + static void + power_keyboard_ready_cb (GObject *source_object, + GAsyncResult *res, +@@ -3505,16 +3466,6 @@ on_bus_gotten (GObject *source_object, + (GAsyncReadyCallback) power_ready_cb, + manager); + +- g_dbus_proxy_new (priv->connection, +- G_DBUS_PROXY_FLAGS_NONE, +- NULL, +- GSD_DBUS_NAME ".Power", +- GSD_DBUS_PATH "/Power", +- GSD_DBUS_BASE_INTERFACE ".Power.Screen", +- NULL, +- (GAsyncReadyCallback) power_screen_ready_cb, +- manager); +- + g_dbus_proxy_new (priv->connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, +diff --git a/plugins/media-keys/media-keys.h b/plugins/media-keys/media-keys.h +index 801a13be..cf9a8880 100644 +--- a/plugins/media-keys/media-keys.h ++++ b/plugins/media-keys/media-keys.h +@@ -64,9 +64,6 @@ typedef enum { + POWER_KEY, + SUSPEND_KEY, + HIBERNATE_KEY, +- SCREEN_BRIGHTNESS_UP_KEY, +- SCREEN_BRIGHTNESS_DOWN_KEY, +- SCREEN_BRIGHTNESS_CYCLE_KEY, + KEYBOARD_BRIGHTNESS_UP_KEY, + KEYBOARD_BRIGHTNESS_DOWN_KEY, + KEYBOARD_BRIGHTNESS_TOGGLE_KEY, +diff --git a/plugins/media-keys/shortcuts-list.h b/plugins/media-keys/shortcuts-list.h +index c8f084c8..eab49640 100644 +--- a/plugins/media-keys/shortcuts-list.h ++++ b/plugins/media-keys/shortcuts-list.h +@@ -87,9 +87,6 @@ static struct { + /* the kernel / Xorg names really are like this... */ + { SUSPEND_KEY, "suspend", TRUE, POWER_KEYS_MODE, META_KEY_BINDING_IGNORE_AUTOREPEAT }, + { HIBERNATE_KEY, "hibernate", TRUE, POWER_KEYS_MODE, META_KEY_BINDING_IGNORE_AUTOREPEAT }, +- { SCREEN_BRIGHTNESS_UP_KEY, "screen-brightness-up", TRUE, SHELL_ACTION_MODE_ALL }, +- { SCREEN_BRIGHTNESS_DOWN_KEY, "screen-brightness-down", TRUE, SHELL_ACTION_MODE_ALL }, +- { SCREEN_BRIGHTNESS_CYCLE_KEY, "screen-brightness-cycle", TRUE, SHELL_ACTION_MODE_ALL }, + { KEYBOARD_BRIGHTNESS_UP_KEY, "keyboard-brightness-up", TRUE, SHELL_ACTION_MODE_ALL }, + { KEYBOARD_BRIGHTNESS_DOWN_KEY, "keyboard-brightness-down", TRUE, SHELL_ACTION_MODE_ALL }, + { KEYBOARD_BRIGHTNESS_TOGGLE_KEY, "keyboard-brightness-toggle", TRUE, SHELL_ACTION_MODE_ALL, META_KEY_BINDING_IGNORE_AUTOREPEAT }, +-- +2.51.1 + + +From 5108cf07967fe4b2a414530a987a45ad09de388e Mon Sep 17 00:00:00 2001 +From: Sebastian Wick +Date: Fri, 13 Jun 2025 00:49:08 +0200 +Subject: [PATCH 04/10] power: Rename backlight_enable to enable_monitors + +The power saving feature is really about turning the panel off and on, +which can have a backlight but doesn't need to. This can be confused +with some backlight brightness control, so rename it. +--- + plugins/power/gsd-power-manager.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/plugins/power/gsd-power-manager.c b/plugins/power/gsd-power-manager.c +index 449f283a..3ce6eb24 100644 +--- a/plugins/power/gsd-power-manager.c ++++ b/plugins/power/gsd-power-manager.c +@@ -1285,7 +1285,7 @@ set_power_saving_mode (GsdPowerManager *manager, + } + + static void +-backlight_enable (GsdPowerManager *manager) ++enable_monitors (GsdPowerManager *manager) + { + iio_proxy_claim_light (manager, TRUE); + set_power_saving_mode (manager, GSD_POWER_SAVE_MODE_ON); +@@ -1294,7 +1294,7 @@ backlight_enable (GsdPowerManager *manager) + } + + static void +-backlight_disable (GsdPowerManager *manager) ++disable_monitors (GsdPowerManager *manager) + { + iio_proxy_claim_light (manager, FALSE); + set_power_saving_mode (manager, GSD_POWER_SAVE_MODE_OFF); +@@ -1323,7 +1323,7 @@ do_power_action_type (GsdPowerManager *manager, + action_poweroff (manager); + break; + case GSD_POWER_ACTION_BLANK: +- backlight_disable (manager); ++ disable_monitors (manager); + break; + case GSD_POWER_ACTION_NOTHING: + break; +@@ -1789,7 +1789,7 @@ idle_set_mode (GsdPowerManager *manager, GsdPowerIdleMode mode) + /* turn off screen and kbd */ + } else if (mode == GSD_POWER_IDLE_MODE_BLANK) { + +- backlight_disable (manager); ++ disable_monitors (manager); + + /* only toggle keyboard if present and not already toggled */ + if (manager->upower_kbd_proxy && +@@ -1816,7 +1816,7 @@ idle_set_mode (GsdPowerManager *manager, GsdPowerIdleMode mode) + /* turn on screen and restore user-selected brightness level */ + } else if (mode == GSD_POWER_IDLE_MODE_NORMAL) { + +- backlight_enable (manager); ++ enable_monitors (manager); + + /* reset brightness if we dimmed */ + if (manager->backlight && manager->pre_dim_brightness >= 0) { +@@ -2781,7 +2781,7 @@ handle_suspend_actions (GsdPowerManager *manager) + { + /* close any existing notification about idleness */ + notify_close_if_showing (&manager->notification_sleep_warning); +- backlight_disable (manager); ++ disable_monitors (manager); + uninhibit_suspend (manager); + } + +@@ -2789,7 +2789,7 @@ static void + handle_resume_actions (GsdPowerManager *manager) + { + /* ensure we turn the panel back on after resume */ +- backlight_enable (manager); ++ enable_monitors (manager); + + /* set up the delay again */ + inhibit_suspend (manager); +@@ -3079,7 +3079,7 @@ gsd_power_manager_start (GsdPowerManager *manager, + idle_configure (manager); + + /* ensure the default dpms timeouts are cleared */ +- backlight_enable (manager); ++ enable_monitors (manager); + + if (!gnome_settings_is_wayland ()) + manager->xscreensaver_watchdog_timer_id = gsd_power_enable_screensaver_watchdog (); +-- +2.51.1 + + +From 7d3f498b05aec19e508e187e6320c4baf01b1598 Mon Sep 17 00:00:00 2001 +From: Sebastian Wick +Date: Fri, 13 Jun 2025 00:50:55 +0200 +Subject: [PATCH 05/10] power/test: Mock org.gnome.Shell.Brightness interface + +We will use it to dim the screen for power saving reasons and to set the +auto brightness target. It will replace the direct mutter backlight +control. +--- + .../power/dbusmock-templates/gnome-shell.py | 29 +++++++++++++++++++ + plugins/power/test.py | 24 ++++++++++----- + 2 files changed, 45 insertions(+), 8 deletions(-) + create mode 100644 plugins/power/dbusmock-templates/gnome-shell.py + +diff --git a/plugins/power/dbusmock-templates/gnome-shell.py b/plugins/power/dbusmock-templates/gnome-shell.py +new file mode 100644 +index 00000000..107a1885 +--- /dev/null ++++ b/plugins/power/dbusmock-templates/gnome-shell.py +@@ -0,0 +1,29 @@ ++'''gnome-shell proxy mock template ++''' ++ ++# This program is free software; you can redistribute it and/or modify it under ++# the terms of the GNU Lesser General Public License as published by the Free ++# Software Foundation; either version 3 of the License, or (at your option) any ++# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text ++# of the license. ++ ++ ++__author__ = 'Sebastian Wick' ++__copyright__ = '(c) 2025 Red Hat Inc.' ++ ++ ++import dbus ++from dbusmock import MOCK_IFACE, mockobject ++ ++ ++BUS_NAME = 'org.gnome.Shell.Brightness' ++MAIN_OBJ = '/org/gnome/Shell/Brightness' ++MAIN_IFACE = 'org.gnome.Shell.Brightness' ++SYSTEM_BUS = False ++ ++ ++def load(mock, parameters): ++ mock.AddMethod(MAIN_IFACE, 'SetDimming', 'b', '', ''); ++ mock.AddMethod(MAIN_IFACE, 'SetAutoBrightnessTarget', 'd', '', ''); ++ ++ mock.AddProperty(MAIN_IFACE, 'HasBrightnessControl', True) +diff --git a/plugins/power/test.py b/plugins/power/test.py +index 4cfd9184..6633c00e 100755 +--- a/plugins/power/test.py ++++ b/plugins/power/test.py +@@ -13,6 +13,7 @@ import math + import os + import os.path + import signal ++import dbusmock + from pathlib import Path + + project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +@@ -74,6 +75,16 @@ def mutter_at_least(version): + # assume equal + return True + ++def dbusmock_template(name): ++ template = ( ++ Path(os.path.realpath(__file__)).parent ++ / "dbusmock-templates" ++ / f"{name}.py" ++ ) ++ assert template.exists() ++ assert template.suffix == ".py" ++ return template.absolute().as_posix() ++ + class PowerPluginBase(gsdtestcase.GSDTestCase): + '''Test the power plugin''' + +@@ -92,14 +103,7 @@ class PowerPluginBase(gsdtestcase.GSDTestCase): + self.addCleanup(self.stop_logind) + + # start mock mutter +- template = ( +- Path(os.path.realpath(__file__)).parent +- / "dbusmock-templates" +- / "mutter.py" +- ) +- assert template.exists() +- assert template.suffix == ".py" +- (self.mockmutter, self.obj_mockmutter) = self.spawn_server_template(template.absolute().as_posix()) ++ (self.mockmutter, self.obj_mockmutter) = self.spawn_server_template(dbusmock_template('mutter')) + self.addCleanup(self.stop_process, self.mockmutter) + + if 'legacy_brightness' in self.id(): +@@ -107,6 +111,10 @@ class PowerPluginBase(gsdtestcase.GSDTestCase): + else: + self.obj_mockmutter.MockSetBacklight('dp-1', True, 0, 100, 50) + ++ # start mock gnome-shell ++ (self.gnomeshell, self.obj_gnomeshell) = self.spawn_server_template(dbusmock_template('gnome-shell')) ++ self.addCleanup(self.stop_process, self.gnomeshell) ++ + # start mock upowerd + (self.upowerd, self.obj_upower) = self.spawn_server_template( + 'upower', {'DaemonVersion': '0.99', 'OnBattery': True, 'LidIsClosed': False}) +-- +2.51.1 + + +From f25a642a2afe18915f9ef798750b831fba498dcc Mon Sep 17 00:00:00 2001 +From: Sebastian Wick +Date: Fri, 13 Jun 2025 00:52:59 +0200 +Subject: [PATCH 06/10] power: Add new dimming and auto brightness API based on + Shell.Brightness + +We will switch over to them in the next commit. +--- + plugins/power/gsd-power-manager.c | 109 ++++++++++++++++++++++++++++++ + 1 file changed, 109 insertions(+) + +diff --git a/plugins/power/gsd-power-manager.c b/plugins/power/gsd-power-manager.c +index 3ce6eb24..5d1fb33b 100644 +--- a/plugins/power/gsd-power-manager.c ++++ b/plugins/power/gsd-power-manager.c +@@ -79,6 +79,10 @@ + #define SYSTEMD_DBUS_PATH "/org/freedesktop/login1" + #define SYSTEMD_DBUS_INTERFACE "org.freedesktop.login1.Manager" + ++#define SHELL_BRIGHTNESS_DBUS_NAME "org.gnome.Shell.Brightness" ++#define SHELL_BRIGHTNESS_DBUS_PATH "/org/gnome/Shell/Brightness" ++#define SHELL_BRIGHTNESS_DBUS_INTERFACE "org.gnome.Shell.Brightness" ++ + /* Time between notifying the user about a critical action and the action itself in UPower. */ + #define GSD_ACTION_DELAY 20 + /* And the time before we stop the warning sound */ +@@ -172,6 +176,7 @@ struct _GsdPowerManager + /* Brightness */ + GsdBacklight *backlight; + gint pre_dim_brightness; /* level, not percentage */ ++ GDBusProxy *shell_brightness_proxy; + + /* Keyboard */ + GDBusProxy *upower_kbd_proxy; +@@ -1193,6 +1198,94 @@ action_hibernate (GsdPowerManager *manager) + "Error calling Hibernate"); + } + ++static gboolean ++shell_brightness_has_control (GsdPowerManager *manager) ++{ ++ g_autoptr (GVariant) has_control_variant = NULL; ++ gboolean has_control; ++ ++ if (!manager->shell_brightness_proxy) ++ return FALSE; ++ ++ has_control_variant = ++ g_dbus_proxy_get_cached_property (manager->shell_brightness_proxy, ++ "HasBrightnessControl"); ++ ++ if (!has_control_variant) ++ return FALSE; ++ ++ g_variant_get (has_control_variant, "(b)", &has_control); ++ return has_control; ++} ++ ++static void ++shell_brightness_set_auto_target_cb (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ g_autoptr (GVariant) result = NULL; ++ g_autoptr (GError) error = NULL; ++ ++ result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), ++ res, ++ &error); ++ if (result) ++ return; ++ ++ g_warning ("couldn't set the auto brightness target: %s", ++ error->message); ++} ++ ++static void ++shell_brightness_set_auto_target (GsdPowerManager *manager, ++ float target) ++{ ++ if (!manager->shell_brightness_proxy) ++ return; ++ ++ g_dbus_proxy_call (G_DBUS_PROXY (manager->shell_brightness_proxy), ++ "SetAutoBrightnessTarget", ++ g_variant_new ("(d)", target), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, NULL, ++ shell_brightness_set_auto_target_cb, NULL); ++} ++ ++static void ++shell_brightness_set_dimming_cb (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ g_autoptr (GVariant) result = NULL; ++ g_autoptr (GError) error = NULL; ++ gboolean enable = !!GPOINTER_TO_UINT (user_data); ++ ++ result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), ++ res, ++ &error); ++ if (result) ++ return; ++ ++ g_warning ("couldn't %s dimming: %s", ++ enable ? "enable" : "disable", ++ error->message); ++} ++ ++static void ++shell_brightness_set_dimming (GsdPowerManager *manager, ++ gboolean enable) ++{ ++ if (!manager->shell_brightness_proxy) ++ return; ++ ++ g_dbus_proxy_call (G_DBUS_PROXY (manager->shell_brightness_proxy), ++ "SetDimming", ++ g_variant_new ("(b)", enable), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, NULL, ++ shell_brightness_set_dimming_cb, ++ GUINT_TO_POINTER (enable)); ++} + + static void + light_claimed_cb (GObject *source_object, +@@ -3062,6 +3155,21 @@ gsd_power_manager_start (GsdPowerManager *manager, + power_keyboard_proxy_ready_cb, + manager); + ++ manager->shell_brightness_proxy = ++ g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ NULL, ++ SHELL_BRIGHTNESS_DBUS_NAME, ++ SHELL_BRIGHTNESS_DBUS_PATH, ++ SHELL_BRIGHTNESS_DBUS_INTERFACE, ++ NULL, ++ error); ++ if (manager->shell_brightness_proxy == NULL) { ++ g_debug ("No org.gnome.Shell.Brightness support, " ++ "dimming and auto brightness is disabled"); ++ g_clear_error (error); ++ } ++ + manager->devices_array = g_ptr_array_new_with_free_func (g_object_unref); + manager->devices_notified_ht = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); +@@ -3165,6 +3273,7 @@ gsd_power_manager_stop (GsdPowerManager *manager) + + g_clear_object (&manager->idle_monitor); + g_clear_object (&manager->upower_kbd_proxy); ++ g_clear_object (&manager->shell_brightness_proxy); + + if (manager->xscreensaver_watchdog_timer_id > 0) { + g_source_remove (manager->xscreensaver_watchdog_timer_id); +-- +2.51.1 + + +From f5b14548b3cf62a5f38394a93b40431d65dd58b8 Mon Sep 17 00:00:00 2001 +From: Sebastian Wick +Date: Fri, 13 Jun 2025 00:54:01 +0200 +Subject: [PATCH 07/10] power: Switch from direct mutter backlight handling to + Shell.Brightness + +The shell handles dimming and handling the auto brightness target and is +the single source of truth for the actual display brightness control. We +just send semantically useful information to it. +--- + plugins/power/gsd-backlight.c | 558 ---------------------------- + plugins/power/gsd-backlight.h | 76 ---- + plugins/power/gsd-power-constants.h | 4 - + plugins/power/gsd-power-manager.c | 216 +---------- + plugins/power/meson.build | 1 - + plugins/power/test.py | 173 +-------- + 6 files changed, 29 insertions(+), 999 deletions(-) + delete mode 100644 plugins/power/gsd-backlight.c + delete mode 100644 plugins/power/gsd-backlight.h + +diff --git a/plugins/power/gsd-backlight.c b/plugins/power/gsd-backlight.c +deleted file mode 100644 +index a1fa2efb..00000000 +--- a/plugins/power/gsd-backlight.c ++++ /dev/null +@@ -1,558 +0,0 @@ +-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- +- * +- * Copyright (C) 2018 Red Hat Inc. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, see . +- * +- */ +- +-#include "config.h" +-#include +-#include +- +-#include "gnome-settings-bus.h" +-#include "gsd-backlight.h" +-#include "gpm-common.h" +-#include "gsd-power-constants.h" +-#include "gsd-power-manager.h" +- +-struct _GsdBacklight +-{ +- GObject object; +- +- gint brightness_min; +- gint brightness_max; +- gint brightness_val; +- gint brightness_target; +- gint brightness_step; +- +- uint32_t backlight_serial; +- char *backlight_connector; +- +- gboolean initialized; +- +- gboolean builtin_display_disabled; +-}; +- +-enum { +- PROP_BRIGHTNESS = 1, +- PROP_LAST, +-}; +- +-#define MUTTER_DBUS_NAME "org.gnome.Mutter.DisplayConfig" +-#define MUTTER_DBUS_PATH "/org/gnome/Mutter/DisplayConfig" +-#define MUTTER_DBUS_INTERFACE "org.gnome.Mutter.DisplayConfig" +- +-static GParamSpec *props[PROP_LAST]; +- +-static void gsd_backlight_initable_iface_init (GInitableIface *iface); +-static gboolean gsd_backlight_initable_init (GInitable *initable, +- GCancellable *cancellable, +- GError **error); +- +-G_DEFINE_TYPE_EXTENDED (GsdBacklight, gsd_backlight, G_TYPE_OBJECT, 0, +- G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, +- gsd_backlight_initable_iface_init)) +- +-/** +- * gsd_backlight_get_brightness +- * @backlight: a #GsdBacklight +- * @target: Output parameter for the value the target value of pending set operations. +- * +- * The backlight value returns the last known stable value. This value will +- * only update once all pending operations to set a new value have finished. +- * +- * As such, this function may return a different value from the return value +- * of the async brightness setter. This happens when another set operation was +- * queued after it was already running. +- * +- * If the internal display is detected as disabled, then the function will +- * instead return -1. +- * +- * Returns: The last stable backlight value or -1 if the internal display is disabled. +- **/ +-gint +-gsd_backlight_get_brightness (GsdBacklight *backlight, gint *target) +-{ +- if (backlight->builtin_display_disabled) +- return -1; +- +- return ABS_TO_PERCENTAGE (backlight->brightness_min, +- backlight->brightness_max, +- backlight->brightness_val); +-} +- +-/** +- * gsd_backlight_get_target_brightness +- * @backlight: a #GsdBacklight +- * +- * This returns the target value of the pending operations, which might differ +- * from the last known stable value, but is identical to the last value set in +- * the async setter. +- * +- * If the internal display is detected as disabled, then the function will +- * instead return -1. +- * +- * Returns: The current target backlight value or -1 if the internal display is disabled. +- **/ +-gint +-gsd_backlight_get_target_brightness (GsdBacklight *backlight) +-{ +- if (backlight->builtin_display_disabled) +- return -1; +- +- return ABS_TO_PERCENTAGE (backlight->brightness_min, +- backlight->brightness_max, +- backlight->brightness_target); +-} +- +-static void update_mutter_backlight (GsdBacklight *backlight); +- +-static void +-gsd_backlight_set_brightness_val_async (GsdBacklight *backlight, +- int value, +- GCancellable *cancellable, +- GAsyncReadyCallback callback, +- gpointer user_data) +-{ +- GError *error = NULL; +- g_autoptr(GTask) task = NULL; +- const char *monitor; +- GsdDisplayConfig *display_config = +- gnome_settings_bus_get_display_config_proxy (); +- +- value = MIN(backlight->brightness_max, value); +- value = MAX(backlight->brightness_min, value); +- +- backlight->brightness_target = value; +- +- task = g_task_new (backlight, cancellable, callback, user_data); +- if (!backlight->initialized) { +- g_task_return_new_error (task, GSD_POWER_MANAGER_ERROR, +- GSD_POWER_MANAGER_ERROR_FAILED, +- "No backend initialized yet"); +- return; +- } +- +- monitor = backlight->backlight_connector; +- if (!monitor) { +- g_assert_not_reached (); +- +- g_task_return_new_error (task, GSD_POWER_MANAGER_ERROR, +- GSD_POWER_MANAGER_ERROR_FAILED, +- "No method to set brightness!"); +- return; +- } +- +- while (TRUE) { +- uint32_t serial = backlight->backlight_serial; +- +- g_clear_error (&error); +- +- if (gsd_display_config_call_set_backlight_sync (display_config, +- serial, +- monitor, +- value, +- NULL, +- &error)) +- break; +- +- update_mutter_backlight (backlight); +- if (backlight->backlight_serial == serial) { +- g_task_return_error (task, error); +- return; +- } +- } +- +- backlight->brightness_val = value; +- g_object_notify_by_pspec (G_OBJECT (backlight), props[PROP_BRIGHTNESS]); +- g_task_return_int (task, gsd_backlight_get_brightness (backlight)); +- +-} +- +-void +-gsd_backlight_set_brightness_async (GsdBacklight *backlight, +- gint percent, +- GCancellable *cancellable, +- GAsyncReadyCallback callback, +- gpointer user_data) +-{ +- /* Overflow/underflow is handled by gsd_backlight_set_brightness_val_async. */ +- gsd_backlight_set_brightness_val_async (backlight, +- PERCENTAGE_TO_ABS (backlight->brightness_min, +- backlight->brightness_max, +- percent), +- cancellable, +- callback, +- user_data); +-} +- +-/** +- * gsd_backlight_set_brightness_finish +- * @backlight: a #GsdBacklight +- * @res: the #GAsyncResult passed to the callback +- * @error: #GError return address +- * +- * Finish an operation started by gsd_backlight_set_brightness_async(). Will +- * return the value that was actually set (which may be different because of +- * rounding or as multiple set actions were queued up). +- * +- * Please note that a call to gsd_backlight_get_brightness() may not in fact +- * return the same value if further operations to set the value are pending. +- * +- * Returns: The brightness in percent that was set. +- **/ +-gint +-gsd_backlight_set_brightness_finish (GsdBacklight *backlight, +- GAsyncResult *res, +- GError **error) +-{ +- return g_task_propagate_int (G_TASK (res), error); +-} +- +-void +-gsd_backlight_step_up_async (GsdBacklight *backlight, +- GCancellable *cancellable, +- GAsyncReadyCallback callback, +- gpointer user_data) +-{ +- gint value; +- +- /* Overflows are handled by gsd_backlight_set_brightness_val_async. */ +- value = backlight->brightness_target + backlight->brightness_step; +- +- gsd_backlight_set_brightness_val_async (backlight, +- value, +- cancellable, +- callback, +- user_data); +-} +- +-/** +- * gsd_backlight_step_up_finish +- * @backlight: a #GsdBacklight +- * @res: the #GAsyncResult passed to the callback +- * @error: #GError return address +- * +- * Finish an operation started by gsd_backlight_step_up_async(). Will return +- * the value that was actually set (which may be different because of rounding +- * or as multiple set actions were queued up). +- * +- * Please note that a call to gsd_backlight_get_brightness() may not in fact +- * return the same value if further operations to set the value are pending. +- * +- * For simplicity it is also valid to call gsd_backlight_set_brightness_finish() +- * allowing sharing the callback routine for calls to +- * gsd_backlight_set_brightness_async(), gsd_backlight_step_up_async(), +- * gsd_backlight_step_down_async() and gsd_backlight_cycle_up_async(). +- * +- * Returns: The brightness in percent that was set. +- **/ +-gint +-gsd_backlight_step_up_finish (GsdBacklight *backlight, +- GAsyncResult *res, +- GError **error) +-{ +- return g_task_propagate_int (G_TASK (res), error); +-} +- +-void +-gsd_backlight_step_down_async (GsdBacklight *backlight, +- GCancellable *cancellable, +- GAsyncReadyCallback callback, +- gpointer user_data) +-{ +- gint value; +- +- /* Underflows are handled by gsd_backlight_set_brightness_val_async. */ +- value = backlight->brightness_target - backlight->brightness_step; +- +- gsd_backlight_set_brightness_val_async (backlight, +- value, +- cancellable, +- callback, +- user_data); +-} +- +-/** +- * gsd_backlight_step_down_finish +- * @backlight: a #GsdBacklight +- * @res: the #GAsyncResult passed to the callback +- * @error: #GError return address +- * +- * Finish an operation started by gsd_backlight_step_down_async(). Will return +- * the value that was actually set (which may be different because of rounding +- * or as multiple set actions were queued up). +- * +- * Please note that a call to gsd_backlight_get_brightness() may not in fact +- * return the same value if further operations to set the value are pending. +- * +- * For simplicity it is also valid to call gsd_backlight_set_brightness_finish() +- * allowing sharing the callback routine for calls to +- * gsd_backlight_set_brightness_async(), gsd_backlight_step_up_async(), +- * gsd_backlight_step_down_async() and gsd_backlight_cycle_up_async(). +- * +- * Returns: The brightness in percent that was set. +- **/ +-gint +-gsd_backlight_step_down_finish (GsdBacklight *backlight, +- GAsyncResult *res, +- GError **error) +-{ +- return g_task_propagate_int (G_TASK (res), error); +-} +- +-/** +- * gsd_backlight_cycle_up_async +- * @backlight: a #GsdBacklight +- * @cancellable: an optional #GCancellable, NULL to ignore +- * @callback: the #GAsyncReadyCallback invoked for cycle up to be finished +- * @user_data: the #gpointer passed to the callback +- * +- * Start a brightness cycle up operation by gsd_backlight_cycle_up_async(). +- * The brightness will be stepped up if it is not already at the maximum. +- * If it is already at the maximum, it will be set to the minimum brightness. +- **/ +-void +-gsd_backlight_cycle_up_async (GsdBacklight *backlight, +- GCancellable *cancellable, +- GAsyncReadyCallback callback, +- gpointer user_data) +-{ +- if (backlight->brightness_target < backlight->brightness_max) { +- gsd_backlight_step_up_async (backlight, +- cancellable, +- callback, +- user_data); +- } else { +- gsd_backlight_set_brightness_val_async (backlight, +- backlight->brightness_min, +- cancellable, +- callback, +- user_data); +- } +-} +- +-/** +- * gsd_backlight_cycle_up_finish +- * @backlight: a #GsdBacklight +- * @res: the #GAsyncResult passed to the callback +- * @error: #GError return address +- * +- * Finish an operation started by gsd_backlight_cycle_up_async(). Will return +- * the value that was actually set (which may be different because of rounding +- * or as multiple set actions were queued up). +- * +- * Please note that a call to gsd_backlight_get_brightness() may not in fact +- * return the same value if further operations to set the value are pending. +- * +- * For simplicity it is also valid to call gsd_backlight_set_brightness_finish() +- * allowing sharing the callback routine for calls to +- * gsd_backlight_set_brightness_async(), gsd_backlight_step_up_async(), +- * gsd_backlight_step_down_async() and gsd_backlight_cycle_up_async(). +- * +- * Returns: The brightness in percent that was set. +- **/ +-gint +-gsd_backlight_cycle_up_finish (GsdBacklight *backlight, +- GAsyncResult *res, +- GError **error) +-{ +- return g_task_propagate_int (G_TASK (res), error); +-} +- +-/** +- * gsd_backlight_get_connector +- * @backlight: a #GsdBacklight +- * +- * Return the connector for the display that is being controlled by the +- * #GsdBacklight object. This connector can be passed to gnome-shell to show +- * the on screen display only on the affected screen. +- * +- * Returns: The connector of the controlled output or NULL if unknown. +- **/ +-const char* +-gsd_backlight_get_connector (GsdBacklight *backlight) +-{ +- return backlight->backlight_connector; +-} +- +-static void +-gsd_backlight_get_property (GObject *object, +- guint prop_id, +- GValue *value, +- GParamSpec *pspec) +-{ +- GsdBacklight *backlight = GSD_BACKLIGHT (object); +- +- switch (prop_id) { +- case PROP_BRIGHTNESS: +- g_value_set_int (value, gsd_backlight_get_brightness (backlight, NULL)); +- break; +- +- default: +- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +- break; +- } +-} +- +-static void +-update_mutter_backlight (GsdBacklight *backlight) +-{ +- GsdDisplayConfig *display_config = +- gnome_settings_bus_get_display_config_proxy (); +- GVariant *backlights = NULL; +- g_autoptr(GVariant) monitors = NULL; +- g_autoptr(GVariant) monitor = NULL; +- gboolean monitor_active = FALSE; +- +- backlights = gsd_display_config_get_backlight (display_config); +- g_return_if_fail (backlights != NULL); +- +- g_variant_get (backlights, "(u@aa{sv})", +- &backlight->backlight_serial, +- &monitors); +- if (g_variant_n_children (monitors) > 1) { +- g_warning ("Only handling the first out of %lu backlight monitors", +- g_variant_n_children (monitors)); +- } +- +- if (g_variant_n_children (monitors) > 0) { +- g_variant_get_child (monitors, 0, "@a{sv}", &monitor); +- +- g_clear_pointer (&backlight->backlight_connector, g_free); +- g_variant_lookup (monitor, "connector", "s", +- &backlight->backlight_connector); +- g_variant_lookup (monitor, "active", "b", &monitor_active); +- backlight->builtin_display_disabled = !monitor_active; +- +- if (g_variant_lookup (monitor, "value", "i", &backlight->brightness_val)) { +- g_variant_lookup (monitor, "min", "i", &backlight->brightness_min); +- g_variant_lookup (monitor, "max", "i", &backlight->brightness_max); +- backlight->initialized = TRUE; +- } else { +- backlight->brightness_val = -1; +- backlight->brightness_min = -1; +- backlight->brightness_max = -1; +- } +- } +-} +- +-static void +-maybe_update_mutter_backlight (GsdBacklight *backlight) +-{ +- GsdDisplayConfig *display_config = +- gnome_settings_bus_get_display_config_proxy (); +- g_autofree char *name_owner = NULL; +- +- if ((name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (display_config)))) +- update_mutter_backlight (backlight); +-} +- +-static void +-on_backlight_changed (GsdDisplayConfig *display_config, +- GParamSpec *pspec, +- GsdBacklight *backlight) +-{ +- gboolean builtin_display_disabled; +- +- builtin_display_disabled = backlight->builtin_display_disabled; +- update_mutter_backlight (backlight); +- +- if (builtin_display_disabled != backlight->builtin_display_disabled) +- g_object_notify_by_pspec (G_OBJECT (backlight), props[PROP_BRIGHTNESS]); +-} +- +-static gboolean +-gsd_backlight_initable_init (GInitable *initable, +- GCancellable *cancellable, +- GError **error) +-{ +- GsdBacklight *backlight = GSD_BACKLIGHT (initable); +- GsdDisplayConfig *display_config = +- gnome_settings_bus_get_display_config_proxy (); +- +- if (cancellable != NULL) { +- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, +- "GsdBacklight does not support cancelling initialization."); +- return FALSE; +- } +- +- if (!display_config) { +- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, +- "GsdBacklight needs org.gnome.Mutter.DisplayConfig to function"); +- return FALSE; +- } +- +- g_signal_connect_object (display_config, +- "notify::backlight", +- G_CALLBACK (on_backlight_changed), +- backlight, +- 0); +- +- g_signal_connect_object (display_config, +- "notify::g-name-owner", +- G_CALLBACK (maybe_update_mutter_backlight), +- backlight, +- G_CONNECT_SWAPPED); +- maybe_update_mutter_backlight (backlight); +- +- backlight->brightness_target = backlight->brightness_val; +- backlight->brightness_step = +- MAX(backlight->brightness_step, +- BRIGHTNESS_STEP_AMOUNT(backlight->brightness_max - backlight->brightness_min + 1)); +- +- g_debug ("Step size for backlight is %i.", backlight->brightness_step); +- +- return TRUE; +-} +- +-static void +-gsd_backlight_initable_iface_init (GInitableIface *iface) +-{ +- iface->init = gsd_backlight_initable_init; +-} +- +-static void +-gsd_backlight_class_init (GsdBacklightClass *klass) +-{ +- GObjectClass *object_class = G_OBJECT_CLASS (klass); +- +- object_class->get_property = gsd_backlight_get_property; +- +- props[PROP_BRIGHTNESS] = g_param_spec_int ("brightness", "The display brightness", +- "The brightness of the internal display in percent.", +- 0, 100, 100, +- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); +- +- g_object_class_install_properties (object_class, PROP_LAST, props); +-} +- +-static void +-gsd_backlight_init (GsdBacklight *backlight) +-{ +- backlight->brightness_target = -1; +- backlight->brightness_min = -1; +- backlight->brightness_max = -1; +- backlight->brightness_val = -1; +- backlight->brightness_step = 1; +-} +- +-GsdBacklight * +-gsd_backlight_new (GError **error) +-{ +- return GSD_BACKLIGHT (g_initable_new (GSD_TYPE_BACKLIGHT, NULL, error, +- NULL)); +-} +- +diff --git a/plugins/power/gsd-backlight.h b/plugins/power/gsd-backlight.h +deleted file mode 100644 +index 56216b62..00000000 +--- a/plugins/power/gsd-backlight.h ++++ /dev/null +@@ -1,76 +0,0 @@ +-/* -*- mode: c; style: linux -*- +- * +- * Copyright (C) 2017 Red Hat, Inc. +- * +- * Written by: Benjamin Berg +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2, or (at your option) +- * any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, see . +- */ +- +-#ifndef _GSD_BACKLIGHT_H +-#define _GSD_BACKLIGHT_H +- +-#include +-#include +- +-G_BEGIN_DECLS +- +-#define GSD_TYPE_BACKLIGHT gsd_backlight_get_type () +-G_DECLARE_FINAL_TYPE (GsdBacklight, gsd_backlight, GSD, BACKLIGHT, GObject); +- +-gint gsd_backlight_get_brightness (GsdBacklight *backlight, +- gint *target); +- +-void gsd_backlight_set_brightness_async (GsdBacklight *backlight, +- gint percentage, +- GCancellable *cancellable, +- GAsyncReadyCallback callback, +- gpointer user_data); +-void gsd_backlight_step_up_async (GsdBacklight *backlight, +- GCancellable *cancellable, +- GAsyncReadyCallback callback, +- gpointer user_data); +-void gsd_backlight_step_down_async (GsdBacklight *backlight, +- GCancellable *cancellable, +- GAsyncReadyCallback callback, +- gpointer user_data); +-void gsd_backlight_cycle_up_async (GsdBacklight *backlight, +- GCancellable *cancellable, +- GAsyncReadyCallback callback, +- gpointer user_data); +- +-gint gsd_backlight_set_brightness_finish (GsdBacklight *backlight, +- GAsyncResult *res, +- GError **error); +- +-gint gsd_backlight_step_up_finish (GsdBacklight *backlight, +- GAsyncResult *res, +- GError **error); +- +-gint gsd_backlight_step_down_finish (GsdBacklight *backlight, +- GAsyncResult *res, +- GError **error); +- +-gint gsd_backlight_cycle_up_finish (GsdBacklight *backlight, +- GAsyncResult *res, +- GError **error); +- +-const char* gsd_backlight_get_connector (GsdBacklight *backlight); +- +-GsdBacklight* gsd_backlight_new (GError **error); +- +- +-G_END_DECLS +- +-#endif /* _GSD_BACKLIGHT_H */ +diff --git a/plugins/power/gsd-power-constants.h b/plugins/power/gsd-power-constants.h +index 8bf9c641..6d1be7a7 100644 +--- a/plugins/power/gsd-power-constants.h ++++ b/plugins/power/gsd-power-constants.h +@@ -33,10 +33,6 @@ + /* The amount of time we'll undim if the machine is idle when plugged in */ + #define POWER_UP_TIME_ON_AC 15 /* seconds */ + +-/* Default brightness values for the mock backlight used in the test suite */ +-#define GSD_MOCK_DEFAULT_BRIGHTNESS 50 +-#define GSD_MOCK_MAX_BRIGHTNESS 100 +- + /* When unplugging the external monitor, give a certain amount + * of time before suspending the laptop */ + #define LID_CLOSE_SAFETY_TIMEOUT 8 /* seconds */ +diff --git a/plugins/power/gsd-power-manager.c b/plugins/power/gsd-power-manager.c +index 5d1fb33b..21cdaa85 100644 +--- a/plugins/power/gsd-power-manager.c ++++ b/plugins/power/gsd-power-manager.c +@@ -42,7 +42,6 @@ + #include "gsm-presence-flag.h" + #include "gsm-manager-logout-mode.h" + #include "gpm-common.h" +-#include "gsd-backlight.h" + #include "gnome-settings-profile.h" + #include "gnome-settings-bus.h" + #include "gsd-enums.h" +@@ -69,7 +68,6 @@ + #define GSD_POWER_DBUS_NAME GSD_DBUS_NAME ".Power" + #define GSD_POWER_DBUS_PATH GSD_DBUS_PATH "/Power" + #define GSD_POWER_DBUS_INTERFACE GSD_DBUS_BASE_INTERFACE ".Power" +-#define GSD_POWER_DBUS_INTERFACE_SCREEN GSD_POWER_DBUS_INTERFACE ".Screen" + #define GSD_POWER_DBUS_INTERFACE_KEYBOARD GSD_POWER_DBUS_INTERFACE ".Keyboard" + + #define GSD_POWER_MANAGER_NOTIFY_TIMEOUT_SHORT 10 * 1000 /* ms */ +@@ -98,21 +96,6 @@ + + static const gchar introspection_xml[] = + "" +-" " +-" " +-" " +-" " +-" " +-" " +-" " +-" " +-" " +-" " +-" " +-" " +-" " +-" " +-" " + " " + " " + " " +@@ -174,8 +157,6 @@ struct _GsdPowerManager + gboolean battery_is_low; /* battery low, or UPS discharging */ + + /* Brightness */ +- GsdBacklight *backlight; +- gint pre_dim_brightness; /* level, not percentage */ + GDBusProxy *shell_brightness_proxy; + + /* Keyboard */ +@@ -1330,7 +1311,7 @@ iio_proxy_claim_light (GsdPowerManager *manager, gboolean active) + { + if (manager->iio_proxy == NULL) + return; +- if (!manager->backlight) ++ if (!shell_brightness_has_control (manager)) + return; + if (active && !manager->session_is_active) + return; +@@ -1721,32 +1702,6 @@ backlight_iface_emit_changed (GsdPowerManager *manager, + NULL); + } + +-static void +-backlight_notify_brightness_cb (GsdPowerManager *manager, GParamSpec *pspec, GsdBacklight *backlight) +-{ +- backlight_iface_emit_changed (manager, GSD_POWER_DBUS_INTERFACE_SCREEN, +- gsd_backlight_get_brightness (backlight, NULL), NULL); +-} +- +-static void +-display_backlight_dim (GsdPowerManager *manager, +- gint idle_percentage) +-{ +- gint brightness; +- +- if (!manager->backlight) +- return; +- +- /* Fetch the current target brightness (not the actual display brightness) +- * and return if it is already lower than the idle percentage. */ +- gsd_backlight_get_brightness (manager->backlight, &brightness); +- if (brightness < idle_percentage) +- return; +- +- manager->pre_dim_brightness = brightness; +- gsd_backlight_set_brightness_async (manager->backlight, idle_percentage, NULL, NULL, NULL); +-} +- + static gboolean + kbd_backlight_dim (GsdPowerManager *manager, + gint idle_percentage, +@@ -1868,7 +1823,7 @@ idle_set_mode (GsdPowerManager *manager, GsdPowerIdleMode mode) + /* display backlight */ + idle_percentage = g_settings_get_int (manager->settings, + "idle-brightness"); +- display_backlight_dim (manager, idle_percentage); ++ shell_brightness_set_dimming (manager, TRUE); + + /* keyboard backlight */ + ret = kbd_backlight_dim (manager, idle_percentage, &error); +@@ -1911,14 +1866,7 @@ idle_set_mode (GsdPowerManager *manager, GsdPowerIdleMode mode) + + enable_monitors (manager); + +- /* reset brightness if we dimmed */ +- if (manager->backlight && manager->pre_dim_brightness >= 0) { +- gsd_backlight_set_brightness_async (manager->backlight, +- manager->pre_dim_brightness, +- NULL, NULL, NULL); +- /* XXX: Ideally we would do this from the async callback. */ +- manager->pre_dim_brightness = -1; +- } ++ shell_brightness_set_dimming (manager, FALSE); + + /* only toggle keyboard if present and already toggled off */ + if (manager->upower_kbd_proxy && +@@ -2919,7 +2867,7 @@ iio_proxy_changed (GsdPowerManager *manager) + gint pc; + + /* no display hardware */ +- if (!manager->backlight) ++ if (!shell_brightness_has_control (manager)) + return; + + /* disabled */ +@@ -2969,8 +2917,7 @@ iio_proxy_changed (GsdPowerManager *manager) + manager->ambient_accumulator); + pc = manager->ambient_accumulator; + +- if (manager->backlight) +- gsd_backlight_set_brightness_async (manager->backlight, pc, NULL, NULL, NULL); ++ shell_brightness_set_auto_target (manager, pc / 100); + + /* Assume setting worked. */ + manager->ambient_percentage_old = pc; +@@ -3079,14 +3026,6 @@ gsd_power_manager_start (GsdPowerManager *manager, + manager->ambient_last_absolute = -1.f; + manager->ambient_last_time = 0; + +- manager->backlight = gsd_backlight_new (NULL); +- +- if (manager->backlight) +- g_signal_connect_object (manager->backlight, +- "notify::brightness", +- G_CALLBACK (backlight_notify_brightness_cb), +- manager, G_CONNECT_SWAPPED); +- + /* Set up a delay inhibitor to be informed about suspend attempts */ + g_signal_connect (manager->logind_proxy, "g-signal", + G_CALLBACK (logind_proxy_signal_cb), +@@ -3118,7 +3057,6 @@ gsd_power_manager_start (GsdPowerManager *manager, + + manager->kbd_brightness_old = -1; + manager->kbd_brightness_pre_dim = -1; +- manager->pre_dim_brightness = -1; + g_signal_connect (manager->settings, "changed", + G_CALLBACK (engine_settings_key_changed_cb), manager); + g_signal_connect (manager->settings_bus, "changed", +@@ -3192,19 +3130,6 @@ gsd_power_manager_start (GsdPowerManager *manager, + if (!gnome_settings_is_wayland ()) + manager->xscreensaver_watchdog_timer_id = gsd_power_enable_screensaver_watchdog (); + +- /* queue a signal in case the proxy from gnome-shell was created before we got here +- (likely, considering that to get here we need a reply from gnome-shell) +- */ +- if (manager->backlight) { +- manager->ambient_percentage_old = gsd_backlight_get_brightness (manager->backlight, NULL); +- backlight_iface_emit_changed (manager, GSD_POWER_DBUS_INTERFACE_SCREEN, +- manager->ambient_percentage_old, NULL); +- } else { +- backlight_iface_emit_changed (manager, GSD_POWER_DBUS_INTERFACE_SCREEN, -1, NULL); +- } +- +- gnome_settings_profile_end (NULL); +- + gnome_settings_profile_end (NULL); + return TRUE; + } +@@ -3339,91 +3264,6 @@ handle_method_call_keyboard (GsdPowerManager *manager, + } + } + +-static void +-backlight_brightness_step_cb (GObject *object, +- GAsyncResult *res, +- gpointer user_data) +-{ +- GsdBacklight *backlight = GSD_BACKLIGHT (object); +- GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data); +- GsdPowerManager *manager; +- GError *error = NULL; +- const char *connector; +- gint brightness; +- +- manager = g_object_get_data (G_OBJECT (invocation), "gsd-power-manager"); +- brightness = gsd_backlight_set_brightness_finish (backlight, res, &error); +- +- /* ambient brightness no longer valid */ +- manager->ambient_percentage_old = brightness; +- manager->ambient_norm_required = TRUE; +- +- if (error) { +- g_dbus_method_invocation_take_error (invocation, +- error); +- } else { +- connector = gsd_backlight_get_connector (backlight); +- +- g_dbus_method_invocation_return_value (invocation, +- g_variant_new ("(is)", +- brightness, +- connector ? connector : "")); +- } +-} +- +-/* Callback */ +-static void +-backlight_brightness_set_cb (GObject *object, +- GAsyncResult *res, +- gpointer user_data) +-{ +- GsdPowerManager *manager = GSD_POWER_MANAGER (user_data); +- GsdBacklight *backlight = GSD_BACKLIGHT (object); +- gint brightness; +- +- /* Return the invocation. */ +- brightness = gsd_backlight_set_brightness_finish (backlight, res, NULL); +- +- if (brightness >= 0) { +- manager->ambient_percentage_old = brightness; +- manager->ambient_norm_required = TRUE; +- } +- +- g_object_unref (manager); +-} +- +-static void +-handle_method_call_screen (GsdPowerManager *manager, +- const gchar *method_name, +- GVariant *parameters, +- GDBusMethodInvocation *invocation) +-{ +- if (!manager->backlight) { +- g_dbus_method_invocation_return_error_literal (invocation, +- GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_NO_BACKLIGHT, +- "No usable backlight could be found!"); +- return; +- } +- +- g_object_set_data_full (G_OBJECT (invocation), "gsd-power-manager", g_object_ref (manager), g_object_unref); +- +- if (g_strcmp0 (method_name, "StepUp") == 0) { +- g_debug ("screen step up"); +- gsd_backlight_step_up_async (manager->backlight, NULL, backlight_brightness_step_cb, invocation); +- +- } else if (g_strcmp0 (method_name, "StepDown") == 0) { +- g_debug ("screen step down"); +- gsd_backlight_step_down_async (manager->backlight, NULL, backlight_brightness_step_cb, invocation); +- +- } else if (g_strcmp0 (method_name, "Cycle") == 0) { +- g_debug ("screen cycle up"); +- gsd_backlight_cycle_up_async (manager->backlight, NULL, backlight_brightness_step_cb, invocation); +- +- } else { +- g_assert_not_reached (); +- } +-} +- + static void + handle_method_call (GDBusConnection *connection, + const gchar *sender, +@@ -3445,12 +3285,7 @@ handle_method_call (GDBusConnection *connection, + g_debug ("Calling method '%s.%s' for Power", + interface_name, method_name); + +- if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_SCREEN) == 0) { +- handle_method_call_screen (manager, +- method_name, +- parameters, +- invocation); +- } else if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_KEYBOARD) == 0) { ++ if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_KEYBOARD) == 0) { + handle_method_call_keyboard (manager, + method_name, + parameters, +@@ -3470,20 +3305,7 @@ handle_get_property_other (GsdPowerManager *manager, + gint32 value; + + +- if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_SCREEN) == 0) { +- if (g_strcmp0 (property_name, "Brightness") != 0) { +- g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, +- "No such property: %s", property_name); +- return NULL; +- } +- +- if (manager->backlight) +- value = gsd_backlight_get_brightness (manager->backlight, NULL); +- else +- value = -1; +- +- retval = g_variant_new_int32 (value); +- } else if (manager->upower_kbd_proxy && ++ if (manager->upower_kbd_proxy && + g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_KEYBOARD) == 0) { + if (g_strcmp0 (property_name, "Brightness") == 0) { + value = ABS_TO_PERCENTAGE (0, +@@ -3522,8 +3344,7 @@ handle_get_property (GDBusConnection *connection, + return NULL; + } + +- if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_SCREEN) == 0 || +- g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_KEYBOARD) == 0) { ++ if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_KEYBOARD) == 0) { + return handle_get_property_other (manager, interface_name, property_name, error); + } else { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, +@@ -3547,23 +3368,7 @@ handle_set_property_other (GsdPowerManager *manager, + return FALSE; + } + +- if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_SCREEN) == 0) { +- /* To do error reporting we would need to handle the Set call +- * instead of doing it through set_property. +- * But none of our DBus API users actually read the result. */ +- g_variant_get (value, "i", &brightness_value); +- if (manager->backlight) { +- gsd_backlight_set_brightness_async (manager->backlight, brightness_value, +- NULL, +- backlight_brightness_set_cb, g_object_ref (manager)); +- return TRUE; +- } else { +- g_set_error_literal (error, GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_NO_BACKLIGHT, +- "No usable backlight could be found!"); +- return FALSE; +- } +- +- } else if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_KEYBOARD) == 0) { ++ if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_KEYBOARD) == 0) { + g_variant_get (value, "i", &brightness_value); + brightness_value = PERCENTAGE_TO_ABS (0, manager->kbd_brightness_max, + brightness_value); +@@ -3602,8 +3407,7 @@ handle_set_property (GDBusConnection *connection, + return FALSE; + } + +- if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_SCREEN) == 0 || +- g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_KEYBOARD) == 0) { ++ if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_KEYBOARD) == 0) { + return handle_set_property_other (manager, interface_name, property_name, value, error); + } else { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, +diff --git a/plugins/power/meson.build b/plugins/power/meson.build +index e44f8ce9..e9a01308 100644 +--- a/plugins/power/meson.build ++++ b/plugins/power/meson.build +@@ -1,6 +1,5 @@ + sources = files( + 'gpm-common.c', +- 'gsd-backlight.c', + 'gsd-power-manager.c', + 'main.c' + ) +diff --git a/plugins/power/test.py b/plugins/power/test.py +index 6633c00e..b475c964 100755 +--- a/plugins/power/test.py ++++ b/plugins/power/test.py +@@ -106,11 +106,6 @@ class PowerPluginBase(gsdtestcase.GSDTestCase): + (self.mockmutter, self.obj_mockmutter) = self.spawn_server_template(dbusmock_template('mutter')) + self.addCleanup(self.stop_process, self.mockmutter) + +- if 'legacy_brightness' in self.id(): +- self.obj_mockmutter.MockSetBacklight('dp-1', True, 0, 15, 15) +- else: +- self.obj_mockmutter.MockSetBacklight('dp-1', True, 0, 100, 50) +- + # start mock gnome-shell + (self.gnomeshell, self.obj_gnomeshell) = self.spawn_server_template(dbusmock_template('gnome-shell')) + self.addCleanup(self.stop_process, self.gnomeshell) +@@ -164,14 +159,6 @@ class PowerPluginBase(gsdtestcase.GSDTestCase): + # Disable PulseAudio output from libcanberra + env['CANBERRA_DRIVER'] = 'null' + +- # Use dummy script as testing backlight helper +- env['GSD_BACKLIGHT_HELPER'] = os.path.join (project_root, 'plugins', 'power', 'test-backlight-helper') +- if 'POWER_LD_PRELOAD' in env: +- if 'LD_PRELOAD' in env and env['LD_PRELOAD']: +- env['LD_PRELOAD'] = ':'.join((env['POWER_LD_PRELOAD'], env['LD_PRELOAD'])) +- else: +- env['LD_PRELOAD'] = env['POWER_LD_PRELOAD'] +- + self.start_plugin(env) + self.addCleanup(self.stop_plugin) + +@@ -212,18 +199,6 @@ class PowerPluginBase(gsdtestcase.GSDTestCase): + def get_status(self): + return self.obj_session_presence_props.Get('org.gnome.SessionManager.Presence', 'status') + +- def get_brightness(self): +- (serial, bl) = self.obj_mockmutter.Get('org.gnome.Mutter.DisplayConfig', 'Backlight') +- assert bl +- bl = bl[0] +- assert bl +- assert bl['active'] == True +- +- max_brightness = bl['max'] +- value = bl['value'] +- +- return value +- + def set_has_external_monitor(self, external): + if external: + val = b'1' +@@ -431,7 +406,11 @@ class PowerPluginTestScreensaver(PowerPluginBase): + self.reset_idle_timer() + self.check_unblank(2) + +- self.assertTrue(self.get_brightness() == gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS , 'incorrect unblanked brightness (%d != %d)' % (self.get_brightness(), gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS)) ++ mock_intf = dbus.Interface(self.obj_gnomeshell, dbusmock.MOCK_IFACE) ++ method_calls = mock_intf.GetMethodCalls("SetDimming") ++ self.assertEqual(len(method_calls), 1, 'did not call dim') ++ _, args = method_calls[-1] ++ self.assertTrue(args[0] == False, 'expected disabled dimming') + + # Check for no blank before the normal blank timeout + self.check_no_blank(gsdpowerconstants.SCREENSAVER_TIMEOUT_BLANK - 4) +@@ -666,6 +645,7 @@ class PowerPluginTestLid(PowerPluginBase): + self.obj_upower.Set('org.freedesktop.UPower', 'LidIsClosed', False) + self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') + ++ # FIXME: this check fails + # Check for unblanking + self.check_unblank(2) + +@@ -687,18 +667,19 @@ class PowerPluginTestDim(PowerPluginBase): + self.settings_gsd_power['sleep-inactive-battery-timeout'] = idle_delay + 1 + self.settings_gsd_power['sleep-inactive-battery-type'] = 'suspend' + Gio.Settings.sync() +- # This is an absolute percentage, and our brightness is 0..100 +- dim_level = self.settings_gsd_power['idle-brightness']; + + # Check that we're not idle + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) + + # Wait and check we're not idle, but dimmed + self.check_dim(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY + 1) +- # Give time for the brightness to change ++ # Give time for the dimming to get called + time.sleep(2) +- level = self.get_brightness(); +- self.assertTrue(level == dim_level, 'incorrect dim brightness (%d != %d)' % (level, dim_level)) ++ mock_intf = dbus.Interface(self.obj_gnomeshell, dbusmock.MOCK_IFACE) ++ method_calls = mock_intf.GetMethodCalls("SetDimming") ++ self.assertEqual(len(method_calls), 1, 'did not call dim') ++ _, args = method_calls[-1] ++ self.assertTrue(args[0] == True, 'expected enabled dimming') + + self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) + +@@ -717,8 +698,13 @@ class PowerPluginTestDim(PowerPluginBase): + self.logind_obj.EmitSignal('', 'PrepareForSleep', 'b', [False], dbus_interface='org.freedesktop.DBus.Mock') + time.sleep(1) + +- # And check that we have the pre-dim brightness +- self.assertTrue(self.get_brightness() == gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS , 'incorrect unblanked brightness (%d != %d)' % (self.get_brightness(), gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS)) ++ # And check that we disabled dimming ++ time.sleep(2) ++ mock_intf = dbus.Interface(self.obj_gnomeshell, dbusmock.MOCK_IFACE) ++ method_calls = mock_intf.GetMethodCalls("SetDimming") ++ self.assertEqual(len(method_calls), 2, 'did not call dim') ++ _, args = method_calls[-1] ++ self.assertTrue(args[0] == False, 'expected disabled dimming') + + def test_lid_close_inhibition(self): + '''Check that we correctly inhibit suspend with an external monitor''' +@@ -1064,127 +1050,6 @@ class PowerPluginTestBrightness(PowerPluginBase): + self.check_dim(idle_delay + 2) + + class PowerPluginTestBrightnessStep(PowerPluginBase): +- def test_brightness_stepping(self): +- '''Check that stepping the backlight works as expected''' +- +- obj_gsd_power = self.session_bus_con.get_object( +- 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') +- obj_gsd_power_screen_iface = dbus.Interface(obj_gsd_power, 'org.gnome.SettingsDaemon.Power.Screen') +- +- # We start at 50% and step by 5% each time +- obj_gsd_power_screen_iface.StepUp() +- self.assertEqual(self.get_brightness(), 55) +- obj_gsd_power_screen_iface.StepUp() +- self.assertEqual(self.get_brightness(), 60) +- obj_gsd_power_screen_iface.StepUp() +- self.assertEqual(self.get_brightness(), 65) +- obj_gsd_power_screen_iface.StepUp() +- self.assertEqual(self.get_brightness(), 70) +- +- # Now, the same thing should work fine if we step multiple times, +- # even if we are so quick that compression will happen. +- # Use a list to keep rack of replies (as integer is immutable and would +- # not be modified in the outer scope) +- replies = [0] +- +- def handle_reply(*args): +- replies[0] += 1 +- +- def last_reply(*args): +- replies[0] += 1 +- loop.quit() +- +- def error_handler(*args): +- loop.quit() +- +- start = time.time() +- obj_gsd_power_screen_iface.StepDown(reply_handler=handle_reply, error_handler=error_handler) +- obj_gsd_power_screen_iface.StepDown(reply_handler=handle_reply, error_handler=error_handler) +- obj_gsd_power_screen_iface.StepDown(reply_handler=handle_reply, error_handler=error_handler) +- obj_gsd_power_screen_iface.StepDown(reply_handler=last_reply, error_handler=error_handler) +- loop = GLib.MainLoop() +- loop.run() +- stop = time.time() +- +- # The calls need to be returned in order. As we got the last reply, all +- # others must have been received too. +- self.assertEqual(replies[0], 4) +- # Four steps down, so back at 50% +- self.assertEqual(self.get_brightness(), 50) +- # And compression must have happened, so it should take less than 0.8s +- self.assertLess(stop - start, 0.8) +- +- def test_brightness_compression(self): +- '''Check that compression also happens when setting the property''' +- +- # Now test that the compression works correctly. +- # NOTE: Relies on the implementation detail, that the property setter +- # returns immediately rather than waiting for the brightness to +- # be updated. +- # Should this ever be fixed, then this will need to be changed to use +- # async dbus calls similar to the stepping code +- +- obj_gsd_power = self.session_bus_con.get_object( +- 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') +- obj_gsd_power_prop_iface = dbus.Interface(obj_gsd_power, dbus.PROPERTIES_IFACE) +- +- # Quickly ramp the brightness up +- for brightness in range(70, 91): +- obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', brightness) +- +- # The brightness of 80 should be in effect after slightly more than +- # 0.4 seconds. If compression does not work as expected, this would take +- # more than 5 seconds for the 20 steps. +- time.sleep(2.0) +- self.assertEqual(self.get_brightness(), 90) +- +- def test_brightness_step(self): +- for l in self.plugin_startup_msgs: +- if b'Step size for backlight is 5.' in l: +- break +- else: +- self.fail('Step size is not 5') +- +- def test_legacy_brightness_step(self): +- for l in self.plugin_startup_msgs: +- if b'Step size for backlight is 1.' in l: +- break +- else: +- self.fail('Step size is not 1') +- +- def test_legacy_brightness_rounding(self): +- obj_gsd_power = self.session_bus_con.get_object( +- 'org.gnome.SettingsDaemon.Power', '/org/gnome/SettingsDaemon/Power') +- obj_gsd_power_prop_iface = dbus.Interface(obj_gsd_power, dbus.PROPERTIES_IFACE) +- +- obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 0) +- time.sleep(0.6) +- self.assertEqual(self.get_brightness(), 0) +- obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 10) +- time.sleep(0.6) +- self.assertEqual(self.get_brightness(), 2) +- obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 20) +- time.sleep(0.6) +- self.assertEqual(self.get_brightness(), 3) +- obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 25) +- time.sleep(0.6) +- self.assertEqual(self.get_brightness(), 4) +- obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 49) +- time.sleep(0.6) +- self.assertEqual(self.get_brightness(), 7) +- obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 50) +- time.sleep(0.6) +- self.assertEqual(self.get_brightness(), 8) +- obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 56) +- time.sleep(0.6) +- self.assertEqual(self.get_brightness(), 8) +- obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 57) +- time.sleep(0.6) +- self.assertEqual(self.get_brightness(), 9) +- obj_gsd_power_prop_iface.Set('org.gnome.SettingsDaemon.Power.Screen', 'Brightness', 98) +- time.sleep(0.6) +- self.assertEqual(self.get_brightness(), 15) +- + def test_power_saver_on_low_battery(self): + '''Check that the power-saver profile gets held when low on battery''' + +-- +2.51.1 + + +From 97f64190a96ae8d0d89f8da09253d61f8bf34f44 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Mon, 11 Aug 2025 11:43:57 +0200 +Subject: [PATCH 08/10] power: Fix boolean property value extraction + +The code here is expecting a "(b)" GVariant signature, which does +not match with "b" as sent by GNOME Shell. This produces a barrage of +warnings like: + + gsd-power[11982]: the GVariant format string '(b)' has a type of '(b)' but the given value has a type of 'b' + gsd-power[11982]: g_variant_get: assertion 'valid_format_string (format_string, TRUE, value)' failed + +And other side effects. Fix this boolean value extraction, to fix +the warnings and detect correctly that GNOME Shell is in charge of +backlight brightness. + +Fixes: f7555c02 ("power: Add new dimming and auto brightness API based on Shell.Brightness") +--- + plugins/power/gsd-power-manager.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/plugins/power/gsd-power-manager.c b/plugins/power/gsd-power-manager.c +index 21cdaa85..1dcb172d 100644 +--- a/plugins/power/gsd-power-manager.c ++++ b/plugins/power/gsd-power-manager.c +@@ -1183,7 +1183,6 @@ static gboolean + shell_brightness_has_control (GsdPowerManager *manager) + { + g_autoptr (GVariant) has_control_variant = NULL; +- gboolean has_control; + + if (!manager->shell_brightness_proxy) + return FALSE; +@@ -1195,8 +1194,7 @@ shell_brightness_has_control (GsdPowerManager *manager) + if (!has_control_variant) + return FALSE; + +- g_variant_get (has_control_variant, "(b)", &has_control); +- return has_control; ++ return g_variant_get_boolean (has_control_variant); + } + + static void +-- +2.51.1 + + +From 24586c310c3f1f84a8481764932417f28f1ec67c Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Mon, 1 Sep 2025 12:19:08 +0200 +Subject: [PATCH 09/10] power: Refactor light sensor claiming/release + +Refactor the code so checks are a bit less all over the place, +and there's a single decision point for whether the light sensor +should be claimed or not. +--- + plugins/power/gsd-power-manager.c | 44 ++++++++++++++++++++++--------- + 1 file changed, 32 insertions(+), 12 deletions(-) + +diff --git a/plugins/power/gsd-power-manager.c b/plugins/power/gsd-power-manager.c +index 1dcb172d..f114fee3 100644 +--- a/plugins/power/gsd-power-manager.c ++++ b/plugins/power/gsd-power-manager.c +@@ -146,6 +146,8 @@ struct _GsdPowerManager + gboolean lid_is_present; + gboolean lid_is_closed; + gboolean session_is_active; ++ gboolean screen_blanked; ++ gboolean light_claimed; + UpClient *up_client; + GPtrArray *devices_array; + UpDevice *device_composite; +@@ -1303,16 +1305,27 @@ light_released_cb (GObject *source_object, + } + } + ++static gboolean ++iio_proxy_should_claim_light (GsdPowerManager *manager) ++{ ++ if (!shell_brightness_has_control (manager)) ++ return FALSE; ++ if (!manager->session_is_active) ++ return FALSE; ++ if (manager->screen_blanked) ++ return FALSE; ++ ++ return TRUE; ++} + + static void +-iio_proxy_claim_light (GsdPowerManager *manager, gboolean active) ++iio_proxy_claim_light (GsdPowerManager *manager, ++ gboolean active) + { + if (manager->iio_proxy == NULL) + return; +- if (!shell_brightness_has_control (manager)) ++ if (manager->light_claimed == !!active) + return; +- if (active && !manager->session_is_active) +- return; + + /* FIXME: + * Remove when iio-sensor-proxy sends events only to clients instead +@@ -1336,6 +1349,14 @@ iio_proxy_claim_light (GsdPowerManager *manager, gboolean active) + manager->cancellable, + active ? light_claimed_cb : light_released_cb, + manager); ++ ++ manager->light_claimed = !!active; ++} ++ ++static void ++iio_proxy_maybe_claim_light (GsdPowerManager *manager) ++{ ++ iio_proxy_claim_light (manager, iio_proxy_should_claim_light (manager)); + } + + typedef enum { +@@ -1359,8 +1380,9 @@ set_power_saving_mode (GsdPowerManager *manager, + static void + enable_monitors (GsdPowerManager *manager) + { +- iio_proxy_claim_light (manager, TRUE); + set_power_saving_mode (manager, GSD_POWER_SAVE_MODE_ON); ++ manager->screen_blanked = FALSE; ++ iio_proxy_maybe_claim_light (manager); + + g_debug ("TESTSUITE: Unblanked screen"); + } +@@ -1368,8 +1390,9 @@ enable_monitors (GsdPowerManager *manager) + static void + disable_monitors (GsdPowerManager *manager) + { +- iio_proxy_claim_light (manager, FALSE); + set_power_saving_mode (manager, GSD_POWER_SAVE_MODE_OFF); ++ manager->screen_blanked = TRUE; ++ iio_proxy_maybe_claim_light (manager); + + g_debug ("TESTSUITE: Blanked screen"); + } +@@ -2634,12 +2657,9 @@ engine_session_properties_changed_cb (GDBusProxy *session, + manager->session_is_active = active; + /* when doing the fast-user-switch into a new account, + * ensure the new account is undimmed and with the backlight on */ +- if (active) { ++ if (active) + idle_set_mode (manager, GSD_POWER_IDLE_MODE_NORMAL); +- iio_proxy_claim_light (manager, TRUE); +- } else { +- iio_proxy_claim_light (manager, FALSE); +- } ++ iio_proxy_maybe_claim_light (manager); + g_variant_unref (v); + + sync_lid_inhibitor (manager); +@@ -2949,7 +2969,7 @@ iio_proxy_appeared_cb (GDBusConnection *connection, + "net.hadess.SensorProxy", + NULL, + NULL); +- iio_proxy_claim_light (manager, TRUE); ++ iio_proxy_maybe_claim_light (manager); + } + + static void +-- +2.51.1 + + +From 4c4343bb00b47a2c9fdb713a9318058fed400c05 Mon Sep 17 00:00:00 2001 +From: Richard Acayan +Date: Mon, 8 Sep 2025 19:04:40 -0400 +Subject: [PATCH 10/10] power: Do not poll light sensor when automatic + brightness is disabled + +When automatic brightness is disabled, ambient light sensor events are +eventually ignored, but they still get processed by the hardware and +operating system if gsd-power enables it. Disable the light sensor when +automatic brightness is disabled to save some processing power. +--- + plugins/power/gsd-power-manager.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/plugins/power/gsd-power-manager.c b/plugins/power/gsd-power-manager.c +index f114fee3..b7b5b642 100644 +--- a/plugins/power/gsd-power-manager.c ++++ b/plugins/power/gsd-power-manager.c +@@ -1314,6 +1314,8 @@ iio_proxy_should_claim_light (GsdPowerManager *manager) + return FALSE; + if (manager->screen_blanked) + return FALSE; ++ if (!g_settings_get_boolean (manager->settings, "ambient-enabled")) ++ return FALSE; + + return TRUE; + } +@@ -2638,6 +2640,10 @@ engine_settings_key_changed_cb (GSettings *settings, + disable_power_saver (manager); + return; + } ++ if (g_str_equal (key, "ambient-enabled")) { ++ iio_proxy_maybe_claim_light (manager); ++ return; ++ } + } + + static void +-- +2.51.1 +