Merge lp:~fourdollars/ubuntu/trusty/gnome-desktop3/fix-lowest-brightness into lp:ubuntu/trusty-proposed/gnome-desktop3

Proposed by Shih-Yuan Lee
Status: Merged
Merge reported by: Martin Pitt
Merged at revision: not available
Proposed branch: lp:~fourdollars/ubuntu/trusty/gnome-desktop3/fix-lowest-brightness
Merge into: lp:ubuntu/trusty-proposed/gnome-desktop3
Diff against target: 2705 lines (+2659/-0)
6 files modified
.pc/applied-patches (+1/-0)
.pc/fix_lowest_brightness.patch/libgnome-desktop/gnome-rr.c (+2624/-0)
debian/changelog (+7/-0)
debian/patches/fix_lowest_brightness.patch (+21/-0)
debian/patches/series (+1/-0)
libgnome-desktop/gnome-rr.c (+5/-0)
To merge this branch: bzr merge lp:~fourdollars/ubuntu/trusty/gnome-desktop3/fix-lowest-brightness
Reviewer Review Type Date Requested Status
Martin Pitt Approve
Review via email: mp+278110@code.launchpad.net

Commit message

Apply the similar method from the power plugin in unity-settings-daemon to fix the lowest brightness issue.

Description of the change

Please help to review this change.
This change has been applied in xenial.
I am backporting it into trusty.

To post a comment you must log in.
Revision history for this message
Martin Pitt (pitti) wrote :

Uploaded. I didn't merge this into bzr, just grabbed the diff, so closing this manually. Further tracking happens in the SRU bug.

