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