1 /* mate-rr.c
2  *
3  * Copyright 2007, 2008, Red Hat, Inc.
4  *
5  * This file is part of the Mate Library.
6  *
7  * The Mate 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 Mate 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 Mate Library; see the file COPYING.LIB.  If not,
19  * write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * Author: Soren Sandmann <sandmann@redhat.com>
23  */
24 
25 #define MATE_DESKTOP_USE_UNSTABLE_API
26 
27 #include <config.h>
28 #include <glib/gi18n-lib.h>
29 #include <string.h>
30 #include <X11/Xlib.h>
31 
32 #ifdef HAVE_RANDR
33 #include <X11/extensions/Xrandr.h>
34 #endif
35 
36 #include <gtk/gtk.h>
37 #include <gdk/gdkx.h>
38 #include <X11/Xatom.h>
39 
40 #undef MATE_DISABLE_DEPRECATED
41 #include "mate-rr.h"
42 #include "mate-rr-config.h"
43 
44 #include "private.h"
45 #include "mate-rr-private.h"
46 
47 #define DISPLAY(o) ((o)->info->screen->priv->xdisplay)
48 
49 #ifndef HAVE_RANDR
50 /* This is to avoid a ton of ifdefs wherever we use a type from libXrandr */
51 typedef int RROutput;
52 typedef int RRCrtc;
53 typedef int RRMode;
54 typedef int Rotation;
55 #define RR_Rotate_0		1
56 #define RR_Rotate_90		2
57 #define RR_Rotate_180		4
58 #define RR_Rotate_270		8
59 #define RR_Reflect_X		16
60 #define RR_Reflect_Y		32
61 #endif
62 
63 enum {
64     SCREEN_PROP_0,
65     SCREEN_PROP_GDK_SCREEN,
66     SCREEN_PROP_LAST,
67 };
68 
69 enum {
70     SCREEN_CHANGED,
71     SCREEN_SIGNAL_LAST,
72 };
73 
74 gint screen_signals[SCREEN_SIGNAL_LAST];
75 
76 struct MateRROutput
77 {
78     ScreenInfo *	info;
79     RROutput		id;
80 
81     char *		name;
82     MateRRCrtc *	current_crtc;
83     gboolean		connected;
84     gulong		width_mm;
85     gulong		height_mm;
86     MateRRCrtc **	possible_crtcs;
87     MateRROutput **	clones;
88     MateRRMode **	modes;
89     int			n_preferred;
90     guint8 *		edid_data;
91     int         edid_size;
92     char *              connector_type;
93 };
94 
95 struct MateRROutputWrap
96 {
97     RROutput		id;
98 };
99 
100 struct MateRRCrtc
101 {
102     ScreenInfo *	info;
103     RRCrtc		id;
104 
105     MateRRMode *	current_mode;
106     MateRROutput **	current_outputs;
107     MateRROutput **	possible_outputs;
108     int			x;
109     int			y;
110 
111     MateRRRotation	current_rotation;
112     MateRRRotation	rotations;
113     int			gamma_size;
114 };
115 
116 struct MateRRMode
117 {
118     ScreenInfo *	info;
119     RRMode		id;
120     char *		name;
121     int			width;
122     int			height;
123     int			freq;		/* in mHz */
124 };
125 
126 /* MateRRCrtc */
127 static MateRRCrtc *  crtc_new          (ScreenInfo         *info,
128 					 RRCrtc              id);
129 static MateRRCrtc *  crtc_copy         (const MateRRCrtc  *from);
130 static void           crtc_free         (MateRRCrtc        *crtc);
131 
132 #ifdef HAVE_RANDR
133 static gboolean       crtc_initialize   (MateRRCrtc        *crtc,
134 					 XRRScreenResources *res,
135 					 GError            **error);
136 #endif
137 
138 /* MateRROutput */
139 static MateRROutput *output_new        (ScreenInfo         *info,
140 					 RROutput            id);
141 
142 #ifdef HAVE_RANDR
143 static gboolean       output_initialize (MateRROutput      *output,
144 					 XRRScreenResources *res,
145 					 GError            **error);
146 #endif
147 
148 static MateRROutput *output_copy       (const MateRROutput *from);
149 static void           output_free       (MateRROutput      *output);
150 
151 /* MateRRMode */
152 static MateRRMode *  mode_new          (ScreenInfo         *info,
153 					 RRMode              id);
154 
155 #ifdef HAVE_RANDR
156 static void           mode_initialize   (MateRRMode        *mode,
157 					 XRRModeInfo        *info);
158 #endif
159 
160 static MateRRMode *  mode_copy         (const MateRRMode  *from);
161 static void           mode_free         (MateRRMode        *mode);
162 
163 
164 static void mate_rr_screen_finalize (GObject*);
165 static void mate_rr_screen_set_property (GObject*, guint, const GValue*, GParamSpec*);
166 static void mate_rr_screen_get_property (GObject*, guint, GValue*, GParamSpec*);
167 static gboolean mate_rr_screen_initable_init (GInitable*, GCancellable*, GError**);
168 static void mate_rr_screen_initable_iface_init (GInitableIface *iface);
G_DEFINE_TYPE_WITH_CODE(MateRRScreen,mate_rr_screen,G_TYPE_OBJECT,G_ADD_PRIVATE (MateRRScreen)G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,mate_rr_screen_initable_iface_init))169 G_DEFINE_TYPE_WITH_CODE (MateRRScreen, mate_rr_screen, G_TYPE_OBJECT,
170                          G_ADD_PRIVATE(MateRRScreen)
171                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, mate_rr_screen_initable_iface_init))
172 
173 G_DEFINE_BOXED_TYPE (MateRRCrtc, mate_rr_crtc, crtc_copy, crtc_free)
174 G_DEFINE_BOXED_TYPE (MateRROutput, mate_rr_output, output_copy, output_free)
175 G_DEFINE_BOXED_TYPE (MateRRMode, mate_rr_mode, mode_copy, mode_free)
176 
177 /* Errors */
178 
179 /**
180  * mate_rr_error_quark:
181  *
182  * Returns the #GQuark that will be used for #GError values returned by the
183  * MateRR API.
184  *
185  * Return value: a #GQuark used to identify errors coming from the MateRR API.
186  */
187 GQuark
188 mate_rr_error_quark (void)
189 {
190     return g_quark_from_static_string ("mate-rr-error-quark");
191 }
192 
193 /* Screen */
194 static MateRROutput *
mate_rr_output_by_id(ScreenInfo * info,RROutput id)195 mate_rr_output_by_id (ScreenInfo *info, RROutput id)
196 {
197     MateRROutput **output;
198 
199     g_assert (info != NULL);
200 
201     for (output = info->outputs; *output; ++output)
202     {
203 	if ((*output)->id == id)
204 	    return *output;
205     }
206 
207     return NULL;
208 }
209 
210 static MateRRCrtc *
crtc_by_id(ScreenInfo * info,RRCrtc id)211 crtc_by_id (ScreenInfo *info, RRCrtc id)
212 {
213     MateRRCrtc **crtc;
214 
215     if (!info)
216         return NULL;
217 
218     for (crtc = info->crtcs; *crtc; ++crtc)
219     {
220 	if ((*crtc)->id == id)
221 	    return *crtc;
222     }
223 
224     return NULL;
225 }
226 
227 static MateRRMode *
mode_by_id(ScreenInfo * info,RRMode id)228 mode_by_id (ScreenInfo *info, RRMode id)
229 {
230     MateRRMode **mode;
231 
232     g_assert (info != NULL);
233 
234     for (mode = info->modes; *mode; ++mode)
235     {
236 	if ((*mode)->id == id)
237 	    return *mode;
238     }
239 
240     return NULL;
241 }
242 
243 static void
screen_info_free(ScreenInfo * info)244 screen_info_free (ScreenInfo *info)
245 {
246     MateRROutput **output;
247     MateRRCrtc **crtc;
248     MateRRMode **mode;
249 
250     g_assert (info != NULL);
251 
252 #ifdef HAVE_RANDR
253     if (info->resources)
254     {
255 	XRRFreeScreenResources (info->resources);
256 
257 	info->resources = NULL;
258     }
259 #endif
260 
261     if (info->outputs)
262     {
263 	for (output = info->outputs; *output; ++output)
264 	    output_free (*output);
265 	g_free (info->outputs);
266     }
267 
268     if (info->crtcs)
269     {
270 	for (crtc = info->crtcs; *crtc; ++crtc)
271 	    crtc_free (*crtc);
272 	g_free (info->crtcs);
273     }
274 
275     if (info->modes)
276     {
277 	for (mode = info->modes; *mode; ++mode)
278 	    mode_free (*mode);
279 	g_free (info->modes);
280     }
281 
282     if (info->clone_modes)
283     {
284 	/* The modes themselves were freed above */
285 	g_free (info->clone_modes);
286     }
287 
288     g_free (info);
289 }
290 
291 static gboolean
has_similar_mode(MateRROutput * output,MateRRMode * mode)292 has_similar_mode (MateRROutput *output, MateRRMode *mode)
293 {
294     int i;
295     MateRRMode **modes = mate_rr_output_list_modes (output);
296     int width = mate_rr_mode_get_width (mode);
297     int height = mate_rr_mode_get_height (mode);
298 
299     for (i = 0; modes[i] != NULL; ++i)
300     {
301 	MateRRMode *m = modes[i];
302 
303 	if (mate_rr_mode_get_width (m) == width	&&
304 	    mate_rr_mode_get_height (m) == height)
305 	{
306 	    return TRUE;
307 	}
308     }
309 
310     return FALSE;
311 }
312 
313 static void
gather_clone_modes(ScreenInfo * info)314 gather_clone_modes (ScreenInfo *info)
315 {
316     int i;
317     GPtrArray *result = g_ptr_array_new ();
318 
319     for (i = 0; info->outputs[i] != NULL; ++i)
320     {
321 	int j;
322 	MateRROutput *output1, *output2;
323 
324 	output1 = info->outputs[i];
325 
326 	if (!output1->connected)
327 	    continue;
328 
329 	for (j = 0; output1->modes[j] != NULL; ++j)
330 	{
331 	    MateRRMode *mode = output1->modes[j];
332 	    gboolean valid;
333 	    int k;
334 
335 	    valid = TRUE;
336 	    for (k = 0; info->outputs[k] != NULL; ++k)
337 	    {
338 		output2 = info->outputs[k];
339 
340 		if (!output2->connected)
341 		    continue;
342 
343 		if (!has_similar_mode (output2, mode))
344 		{
345 		    valid = FALSE;
346 		    break;
347 		}
348 	    }
349 
350 	    if (valid)
351 		g_ptr_array_add (result, mode);
352 	}
353     }
354 
355     g_ptr_array_add (result, NULL);
356 
357     info->clone_modes = (MateRRMode **)g_ptr_array_free (result, FALSE);
358 }
359 
360 #ifdef HAVE_RANDR
361 static gboolean
fill_screen_info_from_resources(ScreenInfo * info,XRRScreenResources * resources,GError ** error)362 fill_screen_info_from_resources (ScreenInfo *info,
363 				 XRRScreenResources *resources,
364 				 GError **error)
365 {
366     int i;
367     GPtrArray *a;
368     MateRRCrtc **crtc;
369     MateRROutput **output;
370 
371     info->resources = resources;
372 
373     /* We create all the structures before initializing them, so
374      * that they can refer to each other.
375      */
376     a = g_ptr_array_new ();
377     for (i = 0; i < resources->ncrtc; ++i)
378     {
379 	g_ptr_array_add (a, crtc_new (info, resources->crtcs[i]));
380     }
381     g_ptr_array_add (a, NULL);
382     info->crtcs = (MateRRCrtc **)g_ptr_array_free (a, FALSE);
383 
384     a = g_ptr_array_new ();
385     for (i = 0; i < resources->noutput; ++i)
386     {
387 	g_ptr_array_add (a, output_new (info, resources->outputs[i]));
388     }
389     g_ptr_array_add (a, NULL);
390     info->outputs = (MateRROutput **)g_ptr_array_free (a, FALSE);
391 
392     a = g_ptr_array_new ();
393     for (i = 0;  i < resources->nmode; ++i)
394     {
395 	MateRRMode *mode = mode_new (info, resources->modes[i].id);
396 
397 	g_ptr_array_add (a, mode);
398     }
399     g_ptr_array_add (a, NULL);
400     info->modes = (MateRRMode **)g_ptr_array_free (a, FALSE);
401 
402     /* Initialize */
403     for (crtc = info->crtcs; *crtc; ++crtc)
404     {
405 	if (!crtc_initialize (*crtc, resources, error))
406 	    return FALSE;
407     }
408 
409     for (output = info->outputs; *output; ++output)
410     {
411 	if (!output_initialize (*output, resources, error))
412 	    return FALSE;
413     }
414 
415     for (i = 0; i < resources->nmode; ++i)
416     {
417 	MateRRMode *mode = mode_by_id (info, resources->modes[i].id);
418 
419 	mode_initialize (mode, &(resources->modes[i]));
420     }
421 
422     gather_clone_modes (info);
423 
424     return TRUE;
425 }
426 #endif /* HAVE_RANDR */
427 
428 static gboolean
fill_out_screen_info(Display * xdisplay,Window xroot,ScreenInfo * info,gboolean needs_reprobe,GError ** error)429 fill_out_screen_info (Display *xdisplay,
430 		      Window xroot,
431 		      ScreenInfo *info,
432 		      gboolean needs_reprobe,
433 		      GError **error)
434 {
435 #ifdef HAVE_RANDR
436     XRRScreenResources *resources;
437 	GdkDisplay *display;
438 
439     g_assert (xdisplay != NULL);
440     g_assert (info != NULL);
441 
442     /* First update the screen resources */
443 
444     if (needs_reprobe)
445         resources = XRRGetScreenResources (xdisplay, xroot);
446     else
447         resources = XRRGetScreenResourcesCurrent (xdisplay, xroot);
448 
449     if (resources)
450     {
451 	if (!fill_screen_info_from_resources (info, resources, error))
452 	    return FALSE;
453     }
454     else
455     {
456 	g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_RANDR_ERROR,
457 		     /* Translators: a CRTC is a CRT Controller (this is X terminology). */
458 		     _("could not get the screen resources (CRTCs, outputs, modes)"));
459 	return FALSE;
460     }
461 
462     /* Then update the screen size range.  We do this after XRRGetScreenResources() so that
463      * the X server will already have an updated view of the outputs.
464      */
465 
466     if (needs_reprobe) {
467 	gboolean success;
468 
469 	display = gdk_display_get_default ();
470     gdk_x11_display_error_trap_push (display);
471 	success = XRRGetScreenSizeRange (xdisplay, xroot,
472 					 &(info->min_width),
473 					 &(info->min_height),
474 					 &(info->max_width),
475 					 &(info->max_height));
476 	gdk_display_flush (display);
477 	if (gdk_x11_display_error_trap_pop (display)) {
478 	    g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_UNKNOWN,
479 			 _("unhandled X error while getting the range of screen sizes"));
480 	    return FALSE;
481 	}
482 
483 	if (!success) {
484 	    g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_RANDR_ERROR,
485 			 _("could not get the range of screen sizes"));
486             return FALSE;
487         }
488     }
489     else
490     {
491         mate_rr_screen_get_ranges (info->screen,
492 					 &(info->min_width),
493 					 &(info->max_width),
494 					 &(info->min_height),
495 					 &(info->max_height));
496     }
497 
498     info->primary = None;
499 	display = gdk_display_get_default ();
500     gdk_x11_display_error_trap_push (display);
501     info->primary = XRRGetOutputPrimary (xdisplay, xroot);
502     gdk_x11_display_error_trap_pop_ignored (display);
503 
504     return TRUE;
505 #else
506     return FALSE;
507 #endif /* HAVE_RANDR */
508 }
509 
510 static ScreenInfo *
screen_info_new(MateRRScreen * screen,gboolean needs_reprobe,GError ** error)511 screen_info_new (MateRRScreen *screen, gboolean needs_reprobe, GError **error)
512 {
513     ScreenInfo *info = g_new0 (ScreenInfo, 1);
514     MateRRScreenPrivate *priv;
515 
516     g_assert (screen != NULL);
517 
518     priv = screen->priv;
519 
520     info->outputs = NULL;
521     info->crtcs = NULL;
522     info->modes = NULL;
523     info->screen = screen;
524 
525     if (fill_out_screen_info (priv->xdisplay, priv->xroot, info, needs_reprobe, error))
526     {
527 	return info;
528     }
529     else
530     {
531 	screen_info_free (info);
532 	return NULL;
533     }
534 }
535 
536 static gboolean
screen_update(MateRRScreen * screen,gboolean force_callback,gboolean needs_reprobe,GError ** error)537 screen_update (MateRRScreen *screen, gboolean force_callback, gboolean needs_reprobe, GError **error)
538 {
539     ScreenInfo *info;
540     gboolean changed = FALSE;
541 
542     g_assert (screen != NULL);
543 
544     info = screen_info_new (screen, needs_reprobe, error);
545     if (!info)
546 	    return FALSE;
547 
548 #ifdef HAVE_RANDR
549     if (info->resources->configTimestamp != screen->priv->info->resources->configTimestamp)
550 	    changed = TRUE;
551 #endif
552 
553     screen_info_free (screen->priv->info);
554 
555     screen->priv->info = info;
556 
557     if (changed || force_callback)
558 	g_signal_emit (G_OBJECT (screen), screen_signals[SCREEN_CHANGED], 0);
559 
560     return changed;
561 }
562 
563 static GdkFilterReturn
screen_on_event(GdkXEvent * xevent,GdkEvent * event,gpointer data)564 screen_on_event (GdkXEvent *xevent,
565 		 GdkEvent *event,
566 		 gpointer data)
567 {
568 #ifdef HAVE_RANDR
569     MateRRScreen *screen = data;
570     MateRRScreenPrivate *priv = screen->priv;
571     XEvent *e = xevent;
572     int event_num;
573 
574     if (!e)
575 	return GDK_FILTER_CONTINUE;
576 
577     event_num = e->type - priv->randr_event_base;
578 
579     if (event_num == RRScreenChangeNotify) {
580 	/* We don't reprobe the hardware; we just fetch the X server's latest
581 	 * state.  The server already knows the new state of the outputs; that's
582 	 * why it sent us an event!
583 	 */
584         screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */
585 #if 0
586 	/* Enable this code to get a dialog showing the RANDR timestamps, for debugging purposes */
587 	{
588 	    GtkWidget *dialog;
589 	    XRRScreenChangeNotifyEvent *rr_event;
590 	    static int dialog_num;
591 
592 	    rr_event = (XRRScreenChangeNotifyEvent *) e;
593 
594 	    dialog = gtk_message_dialog_new (NULL,
595 					     0,
596 					     GTK_MESSAGE_INFO,
597 					     GTK_BUTTONS_CLOSE,
598 					     "RRScreenChangeNotify timestamps (%d):\n"
599 					     "event change: %u\n"
600 					     "event config: %u\n"
601 					     "event serial: %lu\n"
602 					     "----------------------"
603 					     "screen change: %u\n"
604 					     "screen config: %u\n",
605 					     dialog_num++,
606 					     (guint32) rr_event->timestamp,
607 					     (guint32) rr_event->config_timestamp,
608 					     rr_event->serial,
609 					     (guint32) priv->info->resources->timestamp,
610 					     (guint32) priv->info->resources->configTimestamp);
611 	    g_signal_connect (dialog, "response",
612 			      G_CALLBACK (gtk_widget_destroy), NULL);
613 	    gtk_widget_show (dialog);
614 	}
615 #endif
616     }
617 #if 0
618     /* WHY THIS CODE IS DISABLED:
619      *
620      * Note that in mate_rr_screen_new(), we only select for
621      * RRScreenChangeNotifyMask.  We used to select for other values in
622      * RR*NotifyMask, but we weren't really doing anything useful with those
623      * events.  We only care about "the screens changed in some way or another"
624      * for now.
625      *
626      * If we ever run into a situtation that could benefit from processing more
627      * detailed events, we can enable this code again.
628      *
629      * Note that the X server sends RRScreenChangeNotify in conjunction with the
630      * more detailed events from RANDR 1.2 - see xserver/randr/randr.c:TellChanged().
631      */
632     else if (event_num == RRNotify)
633     {
634 	/* Other RandR events */
635 
636 	XRRNotifyEvent *event = (XRRNotifyEvent *)e;
637 
638 	/* Here we can distinguish between RRNotify events supported
639 	 * since RandR 1.2 such as RRNotify_OutputProperty.  For now, we
640 	 * don't have anything special to do for particular subevent types, so
641 	 * we leave this as an empty switch().
642 	 */
643 	switch (event->subtype)
644 	{
645 	default:
646 	    break;
647 	}
648 
649 	/* No need to reprobe hardware here */
650 	screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */
651     }
652 #endif
653 
654 #endif /* HAVE_RANDR */
655 
656     /* Pass the event on to GTK+ */
657     return GDK_FILTER_CONTINUE;
658 }
659 
660 static gboolean
mate_rr_screen_initable_init(GInitable * initable,GCancellable * canc,GError ** error)661 mate_rr_screen_initable_init (GInitable *initable, GCancellable *canc, GError **error)
662 
663 {
664     MateRRScreen *self = MATE_RR_SCREEN (initable);
665     MateRRScreenPrivate *priv = self->priv;
666     Display *dpy = GDK_SCREEN_XDISPLAY (self->priv->gdk_screen);
667     int event_base;
668     int ignore;
669 
670     priv->connector_type_atom = XInternAtom (dpy, "ConnectorType", FALSE);
671 
672 #ifdef HAVE_RANDR
673     if (XRRQueryExtension (dpy, &event_base, &ignore))
674     {
675         priv->randr_event_base = event_base;
676 
677         XRRQueryVersion (dpy, &priv->rr_major_version, &priv->rr_minor_version);
678         if (priv->rr_major_version < 1 || (priv->rr_major_version == 1 && priv->rr_minor_version < 3)) {
679             g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_NO_RANDR_EXTENSION,
680                 "RANDR extension is too old (must be at least 1.3)");
681             return FALSE;
682         }
683 
684         priv->info = screen_info_new (self, TRUE, error);
685 
686         if (!priv->info) {
687 	    return FALSE;
688 	}
689 
690         XRRSelectInput (priv->xdisplay,
691             priv->xroot,
692             RRScreenChangeNotifyMask);
693         gdk_x11_register_standard_event_type (gdk_screen_get_display (priv->gdk_screen),
694                           event_base,
695                           RRNotify + 1);
696         gdk_window_add_filter (priv->gdk_root, screen_on_event, self);
697 
698         return TRUE;
699     }
700     else
701     {
702 #endif /* HAVE_RANDR */
703     g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_NO_RANDR_EXTENSION,
704         _("RANDR extension is not present"));
705 
706     return FALSE;
707 
708 #ifdef HAVE_RANDR
709    }
710 #endif
711 }
712 
713 void
mate_rr_screen_initable_iface_init(GInitableIface * iface)714 mate_rr_screen_initable_iface_init (GInitableIface *iface)
715 {
716     iface->init = mate_rr_screen_initable_init;
717 }
718 
719 void
mate_rr_screen_finalize(GObject * gobject)720     mate_rr_screen_finalize (GObject *gobject)
721 {
722     MateRRScreen *screen = MATE_RR_SCREEN (gobject);
723 
724     gdk_window_remove_filter (screen->priv->gdk_root, screen_on_event, screen);
725 
726     if (screen->priv->info)
727       screen_info_free (screen->priv->info);
728 
729     G_OBJECT_CLASS (mate_rr_screen_parent_class)->finalize (gobject);
730 }
731 
732 void
mate_rr_screen_set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * property)733 mate_rr_screen_set_property (GObject *gobject, guint property_id, const GValue *value, GParamSpec *property)
734 {
735     MateRRScreen *self = MATE_RR_SCREEN (gobject);
736     MateRRScreenPrivate *priv = self->priv;
737 
738     switch (property_id)
739     {
740     case SCREEN_PROP_GDK_SCREEN:
741         priv->gdk_screen = g_value_get_object (value);
742         priv->gdk_root = gdk_screen_get_root_window (priv->gdk_screen);
743         priv->xroot = GDK_WINDOW_XID (priv->gdk_root);
744         priv->xdisplay = GDK_SCREEN_XDISPLAY (priv->gdk_screen);
745         priv->xscreen = gdk_x11_screen_get_xscreen (priv->gdk_screen);
746         return;
747     default:
748         G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
749         return;
750     }
751 }
752 
753 void
mate_rr_screen_get_property(GObject * gobject,guint property_id,GValue * value,GParamSpec * property)754 mate_rr_screen_get_property (GObject *gobject, guint property_id, GValue *value, GParamSpec *property)
755 {
756     MateRRScreen *self = MATE_RR_SCREEN (gobject);
757     MateRRScreenPrivate *priv = self->priv;
758 
759     switch (property_id)
760     {
761     case SCREEN_PROP_GDK_SCREEN:
762         g_value_set_object (value, priv->gdk_screen);
763         return;
764      default:
765         G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
766         return;
767     }
768 }
769 
770 void
mate_rr_screen_class_init(MateRRScreenClass * klass)771 mate_rr_screen_class_init (MateRRScreenClass *klass)
772 {
773     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
774 
775     gobject_class->set_property = mate_rr_screen_set_property;
776     gobject_class->get_property = mate_rr_screen_get_property;
777     gobject_class->finalize = mate_rr_screen_finalize;
778 
779     g_object_class_install_property(
780         gobject_class,
781         SCREEN_PROP_GDK_SCREEN,
782         g_param_spec_object (
783             "gdk-screen",
784             "GDK Screen",
785             "The GDK Screen represented by this MateRRScreen",
786             GDK_TYPE_SCREEN,
787 	    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)
788         );
789 
790     screen_signals[SCREEN_CHANGED] = g_signal_new("changed",
791         G_TYPE_FROM_CLASS (gobject_class),
792         G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
793         G_STRUCT_OFFSET (MateRRScreenClass, changed),
794         NULL,
795         NULL,
796         g_cclosure_marshal_VOID__VOID,
797         G_TYPE_NONE,
798         0);
799 }
800 
801 void
mate_rr_screen_init(MateRRScreen * self)802 mate_rr_screen_init (MateRRScreen *self)
803 {
804     MateRRScreenPrivate *priv = mate_rr_screen_get_instance_private (self);
805     self->priv = priv;
806 
807     priv->gdk_screen = NULL;
808     priv->gdk_root = NULL;
809     priv->xdisplay = NULL;
810     priv->xroot = None;
811     priv->xscreen = NULL;
812     priv->info = NULL;
813     priv->rr_major_version = 0;
814     priv->rr_minor_version = 0;
815 }
816 
817 /**
818  * mate_rr_screen_new:
819  * @screen: the #GdkScreen on which to operate
820  * @error: will be set if XRandR is not supported
821  *
822  * Creates a new #MateRRScreen instance
823  *
824  * Returns: a new #MateRRScreen instance or NULL if screen could not be created,
825  * for instance if the driver does not support Xrandr 1.2
826  */
827 MateRRScreen *
mate_rr_screen_new(GdkScreen * screen,GError ** error)828 mate_rr_screen_new (GdkScreen *screen,
829                     GError **error)
830 {
831     _mate_desktop_init_i18n ();
832     return g_initable_new (MATE_TYPE_RR_SCREEN, NULL, error, "gdk-screen", screen, NULL);
833 }
834 
835 void
mate_rr_screen_set_size(MateRRScreen * screen,int width,int height,int mm_width,int mm_height)836 mate_rr_screen_set_size (MateRRScreen *screen,
837 			  int	      width,
838 			  int       height,
839 			  int       mm_width,
840 			  int       mm_height)
841 {
842     g_return_if_fail (MATE_IS_RR_SCREEN (screen));
843 
844 #ifdef HAVE_RANDR
845 	GdkDisplay *display;
846 
847 	display = gdk_display_get_default ();
848     gdk_x11_display_error_trap_push (display);
849     XRRSetScreenSize (screen->priv->xdisplay, screen->priv->xroot,
850 		      width, height, mm_width, mm_height);
851     gdk_x11_display_error_trap_pop_ignored (display);
852 #endif
853 }
854 
855 /**
856  * mate_rr_screen_get_ranges:
857  * @screen: a #MateRRScreen
858  * @min_width: (out): the minimum width
859  * @max_width: (out): the maximum width
860  * @min_height: (out): the minimum height
861  * @max_height: (out): the maximum height
862  *
863  * Get the ranges of the screen
864  */
865 void
mate_rr_screen_get_ranges(MateRRScreen * screen,int * min_width,int * max_width,int * min_height,int * max_height)866 mate_rr_screen_get_ranges (MateRRScreen *screen,
867 			    int	          *min_width,
868 			    int	          *max_width,
869 			    int           *min_height,
870 			    int	          *max_height)
871 {
872     MateRRScreenPrivate *priv;
873 
874     g_return_if_fail (MATE_IS_RR_SCREEN (screen));
875 
876     priv = screen->priv;
877 
878     if (min_width)
879 	*min_width = priv->info->min_width;
880 
881     if (max_width)
882 	*max_width = priv->info->max_width;
883 
884     if (min_height)
885 	*min_height = priv->info->min_height;
886 
887     if (max_height)
888 	*max_height = priv->info->max_height;
889 }
890 
891 /**
892  * mate_rr_screen_get_timestamps:
893  * @screen: a #MateRRScreen
894  * @change_timestamp_ret: (out): Location in which to store the timestamp at which the RANDR configuration was last changed
895  * @config_timestamp_ret: (out): Location in which to store the timestamp at which the RANDR configuration was last obtained
896  *
897  * Queries the two timestamps that the X RANDR extension maintains.  The X
898  * server will prevent change requests for stale configurations, those whose
899  * timestamp is not equal to that of the latest request for configuration.  The
900  * X server will also prevent change requests that have an older timestamp to
901  * the latest change request.
902  */
903 void
mate_rr_screen_get_timestamps(MateRRScreen * screen,guint32 * change_timestamp_ret,guint32 * config_timestamp_ret)904 mate_rr_screen_get_timestamps (MateRRScreen *screen,
905 				guint32       *change_timestamp_ret,
906 				guint32       *config_timestamp_ret)
907 {
908     MateRRScreenPrivate *priv;
909 
910     g_return_if_fail (MATE_IS_RR_SCREEN (screen));
911 
912     priv = screen->priv;
913 
914 #ifdef HAVE_RANDR
915     if (change_timestamp_ret)
916 	*change_timestamp_ret = priv->info->resources->timestamp;
917 
918     if (config_timestamp_ret)
919 	*config_timestamp_ret = priv->info->resources->configTimestamp;
920 #endif
921 }
922 
923 static gboolean
force_timestamp_update(MateRRScreen * screen)924 force_timestamp_update (MateRRScreen *screen)
925 {
926 #ifdef HAVE_RANDR
927     MateRRScreenPrivate *priv = screen->priv;
928     MateRRCrtc *crtc;
929     XRRCrtcInfo *current_info;
930 	GdkDisplay *display;
931     Status status;
932     gboolean timestamp_updated;
933 
934     timestamp_updated = FALSE;
935 
936     crtc = priv->info->crtcs[0];
937 
938     if (crtc == NULL)
939 	goto out;
940 
941     current_info = XRRGetCrtcInfo (priv->xdisplay,
942 				   priv->info->resources,
943 				   crtc->id);
944 
945     if (current_info == NULL)
946 	  goto out;
947 
948 	display = gdk_display_get_default ();
949     gdk_x11_display_error_trap_push (display);
950     status = XRRSetCrtcConfig (priv->xdisplay,
951 			       priv->info->resources,
952 			       crtc->id,
953 			       current_info->timestamp,
954 			       current_info->x,
955 			       current_info->y,
956 			       current_info->mode,
957 			       current_info->rotation,
958 			       current_info->outputs,
959 			       current_info->noutput);
960 
961     XRRFreeCrtcInfo (current_info);
962 
963     gdk_display_flush (display);
964     if (gdk_x11_display_error_trap_pop (display))
965 	goto out;
966 
967     if (status == RRSetConfigSuccess)
968 	timestamp_updated = TRUE;
969 out:
970     return timestamp_updated;
971 #else
972     return FALSE;
973 #endif
974 }
975 
976 /**
977  * mate_rr_screen_refresh:
978  * @screen: a #MateRRScreen
979  * @error: location to store error, or %NULL
980  *
981  * Refreshes the screen configuration, and calls the screen's callback if it
982  * exists and if the screen's configuration changed.
983  *
984  * Return value: TRUE if the screen's configuration changed; otherwise, the
985  * function returns FALSE and a NULL error if the configuration didn't change,
986  * or FALSE and a non-NULL error if there was an error while refreshing the
987  * configuration.
988  */
989 gboolean
mate_rr_screen_refresh(MateRRScreen * screen,GError ** error)990 mate_rr_screen_refresh (MateRRScreen *screen,
991 			 GError       **error)
992 {
993     gboolean refreshed;
994 
995     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
996 
997     gdk_x11_display_grab (gdk_screen_get_display (screen->priv->gdk_screen));
998 
999     refreshed = screen_update (screen, FALSE, TRUE, error);
1000     force_timestamp_update (screen); /* this is to keep other clients from thinking that the X server re-detected things by itself - bgo#621046 */
1001 
1002     gdk_x11_display_ungrab (gdk_screen_get_display (screen->priv->gdk_screen));
1003 
1004     return refreshed;
1005 }
1006 
1007 /**
1008  * mate_rr_screen_list_modes:
1009  *
1010  * List available XRandR modes
1011  *
1012  * Returns: (array zero-terminated=1) (transfer none):
1013  */
1014 MateRRMode **
mate_rr_screen_list_modes(MateRRScreen * screen)1015 mate_rr_screen_list_modes (MateRRScreen *screen)
1016 {
1017     g_return_val_if_fail (MATE_IS_RR_SCREEN (screen), NULL);
1018     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1019 
1020     return screen->priv->info->modes;
1021 }
1022 
1023 /**
1024  * mate_rr_screen_list_clone_modes:
1025  *
1026  * List available XRandR clone modes
1027  *
1028  * Returns: (array zero-terminated=1) (transfer none):
1029  */
1030 MateRRMode **
mate_rr_screen_list_clone_modes(MateRRScreen * screen)1031 mate_rr_screen_list_clone_modes   (MateRRScreen *screen)
1032 {
1033     g_return_val_if_fail (MATE_IS_RR_SCREEN (screen), NULL);
1034     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1035 
1036     return screen->priv->info->clone_modes;
1037 }
1038 
1039 /**
1040  * mate_rr_screen_list_crtcs:
1041  *
1042  * List all CRTCs
1043  *
1044  * Returns: (array zero-terminated=1) (transfer none):
1045  */
1046 MateRRCrtc **
mate_rr_screen_list_crtcs(MateRRScreen * screen)1047 mate_rr_screen_list_crtcs (MateRRScreen *screen)
1048 {
1049     g_return_val_if_fail (MATE_IS_RR_SCREEN (screen), NULL);
1050     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1051 
1052     return screen->priv->info->crtcs;
1053 }
1054 
1055 /**
1056  * mate_rr_screen_list_outputs:
1057  *
1058  * List all outputs
1059  *
1060  * Returns: (array zero-terminated=1) (transfer none):
1061  */
1062 MateRROutput **
mate_rr_screen_list_outputs(MateRRScreen * screen)1063 mate_rr_screen_list_outputs (MateRRScreen *screen)
1064 {
1065     g_return_val_if_fail (MATE_IS_RR_SCREEN (screen), NULL);
1066     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1067 
1068     return screen->priv->info->outputs;
1069 }
1070 
1071 /**
1072  * mate_rr_screen_get_crtc_by_id:
1073  *
1074  * Returns: (transfer none): the CRTC identified by @id
1075  */
1076 MateRRCrtc *
mate_rr_screen_get_crtc_by_id(MateRRScreen * screen,guint32 id)1077 mate_rr_screen_get_crtc_by_id (MateRRScreen *screen,
1078 				guint32        id)
1079 {
1080     MateRRCrtc **crtcs;
1081     int i;
1082 
1083     g_return_val_if_fail (MATE_IS_RR_SCREEN (screen), NULL);
1084     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1085 
1086     crtcs = screen->priv->info->crtcs;
1087 
1088     for (i = 0; crtcs[i] != NULL; ++i)
1089     {
1090 	if (crtcs[i]->id == id)
1091 	    return crtcs[i];
1092     }
1093 
1094     return NULL;
1095 }
1096 
1097 /**
1098  * mate_rr_screen_get_output_by_id:
1099  *
1100  * Returns: (transfer none): the output identified by @id
1101  */
1102 MateRROutput *
mate_rr_screen_get_output_by_id(MateRRScreen * screen,guint32 id)1103 mate_rr_screen_get_output_by_id (MateRRScreen *screen,
1104 				  guint32        id)
1105 {
1106     MateRROutput **outputs;
1107     int i;
1108 
1109     g_return_val_if_fail (MATE_IS_RR_SCREEN (screen), NULL);
1110     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1111 
1112     outputs = screen->priv->info->outputs;
1113 
1114     for (i = 0; outputs[i] != NULL; ++i)
1115     {
1116 	if (outputs[i]->id == id)
1117 	    return outputs[i];
1118     }
1119 
1120     return NULL;
1121 }
1122 
1123 /* MateRROutput */
1124 static MateRROutput *
output_new(ScreenInfo * info,RROutput id)1125 output_new (ScreenInfo *info, RROutput id)
1126 {
1127     MateRROutput *output = g_slice_new0 (MateRROutput);
1128 
1129     output->id = id;
1130     output->info = info;
1131 
1132     return output;
1133 }
1134 
1135 static guint8 *
get_property(Display * dpy,RROutput output,Atom atom,int * len)1136 get_property (Display *dpy,
1137 	      RROutput output,
1138 	      Atom atom,
1139 	      int *len)
1140 {
1141 #ifdef HAVE_RANDR
1142     unsigned char *prop;
1143     int actual_format;
1144     unsigned long nitems, bytes_after;
1145     Atom actual_type;
1146     guint8 *result;
1147 
1148     XRRGetOutputProperty (dpy, output, atom,
1149 			  0, 100, False, False,
1150 			  AnyPropertyType,
1151 			  &actual_type, &actual_format,
1152 			  &nitems, &bytes_after, &prop);
1153 
1154     if (actual_type == XA_INTEGER && actual_format == 8)
1155     {
1156 #ifdef GLIB_VERSION_2_68
1157 	result = g_memdup2 (prop, nitems);
1158 #else
1159 	result = g_memdup (prop, nitems);
1160 #endif
1161 	if (len)
1162 	    *len = nitems;
1163     }
1164     else
1165     {
1166 	result = NULL;
1167     }
1168 
1169     XFree (prop);
1170 
1171     return result;
1172 #else
1173     return NULL;
1174 #endif /* HAVE_RANDR */
1175 }
1176 
1177 static guint8 *
read_edid_data(MateRROutput * output,int * len)1178 read_edid_data (MateRROutput *output, int *len)
1179 {
1180     Atom edid_atom;
1181     guint8 *result;
1182 
1183     edid_atom = XInternAtom (DISPLAY (output), "EDID", FALSE);
1184     result = get_property (DISPLAY (output),
1185 			   output->id, edid_atom, len);
1186 
1187     if (!result)
1188     {
1189 	edid_atom = XInternAtom (DISPLAY (output), "EDID_DATA", FALSE);
1190 	result = get_property (DISPLAY (output),
1191 			       output->id, edid_atom, len);
1192     }
1193 
1194     if (result)
1195     {
1196 	if (*len % 128 == 0)
1197 	    return result;
1198 	else
1199 	    g_free (result);
1200     }
1201 
1202     return NULL;
1203 }
1204 
1205 static char *
get_connector_type_string(MateRROutput * output)1206 get_connector_type_string (MateRROutput *output)
1207 {
1208 #ifdef HAVE_RANDR
1209     char *result;
1210     unsigned char *prop;
1211     int actual_format;
1212     unsigned long nitems, bytes_after;
1213     Atom actual_type;
1214     Atom connector_type;
1215     char *connector_type_str;
1216 
1217     result = NULL;
1218 
1219     if (XRRGetOutputProperty (DISPLAY (output), output->id, output->info->screen->priv->connector_type_atom,
1220 			      0, 100, False, False,
1221 			      AnyPropertyType,
1222 			      &actual_type, &actual_format,
1223 			      &nitems, &bytes_after, &prop) != Success)
1224 	return NULL;
1225 
1226     if (!(actual_type == XA_ATOM && actual_format == 32 && nitems == 1))
1227 	goto out;
1228 
1229     connector_type = *((Atom *) prop);
1230 
1231     connector_type_str = XGetAtomName (DISPLAY (output), connector_type);
1232     if (connector_type_str) {
1233 	result = g_strdup (connector_type_str); /* so the caller can g_free() it */
1234 	XFree (connector_type_str);
1235     }
1236 
1237 out:
1238 
1239     XFree (prop);
1240 
1241     return result;
1242 #else
1243     return NULL;
1244 #endif
1245 }
1246 
1247 #ifdef HAVE_RANDR
1248 static gboolean
output_initialize(MateRROutput * output,XRRScreenResources * res,GError ** error)1249 output_initialize (MateRROutput *output, XRRScreenResources *res, GError **error)
1250 {
1251     XRROutputInfo *info = XRRGetOutputInfo (
1252 	DISPLAY (output), res, output->id);
1253     GPtrArray *a;
1254     int i;
1255 
1256 #if 0
1257     g_print ("Output %lx Timestamp: %u\n", output->id, (guint32)info->timestamp);
1258 #endif
1259 
1260     if (!info || !output->info)
1261     {
1262 	/* FIXME: see the comment in crtc_initialize() */
1263 	/* Translators: here, an "output" is a video output */
1264 	g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_RANDR_ERROR,
1265 		     _("could not get information about output %d"),
1266 		     (int) output->id);
1267 	return FALSE;
1268     }
1269 
1270     output->name = g_strdup (info->name); /* FIXME: what is nameLen used for? */
1271     output->current_crtc = crtc_by_id (output->info, info->crtc);
1272     output->width_mm = info->mm_width;
1273     output->height_mm = info->mm_height;
1274     output->connected = (info->connection == RR_Connected);
1275     output->connector_type = get_connector_type_string (output);
1276 
1277     /* Possible crtcs */
1278     a = g_ptr_array_new ();
1279 
1280     for (i = 0; i < info->ncrtc; ++i)
1281     {
1282 	MateRRCrtc *crtc = crtc_by_id (output->info, info->crtcs[i]);
1283 
1284 	if (crtc)
1285 	    g_ptr_array_add (a, crtc);
1286     }
1287     g_ptr_array_add (a, NULL);
1288     output->possible_crtcs = (MateRRCrtc **)g_ptr_array_free (a, FALSE);
1289 
1290     /* Clones */
1291     a = g_ptr_array_new ();
1292     for (i = 0; i < info->nclone; ++i)
1293     {
1294 	MateRROutput *mate_rr_output = mate_rr_output_by_id (output->info, info->clones[i]);
1295 
1296 	if (mate_rr_output)
1297 	    g_ptr_array_add (a, mate_rr_output);
1298     }
1299     g_ptr_array_add (a, NULL);
1300     output->clones = (MateRROutput **)g_ptr_array_free (a, FALSE);
1301 
1302     /* Modes */
1303     a = g_ptr_array_new ();
1304     for (i = 0; i < info->nmode; ++i)
1305     {
1306 	MateRRMode *mode = mode_by_id (output->info, info->modes[i]);
1307 
1308 	if (mode)
1309 	    g_ptr_array_add (a, mode);
1310     }
1311     g_ptr_array_add (a, NULL);
1312     output->modes = (MateRRMode **)g_ptr_array_free (a, FALSE);
1313 
1314     output->n_preferred = info->npreferred;
1315 
1316     /* Edid data */
1317     output->edid_data = read_edid_data (output, &output->edid_size);
1318 
1319     XRRFreeOutputInfo (info);
1320 
1321     return TRUE;
1322 }
1323 #endif /* HAVE_RANDR */
1324 
1325 static MateRROutput*
output_copy(const MateRROutput * from)1326 output_copy (const MateRROutput *from)
1327 {
1328     GPtrArray *array;
1329     MateRRCrtc **p_crtc;
1330     MateRROutput **p_output;
1331     MateRRMode **p_mode;
1332     MateRROutput *output = g_slice_new0 (MateRROutput);
1333 
1334     output->id = from->id;
1335     output->info = from->info;
1336     output->name = g_strdup (from->name);
1337     output->current_crtc = from->current_crtc;
1338     output->width_mm = from->width_mm;
1339     output->height_mm = from->height_mm;
1340     output->connected = from->connected;
1341     output->n_preferred = from->n_preferred;
1342     output->connector_type = g_strdup (from->connector_type);
1343 
1344     array = g_ptr_array_new ();
1345     for (p_crtc = from->possible_crtcs; *p_crtc != NULL; p_crtc++)
1346     {
1347         g_ptr_array_add (array, *p_crtc);
1348     }
1349     output->possible_crtcs = (MateRRCrtc**) g_ptr_array_free (array, FALSE);
1350 
1351     array = g_ptr_array_new ();
1352     for (p_output = from->clones; *p_output != NULL; p_output++)
1353     {
1354         g_ptr_array_add (array, *p_output);
1355     }
1356     output->clones = (MateRROutput**) g_ptr_array_free (array, FALSE);
1357 
1358     array = g_ptr_array_new ();
1359     for (p_mode = from->modes; *p_mode != NULL; p_mode++)
1360     {
1361         g_ptr_array_add (array, *p_mode);
1362     }
1363     output->modes = (MateRRMode**) g_ptr_array_free (array, FALSE);
1364 
1365     output->edid_size = from->edid_size;
1366 #ifdef GLIB_VERSION_2_68
1367     output->edid_data = g_memdup2 (from->edid_data, from->edid_size);
1368 #else
1369     output->edid_data = g_memdup (from->edid_data, from->edid_size);
1370 #endif
1371     return output;
1372 }
1373 
1374 static void
output_free(MateRROutput * output)1375 output_free (MateRROutput *output)
1376 {
1377     g_free (output->clones);
1378     g_free (output->modes);
1379     g_free (output->possible_crtcs);
1380     g_free (output->edid_data);
1381     g_free (output->name);
1382     g_free (output->connector_type);
1383     g_slice_free (MateRROutput, output);
1384 }
1385 
1386 guint32
mate_rr_output_get_id(MateRROutput * output)1387 mate_rr_output_get_id (MateRROutput *output)
1388 {
1389     g_assert(output != NULL);
1390 
1391     return output->id;
1392 }
1393 
1394 const guint8 *
mate_rr_output_get_edid_data(MateRROutput * output)1395 mate_rr_output_get_edid_data (MateRROutput *output)
1396 {
1397     g_return_val_if_fail (output != NULL, NULL);
1398 
1399     return output->edid_data;
1400 }
1401 
1402 /**
1403  * mate_rr_screen_get_output_by_name:
1404  *
1405  * Returns: (transfer none): the output identified by @name
1406  */
1407 MateRROutput *
mate_rr_screen_get_output_by_name(MateRRScreen * screen,const char * name)1408 mate_rr_screen_get_output_by_name (MateRRScreen *screen,
1409 				    const char    *name)
1410 {
1411     int i;
1412 
1413     g_return_val_if_fail (MATE_IS_RR_SCREEN (screen), NULL);
1414     g_return_val_if_fail (screen->priv->info != NULL, NULL);
1415 
1416     for (i = 0; screen->priv->info->outputs[i] != NULL; ++i)
1417     {
1418 	MateRROutput *output = screen->priv->info->outputs[i];
1419 
1420 	if (strcmp (output->name, name) == 0)
1421 	    return output;
1422     }
1423 
1424     return NULL;
1425 }
1426 
1427 /**
1428  * mate_rr_output_get_crtc:
1429  * @output: a #MateRROutput
1430  * Returns: (transfer none):
1431  */
1432 MateRRCrtc *
mate_rr_output_get_crtc(MateRROutput * output)1433 mate_rr_output_get_crtc (MateRROutput *output)
1434 {
1435     g_return_val_if_fail (output != NULL, NULL);
1436 
1437     return output->current_crtc;
1438 }
1439 
1440 /**
1441  * mate_rr_output_get_possible_crtcs:
1442  * @output: a #MateRROutput
1443  * Returns: (array zero-terminated=1) (transfer none):
1444  */
1445 MateRRCrtc **
mate_rr_output_get_possible_crtcs(MateRROutput * output)1446 mate_rr_output_get_possible_crtcs (MateRROutput *output)
1447 {
1448     g_return_val_if_fail (output != NULL, NULL);
1449 
1450     return output->possible_crtcs;
1451 }
1452 
1453 /* Returns NULL if the ConnectorType property is not available */
1454 const char *
mate_rr_output_get_connector_type(MateRROutput * output)1455 mate_rr_output_get_connector_type (MateRROutput *output)
1456 {
1457     g_return_val_if_fail (output != NULL, NULL);
1458 
1459     return output->connector_type;
1460 }
1461 
1462 gboolean
_mate_rr_output_name_is_laptop(const char * name)1463 _mate_rr_output_name_is_laptop (const char *name)
1464 {
1465     if (!name)
1466         return FALSE;
1467 
1468     if (strstr (name, "lvds") || /* Most drivers use an "LVDS" prefix... */
1469         strstr (name, "LVDS") ||
1470         strstr (name, "Lvds") ||
1471         strstr (name, "LCD")  || /* ... but fglrx uses "LCD" in some versions.  Shoot me now, kthxbye. */
1472         strstr (name, "eDP"))    /* eDP is for internal laptop panel connections */
1473         return TRUE;
1474 
1475     return FALSE;
1476 }
1477 
1478 gboolean
mate_rr_output_is_laptop(MateRROutput * output)1479 mate_rr_output_is_laptop (MateRROutput *output)
1480 {
1481     g_return_val_if_fail (output != NULL, FALSE);
1482 
1483     if (!output->connected)
1484         return FALSE;
1485 
1486     if (g_strcmp0 (output->connector_type, MATE_RR_CONNECTOR_TYPE_PANEL) == 0)
1487         return TRUE;
1488 
1489     /* Fallback (see https://bugs.freedesktop.org/show_bug.cgi?id=26736) */
1490     return _mate_rr_output_name_is_laptop (output->name);
1491 }
1492 
1493 /**
1494  * mate_rr_output_get_current_mode:
1495  * @output: a #MateRROutput
1496  * Returns: (transfer none): the current mode of this output
1497  */
1498 MateRRMode *
mate_rr_output_get_current_mode(MateRROutput * output)1499 mate_rr_output_get_current_mode (MateRROutput *output)
1500 {
1501     MateRRCrtc *crtc;
1502 
1503     g_return_val_if_fail (output != NULL, NULL);
1504 
1505     if ((crtc = mate_rr_output_get_crtc (output)))
1506 	return mate_rr_crtc_get_current_mode (crtc);
1507 
1508     return NULL;
1509 }
1510 
1511 /**
1512  * mate_rr_output_get_position:
1513  * @output: a #MateRROutput
1514  * @x: (out) (allow-none):
1515  * @y: (out) (allow-none):
1516  */
1517 void
mate_rr_output_get_position(MateRROutput * output,int * x,int * y)1518 mate_rr_output_get_position (MateRROutput   *output,
1519 			      int             *x,
1520 			      int             *y)
1521 {
1522     MateRRCrtc *crtc;
1523 
1524     g_return_if_fail (output != NULL);
1525 
1526     if ((crtc = mate_rr_output_get_crtc (output)))
1527 	mate_rr_crtc_get_position (crtc, x, y);
1528 }
1529 
1530 const char *
mate_rr_output_get_name(MateRROutput * output)1531 mate_rr_output_get_name (MateRROutput *output)
1532 {
1533     g_assert (output != NULL);
1534     return output->name;
1535 }
1536 
1537 int
mate_rr_output_get_width_mm(MateRROutput * output)1538 mate_rr_output_get_width_mm (MateRROutput *output)
1539 {
1540     g_assert (output != NULL);
1541     return output->width_mm;
1542 }
1543 
1544 int
mate_rr_output_get_height_mm(MateRROutput * output)1545 mate_rr_output_get_height_mm (MateRROutput *output)
1546 {
1547     g_assert (output != NULL);
1548     return output->height_mm;
1549 }
1550 
1551 /**
1552  * mate_rr_output_get_preferred_mode:
1553  * @output: a #MateRROutput
1554  * Returns: (transfer none):
1555  */
1556 MateRRMode *
mate_rr_output_get_preferred_mode(MateRROutput * output)1557 mate_rr_output_get_preferred_mode (MateRROutput *output)
1558 {
1559     g_return_val_if_fail (output != NULL, NULL);
1560     if (output->n_preferred)
1561 	return output->modes[0];
1562 
1563     return NULL;
1564 }
1565 
1566 /**
1567  * mate_rr_output_list_modes:
1568  * @output: a #MateRROutput
1569  * Returns: (array zero-terminated=1) (transfer none):
1570  */
1571 
1572 MateRRMode **
mate_rr_output_list_modes(MateRROutput * output)1573 mate_rr_output_list_modes (MateRROutput *output)
1574 {
1575     g_return_val_if_fail (output != NULL, NULL);
1576     return output->modes;
1577 }
1578 
1579 gboolean
mate_rr_output_is_connected(MateRROutput * output)1580 mate_rr_output_is_connected (MateRROutput *output)
1581 {
1582     g_return_val_if_fail (output != NULL, FALSE);
1583     return output->connected;
1584 }
1585 
1586 gboolean
mate_rr_output_supports_mode(MateRROutput * output,MateRRMode * mode)1587 mate_rr_output_supports_mode (MateRROutput *output,
1588 			       MateRRMode   *mode)
1589 {
1590     int i;
1591 
1592     g_return_val_if_fail (output != NULL, FALSE);
1593     g_return_val_if_fail (mode != NULL, FALSE);
1594 
1595     for (i = 0; output->modes[i] != NULL; ++i)
1596     {
1597 	if (output->modes[i] == mode)
1598 	    return TRUE;
1599     }
1600 
1601     return FALSE;
1602 }
1603 
1604 gboolean
mate_rr_output_can_clone(MateRROutput * output,MateRROutput * clone)1605 mate_rr_output_can_clone (MateRROutput *output,
1606 			   MateRROutput *clone)
1607 {
1608     int i;
1609 
1610     g_return_val_if_fail (output != NULL, FALSE);
1611     g_return_val_if_fail (clone != NULL, FALSE);
1612 
1613     for (i = 0; output->clones[i] != NULL; ++i)
1614     {
1615 	if (output->clones[i] == clone)
1616 	    return TRUE;
1617     }
1618 
1619     return FALSE;
1620 }
1621 
1622 gboolean
mate_rr_output_get_is_primary(MateRROutput * output)1623 mate_rr_output_get_is_primary (MateRROutput *output)
1624 {
1625 #ifdef HAVE_RANDR
1626     return output->info->primary == output->id;
1627 #else
1628     return FALSE;
1629 #endif
1630 }
1631 
1632 void
mate_rr_screen_set_primary_output(MateRRScreen * screen,MateRROutput * output)1633 mate_rr_screen_set_primary_output (MateRRScreen *screen,
1634                                     MateRROutput *output)
1635 {
1636 #ifdef HAVE_RANDR
1637     MateRRScreenPrivate *priv;
1638 
1639     g_return_if_fail (MATE_IS_RR_SCREEN (screen));
1640 
1641     priv = screen->priv;
1642 
1643     RROutput id;
1644 
1645     if (output)
1646         id = output->id;
1647     else
1648         id = None;
1649 
1650     XRRSetOutputPrimary (priv->xdisplay, priv->xroot, id);
1651 #endif
1652 }
1653 
1654 /* MateRRCrtc */
1655 typedef struct
1656 {
1657     Rotation xrot;
1658     MateRRRotation rot;
1659 } RotationMap;
1660 
1661 static const RotationMap rotation_map[] =
1662 {
1663     { RR_Rotate_0, MATE_RR_ROTATION_0 },
1664     { RR_Rotate_90, MATE_RR_ROTATION_90 },
1665     { RR_Rotate_180, MATE_RR_ROTATION_180 },
1666     { RR_Rotate_270, MATE_RR_ROTATION_270 },
1667     { RR_Reflect_X, MATE_RR_REFLECT_X },
1668     { RR_Reflect_Y, MATE_RR_REFLECT_Y },
1669 };
1670 
1671 static MateRRRotation
mate_rr_rotation_from_xrotation(Rotation r)1672 mate_rr_rotation_from_xrotation (Rotation r)
1673 {
1674     int i;
1675     MateRRRotation result = 0;
1676 
1677     for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i)
1678     {
1679 	if (r & rotation_map[i].xrot)
1680 	    result |= rotation_map[i].rot;
1681     }
1682 
1683     return result;
1684 }
1685 
1686 static Rotation
xrotation_from_rotation(MateRRRotation r)1687 xrotation_from_rotation (MateRRRotation r)
1688 {
1689     int i;
1690     Rotation result = 0;
1691 
1692     for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i)
1693     {
1694 	if (r & rotation_map[i].rot)
1695 	    result |= rotation_map[i].xrot;
1696     }
1697 
1698     return result;
1699 }
1700 
1701 #ifndef MATE_DISABLE_DEPRECATED_SOURCE
1702 gboolean
mate_rr_crtc_set_config(MateRRCrtc * crtc,int x,int y,MateRRMode * mode,MateRRRotation rotation,MateRROutput ** outputs,int n_outputs,GError ** error)1703 mate_rr_crtc_set_config (MateRRCrtc      *crtc,
1704 			  int               x,
1705 			  int               y,
1706 			  MateRRMode      *mode,
1707 			  MateRRRotation   rotation,
1708 			  MateRROutput   **outputs,
1709 			  int               n_outputs,
1710 			  GError          **error)
1711 {
1712     return mate_rr_crtc_set_config_with_time (crtc, GDK_CURRENT_TIME, x, y, mode, rotation, outputs, n_outputs, error);
1713 }
1714 #endif
1715 
1716 gboolean
mate_rr_crtc_set_config_with_time(MateRRCrtc * crtc,guint32 timestamp,int x,int y,MateRRMode * mode,MateRRRotation rotation,MateRROutput ** outputs,int n_outputs,GError ** error)1717 mate_rr_crtc_set_config_with_time (MateRRCrtc      *crtc,
1718 				    guint32           timestamp,
1719 				    int               x,
1720 				    int               y,
1721 				    MateRRMode      *mode,
1722 				    MateRRRotation   rotation,
1723 				    MateRROutput   **outputs,
1724 				    int               n_outputs,
1725 				    GError          **error)
1726 {
1727 #ifdef HAVE_RANDR
1728     ScreenInfo *info;
1729     GArray *output_ids;
1730 	GdkDisplay *display;
1731     Status status;
1732     gboolean result;
1733     int i;
1734 
1735     g_return_val_if_fail (crtc != NULL, FALSE);
1736     g_return_val_if_fail (mode != NULL || outputs == NULL || n_outputs == 0, FALSE);
1737     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1738 
1739     info = crtc->info;
1740 
1741     if (mode)
1742     {
1743 	if (x + mode->width > info->max_width
1744 	    || y + mode->height > info->max_height)
1745 	{
1746 	    g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_BOUNDS_ERROR,
1747 			 /* Translators: the "position", "size", and "maximum"
1748 			  * words here are not keywords; please translate them
1749 			  * as usual.  A CRTC is a CRT Controller (this is X terminology) */
1750 			 _("requested position/size for CRTC %d is outside the allowed limit: "
1751 			   "position=(%d, %d), size=(%d, %d), maximum=(%d, %d)"),
1752 			 (int) crtc->id,
1753 			 x, y,
1754 			 mode->width, mode->height,
1755 			 info->max_width, info->max_height);
1756 	    return FALSE;
1757 	}
1758     }
1759 
1760     output_ids = g_array_new (FALSE, FALSE, sizeof (RROutput));
1761 
1762     if (outputs)
1763     {
1764 	for (i = 0; i < n_outputs; ++i)
1765 	    g_array_append_val (output_ids, outputs[i]->id);
1766     }
1767 
1768 	display = gdk_display_get_default ();
1769     gdk_x11_display_error_trap_push (display);
1770     status = XRRSetCrtcConfig (DISPLAY (crtc), info->resources, crtc->id,
1771 			       timestamp,
1772 			       x, y,
1773 			       mode ? mode->id : None,
1774 			       xrotation_from_rotation (rotation),
1775 			       (RROutput *)output_ids->data,
1776 			       output_ids->len);
1777 
1778     g_array_free (output_ids, TRUE);
1779 
1780     if (gdk_x11_display_error_trap_pop (display) || status != RRSetConfigSuccess) {
1781         /* Translators: CRTC is a CRT Controller (this is X terminology).
1782          * It is *very* unlikely that you'll ever get this error, so it is
1783          * only listed for completeness. */
1784         g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_RANDR_ERROR,
1785                      _("could not set the configuration for CRTC %d"),
1786                      (int) crtc->id);
1787         return FALSE;
1788     } else {
1789         result = TRUE;
1790     }
1791 
1792     return result;
1793 #else
1794     return FALSE;
1795 #endif /* HAVE_RANDR */
1796 }
1797 
1798 /**
1799  * mate_rr_crtc_get_current_mode:
1800  * @crtc: a #MateRRCrtc
1801  * Returns: (transfer none): the current mode of this crtc
1802  */
1803 MateRRMode *
mate_rr_crtc_get_current_mode(MateRRCrtc * crtc)1804 mate_rr_crtc_get_current_mode (MateRRCrtc *crtc)
1805 {
1806     g_return_val_if_fail (crtc != NULL, NULL);
1807 
1808     return crtc->current_mode;
1809 }
1810 
1811 guint32
mate_rr_crtc_get_id(MateRRCrtc * crtc)1812 mate_rr_crtc_get_id (MateRRCrtc *crtc)
1813 {
1814     g_return_val_if_fail (crtc != NULL, 0);
1815 
1816     return crtc->id;
1817 }
1818 
1819 gboolean
mate_rr_crtc_can_drive_output(MateRRCrtc * crtc,MateRROutput * output)1820 mate_rr_crtc_can_drive_output (MateRRCrtc   *crtc,
1821 				MateRROutput *output)
1822 {
1823     int i;
1824 
1825     g_return_val_if_fail (crtc != NULL, FALSE);
1826     g_return_val_if_fail (output != NULL, FALSE);
1827 
1828     for (i = 0; crtc->possible_outputs[i] != NULL; ++i)
1829     {
1830 	if (crtc->possible_outputs[i] == output)
1831 	    return TRUE;
1832     }
1833 
1834     return FALSE;
1835 }
1836 
1837 /* FIXME: merge with get_mode()? */
1838 
1839 /**
1840  * mate_rr_crtc_get_position:
1841  * @crtc: a #MateRRCrtc
1842  * @x: (out) (allow-none):
1843  * @y: (out) (allow-none):
1844  */
1845 void
mate_rr_crtc_get_position(MateRRCrtc * crtc,int * x,int * y)1846 mate_rr_crtc_get_position (MateRRCrtc *crtc,
1847 			    int         *x,
1848 			    int         *y)
1849 {
1850     g_return_if_fail (crtc != NULL);
1851 
1852     if (x)
1853 	*x = crtc->x;
1854 
1855     if (y)
1856 	*y = crtc->y;
1857 }
1858 
1859 /* FIXME: merge with get_mode()? */
1860 MateRRRotation
mate_rr_crtc_get_current_rotation(MateRRCrtc * crtc)1861 mate_rr_crtc_get_current_rotation (MateRRCrtc *crtc)
1862 {
1863     g_assert(crtc != NULL);
1864     return crtc->current_rotation;
1865 }
1866 
1867 MateRRRotation
mate_rr_crtc_get_rotations(MateRRCrtc * crtc)1868 mate_rr_crtc_get_rotations (MateRRCrtc *crtc)
1869 {
1870     g_assert(crtc != NULL);
1871     return crtc->rotations;
1872 }
1873 
1874 gboolean
mate_rr_crtc_supports_rotation(MateRRCrtc * crtc,MateRRRotation rotation)1875 mate_rr_crtc_supports_rotation (MateRRCrtc *   crtc,
1876 				 MateRRRotation rotation)
1877 {
1878     g_return_val_if_fail (crtc != NULL, FALSE);
1879     return (crtc->rotations & rotation);
1880 }
1881 
1882 static MateRRCrtc *
crtc_new(ScreenInfo * info,RROutput id)1883 crtc_new (ScreenInfo *info, RROutput id)
1884 {
1885     MateRRCrtc *crtc = g_slice_new0 (MateRRCrtc);
1886 
1887     crtc->id = id;
1888     crtc->info = info;
1889 
1890     return crtc;
1891 }
1892 
1893 static MateRRCrtc *
crtc_copy(const MateRRCrtc * from)1894 crtc_copy (const MateRRCrtc *from)
1895 {
1896     MateRROutput **p_output;
1897     GPtrArray *array;
1898     MateRRCrtc *to = g_slice_new0 (MateRRCrtc);
1899 
1900     to->info = from->info;
1901     to->id = from->id;
1902     to->current_mode = from->current_mode;
1903     to->x = from->x;
1904     to->y = from->y;
1905     to->current_rotation = from->current_rotation;
1906     to->rotations = from->rotations;
1907     to->gamma_size = from->gamma_size;
1908 
1909     array = g_ptr_array_new ();
1910     for (p_output = from->current_outputs; *p_output != NULL; p_output++)
1911     {
1912         g_ptr_array_add (array, *p_output);
1913     }
1914     to->current_outputs = (MateRROutput**) g_ptr_array_free (array, FALSE);
1915 
1916     array = g_ptr_array_new ();
1917     for (p_output = from->possible_outputs; *p_output != NULL; p_output++)
1918     {
1919         g_ptr_array_add (array, *p_output);
1920     }
1921     to->possible_outputs = (MateRROutput**) g_ptr_array_free (array, FALSE);
1922 
1923     return to;
1924 }
1925 
1926 #ifdef HAVE_RANDR
1927 static gboolean
crtc_initialize(MateRRCrtc * crtc,XRRScreenResources * res,GError ** error)1928 crtc_initialize (MateRRCrtc        *crtc,
1929 		 XRRScreenResources *res,
1930 		 GError            **error)
1931 {
1932     XRRCrtcInfo *info = XRRGetCrtcInfo (DISPLAY (crtc), res, crtc->id);
1933     GPtrArray *a;
1934     int i;
1935 
1936 #if 0
1937     g_print ("CRTC %lx Timestamp: %u\n", crtc->id, (guint32)info->timestamp);
1938 #endif
1939 
1940     if (!info)
1941     {
1942 	/* FIXME: We need to reaquire the screen resources */
1943 	/* FIXME: can we actually catch BadRRCrtc, and does it make sense to emit that? */
1944 
1945 	/* Translators: CRTC is a CRT Controller (this is X terminology).
1946 	 * It is *very* unlikely that you'll ever get this error, so it is
1947 	 * only listed for completeness. */
1948 	g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_RANDR_ERROR,
1949 		     _("could not get information about CRTC %d"),
1950 		     (int) crtc->id);
1951 	return FALSE;
1952     }
1953 
1954     /* MateRRMode */
1955     crtc->current_mode = mode_by_id (crtc->info, info->mode);
1956 
1957     crtc->x = info->x;
1958     crtc->y = info->y;
1959 
1960     /* Current outputs */
1961     a = g_ptr_array_new ();
1962     for (i = 0; i < info->noutput; ++i)
1963     {
1964 	MateRROutput *output = mate_rr_output_by_id (crtc->info, info->outputs[i]);
1965 
1966 	if (output)
1967 	    g_ptr_array_add (a, output);
1968     }
1969     g_ptr_array_add (a, NULL);
1970     crtc->current_outputs = (MateRROutput **)g_ptr_array_free (a, FALSE);
1971 
1972     /* Possible outputs */
1973     a = g_ptr_array_new ();
1974     for (i = 0; i < info->npossible; ++i)
1975     {
1976 	MateRROutput *output = mate_rr_output_by_id (crtc->info, info->possible[i]);
1977 
1978 	if (output)
1979 	    g_ptr_array_add (a, output);
1980     }
1981     g_ptr_array_add (a, NULL);
1982     crtc->possible_outputs = (MateRROutput **)g_ptr_array_free (a, FALSE);
1983 
1984     /* Rotations */
1985     crtc->current_rotation = mate_rr_rotation_from_xrotation (info->rotation);
1986     crtc->rotations = mate_rr_rotation_from_xrotation (info->rotations);
1987 
1988     XRRFreeCrtcInfo (info);
1989 
1990     /* get an store gamma size */
1991     crtc->gamma_size = XRRGetCrtcGammaSize (DISPLAY (crtc), crtc->id);
1992 
1993     return TRUE;
1994 }
1995 #endif
1996 
1997 static void
crtc_free(MateRRCrtc * crtc)1998 crtc_free (MateRRCrtc *crtc)
1999 {
2000     g_free (crtc->current_outputs);
2001     g_free (crtc->possible_outputs);
2002     g_slice_free (MateRRCrtc, crtc);
2003 }
2004 
2005 /* MateRRMode */
2006 static MateRRMode *
mode_new(ScreenInfo * info,RRMode id)2007 mode_new (ScreenInfo *info, RRMode id)
2008 {
2009     MateRRMode *mode = g_slice_new0 (MateRRMode);
2010 
2011     mode->id = id;
2012     mode->info = info;
2013 
2014     return mode;
2015 }
2016 
2017 guint32
mate_rr_mode_get_id(MateRRMode * mode)2018 mate_rr_mode_get_id (MateRRMode *mode)
2019 {
2020     g_return_val_if_fail (mode != NULL, 0);
2021     return mode->id;
2022 }
2023 
2024 guint
mate_rr_mode_get_width(MateRRMode * mode)2025 mate_rr_mode_get_width (MateRRMode *mode)
2026 {
2027     g_return_val_if_fail (mode != NULL, 0);
2028     return mode->width;
2029 }
2030 
2031 int
mate_rr_mode_get_freq(MateRRMode * mode)2032 mate_rr_mode_get_freq (MateRRMode *mode)
2033 {
2034     g_return_val_if_fail (mode != NULL, 0);
2035     return (mode->freq) / 1000;
2036 }
2037 
2038 guint
mate_rr_mode_get_height(MateRRMode * mode)2039 mate_rr_mode_get_height (MateRRMode *mode)
2040 {
2041     g_return_val_if_fail (mode != NULL, 0);
2042     return mode->height;
2043 }
2044 
2045 #ifdef HAVE_RANDR
2046 static void
mode_initialize(MateRRMode * mode,XRRModeInfo * info)2047 mode_initialize (MateRRMode *mode, XRRModeInfo *info)
2048 {
2049     g_assert (mode != NULL);
2050     g_assert (info != NULL);
2051 
2052     mode->name = g_strdup (info->name);
2053     mode->width = info->width;
2054     mode->height = info->height;
2055     mode->freq = ((info->dotClock / (double)info->hTotal) / info->vTotal + 0.5) * 1000;
2056 }
2057 #endif /* HAVE_RANDR */
2058 
2059 static MateRRMode *
mode_copy(const MateRRMode * from)2060 mode_copy (const MateRRMode *from)
2061 {
2062     MateRRMode *to = g_slice_new0 (MateRRMode);
2063 
2064     to->id = from->id;
2065     to->info = from->info;
2066     to->name = g_strdup (from->name);
2067     to->width = from->width;
2068     to->height = from->height;
2069     to->freq = from->freq;
2070 
2071     return to;
2072 }
2073 
2074 static void
mode_free(MateRRMode * mode)2075 mode_free (MateRRMode *mode)
2076 {
2077     g_free (mode->name);
2078     g_slice_free (MateRRMode, mode);
2079 }
2080 
2081 void
mate_rr_crtc_set_gamma(MateRRCrtc * crtc,int size,unsigned short * red,unsigned short * green,unsigned short * blue)2082 mate_rr_crtc_set_gamma (MateRRCrtc *crtc, int size,
2083 			 unsigned short *red,
2084 			 unsigned short *green,
2085 			 unsigned short *blue)
2086 {
2087 #ifdef HAVE_RANDR
2088     int copy_size;
2089     XRRCrtcGamma *gamma;
2090 
2091     g_return_if_fail (crtc != NULL);
2092     g_return_if_fail (red != NULL);
2093     g_return_if_fail (green != NULL);
2094     g_return_if_fail (blue != NULL);
2095 
2096     if (size != crtc->gamma_size)
2097 	return;
2098 
2099     gamma = XRRAllocGamma (crtc->gamma_size);
2100 
2101     copy_size = crtc->gamma_size * sizeof (unsigned short);
2102     memcpy (gamma->red, red, copy_size);
2103     memcpy (gamma->green, green, copy_size);
2104     memcpy (gamma->blue, blue, copy_size);
2105 
2106     XRRSetCrtcGamma (DISPLAY (crtc), crtc->id, gamma);
2107     XRRFreeGamma (gamma);
2108 #endif /* HAVE_RANDR */
2109 }
2110 
2111 /**
2112  * mate_rr_crtc_get_gamma:
2113  * @crtc: a #MateRRCrtc
2114  * @size:
2115  * @red: (out): the minimum width
2116  * @green: (out): the maximum width
2117  * @blue: (out): the minimum height
2118  *
2119  * Returns: %TRUE for success
2120  */
2121 gboolean
mate_rr_crtc_get_gamma(MateRRCrtc * crtc,int * size,unsigned short ** red,unsigned short ** green,unsigned short ** blue)2122 mate_rr_crtc_get_gamma (MateRRCrtc *crtc, int *size,
2123 			 unsigned short **red, unsigned short **green,
2124 			 unsigned short **blue)
2125 {
2126 #ifdef HAVE_RANDR
2127     int copy_size;
2128     unsigned short *r, *g, *b;
2129     XRRCrtcGamma *gamma;
2130 
2131     g_return_val_if_fail (crtc != NULL, FALSE);
2132 
2133     gamma = XRRGetCrtcGamma (DISPLAY (crtc), crtc->id);
2134     if (!gamma)
2135 	return FALSE;
2136 
2137     copy_size = crtc->gamma_size * sizeof (unsigned short);
2138 
2139     if (red) {
2140 	r = g_new0 (unsigned short, crtc->gamma_size);
2141 	memcpy (r, gamma->red, copy_size);
2142 	*red = r;
2143     }
2144 
2145     if (green) {
2146 	g = g_new0 (unsigned short, crtc->gamma_size);
2147 	memcpy (g, gamma->green, copy_size);
2148 	*green = g;
2149     }
2150 
2151     if (blue) {
2152 	b = g_new0 (unsigned short, crtc->gamma_size);
2153 	memcpy (b, gamma->blue, copy_size);
2154 	*blue = b;
2155     }
2156 
2157     XRRFreeGamma (gamma);
2158 
2159     if (size)
2160 	*size = crtc->gamma_size;
2161 
2162     return TRUE;
2163 #else
2164     return FALSE;
2165 #endif /* HAVE_RANDR */
2166 }
2167 
2168