[gnome-settings-daemon/wip/benzea/backlight: 13/18] power: Refactor backlight handling to be asynchronous



commit 22edbbc7badc1680c75431995b98442ac1cb1c1d
Author: Benjamin Berg <bberg redhat com>
Date:   Wed Jan 10 20:09:30 2018 +0100

    power: Refactor backlight handling to be asynchronous
    
    Writing the backlight value can take quite long, in particular when we
    also get the overhead of starting up the helper process for writing.
    
    Add code to handle the writing asynchronously and compress multiple set
    actions into one write. The notification of the change only happens once
    all of the queued up set operations have finished.
    
    A trivial bugfix included is that the OSD display is again shown only on
    the affected (internal) screen.
    
    Note that a possible further improvement would be to just run the
    backlight helper once and communicate with it through stdin or some
    other methods, avoiding the overhead of starting it up all the time.
    There are a few minor disadvantages with this approach though, see
    comment #16 and following for more information.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=758413

 plugins/power/gpm-common.c        | 498 +-----------------------
 plugins/power/gpm-common.h        |   2 +
 plugins/power/gsd-backlight.c     | 777 ++++++++++++++++++++++++++++++++++++++
 plugins/power/gsd-backlight.h     |  70 ++++
 plugins/power/gsd-power-manager.c | 213 ++++++-----
 plugins/power/meson.build         |   2 +-
 6 files changed, 956 insertions(+), 606 deletions(-)
---
diff --git a/plugins/power/gpm-common.c b/plugins/power/gpm-common.c
index e83f1549..60f3795f 100644
--- a/plugins/power/gpm-common.c
+++ b/plugins/power/gpm-common.c
@@ -38,18 +38,11 @@
 #include "gpm-common.h"
 #include "gsd-power-constants.h"
 #include "gsd-power-manager.h"
-#include "gsd-backlight-linux.h"
 
 #define XSCREENSAVER_WATCHDOG_TIMEOUT           120 /* seconds */
 #define UPS_SOUND_LOOP_ID                        99
 #define GSD_POWER_MANAGER_CRITICAL_ALERT_TIMEOUT  5 /* seconds */
 
-enum BacklightHelperCommand {
-        BACKLIGHT_HELPER_GET,
-        BACKLIGHT_HELPER_GET_MAX,
-        BACKLIGHT_HELPER_SET
-};
-
 /* take a discrete value with offset and convert to percentage */
 int
 gsd_power_backlight_abs_to_percentage (int min, int max, int value)
@@ -321,29 +314,6 @@ gsd_power_enable_screensaver_watchdog (void)
         return id;
 }
 
-static GnomeRROutput *
-get_primary_output (GnomeRRScreen *rr_screen)
-{
-        GnomeRROutput *output = NULL;
-        GnomeRROutput **outputs;
-        guint i;
-
-        /* search all X11 outputs for the device id */
-        outputs = gnome_rr_screen_list_outputs (rr_screen);
-        if (outputs == NULL)
-                goto out;
-
-        for (i = 0; outputs[i] != NULL; i++) {
-                if (gnome_rr_output_is_builtin_display (outputs[i]) &&
-                    gnome_rr_output_get_backlight (outputs[i]) >= 0) {
-                        output = outputs[i];
-                        break;
-                }
-        }
-out:
-        return output;
-}
-
 static gpointer
 parse_mocked (gpointer data)
 {
@@ -354,7 +324,7 @@ parse_mocked (gpointer data)
        return GINT_TO_POINTER (TRUE);
 }
 
-static gboolean
+gboolean
 is_mocked (void)
 {
          static GOnce mocked_once = G_ONCE_INIT;
@@ -362,472 +332,6 @@ is_mocked (void)
          return GPOINTER_TO_INT (mocked_once.retval);
 }
 
