1 /* mate-rr-config.c
2 * -*- c-basic-offset: 4 -*-
3 *
4 * Copyright 2007, 2008, Red Hat, Inc.
5 * Copyright 2010 Giovanni Campagna
6 *
7 * This file is part of the Mate Library.
8 *
9 * The Mate Library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * The Mate Library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with the Mate Library; see the file COPYING.LIB. If not,
21 * write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 * Author: Soren Sandmann <sandmann@redhat.com>
25 */
26
27 #define MATE_DESKTOP_USE_UNSTABLE_API
28
29 #include <config.h>
30 #include <glib/gi18n-lib.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <glib.h>
34 #include <glib/gstdio.h>
35
36 #include <X11/Xlib.h>
37 #include <gdk/gdkx.h>
38
39 #include "mate-rr-config.h"
40
41 #include "edid.h"
42 #include "mate-rr-private.h"
43
44 #define CONFIG_INTENDED_BASENAME "monitors.xml"
45 #define CONFIG_BACKUP_BASENAME "monitors.xml.backup"
46
47 /* In version 0 of the config file format, we had several <configuration>
48 * toplevel elements and no explicit version number. So, the filed looked
49 * like
50 *
51 * <configuration>
52 * ...
53 * </configuration>
54 * <configuration>
55 * ...
56 * </configuration>
57 *
58 * Since version 1 of the config file, the file has a toplevel <monitors>
59 * element to group all the configurations. That element has a "version"
60 * attribute which is an integer. So, the file looks like this:
61 *
62 * <monitors version="1">
63 * <configuration>
64 * ...
65 * </configuration>
66 * <configuration>
67 * ...
68 * </configuration>
69 * </monitors>
70 */
71
72 /* A helper wrapper around the GMarkup parser stuff */
73 static gboolean parse_file_gmarkup (const gchar *file,
74 const GMarkupParser *parser,
75 gpointer data,
76 GError **err);
77
78 typedef struct CrtcAssignment CrtcAssignment;
79
80 static gboolean crtc_assignment_apply (CrtcAssignment *assign,
81 guint32 timestamp,
82 GError **error);
83 static CrtcAssignment *crtc_assignment_new (MateRRScreen *screen,
84 MateRROutputInfo **outputs,
85 GError **error);
86 static void crtc_assignment_free (CrtcAssignment *assign);
87
88 enum {
89 PROP_0,
90 PROP_SCREEN,
91 PROP_LAST
92 };
93
94 G_DEFINE_TYPE_WITH_PRIVATE (MateRRConfig, mate_rr_config, G_TYPE_OBJECT)
95
96 typedef struct Parser Parser;
97
98 /* Parser for monitor configurations */
99 struct Parser
100 {
101 int config_file_version;
102 MateRROutputInfo * output;
103 MateRRConfig * configuration;
104 GPtrArray * outputs;
105 GPtrArray * configurations;
106 GQueue * stack;
107 };
108
109 static int
parse_int(const char * text)110 parse_int (const char *text)
111 {
112 return strtol (text, NULL, 0);
113 }
114
115 static guint
parse_uint(const char * text)116 parse_uint (const char *text)
117 {
118 return strtoul (text, NULL, 0);
119 }
120
121 static gboolean
stack_is(Parser * parser,const char * s1,...)122 stack_is (Parser *parser,
123 const char *s1,
124 ...)
125 {
126 GList *stack = NULL;
127 const char *s;
128 GList *l1, *l2;
129 va_list args;
130
131 stack = g_list_prepend (stack, (gpointer)s1);
132
133 va_start (args, s1);
134
135 s = va_arg (args, const char *);
136 while (s)
137 {
138 stack = g_list_prepend (stack, (gpointer)s);
139 s = va_arg (args, const char *);
140 }
141
142 va_end (args);
143
144 l1 = stack;
145 l2 = parser->stack->head;
146
147 while (l1 && l2)
148 {
149 if (strcmp (l1->data, l2->data) != 0)
150 {
151 g_list_free (stack);
152 return FALSE;
153 }
154
155 l1 = l1->next;
156 l2 = l2->next;
157 }
158
159 g_list_free (stack);
160
161 return (!l1 && !l2);
162 }
163
164 static void
handle_start_element(GMarkupParseContext * context,const gchar * name,const gchar ** attr_names,const gchar ** attr_values,gpointer user_data,GError ** err)165 handle_start_element (GMarkupParseContext *context,
166 const gchar *name,
167 const gchar **attr_names,
168 const gchar **attr_values,
169 gpointer user_data,
170 GError **err)
171 {
172 Parser *parser = user_data;
173
174 if (strcmp (name, "output") == 0)
175 {
176 int i;
177 g_assert (parser->output == NULL);
178
179 parser->output = g_object_new (MATE_TYPE_RR_OUTPUT_INFO, NULL);
180 parser->output->priv->rotation = 0;
181
182 for (i = 0; attr_names[i] != NULL; ++i)
183 {
184 if (strcmp (attr_names[i], "name") == 0)
185 {
186 parser->output->priv->name = g_strdup (attr_values[i]);
187 break;
188 }
189 }
190
191 if (!parser->output->priv->name)
192 {
193 /* This really shouldn't happen, but it's better to make
194 * something up than to crash later.
195 */
196 g_warning ("Malformed monitor configuration file");
197
198 parser->output->priv->name = g_strdup ("default");
199 }
200 parser->output->priv->connected = FALSE;
201 parser->output->priv->on = FALSE;
202 parser->output->priv->primary = FALSE;
203 }
204 else if (strcmp (name, "configuration") == 0)
205 {
206 g_assert (parser->configuration == NULL);
207
208 parser->configuration = g_object_new (MATE_TYPE_RR_CONFIG, NULL);
209 parser->configuration->priv->clone = FALSE;
210 parser->configuration->priv->outputs = NULL;
211 }
212 else if (strcmp (name, "monitors") == 0)
213 {
214 int i;
215
216 for (i = 0; attr_names[i] != NULL; i++)
217 {
218 if (strcmp (attr_names[i], "version") == 0)
219 {
220 parser->config_file_version = parse_int (attr_values[i]);
221 break;
222 }
223 }
224 }
225
226 g_queue_push_tail (parser->stack, g_strdup (name));
227 }
228
229 static void
handle_end_element(GMarkupParseContext * context,const gchar * name,gpointer user_data,GError ** err)230 handle_end_element (GMarkupParseContext *context,
231 const gchar *name,
232 gpointer user_data,
233 GError **err)
234 {
235 Parser *parser = user_data;
236
237 if (strcmp (name, "output") == 0)
238 {
239 /* If no rotation properties were set, just use MATE_RR_ROTATION_0 */
240 if (parser->output->priv->rotation == 0)
241 parser->output->priv->rotation = MATE_RR_ROTATION_0;
242
243 g_ptr_array_add (parser->outputs, parser->output);
244
245 parser->output = NULL;
246 }
247 else if (strcmp (name, "configuration") == 0)
248 {
249 g_ptr_array_add (parser->outputs, NULL);
250 parser->configuration->priv->outputs =
251 (MateRROutputInfo **)g_ptr_array_free (parser->outputs, FALSE);
252 parser->outputs = g_ptr_array_new ();
253 g_ptr_array_add (parser->configurations, parser->configuration);
254 parser->configuration = NULL;
255 }
256
257 g_free (g_queue_pop_tail (parser->stack));
258 }
259
260 #define TOPLEVEL_ELEMENT (parser->config_file_version > 0 ? "monitors" : NULL)
261
262 static void
handle_text(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** err)263 handle_text (GMarkupParseContext *context,
264 const gchar *text,
265 gsize text_len,
266 gpointer user_data,
267 GError **err)
268 {
269 Parser *parser = user_data;
270
271 if (stack_is (parser, "vendor", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
272 {
273 parser->output->priv->connected = TRUE;
274
275 strncpy ((gchar*) parser->output->priv->vendor, text, 3);
276 parser->output->priv->vendor[3] = 0;
277 }
278 else if (stack_is (parser, "clone", "configuration", TOPLEVEL_ELEMENT, NULL))
279 {
280 if (strcmp (text, "yes") == 0)
281 parser->configuration->priv->clone = TRUE;
282 }
283 else if (stack_is (parser, "product", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
284 {
285 parser->output->priv->connected = TRUE;
286
287 parser->output->priv->product = parse_int (text);
288 }
289 else if (stack_is (parser, "serial", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
290 {
291 parser->output->priv->connected = TRUE;
292
293 parser->output->priv->serial = parse_uint (text);
294 }
295 else if (stack_is (parser, "width", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
296 {
297 parser->output->priv->on = TRUE;
298
299 parser->output->priv->width = parse_int (text);
300 }
301 else if (stack_is (parser, "x", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
302 {
303 parser->output->priv->on = TRUE;
304
305 parser->output->priv->x = parse_int (text);
306 }
307 else if (stack_is (parser, "y", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
308 {
309 parser->output->priv->on = TRUE;
310
311 parser->output->priv->y = parse_int (text);
312 }
313 else if (stack_is (parser, "height", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
314 {
315 parser->output->priv->on = TRUE;
316
317 parser->output->priv->height = parse_int (text);
318 }
319 else if (stack_is (parser, "rate", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
320 {
321 parser->output->priv->on = TRUE;
322
323 parser->output->priv->rate = parse_int (text);
324 }
325 else if (stack_is (parser, "rotation", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
326 {
327 if (strcmp (text, "normal") == 0)
328 {
329 parser->output->priv->rotation |= MATE_RR_ROTATION_0;
330 }
331 else if (strcmp (text, "left") == 0)
332 {
333 parser->output->priv->rotation |= MATE_RR_ROTATION_90;
334 }
335 else if (strcmp (text, "upside_down") == 0)
336 {
337 parser->output->priv->rotation |= MATE_RR_ROTATION_180;
338 }
339 else if (strcmp (text, "right") == 0)
340 {
341 parser->output->priv->rotation |= MATE_RR_ROTATION_270;
342 }
343 }
344 else if (stack_is (parser, "reflect_x", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
345 {
346 if (strcmp (text, "yes") == 0)
347 {
348 parser->output->priv->rotation |= MATE_RR_REFLECT_X;
349 }
350 }
351 else if (stack_is (parser, "reflect_y", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
352 {
353 if (strcmp (text, "yes") == 0)
354 {
355 parser->output->priv->rotation |= MATE_RR_REFLECT_Y;
356 }
357 }
358 else if (stack_is (parser, "primary", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
359 {
360 if (strcmp (text, "yes") == 0)
361 {
362 parser->output->priv->primary = TRUE;
363 }
364 }
365 else
366 {
367 /* Ignore other properties so we can expand the format in the future */
368 }
369 }
370
371 static void
parser_free(Parser * parser)372 parser_free (Parser *parser)
373 {
374 int i;
375 GList *list;
376
377 g_assert (parser != NULL);
378
379 if (parser->output)
380 g_object_unref (parser->output);
381
382 if (parser->configuration)
383 g_object_unref (parser->configuration);
384
385 for (i = 0; i < parser->outputs->len; ++i)
386 {
387 MateRROutputInfo *output = parser->outputs->pdata[i];
388
389 g_object_unref (output);
390 }
391
392 g_ptr_array_free (parser->outputs, TRUE);
393
394 for (i = 0; i < parser->configurations->len; ++i)
395 {
396 MateRRConfig *config = parser->configurations->pdata[i];
397
398 g_object_unref (config);
399 }
400
401 g_ptr_array_free (parser->configurations, TRUE);
402
403 for (list = parser->stack->head; list; list = list->next)
404 g_free (list->data);
405 g_queue_free (parser->stack);
406
407 g_free (parser);
408 }
409
410 static MateRRConfig **
configurations_read_from_file(const gchar * filename,GError ** error)411 configurations_read_from_file (const gchar *filename, GError **error)
412 {
413 Parser *parser = g_new0 (Parser, 1);
414 MateRRConfig **result;
415 GMarkupParser callbacks = {
416 handle_start_element,
417 handle_end_element,
418 handle_text,
419 NULL, /* passthrough */
420 NULL, /* error */
421 };
422
423 parser->config_file_version = 0;
424 parser->configurations = g_ptr_array_new ();
425 parser->outputs = g_ptr_array_new ();
426 parser->stack = g_queue_new ();
427
428 if (!parse_file_gmarkup (filename, &callbacks, parser, error))
429 {
430 result = NULL;
431
432 g_assert (parser->outputs);
433 goto out;
434 }
435
436 g_assert (parser->outputs);
437
438 g_ptr_array_add (parser->configurations, NULL);
439 result = (MateRRConfig **)g_ptr_array_free (parser->configurations, FALSE);
440 parser->configurations = g_ptr_array_new ();
441
442 g_assert (parser->outputs);
443 out:
444 parser_free (parser);
445
446 return result;
447 }
448
449 static void
mate_rr_config_init(MateRRConfig * self)450 mate_rr_config_init (MateRRConfig *self)
451 {
452 self->priv = mate_rr_config_get_instance_private (self);
453
454 self->priv->clone = FALSE;
455 self->priv->screen = NULL;
456 self->priv->outputs = NULL;
457 }
458
459 static void
mate_rr_config_set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * property)460 mate_rr_config_set_property (GObject *gobject, guint property_id, const GValue *value, GParamSpec *property)
461 {
462 MateRRConfig *self = MATE_RR_CONFIG (gobject);
463
464 switch (property_id) {
465 case PROP_SCREEN:
466 self->priv->screen = g_value_dup_object (value);
467 return;
468 default:
469 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
470 }
471 }
472
473 static void
mate_rr_config_finalize(GObject * gobject)474 mate_rr_config_finalize (GObject *gobject)
475 {
476 MateRRConfig *self = MATE_RR_CONFIG (gobject);
477
478 if (self->priv->screen)
479 g_object_unref (self->priv->screen);
480
481 if (self->priv->outputs) {
482 int i;
483
484 for (i = 0; self->priv->outputs[i] != NULL; i++) {
485 MateRROutputInfo *output = self->priv->outputs[i];
486 g_object_unref (output);
487 }
488 g_free (self->priv->outputs);
489 }
490
491 G_OBJECT_CLASS (mate_rr_config_parent_class)->finalize (gobject);
492 }
493
494 gboolean
mate_rr_config_load_current(MateRRConfig * config,GError ** error)495 mate_rr_config_load_current (MateRRConfig *config, GError **error)
496 {
497 GPtrArray *a;
498 MateRROutput **rr_outputs;
499 int i;
500 int clone_width = -1;
501 int clone_height = -1;
502 int last_x;
503
504 g_return_val_if_fail (MATE_IS_RR_CONFIG (config), FALSE);
505
506 a = g_ptr_array_new ();
507 rr_outputs = mate_rr_screen_list_outputs (config->priv->screen);
508
509 config->priv->clone = FALSE;
510
511 for (i = 0; rr_outputs[i] != NULL; ++i)
512 {
513 MateRROutput *rr_output = rr_outputs[i];
514 MateRROutputInfo *output = g_object_new (MATE_TYPE_RR_OUTPUT_INFO, NULL);
515 MateRRMode *mode = NULL;
516 const guint8 *edid_data = mate_rr_output_get_edid_data (rr_output);
517 MateRRCrtc *crtc;
518
519 output->priv->name = g_strdup (mate_rr_output_get_name (rr_output));
520 output->priv->connected = mate_rr_output_is_connected (rr_output);
521
522 if (!output->priv->connected)
523 {
524 output->priv->x = -1;
525 output->priv->y = -1;
526 output->priv->width = -1;
527 output->priv->height = -1;
528 output->priv->rate = -1;
529 output->priv->rotation = MATE_RR_ROTATION_0;
530 }
531 else
532 {
533 MonitorInfo *info = NULL;
534
535 if (edid_data)
536 info = decode_edid (edid_data);
537
538 if (info)
539 {
540 memcpy (output->priv->vendor, info->manufacturer_code,
541 sizeof (output->priv->vendor));
542
543 output->priv->product = info->product_code;
544 output->priv->serial = info->serial_number;
545 output->priv->aspect = info->aspect_ratio;
546 }
547 else
548 {
549 g_strlcpy (output->priv->vendor, "???", sizeof (output->priv->vendor));
550 output->priv->product = 0;
551 output->priv->serial = 0;
552 }
553
554 if (mate_rr_output_is_laptop (rr_output))
555 output->priv->display_name = g_strdup (_("Laptop"));
556 else
557 output->priv->display_name = make_display_name (info);
558
559 g_free (info);
560
561 crtc = mate_rr_output_get_crtc (rr_output);
562 mode = crtc? mate_rr_crtc_get_current_mode (crtc) : NULL;
563
564 if (crtc && mode)
565 {
566 output->priv->on = TRUE;
567
568 mate_rr_crtc_get_position (crtc, &output->priv->x, &output->priv->y);
569 output->priv->width = mate_rr_mode_get_width (mode);
570 output->priv->height = mate_rr_mode_get_height (mode);
571 output->priv->rate = mate_rr_mode_get_freq (mode);
572 output->priv->rotation = mate_rr_crtc_get_current_rotation (crtc);
573
574 if (output->priv->x == 0 && output->priv->y == 0) {
575 if (clone_width == -1) {
576 clone_width = output->priv->width;
577 clone_height = output->priv->height;
578 } else if (clone_width == output->priv->width &&
579 clone_height == output->priv->height) {
580 config->priv->clone = TRUE;
581 }
582 }
583 }
584 else
585 {
586 output->priv->on = FALSE;
587 config->priv->clone = FALSE;
588 }
589
590 /* Get preferred size for the monitor */
591 mode = mate_rr_output_get_preferred_mode (rr_output);
592
593 if (!mode)
594 {
595 MateRRMode **modes = mate_rr_output_list_modes (rr_output);
596
597 /* FIXME: we should pick the "best" mode here, where best is
598 * sorted wrt
599 *
600 * - closest aspect ratio
601 * - mode area
602 * - refresh rate
603 * - We may want to extend randrwrap so that get_preferred
604 * returns that - although that could also depend on
605 * the crtc.
606 */
607 if (modes[0])
608 mode = modes[0];
609 }
610
611 if (mode)
612 {
613 output->priv->pref_width = mate_rr_mode_get_width (mode);
614 output->priv->pref_height = mate_rr_mode_get_height (mode);
615 }
616 else
617 {
618 /* Pick some random numbers. This should basically never happen */
619 output->priv->pref_width = 1024;
620 output->priv->pref_height = 768;
621 }
622 }
623
624 output->priv->primary = mate_rr_output_get_is_primary (rr_output);
625
626 g_ptr_array_add (a, output);
627 }
628
629 g_ptr_array_add (a, NULL);
630
631 config->priv->outputs = (MateRROutputInfo **)g_ptr_array_free (a, FALSE);
632
633 /* Walk the outputs computing the right-most edge of all
634 * lit-up displays
635 */
636 last_x = 0;
637 for (i = 0; config->priv->outputs[i] != NULL; ++i)
638 {
639 MateRROutputInfo *output = config->priv->outputs[i];
640
641 if (output->priv->on)
642 {
643 last_x = MAX (last_x, output->priv->x + output->priv->width);
644 }
645 }
646
647 /* Now position all off displays to the right of the
648 * on displays
649 */
650 for (i = 0; config->priv->outputs[i] != NULL; ++i)
651 {
652 MateRROutputInfo *output = config->priv->outputs[i];
653
654 if (output->priv->connected && !output->priv->on)
655 {
656 output->priv->x = last_x;
657 last_x = output->priv->x + output->priv->width;
658 }
659 }
660
661 g_assert (mate_rr_config_match (config, config));
662
663 return TRUE;
664 }
665
666 gboolean
mate_rr_config_load_filename(MateRRConfig * result,const char * filename,GError ** error)667 mate_rr_config_load_filename (MateRRConfig *result, const char *filename, GError **error)
668 {
669 MateRRConfig *current;
670 MateRRConfig **configs;
671 gboolean found = FALSE;
672
673 g_return_val_if_fail (MATE_IS_RR_CONFIG (result), FALSE);
674 g_return_val_if_fail (filename != NULL, FALSE);
675 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
676
677 if (filename == NULL)
678 filename = mate_rr_config_get_intended_filename ();
679
680 current = mate_rr_config_new_current (result->priv->screen, error);
681
682 configs = configurations_read_from_file (filename, error);
683
684 if (configs)
685 {
686 int i;
687
688 for (i = 0; configs[i] != NULL; ++i)
689 {
690 if (mate_rr_config_match (configs[i], current))
691 {
692 int j;
693 GPtrArray *array;
694 result->priv->clone = configs[i]->priv->clone;
695
696 array = g_ptr_array_new ();
697 for (j = 0; configs[i]->priv->outputs[j] != NULL; j++) {
698 g_object_ref (configs[i]->priv->outputs[j]);
699 g_ptr_array_add (array, configs[i]->priv->outputs[j]);
700 }
701 g_ptr_array_add (array, NULL);
702 result->priv->outputs = (MateRROutputInfo **) g_ptr_array_free (array, FALSE);
703
704 found = TRUE;
705 break;
706 }
707 g_object_unref (configs[i]);
708 }
709 g_free (configs);
710
711 if (!found)
712 g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_NO_MATCHING_CONFIG,
713 _("none of the saved display configurations matched the active configuration"));
714 }
715
716 g_object_unref (current);
717 return found;
718 }
719
720 static void
mate_rr_config_class_init(MateRRConfigClass * klass)721 mate_rr_config_class_init (MateRRConfigClass *klass)
722 {
723 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
724
725 gobject_class->set_property = mate_rr_config_set_property;
726 gobject_class->finalize = mate_rr_config_finalize;
727
728 g_object_class_install_property (gobject_class, PROP_SCREEN,
729 g_param_spec_object ("screen", "Screen", "The MateRRScreen this config applies to", MATE_TYPE_RR_SCREEN,
730 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
731 }
732
733 MateRRConfig *
mate_rr_config_new_current(MateRRScreen * screen,GError ** error)734 mate_rr_config_new_current (MateRRScreen *screen, GError **error)
735 {
736 MateRRConfig *self = g_object_new (MATE_TYPE_RR_CONFIG, "screen", screen, NULL);
737
738 if (mate_rr_config_load_current (self, error))
739 return self;
740 else
741 {
742 g_object_unref (self);
743 return NULL;
744 }
745 }
746
747 MateRRConfig *
mate_rr_config_new_stored(MateRRScreen * screen,GError ** error)748 mate_rr_config_new_stored (MateRRScreen *screen, GError **error)
749 {
750 MateRRConfig *self = g_object_new (MATE_TYPE_RR_CONFIG, "screen", screen, NULL);
751 char *filename;
752 gboolean success;
753
754 filename = mate_rr_config_get_intended_filename ();
755
756 success = mate_rr_config_load_filename (self, filename, error);
757
758 g_free (filename);
759
760 if (success)
761 return self;
762 else
763 {
764 g_object_unref (self);
765 return NULL;
766 }
767 }
768
769 static gboolean
parse_file_gmarkup(const gchar * filename,const GMarkupParser * parser,gpointer data,GError ** err)770 parse_file_gmarkup (const gchar *filename,
771 const GMarkupParser *parser,
772 gpointer data,
773 GError **err)
774 {
775 GMarkupParseContext *context = NULL;
776 gchar *contents = NULL;
777 gboolean result = TRUE;
778 gsize len;
779
780 if (!g_file_get_contents (filename, &contents, &len, err))
781 {
782 result = FALSE;
783 goto out;
784 }
785
786 context = g_markup_parse_context_new (parser, 0, data, NULL);
787
788 if (!g_markup_parse_context_parse (context, contents, len, err))
789 {
790 result = FALSE;
791 goto out;
792 }
793
794 if (!g_markup_parse_context_end_parse (context, err))
795 {
796 result = FALSE;
797 goto out;
798 }
799
800 out:
801 if (contents)
802 g_free (contents);
803
804 if (context)
805 g_markup_parse_context_free (context);
806
807 return result;
808 }
809
810 static gboolean
output_match(MateRROutputInfo * output1,MateRROutputInfo * output2)811 output_match (MateRROutputInfo *output1, MateRROutputInfo *output2)
812 {
813 g_assert (MATE_IS_RR_OUTPUT_INFO (output1));
814 g_assert (MATE_IS_RR_OUTPUT_INFO (output2));
815
816 if (strcmp (output1->priv->name, output2->priv->name) != 0)
817 return FALSE;
818
819 if (strcmp (output1->priv->vendor, output2->priv->vendor) != 0)
820 return FALSE;
821
822 if (output1->priv->product != output2->priv->product)
823 return FALSE;
824
825 if (output1->priv->serial != output2->priv->serial)
826 return FALSE;
827
828 if (output1->priv->connected != output2->priv->connected)
829 return FALSE;
830
831 return TRUE;
832 }
833
834 static gboolean
output_equal(MateRROutputInfo * output1,MateRROutputInfo * output2)835 output_equal (MateRROutputInfo *output1, MateRROutputInfo *output2)
836 {
837 g_assert (MATE_IS_RR_OUTPUT_INFO (output1));
838 g_assert (MATE_IS_RR_OUTPUT_INFO (output2));
839
840 if (!output_match (output1, output2))
841 return FALSE;
842
843 if (output1->priv->on != output2->priv->on)
844 return FALSE;
845
846 if (output1->priv->on)
847 {
848 if (output1->priv->width != output2->priv->width)
849 return FALSE;
850
851 if (output1->priv->height != output2->priv->height)
852 return FALSE;
853
854 if (output1->priv->rate != output2->priv->rate)
855 return FALSE;
856
857 if (output1->priv->x != output2->priv->x)
858 return FALSE;
859
860 if (output1->priv->y != output2->priv->y)
861 return FALSE;
862
863 if (output1->priv->rotation != output2->priv->rotation)
864 return FALSE;
865 }
866
867 return TRUE;
868 }
869
870 static MateRROutputInfo *
find_output(MateRRConfig * config,const char * name)871 find_output (MateRRConfig *config, const char *name)
872 {
873 int i;
874
875 for (i = 0; config->priv->outputs[i] != NULL; ++i)
876 {
877 MateRROutputInfo *output = config->priv->outputs[i];
878
879 if (strcmp (name, output->priv->name) == 0)
880 return output;
881 }
882
883 return NULL;
884 }
885
886 /* Match means "these configurations apply to the same hardware
887 * setups"
888 */
889 gboolean
mate_rr_config_match(MateRRConfig * c1,MateRRConfig * c2)890 mate_rr_config_match (MateRRConfig *c1, MateRRConfig *c2)
891 {
892 int i;
893 g_return_val_if_fail (MATE_IS_RR_CONFIG (c1), FALSE);
894 g_return_val_if_fail (MATE_IS_RR_CONFIG (c2), FALSE);
895
896 for (i = 0; c1->priv->outputs[i] != NULL; ++i)
897 {
898 MateRROutputInfo *output1 = c1->priv->outputs[i];
899 MateRROutputInfo *output2;
900
901 output2 = find_output (c2, output1->priv->name);
902 if (!output2 || !output_match (output1, output2))
903 return FALSE;
904 }
905
906 return TRUE;
907 }
908
909 /* Equal means "the configurations will result in the same
910 * modes being set on the outputs"
911 */
912 gboolean
mate_rr_config_equal(MateRRConfig * c1,MateRRConfig * c2)913 mate_rr_config_equal (MateRRConfig *c1,
914 MateRRConfig *c2)
915 {
916 int i;
917 g_return_val_if_fail (MATE_IS_RR_CONFIG (c1), FALSE);
918 g_return_val_if_fail (MATE_IS_RR_CONFIG (c2), FALSE);
919
920 for (i = 0; c1->priv->outputs[i] != NULL; ++i)
921 {
922 MateRROutputInfo *output1 = c1->priv->outputs[i];
923 MateRROutputInfo *output2;
924
925 output2 = find_output (c2, output1->priv->name);
926 if (!output2 || !output_equal (output1, output2))
927 return FALSE;
928 }
929
930 return TRUE;
931 }
932
933 static MateRROutputInfo **
make_outputs(MateRRConfig * config)934 make_outputs (MateRRConfig *config)
935 {
936 GPtrArray *outputs;
937 MateRROutputInfo *first_on;
938 int i;
939
940 outputs = g_ptr_array_new ();
941
942 first_on = NULL;
943
944 for (i = 0; config->priv->outputs[i] != NULL; ++i)
945 {
946 MateRROutputInfo *old = config->priv->outputs[i];
947 MateRROutputInfo *new = g_object_new (MATE_TYPE_RR_OUTPUT_INFO, NULL);
948 *(new->priv) = *(old->priv);
949 if (old->priv->name)
950 new->priv->name = g_strdup (old->priv->name);
951 if (old->priv->display_name)
952 new->priv->display_name = g_strdup (old->priv->display_name);
953
954 if (old->priv->on && !first_on)
955 first_on = old;
956
957 if (config->priv->clone && new->priv->on)
958 {
959 g_assert (first_on);
960
961 new->priv->width = first_on->priv->width;
962 new->priv->height = first_on->priv->height;
963 new->priv->rotation = first_on->priv->rotation;
964 new->priv->x = 0;
965 new->priv->y = 0;
966 }
967
968 g_ptr_array_add (outputs, new);
969 }
970
971 g_ptr_array_add (outputs, NULL);
972
973 return (MateRROutputInfo **)g_ptr_array_free (outputs, FALSE);
974 }
975
976 gboolean
mate_rr_config_applicable(MateRRConfig * configuration,MateRRScreen * screen,GError ** error)977 mate_rr_config_applicable (MateRRConfig *configuration,
978 MateRRScreen *screen,
979 GError **error)
980 {
981 MateRROutputInfo **outputs;
982 CrtcAssignment *assign;
983 gboolean result;
984 int i;
985
986 g_return_val_if_fail (MATE_IS_RR_CONFIG (configuration), FALSE);
987 g_return_val_if_fail (MATE_IS_RR_SCREEN (screen), FALSE);
988 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
989
990 outputs = make_outputs (configuration);
991 assign = crtc_assignment_new (screen, outputs, error);
992
993 if (assign)
994 {
995 result = TRUE;
996 crtc_assignment_free (assign);
997 }
998 else
999 {
1000 result = FALSE;
1001 }
1002
1003 for (i = 0; outputs[i] != NULL; i++) {
1004 g_object_unref (outputs[i]);
1005 }
1006
1007 return result;
1008 }
1009
1010 /* Database management */
1011
1012 static void
ensure_config_directory(void)1013 ensure_config_directory (void)
1014 {
1015 g_mkdir_with_parents (g_get_user_config_dir (), 0700);
1016 }
1017
1018 char *
mate_rr_config_get_backup_filename(void)1019 mate_rr_config_get_backup_filename (void)
1020 {
1021 ensure_config_directory ();
1022 return g_build_filename (g_get_user_config_dir (), CONFIG_BACKUP_BASENAME, NULL);
1023 }
1024
1025 char *
mate_rr_config_get_intended_filename(void)1026 mate_rr_config_get_intended_filename (void)
1027 {
1028 ensure_config_directory ();
1029 return g_build_filename (g_get_user_config_dir (), CONFIG_INTENDED_BASENAME, NULL);
1030 }
1031
1032 static const char *
get_rotation_name(MateRRRotation r)1033 get_rotation_name (MateRRRotation r)
1034 {
1035 if (r & MATE_RR_ROTATION_0)
1036 return "normal";
1037 if (r & MATE_RR_ROTATION_90)
1038 return "left";
1039 if (r & MATE_RR_ROTATION_180)
1040 return "upside_down";
1041 if (r & MATE_RR_ROTATION_270)
1042 return "right";
1043
1044 return "normal";
1045 }
1046
1047 static const char *
yes_no(int x)1048 yes_no (int x)
1049 {
1050 return x? "yes" : "no";
1051 }
1052
1053 static const char *
get_reflect_x(MateRRRotation r)1054 get_reflect_x (MateRRRotation r)
1055 {
1056 return yes_no (r & MATE_RR_REFLECT_X);
1057 }
1058
1059 static const char *
get_reflect_y(MateRRRotation r)1060 get_reflect_y (MateRRRotation r)
1061 {
1062 return yes_no (r & MATE_RR_REFLECT_Y);
1063 }
1064
1065 static void
emit_configuration(MateRRConfig * config,GString * string)1066 emit_configuration (MateRRConfig *config,
1067 GString *string)
1068 {
1069 int j;
1070
1071 g_string_append_printf (string, " <configuration>\n");
1072
1073 g_string_append_printf (string, " <clone>%s</clone>\n", yes_no (config->priv->clone));
1074
1075 for (j = 0; config->priv->outputs[j] != NULL; ++j)
1076 {
1077 MateRROutputInfo *output = config->priv->outputs[j];
1078
1079 g_string_append_printf (
1080 string, " <output name=\"%s\">\n", output->priv->name);
1081
1082 if (output->priv->connected && *output->priv->vendor != '\0')
1083 {
1084 g_string_append_printf (
1085 string, " <vendor>%s</vendor>\n", output->priv->vendor);
1086 g_string_append_printf (
1087 string, " <product>0x%04x</product>\n", output->priv->product);
1088 g_string_append_printf (
1089 string, " <serial>0x%08x</serial>\n", output->priv->serial);
1090 }
1091
1092 /* An unconnected output which is on does not make sense */
1093 if (output->priv->connected && output->priv->on)
1094 {
1095 g_string_append_printf (
1096 string, " <width>%d</width>\n", output->priv->width);
1097 g_string_append_printf (
1098 string, " <height>%d</height>\n", output->priv->height);
1099 g_string_append_printf (
1100 string, " <rate>%d</rate>\n", output->priv->rate);
1101 g_string_append_printf (
1102 string, " <x>%d</x>\n", output->priv->x);
1103 g_string_append_printf (
1104 string, " <y>%d</y>\n", output->priv->y);
1105 g_string_append_printf (
1106 string, " <rotation>%s</rotation>\n", get_rotation_name (output->priv->rotation));
1107 g_string_append_printf (
1108 string, " <reflect_x>%s</reflect_x>\n", get_reflect_x (output->priv->rotation));
1109 g_string_append_printf (
1110 string, " <reflect_y>%s</reflect_y>\n", get_reflect_y (output->priv->rotation));
1111 g_string_append_printf (
1112 string, " <primary>%s</primary>\n", yes_no (output->priv->primary));
1113 }
1114
1115 g_string_append_printf (string, " </output>\n");
1116 }
1117
1118 g_string_append_printf (string, " </configuration>\n");
1119 }
1120
1121 void
mate_rr_config_sanitize(MateRRConfig * config)1122 mate_rr_config_sanitize (MateRRConfig *config)
1123 {
1124 int i;
1125 int x_offset, y_offset;
1126 gboolean found;
1127
1128 /* Offset everything by the top/left-most coordinate to
1129 * make sure the configuration starts at (0, 0)
1130 */
1131 x_offset = y_offset = G_MAXINT;
1132 for (i = 0; config->priv->outputs[i]; ++i)
1133 {
1134 MateRROutputInfo *output = config->priv->outputs[i];
1135
1136 if (output->priv->on)
1137 {
1138 x_offset = MIN (x_offset, output->priv->x);
1139 y_offset = MIN (y_offset, output->priv->y);
1140 }
1141 }
1142
1143 for (i = 0; config->priv->outputs[i]; ++i)
1144 {
1145 MateRROutputInfo *output = config->priv->outputs[i];
1146
1147 if (output->priv->on)
1148 {
1149 output->priv->x -= x_offset;
1150 output->priv->y -= y_offset;
1151 }
1152 }
1153
1154 /* Only one primary, please */
1155 found = FALSE;
1156 for (i = 0; config->priv->outputs[i]; ++i)
1157 {
1158 if (config->priv->outputs[i]->priv->primary)
1159 {
1160 if (found)
1161 {
1162 config->priv->outputs[i]->priv->primary = FALSE;
1163 }
1164 else
1165 {
1166 found = TRUE;
1167 }
1168 }
1169 }
1170 }
1171
1172 gboolean
mate_rr_config_ensure_primary(MateRRConfig * configuration)1173 mate_rr_config_ensure_primary (MateRRConfig *configuration)
1174 {
1175 int i;
1176 MateRROutputInfo *laptop;
1177 MateRROutputInfo *top_left;
1178 gboolean found;
1179 MateRRConfigPrivate *priv;
1180
1181 g_return_val_if_fail (MATE_IS_RR_CONFIG (configuration), FALSE);
1182
1183 laptop = NULL;
1184 top_left = NULL;
1185 found = FALSE;
1186 priv = configuration->priv;
1187
1188 for (i = 0; priv->outputs[i] != NULL; ++i) {
1189 MateRROutputInfo *info = priv->outputs[i];
1190
1191 if (! info->priv->on) {
1192 info->priv->primary = FALSE;
1193 continue;
1194 }
1195
1196 /* ensure only one */
1197 if (info->priv->primary) {
1198 if (found) {
1199 info->priv->primary = FALSE;
1200 } else {
1201 found = TRUE;
1202 }
1203 }
1204
1205 if (top_left == NULL
1206 || (info->priv->x < top_left->priv->x
1207 && info->priv->y < top_left->priv->y)) {
1208 top_left = info;
1209 }
1210 if (laptop == NULL
1211 && _mate_rr_output_name_is_laptop (info->priv->name)) {
1212 laptop = info;
1213 }
1214 }
1215
1216 if (!found) {
1217 if (laptop != NULL) {
1218 laptop->priv->primary = TRUE;
1219 } else if (top_left != NULL) {
1220 /* Note: top_left can be NULL if all outputs are off */
1221 top_left->priv->primary = TRUE;
1222 }
1223 }
1224
1225 return !found;
1226 }
1227
1228 gboolean
mate_rr_config_save(MateRRConfig * configuration,GError ** error)1229 mate_rr_config_save (MateRRConfig *configuration, GError **error)
1230 {
1231 MateRRConfig **configurations;
1232 GString *output;
1233 int i;
1234 gchar *intended_filename;
1235 gchar *backup_filename;
1236 gboolean result;
1237
1238 g_return_val_if_fail (MATE_IS_RR_CONFIG (configuration), FALSE);
1239 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1240
1241 output = g_string_new ("");
1242
1243 backup_filename = mate_rr_config_get_backup_filename ();
1244 intended_filename = mate_rr_config_get_intended_filename ();
1245
1246 configurations = configurations_read_from_file (intended_filename, NULL); /* NULL-GError */
1247
1248 g_string_append_printf (output, "<monitors version=\"1\">\n");
1249
1250 if (configurations)
1251 {
1252 for (i = 0; configurations[i] != NULL; ++i)
1253 {
1254 if (!mate_rr_config_match (configurations[i], configuration))
1255 emit_configuration (configurations[i], output);
1256 g_object_unref (configurations[i]);
1257 }
1258
1259 g_free (configurations);
1260 }
1261
1262 emit_configuration (configuration, output);
1263
1264 g_string_append_printf (output, "</monitors>\n");
1265
1266 /* backup the file first */
1267 rename (intended_filename, backup_filename); /* no error checking because the intended file may not even exist */
1268
1269 result = g_file_set_contents (intended_filename, output->str, -1, error);
1270
1271 if (!result)
1272 rename (backup_filename, intended_filename); /* no error checking because the backup may not even exist */
1273
1274 g_free (backup_filename);
1275 g_free (intended_filename);
1276 g_string_free (output, TRUE);
1277
1278 return result;
1279 }
1280
1281 gboolean
mate_rr_config_apply_with_time(MateRRConfig * config,MateRRScreen * screen,guint32 timestamp,GError ** error)1282 mate_rr_config_apply_with_time (MateRRConfig *config,
1283 MateRRScreen *screen,
1284 guint32 timestamp,
1285 GError **error)
1286 {
1287 CrtcAssignment *assignment;
1288 MateRROutputInfo **outputs;
1289 gboolean result = FALSE;
1290 int i;
1291 GdkDisplay *display;
1292
1293 g_return_val_if_fail (MATE_IS_RR_CONFIG (config), FALSE);
1294 g_return_val_if_fail (MATE_IS_RR_SCREEN (screen), FALSE);
1295
1296 outputs = make_outputs (config);
1297
1298 assignment = crtc_assignment_new (screen, outputs, error);
1299
1300 for (i = 0; outputs[i] != NULL; i++)
1301 g_object_unref (outputs[i]);
1302 g_free (outputs);
1303
1304 if (assignment)
1305 {
1306 if (crtc_assignment_apply (assignment, timestamp, error))
1307 result = TRUE;
1308
1309 crtc_assignment_free (assignment);
1310
1311 display = gdk_display_get_default ();
1312 gdk_display_flush (display);
1313 }
1314
1315 return result;
1316 }
1317
1318 /* mate_rr_config_apply_from_filename_with_time:
1319 * @screen: A #MateRRScreen
1320 * @filename: Path of the file to look in for stored RANDR configurations.
1321 * @timestamp: X server timestamp from the event that causes the screen configuration to change (a user's button press, for example)
1322 * @error: Location to store error, or %NULL
1323 *
1324 * First, this function refreshes the @screen to match the current RANDR
1325 * configuration from the X server. Then, it tries to load the file in
1326 * @filename and looks for suitable matching RANDR configurations in the file;
1327 * if one is found, that configuration will be applied to the current set of
1328 * RANDR outputs.
1329 *
1330 * Typically, @filename is the result of mate_rr_config_get_intended_filename() or
1331 * mate_rr_config_get_backup_filename().
1332 *
1333 * Returns: TRUE if the RANDR configuration was loaded and applied from
1334 * $(XDG_CONFIG_HOME)/monitors.xml, or FALSE otherwise:
1335 *
1336 * If the current RANDR configuration could not be refreshed, the @error will
1337 * have a domain of #MATE_RR_ERROR and a corresponding error code.
1338 *
1339 * If the file in question is loaded successfully but the configuration cannot
1340 * be applied, the @error will have a domain of #MATE_RR_ERROR. Note that an
1341 * error code of #MATE_RR_ERROR_NO_MATCHING_CONFIG is not a real error; it
1342 * simply means that there were no stored configurations that match the current
1343 * set of RANDR outputs.
1344 *
1345 * If the file in question cannot be loaded, the @error will have a domain of
1346 * #G_FILE_ERROR. Note that an error code of G_FILE_ERROR_NOENT is not really
1347 * an error, either; it means that there was no stored configuration file and so
1348 * nothing is changed.
1349 */
1350 gboolean
mate_rr_config_apply_from_filename_with_time(MateRRScreen * screen,const char * filename,guint32 timestamp,GError ** error)1351 mate_rr_config_apply_from_filename_with_time (MateRRScreen *screen, const char *filename, guint32 timestamp, GError **error)
1352 {
1353 MateRRConfig *stored;
1354 GError *my_error;
1355
1356 g_return_val_if_fail (MATE_IS_RR_SCREEN (screen), FALSE);
1357 g_return_val_if_fail (filename != NULL, FALSE);
1358 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1359
1360 my_error = NULL;
1361 if (!mate_rr_screen_refresh (screen, &my_error)) {
1362 if (my_error) {
1363 g_propagate_error (error, my_error);
1364 return FALSE; /* This is a genuine error */
1365 }
1366
1367 /* This means the screen didn't change, so just proceed */
1368 }
1369
1370 stored = g_object_new (MATE_TYPE_RR_CONFIG, "screen", screen, NULL);
1371
1372 if (mate_rr_config_load_filename (stored, filename, error))
1373 {
1374 gboolean result;
1375
1376 mate_rr_config_ensure_primary (stored);
1377 result = mate_rr_config_apply_with_time (stored, screen, timestamp, error);
1378
1379 g_object_unref (stored);
1380
1381 return result;
1382 }
1383 else
1384 {
1385 g_object_unref (stored);
1386 return FALSE;
1387 }
1388 }
1389
1390 /**
1391 * mate_rr_config_get_outputs:
1392 *
1393 * Returns: (array zero-terminated=1) (element-type MateDesktop.RROutputInfo) (transfer none): the output configuration for this #MateRRConfig
1394 */
1395 MateRROutputInfo **
mate_rr_config_get_outputs(MateRRConfig * self)1396 mate_rr_config_get_outputs (MateRRConfig *self)
1397 {
1398 g_return_val_if_fail (MATE_IS_RR_CONFIG (self), NULL);
1399
1400 return self->priv->outputs;
1401 }
1402
1403 /**
1404 * mate_rr_config_get_clone:
1405 *
1406 * Returns: whether at least two outputs are at (0, 0) offset and they
1407 * have the same width/height. Those outputs are of course connected and on
1408 * (i.e. they have a CRTC assigned).
1409 */
1410 gboolean
mate_rr_config_get_clone(MateRRConfig * self)1411 mate_rr_config_get_clone (MateRRConfig *self)
1412 {
1413 g_return_val_if_fail (MATE_IS_RR_CONFIG (self), FALSE);
1414
1415 return self->priv->clone;
1416 }
1417
1418 void
mate_rr_config_set_clone(MateRRConfig * self,gboolean clone)1419 mate_rr_config_set_clone (MateRRConfig *self, gboolean clone)
1420 {
1421 g_return_if_fail (MATE_IS_RR_CONFIG (self));
1422
1423 self->priv->clone = clone;
1424 }
1425
1426
1427 /*
1428 * CRTC assignment
1429 */
1430 typedef struct CrtcInfo CrtcInfo;
1431
1432 struct CrtcInfo
1433 {
1434 MateRRMode *mode;
1435 int x;
1436 int y;
1437 MateRRRotation rotation;
1438 GPtrArray *outputs;
1439 };
1440
1441 struct CrtcAssignment
1442 {
1443 MateRRScreen *screen;
1444 GHashTable *info;
1445 MateRROutput *primary;
1446 };
1447
1448 static gboolean
can_clone(CrtcInfo * info,MateRROutput * output)1449 can_clone (CrtcInfo *info,
1450 MateRROutput *output)
1451 {
1452 int i;
1453
1454 for (i = 0; i < info->outputs->len; ++i)
1455 {
1456 MateRROutput *clone = info->outputs->pdata[i];
1457
1458 if (!mate_rr_output_can_clone (clone, output))
1459 return FALSE;
1460 }
1461
1462 return TRUE;
1463 }
1464
1465 static gboolean
crtc_assignment_assign(CrtcAssignment * assign,MateRRCrtc * crtc,MateRRMode * mode,int x,int y,MateRRRotation rotation,gboolean primary,MateRROutput * output,GError ** error)1466 crtc_assignment_assign (CrtcAssignment *assign,
1467 MateRRCrtc *crtc,
1468 MateRRMode *mode,
1469 int x,
1470 int y,
1471 MateRRRotation rotation,
1472 gboolean primary,
1473 MateRROutput *output,
1474 GError **error)
1475 {
1476 CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
1477 guint32 crtc_id;
1478 const char *output_name;
1479
1480 crtc_id = mate_rr_crtc_get_id (crtc);
1481 output_name = mate_rr_output_get_name (output);
1482
1483 if (!mate_rr_crtc_can_drive_output (crtc, output))
1484 {
1485 g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_CRTC_ASSIGNMENT,
1486 _("CRTC %d cannot drive output %s"), crtc_id, output_name);
1487 return FALSE;
1488 }
1489
1490 if (!mate_rr_output_supports_mode (output, mode))
1491 {
1492 g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_CRTC_ASSIGNMENT,
1493 _("output %s does not support mode %dx%d@%dHz"),
1494 output_name,
1495 mate_rr_mode_get_width (mode),
1496 mate_rr_mode_get_height (mode),
1497 mate_rr_mode_get_freq (mode));
1498 return FALSE;
1499 }
1500
1501 if (!mate_rr_crtc_supports_rotation (crtc, rotation))
1502 {
1503 g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_CRTC_ASSIGNMENT,
1504 _("CRTC %d does not support rotation=%s"),
1505 crtc_id,
1506 get_rotation_name (rotation));
1507 return FALSE;
1508 }
1509
1510 if (info)
1511 {
1512 if (!(info->mode == mode &&
1513 info->x == x &&
1514 info->y == y &&
1515 info->rotation == rotation))
1516 {
1517 g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_CRTC_ASSIGNMENT,
1518 _("output %s does not have the same parameters as another cloned output:\n"
1519 "existing mode = %d, new mode = %d\n"
1520 "existing coordinates = (%d, %d), new coordinates = (%d, %d)\n"
1521 "existing rotation = %s, new rotation = %s"),
1522 output_name,
1523 mate_rr_mode_get_id (info->mode), mate_rr_mode_get_id (mode),
1524 info->x, info->y,
1525 x, y,
1526 get_rotation_name (info->rotation), get_rotation_name (rotation));
1527 return FALSE;
1528 }
1529
1530 if (!can_clone (info, output))
1531 {
1532 g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_CRTC_ASSIGNMENT,
1533 _("cannot clone to output %s"),
1534 output_name);
1535 return FALSE;
1536 }
1537
1538 g_ptr_array_add (info->outputs, output);
1539
1540 if (primary && !assign->primary)
1541 {
1542 assign->primary = output;
1543 }
1544
1545 return TRUE;
1546 }
1547 else
1548 {
1549 info = g_new0 (CrtcInfo, 1);
1550
1551 info->mode = mode;
1552 info->x = x;
1553 info->y = y;
1554 info->rotation = rotation;
1555 info->outputs = g_ptr_array_new ();
1556
1557 g_ptr_array_add (info->outputs, output);
1558
1559 g_hash_table_insert (assign->info, crtc, info);
1560
1561 if (primary && !assign->primary)
1562 {
1563 assign->primary = output;
1564 }
1565
1566 return TRUE;
1567 }
1568 }
1569
1570 static void
crtc_assignment_unassign(CrtcAssignment * assign,MateRRCrtc * crtc,MateRROutput * output)1571 crtc_assignment_unassign (CrtcAssignment *assign,
1572 MateRRCrtc *crtc,
1573 MateRROutput *output)
1574 {
1575 CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
1576
1577 if (info)
1578 {
1579 g_ptr_array_remove (info->outputs, output);
1580
1581 if (assign->primary == output)
1582 {
1583 assign->primary = NULL;
1584 }
1585
1586 if (info->outputs->len == 0)
1587 g_hash_table_remove (assign->info, crtc);
1588 }
1589 }
1590
1591 static void
crtc_assignment_free(CrtcAssignment * assign)1592 crtc_assignment_free (CrtcAssignment *assign)
1593 {
1594 g_hash_table_destroy (assign->info);
1595
1596 g_free (assign);
1597 }
1598
1599 typedef struct {
1600 guint32 timestamp;
1601 gboolean has_error;
1602 GError **error;
1603 } ConfigureCrtcState;
1604
1605 static void
configure_crtc(gpointer key,gpointer value,gpointer data)1606 configure_crtc (gpointer key,
1607 gpointer value,
1608 gpointer data)
1609 {
1610 MateRRCrtc *crtc = key;
1611 CrtcInfo *info = value;
1612 ConfigureCrtcState *state = data;
1613
1614 if (state->has_error)
1615 return;
1616
1617 if (!mate_rr_crtc_set_config_with_time (crtc,
1618 state->timestamp,
1619 info->x, info->y,
1620 info->mode,
1621 info->rotation,
1622 (MateRROutput **)info->outputs->pdata,
1623 info->outputs->len,
1624 state->error))
1625 state->has_error = TRUE;
1626 }
1627
1628 static gboolean
mode_is_rotated(CrtcInfo * info)1629 mode_is_rotated (CrtcInfo *info)
1630 {
1631 if ((info->rotation & MATE_RR_ROTATION_270) ||
1632 (info->rotation & MATE_RR_ROTATION_90))
1633 {
1634 return TRUE;
1635 }
1636 return FALSE;
1637 }
1638
1639 static gboolean
crtc_is_rotated(MateRRCrtc * crtc)1640 crtc_is_rotated (MateRRCrtc *crtc)
1641 {
1642 MateRRRotation r = mate_rr_crtc_get_current_rotation (crtc);
1643
1644 if ((r & MATE_RR_ROTATION_270) ||
1645 (r & MATE_RR_ROTATION_90))
1646 {
1647 return TRUE;
1648 }
1649
1650 return FALSE;
1651 }
1652
1653 static void
accumulate_error(GString * accumulated_error,GError * error)1654 accumulate_error (GString *accumulated_error, GError *error)
1655 {
1656 g_string_append_printf (accumulated_error, " %s\n", error->message);
1657 g_error_free (error);
1658 }
1659
1660 /* Check whether the given set of settings can be used
1661 * at the same time -- ie. whether there is an assignment
1662 * of CRTC's to outputs.
1663 *
1664 * Brute force - the number of objects involved is small
1665 * enough that it doesn't matter.
1666 */
1667 static gboolean
real_assign_crtcs(MateRRScreen * screen,MateRROutputInfo ** outputs,CrtcAssignment * assignment,GError ** error)1668 real_assign_crtcs (MateRRScreen *screen,
1669 MateRROutputInfo **outputs,
1670 CrtcAssignment *assignment,
1671 GError **error)
1672 {
1673 MateRRCrtc **crtcs = mate_rr_screen_list_crtcs (screen);
1674 MateRROutputInfo *output;
1675 int i;
1676 gboolean tried_mode;
1677 GError *my_error;
1678 GString *accumulated_error;
1679 gboolean success;
1680
1681 output = *outputs;
1682 if (!output)
1683 return TRUE;
1684
1685 /* It is always allowed for an output to be turned off */
1686 if (!output->priv->on)
1687 {
1688 return real_assign_crtcs (screen, outputs + 1, assignment, error);
1689 }
1690
1691 success = FALSE;
1692 tried_mode = FALSE;
1693 accumulated_error = g_string_new (NULL);
1694
1695 for (i = 0; crtcs[i] != NULL; ++i)
1696 {
1697 MateRRCrtc *crtc = crtcs[i];
1698 int crtc_id = mate_rr_crtc_get_id (crtc);
1699 int pass;
1700
1701 g_string_append_printf (accumulated_error,
1702 _("Trying modes for CRTC %d\n"),
1703 crtc_id);
1704
1705 /* Make two passes, one where frequencies must match, then
1706 * one where they don't have to
1707 */
1708 for (pass = 0; pass < 2; ++pass)
1709 {
1710 MateRROutput *mate_rr_output = mate_rr_screen_get_output_by_name (screen, output->priv->name);
1711 MateRRMode **modes = mate_rr_output_list_modes (mate_rr_output);
1712 int j;
1713
1714 for (j = 0; modes[j] != NULL; ++j)
1715 {
1716 MateRRMode *mode = modes[j];
1717 int mode_width;
1718 int mode_height;
1719 int mode_freq;
1720
1721 mode_width = mate_rr_mode_get_width (mode);
1722 mode_height = mate_rr_mode_get_height (mode);
1723 mode_freq = mate_rr_mode_get_freq (mode);
1724
1725 g_string_append_printf (accumulated_error,
1726 _("CRTC %d: trying mode %dx%d@%dHz with output at %dx%d@%dHz (pass %d)\n"),
1727 crtc_id,
1728 mode_width, mode_height, mode_freq,
1729 output->priv->width, output->priv->height, output->priv->rate,
1730 pass);
1731
1732 if (mode_width == output->priv->width &&
1733 mode_height == output->priv->height &&
1734 (pass == 1 || mode_freq == output->priv->rate))
1735 {
1736 tried_mode = TRUE;
1737
1738 my_error = NULL;
1739 if (crtc_assignment_assign (
1740 assignment, crtc, modes[j],
1741 output->priv->x, output->priv->y,
1742 output->priv->rotation,
1743 output->priv->primary,
1744 mate_rr_output,
1745 &my_error))
1746 {
1747 my_error = NULL;
1748 if (real_assign_crtcs (screen, outputs + 1, assignment, &my_error)) {
1749 success = TRUE;
1750 goto out;
1751 } else
1752 accumulate_error (accumulated_error, my_error);
1753
1754 crtc_assignment_unassign (assignment, crtc, mate_rr_output);
1755 } else
1756 accumulate_error (accumulated_error, my_error);
1757 }
1758 }
1759 }
1760 }
1761
1762 out:
1763
1764 if (success)
1765 g_string_free (accumulated_error, TRUE);
1766 else {
1767 char *str;
1768
1769 str = g_string_free (accumulated_error, FALSE);
1770
1771 if (tried_mode)
1772 g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_CRTC_ASSIGNMENT,
1773 _("could not assign CRTCs to outputs:\n%s"),
1774 str);
1775 else
1776 g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_CRTC_ASSIGNMENT,
1777 _("none of the selected modes were compatible with the possible modes:\n%s"),
1778 str);
1779
1780 g_free (str);
1781 }
1782
1783 return success;
1784 }
1785
1786 static void
crtc_info_free(CrtcInfo * info)1787 crtc_info_free (CrtcInfo *info)
1788 {
1789 g_ptr_array_free (info->outputs, TRUE);
1790 g_free (info);
1791 }
1792
1793 static void
get_required_virtual_size(CrtcAssignment * assign,int * width,int * height)1794 get_required_virtual_size (CrtcAssignment *assign, int *width, int *height)
1795 {
1796 GList *active_crtcs = g_hash_table_get_keys (assign->info);
1797 GList *list;
1798 int d;
1799
1800 if (!width)
1801 width = &d;
1802 if (!height)
1803 height = &d;
1804
1805 /* Compute size of the screen */
1806 *width = *height = 1;
1807 for (list = active_crtcs; list != NULL; list = list->next)
1808 {
1809 MateRRCrtc *crtc = list->data;
1810 CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
1811 int w, h;
1812
1813 w = mate_rr_mode_get_width (info->mode);
1814 h = mate_rr_mode_get_height (info->mode);
1815
1816 if (mode_is_rotated (info))
1817 {
1818 int tmp = h;
1819 h = w;
1820 w = tmp;
1821 }
1822
1823 *width = MAX (*width, info->x + w);
1824 *height = MAX (*height, info->y + h);
1825 }
1826
1827 g_list_free (active_crtcs);
1828 }
1829
1830 static CrtcAssignment *
crtc_assignment_new(MateRRScreen * screen,MateRROutputInfo ** outputs,GError ** error)1831 crtc_assignment_new (MateRRScreen *screen, MateRROutputInfo **outputs, GError **error)
1832 {
1833 CrtcAssignment *assignment = g_new0 (CrtcAssignment, 1);
1834
1835 assignment->info = g_hash_table_new_full (
1836 g_direct_hash, g_direct_equal, NULL, (GFreeFunc)crtc_info_free);
1837
1838 if (real_assign_crtcs (screen, outputs, assignment, error))
1839 {
1840 int width, height;
1841 int min_width, max_width, min_height, max_height;
1842 int required_pixels, min_pixels, max_pixels;
1843
1844 get_required_virtual_size (assignment, &width, &height);
1845
1846 mate_rr_screen_get_ranges (
1847 screen, &min_width, &max_width, &min_height, &max_height);
1848
1849 required_pixels = width * height;
1850 min_pixels = min_width * min_height;
1851 max_pixels = max_width * max_height;
1852
1853 if (required_pixels < min_pixels || required_pixels > max_pixels)
1854 {
1855 g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_BOUNDS_ERROR,
1856 /* Translators: the "requested", "minimum", and
1857 * "maximum" words here are not keywords; please
1858 * translate them as usual. */
1859 _("required virtual size does not fit available size: "
1860 "requested=(%d, %d), minimum=(%d, %d), maximum=(%d, %d)"),
1861 width, height,
1862 min_width, min_height,
1863 max_width, max_height);
1864 goto fail;
1865 }
1866
1867 assignment->screen = screen;
1868
1869 return assignment;
1870 }
1871
1872 fail:
1873 crtc_assignment_free (assignment);
1874
1875 return NULL;
1876 }
1877
1878 static gboolean
crtc_assignment_apply(CrtcAssignment * assign,guint32 timestamp,GError ** error)1879 crtc_assignment_apply (CrtcAssignment *assign, guint32 timestamp, GError **error)
1880 {
1881 MateRRCrtc **all_crtcs = mate_rr_screen_list_crtcs (assign->screen);
1882 int width, height;
1883 int i;
1884 int min_width, max_width, min_height, max_height;
1885 int width_mm, height_mm;
1886 gboolean success = TRUE;
1887
1888 /* Compute size of the screen */
1889 get_required_virtual_size (assign, &width, &height);
1890
1891 mate_rr_screen_get_ranges (
1892 assign->screen, &min_width, &max_width, &min_height, &max_height);
1893
1894 /* We should never get here if the dimensions don't fit in the virtual size,
1895 * but just in case we do, fix it up.
1896 */
1897 width = MAX (min_width, width);
1898 width = MIN (max_width, width);
1899 height = MAX (min_height, height);
1900 height = MIN (max_height, height);
1901
1902 /* FMQ: do we need to check the sizes instead of clamping them? */
1903
1904 /* Grab the server while we fiddle with the CRTCs and the screen, so that
1905 * apps that listen for RANDR notifications will only receive the final
1906 * status.
1907 */
1908
1909 gdk_x11_display_grab (gdk_screen_get_display (assign->screen->priv->gdk_screen));
1910
1911 /* Turn off all crtcs that are currently displaying outside the new screen,
1912 * or are not used in the new setup
1913 */
1914 for (i = 0; all_crtcs[i] != NULL; ++i)
1915 {
1916 MateRRCrtc *crtc = all_crtcs[i];
1917 MateRRMode *mode = mate_rr_crtc_get_current_mode (crtc);
1918 int x, y;
1919
1920 if (mode)
1921 {
1922 int w, h;
1923 mate_rr_crtc_get_position (crtc, &x, &y);
1924
1925 w = mate_rr_mode_get_width (mode);
1926 h = mate_rr_mode_get_height (mode);
1927
1928 if (crtc_is_rotated (crtc))
1929 {
1930 int tmp = h;
1931 h = w;
1932 w = tmp;
1933 }
1934
1935 if (x + w > width || y + h > height || !g_hash_table_lookup (assign->info, crtc))
1936 {
1937 if (!mate_rr_crtc_set_config_with_time (crtc, timestamp, 0, 0, NULL, MATE_RR_ROTATION_0, NULL, 0, error))
1938 {
1939 success = FALSE;
1940 break;
1941 }
1942
1943 }
1944 }
1945 }
1946
1947 /* The 'physical size' of an X screen is meaningless if that screen
1948 * can consist of many monitors. So just pick a size that make the
1949 * dpi 96.
1950 *
1951 * Firefox and Evince apparently believe what X tells them.
1952 */
1953 width_mm = (width / 96.0) * 25.4 + 0.5;
1954 height_mm = (height / 96.0) * 25.4 + 0.5;
1955
1956 if (success)
1957 {
1958 ConfigureCrtcState state;
1959
1960 mate_rr_screen_set_size (assign->screen, width, height, width_mm, height_mm);
1961
1962 state.timestamp = timestamp;
1963 state.has_error = FALSE;
1964 state.error = error;
1965
1966 g_hash_table_foreach (assign->info, configure_crtc, &state);
1967
1968 success = !state.has_error;
1969 }
1970
1971 mate_rr_screen_set_primary_output (assign->screen, assign->primary);
1972
1973 gdk_x11_display_ungrab (gdk_screen_get_display (assign->screen->priv->gdk_screen));
1974
1975 return success;
1976 }
1977