Merge lp:~robert-ancell/unity-control-center/xrandr into lp:unity-control-center

Proposed by Robert Ancell on 2014-06-26
Status: Rejected
Rejected by: Robert Ancell on 2014-08-29
Proposed branch: lp:~robert-ancell/unity-control-center/xrandr
Merge into: lp:unity-control-center
Diff against target: 8940 lines (+7146/-344)
22 files modified
configure.ac (+4/-0)
debian/unity-control-center.install (+1/-0)
panels/common/Makefile.am (+29/-1)
panels/common/cc-pnp-ids.c (+341/-0)
panels/common/cc-pnp-ids.h (+58/-0)
panels/common/cc-rr-config.c (+2091/-0)
panels/common/cc-rr-config.h (+151/-0)
panels/common/cc-rr-output-info.c (+242/-0)
panels/common/cc-rr-private.h (+79/-0)
panels/common/cc-rr.c (+2622/-0)
panels/common/cc-rr.h (+222/-0)
panels/common/check_gl_texture_size.c (+86/-0)
panels/common/display-name.c (+144/-0)
panels/common/edid-parse.c (+540/-0)
panels/common/edid.h (+195/-0)
panels/display/Makefile.am (+3/-2)
panels/display/cc-display-panel.c (+232/-233)
panels/display/cc-rr-labeler.c (+19/-19)
panels/display/cc-rr-labeler.h (+3/-4)
panels/wacom/Makefile.am (+3/-2)
panels/wacom/cc-wacom-mapping-panel.c (+14/-15)
panels/wacom/gsd-wacom-device.c (+67/-68)
To merge this branch: bzr merge lp:~robert-ancell/unity-control-center/xrandr
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve on 2014-06-26
Unity Control Center development team 2014-06-26 Pending
Review via email: mp+224547@code.launchpad.net

Commit message

Move libgnome-desktop RandR code into u-c-c since newer versions remove this unstable API

To post a comment you must log in.
Robert Ancell (robert-ancell) wrote :
Sebastien Bacher (seb128) wrote :

That's quite some changes to review, but mostly a copy of existing code, not I don't think it needs a detailled code review. The changes look fine to me on principle but we should give that a proper round of testing, including multimonitor setups and screensaver/idle usecases

Tim Lunn (darkxst) wrote :

This lgtm, lots of cut+paste and a bit of renaming, don't think it will cause any issues

Unmerged revisions

12784. By Robert Ancell on 2014-06-26

install check_gl_texture_size

12783. By Robert Ancell on 2014-06-25

Fix some renaming

12782. By Robert Ancell on 2014-06-25

