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