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