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