/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2008-2010 Richard Hughes * Copyright (C) 2012-2021 MATE Developers * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ #include "egg-discrete.h" #include "gpm-brightness.h" #include "gpm-common.h" #include "gpm-marshal.h" #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; 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_WITH_PRIVATE (GpmBrightness, gpm_brightness, G_TYPE_OBJECT) static guint signals [LAST_SIGNAL] = { 0 }; static gpointer gpm_brightness_object = NULL; /** * gpm_brightness_helper_strtoint: * @text: The text to be converted * @value: The return numeric return value * * Convert a string to a signed integer value in a safe way. * * Return value: %TRUE if the string was converted correctly **/ static gboolean gpm_brightness_helper_strtoint (const gchar *text, gint *value) { gchar *endptr = NULL; gint64 value_raw; if (text == NULL) return FALSE; value_raw = g_ascii_strtoll (text, &endptr, 10); if (endptr == text) return FALSE; if (errno == ERANGE || value_raw > G_MAXINT || value_raw < G_MININT) return FALSE; *value = (gint) value_raw; return TRUE; } /** * 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) { g_warning ("failed to get value: %s", error->message); g_error_free (error); goto out; } g_debug ("executing %s retval: %i", command, exit_status); /* parse for a number */ ret = gpm_brightness_helper_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) { g_warning ("failed to get value: %s", error->message); g_error_free (error); goto out; } g_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) { g_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) { GdkDisplay *display; gboolean ret = TRUE; g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE); /* don't abort on error */ display = gdk_display_get_default (); gdk_x11_display_error_trap_push (display); XRRChangeOutputProperty (brightness->priv->dpy, output, brightness->priv->backlight, XA_INTEGER, 32, PropModeReplace, (unsigned char *) &value, 1); XFlush (brightness->priv->dpy); gdk_display_flush (display); if (gdk_x11_display_error_trap_pop (display)) { g_warning ("failed to XRRChangeOutputProperty for brightness %u", 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) { g_critical ("Cannot open display"); return FALSE; } /* is XRandR new enough? */ if (!XRRQueryVersion (brightness->priv->dpy, &major, &minor)) { g_debug ("RandR extension missing"); return FALSE; } if (major < 1 || (major == 1 && minor < 3)) { g_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) { /* Do we support "BACKLIGHT" (legacy) */ brightness->priv->backlight = XInternAtom (brightness->priv->dpy, "BACKLIGHT", True); if (brightness->priv->backlight == None) { g_debug ("No outputs have backlight property"); return FALSE; } } return TRUE; } /** * 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) { g_debug ("could not get output property"); return FALSE; } if (!info->range || info->num_values != 2) { g_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; g_debug ("hard value=%u, min=%u, max=%u", cur, min, max); percentage = egg_discrete_to_percent (cur, (max-min)+1); g_debug ("percentage %u", 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; g_debug ("hard value=%u, min=%u, max=%u", cur, min, max); if (cur == min) { g_debug ("already min"); return TRUE; } step = gpm_brightness_get_step ((max-min)+1); if (cur < step) { g_debug ("truncating to %u", 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; g_debug ("hard value=%u, min=%u, max=%u", cur, min, max); if (cur == max) { g_debug ("already max"); return TRUE; } cur += gpm_brightness_get_step ((max-min)+1); if (cur > max) { g_debug ("truncating to %u", 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); g_debug ("percent=%u, absolute=%i", brightness->priv->shared_value, shared_value_abs); g_debug ("hard value=%u, min=%u, max=%u", 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) { g_debug ("already set %u", 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); g_debug ("using step of %u", 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); g_debug ("using step of %u", 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; inoutput; i++) { output = resources->outputs[i]; g_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; g_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; ipriv->resources, i); g_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) { g_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) { g_debug ("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) { g_debug ("not setting the same value %u", 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) { g_warning ("percentage value of %u 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) { g_warning ("failed to get output"); return; } g_debug ("emitting brightness-changed (%u)", 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; 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); display = gdk_display_get_default (); gscreen = gdk_display_get_default_screen (display); /* 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) { g_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, 0); gdk_x11_display_error_trap_push (display); resource = XRRGetScreenResourcesCurrent (brightness->priv->dpy, root); if (gdk_x11_display_error_trap_pop (display) || resource == NULL) { g_warning ("failed to XRRGetScreenResourcesCurrent"); } if (resource != NULL) { g_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); } /** * 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_instance_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); if (brightness->priv->has_extension == FALSE) g_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)) { g_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_x11_display_error_trap_push (display); 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_display_flush (display); if (gdk_x11_display_error_trap_pop (display)) g_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); }