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

Proposed by Robert Ancell
Status: Rejected
Rejected by: Robert Ancell
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
Unity Control Center development team 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.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Robert Ancell (robert-ancell) wrote :
Revision history for this message
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

Revision history for this message
Tim Lunn (darkxst) wrote :

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

Revision history for this message
Robert Ancell (robert-ancell) wrote :

Unmerged revisions

12784. By Robert Ancell

install check_gl_texture_size

12783. By Robert Ancell

Fix some renaming

12782. By Robert Ancell

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
=== modified file 'configure.ac'
--- configure.ac 2014-02-26 20:00:59 +0000
+++ configure.ac 2014-06-26 03:13:28 +0000
@@ -104,6 +104,8 @@
104CLUTTER_REQUIRED_VERSION=1.11.3104CLUTTER_REQUIRED_VERSION=1.11.3
105GOA_REQUIRED_VERSION=3.5.90105GOA_REQUIRED_VERSION=3.5.90
106ACCOUNTSSERVICE_REQUIRED_VERSION=0.6.30106ACCOUNTSSERVICE_REQUIRED_VERSION=0.6.30
107XRANDR_REQUIRED=1.3
108XEXT_REQUIRED=1.1
107109
108COMMON_MODULES="gtk+-3.0 >= $GTK_REQUIRED_VERSION110COMMON_MODULES="gtk+-3.0 >= $GTK_REQUIRED_VERSION
109 glib-2.0 >= $GLIB_REQUIRED_VERSION111 glib-2.0 >= $GLIB_REQUIRED_VERSION
@@ -115,6 +117,8 @@
115117
116PKG_CHECK_MODULES(LIBUNITY_CONTROL_CENTER, $COMMON_MODULES)118PKG_CHECK_MODULES(LIBUNITY_CONTROL_CENTER, $COMMON_MODULES)
117PKG_CHECK_MODULES(LIBLANGUAGE, $COMMON_MODULES gnome-desktop-3.0 fontconfig)119PKG_CHECK_MODULES(LIBLANGUAGE, $COMMON_MODULES gnome-desktop-3.0 fontconfig)
120PKG_CHECK_MODULES(LIBRR, $COMMON_MODULES xrandr >= $XRANDR_REQUIRED xext >= $XEXT_REQUIRED)
121PKG_CHECK_MODULES(CHECK_GL_TEXTURE_SIZE, gl x11)
118PKG_CHECK_MODULES(LIBSHORTCUTS, $COMMON_MODULES x11)122PKG_CHECK_MODULES(LIBSHORTCUTS, $COMMON_MODULES x11)
119PKG_CHECK_MODULES(SHELL, $COMMON_MODULES libgnome-menu-3.0 gio-unix-2.0 x11)123PKG_CHECK_MODULES(SHELL, $COMMON_MODULES libgnome-menu-3.0 gio-unix-2.0 x11)
120PKG_CHECK_MODULES(APPEARANCE_PANEL, $COMMON_MODULES libxml-2.0 gnome-desktop-3.0124PKG_CHECK_MODULES(APPEARANCE_PANEL, $COMMON_MODULES libxml-2.0 gnome-desktop-3.0
121125
=== modified file 'debian/unity-control-center.install'
--- debian/unity-control-center.install 2014-02-24 17:28:38 +0000
+++ debian/unity-control-center.install 2014-06-26 03:13:28 +0000
@@ -1,5 +1,6 @@
1etc/xdg/menus1etc/xdg/menus
2usr/bin2usr/bin
3usr/lib/unity-control-center
3usr/lib/*/unity-control-center-1/panels/*.so4usr/lib/*/unity-control-center-1/panels/*.so
4usr/share/applications5usr/share/applications
5usr/share/bash-completion6usr/share/bash-completion
67
=== modified file 'panels/common/Makefile.am'
--- panels/common/Makefile.am 2013-11-29 06:28:37 +0000
+++ panels/common/Makefile.am 2014-06-26 03:13:28 +0000
@@ -1,14 +1,17 @@
1# This is used in PANEL_CFLAGS1# This is used in PANEL_CFLAGS
2cappletname = common2cappletname = common
33
4noinst_LTLIBRARIES = liblanguage.la4noinst_LTLIBRARIES = liblanguage.la librr.la
5noinst_PROGRAMS = list-languages5noinst_PROGRAMS = list-languages
6libexec_PROGRAMS = check_gl_texture_size
67
7AM_CPPFLAGS = \8AM_CPPFLAGS = \
8 $(PANEL_CFLAGS) \9 $(PANEL_CFLAGS) \
9 $(LIBLANGUAGE_CFLAGS) \10 $(LIBLANGUAGE_CFLAGS) \
10 -DDATADIR=\""$(datadir)"\" \11 -DDATADIR=\""$(datadir)"\" \
12 -DLIBEXECDIR=\""$(libexecdir)\"" \
11 -DUIDIR=\""$(pkgdatadir)/ui"\" \13 -DUIDIR=\""$(pkgdatadir)/ui"\" \
14 -DPNP_IDS=\""$(datadir)/hwdata/pnp.ids"\" \
12 -DLIBLOCALEDIR=\""$(prefix)/lib/locale"\" \15 -DLIBLOCALEDIR=\""$(prefix)/lib/locale"\" \
13 -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \16 -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
14 -DUM_PIXMAP_DIR=\""$(pkgdatadir)/pixmaps"\"17 -DUM_PIXMAP_DIR=\""$(pkgdatadir)/pixmaps"\"
@@ -32,6 +35,31 @@
32list_languages_LDADD = liblanguage.la35list_languages_LDADD = liblanguage.la
33list_languages_CFLAGS = $(LIBLANGUAGE_CFLAGS)36list_languages_CFLAGS = $(LIBLANGUAGE_CFLAGS)
3437
38librr_la_SOURCES = \
39 cc-pnp-ids.c \
40 cc-pnp-ids.h \
41 cc-rr.c \
42 cc-rr.h \
43 cc-rr-config.c \
44 cc-rr-config.h \
45 cc-rr-output-info.c \
46 cc-rr-private.h \
47 display-name.c \
48 edid-parse.c \
49 edid.h
50
51librr_la_LIBADD = \
52 $(PANEL_LIBS) \
53 $(LIBRR_LIBS)
54
55librr_la_LDFLAGS = $(PANEL_LDFLAGS)
56
57check_gl_texture_size_CPPFLAGS = \
58 $(CHECK_GL_TEXTURE_SIZE_CFLAGS)
59
60check_gl_texture_size_LDADD = \
61 $(CHECK_GL_TEXTURE_SIZE_LIBS)
62
35uidir = $(pkgdatadir)/ui63uidir = $(pkgdatadir)/ui
3664
37dist_ui_DATA = \65dist_ui_DATA = \
3866
=== added file 'panels/common/cc-pnp-ids.c'
--- panels/common/cc-pnp-ids.c 1970-01-01 00:00:00 +0000
+++ panels/common/cc-pnp-ids.c 2014-06-26 03:13:28 +0000
@@ -0,0 +1,341 @@
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2009-2011 Richard Hughes <richard@hughsie.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "config.h"
21
22#include <glib-object.h>
23
24#include <cc-pnp-ids.h>
25
26static void cc_pnp_ids_finalize (GObject *object);
27
28#define CC_PNP_IDS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_PNP_IDS, CCPnpIdsPrivate))
29
30struct _CCPnpIdsPrivate
31{
32 gchar *table_data;
33 GHashTable *pnp_table;
34};
35
36static gpointer cc_pnp_ids_object = NULL;
37
38G_DEFINE_TYPE (CCPnpIds, cc_pnp_ids, G_TYPE_OBJECT)
39
40typedef struct Vendor Vendor;
41struct Vendor
42{
43 const char vendor_id[4];
44 const char vendor_name[28];
45};
46
47/* This list of vendor codes derived from lshw
48 *
49 * http://ezix.org/project/wiki/HardwareLiSter
50 *
51 * Note: we now prefer to use data coming from hwdata (and shipped with
52 * gnome-desktop). See
53 * http://git.fedorahosted.org/git/?p=hwdata.git;a=blob_plain;f=pnp.ids;hb=HEAD
54 * All contributions to the list of vendors should go there.
55 */
56static const struct Vendor vendors[] =
57{
58 { "AIC", "AG Neovo" },
59 { "ACR", "Acer" },
60 { "DEL", "DELL" },
61 { "SAM", "SAMSUNG" },
62 { "SNY", "SONY" },
63 { "SEC", "Epson" },
64 { "WAC", "Wacom" },
65 { "NEC", "NEC" },
66 { "CMO", "CMO" }, /* Chi Mei */
67 { "BNQ", "BenQ" },
68
69 { "ABP", "Advansys" },
70 { "ACC", "Accton" },
71 { "ACE", "Accton" },
72 { "ADP", "Adaptec" },
73 { "ADV", "AMD" },
74 { "AIR", "AIR" },
75 { "AMI", "AMI" },
76 { "ASU", "ASUS" },
77 { "ATI", "ATI" },
78 { "ATK", "Allied Telesyn" },
79 { "AZT", "Aztech" },
80 { "BAN", "Banya" },
81 { "BRI", "Boca Research" },
82 { "BUS", "Buslogic" },
83 { "CCI", "Cache Computers Inc." },
84 { "CHA", "Chase" },
85 { "CMD", "CMD Technology, Inc." },
86 { "COG", "Cogent" },
87 { "CPQ", "Compaq" },
88 { "CRS", "Crescendo" },
89 { "CSC", "Crystal" },
90 { "CSI", "CSI" },
91 { "CTL", "Creative Labs" },
92 { "DBI", "Digi" },
93 { "DEC", "Digital Equipment" },
94 { "DBK", "Databook" },
95 { "EGL", "Eagle Technology" },
96 { "ELS", "ELSA" },
97 { "ESS", "ESS" },
98 { "FAR", "Farallon" },
99 { "FDC", "Future Domain" },
100 { "HWP", "Hewlett-Packard" },
101 { "IBM", "IBM" },
102 { "INT", "Intel" },
103 { "ISA", "Iomega" },
104 { "LEN", "Lenovo" },
105 { "MDG", "Madge" },
106 { "MDY", "Microdyne" },
107 { "MET", "Metheus" },
108 { "MIC", "Micronics" },
109 { "MLX", "Mylex" },
110 { "NVL", "Novell" },
111 { "OLC", "Olicom" },
112 { "PRO", "Proteon" },
113 { "RII", "Racal" },
114 { "RTL", "Realtek" },
115 { "SCM", "SCM" },
116 { "SKD", "SysKonnect" },
117 { "SGI", "SGI" },
118 { "SMC", "SMC" },
119 { "SNI", "Siemens Nixdorf" },
120 { "STL", "Stallion Technologies" },
121 { "SUN", "Sun" },
122 { "SUP", "SupraExpress" },
123 { "SVE", "SVEC" },
124 { "TCC", "Thomas-Conrad" },
125 { "TCI", "Tulip" },
126 { "TCM", "3Com" },
127 { "TCO", "Thomas-Conrad" },
128 { "TEC", "Tecmar" },
129 { "TRU", "Truevision" },
130 { "TOS", "Toshiba" },
131 { "TYN", "Tyan" },
132 { "UBI", "Ungermann-Bass" },
133 { "USC", "UltraStor" },
134 { "VDM", "Vadem" },
135 { "VMI", "Vermont" },
136 { "WDC", "Western Digital" },
137 { "ZDS", "Zeos" },
138
139 /* From http://faydoc.tripod.com/structures/01/0136.htm */
140 { "ACT", "Targa" },
141 { "ADI", "ADI" },
142 { "AOC", "AOC Intl" },
143 { "API", "Acer America" },
144 { "APP", "Apple Computer" },
145 { "ART", "ArtMedia" },
146 { "AST", "AST Research" },
147 { "CPL", "Compal" },
148 { "CTX", "Chuntex Electronic Co." },
149 { "DPC", "Delta Electronics" },
150 { "DWE", "Daewoo" },
151 { "ECS", "ELITEGROUP" },
152 { "EIZ", "EIZO" },
153 { "FCM", "Funai" },
154 { "GSM", "LG Electronics" },
155 { "GWY", "Gateway 2000" },
156 { "HEI", "Hyundai" },
157 { "HIT", "Hitachi" },
158 { "HSL", "Hansol" },
159 { "HTC", "Hitachi" },
160 { "ICL", "Fujitsu ICL" },
161 { "IVM", "Idek Iiyama" },
162 { "KFC", "KFC Computek" },
163 { "LKM", "ADLAS" },
164 { "LNK", "LINK Tech" },
165 { "LTN", "Lite-On" },
166 { "MAG", "MAG InnoVision" },
167 { "MAX", "Maxdata" },
168 { "MEI", "Panasonic" },
169 { "MEL", "Mitsubishi" },
170 { "MIR", "miro" },
171 { "MTC", "MITAC" },
172 { "NAN", "NANAO" },
173 { "NEC", "NEC Tech" },
174 { "NOK", "Nokia" },
175 { "OQI", "OPTIQUEST" },
176 { "PBN", "Packard Bell" },
177 { "PGS", "Princeton" },
178 { "PHL", "Philips" },
179 { "REL", "Relisys" },
180 { "SDI", "Samtron" },
181 { "SMI", "Smile" },
182 { "SPT", "Sceptre" },
183 { "SRC", "Shamrock Technology" },
184 { "STP", "Sceptre" },
185 { "TAT", "Tatung" },
186 { "TRL", "Royal Information Company" },
187 { "TSB", "Toshiba, Inc." },
188 { "UNM", "Unisys" },
189 { "VSC", "ViewSonic" },
190 { "WTC", "Wen Tech" },
191 { "ZCM", "Zenith Data Systems" },
192
193 { "???", "Unknown" },
194};
195
196static gboolean
197cc_pnp_ids_load (CCPnpIds *pnp_ids, GError **error)
198{
199 gchar *retval = NULL;
200 CCPnpIdsPrivate *priv = pnp_ids->priv;
201 guint i;
202
203 /* load the contents */
204 g_debug ("loading: %s", PNP_IDS);
205 if (g_file_get_contents (PNP_IDS, &priv->table_data, NULL, error) == FALSE)
206 return FALSE;
207
208 /* parse into lines */
209 retval = priv->table_data;
210 for (i = 0; priv->table_data[i] != '\0'; i++) {
211
212 /* ignore */
213 if (priv->table_data[i] != '\n')
214 continue;
215
216 /* convert newline to NULL */
217 priv->table_data[i] = '\0';
218
219 /* the ID to text is a fixed offset */
220 if (retval[0] && retval[1] && retval[2] && retval[3] == '\t' && retval[4]) {
221 retval[3] = '\0';
222 g_hash_table_insert (priv->pnp_table,
223 retval,
224 retval+4);
225 retval = &priv->table_data[i+1];
226 }
227 }
228
229 g_debug ("Added %i items to the vendor hashtable", i);
230
231 return TRUE;
232}
233
234static const char *
235find_vendor (const char *pnp_id)
236{
237 guint i;
238
239 for (i = 0; i < G_N_ELEMENTS (vendors); i++) {
240 if (g_strcmp0 (vendors[i].vendor_id, pnp_id) == 0)
241 return vendors[i].vendor_name;
242 }
243
244 return NULL;
245}
246
247/**
248 * cc_pnp_ids_get_pnp_id:
249 * @pnp_ids: a #CCPnpIds object
250 * @pnp_id: the PNP ID to look for
251 *
252 * Find the full manufacturer name for the given PNP ID.
253 *
254 * Returns: (transfer full): a new string representing the manufacturer name,
255 * or %NULL when not found.
256 */
257gchar *
258cc_pnp_ids_get_pnp_id (CCPnpIds *pnp_ids, const gchar *pnp_id)
259{
260 CCPnpIdsPrivate *priv = pnp_ids->priv;
261 const char *found;
262 GError *error = NULL;
263 guint size;
264
265 g_return_val_if_fail (CC_IS_PNP_IDS (pnp_ids), NULL);
266 g_return_val_if_fail (pnp_id != NULL, NULL);
267
268 /* if table is empty, try to load it */
269 size = g_hash_table_size (priv->pnp_table);
270 if (size == 0) {
271 if (cc_pnp_ids_load (pnp_ids, &error) == FALSE) {
272 g_warning ("Failed to load PNP ids: %s", error->message);
273 g_error_free (error);
274 return NULL;
275 }
276 }
277
278 /* look this up in the table */
279 found = g_hash_table_lookup (priv->pnp_table, pnp_id);
280 if (found == NULL) {
281 found = find_vendor (pnp_id);
282 if (found == NULL)
283 return NULL;
284 }
285
286 return g_strdup (found);
287}
288
289static void
290cc_pnp_ids_class_init (CCPnpIdsClass *klass)
291{
292 GObjectClass *object_class = G_OBJECT_CLASS (klass);
293 object_class->finalize = cc_pnp_ids_finalize;
294 g_type_class_add_private (klass, sizeof (CCPnpIdsPrivate));
295}
296
297static void
298cc_pnp_ids_init (CCPnpIds *pnp_ids)
299{
300 pnp_ids->priv = CC_PNP_IDS_GET_PRIVATE (pnp_ids);
301
302 /* we don't keep malloc'd data in the hash; instead we read it
303 * out into priv->table_data and then link to it in the hash */
304 pnp_ids->priv->pnp_table = g_hash_table_new_full (g_str_hash,
305 g_str_equal,
306 NULL,
307 NULL);
308}
309
310static void
311cc_pnp_ids_finalize (GObject *object)
312{
313 CCPnpIds *pnp_ids = CC_PNP_IDS (object);
314 CCPnpIdsPrivate *priv = pnp_ids->priv;
315
316 g_free (priv->table_data);
317 g_hash_table_unref (priv->pnp_table);
318
319 G_OBJECT_CLASS (cc_pnp_ids_parent_class)->finalize (object);
320}
321
322/**
323 * cc_pnp_ids_new:
324 *
325 * Returns a reference to a #CCPnpIds object, or creates
326 * a new one if none have been created.
327 *
328 * Returns: (transfer full): a #CCPnpIds object.
329 */
330CCPnpIds *
331cc_pnp_ids_new (void)
332{
333 if (cc_pnp_ids_object != NULL) {
334 g_object_ref (cc_pnp_ids_object);
335 } else {
336 cc_pnp_ids_object = g_object_new (CC_TYPE_PNP_IDS, NULL);
337 g_object_add_weak_pointer (cc_pnp_ids_object, &cc_pnp_ids_object);
338 }
339 return CC_PNP_IDS (cc_pnp_ids_object);
340}
341
0342
=== added file 'panels/common/cc-pnp-ids.h'
--- panels/common/cc-pnp-ids.h 1970-01-01 00:00:00 +0000
+++ panels/common/cc-pnp-ids.h 2014-06-26 03:13:28 +0000
@@ -0,0 +1,58 @@
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2009-2010 Richard Hughes <richard@hughsie.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#ifndef __CC_PNP_IDS_H
21#define __CC_PNP_IDS_H
22
23#include <glib-object.h>
24
25G_BEGIN_DECLS
26
27#define CC_TYPE_PNP_IDS (cc_pnp_ids_get_type ())
28#define CC_PNP_IDS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CC_TYPE_PNP_IDS, CCPnpIds))
29#define CC_PNP_IDS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CC_TYPE_PNP_IDS, CCPnpIdsClass))
30#define CC_IS_PNP_IDS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CC_TYPE_PNP_IDS))
31#define CC_IS_PNP_IDS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CC_TYPE_PNP_IDS))
32#define CC_PNP_IDS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CC_TYPE_PNP_IDS, CCPnpIdsClass))
33#define CC_PNP_IDS_ERROR (cc_pnp_ids_error_quark ())
34
35typedef struct _CCPnpIdsPrivate CCPnpIdsPrivate;
36typedef struct _CCPnpIds CCPnpIds;
37typedef struct _CCPnpIdsClass CCPnpIdsClass;
38
39struct _CCPnpIds
40{
41 GObject parent;
42 CCPnpIdsPrivate *priv;
43};
44
45struct _CCPnpIdsClass
46{
47 GObjectClass parent_class;
48};
49
50GType cc_pnp_ids_get_type (void);
51CCPnpIds *cc_pnp_ids_new (void);
52gchar *cc_pnp_ids_get_pnp_id (CCPnpIds *pnp_ids,
53 const gchar *pnp_id);
54
55G_END_DECLS
56
57#endif /* __CC_PNP_IDS_H */
58
059
=== added file 'panels/common/cc-rr-config.c'
--- panels/common/cc-rr-config.c 1970-01-01 00:00:00 +0000
+++ panels/common/cc-rr-config.c 2014-06-26 03:13:28 +0000
@@ -0,0 +1,2091 @@
1/* gnome-rr-config.c
2 * -*- c-basic-offset: 4 -*-
3 *
4 * Copyright 2007, 2008, Red Hat, Inc.
5 * Copyright 2010 Giovanni Campagna
6 *
7 * This file is part of the Gnome Library.
8 *
9 * The Gnome Library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * The Gnome Library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with the Gnome Library; see the file COPYING.LIB. If not,
21 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 * Author: Soren Sandmann <sandmann@redhat.com>
25 */
26
27#include <config.h>
28#include <glib/gi18n-lib.h>
29#include <stdlib.h>
30#include <string.h>
31#include <glib.h>
32#include <glib/gstdio.h>
33
34#include <X11/Xlib.h>
35#include <gdk/gdkx.h>
36
37#include <unistd.h>
38#include <sys/wait.h>
39#include <signal.h>
40
41#include "cc-rr-config.h"
42
43#include "edid.h"
44#include "cc-rr-private.h"
45
46#define CONFIG_INTENDED_BASENAME "monitors.xml"
47#define CONFIG_BACKUP_BASENAME "monitors.xml.backup"
48
49/* Look for DPI_FALLBACK in:
50 * http://git.gnome.org/browse/gnome-settings-daemon/tree/plugins/xsettings/gsd-xsettings-manager.c
51 * for the reasoning */
52#define DPI_FALLBACK 96.0
53
54/* In version 0 of the config file format, we had several <configuration>
55 * toplevel elements and no explicit version number. So, the filed looked
56 * like
57 *
58 * <configuration>
59 * ...
60 * </configuration>
61 * <configuration>
62 * ...
63 * </configuration>
64 *
65 * Since version 1 of the config file, the file has a toplevel <monitors>
66 * element to group all the configurations. That element has a "version"
67 * attribute which is an integer. So, the file looks like this:
68 *
69 * <monitors version="1">
70 * <configuration>
71 * ...
72 * </configuration>
73 * <configuration>
74 * ...
75 * </configuration>
76 * </monitors>
77 */
78
79/* A helper wrapper around the GMarkup parser stuff */
80static gboolean parse_file_gmarkup (const gchar *file,
81 const GMarkupParser *parser,
82 gpointer data,
83 GError **err);
84
85typedef struct CrtcAssignment CrtcAssignment;
86
87static gboolean crtc_assignment_apply (CrtcAssignment *assign,
88 guint32 timestamp,
89 GError **error);
90static CrtcAssignment *crtc_assignment_new (CCRRScreen *screen,
91 CCRROutputInfo **outputs,
92 GError **error);
93static void crtc_assignment_free (CrtcAssignment *assign);
94
95enum {
96 PROP_0,
97 PROP_SCREEN,
98 PROP_LAST
99};
100
101G_DEFINE_TYPE (CCRRConfig, cc_rr_config, G_TYPE_OBJECT)
102
103typedef struct Parser Parser;
104
105/* Parser for monitor configurations */
106struct Parser
107{
108 int config_file_version;
109 CCRROutputInfo * output;
110 CCRRConfig * configuration;
111 GPtrArray * outputs;
112 GPtrArray * configurations;
113 GQueue * stack;
114};
115
116static int
117parse_int (const char *text)
118{
119 return strtol (text, NULL, 0);
120}
121
122static guint
123parse_uint (const char *text)
124{
125 return strtoul (text, NULL, 0);
126}
127
128static gboolean
129stack_is (Parser *parser,
130 const char *s1,
131 ...)
132{
133 GList *stack = NULL;
134 const char *s;
135 GList *l1, *l2;
136 va_list args;
137
138 stack = g_list_prepend (stack, (gpointer)s1);
139
140 va_start (args, s1);
141
142 s = va_arg (args, const char *);
143 while (s)
144 {
145 stack = g_list_prepend (stack, (gpointer)s);
146 s = va_arg (args, const char *);
147 }
148
149 l1 = stack;
150 l2 = parser->stack->head;
151
152 while (l1 && l2)
153 {
154 if (strcmp (l1->data, l2->data) != 0)
155 {
156 g_list_free (stack);
157 return FALSE;
158 }
159
160 l1 = l1->next;
161 l2 = l2->next;
162 }
163
164 g_list_free (stack);
165
166 return (!l1 && !l2);
167}
168
169static void
170handle_start_element (GMarkupParseContext *context,
171 const gchar *name,
172 const gchar **attr_names,
173 const gchar **attr_values,
174 gpointer user_data,
175 GError **err)
176{
177 Parser *parser = user_data;
178
179 if (strcmp (name, "output") == 0)
180 {
181 int i;
182 g_assert (parser->output == NULL);
183
184 parser->output = g_object_new (CC_TYPE_RR_OUTPUT_INFO, NULL);
185 parser->output->priv->rotation = 0;
186
187 for (i = 0; attr_names[i] != NULL; ++i)
188 {
189 if (strcmp (attr_names[i], "name") == 0)
190 {
191 parser->output->priv->name = g_strdup (attr_values[i]);
192 break;
193 }
194 }
195
196 if (!parser->output->priv->name)
197 {
198 /* This really shouldn't happen, but it's better to make
199 * something up than to crash later.
200 */
201 g_warning ("Malformed monitor configuration file");
202
203 parser->output->priv->name = g_strdup ("default");
204 }
205 parser->output->priv->connected = FALSE;
206 parser->output->priv->on = FALSE;
207 parser->output->priv->primary = FALSE;
208 }
209 else if (strcmp (name, "configuration") == 0)
210 {
211 g_assert (parser->configuration == NULL);
212
213 parser->configuration = g_object_new (CC_TYPE_RR_CONFIG, NULL);
214 parser->configuration->priv->clone = FALSE;
215 parser->configuration->priv->outputs = NULL;
216 }
217 else if (strcmp (name, "monitors") == 0)
218 {
219 int i;
220
221 for (i = 0; attr_names[i] != NULL; i++)
222 {
223 if (strcmp (attr_names[i], "version") == 0)
224 {
225 parser->config_file_version = parse_int (attr_values[i]);
226 break;
227 }
228 }
229 }
230
231 g_queue_push_tail (parser->stack, g_strdup (name));
232}
233
234static void
235handle_end_element (GMarkupParseContext *context,
236 const gchar *name,
237 gpointer user_data,
238 GError **err)
239{
240 Parser *parser = user_data;
241
242 if (strcmp (name, "output") == 0)
243 {
244 /* If no rotation properties were set, just use CC_RR_ROTATION_0 */
245 if (parser->output->priv->rotation == 0)
246 parser->output->priv->rotation = CC_RR_ROTATION_0;
247
248 g_ptr_array_add (parser->outputs, parser->output);
249
250 parser->output = NULL;
251 }
252 else if (strcmp (name, "configuration") == 0)
253 {
254 g_ptr_array_add (parser->outputs, NULL);
255 parser->configuration->priv->outputs =
256 (CCRROutputInfo **)g_ptr_array_free (parser->outputs, FALSE);
257 parser->outputs = g_ptr_array_new ();
258 g_ptr_array_add (parser->configurations, parser->configuration);
259 parser->configuration = NULL;
260 }
261
262 g_free (g_queue_pop_tail (parser->stack));
263}
264
265#define TOPLEVEL_ELEMENT (parser->config_file_version > 0 ? "monitors" : NULL)
266
267static void
268handle_text (GMarkupParseContext *context,
269 const gchar *text,
270 gsize text_len,
271 gpointer user_data,
272 GError **err)
273{
274 Parser *parser = user_data;
275
276 if (stack_is (parser, "vendor", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
277 {
278 parser->output->priv->connected = TRUE;
279
280 strncpy ((gchar*) parser->output->priv->vendor, text, 3);
281 parser->output->priv->vendor[3] = 0;
282 }
283 else if (stack_is (parser, "clone", "configuration", TOPLEVEL_ELEMENT, NULL))
284 {
285 if (strcmp (text, "yes") == 0)
286 parser->configuration->priv->clone = TRUE;
287 }
288 else if (stack_is (parser, "product", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
289 {
290 parser->output->priv->connected = TRUE;
291
292 parser->output->priv->product = parse_int (text);
293 }
294 else if (stack_is (parser, "serial", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
295 {
296 parser->output->priv->connected = TRUE;
297
298 parser->output->priv->serial = parse_uint (text);
299 }
300 else if (stack_is (parser, "width", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
301 {
302 parser->output->priv->on = TRUE;
303
304 parser->output->priv->width = parse_int (text);
305 }
306 else if (stack_is (parser, "x", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
307 {
308 parser->output->priv->on = TRUE;
309
310 parser->output->priv->x = parse_int (text);
311 }
312 else if (stack_is (parser, "y", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
313 {
314 parser->output->priv->on = TRUE;
315
316 parser->output->priv->y = parse_int (text);
317 }
318 else if (stack_is (parser, "height", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
319 {
320 parser->output->priv->on = TRUE;
321
322 parser->output->priv->height = parse_int (text);
323 }
324 else if (stack_is (parser, "rate", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
325 {
326 parser->output->priv->on = TRUE;
327
328 parser->output->priv->rate = parse_int (text);
329 }
330 else if (stack_is (parser, "rotation", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
331 {
332 if (strcmp (text, "normal") == 0)
333 {
334 parser->output->priv->rotation |= CC_RR_ROTATION_0;
335 }
336 else if (strcmp (text, "left") == 0)
337 {
338 parser->output->priv->rotation |= CC_RR_ROTATION_90;
339 }
340 else if (strcmp (text, "upside_down") == 0)
341 {
342 parser->output->priv->rotation |= CC_RR_ROTATION_180;
343 }
344 else if (strcmp (text, "right") == 0)
345 {
346 parser->output->priv->rotation |= CC_RR_ROTATION_270;
347 }
348 }
349 else if (stack_is (parser, "reflect_x", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
350 {
351 if (strcmp (text, "yes") == 0)
352 {
353 parser->output->priv->rotation |= CC_RR_REFLECT_X;
354 }
355 }
356 else if (stack_is (parser, "reflect_y", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
357 {
358 if (strcmp (text, "yes") == 0)
359 {
360 parser->output->priv->rotation |= CC_RR_REFLECT_Y;
361 }
362 }
363 else if (stack_is (parser, "primary", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
364 {
365 if (strcmp (text, "yes") == 0)
366 {
367 parser->output->priv->primary = TRUE;
368 }
369 }
370 else
371 {
372 /* Ignore other properties so we can expand the format in the future */
373 }
374}
375
376static void
377parser_free (Parser *parser)
378{
379 int i;
380 GList *list;
381
382 g_assert (parser != NULL);
383
384 if (parser->output)
385 g_object_unref (parser->output);
386
387 if (parser->configuration)
388 g_object_unref (parser->configuration);
389
390 for (i = 0; i < parser->outputs->len; ++i)
391 {
392 CCRROutputInfo *output = parser->outputs->pdata[i];
393
394 g_object_unref (output);
395 }
396
397 g_ptr_array_free (parser->outputs, TRUE);
398
399 for (i = 0; i < parser->configurations->len; ++i)
400 {
401 CCRRConfig *config = parser->configurations->pdata[i];
402
403 g_object_unref (config);
404 }
405
406 g_ptr_array_free (parser->configurations, TRUE);
407
408 for (list = parser->stack->head; list; list = list->next)
409 g_free (list->data);
410 g_queue_free (parser->stack);
411
412 g_free (parser);
413}
414
415static CCRRConfig **
416configurations_read_from_file (const gchar *filename, GError **error)
417{
418 Parser *parser = g_new0 (Parser, 1);
419 CCRRConfig **result;
420 GMarkupParser callbacks = {
421 handle_start_element,
422 handle_end_element,
423 handle_text,
424 NULL, /* passthrough */
425 NULL, /* error */
426 };
427
428 parser->config_file_version = 0;
429 parser->configurations = g_ptr_array_new ();
430 parser->outputs = g_ptr_array_new ();
431 parser->stack = g_queue_new ();
432
433 if (!parse_file_gmarkup (filename, &callbacks, parser, error))
434 {
435 result = NULL;
436
437 g_assert (parser->outputs);
438 goto out;
439 }
440
441 g_assert (parser->outputs);
442
443 g_ptr_array_add (parser->configurations, NULL);
444 result = (CCRRConfig **)g_ptr_array_free (parser->configurations, FALSE);
445 parser->configurations = g_ptr_array_new ();
446
447 g_assert (parser->outputs);
448out:
449 parser_free (parser);
450
451 return result;
452}
453
454static void
455cc_rr_config_init (CCRRConfig *self)
456{
457 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CC_TYPE_RR_CONFIG, CCRRConfigPrivate);
458
459 self->priv->clone = FALSE;
460 self->priv->screen = NULL;
461 self->priv->outputs = NULL;
462}
463
464static void
465cc_rr_config_set_property (GObject *gobject, guint property_id, const GValue *value, GParamSpec *property)
466{
467 CCRRConfig *self = CC_RR_CONFIG (gobject);
468
469 switch (property_id) {
470 case PROP_SCREEN:
471 self->priv->screen = g_value_dup_object (value);
472 return;
473 default:
474 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
475 }
476}
477
478static void
479cc_rr_config_finalize (GObject *gobject)
480{
481 CCRRConfig *self = CC_RR_CONFIG (gobject);
482
483 if (self->priv->screen)
484 g_object_unref (self->priv->screen);
485
486 if (self->priv->outputs) {
487 int i;
488
489 for (i = 0; self->priv->outputs[i] != NULL; i++) {
490 CCRROutputInfo *output = self->priv->outputs[i];
491 g_object_unref (output);
492 }
493 g_free (self->priv->outputs);
494 }
495
496 G_OBJECT_CLASS (cc_rr_config_parent_class)->finalize (gobject);
497}
498
499gboolean
500cc_rr_config_load_current (CCRRConfig *config, GError **error)
501{
502 GPtrArray *a;
503 CCRROutput **rr_outputs;
504 int i;
505 int clone_width = -1;
506 int clone_height = -1;
507 int last_x;
508
509 g_return_val_if_fail (CC_IS_RR_CONFIG (config), FALSE);
510
511 a = g_ptr_array_new ();
512 rr_outputs = cc_rr_screen_list_outputs (config->priv->screen);
513
514 config->priv->clone = FALSE;
515
516 for (i = 0; rr_outputs[i] != NULL; ++i)
517 {
518 CCRROutput *rr_output = rr_outputs[i];
519 CCRROutputInfo *output = g_object_new (CC_TYPE_RR_OUTPUT_INFO, NULL);
520 CCRRMode *mode = NULL;
521 const guint8 *edid_data = cc_rr_output_get_edid_data (rr_output, NULL);
522 CCRRCrtc *crtc;
523
524 output->priv->name = g_strdup (cc_rr_output_get_name (rr_output));
525 output->priv->connected = cc_rr_output_is_connected (rr_output);
526 output->priv->display_name = g_strdup (cc_rr_output_get_display_name (rr_output));
527
528 if (!output->priv->connected)
529 {
530 output->priv->x = -1;
531 output->priv->y = -1;
532 output->priv->width = -1;
533 output->priv->height = -1;
534 output->priv->rate = -1;
535 output->priv->rotation = CC_RR_ROTATION_0;
536 }
537 else
538 {
539 MonitorInfo *info = NULL;
540
541 if (edid_data)
542 info = decode_edid (edid_data);
543
544 if (info)
545 {
546 memcpy (output->priv->vendor, info->manufacturer_code,
547 sizeof (output->priv->vendor));
548
549 output->priv->product = info->product_code;
550 output->priv->serial = info->serial_number;
551 output->priv->aspect = info->aspect_ratio;
552 }
553 else
554 {
555 strcpy (output->priv->vendor, "???");
556 output->priv->product = 0;
557 output->priv->serial = 0;
558 }
559 g_free (info);
560
561 crtc = cc_rr_output_get_crtc (rr_output);
562 mode = crtc? cc_rr_crtc_get_current_mode (crtc) : NULL;
563
564 if (crtc && mode)
565 {
566 output->priv->on = TRUE;
567
568 cc_rr_crtc_get_position (crtc, &output->priv->x, &output->priv->y);
569 output->priv->width = cc_rr_mode_get_width (mode);
570 output->priv->height = cc_rr_mode_get_height (mode);
571 output->priv->rate = cc_rr_mode_get_freq (mode);
572 output->priv->rotation = cc_rr_crtc_get_current_rotation (crtc);
573
574 if (output->priv->x == 0 && output->priv->y == 0) {
575 if (clone_width == -1) {
576 clone_width = output->priv->width;
577 clone_height = output->priv->height;
578 } else if (clone_width == output->priv->width &&
579 clone_height == output->priv->height) {
580 config->priv->clone = TRUE;
581 }
582 }
583 }
584 else
585 {
586 output->priv->on = FALSE;
587 config->priv->clone = FALSE;
588 }
589
590 /* Get preferred size for the monitor */
591 mode = cc_rr_output_get_preferred_mode (rr_output);
592
593 if (!mode)
594 {
595 CCRRMode **modes = cc_rr_output_list_modes (rr_output);
596
597 /* FIXME: we should pick the "best" mode here, where best is
598 * sorted wrt
599 *
600 * - closest aspect ratio
601 * - mode area
602 * - refresh rate
603 * - We may want to extend randrwrap so that get_preferred
604 * returns that - although that could also depend on
605 * the crtc.
606 */
607 if (modes[0])
608 mode = modes[0];
609 }
610
611 if (mode)
612 {
613 output->priv->pref_width = cc_rr_mode_get_width (mode);
614 output->priv->pref_height = cc_rr_mode_get_height (mode);
615 }
616 else
617 {
618 /* Pick some random numbers. This should basically never happen */
619 output->priv->pref_width = 1024;
620 output->priv->pref_height = 768;
621 }
622 }
623
624 output->priv->primary = cc_rr_output_get_is_primary (rr_output);
625
626 g_ptr_array_add (a, output);
627 }
628
629 g_ptr_array_add (a, NULL);
630
631 config->priv->outputs = (CCRROutputInfo **)g_ptr_array_free (a, FALSE);
632
633 /* Walk the outputs computing the right-most edge of all
634 * lit-up displays
635 */
636 last_x = 0;
637 for (i = 0; config->priv->outputs[i] != NULL; ++i)
638 {
639 CCRROutputInfo *output = config->priv->outputs[i];
640
641 if (output->priv->on)
642 {
643 last_x = MAX (last_x, output->priv->x + output->priv->width);
644 }
645 }
646
647 /* Now position all off displays to the right of the
648 * on displays
649 */
650 for (i = 0; config->priv->outputs[i] != NULL; ++i)
651 {
652 CCRROutputInfo *output = config->priv->outputs[i];
653
654 if (output->priv->connected && !output->priv->on)
655 {
656 output->priv->x = last_x;
657 last_x = output->priv->x + output->priv->width;
658 }
659 }
660
661 g_assert (cc_rr_config_match (config, config));
662
663 return TRUE;
664}
665
666gboolean
667cc_rr_config_load_filename (CCRRConfig *result, const char *filename, GError **error)
668{
669 CCRRConfig *current;
670 CCRRConfig **configs;
671 gboolean found = FALSE;
672
673 g_return_val_if_fail (CC_IS_RR_CONFIG (result), FALSE);
674 g_return_val_if_fail (filename != NULL, FALSE);
675 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
676
677 current = cc_rr_config_new_current (result->priv->screen, error);
678
679 configs = configurations_read_from_file (filename, error);
680
681 if (configs)
682 {
683 int i;
684
685 for (i = 0; configs[i] != NULL; ++i)
686 {
687 if (cc_rr_config_match (configs[i], current))
688 {
689 int j;
690 GPtrArray *array;
691 result->priv->clone = configs[i]->priv->clone;
692
693 array = g_ptr_array_new ();
694 for (j = 0; configs[i]->priv->outputs[j] != NULL; j++) {
695 g_object_ref (configs[i]->priv->outputs[j]);
696 g_ptr_array_add (array, configs[i]->priv->outputs[j]);
697 }
698 g_ptr_array_add (array, NULL);
699 result->priv->outputs = (CCRROutputInfo **) g_ptr_array_free (array, FALSE);
700
701 found = TRUE;
702 break;
703 }
704 g_object_unref (configs[i]);
705 }
706 g_free (configs);
707
708 if (!found)
709 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_NO_MATCHING_CONFIG,
710 _("none of the saved display configurations matched the active configuration"));
711 }
712
713 g_object_unref (current);
714 return found;
715}
716
717static void
718cc_rr_config_class_init (CCRRConfigClass *klass)
719{
720 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
721
722 g_type_class_add_private (klass, sizeof (CCRROutputInfoPrivate));
723
724 gobject_class->set_property = cc_rr_config_set_property;
725 gobject_class->finalize = cc_rr_config_finalize;
726
727 g_object_class_install_property (gobject_class, PROP_SCREEN,
728 g_param_spec_object ("screen", "Screen", "The CCRRScreen this config applies to", CC_TYPE_RR_SCREEN,
729 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
730}
731
732CCRRConfig *
733cc_rr_config_new_current (CCRRScreen *screen, GError **error)
734{
735 CCRRConfig *self = g_object_new (CC_TYPE_RR_CONFIG, "screen", screen, NULL);
736
737 if (cc_rr_config_load_current (self, error))
738 return self;
739 else
740 {
741 g_object_unref (self);
742 return NULL;
743 }
744}
745
746CCRRConfig *
747cc_rr_config_new_stored (CCRRScreen *screen, GError **error)
748{
749 CCRRConfig *self = g_object_new (CC_TYPE_RR_CONFIG, "screen", screen, NULL);
750 char *filename;
751 gboolean success;
752
753 filename = cc_rr_config_get_intended_filename ();
754
755 success = cc_rr_config_load_filename (self, filename, error);
756
757 g_free (filename);
758
759 if (success)
760 return self;
761 else
762 {
763 g_object_unref (self);
764 return NULL;
765 }
766}
767
768static gboolean
769parse_file_gmarkup (const gchar *filename,
770 const GMarkupParser *parser,
771 gpointer data,
772 GError **err)
773{
774 GMarkupParseContext *context = NULL;
775 gchar *contents = NULL;
776 gboolean result = TRUE;
777 gsize len;
778
779 if (!g_file_get_contents (filename, &contents, &len, err))
780 {
781 result = FALSE;
782 goto out;
783 }
784
785 context = g_markup_parse_context_new (parser, 0, data, NULL);
786
787 if (!g_markup_parse_context_parse (context, contents, len, err))
788 {
789 result = FALSE;
790 goto out;
791 }
792
793 if (!g_markup_parse_context_end_parse (context, err))
794 {
795 result = FALSE;
796 goto out;
797 }
798
799out:
800 if (contents)
801 g_free (contents);
802
803 if (context)
804 g_markup_parse_context_free (context);
805
806 return result;
807}
808
809static gboolean
810output_match (CCRROutputInfo *output1, CCRROutputInfo *output2)
811{
812 g_assert (CC_IS_RR_OUTPUT_INFO (output1));
813 g_assert (CC_IS_RR_OUTPUT_INFO (output2));
814
815 if (strcmp (output1->priv->name, output2->priv->name) != 0)
816 return FALSE;
817
818 if (strcmp (output1->priv->vendor, output2->priv->vendor) != 0)
819 return FALSE;
820
821 if (output1->priv->product != output2->priv->product)
822 return FALSE;
823
824 if (output1->priv->serial != output2->priv->serial)
825 return FALSE;
826
827 if (output1->priv->connected != output2->priv->connected)
828 return FALSE;
829
830 return TRUE;
831}
832
833static gboolean
834output_equal (CCRROutputInfo *output1, CCRROutputInfo *output2)
835{
836 g_assert (CC_IS_RR_OUTPUT_INFO (output1));
837 g_assert (CC_IS_RR_OUTPUT_INFO (output2));
838
839 if (!output_match (output1, output2))
840 return FALSE;
841
842 if (output1->priv->on != output2->priv->on)
843 return FALSE;
844
845 if (output1->priv->on)
846 {
847 if (output1->priv->width != output2->priv->width)
848 return FALSE;
849
850 if (output1->priv->height != output2->priv->height)
851 return FALSE;
852
853 if (output1->priv->rate != output2->priv->rate)
854 return FALSE;
855
856 if (output1->priv->x != output2->priv->x)
857 return FALSE;
858
859 if (output1->priv->y != output2->priv->y)
860 return FALSE;
861
862 if (output1->priv->rotation != output2->priv->rotation)
863 return FALSE;
864 }
865
866 return TRUE;
867}
868
869static CCRROutputInfo *
870find_output (CCRRConfig *config, const char *name)
871{
872 int i;
873
874 for (i = 0; config->priv->outputs[i] != NULL; ++i)
875 {
876 CCRROutputInfo *output = config->priv->outputs[i];
877
878 if (strcmp (name, output->priv->name) == 0)
879 return output;
880 }
881
882 return NULL;
883}
884
885/* Match means "these configurations apply to the same hardware
886 * setups"
887 */
888gboolean
889cc_rr_config_match (CCRRConfig *c1, CCRRConfig *c2)
890{
891 int i;
892 g_return_val_if_fail (CC_IS_RR_CONFIG (c1), FALSE);
893 g_return_val_if_fail (CC_IS_RR_CONFIG (c2), FALSE);
894
895 for (i = 0; c1->priv->outputs[i] != NULL; ++i)
896 {
897 CCRROutputInfo *output1 = c1->priv->outputs[i];
898 CCRROutputInfo *output2;
899
900 output2 = find_output (c2, output1->priv->name);
901 if (!output2 || !output_match (output1, output2))
902 return FALSE;
903 }
904
905 return TRUE;
906}
907
908/* Equal means "the configurations will result in the same
909 * modes being set on the outputs"
910 */
911gboolean
912cc_rr_config_equal (CCRRConfig *c1,
913 CCRRConfig *c2)
914{
915 int i;
916 g_return_val_if_fail (CC_IS_RR_CONFIG (c1), FALSE);
917 g_return_val_if_fail (CC_IS_RR_CONFIG (c2), FALSE);
918
919 for (i = 0; c1->priv->outputs[i] != NULL; ++i)
920 {
921 CCRROutputInfo *output1 = c1->priv->outputs[i];
922 CCRROutputInfo *output2;
923
924 output2 = find_output (c2, output1->priv->name);
925 if (!output2 || !output_equal (output1, output2))
926 return FALSE;
927 }
928
929 return TRUE;
930}
931
932static CCRROutputInfo **
933make_outputs (CCRRConfig *config)
934{
935 GPtrArray *outputs;
936 CCRROutputInfo *first_on;
937 int i;
938
939 outputs = g_ptr_array_new ();
940
941 first_on = NULL;
942
943 for (i = 0; config->priv->outputs[i] != NULL; ++i)
944 {
945 CCRROutputInfo *old = config->priv->outputs[i];
946 CCRROutputInfo *new = g_object_new (CC_TYPE_RR_OUTPUT_INFO, NULL);
947 *(new->priv) = *(old->priv);
948 if (old->priv->name)
949 new->priv->name = g_strdup (old->priv->name);
950 if (old->priv->display_name)
951 new->priv->display_name = g_strdup (old->priv->display_name);
952
953 if (old->priv->on && !first_on)
954 first_on = old;
955
956 if (config->priv->clone && new->priv->on)
957 {
958 g_assert (first_on);
959
960 new->priv->width = first_on->priv->width;
961 new->priv->height = first_on->priv->height;
962 new->priv->rotation = first_on->priv->rotation;
963 new->priv->x = 0;
964 new->priv->y = 0;
965 }
966
967 g_ptr_array_add (outputs, new);
968 }
969
970 g_ptr_array_add (outputs, NULL);
971
972 return (CCRROutputInfo **)g_ptr_array_free (outputs, FALSE);
973}
974
975gboolean
976cc_rr_config_applicable (CCRRConfig *configuration,
977 CCRRScreen *screen,
978 GError **error)
979{
980 CCRROutputInfo **outputs;
981 CrtcAssignment *assign;
982 gboolean result;
983 int i;
984
985 g_return_val_if_fail (CC_IS_RR_CONFIG (configuration), FALSE);
986 g_return_val_if_fail (CC_IS_RR_SCREEN (screen), FALSE);
987 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
988
989 outputs = make_outputs (configuration);
990 assign = crtc_assignment_new (screen, outputs, error);
991
992 if (assign)
993 {
994 result = TRUE;
995 crtc_assignment_free (assign);
996 }
997 else
998 {
999 result = FALSE;
1000 }
1001
1002 for (i = 0; outputs[i] != NULL; i++) {
1003 g_object_unref (outputs[i]);
1004 }
1005
1006 return result;
1007}
1008
1009/* Database management */
1010
1011static void
1012ensure_config_directory (void)
1013{
1014 g_mkdir_with_parents (g_get_user_config_dir (), 0700);
1015}
1016
1017char *
1018cc_rr_config_get_backup_filename (void)
1019{
1020 ensure_config_directory ();
1021 return g_build_filename (g_get_user_config_dir (), CONFIG_BACKUP_BASENAME, NULL);
1022}
1023
1024char *
1025cc_rr_config_get_intended_filename (void)
1026{
1027 ensure_config_directory ();
1028 return g_build_filename (g_get_user_config_dir (), CONFIG_INTENDED_BASENAME, NULL);
1029}
1030
1031static const char *
1032get_rotation_name (CCRRRotation r)
1033{
1034 if (r & CC_RR_ROTATION_0)
1035 return "normal";
1036 if (r & CC_RR_ROTATION_90)
1037 return "left";
1038 if (r & CC_RR_ROTATION_180)
1039 return "upside_down";
1040 if (r & CC_RR_ROTATION_270)
1041 return "right";
1042
1043 return "normal";
1044}
1045
1046static const char *
1047yes_no (int x)
1048{
1049 return x? "yes" : "no";
1050}
1051
1052static const char *
1053get_reflect_x (CCRRRotation r)
1054{
1055 return yes_no (r & CC_RR_REFLECT_X);
1056}
1057
1058static const char *
1059get_reflect_y (CCRRRotation r)
1060{
1061 return yes_no (r & CC_RR_REFLECT_Y);
1062}
1063
1064static void
1065emit_configuration (CCRRConfig *config,
1066 GString *string)
1067{
1068 int j;
1069
1070 g_string_append_printf (string, " <configuration>\n");
1071
1072 g_string_append_printf (string, " <clone>%s</clone>\n", yes_no (config->priv->clone));
1073
1074 for (j = 0; config->priv->outputs[j] != NULL; ++j)
1075 {
1076 CCRROutputInfo *output = config->priv->outputs[j];
1077
1078 g_string_append_printf (
1079 string, " <output name=\"%s\">\n", output->priv->name);
1080
1081 if (output->priv->connected && *output->priv->vendor != '\0')
1082 {
1083 g_string_append_printf (
1084 string, " <vendor>%s</vendor>\n", output->priv->vendor);
1085 g_string_append_printf (
1086 string, " <product>0x%04x</product>\n", output->priv->product);
1087 g_string_append_printf (
1088 string, " <serial>0x%08x</serial>\n", output->priv->serial);
1089 }
1090
1091 /* An unconnected output which is on does not make sense */
1092 if (output->priv->connected && output->priv->on)
1093 {
1094 g_string_append_printf (
1095 string, " <width>%d</width>\n", output->priv->width);
1096 g_string_append_printf (
1097 string, " <height>%d</height>\n", output->priv->height);
1098 g_string_append_printf (
1099 string, " <rate>%d</rate>\n", output->priv->rate);
1100 g_string_append_printf (
1101 string, " <x>%d</x>\n", output->priv->x);
1102 g_string_append_printf (
1103 string, " <y>%d</y>\n", output->priv->y);
1104 g_string_append_printf (
1105 string, " <rotation>%s</rotation>\n", get_rotation_name (output->priv->rotation));
1106 g_string_append_printf (
1107 string, " <reflect_x>%s</reflect_x>\n", get_reflect_x (output->priv->rotation));
1108 g_string_append_printf (
1109 string, " <reflect_y>%s</reflect_y>\n", get_reflect_y (output->priv->rotation));
1110 g_string_append_printf (
1111 string, " <primary>%s</primary>\n", yes_no (output->priv->primary));
1112 }
1113
1114 g_string_append_printf (string, " </output>\n");
1115 }
1116
1117 g_string_append_printf (string, " </configuration>\n");
1118}
1119
1120void
1121cc_rr_config_sanitize (CCRRConfig *config)
1122{
1123 int i;
1124 int x_offset, y_offset;
1125 gboolean found;
1126
1127 /* Offset everything by the top/left-most coordinate to
1128 * make sure the configuration starts at (0, 0)
1129 */
1130 x_offset = y_offset = G_MAXINT;
1131 for (i = 0; config->priv->outputs[i]; ++i)
1132 {
1133 CCRROutputInfo *output = config->priv->outputs[i];
1134
1135 if (output->priv->on)
1136 {
1137 x_offset = MIN (x_offset, output->priv->x);
1138 y_offset = MIN (y_offset, output->priv->y);
1139 }
1140 }
1141
1142 for (i = 0; config->priv->outputs[i]; ++i)
1143 {
1144 CCRROutputInfo *output = config->priv->outputs[i];
1145
1146 if (output->priv->on)
1147 {
1148 output->priv->x -= x_offset;
1149 output->priv->y -= y_offset;
1150 }
1151 }
1152
1153 /* Only one primary, please */
1154 found = FALSE;
1155 for (i = 0; config->priv->outputs[i]; ++i)
1156 {
1157 if (config->priv->outputs[i]->priv->primary)
1158 {
1159 if (found)
1160 {
1161 config->priv->outputs[i]->priv->primary = FALSE;
1162 }
1163 else
1164 {
1165 found = TRUE;
1166 }
1167 }
1168 }
1169}
1170
1171gboolean
1172cc_rr_config_ensure_primary (CCRRConfig *configuration)
1173{
1174 int i;
1175 CCRROutputInfo *laptop;
1176 CCRROutputInfo *top_left;
1177 gboolean found;
1178 CCRRConfigPrivate *priv;
1179
1180 g_return_val_if_fail (CC_IS_RR_CONFIG (configuration), FALSE);
1181
1182 laptop = NULL;
1183 top_left = NULL;
1184 found = FALSE;
1185 priv = configuration->priv;
1186
1187 for (i = 0; priv->outputs[i] != NULL; ++i) {
1188 CCRROutputInfo *info = priv->outputs[i];
1189
1190 if (!info->priv->on) {
1191 info->priv->primary = FALSE;
1192 continue;
1193 }
1194
1195 /* ensure only one */
1196 if (info->priv->primary) {
1197 if (found) {
1198 info->priv->primary = FALSE;
1199 } else {
1200 found = TRUE;
1201 }
1202 }
1203
1204 if (top_left == NULL
1205 || (info->priv->x < top_left->priv->x
1206 && info->priv->y < top_left->priv->y)) {
1207 top_left = info;
1208 }
1209 if (laptop == NULL
1210 && _cc_rr_output_name_is_laptop (info->priv->name)) {
1211 /* shame we can't find the connector type
1212 as with cc_rr_output_is_laptop */
1213 laptop = info;
1214 }
1215 }
1216
1217 if (!found) {
1218 if (laptop != NULL) {
1219 laptop->priv->primary = TRUE;
1220 } else if (top_left != NULL) {
1221 /* Note: top_left can be NULL if all outputs are off */
1222 top_left->priv->primary = TRUE;
1223 }
1224 }
1225
1226 return !found;
1227}
1228
1229gboolean
1230cc_rr_config_save (CCRRConfig *configuration, GError **error)
1231{
1232 CCRRConfig **configurations;
1233 GString *output;
1234 int i;
1235 gchar *intended_filename;
1236 gchar *backup_filename;
1237 gboolean result;
1238
1239 g_return_val_if_fail (CC_IS_RR_CONFIG (configuration), FALSE);
1240 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1241
1242 output = g_string_new ("");
1243
1244 backup_filename = cc_rr_config_get_backup_filename ();
1245 intended_filename = cc_rr_config_get_intended_filename ();
1246
1247 configurations = configurations_read_from_file (intended_filename, NULL); /* NULL-GError */
1248
1249 g_string_append_printf (output, "<monitors version=\"1\">\n");
1250
1251 if (configurations)
1252 {
1253 for (i = 0; configurations[i] != NULL; ++i)
1254 {
1255 if (!cc_rr_config_match (configurations[i], configuration))
1256 emit_configuration (configurations[i], output);
1257 g_object_unref (configurations[i]);
1258 }
1259
1260 g_free (configurations);
1261 }
1262
1263 emit_configuration (configuration, output);
1264
1265 g_string_append_printf (output, "</monitors>\n");
1266
1267 /* backup the file first */
1268 rename (intended_filename, backup_filename); /* no error checking because the intended file may not even exist */
1269
1270 result = g_file_set_contents (intended_filename, output->str, -1, error);
1271
1272 if (!result)
1273 rename (backup_filename, intended_filename); /* no error checking because the backup may not even exist */
1274
1275 g_free (backup_filename);
1276 g_free (intended_filename);
1277
1278 return result;
1279}
1280
1281gboolean
1282cc_rr_config_apply_with_time (CCRRConfig *config,
1283 CCRRScreen *screen,
1284 guint32 timestamp,
1285 GError **error)
1286{
1287 CrtcAssignment *assignment;
1288 CCRROutputInfo **outputs;
1289 gboolean result = FALSE;
1290 int i;
1291
1292 g_return_val_if_fail (CC_IS_RR_CONFIG (config), FALSE);
1293 g_return_val_if_fail (CC_IS_RR_SCREEN (screen), FALSE);
1294
1295 outputs = make_outputs (config);
1296
1297 assignment = crtc_assignment_new (screen, outputs, error);
1298
1299 for (i = 0; outputs[i] != NULL; i++)
1300 g_object_unref (outputs[i]);
1301 g_free (outputs);
1302
1303 if (assignment)
1304 {
1305 if (crtc_assignment_apply (assignment, timestamp, error))
1306 result = TRUE;
1307
1308 crtc_assignment_free (assignment);
1309
1310 gdk_flush ();
1311 }
1312
1313 return result;
1314}
1315
1316/* cc_rr_config_apply_from_filename_with_time:
1317 * @screen: A #CCRRScreen
1318 * @filename: Path of the file to look in for stored RANDR configurations.
1319 * @timestamp: X server timestamp from the event that causes the screen configuration to change (a user's button press, for example)
1320 * @error: Location to store error, or %NULL
1321 *
1322 * Loads the file in @filename and looks for suitable matching RANDR
1323 * configurations in the file; if one is found, that configuration will be
1324 * applied to the current set of RANDR outputs.
1325 *
1326 * Typically, @filename is the result of cc_rr_config_get_intended_filename() or
1327 * cc_rr_config_get_backup_filename().
1328 *
1329 * Returns: TRUE if the RANDR configuration was loaded and applied from
1330 * the specified file, or FALSE otherwise:
1331 *
1332 * If the file in question is loaded successfully but the configuration cannot
1333 * be applied, the @error will have a domain of #CC_RR_ERROR. Note that an
1334 * error code of #CC_RR_ERROR_NO_MATCHING_CONFIG is not a real error; it
1335 * simply means that there were no stored configurations that match the current
1336 * set of RANDR outputs.
1337 *
1338 * If the file in question cannot be loaded, the @error will have a domain of
1339 * #G_FILE_ERROR. Note that an error code of G_FILE_ERROR_NOENT is not really
1340 * an error, either; it means that there was no stored configuration file and so
1341 * nothing is changed.
1342 */
1343gboolean
1344cc_rr_config_apply_from_filename_with_time (CCRRScreen *screen, const char *filename, guint32 timestamp, GError **error)
1345{
1346 CCRRConfig *stored;
1347
1348 g_return_val_if_fail (CC_IS_RR_SCREEN (screen), FALSE);
1349 g_return_val_if_fail (filename != NULL, FALSE);
1350 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1351
1352 stored = g_object_new (CC_TYPE_RR_CONFIG, "screen", screen, NULL);
1353
1354 if (cc_rr_config_load_filename (stored, filename, error))
1355 {
1356 gboolean result;
1357
1358 cc_rr_config_ensure_primary (stored);
1359 result = cc_rr_config_apply_with_time (stored, screen, timestamp, error);
1360
1361 g_object_unref (stored);
1362 return result;
1363 }
1364 else
1365 {
1366 g_object_unref (stored);
1367 return FALSE;
1368 }
1369}
1370
1371/**
1372 * cc_rr_config_get_outputs:
1373 *
1374 * Returns: (array zero-terminated=1) (element-type GnomeDesktop.RROutputInfo) (transfer none): the output configuration for this #CCRRConfig
1375 */
1376CCRROutputInfo **
1377cc_rr_config_get_outputs (CCRRConfig *self)
1378{
1379 g_return_val_if_fail (CC_IS_RR_CONFIG (self), NULL);
1380
1381 return self->priv->outputs;
1382}
1383
1384/**
1385 * cc_rr_config_get_clone:
1386 *
1387 * Returns: whether at least two outputs are at (0, 0) offset and they
1388 * have the same width/height. Those outputs are of course connected and on
1389 * (i.e. they have a CRTC assigned).
1390 */
1391gboolean
1392cc_rr_config_get_clone (CCRRConfig *self)
1393{
1394 g_return_val_if_fail (CC_IS_RR_CONFIG (self), FALSE);
1395
1396 return self->priv->clone;
1397}
1398
1399void
1400cc_rr_config_set_clone (CCRRConfig *self, gboolean clone)
1401{
1402 g_return_if_fail (CC_IS_RR_CONFIG (self));
1403
1404 self->priv->clone = clone;
1405}
1406
1407/*
1408 * CRTC assignment
1409 */
1410typedef struct CrtcInfo CrtcInfo;
1411
1412struct CrtcInfo
1413{
1414 CCRRMode *mode;
1415 int x;
1416 int y;
1417 CCRRRotation rotation;
1418 GPtrArray *outputs;
1419};
1420
1421struct CrtcAssignment
1422{
1423 CCRRScreen *screen;
1424 GHashTable *info;
1425 CCRROutput *primary;
1426};
1427
1428static gboolean
1429can_clone (CrtcInfo *info,
1430 CCRROutput *output)
1431{
1432 int i;
1433
1434 for (i = 0; i < info->outputs->len; ++i)
1435 {
1436 CCRROutput *clone = info->outputs->pdata[i];
1437
1438 if (!cc_rr_output_can_clone (clone, output))
1439 return FALSE;
1440 }
1441
1442 return TRUE;
1443}
1444
1445static gboolean
1446crtc_assignment_assign (CrtcAssignment *assign,
1447 CCRRCrtc *crtc,
1448 CCRRMode *mode,
1449 int x,
1450 int y,
1451 CCRRRotation rotation,
1452 gboolean primary,
1453 CCRROutput *output,
1454 GError **error)
1455{
1456 CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
1457 guint32 crtc_id;
1458 const char *output_name;
1459
1460 crtc_id = cc_rr_crtc_get_id (crtc);
1461 output_name = cc_rr_output_get_name (output);
1462
1463 if (!cc_rr_crtc_can_drive_output (crtc, output))
1464 {
1465 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
1466 _("CRTC %d cannot drive output %s"), crtc_id, output_name);
1467 return FALSE;
1468 }
1469
1470 if (!cc_rr_output_supports_mode (output, mode))
1471 {
1472 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
1473 _("output %s does not support mode %dx%d@%dHz"),
1474 output_name,
1475 cc_rr_mode_get_width (mode),
1476 cc_rr_mode_get_height (mode),
1477 cc_rr_mode_get_freq (mode));
1478 return FALSE;
1479 }
1480
1481 if (!cc_rr_crtc_supports_rotation (crtc, rotation))
1482 {
1483 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
1484 _("CRTC %d does not support rotation=%s"),
1485 crtc_id,
1486 get_rotation_name (rotation));
1487 return FALSE;
1488 }
1489
1490 if (info)
1491 {
1492 if (!(info->mode == mode &&
1493 info->x == x &&
1494 info->y == y &&
1495 info->rotation == rotation))
1496 {
1497 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
1498 _("output %s does not have the same parameters as another cloned output:\n"
1499 "existing mode = %d, new mode = %d\n"
1500 "existing coordinates = (%d, %d), new coordinates = (%d, %d)\n"
1501 "existing rotation = %s, new rotation = %s"),
1502 output_name,
1503 cc_rr_mode_get_id (info->mode), cc_rr_mode_get_id (mode),
1504 info->x, info->y,
1505 x, y,
1506 get_rotation_name (info->rotation), get_rotation_name (rotation));
1507 return FALSE;
1508 }
1509
1510 if (!can_clone (info, output))
1511 {
1512 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
1513 _("cannot clone to output %s"),
1514 output_name);
1515 return FALSE;
1516 }
1517
1518 g_ptr_array_add (info->outputs, output);
1519
1520 if (primary && !assign->primary)
1521 {
1522 assign->primary = output;
1523 }
1524
1525 return TRUE;
1526 }
1527 else
1528 {
1529 CrtcInfo *info = g_new0 (CrtcInfo, 1);
1530
1531 info->mode = mode;
1532 info->x = x;
1533 info->y = y;
1534 info->rotation = rotation;
1535 info->outputs = g_ptr_array_new ();
1536
1537 g_ptr_array_add (info->outputs, output);
1538
1539 g_hash_table_insert (assign->info, crtc, info);
1540
1541 if (primary && !assign->primary)
1542 {
1543 assign->primary = output;
1544 }
1545
1546 return TRUE;
1547 }
1548}
1549
1550static void
1551crtc_assignment_unassign (CrtcAssignment *assign,
1552 CCRRCrtc *crtc,
1553 CCRROutput *output)
1554{
1555 CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
1556
1557 if (info)
1558 {
1559 g_ptr_array_remove (info->outputs, output);
1560
1561 if (assign->primary == output)
1562 {
1563 assign->primary = NULL;
1564 }
1565
1566 if (info->outputs->len == 0)
1567 g_hash_table_remove (assign->info, crtc);
1568 }
1569}
1570
1571static void
1572crtc_assignment_free (CrtcAssignment *assign)
1573{
1574 g_hash_table_destroy (assign->info);
1575
1576 g_free (assign);
1577}
1578
1579typedef struct {
1580 guint32 timestamp;
1581 gboolean has_error;
1582 GError **error;
1583} ConfigureCrtcState;
1584
1585static void
1586configure_crtc (gpointer key,
1587 gpointer value,
1588 gpointer data)
1589{
1590 CCRRCrtc *crtc = key;
1591 CrtcInfo *info = value;
1592 ConfigureCrtcState *state = data;
1593
1594 if (state->has_error)
1595 return;
1596
1597 if (!cc_rr_crtc_set_config_with_time (crtc,
1598 state->timestamp,
1599 info->x, info->y,
1600 info->mode,
1601 info->rotation,
1602 (CCRROutput **)info->outputs->pdata,
1603 info->outputs->len,
1604 state->error))
1605 state->has_error = TRUE;
1606}
1607
1608static gboolean
1609mode_is_rotated (CrtcInfo *info)
1610{
1611 if ((info->rotation & CC_RR_ROTATION_270) ||
1612 (info->rotation & CC_RR_ROTATION_90))
1613 {
1614 return TRUE;
1615 }
1616 return FALSE;
1617}
1618
1619static gboolean
1620crtc_is_rotated (CCRRCrtc *crtc)
1621{
1622 CCRRRotation r = cc_rr_crtc_get_current_rotation (crtc);
1623
1624 if ((r & CC_RR_ROTATION_270) ||
1625 (r & CC_RR_ROTATION_90))
1626 {
1627 return TRUE;
1628 }
1629
1630 return FALSE;
1631}
1632
1633static void
1634accumulate_error (GString *accumulated_error, GError *error)
1635{
1636 g_string_append_printf (accumulated_error, " %s\n", error->message);
1637 g_error_free (error);
1638}
1639
1640/* Check whether the given set of settings can be used
1641 * at the same time -- ie. whether there is an assignment
1642 * of CRTC's to outputs.
1643 *
1644 * Brute force - the number of objects involved is small
1645 * enough that it doesn't matter.
1646 */
1647static gboolean
1648real_assign_crtcs (CCRRScreen *screen,
1649 CCRROutputInfo **outputs,
1650 CrtcAssignment *assignment,
1651 GError **error)
1652{
1653 CCRRCrtc **crtcs = cc_rr_screen_list_crtcs (screen);
1654 CCRROutputInfo *output;
1655 int i;
1656 gboolean tried_mode;
1657 GError *my_error;
1658 GString *accumulated_error;
1659 gboolean success;
1660
1661 output = *outputs;
1662 if (!output)
1663 return TRUE;
1664
1665 /* It is always allowed for an output to be turned off */
1666 if (!output->priv->on)
1667 {
1668 return real_assign_crtcs (screen, outputs + 1, assignment, error);
1669 }
1670
1671 success = FALSE;
1672 tried_mode = FALSE;
1673 accumulated_error = g_string_new (NULL);
1674
1675 for (i = 0; crtcs[i] != NULL; ++i)
1676 {
1677 CCRRCrtc *crtc = crtcs[i];
1678 int crtc_id = cc_rr_crtc_get_id (crtc);
1679 int pass;
1680
1681 g_string_append_printf (accumulated_error,
1682 _("Trying modes for CRTC %d\n"),
1683 crtc_id);
1684
1685 /* Make two passes, one where frequencies must match, then
1686 * one where they don't have to
1687 */
1688 for (pass = 0; pass < 2; ++pass)
1689 {
1690 CCRROutput *cc_rr_output = cc_rr_screen_get_output_by_name (screen, output->priv->name);
1691 CCRRMode **modes = cc_rr_output_list_modes (cc_rr_output);
1692 int j;
1693
1694 for (j = 0; modes[j] != NULL; ++j)
1695 {
1696 CCRRMode *mode = modes[j];
1697 int mode_width;
1698 int mode_height;
1699 int mode_freq;
1700
1701 mode_width = cc_rr_mode_get_width (mode);
1702 mode_height = cc_rr_mode_get_height (mode);
1703 mode_freq = cc_rr_mode_get_freq (mode);
1704
1705 g_string_append_printf (accumulated_error,
1706 _("CRTC %d: trying mode %dx%d@%dHz with output at %dx%d@%dHz (pass %d)\n"),
1707 crtc_id,
1708 mode_width, mode_height, mode_freq,
1709 output->priv->width, output->priv->height, output->priv->rate,
1710 pass);
1711
1712 if (mode_width == output->priv->width &&
1713 mode_height == output->priv->height &&
1714 (pass == 1 || mode_freq == output->priv->rate))
1715 {
1716 tried_mode = TRUE;
1717
1718 my_error = NULL;
1719 if (crtc_assignment_assign (
1720 assignment, crtc, modes[j],
1721 output->priv->x, output->priv->y,
1722 output->priv->rotation,
1723 output->priv->primary,
1724 cc_rr_output,
1725 &my_error))
1726 {
1727 my_error = NULL;
1728 if (real_assign_crtcs (screen, outputs + 1, assignment, &my_error)) {
1729 success = TRUE;
1730 goto out;
1731 } else
1732 accumulate_error (accumulated_error, my_error);
1733
1734 crtc_assignment_unassign (assignment, crtc, cc_rr_output);
1735 } else
1736 accumulate_error (accumulated_error, my_error);
1737 }
1738 }
1739 }
1740 }
1741
1742out:
1743
1744 if (success)
1745 g_string_free (accumulated_error, TRUE);
1746 else {
1747 char *str;
1748
1749 str = g_string_free (accumulated_error, FALSE);
1750
1751 if (tried_mode)
1752 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
1753 _("could not assign CRTCs to outputs:\n%s"),
1754 str);
1755 else
1756 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_CRTC_ASSIGNMENT,
1757 _("none of the selected modes were compatible with the possible modes:\n%s"),
1758 str);
1759
1760 g_free (str);
1761 }
1762
1763 return success;
1764}
1765
1766static void
1767crtc_info_free (CrtcInfo *info)
1768{
1769 g_ptr_array_free (info->outputs, TRUE);
1770 g_free (info);
1771}
1772
1773static void
1774get_required_virtual_size (CrtcAssignment *assign, int *width, int *height)
1775{
1776 GList *active_crtcs = g_hash_table_get_keys (assign->info);
1777 GList *list;
1778 int d;
1779
1780 if (!width)
1781 width = &d;
1782 if (!height)
1783 height = &d;
1784
1785 /* Compute size of the screen */
1786 *width = *height = 1;
1787 for (list = active_crtcs; list != NULL; list = list->next)
1788 {
1789 CCRRCrtc *crtc = list->data;
1790 CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
1791 int w, h;
1792
1793 w = cc_rr_mode_get_width (info->mode);
1794 h = cc_rr_mode_get_height (info->mode);
1795
1796 if (mode_is_rotated (info))
1797 {
1798 int tmp = h;
1799 h = w;
1800 w = tmp;
1801 }
1802
1803 *width = MAX (*width, info->x + w);
1804 *height = MAX (*height, info->y + h);
1805 }
1806
1807 g_list_free (active_crtcs);
1808}
1809
1810static gboolean
1811unity_running (void)
1812{
1813 const gchar *desktop_environment = g_getenv ("DESKTOP_SESSION");
1814
1815 return !g_strcmp0 (desktop_environment, "ubuntu");
1816}
1817
1818static gint _max_texture_size_cache = -1;
1819
1820static gint
1821get_max_texture_size (CCRRScreen *screen)
1822{
1823 if (_max_texture_size_cache != -1)
1824 {
1825 return _max_texture_size_cache;
1826 } else {
1827 /*
1828 * Spawn a second process to check the GL texture limits
1829 * We do this across a process boundary to ensure that crashes
1830 * in the GL driver (which are unfortunately common) don't take
1831 * down the app.
1832 */
1833 int pipe_fd[2];
1834 pid_t canary_pid;
1835
1836 char * const canary_argv[] = { LIBEXECDIR "/check_gl_texture_size", NULL };
1837 char *canary_env[2];
1838 char display_env[80];
1839
1840 snprintf (display_env, sizeof (display_env), "DISPLAY=%s", DisplayString (screen->priv->xdisplay));
1841 canary_env[0] = display_env;
1842 canary_env[1] = NULL;
1843
1844
1845 if (pipe (pipe_fd) == -1)
1846 {
1847 _max_texture_size_cache = 0;
1848 return 0;
1849 }
1850 canary_pid = fork ();
1851 if (canary_pid == -1)
1852 {
1853 _max_texture_size_cache = 0;
1854 return 0;
1855 }
1856
1857 if (canary_pid == 0)
1858 {
1859 close (pipe_fd[0]);
1860 dup2 (pipe_fd[1], 1);
1861 close (pipe_fd[1]);
1862
1863 execve (canary_argv[0], canary_argv, canary_env);
1864 } else {
1865 char buffer[10];
1866 gint max_texture_size;
1867 int child_status;
1868 int num_char;
1869 struct timespec fifty_msec = {0, 50000000};
1870 int wait_count = 0;
1871
1872 close (pipe_fd[1]);
1873
1874 /* Empirical testing suggests this check takes < 150msec on my
1875 * crappy Atom netbook with slow rotating HDD. A 500msec timeout
1876 * should be generous while not being *too* long if it triggers.
1877 *
1878 * Do a sleep/poll dance because we're a library and there's no
1879 * guarantee that waiting on SIGCHLD won't stomp over a client's
1880 * set up.
1881 */
1882 while (waitpid (canary_pid, &child_status, WNOHANG) == 0 && wait_count < 10) {
1883 g_debug ("Waiting for GL_MAX_TEXTURE_SIZE helper...");
1884 nanosleep (&fifty_msec, NULL);
1885 wait_count++;
1886 }
1887
1888 if (WIFEXITED (child_status) && WEXITSTATUS (child_status) == EXIT_SUCCESS)
1889 {
1890 if ((num_char = read (pipe_fd[0], buffer, sizeof(buffer) - 1)) <= 0)
1891 {
1892 g_warning ("Failed to read GL_MAX_TEXTURE_SIZE from helper.");
1893 max_texture_size = 0;
1894 } else {
1895 buffer[num_char] = '\0';
1896 sscanf (buffer, "%u", &max_texture_size);
1897 /*
1898 * Sanity check the numbers. No hardware I know of has a
1899 * GL_MAX_TEXTURE_SIZE smaller than 1024.
1900 */
1901 if (max_texture_size < 1024)
1902 max_texture_size = 0;
1903 }
1904 } else {
1905 if (wait_count == 10) {
1906 g_warning ("Timed out waiting for GL_MAX_TEXTURE_SIZE helper");
1907
1908 /* Ensure we don't leave processes sitting around. Who knows what they're doing? */
1909 kill (canary_pid, SIGTERM);
1910 waitpid (canary_pid, &child_status, 0);
1911 } else {
1912 g_warning ("GL_MAX_TEXTURE_SIZE helper quit unexpectedly");
1913 }
1914 max_texture_size = 0;
1915 }
1916
1917 close (pipe_fd[0]);
1918 g_debug ("Found GL_MAX_TEXTURE_SIZE of %u", max_texture_size);
1919 _max_texture_size_cache = max_texture_size;
1920 return _max_texture_size_cache;
1921 }
1922 }
1923}
1924
1925static CrtcAssignment *
1926crtc_assignment_new (CCRRScreen *screen, CCRROutputInfo **outputs, GError **error)
1927{
1928 CrtcAssignment *assignment = g_new0 (CrtcAssignment, 1);
1929
1930 assignment->info = g_hash_table_new_full (
1931 g_direct_hash, g_direct_equal, NULL, (GFreeFunc)crtc_info_free);
1932
1933 if (real_assign_crtcs (screen, outputs, assignment, error))
1934 {
1935 int width, height;
1936 int min_width, max_width, min_height, max_height;
1937 int max_texture_size;
1938
1939 get_required_virtual_size (assignment, &width, &height);
1940
1941 cc_rr_screen_get_ranges (
1942 screen, &min_width, &max_width, &min_height, &max_height);
1943
1944 if (width < min_width || width > max_width ||
1945 height < min_height || height > max_height)
1946 {
1947 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_BOUNDS_ERROR,
1948 /* Translators: the "requested", "minimum", and
1949 * "maximum" words here are not keywords; please
1950 * translate them as usual. */
1951 _("required virtual size does not fit available size: "
1952 "requested=(%d, %d), minimum=(%d, %d), maximum=(%d, %d)"),
1953 width, height,
1954 min_width, min_height,
1955 max_width, max_height);
1956 goto fail;
1957 }
1958
1959 /* Hack:
1960 * This should either be solved by
1961 * (a) Allowing the compositor to veto RandR changes
1962 * (b) Fixing the compositor
1963 *
1964 * Nethier of these are feasible at this point, so just fix Unity.
1965 */
1966
1967 if (unity_running ())
1968 {
1969 max_texture_size = get_max_texture_size (screen);
1970 if (max_texture_size > 0 && (width > max_texture_size || height > max_texture_size))
1971 {
1972 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_BOUNDS_ERROR,
1973 _("Requested size (%d, %d) exceeds 3D hardware limit (%d, %d).\n"
1974 "You must either rearrange the displays so that they fit within a (%d, %d) square."),
1975 width, height, max_texture_size, max_texture_size,
1976 max_texture_size, max_texture_size);
1977 goto fail;
1978 }
1979 }
1980
1981
1982 assignment->screen = screen;
1983
1984 return assignment;
1985 }
1986
1987fail:
1988 crtc_assignment_free (assignment);
1989
1990 return NULL;
1991}
1992
1993static gboolean
1994crtc_assignment_apply (CrtcAssignment *assign, guint32 timestamp, GError **error)
1995{
1996 CCRRCrtc **all_crtcs = cc_rr_screen_list_crtcs (assign->screen);
1997 int width, height;
1998 int i;
1999 int min_width, max_width, min_height, max_height;
2000 int width_mm, height_mm;
2001 gboolean success = TRUE;
2002
2003 /* Compute size of the screen */
2004 get_required_virtual_size (assign, &width, &height);
2005
2006 cc_rr_screen_get_ranges (
2007 assign->screen, &min_width, &max_width, &min_height, &max_height);
2008
2009 /* We should never get here if the dimensions don't fit in the virtual size,
2010 * but just in case we do, fix it up.
2011 */
2012 width = MAX (min_width, width);
2013 width = MIN (max_width, width);
2014 height = MAX (min_height, height);
2015 height = MIN (max_height, height);
2016
2017 /* FMQ: do we need to check the sizes instead of clamping them? */
2018
2019 /* Grab the server while we fiddle with the CRTCs and the screen, so that
2020 * apps that listen for RANDR notifications will only receive the final
2021 * status.
2022 */
2023
2024 gdk_x11_display_grab (gdk_screen_get_display (assign->screen->priv->gdk_screen));
2025
2026 /* Turn off all crtcs that are currently displaying outside the new screen,
2027 * or are not used in the new setup
2028 */
2029 for (i = 0; all_crtcs[i] != NULL; ++i)
2030 {
2031 CCRRCrtc *crtc = all_crtcs[i];
2032 CCRRMode *mode = cc_rr_crtc_get_current_mode (crtc);
2033 int x, y;
2034
2035 if (mode)
2036 {
2037 int w, h;
2038 cc_rr_crtc_get_position (crtc, &x, &y);
2039
2040 w = cc_rr_mode_get_width (mode);
2041 h = cc_rr_mode_get_height (mode);
2042
2043 if (crtc_is_rotated (crtc))
2044 {
2045 int tmp = h;
2046 h = w;
2047 w = tmp;
2048 }
2049
2050 if (x + w > width || y + h > height || !g_hash_table_lookup (assign->info, crtc))
2051 {
2052 if (!cc_rr_crtc_set_config_with_time (crtc, timestamp, 0, 0, NULL, CC_RR_ROTATION_0, NULL, 0, error))
2053 {
2054 success = FALSE;
2055 break;
2056 }
2057
2058 }
2059 }
2060 }
2061
2062 /* The 'physical size' of an X screen is meaningless if that screen
2063 * can consist of many monitors. So just pick a size that make the
2064 * dpi 96.
2065 *
2066 * Firefox and Evince apparently believe what X tells them.
2067 */
2068 width_mm = (width / DPI_FALLBACK) * 25.4 + 0.5;
2069 height_mm = (height / DPI_FALLBACK) * 25.4 + 0.5;
2070
2071 if (success)
2072 {
2073 ConfigureCrtcState state;
2074
2075 cc_rr_screen_set_size (assign->screen, width, height, width_mm, height_mm);
2076
2077 state.timestamp = timestamp;
2078 state.has_error = FALSE;
2079 state.error = error;
2080
2081 g_hash_table_foreach (assign->info, configure_crtc, &state);
2082
2083 success = !state.has_error;
2084 }
2085
2086 cc_rr_screen_set_primary_output (assign->screen, assign->primary);
2087
2088 gdk_x11_display_ungrab (gdk_screen_get_display (assign->screen->priv->gdk_screen));
2089
2090 return success;
2091}
02092
=== added file 'panels/common/cc-rr-config.h'
--- panels/common/cc-rr-config.h 1970-01-01 00:00:00 +0000
+++ panels/common/cc-rr-config.h 2014-06-26 03:13:28 +0000
@@ -0,0 +1,151 @@
1/* gnome-rr-config.h
2 * -*- c-basic-offset: 4 -*-
3 *
4 * Copyright 2007, 2008, Red Hat, Inc.
5 * Copyright 2010 Giovanni Campagna
6 *
7 * This file is part of the Gnome Library.
8 *
9 * The Gnome Library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * The Gnome Library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with the Gnome Library; see the file COPYING.LIB. If not,
21 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 * Author: Soren Sandmann <sandmann@redhat.com>
25 */
26#ifndef CC_RR_CONFIG_H
27#define CC_RR_CONFIG_H
28
29#include <glib.h>
30#include <glib-object.h>
31#include "cc-rr.h"
32
33typedef struct _CCRROutputInfo CCRROutputInfo;
34typedef struct _CCRROutputInfoClass CCRROutputInfoClass;
35typedef struct _CCRROutputInfoPrivate CCRROutputInfoPrivate;
36
37struct _CCRROutputInfo
38{
39 GObject parent;
40
41 /*< private >*/
42 CCRROutputInfoPrivate *priv;
43};
44
45struct _CCRROutputInfoClass
46{
47 GObjectClass parent_class;
48};
49
50#define CC_TYPE_RR_OUTPUT_INFO (cc_rr_output_info_get_type())
51#define CC_RR_OUTPUT_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TYPE_RR_OUTPUT_INFO, CCRROutputInfo))
52#define CC_IS_RR_OUTPUT_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TYPE_RR_OUTPUT_INFO))
53#define CC_RR_OUTPUT_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CC_TYPE_RR_OUTPUT_INFO, CCRROutputInfoClass))
54#define CC_IS_RR_OUTPUT_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CC_TYPE_RR_OUTPUT_INFO))
55#define CC_RR_OUTPUT_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CC_TYPE_RR_OUTPUT_INFO, CCRROutputInfoClass))
56
57GType cc_rr_output_info_get_type (void);
58
59char *cc_rr_output_info_get_name (CCRROutputInfo *self);
60
61gboolean cc_rr_output_info_is_active (CCRROutputInfo *self);
62void cc_rr_output_info_set_active (CCRROutputInfo *self, gboolean active);
63
64void cc_rr_output_info_get_geometry (CCRROutputInfo *self, int *x, int *y, int *width, int *height);
65void cc_rr_output_info_set_geometry (CCRROutputInfo *self, int x, int y, int width, int height);
66
67int cc_rr_output_info_get_refresh_rate (CCRROutputInfo *self);
68void cc_rr_output_info_set_refresh_rate (CCRROutputInfo *self, int rate);
69
70CCRRRotation cc_rr_output_info_get_rotation (CCRROutputInfo *self);
71void cc_rr_output_info_set_rotation (CCRROutputInfo *self, CCRRRotation rotation);
72
73gboolean cc_rr_output_info_is_connected (CCRROutputInfo *self);
74void cc_rr_output_info_get_vendor (CCRROutputInfo *self, gchar* vendor);
75guint cc_rr_output_info_get_product (CCRROutputInfo *self);
76guint cc_rr_output_info_get_serial (CCRROutputInfo *self);
77double cc_rr_output_info_get_aspect_ratio (CCRROutputInfo *self);
78char *cc_rr_output_info_get_display_name (CCRROutputInfo *self);
79
80gboolean cc_rr_output_info_get_primary (CCRROutputInfo *self);
81void cc_rr_output_info_set_primary (CCRROutputInfo *self, gboolean primary);
82
83int cc_rr_output_info_get_preferred_width (CCRROutputInfo *self);
84int cc_rr_output_info_get_preferred_height (CCRROutputInfo *self);
85
86typedef struct _CCRRConfig CCRRConfig;
87typedef struct _CCRRConfigClass CCRRConfigClass;
88typedef struct _CCRRConfigPrivate CCRRConfigPrivate;
89
90struct _CCRRConfig
91{
92 GObject parent;
93
94 /*< private >*/
95 CCRRConfigPrivate *priv;
96};
97
98struct _CCRRConfigClass
99{
100 GObjectClass parent_class;
101};
102
103#define CC_TYPE_RR_CONFIG (cc_rr_config_get_type())
104#define CC_RR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TYPE_RR_CONFIG, CCRRConfig))
105#define CC_IS_RR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TYPE_RR_CONFIG))
106#define CC_RR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CC_TYPE_RR_CONFIG, CCRRConfigClass))
107#define CC_IS_RR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CC_TYPE_RR_CONFIG))
108#define CC_RR_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CC_TYPE_RR_CONFIG, CCRRConfigClass))
109
110GType cc_rr_config_get_type (void);
111
112CCRRConfig *cc_rr_config_new_current (CCRRScreen *screen,
113 GError **error);
114CCRRConfig *cc_rr_config_new_stored (CCRRScreen *screen,
115 GError **error);
116gboolean cc_rr_config_load_current (CCRRConfig *self,
117 GError **error);
118gboolean cc_rr_config_load_filename (CCRRConfig *self,
119 const gchar *filename,
120 GError **error);
121gboolean cc_rr_config_match (CCRRConfig *config1,
122 CCRRConfig *config2);
123gboolean cc_rr_config_equal (CCRRConfig *config1,
124 CCRRConfig *config2);
125gboolean cc_rr_config_save (CCRRConfig *configuration,
126 GError **error);
127void cc_rr_config_sanitize (CCRRConfig *configuration);
128gboolean cc_rr_config_ensure_primary (CCRRConfig *configuration);
129
130gboolean cc_rr_config_apply_with_time (CCRRConfig *configuration,
131 CCRRScreen *screen,
132 guint32 timestamp,
133 GError **error);
134
135gboolean cc_rr_config_apply_from_filename_with_time (CCRRScreen *screen,
136 const char *filename,
137 guint32 timestamp,
138 GError **error);
139
140gboolean cc_rr_config_applicable (CCRRConfig *configuration,
141 CCRRScreen *screen,
142 GError **error);
143
144gboolean cc_rr_config_get_clone (CCRRConfig *configuration);
145void cc_rr_config_set_clone (CCRRConfig *configuration, gboolean clone);
146CCRROutputInfo **cc_rr_config_get_outputs (CCRRConfig *configuration);
147
148char *cc_rr_config_get_backup_filename (void);
149char *cc_rr_config_get_intended_filename (void);
150
151#endif
0152
=== added file 'panels/common/cc-rr-output-info.c'
--- panels/common/cc-rr-output-info.c 1970-01-01 00:00:00 +0000
+++ panels/common/cc-rr-output-info.c 2014-06-26 03:13:28 +0000
@@ -0,0 +1,242 @@
1/* gnome-rr-output-info.c
2 * -*- c-basic-offset: 4 -*-
3 *
4 * Copyright 2010 Giovanni Campagna
5 *
6 * This file is part of the Gnome Desktop Library.
7 *
8 * The Gnome Desktop Library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * The Gnome Library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with the Gnome Desktop Library; see the file COPYING.LIB. If not,
20 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include <config.h>
25
26#include "cc-rr-config.h"
27
28#include "edid.h"
29#include "cc-rr-private.h"
30
31G_DEFINE_TYPE (CCRROutputInfo, cc_rr_output_info, G_TYPE_OBJECT)
32
33static void
34cc_rr_output_info_init (CCRROutputInfo *self)
35{
36 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CC_TYPE_RR_OUTPUT_INFO, CCRROutputInfoPrivate);
37
38 self->priv->name = NULL;
39 self->priv->on = FALSE;
40 self->priv->display_name = NULL;
41}
42
43static void
44cc_rr_output_info_finalize (GObject *gobject)
45{
46 CCRROutputInfo *self = CC_RR_OUTPUT_INFO (gobject);
47
48 g_free (self->priv->name);
49 g_free (self->priv->display_name);
50
51 G_OBJECT_CLASS (cc_rr_output_info_parent_class)->finalize (gobject);
52}
53
54static void
55cc_rr_output_info_class_init (CCRROutputInfoClass *klass)
56{
57 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
58
59 g_type_class_add_private (klass, sizeof (CCRROutputInfoPrivate));
60
61 gobject_class->finalize = cc_rr_output_info_finalize;
62}
63
64/**
65 * cc_rr_output_info_get_name:
66 *
67 * Returns: (transfer none): the output name
68 */
69char *cc_rr_output_info_get_name (CCRROutputInfo *self)
70{
71 g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), NULL);
72
73 return self->priv->name;
74}
75
76/**
77 * cc_rr_output_info_is_active:
78 *
79 * Returns: whether there is a CRTC assigned to this output (i.e. a signal is being sent to it)
80 */
81gboolean cc_rr_output_info_is_active (CCRROutputInfo *self)
82{
83 g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), FALSE);
84
85 return self->priv->on;
86}
87
88void cc_rr_output_info_set_active (CCRROutputInfo *self, gboolean active)
89{
90 g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
91
92 self->priv->on = active;
93}
94
95/**
96 * cc_rr_output_info_get_geometry:
97 * @self: a #CCRROutputInfo
98 * @x: (out) (allow-none):
99 * @y: (out) (allow-none):
100 * @width: (out) (allow-none):
101 * @height: (out) (allow-none):
102 */
103void cc_rr_output_info_get_geometry (CCRROutputInfo *self, int *x, int *y, int *width, int *height)
104{
105 g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
106
107 if (x)
108 *x = self->priv->x;
109 if (y)
110 *y = self->priv->y;
111 if (width)
112 *width = self->priv->width;
113 if (height)
114 *height = self->priv->height;
115}
116
117void cc_rr_output_info_set_geometry (CCRROutputInfo *self, int x, int y, int width, int height)
118{
119 g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
120
121 self->priv->x = x;
122 self->priv->y = y;
123 self->priv->width = width;
124 self->priv->height = height;
125}
126
127int cc_rr_output_info_get_refresh_rate (CCRROutputInfo *self)
128{
129 g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), 0);
130
131 return self->priv->rate;
132}
133
134void cc_rr_output_info_set_refresh_rate (CCRROutputInfo *self, int rate)
135{
136 g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
137
138 self->priv->rate = rate;
139}
140
141CCRRRotation cc_rr_output_info_get_rotation (CCRROutputInfo *self)
142{
143 g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), CC_RR_ROTATION_0);
144
145 return self->priv->rotation;
146}
147
148void cc_rr_output_info_set_rotation (CCRROutputInfo *self, CCRRRotation rotation)
149{
150 g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
151
152 self->priv->rotation = rotation;
153}
154
155/**
156 * cc_rr_output_info_is_connected:
157 *
158 * Returns: whether the output is physically connected to a monitor
159 */
160gboolean cc_rr_output_info_is_connected (CCRROutputInfo *self)
161{
162 g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), FALSE);
163
164 return self->priv->connected;
165}
166
167/**
168 * cc_rr_output_info_get_vendor:
169 * @self: a #CCRROutputInfo
170 * @vendor: (out caller-allocates) (array fixed-size=4):
171 */
172void cc_rr_output_info_get_vendor (CCRROutputInfo *self, gchar* vendor)
173{
174 g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
175 g_return_if_fail (vendor != NULL);
176
177 vendor[0] = self->priv->vendor[0];
178 vendor[1] = self->priv->vendor[1];
179 vendor[2] = self->priv->vendor[2];
180 vendor[3] = self->priv->vendor[3];
181}
182
183guint cc_rr_output_info_get_product (CCRROutputInfo *self)
184{
185 g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), 0);
186
187 return self->priv->product;
188}
189
190guint cc_rr_output_info_get_serial (CCRROutputInfo *self)
191{
192 g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), 0);
193
194 return self->priv->serial;
195}
196
197double cc_rr_output_info_get_aspect_ratio (CCRROutputInfo *self)
198{
199 g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), 0);
200
201 return self->priv->aspect;
202}
203
204/**
205 * cc_rr_output_info_get_display_name:
206 *
207 * Returns: (transfer none): the display name of this output
208 */
209char *cc_rr_output_info_get_display_name (CCRROutputInfo *self)
210{
211 g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), NULL);
212
213 return self->priv->display_name;
214}
215
216gboolean cc_rr_output_info_get_primary (CCRROutputInfo *self)
217{
218 g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), FALSE);
219
220 return self->priv->primary;
221}
222
223void cc_rr_output_info_set_primary (CCRROutputInfo *self, gboolean primary)
224{
225 g_return_if_fail (CC_IS_RR_OUTPUT_INFO (self));
226
227 self->priv->primary = primary;
228}
229
230int cc_rr_output_info_get_preferred_width (CCRROutputInfo *self)
231{
232 g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), 0);
233
234 return self->priv->pref_width;
235}
236
237int cc_rr_output_info_get_preferred_height (CCRROutputInfo *self)
238{
239 g_return_val_if_fail (CC_IS_RR_OUTPUT_INFO (self), 0);
240
241 return self->priv->pref_height;
242}
0243
=== added file 'panels/common/cc-rr-private.h'
--- panels/common/cc-rr-private.h 1970-01-01 00:00:00 +0000
+++ panels/common/cc-rr-private.h 2014-06-26 03:13:28 +0000
@@ -0,0 +1,79 @@
1#ifndef CC_RR_PRIVATE_H
2#define CC_RR_PRIVATE_H
3
4#include <X11/Xlib.h>
5
6#include <X11/extensions/Xrandr.h>
7
8typedef struct ScreenInfo ScreenInfo;
9
10struct ScreenInfo
11{
12 int min_width;
13 int max_width;
14 int min_height;
15 int max_height;
16
17 XRRScreenResources *resources;
18
19 CCRROutput ** outputs;
20 CCRRCrtc ** crtcs;
21 CCRRMode ** modes;
22
23 CCRRScreen * screen;
24
25 CCRRMode ** clone_modes;
26
27 RROutput primary;
28};
29
30struct CCRRScreenPrivate
31{
32 GdkScreen * gdk_screen;
33 GdkWindow * gdk_root;
34 Display * xdisplay;
35 Screen * xscreen;
36 Window xroot;
37 ScreenInfo * info;
38
39 int randr_event_base;
40 int rr_major_version;
41 int rr_minor_version;
42
43 Atom connector_type_atom;
44 gboolean dpms_capable;
45};
46
47struct _CCRROutputInfoPrivate
48{
49 char * name;
50
51 gboolean on;
52 int width;
53 int height;
54 int rate;
55 int x;
56 int y;
57 CCRRRotation rotation;
58
59 gboolean connected;
60 gchar vendor[4];
61 guint product;
62 guint serial;
63 double aspect;
64 int pref_width;
65 int pref_height;
66 char * display_name;
67 gboolean primary;
68};
69
70struct _CCRRConfigPrivate
71{
72 gboolean clone;
73 CCRRScreen *screen;
74 CCRROutputInfo **outputs;
75};
76
77gboolean _cc_rr_output_name_is_laptop (const char *name);
78
79#endif
080
=== added file 'panels/common/cc-rr.c'
--- panels/common/cc-rr.c 1970-01-01 00:00:00 +0000
+++ panels/common/cc-rr.c 2014-06-26 03:13:28 +0000
@@ -0,0 +1,2622 @@
1/* gnome-rr.c
2 *
3 * Copyright 2007, 2008, Red Hat, Inc.
4 *
5 * This file is part of the Gnome Library.
6 *
7 * The Gnome Library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * The Gnome Library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with the Gnome Library; see the file COPYING.LIB. If not,
19 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 * Author: Soren Sandmann <sandmann@redhat.com>
23 */
24
25#include <config.h>
26#include <glib/gi18n-lib.h>
27#include <string.h>
28#include <X11/Xlib.h>
29
30#include <X11/extensions/Xrandr.h>
31
32#include <gtk/gtk.h>
33#include <gdk/gdkx.h>
34#include <X11/Xatom.h>
35#include <X11/extensions/dpms.h>
36
37#undef GNOME_DISABLE_DEPRECATED
38#include "cc-rr.h"
39#include "cc-rr-config.h"
40
41#include "edid.h"
42#include "cc-rr-private.h"
43
44#define DISPLAY(o) ((o)->info->screen->priv->xdisplay)
45
46#define SERVERS_RANDR_IS_AT_LEAST_1_3(priv) (priv->rr_major_version > 1 || (priv->rr_major_version == 1 && priv->rr_minor_version >= 3))
47
48enum {
49 SCREEN_PROP_0,
50 SCREEN_PROP_GDK_SCREEN,
51 SCREEN_PROP_LAST,
52};
53
54enum {
55 SCREEN_CHANGED,
56 SCREEN_OUTPUT_CONNECTED,
57 SCREEN_OUTPUT_DISCONNECTED,
58 SCREEN_SIGNAL_LAST,
59};
60
61gint screen_signals[SCREEN_SIGNAL_LAST];
62
63struct CCRROutput
64{
65 ScreenInfo * info;
66 RROutput id;
67
68 char * name;
69 char * display_name;
70 CCRRCrtc * current_crtc;
71 gboolean connected;
72 gulong width_mm;
73 gulong height_mm;
74 CCRRCrtc ** possible_crtcs;
75 CCRROutput ** clones;
76 CCRRMode ** modes;
77 int n_preferred;
78 guint8 * edid_data;
79 gsize edid_size;
80 char * connector_type;
81 gint backlight_min;
82 gint backlight_max;
83};
84
85struct CCRROutputWrap
86{
87 RROutput id;
88};
89
90struct CCRRCrtc
91{
92 ScreenInfo * info;
93 RRCrtc id;
94
95 CCRRMode * current_mode;
96 CCRROutput ** current_outputs;
97 CCRROutput ** possible_outputs;
98 int x;
99 int y;
100
101 CCRRRotation current_rotation;
102 CCRRRotation rotations;
103 int gamma_size;
104};
105
106struct CCRRMode
107{
108 ScreenInfo * info;
109 RRMode id;
110 char * name;
111 int width;
112 int height;
113 int freq; /* in mHz */
114};
115
116/* CCRRCrtc */
117static CCRRCrtc * crtc_new (ScreenInfo *info,
118 RRCrtc id);
119static CCRRCrtc * crtc_copy (const CCRRCrtc *from);
120static void crtc_free (CCRRCrtc *crtc);
121
122static gboolean crtc_initialize (CCRRCrtc *crtc,
123 XRRScreenResources *res,
124 GError **error);
125
126/* CCRROutput */
127static CCRROutput *output_new (ScreenInfo *info,
128 RROutput id);
129
130static gboolean output_initialize (CCRROutput *output,
131 XRRScreenResources *res,
132 GError **error);
133
134static CCRROutput *output_copy (const CCRROutput *from);
135static void output_free (CCRROutput *output);
136
137/* CCRRMode */
138static CCRRMode * mode_new (ScreenInfo *info,
139 RRMode id);
140
141static void mode_initialize (CCRRMode *mode,
142 XRRModeInfo *info);
143
144static CCRRMode * mode_copy (const CCRRMode *from);
145static void mode_free (CCRRMode *mode);
146
147static void cc_rr_screen_finalize (GObject*);
148static void cc_rr_screen_set_property (GObject*, guint, const GValue*, GParamSpec*);
149static void cc_rr_screen_get_property (GObject*, guint, GValue*, GParamSpec*);
150static gboolean cc_rr_screen_initable_init (GInitable*, GCancellable*, GError**);
151static void cc_rr_screen_initable_iface_init (GInitableIface *iface);
152G_DEFINE_TYPE_WITH_CODE (CCRRScreen, cc_rr_screen, G_TYPE_OBJECT,
153 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, cc_rr_screen_initable_iface_init))
154
155G_DEFINE_BOXED_TYPE (CCRRCrtc, cc_rr_crtc, crtc_copy, crtc_free)
156G_DEFINE_BOXED_TYPE (CCRROutput, cc_rr_output, output_copy, output_free)
157G_DEFINE_BOXED_TYPE (CCRRMode, cc_rr_mode, mode_copy, mode_free)
158
159/* Errors */
160
161/**
162 * cc_rr_error_quark:
163 *
164 * Returns the #GQuark that will be used for #GError values returned by the
165 * CCRR API.
166 *
167 * Return value: a #GQuark used to identify errors coming from the CCRR API.
168 */
169GQuark
170cc_rr_error_quark (void)
171{
172 return g_quark_from_static_string ("cc-rr-error-quark");
173}
174
175/* Screen */
176static CCRROutput *
177cc_rr_output_by_id (ScreenInfo *info, RROutput id)
178{
179 CCRROutput **output;
180
181 g_assert (info != NULL);
182
183 for (output = info->outputs; *output; ++output)
184 {
185 if ((*output)->id == id)
186 return *output;
187 }
188
189 return NULL;
190}
191
192static CCRRCrtc *
193crtc_by_id (ScreenInfo *info, RRCrtc id)
194{
195 CCRRCrtc **crtc;
196
197 if (!info)
198 return NULL;
199
200 for (crtc = info->crtcs; *crtc; ++crtc)
201 {
202 if ((*crtc)->id == id)
203 return *crtc;
204 }
205
206 return NULL;
207}
208
209static CCRRMode *
210mode_by_id (ScreenInfo *info, RRMode id)
211{
212 CCRRMode **mode;
213
214 g_assert (info != NULL);
215
216 for (mode = info->modes; *mode; ++mode)
217 {
218 if ((*mode)->id == id)
219 return *mode;
220 }
221
222 return NULL;
223}
224
225static void
226screen_info_free (ScreenInfo *info)
227{
228 CCRROutput **output;
229 CCRRCrtc **crtc;
230 CCRRMode **mode;
231
232 g_assert (info != NULL);
233
234 if (info->resources)
235 {
236 XRRFreeScreenResources (info->resources);
237
238 info->resources = NULL;
239 }
240
241 if (info->outputs)
242 {
243 for (output = info->outputs; *output; ++output)
244 output_free (*output);
245 g_free (info->outputs);
246 }
247
248 if (info->crtcs)
249 {
250 for (crtc = info->crtcs; *crtc; ++crtc)
251 crtc_free (*crtc);
252 g_free (info->crtcs);
253 }
254
255 if (info->modes)
256 {
257 for (mode = info->modes; *mode; ++mode)
258 mode_free (*mode);
259 g_free (info->modes);
260 }
261
262 if (info->clone_modes)
263 {
264 /* The modes themselves were freed above */
265 g_free (info->clone_modes);
266 }
267
268 g_free (info);
269}
270
271static gboolean
272has_similar_mode (CCRROutput *output, CCRRMode *mode)
273{
274 int i;
275 CCRRMode **modes = cc_rr_output_list_modes (output);
276 int width = cc_rr_mode_get_width (mode);
277 int height = cc_rr_mode_get_height (mode);
278
279 for (i = 0; modes[i] != NULL; ++i)
280 {
281 CCRRMode *m = modes[i];
282
283 if (cc_rr_mode_get_width (m) == width &&
284 cc_rr_mode_get_height (m) == height)
285 {
286 return TRUE;
287 }
288 }
289
290 return FALSE;
291}
292
293static void
294gather_clone_modes (ScreenInfo *info)
295{
296 int i;
297 GPtrArray *result = g_ptr_array_new ();
298
299 for (i = 0; info->outputs[i] != NULL; ++i)
300 {
301 int j;
302 CCRROutput *output1, *output2;
303
304 output1 = info->outputs[i];
305
306 if (!output1->connected)
307 continue;
308
309 for (j = 0; output1->modes[j] != NULL; ++j)
310 {
311 CCRRMode *mode = output1->modes[j];
312 gboolean valid;
313 int k;
314
315 valid = TRUE;
316 for (k = 0; info->outputs[k] != NULL; ++k)
317 {
318 output2 = info->outputs[k];
319
320 if (!output2->connected)
321 continue;
322
323 if (!has_similar_mode (output2, mode))
324 {
325 valid = FALSE;
326 break;
327 }
328 }
329
330 if (valid)
331 g_ptr_array_add (result, mode);
332 }
333 }
334
335 g_ptr_array_add (result, NULL);
336
337 info->clone_modes = (CCRRMode **)g_ptr_array_free (result, FALSE);
338}
339
340static gboolean
341fill_screen_info_from_resources (ScreenInfo *info,
342 XRRScreenResources *resources,
343 GError **error)
344{
345 int i;
346 GPtrArray *a;
347 CCRRCrtc **crtc;
348 CCRROutput **output;
349
350 info->resources = resources;
351
352 /* We create all the structures before initializing them, so
353 * that they can refer to each other.
354 */
355 a = g_ptr_array_new ();
356 for (i = 0; i < resources->ncrtc; ++i)
357 {
358 CCRRCrtc *crtc = crtc_new (info, resources->crtcs[i]);
359
360 g_ptr_array_add (a, crtc);
361 }
362 g_ptr_array_add (a, NULL);
363 info->crtcs = (CCRRCrtc **)g_ptr_array_free (a, FALSE);
364
365 a = g_ptr_array_new ();
366 for (i = 0; i < resources->noutput; ++i)
367 {
368 CCRROutput *output = output_new (info, resources->outputs[i]);
369
370 g_ptr_array_add (a, output);
371 }
372 g_ptr_array_add (a, NULL);
373 info->outputs = (CCRROutput **)g_ptr_array_free (a, FALSE);
374
375 a = g_ptr_array_new ();
376 for (i = 0; i < resources->nmode; ++i)
377 {
378 CCRRMode *mode = mode_new (info, resources->modes[i].id);
379
380 g_ptr_array_add (a, mode);
381 }
382 g_ptr_array_add (a, NULL);
383 info->modes = (CCRRMode **)g_ptr_array_free (a, FALSE);
384
385 /* Initialize */
386 for (crtc = info->crtcs; *crtc; ++crtc)
387 {
388 if (!crtc_initialize (*crtc, resources, error))
389 return FALSE;
390 }
391
392 for (output = info->outputs; *output; ++output)
393 {
394 if (!output_initialize (*output, resources, error))
395 return FALSE;
396 }
397
398 for (i = 0; i < resources->nmode; ++i)
399 {
400 CCRRMode *mode = mode_by_id (info, resources->modes[i].id);
401
402 mode_initialize (mode, &(resources->modes[i]));
403 }
404
405 gather_clone_modes (info);
406
407 return TRUE;
408}
409
410static gboolean
411fill_out_screen_info (Display *xdisplay,
412 Window xroot,
413 ScreenInfo *info,
414 gboolean needs_reprobe,
415 GError **error)
416{
417 XRRScreenResources *resources;
418 CCRRScreenPrivate *priv;
419
420 g_assert (xdisplay != NULL);
421 g_assert (info != NULL);
422
423 priv = info->screen->priv;
424
425 /* First update the screen resources */
426
427 if (needs_reprobe)
428 resources = XRRGetScreenResources (xdisplay, xroot);
429 else
430 {
431 /* XRRGetScreenResourcesCurrent is less expensive than
432 * XRRGetScreenResources, however it is available only
433 * in RandR 1.3 or higher
434 */
435 if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv))
436 resources = XRRGetScreenResourcesCurrent (xdisplay, xroot);
437 else
438 resources = XRRGetScreenResources (xdisplay, xroot);
439 }
440
441 if (resources)
442 {
443 if (!fill_screen_info_from_resources (info, resources, error))
444 return FALSE;
445 }
446 else
447 {
448 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_RANDR_ERROR,
449 /* Translators: a CRTC is a CRT Controller (this is X terminology). */
450 _("could not get the screen resources (CRTCs, outputs, modes)"));
451 return FALSE;
452 }
453
454 /* Then update the screen size range. We do this after XRRGetScreenResources() so that
455 * the X server will already have an updated view of the outputs.
456 */
457
458 if (needs_reprobe) {
459 gboolean success;
460
461 gdk_error_trap_push ();
462 success = XRRGetScreenSizeRange (xdisplay, xroot,
463 &(info->min_width),
464 &(info->min_height),
465 &(info->max_width),
466 &(info->max_height));
467 gdk_flush ();
468 if (gdk_error_trap_pop ()) {
469 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_UNKNOWN,
470 _("unhandled X error while getting the range of screen sizes"));
471 return FALSE;
472 }
473
474 if (!success) {
475 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_RANDR_ERROR,
476 _("could not get the range of screen sizes"));
477 return FALSE;
478 }
479 }
480 else
481 {
482 cc_rr_screen_get_ranges (info->screen,
483 &(info->min_width),
484 &(info->max_width),
485 &(info->min_height),
486 &(info->max_height));
487 }
488
489 info->primary = None;
490 if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv)) {
491 gdk_error_trap_push ();
492 info->primary = XRRGetOutputPrimary (xdisplay, xroot);
493 gdk_error_trap_pop_ignored ();
494 }
495
496 /* can the screen do DPMS? */
497 gdk_error_trap_push ();
498 priv->dpms_capable = DPMSCapable (priv->xdisplay);
499 gdk_error_trap_pop_ignored ();
500
501 return TRUE;
502}
503
504static ScreenInfo *
505screen_info_new (CCRRScreen *screen, gboolean needs_reprobe, GError **error)
506{
507 ScreenInfo *info = g_new0 (ScreenInfo, 1);
508 CCRRScreenPrivate *priv;
509
510 g_assert (screen != NULL);
511
512 priv = screen->priv;
513
514 info->outputs = NULL;
515 info->crtcs = NULL;
516 info->modes = NULL;
517 info->screen = screen;
518
519 if (fill_out_screen_info (priv->xdisplay, priv->xroot, info, needs_reprobe, error))
520 {
521 return info;
522 }
523 else
524 {
525 screen_info_free (info);
526 return NULL;
527 }
528}
529
530static CCRROutput *
531find_output_by_id (CCRROutput **haystack, guint32 id)
532{
533 guint i;
534
535 for (i = 0; haystack[i] != NULL; i++)
536 {
537 if (cc_rr_output_get_id (haystack[i]) == id)
538 return haystack[i];
539 }
540 return NULL;
541}
542
543static void
544diff_outputs_and_emit_signals (ScreenInfo *old, ScreenInfo *new)
545{
546 guint i;
547 guint32 id_old, id_new;
548 CCRROutput *output_old;
549 CCRROutput *output_new;
550
551 /* have any outputs been removed or disconnected */
552 for (i = 0; old->outputs[i] != NULL; i++)
553 {
554 id_old = cc_rr_output_get_id (old->outputs[i]);
555 output_new = find_output_by_id (new->outputs, id_old);
556 if (output_new == NULL)
557 {
558 /* output removed (and disconnected) */
559 if (cc_rr_output_is_connected (old->outputs[i]))
560 {
561 g_signal_emit (G_OBJECT (new->screen),
562 screen_signals[SCREEN_OUTPUT_DISCONNECTED], 0,
563 old->outputs[i]);
564 }
565 continue;
566 }
567 if (cc_rr_output_is_connected (old->outputs[i]) &&
568 !cc_rr_output_is_connected (output_new))
569 {
570 /* output disconnected */
571 g_signal_emit (G_OBJECT (new->screen),
572 screen_signals[SCREEN_OUTPUT_DISCONNECTED], 0,
573 old->outputs[i]);
574 }
575 }
576
577 /* have any outputs been created or connected */
578 for (i = 0; new->outputs[i] != NULL; i++)
579 {
580 id_new = cc_rr_output_get_id (new->outputs[i]);
581 output_old = find_output_by_id (old->outputs, id_new);
582 if (output_old == NULL)
583 {
584 /* output created */
585 if (cc_rr_output_is_connected (new->outputs[i]))
586 {
587 g_signal_emit (G_OBJECT (new->screen),
588 screen_signals[SCREEN_OUTPUT_CONNECTED], 0,
589 new->outputs[i]);
590 }
591 continue;
592 }
593 if (!cc_rr_output_is_connected (output_old) &&
594 cc_rr_output_is_connected (new->outputs[i]))
595 {
596 /* output connected */
597 g_signal_emit (G_OBJECT (new->screen),
598 screen_signals[SCREEN_OUTPUT_CONNECTED], 0,
599 new->outputs[i]);
600 }
601 }
602}
603
604static gboolean
605screen_update (CCRRScreen *screen, gboolean force_callback, gboolean needs_reprobe, GError **error)
606{
607 ScreenInfo *info;
608 gboolean changed = FALSE;
609
610 g_assert (screen != NULL);
611
612 info = screen_info_new (screen, needs_reprobe, error);
613 if (!info)
614 return FALSE;
615
616 if (info->resources->configTimestamp != screen->priv->info->resources->configTimestamp)
617 changed = TRUE;
618
619 /* work out if any outputs have changed connected state */
620 diff_outputs_and_emit_signals (screen->priv->info, info);
621
622 screen_info_free (screen->priv->info);
623
624 screen->priv->info = info;
625
626 if (changed || force_callback)
627 g_signal_emit (G_OBJECT (screen), screen_signals[SCREEN_CHANGED], 0);
628
629 return changed;
630}
631
632static GdkFilterReturn
633screen_on_event (GdkXEvent *xevent,
634 GdkEvent *event,
635 gpointer data)
636{
637 CCRRScreen *screen = data;
638 CCRRScreenPrivate *priv = screen->priv;
639 XEvent *e = xevent;
640 int event_num;
641
642 if (!e)
643 return GDK_FILTER_CONTINUE;
644
645 event_num = e->type - priv->randr_event_base;
646
647 if (event_num == RRScreenChangeNotify) {
648 /* We don't reprobe the hardware; we just fetch the X server's latest
649 * state. The server already knows the new state of the outputs; that's
650 * why it sent us an event!
651 */
652 screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */
653#if 0
654 /* Enable this code to get a dialog showing the RANDR timestamps, for debugging purposes */
655 {
656 GtkWidget *dialog;
657 XRRScreenChangeNotifyEvent *rr_event;
658 static int dialog_num;
659
660 rr_event = (XRRScreenChangeNotifyEvent *) e;
661
662 dialog = gtk_message_dialog_new (NULL,
663 0,
664 GTK_MESSAGE_INFO,
665 GTK_BUTTONS_CLOSE,
666 "RRScreenChangeNotify timestamps (%d):\n"
667 "event change: %u\n"
668 "event config: %u\n"
669 "event serial: %lu\n"
670 "----------------------"
671 "screen change: %u\n"
672 "screen config: %u\n",
673 dialog_num++,
674 (guint32) rr_event->timestamp,
675 (guint32) rr_event->config_timestamp,
676 rr_event->serial,
677 (guint32) priv->info->resources->timestamp,
678 (guint32) priv->info->resources->configTimestamp);
679 g_signal_connect (dialog, "response",
680 G_CALLBACK (gtk_widget_destroy), NULL);
681 gtk_widget_show (dialog);
682 }
683#endif
684 }
685#if 0
686 /* WHY THIS CODE IS DISABLED:
687 *
688 * Note that in cc_rr_screen_new(), we only select for
689 * RRScreenChangeNotifyMask. We used to select for other values in
690 * RR*NotifyMask, but we weren't really doing anything useful with those
691 * events. We only care about "the screens changed in some way or another"
692 * for now.
693 *
694 * If we ever run into a situtation that could benefit from processing more
695 * detailed events, we can enable this code again.
696 *
697 * Note that the X server sends RRScreenChangeNotify in conjunction with the
698 * more detailed events from RANDR 1.2 - see xserver/randr/randr.c:TellChanged().
699 */
700 else if (event_num == RRNotify)
701 {
702 /* Other RandR events */
703
704 XRRNotifyEvent *event = (XRRNotifyEvent *)e;
705
706 /* Here we can distinguish between RRNotify events supported
707 * since RandR 1.2 such as RRNotify_OutputProperty. For now, we
708 * don't have anything special to do for particular subevent types, so
709 * we leave this as an empty switch().
710 */
711 switch (event->subtype)
712 {
713 default:
714 break;
715 }
716
717 /* No need to reprobe hardware here */
718 screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */
719 }
720#endif
721
722 /* Pass the event on to GTK+ */
723 return GDK_FILTER_CONTINUE;
724}
725
726static gboolean
727cc_rr_screen_initable_init (GInitable *initable, GCancellable *canc, GError **error)
728{
729 CCRRScreen *self = CC_RR_SCREEN (initable);
730 CCRRScreenPrivate *priv = self->priv;
731 Display *dpy = GDK_SCREEN_XDISPLAY (self->priv->gdk_screen);
732 int event_base;
733 int ignore;
734
735 priv->connector_type_atom = XInternAtom (dpy, "ConnectorType", FALSE);
736
737 if (XRRQueryExtension (dpy, &event_base, &ignore))
738 {
739 priv->randr_event_base = event_base;
740
741 XRRQueryVersion (dpy, &priv->rr_major_version, &priv->rr_minor_version);
742 if (priv->rr_major_version < 1 || (priv->rr_major_version == 1 && priv->rr_minor_version < 2)) {
743 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_NO_RANDR_EXTENSION,
744 "RANDR extension is too old (must be at least 1.2)");
745 return FALSE;
746 }
747
748 priv->info = screen_info_new (self, TRUE, error);
749
750 if (!priv->info) {
751 return FALSE;
752 }
753
754 XRRSelectInput (priv->xdisplay,
755 priv->xroot,
756 RRScreenChangeNotifyMask);
757 gdk_x11_register_standard_event_type (gdk_screen_get_display (priv->gdk_screen),
758 event_base,
759 RRNotify + 1);
760 gdk_window_add_filter (priv->gdk_root, screen_on_event, self);
761
762 return TRUE;
763 }
764 else
765 {
766 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_NO_RANDR_EXTENSION,
767 _("RANDR extension is not present"));
768
769 return FALSE;
770 }
771}
772
773void
774cc_rr_screen_initable_iface_init (GInitableIface *iface)
775{
776 iface->init = cc_rr_screen_initable_init;
777}
778
779void
780cc_rr_screen_finalize (GObject *gobject)
781{
782 CCRRScreen *screen = CC_RR_SCREEN (gobject);
783
784 gdk_window_remove_filter (screen->priv->gdk_root, screen_on_event, screen);
785
786 if (screen->priv->info)
787 screen_info_free (screen->priv->info);
788
789 G_OBJECT_CLASS (cc_rr_screen_parent_class)->finalize (gobject);
790}
791
792void
793cc_rr_screen_set_property (GObject *gobject, guint property_id, const GValue *value, GParamSpec *property)
794{
795 CCRRScreen *self = CC_RR_SCREEN (gobject);
796 CCRRScreenPrivate *priv = self->priv;
797
798 switch (property_id)
799 {
800 case SCREEN_PROP_GDK_SCREEN:
801 priv->gdk_screen = g_value_get_object (value);
802 priv->gdk_root = gdk_screen_get_root_window (priv->gdk_screen);
803 priv->xroot = gdk_x11_window_get_xid (priv->gdk_root);
804 priv->xdisplay = GDK_SCREEN_XDISPLAY (priv->gdk_screen);
805 priv->xscreen = gdk_x11_screen_get_xscreen (priv->gdk_screen);
806 return;
807 default:
808 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
809 return;
810 }
811}
812
813void
814cc_rr_screen_get_property (GObject *gobject, guint property_id, GValue *value, GParamSpec *property)
815{
816 CCRRScreen *self = CC_RR_SCREEN (gobject);
817 CCRRScreenPrivate *priv = self->priv;
818
819 switch (property_id)
820 {
821 case SCREEN_PROP_GDK_SCREEN:
822 g_value_set_object (value, priv->gdk_screen);
823 return;
824 default:
825 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
826 return;
827 }
828}
829
830void
831cc_rr_screen_class_init (CCRRScreenClass *klass)
832{
833 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
834 g_type_class_add_private (klass, sizeof (CCRRScreenPrivate));
835
836 bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
837 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
838
839 gobject_class->set_property = cc_rr_screen_set_property;
840 gobject_class->get_property = cc_rr_screen_get_property;
841 gobject_class->finalize = cc_rr_screen_finalize;
842
843 g_object_class_install_property(
844 gobject_class,
845 SCREEN_PROP_GDK_SCREEN,
846 g_param_spec_object (
847 "gdk-screen",
848 "GDK Screen",
849 "The GDK Screen represented by this CCRRScreen",
850 GDK_TYPE_SCREEN,
851 G_PARAM_READWRITE |
852 G_PARAM_CONSTRUCT_ONLY |
853 G_PARAM_STATIC_STRINGS)
854 );
855
856 screen_signals[SCREEN_CHANGED] = g_signal_new("changed",
857 G_TYPE_FROM_CLASS (gobject_class),
858 G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
859 G_STRUCT_OFFSET (CCRRScreenClass, changed),
860 NULL,
861 NULL,
862 g_cclosure_marshal_VOID__VOID,
863 G_TYPE_NONE,
864 0);
865
866 /**
867 * CCRRScreen::output-connected:
868 * @screen: the #CCRRScreen that emitted the signal
869 * @output: the #CCRROutput that was connected
870 *
871 * This signal is emitted when a display device is connected to a
872 * port, or a port is hotplugged with an active output. The latter
873 * can happen if a laptop is docked, and the dock provides a new
874 * active output.
875 *
876 * The @output value is not a #GObject. The returned @output value can
877 * only assume to be valid during the emission of the signal (i.e. within
878 * your signal handler only), as it may change later when the @screen
879 * is modified due to an event from the X server, or due to another
880 * place in the application modifying the @screen and the @output.
881 * Therefore, deal with changes to the @output right in your signal
882 * handler, instead of keeping the @output reference for an async or
883 * idle function.
884 **/
885 screen_signals[SCREEN_OUTPUT_CONNECTED] = g_signal_new("output-connected",
886 G_TYPE_FROM_CLASS (gobject_class),
887 G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
888 G_STRUCT_OFFSET (CCRRScreenClass, output_connected),
889 NULL,
890 NULL,
891 g_cclosure_marshal_VOID__POINTER,
892 G_TYPE_NONE,
893 1, G_TYPE_POINTER);
894
895 /**
896 * CCRRScreen::output-disconnected:
897 * @screen: the #CCRRScreen that emitted the signal
898 * @output: the #CCRROutput that was disconnected
899 *
900 * This signal is emitted when a display device is disconnected from
901 * a port, or a port output is hot-unplugged. The latter can happen
902 * if a laptop is undocked, and the dock provided the output.
903 *
904 * The @output value is not a #GObject. The returned @output value can
905 * only assume to be valid during the emission of the signal (i.e. within
906 * your signal handler only), as it may change later when the @screen
907 * is modified due to an event from the X server, or due to another
908 * place in the application modifying the @screen and the @output.
909 * Therefore, deal with changes to the @output right in your signal
910 * handler, instead of keeping the @output reference for an async or
911 * idle function.
912 **/
913 screen_signals[SCREEN_OUTPUT_DISCONNECTED] = g_signal_new("output-disconnected",
914 G_TYPE_FROM_CLASS (gobject_class),
915 G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
916 G_STRUCT_OFFSET (CCRRScreenClass, output_disconnected),
917 NULL,
918 NULL,
919 g_cclosure_marshal_VOID__POINTER,
920 G_TYPE_NONE,
921 1, G_TYPE_POINTER);
922}
923
924void
925cc_rr_screen_init (CCRRScreen *self)
926{
927 CCRRScreenPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CC_TYPE_RR_SCREEN, CCRRScreenPrivate);
928 self->priv = priv;
929
930 priv->gdk_screen = NULL;
931 priv->gdk_root = NULL;
932 priv->xdisplay = NULL;
933 priv->xroot = None;
934 priv->xscreen = NULL;
935 priv->info = NULL;
936 priv->rr_major_version = 0;
937 priv->rr_minor_version = 0;
938}
939
940/* Weak reference callback set in cc_rr_screen_new(); we remove the GObject data from the GdkScreen. */
941static void
942rr_screen_weak_notify_cb (gpointer data, GObject *where_the_object_was)
943{
944 GdkScreen *screen = GDK_SCREEN (data);
945
946 g_object_set_data (G_OBJECT (screen), "CCRRScreen", NULL);
947}
948
949/**
950 * cc_rr_screen_new:
951 * @screen: the #GdkScreen on which to operate
952 * @error: will be set if XRandR is not supported
953 *
954 * Creates a unique #CCRRScreen instance for the specified @screen.
955 *
956 * Returns: a unique #CCRRScreen instance, specific to the @screen, or NULL
957 * if this could not be created, for instance if the driver does not support
958 * Xrandr 1.2. Each #GdkScreen thus has a single instance of #CCRRScreen.
959 */
960CCRRScreen *
961cc_rr_screen_new (GdkScreen *screen,
962 GError **error)
963{
964 CCRRScreen *rr_screen;
965
966 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
967 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
968
969 rr_screen = g_object_get_data (G_OBJECT (screen), "CCRRScreen");
970 if (rr_screen)
971 g_object_ref (rr_screen);
972 else {
973 rr_screen = g_initable_new (CC_TYPE_RR_SCREEN, NULL, error, "gdk-screen", screen, NULL);
974 if (rr_screen) {
975 g_object_set_data (G_OBJECT (screen), "CCRRScreen", rr_screen);
976 g_object_weak_ref (G_OBJECT (rr_screen), rr_screen_weak_notify_cb, screen);
977 }
978 }
979
980 return rr_screen;
981}
982
983void
984cc_rr_screen_set_size (CCRRScreen *screen,
985 int width,
986 int height,
987 int mm_width,
988 int mm_height)
989{
990 g_return_if_fail (CC_IS_RR_SCREEN (screen));
991
992 gdk_error_trap_push ();
993 XRRSetScreenSize (screen->priv->xdisplay, screen->priv->xroot,
994 width, height, mm_width, mm_height);
995 gdk_error_trap_pop_ignored ();
996}
997
998/**
999 * cc_rr_screen_get_ranges:
1000 * @screen: a #CCRRScreen
1001 * @min_width: (out): the minimum width
1002 * @max_width: (out): the maximum width
1003 * @min_height: (out): the minimum height
1004 * @max_height: (out): the maximum height
1005 *
1006 * Get the ranges of the screen
1007 */
1008void
1009cc_rr_screen_get_ranges (CCRRScreen *screen,
1010 int *min_width,
1011 int *max_width,
1012 int *min_height,
1013 int *max_height)
1014{
1015 CCRRScreenPrivate *priv;
1016
1017 g_return_if_fail (CC_IS_RR_SCREEN (screen));
1018
1019 priv = screen->priv;
1020
1021 if (min_width)
1022 *min_width = priv->info->min_width;
1023
1024 if (max_width)
1025 *max_width = priv->info->max_width;
1026
1027 if (min_height)
1028 *min_height = priv->info->min_height;
1029
1030 if (max_height)
1031 *max_height = priv->info->max_height;
1032}
1033
1034/**
1035 * cc_rr_screen_get_timestamps:
1036 * @screen: a #CCRRScreen
1037 * @change_timestamp_ret: (out): Location in which to store the timestamp at which the RANDR configuration was last changed
1038 * @config_timestamp_ret: (out): Location in which to store the timestamp at which the RANDR configuration was last obtained
1039 *
1040 * Queries the two timestamps that the X RANDR extension maintains. The X
1041 * server will prevent change requests for stale configurations, those whose
1042 * timestamp is not equal to that of the latest request for configuration. The
1043 * X server will also prevent change requests that have an older timestamp to
1044 * the latest change request.
1045 */
1046void
1047cc_rr_screen_get_timestamps (CCRRScreen *screen,
1048 guint32 *change_timestamp_ret,
1049 guint32 *config_timestamp_ret)
1050{
1051 CCRRScreenPrivate *priv;
1052
1053 g_return_if_fail (CC_IS_RR_SCREEN (screen));
1054
1055 priv = screen->priv;
1056
1057 if (change_timestamp_ret)
1058 *change_timestamp_ret = priv->info->resources->timestamp;
1059
1060 if (config_timestamp_ret)
1061 *config_timestamp_ret = priv->info->resources->configTimestamp;
1062}
1063
1064static gboolean
1065force_timestamp_update (CCRRScreen *screen)
1066{
1067 CCRRScreenPrivate *priv = screen->priv;
1068 CCRRCrtc *crtc;
1069 XRRCrtcInfo *current_info;
1070 Status status;
1071 gboolean timestamp_updated;
1072
1073 timestamp_updated = FALSE;
1074
1075 crtc = priv->info->crtcs[0];
1076
1077 if (crtc == NULL)
1078 goto out;
1079
1080 current_info = XRRGetCrtcInfo (priv->xdisplay,
1081 priv->info->resources,
1082 crtc->id);
1083
1084 if (current_info == NULL)
1085 goto out;
1086
1087 gdk_error_trap_push ();
1088 status = XRRSetCrtcConfig (priv->xdisplay,
1089 priv->info->resources,
1090 crtc->id,
1091 current_info->timestamp,
1092 current_info->x,
1093 current_info->y,
1094 current_info->mode,
1095 current_info->rotation,
1096 current_info->outputs,
1097 current_info->noutput);
1098
1099 XRRFreeCrtcInfo (current_info);
1100
1101 gdk_flush ();
1102 if (gdk_error_trap_pop ())
1103 goto out;
1104
1105 if (status == RRSetConfigSuccess)
1106 timestamp_updated = TRUE;
1107out:
1108 return timestamp_updated;
1109}
1110
1111/**
1112 * cc_rr_screen_refresh:
1113 * @screen: a #CCRRScreen
1114 * @error: location to store error, or %NULL
1115 *
1116 * Refreshes the screen configuration, and calls the screen's callback if it
1117 * exists and if the screen's configuration changed.
1118 *
1119 * Return value: TRUE if the screen's configuration changed; otherwise, the
1120 * function returns FALSE and a NULL error if the configuration didn't change,
1121 * or FALSE and a non-NULL error if there was an error while refreshing the
1122 * configuration.
1123 */
1124gboolean
1125cc_rr_screen_refresh (CCRRScreen *screen,
1126 GError **error)
1127{
1128 gboolean refreshed;
1129
1130 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1131
1132 gdk_x11_display_grab (gdk_screen_get_display (screen->priv->gdk_screen));
1133
1134 refreshed = screen_update (screen, FALSE, TRUE, error);
1135 force_timestamp_update (screen); /* this is to keep other clients from thinking that the X server re-detected things by itself - bgo#621046 */
1136
1137 gdk_x11_display_ungrab (gdk_screen_get_display (screen->priv->gdk_screen));
1138
1139 return refreshed;
1140}
1141
1142/**
1143 * cc_rr_screen_get_dpms_mode:
1144 * @mode: (out): The current #CCRRDpmsMode of this screen
1145 **/
1146gboolean
1147cc_rr_screen_get_dpms_mode (CCRRScreen *screen,
1148 CCRRDpmsMode *mode,
1149 GError **error)
1150{
1151 BOOL enabled = FALSE;
1152 CARD16 state;
1153 gboolean ret = FALSE;
1154
1155 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1156 g_return_val_if_fail (mode != NULL, FALSE);
1157
1158 if (!screen->priv->dpms_capable) {
1159 g_set_error_literal (error,
1160 CC_RR_ERROR,
1161 CC_RR_ERROR_NO_DPMS_EXTENSION,
1162 "Display is not DPMS capable");
1163 goto out;
1164 }
1165
1166 if (!DPMSInfo (screen->priv->xdisplay,
1167 &state,
1168 &enabled)) {
1169 g_set_error_literal (error,
1170 CC_RR_ERROR,
1171 CC_RR_ERROR_UNKNOWN,
1172 "Unable to get DPMS state");
1173 goto out;
1174 }
1175
1176 /* DPMS not enabled is a valid mode */
1177 if (!enabled) {
1178 *mode = CC_RR_DPMS_DISABLED;
1179 ret = TRUE;
1180 goto out;
1181 }
1182
1183 switch (state) {
1184 case DPMSModeOn:
1185 *mode = CC_RR_DPMS_ON;
1186 break;
1187 case DPMSModeStandby:
1188 *mode = CC_RR_DPMS_STANDBY;
1189 break;
1190 case DPMSModeSuspend:
1191 *mode = CC_RR_DPMS_SUSPEND;
1192 break;
1193 case DPMSModeOff:
1194 *mode = CC_RR_DPMS_OFF;
1195 break;
1196 default:
1197 g_assert_not_reached ();
1198 break;
1199 }
1200 ret = TRUE;
1201out:
1202 return ret;
1203}
1204
1205/**
1206 * cc_rr_screen_clear_dpms_timeouts:
1207 **/
1208static gboolean
1209cc_rr_screen_clear_dpms_timeouts (CCRRScreen *screen,
1210 GError **error)
1211{
1212 gdk_error_trap_push ();
1213 /* DPMSSetTimeouts() return value is often a lie, so ignore it */
1214 DPMSSetTimeouts (screen->priv->xdisplay, 0, 0, 0);
1215 if (gdk_error_trap_pop ()) {
1216 g_set_error_literal (error,
1217 CC_RR_ERROR,
1218 CC_RR_ERROR_UNKNOWN,
1219 "Could not set DPMS timeouts");
1220 return FALSE;
1221 }
1222 return TRUE;
1223}
1224
1225/**
1226 * cc_rr_screen_set_dpms_mode:
1227 *
1228 * This method also disables the DPMS timeouts.
1229 **/
1230gboolean
1231cc_rr_screen_set_dpms_mode (CCRRScreen *screen,
1232 CCRRDpmsMode mode,
1233 GError **error)
1234{
1235 CARD16 state = 0;
1236 gboolean ret;
1237 CCRRDpmsMode current_mode;
1238
1239 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1240
1241 /* set, if the new mode is different */
1242 ret = cc_rr_screen_get_dpms_mode (screen, &current_mode, error);
1243 if (!ret)
1244 goto out;
1245 if (current_mode == mode) {
1246 ret = cc_rr_screen_clear_dpms_timeouts (screen, error);
1247 goto out;
1248 }
1249
1250 switch (mode) {
1251 case CC_RR_DPMS_ON:
1252 state = DPMSModeOn;
1253 break;
1254 case CC_RR_DPMS_STANDBY:
1255 state = DPMSModeStandby;
1256 break;
1257 case CC_RR_DPMS_SUSPEND:
1258 state = DPMSModeSuspend;
1259 break;
1260 case CC_RR_DPMS_OFF:
1261 state = DPMSModeOff;
1262 break;
1263 default:
1264 g_assert_not_reached ();
1265 break;
1266 }
1267
1268 gdk_error_trap_push ();
1269 /* DPMSForceLevel() return value is often a lie, so ignore it */
1270 DPMSForceLevel (screen->priv->xdisplay, state);
1271 XSync (screen->priv->xdisplay, False);
1272 if (gdk_error_trap_pop ()) {
1273 ret = FALSE;
1274 g_set_error_literal (error,
1275 CC_RR_ERROR,
1276 CC_RR_ERROR_UNKNOWN,
1277 "Could not change DPMS mode");
1278 goto out;
1279 }
1280
1281 ret = cc_rr_screen_clear_dpms_timeouts (screen, error);
1282 if (!ret)
1283 goto out;
1284out:
1285 return ret;
1286}
1287
1288/**
1289 * cc_rr_screen_list_modes:
1290 *
1291 * List available XRandR modes
1292 *
1293 * Returns: (array zero-terminated=1) (transfer none):
1294 */
1295CCRRMode **
1296cc_rr_screen_list_modes (CCRRScreen *screen)
1297{
1298 g_return_val_if_fail (CC_IS_RR_SCREEN (screen), NULL);
1299 g_return_val_if_fail (screen->priv->info != NULL, NULL);
1300
1301 return screen->priv->info->modes;
1302}
1303
1304/**
1305 * cc_rr_screen_list_clone_modes:
1306 *
1307 * List available XRandR clone modes
1308 *
1309 * Returns: (array zero-terminated=1) (transfer none):
1310 */
1311CCRRMode **
1312cc_rr_screen_list_clone_modes (CCRRScreen *screen)
1313{
1314 g_return_val_if_fail (CC_IS_RR_SCREEN (screen), NULL);
1315 g_return_val_if_fail (screen->priv->info != NULL, NULL);
1316
1317 return screen->priv->info->clone_modes;
1318}
1319
1320/**
1321 * cc_rr_screen_list_crtcs:
1322 *
1323 * List all CRTCs
1324 *
1325 * Returns: (array zero-terminated=1) (transfer none):
1326 */
1327CCRRCrtc **
1328cc_rr_screen_list_crtcs (CCRRScreen *screen)
1329{
1330 g_return_val_if_fail (CC_IS_RR_SCREEN (screen), NULL);
1331 g_return_val_if_fail (screen->priv->info != NULL, NULL);
1332
1333 return screen->priv->info->crtcs;
1334}
1335
1336/**
1337 * cc_rr_screen_list_outputs:
1338 *
1339 * List all outputs
1340 *
1341 * Returns: (array zero-terminated=1) (transfer none):
1342 */
1343CCRROutput **
1344cc_rr_screen_list_outputs (CCRRScreen *screen)
1345{
1346 g_return_val_if_fail (CC_IS_RR_SCREEN (screen), NULL);
1347 g_return_val_if_fail (screen->priv->info != NULL, NULL);
1348
1349 return screen->priv->info->outputs;
1350}
1351
1352/**
1353 * cc_rr_screen_get_crtc_by_id:
1354 *
1355 * Returns: (transfer none): the CRTC identified by @id
1356 */
1357CCRRCrtc *
1358cc_rr_screen_get_crtc_by_id (CCRRScreen *screen,
1359 guint32 id)
1360{
1361 CCRRCrtc **crtcs;
1362 int i;
1363
1364 g_return_val_if_fail (CC_IS_RR_SCREEN (screen), NULL);
1365 g_return_val_if_fail (screen->priv->info != NULL, NULL);
1366
1367 crtcs = screen->priv->info->crtcs;
1368
1369 for (i = 0; crtcs[i] != NULL; ++i)
1370 {
1371 if (crtcs[i]->id == id)
1372 return crtcs[i];
1373 }
1374
1375 return NULL;
1376}
1377
1378/**
1379 * cc_rr_screen_get_output_by_id:
1380 *
1381 * Returns: (transfer none): the output identified by @id
1382 */
1383CCRROutput *
1384cc_rr_screen_get_output_by_id (CCRRScreen *screen,
1385 guint32 id)
1386{
1387 CCRROutput **outputs;
1388 int i;
1389
1390 g_return_val_if_fail (CC_IS_RR_SCREEN (screen), NULL);
1391 g_return_val_if_fail (screen->priv->info != NULL, NULL);
1392
1393 outputs = screen->priv->info->outputs;
1394
1395 for (i = 0; outputs[i] != NULL; ++i)
1396 {
1397 if (outputs[i]->id == id)
1398 return outputs[i];
1399 }
1400
1401 return NULL;
1402}
1403
1404/* CCRROutput */
1405static CCRROutput *
1406output_new (ScreenInfo *info, RROutput id)
1407{
1408 CCRROutput *output = g_slice_new0 (CCRROutput);
1409
1410 output->id = id;
1411 output->info = info;
1412
1413 return output;
1414}
1415
1416static guint8 *
1417get_property (Display *dpy,
1418 RROutput output,
1419 Atom atom,
1420 gsize *len)
1421{
1422 unsigned char *prop;
1423 int actual_format;
1424 unsigned long nitems, bytes_after;
1425 Atom actual_type;
1426 guint8 *result;
1427
1428 XRRGetOutputProperty (dpy, output, atom,
1429 0, 100, False, False,
1430 AnyPropertyType,
1431 &actual_type, &actual_format,
1432 &nitems, &bytes_after, &prop);
1433
1434 if (actual_type == XA_INTEGER && actual_format == 8)
1435 {
1436 result = g_memdup (prop, nitems);
1437 if (len)
1438 *len = nitems;
1439 }
1440 else
1441 {
1442 result = NULL;
1443 }
1444
1445 XFree (prop);
1446
1447 return result;
1448}
1449
1450static guint8 *
1451read_edid_data (CCRROutput *output, gsize *len)
1452{
1453 Atom edid_atom;
1454 guint8 *result;
1455
1456 edid_atom = XInternAtom (DISPLAY (output), "EDID", FALSE);
1457 result = get_property (DISPLAY (output),
1458 output->id, edid_atom, len);
1459
1460 if (!result)
1461 {
1462 edid_atom = XInternAtom (DISPLAY (output), "EDID_DATA", FALSE);
1463 result = get_property (DISPLAY (output),
1464 output->id, edid_atom, len);
1465 }
1466
1467 if (!result)
1468 {
1469 edid_atom = XInternAtom (DISPLAY (output), "XFree86_DDC_EDID1_RAWDATA", FALSE);
1470 result = get_property (DISPLAY (output),
1471 output->id, edid_atom, len);
1472 }
1473
1474 if (result)
1475 {
1476 if (*len % 128 == 0)
1477 return result;
1478 else
1479 g_free (result);
1480 }
1481
1482 return NULL;
1483}
1484
1485static char *
1486get_connector_type_string (CCRROutput *output)
1487{
1488 char *result;
1489 unsigned char *prop;
1490 int actual_format;
1491 unsigned long nitems, bytes_after;
1492 Atom actual_type;
1493 Atom connector_type;
1494 char *connector_type_str;
1495
1496 result = NULL;
1497
1498 if (XRRGetOutputProperty (DISPLAY (output), output->id, output->info->screen->priv->connector_type_atom,
1499 0, 100, False, False,
1500 AnyPropertyType,
1501 &actual_type, &actual_format,
1502 &nitems, &bytes_after, &prop) != Success)
1503 return NULL;
1504
1505 if (!(actual_type == XA_ATOM && actual_format == 32 && nitems == 1))
1506 goto out;
1507
1508 connector_type = *((Atom *) prop);
1509
1510 connector_type_str = XGetAtomName (DISPLAY (output), connector_type);
1511 if (connector_type_str) {
1512 result = g_strdup (connector_type_str); /* so the caller can g_free() it */
1513 XFree (connector_type_str);
1514 }
1515
1516out:
1517
1518 XFree (prop);
1519
1520 return result;
1521}
1522
1523static void
1524update_brightness_limits (CCRROutput *output)
1525{
1526 gint rc;
1527 Atom atom;
1528 XRRPropertyInfo *info;
1529
1530 gdk_error_trap_push ();
1531 atom = XInternAtom (DISPLAY (output), "Backlight", FALSE);
1532 info = XRRQueryOutputProperty (DISPLAY (output), output->id, atom);
1533 rc = gdk_error_trap_pop ();
1534 if (rc != Success)
1535 {
1536 if (rc != BadName)
1537 g_warning ("could not get output property for %s, rc: %i",
1538 output->name, rc);
1539 goto out;
1540 }
1541 if (info == NULL)
1542 {
1543 g_warning ("could not get output property for %s",
1544 output->name);
1545 goto out;
1546 }
1547 if (!info->range || info->num_values != 2)
1548 {
1549 g_debug ("backlight %s was not range", output->name);
1550 goto out;
1551 }
1552 output->backlight_min = info->values[0];
1553 output->backlight_max = info->values[1];
1554out:
1555 if (info != NULL)
1556 {
1557 XFree (info);
1558 }
1559}
1560
1561static gboolean
1562output_initialize (CCRROutput *output, XRRScreenResources *res, GError **error)
1563{
1564 XRROutputInfo *info = XRRGetOutputInfo (
1565 DISPLAY (output), res, output->id);
1566 GPtrArray *a;
1567 int i;
1568
1569#if 0
1570 g_print ("Output %lx Timestamp: %u\n", output->id, (guint32)info->timestamp);
1571#endif
1572
1573 if (!info || !output->info)
1574 {
1575 /* FIXME: see the comment in crtc_initialize() */
1576 /* Translators: here, an "output" is a video output */
1577 g_set_error (error, CC_RR_ERROR, CC_RR_ERROR_RANDR_ERROR,
1578 _("could not get information about output %d"),
1579 (int) output->id);
1580 return FALSE;
1581 }
1582
1583 output->name = g_strdup (info->name); /* FIXME: what is nameLen used for? */
1584 output->display_name = NULL; /* set first time the getter is used */
1585 output->current_crtc = crtc_by_id (output->info, info->crtc);
1586 output->width_mm = info->mm_width;
1587 output->height_mm = info->mm_height;
1588 output->connected = (info->connection == RR_Connected);
1589 output->connector_type = get_connector_type_string (output);
1590
1591 /* Possible crtcs */
1592 a = g_ptr_array_new ();
1593
1594 for (i = 0; i < info->ncrtc; ++i)
1595 {
1596 CCRRCrtc *crtc = crtc_by_id (output->info, info->crtcs[i]);
1597
1598 if (crtc)
1599 g_ptr_array_add (a, crtc);
1600 }
1601 g_ptr_array_add (a, NULL);
1602 output->possible_crtcs = (CCRRCrtc **)g_ptr_array_free (a, FALSE);
1603
1604 /* Clones */
1605 a = g_ptr_array_new ();
1606 for (i = 0; i < info->nclone; ++i)
1607 {
1608 CCRROutput *cc_rr_output = cc_rr_output_by_id (output->info, info->clones[i]);
1609
1610 if (cc_rr_output)
1611 g_ptr_array_add (a, cc_rr_output);
1612 }
1613 g_ptr_array_add (a, NULL);
1614 output->clones = (CCRROutput **)g_ptr_array_free (a, FALSE);
1615
1616 /* Modes */
1617 a = g_ptr_array_new ();
1618 for (i = 0; i < info->nmode; ++i)
1619 {
1620 CCRRMode *mode = mode_by_id (output->info, info->modes[i]);
1621
1622 if (mode)
1623 g_ptr_array_add (a, mode);
1624 }
1625 g_ptr_array_add (a, NULL);
1626 output->modes = (CCRRMode **)g_ptr_array_free (a, FALSE);
1627
1628 output->n_preferred = info->npreferred;
1629
1630 /* Edid data */
1631 output->edid_data = read_edid_data (output, &output->edid_size);
1632
1633 /* brightness data */
1634 if (output->connected)
1635 update_brightness_limits (output);
1636
1637 XRRFreeOutputInfo (info);
1638
1639 return TRUE;
1640}
1641
1642static CCRROutput*
1643output_copy (const CCRROutput *from)
1644{
1645 GPtrArray *array;
1646 CCRRCrtc **p_crtc;
1647 CCRROutput **p_output;
1648 CCRRMode **p_mode;
1649 CCRROutput *output = g_slice_new0 (CCRROutput);
1650
1651 output->id = from->id;
1652 output->info = from->info;
1653 output->name = g_strdup (from->name);
1654 output->current_crtc = from->current_crtc;
1655 output->width_mm = from->width_mm;
1656 output->height_mm = from->height_mm;
1657 output->connected = from->connected;
1658 output->n_preferred = from->n_preferred;
1659 output->connector_type = g_strdup (from->connector_type);
1660 output->backlight_min = -1;
1661 output->backlight_max = -1;
1662
1663 array = g_ptr_array_new ();
1664 for (p_crtc = from->possible_crtcs; *p_crtc != NULL; p_crtc++)
1665 {
1666 g_ptr_array_add (array, *p_crtc);
1667 }
1668 output->possible_crtcs = (CCRRCrtc**) g_ptr_array_free (array, FALSE);
1669
1670 array = g_ptr_array_new ();
1671 for (p_output = from->clones; *p_output != NULL; p_output++)
1672 {
1673 g_ptr_array_add (array, *p_output);
1674 }
1675 output->clones = (CCRROutput**) g_ptr_array_free (array, FALSE);
1676
1677 array = g_ptr_array_new ();
1678 for (p_mode = from->modes; *p_mode != NULL; p_mode++)
1679 {
1680 g_ptr_array_add (array, *p_mode);
1681 }
1682 output->modes = (CCRRMode**) g_ptr_array_free (array, FALSE);
1683
1684 output->edid_size = from->edid_size;
1685 output->edid_data = g_memdup (from->edid_data, from->edid_size);
1686
1687 return output;
1688}
1689
1690static void
1691output_free (CCRROutput *output)
1692{
1693 g_free (output->clones);
1694 g_free (output->modes);
1695 g_free (output->possible_crtcs);
1696 g_free (output->edid_data);
1697 g_free (output->name);
1698 g_free (output->display_name);
1699 g_free (output->connector_type);
1700 g_slice_free (CCRROutput, output);
1701}
1702
1703guint32
1704cc_rr_output_get_id (CCRROutput *output)
1705{
1706 g_assert(output != NULL);
1707
1708 return output->id;
1709}
1710
1711const guint8 *
1712cc_rr_output_get_edid_data (CCRROutput *output, gsize *size)
1713{
1714 g_return_val_if_fail (output != NULL, NULL);
1715 if (size)
1716 *size = output->edid_size;
1717 return output->edid_data;
1718}
1719
1720/**
1721 * cc_rr_output_get_ids_from_edid:
1722 * @output: a #CCRROutput
1723 * @vendor: (out) (allow-none):
1724 * @product: (out) (allow-none):
1725 * @serial: (out) (allow-none):
1726 */
1727gboolean
1728cc_rr_output_get_ids_from_edid (CCRROutput *output,
1729 char **vendor,
1730 int *product,
1731 int *serial)
1732{
1733 MonitorInfo *info;
1734
1735 g_return_val_if_fail (output != NULL, FALSE);
1736
1737 if (!output->edid_data)
1738 return FALSE;
1739 info = decode_edid (output->edid_data);
1740 if (!info)
1741 return FALSE;
1742 if (vendor)
1743 *vendor = g_memdup (info->manufacturer_code, 4);
1744 if (product)
1745 *product = info->product_code;
1746 if (serial)
1747 *serial = info->serial_number;
1748
1749 g_free (info);
1750
1751 return TRUE;
1752
1753}
1754
1755static void
1756ensure_display_name (CCRROutput *output)
1757{
1758 if (output->display_name != NULL)
1759 return;
1760
1761 if (cc_rr_output_is_laptop (output))
1762 output->display_name = g_strdup (_("Built-in Display"));
1763
1764 if (output->display_name == NULL
1765 && output->edid_data != NULL) {
1766 MonitorInfo *info;
1767
1768 info = decode_edid (output->edid_data);
1769 if (info != NULL)
1770 output->display_name = make_display_name (info);
1771
1772 g_free (info);
1773 }
1774
1775 if (output->display_name == NULL) {
1776 char *inches;
1777 inches = make_display_size_string (output->width_mm, output->height_mm);
1778 if (inches != NULL) {
1779 /* Translators: %s is the size of the monitor in inches */
1780 output->display_name = g_strdup_printf (_("%s Display"), inches);
1781 }
1782 g_free (inches);
1783 }
1784
1785 /* last chance on the stairway */
1786 if (output->display_name == NULL) {
1787 output->display_name = g_strdup (_("Unknown Display"));
1788 }
1789}
1790
1791const char *
1792cc_rr_output_get_display_name (CCRROutput *output)
1793{
1794 g_return_val_if_fail (output != NULL, NULL);
1795
1796 ensure_display_name (output);
1797
1798 return output->display_name;
1799}
1800
1801/**
1802 * cc_rr_output_get_backlight_min:
1803 *
1804 * Returns: The mimimum backlight value, or -1 if not supported
1805 */
1806gint
1807cc_rr_output_get_backlight_min (CCRROutput *output)
1808{
1809 g_return_val_if_fail (output != NULL, -1);
1810 return output->backlight_min;
1811}
1812
1813/**
1814 * cc_rr_output_get_backlight_max:
1815 *
1816 * Returns: The maximum backlight value, or -1 if not supported
1817 */
1818gint
1819cc_rr_output_get_backlight_max (CCRROutput *output)
1820{
1821 g_return_val_if_fail (output != NULL, -1);
1822 return output->backlight_max;
1823}
1824
1825/**
1826 * cc_rr_output_get_backlight:
1827 *
1828 * Returns: The currently set backlight brightness
1829 */
1830gint
1831cc_rr_output_get_backlight (CCRROutput *output, GError **error)
1832{
1833 guint now = -1;
1834 unsigned long nitems;
1835 unsigned long bytes_after;
1836 guint *prop;
1837 Atom atom;
1838 Atom actual_type;
1839 int actual_format;
1840 gint retval;
1841
1842 g_return_val_if_fail (output != NULL, -1);
1843
1844 gdk_error_trap_push ();
1845 atom = XInternAtom (DISPLAY (output), "Backlight", FALSE);
1846 retval = XRRGetOutputProperty (DISPLAY (output), output->id, atom,
1847 0, 4, False, False, None,
1848 &actual_type, &actual_format,
1849 &nitems, &bytes_after, ((unsigned char **)&prop));
1850 gdk_flush ();
1851 if (gdk_error_trap_pop ())
1852 {
1853 g_set_error_literal (error,
1854 CC_RR_ERROR,
1855 CC_RR_ERROR_UNKNOWN,
1856 "unhandled X error while getting the range of backlight values");
1857 goto out;
1858 }
1859
1860 if (retval != Success) {
1861 g_set_error_literal (error,
1862 CC_RR_ERROR,
1863 CC_RR_ERROR_RANDR_ERROR,
1864 "could not get the range of backlight values");
1865 goto out;
1866 }
1867 if (actual_type == XA_INTEGER &&
1868 nitems == 1 &&
1869 actual_format == 32)
1870 {
1871 memcpy (&now, prop, sizeof (guint));
1872 }
1873 else
1874 {
1875 g_set_error (error,
1876 CC_RR_ERROR,
1877 CC_RR_ERROR_RANDR_ERROR,
1878 "failed to get correct property type, got %lu,%i",
1879 nitems, actual_format);
1880 }
1881out:
1882 XFree (prop);
1883 return now;
1884}
1885
1886/**
1887 * cc_rr_output_set_backlight:
1888 * @value: the absolute value which is min >= this <= max
1889 *
1890 * Returns: %TRUE for success
1891 */
1892gboolean
1893cc_rr_output_set_backlight (CCRROutput *output, gint value, GError **error)
1894{
1895 gboolean ret = FALSE;
1896 Atom atom;
1897
1898 g_return_val_if_fail (output != NULL, FALSE);
1899
1900 /* check this is sane */
1901 if (value < output->backlight_min ||
1902 value > output->backlight_max)
1903 {
1904 g_set_error (error,
1905 CC_RR_ERROR,
1906 CC_RR_ERROR_BOUNDS_ERROR,
1907 "out of brightness range: %i, has to be %i -> %i",
1908 value,
1909 output->backlight_max, output->backlight_min);
1910 goto out;
1911 }
1912
1913 /* don't abort on error */
1914 gdk_error_trap_push ();
1915 atom = XInternAtom (DISPLAY (output), "Backlight", FALSE);
1916 XRRChangeOutputProperty (DISPLAY (output), output->id, atom,
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches