1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2008-2010 Richard Hughes <richard@hughsie.com>
4 * Copyright (C) 2012-2021 MATE Developers
5 *
6 * Licensed under the GNU General Public License Version 2
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <time.h>
30 #include <errno.h>
31
32 #include <X11/Xatom.h>
33 #include <X11/Xlib.h>
34 #include <X11/extensions/Xrandr.h>
35 #include <gdk/gdkx.h>
36 #include <gtk/gtk.h>
37 #include <string.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif /* HAVE_UNISTD_H */
43
44 #include "egg-discrete.h"
45
46 #include "gpm-brightness.h"
47 #include "gpm-common.h"
48 #include "gpm-marshal.h"
49
50 #define GPM_SOLE_SETTER_USE_CACHE TRUE /* this may be insanity */
51
52 struct GpmBrightnessPrivate
53 {
54 gboolean has_changed_events;
55 gboolean cache_trusted;
56 guint cache_percentage;
57 guint last_set_hw;
58 Atom backlight;
59 Display *dpy;
60 GdkWindow *root_window;
61 guint shared_value;
62 gboolean has_extension;
63 gboolean hw_changed;
64 /* A cache of XRRScreenResources is used as XRRGetScreenResources is expensive */
65 GPtrArray *resources;
66 gint extension_levels;
67 gint extension_current;
68 };
69
70 enum {
71 BRIGHTNESS_CHANGED,
72 LAST_SIGNAL
73 };
74
75 typedef enum {
76 ACTION_BACKLIGHT_GET,
77 ACTION_BACKLIGHT_SET,
78 ACTION_BACKLIGHT_INC,
79 ACTION_BACKLIGHT_DEC
80 } GpmXRandROp;
81
82 G_DEFINE_TYPE_WITH_PRIVATE (GpmBrightness, gpm_brightness, G_TYPE_OBJECT)
83
84 static guint signals [LAST_SIGNAL] = { 0 };
85 static gpointer gpm_brightness_object = NULL;
86
87 /**
88 * gpm_brightness_helper_strtoint:
89 * @text: The text to be converted
90 * @value: The return numeric return value
91 *
92 * Convert a string to a signed integer value in a safe way.
93 *
94 * Return value: %TRUE if the string was converted correctly
95 **/
96 static gboolean
gpm_brightness_helper_strtoint(const gchar * text,gint * value)97 gpm_brightness_helper_strtoint (const gchar *text, gint *value)
98 {
99 gchar *endptr = NULL;
100 gint64 value_raw;
101
102 if (text == NULL)
103 return FALSE;
104
105 value_raw = g_ascii_strtoll (text, &endptr, 10);
106
107 if (endptr == text)
108 return FALSE;
109 if (errno == ERANGE || value_raw > G_MAXINT || value_raw < G_MININT)
110 return FALSE;
111
112 *value = (gint) value_raw;
113 return TRUE;
114 }
115
116 /**
117 * gpm_brightness_helper_get_value:
118 **/
119 static gint
gpm_brightness_helper_get_value(const gchar * argument)120 gpm_brightness_helper_get_value (const gchar *argument)
121 {
122 gboolean ret;
123 GError *error = NULL;
124 gchar *stdout_data = NULL;
125 gint exit_status = 0;
126 gint value = -1;
127 gchar *command = NULL;
128
129 /* get the data */
130 command = g_strdup_printf (SBINDIR "/mate-power-backlight-helper --%s", argument);
131 ret = g_spawn_command_line_sync (command,
132 &stdout_data, NULL, &exit_status, &error);
133 if (!ret) {
134 g_error ("failed to get value: %s", error->message);
135 g_error_free (error);
136 goto out;
137 }
138 g_debug ("executing %s retval: %i", command, exit_status);
139
140 /* parse for a number */
141 ret = gpm_brightness_helper_strtoint (stdout_data, &value);
142 if (!ret)
143 goto out;
144 out:
145 g_free (command);
146 g_free (stdout_data);
147 return value;
148 }
149
150 /**
151 * gpm_brightness_helper_set_value:
152 **/
153 static gboolean
gpm_brightness_helper_set_value(const gchar * argument,gint value)154 gpm_brightness_helper_set_value (const gchar *argument, gint value)
155 {
156 gboolean ret;
157 GError *error = NULL;
158 gint exit_status = 0;
159 gchar *command = NULL;
160
161 /* get the data */
162 command = g_strdup_printf ("pkexec " SBINDIR "/mate-power-backlight-helper --%s %i", argument, value);
163 ret = g_spawn_command_line_sync (command, NULL, NULL, &exit_status, &error);
164 if (!ret) {
165 g_error ("failed to get value: %s", error->message);
166 g_error_free (error);
167 goto out;
168 }
169 g_debug ("executing %s retval: %i", command, exit_status);
170 out:
171 g_free (command);
172 return ret;
173 }
174
175 /**
176 * gpm_brightness_get_step:
177 * @levels: The number of levels supported
178 * Return value: the amount of hardware steps to do on each increment or decrement
179 **/
180 static guint
gpm_brightness_get_step(guint levels)181 gpm_brightness_get_step (guint levels)
182 {
183 /* macbook pro has a bazzillion brightness levels, do in 5% steps */
184 if (levels > 20)
185 return levels / 20;
186 return 1;
187 }
188
189 /**
190 * gpm_brightness_output_get_internal:
191 **/
192 static gboolean
gpm_brightness_output_get_internal(GpmBrightness * brightness,RROutput output,guint * cur)193 gpm_brightness_output_get_internal (GpmBrightness *brightness, RROutput output, guint *cur)
194 {
195 unsigned long nitems;
196 unsigned long bytes_after;
197 guint *prop;
198 Atom actual_type;
199 int actual_format;
200 gboolean ret = FALSE;
201
202 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
203
204 if (brightness->priv->backlight == None)
205 return FALSE;
206
207 if (XRRGetOutputProperty (brightness->priv->dpy, output, brightness->priv->backlight,
208 0, 4, False, False, None,
209 &actual_type, &actual_format,
210 &nitems, &bytes_after, ((unsigned char **)&prop)) != Success) {
211 g_debug ("failed to get property");
212 return FALSE;
213 }
214 if (actual_type == XA_INTEGER && nitems == 1 && actual_format == 32) {
215 memcpy (cur, prop, sizeof (guint));
216 ret = TRUE;
217 }
218 XFree (prop);
219 return ret;
220 }
221
222 /**
223 * gpm_brightness_output_set_internal:
224 **/
225 static gboolean
gpm_brightness_output_set_internal(GpmBrightness * brightness,RROutput output,guint value)226 gpm_brightness_output_set_internal (GpmBrightness *brightness, RROutput output, guint value)
227 {
228 GdkDisplay *display;
229
230 gboolean ret = TRUE;
231
232 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
233
234 /* don't abort on error */
235 display = gdk_display_get_default ();
236 gdk_x11_display_error_trap_push (display);
237 XRRChangeOutputProperty (brightness->priv->dpy, output, brightness->priv->backlight, XA_INTEGER, 32,
238 PropModeReplace, (unsigned char *) &value, 1);
239 XFlush (brightness->priv->dpy);
240 gdk_display_flush (display);
241 if (gdk_x11_display_error_trap_pop (display)) {
242 g_warning ("failed to XRRChangeOutputProperty for brightness %i", value);
243 ret = FALSE;
244 }
245 /* we changed the hardware */
246 if (ret)
247 brightness->priv->hw_changed = TRUE;
248 return ret;
249 }
250
251 /**
252 * gpm_brightness_setup_display:
253 **/
254 static gboolean
gpm_brightness_setup_display(GpmBrightness * brightness)255 gpm_brightness_setup_display (GpmBrightness *brightness)
256 {
257 gint major, minor;
258
259 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
260
261 /* get the display */
262 brightness->priv->dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default());
263 if (!brightness->priv->dpy) {
264 g_error ("Cannot open display");
265 return FALSE;
266 }
267 /* is XRandR new enough? */
268 if (!XRRQueryVersion (brightness->priv->dpy, &major, &minor)) {
269 g_debug ("RandR extension missing");
270 return FALSE;
271 }
272 if (major < 1 || (major == 1 && minor < 3)) {
273 g_debug ("RandR version %d.%d too old", major, minor);
274 return FALSE;
275 }
276 /* Can we support "Backlight" */
277 brightness->priv->backlight = XInternAtom (brightness->priv->dpy, "Backlight", True);
278 if (brightness->priv->backlight == None) {
279 /* Do we support "BACKLIGHT" (legacy) */
280 brightness->priv->backlight = XInternAtom (brightness->priv->dpy, "BACKLIGHT", True);
281 if (brightness->priv->backlight == None) {
282 g_debug ("No outputs have backlight property");
283 return FALSE;
284 }
285 }
286 return TRUE;
287 }
288
289 /**
290 * gpm_brightness_output_get_limits:
291 **/
292 static gboolean
gpm_brightness_output_get_limits(GpmBrightness * brightness,RROutput output,guint * min,guint * max)293 gpm_brightness_output_get_limits (GpmBrightness *brightness, RROutput output,
294 guint *min, guint *max)
295 {
296 XRRPropertyInfo *info;
297 gboolean ret = TRUE;
298
299 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
300
301 info = XRRQueryOutputProperty (brightness->priv->dpy, output, brightness->priv->backlight);
302 if (info == NULL) {
303 g_debug ("could not get output property");
304 return FALSE;
305 }
306 if (!info->range || info->num_values != 2) {
307 g_debug ("was not range");
308 ret = FALSE;
309 goto out;
310 }
311 *min = info->values[0];
312 *max = info->values[1];
313 out:
314 XFree (info);
315 return ret;
316 }
317
318 /**
319 * gpm_brightness_output_get_percentage:
320 **/
321 static gboolean
gpm_brightness_output_get_percentage(GpmBrightness * brightness,RROutput output)322 gpm_brightness_output_get_percentage (GpmBrightness *brightness, RROutput output)
323 {
324 guint cur;
325 gboolean ret;
326 guint min, max;
327 guint percentage;
328
329 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
330
331 ret = gpm_brightness_output_get_internal (brightness, output, &cur);
332 if (!ret)
333 return FALSE;
334 ret = gpm_brightness_output_get_limits (brightness, output, &min, &max);
335 if (!ret || min == max)
336 return FALSE;
337 g_debug ("hard value=%i, min=%i, max=%i", cur, min, max);
338 percentage = egg_discrete_to_percent (cur, (max-min)+1);
339 g_debug ("percentage %i", percentage);
340 brightness->priv->shared_value = percentage;
341 return TRUE;
342 }
343
344 /**
345 * gpm_brightness_output_down:
346 **/
347 static gboolean
gpm_brightness_output_down(GpmBrightness * brightness,RROutput output)348 gpm_brightness_output_down (GpmBrightness *brightness, RROutput output)
349 {
350 guint cur;
351 guint step;
352 gboolean ret;
353 guint min, max;
354
355 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
356
357 ret = gpm_brightness_output_get_internal (brightness, output, &cur);
358 if (!ret)
359 return FALSE;
360 ret = gpm_brightness_output_get_limits (brightness, output, &min, &max);
361 if (!ret || min == max)
362 return FALSE;
363 g_debug ("hard value=%i, min=%i, max=%i", cur, min, max);
364 if (cur == min) {
365 g_debug ("already min");
366 return TRUE;
367 }
368 step = gpm_brightness_get_step ((max-min)+1);
369 if (cur < step) {
370 g_debug ("truncating to %i", min);
371 cur = min;
372 } else {
373 cur -= step;
374 }
375 ret = gpm_brightness_output_set_internal (brightness, output, cur);
376 return ret;
377 }
378
379 /**
380 * gpm_brightness_output_up:
381 **/
382 static gboolean
gpm_brightness_output_up(GpmBrightness * brightness,RROutput output)383 gpm_brightness_output_up (GpmBrightness *brightness, RROutput output)
384 {
385 guint cur;
386 gboolean ret;
387 guint min, max;
388
389 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
390
391 ret = gpm_brightness_output_get_internal (brightness, output, &cur);
392 if (!ret)
393 return FALSE;
394 ret = gpm_brightness_output_get_limits (brightness, output, &min, &max);
395 if (!ret || min == max)
396 return FALSE;
397 g_debug ("hard value=%i, min=%i, max=%i", cur, min, max);
398 if (cur == max) {
399 g_debug ("already max");
400 return TRUE;
401 }
402 cur += gpm_brightness_get_step ((max-min)+1);
403 if (cur > max) {
404 g_debug ("truncating to %i", max);
405 cur = max;
406 }
407 ret = gpm_brightness_output_set_internal (brightness, output, cur);
408 return ret;
409 }
410
411 /**
412 * gpm_brightness_output_set:
413 **/
414 static gboolean
gpm_brightness_output_set(GpmBrightness * brightness,RROutput output)415 gpm_brightness_output_set (GpmBrightness *brightness, RROutput output)
416 {
417 guint cur;
418 gboolean ret;
419 guint min, max;
420 gint i;
421 gint shared_value_abs;
422 guint step;
423
424 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
425
426 ret = gpm_brightness_output_get_internal (brightness, output, &cur);
427 if (!ret)
428 return FALSE;
429 ret = gpm_brightness_output_get_limits (brightness, output, &min, &max);
430 if (!ret || min == max)
431 return FALSE;
432
433 shared_value_abs = egg_discrete_from_percent (brightness->priv->shared_value, (max-min)+1);
434 g_debug ("percent=%i, absolute=%i", brightness->priv->shared_value, shared_value_abs);
435
436 g_debug ("hard value=%i, min=%i, max=%i", cur, min, max);
437 if (shared_value_abs > (gint) max)
438 shared_value_abs = max;
439 if (shared_value_abs < (gint) min)
440 shared_value_abs = min;
441 if ((gint) cur == shared_value_abs) {
442 g_debug ("already set %i", cur);
443 return TRUE;
444 }
445
446 /* step the correct way */
447 if ((gint) cur < shared_value_abs) {
448
449 /* some adaptors have a large number of steps */
450 step = gpm_brightness_get_step (shared_value_abs - cur);
451 g_debug ("using step of %i", step);
452
453 /* going up */
454 for (i=cur; i<=shared_value_abs; i+=step) {
455 ret = gpm_brightness_output_set_internal (brightness, output, i);
456 if (!ret)
457 break;
458 if ((gint) cur != shared_value_abs)
459 g_usleep (1000 * GPM_BRIGHTNESS_DIM_INTERVAL);
460 }
461 } else {
462
463 /* some adaptors have a large number of steps */
464 step = gpm_brightness_get_step (cur - shared_value_abs);
465 g_debug ("using step of %i", step);
466
467 /* going down */
468 for (i=cur; i>=shared_value_abs; i-=step) {
469 ret = gpm_brightness_output_set_internal (brightness, output, i);
470 if (!ret)
471 break;
472 if ((gint) cur != shared_value_abs)
473 g_usleep (1000 * GPM_BRIGHTNESS_DIM_INTERVAL);
474 }
475 }
476 return TRUE;
477 }
478
479 /**
480 * gpm_brightness_foreach_resource:
481 **/
482 static gboolean
gpm_brightness_foreach_resource(GpmBrightness * brightness,GpmXRandROp op,XRRScreenResources * resources)483 gpm_brightness_foreach_resource (GpmBrightness *brightness, GpmXRandROp op, XRRScreenResources *resources)
484 {
485 gint i;
486 gboolean ret;
487 gboolean success_any = FALSE;
488 RROutput output;
489
490 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
491
492 /* do for each output */
493 for (i=0; i<resources->noutput; i++) {
494 output = resources->outputs[i];
495 g_debug ("resource %i of %i", i+1, resources->noutput);
496 if (op==ACTION_BACKLIGHT_GET) {
497 ret = gpm_brightness_output_get_percentage (brightness, output);
498 } else if (op==ACTION_BACKLIGHT_INC) {
499 ret = gpm_brightness_output_up (brightness, output);
500 } else if (op==ACTION_BACKLIGHT_DEC) {
501 ret = gpm_brightness_output_down (brightness, output);
502 } else if (op==ACTION_BACKLIGHT_SET) {
503 ret = gpm_brightness_output_set (brightness, output);
504 } else {
505 ret = FALSE;
506 g_warning ("op not known");
507 }
508 if (ret) {
509 success_any = TRUE;
510 }
511 }
512 return success_any;
513 }
514
515 /**
516 * gpm_brightness_foreach_screen:
517 **/
518 static gboolean
gpm_brightness_foreach_screen(GpmBrightness * brightness,GpmXRandROp op)519 gpm_brightness_foreach_screen (GpmBrightness *brightness, GpmXRandROp op)
520 {
521 guint i;
522 guint length;
523 XRRScreenResources *resource;
524 gboolean ret;
525 gboolean success_any = FALSE;
526
527 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
528
529 /* Return immediately if we can't use XRandR */
530 if (!brightness->priv->has_extension)
531 return FALSE;
532
533 /* do for each screen */
534 length = brightness->priv->resources->len;
535 for (i=0; i<length; i++) {
536 resource = (XRRScreenResources *) g_ptr_array_index (brightness->priv->resources, i);
537 g_debug ("using resource %p", resource);
538 ret = gpm_brightness_foreach_resource (brightness, op, resource);
539 if (ret)
540 success_any = TRUE;
541 }
542 XSync (brightness->priv->dpy, False);
543 return success_any;
544 }
545
546 /**
547 * gpm_brightness_trust_cache:
548 * @brightness: This brightness class instance
549 * Return value: if we can trust the cache
550 **/
551 static gboolean
gpm_brightness_trust_cache(GpmBrightness * brightness)552 gpm_brightness_trust_cache (GpmBrightness *brightness)
553 {
554 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
555 /* only return the cached value if the cache is trusted and we have change events */
556 if (brightness->priv->cache_trusted && brightness->priv->has_changed_events) {
557 g_debug ("using cache for value %u (okay)", brightness->priv->cache_percentage);
558 return TRUE;
559 }
560
561 /* can we trust that if we set a value 5 minutes ago, will it still be valid now?
562 * if we have multiple things setting policy on the workstation, e.g. fast user switching
563 * or kpowersave, then this will be invalid -- this logic may be insane */
564 if (GPM_SOLE_SETTER_USE_CACHE && brightness->priv->cache_trusted) {
565 g_debug ("using cache for value %u (probably okay)", brightness->priv->cache_percentage);
566 return TRUE;
567 }
568 return FALSE;
569 }
570
571 /**
572 * gpm_brightness_set:
573 * @brightness: This brightness class instance
574 * @percentage: The percentage brightness
575 * @hw_changed: If the hardware was changed, i.e. the brightness changed
576 * Return value: %TRUE if success, %FALSE if there was an error
577 **/
578 gboolean
gpm_brightness_set(GpmBrightness * brightness,guint percentage,gboolean * hw_changed)579 gpm_brightness_set (GpmBrightness *brightness, guint percentage, gboolean *hw_changed)
580 {
581 gboolean ret = FALSE;
582 gboolean trust_cache;
583
584 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
585
586 /* can we check the new value with the cache? */
587 trust_cache = gpm_brightness_trust_cache (brightness);
588 if (trust_cache && percentage == brightness->priv->cache_percentage) {
589 g_debug ("not setting the same value %i", percentage);
590 return TRUE;
591 }
592
593 /* set the value we want */
594 brightness->priv->shared_value = percentage;
595
596 /* reset to not-changed */
597 brightness->priv->hw_changed = FALSE;
598 ret = gpm_brightness_foreach_screen (brightness, ACTION_BACKLIGHT_SET);
599
600 /* legacy fallback */
601 if (!ret) {
602 if (brightness->priv->extension_levels < 0)
603 brightness->priv->extension_levels = gpm_brightness_helper_get_value ("get-max-brightness");
604 brightness->priv->extension_current = egg_discrete_from_percent (percentage, brightness->priv->extension_levels+1);
605 ret = gpm_brightness_helper_set_value ("set-brightness", brightness->priv->extension_current);
606 }
607
608 /* did the hardware have to be modified? */
609 if (ret && hw_changed != NULL)
610 *hw_changed = brightness->priv->hw_changed;
611
612 /* we did something to the hardware, so untrusted */
613 if (ret)
614 brightness->priv->cache_trusted = FALSE;
615
616 return ret;
617 }
618
619 /**
620 * gpm_brightness_get:
621 * @brightness: This brightness class instance
622 * Return value: The percentage brightness, or -1 for no hardware or error
623 *
624 * Gets the current (or at least what this class thinks is current) percentage
625 * brightness. This is quick as no HAL inquiry is done.
626 **/
627 gboolean
gpm_brightness_get(GpmBrightness * brightness,guint * percentage)628 gpm_brightness_get (GpmBrightness *brightness, guint *percentage)
629 {
630 gboolean ret = FALSE;
631 gboolean trust_cache;
632 guint percentage_local;
633
634 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
635 g_return_val_if_fail (percentage != NULL, FALSE);
636
637 /* can we use the cache? */
638 trust_cache = gpm_brightness_trust_cache (brightness);
639 if (trust_cache) {
640 *percentage = brightness->priv->cache_percentage;
641 return TRUE;
642 }
643
644 /* get the brightness from hardware -- slow */
645 ret = gpm_brightness_foreach_screen (brightness, ACTION_BACKLIGHT_GET);
646 percentage_local = brightness->priv->shared_value;
647
648 /* legacy fallback */
649 if (!ret) {
650 if (brightness->priv->extension_levels < 0)
651 brightness->priv->extension_levels = gpm_brightness_helper_get_value ("get-max-brightness");
652 brightness->priv->extension_current = gpm_brightness_helper_get_value ("get-brightness");
653 percentage_local = egg_discrete_to_percent (brightness->priv->extension_current, brightness->priv->extension_levels+1);
654 ret = TRUE;
655 }
656
657 /* valid? */
658 if (percentage_local > 100) {
659 g_warning ("percentage value of %i will be truncated", percentage_local);
660 percentage_local = 100;
661 }
662
663 /* a new value is always trusted if the method and checks succeed */
664 if (ret) {
665 brightness->priv->cache_percentage = percentage_local;
666 brightness->priv->cache_trusted = TRUE;
667 *percentage = percentage_local;
668 } else {
669 brightness->priv->cache_trusted = FALSE;
670 }
671 return ret;
672 }
673
674 /**
675 * gpm_brightness_up:
676 * @brightness: This brightness class instance
677 * @hw_changed: If the hardware was changed, i.e. the brightness changed
678 * Return value: %TRUE if success, %FALSE if there was an error
679 *
680 * If possible, put the brightness of the LCD up one unit.
681 **/
682 gboolean
gpm_brightness_up(GpmBrightness * brightness,gboolean * hw_changed)683 gpm_brightness_up (GpmBrightness *brightness, gboolean *hw_changed)
684 {
685 gboolean ret = FALSE;
686 guint step;
687
688 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
689
690 /* reset to not-changed */
691 brightness->priv->hw_changed = FALSE;
692 ret = gpm_brightness_foreach_screen (brightness, ACTION_BACKLIGHT_INC);
693
694 /* did the hardware have to be modified? */
695 if (ret && hw_changed != NULL)
696 *hw_changed = brightness->priv->hw_changed;
697
698 /* we did something to the hardware, so untrusted */
699 if (ret)
700 brightness->priv->cache_trusted = FALSE;
701
702 /* legacy fallback */
703 if (!ret) {
704 if (brightness->priv->extension_levels < 0)
705 brightness->priv->extension_levels = gpm_brightness_helper_get_value ("get-max-brightness");
706 brightness->priv->extension_current = gpm_brightness_helper_get_value ("get-brightness");
707
708 /* increase by the step, limiting to the maximum possible levels */
709 if (brightness->priv->extension_current < brightness->priv->extension_levels) {
710 step = gpm_brightness_get_step (brightness->priv->extension_levels);
711 brightness->priv->extension_current += step;
712 if (brightness->priv->extension_current > brightness->priv->extension_levels)
713 brightness->priv->extension_current = brightness->priv->extension_levels;
714 ret = gpm_brightness_helper_set_value ("set-brightness", brightness->priv->extension_current);
715 }
716 if (hw_changed != NULL)
717 *hw_changed = ret;
718 brightness->priv->cache_trusted = FALSE;
719 goto out;
720 }
721 out:
722 return ret;
723 }
724
725 /**
726 * gpm_brightness_down:
727 * @brightness: This brightness class instance
728 * @hw_changed: If the hardware was changed, i.e. the brightness changed
729 * Return value: %TRUE if success, %FALSE if there was an error
730 *
731 * If possible, put the brightness of the LCD down one unit.
732 **/
733 gboolean
gpm_brightness_down(GpmBrightness * brightness,gboolean * hw_changed)734 gpm_brightness_down (GpmBrightness *brightness, gboolean *hw_changed)
735 {
736 gboolean ret = FALSE;
737 guint step;
738
739 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
740
741 /* reset to not-changed */
742 brightness->priv->hw_changed = FALSE;
743 ret = gpm_brightness_foreach_screen (brightness, ACTION_BACKLIGHT_DEC);
744
745 /* did the hardware have to be modified? */
746 if (ret && hw_changed != NULL)
747 *hw_changed = brightness->priv->hw_changed;
748
749 /* we did something to the hardware, so untrusted */
750 if (ret)
751 brightness->priv->cache_trusted = FALSE;
752
753 /* legacy fallback */
754 if (!ret) {
755 if (brightness->priv->extension_levels < 0)
756 brightness->priv->extension_levels = gpm_brightness_helper_get_value ("get-max-brightness");
757 brightness->priv->extension_current = gpm_brightness_helper_get_value ("get-brightness");
758
759 /* decrease by the step, limiting to zero */
760 if (brightness->priv->extension_current > 0) {
761 step = gpm_brightness_get_step (brightness->priv->extension_levels);
762 brightness->priv->extension_current -= step;
763 if (brightness->priv->extension_current < 0)
764 brightness->priv->extension_current = 0;
765 ret = gpm_brightness_helper_set_value ("set-brightness", brightness->priv->extension_current);
766 }
767 if (hw_changed != NULL)
768 *hw_changed = ret;
769 brightness->priv->cache_trusted = FALSE;
770 goto out;
771 }
772 out:
773 return ret;
774 }
775
776
777 /**
778 * gpm_brightness_may_have_changed:
779 **/
780 static void
gpm_brightness_may_have_changed(GpmBrightness * brightness)781 gpm_brightness_may_have_changed (GpmBrightness *brightness)
782 {
783 gboolean ret;
784 guint percentage;
785 ret = gpm_brightness_get (brightness, &percentage);
786 if (!ret) {
787 g_warning ("failed to get output");
788 return;
789 }
790 g_debug ("emitting brightness-changed (%i)", percentage);
791 g_signal_emit (brightness, signals [BRIGHTNESS_CHANGED], 0, percentage);
792 }
793
794 /**
795 * gpm_brightness_filter_xevents:
796 **/
797 static GdkFilterReturn
gpm_brightness_filter_xevents(GdkXEvent * xevent,GdkEvent * event,gpointer data)798 gpm_brightness_filter_xevents (GdkXEvent *xevent, GdkEvent *event, gpointer data)
799 {
800 GpmBrightness *brightness = GPM_BRIGHTNESS (data);
801 if (event->type == GDK_NOTHING)
802 return GDK_FILTER_CONTINUE;
803 gpm_brightness_may_have_changed (brightness);
804 return GDK_FILTER_CONTINUE;
805 }
806
807
808 static void gpm_brightness_update_cache (GpmBrightness *brightness);
809
810 /**
811 * gpm_brightness_monitors_changed:
812 **/
813 static void
gpm_brightness_monitors_changed(GdkScreen * screen,GpmBrightness * brightness)814 gpm_brightness_monitors_changed (GdkScreen *screen, GpmBrightness *brightness)
815 {
816 g_return_if_fail (GPM_IS_BRIGHTNESS (brightness));
817 gpm_brightness_update_cache (brightness);
818 }
819
820 /**
821 * gpm_brightness_update_cache:
822 **/
823 static void
gpm_brightness_update_cache(GpmBrightness * brightness)824 gpm_brightness_update_cache (GpmBrightness *brightness)
825 {
826 guint length;
827 Window root;
828 GdkScreen *gscreen;
829 GdkDisplay *display;
830 XRRScreenResources *resource;
831
832 g_return_if_fail (GPM_IS_BRIGHTNESS (brightness));
833
834 /* invalidate and remove all the previous entries */
835 length = brightness->priv->resources->len;
836 if (length > 0)
837 g_ptr_array_set_size (brightness->priv->resources, 0);
838
839 display = gdk_display_get_default ();
840 gscreen = gdk_display_get_default_screen (display);
841
842 /* if we have not setup the changed on the monitor, set it here */
843 if (g_object_get_data (G_OBJECT (gscreen), "gpk-set-monitors-changed") == NULL) {
844 g_debug ("watching ::monitors_changed on %p", gscreen);
845 g_object_set_data (G_OBJECT (gscreen), "gpk-set-monitors-changed", (gpointer) "true");
846 g_signal_connect (G_OBJECT (gscreen), "monitors_changed",
847 G_CALLBACK (gpm_brightness_monitors_changed), brightness);
848 }
849
850 root = RootWindow (brightness->priv->dpy, 0);
851
852 gdk_x11_display_error_trap_push (display);
853 resource = XRRGetScreenResourcesCurrent (brightness->priv->dpy, root);
854 if (gdk_x11_display_error_trap_pop (display) || resource == NULL) {
855 g_warning ("failed to XRRGetScreenResourcesCurrent");
856 }
857
858 if (resource != NULL) {
859 g_debug ("adding resource %p", resource);
860 g_ptr_array_add (brightness->priv->resources, resource);
861 }
862 }
863
864 /**
865 * gpm_brightness_has_hw:
866 **/
867 gboolean
gpm_brightness_has_hw(GpmBrightness * brightness)868 gpm_brightness_has_hw (GpmBrightness *brightness)
869 {
870 g_return_val_if_fail (GPM_IS_BRIGHTNESS (brightness), FALSE);
871
872 /* use XRandR first */
873 if (brightness->priv->has_extension)
874 return TRUE;
875
876 /* fallback to legacy access */
877 if (brightness->priv->extension_levels < 0)
878 brightness->priv->extension_levels = gpm_brightness_helper_get_value ("get-max-brightness");
879 if (brightness->priv->extension_levels > 0)
880 return TRUE;
881 return FALSE;
882 }
883
884 /**
885 * gpm_brightness_finalize:
886 **/
887 static void
gpm_brightness_finalize(GObject * object)888 gpm_brightness_finalize (GObject *object)
889 {
890 GpmBrightness *brightness;
891 g_return_if_fail (object != NULL);
892 g_return_if_fail (GPM_IS_BRIGHTNESS (object));
893 brightness = GPM_BRIGHTNESS (object);
894 g_ptr_array_unref (brightness->priv->resources);
895 gdk_window_remove_filter (brightness->priv->root_window,
896 gpm_brightness_filter_xevents, brightness);
897 G_OBJECT_CLASS (gpm_brightness_parent_class)->finalize (object);
898 }
899
900 /**
901 * gpm_brightness_class_init:
902 **/
903 static void
gpm_brightness_class_init(GpmBrightnessClass * klass)904 gpm_brightness_class_init (GpmBrightnessClass *klass)
905 {
906 GObjectClass *object_class = G_OBJECT_CLASS (klass);
907 object_class->finalize = gpm_brightness_finalize;
908
909 signals [BRIGHTNESS_CHANGED] =
910 g_signal_new ("brightness-changed",
911 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
912 G_STRUCT_OFFSET (GpmBrightnessClass, brightness_changed),
913 NULL, NULL, g_cclosure_marshal_VOID__UINT,
914 G_TYPE_NONE, 1, G_TYPE_UINT);
915 }
916
917 /**
918 * gpm_brightness_init:
919 * @brightness: This brightness class instance
920 **/
921 static void
gpm_brightness_init(GpmBrightness * brightness)922 gpm_brightness_init (GpmBrightness *brightness)
923 {
924 GdkScreen *screen;
925 GdkDisplay *display;
926 int event_base;
927 int ignore;
928
929 brightness->priv = gpm_brightness_get_instance_private (brightness);
930
931 brightness->priv->cache_trusted = FALSE;
932 brightness->priv->has_changed_events = FALSE;
933 brightness->priv->cache_percentage = 0;
934 brightness->priv->hw_changed = FALSE;
935 brightness->priv->extension_levels = -1;
936 brightness->priv->resources = g_ptr_array_new_with_free_func ((GDestroyNotify) XRRFreeScreenResources);
937
938 /* can we do this */
939 brightness->priv->has_extension = gpm_brightness_setup_display (brightness);
940 if (brightness->priv->has_extension == FALSE)
941 g_debug ("no XRANDR extension");
942
943 screen = gdk_screen_get_default ();
944 brightness->priv->root_window = gdk_screen_get_root_window (screen);
945 display = gdk_display_get_default ();
946
947 /* as we a filtering by a window, we have to add an event type */
948 if (!XRRQueryExtension (GDK_DISPLAY_XDISPLAY (gdk_display_get_default()), &event_base, &ignore)) {
949 g_warning ("can't get event_base for XRR");
950 }
951 gdk_x11_register_standard_event_type (display, event_base, RRNotify + 1);
952 gdk_window_add_filter (brightness->priv->root_window,
953 gpm_brightness_filter_xevents, brightness);
954
955 /* don't abort on error */
956 gdk_x11_display_error_trap_push (display);
957 XRRSelectInput (GDK_DISPLAY_XDISPLAY (gdk_display_get_default()),
958 GDK_WINDOW_XID (brightness->priv->root_window),
959 RRScreenChangeNotifyMask |
960 RROutputPropertyNotifyMask); /* <--- the only one we need, but see rh:345551 */
961 gdk_display_flush (display);
962 if (gdk_x11_display_error_trap_pop (display))
963 g_warning ("failed to select XRRSelectInput");
964
965 /* create cache of XRRScreenResources as XRRGetScreenResources() is slow */
966 gpm_brightness_update_cache (brightness);
967 }
968
969 /**
970 * gpm_brightness_new:
971 * Return value: A new brightness class instance.
972 * Can return NULL if no suitable hardware is found.
973 **/
974 GpmBrightness *
gpm_brightness_new(void)975 gpm_brightness_new (void)
976 {
977 if (gpm_brightness_object != NULL) {
978 g_object_ref (gpm_brightness_object);
979 } else {
980 gpm_brightness_object = g_object_new (GPM_TYPE_BRIGHTNESS, NULL);
981 g_object_add_weak_pointer (gpm_brightness_object, &gpm_brightness_object);
982 }
983 return GPM_BRIGHTNESS (gpm_brightness_object);
984 }
985
986