Move libgnome-desktop RandR code into u-s-c since newer versions remove this unstable API

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configure.ac'
2--- configure.ac 2014-02-26 20:00:59 +0000
3+++ configure.ac 2014-06-26 03:13:28 +0000
4@@ -104,6 +104,8 @@
5 CLUTTER_REQUIRED_VERSION=1.11.3
6 GOA_REQUIRED_VERSION=3.5.90
7 ACCOUNTSSERVICE_REQUIRED_VERSION=0.6.30
8+XRANDR_REQUIRED=1.3
9+XEXT_REQUIRED=1.1
10
11 COMMON_MODULES="gtk+-3.0 >= $GTK_REQUIRED_VERSION
12 glib-2.0 >= $GLIB_REQUIRED_VERSION
13@@ -115,6 +117,8 @@
14
15 PKG_CHECK_MODULES(LIBUNITY_CONTROL_CENTER, $COMMON_MODULES)
16 PKG_CHECK_MODULES(LIBLANGUAGE, $COMMON_MODULES gnome-desktop-3.0 fontconfig)
17+PKG_CHECK_MODULES(LIBRR, $COMMON_MODULES xrandr >= $XRANDR_REQUIRED xext >= $XEXT_REQUIRED)
18+PKG_CHECK_MODULES(CHECK_GL_TEXTURE_SIZE, gl x11)
19 PKG_CHECK_MODULES(LIBSHORTCUTS, $COMMON_MODULES x11)
20 PKG_CHECK_MODULES(SHELL, $COMMON_MODULES libgnome-menu-3.0 gio-unix-2.0 x11)
21 PKG_CHECK_MODULES(APPEARANCE_PANEL, $COMMON_MODULES libxml-2.0 gnome-desktop-3.0
22
23=== modified file 'debian/unity-control-center.install'
24--- debian/unity-control-center.install 2014-02-24 17:28:38 +0000
25+++ debian/unity-control-center.install 2014-06-26 03:13:28 +0000
26@@ -1,5 +1,6 @@
27 etc/xdg/menus
28 usr/bin
29+usr/lib/unity-control-center
30 usr/lib/*/unity-control-center-1/panels/*.so
31 usr/share/applications
32 usr/share/bash-completion
33
34=== modified file 'panels/common/Makefile.am'
35--- panels/common/Makefile.am 2013-11-29 06:28:37 +0000
36+++ panels/common/Makefile.am 2014-06-26 03:13:28 +0000
37@@ -1,14 +1,17 @@
38 # This is used in PANEL_CFLAGS
39 cappletname = common
40
41-noinst_LTLIBRARIES = liblanguage.la
42+noinst_LTLIBRARIES = liblanguage.la librr.la
43 noinst_PROGRAMS = list-languages
44+libexec_PROGRAMS = check_gl_texture_size
45
46 AM_CPPFLAGS = \
47 $(PANEL_CFLAGS) \
48 $(LIBLANGUAGE_CFLAGS) \
49 -DDATADIR=\""$(datadir)"\" \
50+ -DLIBEXECDIR=\""$(libexecdir)\"" \
51 -DUIDIR=\""$(pkgdatadir)/ui"\" \
52+ -DPNP_IDS=\""$(datadir)/hwdata/pnp.ids"\" \
53 -DLIBLOCALEDIR=\""$(prefix)/lib/locale"\" \
54 -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
55 -DUM_PIXMAP_DIR=\""$(pkgdatadir)/pixmaps"\"
56@@ -32,6 +35,31 @@
57 list_languages_LDADD = liblanguage.la
58 list_languages_CFLAGS = $(LIBLANGUAGE_CFLAGS)
59
60+librr_la_SOURCES = \
61+ cc-pnp-ids.c \
62+ cc-pnp-ids.h \
63+ cc-rr.c \
64+ cc-rr.h \
65+ cc-rr-config.c \
66+ cc-rr-config.h \
67+ cc-rr-output-info.c \
68+ cc-rr-private.h \
69+ display-name.c \
70+ edid-parse.c \
71+ edid.h
72+
73+librr_la_LIBADD = \
74+ $(PANEL_LIBS) \
75+ $(LIBRR_LIBS)
76+
77+librr_la_LDFLAGS = $(PANEL_LDFLAGS)
78+
79+check_gl_texture_size_CPPFLAGS = \
80+ $(CHECK_GL_TEXTURE_SIZE_CFLAGS)
81+
82+check_gl_texture_size_LDADD = \
83+ $(CHECK_GL_TEXTURE_SIZE_LIBS)
84+
85 uidir = $(pkgdatadir)/ui
86
87 dist_ui_DATA = \
88
89=== added file 'panels/common/cc-pnp-ids.c'
90--- panels/common/cc-pnp-ids.c 1970-01-01 00:00:00 +0000
91+++ panels/common/cc-pnp-ids.c 2014-06-26 03:13:28 +0000
92@@ -0,0 +1,341 @@
93+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
94+ *
95+ * Copyright (C) 2009-2011 Richard Hughes <richard@hughsie.com>
96+ *
97+ * This program is free software; you can redistribute it and/or modify
98+ * it under the terms of the GNU General Public License as published by
99+ * the Free Software Foundation; either version 2 of the License, or
100+ * (at your option) any later version.
101+ *
102+ * This program is distributed in the hope that it will be useful,
103+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
104+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
105+ * GNU General Public License for more details.
106+ *
107+ * You should have received a copy of the GNU General Public License
108+ * along with this program; if not, write to the Free Software
109+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
110+ */
111+
112+#include "config.h"
113+
114+#include <glib-object.h>
115+
116+#include <cc-pnp-ids.h>
117+
118+static void cc_pnp_ids_finalize (GObject *object);
119+
120+#define CC_PNP_IDS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_PNP_IDS, CCPnpIdsPrivate))
121+
122+struct _CCPnpIdsPrivate
123+{
124+ gchar *table_data;
125+ GHashTable *pnp_table;
126+};
127+
128+static gpointer cc_pnp_ids_object = NULL;
129+
130+G_DEFINE_TYPE (CCPnpIds, cc_pnp_ids, G_TYPE_OBJECT)
131+
132+typedef struct Vendor Vendor;
133+struct Vendor
134+{
135+ const char vendor_id[4];
136+ const char vendor_name[28];
137+};
138+
139+/* This list of vendor codes derived from lshw
140+ *
141+ * http://ezix.org/project/wiki/HardwareLiSter
142+ *
143+ * Note: we now prefer to use data coming from hwdata (and shipped with
144+ * gnome-desktop). See
145+ * http://git.fedorahosted.org/git/?p=hwdata.git;a=blob_plain;f=pnp.ids;hb=HEAD
146+ * All contributions to the list of vendors should go there.
147+ */
148+static const struct Vendor vendors[] =
149+{
150+ { "AIC", "AG Neovo" },
151+ { "ACR", "Acer" },
152+ { "DEL", "DELL" },
153+ { "SAM", "SAMSUNG" },
154+ { "SNY", "SONY" },
155+ { "SEC", "Epson" },
156+ { "WAC", "Wacom" },
157+ { "NEC", "NEC" },
158+ { "CMO", "CMO" }, /* Chi Mei */
159+ { "BNQ", "BenQ" },
160+
161+ { "ABP", "Advansys" },
162+ { "ACC", "Accton" },
163+ { "ACE", "Accton" },
164+ { "ADP", "Adaptec" },
165+ { "ADV", "AMD" },
166+ { "AIR", "AIR" },
167+ { "AMI", "AMI" },
168+ { "ASU", "ASUS" },
169+ { "ATI", "ATI" },
170+ { "ATK", "Allied Telesyn" },
171+ { "AZT", "Aztech" },
172+ { "BAN", "Banya" },
173+ { "BRI", "Boca Research" },
174+ { "BUS", "Buslogic" },
175+ { "CCI", "Cache Computers Inc." },
176+ { "CHA", "Chase" },
177+ { "CMD", "CMD Technology, Inc." },
178+ { "COG", "Cogent" },
179+ { "CPQ", "Compaq" },
180+ { "CRS", "Crescendo" },
181+ { "CSC", "Crystal" },
182+ { "CSI", "CSI" },
183+ { "CTL", "Creative Labs" },
184+ { "DBI", "Digi" },
185+ { "DEC", "Digital Equipment" },
186+ { "DBK", "Databook" },
187+ { "EGL", "Eagle Technology" },
188+ { "ELS", "ELSA" },
189+ { "ESS", "ESS" },
190+ { "FAR", "Farallon" },
191+ { "FDC", "Future Domain" },
192+ { "HWP", "Hewlett-Packard" },
193+ { "IBM", "IBM" },
194+ { "INT", "Intel" },
195+ { "ISA", "Iomega" },
196+ { "LEN", "Lenovo" },
197+ { "MDG", "Madge" },
198+ { "MDY", "Microdyne" },
199+ { "MET", "Metheus" },
200+ { "MIC", "Micronics" },
201+ { "MLX", "Mylex" },
202+ { "NVL", "Novell" },
203+ { "OLC", "Olicom" },
204+ { "PRO", "Proteon" },
205+ { "RII", "Racal" },
206+ { "RTL", "Realtek" },
207+ { "SCM", "SCM" },
208+ { "SKD", "SysKonnect" },
209+ { "SGI", "SGI" },
210+ { "SMC", "SMC" },
211+ { "SNI", "Siemens Nixdorf" },
212+ { "STL", "Stallion Technologies" },
213+ { "SUN", "Sun" },
214+ { "SUP", "SupraExpress" },
215+ { "SVE", "SVEC" },
216+ { "TCC", "Thomas-Conrad" },
217+ { "TCI", "Tulip" },
218+ { "TCM", "3Com" },
219+ { "TCO", "Thomas-Conrad" },
220+ { "TEC", "Tecmar" },
221+ { "TRU", "Truevision" },
222+ { "TOS", "Toshiba" },
223+ { "TYN", "Tyan" },
224+ { "UBI", "Ungermann-Bass" },
225+ { "USC", "UltraStor" },
226+ { "VDM", "Vadem" },
227+ { "VMI", "Vermont" },
228+ { "WDC", "Western Digital" },
229+ { "ZDS", "Zeos" },
230+
231+ /* From http://faydoc.tripod.com/structures/01/0136.htm */
232+ { "ACT", "Targa" },
233+ { "ADI", "ADI" },
234+ { "AOC", "AOC Intl" },
235+ { "API", "Acer America" },
236+ { "APP", "Apple Computer" },
237+ { "ART", "ArtMedia" },
238+ { "AST", "AST Research" },
239+ { "CPL", "Compal" },
240+ { "CTX", "Chuntex Electronic Co." },
241+ { "DPC", "Delta Electronics" },
242+ { "DWE", "Daewoo" },
243+ { "ECS", "ELITEGROUP" },
244+ { "EIZ", "EIZO" },
245+ { "FCM", "Funai" },
246+ { "GSM", "LG Electronics" },
247+ { "GWY", "Gateway 2000" },
248+ { "HEI", "Hyundai" },
249+ { "HIT", "Hitachi" },
250+ { "HSL", "Hansol" },
251+ { "HTC", "Hitachi" },
252+ { "ICL", "Fujitsu ICL" },
253+ { "IVM", "Idek Iiyama" },
254+ { "KFC", "KFC Computek" },
255+ { "LKM", "ADLAS" },
256+ { "LNK", "LINK Tech" },
257+ { "LTN", "Lite-On" },
258+ { "MAG", "MAG InnoVision" },
259+ { "MAX", "Maxdata" },
260+ { "MEI", "Panasonic" },
261+ { "MEL", "Mitsubishi" },
262+ { "MIR", "miro" },
263+ { "MTC", "MITAC" },
264+ { "NAN", "NANAO" },
265+ { "NEC", "NEC Tech" },
266+ { "NOK", "Nokia" },
267+ { "OQI", "OPTIQUEST" },
268+ { "PBN", "Packard Bell" },
269+ { "PGS", "Princeton" },
270+ { "PHL", "Philips" },
271+ { "REL", "Relisys" },
272+ { "SDI", "Samtron" },
273+ { "SMI", "Smile" },
274+ { "SPT", "Sceptre" },
275+ { "SRC", "Shamrock Technology" },
276+ { "STP", "Sceptre" },
277+ { "TAT", "Tatung" },
278+ { "TRL", "Royal Information Company" },
279+ { "TSB", "Toshiba, Inc." },
280+ { "UNM", "Unisys" },
281+ { "VSC", "ViewSonic" },
282+ { "WTC", "Wen Tech" },
283+ { "ZCM", "Zenith Data Systems" },
284+
285+ { "???", "Unknown" },
286+};
287+
288+static gboolean
289+cc_pnp_ids_load (CCPnpIds *pnp_ids, GError **error)
290+{
291+ gchar *retval = NULL;
292+ CCPnpIdsPrivate *priv = pnp_ids->priv;
293+ guint i;
294+
295+ /* load the contents */
296+ g_debug ("loading: %s", PNP_IDS);
297+ if (g_file_get_contents (PNP_IDS, &priv->table_data, NULL, error) == FALSE)
298+ return FALSE;
299+
300+ /* parse into lines */
301+ retval = priv->table_data;
302+ for (i = 0; priv->table_data[i] != '\0'; i++) {
303+
304+ /* ignore */
305+ if (priv->table_data[i] != '\n')
306+ continue;
307+
308+ /* convert newline to NULL */
309+ priv->table_data[i] = '\0';
310+
311+ /* the ID to text is a fixed offset */
312+ if (retval[0] && retval[1] && retval[2] && retval[3] == '\t' && retval[4]) {
313+ retval[3] = '\0';
314+ g_hash_table_insert (priv->pnp_table,
315+ retval,
316+ retval+4);
317+ retval = &priv->table_data[i+1];
318+ }
319+ }
320+
321+ g_debug ("Added %i items to the vendor hashtable", i);
322+
323+ return TRUE;
324+}
325+
326+static const char *
327+find_vendor (const char *pnp_id)
328+{
329+ guint i;
330+
331+ for (i = 0; i < G_N_ELEMENTS (vendors); i++) {
332+ if (g_strcmp0 (vendors[i].vendor_id, pnp_id) == 0)
333+ return vendors[i].vendor_name;
334+ }
335+
336+ return NULL;
337+}
338+
339+/**
340+ * cc_pnp_ids_get_pnp_id:
341+ * @pnp_ids: a #CCPnpIds object
342+ * @pnp_id: the PNP ID to look for
343+ *
344+ * Find the full manufacturer name for the given PNP ID.
345+ *
346+ * Returns: (transfer full): a new string representing the manufacturer name,
347+ * or %NULL when not found.
348+ */
349+gchar *
350+cc_pnp_ids_get_pnp_id (CCPnpIds *pnp_ids, const gchar *pnp_id)
351+{
352+ CCPnpIdsPrivate *priv = pnp_ids->priv;
353+ const char *found;
354+ GError *error = NULL;
355+ guint size;
356+
357+ g_return_val_if_fail (CC_IS_PNP_IDS (pnp_ids), NULL);
358+ g_return_val_if_fail (pnp_id != NULL, NULL);
359+
360+ /* if table is empty, try to load it */
361+ size = g_hash_table_size (priv->pnp_table);
362+ if (size == 0) {
363+ if (cc_pnp_ids_load (pnp_ids, &error) == FALSE) {
364+ g_warning ("Failed to load PNP ids: %s", error->message);
365+ g_error_free (error);
366+ return NULL;
367+ }
368+ }
369+
370+ /* look this up in the table */
371+ found = g_hash_table_lookup (priv->pnp_table, pnp_id);
372+ if (found == NULL) {
373+ found = find_vendor (pnp_id);
374+ if (found == NULL)
375+ return NULL;
376+ }
377+
378+ return g_strdup (found);
379+}
380+
381+static void
382+cc_pnp_ids_class_init (CCPnpIdsClass *klass)
383+{
384+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
385+ object_class->finalize = cc_pnp_ids_finalize;
386+ g_type_class_add_private (klass, sizeof (CCPnpIdsPrivate));
387+}
388+
389+static void
390+cc_pnp_ids_init (CCPnpIds *pnp_ids)
391+{
392+ pnp_ids->priv = CC_PNP_IDS_GET_PRIVATE (pnp_ids);
393+
394+ /* we don't keep malloc'd data in the hash; instead we read it
395+ * out into priv->table_data and then link to it in the hash */
396+ pnp_ids->priv->pnp_table = g_hash_table_new_full (g_str_hash,
397+ g_str_equal,
398+ NULL,
399+ NULL);
400+}
401+
402+static void
403+cc_pnp_ids_finalize (GObject *object)
404+{
405+ CCPnpIds *pnp_ids = CC_PNP_IDS (object);
406+ CCPnpIdsPrivate *priv = pnp_ids->priv;
407+
408+ g_free (priv->table_data);
409+ g_hash_table_unref (priv->pnp_table);
410+
411+ G_OBJECT_CLASS (cc_pnp_ids_parent_class)->finalize (object);
412+}
413+
414+/**
415+ * cc_pnp_ids_new:
416+ *
417+ * Returns a reference to a #CCPnpIds object, or creates
418+ * a new one if none have been created.
419+ *
420+ * Returns: (transfer full): a #CCPnpIds object.
421+ */
422+CCPnpIds *
423+cc_pnp_ids_new (void)
424+{
425+ if (cc_pnp_ids_object != NULL) {
426+ g_object_ref (cc_pnp_ids_object);
427+ } else {
428+ cc_pnp_ids_object = g_object_new (CC_TYPE_PNP_IDS, NULL);
429+ g_object_add_weak_pointer (cc_pnp_ids_object, &cc_pnp_ids_object);
430+ }
431+ return CC_PNP_IDS (cc_pnp_ids_object);
432+}
433+
434
435=== added file 'panels/common/cc-pnp-ids.h'
436--- panels/common/cc-pnp-ids.h 1970-01-01 00:00:00 +0000
437+++ panels/common/cc-pnp-ids.h 2014-06-26 03:13:28 +0000
438@@ -0,0 +1,58 @@
439+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
440+ *
441+ * Copyright (C) 2009-2010 Richard Hughes <richard@hughsie.com>
442+ *
443+ * This program is free software; you can redistribute it and/or modify
444+ * it under the terms of the GNU General Public License as published by
445+ * the Free Software Foundation; either version 2 of the License, or
446+ * (at your option) any later version.
447+ *
448+ * This program is distributed in the hope that it will be useful,
449+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
450+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
451+ * GNU General Public License for more details.
452+ *
453+ * You should have received a copy of the GNU General Public License
454+ * along with this program; if not, write to the Free Software
455+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
456+ */
457+
458+#ifndef __CC_PNP_IDS_H
459+#define __CC_PNP_IDS_H
460+
461+#include <glib-object.h>
462+
463+G_BEGIN_DECLS
464+
465+#define CC_TYPE_PNP_IDS (cc_pnp_ids_get_type ())
466+#define CC_PNP_IDS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CC_TYPE_PNP_IDS, CCPnpIds))
467+#define CC_PNP_IDS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CC_TYPE_PNP_IDS, CCPnpIdsClass))
468+#define CC_IS_PNP_IDS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CC_TYPE_PNP_IDS))
469+#define CC_IS_PNP_IDS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CC_TYPE_PNP_IDS))
470+#define CC_PNP_IDS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CC_TYPE_PNP_IDS, CCPnpIdsClass))
471+#define CC_PNP_IDS_ERROR (cc_pnp_ids_error_quark ())
472+
473+typedef struct _CCPnpIdsPrivate CCPnpIdsPrivate;
474+typedef struct _CCPnpIds CCPnpIds;
475+typedef struct _CCPnpIdsClass CCPnpIdsClass;
476+
477+struct _CCPnpIds
478+{
479+ GObject parent;
480+ CCPnpIdsPrivate *priv;
481+};
482+
483+struct _CCPnpIdsClass
484+{
485+ GObjectClass parent_class;
486+};
487+
488+GType cc_pnp_ids_get_type (void);
489+CCPnpIds *cc_pnp_ids_new (void);
490+gchar *cc_pnp_ids_get_pnp_id (CCPnpIds *pnp_ids,
491+ const gchar *pnp_id);
492+
493+G_END_DECLS
494+
495+#endif /* __CC_PNP_IDS_H */
496+
497
498=== added file 'panels/common/cc-rr-config.c'
499--- panels/common/cc-rr-config.c 1970-01-01 00:00:00 +0000
500+++ panels/common/cc-rr-config.c 2014-06-26 03:13:28 +0000
501@@ -0,0 +1,2091 @@
502+/* gnome-rr-config.c
503+ * -*- c-basic-offset: 4 -*-
504+ *
505+ * Copyright 2007, 2008, Red Hat, Inc.
506+ * Copyright 2010 Giovanni Campagna
507+ *
508+ * This file is part of the Gnome Library.
509+ *
510+ * The Gnome Library is free software; you can redistribute it and/or
511+ * modify it under the terms of the GNU Library General Public License as
512+ * published by the Free Software Foundation; either version 2 of the
513+ * License, or (at your option) any later version.
514+ *
515+ * The Gnome Library is distributed in the hope that it will be useful,
516+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
517+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
518+ * Library General Public License for more details.
519+ *
520+ * You should have received a copy of the GNU Library General Public
521+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
522+ * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
523+ * Boston, MA 02110-1301, USA.
524+ *
525+ * Author: Soren Sandmann <sandmann@redhat.com>
526+ */
527+
528+#include <config.h>
529+#include <glib/gi18n-lib.h>
530+#include <stdlib.h>
531+#include <string.h>
532+#include <glib.h>
533+#include <glib/gstdio.h>
534+
535+#include <X11/Xlib.h>
536+#include <gdk/gdkx.h>
537+
538+#include <unistd.h>
539+#include <sys/wait.h>
540+#include <signal.h>
541+
542+#include "cc-rr-config.h"
543+
544+#include "edid.h"
545+#include "cc-rr-private.h"
546+
547+#define CONFIG_INTENDED_BASENAME "monitors.xml"
548+#define CONFIG_BACKUP_BASENAME "monitors.xml.backup"
549+
550+/* Look for DPI_FALLBACK in:
551+ * http://git.gnome.org/browse/gnome-settings-daemon/tree/plugins/xsettings/gsd-xsettings-manager.c
552+ * for the reasoning */
553+#define DPI_FALLBACK 96.0
554+
555+/* In version 0 of the config file format, we had several <configuration>
556+ * toplevel elements and no explicit version number. So, the filed looked
557+ * like
558+ *
559+ * <configuration>
560+ * ...
561+ * </configuration>
562+ * <configuration>
563+ * ...
564+ * </configuration>
565+ *
566+ * Since version 1 of the config file, the file has a toplevel <monitors>
567+ * element to group all the configurations. That element has a "version"
568+ * attribute which is an integer. So, the file looks like this:
569+ *
570+ * <monitors version="1">
571+ * <configuration>
572+ * ...
573+ * </configuration>
574+ * <configuration>
575+ * ...
576+ * </configuration>
577+ * </monitors>
578+ */
579+
580+/* A helper wrapper around the GMarkup parser stuff */
581+static gboolean parse_file_gmarkup (const gchar *file,
582+ const GMarkupParser *parser,
583+ gpointer data,
584+ GError **err);
585+
586+typedef struct CrtcAssignment CrtcAssignment;
587+
588+static gboolean crtc_assignment_apply (CrtcAssignment *assign,
589+ guint32 timestamp,
590+ GError **error);
591+static CrtcAssignment *crtc_assignment_new (CCRRScreen *screen,
592+ CCRROutputInfo **outputs,
593+ GError **error);
594+static void crtc_assignment_free (CrtcAssignment *assign);
595+
596+enum {
597+ PROP_0,
598+ PROP_SCREEN,
599+ PROP_LAST
600+};
601+
602+G_DEFINE_TYPE (CCRRConfig, cc_rr_config, G_TYPE_OBJECT)
603+
604+typedef struct Parser Parser;
605+
606+/* Parser for monitor configurations */
607+struct Parser
608+{
609+ int config_file_version;
610+ CCRROutputInfo * output;
611+ CCRRConfig * configuration;
612+ GPtrArray * outputs;
613+ GPtrArray * configurations;
614+ GQueue * stack;
615+};
616+
617+static int
618+parse_int (const char *text)
619+{
620+ return strtol (text, NULL, 0);
621+}
622+
623+static guint
624+parse_uint (const char *text)
625+{
626+ return strtoul (text, NULL, 0);
627+}
628+
629+static gboolean
630+stack_is (Parser *parser,
631+ const char *s1,
632+ ...)
633+{
634+ GList *stack = NULL;
635+ const char *s;
636+ GList *l1, *l2;
637+ va_list args;
638+
639+ stack = g_list_prepend (stack, (gpointer)s1);
640+
641+ va_start (args, s1);
642+
643+ s = va_arg (args, const char *);
644+ while (s)
645+ {
646+ stack = g_list_prepend (stack, (gpointer)s);
647+ s = va_arg (args, const char *);
648+ }
649+
650+ l1 = stack;
651+ l2 = parser->stack->head;
652+
653+ while (l1 && l2)
654+ {
655+ if (strcmp (l1->data, l2->data) != 0)
656+ {
657+ g_list_free (stack);
658+ return FALSE;
659+ }
660+
661+ l1 = l1->next;
662+ l2 = l2->next;
663+ }
664+
665+ g_list_free (stack);
666+
667+ return (!l1 && !l2);
668+}
669+
670+static void
671+handle_start_element (GMarkupParseContext *context,
672+ const gchar *name,
673+ const gchar **attr_names,
674+ const gchar **attr_values,
675+ gpointer user_data,
676+ GError **err)
677+{
678+ Parser *parser = user_data;
679+
680+ if (strcmp (name, "output") == 0)
681+ {
682+ int i;
683+ g_assert (parser->output == NULL);
684+
685+ parser->output = g_object_new (CC_TYPE_RR_OUTPUT_INFO, NULL);
686+ parser->output->priv->rotation = 0;
687+
688+ for (i = 0; attr_names[i] != NULL; ++i)
689+ {
690+ if (strcmp (attr_names[i], "name") == 0)
691+ {
692+ parser->output->priv->name = g_strdup (attr_values[i]);
693+ break;
694+ }
695+ }
696+
697+ if (!parser->output->priv->name)
698+ {
699+ /* This really shouldn't happen, but it's better to make
700+ * something up than to crash later.
701+ */
702+ g_warning ("Malformed monitor configuration file");
703+
704+ parser->output->priv->name = g_strdup ("default");
705+ }
706+ parser->output->priv->connected = FALSE;
707+ parser->output->priv->on = FALSE;
708+ parser->output->priv->primary = FALSE;
709+ }
710+ else if (strcmp (name, "configuration") == 0)
711+ {
712+ g_assert (parser->configuration == NULL);
713+
714+ parser->configuration = g_object_new (CC_TYPE_RR_CONFIG, NULL);
715+ parser->configuration->priv->clone = FALSE;
716+ parser->configuration->priv->outputs = NULL;
717+ }
718+ else if (strcmp (name, "monitors") == 0)
719+ {
720+ int i;
721+
722+ for (i = 0; attr_names[i] != NULL; i++)
723+ {
724+ if (strcmp (attr_names[i], "version") == 0)
725+ {
726+ parser->config_file_version = parse_int (attr_values[i]);
727+ break;
728+ }
729+ }
730+ }
731+
732+ g_queue_push_tail (parser->stack, g_strdup (name));
733+}
734+
735+static void
736+handle_end_element (GMarkupParseContext *context,
737+ const gchar *name,
738+ gpointer user_data,
739+ GError **err)
740+{
741+ Parser *parser = user_data;
742+
743+ if (strcmp (name, "output") == 0)
744+ {
745+ /* If no rotation properties were set, just use CC_RR_ROTATION_0 */
746+ if (parser->output->priv->rotation == 0)
747+ parser->output->priv->rotation = CC_RR_ROTATION_0;
748+
749+ g_ptr_array_add (parser->outputs, parser->output);
750+
751+ parser->output = NULL;
752+ }
753+ else if (strcmp (name, "configuration") == 0)
754+ {
755+ g_ptr_array_add (parser->outputs, NULL);
756+ parser->configuration->priv->outputs =
757+ (CCRROutputInfo **)g_ptr_array_free (parser->outputs, FALSE);
758+ parser->outputs = g_ptr_array_new ();
759+ g_ptr_array_add (parser->configurations, parser->configuration);
760+ parser->configuration = NULL;
761+ }
762+
763+ g_free (g_queue_pop_tail (parser->stack));
764+}
765+
766+#define TOPLEVEL_ELEMENT (parser->config_file_version > 0 ? "monitors" : NULL)
767+
768+static void
769+handle_text (GMarkupParseContext *context,
770+ const gchar *text,
771+ gsize text_len,
772+ gpointer user_data,
773+ GError **err)
774+{
775+ Parser *parser = user_data;
776+
777+ if (stack_is (parser, "vendor", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
778+ {
779+ parser->output->priv->connected = TRUE;
780+
781+ strncpy ((gchar*) parser->output->priv->vendor, text, 3);
782+ parser->output->priv->vendor[3] = 0;
783+ }
784+ else if (stack_is (parser, "clone", "configuration", TOPLEVEL_ELEMENT, NULL))
785+ {
786+ if (strcmp (text, "yes") == 0)
787+ parser->configuration->priv->clone = TRUE;
788+ }
789+ else if (stack_is (parser, "product", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
790+ {
791+ parser->output->priv->connected = TRUE;
792+
793+ parser->output->priv->product = parse_int (text);
794+ }
795+ else if (stack_is (parser, "serial", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
796+ {
797+ parser->output->priv->connected = TRUE;
798+
799+ parser->output->priv->serial = parse_uint (text);
800+ }
801+ else if (stack_is (parser, "width", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
802+ {
803+ parser->output->priv->on = TRUE;
804+
805+ parser->output->priv->width = parse_int (text);
806+ }
807+ else if (stack_is (parser, "x", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
808+ {
809+ parser->output->priv->on = TRUE;
810+
811+ parser->output->priv->x = parse_int (text);
812+ }
813+ else if (stack_is (parser, "y", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
814+ {
815+ parser->output->priv->on = TRUE;
816+
817+ parser->output->priv->y = parse_int (text);
818+ }
819+ else if (stack_is (parser, "height", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
820+ {
821+ parser->output->priv->on = TRUE;
822+
823+ parser->output->priv->height = parse_int (text);
824+ }
825+ else if (stack_is (parser, "rate", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
826+ {
827+ parser->output->priv->on = TRUE;
828+
829+ parser->output->priv->rate = parse_int (text);
830+ }
831+ else if (stack_is (parser, "rotation", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
832+ {
833+ if (strcmp (text, "normal") == 0)
834+ {
835+ parser->output->priv->rotation |= CC_RR_ROTATION_0;
836+ }
837+ else if (strcmp (text, "left") == 0)
838+ {
839+ parser->output->priv->rotation |= CC_RR_ROTATION_90;
840+ }
841+ else if (strcmp (text, "upside_down") == 0)
842+ {
843+ parser->output->priv->rotation |= CC_RR_ROTATION_180;
844+ }
845+ else if (strcmp (text, "right") == 0)
846+ {
847+ parser->output->priv->rotation |= CC_RR_ROTATION_270;
848+ }
849+ }
850+ else if (stack_is (parser, "reflect_x", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
851+ {
852+ if (strcmp (text, "yes") == 0)
853+ {
854+ parser->output->priv->rotation |= CC_RR_REFLECT_X;
855+ }
856+ }
857+ else if (stack_is (parser, "reflect_y", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
858+ {
859+ if (strcmp (text, "yes") == 0)
860+ {
861+ parser->output->priv->rotation |= CC_RR_REFLECT_Y;
862+ }
863+ }
864+ else if (stack_is (parser, "primary", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
865+ {
866+ if (strcmp (text, "yes") == 0)
867+ {
868+ parser->output->priv->primary = TRUE;
869+ }
870+ }
871+ else
872+ {
873+ /* Ignore other properties so we can expand the format in the future */
874+ }
875+}
876+
877+static void
878+parser_free (Parser *parser)
879+{
880+ int i;
881+ GList *list;
882+
883+ g_assert (parser != NULL);
884+
885+ if (parser->output)
886+ g_object_unref (parser->output);
887+
888+ if (parser->configuration)
889+ g_object_unref (parser->configuration);
890+
891+ for (i = 0; i < parser->outputs->len; ++i)
892+ {
893+ CCRROutputInfo *output = parser->outputs->pdata[i];
894+
895+ g_object_unref (output);
896+ }
897+
898+ g_ptr_array_free (parser->outputs, TRUE);
899+
900+ for (i = 0; i < parser->configurations->len; ++i)
901+ {
902+ CCRRConfig *config = parser->configurations->pdata[i];
903+
904+ g_object_unref (config);
905+ }
906+
907+ g_ptr_array_free (parser->configurations, TRUE);
908+
909+ for (list = parser->stack->head; list; list = list->next)
910+ g_free (list->data);
911+ g_queue_free (parser->stack);
912+
913+ g_free (parser);
914+}
915+
916+static CCRRConfig **
917+configurations_read_from_file (const gchar *filename, GError **error)
918+{
919+ Parser *parser = g_new0 (Parser, 1);
920+ CCRRConfig **result;
921+ GMarkupParser callbacks = {
922+ handle_start_element,
923+ handle_end_element,
924+ handle_text,
925+ NULL, /* passthrough */
926+ NULL, /* error */
927+ };
928+
929+ parser->config_file_version = 0;
930+ parser->configurations = g_ptr_array_new ();
931+ parser->outputs = g_ptr_array_new ();
932+ parser->stack = g_queue_new ();
933+
934+ if (!parse_file_gmarkup (filename, &callbacks, parser, error))
935+ {
936+ result = NULL;
937+
938+ g_assert (parser->outputs);
939+ goto out;
940+ }
941+
942+ g_assert (parser->outputs);
943+
944+ g_ptr_array_add (parser->configurations, NULL);
945+ result = (CCRRConfig **)g_ptr_array_free (parser->configurations, FALSE);
946+ parser->configurations = g_ptr_array_new ();
947+
948+ g_assert (parser->outputs);
949+out:
950+ parser_free (parser);
951+
952+ return result;
953+}
954+
955+static void
956+cc_rr_config_init (CCRRConfig *self)
957+{
958+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CC_TYPE_RR_CONFIG, CCRRConfigPrivate);
959+
960+ self->priv->clone = FALSE;
961+ self->priv->screen = NULL;
962+ self->priv->outputs = NULL;
963+}
964+
965+static void
966+cc_rr_config_set_property (GObject *gobject, guint property_id, const GValue *value, GParamSpec *property)
967+{
968+ CCRRConfig *self = CC_RR_CONFIG (gobject);
969+
970+ switch (property_id) {
971+ case PROP_SCREEN:
972+ self->priv->screen = g_value_dup_object (value);
973+ return;
974+ default:
975+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
976+ }
977+}
978+
979+static void
980+cc_rr_config_finalize (GObject *gobject)
981+{
982+ CCRRConfig *self = CC_RR_CONFIG (gobject);
983+
984+ if (self->priv->screen)
985+ g_object_unref (self->priv->screen);
986+
987+ if (self->priv->outputs) {
988+ int i;
989+
990+ for (i = 0; self->priv->outputs[i] != NULL; i++) {
991+ CCRROutputInfo *output = self->priv->outputs[i];
992+ g_object_unref (output);
993+ }
994+ g_free (self->priv->outputs);
995+ }
996+
997+ G_OBJECT_CLASS (cc_rr_config_parent_class)->finalize (gobject);
998+}
999+
1000+gboolean
1001+cc_rr_config_load_current (CCRRConfig *config, GError **error)
1002+{
1003+ GPtrArray *a;
1004+ CCRROutput **rr_outputs;
1005+ int i;
1006+ int clone_width = -1;
1007+ int clone_height = -1;
1008+ int last_x;
1009+
1010+ g_return_val_if_fail (CC_IS_RR_CONFIG (config), FALSE);
1011+
1012+ a = g_ptr_array_new ();
1013+ rr_outputs = cc_rr_screen_list_outputs (config->priv->screen);
1014+
1015+ config->priv->clone = FALSE;
1016+
1017+ for (i = 0; rr_outputs[i] != NULL; ++i)
1018+ {
1019+ CCRROutput *rr_output = rr_outputs[i];
1020+ CCRROutputInfo *output = g_object_new (CC_TYPE_RR_OUTPUT_INFO, NULL);
1021+ CCRRMode *mode = NULL;
1022+ const guint8 *edid_data = cc_rr_output_get_edid_data (rr_output, NULL);
1023+ CCRRCrtc *crtc;
1024+
1025+ output->priv->name = g_strdup (cc_rr_output_get_name (rr_output));
1026+ output->priv->connected = cc_rr_output_is_connected (rr_output);
1027+ output->priv->display_name = g_strdup (cc_rr_output_get_display_name (rr_output));
1028+
1029+ if (!output->priv->connected)
1030+ {
1031+ output->priv->x = -1;
1032+ output->priv->y = -1;
1033+ output->priv->width = -1;
1034+ output->priv->height = -1;
1035+ output->priv->rate = -1;
1036+ output->priv->rotation = CC_RR_ROTATION_0;
1037+ }
1038+ else
1039+ {
1040+ MonitorInfo *info = NULL;
1041+
1042+ if (edid_data)
1043+ info = decode_edid (edid_data);
1044+
1045+ if (info)
1046+ {
1047+ memcpy (output->priv->vendor, info->manufacturer_code,
1048+ sizeof (output->priv->vendor));
1049+
1050+ output->priv->product = info->product_code;
1051+ output->priv->serial = info->serial_number;
1052+ output->priv->aspect = info->aspect_ratio;
1053+ }
1054+ else
1055+ {
1056+ strcpy (output->priv->vendor, "???");
1057+ output->priv->product = 0;
1058+ output->priv->serial = 0;
1059+ }
1060+ g_free (info);
1061+
1062+ crtc = cc_rr_output_get_crtc (rr_output);
1063+ mode = crtc? cc_rr_crtc_get_current_mode (crtc) : NULL;
1064+
1065+ if (crtc && mode)
1066+ {
1067+ output->priv->on = TRUE;
1068+
1069+ cc_rr_crtc_get_position (crtc, &output->priv->x, &output->priv->y);
1070+ output->priv->width = cc_rr_mode_get_width (mode);
1071+ output->priv->height = cc_rr_mode_get_height (mode);
1072+ output->priv->rate = cc_rr_mode_get_freq (mode);
1073+ output->priv->rotation = cc_rr_crtc_get_current_rotation (crtc);
1074+
1075+ if (output->priv->x == 0 && output->priv->y == 0) {
1076+ if (clone_width == -1) {
1077+ clone_width = output->priv->width;
1078+ clone_height = output->priv->height;
1079+ } else if (clone_width == output->priv->width &&
1080+ clone_height == output->priv->height) {
1081+ config->priv->clone = TRUE;
1082+ }
1083+ }
1084+ }
1085+ else
1086+ {
1087+ output->priv->on = FALSE;
1088+ config->priv->clone = FALSE;
1089+ }
1090+
1091+ /* Get preferred size for the monitor */
1092+ mode = cc_rr_output_get_preferred_mode (rr_output);
1093+
1094+ if (!mode)
1095+ {
1096+ CCRRMode **modes = cc_rr_output_list_modes (rr_output);
1097+
1098+ /* FIXME: we should pick the "best" mode here, where best is
1099+ * sorted wrt
1100+ *
1101+ * - closest aspect ratio
1102+ * - mode area
1103+ * - refresh rate
1104+ * - We may want to extend randrwrap so that get_preferred
1105+ * returns that - although that could also depend on
1106+ * the crtc.
1107+ */
1108+ if (modes[0])
1109+ mode = modes[0];
1110+ }
1111+
1112+ if (mode)
1113+ {
1114+ output->priv->pref_width = cc_rr_mode_get_width (mode);
1115+ output->priv->pref_height = cc_rr_mode_get_height (mode);
1116+ }
1117+ else
1118+ {
1119+ /* Pick some random numbers. This should basically never happen */
1120+ output->priv->pref_width = 1024;
1121+ output->priv->pref_height = 768;
1122+ }
1123+ }
1124+
1125+ output->priv->primary = cc_rr_output_get_is_primary (rr_output);
1126+
1127+ g_ptr_array_add (a, output);
1128+ }
1129+
1130+ g_ptr_array_add (a, NULL);
1131+
1132+ config->priv->outputs = (CCRROutputInfo **)g_ptr_array_free (a, FALSE);
1133+
1134+ /* Walk the outputs computing the right-most edge of all
1135+ * lit-up displays
1136+ */
1137+ last_x = 0;
1138+ for (i = 0; config->priv->outputs[i] != NULL; ++i)
1139+ {
1140+ CCRROutputInfo *output = config->priv->outputs[i];
1141+
1142+ if (output->priv->on)
1143+ {
1144+ last_x = MAX (last_x, output->priv->x + output->priv->width);
1145+ }
1146+ }
1147+
1148+ /* Now position all off displays to the right of the
1149+ * on displays
1150+ */
1151+ for (i = 0; config->priv->outputs[i] != NULL; ++i)
1152+ {
1153+ CCRROutputInfo *output = config->priv->outputs[i];
1154+
1155+ if (output->priv->connected && !output->priv->on)
1156+ {
1157+ output->priv->x = last_x;
1158+ last_x = output->priv->x + output->priv->width;
1159+ }
1160+ }
1161+
1162+ g_assert (cc_rr_config_match (config, config));
1163+
1164+ return TRUE;
1165+}
1166+
1167+gboolean
1168+cc_rr_config_load_filename (CCRRConfig *result, const char *filename, GError **error)
1169+{
1170+ CCRRConfig *current;
1171+ CCRRConfig **configs;
1172+ gboolean found = FALSE;
1173+
1174+ g_return_val_if_fail (CC_IS_RR_CONFIG (result), FALSE);
1175+ g_return_val_if_fail (filename != NULL, FALSE);
1176+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1177+
1178+ current = cc_rr_config_new_current (result->priv->screen, error);
1179+
1180+ configs = configurations_read_from_file (filename, error);
1181+
1182+ if (configs)
1183+ {
1184+ int i;
1185+
1186+ for (i = 0; configs[i] != NULL; ++i)
1187+ {
1188+ if (cc_rr_config_match (configs[i], current))
1189+ {
1190+ int j;
1191+ GPtrArray *array;
1192+ result->priv->clone = configs[i]->priv->clone;
1193+
1194+ array = g_ptr_array_new ();
1195+ for (j = 0; configs[i]->priv->outputs[j] != NULL; j++) {
1196+ g_object_ref (configs[i]->priv->outputs[j]);
1197+ g_ptr_array_add (array, configs[i]->priv->outputs[j]);
1198+ }
1199+ g_ptr_array_add (array, NULL);
1200+ result->priv->outputs = (CCRROutputInfo **) g_ptr_array_free (array, FALSE);
1201+
1202+ found = TRUE;
1203+ break;
1204+ }
1205+ g_object_unref (configs[i]);
1206+ }
1207+ g_free (configs);
1208+
1209+ if (!found)
1210+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_NO_MATCHING_CONFIG,
1211+ _("none of the saved display configurations matched the active configuration"));
1212+ }
1213+
1214+ g_object_unref (current);
1215+ return found;
1216+}
1217+
1218+static void
1219+cc_rr_config_class_init (CCRRConfigClass *klass)
1220+{
1221+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1222+
1223+ g_type_class_add_private (klass, sizeof (CCRROutputInfoPrivate));
1224+
1225+ gobject_class->set_property = cc_rr_config_set_property;
1226+ gobject_class->finalize = cc_rr_config_finalize;
1227+
1228+ g_object_class_install_property (gobject_class, PROP_SCREEN,
1229+ g_param_spec_object ("screen", "Screen", "The CCRRScreen this config applies to", CC_TYPE_RR_SCREEN,
1230+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1231+}
1232+
1233+CCRRConfig *
1234+cc_rr_config_new_current (CCRRScreen *screen, GError **error)
1235+{
1236+ CCRRConfig *self = g_object_new (CC_TYPE_RR_CONFIG, "screen", screen, NULL);
1237+
1238+ if (cc_rr_config_load_current (self, error))
1239+ return self;
1240+ else
1241+ {
1242+ g_object_unref (self);
1243+ return NULL;
1244+ }
1245+}
1246+
1247+CCRRConfig *
1248+cc_rr_config_new_stored (CCRRScreen *screen, GError **error)
1249+{
1250+ CCRRConfig *self = g_object_new (CC_TYPE_RR_CONFIG, "screen", screen, NULL);
1251+ char *filename;
1252+ gboolean success;
1253+
1254+ filename = cc_rr_config_get_intended_filename ();
1255+
1256+ success = cc_rr_config_load_filename (self, filename, error);
1257+
1258+ g_free (filename);
1259+
1260+ if (success)
1261+ return self;
1262+ else
1263+ {
1264+ g_object_unref (self);
1265+ return NULL;
1266+ }
1267+}
1268+
1269+static gboolean
1270+parse_file_gmarkup (const gchar *filename,
1271+ const GMarkupParser *parser,
1272+ gpointer data,
1273+ GError **err)
1274+{
1275+ GMarkupParseContext *context = NULL;
1276+ gchar *contents = NULL;
1277+ gboolean result = TRUE;
1278+ gsize len;
1279+
1280+ if (!g_file_get_contents (filename, &contents, &len, err))
1281+ {
1282+ result = FALSE;
1283+ goto out;
1284+ }
1285+
1286+ context = g_markup_parse_context_new (parser, 0, data, NULL);
1287+
1288+ if (!g_markup_parse_context_parse (context, contents, len, err))
1289+ {
1290+ result = FALSE;
1291+ goto out;
1292+ }
1293+
1294+ if (!g_markup_parse_context_end_parse (context, err))
1295+ {
1296+ result = FALSE;
1297+ goto out;
1298+ }
1299+
1300+out:
1301+ if (contents)
1302+ g_free (contents);
1303+
1304+ if (context)
1305+ g_markup_parse_context_free (context);
1306+
1307+ return result;
1308+}
1309+
1310+static gboolean
1311+output_match (CCRROutputInfo *output1, CCRROutputInfo *output2)
1312+{
1313+ g_assert (CC_IS_RR_OUTPUT_INFO (output1));
1314+ g_assert (CC_IS_RR_OUTPUT_INFO (output2));
1315+
1316+ if (strcmp (output1->priv->name, output2->priv->name) != 0)
1317+ return FALSE;
1318+
1319+ if (strcmp (output1->priv->vendor, output2->priv->vendor) != 0)
1320+ return FALSE;
1321+
1322+ if (output1->priv->product != output2->priv->product)
1323+ return FALSE;
1324+
1325+ if (output1->priv->serial != output2->priv->serial)
1326+ return FALSE;
1327+
1328+ if (output1->priv->connected != output2->priv->connected)
1329+ return FALSE;
1330+
1331+ return TRUE;
1332+}
1333+
1334+static gboolean
1335+output_equal (CCRROutputInfo *output1, CCRROutputInfo *output2)
1336+{
1337+ g_assert (CC_IS_RR_OUTPUT_INFO (output1));
1338+ g_assert (CC_IS_RR_OUTPUT_INFO (output2));
1339+
1340+ if (!output_match (output1, output2))
1341+ return FALSE;
1342+
1343+ if (output1->priv->on != output2->priv->on)
1344+ return FALSE;
1345+
1346+ if (output1->priv->on)
1347+ {
1348+ if (output1->priv->width != output2->priv->width)
1349+ return FALSE;
1350+
1351+ if (output1->priv->height != output2->priv->height)
1352+ return FALSE;
1353+
1354+ if (output1->priv->rate != output2->priv->rate)
1355+ return FALSE;
1356+
1357+ if (output1->priv->x != output2->priv->x)
1358+ return FALSE;
1359+
1360+ if (output1->priv->y != output2->priv->y)
1361+ return FALSE;
1362+
1363+ if (output1->priv->rotation != output2->priv->rotation)
1364+ return FALSE;
1365+ }
1366+
1367+ return TRUE;
1368+}
1369+
1370+static CCRROutputInfo *
1371+find_output (CCRRConfig *config, const char *name)
1372+{
1373+ int i;
1374+
1375+ for (i = 0; config->priv->outputs[i] != NULL; ++i)
1376+ {
1377+ CCRROutputInfo *output = config->priv->outputs[i];
1378+
1379+ if (strcmp (name, output->priv->name) == 0)
1380+ return output;
1381+ }
1382+
1383+ return NULL;
1384+}
1385+
1386+/* Match means "these configurations apply to the same hardware
1387+ * setups"
1388+ */
1389+gboolean
1390+cc_rr_config_match (CCRRConfig *c1, CCRRConfig *c2)
1391+{
1392+ int i;
1393+ g_return_val_if_fail (CC_IS_RR_CONFIG (c1), FALSE);
1394+ g_return_val_if_fail (CC_IS_RR_CONFIG (c2), FALSE);
1395+
1396+ for (i = 0; c1->priv->outputs[i] != NULL; ++i)
1397+ {
1398+ CCRROutputInfo *output1 = c1->priv->outputs[i];
1399+ CCRROutputInfo *output2;
1400+
1401+ output2 = find_output (c2, output1->priv->name);
1402+ if (!output2 || !output_match (output1, output2))
1403+ return FALSE;
1404+ }
1405+
1406+ return TRUE;
1407+}
1408+
1409+/* Equal means "the configurations will result in the same
1410+ * modes being set on the outputs"
1411+ */
1412+gboolean
1413+cc_rr_config_equal (CCRRConfig *c1,
1414+ CCRRConfig *c2)
1415+{
1416+ int i;
1417+ g_return_val_if_fail (CC_IS_RR_CONFIG (c1), FALSE);
1418+ g_return_val_if_fail (CC_IS_RR_CONFIG (c2), FALSE);
1419+
1420+ for (i = 0; c1->priv->outputs[i] != NULL; ++i)
1421+ {
1422+ CCRROutputInfo *output1 = c1->priv->outputs[i];
1423+ CCRROutputInfo *output2;
1424+
1425+ output2 = find_output (c2, output1->priv->name);
1426+ if (!output2 || !output_equal (output1, output2))
1427+ return FALSE;
1428+ }
1429+
1430+ return TRUE;
1431+}
1432+
1433+static CCRROutputInfo **
1434+make_outputs (CCRRConfig *config)
1435+{
1436+ GPtrArray *outputs;
1437+ CCRROutputInfo *first_on;
1438+ int i;
1439+
1440+ outputs = g_ptr_array_new ();
1441+
1442+ first_on = NULL;
1443+
1444+ for (i = 0; config->priv->outputs[i] != NULL; ++i)
1445+ {
1446+ CCRROutputInfo *old = config->priv->outputs[i];
1447+ CCRROutputInfo *new = g_object_new (CC_TYPE_RR_OUTPUT_INFO, NULL);
1448+ *(new->priv) = *(old->priv);
1449+ if (old->priv->name)
1450+ new->priv->name = g_strdup (old->priv->name);
1451+ if (old->priv->display_name)
1452+ new->priv->display_name = g_strdup (old->priv->display_name);
1453+
1454+ if (old->priv->on && !first_on)
1455+ first_on = old;
1456+
1457+ if (config->priv->clone && new->priv->on)
1458+ {
1459+ g_assert (first_on);
1460+
1461+ new->priv->width = first_on->priv->width;
1462+ new->priv->height = first_on->priv->height;
1463+ new->priv->rotation = first_on->priv->rotation;
1464+ new->priv->x = 0;
1465+ new->priv->y = 0;
1466+ }
1467+
1468+ g_ptr_array_add (outputs, new);
1469+ }
1470+
1471+ g_ptr_array_add (outputs, NULL);
1472+
1473+ return (CCRROutputInfo **)g_ptr_array_free (outputs, FALSE);
1474+}
1475+
1476+gboolean
1477+cc_rr_config_applicable (CCRRConfig *configuration,
1478+ CCRRScreen *screen,
1479+ GError **error)
1480+{
1481+ CCRROutputInfo **outputs;
1482+ CrtcAssignment *assign;
1483+ gboolean result;
1484+ int i;
1485+
1486+ g_return_val_if_fail (CC_IS_RR_CONFIG (configuration), FALSE);
1487+ g_return_val_if_fail (CC_IS_RR_SCREEN (screen), FALSE);
1488+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1489+
1490+ outputs = make_outputs (configuration);
1491+ assign = crtc_assignment_new (screen, outputs, error);
1492+
1493+ if (assign)
1494+ {
1495+ result = TRUE;
1496+ crtc_assignment_free (assign);
1497+ }
1498+ else
1499+ {
1500+ result = FALSE;
1501+ }
1502+
1503+ for (i = 0; outputs[i] != NULL; i++) {
1504+ g_object_unref (outputs[i]);
1505+ }
1506+
1507+ return result;
1508+}
1509+
1510+/* Database management */
1511+
1512+static void
1513+ensure_config_directory (void)
1514+{
1515+ g_mkdir_with_parents (g_get_user_config_dir (), 0700);
1516+}
1517+
1518+char *
1519+cc_rr_config_get_backup_filename (void)
1520+{
1521+ ensure_config_directory ();
1522+ return g_build_filename (g_get_user_config_dir (), CONFIG_BACKUP_BASENAME, NULL);
1523+}
1524+
1525+char *
1526+cc_rr_config_get_intended_filename (void)
1527+{
1528+ ensure_config_directory ();
1529+ return g_build_filename (g_get_user_config_dir (), CONFIG_INTENDED_BASENAME, NULL);
1530+}
1531+
1532+static const char *
1533+get_rotation_name (CCRRRotation r)
1534+{
1535+ if (r & CC_RR_ROTATION_0)
1536+ return "normal";
1537+ if (r & CC_RR_ROTATION_90)
1538+ return "left";
1539+ if (r & CC_RR_ROTATION_180)
1540+ return "upside_down";
1541+ if (r & CC_RR_ROTATION_270)
1542+ return "right";
1543+
1544+ return "normal";
1545+}
1546+
1547+static const char *
1548+yes_no (int x)
1549+{
1550+ return x? "yes" : "no";
1551+}
1552+
1553+static const char *
1554+get_reflect_x (CCRRRotation r)
1555+{
1556+ return yes_no (r & CC_RR_REFLECT_X);
1557+}
1558+
1559+static const char *
1560+get_reflect_y (CCRRRotation r)
1561+{
1562+ return yes_no (r & CC_RR_REFLECT_Y);
1563+}
1564+
1565+static void
1566+emit_configuration (CCRRConfig *config,
1567+ GString *string)
1568+{
1569+ int j;
1570+
1571+ g_string_append_printf (string, " <configuration>\n");
1572+
1573+ g_string_append_printf (string, " <clone>%s</clone>\n", yes_no (config->priv->clone));
1574+
1575+ for (j = 0; config->priv->outputs[j] != NULL; ++j)
1576+ {
1577+ CCRROutputInfo *output = config->priv->outputs[j];
1578+
1579+ g_string_append_printf (
1580+ string, " <output name=\"%s\">\n", output->priv->name);
1581+
1582+ if (output->priv->connected && *output->priv->vendor != '\0')
1583+ {
1584+ g_string_append_printf (
1585+ string, " <vendor>%s</vendor>\n", output->priv->vendor);
1586+ g_string_append_printf (
1587+ string, " <product>0x%04x</product>\n", output->priv->product);
1588+ g_string_append_printf (
1589+ string, " <serial>0x%08x</serial>\n", output->priv->serial);
1590+ }
1591+
1592+ /* An unconnected output which is on does not make sense */
1593+ if (output->priv->connected && output->priv->on)
1594+ {
1595+ g_string_append_printf (
1596+ string, " <width>%d</width>\n", output->priv->width);
1597+ g_string_append_printf (
1598+ string, " <height>%d</height>\n", output->priv->height);
1599+ g_string_append_printf (
1600+ string, " <rate>%d</rate>\n", output->priv->rate);
1601+ g_string_append_printf (
1602+ string, " <x>%d</x>\n", output->priv->x);
1603+ g_string_append_printf (
1604+ string, " <y>%d</y>\n", output->priv->y);
1605+ g_string_append_printf (
1606+ string, " <rotation>%s</rotation>\n", get_rotation_name (output->priv->rotation));
1607+ g_string_append_printf (
1608+ string, " <reflect_x>%s</reflect_x>\n", get_reflect_x (output->priv->rotation));
1609+ g_string_append_printf (
1610+ string, " <reflect_y>%s</reflect_y>\n", get_reflect_y (output->priv->rotation));
1611+ g_string_append_printf (
1612+ string, " <primary>%s</primary>\n", yes_no (output->priv->primary));
1613+ }
1614+
1615+ g_string_append_printf (string, " </output>\n");
1616+ }
1617+
1618+ g_string_append_printf (string, " </configuration>\n");
1619+}
1620+
1621+void
1622+cc_rr_config_sanitize (CCRRConfig *config)
1623+{
1624+ int i;
1625+ int x_offset, y_offset;
1626+ gboolean found;
1627+
1628+ /* Offset everything by the top/left-most coordinate to
1629+ * make sure the configuration starts at (0, 0)
1630+ */
1631+ x_offset = y_offset = G_MAXINT;
1632+ for (i = 0; config->priv->outputs[i]; ++i)
1633+ {
1634+ CCRROutputInfo *output = config->priv->outputs[i];
1635+
1636+ if (output->priv->on)
1637+ {
1638+ x_offset = MIN (x_offset, output->priv->x);
1639+ y_offset = MIN (y_offset, output->priv->y);
1640+ }
1641+ }
1642+
1643+ for (i = 0; config->priv->outputs[i]; ++i)
1644+ {
1645+ CCRROutputInfo *output = config->priv->outputs[i];
1646+
1647+ if (output->priv->on)
1648+ {
1649+ output->priv->x -= x_offset;
1650+ output->priv->y -= y_offset;
1651+ }
1652+ }
1653+
1654+ /* Only one primary, please */
1655+ found = FALSE;
1656+ for (i = 0; config->priv->outputs[i]; ++i)
1657+ {
1658+ if (config->priv->outputs[i]->priv->primary)
1659+ {
1660+ if (found)
1661+ {
1662+ config->priv->outputs[i]->priv->primary = FALSE;
1663+ }
1664+ else
1665+ {
1666+ found = TRUE;
1667+ }
1668+ }
1669+ }
1670+}
1671+
1672+gboolean
1673+cc_rr_config_ensure_primary (CCRRConfig *configuration)
1674+{
1675+ int i;
1676+ CCRROutputInfo *laptop;
1677+ CCRROutputInfo *top_left;
1678+ gboolean found;
1679+ CCRRConfigPrivate *priv;
1680+
1681+ g_return_val_if_fail (CC_IS_RR_CONFIG (configuration), FALSE);
1682+
1683+ laptop = NULL;
1684+ top_left = NULL;
1685+ found = FALSE;
1686+ priv = configuration->priv;
1687+
1688+ for (i = 0; priv->outputs[i] != NULL; ++i) {
1689+ CCRROutputInfo *info = priv->outputs[i];
1690+
1691+ if (!info->priv->on) {
1692+ info->priv->primary = FALSE;
1693+ continue;
1694+ }
1695+
1696+ /* ensure only one */
1697+ if (info->priv->primary) {
1698+ if (found) {
1699+ info->priv->primary = FALSE;
1700+ } else {
1701+ found = TRUE;
1702+ }
1703+ }
1704+
1705+ if (top_left == NULL
1706+ || (info->priv->x < top_left->priv->x
1707+ && info->priv->y < top_left->priv->y)) {
1708+ top_left = info;
1709+ }
1710+ if (laptop == NULL
1711+ && _cc_rr_output_name_is_laptop (info->priv->name)) {
1712+ /* shame we can't find the connector type
1713+ as with cc_rr_output_is_laptop */
1714+ laptop = info;
1715+ }
1716+ }
1717+
1718+ if (!found) {
1719+ if (laptop != NULL) {
1720+ laptop->priv->primary = TRUE;
1721+ } else if (top_left != NULL) {
1722+ /* Note: top_left can be NULL if all outputs are off */
1723+ top_left->priv->primary = TRUE;
1724+ }
1725+ }
1726+
1727+ return !found;
1728+}
1729+
1730+gboolean
1731+cc_rr_config_save (CCRRConfig *configuration, GError **error)
1732+{
1733+ CCRRConfig **configurations;
1734+ GString *output;
1735+ int i;
1736+ gchar *intended_filename;
1737+ gchar *backup_filename;
1738+ gboolean result;
1739+
1740+ g_return_val_if_fail (CC_IS_RR_CONFIG (configuration), FALSE);
1741+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1742+
1743+ output = g_string_new ("");
1744+
1745+ backup_filename = cc_rr_config_get_backup_filename ();
1746+ intended_filename = cc_rr_config_get_intended_filename ();
1747+
1748+ configurations = configurations_read_from_file (intended_filename, NULL); /* NULL-GError */
1749+
1750+ g_string_append_printf (output, "<monitors version=\"1\">\n");
1751+
1752+ if (configurations)
1753+ {
1754+ for (i = 0; configurations[i] != NULL; ++i)
1755+ {
1756+ if (!cc_rr_config_match (configurations[i], configuration))
1757+ emit_configuration (configurations[i], output);
1758+ g_object_unref (configurations[i]);
1759+ }
1760+
1761+ g_free (configurations);
1762+ }
1763+
1764+ emit_configuration (configuration, output);
1765+
1766+ g_string_append_printf (output, "</monitors>\n");
1767+
1768+ /* backup the file first */
1769+ rename (intended_filename, backup_filename); /* no error checking because the intended file may not even exist */
1770+
1771+ result = g_file_set_contents (intended_filename, output->str, -1, error);
1772+
1773+ if (!result)
1774+ rename (backup_filename, intended_filename); /* no error checking because the backup may not even exist */
1775+
1776+ g_free (backup_filename);
1777+ g_free (intended_filename);
1778+
1779+ return result;
1780+}
1781+
1782+gboolean
1783+cc_rr_config_apply_with_time (CCRRConfig *config,
1784+ CCRRScreen *screen,
1785+ guint32 timestamp,
1786+ GError **error)
1787+{
1788+ CrtcAssignment *assignment;
1789+ CCRROutputInfo **outputs;
1790+ gboolean result = FALSE;
1791+ int i;
1792+
1793+ g_return_val_if_fail (CC_IS_RR_CONFIG (config), FALSE);
1794+ g_return_val_if_fail (CC_IS_RR_SCREEN (screen), FALSE);
1795+
1796+ outputs = make_outputs (config);
1797+
1798+ assignment = crtc_assignment_new (screen, outputs, error);
1799+
1800+ for (i = 0; outputs[i] != NULL; i++)
1801+ g_object_unref (outputs[i]);
1802+ g_free (outputs);
1803+
1804+ if (assignment)
1805+ {
1806+ if (crtc_assignment_apply (assignment, timestamp, error))
1807+ result = TRUE;
1808+
1809+ crtc_assignment_free (assignment);
1810+
1811+ gdk_flush ();
1812+ }
1813+
1814+ return result;
1815+}
1816+
1817+/* cc_rr_config_apply_from_filename_with_time:
1818+ * @screen: A #CCRRScreen
1819+ * @filename: Path of the file to look in for stored RANDR configurations.
1820+ * @timestamp: X server timestamp from the event that causes the screen configuration to change (a user's button press, for example)
1821+ * @error: Location to store error, or %NULL
1822+ *
1823+ * Loads the file in @filename and looks for suitable matching RANDR
1824+ * configurations in the file; if one is found, that configuration will be
1825+ * applied to the current set of RANDR outputs.
1826+ *
1827+ * Typically, @filename is the result of cc_rr_config_get_intended_filename() or
1828+ * cc_rr_config_get_backup_filename().
1829+ *
1830+ * Returns: TRUE if the RANDR configuration was loaded and applied from
1831+ * the specified file, or FALSE otherwise:
1832+ *
1833+ * If the file in question is loaded successfully but the configuration cannot
1834+ * be applied, the @error will have a domain of #CC_RR_ERROR. Note that an
1835+ * error code of #CC_RR_ERROR_NO_MATCHING_CONFIG is not a real error; it
1836+ * simply means that there were no stored configurations that match the current
1837+ * set of RANDR outputs.
1838+ *
1839+ * If the file in question cannot be loaded, the @error will have a domain of
1840+ * #G_FILE_ERROR. Note that an error code of G_FILE_ERROR_NOENT is not really
1841+ * an error, either; it means that there was no stored configuration file and so
1842+ * nothing is changed.
1843+ */
1844+gboolean
1845+cc_rr_config_apply_from_filename_with_time (CCRRScreen *screen, const char *filename, guint32 timestamp, GError **error)
1846+{
1847+ CCRRConfig *stored;
1848+
1849+ g_return_val_if_fail (CC_IS_RR_SCREEN (screen), FALSE);
1850+ g_return_val_if_fail (filename != NULL, FALSE);
1851+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1852+
1853+ stored = g_object_new (CC_TYPE_RR_CONFIG, "screen", screen, NULL);
1854+
1855+ if (cc_rr_config_load_filename (stored, filename, error))
1856+ {
1857+ gboolean result;
1858+
1859+ cc_rr_config_ensure_primary (stored);
1860+ result = cc_rr_config_apply_with_time (stored, screen, timestamp, error);
1861+
1862+ g_object_unref (stored);
1863+ return result;
1864+ }
1865+ else
1866+ {
1867+ g_object_unref (stored);
1868+ return FALSE;
1869+ }
1870+}
1871+
1872+/**
1873+ * cc_rr_config_get_outputs:
1874+ *
1875+ * Returns: (array zero-terminated=1) (element-type GnomeDesktop.RROutputInfo) (transfer none): the output configuration for this #CCRRConfig
1876+ */
1877+CCRROutputInfo **
1878+cc_rr_config_get_outputs (CCRRConfig *self)
1879+{
1880+ g_return_val_if_fail (CC_IS_RR_CONFIG (self), NULL);
1881+
1882+ return self->priv->outputs;
1883+}
1884+
1885+/**
1886+ * cc_rr_config_get_clone:
1887+ *
1888+ * Returns: whether at least two outputs are at (0, 0) offset and they
1889+ * have the same width/height. Those outputs are of course connected and on
1890+ * (i.e. they have a CRTC assigned).
1891+ */
1892+gboolean
1893+cc_rr_config_get_clone (CCRRConfig *self)
1894+{
1895+ g_return_val_if_fail (CC_IS_RR_CONFIG (self), FALSE);
1896+
1897+ return self->priv->clone;
1898+}
1899+
1900+void
1901+cc_rr_config_set_clone (CCRRConfig *self, gboolean clone)
1902+{
1903+ g_return_if_fail (CC_IS_RR_CONFIG (self));
1904+
1905+ self->priv->clone = clone;
1906+}
1907+
1908+/*
1909+ * CRTC assignment
1910+ */
1911+typedef struct CrtcInfo CrtcInfo;
1912+
1913+struct CrtcInfo
1914+{
1915+ CCRRMode *mode;
1916+ int x;
1917+ int y;
1918+ CCRRRotation rotation;
1919+ GPtrArray *outputs;
1920+};
1921+
1922+struct CrtcAssignment
1923+{
1924+ CCRRScreen *screen;
1925+ GHashTable *info;
1926+ CCRROutput *primary;
1927+};
1928+
1929+static gboolean
1930+can_clone (CrtcInfo *info,
1931+ CCRROutput *output)
1932+{
1933+ int i;
1934+
1935+ for (i = 0; i < info->outputs->len; ++i)
1936+ {
1937+ CCRROutput *clone = info->outputs->pdata[i];
1938+
1939+ if (!cc_rr_output_can_clone (clone, output))
1940+ return FALSE;
1941+ }
1942+
1943+ return TRUE;
1944+}
1945+
1946+static gboolean
1947+crtc_assignment_assign (CrtcAssignment *assign,
1948+ CCRRCrtc *crtc,
1949+ CCRRMode *mode,
1950+ int x,
1951+ int y,
1952+ CCRRRotation rotation,
1953+ gboolean primary,
1954+ CCRROutput *output,
1955+ GError **error)
1956+{
1957+ CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
1958+ guint32 crtc_id;
1959+ const char *output_name;
1960+
1961+ crtc_id = cc_rr_crtc_get_id (crtc);
1962+ output_name = cc_rr_output_get_name (output);
1963+
1964+ if (!cc_rr_crtc_can_drive_output (crtc, output))
1965+ {
1966+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
1967+ _("CRTC %d cannot drive output %s"), crtc_id, output_name);
1968+ return FALSE;
1969+ }
1970+
1971+ if (!cc_rr_output_supports_mode (output, mode))
1972+ {
1973+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
1974+ _("output %s does not support mode %dx%d@%dHz"),
1975+ output_name,
1976+ cc_rr_mode_get_width (mode),
1977+ cc_rr_mode_get_height (mode),
1978+ cc_rr_mode_get_freq (mode));
1979+ return FALSE;
1980+ }
1981+
1982+ if (!cc_rr_crtc_supports_rotation (crtc, rotation))
1983+ {
1984+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
1985+ _("CRTC %d does not support rotation=%s"),
1986+ crtc_id,
1987+ get_rotation_name (rotation));
1988+ return FALSE;
1989+ }
1990+
1991+ if (info)
1992+ {
1993+ if (!(info->mode == mode &&
1994+ info->x == x &&
1995+ info->y == y &&
1996+ info->rotation == rotation))
1997+ {
1998+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
1999+ _("output %s does not have the same parameters as another cloned output:\n"
2000+ "existing mode = %d, new mode = %d\n"
2001+ "existing coordinates = (%d, %d), new coordinates = (%d, %d)\n"
2002+ "existing rotation = %s, new rotation = %s"),
2003+ output_name,
2004+ cc_rr_mode_get_id (info->mode), cc_rr_mode_get_id (mode),
2005+ info->x, info->y,
2006+ x, y,
2007+ get_rotation_name (info->rotation), get_rotation_name (rotation));
2008+ return FALSE;
2009+ }
2010+
2011+ if (!can_clone (info, output))
2012+ {
2013+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
2014+ _("cannot clone to output %s"),
2015+ output_name);
2016+ return FALSE;
2017+ }
2018+
2019+ g_ptr_array_add (info->outputs, output);
2020+
2021+ if (primary && !assign->primary)
2022+ {
2023+ assign->primary = output;
2024+ }
2025+
2026+ return TRUE;
2027+ }
2028+ else
2029+ {
2030+ CrtcInfo *info = g_new0 (CrtcInfo, 1);
2031+
2032+ info->mode = mode;
2033+ info->x = x;
2034+ info->y = y;
2035+ info->rotation = rotation;
2036+ info->outputs = g_ptr_array_new ();
2037+
2038+ g_ptr_array_add (info->outputs, output);
2039+
2040+ g_hash_table_insert (assign->info, crtc, info);
2041+
2042+ if (primary && !assign->primary)
2043+ {
2044+ assign->primary = output;
2045+ }
2046+
2047+ return TRUE;
2048+ }
2049+}
2050+
2051+static void
2052+crtc_assignment_unassign (CrtcAssignment *assign,
2053+ CCRRCrtc *crtc,
2054+ CCRROutput *output)
2055+{
2056+ CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
2057+
2058+ if (info)
2059+ {
2060+ g_ptr_array_remove (info->outputs, output);
2061+
2062+ if (assign->primary == output)
2063+ {
2064+ assign->primary = NULL;
2065+ }
2066+
2067+ if (info->outputs->len == 0)
2068+ g_hash_table_remove (assign->info, crtc);
2069+ }
2070+}
2071+
2072+static void
2073+crtc_assignment_free (CrtcAssignment *assign)
2074+{
2075+ g_hash_table_destroy (assign->info);
2076+
2077+ g_free (assign);
2078+}
2079+
2080+typedef struct {
2081+ guint32 timestamp;
2082+ gboolean has_error;
2083+ GError **error;
2084+} ConfigureCrtcState;
2085+
2086+static void
2087+configure_crtc (gpointer key,
2088+ gpointer value,
2089+ gpointer data)
2090+{
2091+ CCRRCrtc *crtc = key;
2092+ CrtcInfo *info = value;
2093+ ConfigureCrtcState *state = data;
2094+
2095+ if (state->has_error)
2096+ return;
2097+
2098+ if (!cc_rr_crtc_set_config_with_time (crtc,
2099+ state->timestamp,
2100+ info->x, info->y,
2101+ info->mode,
2102+ info->rotation,
2103+ (CCRROutput **)info->outputs->pdata,
2104+ info->outputs->len,
2105+ state->error))
2106+ state->has_error = TRUE;
2107+}
2108+
2109+static gboolean
2110+mode_is_rotated (CrtcInfo *info)
2111+{
2112+ if ((info->rotation & CC_RR_ROTATION_270) ||
2113+ (info->rotation & CC_RR_ROTATION_90))
2114+ {
2115+ return TRUE;
2116+ }
2117+ return FALSE;
2118+}
2119+
2120+static gboolean
2121+crtc_is_rotated (CCRRCrtc *crtc)
2122+{
2123+ CCRRRotation r = cc_rr_crtc_get_current_rotation (crtc);
2124+
2125+ if ((r & CC_RR_ROTATION_270) ||
2126+ (r & CC_RR_ROTATION_90))
2127+ {
2128+ return TRUE;
2129+ }
2130+
2131+ return FALSE;
2132+}
2133+
2134+static void
2135+accumulate_error (GString *accumulated_error, GError *error)
2136+{
2137+ g_string_append_printf (accumulated_error, " %s\n", error->message);
2138+ g_error_free (error);
2139+}
2140+
2141+/* Check whether the given set of settings can be used
2142+ * at the same time -- ie. whether there is an assignment
2143+ * of CRTC's to outputs.
2144+ *
2145+ * Brute force - the number of objects involved is small
2146+ * enough that it doesn't matter.
2147+ */
2148+static gboolean
2149+real_assign_crtcs (CCRRScreen *screen,
2150+ CCRROutputInfo **outputs,
2151+ CrtcAssignment *assignment,
2152+ GError **error)
2153+{
2154+ CCRRCrtc **crtcs = cc_rr_screen_list_crtcs (screen);
2155+ CCRROutputInfo *output;
2156+ int i;
2157+ gboolean tried_mode;
2158+ GError *my_error;
2159+ GString *accumulated_error;
2160+ gboolean success;
2161+
2162+ output = *outputs;
2163+ if (!output)
2164+ return TRUE;
2165+
2166+ /* It is always allowed for an output to be turned off */
2167+ if (!output->priv->on)
2168+ {
2169+ return real_assign_crtcs (screen, outputs + 1, assignment, error);
2170+ }
2171+
2172+ success = FALSE;
2173+ tried_mode = FALSE;
2174+ accumulated_error = g_string_new (NULL);
2175+
2176+ for (i = 0; crtcs[i] != NULL; ++i)
2177+ {
2178+ CCRRCrtc *crtc = crtcs[i];
2179+ int crtc_id = cc_rr_crtc_get_id (crtc);
2180+ int pass;
2181+
2182+ g_string_append_printf (accumulated_error,
2183+ _("Trying modes for CRTC %d\n"),
2184+ crtc_id);
2185+
2186+ /* Make two passes, one where frequencies must match, then
2187+ * one where they don't have to
2188+ */
2189+ for (pass = 0; pass < 2; ++pass)
2190+ {
2191+ CCRROutput *cc_rr_output = cc_rr_screen_get_output_by_name (screen, output->priv->name);
2192+ CCRRMode **modes = cc_rr_output_list_modes (cc_rr_output);
2193+ int j;
2194+
2195+ for (j = 0; modes[j] != NULL; ++j)
2196+ {
2197+ CCRRMode *mode = modes[j];
2198+ int mode_width;
2199+ int mode_height;
2200+ int mode_freq;
2201+
2202+ mode_width = cc_rr_mode_get_width (mode);
2203+ mode_height = cc_rr_mode_get_height (mode);
2204+ mode_freq = cc_rr_mode_get_freq (mode);
2205+
2206+ g_string_append_printf (accumulated_error,
2207+ _("CRTC %d: trying mode %dx%d@%dHz with output at %dx%d@%dHz (pass %d)\n"),
2208+ crtc_id,
2209+ mode_width, mode_height, mode_freq,
2210+ output->priv->width, output->priv->height, output->priv->rate,
2211+ pass);
2212+
2213+ if (mode_width == output->priv->width &&
2214+ mode_height == output->priv->height &&
2215+ (pass == 1 || mode_freq == output->priv->rate))
2216+ {
2217+ tried_mode = TRUE;
2218+
2219+ my_error = NULL;
2220+ if (crtc_assignment_assign (
2221+ assignment, crtc, modes[j],
2222+ output->priv->x, output->priv->y,
2223+ output->priv->rotation,
2224+ output->priv->primary,
2225+ cc_rr_output,
2226+ &my_error))
2227+ {
2228+ my_error = NULL;
2229+ if (real_assign_crtcs (screen, outputs + 1, assignment, &my_error)) {
2230+ success = TRUE;
2231+ goto out;
2232+ } else
2233+ accumulate_error (accumulated_error, my_error);
2234+
2235+ crtc_assignment_unassign (assignment, crtc, cc_rr_output);
2236+ } else
2237+ accumulate_error (accumulated_error, my_error);
2238+ }
2239+ }
2240+ }
2241+ }
2242+
2243+out:
2244+
2245+ if (success)
2246+ g_string_free (accumulated_error, TRUE);
2247+ else {
2248+ char *str;
2249+
2250+ str = g_string_free (accumulated_error, FALSE);
2251+
2252+ if (tried_mode)
2253+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
2254+ _("could not assign CRTCs to outputs:\n%s"),
2255+ str);
2256+ else
2257+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
2258+ _("none of the selected modes were compatible with the possible modes:\n%s"),
2259+ str);
2260+
2261+ g_free (str);
2262+ }
2263+
2264+ return success;
2265+}
2266+
2267+static void
2268+crtc_info_free (CrtcInfo *info)
2269+{
2270+ g_ptr_array_free (info->outputs, TRUE);
2271+ g_free (info);
2272+}
2273+
2274+static void
2275+get_required_virtual_size (CrtcAssignment *assign, int *width, int *height)
2276+{
2277+ GList *active_crtcs = g_hash_table_get_keys (assign->info);
2278+ GList *list;
2279+ int d;
2280+
2281+ if (!width)
2282+ width = &d;
2283+ if (!height)
2284+ height = &d;
2285+
2286+ /* Compute size of the screen */
2287+ *width = *height = 1;
2288+ for (list = active_crtcs; list != NULL; list = list->next)
2289+ {
2290+ CCRRCrtc *crtc = list->data;
2291+ CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
2292+ int w, h;
2293+
2294+ w = cc_rr_mode_get_width (info->mode);
2295+ h = cc_rr_mode_get_height (info->mode);
2296+
2297+ if (mode_is_rotated (info))
2298+ {
2299+ int tmp = h;
2300+ h = w;
2301+ w = tmp;
2302+ }
2303+
2304+ *width = MAX (*width, info->x + w);
2305+ *height = MAX (*height, info->y + h);
2306+ }
2307+
2308+ g_list_free (active_crtcs);
2309+}
2310+
2311+static gboolean
2312+unity_running (void)
2313+{
2314+ const gchar *desktop_environment = g_getenv ("DESKTOP_SESSION");
2315+
2316+ return !g_strcmp0 (desktop_environment, "ubuntu");
2317+}
2318+
2319+static gint _max_texture_size_cache = -1;
2320+
2321+static gint
2322+get_max_texture_size (CCRRScreen *screen)
2323+{
2324+ if (_max_texture_size_cache != -1)
2325+ {
2326+ return _max_texture_size_cache;
2327+ } else {
2328+ /*
2329+ * Spawn a second process to check the GL texture limits
2330+ * We do this across a process boundary to ensure that crashes
2331+ * in the GL driver (which are unfortunately common) don't take
2332+ * down the app.
2333+ */
2334+ int pipe_fd[2];
2335+ pid_t canary_pid;
2336+
2337+ char * const canary_argv[] = { LIBEXECDIR "/check_gl_texture_size", NULL };
2338+ char *canary_env[2];
2339+ char display_env[80];
2340+
2341+ snprintf (display_env, sizeof (display_env), "DISPLAY=%s", DisplayString (screen->priv->xdisplay));
2342+ canary_env[0] = display_env;
2343+ canary_env[1] = NULL;
2344+
2345+
2346+ if (pipe (pipe_fd) == -1)
2347+ {
2348+ _max_texture_size_cache = 0;
2349+ return 0;
2350+ }
2351+ canary_pid = fork ();
2352+ if (canary_pid == -1)
2353+ {
2354+ _max_texture_size_cache = 0;
2355+ return 0;
2356+ }
2357+
2358+ if (canary_pid == 0)
2359+ {
2360+ close (pipe_fd[0]);
2361+ dup2 (pipe_fd[1], 1);
2362+ close (pipe_fd[1]);
2363+
2364+ execve (canary_argv[0], canary_argv, canary_env);
2365+ } else {
2366+ char buffer[10];
2367+ gint max_texture_size;
2368+ int child_status;
2369+ int num_char;
2370+ struct timespec fifty_msec = {0, 50000000};
2371+ int wait_count = 0;
2372+
2373+ close (pipe_fd[1]);
2374+
2375+ /* Empirical testing suggests this check takes < 150msec on my
2376+ * crappy Atom netbook with slow rotating HDD. A 500msec timeout
2377+ * should be generous while not being *too* long if it triggers.
2378+ *
2379+ * Do a sleep/poll dance because we're a library and there's no
2380+ * guarantee that waiting on SIGCHLD won't stomp over a client's
2381+ * set up.
2382+ */
2383+ while (waitpid (canary_pid, &child_status, WNOHANG) == 0 && wait_count < 10) {
2384+ g_debug ("Waiting for GL_MAX_TEXTURE_SIZE helper...");
2385+ nanosleep (&fifty_msec, NULL);
2386+ wait_count++;
2387+ }
2388+
2389+ if (WIFEXITED (child_status) && WEXITSTATUS (child_status) == EXIT_SUCCESS)
2390+ {
2391+ if ((num_char = read (pipe_fd[0], buffer, sizeof(buffer) - 1)) <= 0)
2392+ {
2393+ g_warning ("Failed to read GL_MAX_TEXTURE_SIZE from helper.");
2394+ max_texture_size = 0;
2395+ } else {
2396+ buffer[num_char] = '\0';
2397+ sscanf (buffer, "%u", &max_texture_size);
2398+ /*
2399+ * Sanity check the numbers. No hardware I know of has a
2400+ * GL_MAX_TEXTURE_SIZE smaller than 1024.
2401+ */
2402+ if (max_texture_size < 1024)
2403+ max_texture_size = 0;
2404+ }
2405+ } else {
2406+ if (wait_count == 10) {
2407+ g_warning ("Timed out waiting for GL_MAX_TEXTURE_SIZE helper");
2408+
2409+ /* Ensure we don't leave processes sitting around. Who knows what they're doing? */
2410+ kill (canary_pid, SIGTERM);
2411+ waitpid (canary_pid, &child_status, 0);
2412+ } else {
2413+ g_warning ("GL_MAX_TEXTURE_SIZE helper quit unexpectedly");
2414+ }
2415+ max_texture_size = 0;
2416+ }
2417+
2418+ close (pipe_fd[0]);
2419+ g_debug ("Found GL_MAX_TEXTURE_SIZE of %u", max_texture_size);
2420+ _max_texture_size_cache = max_texture_size;
2421+ return _max_texture_size_cache;
2422+ }
2423+ }
2424+}
2425+
2426+static CrtcAssignment *
2427+crtc_assignment_new (CCRRScreen *screen, CCRROutputInfo **outputs, GError **error)
2428+{
2429+ CrtcAssignment *assignment = g_new0 (CrtcAssignment, 1);
2430+
2431+ assignment->info = g_hash_table_new_full (
2432+ g_direct_hash, g_direct_equal, NULL, (GFreeFunc)crtc_info_free);
2433+
2434+ if (real_assign_crtcs (screen, outputs, assignment, error))
2435+ {
2436+ int width, height;
2437+ int min_width, max_width, min_height, max_height;
2438+ int max_texture_size;
2439+
2440+ get_required_virtual_size (assignment, &width, &height);
2441+
2442+ cc_rr_screen_get_ranges (
2443+ screen, &min_width, &max_width, &min_height, &max_height);
2444+
2445+ if (width < min_width || width > max_width ||
2446+ height < min_height || height > max_height)
2447+ {
2448+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_BOUNDS_ERROR,
2449+ /* Translators: the "requested", "minimum", and
2450+ * "maximum" words here are not keywords; please
2451+ * translate them as usual. */
2452+ _("required virtual size does not fit available size: "
2453+ "requested=(%d, %d), minimum=(%d, %d), maximum=(%d, %d)"),
2454+ width, height,
2455+ min_width, min_height,
2456+ max_width, max_height);
2457+ goto fail;
2458+ }
2459+
2460+ /* Hack:
2461+ * This should either be solved by
2462+ * (a) Allowing the compositor to veto RandR changes
2463+ * (b) Fixing the compositor
2464+ *
2465+ * Nethier of these are feasible at this point, so just fix Unity.
2466+ */
2467+
2468+ if (unity_running ())
2469+ {
2470+ max_texture_size = get_max_texture_size (screen);
2471+ if (max_texture_size > 0 && (width > max_texture_size || height > max_texture_size))
2472+ {
2473+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_BOUNDS_ERROR,
2474+ _("Requested size (%d, %d) exceeds 3D hardware limit (%d, %d).\n"
2475+ "You must either rearrange the displays so that they fit within a (%d, %d) square."),
2476+ width, height, max_texture_size, max_texture_size,
2477+ max_texture_size, max_texture_size);
2478+ goto fail;
2479+ }
2480+ }
2481+
2482+
2483+ assignment->screen = screen;
2484+
2485+ return assignment;
2486+ }
2487+
2488+fail:
2489+ crtc_assignment_free (assignment);
2490+
2491+ return NULL;
2492+}
2493+
2494+static gboolean
2495+crtc_assignment_apply (CrtcAssignment *assign, guint32 timestamp, GError **error)
2496+{
2497+ CCRRCrtc **all_crtcs = cc_rr_screen_list_crtcs (assign->screen);
2498+ int width, height;
2499+ int i;
2500+ int min_width, max_width, min_height, max_height;
2501+ int width_mm, height_mm;
2502+ gboolean success = TRUE;
2503+
2504+ /* Compute size of the screen */
2505+ get_required_virtual_size (assign, &width, &height);
2506+
2507+ cc_rr_screen_get_ranges (
2508+ assign->screen, &min_width, &max_width, &min_height, &max_height);
2509+
2510+ /* We should never get here if the dimensions don't fit in the virtual size,
2511+ * but just in case we do, fix it up.
2512+ */
2513+ width = MAX (min_width, width);
2514+ width = MIN (max_width, width);
2515+ height = MAX (min_height, height);
2516+ height = MIN (max_height, height);
2517+
2518+ /* FMQ: do we need to check the sizes instead of clamping them? */
2519+
2520+ /* Grab the server while we fiddle with the CRTCs and the screen, so that
2521+ * apps that listen for RANDR notifications will only receive the final
2522+ * status.
2523+ */
2524+
2525+ gdk_x11_display_grab (gdk_screen_get_display (assign->screen->priv->gdk_screen));
2526+
2527+ /* Turn off all crtcs that are currently displaying outside the new screen,
2528+ * or are not used in the new setup
2529+ */
2530+ for (i = 0; all_crtcs[i] != NULL; ++i)
2531+ {
2532+ CCRRCrtc *crtc = all_crtcs[i];
2533+ CCRRMode *mode = cc_rr_crtc_get_current_mode (crtc);
2534+ int x, y;
2535+
2536+ if (mode)
2537+ {
2538+ int w, h;
2539+ cc_rr_crtc_get_position (crtc, &x, &y);
2540+
2541+ w = cc_rr_mode_get_width (mode);
2542+ h = cc_rr_mode_get_height (mode);
2543+
2544+ if (crtc_is_rotated (crtc))
2545+ {
2546+ int tmp = h;
2547+ h = w;
2548+ w = tmp;
2549+ }
2550+
2551+ if (x + w > width || y + h > height || !g_hash_table_lookup (assign->info, crtc))
2552+ {
2553+ if (!cc_rr_crtc_set_config_with_time (crtc, timestamp, 0, 0, NULL, CC_RR_ROTATION_0, NULL, 0, error))
2554+ {
2555+ success = FALSE;
2556+ break;
2557+ }
2558+
2559+ }
2560+ }
2561+ }
2562+
2563+ /* The 'physical size' of an X screen is meaningless if that screen
2564+ * can consist of many monitors. So just pick a size that make the
2565+ * dpi 96.
2566+ *
2567+ * Firefox and Evince apparently believe what X tells them.
2568+ */
2569+ width_mm = (width / DPI_FALLBACK) * 25.4 + 0.5;
2570+ height_mm = (height / DPI_FALLBACK) * 25.4 + 0.5;
2571+
2572+ if (success)
2573+ {
2574+ ConfigureCrtcState state;
2575+
2576+ cc_rr_screen_set_size (assign->screen, width, height, width_mm, height_mm);
2577+
2578+ state.timestamp = timestamp;
2579+ state.has_error = FALSE;
2580+ state.error = error;
2581+
2582+ g_hash_table_foreach (assign->info, configure_crtc, &state);
2583+
2584+ success = !state.has_error;
2585+ }
2586+
2587+ cc_rr_screen_set_primary_output (assign->screen, assign->primary);
2588+
2589+ gdk_x11_display_ungrab (gdk_screen_get_display (assign->screen->priv->gdk_screen));
2590+
2591+ return success;
2592+}
2593
2594=== added file 'panels/common/cc-rr-config.h'
2595--- panels/common/cc-rr-config.h 1970-01-01 00:00:00 +0000
2596+++ panels/common/cc-rr-config.h 2014-06-26 03:13:28 +0000
2597@@ -0,0 +1,151 @@
2598+/* gnome-rr-config.h
2599+ * -*- c-basic-offset: 4 -*-
2600+ *
2601+ * Copyright 2007, 2008, Red Hat, Inc.
2602+ * Copyright 2010 Giovanni Campagna
2603+ *
2604+ * This file is part of the Gnome Library.
2605+ *
2606+ * The Gnome Library is free software; you can redistribute it and/or
2607+ * modify it under the terms of the GNU Library General Public License as
2608+ * published by the Free Software Foundation; either version 2 of the
2609+ * License, or (at your option) any later version.
2610+ *
2611+ * The Gnome Library is distributed in the hope that it will be useful,
2612+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2613+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2614+ * Library General Public License for more details.
2615+ *
2616+ * You should have received a copy of the GNU Library General Public
2617+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
2618+ * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
2619+ * Boston, MA 02110-1301, USA.
2620+ *
2621+ * Author: Soren Sandmann <sandmann@redhat.com>
2622+ */
2623+#ifndef CC_RR_CONFIG_H
2624+#define CC_RR_CONFIG_H
2625+
2626+#include <glib.h>
2627+#include <glib-object.h>
2628+#include "cc-rr.h"
2629+
2630+typedef struct _CCRROutputInfo CCRROutputInfo;
2631+typedef struct _CCRROutputInfoClass CCRROutputInfoClass;
2632+typedef struct _CCRROutputInfoPrivate CCRROutputInfoPrivate;
2633+
2634+struct _CCRROutputInfo
2635+{
2636+ GObject parent;
2637+
2638+ /*< private >*/
2639+ CCRROutputInfoPrivate *priv;
2640+};
2641+
2642+struct _CCRROutputInfoClass
2643+{
2644+ GObjectClass parent_class;
2645+};
2646+
2647+#define CC_TYPE_RR_OUTPUT_INFO (cc_rr_output_info_get_type())
2648+#define CC_RR_OUTPUT_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TYPE_RR_OUTPUT_INFO, CCRROutputInfo))
2649+#define CC_IS_RR_OUTPUT_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TYPE_RR_OUTPUT_INFO))
2650+#define CC_RR_OUTPUT_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CC_TYPE_RR_OUTPUT_INFO, CCRROutputInfoClass))
2651+#define CC_IS_RR_OUTPUT_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CC_TYPE_RR_OUTPUT_INFO))
2652+#define CC_RR_OUTPUT_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CC_TYPE_RR_OUTPUT_INFO, CCRROutputInfoClass))
2653+
2654+GType cc_rr_output_info_get_type (void);
2655+
2656+char *cc_rr_output_info_get_name (CCRROutputInfo *self);
2657+
2658+gboolean cc_rr_output_info_is_active (CCRROutputInfo *self);
2659+void cc_rr_output_info_set_active (CCRROutputInfo *self, gboolean active);
2660+
2661+void cc_rr_output_info_get_geometry (CCRROutputInfo *self, int *x, int *y, int *width, int *height);
2662+void cc_rr_output_info_set_geometry (CCRROutputInfo *self, int x, int y, int width, int height);
2663+
2664+int cc_rr_output_info_get_refresh_rate (CCRROutputInfo *self);
2665+void cc_rr_output_info_set_refresh_rate (CCRROutputInfo *self, int rate);
2666+
2667+CCRRRotation cc_rr_output_info_get_rotation (CCRROutputInfo *self);
2668+void cc_rr_output_info_set_rotation (CCRROutputInfo *self, CCRRRotation rotation);
2669+
2670+gboolean cc_rr_output_info_is_connected (CCRROutputInfo *self);
2671+void cc_rr_output_info_get_vendor (CCRROutputInfo *self, gchar* vendor);
2672+guint cc_rr_output_info_get_product (CCRROutputInfo *self);
2673+guint cc_rr_output_info_get_serial (CCRROutputInfo *self);
2674+double cc_rr_output_info_get_aspect_ratio (CCRROutputInfo *self);
2675+char *cc_rr_output_info_get_display_name (CCRROutputInfo *self);
2676+
2677+gboolean cc_rr_output_info_get_primary (CCRROutputInfo *self);
2678+void cc_rr_output_info_set_primary (CCRROutputInfo *self, gboolean primary);
2679+
2680+int cc_rr_output_info_get_preferred_width (CCRROutputInfo *self);
2681+int cc_rr_output_info_get_preferred_height (CCRROutputInfo *self);
2682+
2683+typedef struct _CCRRConfig CCRRConfig;
2684+typedef struct _CCRRConfigClass CCRRConfigClass;
2685+typedef struct _CCRRConfigPrivate CCRRConfigPrivate;
2686+
2687+struct _CCRRConfig
2688+{
2689+ GObject parent;
2690+
2691+ /*< private >*/
2692+ CCRRConfigPrivate *priv;
2693+};
2694+
2695+struct _CCRRConfigClass
2696+{
2697+ GObjectClass parent_class;
2698+};
2699+
2700+#define CC_TYPE_RR_CONFIG (cc_rr_config_get_type())
2701+#define CC_RR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TYPE_RR_CONFIG, CCRRConfig))
2702+#define CC_IS_RR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TYPE_RR_CONFIG))
2703+#define CC_RR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CC_TYPE_RR_CONFIG, CCRRConfigClass))
2704+#define CC_IS_RR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CC_TYPE_RR_CONFIG))
2705+#define CC_RR_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CC_TYPE_RR_CONFIG, CCRRConfigClass))
2706+
2707+GType cc_rr_config_get_type (void);
2708+
2709+CCRRConfig *cc_rr_config_new_current (CCRRScreen *screen,
2710+ GError **error);
2711+CCRRConfig *cc_rr_config_new_stored (CCRRScreen *screen,
2712+ GError **error);
2713+gboolean cc_rr_config_load_current (CCRRConfig *self,
2714+ GError **error);
2715+gboolean cc_rr_config_load_filename (CCRRConfig *self,
2716+ const gchar *filename,
2717+ GError **error);
2718+gboolean cc_rr_config_match (CCRRConfig *config1,
2719+ CCRRConfig *config2);
2720+gboolean cc_rr_config_equal (CCRRConfig *config1,
2721+ CCRRConfig *config2);
2722+gboolean cc_rr_config_save (CCRRConfig *configuration,
2723+ GError **error);
2724+void cc_rr_config_sanitize (CCRRConfig *configuration);
2725+gboolean cc_rr_config_ensure_primary (CCRRConfig *configuration);
2726+
2727+gboolean cc_rr_config_apply_with_time (CCRRConfig *configuration,
2728+ CCRRScreen *screen,
2729+ guint32 timestamp,
2730+ GError **error);
2731+
2732+gboolean cc_rr_config_apply_from_filename_with_time (CCRRScreen *screen,
2733+ const char *filename,
2734+ guint32 timestamp,
2735+ GError **error);
2736+
2737+gboolean cc_rr_config_applicable (CCRRConfig *configuration,
2738+ CCRRScreen *screen,
2739+ GError **error);
2740+
2741+gboolean cc_rr_config_get_clone (CCRRConfig *configuration);
2742+void cc_rr_config_set_clone (CCRRConfig *configuration, gboolean clone);
2743+CCRROutputInfo **cc_rr_config_get_outputs (CCRRConfig *configuration);
2744+
2745+char *cc_rr_config_get_backup_filename (void);
2746+char *cc_rr_config_get_intended_filename (void);
2747+
2748+#endif
2749
2750=== added file 'panels/common/cc-rr-output-info.c'
2751--- panels/common/cc-rr-output-info.c 1970-01-01 00:00:00 +0000
2752+++ panels/common/cc-rr-output-info.c 2014-06-26 03:13:28 +0000
2753@@ -0,0 +1,242 @@
2754+/* gnome-rr-output-info.c
2755+ * -*- c-basic-offset: 4 -*-
2756+ *
2757+ * Copyright 2010 Giovanni Campagna
2758+ *
2759+ * This file is part of the Gnome Desktop Library.
2760+ *
2761+ * The Gnome Desktop Library is free software; you can redistribute it and/or
2762+ * modify it under the terms of the GNU Library General Public License as
2763+ * published by the Free Software Foundation; either version 2 of the
2764+ * License, or (at your option) any later version.
2765+ *
2766+ * The Gnome Library is distributed in the hope that it will be useful,
2767+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2768+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2769+ * Library General Public License for more details.
2770+ *
2771+ * You should have received a copy of the GNU Library General Public
2772+ * License along with the Gnome Desktop Library; see the file COPYING.LIB. If not,
2773+ * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
2774+ * Boston, MA 02110-1301, USA.
2775+ */
2776+
2777+#include <config.h>
2778+
2779+#include "cc-rr-config.h"
2780+
2781+#include "edid.h"
2782+#include "cc-rr-private.h"
2783+
2784+G_DEFINE_TYPE (CCRROutputInfo, cc_rr_output_info, G_TYPE_OBJECT)
2785+
2786+static void
2787+cc_rr_output_info_init (CCRROutputInfo *self)
2788+{
2789+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CC_TYPE_RR_OUTPUT_INFO, CCRROutputInfoPrivate);
2790+
2791+ self->priv->name = NULL;
2792+ self->priv->on = FALSE;
2793+ self->priv->display_name = NULL;
2794+}
2795+
2796+static void
2797+cc_rr_output_info_finalize (GObject *gobject)
2798+{
2799+ CCRROutputInfo *self = CC_RR_OUTPUT_INFO (gobject);
2800+
2801+ g_free (self->priv->name);
2802+ g_free (self->priv->display_name);
2803+
2804+ G_OBJECT_CLASS (cc_rr_output_info_parent_class)->finalize (gobject);
2805+}
2806+
2807+static void
2808+cc_rr_output_info_class_init (CCRROutputInfoClass *klass)
2809+{
2810+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2811+
2812+ g_type_class_add_private (klass, sizeof (CCRROutputInfoPrivate));
2813+
2814+ gobject_class->finalize = cc_rr_output_info_finalize;
2815+}
2816+
2817+/**
2818+ * cc_rr_output_info_get_name:
2819+ *
2820+ * Returns: (transfer none): the output name
2821+ */
2822+char *cc_rr_output_info_get_name (CCRROutputInfo *self)
2823+{
2824+ g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), NULL);
2825+
2826+ return self->priv->name;
2827+}
2828+
2829+/**
2830+ * cc_rr_output_info_is_active:
2831+ *
2832+ * Returns: whether there is a CRTC assigned to this output (i.e. a signal is being sent to it)
2833+ */
2834+gboolean cc_rr_output_info_is_active (CCRROutputInfo *self)
2835+{
2836+ g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), FALSE);
2837+
2838+ return self->priv->on;
2839+}
2840+
2841+void cc_rr_output_info_set_active (CCRROutputInfo *self, gboolean active)
2842+{
2843+ g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
2844+
2845+ self->priv->on = active;
2846+}
2847+
2848+/**
2849+ * cc_rr_output_info_get_geometry:
2850+ * @self: a #CCRROutputInfo
2851+ * @x: (out) (allow-none):
2852+ * @y: (out) (allow-none):
2853+ * @width: (out) (allow-none):
2854+ * @height: (out) (allow-none):
2855+ */
2856+void cc_rr_output_info_get_geometry (CCRROutputInfo *self, int *x, int *y, int *width, int *height)
2857+{
2858+ g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
2859+
2860+ if (x)
2861+ *x = self->priv->x;
2862+ if (y)
2863+ *y = self->priv->y;
2864+ if (width)
2865+ *width = self->priv->width;
2866+ if (height)
2867+ *height = self->priv->height;
2868+}
2869+
2870+void cc_rr_output_info_set_geometry (CCRROutputInfo *self, int x, int y, int width, int height)
2871+{
2872+ g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
2873+
2874+ self->priv->x = x;
2875+ self->priv->y = y;
2876+ self->priv->width = width;
2877+ self->priv->height = height;
2878+}
2879+
2880+int cc_rr_output_info_get_refresh_rate (CCRROutputInfo *self)
2881+{
2882+ g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), 0);
2883+
2884+ return self->priv->rate;
2885+}
2886+
2887+void cc_rr_output_info_set_refresh_rate (CCRROutputInfo *self, int rate)
2888+{
2889+ g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
2890+
2891+ self->priv->rate = rate;
2892+}
2893+
2894+CCRRRotation cc_rr_output_info_get_rotation (CCRROutputInfo *self)
2895+{
2896+ g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), CC_RR_ROTATION_0);
2897+
2898+ return self->priv->rotation;
2899+}
2900+
2901+void cc_rr_output_info_set_rotation (CCRROutputInfo *self, CCRRRotation rotation)
2902+{
2903+ g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
2904+
2905+ self->priv->rotation = rotation;
2906+}
2907+
2908+/**
2909+ * cc_rr_output_info_is_connected:
2910+ *
2911+ * Returns: whether the output is physically connected to a monitor
2912+ */
2913+gboolean cc_rr_output_info_is_connected (CCRROutputInfo *self)
2914+{
2915+ g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), FALSE);
2916+
2917+ return self->priv->connected;
2918+}
2919+
2920+/**
2921+ * cc_rr_output_info_get_vendor:
2922+ * @self: a #CCRROutputInfo
2923+ * @vendor: (out caller-allocates) (array fixed-size=4):
2924+ */
2925+void cc_rr_output_info_get_vendor (CCRROutputInfo *self, gchar* vendor)
2926+{
2927+ g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
2928+ g_return_if_fail (vendor != NULL);
2929+
2930+ vendor[0] = self->priv->vendor[0];
2931+ vendor[1] = self->priv->vendor[1];
2932+ vendor[2] = self->priv->vendor[2];
2933+ vendor[3] = self->priv->vendor[3];
2934+}
2935+
2936+guint cc_rr_output_info_get_product (CCRROutputInfo *self)
2937+{
2938+ g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), 0);
2939+
2940+ return self->priv->product;
2941+}
2942+
2943+guint cc_rr_output_info_get_serial (CCRROutputInfo *self)
2944+{
2945+ g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), 0);
2946+
2947+ return self->priv->serial;
2948+}
2949+
2950+double cc_rr_output_info_get_aspect_ratio (CCRROutputInfo *self)
2951+{
2952+ g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), 0);
2953+
2954+ return self->priv->aspect;
2955+}
2956+
2957+/**
2958+ * cc_rr_output_info_get_display_name:
2959+ *
2960+ * Returns: (transfer none): the display name of this output
2961+ */
2962+char *cc_rr_output_info_get_display_name (CCRROutputInfo *self)
2963+{
2964+ g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), NULL);
2965+
2966+ return self->priv->display_name;
2967+}
2968+
2969+gboolean cc_rr_output_info_get_primary (CCRROutputInfo *self)
2970+{
2971+ g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), FALSE);
2972+
2973+ return self->priv->primary;
2974+}
2975+
2976+void cc_rr_output_info_set_primary (CCRROutputInfo *self, gboolean primary)
2977+{
2978+ g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
2979+
2980+ self->priv->primary = primary;
2981+}
2982+
2983+int cc_rr_output_info_get_preferred_width (CCRROutputInfo *self)
2984+{
2985+ g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), 0);
2986+
2987+ return self->priv->pref_width;
2988+}
2989+
2990+int cc_rr_output_info_get_preferred_height (CCRROutputInfo *self)
2991+{
2992+ g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), 0);
2993+
2994+ return self->priv->pref_height;
2995+}
2996
2997=== added file 'panels/common/cc-rr-private.h'
2998--- panels/common/cc-rr-private.h 1970-01-01 00:00:00 +0000
2999+++ panels/common/cc-rr-private.h 2014-06-26 03:13:28 +0000
3000@@ -0,0 +1,79 @@
3001+#ifndef CC_RR_PRIVATE_H
3002+#define CC_RR_PRIVATE_H
3003+
3004+#include <X11/Xlib.h>
3005+
3006+#include <X11/extensions/Xrandr.h>
3007+
3008+typedef struct ScreenInfo ScreenInfo;
3009+
3010+struct ScreenInfo
3011+{
3012+ int min_width;
3013+ int max_width;
3014+ int min_height;
3015+ int max_height;
3016+
3017+ XRRScreenResources *resources;
3018+
3019+ CCRROutput ** outputs;
3020+ CCRRCrtc ** crtcs;
3021+ CCRRMode ** modes;
3022+
3023+ CCRRScreen * screen;
3024+
3025+ CCRRMode ** clone_modes;
3026+
3027+ RROutput primary;
3028+};
3029+
3030+struct CCRRScreenPrivate
3031+{
3032+ GdkScreen * gdk_screen;
3033+ GdkWindow * gdk_root;
3034+ Display * xdisplay;
3035+ Screen * xscreen;
3036+ Window xroot;
3037+ ScreenInfo * info;
3038+
3039+ int randr_event_base;
3040+ int rr_major_version;
3041+ int rr_minor_version;
3042+
3043+ Atom connector_type_atom;
3044+ gboolean dpms_capable;
3045+};
3046+
3047+struct _CCRROutputInfoPrivate
3048+{
3049+ char * name;
3050+
3051+ gboolean on;
3052+ int width;
3053+ int height;
3054+ int rate;
3055+ int x;
3056+ int y;
3057+ CCRRRotation rotation;
3058+
3059+ gboolean connected;
3060+ gchar vendor[4];
3061+ guint product;
3062+ guint serial;
3063+ double aspect;
3064+ int pref_width;
3065+ int pref_height;
3066+ char * display_name;
3067+ gboolean primary;
3068+};
3069+
3070+struct _CCRRConfigPrivate
3071+{
3072+ gboolean clone;
3073+ CCRRScreen *screen;
3074+ CCRROutputInfo **outputs;
3075+};
3076+
3077+gboolean _cc_rr_output_name_is_laptop (const char *name);
3078+
3079+#endif
3080
3081=== added file 'panels/common/cc-rr.c'
3082--- panels/common/cc-rr.c 1970-01-01 00:00:00 +0000
3083+++ panels/common/cc-rr.c 2014-06-26 03:13:28 +0000
3084@@ -0,0 +1,2622 @@
3085+/* gnome-rr.c
3086+ *
3087+ * Copyright 2007, 2008, Red Hat, Inc.
3088+ *
3089+ * This file is part of the Gnome Library.
3090+ *
3091+ * The Gnome Library is free software; you can redistribute it and/or
3092+ * modify it under the terms of the GNU Library General Public License as
3093+ * published by the Free Software Foundation; either version 2 of the
3094+ * License, or (at your option) any later version.
3095+ *
3096+ * The Gnome Library is distributed in the hope that it will be useful,
3097+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3098+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3099+ * Library General Public License for more details.
3100+ *
3101+ * You should have received a copy of the GNU Library General Public
3102+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
3103+ * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
3104+ * Boston, MA 02110-1301, USA.
3105+ *
3106+ * Author: Soren Sandmann <sandmann@redhat.com>
3107+ */
3108+
3109+#include <config.h>
3110+#include <glib/gi18n-lib.h>
3111+#include <string.h>
3112+#include <X11/Xlib.h>
3113+
3114+#include <X11/extensions/Xrandr.h>
3115+
3116+#include <gtk/gtk.h>
3117+#include <gdk/gdkx.h>
3118+#include <X11/Xatom.h>
3119+#include <X11/extensions/dpms.h>
3120+
3121+#undef GNOME_DISABLE_DEPRECATED
3122+#include "cc-rr.h"
3123+#include "cc-rr-config.h"
3124+
3125+#include "edid.h"
3126+#include "cc-rr-private.h"
3127+
3128+#define DISPLAY(o) ((o)->info->screen->priv->xdisplay)
3129+
3130+#define SERVERS_RANDR_IS_AT_LEAST_1_3(priv) (priv->rr_major_version > 1 || (priv->rr_major_version == 1 && priv->rr_minor_version >= 3))
3131+
3132+enum {
3133+ SCREEN_PROP_0,
3134+ SCREEN_PROP_GDK_SCREEN,
3135+ SCREEN_PROP_LAST,
3136+};
3137+
3138+enum {
3139+ SCREEN_CHANGED,
3140+ SCREEN_OUTPUT_CONNECTED,
3141+ SCREEN_OUTPUT_DISCONNECTED,
3142+ SCREEN_SIGNAL_LAST,
3143+};
3144+
3145+gint screen_signals[SCREEN_SIGNAL_LAST];
3146+
3147+struct CCRROutput
3148+{
3149+ ScreenInfo * info;
3150+ RROutput id;
3151+
3152+ char * name;
3153+ char * display_name;
3154+ CCRRCrtc * current_crtc;
3155+ gboolean connected;
3156+ gulong width_mm;
3157+ gulong height_mm;
3158+ CCRRCrtc ** possible_crtcs;
3159+ CCRROutput ** clones;
3160+ CCRRMode ** modes;
3161+ int n_preferred;
3162+ guint8 * edid_data;
3163+ gsize edid_size;
3164+ char * connector_type;
3165+ gint backlight_min;
3166+ gint backlight_max;
3167+};
3168+
3169+struct CCRROutputWrap
3170+{
3171+ RROutput id;
3172+};
3173+
3174+struct CCRRCrtc
3175+{
3176+ ScreenInfo * info;
3177+ RRCrtc id;
3178+
3179+ CCRRMode * current_mode;
3180+ CCRROutput ** current_outputs;
3181+ CCRROutput ** possible_outputs;
3182+ int x;
3183+ int y;
3184+
3185+ CCRRRotation current_rotation;
3186+ CCRRRotation rotations;
3187+ int gamma_size;
3188+};
3189+
3190+struct CCRRMode
3191+{
3192+ ScreenInfo * info;
3193+ RRMode id;
3194+ char * name;
3195+ int width;
3196+ int height;
3197+ int freq; /* in mHz */
3198+};
3199+
3200+/* CCRRCrtc */
3201+static CCRRCrtc * crtc_new (ScreenInfo *info,
3202+ RRCrtc id);
3203+static CCRRCrtc * crtc_copy (const CCRRCrtc *from);
3204+static void crtc_free (CCRRCrtc *crtc);
3205+
3206+static gboolean crtc_initialize (CCRRCrtc *crtc,
3207+ XRRScreenResources *res,
3208+ GError **error);
3209+
3210+/* CCRROutput */
3211+static CCRROutput *output_new (ScreenInfo *info,
3212+ RROutput id);
3213+
3214+static gboolean output_initialize (CCRROutput *output,
3215+ XRRScreenResources *res,
3216+ GError **error);
3217+
3218+static CCRROutput *output_copy (const CCRROutput *from);
3219+static void output_free (CCRROutput *output);
3220+
3221+/* CCRRMode */
3222+static CCRRMode * mode_new (ScreenInfo *info,
3223+ RRMode id);
3224+
3225+static void mode_initialize (CCRRMode *mode,
3226+ XRRModeInfo *info);
3227+
3228+static CCRRMode * mode_copy (const CCRRMode *from);
3229+static void mode_free (CCRRMode *mode);
3230+
3231+static void cc_rr_screen_finalize (GObject*);
3232+static void cc_rr_screen_set_property (GObject*, guint, const GValue*, GParamSpec*);
3233+static void cc_rr_screen_get_property (GObject*, guint, GValue*, GParamSpec*);
3234+static gboolean cc_rr_screen_initable_init (GInitable*, GCancellable*, GError**);
3235+static void cc_rr_screen_initable_iface_init (GInitableIface *iface);
3236+G_DEFINE_TYPE_WITH_CODE (CCRRScreen, cc_rr_screen, G_TYPE_OBJECT,
3237+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, cc_rr_screen_initable_iface_init))
3238+
3239+G_DEFINE_BOXED_TYPE (CCRRCrtc, cc_rr_crtc, crtc_copy, crtc_free)
3240+G_DEFINE_BOXED_TYPE (CCRROutput, cc_rr_output, output_copy, output_free)
3241+G_DEFINE_BOXED_TYPE (CCRRMode, cc_rr_mode, mode_copy, mode_free)
3242+
3243+/* Errors */
3244+
3245+/**
3246+ * cc_rr_error_quark:
3247+ *
3248+ * Returns the #GQuark that will be used for #GError values returned by the
3249+ * CCRR API.
3250+ *
3251+ * Return value: a #GQuark used to identify errors coming from the CCRR API.
3252+ */
3253+GQuark
3254+cc_rr_error_quark (void)
3255+{
3256+ return g_quark_from_static_string ("cc-rr-error-quark");
3257+}
3258+
3259+/* Screen */
3260+static CCRROutput *
3261+cc_rr_output_by_id (ScreenInfo *info, RROutput id)
3262+{
3263+ CCRROutput **output;
3264+
3265+ g_assert (info != NULL);
3266+
3267+ for (output = info->outputs; *output; ++output)
3268+ {
3269+ if ((*output)->id == id)
3270+ return *output;
3271+ }
3272+
3273+ return NULL;
3274+}
3275+
3276+static CCRRCrtc *
3277+crtc_by_id (ScreenInfo *info, RRCrtc id)
3278+{
3279+ CCRRCrtc **crtc;
3280+
3281+ if (!info)
3282+ return NULL;
3283+
3284+ for (crtc = info->crtcs; *crtc; ++crtc)
3285+ {
3286+ if ((*crtc)->id == id)
3287+ return *crtc;
3288+ }
3289+
3290+ return NULL;
3291+}
3292+
3293+static CCRRMode *
3294+mode_by_id (ScreenInfo *info, RRMode id)
3295+{
3296+ CCRRMode **mode;
3297+
3298+ g_assert (info != NULL);
3299+
3300+ for (mode = info->modes; *mode; ++mode)
3301+ {
3302+ if ((*mode)->id == id)
3303+ return *mode;
3304+ }
3305+
3306+ return NULL;
3307+}
3308+
3309+static void
3310+screen_info_free (ScreenInfo *info)
3311+{
3312+ CCRROutput **output;
3313+ CCRRCrtc **crtc;
3314+ CCRRMode **mode;
3315+
3316+ g_assert (info != NULL);
3317+
3318+ if (info->resources)
3319+ {
3320+ XRRFreeScreenResources (info->resources);
3321+
3322+ info->resources = NULL;
3323+ }
3324+
3325+ if (info->outputs)
3326+ {
3327+ for (output = info->outputs; *output; ++output)
3328+ output_free (*output);
3329+ g_free (info->outputs);
3330+ }
3331+
3332+ if (info->crtcs)
3333+ {
3334+ for (crtc = info->crtcs; *crtc; ++crtc)
3335+ crtc_free (*crtc);
3336+ g_free (info->crtcs);
3337+ }
3338+
3339+ if (info->modes)
3340+ {
3341+ for (mode = info->modes; *mode; ++mode)
3342+ mode_free (*mode);
3343+ g_free (info->modes);
3344+ }
3345+
3346+ if (info->clone_modes)
3347+ {
3348+ /* The modes themselves were freed above */
3349+ g_free (info->clone_modes);
3350+ }
3351+
3352+ g_free (info);
3353+}
3354+
3355+static gboolean
3356+has_similar_mode (CCRROutput *output, CCRRMode *mode)
3357+{
3358+ int i;
3359+ CCRRMode **modes = cc_rr_output_list_modes (output);
3360+ int width = cc_rr_mode_get_width (mode);
3361+ int height = cc_rr_mode_get_height (mode);
3362+
3363+ for (i = 0; modes[i] != NULL; ++i)
3364+ {
3365+ CCRRMode *m = modes[i];
3366+
3367+ if (cc_rr_mode_get_width (m) == width &&
3368+ cc_rr_mode_get_height (m) == height)
3369+ {
3370+ return TRUE;
3371+ }
3372+ }
3373+
3374+ return FALSE;
3375+}
3376+
3377+static void
3378+gather_clone_modes (ScreenInfo *info)
3379+{
3380+ int i;
3381+ GPtrArray *result = g_ptr_array_new ();
3382+
3383+ for (i = 0; info->outputs[i] != NULL; ++i)
3384+ {
3385+ int j;
3386+ CCRROutput *output1, *output2;
3387+
3388+ output1 = info->outputs[i];
3389+
3390+ if (!output1->connected)
3391+ continue;
3392+
3393+ for (j = 0; output1->modes[j] != NULL; ++j)
3394+ {
3395+ CCRRMode *mode = output1->modes[j];
3396+ gboolean valid;
3397+ int k;
3398+
3399+ valid = TRUE;
3400+ for (k = 0; info->outputs[k] != NULL; ++k)
3401+ {
3402+ output2 = info->outputs[k];
3403+
3404+ if (!output2->connected)
3405+ continue;
3406+
3407+ if (!has_similar_mode (output2, mode))
3408+ {
3409+ valid = FALSE;
3410+ break;
3411+ }
3412+ }
3413+
3414+ if (valid)
3415+ g_ptr_array_add (result, mode);
3416+ }
3417+ }
3418+
3419+ g_ptr_array_add (result, NULL);
3420+
3421+ info->clone_modes = (CCRRMode **)g_ptr_array_free (result, FALSE);
3422+}
3423+
3424+static gboolean
3425+fill_screen_info_from_resources (ScreenInfo *info,
3426+ XRRScreenResources *resources,
3427+ GError **error)
3428+{
3429+ int i;
3430+ GPtrArray *a;
3431+ CCRRCrtc **crtc;
3432+ CCRROutput **output;
3433+
3434+ info->resources = resources;
3435+
3436+ /* We create all the structures before initializing them, so
3437+ * that they can refer to each other.
3438+ */
3439+ a = g_ptr_array_new ();
3440+ for (i = 0; i < resources->ncrtc; ++i)
3441+ {
3442+ CCRRCrtc *crtc = crtc_new (info, resources->crtcs[i]);
3443+
3444+ g_ptr_array_add (a, crtc);
3445+ }
3446+ g_ptr_array_add (a, NULL);
3447+ info->crtcs = (CCRRCrtc **)g_ptr_array_free (a, FALSE);
3448+
3449+ a = g_ptr_array_new ();
3450+ for (i = 0; i < resources->noutput; ++i)
3451+ {
3452+ CCRROutput *output = output_new (info, resources->outputs[i]);
3453+
3454+ g_ptr_array_add (a, output);
3455+ }
3456+ g_ptr_array_add (a, NULL);
3457+ info->outputs = (CCRROutput **)g_ptr_array_free (a, FALSE);
3458+
3459+ a = g_ptr_array_new ();
3460+ for (i = 0; i < resources->nmode; ++i)
3461+ {
3462+ CCRRMode *mode = mode_new (info, resources->modes[i].id);
3463+
3464+ g_ptr_array_add (a, mode);
3465+ }
3466+ g_ptr_array_add (a, NULL);
3467+ info->modes = (CCRRMode **)g_ptr_array_free (a, FALSE);
3468+
3469+ /* Initialize */
3470+ for (crtc = info->crtcs; *crtc; ++crtc)
3471+ {
3472+ if (!crtc_initialize (*crtc, resources, error))
3473+ return FALSE;
3474+ }
3475+
3476+ for (output = info->outputs; *output; ++output)
3477+ {
3478+ if (!output_initialize (*output, resources, error))
3479+ return FALSE;
3480+ }
3481+
3482+ for (i = 0; i < resources->nmode; ++i)
3483+ {
3484+ CCRRMode *mode = mode_by_id (info, resources->modes[i].id);
3485+
3486+ mode_initialize (mode, &(resources->modes[i]));
3487+ }
3488+
3489+ gather_clone_modes (info);
3490+
3491+ return TRUE;
3492+}
3493+
3494+static gboolean
3495+fill_out_screen_info (Display *xdisplay,
3496+ Window xroot,
3497+ ScreenInfo *info,
3498+ gboolean needs_reprobe,
3499+ GError **error)
3500+{
3501+ XRRScreenResources *resources;
3502+ CCRRScreenPrivate *priv;
3503+
3504+ g_assert (xdisplay != NULL);
3505+ g_assert (info != NULL);
3506+
3507+ priv = info->screen->priv;
3508+
3509+ /* First update the screen resources */
3510+
3511+ if (needs_reprobe)
3512+ resources = XRRGetScreenResources (xdisplay, xroot);
3513+ else
3514+ {
3515+ /* XRRGetScreenResourcesCurrent is less expensive than
3516+ * XRRGetScreenResources, however it is available only
3517+ * in RandR 1.3 or higher
3518+ */
3519+ if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv))
3520+ resources = XRRGetScreenResourcesCurrent (xdisplay, xroot);
3521+ else
3522+ resources = XRRGetScreenResources (xdisplay, xroot);
3523+ }
3524+
3525+ if (resources)
3526+ {
3527+ if (!fill_screen_info_from_resources (info, resources, error))
3528+ return FALSE;
3529+ }
3530+ else
3531+ {
3532+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_RANDR_ERROR,
3533+ /* Translators: a CRTC is a CRT Controller (this is X terminology). */
3534+ _("could not get the screen resources (CRTCs, outputs, modes)"));
3535+ return FALSE;
3536+ }
3537+
3538+ /* Then update the screen size range. We do this after XRRGetScreenResources() so that
3539+ * the X server will already have an updated view of the outputs.
3540+ */
3541+
3542+ if (needs_reprobe) {
3543+ gboolean success;
3544+
3545+ gdk_error_trap_push ();
3546+ success = XRRGetScreenSizeRange (xdisplay, xroot,
3547+ &(info->min_width),
3548+ &(info->min_height),
3549+ &(info->max_width),
3550+ &(info->max_height));
3551+ gdk_flush ();
3552+ if (gdk_error_trap_pop ()) {
3553+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_UNKNOWN,
3554+ _("unhandled X error while getting the range of screen sizes"));
3555+ return FALSE;
3556+ }
3557+
3558+ if (!success) {
3559+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_RANDR_ERROR,
3560+ _("could not get the range of screen sizes"));
3561+ return FALSE;
3562+ }
3563+ }
3564+ else
3565+ {
3566+ cc_rr_screen_get_ranges (info->screen,
3567+ &(info->min_width),
3568+ &(info->max_width),
3569+ &(info->min_height),
3570+ &(info->max_height));
3571+ }
3572+
3573+ info->primary = None;
3574+ if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv)) {
3575+ gdk_error_trap_push ();
3576+ info->primary = XRRGetOutputPrimary (xdisplay, xroot);
3577+ gdk_error_trap_pop_ignored ();
3578+ }
3579+
3580+ /* can the screen do DPMS? */
3581+ gdk_error_trap_push ();
3582+ priv->dpms_capable = DPMSCapable (priv->xdisplay);
3583+ gdk_error_trap_pop_ignored ();
3584+
3585+ return TRUE;
3586+}
3587+
3588+static ScreenInfo *
3589+screen_info_new (CCRRScreen *screen, gboolean needs_reprobe, GError **error)
3590+{
3591+ ScreenInfo *info = g_new0 (ScreenInfo, 1);
3592+ CCRRScreenPrivate *priv;
3593+
3594+ g_assert (screen != NULL);
3595+
3596+ priv = screen->priv;
3597+
3598+ info->outputs = NULL;
3599+ info->crtcs = NULL;
3600+ info->modes = NULL;
3601+ info->screen = screen;
3602+
3603+ if (fill_out_screen_info (priv->xdisplay, priv->xroot, info, needs_reprobe, error))
3604+ {
3605+ return info;
3606+ }
3607+ else
3608+ {
3609+ screen_info_free (info);
3610+ return NULL;
3611+ }
3612+}
3613+
3614+static CCRROutput *
3615+find_output_by_id (CCRROutput **haystack, guint32 id)
3616+{
3617+ guint i;
3618+
3619+ for (i = 0; haystack[i] != NULL; i++)
3620+ {
3621+ if (cc_rr_output_get_id (haystack[i]) == id)
3622+ return haystack[i];
3623+ }
3624+ return NULL;
3625+}
3626+
3627+static void
3628+diff_outputs_and_emit_signals (ScreenInfo *old, ScreenInfo *new)
3629+{
3630+ guint i;
3631+ guint32 id_old, id_new;
3632+ CCRROutput *output_old;
3633+ CCRROutput *output_new;
3634+
3635+ /* have any outputs been removed or disconnected */
3636+ for (i = 0; old->outputs[i] != NULL; i++)
3637+ {
3638+ id_old = cc_rr_output_get_id (old->outputs[i]);
3639+ output_new = find_output_by_id (new->outputs, id_old);
3640+ if (output_new == NULL)
3641+ {
3642+ /* output removed (and disconnected) */
3643+ if (cc_rr_output_is_connected (old->outputs[i]))
3644+ {
3645+ g_signal_emit (G_OBJECT (new->screen),
3646+ screen_signals[SCREEN_OUTPUT_DISCONNECTED], 0,
3647+ old->outputs[i]);
3648+ }
3649+ continue;
3650+ }
3651+ if (cc_rr_output_is_connected (old->outputs[i]) &&
3652+ !cc_rr_output_is_connected (output_new))
3653+ {
3654+ /* output disconnected */
3655+ g_signal_emit (G_OBJECT (new->screen),
3656+ screen_signals[SCREEN_OUTPUT_DISCONNECTED], 0,
3657+ old->outputs[i]);
3658+ }
3659+ }
3660+
3661+ /* have any outputs been created or connected */
3662+ for (i = 0; new->outputs[i] != NULL; i++)
3663+ {
3664+ id_new = cc_rr_output_get_id (new->outputs[i]);
3665+ output_old = find_output_by_id (old->outputs, id_new);
3666+ if (output_old == NULL)
3667+ {
3668+ /* output created */
3669+ if (cc_rr_output_is_connected (new->outputs[i]))
3670+ {
3671+ g_signal_emit (G_OBJECT (new->screen),
3672+ screen_signals[SCREEN_OUTPUT_CONNECTED], 0,
3673+ new->outputs[i]);
3674+ }
3675+ continue;
3676+ }
3677+ if (!cc_rr_output_is_connected (output_old) &&
3678+ cc_rr_output_is_connected (new->outputs[i]))
3679+ {
3680+ /* output connected */
3681+ g_signal_emit (G_OBJECT (new->screen),
3682+ screen_signals[SCREEN_OUTPUT_CONNECTED], 0,
3683+ new->outputs[i]);
3684+ }
3685+ }
3686+}
3687+
3688+static gboolean
3689+screen_update (CCRRScreen *screen, gboolean force_callback, gboolean needs_reprobe, GError **error)
3690+{
3691+ ScreenInfo *info;
3692+ gboolean changed = FALSE;
3693+
3694+ g_assert (screen != NULL);
3695+
3696+ info = screen_info_new (screen, needs_reprobe, error);
3697+ if (!info)
3698+ return FALSE;
3699+
3700+ if (info->resources->configTimestamp != screen->priv->info->resources->configTimestamp)
3701+ changed = TRUE;
3702+
3703+ /* work out if any outputs have changed connected state */
3704+ diff_outputs_and_emit_signals (screen->priv->info, info);
3705+
3706+ screen_info_free (screen->priv->info);
3707+
3708+ screen->priv->info = info;
3709+
3710+ if (changed || force_callback)
3711+ g_signal_emit (G_OBJECT (screen), screen_signals[SCREEN_CHANGED], 0);
3712+
3713+ return changed;
3714+}
3715+
3716+static GdkFilterReturn
3717+screen_on_event (GdkXEvent *xevent,
3718+ GdkEvent *event,
3719+ gpointer data)
3720+{
3721+ CCRRScreen *screen = data;
3722+ CCRRScreenPrivate *priv = screen->priv;
3723+ XEvent *e = xevent;
3724+ int event_num;
3725+
3726+ if (!e)
3727+ return GDK_FILTER_CONTINUE;
3728+
3729+ event_num = e->type - priv->randr_event_base;
3730+
3731+ if (event_num == RRScreenChangeNotify) {
3732+ /* We don't reprobe the hardware; we just fetch the X server's latest
3733+ * state. The server already knows the new state of the outputs; that's
3734+ * why it sent us an event!
3735+ */
3736+ screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */
3737+#if 0
3738+ /* Enable this code to get a dialog showing the RANDR timestamps, for debugging purposes */
3739+ {
3740+ GtkWidget *dialog;
3741+ XRRScreenChangeNotifyEvent *rr_event;
3742+ static int dialog_num;
3743+
3744+ rr_event = (XRRScreenChangeNotifyEvent *) e;
3745+
3746+ dialog = gtk_message_dialog_new (NULL,
3747+ 0,
3748+ GTK_MESSAGE_INFO,
3749+ GTK_BUTTONS_CLOSE,
3750+ "RRScreenChangeNotify timestamps (%d):\n"
3751+ "event change: %u\n"
3752+ "event config: %u\n"
3753+ "event serial: %lu\n"
3754+ "----------------------"
3755+ "screen change: %u\n"
3756+ "screen config: %u\n",
3757+ dialog_num++,
3758+ (guint32) rr_event->timestamp,
3759+ (guint32) rr_event->config_timestamp,
3760+ rr_event->serial,
3761+ (guint32) priv->info->resources->timestamp,
3762+ (guint32) priv->info->resources->configTimestamp);
3763+ g_signal_connect (dialog, "response",
3764+ G_CALLBACK (gtk_widget_destroy), NULL);
3765+ gtk_widget_show (dialog);
3766+ }
3767+#endif
3768+ }
3769+#if 0
3770+ /* WHY THIS CODE IS DISABLED:
3771+ *
3772+ * Note that in cc_rr_screen_new(), we only select for
3773+ * RRScreenChangeNotifyMask. We used to select for other values in
3774+ * RR*NotifyMask, but we weren't really doing anything useful with those
3775+ * events. We only care about "the screens changed in some way or another"
3776+ * for now.
3777+ *
3778+ * If we ever run into a situtation that could benefit from processing more
3779+ * detailed events, we can enable this code again.
3780+ *
3781+ * Note that the X server sends RRScreenChangeNotify in conjunction with the
3782+ * more detailed events from RANDR 1.2 - see xserver/randr/randr.c:TellChanged().
3783+ */
3784+ else if (event_num == RRNotify)
3785+ {
3786+ /* Other RandR events */
3787+
3788+ XRRNotifyEvent *event = (XRRNotifyEvent *)e;
3789+
3790+ /* Here we can distinguish between RRNotify events supported
3791+ * since RandR 1.2 such as RRNotify_OutputProperty. For now, we
3792+ * don't have anything special to do for particular subevent types, so
3793+ * we leave this as an empty switch().
3794+ */
3795+ switch (event->subtype)
3796+ {
3797+ default:
3798+ break;
3799+ }
3800+
3801+ /* No need to reprobe hardware here */
3802+ screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */
3803+ }
3804+#endif
3805+
3806+ /* Pass the event on to GTK+ */
3807+ return GDK_FILTER_CONTINUE;
3808+}
3809+
3810+static gboolean
3811+cc_rr_screen_initable_init (GInitable *initable, GCancellable *canc, GError **error)
3812+{
3813+ CCRRScreen *self = CC_RR_SCREEN (initable);
3814+ CCRRScreenPrivate *priv = self->priv;
3815+ Display *dpy = GDK_SCREEN_XDISPLAY (self->priv->gdk_screen);
3816+ int event_base;
3817+ int ignore;
3818+
3819+ priv->connector_type_atom = XInternAtom (dpy, "ConnectorType", FALSE);
3820+
3821+ if (XRRQueryExtension (dpy, &event_base, &ignore))
3822+ {
3823+ priv->randr_event_base = event_base;
3824+
3825+ XRRQueryVersion (dpy, &priv->rr_major_version, &priv->rr_minor_version);
3826+ if (priv->rr_major_version < 1 || (priv->rr_major_version == 1 && priv->rr_minor_version < 2)) {
3827+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_NO_RANDR_EXTENSION,
3828+ "RANDR extension is too old (must be at least 1.2)");
3829+ return FALSE;
3830+ }
3831+
3832+ priv->info = screen_info_new (self, TRUE, error);
3833+
3834+ if (!priv->info) {
3835+ return FALSE;
3836+ }
3837+
3838+ XRRSelectInput (priv->xdisplay,
3839+ priv->xroot,
3840+ RRScreenChangeNotifyMask);
3841+ gdk_x11_register_standard_event_type (gdk_screen_get_display (priv->gdk_screen),
3842+ event_base,
3843+ RRNotify + 1);
3844+ gdk_window_add_filter (priv->gdk_root, screen_on_event, self);
3845+
3846+ return TRUE;
3847+ }
3848+ else
3849+ {
3850+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_NO_RANDR_EXTENSION,
3851+ _("RANDR extension is not present"));
3852+
3853+ return FALSE;
3854+ }
3855+}
3856+
3857+void
3858+cc_rr_screen_initable_iface_init (GInitableIface *iface)
3859+{
3860+ iface->init = cc_rr_screen_initable_init;
3861+}
3862+
3863+void
3864+cc_rr_screen_finalize (GObject *gobject)
3865+{
3866+ CCRRScreen *screen = CC_RR_SCREEN (gobject);
3867+
3868+ gdk_window_remove_filter (screen->priv->gdk_root, screen_on_event, screen);
3869+
3870+ if (screen->priv->info)
3871+ screen_info_free (screen->priv->info);
3872+
3873+ G_OBJECT_CLASS (cc_rr_screen_parent_class)->finalize (gobject);
3874+}
3875+
3876+void
3877+cc_rr_screen_set_property (GObject *gobject, guint property_id, const GValue *value, GParamSpec *property)
3878+{
3879+ CCRRScreen *self = CC_RR_SCREEN (gobject);
3880+ CCRRScreenPrivate *priv = self->priv;
3881+
3882+ switch (property_id)
3883+ {
3884+ case SCREEN_PROP_GDK_SCREEN:
3885+ priv->gdk_screen = g_value_get_object (value);
3886+ priv->gdk_root = gdk_screen_get_root_window (priv->gdk_screen);
3887+ priv->xroot = gdk_x11_window_get_xid (priv->gdk_root);
3888+ priv->xdisplay = GDK_SCREEN_XDISPLAY (priv->gdk_screen);
3889+ priv->xscreen = gdk_x11_screen_get_xscreen (priv->gdk_screen);
3890+ return;
3891+ default:
3892+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
3893+ return;
3894+ }
3895+}
3896+
3897+void
3898+cc_rr_screen_get_property (GObject *gobject, guint property_id, GValue *value, GParamSpec *property)
3899+{
3900+ CCRRScreen *self = CC_RR_SCREEN (gobject);
3901+ CCRRScreenPrivate *priv = self->priv;
3902+
3903+ switch (property_id)
3904+ {
3905+ case SCREEN_PROP_GDK_SCREEN:
3906+ g_value_set_object (value, priv->gdk_screen);
3907+ return;
3908+ default:
3909+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
3910+ return;
3911+ }
3912+}
3913+
3914+void
3915+cc_rr_screen_class_init (CCRRScreenClass *klass)
3916+{
3917+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3918+ g_type_class_add_private (klass, sizeof (CCRRScreenPrivate));
3919+
3920+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
3921+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
3922+
3923+ gobject_class->set_property = cc_rr_screen_set_property;
3924+ gobject_class->get_property = cc_rr_screen_get_property;
3925+ gobject_class->finalize = cc_rr_screen_finalize;
3926+
3927+ g_object_class_install_property(
3928+ gobject_class,
3929+ SCREEN_PROP_GDK_SCREEN,
3930+ g_param_spec_object (
3931+ "gdk-screen",
3932+ "GDK Screen",
3933+ "The GDK Screen represented by this CCRRScreen",
3934+ GDK_TYPE_SCREEN,
3935+ G_PARAM_READWRITE |
3936+ G_PARAM_CONSTRUCT_ONLY |
3937+ G_PARAM_STATIC_STRINGS)
3938+ );
3939+
3940+ screen_signals[SCREEN_CHANGED] = g_signal_new("changed",
3941+ G_TYPE_FROM_CLASS (gobject_class),
3942+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
3943+ G_STRUCT_OFFSET (CCRRScreenClass, changed),
3944+ NULL,
3945+ NULL,
3946+ g_cclosure_marshal_VOID__VOID,
3947+ G_TYPE_NONE,
3948+ 0);
3949+
3950+ /**
3951+ * CCRRScreen::output-connected:
3952+ * @screen: the #CCRRScreen that emitted the signal
3953+ * @output: the #CCRROutput that was connected
3954+ *
3955+ * This signal is emitted when a display device is connected to a
3956+ * port, or a port is hotplugged with an active output. The latter
3957+ * can happen if a laptop is docked, and the dock provides a new
3958+ * active output.
3959+ *
3960+ * The @output value is not a #GObject. The returned @output value can
3961+ * only assume to be valid during the emission of the signal (i.e. within
3962+ * your signal handler only), as it may change later when the @screen
3963+ * is modified due to an event from the X server, or due to another
3964+ * place in the application modifying the @screen and the @output.
3965+ * Therefore, deal with changes to the @output right in your signal
3966+ * handler, instead of keeping the @output reference for an async or
3967+ * idle function.
3968+ **/
3969+ screen_signals[SCREEN_OUTPUT_CONNECTED] = g_signal_new("output-connected",
3970+ G_TYPE_FROM_CLASS (gobject_class),
3971+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
3972+ G_STRUCT_OFFSET (CCRRScreenClass, output_connected),
3973+ NULL,
3974+ NULL,
3975+ g_cclosure_marshal_VOID__POINTER,
3976+ G_TYPE_NONE,
3977+ 1, G_TYPE_POINTER);
3978+
3979+ /**
3980+ * CCRRScreen::output-disconnected:
3981+ * @screen: the #CCRRScreen that emitted the signal
3982+ * @output: the #CCRROutput that was disconnected
3983+ *
3984+ * This signal is emitted when a display device is disconnected from
3985+ * a port, or a port output is hot-unplugged. The latter can happen
3986+ * if a laptop is undocked, and the dock provided the output.
3987+ *
3988+ * The @output value is not a #GObject. The returned @output value can
3989+ * only assume to be valid during the emission of the signal (i.e. within
3990+ * your signal handler only), as it may change later when the @screen
3991+ * is modified due to an event from the X server, or due to another
3992+ * place in the application modifying the @screen and the @output.
3993+ * Therefore, deal with changes to the @output right in your signal
3994+ * handler, instead of keeping the @output reference for an async or
3995+ * idle function.
3996+ **/
3997+ screen_signals[SCREEN_OUTPUT_DISCONNECTED] = g_signal_new("output-disconnected",
3998+ G_TYPE_FROM_CLASS (gobject_class),
3999+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
4000+ G_STRUCT_OFFSET (CCRRScreenClass, output_disconnected),
4001+ NULL,
4002+ NULL,
4003+ g_cclosure_marshal_VOID__POINTER,
4004+ G_TYPE_NONE,
4005+ 1, G_TYPE_POINTER);
4006+}
4007+
4008+void
4009+cc_rr_screen_init (CCRRScreen *self)
4010+{
4011+ CCRRScreenPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CC_TYPE_RR_SCREEN, CCRRScreenPrivate);
4012+ self->priv = priv;
4013+
4014+ priv->gdk_screen = NULL;
4015+ priv->gdk_root = NULL;
4016+ priv->xdisplay = NULL;
4017+ priv->xroot = None;
4018+ priv->xscreen = NULL;
4019+ priv->info = NULL;
4020+ priv->rr_major_version = 0;
4021+ priv->rr_minor_version = 0;
4022+}
4023+
4024+/* Weak reference callback set in cc_rr_screen_new(); we remove the GObject data from the GdkScreen. */
4025+static void
4026+rr_screen_weak_notify_cb (gpointer data, GObject *where_the_object_was)
4027+{
4028+ GdkScreen *screen = GDK_SCREEN (data);
4029+
4030+ g_object_set_data (G_OBJECT (screen), "CCRRScreen", NULL);
4031+}
4032+
4033+/**
4034+ * cc_rr_screen_new:
4035+ * @screen: the #GdkScreen on which to operate
4036+ * @error: will be set if XRandR is not supported
4037+ *
4038+ * Creates a unique #CCRRScreen instance for the specified @screen.
4039+ *
4040+ * Returns: a unique #CCRRScreen instance, specific to the @screen, or NULL
4041+ * if this could not be created, for instance if the driver does not support
4042+ * Xrandr 1.2. Each #GdkScreen thus has a single instance of #CCRRScreen.
4043+ */
4044+CCRRScreen *
4045+cc_rr_screen_new (GdkScreen *screen,
4046+ GError **error)
4047+{
4048+ CCRRScreen *rr_screen;
4049+
4050+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
4051+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4052+
4053+ rr_screen = g_object_get_data (G_OBJECT (screen), "CCRRScreen");
4054+ if (rr_screen)
4055+ g_object_ref (rr_screen);
4056+ else {
4057+ rr_screen = g_initable_new (CC_TYPE_RR_SCREEN, NULL, error, "gdk-screen", screen, NULL);
4058+ if (rr_screen) {
4059+ g_object_set_data (G_OBJECT (screen), "CCRRScreen", rr_screen);
4060+ g_object_weak_ref (G_OBJECT (rr_screen), rr_screen_weak_notify_cb, screen);
4061+ }
4062+ }
4063+
4064+ return rr_screen;
4065+}
4066+
4067+void
4068+cc_rr_screen_set_size (CCRRScreen *screen,
4069+ int width,
4070+ int height,
4071+ int mm_width,
4072+ int mm_height)
4073+{
4074+ g_return_if_fail (CC_IS_RR_SCREEN (screen));
4075+
4076+ gdk_error_trap_push ();
4077+ XRRSetScreenSize (screen->priv->xdisplay, screen->priv->xroot,
4078+ width, height, mm_width, mm_height);
4079+ gdk_error_trap_pop_ignored ();
4080+}
4081+
4082+/**
4083+ * cc_rr_screen_get_ranges:
4084+ * @screen: a #CCRRScreen
4085+ * @min_width: (out): the minimum width
4086+ * @max_width: (out): the maximum width
4087+ * @min_height: (out): the minimum height
4088+ * @max_height: (out): the maximum height
4089+ *
4090+ * Get the ranges of the screen
4091+ */
4092+void
4093+cc_rr_screen_get_ranges (CCRRScreen *screen,
4094+ int *min_width,
4095+ int *max_width,
4096+ int *min_height,
4097+ int *max_height)
4098+{
4099+ CCRRScreenPrivate *priv;
4100+
4101+ g_return_if_fail (CC_IS_RR_SCREEN (screen));
4102+
4103+ priv = screen->priv;
4104+
4105+ if (min_width)
4106+ *min_width = priv->info->min_width;
4107+
4108+ if (max_width)
4109+ *max_width = priv->info->max_width;
4110+
4111+ if (min_height)
4112+ *min_height = priv->info->min_height;
4113+
4114+ if (max_height)
4115+ *max_height = priv->info->max_height;
4116+}
4117+
4118+/**
4119+ * cc_rr_screen_get_timestamps:
4120+ * @screen: a #CCRRScreen
4121+ * @change_timestamp_ret: (out): Location in which to store the timestamp at which the RANDR configuration was last changed
4122+ * @config_timestamp_ret: (out): Location in which to store the timestamp at which the RANDR configuration was last obtained
4123+ *
4124+ * Queries the two timestamps that the X RANDR extension maintains. The X
4125+ * server will prevent change requests for stale configurations, those whose
4126+ * timestamp is not equal to that of the latest request for configuration. The
4127+ * X server will also prevent change requests that have an older timestamp to
4128+ * the latest change request.
4129+ */
4130+void
4131+cc_rr_screen_get_timestamps (CCRRScreen *screen,
4132+ guint32 *change_timestamp_ret,
4133+ guint32 *config_timestamp_ret)
4134+{
4135+ CCRRScreenPrivate *priv;
4136+
4137+ g_return_if_fail (CC_IS_RR_SCREEN (screen));
4138+
4139+ priv = screen->priv;
4140+
4141+ if (change_timestamp_ret)
4142+ *change_timestamp_ret = priv->info->resources->timestamp;
4143+
4144+ if (config_timestamp_ret)
4145+ *config_timestamp_ret = priv->info->resources->configTimestamp;
4146+}
4147+
4148+static gboolean
4149+force_timestamp_update (CCRRScreen *screen)
4150+{
4151+ CCRRScreenPrivate *priv = screen->priv;
4152+ CCRRCrtc *crtc;
4153+ XRRCrtcInfo *current_info;
4154+ Status status;
4155+ gboolean timestamp_updated;
4156+
4157+ timestamp_updated = FALSE;
4158+
4159+ crtc = priv->info->crtcs[0];
4160+
4161+ if (crtc == NULL)
4162+ goto out;
4163+
4164+ current_info = XRRGetCrtcInfo (priv->xdisplay,
4165+ priv->info->resources,
4166+ crtc->id);
4167+
4168+ if (current_info == NULL)
4169+ goto out;
4170+
4171+ gdk_error_trap_push ();
4172+ status = XRRSetCrtcConfig (priv->xdisplay,
4173+ priv->info->resources,
4174+ crtc->id,
4175+ current_info->timestamp,
4176+ current_info->x,
4177+ current_info->y,
4178+ current_info->mode,
4179+ current_info->rotation,
4180+ current_info->outputs,
4181+ current_info->noutput);
4182+
4183+ XRRFreeCrtcInfo (current_info);
4184+
4185+ gdk_flush ();
4186+ if (gdk_error_trap_pop ())
4187+ goto out;
4188+
4189+ if (status == RRSetConfigSuccess)
4190+ timestamp_updated = TRUE;
4191+out:
4192+ return timestamp_updated;
4193+}
4194+
4195+/**
4196+ * cc_rr_screen_refresh:
4197+ * @screen: a #CCRRScreen
4198+ * @error: location to store error, or %NULL
4199+ *
4200+ * Refreshes the screen configuration, and calls the screen's callback if it
4201+ * exists and if the screen's configuration changed.
4202+ *
4203+ * Return value: TRUE if the screen's configuration changed; otherwise, the
4204+ * function returns FALSE and a NULL error if the configuration didn't change,
4205+ * or FALSE and a non-NULL error if there was an error while refreshing the
4206+ * configuration.
4207+ */
4208+gboolean
4209+cc_rr_screen_refresh (CCRRScreen *screen,
4210+ GError **error)
4211+{
4212+ gboolean refreshed;
4213+
4214+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4215+
4216+ gdk_x11_display_grab (gdk_screen_get_display (screen->priv->gdk_screen));
4217+
4218+ refreshed = screen_update (screen, FALSE, TRUE, error);
4219+ force_timestamp_update (screen); /* this is to keep other clients from thinking that the X server re-detected things by itself - bgo#621046 */
4220+
4221+ gdk_x11_display_ungrab (gdk_screen_get_display (screen->priv->gdk_screen));
4222+
4223+ return refreshed;
4224+}
4225+
4226+/**
4227+ * cc_rr_screen_get_dpms_mode:
4228+ * @mode: (out): The current #CCRRDpmsMode of this screen
4229+ **/
4230+gboolean
4231+cc_rr_screen_get_dpms_mode (CCRRScreen *screen,
4232+ CCRRDpmsMode *mode,
4233+ GError **error)
4234+{
4235+ BOOL enabled = FALSE;
4236+ CARD16 state;
4237+ gboolean ret = FALSE;
4238+
4239+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4240+ g_return_val_if_fail (mode != NULL, FALSE);
4241+
4242+ if (!screen->priv->dpms_capable) {
4243+ g_set_error_literal (error,
4244+ CC_RR_ERROR,
4245+ CC_RR_ERROR_NO_DPMS_EXTENSION,
4246+ "Display is not DPMS capable");
4247+ goto out;
4248+ }
4249+
4250+ if (!DPMSInfo (screen->priv->xdisplay,
4251+ &state,
4252+ &enabled)) {
4253+ g_set_error_literal (error,
4254+ CC_RR_ERROR,
4255+ CC_RR_ERROR_UNKNOWN,
4256+ "Unable to get DPMS state");
4257+ goto out;
4258+ }
4259+
4260+ /* DPMS not enabled is a valid mode */
4261+ if (!enabled) {
4262+ *mode = CC_RR_DPMS_DISABLED;
4263+ ret = TRUE;
4264+ goto out;
4265+ }
4266+
4267+ switch (state) {
4268+ case DPMSModeOn:
4269+ *mode = CC_RR_DPMS_ON;
4270+ break;
4271+ case DPMSModeStandby:
4272+ *mode = CC_RR_DPMS_STANDBY;
4273+ break;
4274+ case DPMSModeSuspend:
4275+ *mode = CC_RR_DPMS_SUSPEND;
4276+ break;
4277+ case DPMSModeOff:
4278+ *mode = CC_RR_DPMS_OFF;
4279+ break;
4280+ default:
4281+ g_assert_not_reached ();
4282+ break;
4283+ }
4284+ ret = TRUE;
4285+out:
4286+ return ret;
4287+}
4288+
4289+/**
4290+ * cc_rr_screen_clear_dpms_timeouts:
4291+ **/
4292+static gboolean
4293+cc_rr_screen_clear_dpms_timeouts (CCRRScreen *screen,
4294+ GError **error)
4295+{
4296+ gdk_error_trap_push ();
4297+ /* DPMSSetTimeouts() return value is often a lie, so ignore it */
4298+ DPMSSetTimeouts (screen->priv->xdisplay, 0, 0, 0);
4299+ if (gdk_error_trap_pop ()) {
4300+ g_set_error_literal (error,
4301+ CC_RR_ERROR,
4302+ CC_RR_ERROR_UNKNOWN,
4303+ "Could not set DPMS timeouts");
4304+ return FALSE;
4305+ }
4306+ return TRUE;
4307+}
4308+
4309+/**
4310+ * cc_rr_screen_set_dpms_mode:
4311+ *
4312+ * This method also disables the DPMS timeouts.
4313+ **/
4314+gboolean
4315+cc_rr_screen_set_dpms_mode (CCRRScreen *screen,
4316+ CCRRDpmsMode mode,
4317+ GError **error)
4318+{
4319+ CARD16 state = 0;
4320+ gboolean ret;
4321+ CCRRDpmsMode current_mode;
4322+
4323+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4324+
4325+ /* set, if the new mode is different */
4326+ ret = cc_rr_screen_get_dpms_mode (screen, &current_mode, error);
4327+ if (!ret)
4328+ goto out;
4329+ if (current_mode == mode) {
4330+ ret = cc_rr_screen_clear_dpms_timeouts (screen, error);
4331+ goto out;
4332+ }
4333+
4334+ switch (mode) {
4335+ case CC_RR_DPMS_ON:
4336+ state = DPMSModeOn;
4337+ break;
4338+ case CC_RR_DPMS_STANDBY:
4339+ state = DPMSModeStandby;
4340+ break;
4341+ case CC_RR_DPMS_SUSPEND:
4342+ state = DPMSModeSuspend;
4343+ break;
4344+ case CC_RR_DPMS_OFF:
4345+ state = DPMSModeOff;
4346+ break;
4347+ default:
4348+ g_assert_not_reached ();
4349+ break;
4350+ }
4351+
4352+ gdk_error_trap_push ();
4353+ /* DPMSForceLevel() return value is often a lie, so ignore it */
4354+ DPMSForceLevel (screen->priv->xdisplay, state);
4355+ XSync (screen->priv->xdisplay, False);
4356+ if (gdk_error_trap_pop ()) {
4357+ ret = FALSE;
4358+ g_set_error_literal (error,
4359+ CC_RR_ERROR,
4360+ CC_RR_ERROR_UNKNOWN,
4361+ "Could not change DPMS mode");
4362+ goto out;
4363+ }
4364+
4365+ ret = cc_rr_screen_clear_dpms_timeouts (screen, error);
4366+ if (!ret)
4367+ goto out;
4368+out:
4369+ return ret;
4370+}
4371+
4372+/**
4373+ * cc_rr_screen_list_modes:
4374+ *
4375+ * List available XRandR modes
4376+ *
4377+ * Returns: (array zero-terminated=1) (transfer none):
4378+ */
4379+CCRRMode **
4380+cc_rr_screen_list_modes (CCRRScreen *screen)
4381+{
4382+ g_return_val_if_fail (CC_IS_RR_SCREEN (screen), NULL);
4383+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
4384+
4385+ return screen->priv->info->modes;
4386+}
4387+
4388+/**
4389+ * cc_rr_screen_list_clone_modes:
4390+ *
4391+ * List available XRandR clone modes
4392+ *
4393+ * Returns: (array zero-terminated=1) (transfer none):
4394+ */
4395+CCRRMode **
4396+cc_rr_screen_list_clone_modes (CCRRScreen *screen)
4397+{
4398+ g_return_val_if_fail (CC_IS_RR_SCREEN (screen), NULL);
4399+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
4400+
4401+ return screen->priv->info->clone_modes;
4402+}
4403+
4404+/**
4405+ * cc_rr_screen_list_crtcs:
4406+ *
4407+ * List all CRTCs
4408+ *
4409+ * Returns: (array zero-terminated=1) (transfer none):
4410+ */
4411+CCRRCrtc **
4412+cc_rr_screen_list_crtcs (CCRRScreen *screen)
4413+{
4414+ g_return_val_if_fail (CC_IS_RR_SCREEN (screen), NULL);
4415+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
4416+
4417+ return screen->priv->info->crtcs;
4418+}
4419+
4420+/**
4421+ * cc_rr_screen_list_outputs:
4422+ *
4423+ * List all outputs
4424+ *
4425+ * Returns: (array zero-terminated=1) (transfer none):
4426+ */
4427+CCRROutput **
4428+cc_rr_screen_list_outputs (CCRRScreen *screen)
4429+{
4430+ g_return_val_if_fail (CC_IS_RR_SCREEN (screen), NULL);
4431+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
4432+
4433+ return screen->priv->info->outputs;
4434+}
4435+
4436+/**
4437+ * cc_rr_screen_get_crtc_by_id:
4438+ *
4439+ * Returns: (transfer none): the CRTC identified by @id
4440+ */
4441+CCRRCrtc *
4442+cc_rr_screen_get_crtc_by_id (CCRRScreen *screen,
4443+ guint32 id)
4444+{
4445+ CCRRCrtc **crtcs;
4446+ int i;
4447+
4448+ g_return_val_if_fail (CC_IS_RR_SCREEN (screen), NULL);
4449+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
4450+
4451+ crtcs = screen->priv->info->crtcs;
4452+
4453+ for (i = 0; crtcs[i] != NULL; ++i)
4454+ {
4455+ if (crtcs[i]->id == id)
4456+ return crtcs[i];
4457+ }
4458+
4459+ return NULL;
4460+}
4461+
4462+/**
4463+ * cc_rr_screen_get_output_by_id:
4464+ *
4465+ * Returns: (transfer none): the output identified by @id
4466+ */
4467+CCRROutput *
4468+cc_rr_screen_get_output_by_id (CCRRScreen *screen,
4469+ guint32 id)
4470+{
4471+ CCRROutput **outputs;
4472+ int i;
4473+
4474+ g_return_val_if_fail (CC_IS_RR_SCREEN (screen), NULL);
4475+ g_return_val_if_fail (screen->priv->info != NULL, NULL);
4476+
4477+ outputs = screen->priv->info->outputs;
4478+
4479+ for (i = 0; outputs[i] != NULL; ++i)
4480+ {
4481+ if (outputs[i]->id == id)
4482+ return outputs[i];
4483+ }
4484+
4485+ return NULL;
4486+}
4487+
4488+/* CCRROutput */
4489+static CCRROutput *
4490+output_new (ScreenInfo *info, RROutput id)
4491+{
4492+ CCRROutput *output = g_slice_new0 (CCRROutput);
4493+
4494+ output->id = id;
4495+ output->info = info;
4496+
4497+ return output;
4498+}
4499+
4500+static guint8 *
4501+get_property (Display *dpy,
4502+ RROutput output,
4503+ Atom atom,
4504+ gsize *len)
4505+{
4506+ unsigned char *prop;
4507+ int actual_format;
4508+ unsigned long nitems, bytes_after;
4509+ Atom actual_type;
4510+ guint8 *result;
4511+
4512+ XRRGetOutputProperty (dpy, output, atom,
4513+ 0, 100, False, False,
4514+ AnyPropertyType,
4515+ &actual_type, &actual_format,
4516+ &nitems, &bytes_after, &prop);
4517+
4518+ if (actual_type == XA_INTEGER && actual_format == 8)
4519+ {
4520+ result = g_memdup (prop, nitems);
4521+ if (len)
4522+ *len = nitems;
4523+ }
4524+ else
4525+ {
4526+ result = NULL;
4527+ }
4528+
4529+ XFree (prop);
4530+
4531+ return result;
4532+}
4533+
4534+static guint8 *
4535+read_edid_data (CCRROutput *output, gsize *len)
4536+{
4537+ Atom edid_atom;
4538+ guint8 *result;
4539+
4540+ edid_atom = XInternAtom (DISPLAY (output), "EDID", FALSE);
4541+ result = get_property (DISPLAY (output),
4542+ output->id, edid_atom, len);
4543+
4544+ if (!result)
4545+ {
4546+ edid_atom = XInternAtom (DISPLAY (output), "EDID_DATA", FALSE);
4547+ result = get_property (DISPLAY (output),
4548+ output->id, edid_atom, len);
4549+ }
4550+
4551+ if (!result)
4552+ {
4553+ edid_atom = XInternAtom (DISPLAY (output), "XFree86_DDC_EDID1_RAWDATA", FALSE);
4554+ result = get_property (DISPLAY (output),
4555+ output->id, edid_atom, len);
4556+ }
4557+
4558+ if (result)
4559+ {
4560+ if (*len % 128 == 0)
4561+ return result;
4562+ else
4563+ g_free (result);
4564+ }
4565+
4566+ return NULL;
4567+}
4568+
4569+static char *
4570+get_connector_type_string (CCRROutput *output)
4571+{
4572+ char *result;
4573+ unsigned char *prop;
4574+ int actual_format;
4575+ unsigned long nitems, bytes_after;
4576+ Atom actual_type;
4577+ Atom connector_type;
4578+ char *connector_type_str;
4579+
4580+ result = NULL;
4581+
4582+ if (XRRGetOutputProperty (DISPLAY (output), output->id, output->info->screen->priv->connector_type_atom,
4583+ 0, 100, False, False,
4584+ AnyPropertyType,
4585+ &actual_type, &actual_format,
4586+ &nitems, &bytes_after, &prop) != Success)
4587+ return NULL;
4588+
4589+ if (!(actual_type == XA_ATOM && actual_format == 32 && nitems == 1))
4590+ goto out;
4591+
4592+ connector_type = *((Atom *) prop);
4593+
4594+ connector_type_str = XGetAtomName (DISPLAY (output), connector_type);
4595+ if (connector_type_str) {
4596+ result = g_strdup (connector_type_str); /* so the caller can g_free() it */
4597+ XFree (connector_type_str);
4598+ }
4599+
4600+out:
4601+
4602+ XFree (prop);
4603+
4604+ return result;
4605+}
4606+
4607+static void
4608+update_brightness_limits (CCRROutput *output)
4609+{
4610+ gint rc;
4611+ Atom atom;
4612+ XRRPropertyInfo *info;
4613+
4614+ gdk_error_trap_push ();
4615+ atom = XInternAtom (DISPLAY (output), "Backlight", FALSE);
4616+ info = XRRQueryOutputProperty (DISPLAY (output), output->id, atom);
4617+ rc = gdk_error_trap_pop ();
4618+ if (rc != Success)
4619+ {
4620+ if (rc != BadName)
4621+ g_warning ("could not get output property for %s, rc: %i",
4622+ output->name, rc);
4623+ goto out;
4624+ }
4625+ if (info == NULL)
4626+ {
4627+ g_warning ("could not get output property for %s",
4628+ output->name);
4629+ goto out;
4630+ }
4631+ if (!info->range || info->num_values != 2)
4632+ {
4633+ g_debug ("backlight %s was not range", output->name);
4634+ goto out;
4635+ }
4636+ output->backlight_min = info->values[0];
4637+ output->backlight_max = info->values[1];
4638+out:
4639+ if (info != NULL)
4640+ {
4641+ XFree (info);
4642+ }
4643+}
4644+
4645+static gboolean
4646+output_initialize (CCRROutput *output, XRRScreenResources *res, GError **error)
4647+{
4648+ XRROutputInfo *info = XRRGetOutputInfo (
4649+ DISPLAY (output), res, output->id);
4650+ GPtrArray *a;
4651+ int i;
4652+
4653+#if 0
4654+ g_print ("Output %lx Timestamp: %u\n", output->id, (guint32)info->timestamp);
4655+#endif
4656+
4657+ if (!info || !output->info)
4658+ {
4659+ /* FIXME: see the comment in crtc_initialize() */
4660+ /* Translators: here, an "output" is a video output */
4661+ g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_RANDR_ERROR,
4662+ _("could not get information about output %d"),
4663+ (int) output->id);
4664+ return FALSE;
4665+ }
4666+
4667+ output->name = g_strdup (info->name); /* FIXME: what is nameLen used for? */
4668+ output->display_name = NULL; /* set first time the getter is used */
4669+ output->current_crtc = crtc_by_id (output->info, info->crtc);
4670+ output->width_mm = info->mm_width;
4671+ output->height_mm = info->mm_height;
4672+ output->connected = (info->connection == RR_Connected);
4673+ output->connector_type = get_connector_type_string (output);
4674+
4675+ /* Possible crtcs */
4676+ a = g_ptr_array_new ();
4677+
4678+ for (i = 0; i < info->ncrtc; ++i)
4679+ {
4680+ CCRRCrtc *crtc = crtc_by_id (output->info, info->crtcs[i]);
4681+
4682+ if (crtc)
4683+ g_ptr_array_add (a, crtc);
4684+ }
4685+ g_ptr_array_add (a, NULL);
4686+ output->possible_crtcs = (CCRRCrtc **)g_ptr_array_free (a, FALSE);
4687+
4688+ /* Clones */
4689+ a = g_ptr_array_new ();
4690+ for (i = 0; i < info->nclone; ++i)
4691+ {
4692+ CCRROutput *cc_rr_output = cc_rr_output_by_id (output->info, info->clones[i]);
4693+
4694+ if (cc_rr_output)
4695+ g_ptr_array_add (a, cc_rr_output);
4696+ }
4697+ g_ptr_array_add (a, NULL);
4698+ output->clones = (CCRROutput **)g_ptr_array_free (a, FALSE);
4699+
4700+ /* Modes */
4701+ a = g_ptr_array_new ();
4702+ for (i = 0; i < info->nmode; ++i)
4703+ {
4704+ CCRRMode *mode = mode_by_id (output->info, info->modes[i]);
4705+
4706+ if (mode)
4707+ g_ptr_array_add (a, mode);
4708+ }
4709+ g_ptr_array_add (a, NULL);
4710+ output->modes = (CCRRMode **)g_ptr_array_free (a, FALSE);
4711+
4712+ output->n_preferred = info->npreferred;
4713+
4714+ /* Edid data */
4715+ output->edid_data = read_edid_data (output, &output->edid_size);
4716+
4717+ /* brightness data */
4718+ if (output->connected)
4719+ update_brightness_limits (output);
4720+
4721+ XRRFreeOutputInfo (info);
4722+
4723+ return TRUE;
4724+}
4725+
4726+static CCRROutput*
4727+output_copy (const CCRROutput *from)
4728+{
4729+ GPtrArray *array;
4730+ CCRRCrtc **p_crtc;
4731+ CCRROutput **p_output;
4732+ CCRRMode **p_mode;
4733+ CCRROutput *output = g_slice_new0 (CCRROutput);
4734+
4735+ output->id = from->id;
4736+ output->info = from->info;
4737+ output->name = g_strdup (from->name);
4738+ output->current_crtc = from->current_crtc;
4739+ output->width_mm = from->width_mm;
4740+ output->height_mm = from->height_mm;
4741+ output->connected = from->connected;
4742+ output->n_preferred = from->n_preferred;
4743+ output->connector_type = g_strdup (from->connector_type);
4744+ output->backlight_min = -1;
4745+ output->backlight_max = -1;
4746+
4747+ array = g_ptr_array_new ();
4748+ for (p_crtc = from->possible_crtcs; *p_crtc != NULL; p_crtc++)
4749+ {
4750+ g_ptr_array_add (array, *p_crtc);
4751+ }
4752+ output->possible_crtcs = (CCRRCrtc**) g_ptr_array_free (array, FALSE);
4753+
4754+ array = g_ptr_array_new ();
4755+ for (p_output = from->clones; *p_output != NULL; p_output++)
4756+ {
4757+ g_ptr_array_add (array, *p_output);
4758+ }
4759+ output->clones = (CCRROutput**) g_ptr_array_free (array, FALSE);
4760+
4761+ array = g_ptr_array_new ();
4762+ for (p_mode = from->modes; *p_mode != NULL; p_mode++)
4763+ {
4764+ g_ptr_array_add (array, *p_mode);
4765+ }
4766+ output->modes = (CCRRMode**) g_ptr_array_free (array, FALSE);
4767+
4768+ output->edid_size = from->edid_size;
4769+ output->edid_data = g_memdup (from->edid_data, from->edid_size);
4770+
4771+ return output;
4772+}
4773+
4774+static void
4775+output_free (CCRROutput *output)
4776+{
4777+ g_free (output->clones);
4778+ g_free (output->modes);
4779+ g_free (output->possible_crtcs);
4780+ g_free (output->edid_data);
4781+ g_free (output->name);
4782+ g_free (output->display_name);
4783+ g_free (output->connector_type);
4784+ g_slice_free (CCRROutput, output);
4785+}
4786+
4787+guint32
4788+cc_rr_output_get_id (CCRROutput *output)
4789+{
4790+ g_assert(output != NULL);
4791+
4792+ return output->id;
4793+}
4794+
4795+const guint8 *
4796+cc_rr_output_get_edid_data (CCRROutput *output, gsize *size)
4797+{
4798+ g_return_val_if_fail (output != NULL, NULL);
4799+ if (size)
4800+ *size = output->edid_size;
4801+ return output->edid_data;
4802+}
4803+
4804+/**
4805+ * cc_rr_output_get_ids_from_edid:
4806+ * @output: a #CCRROutput
4807+ * @vendor: (out) (allow-none):
4808+ * @product: (out) (allow-none):
4809+ * @serial: (out) (allow-none):
4810+ */
4811+gboolean
4812+cc_rr_output_get_ids_from_edid (CCRROutput *output,
4813+ char **vendor,
4814+ int *product,
4815+ int *serial)
4816+{
4817+ MonitorInfo *info;
4818+
4819+ g_return_val_if_fail (output != NULL, FALSE);
4820+
4821+ if (!output->edid_data)
4822+ return FALSE;
4823+ info = decode_edid (output->edid_data);
4824+ if (!info)
4825+ return FALSE;
4826+ if (vendor)
4827+ *vendor = g_memdup (info->manufacturer_code, 4);
4828+ if (product)
4829+ *product = info->product_code;
4830+ if (serial)
4831+ *serial = info->serial_number;
4832+
4833+ g_free (info);
4834+
4835+ return TRUE;
4836+
4837+}
4838+
4839+static void
4840+ensure_display_name (CCRROutput *output)
4841+{
4842+ if (output->display_name != NULL)
4843+ return;
4844+
4845+ if (cc_rr_output_is_laptop (output))
4846+ output->display_name = g_strdup (_("Built-in Display"));
4847+
4848+ if (output->display_name == NULL
4849+ && output->edid_data != NULL) {
4850+ MonitorInfo *info;
4851+
4852+ info = decode_edid (output->edid_data);
4853+ if (info != NULL)
4854+ output->display_name = make_display_name (info);
4855+
4856+ g_free (info);
4857+ }
4858+
4859+ if (output->display_name == NULL) {
4860+ char *inches;
4861+ inches = make_display_size_string (output->width_mm, output->height_mm);
4862+ if (inches != NULL) {
4863+ /* Translators: %s is the size of the monitor in inches */
4864+ output->display_name = g_strdup_printf (_("%s Display"), inches);
4865+ }
4866+ g_free (inches);
4867+ }
4868+
4869+ /* last chance on the stairway */
4870+ if (output->display_name == NULL) {
4871+ output->display_name = g_strdup (_("Unknown Display"));
4872+ }
4873+}
4874+
4875+const char *
4876+cc_rr_output_get_display_name (CCRROutput *output)
4877+{
4878+ g_return_val_if_fail (output != NULL, NULL);
4879+
4880+ ensure_display_name (output);
4881+
4882+ return output->display_name;
4883+}
4884+
4885+/**
4886+ * cc_rr_output_get_backlight_min:
4887+ *
4888+ * Returns: The mimimum backlight value, or -1 if not supported
4889+ */
4890+gint
4891+cc_rr_output_get_backlight_min (CCRROutput *output)
4892+{
4893+ g_return_val_if_fail (output != NULL, -1);
4894+ return output->backlight_min;
4895+}
4896+
4897+/**
4898+ * cc_rr_output_get_backlight_max:
4899+ *
4900+ * Returns: The maximum backlight value, or -1 if not supported
4901+ */
4902+gint
4903+cc_rr_output_get_backlight_max (CCRROutput *output)
4904+{
4905+ g_return_val_if_fail (output != NULL, -1);
4906+ return output->backlight_max;
4907+}
4908+
4909+/**
4910+ * cc_rr_output_get_backlight:
4911+ *
4912+ * Returns: The currently set backlight brightness
4913+ */
4914+gint
4915+cc_rr_output_get_backlight (CCRROutput *output, GError **error)
4916+{
4917+ guint now = -1;
4918+ unsigned long nitems;
4919+ unsigned long bytes_after;
4920+ guint *prop;
4921+ Atom atom;
4922+ Atom actual_type;
4923+ int actual_format;
4924+ gint retval;
4925+
4926+ g_return_val_if_fail (output != NULL, -1);
4927+
4928+ gdk_error_trap_push ();
4929+ atom = XInternAtom (DISPLAY (output), "Backlight", FALSE);
4930+ retval = XRRGetOutputProperty (DISPLAY (output), output->id, atom,
4931+ 0, 4, False, False, None,
4932+ &actual_type, &actual_format,
4933+ &nitems, &bytes_after, ((unsigned char **)&prop));
4934+ gdk_flush ();
4935+ if (gdk_error_trap_pop ())
4936+ {
4937+ g_set_error_literal (error,
4938+ CC_RR_ERROR,
4939+ CC_RR_ERROR_UNKNOWN,
4940+ "unhandled X error while getting the range of backlight values");
4941+ goto out;
4942+ }
4943+
4944+ if (retval != Success) {
4945+ g_set_error_literal (error,
4946+ CC_RR_ERROR,
4947+ CC_RR_ERROR_RANDR_ERROR,
4948+ "could not get the range of backlight values");
4949+ goto out;
4950+ }
4951+ if (actual_type == XA_INTEGER &&
4952+ nitems == 1 &&
4953+ actual_format == 32)
4954+ {
4955+ memcpy (&now, prop, sizeof (guint));
4956+ }
4957+ else
4958+ {
4959+ g_set_error (error,
4960+ CC_RR_ERROR,
4961+ CC_RR_ERROR_RANDR_ERROR,
4962+ "failed to get correct property type, got %lu,%i",
4963+ nitems, actual_format);
4964+ }
4965+out:
4966+ XFree (prop);
4967+ return now;
4968+}
4969+
4970+/**
4971+ * cc_rr_output_set_backlight:
4972+ * @value: the absolute value which is min >= this <= max
4973+ *
4974+ * Returns: %TRUE for success
4975+ */
4976+gboolean
4977+cc_rr_output_set_backlight (CCRROutput *output, gint value, GError **error)
4978+{
4979+ gboolean ret = FALSE;
4980+ Atom atom;
4981+
4982+ g_return_val_if_fail (output != NULL, FALSE);
4983+
4984+ /* check this is sane */
4985+ if (value < output->backlight_min ||
4986+ value > output->backlight_max)
4987+ {
4988+ g_set_error (error,
4989+ CC_RR_ERROR,
4990+ CC_RR_ERROR_BOUNDS_ERROR,
4991+ "out of brightness range: %i, has to be %i -> %i",
4992+ value,
4993+ output->backlight_max, output->backlight_min);
4994+ goto out;
4995+ }
4996+
4997+ /* don't abort on error */
4998+ gdk_error_trap_push ();
4999+ atom = XInternAtom (DISPLAY (output), "Backlight", FALSE);
5000+ XRRChangeOutputProperty (DISPLAY (output), output->id, atom,
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches