1 /* gnome-rr.c
2  *
3  * Copyright 2007, 2008, 2013 Red Hat, Inc.
4  * Copyright 2020 NVIDIA CORPORATION
5  *
6  * This file is part of the Gnome Library.
7  *
8  * The Gnome Library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * The Gnome Library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
20  * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  * Author: Soren Sandmann <sandmann@redhat.com>
24  *         Giovanni Campagna <gcampagn@redhat.com>
25  */
26 
27 #define GNOME_DESKTOP_USE_UNSTABLE_API
28 
29 #include <config.h>
30 #include <glib/gi18n-lib.h>
31 #include <string.h>
32 
33 #include <gtk/gtk.h>
34 
35 #undef GNOME_DISABLE_DEPRECATED
36 #include "gnome-rr.h"
37 #include "gnome-rr-config.h"
38 
39 #include "gnome-rr-private.h"
40 
41 /* From xf86drmMode.h: it's ABI so it won't change */
42 #define DRM_MODE_FLAG_INTERLACE			(1<<4)
43 
44 enum {
45     SCREEN_PROP_0,
46     SCREEN_PROP_GDK_SCREEN,
47     SCREEN_PROP_DPMS_MODE,
48     SCREEN_PROP_LAST,
49 };
50 
51 enum {
52     SCREEN_CHANGED,
53     SCREEN_OUTPUT_CONNECTED,
54     SCREEN_OUTPUT_DISCONNECTED,
55     SCREEN_SIGNAL_LAST,
56 };
57 
58 static gint screen_signals[SCREEN_SIGNAL_LAST];
59 
60 struct GnomeRROutput
61 {
62     ScreenInfo *	info;
63     guint		id;
64     glong               winsys_id;
65 
66     char *		name;
67     char *		display_name;
68     char *		connector_type;
69     GnomeRRCrtc *	current_crtc;
70     GnomeRRCrtc **	possible_crtcs;
71     GnomeRROutput **	clones;
72     GnomeRRMode **	modes;
73 
74     char *              vendor;
75     char *              product;
76     char *              serial;
77     int                 width_mm;
78     int                 height_mm;
79     GBytes *            edid;
80     char *              edid_file;
81 
82     int                 backlight;
83     int                 min_backlight_step;
84 
85     gboolean            is_primary;
86     gboolean            is_presentation;
87     gboolean            is_underscanning;
88     gboolean            supports_underscanning;
89     gboolean            supports_color_transform;
90 
91     GnomeRRTile         tile_info;
92 };
93 
94 struct GnomeRRCrtc
95 {
96     ScreenInfo *	info;
97     guint		id;
98     glong               winsys_id;
99 
100     GnomeRRMode *	current_mode;
101     GnomeRROutput **	current_outputs;
102     GnomeRROutput **	possible_outputs;
103     int			x;
104     int			y;
105 
106     enum wl_output_transform transform;
107     int                 all_transforms;
108     int			gamma_size;
109 };
110 
111 #define UNDEFINED_MODE_ID 0
112 struct GnomeRRMode
113 {
114     ScreenInfo *	info;
115     guint		id;
116     glong               winsys_id;
117     int			width;
118     int			height;
119     int			freq;		/* in mHz */
120     gboolean		tiled;
121     guint32             flags;
122 };
123 
124 /* GnomeRRCrtc */
125 static GnomeRRCrtc *  crtc_new          (ScreenInfo         *info,
126 					 guint               id);
127 static GnomeRRCrtc *  crtc_copy         (const GnomeRRCrtc  *from);
128 static void           crtc_free         (GnomeRRCrtc        *crtc);
129 
130 static void           crtc_initialize   (GnomeRRCrtc        *crtc,
131 					 GVariant           *res);
132 
133 /* GnomeRROutput */
134 static GnomeRROutput *output_new        (ScreenInfo         *info,
135 					 guint               id);
136 
137 static void           output_initialize (GnomeRROutput      *output,
138 					 GVariant           *res);
139 
140 static GnomeRROutput *output_copy       (const GnomeRROutput *from);
141 static void           output_free       (GnomeRROutput      *output);
142 
143 /* GnomeRRMode */
144 static GnomeRRMode *  mode_new          (ScreenInfo         *info,
145 					 guint               id);
146 
147 static void           mode_initialize   (GnomeRRMode        *mode,
148 					 GVariant           *info);
149 
150 static GnomeRRMode *  mode_copy         (const GnomeRRMode  *from);
151 static void           mode_free         (GnomeRRMode        *mode);
152 
153 static void gnome_rr_screen_finalize (GObject*);
154 static void gnome_rr_screen_set_property (GObject*, guint, const GValue*, GParamSpec*);
155 static void gnome_rr_screen_get_property (GObject*, guint, GValue*, GParamSpec*);
156 static gboolean gnome_rr_screen_initable_init (GInitable*, GCancellable*, GError**);
157 static void gnome_rr_screen_initable_iface_init (GInitableIface *iface);
158 static void gnome_rr_screen_async_initable_init (GAsyncInitableIface *iface);
G_DEFINE_TYPE_WITH_CODE(GnomeRRScreen,gnome_rr_screen,G_TYPE_OBJECT,G_ADD_PRIVATE (GnomeRRScreen)G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,gnome_rr_screen_initable_iface_init)G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,gnome_rr_screen_async_initable_init))159 G_DEFINE_TYPE_WITH_CODE (GnomeRRScreen, gnome_rr_screen, G_TYPE_OBJECT,
160                          G_ADD_PRIVATE (GnomeRRScreen)
161                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gnome_rr_screen_initable_iface_init)
162                          G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, gnome_rr_screen_async_initable_init))
163 
164 G_DEFINE_BOXED_TYPE (GnomeRRCrtc, gnome_rr_crtc, crtc_copy, crtc_free)
165 G_DEFINE_BOXED_TYPE (GnomeRROutput, gnome_rr_output, output_copy, output_free)
166 G_DEFINE_BOXED_TYPE (GnomeRRMode, gnome_rr_mode, mode_copy, mode_free)
167 
168 /* Errors */
169 
170 /**
171  * gnome_rr_error_quark:
172  *
173  * Returns the #GQuark that will be used for #GError values returned by the
174  * GnomeRR API.
175  *
176  * Return value: a #GQuark used to identify errors coming from the GnomeRR API.
177  */
178 GQuark
179 gnome_rr_error_quark (void)
180 {
181     return g_quark_from_static_string ("gnome-rr-error-quark");
182 }
183 
184 /* Screen */
185 static GnomeRROutput *
gnome_rr_output_by_id(ScreenInfo * info,guint id)186 gnome_rr_output_by_id (ScreenInfo *info, guint id)
187 {
188     GnomeRROutput **output;
189 
190     g_assert (info != NULL);
191 
192     for (output = info->outputs; *output; ++output)
193     {
194 	if ((*output)->id == id)
195 	    return *output;
196     }
197 
198     return NULL;
199 }
200 
201 static GnomeRRCrtc *
crtc_by_id(ScreenInfo * info,guint id)202 crtc_by_id (ScreenInfo *info, guint id)
203 {
204     GnomeRRCrtc **crtc;
205 
206     if (!info)
207         return NULL;
208 
209     for (crtc = info->crtcs; *crtc; ++crtc)
210     {
211 	if ((*crtc)->id == id)
212 	    return *crtc;
213     }
214 
215     return NULL;
216 }
217 
218 static GnomeRRMode *
mode_by_id(ScreenInfo * info,guint id)219 mode_by_id (ScreenInfo *info, guint id)
220 {
221     GnomeRRMode **mode;
222 
223     g_assert (info != NULL);
224 
225     for (mode = info->modes; *mode; ++mode)
226     {
227 	if ((*mode)->id == id)
228 	    return *mode;
229     }
230 
231     return NULL;
232 }
233 
234 static void
screen_info_free(ScreenInfo * info)235 screen_info_free (ScreenInfo *info)
236 {
237     GnomeRROutput **output;
238     GnomeRRCrtc **crtc;
239     GnomeRRMode **mode;
240 
241     g_assert (info != NULL);
242 
243     if (info->outputs)
244     {
245 	for (output = info->outputs; *output; ++output)
246 	    output_free (*output);
247 	g_free (info->outputs);
248     }
249 
250     if (info->crtcs)
251     {
252 	for (crtc = info->crtcs; *crtc; ++crtc)
253 	    crtc_free (*crtc);
254 	g_free (info->crtcs);
255     }
256 
257     if (info->modes)
258     {
259 	for (mode = info->modes; *mode; ++mode)
260 	    mode_free (*mode);
261 	g_free (info->modes);
262     }
263 
264     if (info->clone_modes)
265     {
266 	/* The modes themselves were freed above */
267 	g_free (info->clone_modes);
268     }
269 
270     g_free (info);
271 }
272 
273 static gboolean
has_similar_mode(GnomeRROutput * output,GnomeRRMode * mode)274 has_similar_mode (GnomeRROutput *output, GnomeRRMode *mode)
275 {
276     int i;
277     GnomeRRMode **modes = gnome_rr_output_list_modes (output);
278     guint width = gnome_rr_mode_get_width (mode);
279     guint height = gnome_rr_mode_get_height (mode);
280 
281     for (i = 0; modes[i] != NULL; ++i)
282     {
283 	GnomeRRMode *m = modes[i];
284 
285 	if (gnome_rr_mode_get_width (m) == width	&&
286 	    gnome_rr_mode_get_height (m) == height)
287 	{
288 	    return TRUE;
289 	}
290     }
291 
292     return FALSE;
293 }
294 
295 gboolean
_gnome_rr_output_get_tiled_display_size(GnomeRROutput * output,int * tile_w,int * tile_h,int * total_width,int * total_height)296 _gnome_rr_output_get_tiled_display_size (GnomeRROutput *output,
297 					 int *tile_w, int *tile_h,
298 					 int *total_width, int *total_height)
299 {
300     GnomeRRTile tile;
301     guint ht, vt;
302     int i, total_h = 0, total_w = 0;
303 
304     if (!_gnome_rr_output_get_tile_info (output, &tile))
305 	return FALSE;
306 
307     if (tile.loc_horiz != 0 ||
308 	tile.loc_vert != 0)
309 	return FALSE;
310 
311     if (tile_w)
312 	*tile_w = tile.width;
313     if (tile_h)
314 	*tile_h = tile.height;
315 
316     for (ht = 0; ht < tile.max_horiz_tiles; ht++)
317     {
318 	for (vt = 0; vt < tile.max_vert_tiles; vt++)
319 	{
320 	    for (i = 0; output->info->outputs[i]; i++)
321 	    {
322 		GnomeRRTile this_tile;
323 
324 		if (!_gnome_rr_output_get_tile_info (output->info->outputs[i], &this_tile))
325 		    continue;
326 
327 		if (this_tile.group_id != tile.group_id)
328 		    continue;
329 
330 		if (this_tile.loc_horiz != ht ||
331 		    this_tile.loc_vert != vt)
332 		    continue;
333 
334 		if (this_tile.loc_horiz == 0)
335 		    total_h += this_tile.height;
336 
337 		if (this_tile.loc_vert == 0)
338 		    total_w += this_tile.width;
339 	    }
340 	}
341     }
342 
343     *total_width = total_w;
344     *total_height = total_h;
345     return TRUE;
346 }
347 
348 static void
gather_tile_modes_output(ScreenInfo * info,GnomeRROutput * output)349 gather_tile_modes_output (ScreenInfo *info, GnomeRROutput *output)
350 {
351     GPtrArray *a;
352     GnomeRRMode *mode;
353     int width, height;
354     int tile_w, tile_h;
355     int i;
356 
357     if (!_gnome_rr_output_get_tiled_display_size (output, &tile_w, &tile_h,
358 						  &width, &height))
359 	return;
360 
361     /* now stick the mode into the modelist */
362     a = g_ptr_array_new ();
363     mode = mode_new (info, UNDEFINED_MODE_ID);
364     mode->winsys_id = 0;
365     mode->width = width;
366     mode->height = height;
367     mode->freq = 0;
368     mode->tiled = TRUE;
369 
370     g_ptr_array_add (a, mode);
371     for (i = 0; output->modes[i]; i++)
372 	g_ptr_array_add (a, output->modes[i]);
373 
374     g_ptr_array_add (a, NULL);
375     output->modes = (GnomeRRMode **)g_ptr_array_free (a, FALSE);
376 }
377 
378 static void
gather_tile_modes(ScreenInfo * info)379 gather_tile_modes (ScreenInfo *info)
380 {
381     int i;
382 
383     for (i = 0; info->outputs[i]; i++)
384 	gather_tile_modes_output (info, info->outputs[i]);
385 }
386 
387 static void
gather_clone_modes(ScreenInfo * info)388 gather_clone_modes (ScreenInfo *info)
389 {
390     int i;
391     GPtrArray *result = g_ptr_array_new ();
392 
393     for (i = 0; info->outputs[i] != NULL; ++i)
394     {
395 	int j;
396 	GnomeRROutput *output1, *output2;
397 
398 	output1 = info->outputs[i];
399 
400 	for (j = 0; output1->modes[j] != NULL; ++j)
401 	{
402 	    GnomeRRMode *mode = output1->modes[j];
403 	    gboolean valid;
404 	    int k;
405 
406 	    valid = TRUE;
407 	    for (k = 0; info->outputs[k] != NULL; ++k)
408 	    {
409 		output2 = info->outputs[k];
410 
411 		if (!has_similar_mode (output2, mode))
412 		{
413 		    valid = FALSE;
414 		    break;
415 		}
416 	    }
417 
418 	    if (valid)
419 		g_ptr_array_add (result, mode);
420 	}
421     }
422 
423     g_ptr_array_add (result, NULL);
424 
425     info->clone_modes = (GnomeRRMode **)g_ptr_array_free (result, FALSE);
426 }
427 
428 static void
fill_screen_info_from_resources(ScreenInfo * info,guint serial,GVariant * crtcs,GVariant * outputs,GVariant * modes,int max_width,int max_height)429 fill_screen_info_from_resources (ScreenInfo *info,
430 				 guint       serial,
431 				 GVariant   *crtcs,
432 				 GVariant   *outputs,
433 				 GVariant   *modes,
434 				 int         max_width,
435 				 int         max_height)
436 {
437     guint i;
438     GPtrArray *a;
439     GnomeRRCrtc **crtc;
440     GnomeRROutput **output;
441     GnomeRRMode **mode;
442     guint ncrtc, noutput, nmode;
443     guint id;
444 
445     info->min_width = 312;
446     info->min_height = 312;
447     info->max_width = max_width;
448     info->max_height = max_height;
449     info->serial = serial;
450 
451     ncrtc = g_variant_n_children (crtcs);
452     noutput = g_variant_n_children (outputs);
453     nmode = g_variant_n_children (modes);
454 
455     /* We create all the structures before initializing them, so
456      * that they can refer to each other.
457      */
458     a = g_ptr_array_new ();
459     for (i = 0; i < ncrtc; ++i)
460     {
461 	g_variant_get_child (crtcs, i, META_CRTC_STRUCT, &id,
462 			     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
463 
464 	g_ptr_array_add (a, crtc_new (info, id));
465     }
466     g_ptr_array_add (a, NULL);
467     info->crtcs = (GnomeRRCrtc **)g_ptr_array_free (a, FALSE);
468 
469     a = g_ptr_array_new ();
470     for (i = 0; i < noutput; ++i)
471     {
472 	g_variant_get_child (outputs, i, META_OUTPUT_STRUCT, &id,
473 			     NULL, NULL, NULL, NULL, NULL, NULL, NULL);
474 
475 	g_ptr_array_add (a, output_new (info, id));
476     }
477     g_ptr_array_add (a, NULL);
478     info->outputs = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
479 
480     a = g_ptr_array_new ();
481     for (i = 0;  i < nmode; ++i)
482     {
483 	g_variant_get_child (modes, i, META_MONITOR_MODE_STRUCT, &id,
484 			     NULL, NULL, NULL, NULL, NULL);
485 
486 	g_ptr_array_add (a, mode_new (info, id));
487     }
488     g_ptr_array_add (a, NULL);
489     info->modes = (GnomeRRMode **)g_ptr_array_free (a, FALSE);
490 
491     /* Initialize */
492     for (i = 0, crtc = info->crtcs; *crtc; ++i, ++crtc)
493     {
494 	GVariant *child = g_variant_get_child_value (crtcs, i);
495 	crtc_initialize (*crtc, child);
496 	g_variant_unref (child);
497     }
498 
499     for (i = 0, output = info->outputs; *output; ++i, ++output)
500     {
501 	GVariant *child = g_variant_get_child_value (outputs, i);
502 	output_initialize (*output, child);
503 	g_variant_unref (child);
504     }
505 
506     for (i = 0, mode = info->modes; *mode; ++i, ++mode)
507     {
508 	GVariant *child = g_variant_get_child_value (modes, i);
509 	mode_initialize (*mode, child);
510 	g_variant_unref (child);
511     }
512 
513     gather_clone_modes (info);
514 
515     gather_tile_modes (info);
516 }
517 
518 static gboolean
fill_out_screen_info(ScreenInfo * info,GError ** error)519 fill_out_screen_info (ScreenInfo  *info,
520 		      GError     **error)
521 {
522     GnomeRRScreenPrivate *priv;
523     guint serial;
524     GVariant *crtcs, *outputs, *modes;
525     int max_width, max_height;
526 
527     g_assert (info != NULL);
528 
529     priv = info->screen->priv;
530 
531     if (!meta_dbus_display_config_call_get_resources_sync (priv->proxy,
532 							   &serial,
533 							   &crtcs,
534 							   &outputs,
535 							   &modes,
536 							   &max_width,
537 							   &max_height,
538 							   NULL,
539 							   error))
540 	return FALSE;
541 
542     fill_screen_info_from_resources (info, serial, crtcs, outputs,
543 				     modes, max_width, max_height);
544 
545     g_variant_unref (crtcs);
546     g_variant_unref (outputs);
547     g_variant_unref (modes);
548 
549     return TRUE;
550 }
551 
552 static ScreenInfo *
screen_info_new(GnomeRRScreen * screen,GError ** error)553 screen_info_new (GnomeRRScreen *screen, GError **error)
554 {
555     ScreenInfo *info = g_new0 (ScreenInfo, 1);
556 
557     g_assert (screen != NULL);
558 
559     info->outputs = NULL;
560     info->crtcs = NULL;
561     info->modes = NULL;
562     info->screen = screen;
563 
564     if (fill_out_screen_info (info, error))
565     {
566 	return info;
567     }
568     else
569     {
570 	screen_info_free (info);
571 	return NULL;
572     }
573 }
574 
575 static GnomeRROutput *
find_output_by_winsys_id(GnomeRROutput ** haystack,glong winsys_id)576 find_output_by_winsys_id (GnomeRROutput **haystack, glong winsys_id)
577 {
578     guint i;
579 
580     for (i = 0; haystack[i] != NULL; i++)
581     {
582 	if (haystack[i]->winsys_id == winsys_id)
583 	    return haystack[i];
584     }
585     return NULL;
586 }
587 
588 static void
diff_outputs_and_emit_signals(ScreenInfo * old,ScreenInfo * new)589 diff_outputs_and_emit_signals (ScreenInfo *old, ScreenInfo *new)
590 {
591     guint i;
592     gulong winsys_id_old, winsys_id_new;
593     GnomeRROutput *output_old;
594     GnomeRROutput *output_new;
595 
596     /* have any outputs been removed/disconnected */
597     for (i = 0; old->outputs[i] != NULL; i++)
598     {
599         winsys_id_old = old->outputs[i]->winsys_id;
600         output_new = find_output_by_winsys_id (new->outputs, winsys_id_old);
601 	if (output_new == NULL)
602 	{
603 	    g_signal_emit (G_OBJECT (new->screen),
604 			   screen_signals[SCREEN_OUTPUT_DISCONNECTED], 0,
605 			   old->outputs[i]);
606 	}
607     }
608 
609     /* have any outputs been created/connected */
610     for (i = 0; new->outputs[i] != NULL; i++)
611     {
612         winsys_id_new = new->outputs[i]->winsys_id;
613         output_old = find_output_by_winsys_id (old->outputs, winsys_id_new);
614 	if (output_old == NULL)
615 	{
616 	    g_signal_emit (G_OBJECT (new->screen),
617 			   screen_signals[SCREEN_OUTPUT_CONNECTED], 0,
618 			   new->outputs[i]);
619 	}
620     }
621 }
622 
623 typedef enum {
624     REFRESH_NONE = 0,
625     REFRESH_IGNORE_SERIAL = 1,
626     REFRESH_FORCE_CALLBACK = 2
627 } RefreshFlags;
628 
629 static gboolean
screen_update(GnomeRRScreen * screen,RefreshFlags flags,GError ** error)630 screen_update (GnomeRRScreen *screen, RefreshFlags flags, GError **error)
631 {
632     ScreenInfo *info;
633     gboolean changed = FALSE;
634 
635     g_assert (screen != NULL);
636 
637     info = screen_info_new (screen, error);
638     if (!info)
639 	    return FALSE;
640 
641     if ((flags & REFRESH_IGNORE_SERIAL) || info->serial != screen->priv->info->serial)
642 	    changed = TRUE;
643 
644     /* work out if any outputs have changed connected state */
645     diff_outputs_and_emit_signals (screen->priv->info, info);
646 
647     screen_info_free (screen->priv->info);
648     screen->priv->info = info;
649 
650     if (changed || (flags & REFRESH_FORCE_CALLBACK))
651         g_signal_emit (G_OBJECT (screen), screen_signals[SCREEN_CHANGED], 0);
652 
653     return changed;
654 }
655 
656 static void
screen_on_monitors_changed(MetaDBusDisplayConfig * proxy,gpointer data)657 screen_on_monitors_changed (MetaDBusDisplayConfig *proxy,
658 			    gpointer data)
659 {
660     GnomeRRScreen *screen = data;
661 
662     screen_update (screen, REFRESH_FORCE_CALLBACK, NULL);
663 }
664 
665 static void
name_owner_changed(GObject * object,GParamSpec * pspec,GnomeRRScreen * self)666 name_owner_changed (GObject       *object,
667 		    GParamSpec    *pspec,
668 		    GnomeRRScreen *self)
669 {
670     GError *error;
671     char *new_name_owner;
672 
673     new_name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object));
674     if (new_name_owner == NULL)
675 	return;
676 
677     error = NULL;
678     if (!screen_update (self, REFRESH_IGNORE_SERIAL | REFRESH_FORCE_CALLBACK, &error))
679 	g_warning ("Failed to refresh screen configuration after mutter was restarted: %s",
680 		   error->message);
681 
682     g_clear_error (&error);
683     g_free (new_name_owner);
684 }
685 
686 static void
power_save_mode_changed(GObject * object,GParamSpec * pspec,GnomeRRScreen * self)687 power_save_mode_changed (GObject       *object,
688                          GParamSpec    *pspec,
689                          GnomeRRScreen *self)
690 {
691         g_object_notify (G_OBJECT (self), "dpms-mode");
692 }
693 
694 static gboolean
gnome_rr_screen_initable_init(GInitable * initable,GCancellable * canc,GError ** error)695 gnome_rr_screen_initable_init (GInitable *initable, GCancellable *canc, GError **error)
696 {
697     GnomeRRScreen *self = GNOME_RR_SCREEN (initable);
698     GnomeRRScreenPrivate *priv = self->priv;
699     MetaDBusDisplayConfig *proxy;
700 
701     proxy = meta_dbus_display_config_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
702 							     G_DBUS_PROXY_FLAGS_NONE,
703 							     "org.gnome.Mutter.DisplayConfig",
704 							     "/org/gnome/Mutter/DisplayConfig",
705 							     NULL, error);
706     if (!proxy)
707 	return FALSE;
708 
709     priv->proxy = META_DBUS_DISPLAY_CONFIG (proxy);
710 
711     priv->info = screen_info_new (self, error);
712     if (!priv->info)
713 	return FALSE;
714 
715     g_signal_connect_object (priv->proxy, "notify::g-name-owner",
716 			     G_CALLBACK (name_owner_changed), self, 0);
717     g_signal_connect_object (priv->proxy, "monitors-changed",
718 			     G_CALLBACK (screen_on_monitors_changed), self, 0);
719     g_signal_connect_object (priv->proxy, "notify::power-save-mode",
720                              G_CALLBACK (power_save_mode_changed), self, 0);
721     return TRUE;
722 }
723 
724 static void
on_proxy_acquired(GObject * object,GAsyncResult * result,gpointer user_data)725 on_proxy_acquired (GObject      *object,
726                    GAsyncResult *result,
727                    gpointer      user_data)
728 {
729     GTask *task = user_data;
730     GnomeRRScreen *self = g_task_get_source_object (task);
731     GnomeRRScreenPrivate *priv = self->priv;
732     MetaDBusDisplayConfig *proxy;
733     GError *error;
734 
735     error = NULL;
736     proxy = meta_dbus_display_config_proxy_new_for_bus_finish (result, &error);
737     if (!proxy)
738 	return g_task_return_error (task, error);
739 
740     priv->proxy = META_DBUS_DISPLAY_CONFIG (proxy);
741 
742     priv->info = screen_info_new (self, &error);
743     if (!priv->info)
744 	return g_task_return_error (task, error);
745 
746     g_signal_connect_object (priv->proxy, "notify::g-name-owner",
747 			     G_CALLBACK (name_owner_changed), self, 0);
748     g_signal_connect_object (priv->proxy, "monitors-changed",
749 			     G_CALLBACK (screen_on_monitors_changed), self, 0);
750     g_signal_connect_object (priv->proxy, "notify::power-save-mode",
751                              G_CALLBACK (power_save_mode_changed), self, 0);
752     g_task_return_boolean (task, TRUE);
753 }
754 
755 static void
on_name_appeared(GDBusConnection * connection,const char * name,const char * name_owner,gpointer user_data)756 on_name_appeared (GDBusConnection *connection,
757                   const char      *name,
758                   const char      *name_owner,
759                   gpointer         user_data)
760 {
761     GTask *task = user_data;
762     GnomeRRScreen *self = g_task_get_source_object (task);
763     GnomeRRScreenPrivate *priv = self->priv;
764 
765     meta_dbus_display_config_proxy_new_for_bus (G_BUS_TYPE_SESSION,
766                                                 G_DBUS_PROXY_FLAGS_NONE,
767                                                 "org.gnome.Mutter.DisplayConfig",
768                                                 "/org/gnome/Mutter/DisplayConfig",
769                                                 g_task_get_cancellable (task),
770                                                 on_proxy_acquired, g_object_ref (task));
771 
772     g_bus_unwatch_name (priv->init_name_watch_id);
773 }
774 
775 static void
gnome_rr_screen_async_initable_init_async(GAsyncInitable * initable,int io_priority,GCancellable * canc,GAsyncReadyCallback callback,gpointer user_data)776 gnome_rr_screen_async_initable_init_async (GAsyncInitable      *initable,
777                                            int                  io_priority,
778                                            GCancellable        *canc,
779                                            GAsyncReadyCallback  callback,
780                                            gpointer             user_data)
781 {
782     GnomeRRScreen *self = GNOME_RR_SCREEN (initable);
783     GnomeRRScreenPrivate *priv = self->priv;
784     GTask *task;
785 
786     task = g_task_new (self, canc, callback, user_data);
787 
788     priv->init_name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
789                                                  "org.gnome.Mutter.DisplayConfig",
790                                                  G_BUS_NAME_WATCHER_FLAGS_NONE,
791                                                  on_name_appeared,
792                                                  NULL,
793                                                  task, g_object_unref);
794 }
795 
796 static gboolean
gnome_rr_screen_async_initable_init_finish(GAsyncInitable * initable,GAsyncResult * result,GError ** error)797 gnome_rr_screen_async_initable_init_finish (GAsyncInitable    *initable,
798                                             GAsyncResult      *result,
799                                             GError           **error)
800 {
801     return g_task_propagate_boolean (G_TASK (result), error);
802 }
803 
804 static void
gnome_rr_screen_initable_iface_init(GInitableIface * iface)805 gnome_rr_screen_initable_iface_init (GInitableIface *iface)
806 {
807     iface->init = gnome_rr_screen_initable_init;
808 }
809 
810 static void
gnome_rr_screen_async_initable_init(GAsyncInitableIface * iface)811 gnome_rr_screen_async_initable_init (GAsyncInitableIface *iface)
812 {
813     iface->init_async = gnome_rr_screen_async_initable_init_async;
814     iface->init_finish = gnome_rr_screen_async_initable_init_finish;
815 }
816 
817 void
gnome_rr_screen_finalize(GObject * gobject)818 gnome_rr_screen_finalize (GObject *gobject)
819 {
820     GnomeRRScreen *screen = GNOME_RR_SCREEN (gobject);
821 
822     if (screen->priv->info)
823       screen_info_free (screen->priv->info);
824 
825     g_clear_object (&screen->priv->proxy);
826 
827     G_OBJECT_CLASS (gnome_rr_screen_parent_class)->finalize (gobject);
828 }
829 
830 void
gnome_rr_screen_set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * property)831 gnome_rr_screen_set_property (GObject *gobject, guint property_id, const GValue *value, GParamSpec *property)
832 {
833     GnomeRRScreen *self = GNOME_RR_SCREEN (gobject);
834     GnomeRRScreenPrivate *priv = self->priv;
835 
836     switch (property_id)
837     {
838     case SCREEN_PROP_GDK_SCREEN:
839         priv->gdk_screen = g_value_get_object (value);
840         return;
841     case SCREEN_PROP_DPMS_MODE:
842         gnome_rr_screen_set_dpms_mode (self, g_value_get_enum (value), NULL);
843         return;
844     default:
845         G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
846         return;
847     }
848 }
849 
850 void
gnome_rr_screen_get_property(GObject * gobject,guint property_id,GValue * value,GParamSpec * property)851 gnome_rr_screen_get_property (GObject *gobject, guint property_id, GValue *value, GParamSpec *property)
852 {
853     GnomeRRScreen *self = GNOME_RR_SCREEN (gobject);
854     GnomeRRScreenPrivate *priv = self->priv;
855 
856     switch (property_id)
857     {
858     case SCREEN_PROP_GDK_SCREEN:
859         g_value_set_object (value, priv->gdk_screen);
860         return;
861     case SCREEN_PROP_DPMS_MODE: {
862         GnomeRRDpmsMode mode;
863         if (gnome_rr_screen_get_dpms_mode (self, &mode, NULL))
864                 g_value_set_enum (value, mode);
865         else
866                 g_value_set_enum (value, GNOME_RR_DPMS_UNKNOWN);
867         }
868         return;
869     default:
870         G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
871         return;
872     }
873 }
874 
875 void
gnome_rr_screen_class_init(GnomeRRScreenClass * klass)876 gnome_rr_screen_class_init (GnomeRRScreenClass *klass)
877 {
878     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
879 
880     bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
881     bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
882 
883     gobject_class->set_property = gnome_rr_screen_set_property;
884     gobject_class->get_property = gnome_rr_screen_get_property;
885     gobject_class->finalize = gnome_rr_screen_finalize;
886 
887     g_object_class_install_property(
888             gobject_class,
889             SCREEN_PROP_GDK_SCREEN,
890             g_param_spec_object (
891                     "gdk-screen",
892                     "GDK Screen",
893                     "The GDK Screen represented by this GnomeRRScreen",
894                     GDK_TYPE_SCREEN,
895                     G_PARAM_READWRITE |
896 		    G_PARAM_CONSTRUCT_ONLY |
897 		    G_PARAM_STATIC_STRINGS)
898             );
899 
900     g_object_class_install_property(
901             gobject_class,
902             SCREEN_PROP_DPMS_MODE,
903             g_param_spec_enum (
904                     "dpms-mode",
905                     "DPMS Mode",
906                     "The DPMS mode for this GnomeRRScreen",
907                     GNOME_TYPE_RR_DPMS_MODE,
908                     GNOME_RR_DPMS_UNKNOWN,
909                     G_PARAM_READWRITE |
910                     G_PARAM_STATIC_STRINGS)
911             );
912 
913     screen_signals[SCREEN_CHANGED] = g_signal_new("changed",
914             G_TYPE_FROM_CLASS (gobject_class),
915             G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
916             G_STRUCT_OFFSET (GnomeRRScreenClass, changed),
917             NULL,
918             NULL,
919             g_cclosure_marshal_VOID__VOID,
920             G_TYPE_NONE,
921 	    0);
922 
923     /**
924      * GnomeRRScreen::output-connected:
925      * @screen: the #GnomeRRScreen that emitted the signal
926      * @output: the #GnomeRROutput that was connected
927      *
928      * This signal is emitted when a display device is connected to a
929      * port, or a port is hotplugged with an active output. The latter
930      * can happen if a laptop is docked, and the dock provides a new
931      * active output.
932      *
933      * The @output value is not a #GObject. The returned @output value can
934      * only assume to be valid during the emission of the signal (i.e. within
935      * your signal handler only), as it may change later when the @screen
936      * is modified due to an event from the X server, or due to another
937      * place in the application modifying the @screen and the @output.
938      * Therefore, deal with changes to the @output right in your signal
939      * handler, instead of keeping the @output reference for an async or
940      * idle function.
941      **/
942     screen_signals[SCREEN_OUTPUT_CONNECTED] = g_signal_new("output-connected",
943             G_TYPE_FROM_CLASS (gobject_class),
944             G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
945             G_STRUCT_OFFSET (GnomeRRScreenClass, output_connected),
946             NULL,
947             NULL,
948             g_cclosure_marshal_VOID__POINTER,
949             G_TYPE_NONE,
950             1, G_TYPE_POINTER);
951 
952     /**
953      * GnomeRRScreen::output-disconnected:
954      * @screen: the #GnomeRRScreen that emitted the signal
955      * @output: the #GnomeRROutput that was disconnected
956      *
957      * This signal is emitted when a display device is disconnected from
958      * a port, or a port output is hot-unplugged. The latter can happen
959      * if a laptop is undocked, and the dock provided the output.
960      *
961      * The @output value is not a #GObject. The returned @output value can
962      * only assume to be valid during the emission of the signal (i.e. within
963      * your signal handler only), as it may change later when the @screen
964      * is modified due to an event from the X server, or due to another
965      * place in the application modifying the @screen and the @output.
966      * Therefore, deal with changes to the @output right in your signal
967      * handler, instead of keeping the @output reference for an async or
968      * idle function.
969      **/
970     screen_signals[SCREEN_OUTPUT_DISCONNECTED] = g_signal_new("output-disconnected",
971             G_TYPE_FROM_CLASS (gobject_class),
972             G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
973             G_STRUCT_OFFSET (GnomeRRScreenClass, output_disconnected),
974             NULL,
975             NULL,
976             g_cclosure_marshal_VOID__POINTER,
977             G_TYPE_NONE,
978             1, G_TYPE_POINTER);
979 }
980 
981 void
gnome_rr_screen_init(GnomeRRScreen * self)982 gnome_rr_screen_init (GnomeRRScreen *self)
983 {
984     self->priv = gnome_rr_screen_get_instance_private (self);
985 }
986 
987 /* Weak reference callback set in gnome_rr_screen_new(); we remove the GObject data from the GdkScreen. */
988 static void
rr_screen_weak_notify_cb(gpointer data,GObject * where_the_object_was)989 rr_screen_weak_notify_cb (gpointer data, GObject *where_the_object_was)
990 {
991     GdkScreen *screen = GDK_SCREEN (data);
992 
993     g_object_set_data (G_OBJECT (screen), "GnomeRRScreen", NULL);
994 }
995 
996 /**
997  * gnome_rr_screen_new:
998  * @screen: the #GdkScreen on which to operate
999  * @error: will be set if XRandR is not supported
1000  *
1001  * Creates a unique #GnomeRRScreen instance for the specified @screen.
1002  *
1003  * Returns: a unique #GnomeRRScreen instance, specific to the @screen, or NULL
1004  * if this could not be created, for instance if the driver does not support
1005  * Xrandr 1.2.  Each #GdkScreen thus has a single instance of #GnomeRRScreen.
1006  */
1007 GnomeRRScreen *
gnome_rr_screen_new(GdkScreen * screen,GError ** error)1008 gnome_rr_screen_new (GdkScreen *screen,
1009 		     GError **error)
1010 {
1011     GnomeRRScreen *rr_screen;
1012 
1013     g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
1014     g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1015 
1016     rr_screen = g_object_get_data (G_OBJECT (screen), "GnomeRRScreen");
1017     if (rr_screen)
1018 	g_object_ref (rr_screen);
1019     else {
1020 	rr_screen = g_initable_new (GNOME_TYPE_RR_SCREEN, NULL, error, "gdk-screen", screen, NULL);
1021 	if (rr_screen) {
1022 	    g_object_set_data (G_OBJECT (screen), "GnomeRRScreen", rr_screen);
1023 	    g_object_weak_ref (G_OBJECT (rr_screen), rr_screen_weak_notify_cb, screen);
1024 	}
1025     }
1026 
1027     return rr_screen;
1028 }
1029 
1030 void
gnome_rr_screen_new_async(GdkScreen * screen,GAsyncReadyCallback callback,gpointer user_data)1031 gnome_rr_screen_new_async (GdkScreen           *screen,
1032                            GAsyncReadyCallback  callback,
1033                            gpointer             user_data)
1034 {
1035     g_return_if_fail (GDK_IS_SCREEN (screen));
1036 
1037     g_async_initable_new_async (GNOME_TYPE_RR_SCREEN, G_PRIORITY_DEFAULT,
1038                                 NULL, callback, user_data,
1039                                 "gdk-screen", screen, NULL);
1040 }
1041 
1042 GnomeRRScreen *
gnome_rr_screen_new_finish(GAsyncResult * result,GError ** error)1043 gnome_rr_screen_new_finish (GAsyncResult  *result,
1044                             GError       **error)
1045 {
1046     GObject *source_object;
1047     GnomeRRScreen *screen;
1048 
1049     source_object = g_async_result_get_source_object (result);
1050     screen = GNOME_RR_SCREEN (g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, error));
1051 
1052     g_object_unref (source_object);
1053     return screen;
1054 }
1055 
1056 /**
1057  * gnome_rr_screen_get_ranges:
1058  * @screen: a #GnomeRRScreen
1059  * @min_width: (out): the minimum width
1060  * @max_width: (out): the maximum width
1061  * @min_height: (out): the minimum height
1062  * @max_height: (out): the maximum height
1063  *
1064  * Get the ranges of the screen
1065  */
1066 void
gnome_rr_screen_get_ranges(GnomeRRScreen * screen,int * min_width,int * max_width,int * min_height,int * max_height)1067 gnome_rr_screen_get_ranges (GnomeRRScreen *screen,
1068 			    int	          *min_width,
1069 			    int	          *max_width,
1070 			    int           *min_height,
1071 			    int	          *max_height)
1072 {
1073     GnomeRRScreenPrivate *priv;
1074 
1075     g_return_if_fail (GNOME_IS_RR_SCREEN (screen));
1076 
1077     priv = screen->priv;
1078 
1079     if (min_width)
1080 	*min_width = priv->info->min_width;
1081 
1082     if (max_width)
1083 	*max_width = priv->info->max_width;
1084 
1085     if (min_height)
1086 	*min_height = priv->info->min_height;
1087 
1088     if (max_height)
1089 	*max_height = priv->info->max_height;
1090 }
1091 
1092 /**
1093  * gnome_rr_screen_refresh:
1094  * @screen: a #GnomeRRScreen
1095  * @error: location to store error, or %NULL
1096  *
1097  * Refreshes the screen configuration, and calls the screen's callback if it
1098  * exists and if the screen's configuration changed.
1099  *
1100  * Return value: TRUE if the screen's configuration changed; otherwise, the
1101  * function returns FALSE and a NULL error if the configuration didn't change,
1102  * or FALSE and a non-NULL error if there was an error while refreshing the
1103  * configuration.
1104  */
1105 gboolean
gnome_rr_screen_refresh(GnomeRRScreen * screen,GError ** error)1106 gnome_rr_screen_refresh (GnomeRRScreen *screen,
1107 			 GError       **error)
1108 {
1109     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1110 
1111     return screen_update (screen, REFRESH_NONE, error);
1112 }
1113 
1114 /**
1115  * gnome_rr_screen_get_dpms_mode:
1116  * @mode: (out): The current #GnomeRRDpmsMode of this screen
1117  **/
1118 gboolean
gnome_rr_screen_get_dpms_mode(GnomeRRScreen * screen,GnomeRRDpmsMode * mode,GError ** error)1119 gnome_rr_screen_get_dpms_mode (GnomeRRScreen    *screen,
1120                                GnomeRRDpmsMode  *mode,
1121                                GError          **error)
1122 {
1123     MetaPowerSave power_save;
1124 
1125     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1126     g_return_val_if_fail (mode != NULL, FALSE);
1127 
1128     power_save = meta_dbus_display_config_get_power_save_mode (screen->priv->proxy);
1129     switch (power_save) {
1130     case META_POWER_SAVE_UNKNOWN:
1131         g_set_error_literal (error,
1132                              GNOME_RR_ERROR,
1133                              GNOME_RR_ERROR_NO_DPMS_EXTENSION,
1134                              "Display is not DPMS capable");
1135         return FALSE;
1136     case META_POWER_SAVE_ON:
1137         *mode = GNOME_RR_DPMS_ON;
1138         break;
1139     case META_POWER_SAVE_STANDBY:
1140         *mode = GNOME_RR_DPMS_STANDBY;
1141         break;
1142     case META_POWER_SAVE_SUSPEND:
1143         *mode = GNOME_RR_DPMS_SUSPEND;
1144         break;
1145     case META_POWER_SAVE_OFF:
1146         *mode = GNOME_RR_DPMS_OFF;
1147         break;
1148     default:
1149         g_assert_not_reached ();
1150         break;
1151     }
1152 
1153     return TRUE;
1154 }
1155 
1156 /**
1157  * gnome_rr_screen_set_dpms_mode:
1158  *
1159  * This method also disables the DPMS timeouts.
1160  **/
1161 gboolean
gnome_rr_screen_set_dpms_mode(GnomeRRScreen * screen,GnomeRRDpmsMode mode,GError ** error)1162 gnome_rr_screen_set_dpms_mode (GnomeRRScreen    *screen,
1163                                GnomeRRDpmsMode   mode,
1164                                GError          **error)
1165 {
1166     MetaPowerSave power_save;
1167 
1168     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1169 
1170     switch (mode) {
1171     case GNOME_RR_DPMS_UNKNOWN:
1172         power_save = META_POWER_SAVE_UNKNOWN;
1173         break;
1174     case GNOME_RR_DPMS_ON:
1175         power_save = META_POWER_SAVE_ON;
1176         break;
1177     case GNOME_RR_DPMS_STANDBY:
1178 	power_save = META_POWER_SAVE_STANDBY;
1179         break;
1180     case GNOME_RR_DPMS_SUSPEND:
1181 	power_save = META_POWER_SAVE_SUSPEND;
1182         break;
1183     case GNOME_RR_DPMS_OFF:
1184 	power_save = META_POWER_SAVE_OFF;
1185         break;
1186     default:
1187         g_assert_not_reached ();
1188         break;
1189     }
1190 
1191     meta_dbus_display_config_set_power_save_mode (screen->priv->proxy, power_save);
1192 
1193     return TRUE;
1194 }
1195 
1196 /**
1197  * gnome_rr_screen_list_modes:
1198  *
1199  * List available XRandR modes
1200  *
1201  * Returns: (array zero-terminated=1) (transfer none):
1202  */
1203 GnomeRRMode **
gnome_rr_screen_list_modes(GnomeRRScreen * screen)1204 gnome_rr_screen_list_modes (GnomeRRScreen *screen)
1205 {
1206     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1207     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1208 
1209     return screen->priv->info->modes;
1210 }
1211 
1212 /**
1213  * gnome_rr_screen_list_clone_modes:
1214  *
1215  * List available XRandR clone modes
1216  *
1217  * Returns: (array zero-terminated=1) (transfer none):
1218  */
1219 GnomeRRMode **
gnome_rr_screen_list_clone_modes(GnomeRRScreen * screen)1220 gnome_rr_screen_list_clone_modes   (GnomeRRScreen *screen)
1221 {
1222     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1223     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1224 
1225     return screen->priv->info->clone_modes;
1226 }
1227 
1228 /**
1229  * gnome_rr_screen_list_crtcs:
1230  *
1231  * List all CRTCs
1232  *
1233  * Returns: (array zero-terminated=1) (transfer none):
1234  */
1235 GnomeRRCrtc **
gnome_rr_screen_list_crtcs(GnomeRRScreen * screen)1236 gnome_rr_screen_list_crtcs (GnomeRRScreen *screen)
1237 {
1238     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1239     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1240 
1241     return screen->priv->info->crtcs;
1242 }
1243 
1244 /**
1245  * gnome_rr_screen_list_outputs:
1246  *
1247  * List all outputs
1248  *
1249  * Returns: (array zero-terminated=1) (transfer none):
1250  */
1251 GnomeRROutput **
gnome_rr_screen_list_outputs(GnomeRRScreen * screen)1252 gnome_rr_screen_list_outputs (GnomeRRScreen *screen)
1253 {
1254     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1255     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1256 
1257     return screen->priv->info->outputs;
1258 }
1259 
1260 /**
1261  * gnome_rr_screen_get_crtc_by_id:
1262  *
1263  * Returns: (transfer none): the CRTC identified by @id
1264  */
1265 GnomeRRCrtc *
gnome_rr_screen_get_crtc_by_id(GnomeRRScreen * screen,guint32 id)1266 gnome_rr_screen_get_crtc_by_id (GnomeRRScreen *screen,
1267 				guint32        id)
1268 {
1269     GnomeRRCrtc **crtcs;
1270     int i;
1271 
1272     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1273     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1274 
1275     crtcs = screen->priv->info->crtcs;
1276 
1277     for (i = 0; crtcs[i] != NULL; ++i)
1278     {
1279 	if (crtcs[i]->id == id)
1280 	    return crtcs[i];
1281     }
1282 
1283     return NULL;
1284 }
1285 
1286 /**
1287  * gnome_rr_screen_get_output_by_id:
1288  *
1289  * Returns: (transfer none): the output identified by @id
1290  */
1291 GnomeRROutput *
gnome_rr_screen_get_output_by_id(GnomeRRScreen * screen,guint32 id)1292 gnome_rr_screen_get_output_by_id (GnomeRRScreen *screen,
1293 				  guint32        id)
1294 {
1295     GnomeRROutput **outputs;
1296     int i;
1297 
1298     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1299     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1300 
1301     outputs = screen->priv->info->outputs;
1302 
1303     for (i = 0; outputs[i] != NULL; ++i)
1304     {
1305 	if (outputs[i]->id == id)
1306 	    return outputs[i];
1307     }
1308 
1309     return NULL;
1310 }
1311 
1312 /* GnomeRROutput */
1313 static GnomeRROutput *
output_new(ScreenInfo * info,guint id)1314 output_new (ScreenInfo *info, guint id)
1315 {
1316     GnomeRROutput *output = g_slice_new0 (GnomeRROutput);
1317 
1318     output->id = id;
1319     output->info = info;
1320 
1321     return output;
1322 }
1323 
1324 static void
append_output_array(GnomeRROutput *** array,GnomeRROutput * output)1325 append_output_array (GnomeRROutput ***array, GnomeRROutput *output)
1326 {
1327     unsigned i;
1328 
1329     for (i = 0; (*array)[i]; i++);
1330 
1331     *array = g_renew (GnomeRROutput *, *array, i + 2);
1332 
1333     (*array)[i] = output;
1334     (*array)[i + 1] = NULL;
1335 }
1336 
1337 static void
output_initialize(GnomeRROutput * output,GVariant * info)1338 output_initialize (GnomeRROutput *output, GVariant *info)
1339 {
1340     GPtrArray *a;
1341     GVariantIter *crtcs, *clones, *modes;
1342     GVariant *properties, *edid, *tile;
1343     gint32 current_crtc_id;
1344     guint32 id;
1345 
1346     g_variant_get (info, META_OUTPUT_STRUCT,
1347 		   &output->id, &output->winsys_id,
1348 		   &current_crtc_id, &crtcs,
1349 		   &output->name,
1350 		   &modes, &clones, &properties);
1351 
1352     /* Possible crtcs */
1353     a = g_ptr_array_new ();
1354     while (g_variant_iter_loop (crtcs, "u", &id))
1355     {
1356 	GnomeRRCrtc *crtc = crtc_by_id (output->info, id);
1357 
1358 	if (!crtc)
1359 	    continue;
1360 
1361 	g_ptr_array_add (a, crtc);
1362 
1363 	if (current_crtc_id != -1 && crtc->id == (guint32) current_crtc_id)
1364 	{
1365 	    output->current_crtc = crtc;
1366 	    append_output_array (&crtc->current_outputs, output);
1367 	}
1368 
1369 	append_output_array (&crtc->possible_outputs, output);
1370     }
1371     g_ptr_array_add (a, NULL);
1372     output->possible_crtcs = (GnomeRRCrtc **)g_ptr_array_free (a, FALSE);
1373     g_variant_iter_free (crtcs);
1374 
1375     /* Clones */
1376     a = g_ptr_array_new ();
1377     while (g_variant_iter_loop (clones, "u", &id))
1378     {
1379 	GnomeRROutput *gnome_rr_output = gnome_rr_output_by_id (output->info, id);
1380 
1381 	if (gnome_rr_output)
1382 	    g_ptr_array_add (a, gnome_rr_output);
1383     }
1384     g_ptr_array_add (a, NULL);
1385     output->clones = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
1386     g_variant_iter_free (clones);
1387 
1388     /* Modes */
1389     a = g_ptr_array_new ();
1390     while (g_variant_iter_loop (modes, "u", &id))
1391     {
1392 	GnomeRRMode *mode = mode_by_id (output->info, id);
1393 
1394 	if (mode)
1395 	    g_ptr_array_add (a, mode);
1396     }
1397     g_ptr_array_add (a, NULL);
1398     output->modes = (GnomeRRMode **)g_ptr_array_free (a, FALSE);
1399     g_variant_iter_free (modes);
1400 
1401     g_variant_lookup (properties, "vendor", "s", &output->vendor);
1402     g_variant_lookup (properties, "product", "s", &output->product);
1403     g_variant_lookup (properties, "serial", "s", &output->serial);
1404     g_variant_lookup (properties, "width-mm", "i", &output->width_mm);
1405     g_variant_lookup (properties, "height-mm", "i", &output->height_mm);
1406     g_variant_lookup (properties, "display-name", "s", &output->display_name);
1407     g_variant_lookup (properties, "connector-type", "s", &output->connector_type);
1408     g_variant_lookup (properties, "backlight", "i", &output->backlight);
1409     g_variant_lookup (properties, "min-backlight-step", "i", &output->min_backlight_step);
1410     g_variant_lookup (properties, "primary", "b", &output->is_primary);
1411     g_variant_lookup (properties, "presentation", "b", &output->is_presentation);
1412     g_variant_lookup (properties, "underscanning", "b", &output->is_underscanning);
1413     g_variant_lookup (properties, "supports-underscanning", "b", &output->supports_underscanning);
1414     g_variant_lookup (properties, "supports-color-transform", "b", &output->supports_color_transform);
1415 
1416     if ((edid = g_variant_lookup_value (properties, "edid", G_VARIANT_TYPE ("ay"))))
1417       {
1418 	output->edid = g_variant_get_data_as_bytes (edid);
1419 	g_variant_unref (edid);
1420       }
1421     else
1422       g_variant_lookup (properties, "edid-file", "s", &output->edid_file);
1423 
1424     if ((tile = g_variant_lookup_value (properties, "tile", G_VARIANT_TYPE ("(uuuuuuuu)"))))
1425       {
1426 	g_variant_get (tile, "(uuuuuuuu)",
1427 		       &output->tile_info.group_id, &output->tile_info.flags,
1428 		       &output->tile_info.max_horiz_tiles, &output->tile_info.max_vert_tiles,
1429 		       &output->tile_info.loc_horiz, &output->tile_info.loc_vert,
1430 		       &output->tile_info.width, &output->tile_info.height);
1431 	g_variant_unref (tile);
1432       }
1433     else
1434       memset(&output->tile_info, 0, sizeof(output->tile_info));
1435 
1436     if (output->is_primary)
1437 	output->info->primary = output;
1438 
1439     g_variant_unref (properties);
1440 }
1441 
1442 static GnomeRROutput*
output_copy(const GnomeRROutput * from)1443 output_copy (const GnomeRROutput *from)
1444 {
1445     GPtrArray *array;
1446     GnomeRRCrtc **p_crtc;
1447     GnomeRROutput **p_output;
1448     GnomeRRMode **p_mode;
1449     GnomeRROutput *output = g_slice_new0 (GnomeRROutput);
1450 
1451     output->id = from->id;
1452     output->info = from->info;
1453     output->name = g_strdup (from->name);
1454     output->display_name = g_strdup (from->display_name);
1455     output->connector_type = g_strdup (from->connector_type);
1456     output->vendor = g_strdup (from->vendor);
1457     output->product = g_strdup (from->product);
1458     output->serial = g_strdup (from->serial);
1459     output->current_crtc = from->current_crtc;
1460     output->backlight = from->backlight;
1461     if (from->edid)
1462       output->edid = g_bytes_ref (from->edid);
1463     output->edid_file = g_strdup (from->edid_file);
1464 
1465     output->is_primary = from->is_primary;
1466     output->is_presentation = from->is_presentation;
1467 
1468     array = g_ptr_array_new ();
1469     for (p_crtc = from->possible_crtcs; *p_crtc != NULL; p_crtc++)
1470     {
1471         g_ptr_array_add (array, *p_crtc);
1472     }
1473     output->possible_crtcs = (GnomeRRCrtc**) g_ptr_array_free (array, FALSE);
1474 
1475     array = g_ptr_array_new ();
1476     for (p_output = from->clones; *p_output != NULL; p_output++)
1477     {
1478         g_ptr_array_add (array, *p_output);
1479     }
1480     output->clones = (GnomeRROutput**) g_ptr_array_free (array, FALSE);
1481 
1482     array = g_ptr_array_new ();
1483     for (p_mode = from->modes; *p_mode != NULL; p_mode++)
1484     {
1485         g_ptr_array_add (array, *p_mode);
1486     }
1487     output->modes = (GnomeRRMode**) g_ptr_array_free (array, FALSE);
1488 
1489     return output;
1490 }
1491 
1492 static void
output_free(GnomeRROutput * output)1493 output_free (GnomeRROutput *output)
1494 {
1495     g_free (output->clones);
1496     g_free (output->modes);
1497     g_free (output->possible_crtcs);
1498     g_free (output->name);
1499     g_free (output->vendor);
1500     g_free (output->product);
1501     g_free (output->serial);
1502     g_free (output->display_name);
1503     g_free (output->connector_type);
1504     g_free (output->edid_file);
1505     if (output->edid)
1506       g_bytes_unref (output->edid);
1507     g_slice_free (GnomeRROutput, output);
1508 }
1509 
1510 guint32
gnome_rr_output_get_id(GnomeRROutput * output)1511 gnome_rr_output_get_id (GnomeRROutput *output)
1512 {
1513     g_assert(output != NULL);
1514 
1515     return output->id;
1516 }
1517 
1518 const guint8 *
gnome_rr_output_get_edid_data(GnomeRROutput * output,gsize * size)1519 gnome_rr_output_get_edid_data (GnomeRROutput *output,
1520 			       gsize         *size)
1521 {
1522   if (output->edid)
1523     return g_bytes_get_data (output->edid, size);
1524 
1525   if (output->edid_file)
1526     {
1527       GMappedFile *mmap;
1528 
1529       mmap = g_mapped_file_new (output->edid_file, FALSE, NULL);
1530 
1531       if (mmap)
1532 	{
1533 	  output->edid = g_mapped_file_get_bytes (mmap);
1534 
1535 	  g_mapped_file_unref (mmap);
1536 
1537 	  return g_bytes_get_data (output->edid, size);
1538 	}
1539     }
1540 
1541   return NULL;
1542 }
1543 
1544 /**
1545  * gnome_rr_output_get_ids_from_edid:
1546  * @output: a #GnomeRROutput
1547  * @vendor: (out) (allow-none):
1548  * @product: (out) (allow-none):
1549  * @serial: (out) (allow-none):
1550  */
1551 void
gnome_rr_output_get_ids_from_edid(GnomeRROutput * output,char ** vendor,char ** product,char ** serial)1552 gnome_rr_output_get_ids_from_edid (GnomeRROutput         *output,
1553                                    char                 **vendor,
1554                                    char                 **product,
1555                                    char                 **serial)
1556 {
1557     g_return_if_fail (output != NULL);
1558 
1559     *vendor = g_strdup (output->vendor);
1560     *product = g_strdup (output->product);
1561     *serial = g_strdup (output->serial);
1562 }
1563 
1564 /**
1565  * gnome_rr_output_get_physical_size:
1566  * @output: a #GnomeRROutput
1567  * @width_mm: (out) (allow-none):
1568  * @height_mm: (out) (allow-none):
1569  */
1570 void
gnome_rr_output_get_physical_size(GnomeRROutput * output,int * width_mm,int * height_mm)1571 gnome_rr_output_get_physical_size (GnomeRROutput *output,
1572 				   int           *width_mm,
1573 				   int           *height_mm)
1574 {
1575     g_return_if_fail (output != NULL);
1576 
1577     if (width_mm)
1578 	*width_mm = output->width_mm;
1579     if (height_mm)
1580 	*height_mm = output->height_mm;
1581 }
1582 
1583 const char *
gnome_rr_output_get_display_name(GnomeRROutput * output)1584 gnome_rr_output_get_display_name (GnomeRROutput *output)
1585 {
1586     g_return_val_if_fail (output != NULL, NULL);
1587 
1588     return output->display_name;
1589 }
1590 
1591 /**
1592  * gnome_rr_output_get_backlight:
1593  *
1594  * Returns: The currently set backlight brightness
1595  */
1596 int
gnome_rr_output_get_backlight(GnomeRROutput * output)1597 gnome_rr_output_get_backlight (GnomeRROutput *output)
1598 {
1599     g_return_val_if_fail (output != NULL, -1);
1600 
1601     return output->backlight;
1602 }
1603 
1604 /**
1605  * gnome_rr_output_get_min_backlight_step:
1606  *
1607  * Returns: The minimum backlight step available in percent
1608  */
1609 int
gnome_rr_output_get_min_backlight_step(GnomeRROutput * output)1610 gnome_rr_output_get_min_backlight_step (GnomeRROutput *output)
1611 {
1612     g_return_val_if_fail (output != NULL, -1);
1613 
1614     return output->min_backlight_step;
1615 }
1616 
1617 /**
1618  * gnome_rr_output_set_backlight:
1619  * @value: the absolute value which is 0 >= this <= 100
1620  *
1621  * Returns: %TRUE for success
1622  */
1623 gboolean
gnome_rr_output_set_backlight(GnomeRROutput * output,gint value,GError ** error)1624 gnome_rr_output_set_backlight (GnomeRROutput *output, gint value, GError **error)
1625 {
1626     g_return_val_if_fail (output != NULL, FALSE);
1627 
1628     return meta_dbus_display_config_call_change_backlight_sync (output->info->screen->priv->proxy,
1629 								output->info->serial,
1630 								output->id, value,
1631 								&output->backlight,
1632 								NULL, error);
1633 }
1634 
1635 gboolean
gnome_rr_output_set_color_transform(GnomeRROutput * output,GnomeRRCTM ctm,GError ** error)1636 gnome_rr_output_set_color_transform (GnomeRROutput *output, GnomeRRCTM ctm,
1637                                      GError **error)
1638 {
1639     GVariant *ctm_var;
1640     GVariant *values[9];
1641     int i;
1642 
1643     g_return_val_if_fail (output != NULL, FALSE);
1644 
1645     for (i = 0; i < 9; i++)
1646         values[i] = g_variant_new_uint64 (ctm.matrix[i]);
1647 
1648     ctm_var = g_variant_new_tuple (values, 9);
1649 
1650     return meta_dbus_display_config_call_set_output_ctm_sync (output->info->screen->priv->proxy,
1651                                                               output->info->serial,
1652                                                               output->id,
1653                                                               ctm_var,
1654                                                               NULL, error);
1655 }
1656 
1657 /**
1658  * gnome_rr_screen_get_output_by_name:
1659  *
1660  * Returns: (transfer none): the output identified by @name
1661  */
1662 GnomeRROutput *
gnome_rr_screen_get_output_by_name(GnomeRRScreen * screen,const char * name)1663 gnome_rr_screen_get_output_by_name (GnomeRRScreen *screen,
1664 				    const char    *name)
1665 {
1666     int i;
1667 
1668     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1669     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1670 
1671     for (i = 0; screen->priv->info->outputs[i] != NULL; ++i)
1672     {
1673 	GnomeRROutput *output = screen->priv->info->outputs[i];
1674 
1675 	if (strcmp (output->name, name) == 0)
1676 	    return output;
1677     }
1678 
1679     return NULL;
1680 }
1681 
1682 /**
1683  * gnome_rr_output_get_crtc:
1684  * @output: a #GnomeRROutput
1685  * Returns: (transfer none):
1686  */
1687 GnomeRRCrtc *
gnome_rr_output_get_crtc(GnomeRROutput * output)1688 gnome_rr_output_get_crtc (GnomeRROutput *output)
1689 {
1690     g_return_val_if_fail (output != NULL, NULL);
1691 
1692     return output->current_crtc;
1693 }
1694 
1695 /**
1696  * gnome_rr_output_get_possible_crtcs:
1697  * @output: a #GnomeRROutput
1698  * Returns: (array zero-terminated=1) (transfer none):
1699  */
1700 GnomeRRCrtc **
gnome_rr_output_get_possible_crtcs(GnomeRROutput * output)1701 gnome_rr_output_get_possible_crtcs (GnomeRROutput *output)
1702 {
1703     g_return_val_if_fail (output != NULL, NULL);
1704 
1705     return output->possible_crtcs;
1706 }
1707 
1708 gboolean
_gnome_rr_output_connector_type_is_builtin_display(const char * connector_type)1709 _gnome_rr_output_connector_type_is_builtin_display (const char *connector_type)
1710 {
1711     if (!connector_type)
1712         return FALSE;
1713 
1714     if (strcmp (connector_type, "LVDS") == 0 ||
1715 	strcmp (connector_type, "eDP") == 0  ||
1716 	strcmp (connector_type, "DSI") == 0)
1717         return TRUE;
1718 
1719     return FALSE;
1720 }
1721 
1722 gboolean
gnome_rr_output_is_builtin_display(GnomeRROutput * output)1723 gnome_rr_output_is_builtin_display (GnomeRROutput *output)
1724 {
1725     g_return_val_if_fail (output != NULL, FALSE);
1726 
1727     return _gnome_rr_output_connector_type_is_builtin_display (output->connector_type);
1728 }
1729 
1730 /**
1731  * gnome_rr_output_get_current_mode:
1732  * @output: a #GnomeRROutput
1733  * Returns: (transfer none): the current mode of this output
1734  */
1735 GnomeRRMode *
gnome_rr_output_get_current_mode(GnomeRROutput * output)1736 gnome_rr_output_get_current_mode (GnomeRROutput *output)
1737 {
1738     GnomeRRCrtc *crtc;
1739     GnomeRRMode *mode;
1740     g_return_val_if_fail (output != NULL, NULL);
1741 
1742     if ((crtc = gnome_rr_output_get_crtc (output)))
1743     {
1744 	int total_w, total_h, tile_w, tile_h;
1745 	mode = gnome_rr_crtc_get_current_mode (crtc);
1746 
1747 	if (_gnome_rr_output_get_tiled_display_size (output, &tile_w, &tile_h, &total_w, &total_h))
1748 	{
1749 	    if (mode->width == tile_w &&
1750 		mode->height == tile_h) {
1751 		if (output->modes[0]->tiled)
1752 		    return output->modes[0];
1753 	    }
1754 	}
1755 	return gnome_rr_crtc_get_current_mode (crtc);
1756     }
1757     return NULL;
1758 }
1759 
1760 /**
1761  * gnome_rr_output_get_position:
1762  * @output: a #GnomeRROutput
1763  * @x: (out) (allow-none):
1764  * @y: (out) (allow-none):
1765  */
1766 void
gnome_rr_output_get_position(GnomeRROutput * output,int * x,int * y)1767 gnome_rr_output_get_position (GnomeRROutput   *output,
1768 			      int             *x,
1769 			      int             *y)
1770 {
1771     GnomeRRCrtc *crtc;
1772 
1773     g_return_if_fail (output != NULL);
1774 
1775     if ((crtc = gnome_rr_output_get_crtc (output)))
1776 	gnome_rr_crtc_get_position (crtc, x, y);
1777 }
1778 
1779 const char *
gnome_rr_output_get_name(GnomeRROutput * output)1780 gnome_rr_output_get_name (GnomeRROutput *output)
1781 {
1782     g_assert (output != NULL);
1783     return output->name;
1784 }
1785 
1786 /**
1787  * gnome_rr_output_get_preferred_mode:
1788  * @output: a #GnomeRROutput
1789  * Returns: (transfer none):
1790  */
1791 GnomeRRMode *
gnome_rr_output_get_preferred_mode(GnomeRROutput * output)1792 gnome_rr_output_get_preferred_mode (GnomeRROutput *output)
1793 {
1794     g_return_val_if_fail (output != NULL, NULL);
1795     return output->modes[0];
1796 }
1797 
1798 /**
1799  * gnome_rr_output_list_modes:
1800  * @output: a #GnomeRROutput
1801  * Returns: (array zero-terminated=1) (transfer none):
1802  */
1803 GnomeRRMode **
gnome_rr_output_list_modes(GnomeRROutput * output)1804 gnome_rr_output_list_modes (GnomeRROutput *output)
1805 {
1806     g_return_val_if_fail (output != NULL, NULL);
1807     return output->modes;
1808 }
1809 
1810 gboolean
gnome_rr_output_supports_mode(GnomeRROutput * output,GnomeRRMode * mode)1811 gnome_rr_output_supports_mode (GnomeRROutput *output,
1812 			       GnomeRRMode   *mode)
1813 {
1814     int i;
1815 
1816     g_return_val_if_fail (output != NULL, FALSE);
1817     g_return_val_if_fail (mode != NULL, FALSE);
1818 
1819     for (i = 0; output->modes[i] != NULL; ++i)
1820     {
1821 	if (output->modes[i] == mode)
1822 	    return TRUE;
1823     }
1824 
1825     return FALSE;
1826 }
1827 
1828 gboolean
gnome_rr_output_can_clone(GnomeRROutput * output,GnomeRROutput * clone)1829 gnome_rr_output_can_clone (GnomeRROutput *output,
1830 			   GnomeRROutput *clone)
1831 {
1832     int i;
1833 
1834     g_return_val_if_fail (output != NULL, FALSE);
1835     g_return_val_if_fail (clone != NULL, FALSE);
1836 
1837     for (i = 0; output->clones[i] != NULL; ++i)
1838     {
1839 	if (output->clones[i] == clone)
1840 	    return TRUE;
1841     }
1842 
1843     return FALSE;
1844 }
1845 
1846 gboolean
gnome_rr_output_get_is_primary(GnomeRROutput * output)1847 gnome_rr_output_get_is_primary (GnomeRROutput *output)
1848 {
1849     return output->is_primary;
1850 }
1851 
1852 /* GnomeRRCrtc */
1853 static const GnomeRRRotation rotation_map[] =
1854 {
1855     GNOME_RR_ROTATION_0,
1856     GNOME_RR_ROTATION_90,
1857     GNOME_RR_ROTATION_180,
1858     GNOME_RR_ROTATION_270,
1859     GNOME_RR_REFLECT_X | GNOME_RR_ROTATION_0,
1860     GNOME_RR_REFLECT_X | GNOME_RR_ROTATION_90,
1861     GNOME_RR_REFLECT_X | GNOME_RR_ROTATION_180,
1862     GNOME_RR_REFLECT_X | GNOME_RR_ROTATION_270,
1863 };
1864 
1865 static GnomeRRRotation
gnome_rr_rotation_from_transform(enum wl_output_transform transform)1866 gnome_rr_rotation_from_transform (enum wl_output_transform transform)
1867 {
1868     return rotation_map[transform];
1869 }
1870 
1871 /**
1872  * gnome_rr_crtc_get_current_mode:
1873  * @crtc: a #GnomeRRCrtc
1874  * Returns: (transfer none): the current mode of this crtc
1875  */
1876 GnomeRRMode *
gnome_rr_crtc_get_current_mode(GnomeRRCrtc * crtc)1877 gnome_rr_crtc_get_current_mode (GnomeRRCrtc *crtc)
1878 {
1879     g_return_val_if_fail (crtc != NULL, NULL);
1880 
1881     return crtc->current_mode;
1882 }
1883 
1884 guint32
gnome_rr_crtc_get_id(GnomeRRCrtc * crtc)1885 gnome_rr_crtc_get_id (GnomeRRCrtc *crtc)
1886 {
1887     g_return_val_if_fail (crtc != NULL, 0);
1888 
1889     return crtc->id;
1890 }
1891 
1892 gboolean
gnome_rr_crtc_can_drive_output(GnomeRRCrtc * crtc,GnomeRROutput * output)1893 gnome_rr_crtc_can_drive_output (GnomeRRCrtc   *crtc,
1894 				GnomeRROutput *output)
1895 {
1896     int i;
1897 
1898     g_return_val_if_fail (crtc != NULL, FALSE);
1899     g_return_val_if_fail (output != NULL, FALSE);
1900 
1901     for (i = 0; crtc->possible_outputs[i] != NULL; ++i)
1902     {
1903 	if (crtc->possible_outputs[i] == output)
1904 	    return TRUE;
1905     }
1906 
1907     return FALSE;
1908 }
1909 
1910 /**
1911  * gnome_rr_crtc_get_position:
1912  * @crtc: a #GnomeRRCrtc
1913  * @x: (out) (allow-none):
1914  * @y: (out) (allow-none):
1915  */
1916 void
gnome_rr_crtc_get_position(GnomeRRCrtc * crtc,int * x,int * y)1917 gnome_rr_crtc_get_position (GnomeRRCrtc *crtc,
1918 			    int         *x,
1919 			    int         *y)
1920 {
1921     g_return_if_fail (crtc != NULL);
1922 
1923     if (x)
1924 	*x = crtc->x;
1925 
1926     if (y)
1927 	*y = crtc->y;
1928 }
1929 
1930 GnomeRRRotation
gnome_rr_crtc_get_current_rotation(GnomeRRCrtc * crtc)1931 gnome_rr_crtc_get_current_rotation (GnomeRRCrtc *crtc)
1932 {
1933     g_assert(crtc != NULL);
1934     return gnome_rr_rotation_from_transform (crtc->transform);
1935 }
1936 
1937 static GnomeRRRotation
gnome_rr_rotation_from_all_transforms(int all_transforms)1938 gnome_rr_rotation_from_all_transforms (int all_transforms)
1939 {
1940     GnomeRRRotation ret = all_transforms & 0xF;
1941 
1942     if (all_transforms & (1 << WL_OUTPUT_TRANSFORM_FLIPPED))
1943 	ret |= GNOME_RR_REFLECT_X;
1944 
1945     if (all_transforms & (1 << WL_OUTPUT_TRANSFORM_FLIPPED_180))
1946 	ret |= GNOME_RR_REFLECT_Y;
1947 
1948     return ret;
1949 }
1950 
1951 GnomeRRRotation
gnome_rr_crtc_get_rotations(GnomeRRCrtc * crtc)1952 gnome_rr_crtc_get_rotations (GnomeRRCrtc *crtc)
1953 {
1954     g_assert(crtc != NULL);
1955     return gnome_rr_rotation_from_all_transforms (crtc->all_transforms);
1956 }
1957 
1958 gboolean
gnome_rr_crtc_supports_rotation(GnomeRRCrtc * crtc,GnomeRRRotation rotation)1959 gnome_rr_crtc_supports_rotation (GnomeRRCrtc *   crtc,
1960 				 GnomeRRRotation rotation)
1961 {
1962     g_return_val_if_fail (crtc != NULL, FALSE);
1963     return (gnome_rr_rotation_from_all_transforms (crtc->all_transforms) & rotation);
1964 }
1965 
1966 static GnomeRRCrtc *
crtc_new(ScreenInfo * info,guint id)1967 crtc_new (ScreenInfo *info, guint id)
1968 {
1969     GnomeRRCrtc *crtc = g_slice_new0 (GnomeRRCrtc);
1970 
1971     crtc->id = id;
1972     crtc->info = info;
1973     crtc->current_outputs = g_new0 (GnomeRROutput *, 1);
1974     crtc->possible_outputs = g_new0 (GnomeRROutput *, 1);
1975 
1976     return crtc;
1977 }
1978 
1979 static GnomeRRCrtc *
crtc_copy(const GnomeRRCrtc * from)1980 crtc_copy (const GnomeRRCrtc *from)
1981 {
1982     GnomeRROutput **p_output;
1983     GPtrArray *array;
1984     GnomeRRCrtc *to = g_slice_new0 (GnomeRRCrtc);
1985 
1986     to->info = from->info;
1987     to->id = from->id;
1988     to->current_mode = from->current_mode;
1989     to->x = from->x;
1990     to->y = from->y;
1991     to->transform = from->transform;
1992     to->all_transforms = from->all_transforms;
1993     to->gamma_size = from->gamma_size;
1994 
1995     array = g_ptr_array_new ();
1996     for (p_output = from->current_outputs; *p_output != NULL; p_output++)
1997     {
1998         g_ptr_array_add (array, *p_output);
1999     }
2000     to->current_outputs = (GnomeRROutput**) g_ptr_array_free (array, FALSE);
2001 
2002     array = g_ptr_array_new ();
2003     for (p_output = from->possible_outputs; *p_output != NULL; p_output++)
2004     {
2005         g_ptr_array_add (array, *p_output);
2006     }
2007     to->possible_outputs = (GnomeRROutput**) g_ptr_array_free (array, FALSE);
2008 
2009     return to;
2010 }
2011 
2012 static void
crtc_initialize(GnomeRRCrtc * crtc,GVariant * info)2013 crtc_initialize (GnomeRRCrtc *crtc, GVariant *info)
2014 {
2015     GVariantIter *all_transforms;
2016     int current_mode_id;
2017     guint transform;
2018 
2019     g_variant_get (info, META_CRTC_STRUCT,
2020 		   &crtc->id, &crtc->winsys_id,
2021 		   &crtc->x, &crtc->y,
2022 		   NULL, NULL,
2023 		   &current_mode_id,
2024 		   &crtc->transform, &all_transforms,
2025 		   NULL);
2026 
2027     if (current_mode_id >= 0)
2028       crtc->current_mode = mode_by_id (crtc->info, current_mode_id);
2029 
2030     while (g_variant_iter_loop (all_transforms, "u", &transform))
2031 	crtc->all_transforms |= 1 << transform;
2032     g_variant_iter_free (all_transforms);
2033 }
2034 
2035 static void
crtc_free(GnomeRRCrtc * crtc)2036 crtc_free (GnomeRRCrtc *crtc)
2037 {
2038     g_free (crtc->current_outputs);
2039     g_free (crtc->possible_outputs);
2040     g_slice_free (GnomeRRCrtc, crtc);
2041 }
2042 
2043 /* GnomeRRMode */
2044 static GnomeRRMode *
mode_new(ScreenInfo * info,guint id)2045 mode_new (ScreenInfo *info, guint id)
2046 {
2047     GnomeRRMode *mode = g_slice_new0 (GnomeRRMode);
2048 
2049     mode->id = id;
2050     mode->info = info;
2051 
2052     return mode;
2053 }
2054 
2055 guint32
gnome_rr_mode_get_id(GnomeRRMode * mode)2056 gnome_rr_mode_get_id (GnomeRRMode *mode)
2057 {
2058     g_return_val_if_fail (mode != NULL, 0);
2059     return mode->id;
2060 }
2061 
2062 guint
gnome_rr_mode_get_width(GnomeRRMode * mode)2063 gnome_rr_mode_get_width (GnomeRRMode *mode)
2064 {
2065     g_return_val_if_fail (mode != NULL, 0);
2066     return mode->width;
2067 }
2068 
2069 int
gnome_rr_mode_get_freq(GnomeRRMode * mode)2070 gnome_rr_mode_get_freq (GnomeRRMode *mode)
2071 {
2072     g_return_val_if_fail (mode != NULL, 0);
2073     return (mode->freq) / 1000;
2074 }
2075 
2076 double
gnome_rr_mode_get_freq_f(GnomeRRMode * mode)2077 gnome_rr_mode_get_freq_f (GnomeRRMode *mode)
2078 {
2079     g_return_val_if_fail (mode != NULL, 0.0);
2080     return (mode->freq) / 1000.0;
2081 }
2082 
2083 guint
gnome_rr_mode_get_height(GnomeRRMode * mode)2084 gnome_rr_mode_get_height (GnomeRRMode *mode)
2085 {
2086     g_return_val_if_fail (mode != NULL, 0);
2087     return mode->height;
2088 }
2089 
2090 /**
2091  * gnome_rr_mode_get_is_tiled:
2092  * @mode: a #GnomeRRMode
2093  *
2094  * Returns TRUE if this mode is a tiled
2095  * mode created for span a tiled monitor.
2096  */
2097 gboolean
gnome_rr_mode_get_is_tiled(GnomeRRMode * mode)2098 gnome_rr_mode_get_is_tiled (GnomeRRMode *mode)
2099 {
2100     g_return_val_if_fail (mode != NULL, FALSE);
2101     return mode->tiled;
2102 }
2103 
2104 gboolean
gnome_rr_mode_get_is_interlaced(GnomeRRMode * mode)2105 gnome_rr_mode_get_is_interlaced (GnomeRRMode *mode)
2106 {
2107     g_return_val_if_fail (mode != NULL, 0);
2108     return (mode->flags & DRM_MODE_FLAG_INTERLACE) != 0;
2109 }
2110 
2111 static void
mode_initialize(GnomeRRMode * mode,GVariant * info)2112 mode_initialize (GnomeRRMode *mode, GVariant *info)
2113 {
2114     gdouble frequency;
2115 
2116     g_variant_get (info, META_MONITOR_MODE_STRUCT,
2117 		   &mode->id, &mode->winsys_id,
2118 		   &mode->width, &mode->height,
2119 		   &frequency, &mode->flags);
2120 
2121     mode->freq = frequency * 1000;
2122 }
2123 
2124 static GnomeRRMode *
mode_copy(const GnomeRRMode * from)2125 mode_copy (const GnomeRRMode *from)
2126 {
2127     GnomeRRMode *to = g_slice_new0 (GnomeRRMode);
2128 
2129     to->id = from->id;
2130     to->info = from->info;
2131     to->width = from->width;
2132     to->height = from->height;
2133     to->freq = from->freq;
2134 
2135     return to;
2136 }
2137 
2138 static void
mode_free(GnomeRRMode * mode)2139 mode_free (GnomeRRMode *mode)
2140 {
2141     g_slice_free (GnomeRRMode, mode);
2142 }
2143 
2144 gboolean
_gnome_rr_screen_apply_configuration(GnomeRRScreen * screen,gboolean persistent,GVariant * crtcs,GVariant * outputs,GError ** error)2145 _gnome_rr_screen_apply_configuration (GnomeRRScreen  *screen,
2146 				      gboolean        persistent,
2147 				      GVariant       *crtcs,
2148 				      GVariant       *outputs,
2149 				      GError        **error)
2150 {
2151   return meta_dbus_display_config_call_apply_configuration_sync (screen->priv->proxy,
2152 								 screen->priv->info->serial,
2153 								 persistent,
2154 								 crtcs, outputs,
2155 								 NULL, error);
2156 }
2157 
2158 gboolean
gnome_rr_crtc_set_gamma(GnomeRRCrtc * crtc,int size,unsigned short * red,unsigned short * green,unsigned short * blue)2159 gnome_rr_crtc_set_gamma (GnomeRRCrtc    *crtc,
2160 			 int             size,
2161 			 unsigned short *red,
2162 			 unsigned short *green,
2163 			 unsigned short *blue)
2164 {
2165   GBytes *red_bytes, *green_bytes, *blue_bytes;
2166   GVariant *red_v, *green_v, *blue_v;
2167   gboolean ok;
2168 
2169   red_bytes = g_bytes_new (red, size * sizeof (unsigned short));
2170   green_bytes = g_bytes_new (green, size * sizeof (unsigned short));
2171   blue_bytes = g_bytes_new (blue, size * sizeof (unsigned short));
2172 
2173   red_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"),
2174 				    red_bytes, TRUE);
2175   green_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"),
2176 				      green_bytes, TRUE);
2177   blue_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"),
2178 				     blue_bytes, TRUE);
2179 
2180   ok = meta_dbus_display_config_call_set_crtc_gamma_sync (crtc->info->screen->priv->proxy,
2181 							  crtc->info->serial,
2182 							  crtc->id,
2183 							  red_v,
2184 							  green_v,
2185 							  blue_v,
2186 							  NULL, NULL);
2187 
2188   g_bytes_unref (red_bytes);
2189   g_bytes_unref (green_bytes);
2190   g_bytes_unref (blue_bytes);
2191   /* The variant above are floating, no need to free them */
2192 
2193   return ok;
2194 }
2195 
2196 /**
2197  * gnome_rr_crtc_get_gamma:
2198  * @crtc: a #GnomeRRCrtc
2199  * @size:
2200  * @red: (out): the minimum width
2201  * @green: (out): the maximum width
2202  * @blue: (out): the minimum height
2203  *
2204  * Returns: %TRUE for success
2205  */
2206 gboolean
gnome_rr_crtc_get_gamma(GnomeRRCrtc * crtc,int * size,unsigned short ** red,unsigned short ** green,unsigned short ** blue)2207 gnome_rr_crtc_get_gamma (GnomeRRCrtc     *crtc,
2208 			 int             *size,
2209 			 unsigned short **red,
2210 			 unsigned short **green,
2211 			 unsigned short **blue)
2212 {
2213   GBytes *red_bytes, *green_bytes, *blue_bytes;
2214   GVariant *red_v, *green_v, *blue_v;
2215   gboolean ok;
2216   gsize dummy;
2217 
2218   ok = meta_dbus_display_config_call_get_crtc_gamma_sync (crtc->info->screen->priv->proxy,
2219 							  crtc->info->serial,
2220 							  crtc->id,
2221 							  &red_v,
2222 							  &green_v,
2223 							  &blue_v,
2224 							  NULL, NULL);
2225   if (!ok)
2226     return FALSE;
2227 
2228   red_bytes = g_variant_get_data_as_bytes (red_v);
2229   green_bytes = g_variant_get_data_as_bytes (green_v);
2230   blue_bytes = g_variant_get_data_as_bytes (blue_v);
2231 
2232   /* Unref the variant early so that the bytes hold the only reference to
2233      the data and we don't need to copy
2234   */
2235   g_variant_unref (red_v);
2236   g_variant_unref (green_v);
2237   g_variant_unref (blue_v);
2238 
2239   if (size)
2240     *size = g_bytes_get_size (red_bytes) / sizeof (unsigned short);
2241 
2242   if (red)
2243     *red = g_bytes_unref_to_data (red_bytes, &dummy);
2244   else
2245     g_bytes_unref (red_bytes);
2246   if (green)
2247     *green = g_bytes_unref_to_data (green_bytes, &dummy);
2248   else
2249     g_bytes_unref (green_bytes);
2250   if (blue)
2251     *blue = g_bytes_unref_to_data (blue_bytes, &dummy);
2252   else
2253     g_bytes_unref (blue_bytes);
2254 
2255   return TRUE;
2256 }
2257 
2258 gboolean
gnome_rr_output_get_is_underscanning(GnomeRROutput * output)2259 gnome_rr_output_get_is_underscanning (GnomeRROutput *output)
2260 {
2261     g_assert(output != NULL);
2262     return output->is_underscanning;
2263 }
2264 
2265 gboolean
gnome_rr_output_supports_underscanning(GnomeRROutput * output)2266 gnome_rr_output_supports_underscanning (GnomeRROutput *output)
2267 {
2268     g_assert (output != NULL);
2269     return output->supports_underscanning;
2270 }
2271 
2272 gboolean
gnome_rr_output_supports_color_transform(const GnomeRROutput * output)2273 gnome_rr_output_supports_color_transform (const GnomeRROutput *output)
2274 {
2275     g_assert (output != NULL);
2276     return output->supports_color_transform;
2277 }
2278 
2279 const char *
_gnome_rr_output_get_connector_type(GnomeRROutput * output)2280 _gnome_rr_output_get_connector_type (GnomeRROutput *output)
2281 {
2282     g_return_val_if_fail (output != NULL, NULL);
2283 
2284     return output->connector_type;
2285 }
2286 
2287 gboolean
_gnome_rr_output_get_tile_info(GnomeRROutput * output,GnomeRRTile * tile)2288 _gnome_rr_output_get_tile_info (GnomeRROutput *output,
2289 				GnomeRRTile *tile)
2290 {
2291     if (output->tile_info.group_id == UNDEFINED_GROUP_ID)
2292         return FALSE;
2293 
2294     if (!tile)
2295         return FALSE;
2296 
2297     *tile = output->tile_info;
2298     return TRUE;
2299 }
2300 
2301 GType
gnome_rr_dpms_mode_get_type(void)2302 gnome_rr_dpms_mode_get_type (void)
2303 {
2304   static GType etype = 0;
2305   if (etype == 0) {
2306     static const GEnumValue values[] = {
2307       { GNOME_RR_DPMS_ON, "GNOME_RR_DPMS_ON", "on" },
2308       { GNOME_RR_DPMS_STANDBY, "GNOME_RR_DPMS_STANDBY", "standby" },
2309       { GNOME_RR_DPMS_SUSPEND, "GNOME_RR_DPMS_SUSPEND", "suspend" },
2310       { GNOME_RR_DPMS_OFF, "GNOME_RR_DPMS_OFF", "off" },
2311       { GNOME_RR_DPMS_UNKNOWN, "GNOME_RR_DPMS_UNKNOWN", "unknown" },
2312       { 0, NULL, NULL }
2313     };
2314     etype = g_enum_register_static ("GnomeRRDpmsModeType", values);
2315   }
2316   return etype;
2317 }
2318