-static void
-backlight_set_mock_value (gint value)
-{
-       const char *filename;
-       char *contents;
-       GError *error = NULL;
-
-       g_debug ("Setting mock brightness: %d", value);
-
-       filename = "GSD_MOCK_brightness";
-       contents = g_strdup_printf ("%d", value);
-       if (!g_file_set_contents (filename, contents, -1, &error))
-               g_warning ("Setting mock brightness failed: %s", error->message);
-       g_clear_error (&error);
-       g_free (contents);
-}
-
-static gint64
-backlight_get_mock_value (enum BacklightHelperCommand command)
-{
-       char *contents;
-       gint64 ret;
-
-        if (command == BACKLIGHT_HELPER_GET_MAX) {
-               g_debug ("Returning max mock brightness: %d", GSD_MOCK_MAX_BRIGHTNESS);
-               return GSD_MOCK_MAX_BRIGHTNESS;
-       }
-
-        g_assert (command == BACKLIGHT_HELPER_GET);
-
-       if (g_file_get_contents ("GSD_MOCK_brightness", &contents, NULL, NULL)) {
-               ret = g_ascii_strtoll (contents, NULL, 0);
-               g_free (contents);
-               g_debug ("Returning mock brightness: %"G_GINT64_FORMAT, ret);
-       } else {
-               ret = GSD_MOCK_DEFAULT_BRIGHTNESS;
-               backlight_set_mock_value (GSD_MOCK_DEFAULT_BRIGHTNESS);
-               g_debug ("Returning default mock brightness: %"G_GINT64_FORMAT, ret);
-       }
-
-       return ret;
-}
-
-gboolean
-backlight_available (GnomeRRScreen *rr_screen)
-{
-        char *path;
-
-       if (is_mocked ())
-               return TRUE;
-
-#ifndef __linux__
-        return (get_primary_output (rr_screen) != NULL);
-#endif
-
-        path = gsd_backlight_helper_get_best_backlight (NULL);
-        if (path == NULL)
-                return FALSE;
-
-        g_free (path);
-        return TRUE;
-}
-
-static gchar **
-get_backlight_helper_environ (void)
-{
-        static gchar **environ = NULL;
-
-        if (environ)
-                return environ;
-
-        environ = g_environ_unsetenv (g_get_environ (), "SHELL");
-        return environ;
-}
-
-static gboolean
-run_backlight_helper (enum BacklightHelperCommand   command,
-                      gchar                        *value,
-                      gchar                       **stdout_data,
-                      gint                         *exit_status,
-                      GError                      **error)
-{
-        static gchar *helper_args[] = {
-                "--get-brightness",
-                "--get-max-brightness",
-                "--set-brightness",
-        };
-        gchar *argv[5] = { 0 };
-
-        argv[0] = "pkexec";
-        argv[1] = LIBEXECDIR "/gsd-backlight-helper";
-        argv[2] = helper_args[command];
-        argv[3] = value;
-
-        return g_spawn_sync (NULL,
-                             command == BACKLIGHT_HELPER_SET ? argv : &argv[1],
-                             get_backlight_helper_environ (),
-                             G_SPAWN_SEARCH_PATH,
-                             NULL,
-                             NULL,
-                             stdout_data,
-                             NULL,
-                             exit_status,
-                             error);
-}
-
-/**
- * backlight_helper_get_value:
- *
- * Gets a brightness value from the PolicyKit helper.
- *
- * Return value: the signed integer value from the helper, or -1
- * for failure. If -1 then @error is set.
- **/
-static gint64
-backlight_helper_get_value (enum BacklightHelperCommand command, GError **error)
-{
-        gboolean ret;
-        gchar *stdout_data = NULL;
-        gint exit_status = 0;
-        gint64 value = -1;
-        gchar *endptr = NULL;
-
-       if (is_mocked ())
-               return backlight_get_mock_value (command);
-
-#ifndef __linux__
-        /* non-Linux platforms won't have /sys/class/backlight */
-        g_set_error_literal (error,
-                             GSD_POWER_MANAGER_ERROR,
-                             GSD_POWER_MANAGER_ERROR_FAILED,
-                             "The sysfs backlight helper is only for Linux");
-        goto out;
-#endif
-
-        /* get the data */
-        ret = run_backlight_helper (command, NULL,
-                                    &stdout_data, &exit_status, error);
-        if (!ret)
-                goto out;
-
-        if (WEXITSTATUS (exit_status) != 0) {
-                 g_set_error (error,
-                             GSD_POWER_MANAGER_ERROR,
-                             GSD_POWER_MANAGER_ERROR_FAILED,
-                             "gsd-backlight-helper failed: %s",
-                             stdout_data ? stdout_data : "No reason");
-                goto out;
-        }
-
-        /* parse */
-        value = g_ascii_strtoll (stdout_data, &endptr, 10);
-
-        /* parsing error */
-        if (endptr == stdout_data) {
-                value = -1;
-                g_set_error (error,
-                             GSD_POWER_MANAGER_ERROR,
-                             GSD_POWER_MANAGER_ERROR_FAILED,
-                             "failed to parse value: %s",
-                             stdout_data);
-                goto out;
-        }
-
-        /* out of range */
-        if (value > G_MAXINT) {
-                value = -1;
-                g_set_error (error,
-                             GSD_POWER_MANAGER_ERROR,
-                             GSD_POWER_MANAGER_ERROR_FAILED,
-                             "value out of range: %s",
-                             stdout_data);
-                goto out;
-        }
-
-        /* Fetching the value failed, for some other reason */
-        if (value < 0) {
-                g_set_error (error,
-                             GSD_POWER_MANAGER_ERROR,
-                             GSD_POWER_MANAGER_ERROR_FAILED,
-                             "value negative, but helper did not fail: %s",
-                             stdout_data);
-                goto out;
-        }
-
-out:
-        g_free (stdout_data);
-        return value;
-}
-
-/**
- * backlight_helper_set_value:
- *
- * Sets a brightness value using the PolicyKit helper.
- *
- * Return value: Success. If FALSE then @error is set.
- **/
-static gboolean
-backlight_helper_set_value (gint value,
-                            GError **error)
-{
-        gboolean ret = FALSE;
-        gint exit_status = 0;
-        gchar *vstr = NULL;
-
-       if (is_mocked ()) {
-               backlight_set_mock_value (value);
-               return TRUE;
-       }
-
-#ifndef __linux__
-        /* non-Linux platforms won't have /sys/class/backlight */
-        g_set_error_literal (error,
-                             GSD_POWER_MANAGER_ERROR,
-                             GSD_POWER_MANAGER_ERROR_FAILED,
-                             "The sysfs backlight helper is only for Linux");
-        return FALSE;
-#endif
-
-        /* get the data */
-        vstr = g_strdup_printf ("%i", value);
-        ret = run_backlight_helper (BACKLIGHT_HELPER_SET, vstr,
-                                    NULL, &exit_status, error);
-        g_free (vstr);
-        return ret;
-}
-
-int
-backlight_get_output_id (GnomeRRScreen *rr_screen)
-{
-        GnomeRROutput *output;
-        GnomeRRCrtc *crtc;
-        GdkScreen *gdk_screen;
-        gint x, y;
-
-        output = get_primary_output (rr_screen);
-        if (output == NULL)
-                return -1;
-
-        crtc = gnome_rr_output_get_crtc (output);
-        if (crtc == NULL)
-                return -1;
-
-        gdk_screen = gdk_screen_get_default ();
-        gnome_rr_crtc_get_position (crtc, &x, &y);
-
-        return gdk_screen_get_monitor_at_point (gdk_screen, x, y);
-}
-
-int
-backlight_get_abs (GnomeRRScreen *rr_screen, GError **error)
-{
-#ifndef __linux__
-        GnomeRROutput *output;
-        output = get_primary_output (rr_screen);
-        if (output != NULL) {
-                return gnome_rr_output_get_backlight (output);
-        }
-        return -1;
-#else
-        return backlight_helper_get_value (BACKLIGHT_HELPER_GET, error);
-#endif
-}
-
-int
-backlight_get_percentage (GnomeRRScreen *rr_screen, GError **error)
-{
-        gint now;
-        gint value = -1;
-        gint max;
-#ifndef __linux__
-        GnomeRROutput *output;
-        output = get_primary_output (rr_screen);
-        if (output != NULL) {
-                now = gnome_rr_output_get_backlight (output);
-                if (now < 0)
-                        return value;
-                value = ABS_TO_PERCENTAGE (0, 100, now);
-        }
-        return value;
-#else
-        max = backlight_helper_get_value (BACKLIGHT_HELPER_GET_MAX, error);
-        if (max < 0)
-                return value;
-        now = backlight_helper_get_value (BACKLIGHT_HELPER_GET, error);
-        if (now < 0)
-                return value;
-        value = ABS_TO_PERCENTAGE (0, max, now);
-        return value;
-#endif
-}
-
-int
-backlight_get_min (GnomeRRScreen *rr_screen)
-{
-        return 0;
-}
-
-int
-backlight_get_max (GnomeRRScreen *rr_screen, GError **error)
-{
-#ifndef __linux__
-        return 100;
-#else
-        return  backlight_helper_get_value (BACKLIGHT_HELPER_GET_MAX, error);
-#endif
-}
-
-gboolean
-backlight_set_percentage (GnomeRRScreen *rr_screen,
-                          gint *value,
-                          GError **error)
-{
-        gboolean ret = FALSE;
-        gint max;
-        guint discrete;
-#ifndef __linux__
-        GnomeRROutput *output;
-        output = get_primary_output (rr_screen);
-        if (output != NULL) {
-                if (!gnome_rr_output_set_backlight (output, *value, error))
-                        return ret;
-                *value = gnome_rr_output_get_backlight (output);
-                ret = TRUE;
-        }
-        return ret;
-#else
-        max = backlight_helper_get_value (BACKLIGHT_HELPER_GET_MAX, error);
-        if (max < 0)
-                return ret;
-        discrete = PERCENTAGE_TO_ABS (0, max, *value);
-        ret = backlight_helper_set_value (discrete, error);
-        if (ret)
-                *value = ABS_TO_PERCENTAGE (0, max, discrete);
-
-        return ret;
-#endif
-}
-
-int
-backlight_step_up (GnomeRRScreen *rr_screen, GError **error)
-{
-        gboolean ret = FALSE;
-        gint percentage_value = -1;
-        gint max;
-        gint now;
-        gint step;
-        guint discrete;
-#ifndef __linux__
-        GnomeRRCrtc *crtc;
-        GnomeRROutput *output;
-        output = get_primary_output (rr_screen);
-        if (output != NULL) {
-
-                crtc = gnome_rr_output_get_crtc (output);
-                if (crtc == NULL) {
-                        g_set_error (error,
-                                     GSD_POWER_MANAGER_ERROR,
-                                     GSD_POWER_MANAGER_ERROR_FAILED,
-                                     "no crtc for %s",
-                                     gnome_rr_output_get_name (output));
-                        return percentage_value;
-                }
-                max = 100;
-                now = gnome_rr_output_get_backlight (output);
-                if (now < 0)
-                       return percentage_value;
-                step = MAX(gnome_rr_output_get_min_backlight_step (output), BRIGHTNESS_STEP_AMOUNT(max + 1));
-                discrete = MIN (now + step, max);
-                ret = gnome_rr_output_set_backlight (output,
-                                                     discrete,
-                                                     error);
-                if (ret)
-                        percentage_value = ABS_TO_PERCENTAGE (0, max, discrete);
-        }
-        return percentage_value;
-#else
-        now = backlight_helper_get_value (BACKLIGHT_HELPER_GET, error);
-        if (now < 0)
-                return percentage_value;
-        max = backlight_helper_get_value (BACKLIGHT_HELPER_GET_MAX, error);
-        if (max < 0)
-                return percentage_value;
-        step = BRIGHTNESS_STEP_AMOUNT (max + 1);
-        discrete = MIN (now + step, max);
-        ret = backlight_helper_set_value (discrete, error);
-        if (ret)
-                percentage_value = ABS_TO_PERCENTAGE (0, max, discrete);
-
-        return percentage_value;
-#endif
-}
-
-int
-backlight_step_down (GnomeRRScreen *rr_screen, GError **error)
-{
-        gboolean ret = FALSE;
-        gint percentage_value = -1;
-        gint max;
-        gint now;
-        gint step;
-        guint discrete;
-#ifndef __linux__
-        GnomeRRCrtc *crtc;
-        GnomeRROutput *output;
-        output = get_primary_output (rr_screen);
-        if (output != NULL) {
-
-                crtc = gnome_rr_output_get_crtc (output);
-                if (crtc == NULL) {
-                        g_set_error (error,
-                                     GSD_POWER_MANAGER_ERROR,
-                                     GSD_POWER_MANAGER_ERROR_FAILED,
-                                     "no crtc for %s",
-                                     gnome_rr_output_get_name (output));
-                        return percentage_value;
-                }
-                max = 100;
-                now = gnome_rr_output_get_backlight (output);
-                if (now < 0)
-                       return percentage_value;
-                step = MAX (gnome_rr_output_get_min_backlight_step (output), BRIGHTNESS_STEP_AMOUNT (max + 
1));
-                discrete = MAX (now - step, 0);
-                ret = gnome_rr_output_set_backlight (output,
-                                                     discrete,
-                                                     error);
-                if (ret)
-                        percentage_value = ABS_TO_PERCENTAGE (0, max, discrete);
-        }
-        return percentage_value;
-#else
-        now = backlight_helper_get_value (BACKLIGHT_HELPER_GET, error);
-        if (now < 0)
-                return percentage_value;
-        max = backlight_helper_get_value (BACKLIGHT_HELPER_GET_MAX, error);
-        if (max < 0)
-                return percentage_value;
-        step = BRIGHTNESS_STEP_AMOUNT (max + 1);
-        discrete = MAX (now - step, 0);
-        ret = backlight_helper_set_value (discrete, error);
-        if (ret)
-                percentage_value = ABS_TO_PERCENTAGE (0, max, discrete);
-
-        return percentage_value;
-#endif
-}
-
-int
-backlight_set_abs (GnomeRRScreen *rr_screen,
-                   guint value,
-                   GError **error)
-{
-        gboolean ret = FALSE;
-#ifndef __linux__
-        GnomeRROutput *output;
-        output = get_primary_output (rr_screen);
-        if (output != NULL)
-                return gnome_rr_output_set_backlight (output, value, error);
-        return ret;
-#else
-        ret = backlight_helper_set_value (value, error);
-
-        return ret;
-#endif
-}
-
 static gboolean
 randr_output_is_on (GnomeRROutput *output)
 {
diff --git a/plugins/power/gpm-common.h b/plugins/power/gpm-common.h
index 251cd319..ad0da075 100644
--- a/plugins/power/gpm-common.h
+++ b/plugins/power/gpm-common.h
@@ -36,6 +36,8 @@ gboolean         gsd_power_is_hardware_a_tablet         (void);
 guint            gsd_power_enable_screensaver_watchdog  (void);
 void             reset_idletime                         (void);
 
+gboolean        is_mocked (void);
+
 /* Backlight helpers */
 
 /* on ACPI machines we have 4-16 levels, on others it's ~150 */
diff --git a/plugins/power/gsd-backlight.c b/plugins/power/gsd-backlight.c
new file mode 100644
index 00000000..23a74dbe
--- /dev/null
+++ b/plugins/power/gsd-backlight.c
@@ -0,0 +1,777 @@
+/* -*- 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+#include <stdlib.h>
+
+#include "gsd-backlight.h"
+#include "gpm-common.h"
+#include "gsd-power-constants.h"
+#include "gsd-power-manager.h"
+
+#define MOCK_BRIGHTNESS_FILE "GSD_MOCK_brightness"
+
+#ifdef HAVE_GUDEV
+#include <gudev/gudev.h>
+#endif /* HAVE_GUDEV */
+
+typedef struct
+{
+        gboolean available;
+
+        gint brightness_min;
+        gint brightness_max;
+        gint brightness_val;
+        gint brightness_target;
+        gint brightness_step;
+
+#ifdef HAVE_GUDEV
+        GUdevClient *udev;
+        GUdevDevice *udev_device;
+
+        GTask *active_task;
+        GQueue tasks;
+
+        gint idle_update;
+#endif
+
+        GnomeRRScreen *rr_screen;
+} GsdBacklightPrivate;
+
+enum {
+        PROP_0,
+        PROP_RR_SCREEN,
+        PROP_AVAILABLE,
+        PROP_BRIGHTNESS,
+        PROP_LAST,
+};
+
+static GParamSpec *props[PROP_LAST];
+
+G_DEFINE_TYPE_WITH_PRIVATE (GsdBacklight, gsd_backlight, G_TYPE_OBJECT)
+
+#define GSD_BACKLIGHT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_BACKLIGHT, 
GsdBacklightPrivate))
+
+
+#ifdef HAVE_GUDEV
+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)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+        GList *devices;
+
+        g_assert (priv->udev != NULL);
+
+        devices = g_udev_client_query_by_subsystem (priv->udev, "backlight");
+        if (devices == NULL)
+                goto out;
+
+        /* Search the backlight devices and prefer the types:
+         * firmware -> platform -> raw */
+        priv->udev_device = gsd_backlight_udev_get_type (devices, "firmware");
+        if (priv->udev_device != NULL)
+                goto out;
+
+        priv->udev_device = gsd_backlight_udev_get_type (devices, "platform");
+        if (priv->udev_device != NULL)
+                goto out;
+
+        priv->udev_device = gsd_backlight_udev_get_raw (devices);
+        if (priv->udev_device != NULL)
+                goto out;
+
+out:
+        g_list_free_full (devices, g_object_unref);
+}
+
+static gboolean
+gsd_backlight_udev_idle_update_cb (GsdBacklight *backlight)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+        g_autoptr(GError) error = NULL;
+        gint brightness;
+        g_autofree gchar *path = NULL;
+        g_autofree gchar *contents = NULL;
+        priv->idle_update = 0;
+
+        /* If we are active again now, just stop. */
+        if (priv->active_task)
+                return FALSE;
+
+        path = g_build_filename (g_udev_device_get_sysfs_path (priv->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 = atoi(contents);
+
+        /* Only notify if brightness has changed. */
+        if (brightness == priv->brightness_val)
+                return FALSE;
+
+        priv->brightness_val = brightness;
+        priv->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)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+
+        if (priv->idle_update)
+                return;
+
+        priv->idle_update = g_idle_add ((GSourceFunc) gsd_backlight_udev_idle_update_cb, backlight);
+}
+
+
+static void
+gsd_backlight_udev_uevent (GUdevClient *client, gchar *action, GUdevDevice *device, gpointer user_data)
+{
+        GsdBacklight *backlight = GSD_BACKLIGHT (user_data);
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+
+        /* We are going to update our state after processing the tasks anyway. */
+        if (!g_queue_is_empty (&priv->tasks))
+                return;
+
+        if (g_strcmp0 (g_udev_device_get_sysfs_path (device),
+                       g_udev_device_get_sysfs_path (priv->udev_device)) != 0)
+                return;
+
+        gsd_backlight_udev_idle_update (backlight);
+}
+
+
+static gboolean
+gsd_backlight_udev_init (GsdBacklight *backlight)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+        const gchar* const subsystems[] = {"backlight", NULL};
+
+        priv->udev = g_udev_client_new (subsystems);
+        gsd_backlight_udev_resolve (backlight);
+        if (priv->udev_device == NULL)
+                return FALSE;
+
+        priv->available = TRUE;
+        priv->brightness_min = 1;
+        priv->brightness_max = g_udev_device_get_sysfs_attr_as_int (priv->udev_device,
+                                                                    "max_brightness");
+
+        /* 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 (priv->brightness_max < 99 &&
+            g_strcmp0 (g_udev_device_get_sysfs_attr (priv->udev_device, "type"), "raw") == 0)
+                priv->brightness_min = 0;
+
+        priv->brightness_val = g_udev_device_get_sysfs_attr_as_int (priv->udev_device,
+                                                                    "brightness");
+        g_debug ("Using udev device with brightness from %i to %i. Current brightness is %i.",
+                 priv->brightness_min, priv->brightness_max, priv->brightness_val);
+
+        g_signal_connect_object (priv->udev, "uevent",
+                                 G_CALLBACK (gsd_backlight_udev_uevent),
+                                 backlight, 0);
+
+        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, GError *error)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+        GTask *finished_task;
+        gint percent = ABS_TO_PERCENTAGE (priv->brightness_min, priv->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 (&priv->tasks)) {
+                if (error == NULL) {
+                        g_assert (priv->brightness_target == result);
+
+                        priv->brightness_val = priv->brightness_target;
+                        g_debug ("New brightness value is in effect %i (%i..%i)",
+                                 priv->brightness_val, priv->brightness_min, priv->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 (&priv->tasks);
+
+                if (error)
+                        g_task_return_error (finished_task, g_error_copy (error));
+                else
+                        g_task_return_int (finished_task, percent);
+        } 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);
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+        g_autoptr(GError) error = NULL;
+
+        g_assert (task == priv->active_task);
+        priv->active_task = NULL;
+
+        g_subprocess_wait_finish (proc, res, &error);
+
+        if (error)
+                goto done;
+
+        g_spawn_check_exit_status (g_subprocess_get_exit_status (proc), &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)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+        GSubprocess *proc = NULL;
+        BacklightHelperData *data = g_task_get_task_data (task);
+        GError *error = NULL;
+
+        g_assert (priv->active_task == NULL);
+        priv->active_task = task;
+
+        if (data->value_str == NULL)
+                data->value_str = g_strdup_printf ("%d", data->value);
+
+        proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE,
+                                 &error,
+                                 "pkexec", 
+                                 LIBEXECDIR "/gsd-backlight-helper",
+                                 "--set-brightness",
+                                 data->value_str, NULL);
+
+        if (proc == NULL) {
+                gsd_backlight_set_helper_return (backlight, task, -1, error);
+                return;
+        }
+
+        g_subprocess_wait_async (proc, g_task_get_cancellable (task),
+                                 gsd_backlight_set_helper_finish,
+                                 task);
+}
+
+static void
+gsd_backlight_process_taskqueue (GsdBacklight *backlight)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+        GTask *to_run;
+
+        /* There is already a task active, nothing to do. */
+        if (priv->active_task)
+                return;
+
+        /* Get the last added task, thereby compressing the updates into one. */
+        to_run = G_TASK (g_queue_peek_tail (&priv->tasks));
+        if (to_run == NULL)
+                return;
+
+        /* And run it! */
+        gsd_backlight_run_set_helper (backlight, to_run);
+}
+#endif /* HAVE_GUDEV */
+
+static GnomeRROutput*
+gsd_backlight_rr_find_output (GsdBacklight *backlight, gboolean controllable)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+        GnomeRROutput *output = NULL;
+        GnomeRROutput **outputs;
+        guint i;
+
+        /* search all X11 outputs for the device id */
+        outputs = gnome_rr_screen_list_outputs (priv->rr_screen);
+        if (outputs == NULL)
+                goto out;
+
+        for (i = 0; outputs[i] != NULL; i++) {
+                gboolean builtin = gnome_rr_output_is_builtin_display (outputs[i]);
+                gint backlight = gnome_rr_output_get_backlight (outputs[i]);
+
+                g_debug("Output %d: %s, backlight %d", i, builtin ? "builtin" : "external", backlight);
+                if (builtin && (!controllable || backlight >= 0)) {
+                        output = outputs[i];
+                        break;
+                }
+        }
+out:
+        return output;
+
+}
+
+gboolean
+gsd_backlight_get_available (GsdBacklight *backlight)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+
+        return priv->available;
+}
+
+/**
+ * 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.
+ *
+ * Returns: The last stable backlight value.
+ **/
+gint
+gsd_backlight_get_brightness (GsdBacklight *backlight, gint *target)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+
+        if (!priv->available)
+                return -1;
+
+        if (target)
+                *target = ABS_TO_PERCENTAGE (priv->brightness_min, priv->brightness_max, 
priv->brightness_target);
+
+        return ABS_TO_PERCENTAGE (priv->brightness_min, priv->brightness_max, priv->brightness_val);
+}
+
+static void
+gsd_backlight_set_brightness_val_async (GsdBacklight *backlight,
+                                        int value,
+                                        GCancellable *cancellable,
+                                        GAsyncReadyCallback callback,
+                                        gpointer user_data)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+        GError *error = NULL;
+        GTask *task = NULL;
+        GnomeRROutput *output;
+
+        value = MIN(priv->brightness_max, value);
+        value = MAX(priv->brightness_min, value);
+
+        priv->brightness_target = value;
+
+        task = g_task_new (backlight, cancellable, callback, user_data);
+        if (!priv->available) {
+                g_task_return_new_error (task, GSD_POWER_MANAGER_ERROR,
+                                         GSD_POWER_MANAGER_ERROR_FAILED,
+                                         "Cannot set brightness as no backlight was detected!");
+                return;
+        }
+
+        if (is_mocked ()) {
+                g_autofree gchar *contents = NULL;
+                g_debug ("Setting mock brightness: %d", value);
+
+                contents = g_strdup_printf ("%d", value);
+                if (!g_file_set_contents (MOCK_BRIGHTNESS_FILE, contents, -1, &error)) {
+                        g_warning ("Setting mock brightness failed: %s", error->message);
+                        g_task_return_error (task, error);
+                }
+                priv->brightness_val = priv->brightness_target;
+                g_object_notify_by_pspec (G_OBJECT (backlight), props[PROP_BRIGHTNESS]);
+                g_task_return_int (task, gsd_backlight_get_brightness (backlight, NULL));
+
+                return;
+        }
+
+#ifdef HAVE_GUDEV
+        if (priv->udev_device != NULL) {
+                BacklightHelperData *task_data;
+
+                task_data = g_new0 (BacklightHelperData, 1);
+                task_data->value = priv->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 (&priv->tasks, task);
+                gsd_backlight_process_taskqueue (backlight);
+
+                return;
+        }
+#endif /* HAVE_GUDEV */
+
+        /* Fallback to setting via GNOME RR/X11 */
+        output = gsd_backlight_rr_find_output (backlight, TRUE);
+        if (output) {
+                if (!gnome_rr_output_set_backlight (output, value, &error)) {
+                        g_warning ("Setting brightness failed: %s", error->message);
+                        g_task_return_error (task, error);
+                        return;
+                }
+                priv->brightness_val = gnome_rr_output_get_backlight (output);
+                g_object_notify_by_pspec (G_OBJECT (backlight), props[PROP_BRIGHTNESS]);
+                g_task_return_int (task, gsd_backlight_get_brightness (backlight, NULL));
+
+                return;
+        }
+
+        g_task_return_new_error (task, GSD_POWER_MANAGER_ERROR,
+                                 GSD_POWER_MANAGER_ERROR_FAILED,
+                                 "No method to set brightness!");
+
+        g_warning ("No method to set brightness! This is likely a programming error.");
+}
+
+void
+gsd_backlight_set_brightness_async (GsdBacklight *backlight,
+                                    gint percent,
+                                    GCancellable *cancellable,
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+
+        gsd_backlight_set_brightness_val_async (backlight,
+                                                PERCENTAGE_TO_ABS (priv->brightness_min, 
priv->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,
+ * gsd_backlight_step_up_async or 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 where 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 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)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+        gint value;
+
+        value = priv->brightness_target + priv->brightness_step;
+
+        gsd_backlight_set_brightness_val_async (backlight,
+                                                value,
+                                                cancellable,
+                                                callback,
+                                                user_data);
+}
+
+void
+gsd_backlight_step_down_async (GsdBacklight *backlight,
+                               GCancellable *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+        gint value;
+
+        value = priv->brightness_target - priv->brightness_step;
+
+        gsd_backlight_set_brightness_val_async (backlight,
+                                                value,
+                                                cancellable,
+                                                callback,
+                                                user_data);
+}
+
+gint
+gsd_backlight_get_output_id (GsdBacklight *backlight)
+{
+        GnomeRROutput *output;
+
+        output = gsd_backlight_rr_find_output (backlight, FALSE);
+        if (output == NULL)
+                return -1;
+
+        /* XXX: Is this really that simple? The old code did a lot more, but
+         * did not return anything sensible these days.
+         * The outputs need to be in the same order as the MetaScreen object
+         * returns to the shell. */
+        return gnome_rr_output_get_id (output);
+}
+
+static void
+gsd_backlight_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+        GsdBacklight *backlight = GSD_BACKLIGHT (object);
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+
+        switch (prop_id) {
+        case PROP_RR_SCREEN:
+                g_value_set_object (value, priv->rr_screen);
+                break;
+
+        case PROP_AVAILABLE:
+                g_value_set_boolean (value, priv->available);
+                break;
+
+        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
+gsd_backlight_set_property (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (object);
+
+        switch (prop_id) {
+        case PROP_RR_SCREEN:
+                priv->rr_screen = g_value_dup_object (value);
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gsd_backlight_constructed (GObject *object)
+{
+        GsdBacklight *backlight = GSD_BACKLIGHT (object);
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (object);
+        GnomeRROutput* output = NULL;
+
+        /* If mocked, set as available and set the brightness (which will also
+         * create the file for the test environment). */
+        if (is_mocked ()) {
+                g_debug ("Using mock for backlight.");
+                priv->available = TRUE;
+                priv->brightness_min = 0;
+                priv->brightness_max = 100;
+
+                gsd_backlight_set_brightness_async (backlight, GSD_MOCK_DEFAULT_BRIGHTNESS, NULL, NULL, 
NULL);
+
+                goto done;
+        }
+
+#ifdef HAVE_GUDEV
+        /* Try finding a udev device. */
+        if (gsd_backlight_udev_init (backlight))
+                goto done;
+#endif /* HAVE_GUDEV */
+
+        /* Try GNOME RR as a fallback. */
+        output = gsd_backlight_rr_find_output (backlight, TRUE);
+        if (output) {
+                g_debug ("Using GNOME RR (mutter) for backlight.");
+                priv->available = TRUE;
+                priv->brightness_min = 0;
+                priv->brightness_max = 100;
+                priv->brightness_val = gnome_rr_output_get_backlight (output);
+                priv->brightness_step = gnome_rr_output_get_min_backlight_step (output);
+        }
+
+        g_debug ("No usable backlight found.");
+
+done:
+        priv->brightness_target = priv->brightness_val;
+        priv->brightness_step = MAX(priv->brightness_step, BRIGHTNESS_STEP_AMOUNT(priv->brightness_max - 
priv->brightness_min + 1));
+}
+
+static void
+gsd_backlight_finalize (GObject *object)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (object);
+
+#ifdef HAVE_GUDEV
+        g_assert (priv->active_task == NULL);
+        g_assert (g_queue_is_empty (&priv->tasks));
+        g_clear_object (&priv->udev);
+        g_clear_object (&priv->udev_device);
+        if (priv->idle_update) {
+                g_source_remove (priv->idle_update);
+                priv->idle_update = 0;
+        }
+#endif /* HAVE_GUDEV */
+
+        g_clear_object (&priv->rr_screen);
+}
+
+static void
+gsd_backlight_class_init (GsdBacklightClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->constructed = gsd_backlight_constructed;
+        object_class->finalize = gsd_backlight_finalize;
+        object_class->get_property = gsd_backlight_get_property;
+        object_class->set_property = gsd_backlight_set_property;
+
+        props[PROP_RR_SCREEN] = g_param_spec_object ("rr-screen", "GnomeRRScreen",
+                                                     "GnomeRRScreen usable for backlight control.",
+                                                     GNOME_TYPE_RR_SCREEN,
+                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 
G_PARAM_STATIC_STRINGS);
+
+        props[PROP_AVAILABLE] = g_param_spec_boolean ("available", "Brightness control availability",
+                                                      "Whether screen brightness for the internal screen can 
be controlled.",
+                                                      FALSE,
+                                                      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+        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)
+{
+        GsdBacklightPrivate *priv = GSD_BACKLIGHT_GET_PRIVATE (backlight);
+
+        priv->available = FALSE;
+        priv->brightness_target = -1;
+        priv->brightness_min = -1;
+        priv->brightness_max = -1;
+        priv->brightness_val = -1;
+        priv->brightness_step = 1;
+
+#ifdef HAVE_GUDEV
+        priv->active_task = NULL;
+        g_queue_init (&priv->tasks);
+#endif /* HAVE_GUDEV */
+}
+
+GsdBacklight *
+gsd_backlight_new (GnomeRRScreen *rr_screen)
+{
+        return g_object_new (GSD_TYPE_BACKLIGHT, "rr-screen", rr_screen, NULL);
+}
+
diff --git a/plugins/power/gsd-backlight.h b/plugins/power/gsd-backlight.h
new file mode 100644
index 00000000..281fc658
--- /dev/null
+++ b/plugins/power/gsd-backlight.h
@@ -0,0 +1,70 @@
+/* -*- mode: c; style: linux -*-
+ * 
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Written by: Benjamin Berg <bberg redhat com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GSD_BACKLIGHT_H
+#define _GSD_BACKLIGHT_H
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnome-desktop/gnome-rr.h>
+
+
+G_BEGIN_DECLS
+
+struct _GsdBacklight
+{
+  GObject object;
+};
+
+#define GSD_TYPE_BACKLIGHT gsd_backlight_get_type ()
+G_DECLARE_FINAL_TYPE (GsdBacklight, gsd_backlight, GSD, BACKLIGHT, GObject);
+
+gboolean gsd_backlight_get_available     (GsdBacklight         *backlight);
+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);
+
+gint gsd_backlight_set_brightness_finish (GsdBacklight         *backlight,
+                                          GAsyncResult         *res,
+                                          GError              **error);
+
+
+gint gsd_backlight_get_output_id         (GsdBacklight         *backlight);
+GsdBacklight* gsd_backlight_new          (GnomeRRScreen        *screen);
+
+
+G_END_DECLS
+
+#endif /* _GSD_BACKLIGHT_H */
diff --git a/plugins/power/gsd-power-manager.c b/plugins/power/gsd-power-manager.c
index 67d475cf..dbb3eb01 100644
--- a/plugins/power/gsd-power-manager.c
+++ b/plugins/power/gsd-power-manager.c
@@ -43,6 +43,7 @@
 #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"
@@ -157,6 +158,7 @@ struct GsdPowerManagerPrivate
         gboolean                 battery_is_low; /* laptop battery low, or UPS discharging */
 
         /* Brightness */
+        GsdBacklight            *backlight;
         gboolean                 backlight_available;
         gint                     pre_dim_brightness; /* level, not percentage */
 
@@ -1439,52 +1441,30 @@ backlight_iface_emit_changed (GsdPowerManager *manager,
                                        NULL);
 }
 
-static gboolean
+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,
-                       GError **error)
+                       gint idle_percentage)
 {
-        gint min;
-        gint max;
-        gint now;
-        gint idle;
-        gboolean ret = FALSE;
+        gint brightness;
 
         if (!manager->priv->backlight_available)
-                return TRUE;
-
-        now = backlight_get_abs (manager->priv->rr_screen, error);
-        if (now < 0) {
-                goto out;
-        }
-
-        /* is the dim brightness actually *dimmer* than the
-         * brightness we have now? */
-        min = backlight_get_min (manager->priv->rr_screen);
-        max = backlight_get_max (manager->priv->rr_screen, error);
-        if (max < 0) {
-                goto out;
-        }
-        idle = PERCENTAGE_TO_ABS (min, max, idle_percentage);
-        if (idle > now) {
-                g_debug ("brightness already now %i/%i, so "
-                         "ignoring dim to %i/%i",
-                         now, max, idle, max);
-                ret = TRUE;
-                goto out;
-        }
-        ret = backlight_set_abs (manager->priv->rr_screen,
-                                 idle,
-                                 error);
-        if (!ret) {
-                goto out;
-        }
+                return;
 
-        /* save for undim */
-        manager->priv->pre_dim_brightness = now;
+        /* 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->priv->backlight, &brightness);
+        if (brightness < idle_percentage)
+                return;
 
-out:
-        return ret;
+        manager->priv->pre_dim_brightness = brightness;
+        gsd_backlight_set_brightness_async (manager->priv->backlight, idle_percentage, NULL, NULL, NULL);
 }
 
 static gboolean
@@ -1611,13 +1591,7 @@ idle_set_mode (GsdPowerManager *manager, GsdPowerIdleMode mode)
                 /* display backlight */
                 idle_percentage = g_settings_get_int (manager->priv->settings,
                                                       "idle-brightness");
-                ret = display_backlight_dim (manager, idle_percentage, &error);
-                if (!ret) {
-                        g_warning ("failed to set dim backlight to %i%%: %s",
-                                   idle_percentage,
-                                   error->message);
-                        g_clear_error (&error);
-                }
+                display_backlight_dim (manager, idle_percentage);
 
                 /* keyboard backlight */
                 ret = kbd_backlight_dim (manager, idle_percentage, &error);
@@ -1662,17 +1636,11 @@ idle_set_mode (GsdPowerManager *manager, GsdPowerIdleMode mode)
 
                 /* reset brightness if we dimmed */
                 if (manager->priv->pre_dim_brightness >= 0) {
-                        ret = backlight_set_abs (manager->priv->rr_screen,
-                                                 manager->priv->pre_dim_brightness,
-                                                 &error);
-                        if (!ret) {
-                                g_warning ("failed to restore backlight to %i: %s",
-                                           manager->priv->pre_dim_brightness,
-                                           error->message);
-                                g_clear_error (&error);
-                        } else {
-                                manager->priv->pre_dim_brightness = -1;
-                        }
+                        gsd_backlight_set_brightness_async (manager->priv->backlight,
+                                                            manager->priv->pre_dim_brightness,
+                                                            NULL, NULL, NULL);
+                        /* XXX: Ideally we would do this from the async callback. */
+                        manager->priv->pre_dim_brightness = -1;
                 }
 
                 /* only toggle keyboard if present and already toggled off */
@@ -2489,8 +2457,14 @@ on_rr_screen_acquired (GObject      *object,
                 on_randr_event (manager->priv->rr_screen, manager);
         }
 
-        /* check whether a backlight is available */
-        manager->priv->backlight_available = backlight_available (manager->priv->rr_screen);
+        /* Resolve screen backlight */
+        manager->priv->backlight = gsd_backlight_new (manager->priv->rr_screen);
+        manager->priv->backlight_available = gsd_backlight_get_available (manager->priv->backlight);
+
+        g_signal_connect_object (manager->priv->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->priv->logind_proxy, "g-signal",
@@ -2567,7 +2541,7 @@ on_rr_screen_acquired (GObject      *object,
            (likely, considering that to get here we need a reply from gnome-shell)
         */
         if (manager->priv->backlight_available) {
-                manager->priv->ambient_percentage_old = backlight_get_percentage (manager->priv->rr_screen, 
NULL);
+                manager->priv->ambient_percentage_old = gsd_backlight_get_brightness 
(manager->priv->backlight, NULL);
                 backlight_iface_emit_changed (manager, GSD_POWER_DBUS_INTERFACE_SCREEN,
                                               manager->priv->ambient_percentage_old, NULL);
         } else {
@@ -2580,7 +2554,6 @@ on_rr_screen_acquired (GObject      *object,
 static void
 iio_proxy_changed (GsdPowerManager *manager)
 {
-        GError *error = NULL;
         GVariant *val_has = NULL;
         GVariant *val_als = NULL;
         gdouble brightness;
@@ -2627,10 +2600,10 @@ iio_proxy_changed (GsdPowerManager *manager)
         g_debug ("Setting brightness from ambient %.1f%%",
                  manager->priv->ambient_accumulator);
         pc = manager->priv->ambient_accumulator;
-        if (!backlight_set_percentage (manager->priv->rr_screen, &pc, &error)) {
-                g_warning ("failed to set brightness: %s", error->message);
-                g_error_free (error);
-        }
+
+        gsd_backlight_set_brightness_async (manager->priv->backlight, pc, NULL, NULL, NULL);
+
+        /* Assume setting worked. */
         manager->priv->ambient_percentage_old = pc;
 out:
         g_clear_pointer (&val_has, g_variant_unref);
@@ -2856,48 +2829,76 @@ handle_method_call_keyboard (GsdPowerManager *manager,
 }
 
 static void
-handle_method_call_screen (GsdPowerManager *manager,
-                           const gchar *method_name,
-                           GVariant *parameters,
-                           GDBusMethodInvocation *invocation)
+backlight_brightness_step_cb (GObject *object,
+                              GAsyncResult *res,
+                              gpointer user_data)
 {
-        gint value = -1;
+        GsdBacklight *backlight = GSD_BACKLIGHT (object);
+        GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
+        GsdPowerManager *manager;
         GError *error = NULL;
+        gint brightness;
 
-        if (!manager->priv->backlight_available) {
-               g_set_error_literal (&error,
-                                    GSD_POWER_MANAGER_ERROR,
-                                    GSD_POWER_MANAGER_ERROR_FAILED,
-                                    "Screen backlight not available");
-                goto out;
-        }
-
-        if (g_strcmp0 (method_name, "StepUp") == 0) {
-                g_debug ("screen step up");
-                value = backlight_step_up (manager->priv->rr_screen, &error);
-                backlight_iface_emit_changed (manager, GSD_POWER_DBUS_INTERFACE_SCREEN, value, NULL);
-        } else if (g_strcmp0 (method_name, "StepDown") == 0) {
-                g_debug ("screen step down");
-                value = backlight_step_down (manager->priv->rr_screen, &error);
-                backlight_iface_emit_changed (manager, GSD_POWER_DBUS_INTERFACE_SCREEN, value, NULL);
-        } else {
-                g_assert_not_reached ();
-        }
+        manager = g_object_get_data (G_OBJECT (invocation), "gsd-power-manager");
+        /* Return the invocation. */
+        brightness = gsd_backlight_set_brightness_finish (backlight, res, &error);
 
         /* ambient brightness no longer valid */
-        manager->priv->ambient_percentage_old = value;
+        manager->priv->ambient_percentage_old = brightness;
         manager->priv->ambient_norm_required = TRUE;
 
-out:
-        /* return value */
-        if (value < 0) {
+        if (error) {
                 g_dbus_method_invocation_take_error (invocation,
                                                      error);
         } else {
                 g_dbus_method_invocation_return_value (invocation,
                                                        g_variant_new ("(ii)",
-                                                                      value,
-                                                                      backlight_get_output_id 
(manager->priv->rr_screen)));
+                                                                      brightness,
+                                                                      gsd_backlight_get_output_id 
(backlight)));
+        }
+
+        g_object_unref (manager);
+}
+
+/* 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->priv->ambient_percentage_old = brightness;
+                manager->priv->ambient_norm_required = TRUE;
+        }
+
+        g_object_unref (manager);
+}
+
+static void
+handle_method_call_screen (GsdPowerManager *manager,
+                           const gchar *method_name,
+                           GVariant *parameters,
+                           GDBusMethodInvocation *invocation)
+{
+        g_object_set_data (G_OBJECT (invocation), "gsd-power-manager", g_object_ref (manager));
+
+        if (g_strcmp0 (method_name, "StepUp") == 0) {
+                g_debug ("screen step up");
+                gsd_backlight_step_up_async (manager->priv->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->priv->backlight, NULL, backlight_brightness_step_cb, 
invocation);
+
+        } else {
+                g_assert_not_reached ();
         }
 }
 
@@ -2953,7 +2954,7 @@ handle_get_property_other (GsdPowerManager *manager,
         }
 
         if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_SCREEN) == 0) {
-                value = backlight_get_percentage (manager->priv->rr_screen, NULL);
+                value = gsd_backlight_get_brightness (manager->priv->backlight, NULL);
                 retval = g_variant_new_int32 (value);
         } else if (manager->priv->upower_kbd_proxy &&
                    g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_KEYBOARD) == 0) {
@@ -3015,19 +3016,15 @@ handle_set_property_other (GsdPowerManager *manager,
         }
 
         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 (backlight_set_percentage (manager->priv->rr_screen, &brightness_value, error)) {
-                        backlight_iface_emit_changed (manager, GSD_POWER_DBUS_INTERFACE_SCREEN, 
brightness_value, NULL);
+                gsd_backlight_set_brightness_async (manager->priv->backlight, brightness_value,
+                                                    NULL,
+                                                    backlight_brightness_set_cb, g_object_ref (manager));
 
-                        /* ambient brightness no longer valid */
-                        manager->priv->ambient_percentage_old = brightness_value;
-                        manager->priv->ambient_norm_required = TRUE;
-                        return TRUE;
-                } else {
-                        g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
-                                     "Setting %s.%s failed", interface_name, property_name);
-                        return FALSE;
-                }
+                return TRUE;
         } else 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->priv->kbd_brightness_max,
diff --git a/plugins/power/meson.build b/plugins/power/meson.build
index 631823a8..60e0f15d 100644
--- a/plugins/power/meson.build
+++ b/plugins/power/meson.build
@@ -1,6 +1,6 @@
 sources = files(
   'gpm-common.c',
-  'gsd-backlight-linux.c',
+  'gsd-backlight.c',
   'gsd-power-manager.c',
   'main.c'
 )


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]