Thank you!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.pc/applied-patches'
2--- .pc/applied-patches 2015-03-31 10:59:01 +0000
3+++ .pc/applied-patches 2015-11-20 08:10:07 +0000
4@@ -8,3 +8,4 @@
5 ignore_errors_with_primary_outputs.patch
6 backlight-property-name.patch
7 idletime_sanity_checks.patch
8+fix_lowest_brightness.patch
9
10=== added directory '.pc/fix_lowest_brightness.patch'
11=== added directory '.pc/fix_lowest_brightness.patch/libgnome-desktop'
12=== added file '.pc/fix_lowest_brightness.patch/libgnome-desktop/gnome-rr.c'
13--- .pc/fix_lowest_brightness.patch/libgnome-desktop/gnome-rr.c 1970-01-01 00:00:00 +0000
14+++ .pc/fix_lowest_brightness.patch/libgnome-desktop/gnome-rr.c 2015-11-20 08:10:07 +0000
15@@ -0,0 +1,2624 @@
16+/* gnome-rr.c
17+ *
18+ * Copyright 2007, 2008, Red Hat, Inc.
19+ *
20+ * This file is part of the Gnome Library.
21+ *
22+ * The Gnome Library is free software; you can redistribute it and/or
23+ * modify it under the terms of the GNU Library General Public License as
24+ * published by the Free Software Foundation; either version 2 of the
25+ * License, or (at your option) any later version.
26+ *
27+ * The Gnome Library is distributed in the hope that it will be useful,
28+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
29+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30+ * Library General Public License for more details.
31+ *
32+ * You should have received a copy of the GNU Library General Public
33+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
34+ * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
35+ * Boston, MA 02110-1301, USA.
36+ *
37+ * Author: Soren Sandmann <sandmann@redhat.com>
38+ */
39+
40+#define GNOME_DESKTOP_USE_UNSTABLE_API
41+
42+#include <config.h>
43+#include <glib/gi18n-lib.h>
44+#include <string.h>
45+#include <X11/Xlib.h>
46+
47+#include <X11/extensions/Xrandr.h>
48+
49+#include <gtk/gtk.h>
50+#include <gdk/gdkx.h>
51+#include <X11/Xatom.h>
52+#include <X11/extensions/dpms.h>
53+
54+#undef GNOME_DISABLE_DEPRECATED
55+#include "gnome-rr.h"
56+#include "gnome-rr-config.h"
57+
58+#include "edid.h"
59+#include "gnome-rr-private.h"
60+
61+#define DISPLAY(o) ((o)->info->screen->priv->xdisplay)
62+
63+#define SERVERS_RANDR_IS_AT_LEAST_1_3(priv) (priv->rr_major_version > 1 || (priv->rr_major_version == 1 && priv->rr_minor_version >= 3))
64+
65+enum {
66+ SCREEN_PROP_0,
67+ SCREEN_PROP_GDK_SCREEN,
68+ SCREEN_PROP_LAST,
69+};
70+
71+enum {
72+ SCREEN_CHANGED,
73+ SCREEN_OUTPUT_CONNECTED,
74+ SCREEN_OUTPUT_DISCONNECTED,
75+ SCREEN_SIGNAL_LAST,
76+};
77+
78+gint screen_signals[SCREEN_SIGNAL_LAST];
79+
80+struct GnomeRROutput
81+{
82+ ScreenInfo * info;
83+ RROutput id;
84+
85+ char * name;
86+ char * display_name;
87+ GnomeRRCrtc * current_crtc;
88+ gboolean connected;
89+ gulong width_mm;
90+ gulong height_mm;
91+ GnomeRRCrtc ** possible_crtcs;
92+ GnomeRROutput ** clones;
93+ GnomeRRMode ** modes;
94+ int n_preferred;
95+ guint8 * edid_data;
96+ gsize edid_size;
97+ char * connector_type;
98+ gint backlight_min;
99+ gint backlight_max;
100+};
101+
102+struct GnomeRROutputWrap
103+{
104+ RROutput id;
105+};
106+
107+struct GnomeRRCrtc
108+{
109+ ScreenInfo * info;
110+ RRCrtc id;
111+
112+ GnomeRRMode * current_mode;
113+ GnomeRROutput ** current_outputs;
114+ GnomeRROutput ** possible_outputs;
115+ int x;
116+ int y;
117+
118+ GnomeRRRotation current_rotation;
119+ GnomeRRRotation rotations;
120+ int gamma_size;
121+};
122+
123+struct GnomeRRMode
124+{
125+ ScreenInfo * info;
126+ RRMode id;
127+ char * name;
128+ int width;
129+ int height;
130+ int freq; /* in mHz */
131+};
132+
133+/* GnomeRRCrtc */
134+static GnomeRRCrtc * crtc_new (ScreenInfo *info,
135+ RRCrtc id);
136+static GnomeRRCrtc * crtc_copy (const GnomeRRCrtc *from);
137+static void crtc_free (GnomeRRCrtc *crtc);
138+
139+static gboolean crtc_initialize (GnomeRRCrtc *crtc,
140+ XRRScreenResources *res,
141+ GError **error);
142+
143+/* GnomeRROutput */
144+static GnomeRROutput *output_new (ScreenInfo *info,
145+ RROutput id);
146+
147+static gboolean output_initialize (GnomeRROutput *output,
148+ XRRScreenResources *res,
149+ GError **error);
150+
151+static GnomeRROutput *output_copy (const GnomeRROutput *from);
152+static void output_free (GnomeRROutput *output);
153+
154+/* GnomeRRMode */
155+static GnomeRRMode * mode_new (ScreenInfo *info,
156+ RRMode id);
157+
158+static void mode_initialize (GnomeRRMode *mode,
159+ XRRModeInfo *info);
160+
161+static GnomeRRMode * mode_copy (const GnomeRRMode *from);
162+static void mode_free (GnomeRRMode *mode);
163+
164+static void gnome_rr_screen_finalize (GObject*);
165+static void gnome_rr_screen_set_property (GObject*, guint, const GValue*, GParamSpec*);
166+static void gnome_rr_screen_get_property (GObject*, guint, GValue*, GParamSpec*);
167+static gboolean gnome_rr_screen_initable_init (GInitable*, GCancellable*, GError**);
168+static void gnome_rr_screen_initable_iface_init (GInitableIface *iface);
169+G_DEFINE_TYPE_WITH_CODE (GnomeRRScreen, gnome_rr_screen, G_TYPE_OBJECT,
170+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gnome_rr_screen_initable_iface_init))
171+
172+G_DEFINE_BOXED_TYPE (GnomeRRCrtc, gnome_rr_crtc, crtc_copy, crtc_free)
173+G_DEFINE_BOXED_TYPE (GnomeRROutput, gnome_rr_output, output_copy, output_free)
174+G_DEFINE_BOXED_TYPE (GnomeRRMode, gnome_rr_mode, mode_copy, mode_free)
175+
176+/* Errors */
177+
178+/**
179+ * gnome_rr_error_quark:
180+ *
181+ * Returns the #GQuark that will be used for #GError values returned by the
182+ * GnomeRR API.
183+ *
184+ * Return value: a #GQuark used to identify errors coming from the GnomeRR API.
185+ */
186+GQuark
187+gnome_rr_error_quark (void)
188+{
189+ return g_quark_from_static_string ("gnome-rr-error-quark");
190+}
191+
192+/* Screen */
193+static GnomeRROutput *
194+gnome_rr_output_by_id (ScreenInfo *info, RROutput id)
195+{
196+ GnomeRROutput **output;
197+
198+ g_assert (info != NULL);
199+
200+ for (output = info->outputs; *output; ++output)
201+ {
202+ if ((*output)->id == id)
203+ return *output;
204+ }
205+
206+ return NULL;
207+}
208+
209+static GnomeRRCrtc *
210+crtc_by_id (ScreenInfo *info, RRCrtc id)
211+{
212+ GnomeRRCrtc **crtc;
213+
214+ if (!info)
215+ return NULL;
216+
217+ for (crtc = info->crtcs; *crtc; ++crtc)
218+ {
219+ if ((*crtc)->id == id)
220+ return *crtc;
221+ }
222+
223+ return NULL;
224+}
225+
226+static GnomeRRMode *
227+mode_by_id (ScreenInfo *info, RRMode id)
228+{
229+ GnomeRRMode **mode;
230+
231+ g_assert (info != NULL);
232+
233+ for (mode = info->modes; *mode; ++mode)
234+ {
235+ if ((*mode)->id == id)
236+ return *mode;
237+ }
238+
239+ return NULL;
240+}
241+
242+static void
243+screen_info_free (ScreenInfo *info)
244+{
245+ GnomeRROutput **output;
246+ GnomeRRCrtc **crtc;
247+ GnomeRRMode **mode;
248+
249+ g_assert (info != NULL);
250+
251+ if (info->resources)
252+ {
253+ XRRFreeScreenResources (info->resources);
254+
255+ info->resources = NULL;
256+ }
257+
258+ if (info->outputs)
259+ {
260+ for (output = info->outputs; *output; ++output)
261+ output_free (*output);
262+ g_free (info->outputs);
263+ }
264+
265+ if (info->crtcs)
266+ {
267+ for (crtc = info->crtcs; *crtc; ++crtc)
268+ crtc_free (*crtc);
269+ g_free (info->crtcs);
270+ }
271+
272+ if (info->modes)
273+ {
274+ for (mode = info->modes; *mode; ++mode)
275+ mode_free (*mode);
276+ g_free (info->modes);
277+ }
278+
279+ if (info->clone_modes)
280+ {
281+ /* The modes themselves were freed above */
282+ g_free (info->clone_modes);
283+ }
284+
285+ g_free (info);
286+}
287+
288+static gboolean
289+has_similar_mode (GnomeRROutput *output, GnomeRRMode *mode)
290+{
291+ int i;
292+ GnomeRRMode **modes = gnome_rr_output_list_modes (output);
293+ int width = gnome_rr_mode_get_width (mode);
294+ int height = gnome_rr_mode_get_height (mode);
295+
296+ for (i = 0; modes[i] != NULL; ++i)
297+ {
298+ GnomeRRMode *m = modes[i];
299+
300+ if (gnome_rr_mode_get_width (m) == width &&
301+ gnome_rr_mode_get_height (m) == height)
302+ {
303+ return TRUE;
304+ }
305+ }
306+
307+ return FALSE;
308+}
309+
310+static void
311+gather_clone_modes (ScreenInfo *info)
312+{
313+ int i;
314+ GPtrArray *result = g_ptr_array_new ();
315+
316+ for (i = 0; info->outputs[i] != NULL; ++i)
317+ {
318+ int j;
319+ GnomeRROutput *output1, *output2;
320+
321+ output1 = info->outputs[i];
322+
323+ if (!output1->connected)
324+ continue;
325+
326+ for (j = 0; output1->modes[j] != NULL; ++j)
327+ {
328+ GnomeRRMode *mode = output1->modes[j];
329+ gboolean valid;
330+ int k;
331+
332+ valid = TRUE;
333+ for (k = 0; info->outputs[k] != NULL; ++k)
334+ {
335+ output2 = info->outputs[k];
336+
337+ if (!output2->connected)
338+ continue;
339+
340+ if (!has_similar_mode (output2, mode))
341+ {
342+ valid = FALSE;
343+ break;
344+ }
345+ }
346+
347+ if (valid)
348+ g_ptr_array_add (result, mode);
349+ }
350+ }
351+
352+ g_ptr_array_add (result, NULL);
353+
354+ info->clone_modes = (GnomeRRMode **)g_ptr_array_free (result, FALSE);
355+}
356+
357+static gboolean
358+fill_screen_info_from_resources (ScreenInfo *info,
359+ XRRScreenResources *resources,
360+ GError **error)
361+{
362+ int i;
363+ GPtrArray *a;
364+ GnomeRRCrtc **crtc;
365+ GnomeRROutput **output;
366+
367+ info->resources = resources;
368+
369+ /* We create all the structures before initializing them, so
370+ * that they can refer to each other.
371+ */
372+ a = g_ptr_array_new ();
373+ for (i = 0; i < resources->ncrtc; ++i)
374+ {
375+ GnomeRRCrtc *crtc = crtc_new (info, resources->crtcs[i]);
376+
377+ g_ptr_array_add (a, crtc);
378+ }
379+ g_ptr_array_add (a, NULL);
380+ info->crtcs = (GnomeRRCrtc **)g_ptr_array_free (a, FALSE);
381+
382+ a = g_ptr_array_new ();
383+ for (i = 0; i < resources->noutput; ++i)
384+ {
385+ GnomeRROutput *output = output_new (info, resources->outputs[i]);
386+
387+ g_ptr_array_add (a, output);
388+ }
389+ g_ptr_array_add (a, NULL);
390+ info->outputs = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
391+
392+ a = g_ptr_array_new ();
393+ for (i = 0; i < resources->nmode; ++i)
394+ {
395+ GnomeRRMode *mode = mode_new (info, resources->modes[i].id);
396+
397+ g_ptr_array_add (a, mode);
398+ }
399+ g_ptr_array_add (a, NULL);
400+ info->modes = (GnomeRRMode **)g_ptr_array_free (a, FALSE);
401+
402+ /* Initialize */
403+ for (crtc = info->crtcs; *crtc; ++crtc)
404+ {
405+ if (!crtc_initialize (*crtc, resources, error))
406+ return FALSE;
407+ }
408+
409+ for (output = info->outputs; *output; ++output)
410+ {
411+ if (!output_initialize (*output, resources, error))
412+ return FALSE;
413+ }
414+
415+ for (i = 0; i < resources->nmode; ++i)
416+ {
417+ GnomeRRMode *mode = mode_by_id (info, resources->modes[i].id);
418+
419+ mode_initialize (mode, &(resources->modes[i]));
420+ }
421+
422+ gather_clone_modes (info);
423+
424+ return TRUE;
425+}
426+
427+static gboolean
428+fill_out_screen_info (Display *xdisplay,
429+ Window xroot,
430+ ScreenInfo *info,
431+ gboolean needs_reprobe,
432+ GError **error)
433+{
434+ XRRScreenResources *resources;
435+ GnomeRRScreenPrivate *priv;
436+
437+ g_assert (xdisplay != NULL);
438+ g_assert (info != NULL);
439+
440+ priv = info->screen->priv;
441+
442+ /* First update the screen resources */
443+
444+ if (needs_reprobe)
445+ resources = XRRGetScreenResources (xdisplay, xroot);
446+ else
447+ {
448+ /* XRRGetScreenResourcesCurrent is less expensive than
449+ * XRRGetScreenResources, however it is available only
450+ * in RandR 1.3 or higher
451+ */
452+ if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv))
453+ resources = XRRGetScreenResourcesCurrent (xdisplay, xroot);
454+ else
455+ resources = XRRGetScreenResources (xdisplay, xroot);
456+ }
457+
458+ if (resources)
459+ {
460+ if (!fill_screen_info_from_resources (info, resources, error))
461+ return FALSE;
462+ }
463+ else
464+ {
465+ g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR,
466+ /* Translators: a CRTC is a CRT Controller (this is X terminology). */
467+ _("could not get the screen resources (CRTCs, outputs, modes)"));
468+ return FALSE;
469+ }
470+
471+ /* Then update the screen size range. We do this after XRRGetScreenResources() so that
472+ * the X server will already have an updated view of the outputs.
473+ */
474+
475+ if (needs_reprobe) {
476+ gboolean success;
477+
478+ gdk_error_trap_push ();
479+ success = XRRGetScreenSizeRange (xdisplay, xroot,
480+ &(info->min_width),
481+ &(info->min_height),
482+ &(info->max_width),
483+ &(info->max_height));
484+ gdk_flush ();
485+ if (gdk_error_trap_pop ()) {
486+ g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_UNKNOWN,
487+ _("unhandled X error while getting the range of screen sizes"));
488+ return FALSE;
489+ }
490+
491+ if (!success) {
492+ g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR,
493+ _("could not get the range of screen sizes"));
494+ return FALSE;
495+ }
496+ }
497+ else
498+ {
499+ gnome_rr_screen_get_ranges (info->screen,
500+ &(info->min_width),
501+ &(info->max_width),
502+ &(info->min_height),
503+ &(info->max_height));
504+ }
505+
506+ info->primary = None;
507+ if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv)) {
508+ gdk_error_trap_push ();
509+ info->primary = XRRGetOutputPrimary (xdisplay, xroot);
510+ gdk_error_trap_pop_ignored ();
511+ }
512+
513+ /* can the screen do DPMS? */
514+ gdk_error_trap_push ();
515+ priv->dpms_capable = DPMSCapable (priv->xdisplay);
516+ gdk_error_trap_pop_ignored ();
517+
518+ return TRUE;
519+}
520+
521+static ScreenInfo *
522+screen_info_new (GnomeRRScreen *screen, gboolean needs_reprobe, GError **error)
523+{
524+ ScreenInfo *info = g_new0 (ScreenInfo, 1);
525+ GnomeRRScreenPrivate *priv;
526+
527+ g_assert (screen != NULL);
528+
529+ priv = screen->priv;
530+
531+ info->outputs = NULL;
532+ info->crtcs = NULL;
533+ info->modes = NULL;
534+ info->screen = screen;
535+
536+ if (fill_out_screen_info (priv->xdisplay, priv->xroot, info, needs_reprobe, error))
537+ {
538+ return info;
539+ }
540+ else
541+ {
542+ screen_info_free (info);
543+ return NULL;
544+ }
545+}
546+
547+static GnomeRROutput *
548+find_output_by_id (GnomeRROutput **haystack, guint32 id)
549+{
550+ guint i;
551+
552+ for (i = 0; haystack[i] != NULL; i++)
553+ {
554+ if (gnome_rr_output_get_id (haystack[i]) == id)
555+ return haystack[i];
556+ }
557+ return NULL;
558+}
559+
560+static void
561+diff_outputs_and_emit_signals (ScreenInfo *old, ScreenInfo *new)
562+{
563+ guint i;
564+ guint32 id_old, id_new;
565+ GnomeRROutput *output_old;
566+ GnomeRROutput *output_new;
567+
568+ /* have any outputs been removed or disconnected */
569+ for (i = 0; old->outputs[i] != NULL; i++)
570+ {
571+ id_old = gnome_rr_output_get_id (old->outputs[i]);
572+ output_new = find_output_by_id (new->outputs, id_old);
573+ if (output_new == NULL)
574+ {
575+ /* output removed (and disconnected) */
576+ if (gnome_rr_output_is_connected (old->outputs[i]))
577+ {
578+ g_signal_emit (G_OBJECT (new->screen),
579+ screen_signals[SCREEN_OUTPUT_DISCONNECTED], 0,
580+ old->outputs[i]);
581+ }
582+ continue;
583+ }
584+ if (gnome_rr_output_is_connected (old->outputs[i]) &&
585+ !gnome_rr_output_is_connected (output_new))
586+ {
587+ /* output disconnected */
588+ g_signal_emit (G_OBJECT (new->screen),
589+ screen_signals[SCREEN_OUTPUT_DISCONNECTED], 0,
590+ old->outputs[i]);
591+ }
592+ }
593+
594+ /* have any outputs been created or connected */
595+ for (i = 0; new->outputs[i] != NULL; i++)
596+ {
597+ id_new = gnome_rr_output_get_id (new->outputs[i]);
598+ output_old = find_output_by_id (old->outputs, id_new);
599+ if (output_old == NULL)
600+ {
601+ /* output created */
602+ if (gnome_rr_output_is_connected (new->outputs[i]))
603+ {
604+ g_signal_emit (G_OBJECT (new->screen),
605+ screen_signals[SCREEN_OUTPUT_CONNECTED], 0,
606+ new->outputs[i]);
607+ }
608+ continue;
609+ }
610+ if (!gnome_rr_output_is_connected (output_old) &&
611+ gnome_rr_output_is_connected (new->outputs[i]))
612+ {
613+ /* output connected */
614+ g_signal_emit (G_OBJECT (new->screen),
615+ screen_signals[SCREEN_OUTPUT_CONNECTED], 0,
616+ new->outputs[i]);
617+ }
618+ }
619+}
620+
621+static gboolean
622+screen_update (GnomeRRScreen *screen, gboolean force_callback, gboolean needs_reprobe, GError **error)
623+{
624+ ScreenInfo *info;
625+ gboolean changed = FALSE;
626+
627+ g_assert (screen != NULL);
628+
629+ info = screen_info_new (screen, needs_reprobe, error);
630+ if (!info)
631+ return FALSE;
632+
633+ if (info->resources->configTimestamp != screen->priv->info->resources->configTimestamp)
634+ changed = TRUE;
635+
636+ /* work out if any outputs have changed connected state */
637+ diff_outputs_and_emit_signals (screen->priv->info, info);
638+
639+ screen_info_free (screen->priv->info);
640+
641+ screen->priv->info = info;
642+
643+ if (changed || force_callback)
644+ g_signal_emit (G_OBJECT (screen), screen_signals[SCREEN_CHANGED], 0);
645+
646+ return changed;
647+}
648+
649+static GdkFilterReturn
650+screen_on_event (GdkXEvent *xevent,
651+ GdkEvent *event,
652+ gpointer data)
653+{
654+ GnomeRRScreen *screen = data;
655+ GnomeRRScreenPrivate *priv = screen->priv;
656+ XEvent *e = xevent;
657+ int event_num;
658+
659+ if (!e)
660+ return GDK_FILTER_CONTINUE;
661+
662+ event_num = e->type - priv->randr_event_base;
663+
664+ if (event_num == RRScreenChangeNotify) {
665+ /* We don't reprobe the hardware; we just fetch the X server's latest
666+ * state. The server already knows the new state of the outputs; that's
667+ * why it sent us an event!
668+ */
669+ screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */
670+#if 0
671+ /* Enable this code to get a dialog showing the RANDR timestamps, for debugging purposes */
672+ {
673+ GtkWidget *dialog;
674+ XRRScreenChangeNotifyEvent *rr_event;
675+ static int dialog_num;
676+
677+ rr_event = (XRRScreenChangeNotifyEvent *) e;
678+
679+ dialog = gtk_message_dialog_new (NULL,
680+ 0,
681+ GTK_MESSAGE_INFO,
682+ GTK_BUTTONS_CLOSE,
683+ "RRScreenChangeNotify timestamps (%d):\n"
684+ "event change: %u\n"
685+ "event config: %u\n"
686+ "event serial: %lu\n"
687+ "----------------------"
688+ "screen change: %u\n"
689+ "screen config: %u\n",
690+ dialog_num++,
691+ (guint32) rr_event->timestamp,
692+ (guint32) rr_event->config_timestamp,
693+ rr_event->serial,
694+ (guint32) priv->info->resources->timestamp,
695+ (guint32) priv->info->resources->configTimestamp);
696+ g_signal_connect (dialog, "response",
697+ G_CALLBACK (gtk_widget_destroy), NULL);
698+ gtk_widget_show (dialog);
699+ }
700+#endif
701+ }
702+#if 0
703+ /* WHY THIS CODE IS DISABLED:
704+ *
705+ * Note that in gnome_rr_screen_new(), we only select for
706+ * RRScreenChangeNotifyMask. We used to select for other values in
707+ * RR*NotifyMask, but we weren't really doing anything useful with those
708+ * events. We only care about "the screens changed in some way or another"
709+ * for now.
710+ *
711+ * If we ever run into a situtation that could benefit from processing more
712+ * detailed events, we can enable this code again.
713+ *
714+ * Note that the X server sends RRScreenChangeNotify in conjunction with the
715+ * more detailed events from RANDR 1.2 - see xserver/randr/randr.c:TellChanged().
716+ */
717+ else if (event_num == RRNotify)
718+ {
719+ /* Other RandR events */
720+
721+ XRRNotifyEvent *event = (XRRNotifyEvent *)e;
722+
723+ /* Here we can distinguish between RRNotify events supported
724+ * since RandR 1.2 such as RRNotify_OutputProperty. For now, we
725+ * don't have anything special to do for particular subevent types, so
726+ * we leave this as an empty switch().
727+ */
728+ switch (event->subtype)
729+ {
730+ default:
731+ break;
732+ }
733+
734+ /* No need to reprobe hardware here */
735+ screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */
736+ }
737+#endif
738+
739+ /* Pass the event on to GTK+ */
740+ return GDK_FILTER_CONTINUE;
741+}
742+
743+static gboolean
744+gnome_rr_screen_initable_init (GInitable *initable, GCancellable *canc, GError **error)
745+{
746+ GnomeRRScreen *self = GNOME_RR_SCREEN (initable);
747+ GnomeRRScreenPrivate *priv = self->priv;
748+ Display *dpy = GDK_SCREEN_XDISPLAY (self->priv->gdk_screen);
749+ int event_base;
750+ int ignore;
751+
752+ priv->connector_type_atom = XInternAtom (dpy, "ConnectorType", FALSE);
753+
754+ if (XRRQueryExtension (dpy, &event_base, &ignore))
755+ {
756+ priv->randr_event_base = event_base;
757+
758+ XRRQueryVersion (dpy, &priv->rr_major_version, &priv->rr_minor_version);
759+ if (priv->rr_major_version < 1 || (priv->rr_major_version == 1 && priv->rr_minor_version < 2)) {
760+ g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_NO_RANDR_EXTENSION,
761+ "RANDR extension is too old (must be at least 1.2)");
762+ return FALSE;
763+ }
764+
765+ priv->info = screen_info_new (self, TRUE, error);
766+
767+ if (!priv->info) {
768+ return FALSE;
769+ }
770+
771+ XRRSelectInput (priv->xdisplay,
772+ priv->xroot,
773+ RRScreenChangeNotifyMask);
774+ gdk_x11_register_standard_event_type (gdk_screen_get_display (priv->gdk_screen),
775+ event_base,
776+ RRNotify + 1);
777+ gdk_window_add_filter (priv->gdk_root, screen_on_event, self);
778+
779+ return TRUE;
780+ }
781+ else
782+ {
783+ g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_NO_RANDR_EXTENSION,
784+ _("RANDR extension is not present"));
785+
786+ return FALSE;
787+ }
788+}
789+
790+void
791+gnome_rr_screen_initable_iface_init (GInitableIface *iface)
792+{
793+ iface->init = gnome_rr_screen_initable_init;
794+}
795+
796+void
797+gnome_rr_screen_finalize (GObject *gobject)
798+{
799+ GnomeRRScreen *screen = GNOME_RR_SCREEN (gobject);
800+
801+ gdk_window_remove_filter (screen->priv->gdk_root, screen_on_event, screen);
802+
803+ if (screen->priv->info)
804+ screen_info_free (screen->priv->info);
805+
806+ G_OBJECT_CLASS (gnome_rr_screen_parent_class)->finalize (gobject);
807+}
808+
809+void
810+gnome_rr_screen_set_property (GObject *gobject, guint property_id, const GValue *value, GParamSpec *property)
811+{
812+ GnomeRRScreen *self = GNOME_RR_SCREEN (gobject);
813+ GnomeRRScreenPrivate *priv = self->priv;
814+
815+ switch (property_id)
816+ {
817+ case SCREEN_PROP_GDK_SCREEN:
818+ priv->gdk_screen = g_value_get_object (value);
819+ priv->gdk_root = gdk_screen_get_root_window (priv->gdk_screen);
820+ priv->xroot = gdk_x11_window_get_xid (priv->gdk_root);
821+ priv->xdisplay = GDK_SCREEN_XDISPLAY (priv->gdk_screen);
822+ priv->xscreen = gdk_x11_screen_get_xscreen (priv->gdk_screen);
823+ return;
824+ default:
825+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
826+ return;
827+ }
828+}
829+
830+void
831+gnome_rr_screen_get_property (GObject *gobject, guint property_id, GValue *value, GParamSpec *property)
832+{
833+ GnomeRRScreen *self = GNOME_RR_SCREEN (gobject);
834+ GnomeRRScreenPrivate *priv = self->priv;
835+
836+ switch (property_id)
837+ {
838+ case SCREEN_PROP_GDK_SCREEN:
839+ g_value_set_object (value, priv->gdk_screen);
840+ return;
841+ default:
842+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
843+ return;
844+ }
845+}
846+
847+void
848+gnome_rr_screen_class_init (GnomeRRScreenClass *klass)
849+{
850+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
851+ g_type_class_add_private (klass, sizeof (GnomeRRScreenPrivate));
852+
853+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
854+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
855+
856+ gobject_class->set_property = gnome_rr_screen_set_property;
857+ gobject_class->get_property = gnome_rr_screen_get_property;
858+ gobject_class->finalize = gnome_rr_screen_finalize;
859+
860+ g_object_class_install_property(
861+ gobject_class,
862+ SCREEN_PROP_GDK_SCREEN,
863+ g_param_spec_object (
864+ "gdk-screen",
865+ "GDK Screen",
866+ "The GDK Screen represented by this GnomeRRScreen",
867+ GDK_TYPE_SCREEN,
868+ G_PARAM_READWRITE |
869+ G_PARAM_CONSTRUCT_ONLY |
870+ G_PARAM_STATIC_STRINGS)
871+ );
872+
873+ screen_signals[SCREEN_CHANGED] = g_signal_new("changed",
874+ G_TYPE_FROM_CLASS (gobject_class),
875+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
876+ G_STRUCT_OFFSET (GnomeRRScreenClass, changed),
877+ NULL,
878+ NULL,
879+ g_cclosure_marshal_VOID__VOID,
880+ G_TYPE_NONE,
881+ 0);
882+
883+ /**
884+ * GnomeRRScreen::output-connected:
885+ * @screen: the #GnomeRRScreen that emitted the signal
886+ * @output: the #GnomeRROutput that was connected
887+ *
888+ * This signal is emitted when a display device is connected to a
889+ * port, or a port is hotplugged with an active output. The latter
890+ * can happen if a laptop is docked, and the dock provides a new
891+ * active output.
892+ *
893+ * The @output value is not a #GObject. The returned @output value can
894+ * only assume to be valid during the emission of the signal (i.e. within
895+ * your signal handler only), as it may change later when the @screen
896+ * is modified due to an event from the X server, or due to another
897+ * place in the application modifying the @screen and the @output.
898+ * Therefore, deal with changes to the @output right in your signal
899+ * handler, instead of keeping the @output reference for an async or
900+ * idle function.
901+ **/
902+ screen_signals[SCREEN_OUTPUT_CONNECTED] = g_signal_new("output-connected",
903+ G_TYPE_FROM_CLASS (gobject_class),
904+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
905+ G_STRUCT_OFFSET (GnomeRRScreenClass, output_connected),
906+ NULL,
907+ NULL,
908+ g_cclosure_marshal_VOID__POINTER,
909+ G_TYPE_NONE,
910+ 1, G_TYPE_POINTER);
911+
912+ /**
913+ * GnomeRRScreen::output-disconnected:
914+ * @screen: the #GnomeRRScreen that emitted the signal
915+ * @output: the #GnomeRROutput that was disconnected
916+ *
917+ * This signal is emitted when a display device is disconnected from
918+ * a port, or a port output is hot-unplugged. The latter can happen
919+ * if a laptop is undocked, and the dock provided the output.
920+ *
921+ * The @output value is not a #GObject. The returned @output value can
922+ * only assume to be valid during the emission of the signal (i.e. within
923+ * your signal handler only), as it may change later when the @screen
924+ * is modified due to an event from the X server, or due to another
925+ * place in the application modifying the @screen and the @output.
926+ * Therefore, deal with changes to the @output right in your signal
927+ * handler, instead of keeping the @output reference for an async or
928+ * idle function.
929+ **/
930+ screen_signals[SCREEN_OUTPUT_DISCONNECTED] = g_signal_new("output-disconnected",
931+ G_TYPE_FROM_CLASS (gobject_class),
932+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
933+ G_STRUCT_OFFSET (GnomeRRScreenClass, output_disconnected),
934+ NULL,
935+ NULL,
936+ g_cclosure_marshal_VOID__POINTER,
937+ G_TYPE_NONE,
938+ 1, G_TYPE_POINTER);
939+}
940+
941+void
942+gnome_rr_screen_init (GnomeRRScreen *self)
943+{
944+ GnomeRRScreenPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GNOME_TYPE_RR_SCREEN, GnomeRRScreenPrivate);
945+ self->priv = priv;
946+
947+ priv->gdk_screen = NULL;
948+ priv->gdk_root = NULL;
949+ priv->xdisplay = NULL;
950+ priv->xroot = None;
951+ priv->xscreen = NULL;
952+ priv->info = NULL;
953+ priv->rr_major_version = 0;
954+ priv->rr_minor_version = 0;
955+}
956+
957+/* Weak reference callback set in gnome_rr_screen_new(); we remove the GObject data from the GdkScreen. */
958+static void
959+rr_screen_weak_notify_cb (gpointer data, GObject *where_the_object_was)
960+{
961+ GdkScreen *screen = GDK_SCREEN (data);
962+
963+ g_object_set_data (G_OBJECT (screen), "GnomeRRScreen", NULL);
964+}
965+
966+/**
967+ * gnome_rr_screen_new:
968+ * @screen: the #GdkScreen on which to operate
969+ * @error: will be set if XRandR is not supported
970+ *
971+ * Creates a unique #GnomeRRScreen instance for the specified @screen.
972+ *
973+ * Returns: a unique #GnomeRRScreen instance, specific to the @screen, or NULL
974+ * if this could not be created, for instance if the driver does not support
975+ * Xrandr 1.2. Each #GdkScreen thus has a single instance of #GnomeRRScreen.
976+ */
977+GnomeRRScreen *
978+gnome_rr_screen_new (GdkScreen *screen,
979+ GError **error)
980+{
981+ GnomeRRScreen *rr_screen;
982+
983+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
984+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
985+
986+ rr_screen = g_object_get_data (G_OBJECT (screen), "GnomeRRScreen");
987+ if (rr_screen)
988+ g_object_ref (rr_screen);
989+ else {
990+ rr_screen = g_initable_new (GNOME_TYPE_RR_SCREEN, NULL, error, "gdk-screen", screen, NULL);
991+ if (rr_screen) {
992+ g_object_set_data (G_OBJECT (screen), "GnomeRRScreen", rr_screen);
993+ g_object_weak_ref (G_OBJECT (rr_screen), rr_screen_weak_notify_cb, screen);
994+ }
995+ }
996+
997+ return rr_screen;
998+}
999+
1000+void
1001+gnome_rr_screen_set_size (GnomeRRScreen *screen,
1002+ int width,
1003+ int height,
1004+ int mm_width,
1005+ int mm_height)
1006+{
1007+ g_return_if_fail (GNOME_IS_RR_SCREEN (screen));
1008+
1009+ gdk_error_trap_push ();
1010+ XRRSetScreenSize (screen->priv->xdisplay, screen->priv->xroot,
1011+ width, height, mm_width, mm_height);
1012+ gdk_error_trap_pop_ignored ();
1013+}
1014+
1015+/**
1016+ * gnome_rr_screen_get_ranges:
1017+ * @screen: a #GnomeRRScreen
1018+ * @min_width: (out): the minimum width
1019+ * @max_width: (out): the maximum width
1020+ * @min_height: (out): the minimum height
1021+ * @max_height: (out): the maximum height
1022+ *
1023+ * Get the ranges of the screen
1024+ */
1025+void
1026+gnome_rr_screen_get_ranges (GnomeRRScreen *screen,
1027+ int *min_width,
1028+ int *max_width,
1029+ int *min_height,
1030+ int *max_height)
1031+{
1032+ GnomeRRScreenPrivate *priv;
1033+
1034+ g_return_if_fail (GNOME_IS_RR_SCREEN (screen));
1035+
1036+ priv = screen->priv;
1037+
1038+ if (min_width)
1039+ *min_width = priv->info->min_width;
1040+
1041+ if (max_width)
1042+ *max_width = priv->info->max_width;
1043+
1044+ if (min_height)
1045+ *min_height = priv->info->min_height;
1046+
1047+ if (max_height)
1048+ *max_height = priv->info->max_height;
1049+}
1050+
1051+/**
1052+ * gnome_rr_screen_get_timestamps:
1053+ * @screen: a #GnomeRRScreen
1054+ * @change_timestamp_ret: (out): Location in which to store the timestamp at which the RANDR configuration was last changed
1055+ * @config_timestamp_ret: (out): Location in which to store the timestamp at which the RANDR configuration was last obtained
1056+ *
1057+ * Queries the two timestamps that the X RANDR extension maintains. The X
1058+ * server will prevent change requests for stale configurations, those whose
1059+ * timestamp is not equal to that of the latest request for configuration. The
1060+ * X server will also prevent change requests that have an older timestamp to
1061+ * the latest change request.
1062+ */
1063+void
1064+gnome_rr_screen_get_timestamps (GnomeRRScreen *screen,
1065+ guint32 *change_timestamp_ret,
1066+ guint32 *config_timestamp_ret)
1067+{
1068+ GnomeRRScreenPrivate *priv;
1069+
1070+ g_return_if_fail (GNOME_IS_RR_SCREEN (screen));
1071+
1072+ priv = screen->priv;
1073+
1074+ if (change_timestamp_ret)
1075+ *change_timestamp_ret = priv->info->resources->timestamp;
1076+
1077+ if (config_timestamp_ret)
1078+ *config_timestamp_ret = priv->info->resources->configTimestamp;
1079+}
1080+
1081+static gboolean
1082+force_timestamp_update (GnomeRRScreen *screen)
1083+{
1084+ GnomeRRScreenPrivate *priv = screen->priv;
1085+ GnomeRRCrtc *crtc;
1086+ XRRCrtcInfo *current_info;
1087+ Status status;
1088+ gboolean timestamp_updated;
1089+
1090+ timestamp_updated = FALSE;
1091+
1092+ crtc = priv->info->crtcs[0];
1093+
1094+ if (crtc == NULL)
1095+ goto out;
1096+
1097+ current_info = XRRGetCrtcInfo (priv->xdisplay,
1098+ priv->info->resources,
1099+ crtc->id);
1100+
1101+ if (current_info == NULL)
1102+ goto out;
1103+
1104+ gdk_error_trap_push ();
1105+ status = XRRSetCrtcConfig (priv->xdisplay,
1106+ priv->info->resources,
1107+ crtc->id,
1108+ current_info->timestamp,
1109+ current_info->x,
1110+ current_info->y,
1111+ current_info->mode,
1112+ current_info->rotation,
1113+ current_info->outputs,
1114+ current_info->noutput);
1115+
1116+ XRRFreeCrtcInfo (current_info);
1117+
1118+ gdk_flush ();
1119+ if (gdk_error_trap_pop ())
1120+ goto out;
1121+
1122+ if (status == RRSetConfigSuccess)
1123+ timestamp_updated = TRUE;
1124+out:
1125+ return timestamp_updated;
1126+}
1127+
1128+/**
1129+ * gnome_rr_screen_refresh:
1130+ * @screen: a #GnomeRRScreen
1131+ * @error: location to store error, or %NULL
1132+ *
1133+ * Refreshes the screen configuration, and calls the screen's callback if it
1134+ * exists and if the screen's configuration changed.
1135+ *
1136+ * Return value: TRUE if the screen's configuration changed; otherwise, the
1137+ * function returns FALSE and a NULL error if the configuration didn't change,
1138+ * or FALSE and a non-NULL error if there was an error while refreshing the
1139+ * configuration.
1140+ */
1141+gboolean
1142+gnome_rr_screen_refresh (GnomeRRScreen *screen,
1143+ GError **error)
1144+{
1145+ gboolean refreshed;
1146+
1147+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1148+
1149+ gdk_x11_display_grab (gdk_screen_get_display (screen->priv->gdk_screen));
1150+
1151+ refreshed = screen_update (screen, FALSE, TRUE, error);
1152+ force_timestamp_update (screen); /* this is to keep other clients from thinking that the X server re-detected things by itself - bgo#621046 */
1153+
1154+ gdk_x11_display_ungrab (gdk_screen_get_display (screen->priv->gdk_screen));
1155+
1156+ return refreshed;
1157+}
1158+
1159+/**
1160+ * gnome_rr_screen_get_dpms_mode:
1161+ * @mode: (out): The current #GnomeRRDpmsMode of this screen
1162+ **/
1163+gboolean
1164+gnome_rr_screen_get_dpms_mode (GnomeRRScreen *screen,
1165+ GnomeRRDpmsMode *mode,
1166+ GError **error)
1167+{
1168+ BOOL enabled = FALSE;
1169+ CARD16 state;
1170+ gboolean ret = FALSE;
1171+
1172+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1173+ g_return_val_if_fail (mode != NULL, FALSE);
1174+
1175+ if (!screen->priv->dpms_capable) {
1176+ g_set_error_literal (error,
1177+ GNOME_RR_ERROR,
1178+ GNOME_RR_ERROR_NO_DPMS_EXTENSION,
1179+ "Display is not DPMS capable");
1180+ goto out;
1181+ }
1182+
1183+ if (!DPMSInfo (screen->priv->xdisplay,
1184+ &state,
1185+ &enabled)) {
1186+ g_set_error_literal (error,
1187+ GNOME_RR_ERROR,
1188+ GNOME_RR_ERROR_UNKNOWN,
1189+ "Unable to get DPMS state");
1190+ goto out;
1191+ }
1192+
1193+ /* DPMS not enabled is a valid mode */
1194+ if (!enabled) {
1195+ *mode = GNOME_RR_DPMS_DISABLED;
1196+ ret = TRUE;
1197+ goto out;
1198+ }
1199+
1200+ switch (state) {
1201+ case DPMSModeOn:
1202+ *mode = GNOME_RR_DPMS_ON;
1203+ break;
1204+ case DPMSModeStandby:
1205+ *mode = GNOME_RR_DPMS_STANDBY;
1206+ break;
1207+ case DPMSModeSuspend:
1208+ *mode = GNOME_RR_DPMS_SUSPEND;
1209+ break;
1210+ case DPMSModeOff:
1211+ *mode = GNOME_RR_DPMS_OFF;
1212+ break;
1213+ default:
1214+ g_assert_not_reached ();
1215+ break;
1216+ }
1217+ ret = TRUE;
1218+out:
1219+ return ret;
1220+}
1221+
1222+/**
1223+ * gnome_rr_screen_clear_dpms_timeouts:
1224+ **/
1225+static gboolean
1226+gnome_rr_screen_clear_dpms_timeouts (GnomeRRScreen *screen,
1227+ GError **error)
1228+{
1229+ gdk_error_trap_push ();
1230+ /* DPMSSetTimeouts() return value is often a lie, so ignore it */
1231+ DPMSSetTimeouts (screen->priv->xdisplay, 0, 0, 0);
1232+ if (gdk_error_trap_pop ()) {
1233+ g_set_error_literal (error,
1234+ GNOME_RR_ERROR,
1235+ GNOME_RR_ERROR_UNKNOWN,
1236+ "Could not set DPMS timeouts");
1237+ return FALSE;
1238+ }
1239+ return TRUE;
1240+}
1241+
1242+/**
1243+ * gnome_rr_screen_set_dpms_mode:
1244+ *
1245+ * This method also disables the DPMS timeouts.
1246+ **/
1247+gboolean
1248+gnome_rr_screen_set_dpms_mode (GnomeRRScreen *screen,
1249+ GnomeRRDpmsMode mode,
1250+ GError **error)
1251+{
1252+ CARD16 state = 0;
1253+ gboolean ret;
1254+ GnomeRRDpmsMode current_mode;
1255+
1256+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1257+
1258+ /* set, if the new mode is different */
1259+ ret = gnome_rr_screen_get_dpms_mode (screen, &current_mode, error);
1260+ if (!ret)
1261+ goto out;
1262+ if (current_mode == mode) {
1263+ ret = gnome_rr_screen_clear_dpms_timeouts (screen, error);
1264+ goto out;
1265+ }
1266+
1267+ switch (mode) {
1268+ case GNOME_RR_DPMS_ON:
1269+ state = DPMSModeOn;
1270+ break;
1271+ case GNOME_RR_DPMS_STANDBY:
1272+ state = DPMSModeStandby;
1273+ break;
1274+ case GNOME_RR_DPMS_SUSPEND:
1275+ state = DPMSModeSuspend;
1276+ break;
1277+ case GNOME_RR_DPMS_OFF:
1278+ state = DPMSModeOff;
1279+ break;
1280+ default:
1281+ g_assert_not_reached ();
1282+ break;
1283+ }
1284+
1285+ gdk_error_trap_push ();
1286+ /* DPMSForceLevel() return value is often a lie, so ignore it */
1287+ DPMSForceLevel (screen->priv->xdisplay, state);
1288+ XSync (screen->priv->xdisplay, False);
1289+ if (gdk_error_trap_pop ()) {
1290+ ret = FALSE;
1291+ g_set_error_literal (error,
1292+ GNOME_RR_ERROR,
1293+ GNOME_RR_ERROR_UNKNOWN,
1294+ "Could not change DPMS mode");
1295+ goto out;
1296+ }
1297+
1298+ ret = gnome_rr_screen_clear_dpms_timeouts (screen, error);
1299+ if (!ret)
1300+ goto out;
1301+out:
1302+ return ret;
1303+}
1304+
1305+/**
1306+ * gnome_rr_screen_list_modes:
1307+ *
1308+ * List available XRandR modes
1309+ *
1310+ * Returns: (array zero-terminated=1) (transfer none):
1311+ */
1312+GnomeRRMode **
1313+gnome_rr_screen_list_modes (GnomeRRScreen *screen)
1314+{
1315+ g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1316+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
1317+
1318+ return screen->priv->info->modes;
1319+}
1320+
1321+/**
1322+ * gnome_rr_screen_list_clone_modes:
1323+ *
1324+ * List available XRandR clone modes
1325+ *
1326+ * Returns: (array zero-terminated=1) (transfer none):
1327+ */
1328+GnomeRRMode **
1329+gnome_rr_screen_list_clone_modes (GnomeRRScreen *screen)
1330+{
1331+ g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1332+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
1333+
1334+ return screen->priv->info->clone_modes;
1335+}
1336+
1337+/**
1338+ * gnome_rr_screen_list_crtcs:
1339+ *
1340+ * List all CRTCs
1341+ *
1342+ * Returns: (array zero-terminated=1) (transfer none):
1343+ */
1344+GnomeRRCrtc **
1345+gnome_rr_screen_list_crtcs (GnomeRRScreen *screen)
1346+{
1347+ g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1348+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
1349+
1350+ return screen->priv->info->crtcs;
1351+}
1352+
1353+/**
1354+ * gnome_rr_screen_list_outputs:
1355+ *
1356+ * List all outputs
1357+ *
1358+ * Returns: (array zero-terminated=1) (transfer none):
1359+ */
1360+GnomeRROutput **
1361+gnome_rr_screen_list_outputs (GnomeRRScreen *screen)
1362+{
1363+ g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1364+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
1365+
1366+ return screen->priv->info->outputs;
1367+}
1368+
1369+/**
1370+ * gnome_rr_screen_get_crtc_by_id:
1371+ *
1372+ * Returns: (transfer none): the CRTC identified by @id
1373+ */
1374+GnomeRRCrtc *
1375+gnome_rr_screen_get_crtc_by_id (GnomeRRScreen *screen,
1376+ guint32 id)
1377+{
1378+ GnomeRRCrtc **crtcs;
1379+ int i;
1380+
1381+ g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1382+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
1383+
1384+ crtcs = screen->priv->info->crtcs;
1385+
1386+ for (i = 0; crtcs[i] != NULL; ++i)
1387+ {
1388+ if (crtcs[i]->id == id)
1389+ return crtcs[i];
1390+ }
1391+
1392+ return NULL;
1393+}
1394+
1395+/**
1396+ * gnome_rr_screen_get_output_by_id:
1397+ *
1398+ * Returns: (transfer none): the output identified by @id
1399+ */
1400+GnomeRROutput *
1401+gnome_rr_screen_get_output_by_id (GnomeRRScreen *screen,
1402+ guint32 id)
1403+{
1404+ GnomeRROutput **outputs;
1405+ int i;
1406+
1407+ g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1408+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
1409+
1410+ outputs = screen->priv->info->outputs;
1411+
1412+ for (i = 0; outputs[i] != NULL; ++i)
1413+ {
1414+ if (outputs[i]->id == id)
1415+ return outputs[i];
1416+ }
1417+
1418+ return NULL;
1419+}
1420+
1421+/* GnomeRROutput */
1422+static GnomeRROutput *
1423+output_new (ScreenInfo *info, RROutput id)
1424+{
1425+ GnomeRROutput *output = g_slice_new0 (GnomeRROutput);
1426+
1427+ output->id = id;
1428+ output->info = info;
1429+
1430+ return output;
1431+}
1432+
1433+static guint8 *
1434+get_property (Display *dpy,
1435+ RROutput output,
1436+ Atom atom,
1437+ gsize *len)
1438+{
1439+ unsigned char *prop;
1440+ int actual_format;
1441+ unsigned long nitems, bytes_after;
1442+ Atom actual_type;
1443+ guint8 *result;
1444+
1445+ XRRGetOutputProperty (dpy, output, atom,
1446+ 0, 100, False, False,
1447+ AnyPropertyType,
1448+ &actual_type, &actual_format,
1449+ &nitems, &bytes_after, &prop);
1450+
1451+ if (actual_type == XA_INTEGER && actual_format == 8)
1452+ {
1453+ result = g_memdup (prop, nitems);
1454+ if (len)
1455+ *len = nitems;
1456+ }
1457+ else
1458+ {
1459+ result = NULL;
1460+ }
1461+
1462+ XFree (prop);
1463+
1464+ return result;
1465+}
1466+
1467+static guint8 *
1468+read_edid_data (GnomeRROutput *output, gsize *len)
1469+{
1470+ Atom edid_atom;
1471+ guint8 *result;
1472+
1473+ edid_atom = XInternAtom (DISPLAY (output), "EDID", FALSE);
1474+ result = get_property (DISPLAY (output),
1475+ output->id, edid_atom, len);
1476+
1477+ if (!result)
1478+ {
1479+ edid_atom = XInternAtom (DISPLAY (output), "EDID_DATA", FALSE);
1480+ result = get_property (DISPLAY (output),
1481+ output->id, edid_atom, len);
1482+ }
1483+
1484+ if (!result)
1485+ {
1486+ edid_atom = XInternAtom (DISPLAY (output), "XFree86_DDC_EDID1_RAWDATA", FALSE);
1487+ result = get_property (DISPLAY (output),
1488+ output->id, edid_atom, len);
1489+ }
1490+
1491+ if (result)
1492+ {
1493+ if (*len % 128 == 0)
1494+ return result;
1495+ else
1496+ g_free (result);
1497+ }
1498+
1499+ return NULL;
1500+}
1501+
1502+static char *
1503+get_connector_type_string (GnomeRROutput *output)
1504+{
1505+ char *result;
1506+ unsigned char *prop;
1507+ int actual_format;
1508+ unsigned long nitems, bytes_after;
1509+ Atom actual_type;
1510+ Atom connector_type;
1511+ char *connector_type_str;
1512+
1513+ result = NULL;
1514+
1515+ if (XRRGetOutputProperty (DISPLAY (output), output->id, output->info->screen->priv->connector_type_atom,
1516+ 0, 100, False, False,
1517+ AnyPropertyType,
1518+ &actual_type, &actual_format,
1519+ &nitems, &bytes_after, &prop) != Success)
1520+ return NULL;
1521+
1522+ if (!(actual_type == XA_ATOM && actual_format == 32 && nitems == 1))
1523+ goto out;
1524+
1525+ connector_type = *((Atom *) prop);
1526+
1527+ connector_type_str = XGetAtomName (DISPLAY (output), connector_type);
1528+ if (connector_type_str) {
1529+ result = g_strdup (connector_type_str); /* so the caller can g_free() it */
1530+ XFree (connector_type_str);
1531+ }
1532+
1533+out:
1534+
1535+ XFree (prop);
1536+
1537+ return result;
1538+}
1539+
1540+static void
1541+update_brightness_limits (GnomeRROutput *output)
1542+{
1543+ gint rc;
1544+ Atom atom;
1545+ XRRPropertyInfo *info;
1546+
1547+ gdk_error_trap_push ();
1548+ atom = XInternAtom (DISPLAY (output), "Backlight", FALSE);
1549+ info = XRRQueryOutputProperty (DISPLAY (output), output->id, atom);
1550+ rc = gdk_error_trap_pop ();
1551+ if (rc != Success)
1552+ {
1553+ if (rc != BadName)
1554+ g_warning ("could not get output property for %s, rc: %i",
1555+ output->name, rc);
1556+ goto out;
1557+ }
1558+ if (info == NULL)
1559+ {
1560+ g_warning ("could not get output property for %s",
1561+ output->name);
1562+ goto out;
1563+ }
1564+ if (!info->range || info->num_values != 2)
1565+ {
1566+ g_debug ("backlight %s was not range", output->name);
1567+ goto out;
1568+ }
1569+ output->backlight_min = info->values[0];
1570+ output->backlight_max = info->values[1];
1571+out:
1572+ if (info != NULL)
1573+ {
1574+ XFree (info);
1575+ }
1576+}
1577+
1578+static gboolean
1579+output_initialize (GnomeRROutput *output, XRRScreenResources *res, GError **error)
1580+{
1581+ XRROutputInfo *info = XRRGetOutputInfo (
1582+ DISPLAY (output), res, output->id);
1583+ GPtrArray *a;
1584+ int i;
1585+
1586+#if 0
1587+ g_print ("Output %lx Timestamp: %u\n", output->id, (guint32)info->timestamp);
1588+#endif
1589+
1590+ if (!info || !output->info)
1591+ {
1592+ /* FIXME: see the comment in crtc_initialize() */
1593+ /* Translators: here, an "output" is a video output */
1594+ g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR,
1595+ _("could not get information about output %d"),
1596+ (int) output->id);
1597+ return FALSE;
1598+ }
1599+
1600+ output->name = g_strdup (info->name); /* FIXME: what is nameLen used for? */
1601+ output->display_name = NULL; /* set first time the getter is used */
1602+ output->current_crtc = crtc_by_id (output->info, info->crtc);
1603+ output->width_mm = info->mm_width;
1604+ output->height_mm = info->mm_height;
1605+ output->connected = (info->connection == RR_Connected);
1606+ output->connector_type = get_connector_type_string (output);
1607+
1608+ /* Possible crtcs */
1609+ a = g_ptr_array_new ();
1610+
1611+ for (i = 0; i < info->ncrtc; ++i)
1612+ {
1613+ GnomeRRCrtc *crtc = crtc_by_id (output->info, info->crtcs[i]);
1614+
1615+ if (crtc)
1616+ g_ptr_array_add (a, crtc);
1617+ }
1618+ g_ptr_array_add (a, NULL);
1619+ output->possible_crtcs = (GnomeRRCrtc **)g_ptr_array_free (a, FALSE);
1620+
1621+ /* Clones */
1622+ a = g_ptr_array_new ();
1623+ for (i = 0; i < info->nclone; ++i)
1624+ {
1625+ GnomeRROutput *gnome_rr_output = gnome_rr_output_by_id (output->info, info->clones[i]);
1626+
1627+ if (gnome_rr_output)
1628+ g_ptr_array_add (a, gnome_rr_output);
1629+ }
1630+ g_ptr_array_add (a, NULL);
1631+ output->clones = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
1632+
1633+ /* Modes */
1634+ a = g_ptr_array_new ();
1635+ for (i = 0; i < info->nmode; ++i)
1636+ {
1637+ GnomeRRMode *mode = mode_by_id (output->info, info->modes[i]);
1638+
1639+ if (mode)
1640+ g_ptr_array_add (a, mode);
1641+ }
1642+ g_ptr_array_add (a, NULL);
1643+ output->modes = (GnomeRRMode **)g_ptr_array_free (a, FALSE);
1644+
1645+ output->n_preferred = info->npreferred;
1646+
1647+ /* Edid data */
1648+ output->edid_data = read_edid_data (output, &output->edid_size);
1649+
1650+ /* brightness data */
1651+ if (output->connected)
1652+ update_brightness_limits (output);
1653+
1654+ XRRFreeOutputInfo (info);
1655+
1656+ return TRUE;
1657+}
1658+
1659+static GnomeRROutput*
1660+output_copy (const GnomeRROutput *from)
1661+{
1662+ GPtrArray *array;
1663+ GnomeRRCrtc **p_crtc;
1664+ GnomeRROutput **p_output;
1665+ GnomeRRMode **p_mode;
1666+ GnomeRROutput *output = g_slice_new0 (GnomeRROutput);
1667+
1668+ output->id = from->id;
1669+ output->info = from->info;
1670+ output->name = g_strdup (from->name);
1671+ output->current_crtc = from->current_crtc;
1672+ output->width_mm = from->width_mm;
1673+ output->height_mm = from->height_mm;
1674+ output->connected = from->connected;
1675+ output->n_preferred = from->n_preferred;
1676+ output->connector_type = g_strdup (from->connector_type);
1677+ output->backlight_min = -1;
1678+ output->backlight_max = -1;
1679+
1680+ array = g_ptr_array_new ();
1681+ for (p_crtc = from->possible_crtcs; *p_crtc != NULL; p_crtc++)
1682+ {
1683+ g_ptr_array_add (array, *p_crtc);
1684+ }
1685+ output->possible_crtcs = (GnomeRRCrtc**) g_ptr_array_free (array, FALSE);
1686+
1687+ array = g_ptr_array_new ();
1688+ for (p_output = from->clones; *p_output != NULL; p_output++)
1689+ {
1690+ g_ptr_array_add (array, *p_output);
1691+ }
1692+ output->clones = (GnomeRROutput**) g_ptr_array_free (array, FALSE);
1693+
1694+ array = g_ptr_array_new ();
1695+ for (p_mode = from->modes; *p_mode != NULL; p_mode++)
1696+ {
1697+ g_ptr_array_add (array, *p_mode);
1698+ }
1699+ output->modes = (GnomeRRMode**) g_ptr_array_free (array, FALSE);
1700+
1701+ output->edid_size = from->edid_size;
1702+ output->edid_data = g_memdup (from->edid_data, from->edid_size);
1703+
1704+ return output;
1705+}
1706+
1707+static void
1708+output_free (GnomeRROutput *output)
1709+{
1710+ g_free (output->clones);
1711+ g_free (output->modes);
1712+ g_free (output->possible_crtcs);
1713+ g_free (output->edid_data);
1714+ g_free (output->name);
1715+ g_free (output->display_name);
1716+ g_free (output->connector_type);
1717+ g_slice_free (GnomeRROutput, output);
1718+}
1719+
1720+guint32
1721+gnome_rr_output_get_id (GnomeRROutput *output)
1722+{
1723+ g_assert(output != NULL);
1724+
1725+ return output->id;
1726+}
1727+
1728+const guint8 *
1729+gnome_rr_output_get_edid_data (GnomeRROutput *output, gsize *size)
1730+{
1731+ g_return_val_if_fail (output != NULL, NULL);
1732+ if (size)
1733+ *size = output->edid_size;
1734+ return output->edid_data;
1735+}
1736+
1737+/**
1738+ * gnome_rr_output_get_ids_from_edid:
1739+ * @output: a #GnomeRROutput
1740+ * @vendor: (out) (allow-none):
1741+ * @product: (out) (allow-none):
1742+ * @serial: (out) (allow-none):
1743+ */
1744+gboolean
1745+gnome_rr_output_get_ids_from_edid (GnomeRROutput *output,
1746+ char **vendor,
1747+ int *product,
1748+ int *serial)
1749+{
1750+ MonitorInfo *info;
1751+
1752+ g_return_val_if_fail (output != NULL, FALSE);
1753+
1754+ if (!output->edid_data)
1755+ return FALSE;
1756+ info = decode_edid (output->edid_data);
1757+ if (!info)
1758+ return FALSE;
1759+ if (vendor)
1760+ *vendor = g_memdup (info->manufacturer_code, 4);
1761+ if (product)
1762+ *product = info->product_code;
1763+ if (serial)
1764+ *serial = info->serial_number;
1765+
1766+ g_free (info);
1767+
1768+ return TRUE;
1769+
1770+}
1771+
1772+static void
1773+ensure_display_name (GnomeRROutput *output)
1774+{
1775+ if (output->display_name != NULL)
1776+ return;
1777+
1778+ if (gnome_rr_output_is_laptop (output))
1779+ output->display_name = g_strdup (_("Built-in Display"));
1780+
1781+ if (output->display_name == NULL
1782+ && output->edid_data != NULL) {
1783+ MonitorInfo *info;
1784+
1785+ info = decode_edid (output->edid_data);
1786+ if (info != NULL)
1787+ output->display_name = make_display_name (info);
1788+
1789+ g_free (info);
1790+ }
1791+
1792+ if (output->display_name == NULL) {
1793+ char *inches;
1794+ inches = make_display_size_string (output->width_mm, output->height_mm);
1795+ if (inches != NULL) {
1796+ /* Translators: %s is the size of the monitor in inches */
1797+ output->display_name = g_strdup_printf (_("%s Display"), inches);
1798+ }
1799+ g_free (inches);
1800+ }
1801+
1802+ /* last chance on the stairway */
1803+ if (output->display_name == NULL) {
1804+ output->display_name = g_strdup (_("Unknown Display"));
1805+ }
1806+}
1807+
1808+const char *
1809+gnome_rr_output_get_display_name (GnomeRROutput *output)
1810+{
1811+ g_return_val_if_fail (output != NULL, NULL);
1812+
1813+ ensure_display_name (output);
1814+
1815+ return output->display_name;
1816+}
1817+
1818+/**
1819+ * gnome_rr_output_get_backlight_min:
1820+ *
1821+ * Returns: The mimimum backlight value, or -1 if not supported
1822+ */
1823+gint
1824+gnome_rr_output_get_backlight_min (GnomeRROutput *output)
1825+{
1826+ g_return_val_if_fail (output != NULL, -1);
1827+ return output->backlight_min;
1828+}
1829+
1830+/**
1831+ * gnome_rr_output_get_backlight_max:
1832+ *
1833+ * Returns: The maximum backlight value, or -1 if not supported
1834+ */
1835+gint
1836+gnome_rr_output_get_backlight_max (GnomeRROutput *output)
1837+{
1838+ g_return_val_if_fail (output != NULL, -1);
1839+ return output->backlight_max;
1840+}
1841+
1842+/**
1843+ * gnome_rr_output_get_backlight:
1844+ *
1845+ * Returns: The currently set backlight brightness
1846+ */
1847+gint
1848+gnome_rr_output_get_backlight (GnomeRROutput *output, GError **error)
1849+{
1850+ guint now = -1;
1851+ unsigned long nitems;
1852+ unsigned long bytes_after;
1853+ guint *prop;
1854+ Atom atom;
1855+ Atom actual_type;
1856+ int actual_format;
1857+ gint retval;
1858+
1859+ g_return_val_if_fail (output != NULL, -1);
1860+
1861+ gdk_error_trap_push ();
1862+ atom = XInternAtom (DISPLAY (output), "Backlight", FALSE);
1863+ retval = XRRGetOutputProperty (DISPLAY (output), output->id, atom,
1864+ 0, 4, False, False, None,
1865+ &actual_type, &actual_format,
1866+ &nitems, &bytes_after, ((unsigned char **)&prop));
1867+ gdk_flush ();
1868+ if (gdk_error_trap_pop ())
1869+ {
1870+ g_set_error_literal (error,
1871+ GNOME_RR_ERROR,
1872+ GNOME_RR_ERROR_UNKNOWN,
1873+ "unhandled X error while getting the range of backlight values");
1874+ goto out;
1875+ }
1876+
1877+ if (retval != Success) {
1878+ g_set_error_literal (error,
1879+ GNOME_RR_ERROR,
1880+ GNOME_RR_ERROR_RANDR_ERROR,
1881+ "could not get the range of backlight values");
1882+ goto out;
1883+ }
1884+ if (actual_type == XA_INTEGER &&
1885+ nitems == 1 &&
1886+ actual_format == 32)
1887+ {
1888+ memcpy (&now, prop, sizeof (guint));
1889+ }
1890+ else
1891+ {
1892+ g_set_error (error,
1893+ GNOME_RR_ERROR,
1894+ GNOME_RR_ERROR_RANDR_ERROR,
1895+ "failed to get correct property type, got %lu,%i",
1896+ nitems, actual_format);
1897+ }
1898+out:
1899+ XFree (prop);
1900+ return now;
1901+}
1902+
1903+/**
1904+ * gnome_rr_output_set_backlight:
1905+ * @value: the absolute value which is min >= this <= max
1906+ *
1907+ * Returns: %TRUE for success
1908+ */
1909+gboolean
1910+gnome_rr_output_set_backlight (GnomeRROutput *output, gint value, GError **error)
1911+{
1912+ gboolean ret = FALSE;
1913+ Atom atom;
1914+
1915+ g_return_val_if_fail (output != NULL, FALSE);
1916+
1917+ /* check this is sane */
1918+ if (value < output->backlight_min ||
1919+ value > output->backlight_max)
1920+ {
1921+ g_set_error (error,
1922+ GNOME_RR_ERROR,
1923+ GNOME_RR_ERROR_BOUNDS_ERROR,
1924+ "out of brightness range: %i, has to be %i -> %i",
1925+ value,
1926+ output->backlight_max, output->backlight_min);
1927+ goto out;
1928+ }
1929+
1930+ /* don't abort on error */
1931+ gdk_error_trap_push ();
1932+ atom = XInternAtom (DISPLAY (output), "Backlight", FALSE);
1933+ XRRChangeOutputProperty (DISPLAY (output), output->id, atom,
1934+ XA_INTEGER, 32, PropModeReplace,
1935+ (unsigned char *) &value, 1);
1936+ if (gdk_error_trap_pop ())
1937+ {
1938+ g_set_error_literal (error,
1939+ GNOME_RR_ERROR,
1940+ GNOME_RR_ERROR_UNKNOWN,
1941+ "unhandled X error while setting the backlight values");
1942+ goto out;
1943+ }
1944+
1945+ /* we assume this succeeded as there's no return value */
1946+ ret = TRUE;
1947+out:
1948+ return ret;
1949+}
1950+
1951+/**
1952+ * gnome_rr_screen_get_output_by_name:
1953+ *
1954+ * Returns: (transfer none): the output identified by @name
1955+ */
1956+GnomeRROutput *
1957+gnome_rr_screen_get_output_by_name (GnomeRRScreen *screen,
1958+ const char *name)
1959+{
1960+ int i;
1961+
1962+ g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1963+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
1964+
1965+ for (i = 0; screen->priv->info->outputs[i] != NULL; ++i)
1966+ {
1967+ GnomeRROutput *output = screen->priv->info->outputs[i];
1968+
1969+ if (strcmp (output->name, name) == 0)
1970+ return output;
1971+ }
1972+
1973+ return NULL;
1974+}
1975+
1976+GnomeRRCrtc *
1977+gnome_rr_output_get_crtc (GnomeRROutput *output)
1978+{
1979+ g_return_val_if_fail (output != NULL, NULL);
1980+
1981+ return output->current_crtc;
1982+}
1983+
1984+/* Returns NULL if the ConnectorType property is not available */
1985+const char *
1986+gnome_rr_output_get_connector_type (GnomeRROutput *output)
1987+{
1988+ g_return_val_if_fail (output != NULL, NULL);
1989+
1990+ return output->connector_type;
1991+}
1992+
1993+gboolean
1994+_gnome_rr_output_name_is_laptop (const char *name)
1995+{
1996+ if (!name)
1997+ return FALSE;
1998+
1999+ if (strstr (name, "lvds") || /* Most drivers use an "LVDS" prefix... */
2000+ strstr (name, "LVDS") ||
2001+ strstr (name, "Lvds") ||
2002+ strstr (name, "LCD") || /* ... but fglrx uses "LCD" in some versions. Shoot me now, kthxbye. */
2003+ strstr (name, "eDP") || /* eDP is for internal laptop panel connections */
2004+ strstr (name, "default")) /* Finally, NVidia and all others that don't bother to do RANDR properly */
2005+ return TRUE;
2006+
2007+ return FALSE;
2008+}
2009+
2010+gboolean
2011+gnome_rr_output_is_laptop (GnomeRROutput *output)
2012+{
2013+ g_return_val_if_fail (output != NULL, FALSE);
2014+
2015+ if (!output->connected)
2016+ return FALSE;
2017+
2018+ /* The ConnectorType property is present in RANDR 1.3 and greater */
2019+ if (g_strcmp0 (output->connector_type, GNOME_RR_CONNECTOR_TYPE_PANEL) == 0)
2020+ return TRUE;
2021+
2022+ /* Older versions of RANDR - this is a best guess, as @#$% RANDR doesn't have standard output names,
2023+ * so drivers can use whatever they like.
2024+ */
2025+ if (_gnome_rr_output_name_is_laptop (output->name))
2026+ return TRUE;
2027+
2028+ return FALSE;
2029+}
2030+
2031+GnomeRRMode *
2032+gnome_rr_output_get_current_mode (GnomeRROutput *output)
2033+{
2034+ GnomeRRCrtc *crtc;
2035+
2036+ g_return_val_if_fail (output != NULL, NULL);
2037+
2038+ if ((crtc = gnome_rr_output_get_crtc (output)))
2039+ return gnome_rr_crtc_get_current_mode (crtc);
2040+
2041+ return NULL;
2042+}
2043+
2044+/**
2045+ * gnome_rr_output_get_position:
2046+ * @output: a #GnomeRROutput
2047+ * @x: (out) (allow-none):
2048+ * @y: (out) (allow-none):
2049+ */
2050+void
2051+gnome_rr_output_get_position (GnomeRROutput *output,
2052+ int *x,
2053+ int *y)
2054+{
2055+ GnomeRRCrtc *crtc;
2056+
2057+ g_return_if_fail (output != NULL);
2058+
2059+ if ((crtc = gnome_rr_output_get_crtc (output)))
2060+ gnome_rr_crtc_get_position (crtc, x, y);
2061+}
2062+
2063+const char *
2064+gnome_rr_output_get_name (GnomeRROutput *output)
2065+{
2066+ g_assert (output != NULL);
2067+ return output->name;
2068+}
2069+
2070+int
2071+gnome_rr_output_get_width_mm (GnomeRROutput *output)
2072+{
2073+ g_assert (output != NULL);
2074+ return output->width_mm;
2075+}
2076+
2077+int
2078+gnome_rr_output_get_height_mm (GnomeRROutput *output)
2079+{
2080+ g_assert (output != NULL);
2081+ return output->height_mm;
2082+}
2083+
2084+GnomeRRMode *
2085+gnome_rr_output_get_preferred_mode (GnomeRROutput *output)
2086+{
2087+ g_return_val_if_fail (output != NULL, NULL);
2088+ if (output->n_preferred)
2089+ return output->modes[0];
2090+
2091+ return NULL;
2092+}
2093+
2094+GnomeRRMode **
2095+gnome_rr_output_list_modes (GnomeRROutput *output)
2096+{
2097+ g_return_val_if_fail (output != NULL, NULL);
2098+ return output->modes;
2099+}
2100+
2101+gboolean
2102+gnome_rr_output_is_connected (GnomeRROutput *output)
2103+{
2104+ g_return_val_if_fail (output != NULL, FALSE);
2105+ return output->connected;
2106+}
2107+
2108+gboolean
2109+gnome_rr_output_supports_mode (GnomeRROutput *output,
2110+ GnomeRRMode *mode)
2111+{
2112+ int i;
2113+
2114+ g_return_val_if_fail (output != NULL, FALSE);
2115+ g_return_val_if_fail (mode != NULL, FALSE);
2116+
2117+ for (i = 0; output->modes[i] != NULL; ++i)
2118+ {
2119+ if (output->modes[i] == mode)
2120+ return TRUE;
2121+ }
2122+
2123+ return FALSE;
2124+}
2125+
2126+gboolean
2127+gnome_rr_output_can_clone (GnomeRROutput *output,
2128+ GnomeRROutput *clone)
2129+{
2130+ int i;
2131+
2132+ g_return_val_if_fail (output != NULL, FALSE);
2133+ g_return_val_if_fail (clone != NULL, FALSE);
2134+
2135+ for (i = 0; output->clones[i] != NULL; ++i)
2136+ {
2137+ if (output->clones[i] == clone)
2138+ return TRUE;
2139+ }
2140+
2141+ return FALSE;
2142+}
2143+
2144+gboolean
2145+gnome_rr_output_get_is_primary (GnomeRROutput *output)
2146+{
2147+ return output->info->primary == output->id;
2148+}
2149+
2150+void
2151+gnome_rr_screen_set_primary_output (GnomeRRScreen *screen,
2152+ GnomeRROutput *output)
2153+{
2154+ GnomeRRScreenPrivate *priv;
2155+ RROutput id;
2156+
2157+ g_return_if_fail (GNOME_IS_RR_SCREEN (screen));
2158+
2159+ priv = screen->priv;
2160+
2161+ if (output)
2162+ id = output->id;
2163+ else
2164+ id = None;
2165+
2166+ if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv)) {
2167+ gdk_error_trap_push ();
2168+ XRRSetOutputPrimary (priv->xdisplay, priv->xroot, id);
2169+ gdk_error_trap_pop_ignored ();
2170+ }
2171+}
2172+
2173+/* GnomeRRCrtc */
2174+typedef struct
2175+{
2176+ Rotation xrot;
2177+ GnomeRRRotation rot;
2178+} RotationMap;
2179+
2180+static const RotationMap rotation_map[] =
2181+{
2182+ { RR_Rotate_0, GNOME_RR_ROTATION_0 },
2183+ { RR_Rotate_90, GNOME_RR_ROTATION_90 },
2184+ { RR_Rotate_180, GNOME_RR_ROTATION_180 },
2185+ { RR_Rotate_270, GNOME_RR_ROTATION_270 },
2186+ { RR_Reflect_X, GNOME_RR_REFLECT_X },
2187+ { RR_Reflect_Y, GNOME_RR_REFLECT_Y },
2188+};
2189+
2190+static GnomeRRRotation
2191+gnome_rr_rotation_from_xrotation (Rotation r)
2192+{
2193+ int i;
2194+ GnomeRRRotation result = 0;
2195+
2196+ for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i)
2197+ {
2198+ if (r & rotation_map[i].xrot)
2199+ result |= rotation_map[i].rot;
2200+ }
2201+
2202+ return result;
2203+}
2204+
2205+static Rotation
2206+xrotation_from_rotation (GnomeRRRotation r)
2207+{
2208+ int i;
2209+ Rotation result = 0;
2210+
2211+ for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i)
2212+ {
2213+ if (r & rotation_map[i].rot)
2214+ result |= rotation_map[i].xrot;
2215+ }
2216+
2217+ return result;
2218+}
2219+
2220+gboolean
2221+gnome_rr_crtc_set_config_with_time (GnomeRRCrtc *crtc,
2222+ guint32 timestamp,
2223+ int x,
2224+ int y,
2225+ GnomeRRMode *mode,
2226+ GnomeRRRotation rotation,
2227+ GnomeRROutput **outputs,
2228+ int n_outputs,
2229+ GError **error)
2230+{
2231+ ScreenInfo *info;
2232+ GArray *output_ids;
2233+ Status status;
2234+ gboolean result;
2235+ int i;
2236+
2237+ g_return_val_if_fail (crtc != NULL, FALSE);
2238+ g_return_val_if_fail (mode != NULL || outputs == NULL || n_outputs == 0, FALSE);
2239+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2240+
2241+ info = crtc->info;
2242+
2243+ if (mode)
2244+ {
2245+ if (x + mode->width > info->max_width
2246+ || y + mode->height > info->max_height)
2247+ {
2248+ g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_BOUNDS_ERROR,
2249+ /* Translators: the "position", "size", and "maximum"
2250+ * words here are not keywords; please translate them
2251+ * as usual. A CRTC is a CRT Controller (this is X terminology) */
2252+ _("requested position/size for CRTC %d is outside the allowed limit: "
2253+ "position=(%d, %d), size=(%d, %d), maximum=(%d, %d)"),
2254+ (int) crtc->id,
2255+ x, y,
2256+ mode->width, mode->height,
2257+ info->max_width, info->max_height);
2258+ return FALSE;
2259+ }
2260+ }
2261+
2262+ output_ids = g_array_new (FALSE, FALSE, sizeof (RROutput));
2263+
2264+ if (outputs)
2265+ {
2266+ for (i = 0; i < n_outputs; ++i)
2267+ g_array_append_val (output_ids, outputs[i]->id);
2268+ }
2269+
2270+ gdk_error_trap_push ();
2271+ status = XRRSetCrtcConfig (DISPLAY (crtc), info->resources, crtc->id,
2272+ timestamp,
2273+ x, y,
2274+ mode ? mode->id : None,
2275+ xrotation_from_rotation (rotation),
2276+ (RROutput *)output_ids->data,
2277+ output_ids->len);
2278+
2279+ g_array_free (output_ids, TRUE);
2280+
2281+ if (gdk_error_trap_pop () || status != RRSetConfigSuccess) {
2282+ /* Translators: CRTC is a CRT Controller (this is X terminology).
2283+ * It is *very* unlikely that you'll ever get this error, so it is
2284+ * only listed for completeness. */
2285+ g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR,
2286+ _("could not set the configuration for CRTC %d"),
2287+ (int) crtc->id);
2288+ return FALSE;
2289+ } else {
2290+ result = TRUE;
2291+ }
2292+
2293+ return result;
2294+}
2295+
2296+GnomeRRMode *
2297+gnome_rr_crtc_get_current_mode (GnomeRRCrtc *crtc)
2298+{
2299+ g_return_val_if_fail (crtc != NULL, NULL);
2300+
2301+ return crtc->current_mode;
2302+}
2303+
2304+guint32
2305+gnome_rr_crtc_get_id (GnomeRRCrtc *crtc)
2306+{
2307+ g_return_val_if_fail (crtc != NULL, 0);
2308+
2309+ return crtc->id;
2310+}
2311+
2312+gboolean
2313+gnome_rr_crtc_can_drive_output (GnomeRRCrtc *crtc,
2314+ GnomeRROutput *output)
2315+{
2316+ int i;
2317+
2318+ g_return_val_if_fail (crtc != NULL, FALSE);
2319+ g_return_val_if_fail (output != NULL, FALSE);
2320+
2321+ for (i = 0; crtc->possible_outputs[i] != NULL; ++i)
2322+ {
2323+ if (crtc->possible_outputs[i] == output)
2324+ return TRUE;
2325+ }
2326+
2327+ return FALSE;
2328+}
2329+
2330+/* FIXME: merge with get_mode()? */
2331+/**
2332+ * gnome_rr_crtc_get_position:
2333+ * @crtc: a #GnomeRRCrtc
2334+ * @x: (out) (allow-none):
2335+ * @y: (out) (allow-none):
2336+ */
2337+void
2338+gnome_rr_crtc_get_position (GnomeRRCrtc *crtc,
2339+ int *x,
2340+ int *y)
2341+{
2342+ g_return_if_fail (crtc != NULL);
2343+
2344+ if (x)
2345+ *x = crtc->x;
2346+
2347+ if (y)
2348+ *y = crtc->y;
2349+}
2350+
2351+/* FIXME: merge with get_mode()? */
2352+GnomeRRRotation
2353+gnome_rr_crtc_get_current_rotation (GnomeRRCrtc *crtc)
2354+{
2355+ g_assert(crtc != NULL);
2356+ return crtc->current_rotation;
2357+}
2358+
2359+GnomeRRRotation
2360+gnome_rr_crtc_get_rotations (GnomeRRCrtc *crtc)
2361+{
2362+ g_assert(crtc != NULL);
2363+ return crtc->rotations;
2364+}
2365+
2366+gboolean
2367+gnome_rr_crtc_supports_rotation (GnomeRRCrtc * crtc,
2368+ GnomeRRRotation rotation)
2369+{
2370+ g_return_val_if_fail (crtc != NULL, FALSE);
2371+ return (crtc->rotations & rotation);
2372+}
2373+
2374+static GnomeRRCrtc *
2375+crtc_new (ScreenInfo *info, RROutput id)
2376+{
2377+ GnomeRRCrtc *crtc = g_slice_new0 (GnomeRRCrtc);
2378+
2379+ crtc->id = id;
2380+ crtc->info = info;
2381+
2382+ return crtc;
2383+}
2384+
2385+static GnomeRRCrtc *
2386+crtc_copy (const GnomeRRCrtc *from)
2387+{
2388+ GnomeRROutput **p_output;
2389+ GPtrArray *array;
2390+ GnomeRRCrtc *to = g_slice_new0 (GnomeRRCrtc);
2391+
2392+ to->info = from->info;
2393+ to->id = from->id;
2394+ to->current_mode = from->current_mode;
2395+ to->x = from->x;
2396+ to->y = from->y;
2397+ to->current_rotation = from->current_rotation;
2398+ to->rotations = from->rotations;
2399+ to->gamma_size = from->gamma_size;
2400+
2401+ array = g_ptr_array_new ();
2402+ for (p_output = from->current_outputs; *p_output != NULL; p_output++)
2403+ {
2404+ g_ptr_array_add (array, *p_output);
2405+ }
2406+ to->current_outputs = (GnomeRROutput**) g_ptr_array_free (array, FALSE);
2407+
2408+ array = g_ptr_array_new ();
2409+ for (p_output = from->possible_outputs; *p_output != NULL; p_output++)
2410+ {
2411+ g_ptr_array_add (array, *p_output);
2412+ }
2413+ to->possible_outputs = (GnomeRROutput**) g_ptr_array_free (array, FALSE);
2414+
2415+ return to;
2416+}
2417+
2418+static gboolean
2419+crtc_initialize (GnomeRRCrtc *crtc,
2420+ XRRScreenResources *res,
2421+ GError **error)
2422+{
2423+ XRRCrtcInfo *info = XRRGetCrtcInfo (DISPLAY (crtc), res, crtc->id);
2424+ GPtrArray *a;
2425+ int i;
2426+
2427+#if 0
2428+ g_print ("CRTC %lx Timestamp: %u\n", crtc->id, (guint32)info->timestamp);
2429+#endif
2430+
2431+ if (!info)
2432+ {
2433+ /* FIXME: We need to reaquire the screen resources */
2434+ /* FIXME: can we actually catch BadRRCrtc, and does it make sense to emit that? */
2435+
2436+ /* Translators: CRTC is a CRT Controller (this is X terminology).
2437+ * It is *very* unlikely that you'll ever get this error, so it is
2438+ * only listed for completeness. */
2439+ g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR,
2440+ _("could not get information about CRTC %d"),
2441+ (int) crtc->id);
2442+ return FALSE;
2443+ }
2444+
2445+ /* GnomeRRMode */
2446+ crtc->current_mode = mode_by_id (crtc->info, info->mode);
2447+
2448+ crtc->x = info->x;
2449+ crtc->y = info->y;
2450+
2451+ /* Current outputs */
2452+ a = g_ptr_array_new ();
2453+ for (i = 0; i < info->noutput; ++i)
2454+ {
2455+ GnomeRROutput *output = gnome_rr_output_by_id (crtc->info, info->outputs[i]);
2456+
2457+ if (output)
2458+ g_ptr_array_add (a, output);
2459+ }
2460+ g_ptr_array_add (a, NULL);
2461+ crtc->current_outputs = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
2462+
2463+ /* Possible outputs */
2464+ a = g_ptr_array_new ();
2465+ for (i = 0; i < info->npossible; ++i)
2466+ {
2467+ GnomeRROutput *output = gnome_rr_output_by_id (crtc->info, info->possible[i]);
2468+
2469+ if (output)
2470+ g_ptr_array_add (a, output);
2471+ }
2472+ g_ptr_array_add (a, NULL);
2473+ crtc->possible_outputs = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
2474+
2475+ /* Rotations */
2476+ crtc->current_rotation = gnome_rr_rotation_from_xrotation (info->rotation);
2477+ crtc->rotations = gnome_rr_rotation_from_xrotation (info->rotations);
2478+
2479+ XRRFreeCrtcInfo (info);
2480+
2481+ /* get an store gamma size */
2482+ crtc->gamma_size = XRRGetCrtcGammaSize (DISPLAY (crtc), crtc->id);
2483+
2484+ return TRUE;
2485+}
2486+
2487+static void
2488+crtc_free (GnomeRRCrtc *crtc)
2489+{
2490+ g_free (crtc->current_outputs);
2491+ g_free (crtc->possible_outputs);
2492+ g_slice_free (GnomeRRCrtc, crtc);
2493+}
2494+
2495+/* GnomeRRMode */
2496+static GnomeRRMode *
2497+mode_new (ScreenInfo *info, RRMode id)
2498+{
2499+ GnomeRRMode *mode = g_slice_new0 (GnomeRRMode);
2500+
2501+ mode->id = id;
2502+ mode->info = info;
2503+
2504+ return mode;
2505+}
2506+
2507+guint32
2508+gnome_rr_mode_get_id (GnomeRRMode *mode)
2509+{
2510+ g_return_val_if_fail (mode != NULL, 0);
2511+ return mode->id;
2512+}
2513+
2514+guint
2515+gnome_rr_mode_get_width (GnomeRRMode *mode)
2516+{
2517+ g_return_val_if_fail (mode != NULL, 0);
2518+ return mode->width;
2519+}
2520+
2521+int
2522+gnome_rr_mode_get_freq (GnomeRRMode *mode)
2523+{
2524+ g_return_val_if_fail (mode != NULL, 0);
2525+ return (mode->freq) / 1000;
2526+}
2527+
2528+guint
2529+gnome_rr_mode_get_height (GnomeRRMode *mode)
2530+{
2531+ g_return_val_if_fail (mode != NULL, 0);
2532+ return mode->height;
2533+}
2534+
2535+static void
2536+mode_initialize (GnomeRRMode *mode, XRRModeInfo *info)
2537+{
2538+ g_assert (mode != NULL);
2539+ g_assert (info != NULL);
2540+
2541+ mode->name = g_strdup (info->name);
2542+ mode->width = info->width;
2543+ mode->height = info->height;
2544+ mode->freq = ((info->dotClock / (double)info->hTotal) / info->vTotal + 0.5) * 1000;
2545+}
2546+
2547+static GnomeRRMode *
2548+mode_copy (const GnomeRRMode *from)
2549+{
2550+ GnomeRRMode *to = g_slice_new0 (GnomeRRMode);
2551+
2552+ to->id = from->id;
2553+ to->info = from->info;
2554+ to->name = g_strdup (from->name);
2555+ to->width = from->width;
2556+ to->height = from->height;
2557+ to->freq = from->freq;
2558+
2559+ return to;
2560+}
2561+
2562+static void
2563+mode_free (GnomeRRMode *mode)
2564+{
2565+ g_free (mode->name);
2566+ g_slice_free (GnomeRRMode, mode);
2567+}
2568+
2569+void
2570+gnome_rr_crtc_set_gamma (GnomeRRCrtc *crtc, int size,
2571+ unsigned short *red,
2572+ unsigned short *green,
2573+ unsigned short *blue)
2574+{
2575+ int copy_size;
2576+ XRRCrtcGamma *gamma;
2577+
2578+ g_return_if_fail (crtc != NULL);
2579+ g_return_if_fail (red != NULL);
2580+ g_return_if_fail (green != NULL);
2581+ g_return_if_fail (blue != NULL);
2582+
2583+ if (size != crtc->gamma_size)
2584+ return;
2585+
2586+ gamma = XRRAllocGamma (crtc->gamma_size);
2587+
2588+ copy_size = crtc->gamma_size * sizeof (unsigned short);
2589+ memcpy (gamma->red, red, copy_size);
2590+ memcpy (gamma->green, green, copy_size);
2591+ memcpy (gamma->blue, blue, copy_size);
2592+
2593+ XRRSetCrtcGamma (DISPLAY (crtc), crtc->id, gamma);
2594+ XRRFreeGamma (gamma);
2595+}
2596+
2597+gboolean
2598+gnome_rr_crtc_get_gamma (GnomeRRCrtc *crtc, int *size,
2599+ unsigned short **red, unsigned short **green,
2600+ unsigned short **blue)
2601+{
2602+ int copy_size;
2603+ unsigned short *r, *g, *b;
2604+ XRRCrtcGamma *gamma;
2605+
2606+ g_return_val_if_fail (crtc != NULL, FALSE);
2607+
2608+ gamma = XRRGetCrtcGamma (DISPLAY (crtc), crtc->id);
2609+ if (!gamma)
2610+ return FALSE;
2611+
2612+ copy_size = crtc->gamma_size * sizeof (unsigned short);
2613+
2614+ if (red) {
2615+ r = g_new0 (unsigned short, crtc->gamma_size);
2616+ memcpy (r, gamma->red, copy_size);
2617+ *red = r;
2618+ }
2619+
2620+ if (green) {
2621+ g = g_new0 (unsigned short, crtc->gamma_size);
2622+ memcpy (g, gamma->green, copy_size);
2623+ *green = g;
2624+ }
2625+
2626+ if (blue) {
2627+ b = g_new0 (unsigned short, crtc->gamma_size);
2628+ memcpy (b, gamma->blue, copy_size);
2629+ *blue = b;
2630+ }
2631+
2632+ XRRFreeGamma (gamma);
2633+
2634+ if (size)
2635+ *size = crtc->gamma_size;
2636+
2637+ return TRUE;
2638+}
2639+
2640
2641=== modified file 'debian/changelog'
2642--- debian/changelog 2015-03-31 10:59:01 +0000
2643+++ debian/changelog 2015-11-20 08:10:07 +0000
2644@@ -1,3 +1,10 @@
2645+gnome-desktop3 (3.8.4-0ubuntu3.2) trusty-proposed; urgency=medium
2646+
2647+ * Apply the similar method from the power plugin in unity-settings-daemon to
2648+ fix the lowest brightness issue. (LP: #1381625)
2649+
2650+ -- Shih-Yuan Lee (FourDollars) <sylee@canonical.com> Fri, 20 Nov 2015 15:35:36 +0800
2651+
2652 gnome-desktop3 (3.8.4-0ubuntu3.1) trusty; urgency=medium
2653
2654 * debian/patches/idletime_sanity_checks.patch: Ensure the argument to
2655
2656=== added file 'debian/patches/fix_lowest_brightness.patch'
2657--- debian/patches/fix_lowest_brightness.patch 1970-01-01 00:00:00 +0000
2658+++ debian/patches/fix_lowest_brightness.patch 2015-11-20 08:10:07 +0000
2659@@ -0,0 +1,21 @@
2660+Description: Apply the similar method to fix the lowest brightness issue. (LP: #1381625)
2661+Author: Shih-Yuan Lee (FourDollars) <sylee@canonical.com>
2662+Bug: https://bugs.launchpad.net/bugs/1381625
2663+---
2664+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
2665+Index: trusty/libgnome-desktop/gnome-rr.c
2666+===================================================================
2667+--- trusty.orig/libgnome-desktop/gnome-rr.c 2015-11-20 15:20:59.316229000 +0800
2668++++ trusty/libgnome-desktop/gnome-rr.c 2015-11-20 15:30:13.759172830 +0800
2669+@@ -1553,6 +1553,11 @@
2670+ }
2671+ output->backlight_min = info->values[0];
2672+ output->backlight_max = info->values[1];
2673++ /* If the interface has more than 99 possible values, it's
2674++ * * likely that 0 turns the backlight off so we let 1 be
2675++ * * set in that case. */
2676++ if (output->backlight_max > 99 && output->backlight_min == 0)
2677++ output->backlight_min = 1;
2678+ out:
2679+ if (info != NULL)
2680+ {
2681
2682=== modified file 'debian/patches/series'
2683--- debian/patches/series 2015-03-31 10:59:01 +0000
2684+++ debian/patches/series 2015-11-20 08:10:07 +0000
2685@@ -9,3 +9,4 @@
2686 ignore_errors_with_primary_outputs.patch
2687 backlight-property-name.patch
2688 idletime_sanity_checks.patch
2689+fix_lowest_brightness.patch
2690
2691=== modified file 'libgnome-desktop/gnome-rr.c'
2692--- libgnome-desktop/gnome-rr.c 2014-03-17 17:28:55 +0000
2693+++ libgnome-desktop/gnome-rr.c 2015-11-20 08:10:07 +0000
2694@@ -1553,6 +1553,11 @@
2695 }
2696 output->backlight_min = info->values[0];
2697 output->backlight_max = info->values[1];
2698+ /* If the interface has more than 99 possible values, it's
2699+ * * likely that 0 turns the backlight off so we let 1 be
2700+ * * set in that case. */
2701+ if (output->backlight_max > 99 && output->backlight_min == 0)
2702+ output->backlight_min = 1;
2703 out:
2704 if (info != NULL)
2705 {

Subscribers

People subscribed via source and target branches

to all changes: