diff options
Diffstat (limited to 'src/gpm-brightness.c')
-rw-r--r-- | src/gpm-brightness.c | 997 |
1 files changed, 997 insertions, 0 deletions
diff --git a/src/gpm-brightness.c b/src/gpm-brightness.c new file mode 100644 index 0000000..e92b6e4 --- /dev/null +++ b/src/gpm-brightness.c @@ -0,0 +1,997 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2008-2010 Richard Hughes <[email protected]> + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <errno.h> + +#include <X11/Xatom.h> +#include <X11/Xlib.h> +#include <X11/extensions/Xrandr.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> +#include <string.h> +#include <sys/time.h> +#include <sys/types.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#include "egg-discrete.h" +#include "egg-debug.h" +#include "egg-string.h" + +#include "gpm-brightness.h" +#include "gpm-common.h" +#include "gpm-marshal.h" + +#define GPM_BRIGHTNESS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GPM_TYPE_BRIGHTNESS, GpmBrightnessPrivate)) +#define GPM_SOLE_SETTER_USE_CACHE TRUE /* this may be insanity */ + +struct GpmBrightnessPrivate +{ + gboolean has_changed_events; + gboolean cache_trusted; + guint cache_percentage; + guint last_set_hw; + Atom backlight; + Display *dpy; + GdkWindow *root_window; + guint shared_value; + gboolean has_extension; +#ifdef HAVE_XRANDR_13 + gboolean has_randr13; +#endif + gboolean hw_changed; + /* A cache of XRRScreenResources is used as XRRGetScreenResources is expensive */ + GPtrArray *resources; + gint extension_levels; + gint extension_current; +}; + +enum { + BRIGHTNESS_CHANGED, + LAST_SIGNAL +}; + +typedef enum { + ACTION_BACKLIGHT_GET, + ACTION_BACKLIGHT_SET, + ACTION_BACKLIGHT_INC, + ACTION_BACKLIGHT_DEC +} GpmXRandROp; + +G_DEFINE_TYPE (GpmBrightness, gpm_brightness, G_TYPE_OBJECT) +static guint signals [LAST_SIGNAL] = { 0 }; +static gpointer gpm_brightness_object = NULL; + +/** + * gpm_brightness_helper_get_value: + **/ +static gint +gpm_brightness_helper_get_value (const gchar *argument) +{ + gboolean ret; + GError *error = NULL; + gchar *stdout_data = NULL; + gint exit_status = 0; + gint value = -1; + gchar *command = NULL; + + /* get the data */ + command = g_strdup_printf (SBINDIR "/mate-power-backlight-helper --%s", argument); + ret = g_spawn_command_line_sync (command, + &stdout_data, NULL, &exit_status, &error); + if (!ret) { + egg_error ("failed to get value: %s", error->message); + g_error_free (error); + goto out; + } + egg_debug ("executing %s retval: %i", command, exit_status); + + /* parse for a number */ + ret = egg_strtoint (stdout_data, &value); + if (!ret) + goto out; +out: + g_free (command); + g_free (stdout_data); + return value; +} + +/** + * gpm_brightness_helper_set_value: + **/ +static gboolean +gpm_brightness_helper_set_value (const gchar *argument, gint value) +{ + gboolean ret; + GError *error = NULL; + gint exit_status = 0; + gchar *command = NULL; + + /* get the data */ + command = g_strdup_printf ("pkexec " SBINDIR "/mate-power-backlight-helper --%s %i", argument, value); + ret = g_spawn_command_line_sync (command, NULL, NULL, &exit_status, &error); + if (!ret) { + egg_error ("failed to get value: %s", error->message); + g_error_free (error); + goto out; + } + egg_debug ("executing %s retval: %i", command, exit_status); +out: + g_free (command); + return ret; +} + +/** + * gpm_brightness_get_step: + * @levels: The number of levels supported + * Return value: the amount of hardware steps to do on each increment or decrement + **/ +static guint +gpm_brightness_get_step (guint levels) +{ + /* macbook pro has a bazzillion brightness levels, do in 5% steps */ + if (levels > 20) + return levels / 20; + return 1; +} + +/** + * gpm_brightness_output_get_internal: + **/ +static gboolean +gpm_brightness_output_get_internal (GpmBrightness *brightness, RROutput output, guint *cur) +{ + unsigned long nitems; + unsigned long bytes_after; + guint *prop; + Atom actual_type; + int actual_format; + gboolean ret = FALSE; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + if (brightness->priv->backlight == None) + return FALSE; + + if (XRRGetOutputProperty (brightness->priv->dpy, output, brightness->priv->backlight, + 0, 4, False, False, None, + &actual_type, &actual_format, + &nitems, &bytes_after, ((unsigned char **)&prop)) != Success) { + egg_debug ("failed to get property"); + return FALSE; + } + if (actual_type == XA_INTEGER && nitems == 1 && actual_format == 32) { + memcpy (cur, prop, sizeof (guint)); + ret = TRUE; + } + XFree (prop); + return ret; +} + +/** + * gpm_brightness_output_set_internal: + **/ +static gboolean +gpm_brightness_output_set_internal (GpmBrightness *brightness, RROutput output, guint value) +{ + gboolean ret = TRUE; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + /* don't abort on error */ + gdk_error_trap_push (); + XRRChangeOutputProperty (brightness->priv->dpy, output, brightness->priv->backlight, XA_INTEGER, 32, + PropModeReplace, (unsigned char *) &value, 1); + XFlush (brightness->priv->dpy); + gdk_flush (); + if (gdk_error_trap_pop ()) { + egg_warning ("failed to XRRChangeOutputProperty for brightness %i", value); + ret = FALSE; + } + /* we changed the hardware */ + if (ret) + brightness->priv->hw_changed = TRUE; + return ret; +} + +/** + * gpm_brightness_setup_display: + **/ +static gboolean +gpm_brightness_setup_display (GpmBrightness *brightness) +{ + gint major, minor; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + /* get the display */ + brightness->priv->dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default()); + if (!brightness->priv->dpy) { + egg_error ("Cannot open display"); + return FALSE; + } + /* is XRandR new enough? */ + if (!XRRQueryVersion (brightness->priv->dpy, &major, &minor)) { + egg_debug ("RandR extension missing"); + return FALSE; + } + if (major < 1 || (major == 1 && minor < 2)) { + egg_debug ("RandR version %d.%d too old", major, minor); + return FALSE; + } + /* can we support BACKLIGHT */ + brightness->priv->backlight = XInternAtom (brightness->priv->dpy, "BACKLIGHT", True); + if (brightness->priv->backlight == None) { + egg_debug ("No outputs have backlight property"); + return FALSE; + } + return TRUE; +} + +#ifdef HAVE_XRANDR_13 +/** + * gpm_brightness_setup_version: Check whether xserver really supports xrandr-1.3 features. + **/ +static gboolean +gpm_brightness_setup_version (GpmBrightness *brightness) +{ + gint major, minor; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + /* get the display */ + brightness->priv->dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default()); + if (!brightness->priv->dpy) { + egg_error ("Cannot open display"); + return FALSE; + } + if (!XRRQueryVersion (brightness->priv->dpy, &major, &minor)) { + return FALSE; + } + if (major == 1 && minor < 3) { + egg_debug ("RandR version %d.%d does not support XRRGetScreenResourcesCurrent", major, minor); + return FALSE; + } + return TRUE; +} +#endif + +/** + * gpm_brightness_output_get_limits: + **/ +static gboolean +gpm_brightness_output_get_limits (GpmBrightness *brightness, RROutput output, + guint *min, guint *max) +{ + XRRPropertyInfo *info; + gboolean ret = TRUE; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + info = XRRQueryOutputProperty (brightness->priv->dpy, output, brightness->priv->backlight); + if (info == NULL) { + egg_debug ("could not get output property"); + return FALSE; + } + if (!info->range || info->num_values != 2) { + egg_debug ("was not range"); + ret = FALSE; + goto out; + } + *min = info->values[0]; + *max = info->values[1]; +out: + XFree (info); + return ret; +} + +/** + * gpm_brightness_output_get_percentage: + **/ +static gboolean +gpm_brightness_output_get_percentage (GpmBrightness *brightness, RROutput output) +{ + guint cur; + gboolean ret; + guint min, max; + guint percentage; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + ret = gpm_brightness_output_get_internal (brightness, output, &cur); + if (!ret) + return FALSE; + ret = gpm_brightness_output_get_limits (brightness, output, &min, &max); + if (!ret || min == max) + return FALSE; + egg_debug ("hard value=%i, min=%i, max=%i", cur, min, max); + percentage = egg_discrete_to_percent (cur, (max-min)+1); + egg_debug ("percentage %i", percentage); + brightness->priv->shared_value = percentage; + return TRUE; +} + +/** + * gpm_brightness_output_down: + **/ +static gboolean +gpm_brightness_output_down (GpmBrightness *brightness, RROutput output) +{ + guint cur; + guint step; + gboolean ret; + guint min, max; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + ret = gpm_brightness_output_get_internal (brightness, output, &cur); + if (!ret) + return FALSE; + ret = gpm_brightness_output_get_limits (brightness, output, &min, &max); + if (!ret || min == max) + return FALSE; + egg_debug ("hard value=%i, min=%i, max=%i", cur, min, max); + if (cur == min) { + egg_debug ("already min"); + return TRUE; + } + step = gpm_brightness_get_step ((max-min)+1); + if (cur < step) { + egg_debug ("truncating to %i", min); + cur = min; + } else { + cur -= step; + } + ret = gpm_brightness_output_set_internal (brightness, output, cur); + return ret; +} + +/** + * gpm_brightness_output_up: + **/ +static gboolean +gpm_brightness_output_up (GpmBrightness *brightness, RROutput output) +{ + guint cur; + gboolean ret; + guint min, max; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + ret = gpm_brightness_output_get_internal (brightness, output, &cur); + if (!ret) + return FALSE; + ret = gpm_brightness_output_get_limits (brightness, output, &min, &max); + if (!ret || min == max) + return FALSE; + egg_debug ("hard value=%i, min=%i, max=%i", cur, min, max); + if (cur == max) { + egg_debug ("already max"); + return TRUE; + } + cur += gpm_brightness_get_step ((max-min)+1); + if (cur > max) { + egg_debug ("truncating to %i", max); + cur = max; + } + ret = gpm_brightness_output_set_internal (brightness, output, cur); + return ret; +} + +/** + * gpm_brightness_output_set: + **/ +static gboolean +gpm_brightness_output_set (GpmBrightness *brightness, RROutput output) +{ + guint cur; + gboolean ret; + guint min, max; + gint i; + gint shared_value_abs; + guint step; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + ret = gpm_brightness_output_get_internal (brightness, output, &cur); + if (!ret) + return FALSE; + ret = gpm_brightness_output_get_limits (brightness, output, &min, &max); + if (!ret || min == max) + return FALSE; + + shared_value_abs = egg_discrete_from_percent (brightness->priv->shared_value, (max-min)+1); + egg_debug ("percent=%i, absolute=%i", brightness->priv->shared_value, shared_value_abs); + + egg_debug ("hard value=%i, min=%i, max=%i", cur, min, max); + if (shared_value_abs > (gint) max) + shared_value_abs = max; + if (shared_value_abs < (gint) min) + shared_value_abs = min; + if ((gint) cur == shared_value_abs) { + egg_debug ("already set %i", cur); + return TRUE; + } + + /* step the correct way */ + if ((gint) cur < shared_value_abs) { + + /* some adaptors have a large number of steps */ + step = gpm_brightness_get_step (shared_value_abs - cur); + egg_debug ("using step of %i", step); + + /* going up */ + for (i=cur; i<=shared_value_abs; i+=step) { + ret = gpm_brightness_output_set_internal (brightness, output, i); + if (!ret) + break; + if ((gint) cur != shared_value_abs) + g_usleep (1000 * GPM_BRIGHTNESS_DIM_INTERVAL); + } + } else { + + /* some adaptors have a large number of steps */ + step = gpm_brightness_get_step (cur - shared_value_abs); + egg_debug ("using step of %i", step); + + /* going down */ + for (i=cur; i>=shared_value_abs; i-=step) { + ret = gpm_brightness_output_set_internal (brightness, output, i); + if (!ret) + break; + if ((gint) cur != shared_value_abs) + g_usleep (1000 * GPM_BRIGHTNESS_DIM_INTERVAL); + } + } + return TRUE; +} + +/** + * gpm_brightness_foreach_resource: + **/ +static gboolean +gpm_brightness_foreach_resource (GpmBrightness *brightness, GpmXRandROp op, XRRScreenResources *resources) +{ + gint i; + gboolean ret; + gboolean success_any = FALSE; + RROutput output; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + /* do for each output */ + for (i=0; i<resources->noutput; i++) { + output = resources->outputs[i]; + egg_debug ("resource %i of %i", i+1, resources->noutput); + if (op==ACTION_BACKLIGHT_GET) { + ret = gpm_brightness_output_get_percentage (brightness, output); + } else if (op==ACTION_BACKLIGHT_INC) { + ret = gpm_brightness_output_up (brightness, output); + } else if (op==ACTION_BACKLIGHT_DEC) { + ret = gpm_brightness_output_down (brightness, output); + } else if (op==ACTION_BACKLIGHT_SET) { + ret = gpm_brightness_output_set (brightness, output); + } else { + ret = FALSE; + egg_warning ("op not known"); + } + if (ret) { + success_any = TRUE; + } + } + return success_any; +} + +/** + * gpm_brightness_foreach_screen: + **/ +static gboolean +gpm_brightness_foreach_screen (GpmBrightness *brightness, GpmXRandROp op) +{ + guint i; + guint length; + XRRScreenResources *resource; + gboolean ret; + gboolean success_any = FALSE; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + /* Return immediately if we can't use XRandR */ + if (!brightness->priv->has_extension) + return FALSE; + + /* do for each screen */ + length = brightness->priv->resources->len; + for (i=0; i<length; i++) { + resource = (XRRScreenResources *) g_ptr_array_index (brightness->priv->resources, i); + egg_debug ("using resource %p", resource); + ret = gpm_brightness_foreach_resource (brightness, op, resource); + if (ret) + success_any = TRUE; + } + XSync (brightness->priv->dpy, False); + return success_any; +} + +/** + * gpm_brightness_trust_cache: + * @brightness: This brightness class instance + * Return value: if we can trust the cache + **/ +static gboolean +gpm_brightness_trust_cache (GpmBrightness *brightness) +{ + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + /* only return the cached value if the cache is trusted and we have change events */ + if (brightness->priv->cache_trusted && brightness->priv->has_changed_events) { + egg_debug ("using cache for value %u (okay)", brightness->priv->cache_percentage); + return TRUE; + } + + /* can we trust that if we set a value 5 minutes ago, will it still be valid now? + * if we have multiple things setting policy on the workstation, e.g. fast user switching + * or kpowersave, then this will be invalid -- this logic may be insane */ + if (GPM_SOLE_SETTER_USE_CACHE && brightness->priv->cache_trusted) { + egg_warning ("using cache for value %u (probably okay)", brightness->priv->cache_percentage); + return TRUE; + } + return FALSE; +} + +/** + * gpm_brightness_set: + * @brightness: This brightness class instance + * @percentage: The percentage brightness + * @hw_changed: If the hardware was changed, i.e. the brightness changed + * Return value: %TRUE if success, %FALSE if there was an error + **/ +gboolean +gpm_brightness_set (GpmBrightness *brightness, guint percentage, gboolean *hw_changed) +{ + gboolean ret = FALSE; + gboolean trust_cache; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + /* can we check the new value with the cache? */ + trust_cache = gpm_brightness_trust_cache (brightness); + if (trust_cache && percentage == brightness->priv->cache_percentage) { + egg_debug ("not setting the same value %i", percentage); + return TRUE; + } + + /* set the value we want */ + brightness->priv->shared_value = percentage; + + /* reset to not-changed */ + brightness->priv->hw_changed = FALSE; + ret = gpm_brightness_foreach_screen (brightness, ACTION_BACKLIGHT_SET); + + /* legacy fallback */ + if (!ret) { + if (brightness->priv->extension_levels < 0) + brightness->priv->extension_levels = gpm_brightness_helper_get_value ("get-max-brightness"); + brightness->priv->extension_current = egg_discrete_from_percent (percentage, brightness->priv->extension_levels+1); + ret = gpm_brightness_helper_set_value ("set-brightness", brightness->priv->extension_current); + } + + /* did the hardware have to be modified? */ + if (ret && hw_changed != NULL) + *hw_changed = brightness->priv->hw_changed; + + /* we did something to the hardware, so untrusted */ + if (ret) + brightness->priv->cache_trusted = FALSE; + + return ret; +} + +/** + * gpm_brightness_get: + * @brightness: This brightness class instance + * Return value: The percentage brightness, or -1 for no hardware or error + * + * Gets the current (or at least what this class thinks is current) percentage + * brightness. This is quick as no HAL inquiry is done. + **/ +gboolean +gpm_brightness_get (GpmBrightness *brightness, guint *percentage) +{ + gboolean ret = FALSE; + gboolean trust_cache; + guint percentage_local; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + g_return_val_if_fail (percentage != NULL, FALSE); + + /* can we use the cache? */ + trust_cache = gpm_brightness_trust_cache (brightness); + if (trust_cache) { + *percentage = brightness->priv->cache_percentage; + return TRUE; + } + + /* get the brightness from hardware -- slow */ + ret = gpm_brightness_foreach_screen (brightness, ACTION_BACKLIGHT_GET); + percentage_local = brightness->priv->shared_value; + + /* legacy fallback */ + if (!ret) { + if (brightness->priv->extension_levels < 0) + brightness->priv->extension_levels = gpm_brightness_helper_get_value ("get-max-brightness"); + brightness->priv->extension_current = gpm_brightness_helper_get_value ("get-brightness"); + percentage_local = egg_discrete_to_percent (brightness->priv->extension_current, brightness->priv->extension_levels+1); + ret = TRUE; + } + + /* valid? */ + if (percentage_local > 100) { + egg_warning ("percentage value of %i will be truncated", percentage_local); + percentage_local = 100; + } + + /* a new value is always trusted if the method and checks succeed */ + if (ret) { + brightness->priv->cache_percentage = percentage_local; + brightness->priv->cache_trusted = TRUE; + *percentage = percentage_local; + } else { + brightness->priv->cache_trusted = FALSE; + } + return ret; +} + +/** + * gpm_brightness_up: + * @brightness: This brightness class instance + * @hw_changed: If the hardware was changed, i.e. the brightness changed + * Return value: %TRUE if success, %FALSE if there was an error + * + * If possible, put the brightness of the LCD up one unit. + **/ +gboolean +gpm_brightness_up (GpmBrightness *brightness, gboolean *hw_changed) +{ + gboolean ret = FALSE; + guint step; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + /* reset to not-changed */ + brightness->priv->hw_changed = FALSE; + ret = gpm_brightness_foreach_screen (brightness, ACTION_BACKLIGHT_INC); + + /* did the hardware have to be modified? */ + if (ret && hw_changed != NULL) + *hw_changed = brightness->priv->hw_changed; + + /* we did something to the hardware, so untrusted */ + if (ret) + brightness->priv->cache_trusted = FALSE; + + /* legacy fallback */ + if (!ret) { + if (brightness->priv->extension_levels < 0) + brightness->priv->extension_levels = gpm_brightness_helper_get_value ("get-max-brightness"); + brightness->priv->extension_current = gpm_brightness_helper_get_value ("get-brightness"); + + /* increase by the step, limiting to the maximum possible levels */ + if (brightness->priv->extension_current < brightness->priv->extension_levels) { + step = gpm_brightness_get_step (brightness->priv->extension_levels); + brightness->priv->extension_current += step; + if (brightness->priv->extension_current > brightness->priv->extension_levels) + brightness->priv->extension_current = brightness->priv->extension_levels; + ret = gpm_brightness_helper_set_value ("set-brightness", brightness->priv->extension_current); + } + if (hw_changed != NULL) + *hw_changed = ret; + brightness->priv->cache_trusted = FALSE; + goto out; + } +out: + return ret; +} + +/** + * gpm_brightness_down: + * @brightness: This brightness class instance + * @hw_changed: If the hardware was changed, i.e. the brightness changed + * Return value: %TRUE if success, %FALSE if there was an error + * + * If possible, put the brightness of the LCD down one unit. + **/ +gboolean +gpm_brightness_down (GpmBrightness *brightness, gboolean *hw_changed) +{ + gboolean ret = FALSE; + guint step; + + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + /* reset to not-changed */ + brightness->priv->hw_changed = FALSE; + ret = gpm_brightness_foreach_screen (brightness, ACTION_BACKLIGHT_DEC); + + /* did the hardware have to be modified? */ + if (ret && hw_changed != NULL) + *hw_changed = brightness->priv->hw_changed; + + /* we did something to the hardware, so untrusted */ + if (ret) + brightness->priv->cache_trusted = FALSE; + + /* legacy fallback */ + if (!ret) { + if (brightness->priv->extension_levels < 0) + brightness->priv->extension_levels = gpm_brightness_helper_get_value ("get-max-brightness"); + brightness->priv->extension_current = gpm_brightness_helper_get_value ("get-brightness"); + + /* decrease by the step, limiting to zero */ + if (brightness->priv->extension_current > 0) { + step = gpm_brightness_get_step (brightness->priv->extension_levels); + brightness->priv->extension_current -= step; + if (brightness->priv->extension_current < 0) + brightness->priv->extension_current = 0; + ret = gpm_brightness_helper_set_value ("set-brightness", brightness->priv->extension_current); + } + if (hw_changed != NULL) + *hw_changed = ret; + brightness->priv->cache_trusted = FALSE; + goto out; + } +out: + return ret; +} + + +/** + * gpm_brightness_may_have_changed: + **/ +static void +gpm_brightness_may_have_changed (GpmBrightness *brightness) +{ + gboolean ret; + guint percentage; + ret = gpm_brightness_get (brightness, &percentage); + if (!ret) { + egg_warning ("failed to get output"); + return; + } + egg_debug ("emitting brightness-changed (%i)", percentage); + g_signal_emit (brightness, signals [BRIGHTNESS_CHANGED], 0, percentage); +} + +/** + * gpm_brightness_filter_xevents: + **/ +static GdkFilterReturn +gpm_brightness_filter_xevents (GdkXEvent *xevent, GdkEvent *event, gpointer data) +{ + GpmBrightness *brightness = GPM_BRIGHTNESS (data); + if (event->type == GDK_NOTHING) + return GDK_FILTER_CONTINUE; + gpm_brightness_may_have_changed (brightness); + return GDK_FILTER_CONTINUE; +} + + +static void gpm_brightness_update_cache (GpmBrightness *brightness); + +/** + * gpm_brightness_monitors_changed: + **/ +static void +gpm_brightness_monitors_changed (GdkScreen *screen, GpmBrightness *brightness) +{ + g_return_if_fail (GPM_IS_BRIGHTNESS (brightness)); + gpm_brightness_update_cache (brightness); +} + +/** + * gpm_brightness_update_cache: + **/ +static void +gpm_brightness_update_cache (GpmBrightness *brightness) +{ + guint length; + gint screen; + Window root; + GdkScreen *gscreen; + GdkDisplay *display; + XRRScreenResources *resource; + + g_return_if_fail (GPM_IS_BRIGHTNESS (brightness)); + + /* invalidate and remove all the previous entries */ + length = brightness->priv->resources->len; + if (length > 0) + g_ptr_array_set_size (brightness->priv->resources, 0); + + /* do for each screen */ + display = gdk_display_get_default (); + length = ScreenCount (brightness->priv->dpy); + for (screen = 0; screen < (gint) length; screen++) { + egg_debug ("screen %i of %i", screen+1, length); + gscreen = gdk_display_get_screen (display, screen); + + /* if we have not setup the changed on the monitor, set it here */ + if (g_object_get_data (G_OBJECT (gscreen), "gpk-set-monitors-changed") == NULL) { + egg_debug ("watching ::monitors_changed on %p", gscreen); + g_object_set_data (G_OBJECT (gscreen), "gpk-set-monitors-changed", (gpointer) "true"); + g_signal_connect (G_OBJECT (gscreen), "monitors_changed", + G_CALLBACK (gpm_brightness_monitors_changed), brightness); + } + + root = RootWindow (brightness->priv->dpy, screen); + /* XRRGetScreenResourcesCurrent is less expensive than + XRRGetScreenResources, however it is available only + in RandR 1.3 or higher and of course xserver needs + to support it. + */ +#ifdef HAVE_XRANDR_13 + if (brightness->priv->has_randr13) + resource = XRRGetScreenResourcesCurrent (brightness->priv->dpy, root); + else +#endif + resource = XRRGetScreenResources (brightness->priv->dpy, root); + + if (resource != NULL) { + egg_debug ("adding resource %p", resource); + g_ptr_array_add (brightness->priv->resources, resource); + } + } +} + +/** + * gpm_brightness_has_hw: + **/ +gboolean +gpm_brightness_has_hw (GpmBrightness *brightness) +{ + g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); + + /* use XRandR first */ + if (brightness->priv->has_extension) + return TRUE; + + /* fallback to legacy access */ + if (brightness->priv->extension_levels < 0) + brightness->priv->extension_levels = gpm_brightness_helper_get_value ("get-max-brightness"); + if (brightness->priv->extension_levels > 0) + return TRUE; + return FALSE; +} + +/** + * gpm_brightness_finalize: + **/ +static void +gpm_brightness_finalize (GObject *object) +{ + GpmBrightness *brightness; + g_return_if_fail (object != NULL); + g_return_if_fail (GPM_IS_BRIGHTNESS (object)); + brightness = GPM_BRIGHTNESS (object); + g_ptr_array_unref (brightness->priv->resources); + gdk_window_remove_filter (brightness->priv->root_window, + gpm_brightness_filter_xevents, brightness); + G_OBJECT_CLASS (gpm_brightness_parent_class)->finalize (object); +} + +/** + * gpm_brightness_class_init: + **/ +static void +gpm_brightness_class_init (GpmBrightnessClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = gpm_brightness_finalize; + + signals [BRIGHTNESS_CHANGED] = + g_signal_new ("brightness-changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GpmBrightnessClass, brightness_changed), + NULL, NULL, g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + g_type_class_add_private (klass, sizeof (GpmBrightnessPrivate)); +} + +/** + * gpm_brightness_init: + * @brightness: This brightness class instance + **/ +static void +gpm_brightness_init (GpmBrightness *brightness) +{ + GdkScreen *screen; + GdkDisplay *display; + int event_base; + int ignore; + + brightness->priv = GPM_BRIGHTNESS_GET_PRIVATE (brightness); + + brightness->priv->cache_trusted = FALSE; + brightness->priv->has_changed_events = FALSE; + brightness->priv->cache_percentage = 0; + brightness->priv->hw_changed = FALSE; + brightness->priv->extension_levels = -1; + brightness->priv->resources = g_ptr_array_new_with_free_func ((GDestroyNotify) XRRFreeScreenResources); + + /* can we do this */ + brightness->priv->has_extension = gpm_brightness_setup_display (brightness); +#ifdef HAVE_XRANDR_13 + brightness->priv->has_randr13 = gpm_brightness_setup_version (brightness); +#endif + if (brightness->priv->has_extension == FALSE) + egg_debug ("no XRANDR extension"); + + screen = gdk_screen_get_default (); + brightness->priv->root_window = gdk_screen_get_root_window (screen); + display = gdk_display_get_default (); + + /* as we a filtering by a window, we have to add an event type */ + if (!XRRQueryExtension (GDK_DISPLAY_XDISPLAY (gdk_display_get_default()), &event_base, &ignore)) { + egg_warning ("can't get event_base for XRR"); + } + gdk_x11_register_standard_event_type (display, event_base, RRNotify + 1); + gdk_window_add_filter (brightness->priv->root_window, + gpm_brightness_filter_xevents, brightness); + + /* don't abort on error */ + gdk_error_trap_push (); + XRRSelectInput (GDK_DISPLAY_XDISPLAY (gdk_display_get_default()), + GDK_WINDOW_XID (brightness->priv->root_window), + RRScreenChangeNotifyMask | + RROutputPropertyNotifyMask); /* <--- the only one we need, but see rh:345551 */ + gdk_flush (); + if (gdk_error_trap_pop ()) + egg_warning ("failed to select XRRSelectInput"); + + /* create cache of XRRScreenResources as XRRGetScreenResources() is slow */ + gpm_brightness_update_cache (brightness); +} + +/** + * gpm_brightness_new: + * Return value: A new brightness class instance. + * Can return NULL if no suitable hardware is found. + **/ +GpmBrightness * +gpm_brightness_new (void) +{ + if (gpm_brightness_object != NULL) { + g_object_ref (gpm_brightness_object); + } else { + gpm_brightness_object = g_object_new (GPM_TYPE_BRIGHTNESS, NULL); + g_object_add_weak_pointer (gpm_brightness_object, &gpm_brightness_object); + } + return GPM_BRIGHTNESS (gpm_brightness_object); +} + |