1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /*
4  * Copyright (C) 2016 Red Hat
5  * Copyright (c) 2018 DisplayLink (UK) Ltd.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20  * 02111-1307, USA.
21  */
22 
23 #include "config.h"
24 
25 #include "backends/meta-monitor-config-manager.h"
26 
27 #include "backends/meta-backend-private.h"
28 #include "backends/meta-monitor-config-migration.h"
29 #include "backends/meta-monitor-config-store.h"
30 #include "backends/meta-monitor-manager-private.h"
31 #include "backends/meta-output.h"
32 #include "core/boxes-private.h"
33 #include "meta/meta-monitor-manager.h"
34 
35 #define CONFIG_HISTORY_MAX_SIZE 3
36 
37 struct _MetaMonitorConfigManager
38 {
39   GObject parent;
40 
41   MetaMonitorManager *monitor_manager;
42 
43   MetaMonitorConfigStore *config_store;
44 
45   MetaMonitorsConfig *current_config;
46   GQueue config_history;
47 };
48 
49 G_DEFINE_TYPE (MetaMonitorConfigManager, meta_monitor_config_manager,
50                G_TYPE_OBJECT)
51 
52 G_DEFINE_TYPE (MetaMonitorsConfig, meta_monitors_config,
53                G_TYPE_OBJECT)
54 
55 static void
56 meta_crtc_assignment_free (MetaCrtcAssignment *assignment);
57 
58 static void
59 meta_output_assignment_free (MetaOutputAssignment *assignment);
60 
61 MetaMonitorConfigManager *
meta_monitor_config_manager_new(MetaMonitorManager * monitor_manager)62 meta_monitor_config_manager_new (MetaMonitorManager *monitor_manager)
63 {
64   MetaMonitorConfigManager *config_manager;
65 
66   config_manager = g_object_new (META_TYPE_MONITOR_CONFIG_MANAGER, NULL);
67   config_manager->monitor_manager = monitor_manager;
68   config_manager->config_store =
69     meta_monitor_config_store_new (monitor_manager);
70 
71   return config_manager;
72 }
73 
74 MetaMonitorConfigStore *
meta_monitor_config_manager_get_store(MetaMonitorConfigManager * config_manager)75 meta_monitor_config_manager_get_store (MetaMonitorConfigManager *config_manager)
76 {
77   return config_manager->config_store;
78 }
79 
80 static gboolean
is_crtc_reserved(MetaCrtc * crtc,GArray * reserved_crtcs)81 is_crtc_reserved (MetaCrtc *crtc,
82                   GArray   *reserved_crtcs)
83 {
84   unsigned int i;
85 
86   for (i = 0; i < reserved_crtcs->len; i++)
87     {
88        uint64_t id;
89 
90        id = g_array_index (reserved_crtcs, uint64_t, i);
91        if (id == meta_crtc_get_id (crtc))
92          return TRUE;
93     }
94 
95   return FALSE;
96 }
97 
98 static gboolean
is_crtc_assigned(MetaCrtc * crtc,GPtrArray * crtc_assignments)99 is_crtc_assigned (MetaCrtc  *crtc,
100                   GPtrArray *crtc_assignments)
101 {
102   unsigned int i;
103 
104   for (i = 0; i < crtc_assignments->len; i++)
105     {
106       MetaCrtcAssignment *assigned_crtc_assignment =
107         g_ptr_array_index (crtc_assignments, i);
108 
109       if (assigned_crtc_assignment->crtc == crtc)
110         return TRUE;
111     }
112 
113   return FALSE;
114 }
115 
116 static MetaCrtc *
find_unassigned_crtc(MetaOutput * output,GPtrArray * crtc_assignments,GArray * reserved_crtcs)117 find_unassigned_crtc (MetaOutput *output,
118                       GPtrArray  *crtc_assignments,
119                       GArray     *reserved_crtcs)
120 {
121   MetaCrtc *crtc;
122   const MetaOutputInfo *output_info;
123   unsigned int i;
124 
125   crtc = meta_output_get_assigned_crtc (output);
126   if (crtc && !is_crtc_assigned (crtc, crtc_assignments))
127     return crtc;
128 
129   output_info = meta_output_get_info (output);
130 
131   /* then try to assign a CRTC that wasn't used */
132   for (i = 0; i < output_info->n_possible_crtcs; i++)
133     {
134       crtc = output_info->possible_crtcs[i];
135 
136       if (is_crtc_assigned (crtc, crtc_assignments))
137         continue;
138 
139       if (is_crtc_reserved (crtc, reserved_crtcs))
140         continue;
141 
142       return crtc;
143     }
144 
145   /* finally just give a CRTC that we haven't assigned */
146   for (i = 0; i < output_info->n_possible_crtcs; i++)
147     {
148       crtc = output_info->possible_crtcs[i];
149 
150       if (is_crtc_assigned (crtc, crtc_assignments))
151         continue;
152 
153       return crtc;
154     }
155 
156   return NULL;
157 }
158 
159 typedef struct
160 {
161   MetaMonitorManager *monitor_manager;
162   MetaMonitorsConfig *config;
163   MetaLogicalMonitorConfig *logical_monitor_config;
164   MetaMonitorConfig *monitor_config;
165   GPtrArray *crtc_assignments;
166   GPtrArray *output_assignments;
167   GArray *reserved_crtcs;
168 } MonitorAssignmentData;
169 
170 static gboolean
assign_monitor_crtc(MetaMonitor * monitor,MetaMonitorMode * mode,MetaMonitorCrtcMode * monitor_crtc_mode,gpointer user_data,GError ** error)171 assign_monitor_crtc (MetaMonitor         *monitor,
172                      MetaMonitorMode     *mode,
173                      MetaMonitorCrtcMode *monitor_crtc_mode,
174                      gpointer             user_data,
175                      GError             **error)
176 {
177   MonitorAssignmentData *data = user_data;
178   MetaOutput *output;
179   MetaCrtc *crtc;
180   MetaMonitorTransform transform;
181   MetaMonitorTransform crtc_transform;
182   MetaMonitorTransform crtc_hw_transform;
183   int crtc_x, crtc_y;
184   float x_offset, y_offset;
185   float scale = 0.0;
186   float width, height;
187   MetaCrtcMode *crtc_mode;
188   const MetaCrtcModeInfo *crtc_mode_info;
189   graphene_rect_t crtc_layout;
190   MetaCrtcAssignment *crtc_assignment;
191   MetaOutputAssignment *output_assignment;
192   MetaMonitorConfig *first_monitor_config;
193   gboolean assign_output_as_primary;
194   gboolean assign_output_as_presentation;
195 
196   output = monitor_crtc_mode->output;
197 
198   crtc = find_unassigned_crtc (output,
199                                data->crtc_assignments,
200                                data->reserved_crtcs);
201 
202   if (!crtc)
203     {
204       MetaMonitorSpec *monitor_spec = meta_monitor_get_spec (monitor);
205 
206       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
207                    "No available CRTC for monitor '%s %s' not found",
208                    monitor_spec->vendor, monitor_spec->product);
209       return FALSE;
210     }
211 
212   transform = data->logical_monitor_config->transform;
213   crtc_transform = meta_monitor_logical_to_crtc_transform (monitor, transform);
214   if (meta_monitor_manager_is_transform_handled (data->monitor_manager,
215                                                  crtc,
216                                                  crtc_transform))
217     crtc_hw_transform = crtc_transform;
218   else
219     crtc_hw_transform = META_MONITOR_TRANSFORM_NORMAL;
220 
221   meta_monitor_calculate_crtc_pos (monitor, mode, output, crtc_transform,
222                                    &crtc_x, &crtc_y);
223 
224   x_offset = data->logical_monitor_config->layout.x;
225   y_offset = data->logical_monitor_config->layout.y;
226 
227   switch (data->config->layout_mode)
228     {
229     case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL:
230       scale = data->logical_monitor_config->scale;
231       break;
232     case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL:
233       scale = 1.0;
234       break;
235     }
236 
237   crtc_mode = monitor_crtc_mode->crtc_mode;
238   crtc_mode_info = meta_crtc_mode_get_info (monitor_crtc_mode->crtc_mode);
239 
240   if (meta_monitor_transform_is_rotated (crtc_transform))
241     {
242       width = crtc_mode_info->height / scale;
243       height = crtc_mode_info->width / scale;
244     }
245   else
246     {
247       width = crtc_mode_info->width / scale;
248       height = crtc_mode_info->height / scale;
249     }
250 
251   crtc_layout = GRAPHENE_RECT_INIT (x_offset + (crtc_x / scale),
252                                     y_offset + (crtc_y / scale),
253                                     width,
254                                     height);
255 
256   crtc_assignment = g_new0 (MetaCrtcAssignment, 1);
257   *crtc_assignment = (MetaCrtcAssignment) {
258     .crtc = crtc,
259     .mode = crtc_mode,
260     .layout = crtc_layout,
261     .transform = crtc_hw_transform,
262     .outputs = g_ptr_array_new ()
263   };
264   g_ptr_array_add (crtc_assignment->outputs, output);
265 
266   /*
267    * Only one output can be marked as primary (due to Xrandr limitation),
268    * so only mark the main output of the first monitor in the logical monitor
269    * as such.
270    */
271   first_monitor_config = data->logical_monitor_config->monitor_configs->data;
272   if (data->logical_monitor_config->is_primary &&
273       data->monitor_config == first_monitor_config &&
274       meta_monitor_get_main_output (monitor) == output)
275     assign_output_as_primary = TRUE;
276   else
277     assign_output_as_primary = FALSE;
278 
279   if (data->logical_monitor_config->is_presentation)
280     assign_output_as_presentation = TRUE;
281   else
282     assign_output_as_presentation = FALSE;
283 
284   output_assignment = g_new0 (MetaOutputAssignment, 1);
285   *output_assignment = (MetaOutputAssignment) {
286     .output = output,
287     .is_primary = assign_output_as_primary,
288     .is_presentation = assign_output_as_presentation,
289     .is_underscanning = data->monitor_config->enable_underscanning
290   };
291 
292   g_ptr_array_add (data->crtc_assignments, crtc_assignment);
293   g_ptr_array_add (data->output_assignments, output_assignment);
294 
295   return TRUE;
296 }
297 
298 static gboolean
assign_monitor_crtcs(MetaMonitorManager * manager,MetaMonitorsConfig * config,MetaLogicalMonitorConfig * logical_monitor_config,MetaMonitorConfig * monitor_config,GPtrArray * crtc_assignments,GPtrArray * output_assignments,GArray * reserved_crtcs,GError ** error)299 assign_monitor_crtcs (MetaMonitorManager       *manager,
300                       MetaMonitorsConfig       *config,
301                       MetaLogicalMonitorConfig *logical_monitor_config,
302                       MetaMonitorConfig        *monitor_config,
303                       GPtrArray                *crtc_assignments,
304                       GPtrArray                *output_assignments,
305                       GArray                   *reserved_crtcs,
306                       GError                  **error)
307 {
308   MetaMonitorSpec *monitor_spec = monitor_config->monitor_spec;
309   MetaMonitorModeSpec *monitor_mode_spec = monitor_config->mode_spec;
310   MetaMonitor *monitor;
311   MetaMonitorMode *monitor_mode;
312   MonitorAssignmentData data;
313 
314   monitor = meta_monitor_manager_get_monitor_from_spec (manager, monitor_spec);
315   if (!monitor)
316     {
317       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
318                    "Configured monitor '%s %s' not found",
319                    monitor_spec->vendor, monitor_spec->product);
320       return FALSE;
321     }
322 
323   monitor_mode = meta_monitor_get_mode_from_spec (monitor, monitor_mode_spec);
324   if (!monitor_mode)
325     {
326       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
327                    "Invalid mode %dx%d (%f) for monitor '%s %s'",
328                    monitor_mode_spec->width, monitor_mode_spec->height,
329                    monitor_mode_spec->refresh_rate,
330                    monitor_spec->vendor, monitor_spec->product);
331       return FALSE;
332     }
333 
334   data = (MonitorAssignmentData) {
335     .monitor_manager = manager,
336     .config = config,
337     .logical_monitor_config = logical_monitor_config,
338     .monitor_config = monitor_config,
339     .crtc_assignments = crtc_assignments,
340     .output_assignments = output_assignments,
341     .reserved_crtcs = reserved_crtcs
342   };
343   if (!meta_monitor_mode_foreach_crtc (monitor, monitor_mode,
344                                        assign_monitor_crtc,
345                                        &data,
346                                        error))
347     return FALSE;
348 
349   return TRUE;
350 }
351 
352 static gboolean
assign_logical_monitor_crtcs(MetaMonitorManager * manager,MetaMonitorsConfig * config,MetaLogicalMonitorConfig * logical_monitor_config,GPtrArray * crtc_assignments,GPtrArray * output_assignments,GArray * reserved_crtcs,GError ** error)353 assign_logical_monitor_crtcs (MetaMonitorManager       *manager,
354                               MetaMonitorsConfig       *config,
355                               MetaLogicalMonitorConfig *logical_monitor_config,
356                               GPtrArray                *crtc_assignments,
357                               GPtrArray                *output_assignments,
358                               GArray                   *reserved_crtcs,
359                               GError                  **error)
360 {
361   GList *l;
362 
363   for (l = logical_monitor_config->monitor_configs; l; l = l->next)
364     {
365       MetaMonitorConfig *monitor_config = l->data;
366 
367       if (!assign_monitor_crtcs (manager,
368                                  config,
369                                  logical_monitor_config,
370                                  monitor_config,
371                                  crtc_assignments, output_assignments,
372                                  reserved_crtcs, error))
373         return FALSE;
374     }
375 
376   return TRUE;
377 }
378 
379 gboolean
meta_monitor_config_manager_assign(MetaMonitorManager * manager,MetaMonitorsConfig * config,GPtrArray ** out_crtc_assignments,GPtrArray ** out_output_assignments,GError ** error)380 meta_monitor_config_manager_assign (MetaMonitorManager *manager,
381                                     MetaMonitorsConfig *config,
382                                     GPtrArray         **out_crtc_assignments,
383                                     GPtrArray         **out_output_assignments,
384                                     GError            **error)
385 {
386   GPtrArray *crtc_assignments;
387   GPtrArray *output_assignments;
388   GArray *reserved_crtcs;
389   GList *l;
390 
391   crtc_assignments =
392     g_ptr_array_new_with_free_func ((GDestroyNotify) meta_crtc_assignment_free);
393   output_assignments =
394     g_ptr_array_new_with_free_func ((GDestroyNotify) meta_output_assignment_free);
395   reserved_crtcs = g_array_new (FALSE, FALSE, sizeof (uint64_t));
396 
397   for (l = config->logical_monitor_configs; l; l = l->next)
398     {
399       MetaLogicalMonitorConfig *logical_monitor_config = l->data;
400       GList *k;
401 
402       for (k = logical_monitor_config->monitor_configs; k; k = k->next)
403         {
404           MetaMonitorConfig *monitor_config = k->data;
405           MetaMonitorSpec *monitor_spec = monitor_config->monitor_spec;
406           MetaMonitor *monitor;
407           GList *o;
408 
409           monitor = meta_monitor_manager_get_monitor_from_spec (manager, monitor_spec);
410 
411           for (o = meta_monitor_get_outputs (monitor); o; o = o->next)
412             {
413               MetaOutput *output = o->data;
414               MetaCrtc *crtc;
415 
416               crtc = meta_output_get_assigned_crtc (output);
417               if (crtc)
418                 {
419                   uint64_t crtc_id = meta_crtc_get_id (crtc);
420 
421                   g_array_append_val (reserved_crtcs, crtc_id);
422                 }
423             }
424         }
425     }
426 
427   for (l = config->logical_monitor_configs; l; l = l->next)
428     {
429       MetaLogicalMonitorConfig *logical_monitor_config = l->data;
430 
431       if (!assign_logical_monitor_crtcs (manager,
432                                          config, logical_monitor_config,
433                                          crtc_assignments, output_assignments,
434                                          reserved_crtcs, error))
435         {
436           g_ptr_array_free (crtc_assignments, TRUE);
437           g_ptr_array_free (output_assignments, TRUE);
438           g_array_free (reserved_crtcs, TRUE);
439           return FALSE;
440         }
441     }
442 
443   g_array_free (reserved_crtcs, TRUE);
444 
445   *out_crtc_assignments = crtc_assignments;
446   *out_output_assignments = output_assignments;
447 
448   return TRUE;
449 }
450 
451 static gboolean
is_lid_closed(MetaMonitorManager * monitor_manager)452 is_lid_closed (MetaMonitorManager *monitor_manager)
453 {
454     MetaBackend *backend;
455 
456     backend = meta_monitor_manager_get_backend (monitor_manager);
457     return meta_backend_is_lid_closed (backend);
458 }
459 
460 MetaMonitorsConfigKey *
meta_create_monitors_config_key_for_current_state(MetaMonitorManager * monitor_manager)461 meta_create_monitors_config_key_for_current_state (MetaMonitorManager *monitor_manager)
462 {
463   MetaMonitorsConfigKey *config_key;
464   MetaMonitorSpec *laptop_monitor_spec;
465   GList *l;
466   GList *monitor_specs;
467 
468   laptop_monitor_spec = NULL;
469   monitor_specs = NULL;
470   for (l = monitor_manager->monitors; l; l = l->next)
471     {
472       MetaMonitor *monitor = l->data;
473       MetaMonitorSpec *monitor_spec;
474 
475       if (meta_monitor_is_laptop_panel (monitor))
476         {
477           laptop_monitor_spec = meta_monitor_get_spec (monitor);
478 
479           if (is_lid_closed (monitor_manager))
480             continue;
481         }
482 
483       monitor_spec = meta_monitor_spec_clone (meta_monitor_get_spec (monitor));
484       monitor_specs = g_list_prepend (monitor_specs, monitor_spec);
485     }
486 
487   if (!monitor_specs && laptop_monitor_spec)
488     {
489       monitor_specs =
490         g_list_prepend (NULL, meta_monitor_spec_clone (laptop_monitor_spec));
491     }
492 
493   if (!monitor_specs)
494     return NULL;
495 
496   monitor_specs = g_list_sort (monitor_specs,
497                                (GCompareFunc) meta_monitor_spec_compare);
498 
499   config_key = g_new0 (MetaMonitorsConfigKey, 1);
500   *config_key = (MetaMonitorsConfigKey) {
501     .monitor_specs = monitor_specs
502   };
503 
504   return config_key;
505 }
506 
507 MetaMonitorsConfig *
meta_monitor_config_manager_get_stored(MetaMonitorConfigManager * config_manager)508 meta_monitor_config_manager_get_stored (MetaMonitorConfigManager *config_manager)
509 {
510   MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
511   MetaMonitorsConfigKey *config_key;
512   MetaMonitorsConfig *config;
513   GError *error = NULL;
514 
515   config_key =
516     meta_create_monitors_config_key_for_current_state (monitor_manager);
517   if (!config_key)
518     return NULL;
519 
520   config = meta_monitor_config_store_lookup (config_manager->config_store,
521                                              config_key);
522   meta_monitors_config_key_free (config_key);
523 
524   if (!config)
525     return NULL;
526 
527   if (config->flags & META_MONITORS_CONFIG_FLAG_MIGRATED)
528     {
529       if (!meta_finish_monitors_config_migration (monitor_manager, config,
530                                                   &error))
531         {
532           g_warning ("Failed to finish monitors config migration: %s",
533                      error->message);
534           g_error_free (error);
535           meta_monitor_config_store_remove (config_manager->config_store, config);
536           return NULL;
537         }
538     }
539 
540   return config;
541 }
542 
543 typedef enum _MonitorMatchRule
544 {
545   MONITOR_MATCH_ALL = 0,
546   MONITOR_MATCH_EXTERNAL = (1 << 0),
547   MONITOR_MATCH_BUILTIN = (1 << 1),
548   MONITOR_MATCH_VISIBLE = (1 << 2),
549   MONITOR_MATCH_WITH_SUGGESTED_POSITION = (1 << 3),
550   MONITOR_MATCH_PRIMARY = (1 << 4),
551   MONITOR_MATCH_ALLOW_FALLBACK = (1 << 5),
552 } MonitorMatchRule;
553 
554 static gboolean
monitor_matches_rule(MetaMonitor * monitor,MetaMonitorManager * monitor_manager,MonitorMatchRule match_rule)555 monitor_matches_rule (MetaMonitor        *monitor,
556                       MetaMonitorManager *monitor_manager,
557                       MonitorMatchRule    match_rule)
558 {
559   if (!monitor)
560     return FALSE;
561 
562   if (match_rule & MONITOR_MATCH_BUILTIN)
563     {
564       if (!meta_monitor_is_laptop_panel (monitor))
565         return FALSE;
566     }
567   else if (match_rule & MONITOR_MATCH_EXTERNAL)
568     {
569       if (meta_monitor_is_laptop_panel (monitor))
570         return FALSE;
571     }
572 
573   if (match_rule & MONITOR_MATCH_VISIBLE)
574     {
575       if (meta_monitor_is_laptop_panel (monitor) &&
576           is_lid_closed (monitor_manager))
577         return FALSE;
578     }
579 
580   if (match_rule & MONITOR_MATCH_WITH_SUGGESTED_POSITION)
581     {
582       if (!meta_monitor_get_suggested_position (monitor, NULL, NULL))
583         return FALSE;
584     }
585 
586   return TRUE;
587 }
588 
589 static GList *
find_monitors(MetaMonitorManager * monitor_manager,MonitorMatchRule match_rule,MetaMonitor * not_this_one)590 find_monitors (MetaMonitorManager *monitor_manager,
591                MonitorMatchRule    match_rule,
592                MetaMonitor        *not_this_one)
593 {
594   GList *result = NULL;
595   GList *monitors, *l;
596 
597   monitors = meta_monitor_manager_get_monitors (monitor_manager);
598   for (l = g_list_last (monitors); l; l = l->prev)
599     {
600       MetaMonitor *monitor = l->data;
601 
602       if (not_this_one && monitor == not_this_one)
603         continue;
604 
605       if (monitor_matches_rule (monitor, monitor_manager, match_rule))
606         result = g_list_prepend (result, monitor);
607     }
608 
609   return result;
610 }
611 
612 static MetaMonitor *
find_monitor_with_highest_preferred_resolution(MetaMonitorManager * monitor_manager,MonitorMatchRule match_rule)613 find_monitor_with_highest_preferred_resolution (MetaMonitorManager *monitor_manager,
614                                                 MonitorMatchRule    match_rule)
615 {
616   g_autoptr (GList) monitors = NULL;
617   GList *l;
618   int largest_area = 0;
619   MetaMonitor *largest_monitor = NULL;
620 
621   monitors = find_monitors (monitor_manager, match_rule, NULL);
622   for (l = monitors; l; l = l->next)
623     {
624       MetaMonitor *monitor = l->data;
625       MetaMonitorMode *mode;
626       int width, height;
627       int area;
628 
629       mode = meta_monitor_get_preferred_mode (monitor);
630       meta_monitor_mode_get_resolution (mode, &width, &height);
631       area = width * height;
632 
633       if (area > largest_area)
634         {
635           largest_area = area;
636           largest_monitor = monitor;
637         }
638     }
639 
640   return largest_monitor;
641 }
642 
643 /*
644  * Try to find the primary monitor. The priority of classification is:
645  *
646  * 1. Find the primary monitor as reported by the underlying system,
647  * 2. Find the laptop panel
648  * 3. Find the external monitor with highest resolution
649  *
650  * If the laptop lid is closed, exclude the laptop panel from possible
651  * alternatives, except if no other alternatives exist.
652  */
653 static MetaMonitor *
find_primary_monitor(MetaMonitorManager * monitor_manager,MonitorMatchRule match_rule)654 find_primary_monitor (MetaMonitorManager *monitor_manager,
655                       MonitorMatchRule    match_rule)
656 {
657   MetaMonitor *monitor;
658 
659   monitor = meta_monitor_manager_get_primary_monitor (monitor_manager);
660   if (monitor_matches_rule (monitor, monitor_manager, match_rule))
661     return monitor;
662 
663   monitor = meta_monitor_manager_get_laptop_panel (monitor_manager);
664   if (monitor_matches_rule (monitor, monitor_manager, match_rule))
665     return monitor;
666 
667   monitor = find_monitor_with_highest_preferred_resolution (monitor_manager,
668                                                             match_rule);
669   if (monitor)
670     return monitor;
671 
672   if (match_rule & MONITOR_MATCH_ALLOW_FALLBACK)
673     return find_monitor_with_highest_preferred_resolution (monitor_manager,
674                                                            MONITOR_MATCH_ALL);
675 
676   return NULL;
677 }
678 
679 static MetaMonitorConfig *
create_monitor_config(MetaMonitor * monitor,MetaMonitorMode * mode)680 create_monitor_config (MetaMonitor     *monitor,
681                        MetaMonitorMode *mode)
682 {
683   MetaMonitorSpec *monitor_spec;
684   MetaMonitorModeSpec *mode_spec;
685   MetaMonitorConfig *monitor_config;
686 
687   monitor_spec = meta_monitor_get_spec (monitor);
688   mode_spec = meta_monitor_mode_get_spec (mode);
689 
690   monitor_config = g_new0 (MetaMonitorConfig, 1);
691   *monitor_config = (MetaMonitorConfig) {
692     .monitor_spec = meta_monitor_spec_clone (monitor_spec),
693     .mode_spec = g_memdup2 (mode_spec, sizeof (MetaMonitorModeSpec)),
694     .enable_underscanning = meta_monitor_is_underscanning (monitor)
695   };
696 
697   return monitor_config;
698 }
699 
700 static MetaMonitorTransform
get_monitor_transform(MetaMonitorManager * monitor_manager,MetaMonitor * monitor)701 get_monitor_transform (MetaMonitorManager *monitor_manager,
702                        MetaMonitor        *monitor)
703 {
704   MetaOrientationManager *orientation_manager;
705   MetaOrientation orientation;
706   MetaBackend *backend;
707 
708   if (!meta_monitor_is_laptop_panel (monitor) ||
709       !meta_monitor_manager_get_panel_orientation_managed (monitor_manager))
710     return META_MONITOR_TRANSFORM_NORMAL;
711 
712   backend = meta_monitor_manager_get_backend (monitor_manager);
713   orientation_manager = meta_backend_get_orientation_manager (backend);
714   orientation = meta_orientation_manager_get_orientation (orientation_manager);
715 
716   return meta_monitor_transform_from_orientation (orientation);
717 }
718 
719 static MetaLogicalMonitorConfig *
create_preferred_logical_monitor_config(MetaMonitorManager * monitor_manager,MetaMonitor * monitor,int x,int y,float scale,MetaLogicalMonitorLayoutMode layout_mode)720 create_preferred_logical_monitor_config (MetaMonitorManager          *monitor_manager,
721                                          MetaMonitor                 *monitor,
722                                          int                          x,
723                                          int                          y,
724                                          float                        scale,
725                                          MetaLogicalMonitorLayoutMode layout_mode)
726 {
727   MetaMonitorMode *mode;
728   int width, height;
729   MetaMonitorTransform transform;
730   MetaMonitorConfig *monitor_config;
731   MetaLogicalMonitorConfig *logical_monitor_config;
732 
733   mode = meta_monitor_get_preferred_mode (monitor);
734   meta_monitor_mode_get_resolution (mode, &width, &height);
735 
736   switch (layout_mode)
737     {
738     case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL:
739       width = (int) roundf (width / scale);
740       height = (int) roundf (height / scale);
741       break;
742     case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL:
743       break;
744     }
745 
746   monitor_config = create_monitor_config (monitor, mode);
747 
748   transform = get_monitor_transform (monitor_manager, monitor);
749   if (meta_monitor_transform_is_rotated (transform))
750     {
751       int temp = width;
752       width = height;
753       height = temp;
754     }
755 
756   logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1);
757   *logical_monitor_config = (MetaLogicalMonitorConfig) {
758     .layout = (MetaRectangle) {
759       .x = x,
760       .y = y,
761       .width = width,
762       .height = height
763     },
764     .transform = transform,
765     .scale = scale,
766     .monitor_configs = g_list_append (NULL, monitor_config)
767   };
768 
769   return logical_monitor_config;
770 }
771 
772 static float
compute_scale_for_monitor(MetaMonitorManager * monitor_manager,MetaMonitor * monitor,MetaMonitor * primary_monitor)773 compute_scale_for_monitor (MetaMonitorManager *monitor_manager,
774                            MetaMonitor        *monitor,
775                            MetaMonitor        *primary_monitor)
776 {
777   MetaMonitor *target_monitor = monitor;
778   MetaLogicalMonitorLayoutMode layout_mode;
779   MetaMonitorMode *monitor_mode;
780 
781   if ((meta_monitor_manager_get_capabilities (monitor_manager) &
782        META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED) &&
783       primary_monitor)
784     target_monitor = primary_monitor;
785 
786   layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager);
787   monitor_mode = meta_monitor_get_preferred_mode (target_monitor);
788 
789   return meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager,
790                                                             layout_mode,
791                                                             target_monitor,
792                                                             monitor_mode);
793 }
794 
795 typedef enum _MonitorPositioningMode
796 {
797   MONITOR_POSITIONING_LINEAR,
798   MONITOR_POSITIONING_SUGGESTED,
799 } MonitorPositioningMode;
800 
801 static gboolean
verify_suggested_monitors_config(GList * logical_monitor_configs)802 verify_suggested_monitors_config (GList *logical_monitor_configs)
803 {
804   g_autoptr (GList) region = NULL;
805   GList *l;
806 
807   for (l = logical_monitor_configs; l; l = l->next)
808     {
809       MetaLogicalMonitorConfig *logical_monitor_config = l->data;
810       MetaRectangle *rect = &logical_monitor_config->layout;
811 
812       if (meta_rectangle_overlaps_with_region (region, rect))
813         {
814           g_warning ("Suggested monitor config has overlapping region, "
815                       "rejecting");
816           return FALSE;
817         }
818 
819       region = g_list_prepend (region, rect);
820     }
821 
822   for (l = region; region->next && l; l = l->next)
823     {
824       MetaRectangle *rect = l->data;
825 
826       if (!meta_rectangle_is_adjacent_to_any_in_region (region, rect))
827         {
828           g_warning ("Suggested monitor config has monitors with no "
829                       "neighbors, rejecting");
830           return FALSE;
831         }
832     }
833 
834   return TRUE;
835 }
836 
837 static MetaMonitorsConfig *
create_monitors_config(MetaMonitorConfigManager * config_manager,MonitorMatchRule match_rule,MonitorPositioningMode positioning,MetaMonitorsConfigFlag config_flags)838 create_monitors_config (MetaMonitorConfigManager *config_manager,
839                         MonitorMatchRule          match_rule,
840                         MonitorPositioningMode    positioning,
841                         MetaMonitorsConfigFlag    config_flags)
842 {
843   MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
844   g_autoptr (GList) monitors = NULL;
845   g_autolist (MetaLogicalMonitorConfig) logical_monitor_configs = NULL;
846   MetaMonitor *primary_monitor;
847   MetaLogicalMonitorLayoutMode layout_mode;
848   float scale;
849   GList *l;
850   int x, y;
851 
852   primary_monitor = find_primary_monitor (monitor_manager,
853                                           match_rule | MONITOR_MATCH_VISIBLE);
854   if (!primary_monitor)
855     return NULL;
856 
857   x = y = 0;
858   layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager);
859 
860   if (!(match_rule & MONITOR_MATCH_PRIMARY))
861     monitors = find_monitors (monitor_manager, match_rule, primary_monitor);
862 
863   /* The primary monitor needs to be at the head of the list for the
864    * linear positioning to be correct.
865    */
866   monitors = g_list_prepend (monitors, primary_monitor);
867 
868   for (l = monitors; l; l = l->next)
869     {
870       MetaMonitor *monitor = l->data;
871       MetaLogicalMonitorConfig *logical_monitor_config;
872       gboolean has_suggested_position;
873 
874       switch (positioning)
875         {
876         case MONITOR_POSITIONING_LINEAR:
877           break;
878         case MONITOR_POSITIONING_SUGGESTED:
879           has_suggested_position =
880             meta_monitor_get_suggested_position (monitor, &x, &y);
881           g_assert (has_suggested_position);
882           break;
883         }
884 
885       scale = compute_scale_for_monitor (monitor_manager, monitor,
886                                          primary_monitor);
887       logical_monitor_config =
888         create_preferred_logical_monitor_config (monitor_manager,
889                                                  monitor,
890                                                  x, y, scale,
891                                                  layout_mode);
892       logical_monitor_config->is_primary = (monitor == primary_monitor);
893       logical_monitor_configs = g_list_append (logical_monitor_configs,
894                                                logical_monitor_config);
895 
896       x += logical_monitor_config->layout.width;
897     }
898 
899   if (positioning == MONITOR_POSITIONING_SUGGESTED)
900     {
901       if (!verify_suggested_monitors_config (logical_monitor_configs))
902         return NULL;
903     }
904 
905   return meta_monitors_config_new (monitor_manager,
906                                    g_steal_pointer (&logical_monitor_configs),
907                                    layout_mode,
908                                    config_flags);
909 }
910 
911 MetaMonitorsConfig *
meta_monitor_config_manager_create_linear(MetaMonitorConfigManager * config_manager)912 meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_manager)
913 {
914   return create_monitors_config (config_manager,
915                                  MONITOR_MATCH_VISIBLE |
916                                  MONITOR_MATCH_ALLOW_FALLBACK,
917                                  MONITOR_POSITIONING_LINEAR,
918                                  META_MONITORS_CONFIG_FLAG_NONE);
919 }
920 
921 MetaMonitorsConfig *
meta_monitor_config_manager_create_fallback(MetaMonitorConfigManager * config_manager)922 meta_monitor_config_manager_create_fallback (MetaMonitorConfigManager *config_manager)
923 {
924   return create_monitors_config (config_manager,
925                                  MONITOR_MATCH_PRIMARY |
926                                  MONITOR_MATCH_ALLOW_FALLBACK,
927                                  MONITOR_POSITIONING_LINEAR,
928                                  META_MONITORS_CONFIG_FLAG_NONE);
929 }
930 
931 MetaMonitorsConfig *
meta_monitor_config_manager_create_suggested(MetaMonitorConfigManager * config_manager)932 meta_monitor_config_manager_create_suggested (MetaMonitorConfigManager *config_manager)
933 {
934   return create_monitors_config (config_manager,
935                                  MONITOR_MATCH_WITH_SUGGESTED_POSITION,
936                                  MONITOR_POSITIONING_SUGGESTED,
937                                  META_MONITORS_CONFIG_FLAG_NONE);
938 }
939 
940 static GList *
clone_monitor_config_list(GList * monitor_configs_in)941 clone_monitor_config_list (GList *monitor_configs_in)
942 {
943   MetaMonitorConfig *monitor_config_in;
944   MetaMonitorConfig *monitor_config_out;
945   GList *monitor_configs_out = NULL;
946   GList *l;
947 
948   for (l = monitor_configs_in; l; l = l->next)
949     {
950       monitor_config_in = l->data;
951       monitor_config_out = g_new0 (MetaMonitorConfig, 1);
952       *monitor_config_out = (MetaMonitorConfig) {
953         .monitor_spec = meta_monitor_spec_clone (monitor_config_in->monitor_spec),
954         .mode_spec = g_memdup2 (monitor_config_in->mode_spec,
955                                 sizeof (MetaMonitorModeSpec)),
956         .enable_underscanning = monitor_config_in->enable_underscanning
957       };
958       monitor_configs_out =
959         g_list_append (monitor_configs_out, monitor_config_out);
960     }
961 
962   return monitor_configs_out;
963 }
964 
965 static GList *
clone_logical_monitor_config_list(GList * logical_monitor_configs_in)966 clone_logical_monitor_config_list (GList *logical_monitor_configs_in)
967 {
968   MetaLogicalMonitorConfig *logical_monitor_config_in;
969   MetaLogicalMonitorConfig *logical_monitor_config_out;
970   GList *logical_monitor_configs_out = NULL;
971   GList *l;
972 
973   for (l = logical_monitor_configs_in; l; l = l->next)
974     {
975       logical_monitor_config_in = l->data;
976 
977       logical_monitor_config_out =
978         g_memdup2 (logical_monitor_config_in,
979                    sizeof (MetaLogicalMonitorConfig));
980       logical_monitor_config_out->monitor_configs =
981         clone_monitor_config_list (logical_monitor_config_in->monitor_configs);
982 
983       logical_monitor_configs_out =
984         g_list_append (logical_monitor_configs_out, logical_monitor_config_out);
985     }
986 
987   return logical_monitor_configs_out;
988 }
989 
990 static MetaLogicalMonitorConfig *
find_logical_config_for_builtin_monitor(MetaMonitorConfigManager * config_manager,GList * logical_monitor_configs)991 find_logical_config_for_builtin_monitor (MetaMonitorConfigManager *config_manager,
992                                          GList                    *logical_monitor_configs)
993 {
994   MetaLogicalMonitorConfig *logical_monitor_config;
995   MetaMonitorConfig *monitor_config;
996   MetaMonitor *panel;
997   GList *l;
998 
999   panel = meta_monitor_manager_get_laptop_panel (config_manager->monitor_manager);
1000   if (panel)
1001     {
1002       for (l = logical_monitor_configs; l; l = l->next)
1003         {
1004           logical_monitor_config = l->data;
1005           /*
1006            * We only want to return the config for the panel if it is
1007            * configured on its own, so we skip configs which contain clones.
1008            */
1009           if (g_list_length (logical_monitor_config->monitor_configs) != 1)
1010             continue;
1011 
1012           monitor_config = logical_monitor_config->monitor_configs->data;
1013           if (meta_monitor_spec_equals (meta_monitor_get_spec (panel),
1014                                         monitor_config->monitor_spec))
1015             {
1016               MetaMonitorMode *mode;
1017 
1018               mode = meta_monitor_get_mode_from_spec (panel,
1019                                                       monitor_config->mode_spec);
1020               if (mode)
1021                 return logical_monitor_config;
1022             }
1023         }
1024     }
1025 
1026   return NULL;
1027 }
1028 
1029 static MetaMonitorsConfig *
create_for_builtin_display_rotation(MetaMonitorConfigManager * config_manager,MetaMonitorsConfig * base_config,gboolean rotate,MetaMonitorTransform transform)1030 create_for_builtin_display_rotation (MetaMonitorConfigManager *config_manager,
1031                                      MetaMonitorsConfig       *base_config,
1032                                      gboolean                  rotate,
1033                                      MetaMonitorTransform      transform)
1034 {
1035   MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
1036   MetaLogicalMonitorConfig *logical_monitor_config;
1037   MetaLogicalMonitorConfig *current_logical_monitor_config;
1038   MetaMonitorsConfig *config;
1039   GList *logical_monitor_configs, *current_configs;
1040   MetaLogicalMonitorLayoutMode layout_mode;
1041 
1042   g_return_val_if_fail (base_config, NULL);
1043 
1044   current_configs = base_config->logical_monitor_configs;
1045   current_logical_monitor_config =
1046     find_logical_config_for_builtin_monitor (config_manager, current_configs);
1047   if (!current_logical_monitor_config)
1048     return NULL;
1049 
1050   if (rotate)
1051     transform = (current_logical_monitor_config->transform + 1) % META_MONITOR_TRANSFORM_FLIPPED;
1052   else
1053     {
1054       /*
1055        * The transform coming from the accelerometer should be applied to
1056        * the crtc as is, without taking panel-orientation into account, this
1057        * is done so that non panel-orientation aware desktop environments do the
1058        * right thing. Mutter corrects for panel-orientation when applying the
1059        * transform from a logical-monitor-config, so we must convert here.
1060        */
1061       MetaMonitor *panel =
1062         meta_monitor_manager_get_laptop_panel (config_manager->monitor_manager);
1063 
1064       transform = meta_monitor_crtc_to_logical_transform (panel, transform);
1065     }
1066 
1067   if (current_logical_monitor_config->transform == transform)
1068     return NULL;
1069 
1070   logical_monitor_configs =
1071     clone_logical_monitor_config_list (base_config->logical_monitor_configs);
1072   logical_monitor_config =
1073     find_logical_config_for_builtin_monitor (config_manager,
1074                                              logical_monitor_configs);
1075   logical_monitor_config->transform = transform;
1076 
1077   if (meta_monitor_transform_is_rotated (current_logical_monitor_config->transform) !=
1078       meta_monitor_transform_is_rotated (logical_monitor_config->transform))
1079     {
1080       int temp = logical_monitor_config->layout.width;
1081       logical_monitor_config->layout.width = logical_monitor_config->layout.height;
1082       logical_monitor_config->layout.height = temp;
1083     }
1084 
1085   layout_mode = base_config->layout_mode;
1086   config = meta_monitors_config_new (monitor_manager,
1087                                      logical_monitor_configs,
1088                                      layout_mode,
1089                                      META_MONITORS_CONFIG_FLAG_NONE);
1090   meta_monitors_config_set_parent_config (config, base_config);
1091 
1092   return config;
1093 }
1094 
1095 MetaMonitorsConfig *
meta_monitor_config_manager_create_for_orientation(MetaMonitorConfigManager * config_manager,MetaMonitorsConfig * base_config,MetaMonitorTransform transform)1096 meta_monitor_config_manager_create_for_orientation (MetaMonitorConfigManager *config_manager,
1097                                                     MetaMonitorsConfig       *base_config,
1098                                                     MetaMonitorTransform      transform)
1099 {
1100   return create_for_builtin_display_rotation (config_manager, base_config,
1101                                               FALSE, transform);
1102 }
1103 
1104 MetaMonitorsConfig *
meta_monitor_config_manager_create_for_builtin_orientation(MetaMonitorConfigManager * config_manager,MetaMonitorsConfig * base_config)1105 meta_monitor_config_manager_create_for_builtin_orientation (MetaMonitorConfigManager *config_manager,
1106                                                             MetaMonitorsConfig       *base_config)
1107 {
1108   MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
1109   MetaMonitorTransform current_transform;
1110   MetaMonitor *laptop_panel;
1111 
1112   g_return_val_if_fail (
1113     meta_monitor_manager_get_panel_orientation_managed (monitor_manager), NULL);
1114 
1115   laptop_panel = meta_monitor_manager_get_laptop_panel (monitor_manager);
1116   current_transform = get_monitor_transform (monitor_manager, laptop_panel);
1117 
1118   return create_for_builtin_display_rotation (config_manager, base_config,
1119                                               FALSE, current_transform);
1120 }
1121 
1122 MetaMonitorsConfig *
meta_monitor_config_manager_create_for_rotate_monitor(MetaMonitorConfigManager * config_manager)1123 meta_monitor_config_manager_create_for_rotate_monitor (MetaMonitorConfigManager *config_manager)
1124 {
1125   if (!config_manager->current_config)
1126     return NULL;
1127 
1128   return create_for_builtin_display_rotation (config_manager,
1129                                               config_manager->current_config,
1130                                               TRUE,
1131                                               META_MONITOR_TRANSFORM_NORMAL);
1132 }
1133 
1134 static MetaMonitorsConfig *
create_monitors_switch_config(MetaMonitorConfigManager * config_manager,MonitorMatchRule match_rule,MonitorPositioningMode positioning,MetaMonitorsConfigFlag config_flags,MetaMonitorSwitchConfigType switch_config)1135 create_monitors_switch_config (MetaMonitorConfigManager    *config_manager,
1136                                MonitorMatchRule             match_rule,
1137                                MonitorPositioningMode       positioning,
1138                                MetaMonitorsConfigFlag       config_flags,
1139                                MetaMonitorSwitchConfigType  switch_config)
1140 {
1141   MetaMonitorsConfig *monitors_config;
1142 
1143   monitors_config = create_monitors_config (config_manager, match_rule,
1144                                             positioning, config_flags);
1145 
1146   if (!monitors_config)
1147     return NULL;
1148 
1149   meta_monitors_config_set_switch_config (monitors_config, switch_config);
1150   return monitors_config;
1151 }
1152 
1153 static MetaMonitorsConfig *
create_for_switch_config_all_mirror(MetaMonitorConfigManager * config_manager)1154 create_for_switch_config_all_mirror (MetaMonitorConfigManager *config_manager)
1155 {
1156   MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
1157   MetaLogicalMonitorLayoutMode layout_mode;
1158   MetaLogicalMonitorConfig *logical_monitor_config = NULL;
1159   GList *logical_monitor_configs;
1160   GList *monitor_configs = NULL;
1161   gint common_mode_w = 0, common_mode_h = 0;
1162   float best_scale = 1.0;
1163   MetaMonitor *monitor;
1164   GList *modes;
1165   GList *monitors;
1166   GList *l;
1167   MetaMonitorsConfig *monitors_config;
1168 
1169   layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager);
1170   monitors = meta_monitor_manager_get_monitors (monitor_manager);
1171   monitor = monitors->data;
1172   modes = meta_monitor_get_modes (monitor);
1173   for (l = modes; l; l = l->next)
1174     {
1175       MetaMonitorMode *mode = l->data;
1176       gboolean common_mode_size = TRUE;
1177       gint mode_w, mode_h;
1178       GList *ll;
1179 
1180       meta_monitor_mode_get_resolution (mode, &mode_w, &mode_h);
1181 
1182       for (ll = monitors->next; ll; ll = ll->next)
1183         {
1184           MetaMonitor *monitor_b = ll->data;
1185           gboolean have_same_mode_size = FALSE;
1186           GList *mm;
1187 
1188           for (mm = meta_monitor_get_modes (monitor_b); mm; mm = mm->next)
1189             {
1190               MetaMonitorMode *mode_b = mm->data;
1191               gint mode_b_w, mode_b_h;
1192 
1193               meta_monitor_mode_get_resolution (mode_b, &mode_b_w, &mode_b_h);
1194 
1195               if (mode_w == mode_b_w &&
1196                   mode_h == mode_b_h)
1197                 {
1198                   have_same_mode_size = TRUE;
1199                   break;
1200                 }
1201             }
1202 
1203           if (!have_same_mode_size)
1204             {
1205               common_mode_size = FALSE;
1206               break;
1207             }
1208         }
1209 
1210       if (common_mode_size &&
1211           common_mode_w * common_mode_h < mode_w * mode_h)
1212         {
1213           common_mode_w = mode_w;
1214           common_mode_h = mode_h;
1215         }
1216     }
1217 
1218   if (common_mode_w == 0 || common_mode_h == 0)
1219     return NULL;
1220 
1221   for (l = monitors; l; l = l->next)
1222     {
1223       MetaMonitor *monitor = l->data;
1224       MetaMonitorMode *mode = NULL;
1225       GList *ll;
1226       float scale;
1227 
1228       for (ll = meta_monitor_get_modes (monitor); ll; ll = ll->next)
1229         {
1230           gint mode_w, mode_h;
1231 
1232           mode = ll->data;
1233           meta_monitor_mode_get_resolution (mode, &mode_w, &mode_h);
1234 
1235           if (mode_w == common_mode_w && mode_h == common_mode_h)
1236             break;
1237         }
1238 
1239       if (!mode)
1240         continue;
1241 
1242       scale = meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager,
1243                                                                  layout_mode,
1244                                                                  monitor, mode);
1245       best_scale = MAX (best_scale, scale);
1246       monitor_configs = g_list_prepend (monitor_configs, create_monitor_config (monitor, mode));
1247     }
1248 
1249   logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1);
1250   *logical_monitor_config = (MetaLogicalMonitorConfig) {
1251     .layout = (MetaRectangle) {
1252       .x = 0,
1253       .y = 0,
1254       .width = common_mode_w,
1255       .height = common_mode_h
1256     },
1257     .scale = best_scale,
1258     .monitor_configs = monitor_configs
1259   };
1260 
1261   logical_monitor_configs = g_list_append (NULL, logical_monitor_config);
1262   monitors_config = meta_monitors_config_new (monitor_manager,
1263                                               logical_monitor_configs,
1264                                               layout_mode,
1265                                               META_MONITORS_CONFIG_FLAG_NONE);
1266 
1267   if (monitors_config)
1268     meta_monitors_config_set_switch_config (monitors_config, META_MONITOR_SWITCH_CONFIG_ALL_MIRROR);
1269 
1270   return monitors_config;
1271 }
1272 
1273 static MetaMonitorsConfig *
create_for_switch_config_external(MetaMonitorConfigManager * config_manager)1274 create_for_switch_config_external (MetaMonitorConfigManager *config_manager)
1275 {
1276   return create_monitors_switch_config (config_manager,
1277                                         MONITOR_MATCH_EXTERNAL,
1278                                         MONITOR_POSITIONING_LINEAR,
1279                                         META_MONITORS_CONFIG_FLAG_NONE,
1280                                         META_MONITOR_SWITCH_CONFIG_EXTERNAL);
1281 }
1282 
1283 static MetaMonitorsConfig *
create_for_switch_config_builtin(MetaMonitorConfigManager * config_manager)1284 create_for_switch_config_builtin (MetaMonitorConfigManager *config_manager)
1285 {
1286   return create_monitors_switch_config (config_manager,
1287                                         MONITOR_MATCH_BUILTIN,
1288                                         MONITOR_POSITIONING_LINEAR,
1289                                         META_MONITORS_CONFIG_FLAG_NONE,
1290                                         META_MONITOR_SWITCH_CONFIG_BUILTIN);
1291 }
1292 
1293 MetaMonitorsConfig *
meta_monitor_config_manager_create_for_switch_config(MetaMonitorConfigManager * config_manager,MetaMonitorSwitchConfigType config_type)1294 meta_monitor_config_manager_create_for_switch_config (MetaMonitorConfigManager    *config_manager,
1295                                                       MetaMonitorSwitchConfigType  config_type)
1296 {
1297   MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
1298   MetaMonitorsConfig *config;
1299 
1300   if (!meta_monitor_manager_can_switch_config (monitor_manager))
1301     return NULL;
1302 
1303   switch (config_type)
1304     {
1305     case META_MONITOR_SWITCH_CONFIG_ALL_MIRROR:
1306       config = create_for_switch_config_all_mirror (config_manager);
1307       break;
1308     case META_MONITOR_SWITCH_CONFIG_ALL_LINEAR:
1309       config = meta_monitor_config_manager_create_linear (config_manager);
1310       break;
1311     case META_MONITOR_SWITCH_CONFIG_EXTERNAL:
1312       config = create_for_switch_config_external (config_manager);
1313       break;
1314     case META_MONITOR_SWITCH_CONFIG_BUILTIN:
1315       config = create_for_switch_config_builtin (config_manager);
1316       break;
1317     case META_MONITOR_SWITCH_CONFIG_UNKNOWN:
1318     default:
1319       g_warn_if_reached ();
1320       return NULL;
1321     }
1322 
1323   return config;
1324 }
1325 
1326 static MetaMonitorsConfig *
get_root_config(MetaMonitorsConfig * config)1327 get_root_config (MetaMonitorsConfig *config)
1328 {
1329   if (!config->parent_config)
1330     return config;
1331 
1332   return get_root_config (config->parent_config);
1333 }
1334 
1335 static gboolean
has_same_root_config(MetaMonitorsConfig * config_a,MetaMonitorsConfig * config_b)1336 has_same_root_config (MetaMonitorsConfig *config_a,
1337                       MetaMonitorsConfig *config_b)
1338 {
1339   return get_root_config (config_a) == get_root_config (config_b);
1340 }
1341 
1342 void
meta_monitor_config_manager_set_current(MetaMonitorConfigManager * config_manager,MetaMonitorsConfig * config)1343 meta_monitor_config_manager_set_current (MetaMonitorConfigManager *config_manager,
1344                                          MetaMonitorsConfig       *config)
1345 {
1346   MetaMonitorsConfig *current_config = config_manager->current_config;
1347   gboolean overrides_current = FALSE;
1348 
1349   if (config && current_config &&
1350       has_same_root_config (config, current_config))
1351     {
1352       overrides_current = meta_monitors_config_key_equal (config->key,
1353                                                           current_config->key);
1354     }
1355 
1356   if (current_config && !overrides_current)
1357     {
1358       g_queue_push_head (&config_manager->config_history,
1359                          g_object_ref (config_manager->current_config));
1360       if (g_queue_get_length (&config_manager->config_history) >
1361           CONFIG_HISTORY_MAX_SIZE)
1362         g_object_unref (g_queue_pop_tail (&config_manager->config_history));
1363     }
1364 
1365   g_set_object (&config_manager->current_config, config);
1366 }
1367 
1368 void
meta_monitor_config_manager_save_current(MetaMonitorConfigManager * config_manager)1369 meta_monitor_config_manager_save_current (MetaMonitorConfigManager *config_manager)
1370 {
1371   g_return_if_fail (config_manager->current_config);
1372 
1373   meta_monitor_config_store_add (config_manager->config_store,
1374                                  config_manager->current_config);
1375 }
1376 
1377 MetaMonitorsConfig *
meta_monitor_config_manager_get_current(MetaMonitorConfigManager * config_manager)1378 meta_monitor_config_manager_get_current (MetaMonitorConfigManager *config_manager)
1379 {
1380   return config_manager->current_config;
1381 }
1382 
1383 MetaMonitorsConfig *
meta_monitor_config_manager_pop_previous(MetaMonitorConfigManager * config_manager)1384 meta_monitor_config_manager_pop_previous (MetaMonitorConfigManager *config_manager)
1385 {
1386   return g_queue_pop_head (&config_manager->config_history);
1387 }
1388 
1389 MetaMonitorsConfig *
meta_monitor_config_manager_get_previous(MetaMonitorConfigManager * config_manager)1390 meta_monitor_config_manager_get_previous (MetaMonitorConfigManager *config_manager)
1391 {
1392   return g_queue_peek_head (&config_manager->config_history);
1393 }
1394 
1395 void
meta_monitor_config_manager_clear_history(MetaMonitorConfigManager * config_manager)1396 meta_monitor_config_manager_clear_history (MetaMonitorConfigManager *config_manager)
1397 {
1398   g_queue_foreach (&config_manager->config_history, (GFunc) g_object_unref, NULL);
1399   g_queue_clear (&config_manager->config_history);
1400 }
1401 
1402 static void
meta_monitor_config_manager_dispose(GObject * object)1403 meta_monitor_config_manager_dispose (GObject *object)
1404 {
1405   MetaMonitorConfigManager *config_manager =
1406     META_MONITOR_CONFIG_MANAGER (object);
1407 
1408   g_clear_object (&config_manager->current_config);
1409   meta_monitor_config_manager_clear_history (config_manager);
1410 
1411   G_OBJECT_CLASS (meta_monitor_config_manager_parent_class)->dispose (object);
1412 }
1413 
1414 static void
meta_monitor_config_manager_init(MetaMonitorConfigManager * config_manager)1415 meta_monitor_config_manager_init (MetaMonitorConfigManager *config_manager)
1416 {
1417   g_queue_init (&config_manager->config_history);
1418 }
1419 
1420 static void
meta_monitor_config_manager_class_init(MetaMonitorConfigManagerClass * klass)1421 meta_monitor_config_manager_class_init (MetaMonitorConfigManagerClass *klass)
1422 {
1423   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1424 
1425   object_class->dispose = meta_monitor_config_manager_dispose;
1426 }
1427 
1428 void
meta_monitor_config_free(MetaMonitorConfig * monitor_config)1429 meta_monitor_config_free (MetaMonitorConfig *monitor_config)
1430 {
1431   if (monitor_config->monitor_spec)
1432     meta_monitor_spec_free (monitor_config->monitor_spec);
1433   g_free (monitor_config->mode_spec);
1434   g_free (monitor_config);
1435 }
1436 
1437 void
meta_logical_monitor_config_free(MetaLogicalMonitorConfig * logical_monitor_config)1438 meta_logical_monitor_config_free (MetaLogicalMonitorConfig *logical_monitor_config)
1439 {
1440   g_list_free_full (logical_monitor_config->monitor_configs,
1441                     (GDestroyNotify) meta_monitor_config_free);
1442   g_free (logical_monitor_config);
1443 }
1444 
1445 static MetaMonitorsConfigKey *
meta_monitors_config_key_new(GList * logical_monitor_configs,GList * disabled_monitor_specs)1446 meta_monitors_config_key_new (GList *logical_monitor_configs,
1447                               GList *disabled_monitor_specs)
1448 {
1449   MetaMonitorsConfigKey *config_key;
1450   GList *monitor_specs;
1451   GList *l;
1452 
1453   monitor_specs = NULL;
1454   for (l = logical_monitor_configs; l; l = l->next)
1455     {
1456       MetaLogicalMonitorConfig *logical_monitor_config = l->data;
1457       GList *k;
1458 
1459       for (k = logical_monitor_config->monitor_configs; k; k = k->next)
1460         {
1461           MetaMonitorConfig *monitor_config = k->data;
1462           MetaMonitorSpec *monitor_spec;
1463 
1464           monitor_spec = meta_monitor_spec_clone (monitor_config->monitor_spec);
1465           monitor_specs = g_list_prepend (monitor_specs, monitor_spec);
1466         }
1467     }
1468 
1469   for (l = disabled_monitor_specs; l; l = l->next)
1470     {
1471       MetaMonitorSpec *monitor_spec = l->data;
1472 
1473       monitor_spec = meta_monitor_spec_clone (monitor_spec);
1474       monitor_specs = g_list_prepend (monitor_specs, monitor_spec);
1475     }
1476 
1477   monitor_specs = g_list_sort (monitor_specs,
1478                                (GCompareFunc) meta_monitor_spec_compare);
1479 
1480   config_key = g_new0 (MetaMonitorsConfigKey, 1);
1481   *config_key = (MetaMonitorsConfigKey) {
1482     .monitor_specs = monitor_specs
1483   };
1484 
1485   return config_key;
1486 }
1487 
1488 void
meta_monitors_config_key_free(MetaMonitorsConfigKey * config_key)1489 meta_monitors_config_key_free (MetaMonitorsConfigKey *config_key)
1490 {
1491   g_list_free_full (config_key->monitor_specs,
1492                     (GDestroyNotify) meta_monitor_spec_free);
1493   g_free (config_key);
1494 }
1495 
1496 unsigned int
meta_monitors_config_key_hash(gconstpointer data)1497 meta_monitors_config_key_hash (gconstpointer data)
1498 {
1499   const MetaMonitorsConfigKey *config_key = data;
1500   GList *l;
1501   unsigned long hash;
1502 
1503   hash = 0;
1504   for (l = config_key->monitor_specs; l; l = l->next)
1505     {
1506       MetaMonitorSpec *monitor_spec = l->data;
1507 
1508       hash ^= (g_str_hash (monitor_spec->connector) ^
1509                g_str_hash (monitor_spec->vendor) ^
1510                g_str_hash (monitor_spec->product) ^
1511                g_str_hash (monitor_spec->serial));
1512     }
1513 
1514   return hash;
1515 }
1516 
1517 gboolean
meta_monitors_config_key_equal(gconstpointer data_a,gconstpointer data_b)1518 meta_monitors_config_key_equal (gconstpointer data_a,
1519                                 gconstpointer data_b)
1520 {
1521   const MetaMonitorsConfigKey *config_key_a = data_a;
1522   const MetaMonitorsConfigKey *config_key_b = data_b;
1523   GList *l_a, *l_b;
1524 
1525   for (l_a = config_key_a->monitor_specs, l_b = config_key_b->monitor_specs;
1526        l_a && l_b;
1527        l_a = l_a->next, l_b = l_b->next)
1528     {
1529       MetaMonitorSpec *monitor_spec_a = l_a->data;
1530       MetaMonitorSpec *monitor_spec_b = l_b->data;
1531 
1532       if (!meta_monitor_spec_equals (monitor_spec_a, monitor_spec_b))
1533         return FALSE;
1534     }
1535 
1536   if (l_a || l_b)
1537     return FALSE;
1538 
1539   return TRUE;
1540 }
1541 
1542 MetaMonitorSwitchConfigType
meta_monitors_config_get_switch_config(MetaMonitorsConfig * config)1543 meta_monitors_config_get_switch_config (MetaMonitorsConfig *config)
1544 {
1545   return config->switch_config;
1546 }
1547 
1548 void
meta_monitors_config_set_switch_config(MetaMonitorsConfig * config,MetaMonitorSwitchConfigType switch_config)1549 meta_monitors_config_set_switch_config (MetaMonitorsConfig          *config,
1550                                         MetaMonitorSwitchConfigType  switch_config)
1551 {
1552   config->switch_config = switch_config;
1553 }
1554 
1555 void
meta_monitors_config_set_parent_config(MetaMonitorsConfig * config,MetaMonitorsConfig * parent_config)1556 meta_monitors_config_set_parent_config (MetaMonitorsConfig *config,
1557                                         MetaMonitorsConfig *parent_config)
1558 {
1559   g_assert (config != parent_config);
1560   g_assert (!parent_config || parent_config->parent_config != config);
1561 
1562   g_set_object (&config->parent_config, parent_config);
1563 }
1564 
1565 MetaMonitorsConfig *
meta_monitors_config_new_full(GList * logical_monitor_configs,GList * disabled_monitor_specs,MetaLogicalMonitorLayoutMode layout_mode,MetaMonitorsConfigFlag flags)1566 meta_monitors_config_new_full (GList                        *logical_monitor_configs,
1567                                GList                        *disabled_monitor_specs,
1568                                MetaLogicalMonitorLayoutMode  layout_mode,
1569                                MetaMonitorsConfigFlag        flags)
1570 {
1571   MetaMonitorsConfig *config;
1572 
1573   config = g_object_new (META_TYPE_MONITORS_CONFIG, NULL);
1574   config->logical_monitor_configs = logical_monitor_configs;
1575   config->disabled_monitor_specs = disabled_monitor_specs;
1576   config->key = meta_monitors_config_key_new (logical_monitor_configs,
1577                                               disabled_monitor_specs);
1578   config->layout_mode = layout_mode;
1579   config->flags = flags;
1580   config->switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN;
1581 
1582   return config;
1583 }
1584 
1585 MetaMonitorsConfig *
meta_monitors_config_new(MetaMonitorManager * monitor_manager,GList * logical_monitor_configs,MetaLogicalMonitorLayoutMode layout_mode,MetaMonitorsConfigFlag flags)1586 meta_monitors_config_new (MetaMonitorManager           *monitor_manager,
1587                           GList                        *logical_monitor_configs,
1588                           MetaLogicalMonitorLayoutMode  layout_mode,
1589                           MetaMonitorsConfigFlag        flags)
1590 {
1591   GList *disabled_monitor_specs = NULL;
1592   GList *monitors;
1593   GList *l;
1594 
1595   monitors = meta_monitor_manager_get_monitors (monitor_manager);
1596   for (l = monitors; l; l = l->next)
1597     {
1598       MetaMonitor *monitor = l->data;
1599       MetaMonitorSpec *monitor_spec;
1600 
1601       if (!monitor_matches_rule (monitor, monitor_manager,
1602                                  MONITOR_MATCH_VISIBLE))
1603         continue;
1604 
1605       monitor_spec = meta_monitor_get_spec (monitor);
1606       if (meta_logical_monitor_configs_have_monitor (logical_monitor_configs,
1607                                                      monitor_spec))
1608         continue;
1609 
1610       disabled_monitor_specs =
1611         g_list_prepend (disabled_monitor_specs,
1612                         meta_monitor_spec_clone (monitor_spec));
1613     }
1614 
1615   return meta_monitors_config_new_full (logical_monitor_configs,
1616                                         disabled_monitor_specs,
1617                                         layout_mode,
1618                                         flags);
1619 }
1620 
1621 static void
meta_monitors_config_finalize(GObject * object)1622 meta_monitors_config_finalize (GObject *object)
1623 {
1624   MetaMonitorsConfig *config = META_MONITORS_CONFIG (object);
1625 
1626   g_clear_object (&config->parent_config);
1627   meta_monitors_config_key_free (config->key);
1628   g_list_free_full (config->logical_monitor_configs,
1629                     (GDestroyNotify) meta_logical_monitor_config_free);
1630   g_list_free_full (config->disabled_monitor_specs,
1631                     (GDestroyNotify) meta_monitor_spec_free);
1632 
1633   G_OBJECT_CLASS (meta_monitors_config_parent_class)->finalize (object);
1634 }
1635 
1636 static void
meta_monitors_config_init(MetaMonitorsConfig * config)1637 meta_monitors_config_init (MetaMonitorsConfig *config)
1638 {
1639 }
1640 
1641 static void
meta_monitors_config_class_init(MetaMonitorsConfigClass * klass)1642 meta_monitors_config_class_init (MetaMonitorsConfigClass *klass)
1643 {
1644   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1645 
1646   object_class->finalize = meta_monitors_config_finalize;
1647 }
1648 
1649 static void
meta_crtc_assignment_free(MetaCrtcAssignment * assignment)1650 meta_crtc_assignment_free (MetaCrtcAssignment *assignment)
1651 {
1652   g_ptr_array_free (assignment->outputs, TRUE);
1653   g_free (assignment);
1654 }
1655 
1656 static void
meta_output_assignment_free(MetaOutputAssignment * assignment)1657 meta_output_assignment_free (MetaOutputAssignment *assignment)
1658 {
1659   g_free (assignment);
1660 }
1661 
1662 gboolean
meta_verify_monitor_mode_spec(MetaMonitorModeSpec * monitor_mode_spec,GError ** error)1663 meta_verify_monitor_mode_spec (MetaMonitorModeSpec *monitor_mode_spec,
1664                                GError             **error)
1665 {
1666   if (monitor_mode_spec->width > 0 &&
1667       monitor_mode_spec->height > 0 &&
1668       monitor_mode_spec->refresh_rate > 0.0f)
1669     {
1670       return TRUE;
1671     }
1672   else
1673     {
1674       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1675                    "Monitor mode invalid");
1676       return FALSE;
1677     }
1678 }
1679 
1680 gboolean
meta_verify_monitor_spec(MetaMonitorSpec * monitor_spec,GError ** error)1681 meta_verify_monitor_spec (MetaMonitorSpec *monitor_spec,
1682                           GError         **error)
1683 {
1684   if (monitor_spec->connector &&
1685       monitor_spec->vendor &&
1686       monitor_spec->product &&
1687       monitor_spec->serial)
1688     {
1689       return TRUE;
1690     }
1691   else
1692     {
1693       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1694                    "Monitor spec incomplete");
1695       return FALSE;
1696     }
1697 }
1698 
1699 gboolean
meta_verify_monitor_config(MetaMonitorConfig * monitor_config,GError ** error)1700 meta_verify_monitor_config (MetaMonitorConfig *monitor_config,
1701                             GError           **error)
1702 {
1703   if (monitor_config->monitor_spec && monitor_config->mode_spec)
1704     {
1705       return TRUE;
1706     }
1707   else
1708     {
1709       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1710                    "Monitor config incomplete");
1711       return FALSE;
1712     }
1713 }
1714 
1715 gboolean
meta_verify_logical_monitor_config(MetaLogicalMonitorConfig * logical_monitor_config,MetaLogicalMonitorLayoutMode layout_mode,MetaMonitorManager * monitor_manager,GError ** error)1716 meta_verify_logical_monitor_config (MetaLogicalMonitorConfig    *logical_monitor_config,
1717                                     MetaLogicalMonitorLayoutMode layout_mode,
1718                                     MetaMonitorManager          *monitor_manager,
1719                                     GError                     **error)
1720 {
1721   GList *l;
1722   int expected_mode_width = 0;
1723   int expected_mode_height = 0;
1724 
1725   if (logical_monitor_config->layout.x < 0 ||
1726       logical_monitor_config->layout.y < 0)
1727     {
1728       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1729                    "Invalid logical monitor position (%d, %d)",
1730                    logical_monitor_config->layout.x,
1731                    logical_monitor_config->layout.y);
1732       return FALSE;
1733     }
1734 
1735   if (!logical_monitor_config->monitor_configs)
1736     {
1737       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1738                    "Logical monitor is empty");
1739       return FALSE;
1740     }
1741 
1742   if (meta_monitor_transform_is_rotated (logical_monitor_config->transform))
1743     {
1744       expected_mode_width = logical_monitor_config->layout.height;
1745       expected_mode_height = logical_monitor_config->layout.width;
1746     }
1747   else
1748     {
1749       expected_mode_width = logical_monitor_config->layout.width;
1750       expected_mode_height = logical_monitor_config->layout.height;
1751     }
1752 
1753   switch (layout_mode)
1754     {
1755     case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL:
1756       expected_mode_width = roundf (expected_mode_width *
1757                                     logical_monitor_config->scale);
1758       expected_mode_height = roundf (expected_mode_height *
1759                                      logical_monitor_config->scale);
1760       break;
1761     case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL:
1762       break;
1763     }
1764 
1765   for (l = logical_monitor_config->monitor_configs; l; l = l->next)
1766     {
1767       MetaMonitorConfig *monitor_config = l->data;
1768 
1769       if (monitor_config->mode_spec->width != expected_mode_width ||
1770           monitor_config->mode_spec->height != expected_mode_height)
1771         {
1772           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1773                        "Monitor modes in logical monitor conflict");
1774           return FALSE;
1775         }
1776     }
1777 
1778   return TRUE;
1779 }
1780 
1781 static gboolean
has_adjacent_neighbour(MetaMonitorsConfig * config,MetaLogicalMonitorConfig * logical_monitor_config)1782 has_adjacent_neighbour (MetaMonitorsConfig       *config,
1783                         MetaLogicalMonitorConfig *logical_monitor_config)
1784 {
1785   GList *l;
1786 
1787   if (!config->logical_monitor_configs->next)
1788     {
1789       g_assert (config->logical_monitor_configs->data ==
1790                 logical_monitor_config);
1791       return TRUE;
1792     }
1793 
1794   for (l = config->logical_monitor_configs; l; l = l->next)
1795     {
1796       MetaLogicalMonitorConfig *other_logical_monitor_config = l->data;
1797 
1798       if (logical_monitor_config == other_logical_monitor_config)
1799         continue;
1800 
1801       if (meta_rectangle_is_adjacent_to (&logical_monitor_config->layout,
1802                                          &other_logical_monitor_config->layout))
1803         return TRUE;
1804     }
1805 
1806   return FALSE;
1807 }
1808 
1809 gboolean
meta_logical_monitor_configs_have_monitor(GList * logical_monitor_configs,MetaMonitorSpec * monitor_spec)1810 meta_logical_monitor_configs_have_monitor (GList           *logical_monitor_configs,
1811                                            MetaMonitorSpec *monitor_spec)
1812 {
1813   GList *l;
1814 
1815   for (l = logical_monitor_configs; l; l = l->next)
1816     {
1817       MetaLogicalMonitorConfig *logical_monitor_config = l->data;
1818       GList *k;
1819 
1820       for (k = logical_monitor_config->monitor_configs; k; k = k->next)
1821         {
1822           MetaMonitorConfig *monitor_config = k->data;
1823 
1824           if (meta_monitor_spec_equals (monitor_spec,
1825                                         monitor_config->monitor_spec))
1826             return TRUE;
1827         }
1828     }
1829 
1830   return FALSE;
1831 }
1832 
1833 static gboolean
meta_monitors_config_is_monitor_enabled(MetaMonitorsConfig * config,MetaMonitorSpec * monitor_spec)1834 meta_monitors_config_is_monitor_enabled (MetaMonitorsConfig *config,
1835                                          MetaMonitorSpec    *monitor_spec)
1836 {
1837   return meta_logical_monitor_configs_have_monitor (config->logical_monitor_configs,
1838                                                     monitor_spec);
1839 }
1840 
1841 gboolean
meta_verify_monitors_config(MetaMonitorsConfig * config,MetaMonitorManager * monitor_manager,GError ** error)1842 meta_verify_monitors_config (MetaMonitorsConfig *config,
1843                              MetaMonitorManager *monitor_manager,
1844                              GError            **error)
1845 {
1846   int min_x, min_y;
1847   gboolean has_primary;
1848   GList *region;
1849   GList *l;
1850   gboolean global_scale_required;
1851 
1852   if (!config->logical_monitor_configs)
1853     {
1854       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1855                    "Monitors config incomplete");
1856       return FALSE;
1857     }
1858 
1859   global_scale_required =
1860     !!(meta_monitor_manager_get_capabilities (monitor_manager) &
1861        META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED);
1862 
1863   min_x = INT_MAX;
1864   min_y = INT_MAX;
1865   region = NULL;
1866   has_primary = FALSE;
1867   for (l = config->logical_monitor_configs; l; l = l->next)
1868     {
1869       MetaLogicalMonitorConfig *logical_monitor_config = l->data;
1870 
1871       if (global_scale_required)
1872         {
1873           MetaLogicalMonitorConfig *prev_logical_monitor_config =
1874             l->prev ? l->prev->data : NULL;
1875 
1876           if (prev_logical_monitor_config &&
1877               (prev_logical_monitor_config->scale !=
1878                logical_monitor_config->scale))
1879             {
1880               g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1881                            "Logical monitor scales must be identical");
1882               return FALSE;
1883             }
1884         }
1885 
1886       if (meta_rectangle_overlaps_with_region (region,
1887                                                &logical_monitor_config->layout))
1888         {
1889           g_list_free (region);
1890           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1891                        "Logical monitors overlap");
1892           return FALSE;
1893         }
1894 
1895       if (has_primary && logical_monitor_config->is_primary)
1896         {
1897           g_list_free (region);
1898           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1899                        "Config contains multiple primary logical monitors");
1900           return FALSE;
1901         }
1902       else if (logical_monitor_config->is_primary)
1903         {
1904           has_primary = TRUE;
1905         }
1906 
1907       if (!has_adjacent_neighbour (config, logical_monitor_config))
1908         {
1909           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1910                        "Logical monitors not adjacent");
1911           return FALSE;
1912         }
1913 
1914       min_x = MIN (logical_monitor_config->layout.x, min_x);
1915       min_y = MIN (logical_monitor_config->layout.y, min_y);
1916 
1917       region = g_list_prepend (region, &logical_monitor_config->layout);
1918     }
1919 
1920   g_list_free (region);
1921 
1922   for (l = config->disabled_monitor_specs; l; l = l->next)
1923     {
1924       MetaMonitorSpec *monitor_spec = l->data;
1925 
1926       if (meta_monitors_config_is_monitor_enabled (config, monitor_spec))
1927         {
1928           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1929                        "Assigned monitor explicitly disabled");
1930           return FALSE;
1931         }
1932     }
1933 
1934   if (min_x != 0 || min_y != 0)
1935     {
1936       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1937                    "Logical monitors positions are offset");
1938       return FALSE;
1939     }
1940 
1941   if (!has_primary)
1942     {
1943       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1944                    "Config is missing primary logical");
1945       return FALSE;
1946     }
1947 
1948   return TRUE;
1949 }
1950