1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /*
4 * Copyright (C) 2001, 2002 Havoc Pennington
5 * Copyright (C) 2002, 2003 Red Hat Inc.
6 * Some ICCCM manager selection code derived from fvwm2,
7 * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team
8 * Copyright (C) 2003 Rob Adams
9 * Copyright (C) 2004-2006 Elijah Newren
10 * Copyright (C) 2013, 2017 Red Hat Inc.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <http://www.gnu.org/licenses/>.
24 */
25
26 /*
27 * Portions of this file are derived from gnome-desktop/libgnome-desktop/gnome-rr-config.c
28 *
29 * Copyright 2007, 2008, Red Hat, Inc.
30 * Copyright 2010 Giovanni Campagna
31 *
32 * Author: Soren Sandmann <sandmann@redhat.com>
33 */
34
35 #include "config.h"
36
37 #include "backends/meta-monitor-config-migration.h"
38
39 #include <gio/gio.h>
40 #include <string.h>
41
42 #include "backends/meta-monitor-config-manager.h"
43 #include "backends/meta-monitor-config-store.h"
44 #include "backends/meta-monitor-manager-private.h"
45 #include "meta/boxes.h"
46
47 #define META_MONITORS_CONFIG_MIGRATION_ERROR (meta_monitors_config_migration_error_quark ())
48 static GQuark meta_monitors_config_migration_error_quark (void);
49
50 G_DEFINE_QUARK (meta-monitors-config-migration-error-quark,
51 meta_monitors_config_migration_error)
52
53 enum _MetaConfigMigrationError
54 {
55 META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_TILED,
56 META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_MAIN_TILE
57 } MetaConfigMigrationError;
58
59 typedef struct
60 {
61 char *connector;
62 char *vendor;
63 char *product;
64 char *serial;
65 } MetaOutputKey;
66
67 typedef struct
68 {
69 gboolean enabled;
70 MetaRectangle rect;
71 float refresh_rate;
72 MetaMonitorTransform transform;
73
74 gboolean is_primary;
75 gboolean is_presentation;
76 gboolean is_underscanning;
77 } MetaOutputConfig;
78
79 typedef struct _MetaLegacyMonitorsConfig
80 {
81 MetaOutputKey *keys;
82 MetaOutputConfig *outputs;
83 unsigned int n_outputs;
84 } MetaLegacyMonitorsConfig;
85
86 typedef enum
87 {
88 STATE_INITIAL,
89 STATE_MONITORS,
90 STATE_CONFIGURATION,
91 STATE_OUTPUT,
92 STATE_OUTPUT_FIELD,
93 STATE_CLONE
94 } ParserState;
95
96 typedef struct
97 {
98 ParserState state;
99 int unknown_count;
100
101 GArray *key_array;
102 GArray *output_array;
103 MetaOutputKey key;
104 MetaOutputConfig output;
105
106 char *output_field;
107
108 GHashTable *configs;
109 } ConfigParser;
110
111 static MetaLegacyMonitorsConfig *
legacy_config_new(void)112 legacy_config_new (void)
113 {
114 return g_new0 (MetaLegacyMonitorsConfig, 1);
115 }
116
117 static void
legacy_config_free(gpointer data)118 legacy_config_free (gpointer data)
119 {
120 MetaLegacyMonitorsConfig *config = data;
121
122 g_free (config->keys);
123 g_free (config->outputs);
124 g_free (config);
125 }
126
127 static unsigned long
output_key_hash(const MetaOutputKey * key)128 output_key_hash (const MetaOutputKey *key)
129 {
130 return (g_str_hash (key->connector) ^
131 g_str_hash (key->vendor) ^
132 g_str_hash (key->product) ^
133 g_str_hash (key->serial));
134 }
135
136 static gboolean
output_key_equal(const MetaOutputKey * one,const MetaOutputKey * two)137 output_key_equal (const MetaOutputKey *one,
138 const MetaOutputKey *two)
139 {
140 return (strcmp (one->connector, two->connector) == 0 &&
141 strcmp (one->vendor, two->vendor) == 0 &&
142 strcmp (one->product, two->product) == 0 &&
143 strcmp (one->serial, two->serial) == 0);
144 }
145
146 static unsigned int
legacy_config_hash(gconstpointer data)147 legacy_config_hash (gconstpointer data)
148 {
149 const MetaLegacyMonitorsConfig *config = data;
150 unsigned int i, hash;
151
152 hash = 0;
153 for (i = 0; i < config->n_outputs; i++)
154 hash ^= output_key_hash (&config->keys[i]);
155
156 return hash;
157 }
158
159 static gboolean
legacy_config_equal(gconstpointer one,gconstpointer two)160 legacy_config_equal (gconstpointer one,
161 gconstpointer two)
162 {
163 const MetaLegacyMonitorsConfig *c_one = one;
164 const MetaLegacyMonitorsConfig *c_two = two;
165 unsigned int i;
166 gboolean ok;
167
168 if (c_one->n_outputs != c_two->n_outputs)
169 return FALSE;
170
171 ok = TRUE;
172 for (i = 0; i < c_one->n_outputs && ok; i++)
173 ok = output_key_equal (&c_one->keys[i],
174 &c_two->keys[i]);
175
176 return ok;
177 }
178
179 static void
free_output_key(MetaOutputKey * key)180 free_output_key (MetaOutputKey *key)
181 {
182 g_free (key->connector);
183 g_free (key->vendor);
184 g_free (key->product);
185 g_free (key->serial);
186 }
187
188 static void
handle_start_element(GMarkupParseContext * context,const char * element_name,const char ** attribute_names,const char ** attribute_values,gpointer user_data,GError ** error)189 handle_start_element (GMarkupParseContext *context,
190 const char *element_name,
191 const char **attribute_names,
192 const char **attribute_values,
193 gpointer user_data,
194 GError **error)
195 {
196 ConfigParser *parser = user_data;
197
198 switch (parser->state)
199 {
200 case STATE_INITIAL:
201 {
202 char *version;
203
204 if (strcmp (element_name, "monitors") != 0)
205 {
206 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
207 "Invalid document element %s", element_name);
208 return;
209 }
210
211 if (!g_markup_collect_attributes (element_name,
212 attribute_names,
213 attribute_values,
214 error,
215 G_MARKUP_COLLECT_STRING,
216 "version", &version,
217 G_MARKUP_COLLECT_INVALID))
218 return;
219
220 if (strcmp (version, "1") != 0)
221 {
222 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
223 "Invalid or unsupported version %s", version);
224 return;
225 }
226
227 parser->state = STATE_MONITORS;
228 return;
229 }
230
231 case STATE_MONITORS:
232 {
233 if (strcmp (element_name, "configuration") != 0)
234 {
235 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
236 "Invalid toplevel element %s", element_name);
237 return;
238 }
239
240 parser->key_array = g_array_new (FALSE, FALSE,
241 sizeof (MetaOutputKey));
242 parser->output_array = g_array_new (FALSE, FALSE,
243 sizeof (MetaOutputConfig));
244 parser->state = STATE_CONFIGURATION;
245 return;
246 }
247
248 case STATE_CONFIGURATION:
249 {
250 if (strcmp (element_name, "clone") == 0 &&
251 parser->unknown_count == 0)
252 {
253 parser->state = STATE_CLONE;
254 }
255 else if (strcmp (element_name, "output") == 0 &&
256 parser->unknown_count == 0)
257 {
258 char *name;
259
260 if (!g_markup_collect_attributes (element_name,
261 attribute_names,
262 attribute_values,
263 error,
264 G_MARKUP_COLLECT_STRING,
265 "name", &name,
266 G_MARKUP_COLLECT_INVALID))
267 return;
268
269 memset (&parser->key, 0, sizeof (MetaOutputKey));
270 memset (&parser->output, 0, sizeof (MetaOutputConfig));
271
272 parser->key.connector = g_strdup (name);
273 parser->state = STATE_OUTPUT;
274 }
275 else
276 {
277 parser->unknown_count++;
278 }
279
280 return;
281 }
282
283 case STATE_OUTPUT:
284 {
285 if ((strcmp (element_name, "vendor") == 0 ||
286 strcmp (element_name, "product") == 0 ||
287 strcmp (element_name, "serial") == 0 ||
288 strcmp (element_name, "width") == 0 ||
289 strcmp (element_name, "height") == 0 ||
290 strcmp (element_name, "rate") == 0 ||
291 strcmp (element_name, "x") == 0 ||
292 strcmp (element_name, "y") == 0 ||
293 strcmp (element_name, "rotation") == 0 ||
294 strcmp (element_name, "reflect_x") == 0 ||
295 strcmp (element_name, "reflect_y") == 0 ||
296 strcmp (element_name, "primary") == 0 ||
297 strcmp (element_name, "presentation") == 0 ||
298 strcmp (element_name, "underscanning") == 0) &&
299 parser->unknown_count == 0)
300 {
301 parser->state = STATE_OUTPUT_FIELD;
302
303 parser->output_field = g_strdup (element_name);
304 }
305 else
306 {
307 parser->unknown_count++;
308 }
309
310 return;
311 }
312
313 case STATE_CLONE:
314 case STATE_OUTPUT_FIELD:
315 {
316 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
317 "Unexpected element %s", element_name);
318 return;
319 }
320
321 default:
322 g_assert_not_reached ();
323 }
324 }
325
326 static void
handle_end_element(GMarkupParseContext * context,const char * element_name,gpointer user_data,GError ** error)327 handle_end_element (GMarkupParseContext *context,
328 const char *element_name,
329 gpointer user_data,
330 GError **error)
331 {
332 ConfigParser *parser = user_data;
333
334 switch (parser->state)
335 {
336 case STATE_MONITORS:
337 {
338 parser->state = STATE_INITIAL;
339 return;
340 }
341
342 case STATE_CONFIGURATION:
343 {
344 if (strcmp (element_name, "configuration") == 0 &&
345 parser->unknown_count == 0)
346 {
347 MetaLegacyMonitorsConfig *config = legacy_config_new ();
348
349 g_assert (parser->key_array->len == parser->output_array->len);
350
351 config->n_outputs = parser->key_array->len;
352 config->keys = (void*)g_array_free (parser->key_array, FALSE);
353 config->outputs = (void*)g_array_free (parser->output_array, FALSE);
354
355 g_hash_table_replace (parser->configs, config, config);
356
357 parser->key_array = NULL;
358 parser->output_array = NULL;
359 parser->state = STATE_MONITORS;
360 }
361 else
362 {
363 parser->unknown_count--;
364
365 g_assert (parser->unknown_count >= 0);
366 }
367
368 return;
369 }
370
371 case STATE_OUTPUT:
372 {
373 if (strcmp (element_name, "output") == 0 && parser->unknown_count == 0)
374 {
375 if (parser->key.vendor == NULL ||
376 parser->key.product == NULL ||
377 parser->key.serial == NULL)
378 {
379 /* Disconnected output, ignore */
380 free_output_key (&parser->key);
381 }
382 else
383 {
384 if (parser->output.rect.width == 0 ||
385 parser->output.rect.height == 0)
386 parser->output.enabled = FALSE;
387 else
388 parser->output.enabled = TRUE;
389
390 g_array_append_val (parser->key_array, parser->key);
391 g_array_append_val (parser->output_array, parser->output);
392 }
393
394 memset (&parser->key, 0, sizeof (MetaOutputKey));
395 memset (&parser->output, 0, sizeof (MetaOutputConfig));
396
397 parser->state = STATE_CONFIGURATION;
398 }
399 else
400 {
401 parser->unknown_count--;
402
403 g_assert (parser->unknown_count >= 0);
404 }
405
406 return;
407 }
408
409 case STATE_CLONE:
410 {
411 parser->state = STATE_CONFIGURATION;
412 return;
413 }
414
415 case STATE_OUTPUT_FIELD:
416 {
417 g_free (parser->output_field);
418 parser->output_field = NULL;
419
420 parser->state = STATE_OUTPUT;
421 return;
422 }
423
424 case STATE_INITIAL:
425 default:
426 g_assert_not_reached ();
427 }
428 }
429
430 static void
read_int(const char * text,gsize text_len,gint * field,GError ** error)431 read_int (const char *text,
432 gsize text_len,
433 gint *field,
434 GError **error)
435 {
436 char buf[64];
437 gint64 v;
438 char *end;
439
440 strncpy (buf, text, text_len);
441 buf[MIN (63, text_len)] = 0;
442
443 v = g_ascii_strtoll (buf, &end, 10);
444
445 /* Limit reasonable values (actual limits are a lot smaller that these) */
446 if (*end || v < 0 || v > G_MAXINT16)
447 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
448 "Expected a number, got %s", buf);
449 else
450 *field = v;
451 }
452
453 static void
read_float(const char * text,gsize text_len,gfloat * field,GError ** error)454 read_float (const char *text,
455 gsize text_len,
456 gfloat *field,
457 GError **error)
458 {
459 char buf[64];
460 gfloat v;
461 char *end;
462
463 strncpy (buf, text, text_len);
464 buf[MIN (63, text_len)] = 0;
465
466 v = g_ascii_strtod (buf, &end);
467
468 /* Limit reasonable values (actual limits are a lot smaller that these) */
469 if (*end)
470 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
471 "Expected a number, got %s", buf);
472 else
473 *field = v;
474 }
475
476 static gboolean
read_bool(const char * text,gsize text_len,GError ** error)477 read_bool (const char *text,
478 gsize text_len,
479 GError **error)
480 {
481 if (strncmp (text, "no", text_len) == 0)
482 return FALSE;
483 else if (strncmp (text, "yes", text_len) == 0)
484 return TRUE;
485 else
486 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
487 "Invalid boolean value %.*s", (int)text_len, text);
488
489 return FALSE;
490 }
491
492 static gboolean
is_all_whitespace(const char * text,gsize text_len)493 is_all_whitespace (const char *text,
494 gsize text_len)
495 {
496 gsize i;
497
498 for (i = 0; i < text_len; i++)
499 if (!g_ascii_isspace (text[i]))
500 return FALSE;
501
502 return TRUE;
503 }
504
505 static void
handle_text(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** error)506 handle_text (GMarkupParseContext *context,
507 const gchar *text,
508 gsize text_len,
509 gpointer user_data,
510 GError **error)
511 {
512 ConfigParser *parser = user_data;
513
514 switch (parser->state)
515 {
516 case STATE_MONITORS:
517 {
518 if (!is_all_whitespace (text, text_len))
519 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
520 "Unexpected content at this point");
521 return;
522 }
523
524 case STATE_CONFIGURATION:
525 {
526 if (parser->unknown_count == 0)
527 {
528 if (!is_all_whitespace (text, text_len))
529 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
530 "Unexpected content at this point");
531 }
532 else
533 {
534 /* Handling unknown element, ignore */
535 }
536
537 return;
538 }
539
540 case STATE_OUTPUT:
541 {
542 if (parser->unknown_count == 0)
543 {
544 if (!is_all_whitespace (text, text_len))
545 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
546 "Unexpected content at this point");
547 }
548 else
549 {
550 /* Handling unknown element, ignore */
551 }
552 return;
553 }
554
555 case STATE_CLONE:
556 {
557 /* Ignore the clone flag */
558 return;
559 }
560
561 case STATE_OUTPUT_FIELD:
562 {
563 if (strcmp (parser->output_field, "vendor") == 0)
564 parser->key.vendor = g_strndup (text, text_len);
565 else if (strcmp (parser->output_field, "product") == 0)
566 parser->key.product = g_strndup (text, text_len);
567 else if (strcmp (parser->output_field, "serial") == 0)
568 parser->key.serial = g_strndup (text, text_len);
569 else if (strcmp (parser->output_field, "width") == 0)
570 read_int (text, text_len, &parser->output.rect.width, error);
571 else if (strcmp (parser->output_field, "height") == 0)
572 read_int (text, text_len, &parser->output.rect.height, error);
573 else if (strcmp (parser->output_field, "rate") == 0)
574 read_float (text, text_len, &parser->output.refresh_rate, error);
575 else if (strcmp (parser->output_field, "x") == 0)
576 read_int (text, text_len, &parser->output.rect.x, error);
577 else if (strcmp (parser->output_field, "y") == 0)
578 read_int (text, text_len, &parser->output.rect.y, error);
579 else if (strcmp (parser->output_field, "rotation") == 0)
580 {
581 if (strncmp (text, "normal", text_len) == 0)
582 parser->output.transform = META_MONITOR_TRANSFORM_NORMAL;
583 else if (strncmp (text, "left", text_len) == 0)
584 parser->output.transform = META_MONITOR_TRANSFORM_90;
585 else if (strncmp (text, "upside_down", text_len) == 0)
586 parser->output.transform = META_MONITOR_TRANSFORM_180;
587 else if (strncmp (text, "right", text_len) == 0)
588 parser->output.transform = META_MONITOR_TRANSFORM_270;
589 else
590 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
591 "Invalid rotation type %.*s", (int)text_len, text);
592 }
593 else if (strcmp (parser->output_field, "reflect_x") == 0)
594 parser->output.transform += read_bool (text, text_len, error) ?
595 META_MONITOR_TRANSFORM_FLIPPED : 0;
596 else if (strcmp (parser->output_field, "reflect_y") == 0)
597 {
598 if (read_bool (text, text_len, error))
599 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
600 "Y reflection is not supported");
601 }
602 else if (strcmp (parser->output_field, "primary") == 0)
603 parser->output.is_primary = read_bool (text, text_len, error);
604 else if (strcmp (parser->output_field, "presentation") == 0)
605 parser->output.is_presentation = read_bool (text, text_len, error);
606 else if (strcmp (parser->output_field, "underscanning") == 0)
607 parser->output.is_underscanning = read_bool (text, text_len, error);
608 else
609 g_assert_not_reached ();
610 return;
611 }
612
613 case STATE_INITIAL:
614 default:
615 g_assert_not_reached ();
616 }
617 }
618
619 static const GMarkupParser config_parser = {
620 .start_element = handle_start_element,
621 .end_element = handle_end_element,
622 .text = handle_text,
623 };
624
625 static GHashTable *
load_config_file(GFile * file,GError ** error)626 load_config_file (GFile *file,
627 GError **error)
628 {
629 g_autofree char *contents = NULL;
630 gsize size;
631 g_autoptr (GMarkupParseContext) context = NULL;
632 ConfigParser parser = { 0 };
633
634 if (!g_file_load_contents (file, NULL, &contents, &size, NULL, error))
635 return FALSE;
636
637 parser.configs = g_hash_table_new_full (legacy_config_hash,
638 legacy_config_equal,
639 legacy_config_free,
640 NULL);
641 parser.state = STATE_INITIAL;
642
643 context = g_markup_parse_context_new (&config_parser,
644 G_MARKUP_TREAT_CDATA_AS_TEXT |
645 G_MARKUP_PREFIX_ERROR_POSITION,
646 &parser, NULL);
647 if (!g_markup_parse_context_parse (context, contents, size, error))
648 {
649 if (parser.key_array)
650 g_array_free (parser.key_array, TRUE);
651 if (parser.output_array)
652 g_array_free (parser.output_array, TRUE);
653
654 free_output_key (&parser.key);
655 g_free (parser.output_field);
656 g_hash_table_destroy (parser.configs);
657
658 return NULL;
659 }
660
661 return parser.configs;
662 }
663
664 static MetaMonitorConfig *
create_monitor_config(MetaOutputKey * output_key,MetaOutputConfig * output_config,int mode_width,int mode_height,GError ** error)665 create_monitor_config (MetaOutputKey *output_key,
666 MetaOutputConfig *output_config,
667 int mode_width,
668 int mode_height,
669 GError **error)
670 {
671 MetaMonitorModeSpec *mode_spec;
672 MetaMonitorSpec *monitor_spec;
673 MetaMonitorConfig *monitor_config;
674
675 mode_spec = g_new0 (MetaMonitorModeSpec, 1);
676 *mode_spec = (MetaMonitorModeSpec) {
677 .width = mode_width,
678 .height = mode_height,
679 .refresh_rate = output_config->refresh_rate
680 };
681
682 if (!meta_verify_monitor_mode_spec (mode_spec, error))
683 {
684 g_free (mode_spec);
685 return NULL;
686 }
687
688 monitor_spec = g_new0 (MetaMonitorSpec, 1);
689 *monitor_spec = (MetaMonitorSpec) {
690 .connector = output_key->connector,
691 .vendor = output_key->vendor,
692 .product = output_key->product,
693 .serial = output_key->serial
694 };
695
696 monitor_config = g_new0 (MetaMonitorConfig, 1);
697 *monitor_config = (MetaMonitorConfig) {
698 .monitor_spec = monitor_spec,
699 .mode_spec = mode_spec,
700 .enable_underscanning = output_config->is_underscanning
701 };
702
703 if (!meta_verify_monitor_config (monitor_config, error))
704 {
705 meta_monitor_config_free (monitor_config);
706 return NULL;
707 }
708
709 return monitor_config;
710 }
711
712 typedef struct _MonitorTile
713 {
714 MetaOutputKey *output_key;
715 MetaOutputConfig *output_config;
716 } MonitorTile;
717
718 static MetaMonitorConfig *
try_derive_tiled_monitor_config(MetaLegacyMonitorsConfig * config,MetaOutputKey * output_key,MetaOutputConfig * output_config,MetaMonitorConfigStore * config_store,MetaRectangle * out_layout,GError ** error)719 try_derive_tiled_monitor_config (MetaLegacyMonitorsConfig *config,
720 MetaOutputKey *output_key,
721 MetaOutputConfig *output_config,
722 MetaMonitorConfigStore *config_store,
723 MetaRectangle *out_layout,
724 GError **error)
725 {
726 MonitorTile top_left_tile = { 0 };
727 MonitorTile top_right_tile = { 0 };
728 MonitorTile bottom_left_tile = { 0 };
729 MonitorTile bottom_right_tile = { 0 };
730 MonitorTile origin_tile = { 0 };
731 MetaMonitorTransform transform = output_config->transform;
732 unsigned int i;
733 int max_x = 0;
734 int min_x = INT_MAX;
735 int max_y = 0;
736 int min_y = INT_MAX;
737 int mode_width = 0;
738 int mode_height = 0;
739 MetaMonitorConfig *monitor_config;
740
741 /*
742 * In order to derive a monitor configuration for a tiled monitor,
743 * try to find the origin tile, then combine the discovered output
744 * tiles to given the configured transform a monitor mode.
745 *
746 * If the origin tile is not the main tile (tile always enabled
747 * even for non-tiled modes), this will fail, but since infermation
748 * about tiling is lost, there is no way to discover it.
749 */
750
751 for (i = 0; i < config->n_outputs; i++)
752 {
753 MetaOutputKey *other_output_key = &config->keys[i];
754 MetaOutputConfig *other_output_config = &config->outputs[i];
755 MetaRectangle *rect;
756
757 if (strcmp (output_key->vendor, other_output_key->vendor) != 0 ||
758 strcmp (output_key->product, other_output_key->product) != 0 ||
759 strcmp (output_key->serial, other_output_key->serial) != 0)
760 continue;
761
762 rect = &other_output_config->rect;
763 min_x = MIN (min_x, rect->x);
764 min_y = MIN (min_y, rect->y);
765 max_x = MAX (max_x, rect->x + rect->width);
766 max_y = MAX (max_y, rect->y + rect->height);
767
768 if (min_x == rect->x &&
769 min_y == rect->y)
770 {
771 top_left_tile = (MonitorTile) {
772 .output_key = other_output_key,
773 .output_config = other_output_config
774 };
775 }
776 if (max_x == rect->x + rect->width &&
777 min_y == rect->y)
778 {
779 top_right_tile = (MonitorTile) {
780 .output_key = other_output_key,
781 .output_config = other_output_config
782 };
783 }
784 if (min_x == rect->x &&
785 max_y == rect->y + rect->height)
786 {
787 bottom_left_tile = (MonitorTile) {
788 .output_key = other_output_key,
789 .output_config = other_output_config
790 };
791 }
792 if (max_x == rect->x + rect->width &&
793 max_y == rect->y + rect->height)
794 {
795 bottom_right_tile = (MonitorTile) {
796 .output_key = other_output_key,
797 .output_config = other_output_config
798 };
799 }
800 }
801
802 if (top_left_tile.output_key == bottom_right_tile.output_key)
803 {
804 g_set_error_literal (error,
805 META_MONITORS_CONFIG_MIGRATION_ERROR,
806 META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_TILED,
807 "Not a tiled monitor");
808 return NULL;
809 }
810
811 switch (transform)
812 {
813 case META_MONITOR_TRANSFORM_NORMAL:
814 origin_tile = top_left_tile;
815 mode_width = max_x - min_x;
816 mode_height = max_y - min_y;
817 break;
818 case META_MONITOR_TRANSFORM_90:
819 origin_tile = bottom_left_tile;
820 mode_width = max_y - min_y;
821 mode_height = max_x - min_x;
822 break;
823 case META_MONITOR_TRANSFORM_180:
824 origin_tile = bottom_right_tile;
825 mode_width = max_x - min_x;
826 mode_height = max_y - min_y;
827 break;
828 case META_MONITOR_TRANSFORM_270:
829 origin_tile = top_right_tile;
830 mode_width = max_y - min_y;
831 mode_height = max_x - min_x;
832 break;
833 case META_MONITOR_TRANSFORM_FLIPPED:
834 origin_tile = bottom_left_tile;
835 mode_width = max_x - min_x;
836 mode_height = max_y - min_y;
837 break;
838 case META_MONITOR_TRANSFORM_FLIPPED_90:
839 origin_tile = bottom_right_tile;
840 mode_width = max_y - min_y;
841 mode_height = max_x - min_x;
842 break;
843 case META_MONITOR_TRANSFORM_FLIPPED_180:
844 origin_tile = top_right_tile;
845 mode_width = max_x - min_x;
846 mode_height = max_y - min_y;
847 break;
848 case META_MONITOR_TRANSFORM_FLIPPED_270:
849 origin_tile = top_left_tile;
850 mode_width = max_y - min_y;
851 mode_height = max_x - min_x;
852 break;
853 }
854
855 g_assert (origin_tile.output_key);
856 g_assert (origin_tile.output_config);
857
858 if (origin_tile.output_key != output_key)
859 {
860 g_set_error_literal (error,
861 META_MONITORS_CONFIG_MIGRATION_ERROR,
862 META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_MAIN_TILE,
863 "Not the main tile");
864 return NULL;
865 }
866
867 monitor_config = create_monitor_config (origin_tile.output_key,
868 origin_tile.output_config,
869 mode_width, mode_height,
870 error);
871 if (!monitor_config)
872 return NULL;
873
874 *out_layout = (MetaRectangle) {
875 .x = min_x,
876 .y = min_y,
877 .width = max_x - min_x,
878 .height = max_y - min_y
879 };
880
881 return monitor_config;
882 }
883
884 static MetaMonitorConfig *
derive_monitor_config(MetaOutputKey * output_key,MetaOutputConfig * output_config,MetaRectangle * out_layout,GError ** error)885 derive_monitor_config (MetaOutputKey *output_key,
886 MetaOutputConfig *output_config,
887 MetaRectangle *out_layout,
888 GError **error)
889 {
890 int mode_width;
891 int mode_height;
892 MetaMonitorConfig *monitor_config;
893
894 if (meta_monitor_transform_is_rotated (output_config->transform))
895 {
896 mode_width = output_config->rect.height;
897 mode_height = output_config->rect.width;
898 }
899 else
900 {
901 mode_width = output_config->rect.width;
902 mode_height = output_config->rect.height;
903 }
904
905 monitor_config = create_monitor_config (output_key, output_config,
906 mode_width, mode_height,
907 error);
908 if (!monitor_config)
909 return NULL;
910
911 *out_layout = output_config->rect;
912
913 return monitor_config;
914 }
915
916 static MetaLogicalMonitorConfig *
ensure_logical_monitor(GList ** logical_monitor_configs,MetaOutputConfig * output_config,MetaRectangle * layout)917 ensure_logical_monitor (GList **logical_monitor_configs,
918 MetaOutputConfig *output_config,
919 MetaRectangle *layout)
920 {
921 MetaLogicalMonitorConfig *new_logical_monitor_config;
922 GList *l;
923
924 for (l = *logical_monitor_configs; l; l = l->next)
925 {
926 MetaLogicalMonitorConfig *logical_monitor_config = l->data;
927
928 if (meta_rectangle_equal (&logical_monitor_config->layout, layout))
929 return logical_monitor_config;
930 }
931
932 new_logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1);
933 *new_logical_monitor_config = (MetaLogicalMonitorConfig) {
934 .layout = *layout,
935 .is_primary = output_config->is_primary,
936 .is_presentation = output_config->is_presentation,
937 .transform = output_config->transform,
938 .scale = -1.0,
939 };
940
941 *logical_monitor_configs = g_list_append (*logical_monitor_configs,
942 new_logical_monitor_config);
943
944 return new_logical_monitor_config;
945 }
946
947 static GList *
derive_logical_monitor_configs(MetaLegacyMonitorsConfig * config,MetaMonitorConfigStore * config_store,GError ** error)948 derive_logical_monitor_configs (MetaLegacyMonitorsConfig *config,
949 MetaMonitorConfigStore *config_store,
950 GError **error)
951 {
952 GList *logical_monitor_configs = NULL;
953 unsigned int i;
954
955 for (i = 0; i < config->n_outputs; i++)
956 {
957 MetaOutputKey *output_key = &config->keys[i];
958 MetaOutputConfig *output_config = &config->outputs[i];
959 MetaMonitorConfig *monitor_config = NULL;
960 MetaRectangle layout;
961 MetaLogicalMonitorConfig *logical_monitor_config;
962
963 if (!output_config->enabled)
964 continue;
965
966 if (output_key->vendor &&
967 g_strcmp0 (output_key->vendor, "unknown") != 0 &&
968 output_key->product &&
969 g_strcmp0 (output_key->product, "unknown") != 0 &&
970 output_key->serial &&
971 g_strcmp0 (output_key->serial, "unknown") != 0)
972 {
973 monitor_config = try_derive_tiled_monitor_config (config,
974 output_key,
975 output_config,
976 config_store,
977 &layout,
978 error);
979 if (!monitor_config)
980 {
981 if ((*error)->domain == META_MONITORS_CONFIG_MIGRATION_ERROR)
982 {
983 int error_code = (*error)->code;
984
985 g_clear_error (error);
986
987 switch (error_code)
988 {
989 case META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_TILED:
990 break;
991 case META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_MAIN_TILE:
992 continue;
993 }
994 }
995 else
996 {
997 g_list_free_full (logical_monitor_configs,
998 (GDestroyNotify) meta_logical_monitor_config_free);
999 return NULL;
1000 }
1001 }
1002 }
1003
1004 if (!monitor_config)
1005 monitor_config = derive_monitor_config (output_key, output_config,
1006 &layout,
1007 error);
1008
1009 if (!monitor_config)
1010 {
1011 g_list_free_full (logical_monitor_configs,
1012 (GDestroyNotify) meta_logical_monitor_config_free);
1013 return NULL;
1014 }
1015
1016 logical_monitor_config =
1017 ensure_logical_monitor (&logical_monitor_configs,
1018 output_config, &layout);
1019
1020 logical_monitor_config->monitor_configs =
1021 g_list_append (logical_monitor_config->monitor_configs, monitor_config);
1022 }
1023
1024 if (!logical_monitor_configs)
1025 {
1026 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Empty configuration");
1027 return NULL;
1028 }
1029
1030 return logical_monitor_configs;
1031 }
1032
1033 static char *
generate_config_name(MetaLegacyMonitorsConfig * config)1034 generate_config_name (MetaLegacyMonitorsConfig *config)
1035 {
1036 char **output_strings;
1037 unsigned int i;
1038 char *key_name;
1039
1040 output_strings = g_new0 (char *, config->n_outputs + 1);
1041 for (i = 0; i < config->n_outputs; i++)
1042 {
1043 MetaOutputKey *output_key = &config->keys[i];
1044
1045 output_strings[i] = g_strdup_printf ("%s:%s:%s:%s",
1046 output_key->connector,
1047 output_key->vendor,
1048 output_key->product,
1049 output_key->serial);
1050 }
1051
1052 key_name = g_strjoinv (", ", output_strings);
1053
1054 g_strfreev (output_strings);
1055
1056 return key_name;
1057 }
1058
1059 static GList *
find_disabled_monitor_specs(MetaLegacyMonitorsConfig * legacy_config)1060 find_disabled_monitor_specs (MetaLegacyMonitorsConfig *legacy_config)
1061 {
1062 GList *disabled_monitors = NULL;
1063 unsigned int i;
1064
1065 for (i = 0; i < legacy_config->n_outputs; i++)
1066 {
1067 MetaOutputKey *output_key = &legacy_config->keys[i];
1068 MetaOutputConfig *output_config = &legacy_config->outputs[i];
1069 MetaMonitorSpec *monitor_spec;
1070
1071 if (output_config->enabled)
1072 continue;
1073
1074 monitor_spec = g_new0 (MetaMonitorSpec, 1);
1075 *monitor_spec = (MetaMonitorSpec) {
1076 .connector = output_key->connector,
1077 .vendor = output_key->vendor,
1078 .product = output_key->product,
1079 .serial = output_key->serial
1080 };
1081
1082 disabled_monitors = g_list_prepend (disabled_monitors, monitor_spec);
1083 }
1084
1085 return disabled_monitors;
1086 }
1087
1088 static void
migrate_config(gpointer key,gpointer value,gpointer user_data)1089 migrate_config (gpointer key,
1090 gpointer value,
1091 gpointer user_data)
1092 {
1093 MetaLegacyMonitorsConfig *legacy_config = key;
1094 MetaMonitorConfigStore *config_store = user_data;
1095 MetaMonitorManager *monitor_manager =
1096 meta_monitor_config_store_get_monitor_manager (config_store);
1097 GList *logical_monitor_configs;
1098 MetaLogicalMonitorLayoutMode layout_mode;
1099 GError *error = NULL;
1100 GList *disabled_monitor_specs;
1101 MetaMonitorsConfig *config;
1102
1103 logical_monitor_configs = derive_logical_monitor_configs (legacy_config,
1104 config_store,
1105 &error);
1106 if (!logical_monitor_configs)
1107 {
1108 g_autofree char *config_name = NULL;
1109
1110 config_name = generate_config_name (legacy_config);
1111 g_warning ("Failed to migrate monitor configuration for %s: %s",
1112 config_name, error->message);
1113 return;
1114 }
1115
1116 disabled_monitor_specs = find_disabled_monitor_specs (legacy_config);
1117
1118 layout_mode = META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL;
1119 config = meta_monitors_config_new_full (logical_monitor_configs,
1120 disabled_monitor_specs,
1121 layout_mode,
1122 META_MONITORS_CONFIG_FLAG_MIGRATED);
1123 if (!meta_verify_monitors_config (config, monitor_manager, &error))
1124 {
1125 g_autofree char *config_name = NULL;
1126
1127 config_name = generate_config_name (legacy_config);
1128 g_warning ("Ignoring invalid monitor configuration for %s: %s",
1129 config_name, error->message);
1130 g_object_unref (config);
1131 return;
1132 }
1133
1134 meta_monitor_config_store_add (config_store, config);
1135 }
1136
1137 gboolean
meta_migrate_old_monitors_config(MetaMonitorConfigStore * config_store,GFile * in_file,GError ** error)1138 meta_migrate_old_monitors_config (MetaMonitorConfigStore *config_store,
1139 GFile *in_file,
1140 GError **error)
1141 {
1142 g_autoptr (GHashTable) configs = NULL;
1143
1144 configs = load_config_file (in_file, error);
1145 if (!configs)
1146 return FALSE;
1147
1148 g_hash_table_foreach (configs, migrate_config, config_store);
1149
1150 return TRUE;
1151 }
1152
1153 gboolean
meta_migrate_old_user_monitors_config(MetaMonitorConfigStore * config_store,GError ** error)1154 meta_migrate_old_user_monitors_config (MetaMonitorConfigStore *config_store,
1155 GError **error)
1156 {
1157 g_autofree char *backup_path = NULL;
1158 g_autoptr (GFile) backup_file = NULL;
1159 g_autofree char *user_file_path = NULL;
1160 g_autoptr (GFile) user_file = NULL;
1161
1162 user_file_path = g_build_filename (g_get_user_config_dir (),
1163 "monitors.xml",
1164 NULL);
1165 user_file = g_file_new_for_path (user_file_path);
1166 backup_path = g_build_filename (g_get_user_config_dir (),
1167 "monitors-v1-backup.xml",
1168 NULL);
1169 backup_file = g_file_new_for_path (backup_path);
1170
1171 if (!g_file_copy (user_file, backup_file,
1172 G_FILE_COPY_OVERWRITE | G_FILE_COPY_BACKUP,
1173 NULL, NULL, NULL,
1174 error))
1175 {
1176 g_warning ("Failed to make a backup of monitors.xml: %s",
1177 (*error)->message);
1178 g_clear_error (error);
1179 }
1180
1181 return meta_migrate_old_monitors_config (config_store, user_file, error);
1182 }
1183
1184 gboolean
meta_finish_monitors_config_migration(MetaMonitorManager * monitor_manager,MetaMonitorsConfig * config,GError ** error)1185 meta_finish_monitors_config_migration (MetaMonitorManager *monitor_manager,
1186 MetaMonitorsConfig *config,
1187 GError **error)
1188 {
1189 MetaMonitorConfigManager *config_manager = monitor_manager->config_manager;
1190 MetaMonitorConfigStore *config_store =
1191 meta_monitor_config_manager_get_store (config_manager);
1192 GList *l;
1193 MetaLogicalMonitorLayoutMode layout_mode;
1194
1195 layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager);
1196
1197 for (l = config->logical_monitor_configs; l; l = l->next)
1198 {
1199 MetaLogicalMonitorConfig *logical_monitor_config = l->data;
1200 MetaMonitorConfig *monitor_config;
1201 MetaMonitorSpec *monitor_spec;
1202 MetaMonitor *monitor;
1203 MetaMonitorModeSpec *monitor_mode_spec;
1204 MetaMonitorMode *monitor_mode;
1205
1206 monitor_config = logical_monitor_config->monitor_configs->data;
1207 monitor_spec = monitor_config->monitor_spec;
1208 monitor = meta_monitor_manager_get_monitor_from_spec (monitor_manager,
1209 monitor_spec);
1210 monitor_mode_spec = monitor_config->mode_spec;
1211 monitor_mode = meta_monitor_get_mode_from_spec (monitor,
1212 monitor_mode_spec);
1213 if (!monitor_mode)
1214 {
1215 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1216 "Mode not available on monitor");
1217 return FALSE;
1218 }
1219
1220 logical_monitor_config->scale =
1221 meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager,
1222 layout_mode,
1223 monitor,
1224 monitor_mode);
1225 }
1226
1227 config->layout_mode = layout_mode;
1228 config->flags &= ~META_MONITORS_CONFIG_FLAG_MIGRATED;
1229
1230 if (!meta_verify_monitors_config (config, monitor_manager, error))
1231 return FALSE;
1232
1233 meta_monitor_config_store_add (config_store, config);
1234
1235 return TRUE;
1236 }
1237