1 /* gnome-rr.c
2  *
3  * Copyright 2007, 2008, Red Hat, Inc.
4  *
5  * This file is part of the Gnome Library.
6  *
7  * The Gnome Library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * The Gnome Library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
19  * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * Author: Soren Sandmann <sandmann@redhat.com>
23  */
24 
25 #define GNOME_DESKTOP_USE_UNSTABLE_API
26 
27 #include <config.h>
28 #include <glib/gi18n-lib.h>
29 #include <string.h>
30 #include <math.h>
31 #include <X11/Xlib.h>
32 
33 #include <X11/extensions/Xrandr.h>
34 
35 #include <gtk/gtk.h>
36 #include <gdk/gdkx.h>
37 #include <X11/Xatom.h>
38 #include <X11/extensions/dpms.h>
39 
40 #undef GNOME_DISABLE_DEPRECATED
41 #include "gnome-rr.h"
42 #include "gnome-rr-config.h"
43 
44 #include "private.h"
45 #include "edid.h"
46 #include "gnome-rr-private.h"
47 
48 #define DISPLAY(o) ((o)->info->screen->priv->xdisplay)
49 
50 #define SERVERS_RANDR_IS_AT_LEAST_1_3(priv) (priv->rr_major_version > 1 || (priv->rr_major_version == 1 && priv->rr_minor_version >= 3))
51 
52 #define INTERFACE_SETTINGS "org.cinnamon.desktop.interface"
53 #define GLOBAL_SCALE_FACTOR_KEY "scaling-factor"
54 #define USE_UPSCALING_KEY "upscale-fractional-scaling"
55 
56 enum {
57     SCREEN_PROP_0,
58     SCREEN_PROP_GDK_SCREEN,
59     SCREEN_PROP_LAST,
60 };
61 
62 enum {
63     SCREEN_CHANGED,
64     SCREEN_OUTPUT_CONNECTED,
65     SCREEN_OUTPUT_DISCONNECTED,
66     SCREEN_SIGNAL_LAST,
67 };
68 
69 static guint screen_signals[SCREEN_SIGNAL_LAST] = { 0 };
70 
71 struct GnomeRROutput
72 {
73     ScreenInfo *	info;
74     RROutput		id;
75 
76     char *		name;
77     GnomeRRCrtc *	current_crtc;
78     gboolean		connected;
79     gulong		width_mm;
80     gulong		height_mm;
81     GnomeRRCrtc **	possible_crtcs;
82     GnomeRROutput **	clones;
83     GnomeRRMode **	modes;
84     int			n_preferred;
85     guint8 *		edid_data;
86     gsize		edid_size;
87     char *              connector_type;
88     gint		backlight_min;
89     gint		backlight_max;
90 };
91 
92 struct GnomeRROutputWrap
93 {
94     RROutput		id;
95 };
96 
97 struct GnomeRRCrtc
98 {
99     ScreenInfo *	info;
100     RRCrtc		id;
101 
102     GnomeRRMode *	current_mode;
103     GnomeRROutput **	current_outputs;
104     GnomeRROutput **	possible_outputs;
105     int			x;
106     int			y;
107     float       scale;
108 
109     GnomeRRRotation	current_rotation;
110     GnomeRRRotation	rotations;
111     int			gamma_size;
112 };
113 
114 struct GnomeRRMode
115 {
116     ScreenInfo *	info;
117     RRMode		id;
118     char *		name;
119     int			width;
120     int			height;
121     int			freq;		/* in mHz */
122     gboolean    doublescan;
123     gboolean    interlaced;
124     gboolean    vsync;
125 };
126 
127 /* GnomeRRCrtc */
128 static GnomeRRCrtc *  crtc_new          (ScreenInfo         *info,
129 					 RRCrtc              id);
130 static GnomeRRCrtc *  crtc_copy         (const GnomeRRCrtc  *from);
131 static void           crtc_free         (GnomeRRCrtc        *crtc);
132 
133 static gboolean       crtc_initialize   (GnomeRRCrtc        *crtc,
134 					 XRRScreenResources *res,
135 					 GError            **error);
136 
137 /* GnomeRROutput */
138 static GnomeRROutput *output_new        (ScreenInfo         *info,
139 					 RROutput            id);
140 
141 static gboolean       output_initialize (GnomeRROutput      *output,
142 					 XRRScreenResources *res,
143 					 GError            **error);
144 
145 static GnomeRROutput *output_copy       (const GnomeRROutput *from);
146 static void           output_free       (GnomeRROutput      *output);
147 
148 /* GnomeRRMode */
149 static GnomeRRMode *  mode_new          (ScreenInfo         *info,
150 					 RRMode              id);
151 
152 static void           mode_initialize   (GnomeRRMode        *mode,
153 					 XRRModeInfo        *info);
154 
155 static GnomeRRMode *  mode_copy         (const GnomeRRMode  *from);
156 static void           mode_free         (GnomeRRMode        *mode);
157 
158 static void gnome_rr_screen_finalize (GObject*);
159 static void gnome_rr_screen_set_property (GObject*, guint, const GValue*, GParamSpec*);
160 static void gnome_rr_screen_get_property (GObject*, guint, GValue*, GParamSpec*);
161 static gboolean gnome_rr_screen_initable_init (GInitable*, GCancellable*, GError**);
162 static void gnome_rr_screen_initable_iface_init (GInitableIface *iface);
G_DEFINE_TYPE_WITH_CODE(GnomeRRScreen,gnome_rr_screen,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,gnome_rr_screen_initable_iface_init))163 G_DEFINE_TYPE_WITH_CODE (GnomeRRScreen, gnome_rr_screen, G_TYPE_OBJECT,
164         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gnome_rr_screen_initable_iface_init))
165 
166 G_DEFINE_BOXED_TYPE (GnomeRRCrtc, gnome_rr_crtc, crtc_copy, crtc_free)
167 G_DEFINE_BOXED_TYPE (GnomeRROutput, gnome_rr_output, output_copy, output_free)
168 G_DEFINE_BOXED_TYPE (GnomeRRMode, gnome_rr_mode, mode_copy, mode_free)
169 
170 /* Errors */
171 
172 /**
173  * gnome_rr_error_quark:
174  *
175  * Returns the #GQuark that will be used for #GError values returned by the
176  * GnomeRR API.
177  *
178  * Return value: a #GQuark used to identify errors coming from the GnomeRR API.
179  */
180 GQuark
181 gnome_rr_error_quark (void)
182 {
183     return g_quark_from_static_string ("gnome-rr-error-quark");
184 }
185 
186 /* Screen */
187 static GnomeRROutput *
gnome_rr_output_by_id(ScreenInfo * info,RROutput id)188 gnome_rr_output_by_id (ScreenInfo *info, RROutput id)
189 {
190     GnomeRROutput **output;
191 
192     g_assert (info != NULL);
193 
194     for (output = info->outputs; *output; ++output)
195     {
196 	if ((*output)->id == id)
197 	    return *output;
198     }
199 
200     return NULL;
201 }
202 
203 static GnomeRRCrtc *
crtc_by_id(ScreenInfo * info,RRCrtc id)204 crtc_by_id (ScreenInfo *info, RRCrtc id)
205 {
206     GnomeRRCrtc **crtc;
207 
208     if (!info)
209         return NULL;
210 
211     for (crtc = info->crtcs; *crtc; ++crtc)
212     {
213 	if ((*crtc)->id == id)
214 	    return *crtc;
215     }
216 
217     return NULL;
218 }
219 
220 static GnomeRRMode *
mode_by_id(ScreenInfo * info,RRMode id)221 mode_by_id (ScreenInfo *info, RRMode id)
222 {
223     GnomeRRMode **mode;
224 
225     g_assert (info != NULL);
226 
227     for (mode = info->modes; *mode; ++mode)
228     {
229 	if ((*mode)->id == id)
230 	    return *mode;
231     }
232 
233     return NULL;
234 }
235 
236 static void
screen_info_free(ScreenInfo * info)237 screen_info_free (ScreenInfo *info)
238 {
239     GnomeRROutput **output;
240     GnomeRRCrtc **crtc;
241     GnomeRRMode **mode;
242 
243     g_assert (info != NULL);
244 
245     if (info->resources)
246     {
247 	XRRFreeScreenResources (info->resources);
248 
249 	info->resources = NULL;
250     }
251 
252     if (info->outputs)
253     {
254 	for (output = info->outputs; *output; ++output)
255 	    output_free (*output);
256 	g_free (info->outputs);
257     }
258 
259     if (info->crtcs)
260     {
261 	for (crtc = info->crtcs; *crtc; ++crtc)
262 	    crtc_free (*crtc);
263 	g_free (info->crtcs);
264     }
265 
266     if (info->modes)
267     {
268 	for (mode = info->modes; *mode; ++mode)
269 	    mode_free (*mode);
270 	g_free (info->modes);
271     }
272 
273     if (info->clone_modes)
274     {
275 	/* The modes themselves were freed above */
276 	g_free (info->clone_modes);
277     }
278 
279     g_free (info);
280 }
281 
282 static gboolean
has_similar_mode(GnomeRROutput * output,GnomeRRMode * mode)283 has_similar_mode (GnomeRROutput *output, GnomeRRMode *mode)
284 {
285     int i;
286     GnomeRRMode **modes = gnome_rr_output_list_modes (output);
287     int width = gnome_rr_mode_get_width (mode);
288     int height = gnome_rr_mode_get_height (mode);
289 
290     for (i = 0; modes[i] != NULL; ++i)
291     {
292         GnomeRRMode *m = modes[i];
293 
294         if (gnome_rr_mode_get_width (m) == width	&&
295             gnome_rr_mode_get_height (m) == height)
296         {
297             return TRUE;
298         }
299     }
300 
301     return FALSE;
302 }
303 
304 static void
gather_clone_modes(ScreenInfo * info)305 gather_clone_modes (ScreenInfo *info)
306 {
307     int i;
308     GPtrArray *result = g_ptr_array_new ();
309 
310     for (i = 0; info->outputs[i] != NULL; ++i)
311     {
312         int j;
313         GnomeRROutput *output1, *output2;
314 
315         output1 = info->outputs[i];
316 
317         if (!output1->connected)
318             continue;
319 
320         for (j = 0; output1->modes[j] != NULL; ++j)
321         {
322             GnomeRRMode *mode = output1->modes[j];
323             gboolean valid;
324             int k;
325 
326             valid = TRUE;
327             for (k = 0; info->outputs[k] != NULL; ++k)
328             {
329                 output2 = info->outputs[k];
330 
331                 if (!output2->connected)
332                     continue;
333 
334                 if (!has_similar_mode (output2, mode))
335                 {
336                     valid = FALSE;
337                     break;
338                 }
339             }
340 
341             if (valid)
342                 g_ptr_array_add (result, mode);
343         }
344     }
345 
346     g_ptr_array_add (result, NULL);
347 
348     info->clone_modes = (GnomeRRMode **)g_ptr_array_free (result, FALSE);
349 }
350 
351 static gboolean
fill_screen_info_from_resources(ScreenInfo * info,XRRScreenResources * resources,GError ** error)352 fill_screen_info_from_resources (ScreenInfo *info,
353 				 XRRScreenResources *resources,
354 				 GError **error)
355 {
356     int i;
357     GPtrArray *a;
358     GnomeRRCrtc **crtc;
359     GnomeRROutput **output;
360     info->resources = resources;
361 
362     /* We create all the structures before initializing them, so
363      * that they can refer to each other.
364      */
365     a = g_ptr_array_new ();
366 
367     for (i = 0; i < resources->ncrtc; ++i)
368     {
369         GnomeRRCrtc *crtc = crtc_new (info, resources->crtcs[i]);
370 
371         g_ptr_array_add (a, crtc);
372     }
373 
374     g_ptr_array_add (a, NULL);
375     info->crtcs = (GnomeRRCrtc **)g_ptr_array_free (a, FALSE);
376 
377     a = g_ptr_array_new ();
378     for (i = 0; i < resources->noutput; ++i)
379     {
380         GnomeRROutput *output = output_new (info, resources->outputs[i]);
381 
382         g_ptr_array_add (a, output);
383     }
384 
385     g_ptr_array_add (a, NULL);
386     info->outputs = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
387 
388     a = g_ptr_array_new ();
389     for (i = 0;  i < resources->nmode; ++i)
390     {
391         GnomeRRMode *mode = mode_new (info, resources->modes[i].id);
392 
393         g_ptr_array_add (a, mode);
394     }
395 
396     g_ptr_array_add (a, NULL);
397     info->modes = (GnomeRRMode **)g_ptr_array_free (a, FALSE);
398 
399     /* Initialize */
400     for (crtc = info->crtcs; *crtc; ++crtc)
401     {
402         if (!crtc_initialize (*crtc, resources, error))
403             return FALSE;
404     }
405 
406     for (output = info->outputs; *output; ++output)
407     {
408         if (!output_initialize (*output, resources, error))
409             return FALSE;
410     }
411 
412     for (i = 0; i < resources->nmode; ++i)
413     {
414         GnomeRRMode *mode = mode_by_id (info, resources->modes[i].id);
415 
416         mode_initialize (mode, &(resources->modes[i]));
417     }
418 
419     gather_clone_modes (info);
420 
421     return TRUE;
422 }
423 
424 static gboolean
fill_out_screen_info(Display * xdisplay,Window xroot,ScreenInfo * info,gboolean needs_reprobe,GError ** error)425 fill_out_screen_info (Display *xdisplay,
426 		      Window xroot,
427 		      ScreenInfo *info,
428 		      gboolean needs_reprobe,
429 		      GError **error)
430 {
431     XRRScreenResources *resources;
432     GnomeRRScreenPrivate *priv;
433 
434     g_assert (xdisplay != NULL);
435     g_assert (info != NULL);
436 
437     priv = info->screen->priv;
438 
439     /* First update the screen resources */
440 
441     if (needs_reprobe)
442         resources = XRRGetScreenResources (xdisplay, xroot);
443     else
444     {
445 	/* XRRGetScreenResourcesCurrent is less expensive than
446 	 * XRRGetScreenResources, however it is available only
447 	 * in RandR 1.3 or higher
448 	 */
449         if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv))
450             resources = XRRGetScreenResourcesCurrent (xdisplay, xroot);
451         else
452             resources = XRRGetScreenResources (xdisplay, xroot);
453     }
454 
455     if (resources)
456     {
457 	if (!fill_screen_info_from_resources (info, resources, error))
458 	    return FALSE;
459     }
460     else
461     {
462 	g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR,
463 		     /* Translators: a CRTC is a CRT Controller (this is X terminology). */
464 		     _("could not get the screen resources (CRTCs, outputs, modes)"));
465 	return FALSE;
466     }
467 
468     /* Then update the screen size range.  We do this after XRRGetScreenResources() so that
469      * the X server will already have an updated view of the outputs.
470      */
471 
472     if (needs_reprobe) {
473 	gboolean success;
474 
475         gdk_error_trap_push ();
476 	success = XRRGetScreenSizeRange (xdisplay, xroot,
477 					 &(info->min_width),
478 					 &(info->min_height),
479 					 &(info->max_width),
480 					 &(info->max_height));
481 	gdk_flush ();
482 	if (gdk_error_trap_pop ()) {
483 	    g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_UNKNOWN,
484 			 _("unhandled X error while getting the range of screen sizes"));
485 	    return FALSE;
486 	}
487 
488 	if (!success) {
489 	    g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR,
490 			 _("could not get the range of screen sizes"));
491             return FALSE;
492         }
493     }
494     else
495     {
496         gnome_rr_screen_get_ranges (info->screen,
497 					 &(info->min_width),
498 					 &(info->max_width),
499 					 &(info->min_height),
500 					 &(info->max_height));
501     }
502 
503     info->primary = None;
504     if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv)) {
505         gdk_error_trap_push ();
506         info->primary = XRRGetOutputPrimary (xdisplay, xroot);
507 	gdk_error_trap_pop_ignored ();
508     }
509 
510     /* can the screen do DPMS? */
511     gdk_error_trap_push ();
512     priv->dpms_capable = DPMSCapable (priv->xdisplay);
513     gdk_error_trap_pop_ignored ();
514 
515     return TRUE;
516 }
517 
518 static ScreenInfo *
screen_info_new(GnomeRRScreen * screen,gboolean needs_reprobe,GError ** error)519 screen_info_new (GnomeRRScreen *screen, gboolean needs_reprobe, GError **error)
520 {
521     ScreenInfo *info = g_new0 (ScreenInfo, 1);
522     GnomeRRScreenPrivate *priv;
523 
524     g_assert (screen != NULL);
525 
526     priv = screen->priv;
527 
528     info->outputs = NULL;
529     info->crtcs = NULL;
530     info->modes = NULL;
531     info->screen = screen;
532 
533     if (fill_out_screen_info (priv->xdisplay, priv->xroot, info, needs_reprobe, error))
534     {
535 	return info;
536     }
537     else
538     {
539 	screen_info_free (info);
540 	return NULL;
541     }
542 }
543 
544 static GnomeRROutput *
find_output_by_id(GnomeRROutput ** haystack,guint32 id)545 find_output_by_id (GnomeRROutput **haystack, guint32 id)
546 {
547     guint i;
548 
549     for (i = 0; haystack[i] != NULL; i++)
550     {
551 	if (gnome_rr_output_get_id (haystack[i]) == id)
552 	    return haystack[i];
553     }
554     return NULL;
555 }
556 
557 static void
diff_outputs_and_emit_signals(ScreenInfo * old,ScreenInfo * new)558 diff_outputs_and_emit_signals (ScreenInfo *old, ScreenInfo *new)
559 {
560     guint i;
561     guint32 id_old, id_new;
562     GnomeRROutput *output_old;
563     GnomeRROutput *output_new;
564 
565     /* have any outputs been removed or disconnected */
566     for (i = 0; old->outputs[i] != NULL; i++)
567     {
568         id_old = gnome_rr_output_get_id (old->outputs[i]);
569         output_new = find_output_by_id (new->outputs, id_old);
570 	if (output_new == NULL)
571 	{
572 	    /* output removed (and disconnected) */
573 	    if (gnome_rr_output_is_connected (old->outputs[i]))
574 	     {
575 	        g_signal_emit (G_OBJECT (new->screen),
576 			       screen_signals[SCREEN_OUTPUT_DISCONNECTED], 0,
577 			       old->outputs[i]);
578              }
579 	    continue;
580 	}
581 	if (gnome_rr_output_is_connected (old->outputs[i]) &&
582 	    !gnome_rr_output_is_connected (output_new))
583 	{
584 	    /* output disconnected */
585 	    g_signal_emit (G_OBJECT (new->screen),
586 			   screen_signals[SCREEN_OUTPUT_DISCONNECTED], 0,
587 			   old->outputs[i]);
588         }
589     }
590 
591     /* have any outputs been created or connected */
592     for (i = 0; new->outputs[i] != NULL; i++)
593     {
594         id_new = gnome_rr_output_get_id (new->outputs[i]);
595         output_old = find_output_by_id (old->outputs, id_new);
596 	if (output_old == NULL)
597 	{
598 	    /* output created */
599 	    if (gnome_rr_output_is_connected (new->outputs[i]))
600 	     {
601 	        g_signal_emit (G_OBJECT (new->screen),
602 			       screen_signals[SCREEN_OUTPUT_CONNECTED], 0,
603 			       new->outputs[i]);
604             }
605 	    continue;
606 	}
607 	if (!gnome_rr_output_is_connected (output_old) &&
608 	    gnome_rr_output_is_connected (new->outputs[i]))
609 	{
610 	    /* output connected */
611 	    g_signal_emit (G_OBJECT (new->screen),
612 			   screen_signals[SCREEN_OUTPUT_CONNECTED], 0,
613 			   new->outputs[i]);
614          }
615     }
616 }
617 
618 static gboolean
screen_update(GnomeRRScreen * screen,gboolean force_callback,gboolean needs_reprobe,GError ** error)619 screen_update (GnomeRRScreen *screen, gboolean force_callback, gboolean needs_reprobe, GError **error)
620 {
621     ScreenInfo *info;
622     gboolean changed = FALSE;
623 
624     g_assert (screen != NULL);
625 
626     info = screen_info_new (screen, needs_reprobe, error);
627     if (!info)
628 	    return FALSE;
629 
630     if (info->resources->configTimestamp != screen->priv->info->resources->configTimestamp)
631 	    changed = TRUE;
632 
633     /* work out if any outputs have changed connected state */
634     diff_outputs_and_emit_signals (screen->priv->info, info);
635 
636     screen_info_free (screen->priv->info);
637 
638     screen->priv->info = info;
639 
640     if (changed || force_callback)
641         g_signal_emit (G_OBJECT (screen), screen_signals[SCREEN_CHANGED], 0);
642 
643     return changed;
644 }
645 
646 static GdkFilterReturn
screen_on_event(GdkXEvent * xevent,GdkEvent * event,gpointer data)647 screen_on_event (GdkXEvent *xevent,
648 		 GdkEvent *event,
649 		 gpointer data)
650 {
651     GnomeRRScreen *screen = data;
652     GnomeRRScreenPrivate *priv = screen->priv;
653     XEvent *e = xevent;
654     int event_num;
655 
656     if (!e)
657 	return GDK_FILTER_CONTINUE;
658 
659     event_num = e->type - priv->randr_event_base;
660 
661     if (event_num == RRScreenChangeNotify) {
662 	/* We don't reprobe the hardware; we just fetch the X server's latest
663 	 * state.  The server already knows the new state of the outputs; that's
664 	 * why it sent us an event!
665 	 */
666         screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */
667 #if 0
668 	/* Enable this code to get a dialog showing the RANDR timestamps, for debugging purposes */
669 	{
670 	    GtkWidget *dialog;
671 	    XRRScreenChangeNotifyEvent *rr_event;
672 	    static int dialog_num;
673 
674 	    rr_event = (XRRScreenChangeNotifyEvent *) e;
675 
676 	    dialog = gtk_message_dialog_new (NULL,
677 					     0,
678 					     GTK_MESSAGE_INFO,
679 					     GTK_BUTTONS_CLOSE,
680 					     "RRScreenChangeNotify timestamps (%d):\n"
681 					     "event change: %u\n"
682 					     "event config: %u\n"
683 					     "event serial: %lu\n"
684 					     "----------------------"
685 					     "screen change: %u\n"
686 					     "screen config: %u\n",
687 					     dialog_num++,
688 					     (guint32) rr_event->timestamp,
689 					     (guint32) rr_event->config_timestamp,
690 					     rr_event->serial,
691 					     (guint32) priv->info->resources->timestamp,
692 					     (guint32) priv->info->resources->configTimestamp);
693 	    g_signal_connect (dialog, "response",
694 			      G_CALLBACK (gtk_widget_destroy), NULL);
695 	    gtk_widget_show (dialog);
696 	}
697 #endif
698     }
699 #if 0
700     /* WHY THIS CODE IS DISABLED:
701      *
702      * Note that in gnome_rr_screen_new(), we only select for
703      * RRScreenChangeNotifyMask.  We used to select for other values in
704      * RR*NotifyMask, but we weren't really doing anything useful with those
705      * events.  We only care about "the screens changed in some way or another"
706      * for now.
707      *
708      * If we ever run into a situtation that could benefit from processing more
709      * detailed events, we can enable this code again.
710      *
711      * Note that the X server sends RRScreenChangeNotify in conjunction with the
712      * more detailed events from RANDR 1.2 - see xserver/randr/randr.c:TellChanged().
713      */
714     else if (event_num == RRNotify)
715     {
716 	/* Other RandR events */
717 
718 	XRRNotifyEvent *event = (XRRNotifyEvent *)e;
719 
720 	/* Here we can distinguish between RRNotify events supported
721 	 * since RandR 1.2 such as RRNotify_OutputProperty.  For now, we
722 	 * don't have anything special to do for particular subevent types, so
723 	 * we leave this as an empty switch().
724 	 */
725 	switch (event->subtype)
726 	{
727 	default:
728 	    break;
729 	}
730 
731 	/* No need to reprobe hardware here */
732 	screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */
733     }
734 #endif
735 
736     /* Pass the event on to GTK+ */
737     return GDK_FILTER_CONTINUE;
738 }
739 
740 static gboolean
gnome_rr_screen_initable_init(GInitable * initable,GCancellable * canc,GError ** error)741 gnome_rr_screen_initable_init (GInitable *initable, GCancellable *canc, GError **error)
742 {
743     GnomeRRScreen *self = GNOME_RR_SCREEN (initable);
744     GnomeRRScreenPrivate *priv = self->priv;
745     Display *dpy = GDK_SCREEN_XDISPLAY (self->priv->gdk_screen);
746     int event_base;
747     int ignore;
748 
749     priv->interface_settings = g_settings_new ("org.cinnamon.desktop.interface");
750 
751     priv->connector_type_atom = XInternAtom (dpy, "ConnectorType", FALSE);
752 
753     if (XRRQueryExtension (dpy, &event_base, &ignore))
754     {
755         priv->randr_event_base = event_base;
756 
757         XRRQueryVersion (dpy, &priv->rr_major_version, &priv->rr_minor_version);
758         if (priv->rr_major_version < 1 || (priv->rr_major_version == 1 && priv->rr_minor_version < 2)) {
759             g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_NO_RANDR_EXTENSION,
760                     "RANDR extension is too old (must be at least 1.2)");
761             return FALSE;
762         }
763 
764         priv->info = screen_info_new (self, TRUE, error);
765 
766         if (!priv->info) {
767             return FALSE;
768         }
769 
770         XRRSelectInput (priv->xdisplay,
771                 priv->xroot,
772                 RRScreenChangeNotifyMask);
773         gdk_x11_register_standard_event_type (gdk_screen_get_display (priv->gdk_screen),
774                           event_base,
775                           RRNotify + 1);
776         gdk_window_add_filter (priv->gdk_root, screen_on_event, self);
777 
778         return TRUE;
779     }
780     else
781     {
782       g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_NO_RANDR_EXTENSION,
783                    _("RANDR extension is not present"));
784 
785       return FALSE;
786    }
787 }
788 
789 void
gnome_rr_screen_initable_iface_init(GInitableIface * iface)790 gnome_rr_screen_initable_iface_init (GInitableIface *iface)
791 {
792     iface->init = gnome_rr_screen_initable_init;
793 }
794 
795 void
gnome_rr_screen_finalize(GObject * gobject)796 gnome_rr_screen_finalize (GObject *gobject)
797 {
798     GnomeRRScreen *screen = GNOME_RR_SCREEN (gobject);
799 
800     gdk_window_remove_filter (screen->priv->gdk_root, screen_on_event, screen);
801 
802     if (screen->priv->info)
803       screen_info_free (screen->priv->info);
804 
805     g_clear_object (&screen->priv->interface_settings);
806 
807     G_OBJECT_CLASS (gnome_rr_screen_parent_class)->finalize (gobject);
808 }
809 
810 void
gnome_rr_screen_set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * property)811 gnome_rr_screen_set_property (GObject *gobject, guint property_id, const GValue *value, GParamSpec *property)
812 {
813     GnomeRRScreen *self = GNOME_RR_SCREEN (gobject);
814     GnomeRRScreenPrivate *priv = self->priv;
815 
816     switch (property_id)
817     {
818     case SCREEN_PROP_GDK_SCREEN:
819         priv->gdk_screen = g_value_get_object (value);
820         priv->gdk_root = gdk_screen_get_root_window (priv->gdk_screen);
821         priv->xroot = gdk_x11_window_get_xid (priv->gdk_root);
822         priv->xdisplay = GDK_SCREEN_XDISPLAY (priv->gdk_screen);
823         priv->xscreen = gdk_x11_screen_get_xscreen (priv->gdk_screen);
824         return;
825     default:
826         G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
827         return;
828     }
829 }
830 
831 void
gnome_rr_screen_get_property(GObject * gobject,guint property_id,GValue * value,GParamSpec * property)832 gnome_rr_screen_get_property (GObject *gobject, guint property_id, GValue *value, GParamSpec *property)
833 {
834     GnomeRRScreen *self = GNOME_RR_SCREEN (gobject);
835     GnomeRRScreenPrivate *priv = self->priv;
836 
837     switch (property_id)
838     {
839     case SCREEN_PROP_GDK_SCREEN:
840         g_value_set_object (value, priv->gdk_screen);
841         return;
842     default:
843         G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
844         return;
845     }
846 }
847 
848 void
gnome_rr_screen_class_init(GnomeRRScreenClass * klass)849 gnome_rr_screen_class_init (GnomeRRScreenClass *klass)
850 {
851     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
852     g_type_class_add_private (klass, sizeof (GnomeRRScreenPrivate));
853 
854     gobject_class->set_property = gnome_rr_screen_set_property;
855     gobject_class->get_property = gnome_rr_screen_get_property;
856     gobject_class->finalize = gnome_rr_screen_finalize;
857 
858     g_object_class_install_property(
859             gobject_class,
860             SCREEN_PROP_GDK_SCREEN,
861             g_param_spec_object (
862                     "gdk-screen",
863                     "GDK Screen",
864                     "The GDK Screen represented by this GnomeRRScreen",
865                     GDK_TYPE_SCREEN,
866                     G_PARAM_READWRITE |
867 		    G_PARAM_CONSTRUCT_ONLY |
868 		    G_PARAM_STATIC_STRINGS)
869             );
870 
871     screen_signals[SCREEN_CHANGED] = g_signal_new("changed",
872             G_TYPE_FROM_CLASS (gobject_class),
873             G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
874             G_STRUCT_OFFSET (GnomeRRScreenClass, changed),
875             NULL,
876             NULL,
877             g_cclosure_marshal_VOID__VOID,
878             G_TYPE_NONE,
879 	    0);
880 
881     /**
882      * GnomeRRScreen::output-connected:
883      * @screen: the #GnomeRRScreen that emitted the signal
884      * @output: the #GnomeRROutput that was connected
885      *
886      * This signal is emitted when a display device is connected to a
887      * port, or a port is hotplugged with an active output. The latter
888      * can happen if a laptop is docked, and the dock provides a new
889      * active output.
890      *
891      * The @output value is not a #GObject. The returned @output value can
892      * only assume to be valid during the emission of the signal (i.e. within
893      * your signal handler only), as it may change later when the @screen
894      * is modified due to an event from the X server, or due to another
895      * place in the application modifying the @screen and the @output.
896      * Therefore, deal with changes to the @output right in your signal
897      * handler, instead of keeping the @output reference for an async or
898      * idle function.
899      **/
900     screen_signals[SCREEN_OUTPUT_CONNECTED] = g_signal_new("output-connected",
901             G_TYPE_FROM_CLASS (gobject_class),
902             G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
903             G_STRUCT_OFFSET (GnomeRRScreenClass, output_connected),
904             NULL,
905             NULL,
906             g_cclosure_marshal_VOID__POINTER,
907             G_TYPE_NONE,
908             1, G_TYPE_POINTER);
909 
910     /**
911      * GnomeRRScreen::output-disconnected:
912      * @screen: the #GnomeRRScreen that emitted the signal
913      * @output: the #GnomeRROutput that was disconnected
914      *
915      * This signal is emitted when a display device is disconnected from
916      * a port, or a port output is hot-unplugged. The latter can happen
917      * if a laptop is undocked, and the dock provided the output.
918      *
919      * The @output value is not a #GObject. The returned @output value can
920      * only assume to be valid during the emission of the signal (i.e. within
921      * your signal handler only), as it may change later when the @screen
922      * is modified due to an event from the X server, or due to another
923      * place in the application modifying the @screen and the @output.
924      * Therefore, deal with changes to the @output right in your signal
925      * handler, instead of keeping the @output reference for an async or
926      * idle function.
927      **/
928     screen_signals[SCREEN_OUTPUT_DISCONNECTED] = g_signal_new("output-disconnected",
929             G_TYPE_FROM_CLASS (gobject_class),
930             G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
931             G_STRUCT_OFFSET (GnomeRRScreenClass, output_disconnected),
932             NULL,
933             NULL,
934             g_cclosure_marshal_VOID__POINTER,
935             G_TYPE_NONE,
936             1, G_TYPE_POINTER);
937 }
938 
939 void
gnome_rr_screen_init(GnomeRRScreen * self)940 gnome_rr_screen_init (GnomeRRScreen *self)
941 {
942     GnomeRRScreenPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GNOME_TYPE_RR_SCREEN, GnomeRRScreenPrivate);
943     self->priv = priv;
944 
945     priv->gdk_screen = NULL;
946     priv->gdk_root = NULL;
947     priv->xdisplay = NULL;
948     priv->xroot = None;
949     priv->xscreen = NULL;
950     priv->info = NULL;
951     priv->rr_major_version = 0;
952     priv->rr_minor_version = 0;
953 }
954 
955 /* Weak reference callback set in gnome_rr_screen_new(); we remove the GObject data from the GdkScreen. */
956 static void
rr_screen_weak_notify_cb(gpointer data,GObject * where_the_object_was)957 rr_screen_weak_notify_cb (gpointer data, GObject *where_the_object_was)
958 {
959     GdkScreen *screen = GDK_SCREEN (data);
960 
961     g_object_set_data (G_OBJECT (screen), "GnomeRRScreen", NULL);
962 }
963 
964 /**
965  * gnome_rr_screen_new:
966  * @screen: the #GdkScreen on which to operate
967  * @error: will be set if XRandR is not supported
968  *
969  * Creates a unique #GnomeRRScreen instance for the specified @screen.
970  *
971  * Returns: a unique #GnomeRRScreen instance, specific to the @screen, or NULL
972  * if this could not be created, for instance if the driver does not support
973  * Xrandr 1.2.  Each #GdkScreen thus has a single instance of #GnomeRRScreen.
974  */
975 GnomeRRScreen *
gnome_rr_screen_new(GdkScreen * screen,GError ** error)976 gnome_rr_screen_new (GdkScreen *screen,
977 		     GError **error)
978 {
979     GnomeRRScreen *rr_screen;
980 
981     g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
982     g_return_val_if_fail (error == NULL || *error == NULL, NULL);
983 
984     rr_screen = g_object_get_data (G_OBJECT (screen), "GnomeRRScreen");
985     if (rr_screen)
986 	g_object_ref (rr_screen);
987     else {
988 	_gnome_desktop_init_i18n ();
989 
990 	rr_screen = g_initable_new (GNOME_TYPE_RR_SCREEN, NULL, error, "gdk-screen", screen, NULL);
991 	if (rr_screen) {
992 	    g_object_set_data (G_OBJECT (screen), "GnomeRRScreen", rr_screen);
993 	    g_object_weak_ref (G_OBJECT (rr_screen), rr_screen_weak_notify_cb, screen);
994 	}
995     }
996 
997     return rr_screen;
998 }
999 
1000 void
gnome_rr_screen_set_size(GnomeRRScreen * screen,int width,int height,int mm_width,int mm_height)1001 gnome_rr_screen_set_size (GnomeRRScreen *screen,
1002 			  int	      width,
1003 			  int       height,
1004 			  int       mm_width,
1005 			  int       mm_height)
1006 {
1007     g_return_if_fail (GNOME_IS_RR_SCREEN (screen));
1008 
1009     g_debug ("Setting screen size: %d x %d, %dmm x %dmm", width, height, mm_width, mm_height);
1010 
1011     gdk_error_trap_push ();
1012     XRRSetScreenSize (screen->priv->xdisplay, screen->priv->xroot,
1013 		      width, height, mm_width, mm_height);
1014     gdk_error_trap_pop_ignored ();
1015 }
1016 
1017 /**
1018  * gnome_rr_screen_get_ranges:
1019  * @screen: a #GnomeRRScreen
1020  * @min_width: (out): the minimum width
1021  * @max_width: (out): the maximum width
1022  * @min_height: (out): the minimum height
1023  * @max_height: (out): the maximum height
1024  *
1025  * Get the ranges of the screen
1026  */
1027 void
gnome_rr_screen_get_ranges(GnomeRRScreen * screen,int * min_width,int * max_width,int * min_height,int * max_height)1028 gnome_rr_screen_get_ranges (GnomeRRScreen *screen,
1029 			    int	          *min_width,
1030 			    int	          *max_width,
1031 			    int           *min_height,
1032 			    int	          *max_height)
1033 {
1034     GnomeRRScreenPrivate *priv;
1035 
1036     g_return_if_fail (GNOME_IS_RR_SCREEN (screen));
1037 
1038     priv = screen->priv;
1039 
1040     if (min_width)
1041 	*min_width = priv->info->min_width;
1042 
1043     if (max_width)
1044 	*max_width = priv->info->max_width;
1045 
1046     if (min_height)
1047 	*min_height = priv->info->min_height;
1048 
1049     if (max_height)
1050 	*max_height = priv->info->max_height;
1051 }
1052 
1053 /**
1054  * gnome_rr_screen_get_timestamps:
1055  * @screen: a #GnomeRRScreen
1056  * @change_timestamp_ret: (out): Location in which to store the timestamp at which the RANDR configuration was last changed
1057  * @config_timestamp_ret: (out): Location in which to store the timestamp at which the RANDR configuration was last obtained
1058  *
1059  * Queries the two timestamps that the X RANDR extension maintains.  The X
1060  * server will prevent change requests for stale configurations, those whose
1061  * timestamp is not equal to that of the latest request for configuration.  The
1062  * X server will also prevent change requests that have an older timestamp to
1063  * the latest change request.
1064  */
1065 void
gnome_rr_screen_get_timestamps(GnomeRRScreen * screen,guint32 * change_timestamp_ret,guint32 * config_timestamp_ret)1066 gnome_rr_screen_get_timestamps (GnomeRRScreen *screen,
1067 				guint32       *change_timestamp_ret,
1068 				guint32       *config_timestamp_ret)
1069 {
1070     GnomeRRScreenPrivate *priv;
1071 
1072     g_return_if_fail (GNOME_IS_RR_SCREEN (screen));
1073 
1074     priv = screen->priv;
1075 
1076     if (change_timestamp_ret)
1077 	*change_timestamp_ret = priv->info->resources->timestamp;
1078 
1079     if (config_timestamp_ret)
1080 	*config_timestamp_ret = priv->info->resources->configTimestamp;
1081 }
1082 
1083 static gboolean
force_timestamp_update(GnomeRRScreen * screen)1084 force_timestamp_update (GnomeRRScreen *screen)
1085 {
1086     GnomeRRScreenPrivate *priv = screen->priv;
1087     GnomeRRCrtc *crtc;
1088     XRRCrtcInfo *current_info;
1089     Status status;
1090     gboolean timestamp_updated;
1091 
1092     timestamp_updated = FALSE;
1093 
1094     crtc = priv->info->crtcs[0];
1095 
1096     if (crtc == NULL)
1097 	goto out;
1098 
1099     current_info = XRRGetCrtcInfo (priv->xdisplay,
1100 				   priv->info->resources,
1101 				   crtc->id);
1102 
1103     if (current_info == NULL)
1104 	goto out;
1105 
1106     gdk_error_trap_push ();
1107     status = XRRSetCrtcConfig (priv->xdisplay,
1108 			       priv->info->resources,
1109 			       crtc->id,
1110 			       current_info->timestamp,
1111 			       current_info->x,
1112 			       current_info->y,
1113 			       current_info->mode,
1114 			       current_info->rotation,
1115 			       current_info->outputs,
1116 			       current_info->noutput);
1117 
1118     XRRFreeCrtcInfo (current_info);
1119 
1120     gdk_flush ();
1121     if (gdk_error_trap_pop ())
1122 	goto out;
1123 
1124     if (status == RRSetConfigSuccess)
1125 	timestamp_updated = TRUE;
1126 out:
1127     return timestamp_updated;
1128 }
1129 
1130 /**
1131  * gnome_rr_screen_refresh:
1132  * @screen: a #GnomeRRScreen
1133  * @error: location to store error, or %NULL
1134  *
1135  * Refreshes the screen configuration, and calls the screen's callback if it
1136  * exists and if the screen's configuration changed.
1137  *
1138  * Return value: TRUE if the screen's configuration changed; otherwise, the
1139  * function returns FALSE and a NULL error if the configuration didn't change,
1140  * or FALSE and a non-NULL error if there was an error while refreshing the
1141  * configuration.
1142  */
1143 gboolean
gnome_rr_screen_refresh(GnomeRRScreen * screen,GError ** error)1144 gnome_rr_screen_refresh (GnomeRRScreen *screen,
1145 			 GError       **error)
1146 {
1147     gboolean refreshed;
1148 
1149     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1150 
1151     gdk_x11_display_grab (gdk_screen_get_display (screen->priv->gdk_screen));
1152 
1153     refreshed = screen_update (screen, FALSE, TRUE, error);
1154     force_timestamp_update (screen); /* this is to keep other clients from thinking that the X server re-detected things by itself - bgo#621046 */
1155 
1156     gdk_x11_display_ungrab (gdk_screen_get_display (screen->priv->gdk_screen));
1157 
1158     return refreshed;
1159 }
1160 
1161 /**
1162  * gnome_rr_screen_get_dpms_mode:
1163  **/
1164 gboolean
gnome_rr_screen_get_dpms_mode(GnomeRRScreen * screen,GnomeRRDpmsMode * mode,GError ** error)1165 gnome_rr_screen_get_dpms_mode (GnomeRRScreen *screen,
1166                                GnomeRRDpmsMode *mode,
1167                                GError **error)
1168 {
1169     BOOL enabled = FALSE;
1170     CARD16 state;
1171     gboolean ret = FALSE;
1172 
1173     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1174     g_return_val_if_fail (mode != NULL, FALSE);
1175 
1176     if (!screen->priv->dpms_capable) {
1177         g_set_error_literal (error,
1178                              GNOME_RR_ERROR,
1179                              GNOME_RR_ERROR_NO_DPMS_EXTENSION,
1180                              "Display is not DPMS capable");
1181         goto out;
1182     }
1183 
1184     if (!DPMSInfo (screen->priv->xdisplay,
1185                    &state,
1186                    &enabled)) {
1187         g_set_error_literal (error,
1188                              GNOME_RR_ERROR,
1189                              GNOME_RR_ERROR_UNKNOWN,
1190                              "Unable to get DPMS state");
1191         goto out;
1192     }
1193 
1194     /* DPMS not enabled is a valid mode */
1195     if (!enabled) {
1196         *mode = GNOME_RR_DPMS_DISABLED;
1197         ret = TRUE;
1198         goto out;
1199     }
1200 
1201     switch (state) {
1202     case DPMSModeOn:
1203         *mode = GNOME_RR_DPMS_ON;
1204         break;
1205     case DPMSModeStandby:
1206         *mode = GNOME_RR_DPMS_STANDBY;
1207         break;
1208     case DPMSModeSuspend:
1209         *mode = GNOME_RR_DPMS_SUSPEND;
1210         break;
1211     case DPMSModeOff:
1212         *mode = GNOME_RR_DPMS_OFF;
1213         break;
1214     default:
1215         g_assert_not_reached ();
1216         break;
1217     }
1218     ret = TRUE;
1219 out:
1220     return ret;
1221 }
1222 
1223 /**
1224  * gnome_rr_screen_clear_dpms_timeouts:
1225  **/
1226 static gboolean
gnome_rr_screen_clear_dpms_timeouts(GnomeRRScreen * screen,GError ** error)1227 gnome_rr_screen_clear_dpms_timeouts (GnomeRRScreen *screen,
1228                                      GError **error)
1229 {
1230     gdk_error_trap_push ();
1231     /* DPMSSetTimeouts() return value is often a lie, so ignore it */
1232     DPMSSetTimeouts (screen->priv->xdisplay, 0, 0, 0);
1233     if (gdk_error_trap_pop ()) {
1234         g_set_error_literal (error,
1235                              GNOME_RR_ERROR,
1236                              GNOME_RR_ERROR_UNKNOWN,
1237                              "Could not set DPMS timeouts");
1238         return FALSE;
1239     }
1240     return TRUE;
1241 }
1242 
1243 /**
1244  * gnome_rr_screen_set_dpms_mode:
1245  *
1246  * This method also disables the DPMS timeouts.
1247  **/
1248 gboolean
gnome_rr_screen_set_dpms_mode(GnomeRRScreen * screen,GnomeRRDpmsMode mode,GError ** error)1249 gnome_rr_screen_set_dpms_mode (GnomeRRScreen *screen,
1250                                GnomeRRDpmsMode mode,
1251                                GError **error)
1252 {
1253     CARD16 state = 0;
1254     gboolean ret;
1255     GnomeRRDpmsMode current_mode;
1256 
1257     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1258 
1259     /* set, if the new mode is different */
1260     ret = gnome_rr_screen_get_dpms_mode (screen, &current_mode, error);
1261     if (!ret)
1262         goto out;
1263     if (current_mode == mode) {
1264         ret = gnome_rr_screen_clear_dpms_timeouts (screen, error);
1265         goto out;
1266     }
1267 
1268     switch (mode) {
1269     case GNOME_RR_DPMS_ON:
1270         state = DPMSModeOn;
1271         break;
1272     case GNOME_RR_DPMS_STANDBY:
1273         state = DPMSModeStandby;
1274         break;
1275     case GNOME_RR_DPMS_SUSPEND:
1276         state = DPMSModeSuspend;
1277         break;
1278     case GNOME_RR_DPMS_OFF:
1279         state = DPMSModeOff;
1280         break;
1281     default:
1282         g_assert_not_reached ();
1283         break;
1284     }
1285 
1286     gdk_error_trap_push ();
1287     /* DPMSForceLevel() return value is often a lie, so ignore it */
1288     DPMSForceLevel (screen->priv->xdisplay, state);
1289     if (gdk_error_trap_pop ()) {
1290         ret = FALSE;
1291         g_set_error_literal (error,
1292                              GNOME_RR_ERROR,
1293                              GNOME_RR_ERROR_UNKNOWN,
1294                              "Could not change DPMS mode");
1295         goto out;
1296     }
1297 
1298     ret = gnome_rr_screen_clear_dpms_timeouts (screen, error);
1299     if (!ret)
1300         goto out;
1301 out:
1302     return ret;
1303 }
1304 
1305 /**
1306  * gnome_rr_screen_list_modes:
1307  *
1308  * List available XRandR modes
1309  *
1310  * Returns: (array zero-terminated=1) (transfer none):
1311  */
1312 GnomeRRMode **
gnome_rr_screen_list_modes(GnomeRRScreen * screen)1313 gnome_rr_screen_list_modes (GnomeRRScreen *screen)
1314 {
1315     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1316     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1317 
1318     return screen->priv->info->modes;
1319 }
1320 
1321 /**
1322  * gnome_rr_screen_list_clone_modes:
1323  *
1324  * List available XRandR clone modes
1325  *
1326  * Returns: (array zero-terminated=1) (transfer none):
1327  */
1328 GnomeRRMode **
gnome_rr_screen_list_clone_modes(GnomeRRScreen * screen)1329 gnome_rr_screen_list_clone_modes   (GnomeRRScreen *screen)
1330 {
1331     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1332     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1333 
1334     return screen->priv->info->clone_modes;
1335 }
1336 
1337 /**
1338  * gnome_rr_screen_list_crtcs:
1339  *
1340  * List all CRTCs
1341  *
1342  * Returns: (array zero-terminated=1) (transfer none):
1343  */
1344 GnomeRRCrtc **
gnome_rr_screen_list_crtcs(GnomeRRScreen * screen)1345 gnome_rr_screen_list_crtcs (GnomeRRScreen *screen)
1346 {
1347     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1348     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1349 
1350     return screen->priv->info->crtcs;
1351 }
1352 
1353 /**
1354  * gnome_rr_screen_list_outputs:
1355  *
1356  * List all outputs
1357  *
1358  * Returns: (array zero-terminated=1) (transfer none):
1359  */
1360 GnomeRROutput **
gnome_rr_screen_list_outputs(GnomeRRScreen * screen)1361 gnome_rr_screen_list_outputs (GnomeRRScreen *screen)
1362 {
1363     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1364     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1365 
1366     return screen->priv->info->outputs;
1367 }
1368 
1369 /**
1370  * gnome_rr_screen_get_crtc_by_id:
1371  *
1372  * Returns: (transfer none): the CRTC identified by @id
1373  */
1374 GnomeRRCrtc *
gnome_rr_screen_get_crtc_by_id(GnomeRRScreen * screen,guint32 id)1375 gnome_rr_screen_get_crtc_by_id (GnomeRRScreen *screen,
1376 				guint32        id)
1377 {
1378     GnomeRRCrtc **crtcs;
1379     int i;
1380 
1381     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1382     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1383 
1384     crtcs = screen->priv->info->crtcs;
1385 
1386     for (i = 0; crtcs[i] != NULL; ++i)
1387     {
1388 	if (crtcs[i]->id == id)
1389 	    return crtcs[i];
1390     }
1391 
1392     return NULL;
1393 }
1394 
1395 /**
1396  * gnome_rr_screen_get_output_by_id:
1397  *
1398  * Returns: (transfer none): the output identified by @id
1399  */
1400 GnomeRROutput *
gnome_rr_screen_get_output_by_id(GnomeRRScreen * screen,guint32 id)1401 gnome_rr_screen_get_output_by_id (GnomeRRScreen *screen,
1402 				  guint32        id)
1403 {
1404     GnomeRROutput **outputs;
1405     int i;
1406 
1407     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1408     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1409 
1410     outputs = screen->priv->info->outputs;
1411 
1412     for (i = 0; outputs[i] != NULL; ++i)
1413     {
1414 	if (outputs[i]->id == id)
1415 	    return outputs[i];
1416     }
1417 
1418     return NULL;
1419 }
1420 
1421 /* GnomeRROutput */
1422 static GnomeRROutput *
output_new(ScreenInfo * info,RROutput id)1423 output_new (ScreenInfo *info, RROutput id)
1424 {
1425     GnomeRROutput *output = g_slice_new0 (GnomeRROutput);
1426 
1427     output->id = id;
1428     output->info = info;
1429 
1430     return output;
1431 }
1432 
1433 static guint8 *
get_property(Display * dpy,RROutput output,Atom atom,gsize * len)1434 get_property (Display *dpy,
1435 	      RROutput output,
1436 	      Atom atom,
1437 	      gsize *len)
1438 {
1439     unsigned char *prop;
1440     int actual_format;
1441     unsigned long nitems, bytes_after;
1442     Atom actual_type;
1443     guint8 *result;
1444 
1445     XRRGetOutputProperty (dpy, output, atom,
1446 			  0, 100, False, False,
1447 			  AnyPropertyType,
1448 			  &actual_type, &actual_format,
1449 			  &nitems, &bytes_after, &prop);
1450 
1451     if (actual_type == XA_INTEGER && actual_format == 8)
1452     {
1453 	result = g_memdup (prop, nitems);
1454 	if (len)
1455 	    *len = nitems;
1456     }
1457     else
1458     {
1459 	result = NULL;
1460     }
1461 
1462     XFree (prop);
1463 
1464     return result;
1465 }
1466 
1467 static guint8 *
read_edid_data(GnomeRROutput * output,gsize * len)1468 read_edid_data (GnomeRROutput *output, gsize *len)
1469 {
1470     Atom edid_atom;
1471     guint8 *result;
1472 
1473     edid_atom = XInternAtom (DISPLAY (output), "EDID", FALSE);
1474     result = get_property (DISPLAY (output),
1475 			   output->id, edid_atom, len);
1476 
1477     if (!result)
1478     {
1479 	edid_atom = XInternAtom (DISPLAY (output), "EDID_DATA", FALSE);
1480 	result = get_property (DISPLAY (output),
1481 			       output->id, edid_atom, len);
1482     }
1483 
1484     if (!result)
1485     {
1486 	edid_atom = XInternAtom (DISPLAY (output), "XFree86_DDC_EDID1_RAWDATA", FALSE);
1487 	result = get_property (DISPLAY (output),
1488 			       output->id, edid_atom, len);
1489     }
1490 
1491     if (result)
1492     {
1493 	if (*len % 128 == 0)
1494 	    return result;
1495 	else
1496 	    g_free (result);
1497     }
1498 
1499     return NULL;
1500 }
1501 
1502 static char *
get_connector_type_string(GnomeRROutput * output)1503 get_connector_type_string (GnomeRROutput *output)
1504 {
1505     char *result;
1506     unsigned char *prop;
1507     int actual_format;
1508     unsigned long nitems, bytes_after;
1509     Atom actual_type;
1510     Atom connector_type;
1511     char *connector_type_str;
1512 
1513     result = NULL;
1514 
1515     if (XRRGetOutputProperty (DISPLAY (output), output->id, output->info->screen->priv->connector_type_atom,
1516 			      0, 100, False, False,
1517 			      AnyPropertyType,
1518 			      &actual_type, &actual_format,
1519 			      &nitems, &bytes_after, &prop) != Success)
1520 	return NULL;
1521 
1522     if (!(actual_type == XA_ATOM && actual_format == 32 && nitems == 1))
1523 	goto out;
1524 
1525     connector_type = *((Atom *) prop);
1526 
1527     connector_type_str = XGetAtomName (DISPLAY (output), connector_type);
1528     if (connector_type_str) {
1529 	result = g_strdup (connector_type_str); /* so the caller can g_free() it */
1530 	XFree (connector_type_str);
1531     }
1532 
1533 out:
1534 
1535     XFree (prop);
1536 
1537     return result;
1538 }
1539 
1540 static void
update_brightness_limits(GnomeRROutput * output)1541 update_brightness_limits (GnomeRROutput *output)
1542 {
1543     gint rc;
1544     Atom atom;
1545     XRRPropertyInfo *info;
1546 
1547     gdk_error_trap_push ();
1548     atom = XInternAtom (DISPLAY (output), "BACKLIGHT", FALSE);
1549     info = XRRQueryOutputProperty (DISPLAY (output), output->id, atom);
1550     rc = gdk_error_trap_pop ();
1551     if (rc != Success)
1552     {
1553         if (rc != BadName)
1554           g_warning ("could not get output property for %s, rc: %i",
1555 		     output->name, rc);
1556         goto out;
1557     }
1558     if (info == NULL)
1559     {
1560         g_warning ("could not get output property for %s",
1561 		   output->name);
1562         goto out;
1563     }
1564     if (!info->range || info->num_values != 2)
1565     {
1566         g_debug ("backlight %s was not range", output->name);
1567         goto out;
1568     }
1569     output->backlight_min = info->values[0];
1570     output->backlight_max = info->values[1];
1571 out:
1572     if (info != NULL)
1573     {
1574         XFree (info);
1575     }
1576 }
1577 
1578 static gboolean
output_initialize(GnomeRROutput * output,XRRScreenResources * res,GError ** error)1579 output_initialize (GnomeRROutput *output, XRRScreenResources *res, GError **error)
1580 {
1581     XRROutputInfo *info = XRRGetOutputInfo (DISPLAY (output),
1582                                             res,
1583                                             output->id);
1584     GPtrArray *a;
1585     int i;
1586 
1587 #if 0
1588     g_print ("Output %lx Timestamp: %u\n", output->id, (guint32)info->timestamp);
1589 #endif
1590 
1591     if (!info || !output->info)
1592     {
1593         /* FIXME: see the comment in crtc_initialize() */
1594         /* Translators: here, an "output" is a video output */
1595         g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR,
1596                      _("could not get information about output %d"),
1597                      (int) output->id);
1598         return FALSE;
1599     }
1600 
1601     output->name = g_strdup (info->name); /* FIXME: what is nameLen used for? */
1602     output->current_crtc = crtc_by_id (output->info, info->crtc);
1603     output->width_mm = info->mm_width;
1604     output->height_mm = info->mm_height;
1605     output->connected = (info->connection == RR_Connected);
1606     output->connector_type = get_connector_type_string (output);
1607 
1608     /* Possible crtcs */
1609     a = g_ptr_array_new ();
1610 
1611     for (i = 0; i < info->ncrtc; ++i)
1612     {
1613         GnomeRRCrtc *crtc = crtc_by_id (output->info, info->crtcs[i]);
1614 
1615         if (crtc)
1616             g_ptr_array_add (a, crtc);
1617     }
1618     g_ptr_array_add (a, NULL);
1619     output->possible_crtcs = (GnomeRRCrtc **)g_ptr_array_free (a, FALSE);
1620 
1621     /* Clones */
1622     a = g_ptr_array_new ();
1623     for (i = 0; i < info->nclone; ++i)
1624     {
1625         GnomeRROutput *gnome_rr_output = gnome_rr_output_by_id (output->info, info->clones[i]);
1626 
1627         if (gnome_rr_output)
1628             g_ptr_array_add (a, gnome_rr_output);
1629     }
1630     g_ptr_array_add (a, NULL);
1631     output->clones = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
1632 
1633     /* Modes */
1634     a = g_ptr_array_new ();
1635     for (i = 0; i < info->nmode; ++i)
1636     {
1637         GnomeRRMode *mode = mode_by_id (output->info, info->modes[i]);
1638 
1639         if (mode)
1640             g_ptr_array_add (a, mode);
1641     }
1642     g_ptr_array_add (a, NULL);
1643     output->modes = (GnomeRRMode **)g_ptr_array_free (a, FALSE);
1644 
1645     output->n_preferred = info->npreferred;
1646 
1647     /* Edid data */
1648     output->edid_data = read_edid_data (output, &output->edid_size);
1649 
1650     /* brightness data */
1651     if (output->connected)
1652         update_brightness_limits (output);
1653 
1654     XRRFreeOutputInfo (info);
1655 
1656     return TRUE;
1657 }
1658 
1659 static GnomeRROutput*
output_copy(const GnomeRROutput * from)1660 output_copy (const GnomeRROutput *from)
1661 {
1662     GPtrArray *array;
1663     GnomeRRCrtc **p_crtc;
1664     GnomeRROutput **p_output;
1665     GnomeRRMode **p_mode;
1666     GnomeRROutput *output = g_slice_new0 (GnomeRROutput);
1667 
1668     output->id = from->id;
1669     output->info = from->info;
1670     output->name = g_strdup (from->name);
1671     output->current_crtc = from->current_crtc;
1672     output->width_mm = from->width_mm;
1673     output->height_mm = from->height_mm;
1674     output->connected = from->connected;
1675     output->n_preferred = from->n_preferred;
1676     output->connector_type = g_strdup (from->connector_type);
1677     output->backlight_min = -1;
1678     output->backlight_max = -1;
1679 
1680     array = g_ptr_array_new ();
1681     for (p_crtc = from->possible_crtcs; *p_crtc != NULL; p_crtc++)
1682     {
1683         g_ptr_array_add (array, *p_crtc);
1684     }
1685     output->possible_crtcs = (GnomeRRCrtc**) g_ptr_array_free (array, FALSE);
1686 
1687     array = g_ptr_array_new ();
1688     for (p_output = from->clones; *p_output != NULL; p_output++)
1689     {
1690         g_ptr_array_add (array, *p_output);
1691     }
1692     output->clones = (GnomeRROutput**) g_ptr_array_free (array, FALSE);
1693 
1694     array = g_ptr_array_new ();
1695     for (p_mode = from->modes; *p_mode != NULL; p_mode++)
1696     {
1697         g_ptr_array_add (array, *p_mode);
1698     }
1699     output->modes = (GnomeRRMode**) g_ptr_array_free (array, FALSE);
1700 
1701     output->edid_size = from->edid_size;
1702     output->edid_data = g_memdup (from->edid_data, from->edid_size);
1703 
1704     return output;
1705 }
1706 
1707 static void
output_free(GnomeRROutput * output)1708 output_free (GnomeRROutput *output)
1709 {
1710     g_free (output->clones);
1711     g_free (output->modes);
1712     g_free (output->possible_crtcs);
1713     g_free (output->edid_data);
1714     g_free (output->name);
1715     g_free (output->connector_type);
1716     g_slice_free (GnomeRROutput, output);
1717 }
1718 
1719 guint32
gnome_rr_output_get_id(GnomeRROutput * output)1720 gnome_rr_output_get_id (GnomeRROutput *output)
1721 {
1722     g_assert(output != NULL);
1723 
1724     return output->id;
1725 }
1726 
1727 const guint8 *
gnome_rr_output_get_edid_data(GnomeRROutput * output,gsize * size)1728 gnome_rr_output_get_edid_data (GnomeRROutput *output, gsize *size)
1729 {
1730     g_return_val_if_fail (output != NULL, NULL);
1731     if (size)
1732         *size = output->edid_size;
1733     return output->edid_data;
1734 }
1735 
1736 gboolean
gnome_rr_output_get_ids_from_edid(GnomeRROutput * output,char ** vendor,int * product,int * serial)1737 gnome_rr_output_get_ids_from_edid (GnomeRROutput         *output,
1738                                    char                 **vendor,
1739                                    int                   *product,
1740                                    int                   *serial)
1741 {
1742     MonitorInfo *info;
1743 
1744     g_return_val_if_fail (output != NULL, FALSE);
1745 
1746     if (!output->edid_data)
1747         return FALSE;
1748     info = decode_edid (output->edid_data);
1749     if (!info)
1750         return FALSE;
1751     if (vendor)
1752         *vendor = g_memdup (info->manufacturer_code, 4);
1753     if (product)
1754         *product = info->product_code;
1755     if (serial)
1756         *serial = info->serial_number;
1757 
1758     g_free (info);
1759 
1760     return TRUE;
1761 
1762 }
1763 
1764 /**
1765  * gnome_rr_output_get_backlight_min:
1766  *
1767  * Returns: The mimimum backlight value, or -1 if not supported
1768  */
1769 gint
gnome_rr_output_get_backlight_min(GnomeRROutput * output)1770 gnome_rr_output_get_backlight_min (GnomeRROutput *output)
1771 {
1772     g_return_val_if_fail (output != NULL, -1);
1773     return output->backlight_min;
1774 }
1775 
1776 /**
1777  * gnome_rr_output_get_backlight_max:
1778  *
1779  * Returns: The maximum backlight value, or -1 if not supported
1780  */
1781 gint
gnome_rr_output_get_backlight_max(GnomeRROutput * output)1782 gnome_rr_output_get_backlight_max (GnomeRROutput *output)
1783 {
1784     g_return_val_if_fail (output != NULL, -1);
1785     return output->backlight_max;
1786 }
1787 
1788 /**
1789  * gnome_rr_output_get_backlight:
1790  *
1791  * Returns: The currently set backlight brightness
1792  */
1793 gint
gnome_rr_output_get_backlight(GnomeRROutput * output,GError ** error)1794 gnome_rr_output_get_backlight (GnomeRROutput *output, GError **error)
1795 {
1796     guint now = -1;
1797     unsigned long nitems;
1798     unsigned long bytes_after;
1799     guint *prop;
1800     Atom atom;
1801     Atom actual_type;
1802     int actual_format;
1803     gint retval;
1804 
1805     g_return_val_if_fail (output != NULL, -1);
1806 
1807     gdk_error_trap_push ();
1808     atom = XInternAtom (DISPLAY (output), "BACKLIGHT", FALSE);
1809     retval = XRRGetOutputProperty (DISPLAY (output), output->id, atom,
1810 				   0, 4, False, False, None,
1811 				   &actual_type, &actual_format,
1812 				   &nitems, &bytes_after, ((unsigned char **)&prop));
1813     gdk_flush ();
1814     if (gdk_error_trap_pop ())
1815     {
1816         g_set_error_literal (error,
1817 			     GNOME_RR_ERROR,
1818 			     GNOME_RR_ERROR_UNKNOWN,
1819 			     "unhandled X error while getting the range of backlight values");
1820         goto out;
1821     }
1822 
1823     if (retval != Success) {
1824         g_set_error_literal (error,
1825 			     GNOME_RR_ERROR,
1826 			     GNOME_RR_ERROR_RANDR_ERROR,
1827 			     "could not get the range of backlight values");
1828         goto out;
1829     }
1830     if (actual_type == XA_INTEGER &&
1831         nitems == 1 &&
1832         actual_format == 32)
1833     {
1834         memcpy (&now, prop, sizeof (guint));
1835     }
1836     else
1837     {
1838 	g_set_error (error,
1839 		     GNOME_RR_ERROR,
1840 		     GNOME_RR_ERROR_RANDR_ERROR,
1841 		     "failed to get correct property type, got %lu,%i",
1842 		     nitems, actual_format);
1843     }
1844 out:
1845     XFree (prop);
1846     return now;
1847 }
1848 
1849 /**
1850  * gnome_rr_output_set_backlight:
1851  * @value: the absolute value which is min >= this <= max
1852  *
1853  * Returns: %TRUE for success
1854  */
1855 gboolean
gnome_rr_output_set_backlight(GnomeRROutput * output,gint value,GError ** error)1856 gnome_rr_output_set_backlight (GnomeRROutput *output, gint value, GError **error)
1857 {
1858     gboolean ret = FALSE;
1859     Atom atom;
1860 
1861     g_return_val_if_fail (output != NULL, FALSE);
1862 
1863     /* check this is sane */
1864     if (value < output->backlight_min ||
1865         value > output->backlight_max)
1866     {
1867 	g_set_error (error,
1868 		     GNOME_RR_ERROR,
1869 		     GNOME_RR_ERROR_BOUNDS_ERROR,
1870 		     "out of brightness range: %i, has to be %i -> %i",
1871 		     value,
1872 		     output->backlight_max, output->backlight_min);
1873 	goto out;
1874     }
1875 
1876     /* don't abort on error */
1877     gdk_error_trap_push ();
1878     atom = XInternAtom (DISPLAY (output), "BACKLIGHT", FALSE);
1879     XRRChangeOutputProperty (DISPLAY (output), output->id, atom,
1880 			     XA_INTEGER, 32, PropModeReplace,
1881 			     (unsigned char *) &value, 1);
1882     if (gdk_error_trap_pop ())
1883     {
1884         g_set_error_literal (error,
1885 			     GNOME_RR_ERROR,
1886 			     GNOME_RR_ERROR_UNKNOWN,
1887 			     "unhandled X error while setting the backlight values");
1888         goto out;
1889     }
1890 
1891     /* we assume this succeeded as there's no return value */
1892     ret = TRUE;
1893 out:
1894     return ret;
1895 }
1896 
1897 /**
1898  * gnome_rr_screen_get_output_by_name:
1899  *
1900  * Returns: (transfer none): the output identified by @name
1901  */
1902 GnomeRROutput *
gnome_rr_screen_get_output_by_name(GnomeRRScreen * screen,const char * name)1903 gnome_rr_screen_get_output_by_name (GnomeRRScreen *screen,
1904 				    const char    *name)
1905 {
1906     int i;
1907 
1908     g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), NULL);
1909     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1910 
1911     for (i = 0; screen->priv->info->outputs[i] != NULL; ++i)
1912     {
1913 	GnomeRROutput *output = screen->priv->info->outputs[i];
1914 
1915 	if (strcmp (output->name, name) == 0)
1916 	    return output;
1917     }
1918 
1919     return NULL;
1920 }
1921 
1922 GnomeRRCrtc *
gnome_rr_output_get_crtc(GnomeRROutput * output)1923 gnome_rr_output_get_crtc (GnomeRROutput *output)
1924 {
1925     g_return_val_if_fail (output != NULL, NULL);
1926 
1927     return output->current_crtc;
1928 }
1929 
1930 /* Returns NULL if the ConnectorType property is not available */
1931 const char *
gnome_rr_output_get_connector_type(GnomeRROutput * output)1932 gnome_rr_output_get_connector_type (GnomeRROutput *output)
1933 {
1934     g_return_val_if_fail (output != NULL, NULL);
1935 
1936     return output->connector_type;
1937 }
1938 
1939 gboolean
_gnome_rr_output_name_is_laptop(const char * name)1940 _gnome_rr_output_name_is_laptop (const char *name)
1941 {
1942     if (!name)
1943         return FALSE;
1944 
1945     if (strstr (name, "lvds") ||  /* Most drivers use an "LVDS" prefix... */
1946 	strstr (name, "LVDS") ||
1947 	strstr (name, "Lvds") ||
1948 	strstr (name, "LCD")  ||  /* ... but fglrx uses "LCD" in some versions.  Shoot me now, kthxbye. */
1949 	strstr (name, "eDP")  ||  /* eDP is for internal laptop panel connections */
1950 	strstr (name, "default")) /* Finally, NVidia and all others that don't bother to do RANDR properly */
1951         return TRUE;
1952 
1953     return FALSE;
1954 }
1955 
1956 gboolean
gnome_rr_output_is_laptop(GnomeRROutput * output)1957 gnome_rr_output_is_laptop (GnomeRROutput *output)
1958 {
1959     g_return_val_if_fail (output != NULL, FALSE);
1960 
1961     if (!output->connected)
1962 	return FALSE;
1963 
1964     /* The ConnectorType property is present in RANDR 1.3 and greater */
1965     if (g_strcmp0 (output->connector_type, GNOME_RR_CONNECTOR_TYPE_PANEL) == 0)
1966 	return TRUE;
1967 
1968     /* Older versions of RANDR - this is a best guess, as @#$% RANDR doesn't have standard output names,
1969      * so drivers can use whatever they like.
1970      */
1971     if (_gnome_rr_output_name_is_laptop (output->name))
1972         return TRUE;
1973 
1974     return FALSE;
1975 }
1976 
1977 GnomeRRMode *
gnome_rr_output_get_current_mode(GnomeRROutput * output)1978 gnome_rr_output_get_current_mode (GnomeRROutput *output)
1979 {
1980     GnomeRRCrtc *crtc;
1981 
1982     g_return_val_if_fail (output != NULL, NULL);
1983 
1984     if ((crtc = gnome_rr_output_get_crtc (output)))
1985 	return gnome_rr_crtc_get_current_mode (crtc);
1986 
1987     return NULL;
1988 }
1989 
1990 void
gnome_rr_output_get_position(GnomeRROutput * output,int * x,int * y)1991 gnome_rr_output_get_position (GnomeRROutput   *output,
1992 			      int             *x,
1993 			      int             *y)
1994 {
1995     GnomeRRCrtc *crtc;
1996 
1997     g_return_if_fail (output != NULL);
1998 
1999     if ((crtc = gnome_rr_output_get_crtc (output)))
2000 	gnome_rr_crtc_get_position (crtc, x, y);
2001 }
2002 
2003 const char *
gnome_rr_output_get_name(GnomeRROutput * output)2004 gnome_rr_output_get_name (GnomeRROutput *output)
2005 {
2006     g_assert (output != NULL);
2007     return output->name;
2008 }
2009 
2010 int
gnome_rr_output_get_width_mm(GnomeRROutput * output)2011 gnome_rr_output_get_width_mm (GnomeRROutput *output)
2012 {
2013     g_assert (output != NULL);
2014     return output->width_mm;
2015 }
2016 
2017 int
gnome_rr_output_get_height_mm(GnomeRROutput * output)2018 gnome_rr_output_get_height_mm (GnomeRROutput *output)
2019 {
2020     g_assert (output != NULL);
2021     return output->height_mm;
2022 }
2023 
2024 GnomeRRMode *
gnome_rr_output_get_preferred_mode(GnomeRROutput * output)2025 gnome_rr_output_get_preferred_mode (GnomeRROutput *output)
2026 {
2027     g_return_val_if_fail (output != NULL, NULL);
2028     if (output->n_preferred)
2029 	return output->modes[0];
2030 
2031     return NULL;
2032 }
2033 
2034 GnomeRRMode **
gnome_rr_output_list_modes(GnomeRROutput * output)2035 gnome_rr_output_list_modes (GnomeRROutput *output)
2036 {
2037     g_return_val_if_fail (output != NULL, NULL);
2038     return output->modes;
2039 }
2040 
2041 gboolean
gnome_rr_output_is_connected(GnomeRROutput * output)2042 gnome_rr_output_is_connected (GnomeRROutput *output)
2043 {
2044     g_return_val_if_fail (output != NULL, FALSE);
2045     return output->connected;
2046 }
2047 
2048 gboolean
gnome_rr_output_supports_mode(GnomeRROutput * output,GnomeRRMode * mode)2049 gnome_rr_output_supports_mode (GnomeRROutput *output,
2050 			       GnomeRRMode   *mode)
2051 {
2052     int i;
2053 
2054     g_return_val_if_fail (output != NULL, FALSE);
2055     g_return_val_if_fail (mode != NULL, FALSE);
2056 
2057     for (i = 0; output->modes[i] != NULL; ++i)
2058     {
2059 	if (output->modes[i] == mode)
2060 	    return TRUE;
2061     }
2062 
2063     return FALSE;
2064 }
2065 
2066 gboolean
gnome_rr_output_can_clone(GnomeRROutput * output,GnomeRROutput * clone)2067 gnome_rr_output_can_clone (GnomeRROutput *output,
2068 			   GnomeRROutput *clone)
2069 {
2070     int i;
2071 
2072     g_return_val_if_fail (output != NULL, FALSE);
2073     g_return_val_if_fail (clone != NULL, FALSE);
2074 
2075     for (i = 0; output->clones[i] != NULL; ++i)
2076     {
2077 	if (output->clones[i] == clone)
2078 	    return TRUE;
2079     }
2080 
2081     return FALSE;
2082 }
2083 
2084 gboolean
gnome_rr_output_get_is_primary(GnomeRROutput * output)2085 gnome_rr_output_get_is_primary (GnomeRROutput *output)
2086 {
2087     return output->info->primary == output->id;
2088 }
2089 
2090 void
gnome_rr_screen_set_primary_output(GnomeRRScreen * screen,GnomeRROutput * output)2091 gnome_rr_screen_set_primary_output (GnomeRRScreen *screen,
2092                                     GnomeRROutput *output)
2093 {
2094     GnomeRRScreenPrivate *priv;
2095     RROutput id;
2096 
2097     g_return_if_fail (GNOME_IS_RR_SCREEN (screen));
2098 
2099     priv = screen->priv;
2100 
2101     if (output)
2102         id = output->id;
2103     else
2104         id = None;
2105 
2106     if (SERVERS_RANDR_IS_AT_LEAST_1_3 (priv))
2107         XRRSetOutputPrimary (priv->xdisplay, priv->xroot, id);
2108 }
2109 
2110 /* GnomeRRCrtc */
2111 typedef struct
2112 {
2113     Rotation xrot;
2114     GnomeRRRotation rot;
2115 } RotationMap;
2116 
2117 static const RotationMap rotation_map[] =
2118 {
2119     { RR_Rotate_0, GNOME_RR_ROTATION_0 },
2120     { RR_Rotate_90, GNOME_RR_ROTATION_90 },
2121     { RR_Rotate_180, GNOME_RR_ROTATION_180 },
2122     { RR_Rotate_270, GNOME_RR_ROTATION_270 },
2123     { RR_Reflect_X, GNOME_RR_REFLECT_X },
2124     { RR_Reflect_Y, GNOME_RR_REFLECT_Y },
2125 };
2126 
2127 static GnomeRRRotation
gnome_rr_rotation_from_xrotation(Rotation r)2128 gnome_rr_rotation_from_xrotation (Rotation r)
2129 {
2130     int i;
2131     GnomeRRRotation result = 0;
2132 
2133     for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i)
2134     {
2135 	if (r & rotation_map[i].xrot)
2136 	    result |= rotation_map[i].rot;
2137     }
2138 
2139     return result;
2140 }
2141 
2142 static Rotation
xrotation_from_rotation(GnomeRRRotation r)2143 xrotation_from_rotation (GnomeRRRotation r)
2144 {
2145     int i;
2146     Rotation result = 0;
2147 
2148     for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i)
2149     {
2150 	if (r & rotation_map[i].rot)
2151 	    result |= rotation_map[i].xrot;
2152     }
2153 
2154     return result;
2155 }
2156 
2157 static void
set_crtc_scale(GnomeRRCrtc * crtc,GnomeRRMode * mode,float scale,guint global_scale)2158 set_crtc_scale (GnomeRRCrtc *crtc,
2159                 GnomeRRMode *mode,
2160                 float        scale,
2161                 guint        global_scale)
2162 {
2163     gchar *filter;
2164     float real_scale;
2165     int i;
2166     int looks_like_w, looks_like_h;
2167 
2168     real_scale = global_scale / scale;
2169 
2170     looks_like_w = 0;
2171     looks_like_h = 0;
2172 
2173     if (mode != NULL)
2174     {
2175         looks_like_w = gnome_rr_mode_get_width (mode) * real_scale / global_scale;
2176         looks_like_h = gnome_rr_mode_get_height (mode) * real_scale / global_scale;
2177     }
2178 
2179     // Fractional scales use bilinear, doubling/halving can use nearest for better quality.
2180     if (real_scale == 0.5 || real_scale == 1.0)
2181     {
2182         filter = g_strdup ("nearest");
2183     }
2184     else
2185     {
2186         filter = g_strdup ("bilinear");
2187     }
2188 
2189     g_debug ("\n\n(xid: %lu) Transforming based on:\n"
2190              "global ui scale: %d\n"
2191              "requested logical scale: %.2f\n"
2192              "requested logical size: %dx%d\n"
2193              "xrandr transform value: %.2f (%d)\n"
2194              "scaling method: %s",
2195              crtc->id,
2196              global_scale, scale,
2197              looks_like_w, looks_like_h,
2198              real_scale, XDoubleToFixed (real_scale),
2199              filter);
2200 
2201     XTransform transform =  {{
2202                     { XDoubleToFixed (real_scale), 0                          , 0                    },
2203                     { 0                          , XDoubleToFixed (real_scale), 0                    },
2204                     { 0                          , 0                          , XDoubleToFixed (1.0) },
2205     }};
2206 
2207     XRRSetCrtcTransform (DISPLAY (crtc), crtc->id,
2208                          &transform,
2209                          filter,
2210                          NULL, 0);
2211 }
2212 
2213 gboolean
gnome_rr_crtc_set_config_with_time(GnomeRRCrtc * crtc,guint32 timestamp,int x,int y,GnomeRRMode * mode,GnomeRRRotation rotation,GnomeRROutput ** outputs,int n_outputs,float scale,guint global_scale,GError ** error)2214 gnome_rr_crtc_set_config_with_time (GnomeRRCrtc      *crtc,
2215 				    guint32           timestamp,
2216 				    int               x,
2217 				    int               y,
2218 				    GnomeRRMode      *mode,
2219 				    GnomeRRRotation   rotation,
2220 				    GnomeRROutput   **outputs,
2221 				    int               n_outputs,
2222                     float             scale,
2223                     guint             global_scale,
2224 				    GError          **error)
2225 {
2226     ScreenInfo *info;
2227     GArray *output_ids;
2228     Status status;
2229     gboolean result;
2230     int i;
2231 
2232     g_return_val_if_fail (crtc != NULL, FALSE);
2233     g_return_val_if_fail (mode != NULL || outputs == NULL || n_outputs == 0, FALSE);
2234     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2235 
2236     info = crtc->info;
2237 
2238     if (mode)
2239     {
2240         if (x + mode->width > info->max_width || y + mode->height > info->max_height)
2241         {
2242             g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_BOUNDS_ERROR,
2243                          /* Translators: the "position", "size", and "maximum"
2244                           * words here are not keywords; please translate them
2245                           * as usual.  A CRTC is a CRT Controller (this is X terminology) */
2246                          _("requested position/size for CRTC %d is outside the allowed limit: "
2247                          "position=(%d, %d), size=(%d, %d), maximum=(%d, %d)"),
2248                          (int) crtc->id,
2249                          x, y,
2250                          mode->width, mode->height,
2251                          info->max_width, info->max_height);
2252             return FALSE;
2253         }
2254     }
2255 
2256     output_ids = g_array_new (FALSE, FALSE, sizeof (RROutput));
2257 
2258     if (outputs)
2259     {
2260 	for (i = 0; i < n_outputs; ++i)
2261 	    g_array_append_val (output_ids, outputs[i]->id);
2262     }
2263 
2264     gdk_error_trap_push ();
2265 
2266     set_crtc_scale (crtc, mode, scale, global_scale);
2267     status = XRRSetCrtcConfig (DISPLAY (crtc), info->resources, crtc->id,
2268 			       timestamp,
2269 			       x, y,
2270 			       mode ? mode->id : None,
2271 			       xrotation_from_rotation (rotation),
2272 			       (RROutput *)output_ids->data,
2273 			       output_ids->len);
2274 
2275     g_array_free (output_ids, TRUE);
2276 
2277     if (gdk_error_trap_pop () || status != RRSetConfigSuccess) {
2278 	/* Translators: CRTC is a CRT Controller (this is X terminology).
2279 	 * It is *very* unlikely that you'll ever get this error, so it is
2280 	 * only listed for completeness. */
2281 	g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR,
2282 		     _("could not set the configuration for CRTC %d"),
2283 		     (int) crtc->id);
2284         return FALSE;
2285     } else {
2286         result = TRUE;
2287     }
2288 
2289     return result;
2290 }
2291 
2292 GnomeRRMode *
gnome_rr_crtc_get_current_mode(GnomeRRCrtc * crtc)2293 gnome_rr_crtc_get_current_mode (GnomeRRCrtc *crtc)
2294 {
2295     g_return_val_if_fail (crtc != NULL, NULL);
2296 
2297     return crtc->current_mode;
2298 }
2299 
2300 guint32
gnome_rr_crtc_get_id(GnomeRRCrtc * crtc)2301 gnome_rr_crtc_get_id (GnomeRRCrtc *crtc)
2302 {
2303     g_return_val_if_fail (crtc != NULL, 0);
2304 
2305     return crtc->id;
2306 }
2307 
2308 gboolean
gnome_rr_crtc_can_drive_output(GnomeRRCrtc * crtc,GnomeRROutput * output)2309 gnome_rr_crtc_can_drive_output (GnomeRRCrtc   *crtc,
2310 				GnomeRROutput *output)
2311 {
2312     int i;
2313 
2314     g_return_val_if_fail (crtc != NULL, FALSE);
2315     g_return_val_if_fail (output != NULL, FALSE);
2316 
2317     for (i = 0; crtc->possible_outputs[i] != NULL; ++i)
2318     {
2319 	if (crtc->possible_outputs[i] == output)
2320 	    return TRUE;
2321     }
2322 
2323     return FALSE;
2324 }
2325 
2326 /* FIXME: merge with get_mode()? */
2327 void
gnome_rr_crtc_get_position(GnomeRRCrtc * crtc,int * x,int * y)2328 gnome_rr_crtc_get_position (GnomeRRCrtc *crtc,
2329 			    int         *x,
2330 			    int         *y)
2331 {
2332     g_return_if_fail (crtc != NULL);
2333 
2334     if (x)
2335 	*x = crtc->x;
2336 
2337     if (y)
2338 	*y = crtc->y;
2339 }
2340 
2341 float
gnome_rr_crtc_get_scale(GnomeRRCrtc * crtc)2342 gnome_rr_crtc_get_scale (GnomeRRCrtc           *crtc)
2343 {
2344     g_return_val_if_fail (crtc != NULL, MINIMUM_LOGICAL_SCALE_FACTOR);
2345 
2346     return crtc->scale;
2347 }
2348 
2349 /* FIXME: merge with get_mode()? */
2350 GnomeRRRotation
gnome_rr_crtc_get_current_rotation(GnomeRRCrtc * crtc)2351 gnome_rr_crtc_get_current_rotation (GnomeRRCrtc *crtc)
2352 {
2353     g_assert(crtc != NULL);
2354     return crtc->current_rotation;
2355 }
2356 
2357 GnomeRRRotation
gnome_rr_crtc_get_rotations(GnomeRRCrtc * crtc)2358 gnome_rr_crtc_get_rotations (GnomeRRCrtc *crtc)
2359 {
2360     g_assert(crtc != NULL);
2361     return crtc->rotations;
2362 }
2363 
2364 gboolean
gnome_rr_crtc_supports_rotation(GnomeRRCrtc * crtc,GnomeRRRotation rotation)2365 gnome_rr_crtc_supports_rotation (GnomeRRCrtc *   crtc,
2366 				 GnomeRRRotation rotation)
2367 {
2368     g_return_val_if_fail (crtc != NULL, FALSE);
2369     return (crtc->rotations & rotation);
2370 }
2371 
2372 static GnomeRRCrtc *
crtc_new(ScreenInfo * info,RROutput id)2373 crtc_new (ScreenInfo *info, RROutput id)
2374 {
2375     GnomeRRCrtc *crtc = g_slice_new0 (GnomeRRCrtc);
2376 
2377     crtc->id = id;
2378     crtc->info = info;
2379 
2380     return crtc;
2381 }
2382 
2383 static GnomeRRCrtc *
crtc_copy(const GnomeRRCrtc * from)2384 crtc_copy (const GnomeRRCrtc *from)
2385 {
2386     GnomeRROutput **p_output;
2387     GPtrArray *array;
2388     GnomeRRCrtc *to = g_slice_new0 (GnomeRRCrtc);
2389 
2390     to->info = from->info;
2391     to->id = from->id;
2392     to->current_mode = from->current_mode;
2393     to->x = from->x;
2394     to->y = from->y;
2395     to->current_rotation = from->current_rotation;
2396     to->rotations = from->rotations;
2397     to->gamma_size = from->gamma_size;
2398 
2399     array = g_ptr_array_new ();
2400     for (p_output = from->current_outputs; *p_output != NULL; p_output++)
2401     {
2402         g_ptr_array_add (array, *p_output);
2403     }
2404     to->current_outputs = (GnomeRROutput**) g_ptr_array_free (array, FALSE);
2405 
2406     array = g_ptr_array_new ();
2407     for (p_output = from->possible_outputs; *p_output != NULL; p_output++)
2408     {
2409         g_ptr_array_add (array, *p_output);
2410     }
2411     to->possible_outputs = (GnomeRROutput**) g_ptr_array_free (array, FALSE);
2412 
2413     return to;
2414 }
2415 
2416 static float
scale_from_transformation(XRRCrtcTransformAttributes * transformation)2417 scale_from_transformation (XRRCrtcTransformAttributes *transformation)
2418 {
2419   XTransform *xt;
2420   float scale;
2421 
2422   if (!transformation)
2423     return 1.0f;
2424 
2425   xt = &transformation->currentTransform;
2426 
2427   if (xt->matrix[0][0] == xt->matrix[1][1])
2428     scale = XFixedToDouble (xt->matrix[0][0]);
2429   else
2430     scale = XFixedToDouble (xt->matrix[0][0] + xt->matrix[1][1]) / 2.0;
2431 
2432   return 1.0f / scale;
2433 }
2434 
2435 static float
get_transform_scale(GnomeRRCrtc * crtc)2436 get_transform_scale (GnomeRRCrtc *crtc)
2437 {
2438     XRRCrtcTransformAttributes *transform_attributes;
2439     float ret;
2440 
2441     if (!XRRGetCrtcTransform (DISPLAY (crtc), crtc->id, &transform_attributes))
2442     {
2443         transform_attributes = NULL;
2444     }
2445 
2446     ret = scale_from_transformation (transform_attributes);
2447 
2448     XFree (transform_attributes);
2449 
2450     return ret;
2451 }
2452 
2453 guint
gnome_rr_screen_get_global_scale(GnomeRRScreen * screen)2454 gnome_rr_screen_get_global_scale (GnomeRRScreen *screen)
2455 {
2456     GdkScreen *gdkscreen;
2457     GValue value = G_VALUE_INIT;
2458     gint window_scale = 1;
2459 
2460     gdkscreen = gdk_screen_get_default ();
2461 
2462     g_value_init (&value, G_TYPE_INT);
2463 
2464     if (gdk_screen_get_setting (gdkscreen, "gdk-window-scaling-factor", &value))
2465     {
2466         window_scale = g_value_get_int (&value);
2467     }
2468 
2469     return (guint) CLAMP (window_scale, MINIMUM_GLOBAL_SCALE_FACTOR, MAXIMUM_GLOBAL_SCALE_FACTOR);
2470 }
2471 
2472 guint
gnome_rr_screen_get_global_scale_setting(GnomeRRScreen * screen)2473 gnome_rr_screen_get_global_scale_setting (GnomeRRScreen *screen)
2474 {
2475     guint scale_factor;
2476 
2477     scale_factor = g_settings_get_uint (screen->priv->interface_settings,
2478                                         GLOBAL_SCALE_FACTOR_KEY);
2479 
2480     return scale_factor;
2481 }
2482 
2483 void
gnome_rr_screen_set_global_scale_setting(GnomeRRScreen * screen,guint scale_factor)2484 gnome_rr_screen_set_global_scale_setting (GnomeRRScreen *screen,
2485                                           guint          scale_factor)
2486 {
2487     g_settings_set_uint (screen->priv->interface_settings,
2488                          GLOBAL_SCALE_FACTOR_KEY,
2489                          scale_factor);
2490 }
2491 
2492 static float
get_crtc_scale(GnomeRRCrtc * crtc)2493 get_crtc_scale (GnomeRRCrtc *crtc)
2494 {
2495     return gnome_rr_screen_get_global_scale (NULL) * get_transform_scale (crtc);
2496 }
2497 
2498 static gboolean
crtc_initialize(GnomeRRCrtc * crtc,XRRScreenResources * res,GError ** error)2499 crtc_initialize (GnomeRRCrtc        *crtc,
2500 		 XRRScreenResources *res,
2501 		 GError            **error)
2502 {
2503     XRRCrtcInfo *info = XRRGetCrtcInfo (DISPLAY (crtc), res, crtc->id);
2504     GPtrArray *a;
2505     int i;
2506 
2507 #if 0
2508     g_print ("CRTC %lx Timestamp: %u\n", crtc->id, (guint32)info->timestamp);
2509 #endif
2510 
2511     if (!info)
2512     {
2513         /* FIXME: We need to reaquire the screen resources */
2514         /* FIXME: can we actually catch BadRRCrtc, and does it make sense to emit that? */
2515 
2516         /* Translators: CRTC is a CRT Controller (this is X terminology).
2517         * It is *very* unlikely that you'll ever get this error, so it is
2518         * only listed for completeness. */
2519         g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_RANDR_ERROR,
2520                      _("could not get information about CRTC %d"),
2521                     (int) crtc->id);
2522         return FALSE;
2523     }
2524 
2525     /* GnomeRRMode */
2526     crtc->current_mode = mode_by_id (crtc->info, info->mode);
2527 
2528     crtc->x = info->x;
2529     crtc->y = info->y;
2530 
2531     /* Current outputs */
2532     a = g_ptr_array_new ();
2533     for (i = 0; i < info->noutput; ++i)
2534     {
2535         GnomeRROutput *output = gnome_rr_output_by_id (crtc->info, info->outputs[i]);
2536 
2537         if (output)
2538             g_ptr_array_add (a, output);
2539     }
2540 
2541     g_ptr_array_add (a, NULL);
2542     crtc->current_outputs = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
2543 
2544     /* Possible outputs */
2545     a = g_ptr_array_new ();
2546     for (i = 0; i < info->npossible; ++i)
2547     {
2548         GnomeRROutput *output = gnome_rr_output_by_id (crtc->info, info->possible[i]);
2549 
2550         if (output)
2551             g_ptr_array_add (a, output);
2552     }
2553 
2554     g_ptr_array_add (a, NULL);
2555     crtc->possible_outputs = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
2556 
2557     /* Rotations */
2558     crtc->current_rotation = gnome_rr_rotation_from_xrotation (info->rotation);
2559     crtc->rotations = gnome_rr_rotation_from_xrotation (info->rotations);
2560     crtc->scale = get_crtc_scale (crtc);
2561 
2562     XRRFreeCrtcInfo (info);
2563 
2564     /* get an store gamma size */
2565     crtc->gamma_size = XRRGetCrtcGammaSize (DISPLAY (crtc), crtc->id);
2566 
2567     g_debug ("Initialized GnomeCrtc: %d, at %d, %d, with a scale factor of %.2f (%u global scale)",
2568              (int) crtc->id, crtc->x, crtc->y, crtc->scale, gnome_rr_screen_get_global_scale (NULL));
2569 
2570     return TRUE;
2571 }
2572 
2573 static void
crtc_free(GnomeRRCrtc * crtc)2574 crtc_free (GnomeRRCrtc *crtc)
2575 {
2576     g_free (crtc->current_outputs);
2577     g_free (crtc->possible_outputs);
2578     g_slice_free (GnomeRRCrtc, crtc);
2579 }
2580 
2581 /* GnomeRRMode */
2582 static GnomeRRMode *
mode_new(ScreenInfo * info,RRMode id)2583 mode_new (ScreenInfo *info, RRMode id)
2584 {
2585     GnomeRRMode *mode = g_slice_new0 (GnomeRRMode);
2586 
2587     mode->id = id;
2588     mode->info = info;
2589 
2590     return mode;
2591 }
2592 
2593 guint32
gnome_rr_mode_get_id(GnomeRRMode * mode)2594 gnome_rr_mode_get_id (GnomeRRMode *mode)
2595 {
2596     g_return_val_if_fail (mode != NULL, 0);
2597     return mode->id;
2598 }
2599 
2600 guint
gnome_rr_mode_get_width(GnomeRRMode * mode)2601 gnome_rr_mode_get_width (GnomeRRMode *mode)
2602 {
2603     g_return_val_if_fail (mode != NULL, 0);
2604     return mode->width;
2605 }
2606 
2607 guint
gnome_rr_mode_get_height(GnomeRRMode * mode)2608 gnome_rr_mode_get_height (GnomeRRMode *mode)
2609 {
2610     g_return_val_if_fail (mode != NULL, 0);
2611     return mode->height;
2612 }
2613 
2614 int
gnome_rr_mode_get_freq(GnomeRRMode * mode)2615 gnome_rr_mode_get_freq (GnomeRRMode *mode)
2616 {
2617     g_return_val_if_fail (mode != NULL, 0);
2618     return (mode->freq) / 1000;
2619 }
2620 
2621 double
gnome_rr_mode_get_freq_f(GnomeRRMode * mode)2622 gnome_rr_mode_get_freq_f (GnomeRRMode *mode)
2623 {
2624     g_return_val_if_fail (mode != NULL, 0.0);
2625     return (mode->freq) / 1000.0;
2626 }
2627 
2628 void
gnome_rr_mode_get_flags(GnomeRRMode * mode,gboolean * doublescan,gboolean * interlaced,gboolean * vsync)2629 gnome_rr_mode_get_flags            (GnomeRRMode           *mode,
2630                                     gboolean              *doublescan,
2631                                     gboolean              *interlaced,
2632                                     gboolean              *vsync)
2633 {
2634     g_return_if_fail (mode != NULL);
2635 
2636     if (doublescan)
2637         *doublescan = mode->doublescan;
2638     if (interlaced)
2639         *interlaced = mode->interlaced;
2640     if (vsync)
2641         *vsync = mode->vsync;
2642 }
2643 
2644 static void
mode_initialize(GnomeRRMode * mode,XRRModeInfo * info)2645 mode_initialize (GnomeRRMode *mode, XRRModeInfo *info)
2646 {
2647     g_assert (mode != NULL);
2648     g_assert (info != NULL);
2649 
2650     mode->name = g_strdup (info->name);
2651     mode->width = info->width;
2652     mode->height = info->height;
2653     mode->freq = info->dotClock / ((float)info->hTotal * info->vTotal) * 1000;
2654     mode->doublescan = (info->modeFlags & RR_DoubleScan);
2655     mode->interlaced = (info->modeFlags & RR_Interlace);
2656     mode->vsync = (info->modeFlags & RR_VSyncPositive);
2657 }
2658 
2659 static GnomeRRMode *
mode_copy(const GnomeRRMode * from)2660 mode_copy (const GnomeRRMode *from)
2661 {
2662     GnomeRRMode *to = g_slice_new0 (GnomeRRMode);
2663 
2664     to->id = from->id;
2665     to->info = from->info;
2666     to->name = g_strdup (from->name);
2667     to->width = from->width;
2668     to->height = from->height;
2669     to->freq = from->freq;
2670 
2671     return to;
2672 }
2673 
2674 static void
mode_free(GnomeRRMode * mode)2675 mode_free (GnomeRRMode *mode)
2676 {
2677     g_free (mode->name);
2678     g_slice_free (GnomeRRMode, mode);
2679 }
2680 
2681 void
gnome_rr_crtc_set_gamma(GnomeRRCrtc * crtc,int size,unsigned short * red,unsigned short * green,unsigned short * blue)2682 gnome_rr_crtc_set_gamma (GnomeRRCrtc *crtc, int size,
2683 			 unsigned short *red,
2684 			 unsigned short *green,
2685 			 unsigned short *blue)
2686 {
2687     int copy_size;
2688     XRRCrtcGamma *gamma;
2689 
2690     g_return_if_fail (crtc != NULL);
2691     g_return_if_fail (red != NULL);
2692     g_return_if_fail (green != NULL);
2693     g_return_if_fail (blue != NULL);
2694 
2695     if (size != crtc->gamma_size)
2696 	return;
2697 
2698     gamma = XRRAllocGamma (crtc->gamma_size);
2699 
2700     copy_size = crtc->gamma_size * sizeof (unsigned short);
2701     memcpy (gamma->red, red, copy_size);
2702     memcpy (gamma->green, green, copy_size);
2703     memcpy (gamma->blue, blue, copy_size);
2704 
2705     XRRSetCrtcGamma (DISPLAY (crtc), crtc->id, gamma);
2706     XRRFreeGamma (gamma);
2707 }
2708 
2709 gboolean
gnome_rr_crtc_get_gamma(GnomeRRCrtc * crtc,int * size,unsigned short ** red,unsigned short ** green,unsigned short ** blue)2710 gnome_rr_crtc_get_gamma (GnomeRRCrtc *crtc, int *size,
2711 			 unsigned short **red, unsigned short **green,
2712 			 unsigned short **blue)
2713 {
2714     int copy_size;
2715     unsigned short *r, *g, *b;
2716     XRRCrtcGamma *gamma;
2717 
2718     g_return_val_if_fail (crtc != NULL, FALSE);
2719 
2720     gamma = XRRGetCrtcGamma (DISPLAY (crtc), crtc->id);
2721     if (!gamma)
2722 	return FALSE;
2723 
2724     copy_size = crtc->gamma_size * sizeof (unsigned short);
2725 
2726     if (red) {
2727 	r = g_new0 (unsigned short, crtc->gamma_size);
2728 	memcpy (r, gamma->red, copy_size);
2729 	*red = r;
2730     }
2731 
2732     if (green) {
2733 	g = g_new0 (unsigned short, crtc->gamma_size);
2734 	memcpy (g, gamma->green, copy_size);
2735 	*green = g;
2736     }
2737 
2738     if (blue) {
2739 	b = g_new0 (unsigned short, crtc->gamma_size);
2740 	memcpy (b, gamma->blue, copy_size);
2741 	*blue = b;
2742     }
2743 
2744     XRRFreeGamma (gamma);
2745 
2746     if (size)
2747 	*size = crtc->gamma_size;
2748 
2749     return TRUE;
2750 }
2751 
2752 #define SCALE_FACTORS_PER_INTEGER 4
2753 #define SCALE_FACTORS_STEPS (1.0 / (float) SCALE_FACTORS_PER_INTEGER)
2754 
2755 /* The minimum resolution at which we turn on a window-scale of 2 */
2756 #define HIDPI_LIMIT 192
2757 #define HIDPI_MIN_SCALED_HEIGHT 720
2758 
2759 /* The minimum screen height at which we turn on a window-scale of 2;
2760  * below this there just isn't enough vertical real estate for GNOME
2761  * apps to work, and it's better to just be tiny */
2762 #define HIDPI_MIN_HEIGHT 1500
2763 
2764 static gboolean
is_logical_size_large_enough(int width,int height)2765 is_logical_size_large_enough (int width,
2766                               int height)
2767 {
2768   return height > HIDPI_MIN_SCALED_HEIGHT;
2769 }
2770 
2771 static gboolean
is_scale_valid_for_size(float width,float height,float scale)2772 is_scale_valid_for_size (float width,
2773                          float height,
2774                          float scale)
2775 {
2776   return scale >= MINIMUM_LOGICAL_SCALE_FACTOR &&
2777          scale <= MAXIMUM_LOGICAL_SCALE_FACTOR;
2778 }
2779 
2780 static float
get_closest_scale_factor_for_resolution(float width,float height,float scale)2781 get_closest_scale_factor_for_resolution (float width,
2782                                          float height,
2783                                          float scale)
2784 {
2785   unsigned int i, j;
2786   float scaled_h;
2787   float scaled_w;
2788   float best_scale;
2789   int base_scaled_w;
2790   gboolean found_one;
2791 
2792   best_scale = 0;
2793 
2794   if (!is_scale_valid_for_size (width, height, scale))
2795     goto out;
2796 
2797   if (fmodf (width, scale) == 0.0 && fmodf (height, scale) == 0.0)
2798     return scale;
2799 
2800   i = 0;
2801   found_one = FALSE;
2802   base_scaled_w = floorf (width / scale);
2803   do
2804     {
2805       for (j = 0; j < 2; j++)
2806         {
2807           float current_scale;
2808           int offset = i * (j ? 1 : -1);
2809 
2810           scaled_w = base_scaled_w + offset;
2811           current_scale = width / scaled_w;
2812           scaled_h = height / current_scale;
2813 
2814           if (current_scale >= scale + SCALE_FACTORS_STEPS ||
2815               current_scale <= scale - SCALE_FACTORS_STEPS ||
2816               current_scale < MINIMUM_LOGICAL_SCALE_FACTOR ||
2817               current_scale > MAXIMUM_LOGICAL_SCALE_FACTOR)
2818             {
2819               goto out;
2820             }
2821 
2822           if (floorf (scaled_h) == scaled_h)
2823             {
2824               found_one = TRUE;
2825 
2826               if (fabsf (current_scale - scale) < fabsf (best_scale - scale))
2827                 best_scale = current_scale;
2828             }
2829         }
2830 
2831       i++;
2832     }
2833   while (!found_one);
2834 
2835 out:
2836   return best_scale;
2837 }
2838 
2839 float *
gnome_rr_screen_calculate_supported_scales(GnomeRRScreen * screen,int width,int height,int * n_supported_scales)2840 gnome_rr_screen_calculate_supported_scales (GnomeRRScreen     *screen,
2841                                             int                width,
2842                                             int                height,
2843                                             int               *n_supported_scales)
2844 {
2845   unsigned int i, j;
2846   GArray *supported_scales;
2847 
2848   supported_scales = g_array_new (FALSE, FALSE, sizeof (float));
2849 
2850   for (i = floorf (MINIMUM_LOGICAL_SCALE_FACTOR);
2851        i <= ceilf (MAXIMUM_LOGICAL_SCALE_FACTOR);
2852        i++)
2853     {
2854       for (j = 0; j < SCALE_FACTORS_PER_INTEGER; j++)
2855         {
2856           float scale;
2857           float scale_value = i + j * SCALE_FACTORS_STEPS;
2858 
2859           scale = get_closest_scale_factor_for_resolution (width,
2860                                                            height,
2861                                                            scale_value);
2862           if (scale > 0.0f)
2863             g_array_append_val (supported_scales, scale);
2864         }
2865     }
2866 
2867   if (supported_scales->len == 0)
2868     {
2869       float fallback_scale;
2870 
2871       fallback_scale = MINIMUM_LOGICAL_SCALE_FACTOR;
2872       g_array_append_val (supported_scales, fallback_scale);
2873     }
2874 
2875   *n_supported_scales = supported_scales->len;
2876   return (float *) g_array_free (supported_scales, FALSE);
2877 }
2878 
2879 static void
get_real_monitor_size(GnomeRRScreen * screen,GdkMonitor * monitor,gint monitor_index,gint * width,gint * height)2880 get_real_monitor_size (GnomeRRScreen *screen,
2881                        GdkMonitor    *monitor,
2882                        gint           monitor_index,
2883                        gint          *width,
2884                        gint          *height)
2885 {
2886     GnomeRROutput **outputs;
2887     XID output_id;
2888     gint i;
2889 
2890     *width = 0;
2891     *height = 0;
2892 
2893     output_id = gdk_x11_screen_get_monitor_output (gdk_screen_get_default (), monitor_index);
2894     outputs = gnome_rr_screen_list_outputs (screen);
2895 
2896     for (i = 0; outputs[i] != NULL; i++)
2897     {
2898         GnomeRROutput *output = outputs[i];
2899 
2900         if (gnome_rr_output_get_id (output) == output_id)
2901         {
2902             GnomeRRMode *mode = gnome_rr_output_get_current_mode (output);
2903 
2904             if (mode == NULL)
2905             {
2906                 mode = gnome_rr_output_get_preferred_mode (output);
2907             }
2908 
2909             if (mode != NULL)
2910             {
2911                 *width = gnome_rr_mode_get_width (mode);
2912                 *height = gnome_rr_mode_get_height (mode);
2913             }
2914 
2915             break;
2916         }
2917     }
2918 
2919     if (*width == 0 || *height == 0)
2920     {
2921         GdkRectangle rect;
2922         gdk_monitor_get_geometry (monitor, &rect);
2923 
2924         *width = rect.width;
2925         *height = rect.height;
2926     }
2927 }
2928 
2929 guint
gnome_rr_screen_calculate_best_global_scale(GnomeRRScreen * screen,gint index)2930 gnome_rr_screen_calculate_best_global_scale (GnomeRRScreen *screen,
2931                                              gint           index)
2932 {
2933     guint window_scale;
2934     GdkDisplay *display;
2935     GdkMonitor *monitor;
2936     int width_mm, height_mm;
2937     int monitor_scale;
2938     double dpi_x, dpi_y;
2939     int real_width, real_height;
2940 
2941     display = gdk_display_get_default ();
2942 
2943     if (index == -1)
2944     {
2945         monitor = gdk_display_get_primary_monitor (display);
2946 
2947         index = 0;
2948     }
2949     else
2950     {
2951         if ((index >= 0) && (index < gdk_display_get_n_monitors (display)))
2952         {
2953             monitor = gdk_display_get_monitor (display, index);
2954         }
2955         else
2956         {
2957             g_warning ("Invalid monitor index provided (%d)", index);
2958             return 1;
2959         }
2960     }
2961 
2962     /* GdkMonitor geometry is affected by any active current transformation/scaling!!!!
2963      * So if I have 2x global scale, and a transform on a 1080 monitor down to 540, that's
2964      * what gdk_monitor_get_geometry() returns.  We actually have to get the dimensions from
2965      * the current RRMode for the given monitor.
2966      */
2967     get_real_monitor_size (screen, monitor, index, &real_width, &real_height);
2968 
2969 
2970     width_mm = gdk_monitor_get_width_mm (monitor);
2971     height_mm = gdk_monitor_get_height_mm (monitor);
2972     monitor_scale = gdk_monitor_get_scale_factor (monitor);
2973 
2974     window_scale = 1;
2975 
2976     g_debug ("Calculating best global scale for monitor %d. Physical size: %dmm x %dmm,"
2977              " REAL pixel size: %d x %d.  Current global scale: %d, reported monitor scale: %d",
2978              index, width_mm, height_mm, real_width, real_height, gnome_rr_screen_get_global_scale (NULL), monitor_scale);
2979 
2980     if (real_height < HIDPI_MIN_HEIGHT)
2981     {
2982         g_debug ("REAL height of %d for monitor %d is less than %d, so the recommended scale will be 1", real_height, index, HIDPI_MIN_HEIGHT);
2983         goto out;
2984     }
2985 
2986     /* Some monitors/TV encode the aspect ratio (16/9 or 16/10) instead of the physical size */
2987     if ((width_mm == 160 && height_mm == 90) ||
2988         (width_mm == 160 && height_mm == 100) ||
2989         (width_mm == 16 && height_mm == 9) ||
2990         (width_mm == 16 && height_mm == 10) ||
2991         (width_mm == 0 || height_mm == 0))
2992     {
2993         g_debug ("Aspect ratio instead of physical dimensions were encoded as the physical size, or the physical"
2994                  " size was not set. Unable to reliably calculate the recommended scale, returning 1");
2995         goto out;
2996     }
2997 
2998     if (width_mm > 0 && height_mm > 0) {
2999             dpi_x = (double) real_width / (width_mm / 25.4);
3000             dpi_y = (double) real_height / (height_mm / 25.4);
3001             /* We don't completely trust these values so both must be high, and never pick higher ratio than
3002               2 automatically */
3003             if (dpi_x > HIDPI_LIMIT && dpi_y > HIDPI_LIMIT)
3004             {
3005                 g_debug ("The REAL monitor DPI of %.1f x %.1f exceeds the cutoff of %d x %d, recommended scale will be 2",
3006                          dpi_x, dpi_y, HIDPI_LIMIT, HIDPI_LIMIT);
3007 
3008                 window_scale = 2;
3009             }
3010             else
3011             {
3012                 g_debug ("The REAL monitor DPI of %.1f x %.1f does not meet the cutoff of %d x %d, recommended scale will be 1",
3013                          dpi_x, dpi_y, HIDPI_LIMIT, HIDPI_LIMIT);
3014             }
3015     }
3016 
3017 out:
3018     return window_scale;
3019 }
3020