GCC Code Coverage Report


Directory: ./
File: panels/display/cc-display-config-dbus.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 975 0.0%
Functions: 0 116 0.0%
Branches: 0 548 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (C) 2017 Red Hat, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 */
19
20 #include <float.h>
21 #include <math.h>
22 #include <gio/gio.h>
23
24 #include "cc-display-config-dbus.h"
25
26 #define MODE_BASE_FORMAT "siiddad"
27 #define MODE_FORMAT "(" MODE_BASE_FORMAT "a{sv})"
28 #define MODES_FORMAT "a" MODE_FORMAT
29 #define MONITOR_SPEC_FORMAT "(ssss)"
30 #define MONITOR_FORMAT "(" MONITOR_SPEC_FORMAT MODES_FORMAT "a{sv})"
31 #define MONITORS_FORMAT "a" MONITOR_FORMAT
32
33 #define LOGICAL_MONITOR_MONITORS_FORMAT "a" MONITOR_SPEC_FORMAT
34 #define LOGICAL_MONITOR_FORMAT "(iidub" LOGICAL_MONITOR_MONITORS_FORMAT "a{sv})"
35 #define LOGICAL_MONITORS_FORMAT "a" LOGICAL_MONITOR_FORMAT
36
37 #define CURRENT_STATE_FORMAT "(u" MONITORS_FORMAT LOGICAL_MONITORS_FORMAT "a{sv})"
38
39 typedef enum _CcDisplayModeFlags
40 {
41 MODE_PREFERRED = 1 << 0,
42 MODE_CURRENT = 1 << 1,
43 MODE_INTERLACED = 1 << 2,
44 } CcDisplayModeFlags;
45
46 struct _CcDisplayModeDBus
47 {
48 CcDisplayMode parent_instance;
49 CcDisplayMonitorDBus *monitor;
50
51 char *id;
52 int width;
53 int height;
54 double refresh_rate;
55 CcDisplayModeRefreshRateMode refresh_rate_mode;
56 double preferred_scale;
57 GArray *supported_scales;
58 guint32 flags;
59 };
60
61 G_DEFINE_TYPE (CcDisplayModeDBus,
62 cc_display_mode_dbus,
63 CC_TYPE_DISPLAY_MODE)
64
65 static gboolean
66 cc_display_mode_dbus_equal (const CcDisplayModeDBus *m1,
67 const CcDisplayModeDBus *m2)
68 {
69 if (!m1 && !m2)
70 return TRUE;
71 else if (!m1 || !m2)
72 return FALSE;
73
74 return m1->width == m2->width &&
75 m1->height == m2->height &&
76 m1->refresh_rate == m2->refresh_rate &&
77 m1->refresh_rate_mode == m2->refresh_rate_mode &&
78 (m1->flags & MODE_INTERLACED) == (m2->flags & MODE_INTERLACED);
79 }
80
81 static gboolean
82 cc_display_mode_dbus_is_clone_mode (CcDisplayMode *pself)
83 {
84 CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
85
86 return !self->id;
87 }
88
89 static void
90 cc_display_mode_dbus_get_resolution (CcDisplayMode *pself,
91 int *w, int *h)
92 {
93 CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
94
95 if (w)
96 *w = self->width;
97 if (h)
98 *h = self->height;
99 }
100
101 static GArray * cc_display_mode_dbus_get_supported_scales (CcDisplayMode *pself);
102
103 static double
104 cc_display_mode_dbus_get_preferred_scale (CcDisplayMode *pself)
105 {
106 CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
107
108 return self->preferred_scale;
109 }
110
111 static gboolean
112 cc_display_mode_dbus_is_supported_scale (CcDisplayMode *pself,
113 double scale)
114 {
115 CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
116
117 guint i;
118 for (i = 0; i < self->supported_scales->len; i++)
119 {
120 double v = g_array_index (self->supported_scales, double, i);
121
122 if (G_APPROX_VALUE (v, scale, DBL_EPSILON))
123 return TRUE;
124 }
125 return FALSE;
126 }
127
128 static CcDisplayModeRefreshRateMode
129 cc_display_mode_dbus_get_refresh_rate_mode (CcDisplayMode *pself)
130 {
131 CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
132
133 return self->refresh_rate_mode;
134 }
135
136 static gboolean
137 cc_display_mode_dbus_is_interlaced (CcDisplayMode *pself)
138 {
139 CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
140
141 return !!(self->flags & MODE_INTERLACED);
142 }
143
144 static gboolean
145 cc_display_mode_dbus_is_preferred (CcDisplayMode *pself)
146 {
147 CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
148
149 return !!(self->flags & MODE_PREFERRED);
150 }
151
152 static int
153 cc_display_mode_dbus_get_freq (CcDisplayMode *pself)
154 {
155 CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
156
157 return self->refresh_rate;
158 }
159
160 static double
161 cc_display_mode_dbus_get_freq_f (CcDisplayMode *pself)
162 {
163 CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
164
165 return self->refresh_rate;
166 }
167
168 static void
169 cc_display_mode_dbus_init (CcDisplayModeDBus *self)
170 {
171 self->supported_scales = g_array_new (FALSE, FALSE, sizeof (double));
172 }
173
174 static void
175 cc_display_mode_dbus_finalize (GObject *object)
176 {
177 CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (object);
178
179 g_free (self->id);
180 g_array_free (self->supported_scales, TRUE);
181
182 G_OBJECT_CLASS (cc_display_mode_dbus_parent_class)->finalize (object);
183 }
184
185 static void
186 cc_display_mode_dbus_class_init (CcDisplayModeDBusClass *klass)
187 {
188 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
189 CcDisplayModeClass *parent_class = CC_DISPLAY_MODE_CLASS (klass);
190
191 gobject_class->finalize = cc_display_mode_dbus_finalize;
192
193 parent_class->is_clone_mode = cc_display_mode_dbus_is_clone_mode;
194 parent_class->get_resolution = cc_display_mode_dbus_get_resolution;
195 parent_class->get_supported_scales = cc_display_mode_dbus_get_supported_scales;
196 parent_class->get_preferred_scale = cc_display_mode_dbus_get_preferred_scale;
197 parent_class->get_refresh_rate_mode = cc_display_mode_dbus_get_refresh_rate_mode;
198 parent_class->is_interlaced = cc_display_mode_dbus_is_interlaced;
199 parent_class->is_preferred = cc_display_mode_dbus_is_preferred;
200 parent_class->get_freq = cc_display_mode_dbus_get_freq;
201 parent_class->get_freq_f = cc_display_mode_dbus_get_freq_f;
202 }
203
204 static CcDisplayModeDBus *
205 cc_display_mode_dbus_new_virtual (int width,
206 int height,
207 double preferred_scale,
208 GArray *supported_scales)
209 {
210 g_autoptr(GVariant) properties_variant = NULL;
211 CcDisplayModeDBus *self;
212
213 self = g_object_new (CC_TYPE_DISPLAY_MODE_DBUS, NULL);
214
215 self->width = width;
216 self->height = height;
217 self->preferred_scale = preferred_scale;
218 self->supported_scales = g_array_ref (supported_scales);
219
220 return self;
221 }
222
223 static CcDisplayModeDBus *
224 cc_display_mode_dbus_new (CcDisplayMonitorDBus *monitor,
225 GVariant *variant)
226 {
227 double d;
228 g_autoptr(GVariantIter) scales_iter = NULL;
229 g_autoptr(GVariant) properties_variant = NULL;
230 gboolean is_current;
231 gboolean is_preferred;
232 gboolean is_interlaced;
233 char *refresh_rate_mode_str;
234 CcDisplayModeDBus *self = g_object_new (CC_TYPE_DISPLAY_MODE_DBUS, NULL);
235
236 self->monitor = monitor;
237
238 g_variant_get (variant, "(" MODE_BASE_FORMAT "@a{sv})",
239 &self->id,
240 &self->width,
241 &self->height,
242 &self->refresh_rate,
243 &self->preferred_scale,
244 &scales_iter,
245 &properties_variant);
246
247 while (g_variant_iter_next (scales_iter, "d", &d))
248 g_array_append_val (self->supported_scales, d);
249
250 if (!g_variant_lookup (properties_variant, "is-current", "b", &is_current))
251 is_current = FALSE;
252 if (!g_variant_lookup (properties_variant, "is-preferred", "b", &is_preferred))
253 is_preferred = FALSE;
254 if (!g_variant_lookup (properties_variant, "is-interlaced", "b", &is_interlaced))
255 is_interlaced = FALSE;
256 if (!g_variant_lookup (properties_variant, "refresh-rate-mode", "s", &refresh_rate_mode_str))
257 refresh_rate_mode_str = "fixed";
258
259 if (is_current)
260 self->flags |= MODE_CURRENT;
261 if (is_preferred)
262 self->flags |= MODE_PREFERRED;
263 if (is_interlaced)
264 self->flags |= MODE_INTERLACED;
265
266 if (g_strcmp0 (refresh_rate_mode_str, "fixed") == 0)
267 self->refresh_rate_mode = MODE_REFRESH_RATE_MODE_FIXED;
268 else if (g_strcmp0 (refresh_rate_mode_str, "variable") == 0)
269 self->refresh_rate_mode = MODE_REFRESH_RATE_MODE_VARIABLE;
270
271 return self;
272 }
273
274
275 #define CC_TYPE_DISPLAY_LOGICAL_MONITOR (cc_display_logical_monitor_get_type ())
276 G_DECLARE_FINAL_TYPE (CcDisplayLogicalMonitor, cc_display_logical_monitor,
277 CC, DISPLAY_LOGICAL_MONITOR, GObject)
278
279 struct _CcDisplayLogicalMonitor
280 {
281 GObject parent_instance;
282
283 int x;
284 int y;
285 double scale;
286 CcDisplayRotation rotation;
287 gboolean primary;
288
289 GHashTable *monitors;
290 };
291
292 G_DEFINE_TYPE (CcDisplayLogicalMonitor,
293 cc_display_logical_monitor,
294 G_TYPE_OBJECT)
295
296 static gboolean
297 cc_display_logical_monitor_equal (const CcDisplayLogicalMonitor *m1,
298 const CcDisplayLogicalMonitor *m2)
299 {
300 if (!m1 && !m2)
301 return TRUE;
302 else if (!m1 || !m2)
303 return FALSE;
304
305 return m1->x == m2->x &&
306 m1->y == m2->y &&
307 G_APPROX_VALUE (m1->scale, m2->scale, DBL_EPSILON) &&
308 m1->rotation == m2->rotation &&
309 m1->primary == m2->primary;
310 }
311
312 static void
313 cc_display_logical_monitor_init (CcDisplayLogicalMonitor *self)
314 {
315 self->scale = 1.0;
316 self->monitors = g_hash_table_new (NULL, NULL);
317 }
318
319 static void
320 cc_display_logical_monitor_finalize (GObject *object)
321 {
322 CcDisplayLogicalMonitor *self = CC_DISPLAY_LOGICAL_MONITOR (object);
323
324 g_warn_if_fail (g_hash_table_size (self->monitors) == 0);
325 g_clear_pointer (&self->monitors, g_hash_table_destroy);
326
327 G_OBJECT_CLASS (cc_display_logical_monitor_parent_class)->finalize (object);
328 }
329
330 static void
331 cc_display_logical_monitor_class_init (CcDisplayLogicalMonitorClass *klass)
332 {
333 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
334
335 gobject_class->finalize = cc_display_logical_monitor_finalize;
336 }
337
338
339 typedef enum _CcDisplayMonitorUnderscanning
340 {
341 UNDERSCANNING_UNSUPPORTED = 0,
342 UNDERSCANNING_DISABLED,
343 UNDERSCANNING_ENABLED
344 } CcDisplayMonitorUnderscanning;
345
346 struct _CcDisplayMonitorDBus
347 {
348 CcDisplayMonitor parent_instance;
349 CcDisplayConfigDBus *config;
350
351 gchar *connector_name;
352 gchar *vendor_name;
353 gchar *product_name;
354 gchar *product_serial;
355 gchar *display_name;
356
357 int width_mm;
358 int height_mm;
359 gboolean builtin;
360 CcDisplayMonitorUnderscanning underscanning;
361 CcDisplayMonitorPrivacy privacy_screen;
362 int max_width;
363 int max_height;
364 int min_refresh_rate;
365
366 GList *modes;
367 CcDisplayMode *current_mode;
368 CcDisplayMode *preferred_mode;
369
370 gboolean supports_variable_refresh_rate;
371
372 CcDisplayLogicalMonitor *logical_monitor;
373 };
374
375 G_DEFINE_TYPE (CcDisplayMonitorDBus,
376 cc_display_monitor_dbus,
377 CC_TYPE_DISPLAY_MONITOR)
378
379 static void
380 register_logical_monitor (CcDisplayConfigDBus *self,
381 CcDisplayLogicalMonitor *logical_monitor);
382 static void
383 cc_display_config_dbus_set_primary (CcDisplayConfigDBus *self,
384 CcDisplayMonitorDBus *new_primary);
385 static void
386 cc_display_config_dbus_unset_primary (CcDisplayConfigDBus *self,
387 CcDisplayMonitorDBus *old_primary);
388 static void
389 cc_display_config_dbus_ensure_non_offset_coords (CcDisplayConfigDBus *self);
390 static void
391 cc_display_config_dbus_append_right (CcDisplayConfigDBus *self,
392 CcDisplayLogicalMonitor *monitor);
393 static void
394 cc_display_config_dbus_make_linear (CcDisplayConfigDBus *self);
395
396
397 static const char *
398 cc_display_monitor_dbus_get_display_name (CcDisplayMonitor *pself)
399 {
400 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
401
402 if (self->display_name)
403 return self->display_name;
404
405 return self->connector_name;
406 }
407
408 static const char *
409 cc_display_monitor_dbus_get_connector_name (CcDisplayMonitor *pself)
410 {
411 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
412
413 return self->connector_name;
414 }
415
416 static gboolean
417 cc_display_monitor_dbus_is_builtin (CcDisplayMonitor *pself)
418 {
419 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
420
421 return self->builtin;
422 }
423
424 static gboolean
425 cc_display_monitor_dbus_is_primary (CcDisplayMonitor *pself)
426 {
427 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
428
429 if (self->logical_monitor)
430 return self->logical_monitor->primary;
431
432 return FALSE;
433 }
434
435 static void
436 cc_display_monitor_dbus_set_primary (CcDisplayMonitor *pself,
437 gboolean primary)
438 {
439 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
440
441 if (primary)
442 cc_display_config_dbus_set_primary (self->config, self);
443 else
444 cc_display_config_dbus_unset_primary (self->config, self);
445 }
446
447 static gboolean
448 cc_display_monitor_dbus_is_active (CcDisplayMonitor *pself)
449 {
450 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
451
452 return self->logical_monitor != NULL;
453 }
454
455 static void
456 cc_display_monitor_dbus_set_logical_monitor (CcDisplayMonitorDBus *self,
457 CcDisplayLogicalMonitor *logical_monitor)
458 {
459 gboolean was_primary = FALSE;
460
461 if (self->logical_monitor)
462 {
463 was_primary = self->logical_monitor->primary;
464 if (was_primary)
465 cc_display_config_dbus_unset_primary (self->config, self);
466 g_hash_table_remove (self->logical_monitor->monitors, self);
467 g_object_unref (self->logical_monitor);
468 }
469
470 self->logical_monitor = logical_monitor;
471
472 if (self->logical_monitor)
473 {
474 g_hash_table_add (self->logical_monitor->monitors, self);
475 g_object_ref (self->logical_monitor);
476 /* unset primary with NULL will select this monitor if it is the only one.*/
477 if (was_primary)
478 cc_display_config_dbus_set_primary (self->config, self);
479 else
480 cc_display_config_dbus_unset_primary (self->config, NULL);
481 }
482 }
483
484 static void
485 cc_display_monitor_dbus_set_active (CcDisplayMonitor *pself,
486 gboolean active)
487 {
488 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
489
490 if (!self->current_mode && active)
491 {
492 if (self->preferred_mode)
493 self->current_mode = self->preferred_mode;
494 else if (self->modes)
495 self->current_mode = (CcDisplayMode *) self->modes->data;
496 else
497 g_warning ("Couldn't find a mode to activate monitor at %s", self->connector_name);
498 }
499
500 if (!self->logical_monitor && active)
501 {
502 CcDisplayLogicalMonitor *logical_monitor;
503 logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL);
504 cc_display_monitor_dbus_set_logical_monitor (self, logical_monitor);
505 cc_display_config_dbus_append_right (self->config, logical_monitor);
506 register_logical_monitor (self->config, logical_monitor);
507 }
508 else if (self->logical_monitor && !active)
509 {
510 cc_display_monitor_dbus_set_logical_monitor (self, NULL);
511 }
512
513 g_signal_emit_by_name (self, "active");
514 }
515
516 static CcDisplayRotation
517 cc_display_monitor_dbus_get_rotation (CcDisplayMonitor *pself)
518 {
519 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
520
521 if (self->logical_monitor)
522 return self->logical_monitor->rotation;
523
524 return CC_DISPLAY_ROTATION_NONE;
525 }
526
527 static void
528 cc_display_monitor_dbus_set_rotation (CcDisplayMonitor *pself,
529 CcDisplayRotation rotation)
530 {
531 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
532
533 if (!self->logical_monitor)
534 return;
535
536 if (self->logical_monitor->rotation != rotation)
537 {
538 self->logical_monitor->rotation = rotation;
539
540 g_signal_emit_by_name (self, "rotation");
541 }
542 }
543
544 static gboolean
545 cc_display_monitor_dbus_supports_rotation (CcDisplayMonitor *pself,
546 CcDisplayRotation rotation)
547 {
548 return TRUE;
549 }
550
551 static void
552 cc_display_monitor_dbus_get_physical_size (CcDisplayMonitor *pself,
553 int *w, int *h)
554 {
555 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
556
557 if (w)
558 *w = self->width_mm;
559 if (h)
560 *h = self->height_mm;
561 }
562
563 static void
564 cc_display_monitor_dbus_get_geometry (CcDisplayMonitor *pself,
565 int *x, int *y, int *w, int *h)
566 {
567 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
568 CcDisplayMode *mode = NULL;
569
570 if (self->logical_monitor)
571 {
572 if (x)
573 *x = self->logical_monitor->x;
574 if (y)
575 *y = self->logical_monitor->y;
576 }
577 else
578 {
579 if (x)
580 *x = -1;
581 if (y)
582 *y = -1;
583 }
584
585 if (self->current_mode)
586 mode = self->current_mode;
587 else if (self->preferred_mode)
588 mode = self->preferred_mode;
589 else if (self->modes)
590 mode = CC_DISPLAY_MODE (self->modes->data);
591
592 if (mode)
593 cc_display_mode_get_resolution (mode, w, h);
594 else
595 {
596 g_warning ("Monitor at %s has no modes?", self->connector_name);
597 if (w)
598 *w = -1;
599 if (h)
600 *h = -1;
601 }
602 }
603
604 static int
605 cc_display_monitor_dbus_get_min_freq (CcDisplayMonitor *pself)
606 {
607 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
608
609 return self->min_refresh_rate;
610 }
611
612 static CcDisplayMode *
613 cc_display_monitor_dbus_get_mode (CcDisplayMonitor *pself)
614 {
615 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
616
617 return self->current_mode;
618 }
619
620 static CcDisplayMode *
621 cc_display_monitor_dbus_get_preferred_mode (CcDisplayMonitor *pself)
622 {
623 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
624
625 return self->preferred_mode;
626 }
627
628 static guint32
629 cc_display_monitor_dbus_get_id (CcDisplayMonitor *pself)
630 {
631 return 0;
632 }
633
634 static GList *
635 cc_display_monitor_dbus_get_modes (CcDisplayMonitor *pself)
636 {
637 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
638
639 return self->modes;
640 }
641
642 static gboolean
643 cc_display_monitor_dbus_supports_variable_refresh_rate (CcDisplayMonitor *pself)
644 {
645 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
646
647 return self->supports_variable_refresh_rate;
648 }
649
650 static gboolean
651 cc_display_monitor_dbus_supports_underscanning (CcDisplayMonitor *pself)
652 {
653 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
654
655 return self->underscanning != UNDERSCANNING_UNSUPPORTED;
656 }
657
658 static gboolean
659 cc_display_monitor_dbus_get_underscanning (CcDisplayMonitor *pself)
660 {
661 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
662
663 return self->underscanning == UNDERSCANNING_ENABLED;
664 }
665
666 static CcDisplayMonitorPrivacy
667 cc_display_monitor_dbus_get_privacy (CcDisplayMonitor *pself)
668 {
669 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
670
671 return self->privacy_screen;
672 }
673
674 static void
675 cc_display_monitor_dbus_set_underscanning (CcDisplayMonitor *pself,
676 gboolean underscanning)
677 {
678 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
679
680 if (self->underscanning == UNDERSCANNING_UNSUPPORTED)
681 return;
682
683 if (underscanning)
684 self->underscanning = UNDERSCANNING_ENABLED;
685 else
686 self->underscanning = UNDERSCANNING_DISABLED;
687 }
688
689 static CcDisplayMode *
690 cc_display_monitor_dbus_get_closest_mode (CcDisplayMonitorDBus *self,
691 int width,
692 int height,
693 double refresh_rate,
694 CcDisplayModeRefreshRateMode refresh_rate_mode,
695 guint32 flags)
696 {
697 CcDisplayModeDBus *best = NULL;
698 GList *l;
699
700 for (l = self->modes; l != NULL; l = l->next)
701 {
702 CcDisplayModeDBus *similar = l->data;
703
704 if (similar->width != width ||
705 similar->height != height ||
706 similar->refresh_rate_mode != refresh_rate_mode)
707 continue;
708
709 if (similar->refresh_rate == refresh_rate &&
710 (similar->flags & MODE_INTERLACED) == (flags & MODE_INTERLACED))
711 {
712 best = similar;
713 break;
714 }
715
716 /* There might be a better heuristic. */
717 if (!best || best->refresh_rate < similar->refresh_rate)
718 {
719 best = similar;
720 continue;
721 }
722 }
723
724 return CC_DISPLAY_MODE (best);
725 }
726
727 static void
728 cc_display_monitor_dbus_set_mode (CcDisplayMonitor *pself,
729 CcDisplayMode *mode)
730 {
731 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
732 CcDisplayModeDBus *mode_dbus = CC_DISPLAY_MODE_DBUS (mode);
733 CcDisplayMode *new_mode;
734
735 g_return_if_fail (mode_dbus != NULL);
736
737 new_mode = cc_display_monitor_dbus_get_closest_mode (self,
738 mode_dbus->width,
739 mode_dbus->height,
740 mode_dbus->refresh_rate,
741 mode_dbus->refresh_rate_mode,
742 mode_dbus->flags);
743
744 self->current_mode = new_mode;
745
746 if (!cc_display_mode_dbus_is_supported_scale (new_mode, cc_display_monitor_get_scale (pself)))
747 cc_display_monitor_set_scale (pself, cc_display_mode_get_preferred_scale (new_mode));
748
749 g_signal_emit_by_name (self, "mode");
750 }
751
752 static void
753 cc_display_monitor_dbus_set_compatible_clone_mode (CcDisplayMonitor *pself,
754 CcDisplayMode *clone_mode)
755 {
756 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
757 GList *l;
758 CcDisplayMode *best_mode = NULL;
759 int clone_width, clone_height;
760
761 g_return_if_fail (cc_display_mode_is_clone_mode (clone_mode));
762
763 cc_display_mode_get_resolution (clone_mode, &clone_width, &clone_height);
764
765 for (l = self->modes; l; l = l->next)
766 {
767 CcDisplayMode *mode = l->data;
768 int width, height;
769
770 cc_display_mode_get_resolution (mode, &width, &height);
771 if (width != clone_width || height != clone_height)
772 continue;
773
774 if (!best_mode)
775 {
776 best_mode = mode;
777 continue;
778 }
779
780 if (cc_display_mode_get_freq_f (mode) >
781 cc_display_mode_get_freq_f (best_mode))
782 best_mode = mode;
783 }
784
785 g_return_if_fail (best_mode);
786
787 cc_display_monitor_set_mode (CC_DISPLAY_MONITOR (self), best_mode);
788 }
789
790 static void
791 cc_display_monitor_dbus_set_refresh_rate_mode (CcDisplayMonitor *pself,
792 CcDisplayModeRefreshRateMode refresh_rate_mode)
793 {
794 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
795 CcDisplayModeDBus *mode_dbus = CC_DISPLAY_MODE_DBUS (self->current_mode);
796 CcDisplayMode *new_mode;
797
798 g_return_if_fail (mode_dbus != NULL);
799
800 new_mode = cc_display_monitor_dbus_get_closest_mode (self,
801 mode_dbus->width,
802 mode_dbus->height,
803 mode_dbus->refresh_rate,
804 refresh_rate_mode,
805 mode_dbus->flags);
806
807 g_return_if_fail (new_mode != NULL);
808
809 cc_display_monitor_set_mode (CC_DISPLAY_MONITOR (self), new_mode);
810 }
811
812 static void
813 cc_display_monitor_dbus_set_position (CcDisplayMonitor *pself,
814 int x, int y)
815 {
816 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
817
818 if (self->logical_monitor)
819 {
820 gboolean notify = FALSE;
821
822 if (self->logical_monitor->x != x || self->logical_monitor->y != y)
823 notify = TRUE;
824
825 self->logical_monitor->x = x;
826 self->logical_monitor->y = y;
827
828 if (notify)
829 g_signal_emit_by_name (self, "position-changed");
830 }
831
832 }
833
834 static double
835 cc_display_monitor_dbus_get_scale (CcDisplayMonitor *pself)
836 {
837 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
838
839 if (self->logical_monitor)
840 return self->logical_monitor->scale;
841
842 return 1.0;
843 }
844
845 static void
846 cc_display_monitor_dbus_set_scale (CcDisplayMonitor *pself,
847 double scale)
848 {
849 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
850
851 if (!self->current_mode)
852 return;
853
854 if (!cc_display_mode_dbus_is_supported_scale (self->current_mode, scale))
855 return;
856
857 if (!self->logical_monitor)
858 return;
859
860 if (!G_APPROX_VALUE (self->logical_monitor->scale, scale, DBL_EPSILON))
861 {
862 self->logical_monitor->scale = scale;
863
864 g_signal_emit_by_name (self, "scale");
865 }
866 }
867
868 static void
869 cc_display_monitor_dbus_init (CcDisplayMonitorDBus *self)
870 {
871 self->underscanning = UNDERSCANNING_UNSUPPORTED;
872 self->max_width = G_MAXINT;
873 self->max_height = G_MAXINT;
874 }
875
876 static void
877 cc_display_monitor_dbus_finalize (GObject *object)
878 {
879 CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (object);
880
881 g_free (self->connector_name);
882 g_free (self->vendor_name);
883 g_free (self->product_name);
884 g_free (self->product_serial);
885 g_free (self->display_name);
886
887 g_list_free_full (self->modes, g_object_unref);
888
889 if (self->logical_monitor)
890 {
891 g_hash_table_remove (self->logical_monitor->monitors, self);
892 g_object_unref (self->logical_monitor);
893 }
894
895 G_OBJECT_CLASS (cc_display_monitor_dbus_parent_class)->finalize (object);
896 }
897
898 static void
899 cc_display_monitor_dbus_class_init (CcDisplayMonitorDBusClass *klass)
900 {
901 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
902 CcDisplayMonitorClass *parent_class = CC_DISPLAY_MONITOR_CLASS (klass);
903
904 gobject_class->finalize = cc_display_monitor_dbus_finalize;
905
906 parent_class->get_display_name = cc_display_monitor_dbus_get_display_name;
907 parent_class->get_connector_name = cc_display_monitor_dbus_get_connector_name;
908 parent_class->is_builtin = cc_display_monitor_dbus_is_builtin;
909 parent_class->is_primary = cc_display_monitor_dbus_is_primary;
910 parent_class->set_primary = cc_display_monitor_dbus_set_primary;
911 parent_class->is_active = cc_display_monitor_dbus_is_active;
912 parent_class->set_active = cc_display_monitor_dbus_set_active;
913 parent_class->get_rotation = cc_display_monitor_dbus_get_rotation;
914 parent_class->set_rotation = cc_display_monitor_dbus_set_rotation;
915 parent_class->supports_rotation = cc_display_monitor_dbus_supports_rotation;
916 parent_class->get_physical_size = cc_display_monitor_dbus_get_physical_size;
917 parent_class->get_geometry = cc_display_monitor_dbus_get_geometry;
918 parent_class->get_min_freq = cc_display_monitor_dbus_get_min_freq;
919 parent_class->get_mode = cc_display_monitor_dbus_get_mode;
920 parent_class->get_preferred_mode = cc_display_monitor_dbus_get_preferred_mode;
921 parent_class->get_id = cc_display_monitor_dbus_get_id;
922 parent_class->get_modes = cc_display_monitor_dbus_get_modes;
923 parent_class->supports_variable_refresh_rate = cc_display_monitor_dbus_supports_variable_refresh_rate;
924 parent_class->supports_underscanning = cc_display_monitor_dbus_supports_underscanning;
925 parent_class->get_underscanning = cc_display_monitor_dbus_get_underscanning;
926 parent_class->set_underscanning = cc_display_monitor_dbus_set_underscanning;
927 parent_class->get_privacy = cc_display_monitor_dbus_get_privacy;
928 parent_class->set_mode = cc_display_monitor_dbus_set_mode;
929 parent_class->set_compatible_clone_mode = cc_display_monitor_dbus_set_compatible_clone_mode;
930 parent_class->set_refresh_rate_mode = cc_display_monitor_dbus_set_refresh_rate_mode;
931 parent_class->set_position = cc_display_monitor_dbus_set_position;
932 parent_class->get_scale = cc_display_monitor_dbus_get_scale;
933 parent_class->set_scale = cc_display_monitor_dbus_set_scale;
934 }
935
936 static void
937 construct_modes (CcDisplayMonitorDBus *self,
938 GVariantIter *modes)
939 {
940 CcDisplayModeDBus *mode;
941
942 while (TRUE)
943 {
944 g_autoptr(GVariant) variant = NULL;
945
946 if (!g_variant_iter_next (modes, "@"MODE_FORMAT, &variant))
947 break;
948
949 mode = cc_display_mode_dbus_new (self, variant);
950 self->modes = g_list_prepend (self->modes, mode);
951
952 if (mode->flags & MODE_PREFERRED)
953 self->preferred_mode = CC_DISPLAY_MODE (mode);
954 if (mode->flags & MODE_CURRENT)
955 self->current_mode = CC_DISPLAY_MODE (mode);
956
957 if (mode->refresh_rate_mode == MODE_REFRESH_RATE_MODE_VARIABLE)
958 self->supports_variable_refresh_rate = TRUE;
959 }
960
961 self->modes = g_list_reverse (self->modes);
962 }
963
964 static CcDisplayMonitorDBus *
965 cc_display_monitor_dbus_new (GVariant *variant,
966 CcDisplayConfigDBus *config)
967 {
968 CcDisplayMonitorDBus *self = g_object_new (CC_TYPE_DISPLAY_MONITOR_DBUS, NULL);
969 gchar *s1, *s2, *s3, *s4;
970 g_autoptr(GVariantIter) modes = NULL;
971 g_autoptr(GVariantIter) props = NULL;
972
973 self->config = config;
974
975 g_variant_get (variant, MONITOR_FORMAT,
976 &s1, &s2, &s3, &s4, &modes, &props);
977 self->connector_name = s1;
978 self->vendor_name = s2;
979 self->product_name = s3;
980 self->product_serial = s4;
981
982 construct_modes (self, modes);
983
984 while (TRUE)
985 {
986 const char *s;
987 g_autoptr(GVariant) v = NULL;
988
989 if (!g_variant_iter_next (props, "{&sv}", &s, &v))
990 break;
991
992 if (g_str_equal (s, "width-mm"))
993 {
994 g_variant_get (v, "i", &self->width_mm);
995 }
996 else if (g_str_equal (s, "height-mm"))
997 {
998 g_variant_get (v, "i", &self->height_mm);
999 }
1000 else if (g_str_equal (s, "is-underscanning"))
1001 {
1002 gboolean underscanning = FALSE;
1003 g_variant_get (v, "b", &underscanning);
1004 if (underscanning)
1005 self->underscanning = UNDERSCANNING_ENABLED;
1006 else
1007 self->underscanning = UNDERSCANNING_DISABLED;
1008 }
1009 else if (g_str_equal (s, "max-screen-size"))
1010 {
1011 g_variant_get (v, "ii", &self->max_width, &self->max_height);
1012 }
1013 else if (g_str_equal (s, "is-builtin"))
1014 {
1015 g_variant_get (v, "b", &self->builtin);
1016 }
1017 else if (g_str_equal (s, "display-name"))
1018 {
1019 g_variant_get (v, "s", &self->display_name);
1020 }
1021 else if (g_str_equal (s, "privacy-screen-state"))
1022 {
1023 gboolean enabled;
1024 gboolean locked;
1025 g_variant_get (v, "(bb)", &enabled, &locked);
1026
1027 if (enabled)
1028 self->privacy_screen = CC_DISPLAY_MONITOR_PRIVACY_ENABLED;
1029 else
1030 self->privacy_screen = CC_DISPLAY_MONITOR_PRIVACY_DISABLED;
1031
1032 if (locked)
1033 self->privacy_screen |= CC_DISPLAY_MONITOR_PRIVACY_LOCKED;
1034 }
1035 else if (g_str_equal (s, "min-refresh-rate"))
1036 {
1037 g_variant_get (v, "i", &self->min_refresh_rate);
1038 }
1039 }
1040
1041 return self;
1042 }
1043
1044
1045 typedef enum _CcDisplayLayoutMode
1046 {
1047 CC_DISPLAY_LAYOUT_MODE_LOGICAL = 1,
1048 CC_DISPLAY_LAYOUT_MODE_PHYSICAL = 2
1049 } CcDisplayLayoutMode;
1050
1051 typedef enum _CcDisplayConfigMethod
1052 {
1053 CC_DISPLAY_CONFIG_METHOD_VERIFY = 0,
1054 CC_DISPLAY_CONFIG_METHOD_TEMPORARY = 1,
1055 CC_DISPLAY_CONFIG_METHOD_PERSISTENT = 2
1056 } CcDisplayConfigMethod;
1057
1058 struct _CcDisplayConfigDBus
1059 {
1060 CcDisplayConfig parent_instance;
1061
1062 GVariant *state;
1063 GDBusConnection *connection;
1064 GDBusProxy *proxy;
1065
1066 int min_width;
1067 int min_height;
1068
1069 guint panel_orientation_managed;
1070
1071 guint32 serial;
1072 gboolean supports_mirroring;
1073 gboolean supports_changing_layout_mode;
1074 gboolean global_scale_required;
1075 CcDisplayLayoutMode layout_mode;
1076
1077 GList *monitors;
1078 CcDisplayMonitorDBus *primary;
1079
1080 GHashTable *logical_monitors;
1081 };
1082
1083 G_DEFINE_TYPE (CcDisplayConfigDBus,
1084 cc_display_config_dbus,
1085 CC_TYPE_DISPLAY_CONFIG)
1086
1087 enum
1088 {
1089 PROP_0,
1090 PROP_STATE,
1091 PROP_CONNECTION,
1092 };
1093
1094 static GList *
1095 cc_display_config_dbus_get_monitors (CcDisplayConfig *pself)
1096 {
1097 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
1098
1099 return self->monitors;
1100 }
1101
1102 static GVariant *
1103 build_monitors_variant (GHashTable *monitors)
1104 {
1105 GVariantBuilder builder;
1106 GHashTableIter iter;
1107 CcDisplayMonitorDBus *monitor;
1108
1109 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
1110 g_hash_table_iter_init (&iter, monitors);
1111
1112 while (g_hash_table_iter_next (&iter, (void **) &monitor, NULL))
1113 {
1114 GVariantBuilder props_builder;
1115 CcDisplayModeDBus *mode_dbus;
1116
1117 if (!monitor->current_mode)
1118 continue;
1119
1120 g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}"));
1121 g_variant_builder_add (&props_builder, "{sv}",
1122 "underscanning",
1123 g_variant_new_boolean (monitor->underscanning == UNDERSCANNING_ENABLED));
1124
1125 mode_dbus = CC_DISPLAY_MODE_DBUS (monitor->current_mode);
1126 g_variant_builder_add (&builder, "(ss@*)",
1127 monitor->connector_name,
1128 mode_dbus->id,
1129 g_variant_builder_end (&props_builder));
1130 }
1131
1132 return g_variant_builder_end (&builder);
1133 }
1134
1135 static GVariant *
1136 build_logical_monitors_parameter (CcDisplayConfigDBus *self)
1137 {
1138 GVariantBuilder builder;
1139 GHashTableIter iter;
1140 CcDisplayLogicalMonitor *logical_monitor;
1141
1142 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(iiduba(ssa{sv}))"));
1143 g_hash_table_iter_init (&iter, self->logical_monitors);
1144
1145 while (g_hash_table_iter_next (&iter, (void **) &logical_monitor, NULL))
1146 g_variant_builder_add (&builder, "(iidub@*)",
1147 logical_monitor->x,
1148 logical_monitor->y,
1149 logical_monitor->scale,
1150 logical_monitor->rotation,
1151 logical_monitor->primary,
1152 build_monitors_variant (logical_monitor->monitors));
1153
1154 return g_variant_builder_end (&builder);
1155 }
1156
1157 static GVariant *
1158 build_apply_parameters (CcDisplayConfigDBus *self,
1159 CcDisplayConfigMethod method)
1160 {
1161 GVariantBuilder props_builder;
1162 g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}"));
1163
1164 if (self->supports_changing_layout_mode)
1165 g_variant_builder_add (&props_builder, "{sv}",
1166 "layout-mode", g_variant_new_uint32 (self->layout_mode));
1167
1168 return g_variant_new ("(uu@*@*)",
1169 self->serial,
1170 method,
1171 build_logical_monitors_parameter (self),
1172 g_variant_builder_end (&props_builder));
1173 }
1174
1175 static gboolean
1176 config_apply (CcDisplayConfigDBus *self,
1177 CcDisplayConfigMethod method,
1178 GError **error)
1179 {
1180 g_autoptr(GVariant) retval = NULL;
1181
1182 cc_display_config_dbus_ensure_non_offset_coords (self);
1183
1184 retval = g_dbus_proxy_call_sync (self->proxy,
1185 "ApplyMonitorsConfig",
1186 build_apply_parameters (self, method),
1187 G_DBUS_CALL_FLAGS_NO_AUTO_START,
1188 -1,
1189 NULL,
1190 error);
1191 return retval != NULL;
1192 }
1193
1194 static gboolean
1195 cc_display_config_dbus_is_applicable (CcDisplayConfig *pself)
1196 {
1197 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
1198 g_autoptr(GError) error = NULL;
1199
1200 if (!config_apply (self, CC_DISPLAY_CONFIG_METHOD_VERIFY, &error))
1201 {
1202 g_warning ("Config not applicable: %s", error->message);
1203 return FALSE;
1204 }
1205 else
1206 {
1207 return TRUE;
1208 }
1209 }
1210
1211 static CcDisplayMonitorDBus *
1212 monitor_from_spec (CcDisplayConfigDBus *self,
1213 const gchar *connector,
1214 const gchar *vendor,
1215 const gchar *product,
1216 const gchar *serial)
1217 {
1218 GList *l;
1219 for (l = self->monitors; l != NULL; l = l->next)
1220 {
1221 CcDisplayMonitorDBus *m = l->data;
1222 if (g_str_equal (m->connector_name, connector) &&
1223 g_str_equal (m->vendor_name, vendor) &&
1224 g_str_equal (m->product_name, product) &&
1225 g_str_equal (m->product_serial, serial))
1226 return m;
1227 }
1228 return NULL;
1229 }
1230
1231 static gboolean
1232 cc_display_config_dbus_equal (CcDisplayConfig *pself,
1233 CcDisplayConfig *pother)
1234 {
1235 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
1236 CcDisplayConfigDBus *other = CC_DISPLAY_CONFIG_DBUS (pother);
1237 GList *l;
1238
1239 g_return_val_if_fail (pself, FALSE);
1240 g_return_val_if_fail (pother, FALSE);
1241
1242 cc_display_config_dbus_ensure_non_offset_coords (self);
1243 cc_display_config_dbus_ensure_non_offset_coords (other);
1244
1245 for (l = self->monitors; l != NULL; l = l->next)
1246 {
1247 CcDisplayMonitorDBus *m1 = l->data;
1248 CcDisplayMonitorDBus *m2 = monitor_from_spec (other,
1249 m1->connector_name,
1250 m1->vendor_name,
1251 m1->product_name,
1252 m1->product_serial);
1253 if (!m2)
1254 return FALSE;
1255
1256 if (m1->underscanning != m2->underscanning)
1257 return FALSE;
1258
1259 if (!cc_display_logical_monitor_equal (m1->logical_monitor, m2->logical_monitor))
1260 return FALSE;
1261
1262 /* Modes should not be compared if both monitors have no logical monitor. */
1263 if (m1->logical_monitor == NULL && m2->logical_monitor == NULL)
1264 continue;
1265
1266 if (!cc_display_mode_dbus_equal (CC_DISPLAY_MODE_DBUS (m1->current_mode),
1267 CC_DISPLAY_MODE_DBUS (m2->current_mode)))
1268 return FALSE;
1269 }
1270
1271 return TRUE;
1272 }
1273
1274 static void
1275 cc_display_config_dbus_set_primary (CcDisplayConfigDBus *self,
1276 CcDisplayMonitorDBus *new_primary)
1277 {
1278 if (self->primary == new_primary)
1279 return;
1280
1281 if (!new_primary->logical_monitor)
1282 return;
1283
1284 if (self->primary && self->primary->logical_monitor)
1285 {
1286 self->primary->logical_monitor->primary = FALSE;
1287 g_signal_emit_by_name (self->primary, "primary");
1288 }
1289
1290 self->primary = new_primary;
1291 self->primary->logical_monitor->primary = TRUE;
1292
1293 g_signal_emit_by_name (self->primary, "primary");
1294 g_signal_emit_by_name (self, "primary");
1295 }
1296
1297 static void
1298 cc_display_config_dbus_unset_primary (CcDisplayConfigDBus *self,
1299 CcDisplayMonitorDBus *old_primary)
1300 {
1301 GList *l;
1302
1303 if (self->primary != old_primary)
1304 return;
1305
1306 for (l = self->monitors; l != NULL; l = l->next)
1307 {
1308 CcDisplayMonitorDBus *monitor = l->data;
1309 if (monitor->logical_monitor &&
1310 monitor != old_primary)
1311 {
1312 cc_display_config_dbus_set_primary (self, monitor);
1313 break;
1314 }
1315 }
1316
1317 if (self->primary == old_primary)
1318 self->primary = NULL;
1319 }
1320
1321 static gboolean
1322 cc_display_config_dbus_is_cloning (CcDisplayConfig *pself)
1323 {
1324 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
1325 guint n_active_monitors = 0;
1326 GList *l;
1327
1328 for (l = self->monitors; l != NULL; l = l->next)
1329 if (cc_display_monitor_is_active (CC_DISPLAY_MONITOR (l->data)))
1330 n_active_monitors += 1;
1331
1332 return n_active_monitors > 1 && g_hash_table_size (self->logical_monitors) == 1;
1333 }
1334
1335 static void
1336 cc_display_config_dbus_set_cloning (CcDisplayConfig *pself,
1337 gboolean clone)
1338 {
1339 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
1340 gboolean is_cloning = cc_display_config_is_cloning (pself);
1341 CcDisplayLogicalMonitor *logical_monitor;
1342 GList *l;
1343
1344 if (clone && !is_cloning)
1345 {
1346 logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL);
1347 for (l = self->monitors; l != NULL; l = l->next)
1348 {
1349 if (!cc_display_monitor_is_usable (l->data))
1350 continue;
1351 cc_display_monitor_dbus_set_logical_monitor (CC_DISPLAY_MONITOR_DBUS (l->data),
1352 logical_monitor);
1353 }
1354 register_logical_monitor (self, logical_monitor);
1355 }
1356 else if (!clone && is_cloning)
1357 {
1358 for (l = self->monitors; l != NULL; l = l->next)
1359 {
1360 logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL);
1361 cc_display_monitor_dbus_set_logical_monitor (CC_DISPLAY_MONITOR_DBUS (l->data),
1362 logical_monitor);
1363 register_logical_monitor (self, logical_monitor);
1364 }
1365 cc_display_config_dbus_make_linear (self);
1366 }
1367 }
1368
1369 static gboolean
1370 mode_supports_scale (CcDisplayMode *mode,
1371 double scale)
1372 {
1373 g_autoptr(GArray) scales = NULL;
1374 int i;
1375
1376 scales = cc_display_mode_get_supported_scales (mode);
1377 for (i = 0; i < scales->len; i++)
1378 {
1379 if (G_APPROX_VALUE (scale, g_array_index (scales, double, i),
1380 DBL_EPSILON))
1381 return TRUE;
1382 }
1383
1384 return FALSE;
1385 }
1386
1387 static void
1388 remove_unsupported_scales (CcDisplayMode *mode,
1389 GArray *supported_scales)
1390 {
1391 g_autoptr(GArray) mode_scales = NULL;
1392 int i;
1393
1394 mode_scales = cc_display_mode_get_supported_scales (mode);
1395 i = 0;
1396 while (i < supported_scales->len)
1397 {
1398 double scale;
1399
1400 if (i == supported_scales->len)
1401 break;
1402
1403 scale = g_array_index (supported_scales, double, i);
1404
1405 if (mode_supports_scale (mode, scale))
1406 {
1407 i++;
1408 continue;
1409 }
1410
1411 g_array_remove_range (supported_scales, i, 1);
1412 }
1413 }
1414
1415 static gboolean
1416 monitor_has_compatible_clone_mode (CcDisplayMonitorDBus *monitor,
1417 CcDisplayModeDBus *mode,
1418 GArray *supported_scales)
1419 {
1420 GList *l;
1421
1422 for (l = monitor->modes; l; l = l->next)
1423 {
1424 CcDisplayModeDBus *other_mode = l->data;
1425
1426 if (other_mode->width != mode->width ||
1427 other_mode->height != mode->height)
1428 continue;
1429
1430 if ((other_mode->flags & MODE_INTERLACED) !=
1431 (mode->flags & MODE_INTERLACED))
1432 continue;
1433
1434 remove_unsupported_scales (CC_DISPLAY_MODE (other_mode), supported_scales);
1435
1436 return TRUE;
1437 }
1438
1439 return FALSE;
1440 }
1441
1442 static gboolean
1443 monitors_has_compatible_clone_mode (CcDisplayConfigDBus *self,
1444 CcDisplayModeDBus *mode,
1445 GArray *supported_scales)
1446 {
1447 GList *l;
1448
1449 for (l = self->monitors; l; l = l->next)
1450 {
1451 CcDisplayMonitorDBus *monitor = l->data;
1452
1453 if (!monitor_has_compatible_clone_mode (monitor, mode, supported_scales))
1454 return FALSE;
1455 }
1456
1457 return TRUE;
1458 }
1459
1460 static gboolean
1461 is_mode_better (CcDisplayModeDBus *mode,
1462 CcDisplayModeDBus *other_mode)
1463 {
1464 if (mode->width * mode->height > other_mode->width * other_mode->height)
1465 return TRUE;
1466 else if (mode->width * mode->height < other_mode->width * other_mode->height)
1467 return FALSE;
1468
1469 if (!(mode->flags & MODE_INTERLACED) &&
1470 (other_mode->flags & MODE_INTERLACED))
1471 return TRUE;
1472
1473 return FALSE;
1474 }
1475
1476 static GList *
1477 cc_display_config_dbus_generate_cloning_modes (CcDisplayConfig *pself)
1478 {
1479 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
1480 CcDisplayMonitorDBus *base_monitor = NULL;
1481 GList *l;
1482 GList *clone_modes = NULL;
1483 CcDisplayModeDBus *best_mode = NULL;
1484
1485 for (l = self->monitors; l; l = l->next)
1486 {
1487 CcDisplayMonitor *monitor = l->data;
1488
1489 if (cc_display_monitor_is_active (monitor))
1490 {
1491 base_monitor = CC_DISPLAY_MONITOR_DBUS (monitor);
1492 break;
1493 }
1494 }
1495
1496 if (!base_monitor)
1497 return NULL;
1498
1499 for (l = base_monitor->modes; l; l = l->next)
1500 {
1501 CcDisplayModeDBus *mode = l->data;
1502 CcDisplayModeDBus *virtual_mode;
1503 g_autoptr (GArray) supported_scales = NULL;
1504
1505 supported_scales =
1506 cc_display_mode_get_supported_scales (CC_DISPLAY_MODE (mode));
1507
1508 if (!monitors_has_compatible_clone_mode (self, mode, supported_scales))
1509 continue;
1510
1511 virtual_mode = cc_display_mode_dbus_new_virtual (mode->width,
1512 mode->height,
1513 mode->preferred_scale,
1514 supported_scales);
1515 clone_modes = g_list_append (clone_modes, virtual_mode);
1516
1517 if (!best_mode || is_mode_better (virtual_mode, best_mode))
1518 best_mode = virtual_mode;
1519 }
1520
1521 if (best_mode)
1522 best_mode->flags |= MODE_PREFERRED;
1523
1524 return clone_modes;
1525 }
1526
1527 static gboolean
1528 cc_display_config_dbus_apply (CcDisplayConfig *pself,
1529 GError **error)
1530 {
1531 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
1532
1533 return config_apply (self, CC_DISPLAY_CONFIG_METHOD_PERSISTENT, error);
1534 }
1535
1536 static gboolean
1537 cc_display_config_dbus_is_layout_logical (CcDisplayConfig *pself)
1538 {
1539 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
1540
1541 return self->layout_mode == CC_DISPLAY_LAYOUT_MODE_LOGICAL;
1542 }
1543
1544 static gboolean
1545 is_scale_allowed_by_active_monitors (CcDisplayConfigDBus *self,
1546 CcDisplayMode *mode,
1547 double scale);
1548
1549 static gboolean
1550 is_scaled_mode_allowed (CcDisplayConfigDBus *self,
1551 CcDisplayModeDBus *mode,
1552 double scale)
1553 {
1554 gint width, height;
1555
1556 /* Do the math as if the monitor is always in landscape mode. */
1557 width = round (mode->width / scale);
1558 height = round (mode->height / scale);
1559
1560 if (MAX (width, height) < self->min_width ||
1561 MIN (width, height) < self->min_height)
1562 return FALSE;
1563
1564 if (!self->global_scale_required)
1565 return TRUE;
1566
1567 return is_scale_allowed_by_active_monitors (self, CC_DISPLAY_MODE (mode), scale);
1568 }
1569
1570 static gboolean
1571 is_scale_allowed_by_active_monitors (CcDisplayConfigDBus *self,
1572 CcDisplayMode *mode,
1573 double scale)
1574 {
1575 GList *l;
1576
1577 for (l = self->monitors; l != NULL; l = l->next)
1578 {
1579 CcDisplayMonitorDBus *m = CC_DISPLAY_MONITOR_DBUS (l->data);
1580
1581 if (!cc_display_monitor_is_active (CC_DISPLAY_MONITOR (m)))
1582 continue;
1583
1584 if (!cc_display_mode_dbus_is_supported_scale (mode, scale))
1585 return FALSE;
1586 }
1587
1588 return TRUE;
1589 }
1590
1591 static GArray *
1592 cc_display_mode_dbus_get_supported_scales (CcDisplayMode *pself)
1593 {
1594 CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
1595 CcDisplayConfig *config = CC_DISPLAY_CONFIG (self->monitor->config);
1596
1597 if (cc_display_config_is_cloning (config))
1598 {
1599 GArray *scales = g_array_copy (self->supported_scales);
1600 int i;
1601
1602 for (i = scales->len - 1; i >= 0; i--)
1603 {
1604 double scale = g_array_index (scales, double, i);
1605
1606 if (!is_scale_allowed_by_active_monitors (self->monitor->config,
1607 pself, scale))
1608 g_array_remove_index (scales, i);
1609 }
1610
1611 return g_steal_pointer (&scales);
1612 }
1613
1614 return g_array_ref (self->supported_scales);
1615 }
1616
1617 static void
1618 filter_out_invalid_scaled_modes (CcDisplayConfigDBus *self)
1619 {
1620 GList *l;
1621
1622 for (l = self->monitors; l; l = l->next)
1623 {
1624 CcDisplayMonitorDBus *monitor = l->data;
1625 GList *ll = monitor->modes;
1626
1627 while (ll != NULL)
1628 {
1629 CcDisplayModeDBus *mode = ll->data;
1630 GList *current = ll;
1631 double current_scale = -1;
1632 int i;
1633
1634 ll = ll->next;
1635
1636 if (monitor->current_mode != CC_DISPLAY_MODE (mode) &&
1637 monitor->preferred_mode != CC_DISPLAY_MODE (mode) &&
1638 !is_scaled_mode_allowed (self, mode, 1.0))
1639 {
1640 g_clear_object (&mode);
1641 monitor->modes = g_list_delete_link (monitor->modes, current);
1642 continue;
1643 }
1644
1645 if (monitor->current_mode == CC_DISPLAY_MODE (mode))
1646 current_scale = cc_display_monitor_dbus_get_scale (CC_DISPLAY_MONITOR (monitor));
1647
1648 for (i = mode->supported_scales->len - 1; i >= 0; i--)
1649 {
1650 float scale = g_array_index (mode->supported_scales, double, i);
1651
1652 if (!G_APPROX_VALUE (scale, current_scale, DBL_EPSILON) &&
1653 !G_APPROX_VALUE (scale, mode->preferred_scale, DBL_EPSILON) &&
1654 !is_scaled_mode_allowed (self, mode, scale))
1655 {
1656 g_array_remove_index (mode->supported_scales, i);
1657 }
1658 }
1659 }
1660 }
1661 }
1662
1663 static void
1664 cc_display_config_dbus_set_minimum_size (CcDisplayConfig *pself,
1665 int width,
1666 int height)
1667 {
1668 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
1669
1670 g_assert (width >= 0 && height >= 0);
1671 g_assert (((self->min_width == 0 && self->min_height == 0) ||
1672 (self->min_width >= width && self->min_height >= height)) &&
1673 "Minimum size can't be set again to higher values");
1674
1675 self->min_width = width;
1676 self->min_height = height;
1677
1678 filter_out_invalid_scaled_modes (self);
1679 }
1680
1681 static gboolean
1682 cc_display_config_dbus_is_scaled_mode_valid (CcDisplayConfig *pself,
1683 CcDisplayMode *mode,
1684 double scale)
1685 {
1686 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
1687
1688 if (cc_display_config_is_cloning (pself))
1689 return is_scale_allowed_by_active_monitors (self, mode, scale);
1690
1691 return cc_display_mode_dbus_is_supported_scale (mode, scale);
1692 }
1693
1694 static gboolean
1695 cc_display_config_dbus_get_panel_orientation_managed (CcDisplayConfig *pself)
1696 {
1697 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
1698
1699 return self->panel_orientation_managed;
1700 }
1701
1702 static void
1703 cc_display_config_dbus_init (CcDisplayConfigDBus *self)
1704 {
1705 self->serial = 0;
1706 self->supports_mirroring = TRUE;
1707 self->supports_changing_layout_mode = FALSE;
1708 self->global_scale_required = FALSE;
1709 self->layout_mode = CC_DISPLAY_LAYOUT_MODE_LOGICAL;
1710 self->logical_monitors = g_hash_table_new (NULL, NULL);
1711 }
1712
1713 static void
1714 remove_logical_monitor (gpointer data,
1715 GObject *object)
1716 {
1717 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (data);
1718
1719 g_hash_table_remove (self->logical_monitors, object);
1720 }
1721
1722 static void
1723 register_logical_monitor (CcDisplayConfigDBus *self,
1724 CcDisplayLogicalMonitor *logical_monitor)
1725 {
1726 g_hash_table_add (self->logical_monitors, logical_monitor);
1727 g_object_weak_ref (G_OBJECT (logical_monitor), remove_logical_monitor, self);
1728 g_object_unref (logical_monitor);
1729 }
1730
1731 static void
1732 apply_global_scale_requirement (CcDisplayConfigDBus *self,
1733 CcDisplayMonitor *monitor)
1734 {
1735 GList *l;
1736 double scale = cc_display_monitor_get_scale (monitor);
1737
1738 for (l = self->monitors; l != NULL; l = l->next)
1739 {
1740 CcDisplayMonitor *m = l->data;
1741 if (m != monitor)
1742 cc_display_monitor_set_scale (m, scale);
1743 }
1744 }
1745
1746 static void
1747 construct_monitors (CcDisplayConfigDBus *self,
1748 GVariantIter *monitors,
1749 GVariantIter *logical_monitors)
1750 {
1751 while (TRUE)
1752 {
1753 CcDisplayMonitorDBus *monitor;
1754 g_autoptr(GVariant) variant = NULL;
1755
1756 if (!g_variant_iter_next (monitors, "@"MONITOR_FORMAT, &variant))
1757 break;
1758
1759 monitor = cc_display_monitor_dbus_new (variant, self);
1760 self->monitors = g_list_prepend (self->monitors, monitor);
1761
1762 if (self->global_scale_required)
1763 g_signal_connect_object (monitor, "scale",
1764 G_CALLBACK (apply_global_scale_requirement),
1765 self, G_CONNECT_SWAPPED);
1766 }
1767
1768 self->monitors = g_list_reverse (self->monitors);
1769
1770 while (TRUE)
1771 {
1772 g_autoptr(GVariant) variant = NULL;
1773 CcDisplayLogicalMonitor *logical_monitor;
1774 g_autoptr(GVariantIter) monitor_specs = NULL;
1775 const gchar *s1, *s2, *s3, *s4;
1776 gboolean primary;
1777
1778 if (!g_variant_iter_next (logical_monitors, "@"LOGICAL_MONITOR_FORMAT, &variant))
1779 break;
1780
1781 logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL);
1782 g_variant_get (variant, LOGICAL_MONITOR_FORMAT,
1783 &logical_monitor->x,
1784 &logical_monitor->y,
1785 &logical_monitor->scale,
1786 &logical_monitor->rotation,
1787 &primary,
1788 &monitor_specs,
1789 NULL);
1790
1791 while (g_variant_iter_next (monitor_specs, "(&s&s&s&s)", &s1, &s2, &s3, &s4))
1792 {
1793 CcDisplayMonitorDBus *m = monitor_from_spec (self, s1, s2, s3, s4);
1794 if (!m)
1795 {
1796 g_warning ("Couldn't find monitor given spec: %s, %s, %s, %s",
1797 s1, s2, s3, s4);
1798 continue;
1799 }
1800
1801 cc_display_monitor_dbus_set_logical_monitor (m, logical_monitor);
1802 }
1803
1804 if (g_hash_table_size (logical_monitor->monitors) > 0)
1805 {
1806 if (primary)
1807 {
1808 CcDisplayMonitorDBus *m = NULL;
1809 GHashTableIter iter;
1810 g_hash_table_iter_init (&iter, logical_monitor->monitors);
1811 g_hash_table_iter_next (&iter, (void **) &m, NULL);
1812
1813 cc_display_config_dbus_set_primary (self, m);
1814 }
1815 }
1816 else
1817 {
1818 g_warning ("Got an empty logical monitor, ignoring");
1819 }
1820
1821 register_logical_monitor (self, logical_monitor);
1822 }
1823 }
1824
1825 static void
1826 update_panel_orientation_managed (CcDisplayConfigDBus *self)
1827 {
1828 g_autoptr(GVariant) v = NULL;
1829 gboolean panel_orientation_managed = FALSE;
1830
1831 if (self->proxy != NULL)
1832 {
1833 v = g_dbus_proxy_get_cached_property (self->proxy, "PanelOrientationManaged");
1834 if (v)
1835 {
1836 panel_orientation_managed = g_variant_get_boolean (v);
1837 }
1838 }
1839
1840 if (panel_orientation_managed == self->panel_orientation_managed)
1841 return;
1842
1843 self->panel_orientation_managed = panel_orientation_managed;
1844 g_signal_emit_by_name (self, "panel-orientation-managed", self->panel_orientation_managed);
1845 }
1846
1847 static void
1848 proxy_properties_changed_cb (CcDisplayConfigDBus *self,
1849 GVariant *changed_properties,
1850 GStrv invalidated_properties)
1851 {
1852 GVariantDict dict;
1853
1854 g_variant_dict_init (&dict, changed_properties);
1855
1856 if (g_variant_dict_contains (&dict, "PanelOrientationManaged"))
1857 update_panel_orientation_managed (self);
1858 }
1859
1860 static void
1861 cc_display_config_dbus_constructed (GObject *object)
1862 {
1863 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object);
1864 g_autoptr(GVariantIter) monitors = NULL;
1865 g_autoptr(GVariantIter) logical_monitors = NULL;
1866 g_autoptr(GVariantIter) props = NULL;
1867 g_autoptr(GError) error = NULL;
1868
1869 g_variant_get (self->state,
1870 CURRENT_STATE_FORMAT,
1871 &self->serial,
1872 &monitors,
1873 &logical_monitors,
1874 &props);
1875
1876 while (TRUE)
1877 {
1878 const char *s;
1879 g_autoptr(GVariant) v = NULL;
1880
1881 if (!g_variant_iter_next (props, "{&sv}", &s, &v))
1882 break;
1883
1884 if (g_str_equal (s, "supports-mirroring"))
1885 {
1886 g_variant_get (v, "b", &self->supports_mirroring);
1887 }
1888 else if (g_str_equal (s, "supports-changing-layout-mode"))
1889 {
1890 g_variant_get (v, "b", &self->supports_changing_layout_mode);
1891 }
1892 else if (g_str_equal (s, "global-scale-required"))
1893 {
1894 g_variant_get (v, "b", &self->global_scale_required);
1895 }
1896 else if (g_str_equal (s, "layout-mode"))
1897 {
1898 guint32 u = 0;
1899 g_variant_get (v, "u", &u);
1900 if (u >= CC_DISPLAY_LAYOUT_MODE_LOGICAL &&
1901 u <= CC_DISPLAY_LAYOUT_MODE_PHYSICAL)
1902 self->layout_mode = u;
1903 }
1904 }
1905
1906 construct_monitors (self, monitors, logical_monitors);
1907 filter_out_invalid_scaled_modes (self);
1908
1909 self->proxy = g_dbus_proxy_new_sync (self->connection,
1910 G_DBUS_PROXY_FLAGS_NONE,
1911 NULL,
1912 "org.gnome.Mutter.DisplayConfig",
1913 "/org/gnome/Mutter/DisplayConfig",
1914 "org.gnome.Mutter.DisplayConfig",
1915 NULL,
1916 &error);
1917 if (error)
1918 g_warning ("Could not create DisplayConfig proxy: %s", error->message);
1919
1920 g_signal_connect_swapped (self->proxy, "g-properties-changed",
1921 G_CALLBACK (proxy_properties_changed_cb), self);
1922 update_panel_orientation_managed (self);
1923
1924 G_OBJECT_CLASS (cc_display_config_dbus_parent_class)->constructed (object);
1925 }
1926
1927 static void
1928 cc_display_config_dbus_set_property (GObject *object,
1929 guint prop_id,
1930 const GValue *value,
1931 GParamSpec *pspec)
1932 {
1933 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object);
1934
1935 switch (prop_id)
1936 {
1937 case PROP_STATE:
1938 self->state = g_value_dup_variant (value);
1939 break;
1940 case PROP_CONNECTION:
1941 self->connection = g_value_dup_object (value);
1942 break;
1943 default:
1944 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1945 }
1946 }
1947
1948 static void
1949 cc_display_config_dbus_get_property (GObject *object,
1950 guint prop_id,
1951 GValue *value,
1952 GParamSpec *pspec)
1953 {
1954 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object);
1955
1956 switch (prop_id)
1957 {
1958 case PROP_STATE:
1959 g_value_set_variant (value, self->state);
1960 break;
1961 case PROP_CONNECTION:
1962 g_value_set_object (value, self->connection);
1963 break;
1964 default:
1965 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1966 }
1967 }
1968
1969 static void
1970 cc_display_config_dbus_dispose (GObject *object)
1971 {
1972 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object);
1973
1974 if (self->logical_monitors)
1975 {
1976 GHashTableIter iter;
1977 gpointer monitor;
1978
1979 g_hash_table_iter_init (&iter, self->logical_monitors);
1980
1981 while (g_hash_table_iter_next (&iter, &monitor, NULL))
1982 g_object_weak_unref (G_OBJECT (monitor), remove_logical_monitor, self);
1983 }
1984
1985 G_OBJECT_CLASS (cc_display_config_dbus_parent_class)->dispose (object);
1986 }
1987
1988 static void
1989 cc_display_config_dbus_finalize (GObject *object)
1990 {
1991 CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object);
1992
1993 g_clear_pointer (&self->state, g_variant_unref);
1994 g_clear_object (&self->connection);
1995 g_clear_object (&self->proxy);
1996
1997 g_clear_list (&self->monitors, g_object_unref);
1998 g_clear_pointer (&self->logical_monitors, g_hash_table_destroy);
1999
2000 G_OBJECT_CLASS (cc_display_config_dbus_parent_class)->finalize (object);
2001 }
2002
2003 static void
2004 cc_display_config_dbus_class_init (CcDisplayConfigDBusClass *klass)
2005 {
2006 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2007 CcDisplayConfigClass *parent_class = CC_DISPLAY_CONFIG_CLASS (klass);
2008 GParamSpec *pspec;
2009
2010 gobject_class->constructed = cc_display_config_dbus_constructed;
2011 gobject_class->set_property = cc_display_config_dbus_set_property;
2012 gobject_class->get_property = cc_display_config_dbus_get_property;
2013 gobject_class->dispose = cc_display_config_dbus_dispose;
2014 gobject_class->finalize = cc_display_config_dbus_finalize;
2015
2016 parent_class->get_monitors = cc_display_config_dbus_get_monitors;
2017 parent_class->is_applicable = cc_display_config_dbus_is_applicable;
2018 parent_class->equal = cc_display_config_dbus_equal;
2019 parent_class->apply = cc_display_config_dbus_apply;
2020 parent_class->is_cloning = cc_display_config_dbus_is_cloning;
2021 parent_class->set_cloning = cc_display_config_dbus_set_cloning;
2022 parent_class->generate_cloning_modes = cc_display_config_dbus_generate_cloning_modes;
2023 parent_class->is_layout_logical = cc_display_config_dbus_is_layout_logical;
2024 parent_class->is_scaled_mode_valid = cc_display_config_dbus_is_scaled_mode_valid;
2025 parent_class->set_minimum_size = cc_display_config_dbus_set_minimum_size;
2026 parent_class->get_panel_orientation_managed =
2027 cc_display_config_dbus_get_panel_orientation_managed;
2028
2029 pspec = g_param_spec_variant ("state",
2030 "GVariant",
2031 "GVariant",
2032 G_VARIANT_TYPE (CURRENT_STATE_FORMAT),
2033 NULL,
2034 G_PARAM_READWRITE |
2035 G_PARAM_STATIC_STRINGS |
2036 G_PARAM_CONSTRUCT_ONLY);
2037 g_object_class_install_property (gobject_class, PROP_STATE, pspec);
2038
2039 pspec = g_param_spec_object ("connection",
2040 "GDBusConnection",
2041 "GDBusConnection",
2042 G_TYPE_DBUS_CONNECTION,
2043 G_PARAM_READWRITE |
2044 G_PARAM_STATIC_STRINGS |
2045 G_PARAM_CONSTRUCT_ONLY);
2046 g_object_class_install_property (gobject_class, PROP_CONNECTION, pspec);
2047 }
2048
2049 static gint
2050 sort_x_axis (gconstpointer a, gconstpointer b)
2051 {
2052 const CcDisplayLogicalMonitor *ma = a;
2053 const CcDisplayLogicalMonitor *mb = b;
2054 return ma->x - mb->x;
2055 }
2056
2057 static gint
2058 sort_y_axis (gconstpointer a, gconstpointer b)
2059 {
2060 const CcDisplayLogicalMonitor *ma = a;
2061 const CcDisplayLogicalMonitor *mb = b;
2062 return ma->y - mb->y;
2063 }
2064
2065 static void
2066 add_x_delta (gpointer d1, gpointer d2)
2067 {
2068 CcDisplayLogicalMonitor *m = d1;
2069 int delta = GPOINTER_TO_INT (d2);
2070 m->x += delta;
2071 }
2072
2073 static gboolean
2074 logical_monitor_is_rotated (CcDisplayLogicalMonitor *lm)
2075 {
2076 switch (lm->rotation)
2077 {
2078 case CC_DISPLAY_ROTATION_90:
2079 case CC_DISPLAY_ROTATION_270:
2080 case CC_DISPLAY_ROTATION_90_FLIPPED:
2081 case CC_DISPLAY_ROTATION_270_FLIPPED:
2082 return TRUE;
2083 default:
2084 return FALSE;
2085 }
2086 }
2087
2088 static int
2089 logical_monitor_width (CcDisplayLogicalMonitor *lm)
2090 {
2091 CcDisplayMonitorDBus *monitor;
2092 CcDisplayModeDBus *mode;
2093 GHashTableIter iter;
2094 int width;
2095
2096 g_hash_table_iter_init (&iter, lm->monitors);
2097 g_hash_table_iter_next (&iter, (void **) &monitor, NULL);
2098 mode = CC_DISPLAY_MODE_DBUS (monitor->current_mode);
2099 if (logical_monitor_is_rotated (lm))
2100 width = mode ? mode->height : 0;
2101 else
2102 width = mode ? mode->width : 0;
2103
2104 if (monitor->config->layout_mode == CC_DISPLAY_LAYOUT_MODE_LOGICAL)
2105 return round (width / lm->scale);
2106 else
2107 return width;
2108 }
2109
2110 static void
2111 add_y_delta (gpointer d1, gpointer d2)
2112 {
2113 CcDisplayLogicalMonitor *m = d1;
2114 int delta = GPOINTER_TO_INT (d2);
2115 m->y += delta;
2116 }
2117
2118 static void
2119 cc_display_config_dbus_ensure_non_offset_coords (CcDisplayConfigDBus *self)
2120 {
2121 GList *x_axis, *y_axis;
2122 CcDisplayLogicalMonitor *m;
2123
2124 if (g_hash_table_size (self->logical_monitors) == 0)
2125 return;
2126
2127 x_axis = g_hash_table_get_keys (self->logical_monitors);
2128 x_axis = g_list_sort (x_axis, sort_x_axis);
2129 y_axis = g_hash_table_get_keys (self->logical_monitors);
2130 y_axis = g_list_sort (y_axis, sort_y_axis);
2131
2132 m = x_axis->data;
2133 if (m->x != 0)
2134 g_list_foreach (x_axis, add_x_delta, GINT_TO_POINTER (- m->x));
2135
2136 m = y_axis->data;
2137 if (m->y != 0)
2138 g_list_foreach (y_axis, add_y_delta, GINT_TO_POINTER (- m->y));
2139
2140 g_list_free (x_axis);
2141 g_list_free (y_axis);
2142 }
2143
2144 static void
2145 cc_display_config_dbus_append_right (CcDisplayConfigDBus *self,
2146 CcDisplayLogicalMonitor *monitor)
2147 {
2148 GList *x_axis;
2149 CcDisplayLogicalMonitor *last;
2150
2151 if (g_hash_table_size (self->logical_monitors) == 0)
2152 {
2153 monitor->x = 0;
2154 monitor->y = 0;
2155 return;
2156 }
2157
2158 x_axis = g_hash_table_get_keys (self->logical_monitors);
2159 x_axis = g_list_sort (x_axis, sort_x_axis);
2160 last = g_list_last (x_axis)->data;
2161 monitor->x = last->x + logical_monitor_width (last);
2162 monitor->y = last->y;
2163
2164 g_list_free (x_axis);
2165 }
2166
2167 static void
2168 cc_display_config_dbus_make_linear (CcDisplayConfigDBus *self)
2169 {
2170 CcDisplayLogicalMonitor *primary;
2171 GList *logical_monitors, *l;
2172 int x;
2173
2174 if (self->primary && self->primary->logical_monitor)
2175 {
2176 primary = self->primary->logical_monitor;
2177 primary->x = primary->y = 0;
2178 x = logical_monitor_width (primary);
2179 }
2180 else
2181 {
2182 primary = NULL;
2183 x = 0;
2184 }
2185
2186 logical_monitors = g_hash_table_get_keys (self->logical_monitors);
2187 for (l = logical_monitors; l != NULL; l = l->next)
2188 {
2189 CcDisplayLogicalMonitor *m = l->data;
2190
2191 if (m == primary)
2192 continue;
2193
2194 m->x = x;
2195 m->y = 0;
2196 x += logical_monitor_width (m);
2197 }
2198
2199 g_list_free (logical_monitors);
2200 }
2201