xrandr.c revision 78038a65
1/*
2 * Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc.
3 * Copyright © 2002 Hewlett Packard Company, Inc.
4 * Copyright © 2006 Intel Corporation
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that copyright
9 * notice and this permission notice appear in supporting documentation, and
10 * that the name of the copyright holders not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission.  The copyright holders make no representations
13 * about the suitability of this software for any purpose.  It is provided "as
14 * is" without express or implied warranty.
15 *
16 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22 * OF THIS SOFTWARE.
23 *
24 * Thanks to Jim Gettys who wrote most of the client side code,
25 * and part of the server code for randr.
26 */
27
28#include <stdio.h>
29#include <X11/Xlib.h>
30#include <X11/Xlibint.h>
31#include <X11/Xproto.h>
32#include <X11/Xatom.h>
33#include <X11/extensions/Xrandr.h>
34#include <X11/extensions/Xrender.h>	/* we share subpixel information */
35#include <string.h>
36#include <stdlib.h>
37#include <stdarg.h>
38#include <math.h>
39
40#ifdef HAVE_CONFIG_H
41#include "config.h"
42#endif
43
44#if RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR >= 2)
45#define HAS_RANDR_1_2 1
46#endif
47
48static char	*program_name;
49static Display	*dpy;
50static Window	root;
51static int	screen = -1;
52static Bool	verbose = False;
53static Bool	automatic = False;
54static Bool	properties = False;
55static Bool	grab_server = True;
56static Bool	no_primary = False;
57
58static char *direction[5] = {
59    "normal",
60    "left",
61    "inverted",
62    "right",
63    "\n"};
64
65static char *reflections[5] = {
66    "normal",
67    "x",
68    "y",
69    "xy",
70    "\n"};
71
72/* subpixel order */
73static char *order[6] = {
74    "unknown",
75    "horizontal rgb",
76    "horizontal bgr",
77    "vertical rgb",
78    "vertical bgr",
79    "no subpixels"};
80
81static const struct {
82    char	    *string;
83    unsigned long   flag;
84} mode_flags[] = {
85    { "+HSync", RR_HSyncPositive },
86    { "-HSync", RR_HSyncNegative },
87    { "+VSync", RR_VSyncPositive },
88    { "-VSync", RR_VSyncNegative },
89    { "Interlace", RR_Interlace },
90    { "DoubleScan", RR_DoubleScan },
91    { "CSync",	    RR_CSync },
92    { "+CSync",	    RR_CSyncPositive },
93    { "-CSync",	    RR_CSyncNegative },
94    { NULL,	    0 }
95};
96
97static void
98usage(void)
99{
100    fprintf(stderr, "usage: %s [options]\n", program_name);
101    fprintf(stderr, "  where options are:\n");
102    fprintf(stderr, "  -display <display> or -d <display>\n");
103    fprintf(stderr, "  -help\n");
104    fprintf(stderr, "  -o <normal,inverted,left,right,0,1,2,3>\n");
105    fprintf(stderr, "            or --orientation <normal,inverted,left,right,0,1,2,3>\n");
106    fprintf(stderr, "  -q        or --query\n");
107    fprintf(stderr, "  -s <size>/<width>x<height> or --size <size>/<width>x<height>\n");
108    fprintf(stderr, "  -r <rate> or --rate <rate> or --refresh <rate>\n");
109    fprintf(stderr, "  -v        or --version\n");
110    fprintf(stderr, "  -x        (reflect in x)\n");
111    fprintf(stderr, "  -y        (reflect in y)\n");
112    fprintf(stderr, "  --screen <screen>\n");
113    fprintf(stderr, "  --verbose\n");
114    fprintf(stderr, "  --dryrun\n");
115    fprintf(stderr, "  --nograb\n");
116#if HAS_RANDR_1_2
117    fprintf(stderr, "  --prop or --properties\n");
118    fprintf(stderr, "  --fb <width>x<height>\n");
119    fprintf(stderr, "  --fbmm <width>x<height>\n");
120    fprintf(stderr, "  --dpi <dpi>/<output>\n");
121#if 0
122    fprintf(stderr, "  --clone\n");
123    fprintf(stderr, "  --extend\n");
124#endif
125    fprintf(stderr, "  --output <output>\n");
126    fprintf(stderr, "      --auto\n");
127    fprintf(stderr, "      --mode <mode>\n");
128    fprintf(stderr, "      --preferred\n");
129    fprintf(stderr, "      --pos <x>x<y>\n");
130    fprintf(stderr, "      --rate <rate> or --refresh <rate>\n");
131    fprintf(stderr, "      --reflect normal,x,y,xy\n");
132    fprintf(stderr, "      --rotate normal,inverted,left,right\n");
133    fprintf(stderr, "      --left-of <output>\n");
134    fprintf(stderr, "      --right-of <output>\n");
135    fprintf(stderr, "      --above <output>\n");
136    fprintf(stderr, "      --below <output>\n");
137    fprintf(stderr, "      --same-as <output>\n");
138    fprintf(stderr, "      --set <property> <value>\n");
139    fprintf(stderr, "      --scale <x>x<y>\n");
140    fprintf(stderr, "      --transform <a>,<b>,<c>,<d>,<e>,<f>,<g>,<h>,<i>\n");
141    fprintf(stderr, "      --off\n");
142    fprintf(stderr, "      --crtc <crtc>\n");
143    fprintf(stderr, "      --panning <w>x<h>[+<x>+<y>[/<track:w>x<h>+<x>+<y>[/<border:l>/<t>/<r>/<b>]]]\n");
144    fprintf(stderr, "      --gamma <r>:<g>:<b>\n");
145    fprintf(stderr, "      --primary\n");
146    fprintf(stderr, "  --noprimary\n");
147    fprintf(stderr, "  --newmode <name> <clock MHz>\n");
148    fprintf(stderr, "            <hdisp> <hsync-start> <hsync-end> <htotal>\n");
149    fprintf(stderr, "            <vdisp> <vsync-start> <vsync-end> <vtotal>\n");
150    fprintf(stderr, "            [+HSync] [-HSync] [+VSync] [-VSync]\n");
151    fprintf(stderr, "  --rmmode <name>\n");
152    fprintf(stderr, "  --addmode <output> <name>\n");
153    fprintf(stderr, "  --delmode <output> <name>\n");
154#endif
155
156    exit(1);
157    /*NOTREACHED*/
158}
159
160static void
161fatal (const char *format, ...)
162{
163    va_list ap;
164
165    va_start (ap, format);
166    fprintf (stderr, "%s: ", program_name);
167    vfprintf (stderr, format, ap);
168    va_end (ap);
169    exit (1);
170    /*NOTREACHED*/
171}
172
173static void
174warning (const char *format, ...)
175{
176    va_list ap;
177
178    va_start (ap, format);
179    fprintf (stderr, "%s: ", program_name);
180    vfprintf (stderr, format, ap);
181    va_end (ap);
182}
183
184static char *
185rotation_name (Rotation rotation)
186{
187    int	i;
188
189    if ((rotation & 0xf) == 0)
190	return "normal";
191    for (i = 0; i < 4; i++)
192	if (rotation & (1 << i))
193	    return direction[i];
194    return "invalid rotation";
195}
196
197static char *
198reflection_name (Rotation rotation)
199{
200    rotation &= (RR_Reflect_X|RR_Reflect_Y);
201    switch (rotation) {
202    case 0:
203	return "none";
204    case RR_Reflect_X:
205	return "X axis";
206    case RR_Reflect_Y:
207	return "Y axis";
208    case RR_Reflect_X|RR_Reflect_Y:
209	return "X and Y axis";
210    }
211    return "invalid reflection";
212}
213
214#if HAS_RANDR_1_2
215typedef enum _policy {
216    clone, extend
217} policy_t;
218
219typedef enum _relation {
220    left_of, right_of, above, below, same_as,
221} relation_t;
222
223typedef struct {
224    int	    x, y, width, height;
225} rectangle_t;
226
227typedef struct {
228    int	    x1, y1, x2, y2;
229} box_t;
230
231typedef struct {
232    int	    x, y;
233} point_t;
234
235typedef enum _changes {
236    changes_none = 0,
237    changes_crtc = (1 << 0),
238    changes_mode = (1 << 1),
239    changes_relation = (1 << 2),
240    changes_position = (1 << 3),
241    changes_rotation = (1 << 4),
242    changes_reflection = (1 << 5),
243    changes_automatic = (1 << 6),
244    changes_refresh = (1 << 7),
245    changes_property = (1 << 8),
246    changes_transform = (1 << 9),
247    changes_panning = (1 << 10),
248    changes_gamma = (1 << 11),
249    changes_primary = (1 << 12),
250} changes_t;
251
252typedef enum _name_kind {
253    name_none = 0,
254    name_string = (1 << 0),
255    name_xid = (1 << 1),
256    name_index = (1 << 2),
257    name_preferred = (1 << 3),
258} name_kind_t;
259
260typedef struct {
261    name_kind_t	    kind;
262    char    	    *string;
263    XID	    	    xid;
264    int		    index;
265} name_t;
266
267typedef struct _crtc crtc_t;
268typedef struct _output	output_t;
269typedef struct _transform transform_t;
270typedef struct _umode	umode_t;
271typedef struct _output_prop output_prop_t;
272
273struct _transform {
274    XTransform	    transform;
275    char	    *filter;
276    int		    nparams;
277    XFixed	    *params;
278};
279
280struct _crtc {
281    name_t	    crtc;
282    Bool	    changing;
283    XRRCrtcInfo	    *crtc_info;
284
285    XRRModeInfo	    *mode_info;
286    XRRPanning      *panning_info;
287    int		    x;
288    int		    y;
289    Rotation	    rotation;
290    output_t	    **outputs;
291    int		    noutput;
292    transform_t	    current_transform, pending_transform;
293};
294
295struct _output_prop {
296    struct _output_prop	*next;
297    char		*name;
298    char		*value;
299};
300
301struct _output {
302    struct _output   *next;
303
304    changes_t	    changes;
305
306    output_prop_t   *props;
307
308    name_t	    output;
309    XRROutputInfo   *output_info;
310
311    name_t	    crtc;
312    crtc_t	    *crtc_info;
313    crtc_t	    *current_crtc_info;
314
315    name_t	    mode;
316    float	    refresh;
317    XRRModeInfo	    *mode_info;
318
319    name_t	    addmode;
320
321    relation_t	    relation;
322    char	    *relative_to;
323
324    int		    x, y;
325    Rotation	    rotation;
326
327    XRRPanning      panning;
328
329    Bool    	    automatic;
330    transform_t	    transform;
331
332    struct {
333	float red;
334	float green;
335	float blue;
336    } gamma;
337
338    Bool	    primary;
339};
340
341typedef enum _umode_action {
342    umode_create, umode_destroy, umode_add, umode_delete
343} umode_action_t;
344
345
346struct _umode {
347    struct _umode   *next;
348
349    umode_action_t  action;
350    XRRModeInfo	    mode;
351    name_t	    output;
352    name_t	    name;
353};
354
355static char *connection[3] = {
356    "connected",
357    "disconnected",
358    "unknown connection"};
359
360#define OUTPUT_NAME 1
361
362#define CRTC_OFF    2
363#define CRTC_UNSET  3
364#define CRTC_INDEX  0x40000000
365
366#define MODE_NAME   1
367#define MODE_OFF    2
368#define MODE_UNSET  3
369#define MODE_PREF   4
370
371#define POS_UNSET   -1
372
373static output_t	*outputs = NULL;
374static output_t	**outputs_tail = &outputs;
375static crtc_t	*crtcs;
376static umode_t	*umodes;
377static int	num_crtcs;
378static XRRScreenResources  *res;
379static int	fb_width = 0, fb_height = 0;
380static int	fb_width_mm = 0, fb_height_mm = 0;
381static float	dpi = 0;
382static char	*dpi_output = NULL;
383static Bool	dryrun = False;
384static int	minWidth, maxWidth, minHeight, maxHeight;
385static Bool    	has_1_2 = False;
386static Bool    	has_1_3 = False;
387
388static int
389mode_height (XRRModeInfo *mode_info, Rotation rotation)
390{
391    switch (rotation & 0xf) {
392    case RR_Rotate_0:
393    case RR_Rotate_180:
394	return mode_info->height;
395    case RR_Rotate_90:
396    case RR_Rotate_270:
397	return mode_info->width;
398    default:
399	return 0;
400    }
401}
402
403static int
404mode_width (XRRModeInfo *mode_info, Rotation rotation)
405{
406    switch (rotation & 0xf) {
407    case RR_Rotate_0:
408    case RR_Rotate_180:
409	return mode_info->width;
410    case RR_Rotate_90:
411    case RR_Rotate_270:
412	return mode_info->height;
413    default:
414	return 0;
415    }
416}
417
418static Bool
419transform_point (XTransform *transform, double *xp, double *yp)
420{
421    double  vector[3];
422    double  result[3];
423    int	    i, j;
424    double  v;
425
426    vector[0] = *xp;
427    vector[1] = *yp;
428    vector[2] = 1;
429    for (j = 0; j < 3; j++)
430    {
431	v = 0;
432	for (i = 0; i < 3; i++)
433	    v += (XFixedToDouble (transform->matrix[j][i]) * vector[i]);
434	if (v > 32767 || v < -32767)
435	    return False;
436	result[j] = v;
437    }
438    if (!result[2])
439	return False;
440    for (j = 0; j < 2; j++)
441	vector[j] = result[j] / result[2];
442    *xp = vector[0];
443    *yp = vector[1];
444    return True;
445}
446
447static void
448path_bounds (XTransform *transform, point_t *points, int npoints, box_t *box)
449{
450    int	    i;
451    box_t   point;
452
453    for (i = 0; i < npoints; i++) {
454	double	x, y;
455	x = points[i].x;
456	y = points[i].y;
457	transform_point (transform, &x, &y);
458	point.x1 = floor (x);
459	point.y1 = floor (y);
460	point.x2 = ceil (x);
461	point.y2 = ceil (y);
462	if (i == 0)
463	    *box = point;
464	else {
465	    if (point.x1 < box->x1) box->x1 = point.x1;
466	    if (point.y1 < box->y1) box->y1 = point.y1;
467	    if (point.x2 > box->x2) box->x2 = point.x2;
468	    if (point.y2 > box->y2) box->y2 = point.y2;
469	}
470    }
471}
472
473static void
474mode_geometry (XRRModeInfo *mode_info, Rotation rotation,
475	       XTransform *transform,
476	       box_t *bounds)
477{
478    point_t rect[4];
479    int	width = mode_width (mode_info, rotation);
480    int height = mode_height (mode_info, rotation);
481
482    rect[0].x = 0;
483    rect[0].y = 0;
484    rect[1].x = width;
485    rect[1].y = 0;
486    rect[2].x = width;
487    rect[2].y = height;
488    rect[3].x = 0;
489    rect[3].y = height;
490    path_bounds (transform, rect, 4, bounds);
491}
492
493/* v refresh frequency in Hz */
494static float
495mode_refresh (XRRModeInfo *mode_info)
496{
497    float rate;
498
499    if (mode_info->hTotal && mode_info->vTotal)
500	rate = ((float) mode_info->dotClock /
501		((float) mode_info->hTotal * (float) mode_info->vTotal));
502    else
503    	rate = 0;
504    return rate;
505}
506
507/* h sync frequency in Hz */
508static float
509mode_hsync (XRRModeInfo *mode_info)
510{
511    float rate;
512
513    if (mode_info->hTotal)
514	rate = (float) mode_info->dotClock / (float) mode_info->hTotal;
515    else
516    	rate = 0;
517    return rate;
518}
519
520static void
521init_name (name_t *name)
522{
523    name->kind = name_none;
524}
525
526static void
527set_name_string (name_t *name, char *string)
528{
529    name->kind |= name_string;
530    name->string = string;
531}
532
533static void
534set_name_xid (name_t *name, XID xid)
535{
536    name->kind |= name_xid;
537    name->xid = xid;
538}
539
540static void
541set_name_index (name_t *name, int index)
542{
543    name->kind |= name_index;
544    name->index = index;
545}
546
547static void
548set_name_preferred (name_t *name)
549{
550    name->kind |= name_preferred;
551}
552
553static void
554set_name_all (name_t *name, name_t *old)
555{
556    if (old->kind & name_xid)
557	name->xid = old->xid;
558    if (old->kind & name_string)
559	name->string = old->string;
560    if (old->kind & name_index)
561	name->index = old->index;
562    name->kind |= old->kind;
563}
564
565static void
566set_name (name_t *name, char *string, name_kind_t valid)
567{
568    unsigned int xid; /* don't make it XID (which is unsigned long):
569			 scanf() takes unsigned int */
570    int index;
571
572    if ((valid & name_xid) && sscanf (string, "0x%x", &xid) == 1)
573	set_name_xid (name, xid);
574    else if ((valid & name_index) && sscanf (string, "%d", &index) == 1)
575	set_name_index (name, index);
576    else if (valid & name_string)
577	set_name_string (name, string);
578    else
579	usage ();
580}
581
582static void
583init_transform (transform_t *transform)
584{
585    int x;
586    memset (&transform->transform, '\0', sizeof (transform->transform));
587    for (x = 0; x < 3; x++)
588	transform->transform.matrix[x][x] = XDoubleToFixed (1.0);
589    transform->filter = "";
590    transform->nparams = 0;
591    transform->params = NULL;
592}
593
594static void
595set_transform (transform_t  *dest,
596	       XTransform   *transform,
597	       char	    *filter,
598	       XFixed	    *params,
599	       int	    nparams)
600{
601    dest->transform = *transform;
602    dest->filter = strdup (filter);
603    dest->nparams = nparams;
604    dest->params = malloc (nparams * sizeof (XFixed));
605    memcpy (dest->params, params, nparams * sizeof (XFixed));
606}
607
608static void
609copy_transform (transform_t *dest, transform_t *src)
610{
611    set_transform (dest, &src->transform,
612		   src->filter, src->params, src->nparams);
613}
614
615static Bool
616equal_transform (transform_t *a, transform_t *b)
617{
618    if (memcmp (&a->transform, &b->transform, sizeof (XTransform)) != 0)
619	return False;
620    if (strcmp (a->filter, b->filter) != 0)
621	return False;
622    if (a->nparams != b->nparams)
623	return False;
624    if (memcmp (a->params, b->params, a->nparams * sizeof (XFixed)) != 0)
625	return False;
626    return True;
627}
628
629static output_t *
630add_output (void)
631{
632    output_t *output = calloc (1, sizeof (output_t));
633
634    if (!output)
635	fatal ("out of memory\n");
636    output->next = NULL;
637    *outputs_tail = output;
638    outputs_tail = &output->next;
639    return output;
640}
641
642static output_t *
643find_output (name_t *name)
644{
645    output_t *output;
646
647    for (output = outputs; output; output = output->next)
648    {
649	name_kind_t common = name->kind & output->output.kind;
650
651	if ((common & name_xid) && name->xid == output->output.xid)
652	    break;
653	if ((common & name_string) && !strcmp (name->string, output->output.string))
654	    break;
655	if ((common & name_index) && name->index == output->output.index)
656	    break;
657    }
658    return output;
659}
660
661static output_t *
662find_output_by_xid (RROutput output)
663{
664    name_t  output_name;
665
666    init_name (&output_name);
667    set_name_xid (&output_name, output);
668    return find_output (&output_name);
669}
670
671static output_t *
672find_output_by_name (char *name)
673{
674    name_t  output_name;
675
676    init_name (&output_name);
677    set_name_string (&output_name, name);
678    return find_output (&output_name);
679}
680
681static crtc_t *
682find_crtc (name_t *name)
683{
684    int	    c;
685    crtc_t  *crtc = NULL;
686
687    for (c = 0; c < num_crtcs; c++)
688    {
689	name_kind_t common;
690
691	crtc = &crtcs[c];
692	common = name->kind & crtc->crtc.kind;
693
694	if ((common & name_xid) && name->xid == crtc->crtc.xid)
695	    break;
696	if ((common & name_string) && !strcmp (name->string, crtc->crtc.string))
697	    break;
698	if ((common & name_index) && name->index == crtc->crtc.index)
699	    break;
700	crtc = NULL;
701    }
702    return crtc;
703}
704
705static crtc_t *
706find_crtc_by_xid (RRCrtc crtc)
707{
708    name_t  crtc_name;
709
710    init_name (&crtc_name);
711    set_name_xid (&crtc_name, crtc);
712    return find_crtc (&crtc_name);
713}
714
715static XRRModeInfo *
716find_mode (name_t *name, float refresh)
717{
718    int		m;
719    XRRModeInfo	*best = NULL;
720    float	bestDist = 0;
721
722    for (m = 0; m < res->nmode; m++)
723    {
724	XRRModeInfo *mode = &res->modes[m];
725	if ((name->kind & name_xid) && name->xid == mode->id)
726	{
727	    best = mode;
728	    break;
729	}
730	if ((name->kind & name_string) && !strcmp (name->string, mode->name))
731	{
732	    float   dist;
733
734	    if (refresh)
735		dist = fabs (mode_refresh (mode) - refresh);
736	    else
737		dist = 0;
738	    if (!best || dist < bestDist)
739	    {
740		bestDist = dist;
741		best = mode;
742	    }
743	    break;
744	}
745    }
746    return best;
747}
748
749static XRRModeInfo *
750find_mode_by_xid (RRMode mode)
751{
752    name_t  mode_name;
753
754    init_name (&mode_name);
755    set_name_xid (&mode_name, mode);
756    return find_mode (&mode_name, 0);
757}
758
759#if 0
760static XRRModeInfo *
761find_mode_by_name (char *name)
762{
763    name_t  mode_name;
764    init_name (&mode_name);
765    set_name_string (&mode_name, name);
766    return find_mode (&mode_name, 0);
767}
768#endif
769
770static
771XRRModeInfo *
772find_mode_for_output (output_t *output, name_t *name)
773{
774    XRROutputInfo   *output_info = output->output_info;
775    int		    m;
776    XRRModeInfo	    *best = NULL;
777    float	    bestDist = 0;
778
779    for (m = 0; m < output_info->nmode; m++)
780    {
781	XRRModeInfo	    *mode;
782
783	mode = find_mode_by_xid (output_info->modes[m]);
784	if (!mode) continue;
785	if ((name->kind & name_xid) && name->xid == mode->id)
786	{
787	    best = mode;
788	    break;
789	}
790	if ((name->kind & name_string) && !strcmp (name->string, mode->name))
791	{
792	    float   dist;
793
794	    /* Stay away from doublescan modes unless refresh rate is specified. */
795	    if (!output->refresh && (mode->modeFlags & RR_DoubleScan))
796		continue;
797
798	    if (output->refresh)
799		dist = fabs (mode_refresh (mode) - output->refresh);
800	    else
801		dist = 0;
802	    if (!best || dist < bestDist)
803	    {
804		bestDist = dist;
805		best = mode;
806	    }
807	}
808    }
809    return best;
810}
811
812static XRRModeInfo *
813preferred_mode (output_t *output)
814{
815    XRROutputInfo   *output_info = output->output_info;
816    int		    m;
817    XRRModeInfo	    *best;
818    int		    bestDist;
819
820    best = NULL;
821    bestDist = 0;
822    for (m = 0; m < output_info->nmode; m++)
823    {
824	XRRModeInfo *mode_info = find_mode_by_xid (output_info->modes[m]);
825	int	    dist;
826
827	if (m < output_info->npreferred)
828	    dist = 0;
829	else if (output_info->mm_height)
830	    dist = (1000 * DisplayHeight(dpy, screen) / DisplayHeightMM(dpy, screen) -
831		    1000 * mode_info->height / output_info->mm_height);
832	else
833	    dist = DisplayHeight(dpy, screen) - mode_info->height;
834
835        if (dist < 0) dist = -dist;
836	if (!best || dist < bestDist)
837	{
838	    best = mode_info;
839	    bestDist = dist;
840	}
841    }
842    return best;
843}
844
845static Bool
846output_can_use_crtc (output_t *output, crtc_t *crtc)
847{
848    XRROutputInfo   *output_info = output->output_info;
849    int		    c;
850
851    for (c = 0; c < output_info->ncrtc; c++)
852	if (output_info->crtcs[c] == crtc->crtc.xid)
853	    return True;
854    return False;
855}
856
857static Bool
858output_can_use_mode (output_t *output, XRRModeInfo *mode)
859{
860    XRROutputInfo   *output_info = output->output_info;
861    int		    m;
862
863    for (m = 0; m < output_info->nmode; m++)
864	if (output_info->modes[m] == mode->id)
865	    return True;
866    return False;
867}
868
869static Bool
870crtc_can_use_rotation (crtc_t *crtc, Rotation rotation)
871{
872    Rotation	rotations = crtc->crtc_info->rotations;
873    Rotation	dir = rotation & (RR_Rotate_0|RR_Rotate_90|RR_Rotate_180|RR_Rotate_270);
874    Rotation	reflect = rotation & (RR_Reflect_X|RR_Reflect_Y);
875    if (((rotations & dir) != 0) && ((rotations & reflect) == reflect))
876	return True;
877    return False;
878}
879
880#if 0
881static Bool
882crtc_can_use_transform (crtc_t *crtc, XTransform *transform)
883{
884    int	major, minor;
885
886    XRRQueryVersion (dpy, &major, &minor);
887    if (major > 1 || (major == 1 && minor >= 3))
888	return True;
889    return False;
890}
891#endif
892
893/*
894 * Report only rotations that are supported by all crtcs
895 */
896static Rotation
897output_rotations (output_t *output)
898{
899    Bool	    found = False;
900    Rotation	    rotation = RR_Rotate_0;
901    XRROutputInfo   *output_info = output->output_info;
902    int		    c;
903
904    for (c = 0; c < output_info->ncrtc; c++)
905    {
906	crtc_t	*crtc = find_crtc_by_xid (output_info->crtcs[c]);
907	if (crtc)
908	{
909	    if (!found) {
910		rotation = crtc->crtc_info->rotations;
911		found = True;
912	    } else
913		rotation &= crtc->crtc_info->rotations;
914	}
915    }
916    return rotation;
917}
918
919static Bool
920output_can_use_rotation (output_t *output, Rotation rotation)
921{
922    XRROutputInfo   *output_info = output->output_info;
923    int		    c;
924
925    /* make sure all of the crtcs can use this rotation.
926     * yes, this is not strictly necessary, but it is
927     * simpler,and we expect most drivers to either
928     * support rotation everywhere or nowhere
929     */
930    for (c = 0; c < output_info->ncrtc; c++)
931    {
932	crtc_t	*crtc = find_crtc_by_xid (output_info->crtcs[c]);
933	if (crtc && !crtc_can_use_rotation (crtc, rotation))
934	    return False;
935    }
936    return True;
937}
938
939static Bool
940output_is_primary(output_t *output)
941{
942    if (has_1_3)
943	    return XRRGetOutputPrimary(dpy, root) == output->output.xid;
944    return False;
945}
946
947static void
948set_output_info (output_t *output, RROutput xid, XRROutputInfo *output_info)
949{
950    /* sanity check output info */
951    if (output_info->connection != RR_Disconnected && !output_info->nmode)
952	warning ("Output %s is not disconnected but has no modes\n",
953		 output_info->name);
954
955    /* set output name and info */
956    if (!(output->output.kind & name_xid))
957	set_name_xid (&output->output, xid);
958    if (!(output->output.kind & name_string))
959	set_name_string (&output->output, output_info->name);
960    output->output_info = output_info;
961
962    /* set crtc name and info */
963    if (!(output->changes & changes_crtc))
964	set_name_xid (&output->crtc, output_info->crtc);
965
966    if (output->crtc.kind == name_xid && output->crtc.xid == None)
967	output->crtc_info = NULL;
968    else
969    {
970	output->crtc_info = find_crtc (&output->crtc);
971	if (!output->crtc_info)
972	{
973	    if (output->crtc.kind & name_xid)
974		fatal ("cannot find crtc 0x%x\n", output->crtc.xid);
975	    if (output->crtc.kind & name_index)
976		fatal ("cannot find crtc %d\n", output->crtc.index);
977	}
978	if (!output_can_use_crtc (output, output->crtc_info))
979	    fatal ("output %s cannot use crtc 0x%x\n", output->output.string,
980		   output->crtc_info->crtc.xid);
981    }
982
983    /* set mode name and info */
984    if (!(output->changes & changes_mode))
985    {
986	if (output->crtc_info)
987	    set_name_xid (&output->mode, output->crtc_info->crtc_info->mode);
988	else
989	    set_name_xid (&output->mode, None);
990	if (output->mode.xid)
991	{
992	    output->mode_info = find_mode_by_xid (output->mode.xid);
993	    if (!output->mode_info)
994		fatal ("server did not report mode 0x%x for output %s\n",
995		       output->mode.xid, output->output.string);
996	}
997	else
998	    output->mode_info = NULL;
999    }
1000    else if (output->mode.kind == name_xid && output->mode.xid == None)
1001	output->mode_info = NULL;
1002    else
1003    {
1004	if (output->mode.kind == name_preferred)
1005	    output->mode_info = preferred_mode (output);
1006	else
1007	    output->mode_info = find_mode_for_output (output, &output->mode);
1008	if (!output->mode_info)
1009	{
1010	    if (output->mode.kind & name_preferred)
1011		fatal ("cannot find preferred mode\n");
1012	    if (output->mode.kind & name_string)
1013		fatal ("cannot find mode %s\n", output->mode.string);
1014	    if (output->mode.kind & name_xid)
1015		fatal ("cannot find mode 0x%x\n", output->mode.xid);
1016	}
1017	if (!output_can_use_mode (output, output->mode_info))
1018	    fatal ("output %s cannot use mode %s\n", output->output.string,
1019		   output->mode_info->name);
1020    }
1021
1022    /* set position */
1023    if (!(output->changes & changes_position))
1024    {
1025	if (output->crtc_info)
1026	{
1027	    output->x = output->crtc_info->crtc_info->x;
1028	    output->y = output->crtc_info->crtc_info->y;
1029	}
1030	else
1031	{
1032	    output->x = 0;
1033	    output->y = 0;
1034	}
1035    }
1036
1037    /* set rotation */
1038    if (!(output->changes & changes_rotation))
1039    {
1040	output->rotation &= ~0xf;
1041	if (output->crtc_info)
1042	    output->rotation |= (output->crtc_info->crtc_info->rotation & 0xf);
1043	else
1044	    output->rotation = RR_Rotate_0;
1045    }
1046    if (!(output->changes & changes_reflection))
1047    {
1048	output->rotation &= ~(RR_Reflect_X|RR_Reflect_Y);
1049	if (output->crtc_info)
1050	    output->rotation |= (output->crtc_info->crtc_info->rotation &
1051				 (RR_Reflect_X|RR_Reflect_Y));
1052    }
1053    if (!output_can_use_rotation (output, output->rotation))
1054	fatal ("output %s cannot use rotation \"%s\" reflection \"%s\"\n",
1055	       output->output.string,
1056	       rotation_name (output->rotation),
1057	       reflection_name (output->rotation));
1058
1059    /* set transformation */
1060    if (!(output->changes & changes_transform))
1061    {
1062	if (output->crtc_info)
1063	    copy_transform (&output->transform, &output->crtc_info->current_transform);
1064	else
1065	    init_transform (&output->transform);
1066    }
1067
1068    /* set primary */
1069    if (!(output->changes & changes_primary))
1070	output->primary = output_is_primary(output);
1071}
1072
1073static void
1074get_screen (Bool current)
1075{
1076    if (!has_1_2)
1077        fatal ("Server RandR version before 1.2\n");
1078
1079    XRRGetScreenSizeRange (dpy, root, &minWidth, &minHeight,
1080			   &maxWidth, &maxHeight);
1081
1082    if (current)
1083	res = XRRGetScreenResourcesCurrent (dpy, root);
1084    else
1085	res = XRRGetScreenResources (dpy, root);
1086    if (!res) fatal ("could not get screen resources");
1087}
1088
1089static void
1090get_crtcs (void)
1091{
1092    int		c;
1093
1094    num_crtcs = res->ncrtc;
1095    crtcs = calloc (num_crtcs, sizeof (crtc_t));
1096    if (!crtcs) fatal ("out of memory\n");
1097
1098    for (c = 0; c < res->ncrtc; c++)
1099    {
1100	XRRCrtcInfo *crtc_info = XRRGetCrtcInfo (dpy, res, res->crtcs[c]);
1101#if RANDR_MAJOR > 1 || RANDR_MINOR >= 3
1102	XRRCrtcTransformAttributes  *attr;
1103#endif
1104	XRRPanning  *panning_info = NULL;
1105
1106	if (has_1_3) {
1107	    XRRPanning zero;
1108	    memset(&zero, 0, sizeof(zero));
1109	    panning_info = XRRGetPanning  (dpy, res, res->crtcs[c]);
1110	    zero.timestamp = panning_info->timestamp;
1111	    if (!memcmp(panning_info, &zero, sizeof(zero))) {
1112		Xfree(panning_info);
1113		panning_info = NULL;
1114	    }
1115	}
1116
1117	set_name_xid (&crtcs[c].crtc, res->crtcs[c]);
1118	set_name_index (&crtcs[c].crtc, c);
1119	if (!crtc_info) fatal ("could not get crtc 0x%x information\n", res->crtcs[c]);
1120	crtcs[c].crtc_info = crtc_info;
1121	crtcs[c].panning_info = panning_info;
1122	if (crtc_info->mode == None)
1123	{
1124	    crtcs[c].mode_info = NULL;
1125	    crtcs[c].x = 0;
1126	    crtcs[c].y = 0;
1127	    crtcs[c].rotation = RR_Rotate_0;
1128	}
1129#if RANDR_MAJOR > 1 || RANDR_MINOR >= 3
1130	if (XRRGetCrtcTransform (dpy, res->crtcs[c], &attr) && attr) {
1131	    set_transform (&crtcs[c].current_transform,
1132			   &attr->currentTransform,
1133			   attr->currentFilter,
1134			   attr->currentParams,
1135			   attr->currentNparams);
1136	    XFree (attr);
1137	}
1138	else
1139#endif
1140	{
1141	    init_transform (&crtcs[c].current_transform);
1142	}
1143	copy_transform (&crtcs[c].pending_transform, &crtcs[c].current_transform);
1144   }
1145}
1146
1147static void
1148crtc_add_output (crtc_t *crtc, output_t *output)
1149{
1150    if (crtc->outputs)
1151	crtc->outputs = realloc (crtc->outputs, (crtc->noutput + 1) * sizeof (output_t *));
1152    else
1153    {
1154	crtc->outputs = malloc (sizeof (output_t *));
1155	crtc->x = output->x;
1156	crtc->y = output->y;
1157	crtc->rotation = output->rotation;
1158	crtc->mode_info = output->mode_info;
1159	copy_transform (&crtc->pending_transform, &output->transform);
1160   }
1161    if (!crtc->outputs) fatal ("out of memory\n");
1162    crtc->outputs[crtc->noutput++] = output;
1163}
1164
1165static void
1166set_crtcs (void)
1167{
1168    output_t	*output;
1169
1170    for (output = outputs; output; output = output->next)
1171    {
1172	if (!output->mode_info) continue;
1173	crtc_add_output (output->crtc_info, output);
1174    }
1175}
1176
1177static void
1178set_panning (void)
1179{
1180    output_t	*output;
1181
1182    for (output = outputs; output; output = output->next)
1183    {
1184	if (! output->crtc_info)
1185	    continue;
1186	if (! (output->changes & changes_panning))
1187	    continue;
1188	if (! output->crtc_info->panning_info)
1189	    output->crtc_info->panning_info = malloc (sizeof(XRRPanning));
1190	memcpy (output->crtc_info->panning_info, &output->panning, sizeof(XRRPanning));
1191	output->crtc_info->changing = 1;
1192    }
1193}
1194
1195static void
1196set_gamma(void)
1197{
1198    output_t	*output;
1199
1200    for (output = outputs; output; output = output->next) {
1201	int i, size;
1202	crtc_t *crtc;
1203	XRRCrtcGamma *gamma;
1204
1205	if (!(output->changes & changes_gamma))
1206	    continue;
1207
1208	if (!output->crtc_info) {
1209	    fatal("Need crtc to set gamma on.\n");
1210	    continue;
1211	}
1212
1213	crtc = output->crtc_info;
1214
1215	size = XRRGetCrtcGammaSize(dpy, crtc->crtc.xid);
1216
1217	if (!size) {
1218	    fatal("Gamma size is 0.\n");
1219	    continue;
1220	}
1221
1222	gamma = XRRAllocGamma(size);
1223	if (!gamma) {
1224	    fatal("Gamma allocation failed.\n");
1225	    continue;
1226	}
1227
1228	for (i = 0; i < size; i++) {
1229	    if (output->gamma.red == 1.0)
1230		gamma->red[i] = i << 8;
1231	    else
1232		gamma->red[i] = (CARD16)(pow((double)i/(double)(size - 1),
1233			    (double)output->gamma.red) * (double)(size - 1) * 256);
1234
1235	    if (output->gamma.green == 1.0)
1236		gamma->green[i] = i << 8;
1237	    else
1238		gamma->green[i] = (CARD16)(pow((double)i/(double)(size - 1),
1239			    (double)output->gamma.green) * (double)(size - 1) * 256);
1240
1241	    if (output->gamma.blue == 1.0)
1242		gamma->blue[i] = i << 8;
1243	    else
1244		gamma->blue[i] = (CARD16)(pow((double)i/(double)(size - 1),
1245			    (double)output->gamma.blue) * (double)(size - 1) * 256);
1246	}
1247
1248	XRRSetCrtcGamma(dpy, crtc->crtc.xid, gamma);
1249
1250	free(gamma);
1251    }
1252}
1253
1254static void
1255set_primary(void)
1256{
1257    output_t *output;
1258
1259    if (no_primary) {
1260	XRRSetOutputPrimary(dpy, root, None);
1261    } else {
1262	for (output = outputs; output; output = output->next) {
1263	    if (!(output->changes & changes_primary))
1264		continue;
1265	    if (output->primary)
1266		XRRSetOutputPrimary(dpy, root, output->output.xid);
1267	}
1268    }
1269}
1270
1271static Status
1272crtc_disable (crtc_t *crtc)
1273{
1274    if (verbose)
1275    	printf ("crtc %d: disable\n", crtc->crtc.index);
1276
1277    if (dryrun)
1278	return RRSetConfigSuccess;
1279    return XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
1280			     0, 0, None, RR_Rotate_0, NULL, 0);
1281}
1282
1283static void
1284crtc_set_transform (crtc_t *crtc, transform_t *transform)
1285{
1286    int	major, minor;
1287
1288    XRRQueryVersion (dpy, &major, &minor);
1289    if (major > 1 || (major == 1 && minor >= 3))
1290	XRRSetCrtcTransform (dpy, crtc->crtc.xid,
1291			     &transform->transform,
1292			     transform->filter,
1293			     transform->params,
1294			     transform->nparams);
1295}
1296
1297static Status
1298crtc_revert (crtc_t *crtc)
1299{
1300    XRRCrtcInfo	*crtc_info = crtc->crtc_info;
1301
1302    if (verbose)
1303    	printf ("crtc %d: revert\n", crtc->crtc.index);
1304
1305    if (dryrun)
1306	return RRSetConfigSuccess;
1307
1308    if (!equal_transform (&crtc->current_transform, &crtc->pending_transform))
1309	crtc_set_transform (crtc, &crtc->current_transform);
1310    return XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
1311			    crtc_info->x, crtc_info->y,
1312			    crtc_info->mode, crtc_info->rotation,
1313			    crtc_info->outputs, crtc_info->noutput);
1314}
1315
1316static Status
1317crtc_apply (crtc_t *crtc)
1318{
1319    RROutput	*rr_outputs;
1320    int		o;
1321    Status	s;
1322    RRMode	mode = None;
1323
1324    if (!crtc->changing || !crtc->mode_info)
1325	return RRSetConfigSuccess;
1326
1327    rr_outputs = calloc (crtc->noutput, sizeof (RROutput));
1328    if (!rr_outputs)
1329	return BadAlloc;
1330    for (o = 0; o < crtc->noutput; o++)
1331	rr_outputs[o] = crtc->outputs[o]->output.xid;
1332    mode = crtc->mode_info->id;
1333    if (verbose) {
1334	printf ("crtc %d: %12s %6.1f +%d+%d", crtc->crtc.index,
1335		crtc->mode_info->name, mode_refresh (crtc->mode_info),
1336		crtc->x, crtc->y);
1337	for (o = 0; o < crtc->noutput; o++)
1338	    printf (" \"%s\"", crtc->outputs[o]->output.string);
1339	printf ("\n");
1340    }
1341
1342    if (dryrun)
1343	s = RRSetConfigSuccess;
1344    else
1345    {
1346	if (!equal_transform (&crtc->current_transform, &crtc->pending_transform))
1347	    crtc_set_transform (crtc, &crtc->pending_transform);
1348	s = XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
1349			      crtc->x, crtc->y, mode, crtc->rotation,
1350			      rr_outputs, crtc->noutput);
1351	if (s == RRSetConfigSuccess && crtc->panning_info) {
1352	    if (has_1_3)
1353		s = XRRSetPanning (dpy, res, crtc->crtc.xid, crtc->panning_info);
1354	    else
1355		fatal ("panning needs RandR 1.3\n");
1356	}
1357    }
1358    free (rr_outputs);
1359    return s;
1360}
1361
1362static void
1363screen_revert (void)
1364{
1365    if (verbose)
1366	printf ("screen %d: revert\n", screen);
1367
1368    if (dryrun)
1369	return;
1370    XRRSetScreenSize (dpy, root,
1371		      DisplayWidth (dpy, screen),
1372		      DisplayHeight (dpy, screen),
1373		      DisplayWidthMM (dpy, screen),
1374		      DisplayHeightMM (dpy, screen));
1375}
1376
1377static void
1378screen_apply (void)
1379{
1380    if (fb_width == DisplayWidth (dpy, screen) &&
1381	fb_height == DisplayHeight (dpy, screen) &&
1382	fb_width_mm == DisplayWidthMM (dpy, screen) &&
1383	fb_height_mm == DisplayHeightMM (dpy, screen))
1384    {
1385	return;
1386    }
1387    if (verbose)
1388	printf ("screen %d: %dx%d %dx%d mm %6.2fdpi\n", screen,
1389		fb_width, fb_height, fb_width_mm, fb_height_mm, dpi);
1390    if (dryrun)
1391	return;
1392    XRRSetScreenSize (dpy, root, fb_width, fb_height,
1393		      fb_width_mm, fb_height_mm);
1394}
1395
1396static void
1397revert (void)
1398{
1399    int	c;
1400
1401    /* first disable all crtcs */
1402    for (c = 0; c < res->ncrtc; c++)
1403	crtc_disable (&crtcs[c]);
1404    /* next reset screen size */
1405    screen_revert ();
1406    /* now restore all crtcs */
1407    for (c = 0; c < res->ncrtc; c++)
1408	crtc_revert (&crtcs[c]);
1409}
1410
1411/*
1412 * uh-oh, something bad happened in the middle of changing
1413 * the configuration. Revert to the previous configuration
1414 * and bail
1415 */
1416static void
1417panic (Status s, crtc_t *crtc)
1418{
1419    int	    c = crtc->crtc.index;
1420    char    *message;
1421
1422    switch (s) {
1423    case RRSetConfigSuccess:		message = "succeeded";		    break;
1424    case BadAlloc:			message = "out of memory";	    break;
1425    case RRSetConfigFailed:		message = "failed";		    break;
1426    case RRSetConfigInvalidConfigTime:	message = "invalid config time";    break;
1427    case RRSetConfigInvalidTime:	message = "invalid time";	    break;
1428    default:				message = "unknown failure";	    break;
1429    }
1430
1431    fprintf (stderr, "%s: Configure crtc %d %s\n", program_name, c, message);
1432    revert ();
1433    exit (1);
1434}
1435
1436static void
1437apply (void)
1438{
1439    Status  s;
1440    int	    c;
1441
1442    /*
1443     * Hold the server grabbed while messing with
1444     * the screen so that apps which notice the resize
1445     * event and ask for xinerama information from the server
1446     * receive up-to-date information
1447     */
1448    if (grab_server)
1449	XGrabServer (dpy);
1450
1451    /*
1452     * Turn off any crtcs which are to be disabled or which are
1453     * larger than the target size
1454     */
1455    for (c = 0; c < res->ncrtc; c++)
1456    {
1457	crtc_t	    *crtc = &crtcs[c];
1458	XRRCrtcInfo *crtc_info = crtc->crtc_info;
1459
1460	/* if this crtc is already disabled, skip it */
1461	if (crtc_info->mode == None)
1462	    continue;
1463
1464	/*
1465	 * If this crtc is to be left enabled, make
1466	 * sure the old size fits then new screen
1467	 */
1468	if (crtc->mode_info)
1469	{
1470	    XRRModeInfo	*old_mode = find_mode_by_xid (crtc_info->mode);
1471	    int x, y, w, h;
1472	    box_t bounds;
1473
1474	    if (!old_mode)
1475		panic (RRSetConfigFailed, crtc);
1476
1477	    /* old position and size information */
1478	    mode_geometry (old_mode, crtc_info->rotation,
1479			   &crtc->current_transform.transform,
1480			   &bounds);
1481
1482	    x = crtc_info->x + bounds.x1;
1483	    y = crtc_info->y + bounds.y1;
1484	    w = bounds.x2 - bounds.x1;
1485	    h = bounds.y2 - bounds.y1;
1486
1487	    /* if it fits, skip it */
1488	    if (x + w <= fb_width && y + h <= fb_height)
1489		continue;
1490	    crtc->changing = True;
1491	}
1492	s = crtc_disable (crtc);
1493	if (s != RRSetConfigSuccess)
1494	    panic (s, crtc);
1495    }
1496
1497    /*
1498     * Set the screen size
1499     */
1500    screen_apply ();
1501
1502    /*
1503     * Set crtcs
1504     */
1505
1506    for (c = 0; c < res->ncrtc; c++)
1507    {
1508	crtc_t	*crtc = &crtcs[c];
1509
1510	s = crtc_apply (crtc);
1511	if (s != RRSetConfigSuccess)
1512	    panic (s, crtc);
1513    }
1514
1515    set_primary ();
1516
1517    /*
1518     * Release the server grab and let all clients
1519     * respond to the updated state
1520     */
1521    if (grab_server)
1522	XUngrabServer (dpy);
1523}
1524
1525/*
1526 * Use current output state to complete the output list
1527 */
1528static void
1529get_outputs (void)
1530{
1531    int		o;
1532
1533    for (o = 0; o < res->noutput; o++)
1534    {
1535	XRROutputInfo	*output_info = XRRGetOutputInfo (dpy, res, res->outputs[o]);
1536	output_t	*output;
1537	name_t		output_name;
1538	if (!output_info) fatal ("could not get output 0x%x information\n", res->outputs[o]);
1539	set_name_xid (&output_name, res->outputs[o]);
1540	set_name_index (&output_name, o);
1541	set_name_string (&output_name, output_info->name);
1542	output = find_output (&output_name);
1543	if (!output)
1544	{
1545	    output = add_output ();
1546	    set_name_all (&output->output, &output_name);
1547	    /*
1548	     * When global --automatic mode is set, turn on connected but off
1549	     * outputs, turn off disconnected but on outputs
1550	     */
1551	    if (automatic)
1552	    {
1553		switch (output_info->connection) {
1554		case RR_Connected:
1555		    if (!output_info->crtc) {
1556			output->changes |= changes_automatic;
1557			output->automatic = True;
1558		    }
1559		    break;
1560		case RR_Disconnected:
1561		    if (output_info->crtc)
1562		    {
1563			output->changes |= changes_automatic;
1564			output->automatic = True;
1565		    }
1566		    break;
1567		}
1568	    }
1569	}
1570
1571	/*
1572	 * Automatic mode -- track connection state and enable/disable outputs
1573	 * as necessary
1574	 */
1575	if (output->automatic)
1576	{
1577	    switch (output_info->connection) {
1578	    case RR_Connected:
1579	    case RR_UnknownConnection:
1580		if ((!(output->changes & changes_mode)))
1581		{
1582		    set_name_preferred (&output->mode);
1583		    output->changes |= changes_mode;
1584		}
1585		break;
1586	    case RR_Disconnected:
1587		if ((!(output->changes & changes_mode)))
1588		{
1589		    set_name_xid (&output->mode, None);
1590		    set_name_xid (&output->crtc, None);
1591		    output->changes |= changes_mode;
1592		    output->changes |= changes_crtc;
1593		}
1594		break;
1595	    }
1596	}
1597
1598	set_output_info (output, res->outputs[o], output_info);
1599    }
1600}
1601
1602static void
1603mark_changing_crtcs (void)
1604{
1605    int	c;
1606
1607    for (c = 0; c < num_crtcs; c++)
1608    {
1609	crtc_t	    *crtc = &crtcs[c];
1610	int	    o;
1611	output_t    *output;
1612
1613	/* walk old output list (to catch disables) */
1614	for (o = 0; o < crtc->crtc_info->noutput; o++)
1615	{
1616	    output = find_output_by_xid (crtc->crtc_info->outputs[o]);
1617	    if (!output) fatal ("cannot find output 0x%x\n",
1618				crtc->crtc_info->outputs[o]);
1619	    if (output->changes)
1620		crtc->changing = True;
1621	}
1622	/* walk new output list */
1623	for (o = 0; o < crtc->noutput; o++)
1624	{
1625	    output = crtc->outputs[o];
1626	    if (output->changes)
1627		crtc->changing = True;
1628	}
1629    }
1630}
1631
1632/*
1633 * Test whether 'crtc' can be used for 'output'
1634 */
1635static Bool
1636check_crtc_for_output (crtc_t *crtc, output_t *output)
1637{
1638    int		c;
1639    int		l;
1640    output_t    *other;
1641
1642    for (c = 0; c < output->output_info->ncrtc; c++)
1643	if (output->output_info->crtcs[c] == crtc->crtc.xid)
1644	    break;
1645    if (c == output->output_info->ncrtc)
1646	return False;
1647    for (other = outputs; other; other = other->next)
1648    {
1649	if (other == output)
1650	    continue;
1651
1652	if (other->mode_info == NULL)
1653	    continue;
1654
1655	if (other->crtc_info != crtc)
1656	    continue;
1657
1658	/* see if the output connected to the crtc can clone to this output */
1659	for (l = 0; l < output->output_info->nclone; l++)
1660	    if (output->output_info->clones[l] == other->output.xid)
1661		break;
1662	/* not on the list, can't clone */
1663	if (l == output->output_info->nclone)
1664	    return False;
1665    }
1666
1667    if (crtc->noutput)
1668    {
1669	/* make sure the state matches */
1670	if (crtc->mode_info != output->mode_info)
1671	    return False;
1672	if (crtc->x != output->x)
1673	    return False;
1674	if (crtc->y != output->y)
1675	    return False;
1676	if (crtc->rotation != output->rotation)
1677	    return False;
1678	if (!equal_transform (&crtc->current_transform, &output->transform))
1679	    return False;
1680    }
1681    else if (crtc->crtc_info->noutput)
1682    {
1683	/* make sure the state matches the already used state */
1684	XRRModeInfo *mode = find_mode_by_xid (crtc->crtc_info->mode);
1685
1686	if (mode != output->mode_info)
1687	    return False;
1688	if (crtc->crtc_info->x != output->x)
1689	    return False;
1690	if (crtc->crtc_info->y != output->y)
1691	    return False;
1692	if (crtc->crtc_info->rotation != output->rotation)
1693	    return False;
1694    }
1695    return True;
1696}
1697
1698static crtc_t *
1699find_crtc_for_output (output_t *output)
1700{
1701    int	    c;
1702
1703    for (c = 0; c < output->output_info->ncrtc; c++)
1704    {
1705	crtc_t	    *crtc;
1706
1707	crtc = find_crtc_by_xid (output->output_info->crtcs[c]);
1708	if (!crtc) fatal ("cannot find crtc 0x%x\n", output->output_info->crtcs[c]);
1709
1710	if (check_crtc_for_output (crtc, output))
1711	    return crtc;
1712    }
1713    return NULL;
1714}
1715
1716static void
1717set_positions (void)
1718{
1719    output_t	*output;
1720    Bool	keep_going;
1721    Bool	any_set;
1722    int		min_x, min_y;
1723
1724    for (;;)
1725    {
1726	any_set = False;
1727	keep_going = False;
1728	for (output = outputs; output; output = output->next)
1729	{
1730	    output_t    *relation;
1731	    name_t	relation_name;
1732
1733	    if (!(output->changes & changes_relation)) continue;
1734
1735	    if (output->mode_info == NULL) continue;
1736
1737	    init_name (&relation_name);
1738	    set_name_string (&relation_name, output->relative_to);
1739	    relation = find_output (&relation_name);
1740	    if (!relation) fatal ("cannot find output \"%s\"\n", output->relative_to);
1741
1742	    if (relation->mode_info == NULL)
1743	    {
1744		output->x = 0;
1745		output->y = 0;
1746		output->changes |= changes_position;
1747		any_set = True;
1748		continue;
1749	    }
1750	    /*
1751	     * Make sure the dependent object has been set in place
1752	     */
1753	    if ((relation->changes & changes_relation) &&
1754		!(relation->changes & changes_position))
1755	    {
1756		keep_going = True;
1757		continue;
1758	    }
1759
1760	    switch (output->relation) {
1761	    case left_of:
1762		output->y = relation->y;
1763		output->x = relation->x - mode_width (output->mode_info, output->rotation);
1764		break;
1765	    case right_of:
1766		output->y = relation->y;
1767		output->x = relation->x + mode_width (relation->mode_info, relation->rotation);
1768		break;
1769	    case above:
1770		output->x = relation->x;
1771		output->y = relation->y - mode_height (output->mode_info, output->rotation);
1772		break;
1773	    case below:
1774		output->x = relation->x;
1775		output->y = relation->y + mode_height (relation->mode_info, relation->rotation);
1776		break;
1777	    case same_as:
1778		output->x = relation->x;
1779		output->y = relation->y;
1780	    }
1781	    output->changes |= changes_position;
1782	    any_set = True;
1783	}
1784	if (!keep_going)
1785	    break;
1786	if (!any_set)
1787	    fatal ("loop in relative position specifications\n");
1788    }
1789
1790    /*
1791     * Now normalize positions so the upper left corner of all outputs is at 0,0
1792     */
1793    min_x = 32768;
1794    min_y = 32768;
1795    for (output = outputs; output; output = output->next)
1796    {
1797	if (output->mode_info == NULL) continue;
1798
1799	if (output->x < min_x) min_x = output->x;
1800	if (output->y < min_y) min_y = output->y;
1801    }
1802    if (min_x || min_y)
1803    {
1804	/* move all outputs */
1805	for (output = outputs; output; output = output->next)
1806	{
1807	    if (output->mode_info == NULL) continue;
1808
1809	    output->x -= min_x;
1810	    output->y -= min_y;
1811	    output->changes |= changes_position;
1812	}
1813    }
1814}
1815
1816static void
1817set_screen_size (void)
1818{
1819    output_t	*output;
1820    Bool	fb_specified = fb_width != 0 && fb_height != 0;
1821
1822    for (output = outputs; output; output = output->next)
1823    {
1824	XRRModeInfo *mode_info = output->mode_info;
1825	int	    x, y, w, h;
1826	box_t	    bounds;
1827
1828	if (!mode_info) continue;
1829
1830	mode_geometry (mode_info, output->rotation,
1831		       &output->transform.transform,
1832		       &bounds);
1833	x = output->x + bounds.x1;
1834	y = output->y + bounds.y1;
1835	w = bounds.x2 - bounds.x1;
1836	h = bounds.y2 - bounds.y1;
1837	/* make sure output fits in specified size */
1838	if (fb_specified)
1839	{
1840	    if (x + w > fb_width || y + h > fb_height)
1841		warning ("specified screen %dx%d not large enough for output %s (%dx%d+%d+%d)\n",
1842			 fb_width, fb_height, output->output.string, w, h, x, y);
1843	}
1844	/* fit fb to output */
1845	else
1846	{
1847	    XRRPanning *pan;
1848	    if (x + w > fb_width)
1849		fb_width = x + w;
1850	    if (y + h > fb_height)
1851		fb_height = y + h;
1852	    if (output->changes & changes_panning)
1853		pan = &output->panning;
1854	    else
1855		pan = output->crtc_info ? output->crtc_info->panning_info : NULL;
1856	    if (pan && pan->left + pan->width > fb_width)
1857		fb_width = pan->left + pan->width;
1858	    if (pan && pan->top + pan->height > fb_height)
1859		fb_height = pan->top + pan->height;
1860	}
1861    }
1862
1863    if (fb_width > maxWidth || fb_height > maxHeight)
1864        fatal ("screen cannot be larger than %dx%d (desired size %dx%d)\n",
1865	       maxWidth, maxHeight, fb_width, fb_height);
1866    if (fb_specified)
1867    {
1868	if (fb_width < minWidth || fb_height < minHeight)
1869	    fatal ("screen must be at least %dx%d\n", minWidth, minHeight);
1870    }
1871    else
1872    {
1873	if (fb_width < minWidth) fb_width = minWidth;
1874	if (fb_height < minHeight) fb_height = minHeight;
1875    }
1876}
1877
1878#endif
1879
1880static void
1881disable_outputs (output_t *outputs)
1882{
1883    while (outputs)
1884    {
1885	outputs->crtc_info = NULL;
1886	outputs = outputs->next;
1887    }
1888}
1889
1890/*
1891 * find the best mapping from output to crtc available
1892 */
1893static int
1894pick_crtcs_score (output_t *outputs)
1895{
1896    output_t	*output;
1897    int		best_score;
1898    int		my_score;
1899    int		score;
1900    crtc_t	*best_crtc;
1901    int		c;
1902
1903    if (!outputs)
1904	return 0;
1905
1906    output = outputs;
1907    outputs = outputs->next;
1908    /*
1909     * Score with this output disabled
1910     */
1911    output->crtc_info = NULL;
1912    best_score = pick_crtcs_score (outputs);
1913    if (output->mode_info == NULL)
1914	return best_score;
1915
1916    best_crtc = NULL;
1917    /*
1918     * Now score with this output any valid crtc
1919     */
1920    for (c = 0; c < output->output_info->ncrtc; c++)
1921    {
1922	crtc_t	    *crtc;
1923
1924	crtc = find_crtc_by_xid (output->output_info->crtcs[c]);
1925	if (!crtc)
1926	    fatal ("cannot find crtc 0x%x\n", output->output_info->crtcs[c]);
1927
1928	/* reset crtc allocation for following outputs */
1929	disable_outputs (outputs);
1930	if (!check_crtc_for_output (crtc, output))
1931	    continue;
1932
1933	my_score = 1000;
1934	/* slight preference for existing connections */
1935	if (crtc == output->current_crtc_info)
1936	    my_score++;
1937
1938	output->crtc_info = crtc;
1939	score = my_score + pick_crtcs_score (outputs);
1940	if (score > best_score)
1941	{
1942	    best_crtc = crtc;
1943	    best_score = score;
1944	}
1945    }
1946    if (output->crtc_info != best_crtc)
1947	output->crtc_info = best_crtc;
1948    /*
1949     * Reset other outputs based on this one using the best crtc
1950     */
1951    (void) pick_crtcs_score (outputs);
1952
1953    return best_score;
1954}
1955
1956/*
1957 * Pick crtcs for any changing outputs that don't have one
1958 */
1959static void
1960pick_crtcs (void)
1961{
1962    output_t	*output;
1963
1964    /*
1965     * First try to match up newly enabled outputs with spare crtcs
1966     */
1967    for (output = outputs; output; output = output->next)
1968    {
1969	if (output->changes && output->mode_info)
1970	{
1971	    if (output->crtc_info) {
1972		if (output->crtc_info->crtc_info->noutput > 0 &&
1973		    (output->crtc_info->crtc_info->noutput > 1 ||
1974		     output != find_output_by_xid (output->crtc_info->crtc_info->outputs[0])))
1975		    break;
1976	    } else {
1977		output->crtc_info = find_crtc_for_output (output);
1978		if (!output->crtc_info)
1979		    break;
1980	    }
1981	}
1982    }
1983    /*
1984     * Everyone is happy
1985     */
1986    if (!output)
1987	return;
1988    /*
1989     * When the simple way fails, see if there is a way
1990     * to swap crtcs around and make things work
1991     */
1992    for (output = outputs; output; output = output->next)
1993	output->current_crtc_info = output->crtc_info;
1994    pick_crtcs_score (outputs);
1995    for (output = outputs; output; output = output->next)
1996    {
1997	if (output->mode_info && !output->crtc_info)
1998	    fatal ("cannot find crtc for output %s\n", output->output.string);
1999	if (!output->changes && output->crtc_info != output->current_crtc_info)
2000	    output->changes |= changes_crtc;
2001    }
2002}
2003
2004int
2005main (int argc, char **argv)
2006{
2007    XRRScreenSize *sizes;
2008    XRRScreenConfiguration *sc;
2009    int		nsize;
2010    int		nrate;
2011    short		*rates;
2012    Status	status = RRSetConfigFailed;
2013    int		rot = -1;
2014    int		query = 0;
2015    Rotation	rotation, current_rotation, rotations;
2016    XEvent	event;
2017    XRRScreenChangeNotifyEvent *sce;
2018    char          *display_name = NULL;
2019    int 		i, j;
2020    SizeID	current_size;
2021    short	current_rate;
2022    float    	rate = -1;
2023    int		size = -1;
2024    int		dirind = 0;
2025    Bool	setit = False;
2026    Bool    	version = False;
2027    int		event_base, error_base;
2028    int		reflection = 0;
2029    int		width = 0, height = 0;
2030    Bool    	have_pixel_size = False;
2031    int		ret = 0;
2032#if HAS_RANDR_1_2
2033    output_t	*output = NULL;
2034    policy_t	policy = clone;
2035    Bool    	setit_1_2 = False;
2036    Bool    	query_1_2 = False;
2037    Bool	modeit = False;
2038    Bool	propit = False;
2039    Bool	query_1 = False;
2040    int		major, minor;
2041    Bool	current = False;
2042#endif
2043
2044    program_name = argv[0];
2045    if (argc == 1) query = True;
2046    for (i = 1; i < argc; i++) {
2047	if (!strcmp ("-display", argv[i]) || !strcmp ("-d", argv[i])) {
2048	    if (++i>=argc) usage ();
2049	    display_name = argv[i];
2050	    continue;
2051	}
2052	if (!strcmp("-help", argv[i])) {
2053	    usage();
2054	    continue;
2055	}
2056	if (!strcmp ("--verbose", argv[i])) {
2057	    verbose = True;
2058	    continue;
2059	}
2060	if (!strcmp ("--dryrun", argv[i])) {
2061	    dryrun = True;
2062	    verbose = True;
2063	    continue;
2064	}
2065	if (!strcmp ("--nograb", argv[i])) {
2066	    grab_server = False;
2067	    continue;
2068	}
2069	if (!strcmp("--current", argv[i])) {
2070	    current = True;
2071	    /* if --current was the only arg, then query */
2072	    if (argc == 2) query = True;
2073	    continue;
2074	}
2075
2076	if (!strcmp ("-s", argv[i]) || !strcmp ("--size", argv[i])) {
2077	    if (++i>=argc) usage ();
2078	    if (sscanf (argv[i], "%dx%d", &width, &height) == 2)
2079		have_pixel_size = True;
2080	    else {
2081		size = atoi (argv[i]);
2082		if (size < 0) usage();
2083	    }
2084	    setit = True;
2085	    continue;
2086	}
2087
2088	if (!strcmp ("-r", argv[i]) ||
2089	    !strcmp ("--rate", argv[i]) ||
2090	    !strcmp ("--refresh", argv[i]))
2091	{
2092	    if (++i>=argc) usage ();
2093	    if (sscanf (argv[i], "%f", &rate) != 1)
2094		usage ();
2095	    setit = True;
2096#if HAS_RANDR_1_2
2097	    if (output)
2098	    {
2099		output->refresh = rate;
2100		output->changes |= changes_refresh;
2101		setit_1_2 = True;
2102	    }
2103#endif
2104	    continue;
2105	}
2106
2107	if (!strcmp ("-v", argv[i]) || !strcmp ("--version", argv[i])) {
2108	    version = True;
2109	    continue;
2110	}
2111
2112	if (!strcmp ("-x", argv[i])) {
2113	    reflection |= RR_Reflect_X;
2114	    setit = True;
2115	    continue;
2116	}
2117	if (!strcmp ("-y", argv[i])) {
2118	    reflection |= RR_Reflect_Y;
2119	    setit = True;
2120	    continue;
2121	}
2122	if (!strcmp ("--screen", argv[i])) {
2123	    if (++i>=argc) usage ();
2124	    screen = atoi (argv[i]);
2125	    if (screen < 0) usage();
2126	    continue;
2127	}
2128	if (!strcmp ("-q", argv[i]) || !strcmp ("--query", argv[i])) {
2129	    query = True;
2130	    continue;
2131	}
2132	if (!strcmp ("-o", argv[i]) || !strcmp ("--orientation", argv[i])) {
2133	    char *endptr;
2134	    if (++i>=argc) usage ();
2135	    dirind = strtol(argv[i], &endptr, 0);
2136	    if (*endptr != '\0') {
2137		for (dirind = 0; dirind < 4; dirind++) {
2138		    if (strcmp (direction[dirind], argv[i]) == 0) break;
2139		}
2140		if ((dirind < 0) || (dirind > 3))  usage();
2141	    }
2142	    rot = dirind;
2143	    setit = True;
2144	    continue;
2145	}
2146#if HAS_RANDR_1_2
2147	if (!strcmp ("--prop", argv[i]) ||
2148	    !strcmp ("--props", argv[i]) ||
2149	    !strcmp ("--madprops", argv[i]) ||
2150	    !strcmp ("--properties", argv[i]))
2151	{
2152	    query_1_2 = True;
2153	    properties = True;
2154	    continue;
2155	}
2156	if (!strcmp ("--output", argv[i])) {
2157	    if (++i >= argc) usage();
2158
2159	    output = find_output_by_name (argv[i]);
2160	    if (!output) {
2161		output = add_output ();
2162		set_name (&output->output, argv[i], name_string|name_xid);
2163	    }
2164
2165	    setit_1_2 = True;
2166	    continue;
2167	}
2168	if (!strcmp ("--crtc", argv[i])) {
2169	    if (++i >= argc) usage();
2170	    if (!output) usage();
2171	    set_name (&output->crtc, argv[i], name_xid|name_index);
2172	    output->changes |= changes_crtc;
2173	    continue;
2174	}
2175	if (!strcmp ("--mode", argv[i])) {
2176	    if (++i >= argc) usage();
2177	    if (!output) usage();
2178	    set_name (&output->mode, argv[i], name_string|name_xid);
2179	    output->changes |= changes_mode;
2180	    continue;
2181	}
2182	if (!strcmp ("--preferred", argv[i])) {
2183	    if (!output) usage();
2184	    set_name_preferred (&output->mode);
2185	    output->changes |= changes_mode;
2186	    continue;
2187	}
2188	if (!strcmp ("--pos", argv[i])) {
2189	    if (++i>=argc) usage ();
2190	    if (!output) usage();
2191	    if (sscanf (argv[i], "%dx%d",
2192			&output->x, &output->y) != 2)
2193		usage ();
2194	    output->changes |= changes_position;
2195	    continue;
2196	}
2197	if (!strcmp ("--rotation", argv[i]) || !strcmp ("--rotate", argv[i])) {
2198	    if (++i>=argc) usage ();
2199	    if (!output) usage();
2200	    for (dirind = 0; dirind < 4; dirind++) {
2201		if (strcmp (direction[dirind], argv[i]) == 0) break;
2202	    }
2203	    if (dirind == 4)
2204		usage ();
2205	    output->rotation &= ~0xf;
2206	    output->rotation |= 1 << dirind;
2207	    output->changes |= changes_rotation;
2208	    continue;
2209	}
2210	if (!strcmp ("--reflect", argv[i]) || !strcmp ("--reflection", argv[i])) {
2211	    if (++i>=argc) usage ();
2212	    if (!output) usage();
2213	    for (dirind = 0; dirind < 4; dirind++) {
2214		if (strcmp (reflections[dirind], argv[i]) == 0) break;
2215	    }
2216	    if (dirind == 4)
2217		usage ();
2218	    output->rotation &= ~(RR_Reflect_X|RR_Reflect_Y);
2219	    output->rotation |= dirind * RR_Reflect_X;
2220	    output->changes |= changes_reflection;
2221	    continue;
2222	}
2223	if (!strcmp ("--left-of", argv[i])) {
2224	    if (++i>=argc) usage ();
2225	    if (!output) usage();
2226	    output->relation = left_of;
2227	    output->relative_to = argv[i];
2228	    output->changes |= changes_relation;
2229	    continue;
2230	}
2231	if (!strcmp ("--right-of", argv[i])) {
2232	    if (++i>=argc) usage ();
2233	    if (!output) usage();
2234	    output->relation = right_of;
2235	    output->relative_to = argv[i];
2236	    output->changes |= changes_relation;
2237	    continue;
2238	}
2239	if (!strcmp ("--above", argv[i])) {
2240	    if (++i>=argc) usage ();
2241	    if (!output) usage();
2242	    output->relation = above;
2243	    output->relative_to = argv[i];
2244	    output->changes |= changes_relation;
2245	    continue;
2246	}
2247	if (!strcmp ("--below", argv[i])) {
2248	    if (++i>=argc) usage ();
2249	    if (!output) usage();
2250	    output->relation = below;
2251	    output->relative_to = argv[i];
2252	    output->changes |= changes_relation;
2253	    continue;
2254	}
2255	if (!strcmp ("--same-as", argv[i])) {
2256	    if (++i>=argc) usage ();
2257	    if (!output) usage();
2258	    output->relation = same_as;
2259	    output->relative_to = argv[i];
2260	    output->changes |= changes_relation;
2261	    continue;
2262	}
2263	if (!strcmp ("--panning", argv[i])) {
2264	    XRRPanning *pan;
2265	    if (++i>=argc) usage ();
2266	    if (!output) usage();
2267	    pan = &output->panning;
2268	    switch (sscanf (argv[i], "%dx%d+%d+%d/%dx%d+%d+%d/%d/%d/%d/%d",
2269			    &pan->width, &pan->height, &pan->left, &pan->top,
2270			    &pan->track_width, &pan->track_height,
2271			    &pan->track_left, &pan->track_top,
2272			    &pan->border_left, &pan->border_top,
2273			    &pan->border_right, &pan->border_bottom)) {
2274	    case 2:
2275		pan->left = pan->top = 0;
2276		/* fall through */
2277	    case 4:
2278		pan->track_left = pan->track_top =
2279		    pan->track_width = pan->track_height = 0;
2280		/* fall through */
2281	    case 8:
2282		pan->border_left = pan->border_top =
2283		    pan->border_right = pan->border_bottom = 0;
2284		/* fall through */
2285	    case 12:
2286		break;
2287	    default:
2288		usage ();
2289	    }
2290	    output->changes |= changes_panning;
2291	    continue;
2292	}
2293	if (!strcmp ("--gamma", argv[i])) {
2294	    if (!output) usage();
2295	    if (++i>=argc) usage ();
2296	    if (sscanf(argv[i], "%f:%f:%f", &output->gamma.red,
2297		    &output->gamma.green, &output->gamma.blue) != 3)
2298		usage ();
2299	    output->changes |= changes_gamma;
2300	    setit_1_2 = True;
2301	    continue;
2302	}
2303	if (!strcmp ("--primary", argv[i])) {
2304	    if (!output) usage();
2305	    output->changes |= changes_primary;
2306	    output->primary = True;
2307	    setit_1_2 = True;
2308	    continue;
2309	}
2310	if (!strcmp ("--noprimary", argv[i])) {
2311	    no_primary = True;
2312	    setit_1_2 = True;
2313	    continue;
2314	}
2315	if (!strcmp ("--set", argv[i])) {
2316	    output_prop_t   *prop;
2317	    if (!output) usage();
2318	    prop = malloc (sizeof (output_prop_t));
2319	    prop->next = output->props;
2320	    output->props = prop;
2321	    if (++i>=argc) usage ();
2322	    prop->name = argv[i];
2323	    if (++i>=argc) usage ();
2324	    prop->value = argv[i];
2325	    propit = True;
2326	    output->changes |= changes_property;
2327	    setit_1_2 = True;
2328	    continue;
2329	}
2330	if (!strcmp ("--scale", argv[i]))
2331	{
2332	    double  sx, sy;
2333	    if (++i>=argc) usage();
2334	    if (sscanf (argv[i], "%lfx%lf", &sx, &sy) != 2)
2335		usage ();
2336	    init_transform (&output->transform);
2337	    output->transform.transform.matrix[0][0] = XDoubleToFixed (sx);
2338	    output->transform.transform.matrix[1][1] = XDoubleToFixed (sy);
2339	    output->transform.transform.matrix[2][2] = XDoubleToFixed (1.0);
2340	    if (sx != 1 || sy != 1)
2341		output->transform.filter = "bilinear";
2342	    else
2343		output->transform.filter = "nearest";
2344	    output->transform.nparams = 0;
2345	    output->transform.params = NULL;
2346	    output->changes |= changes_transform;
2347	    continue;
2348	}
2349	if (!strcmp ("--transform", argv[i])) {
2350	    double  transform[3][3];
2351	    int	    k, l;
2352	    if (++i>=argc) usage ();
2353	    init_transform (&output->transform);
2354	    if (strcmp (argv[i], "none") != 0)
2355	    {
2356		if (sscanf(argv[i], "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
2357			   &transform[0][0],&transform[0][1],&transform[0][2],
2358			   &transform[1][0],&transform[1][1],&transform[1][2],
2359			   &transform[2][0],&transform[2][1],&transform[2][2])
2360		    != 9)
2361		    usage ();
2362		init_transform (&output->transform);
2363		for (k = 0; k < 3; k++)
2364		    for (l = 0; l < 3; l++) {
2365			output->transform.transform.matrix[k][l] = XDoubleToFixed (transform[k][l]);
2366		    }
2367		output->transform.filter = "bilinear";
2368		output->transform.nparams = 0;
2369		output->transform.params = NULL;
2370	    }
2371	    output->changes |= changes_transform;
2372	    continue;
2373	}
2374	if (!strcmp ("--off", argv[i])) {
2375	    if (!output) usage();
2376	    set_name_xid (&output->mode, None);
2377	    set_name_xid (&output->crtc, None);
2378	    output->changes |= changes_mode;
2379	    continue;
2380	}
2381	if (!strcmp ("--fb", argv[i])) {
2382	    if (++i>=argc) usage ();
2383	    if (sscanf (argv[i], "%dx%d",
2384			&fb_width, &fb_height) != 2)
2385		usage ();
2386	    setit_1_2 = True;
2387	    continue;
2388	}
2389	if (!strcmp ("--fbmm", argv[i])) {
2390	    if (++i>=argc) usage ();
2391	    if (sscanf (argv[i], "%dx%d",
2392			&fb_width_mm, &fb_height_mm) != 2)
2393		usage ();
2394	    setit_1_2 = True;
2395	    continue;
2396	}
2397	if (!strcmp ("--dpi", argv[i])) {
2398	    if (++i>=argc) usage ();
2399	    if (sscanf (argv[i], "%f", &dpi) != 1)
2400	    {
2401		dpi = 0.0;
2402		dpi_output = argv[i];
2403	    }
2404	    setit_1_2 = True;
2405	    continue;
2406	}
2407	if (!strcmp ("--clone", argv[i])) {
2408	    policy = clone;
2409	    setit_1_2 = True;
2410	    continue;
2411	}
2412	if (!strcmp ("--extend", argv[i])) {
2413	    policy = extend;
2414	    setit_1_2 = True;
2415	    continue;
2416	}
2417	if (!strcmp ("--auto", argv[i])) {
2418	    if (output)
2419	    {
2420		output->automatic = True;
2421		output->changes |= changes_automatic;
2422	    }
2423	    else
2424		automatic = True;
2425	    setit_1_2 = True;
2426	    continue;
2427	}
2428	if (!strcmp ("--q12", argv[i]))
2429	{
2430	    query_1_2 = True;
2431	    continue;
2432	}
2433	if (!strcmp ("--q1", argv[i]))
2434	{
2435	    query_1 = True;
2436	    continue;
2437	}
2438	if (!strcmp ("--newmode", argv[i]))
2439	{
2440	    umode_t  *m = malloc (sizeof (umode_t));
2441	    float   clock;
2442
2443	    ++i;
2444	    if (i + 9 >= argc) usage ();
2445	    m->mode.name = argv[i];
2446	    m->mode.nameLength = strlen (argv[i]);
2447	    i++;
2448	    if (sscanf (argv[i++], "%f", &clock) != 1)
2449		usage ();
2450	    m->mode.dotClock = clock * 1e6;
2451
2452	    if (sscanf (argv[i++], "%d", &m->mode.width) != 1) usage();
2453	    if (sscanf (argv[i++], "%d", &m->mode.hSyncStart) != 1) usage();
2454	    if (sscanf (argv[i++], "%d", &m->mode.hSyncEnd) != 1) usage();
2455	    if (sscanf (argv[i++], "%d", &m->mode.hTotal) != 1) usage();
2456	    if (sscanf (argv[i++], "%d", &m->mode.height) != 1) usage();
2457	    if (sscanf (argv[i++], "%d", &m->mode.vSyncStart) != 1) usage();
2458	    if (sscanf (argv[i++], "%d", &m->mode.vSyncEnd) != 1) usage();
2459	    if (sscanf (argv[i++], "%d", &m->mode.vTotal) != 1) usage();
2460	    m->mode.modeFlags = 0;
2461	    while (i < argc) {
2462		int f;
2463
2464		for (f = 0; mode_flags[f].string; f++)
2465		    if (!strcasecmp (mode_flags[f].string, argv[i]))
2466			break;
2467
2468		if (!mode_flags[f].string)
2469		    break;
2470    		m->mode.modeFlags |= mode_flags[f].flag;
2471    		i++;
2472	    }
2473	    m->next = umodes;
2474	    m->action = umode_create;
2475	    umodes = m;
2476	    modeit = True;
2477	    continue;
2478	}
2479	if (!strcmp ("--rmmode", argv[i]))
2480	{
2481	    umode_t  *m = malloc (sizeof (umode_t));
2482
2483	    if (++i>=argc) usage ();
2484	    set_name (&m->name, argv[i], name_string|name_xid);
2485	    m->action = umode_destroy;
2486	    m->next = umodes;
2487	    umodes = m;
2488	    modeit = True;
2489	    continue;
2490	}
2491	if (!strcmp ("--addmode", argv[i]))
2492	{
2493	    umode_t  *m = malloc (sizeof (umode_t));
2494
2495	    if (++i>=argc) usage ();
2496	    set_name (&m->output, argv[i], name_string|name_xid);
2497	    if (++i>=argc) usage();
2498	    set_name (&m->name, argv[i], name_string|name_xid);
2499	    m->action = umode_add;
2500	    m->next = umodes;
2501	    umodes = m;
2502	    modeit = True;
2503	    continue;
2504	}
2505	if (!strcmp ("--delmode", argv[i]))
2506	{
2507	    umode_t  *m = malloc (sizeof (umode_t));
2508
2509	    if (++i>=argc) usage ();
2510	    set_name (&m->output, argv[i], name_string|name_xid);
2511	    if (++i>=argc) usage();
2512	    set_name (&m->name, argv[i], name_string|name_xid);
2513	    m->action = umode_delete;
2514	    m->next = umodes;
2515	    umodes = m;
2516	    modeit = True;
2517	    continue;
2518	}
2519#endif
2520	usage();
2521    }
2522    if (verbose)
2523    {
2524	query = True;
2525	if (setit && !setit_1_2)
2526	    query_1 = True;
2527    }
2528    if (version)
2529	printf("xrandr program version       " VERSION "\n");
2530
2531    dpy = XOpenDisplay (display_name);
2532
2533    if (dpy == NULL) {
2534	fprintf (stderr, "Can't open display %s\n", XDisplayName(display_name));
2535	exit (1);
2536    }
2537    if (screen < 0)
2538	screen = DefaultScreen (dpy);
2539    if (screen >= ScreenCount (dpy)) {
2540	fprintf (stderr, "Invalid screen number %d (display has %d)\n",
2541		 screen, ScreenCount (dpy));
2542	exit (1);
2543    }
2544
2545    root = RootWindow (dpy, screen);
2546
2547#if HAS_RANDR_1_2
2548    if (!XRRQueryVersion (dpy, &major, &minor))
2549    {
2550	fprintf (stderr, "RandR extension missing\n");
2551	exit (1);
2552    }
2553    if (major > 1 || (major == 1 && minor >= 2))
2554	has_1_2 = True;
2555    if (major > 1 || (major == 1 && minor >= 3))
2556	has_1_3 = True;
2557
2558    if (has_1_2 && modeit)
2559    {
2560	umode_t	*m;
2561
2562        get_screen (current);
2563	get_crtcs();
2564	get_outputs();
2565
2566	for (m = umodes; m; m = m->next)
2567	{
2568	    XRRModeInfo *e;
2569	    output_t	*o;
2570
2571	    switch (m->action) {
2572	    case umode_create:
2573		XRRCreateMode (dpy, root, &m->mode);
2574		break;
2575	    case umode_destroy:
2576		e = find_mode (&m->name, 0);
2577		if (!e)
2578		    fatal ("cannot find mode \"%s\"\n", m->name.string);
2579		XRRDestroyMode (dpy, e->id);
2580		break;
2581	    case umode_add:
2582		o = find_output (&m->output);
2583		if (!o)
2584		    fatal ("cannot find output \"%s\"\n", m->output.string);
2585		e = find_mode (&m->name, 0);
2586		if (!e)
2587		    fatal ("cannot find mode \"%s\"\n", m->name.string);
2588		XRRAddOutputMode (dpy, o->output.xid, e->id);
2589		break;
2590	    case umode_delete:
2591		o = find_output (&m->output);
2592		if (!o)
2593		    fatal ("cannot find output \"%s\"\n", m->output.string);
2594		e = find_mode (&m->name, 0);
2595		if (!e)
2596		    fatal ("cannot find mode \"%s\"\n", m->name.string);
2597		XRRDeleteOutputMode (dpy, o->output.xid, e->id);
2598		break;
2599	    }
2600	}
2601	if (!setit_1_2)
2602	{
2603	    XSync (dpy, False);
2604	    exit (0);
2605	}
2606    }
2607    if (has_1_2 && propit)
2608    {
2609
2610        get_screen (current);
2611	get_crtcs();
2612	get_outputs();
2613
2614	for (output = outputs; output; output = output->next)
2615	{
2616	    output_prop_t   *prop;
2617
2618	    for (prop = output->props; prop; prop = prop->next)
2619	    {
2620		Atom		name = XInternAtom (dpy, prop->name, False);
2621		Atom		type;
2622		int		format;
2623		unsigned char	*data;
2624		int		nelements;
2625		int		int_value;
2626		unsigned long	ulong_value;
2627		unsigned char	*prop_data;
2628		int		actual_format;
2629		unsigned long	nitems, bytes_after;
2630		Atom		actual_type;
2631		XRRPropertyInfo *propinfo;
2632
2633		type = AnyPropertyType;
2634		format=0;
2635
2636		if (XRRGetOutputProperty (dpy, output->output.xid, name,
2637					  0, 100, False, False,
2638					  AnyPropertyType,
2639					  &actual_type, &actual_format,
2640					  &nitems, &bytes_after, &prop_data) == Success &&
2641
2642		    (propinfo = XRRQueryOutputProperty(dpy, output->output.xid,
2643						      name)))
2644		{
2645		    type = actual_type;
2646		    format = actual_format;
2647		}
2648
2649		if ((type == XA_INTEGER || type == AnyPropertyType) &&
2650		    (sscanf (prop->value, "%d", &int_value) == 1 ||
2651		     sscanf (prop->value, "0x%x", &int_value) == 1))
2652		{
2653		    type = XA_INTEGER;
2654		    ulong_value = int_value;
2655		    data = (unsigned char *) &ulong_value;
2656		    nelements = 1;
2657		    format = 32;
2658		}
2659		else if ((type == XA_ATOM))
2660		{
2661		    ulong_value = XInternAtom (dpy, prop->value, False);
2662		    data = (unsigned char *) &ulong_value;
2663		    nelements = 1;
2664		    format = 32;
2665		}
2666		else if ((type == XA_STRING || type == AnyPropertyType))
2667		{
2668		    type = XA_STRING;
2669		    data = (unsigned char *) prop->value;
2670		    nelements = strlen (prop->value);
2671		    format = 8;
2672		}
2673		else
2674		    continue;
2675		XRRChangeOutputProperty (dpy, output->output.xid,
2676					 name, type, format, PropModeReplace,
2677					 data, nelements);
2678	    }
2679	}
2680	if (!setit_1_2)
2681	{
2682	    XSync (dpy, False);
2683	    exit (0);
2684	}
2685    }
2686    if (setit_1_2)
2687    {
2688	get_screen (current);
2689	get_crtcs ();
2690	get_outputs ();
2691	set_positions ();
2692	set_screen_size ();
2693
2694	pick_crtcs ();
2695
2696	/*
2697	 * Assign outputs to crtcs
2698	 */
2699	set_crtcs ();
2700
2701	/*
2702	 * Mark changing crtcs
2703	 */
2704	mark_changing_crtcs ();
2705
2706	/*
2707	 * If an output was specified to track dpi, use it
2708	 */
2709	if (dpi_output)
2710	{
2711	    output_t	*output = find_output_by_name (dpi_output);
2712	    XRROutputInfo	*output_info;
2713	    XRRModeInfo	*mode_info;
2714	    if (!output)
2715		fatal ("Cannot find output %s\n", dpi_output);
2716	    output_info = output->output_info;
2717	    mode_info = output->mode_info;
2718	    if (output_info && mode_info && output_info->mm_height)
2719	    {
2720		/*
2721		 * When this output covers the whole screen, just use
2722		 * the known physical size
2723		 */
2724		if (fb_width == mode_info->width &&
2725		    fb_height == mode_info->height)
2726		{
2727		    fb_width_mm = output_info->mm_width;
2728		    fb_height_mm = output_info->mm_height;
2729		}
2730		else
2731		{
2732		    dpi = (25.4 * mode_info->height) / output_info->mm_height;
2733		}
2734	    }
2735	}
2736
2737	/*
2738	 * Compute physical screen size
2739	 */
2740	if (fb_width_mm == 0 || fb_height_mm == 0)
2741	{
2742	    if (fb_width != DisplayWidth (dpy, screen) ||
2743		fb_height != DisplayHeight (dpy, screen) || dpi != 0.0)
2744	    {
2745		if (dpi <= 0)
2746		    dpi = (25.4 * DisplayHeight (dpy, screen)) / DisplayHeightMM(dpy, screen);
2747
2748		fb_width_mm = (25.4 * fb_width) / dpi;
2749		fb_height_mm = (25.4 * fb_height) / dpi;
2750	    }
2751	    else
2752	    {
2753		fb_width_mm = DisplayWidthMM (dpy, screen);
2754		fb_height_mm = DisplayHeightMM (dpy, screen);
2755	    }
2756	}
2757
2758	/*
2759	 * Set panning
2760	 */
2761	set_panning ();
2762
2763	/*
2764	 * Set gamma on crtc's that belong to the outputs.
2765	 */
2766	set_gamma ();
2767
2768	/*
2769	 * Now apply all of the changes
2770	 */
2771	apply ();
2772
2773	XSync (dpy, False);
2774	exit (0);
2775    }
2776    if (query_1_2 || (query && has_1_2 && !query_1))
2777    {
2778	output_t    *output;
2779	int	    m;
2780
2781#define ModeShown   0x80000000
2782
2783	get_screen (current);
2784	get_crtcs ();
2785	get_outputs ();
2786
2787        printf ("Screen %d: minimum %d x %d, current %d x %d, maximum %d x %d\n",
2788		screen, minWidth, minHeight,
2789		DisplayWidth (dpy, screen), DisplayHeight(dpy, screen),
2790		maxWidth, maxHeight);
2791
2792	for (output = outputs; output; output = output->next)
2793	{
2794	    XRROutputInfo   *output_info = output->output_info;
2795	    crtc_t	    *crtc = output->crtc_info;
2796	    XRRCrtcInfo	    *crtc_info = crtc ? crtc->crtc_info : NULL;
2797	    XRRModeInfo	    *mode = output->mode_info;
2798	    Atom	    *props;
2799	    int		    j, k, nprop;
2800	    Bool	    *mode_shown;
2801	    Rotation	    rotations = output_rotations (output);
2802
2803	    printf ("%s %s", output_info->name, connection[output_info->connection]);
2804	    if (mode)
2805	    {
2806		if (crtc_info) {
2807		    printf (" %dx%d+%d+%d",
2808			    crtc_info->width, crtc_info->height,
2809			    crtc_info->x, crtc_info->y);
2810		} else {
2811		    printf (" %dx%d+%d+%d",
2812			    mode->width, mode->height, output->x, output->y);
2813		}
2814		if (verbose)
2815		    printf (" (0x%x)", (int)mode->id);
2816		if (output->rotation != RR_Rotate_0 || verbose)
2817		{
2818		    printf (" %s",
2819			    rotation_name (output->rotation));
2820		    if (output->rotation & (RR_Reflect_X|RR_Reflect_Y))
2821			printf (" %s", reflection_name (output->rotation));
2822		}
2823	    }
2824	    if (rotations != RR_Rotate_0 || verbose)
2825	    {
2826		Bool    first = True;
2827		printf (" (");
2828		for (i = 0; i < 4; i ++) {
2829		    if ((rotations >> i) & 1) {
2830			if (!first) printf (" "); first = False;
2831			printf("%s", direction[i]);
2832			first = False;
2833		    }
2834		}
2835		if (rotations & RR_Reflect_X)
2836		{
2837		    if (!first) printf (" "); first = False;
2838		    printf ("x axis");
2839		}
2840		if (rotations & RR_Reflect_Y)
2841		{
2842		    if (!first) printf (" "); first = False;
2843		    printf ("y axis");
2844		}
2845		printf (")");
2846	    }
2847
2848	    if (mode)
2849	    {
2850		printf (" %dmm x %dmm",
2851			(int)output_info->mm_width, (int)output_info->mm_height);
2852	    }
2853
2854	    if (crtc && crtc->panning_info && crtc->panning_info->width > 0)
2855	    {
2856		XRRPanning *pan = crtc->panning_info;
2857		printf (" panning %dx%d+%d+%d",
2858			pan->width, pan->height, pan->left, pan->top);
2859		if ((pan->track_width    != 0 &&
2860		     (pan->track_left    != pan->left		||
2861		      pan->track_width   != pan->width		||
2862		      pan->border_left   != 0			||
2863		      pan->border_right  != 0))			||
2864		    (pan->track_height   != 0 &&
2865		     (pan->track_top     != pan->top		||
2866		      pan->track_height  != pan->height		||
2867		      pan->border_top    != 0			||
2868		      pan->border_bottom != 0)))
2869		    printf (" tracking %dx%d+%d+%d border %d/%d/%d/%d",
2870			    pan->track_width,  pan->track_height,
2871			    pan->track_left,   pan->track_top,
2872			    pan->border_left,  pan->border_top,
2873			    pan->border_right, pan->border_bottom);
2874	    }
2875	    printf ("\n");
2876
2877	    if (verbose)
2878	    {
2879		printf ("\tIdentifier: 0x%x\n", (int)output->output.xid);
2880		printf ("\tTimestamp:  %d\n", (int)output_info->timestamp);
2881		printf ("\tSubpixel:   %s\n", order[output_info->subpixel_order]);
2882		printf ("\tClones:    ");
2883		for (j = 0; j < output_info->nclone; j++)
2884		{
2885		    output_t	*clone = find_output_by_xid (output_info->clones[j]);
2886
2887		    if (clone) printf (" %s", clone->output.string);
2888		}
2889		printf ("\n");
2890		if (output->crtc_info)
2891		    printf ("\tCRTC:       %d\n", output->crtc_info->crtc.index);
2892		printf ("\tCRTCs:     ");
2893		for (j = 0; j < output_info->ncrtc; j++)
2894		{
2895		    crtc_t	*crtc = find_crtc_by_xid (output_info->crtcs[j]);
2896		    if (crtc)
2897			printf (" %d", crtc->crtc.index);
2898		}
2899		printf ("\n");
2900		if (output->crtc_info && output->crtc_info->panning_info) {
2901		    XRRPanning *pan = output->crtc_info->panning_info;
2902		    printf ("\tPanning:    %dx%d+%d+%d\n",
2903			    pan->width, pan->height, pan->left, pan->top);
2904		    printf ("\tTracking:   %dx%d+%d+%d\n",
2905			    pan->track_width,  pan->track_height,
2906			    pan->track_left,   pan->track_top);
2907		    printf ("\tBorder:     %d/%d/%d/%d\n",
2908			    pan->border_left,  pan->border_top,
2909			    pan->border_right, pan->border_bottom);
2910		}
2911	    }
2912	    if (verbose)
2913	    {
2914		int x, y;
2915
2916		printf ("\tTransform: ");
2917		for (y = 0; y < 3; y++)
2918		{
2919		    for (x = 0; x < 3; x++)
2920			printf (" %f", XFixedToDouble (output->transform.transform.matrix[y][x]));
2921		    if (y < 2)
2922			printf ("\n\t           ");
2923		}
2924		if (output->transform.filter)
2925		    printf ("\n\t           filter: %s", output->transform.filter);
2926		printf ("\n");
2927	    }
2928	    if (verbose || properties)
2929	    {
2930		props = XRRListOutputProperties (dpy, output->output.xid,
2931						 &nprop);
2932		for (j = 0; j < nprop; j++) {
2933		    unsigned char *prop;
2934		    int actual_format;
2935		    unsigned long nitems, bytes_after;
2936		    Atom actual_type;
2937		    XRRPropertyInfo *propinfo;
2938
2939		    XRRGetOutputProperty (dpy, output->output.xid, props[j],
2940					  0, 100, False, False,
2941					  AnyPropertyType,
2942					  &actual_type, &actual_format,
2943					  &nitems, &bytes_after, &prop);
2944
2945		    propinfo = XRRQueryOutputProperty(dpy, output->output.xid,
2946						      props[j]);
2947
2948		    if (actual_type == XA_INTEGER && actual_format == 8) {
2949			int k;
2950
2951			printf("\t%s:\n", XGetAtomName (dpy, props[j]));
2952			for (k = 0; k < nitems; k++) {
2953			    if (k % 16 == 0)
2954				printf ("\t\t");
2955			    printf("%02x", (unsigned char)prop[k]);
2956			    if (k % 16 == 15)
2957				printf("\n");
2958			}
2959		    } else if (actual_type == XA_INTEGER &&
2960			       actual_format == 32)
2961		    {
2962			printf("\t%s: ", XGetAtomName (dpy, props[j]));
2963			for (k = 0; k < nitems; k++) {
2964			    if (k > 0)
2965				printf ("\n\t\t\t");
2966			    printf("%d (0x%08x)",
2967				   (int)((INT32 *)prop)[k], (int)((INT32 *)prop)[k]);
2968			}
2969
2970 			if (propinfo->range && propinfo->num_values > 0) {
2971			    if (nitems > 1)
2972				printf ("\n\t\t");
2973			    printf("\trange%s: ",
2974				   (propinfo->num_values == 2) ? "" : "s");
2975
2976			    for (k = 0; k < propinfo->num_values / 2; k++)
2977				printf(" (%d,%d)", (int)propinfo->values[k * 2],
2978				       (int)propinfo->values[k * 2 + 1]);
2979			}
2980
2981			printf("\n");
2982		    } else if (actual_type == XA_ATOM &&
2983			       actual_format == 32)
2984		    {
2985			printf("\t%s:", XGetAtomName (dpy, props[j]));
2986			for (k = 0; k < nitems; k++) {
2987			    if (k > 0 && (k & 1) == 0)
2988				printf ("\n\t\t");
2989			    printf("\t%s", XGetAtomName (dpy, ((Atom *)prop)[k]));
2990			}
2991
2992 			if (!propinfo->range && propinfo->num_values > 0) {
2993			    printf("\n\t\tsupported:");
2994
2995			    for (k = 0; k < propinfo->num_values; k++)
2996			    {
2997				printf(" %-12.12s", XGetAtomName (dpy,
2998							    propinfo->values[k]));
2999				if (k % 4 == 3 && k < propinfo->num_values - 1)
3000				    printf ("\n\t\t          ");
3001			    }
3002			}
3003			printf("\n");
3004		    } else if (actual_format == 8) {
3005			printf ("\t%s: %s%s\n", XGetAtomName (dpy, props[j]),
3006				prop, bytes_after ? "..." : "");
3007		    } else {
3008			char	*type = actual_type ? XGetAtomName (dpy, actual_type) : "none";
3009			printf ("\t%s: %s(%d) (format %d items %d) ????\n",
3010				XGetAtomName (dpy, props[j]),
3011				type, (int)actual_type, actual_format, (int)nitems);
3012		    }
3013
3014		    free(propinfo);
3015		}
3016	    }
3017
3018	    if (verbose)
3019	    {
3020		for (j = 0; j < output_info->nmode; j++)
3021		{
3022		    XRRModeInfo	*mode = find_mode_by_xid (output_info->modes[j]);
3023		    int		f;
3024
3025		    printf ("  %s (0x%x) %6.1fMHz",
3026			    mode->name, (int)mode->id,
3027			    (float)mode->dotClock / 1000000.0);
3028		    for (f = 0; mode_flags[f].flag; f++)
3029			if (mode->modeFlags & mode_flags[f].flag)
3030			    printf (" %s", mode_flags[f].string);
3031		    if (mode == output->mode_info)
3032			printf (" *current");
3033		    if (j < output_info->npreferred)
3034			printf (" +preferred");
3035		    printf ("\n");
3036		    printf ("        h: width  %4d start %4d end %4d total %4d skew %4d clock %6.1fKHz\n",
3037			    mode->width, mode->hSyncStart, mode->hSyncEnd,
3038			    mode->hTotal, mode->hSkew, mode_hsync (mode) / 1000);
3039		    printf ("        v: height %4d start %4d end %4d total %4d           clock %6.1fHz\n",
3040			    mode->height, mode->vSyncStart, mode->vSyncEnd, mode->vTotal,
3041			    mode_refresh (mode));
3042		    mode->modeFlags |= ModeShown;
3043		}
3044	    }
3045	    else
3046	    {
3047		mode_shown = calloc (output_info->nmode, sizeof (Bool));
3048		if (!mode_shown) fatal ("out of memory\n");
3049		for (j = 0; j < output_info->nmode; j++)
3050		{
3051		    XRRModeInfo *jmode, *kmode;
3052
3053		    if (mode_shown[j]) continue;
3054
3055		    jmode = find_mode_by_xid (output_info->modes[j]);
3056		    printf (" ");
3057		    printf ("  %-12s", jmode->name);
3058		    for (k = j; k < output_info->nmode; k++)
3059		    {
3060			if (mode_shown[k]) continue;
3061			kmode = find_mode_by_xid (output_info->modes[k]);
3062			if (strcmp (jmode->name, kmode->name) != 0) continue;
3063			mode_shown[k] = True;
3064			kmode->modeFlags |= ModeShown;
3065			printf (" %6.1f", mode_refresh (kmode));
3066			if (kmode == output->mode_info)
3067			    printf ("*");
3068			else
3069			    printf (" ");
3070			if (k < output_info->npreferred)
3071			    printf ("+");
3072			else
3073			    printf (" ");
3074		    }
3075		    printf ("\n");
3076		}
3077		free (mode_shown);
3078	    }
3079	}
3080	for (m = 0; m < res->nmode; m++)
3081	{
3082	    XRRModeInfo	*mode = &res->modes[m];
3083
3084	    if (!(mode->modeFlags & ModeShown))
3085	    {
3086		printf ("  %s (0x%x) %6.1fMHz\n",
3087			mode->name, (int)mode->id,
3088			(float)mode->dotClock / 1000000.0);
3089		printf ("        h: width  %4d start %4d end %4d total %4d skew %4d clock %6.1fKHz\n",
3090			mode->width, mode->hSyncStart, mode->hSyncEnd,
3091			mode->hTotal, mode->hSkew, mode_hsync (mode) / 1000);
3092		printf ("        v: height %4d start %4d end %4d total %4d           clock %6.1fHz\n",
3093			mode->height, mode->vSyncStart, mode->vSyncEnd, mode->vTotal,
3094			mode_refresh (mode));
3095	    }
3096	}
3097	exit (0);
3098    }
3099#endif
3100
3101    sc = XRRGetScreenInfo (dpy, root);
3102
3103    if (sc == NULL)
3104	exit (1);
3105
3106    current_size = XRRConfigCurrentConfiguration (sc, &current_rotation);
3107
3108    sizes = XRRConfigSizes(sc, &nsize);
3109
3110    if (have_pixel_size) {
3111	for (size = 0; size < nsize; size++)
3112	{
3113	    if (sizes[size].width == width && sizes[size].height == height)
3114		break;
3115	}
3116	if (size >= nsize) {
3117	    fprintf (stderr,
3118		     "Size %dx%d not found in available modes\n", width, height);
3119	    exit (1);
3120	}
3121    }
3122    else if (size < 0)
3123	size = current_size;
3124    else if (size >= nsize) {
3125	fprintf (stderr,
3126		 "Size index %d is too large, there are only %d sizes\n",
3127		 size, nsize);
3128	exit (1);
3129    }
3130
3131    if (rot < 0)
3132    {
3133	for (rot = 0; rot < 4; rot++)
3134	    if (1 << rot == (current_rotation & 0xf))
3135		break;
3136    }
3137
3138    current_rate = XRRConfigCurrentRate (sc);
3139
3140    if (rate < 0)
3141    {
3142	if (size == current_size)
3143	    rate = current_rate;
3144	else
3145	    rate = 0;
3146    }
3147    else
3148    {
3149	rates = XRRConfigRates (sc, size, &nrate);
3150	for (i = 0; i < nrate; i++)
3151	    if (rate == rates[i])
3152		break;
3153	if (i == nrate) {
3154	    fprintf (stderr, "Rate %.1f Hz not available for this size\n", rate);
3155	    exit (1);
3156	}
3157    }
3158
3159    if (version) {
3160	int major_version, minor_version;
3161	XRRQueryVersion (dpy, &major_version, &minor_version);
3162	printf("Server reports RandR version %d.%d\n",
3163	       major_version, minor_version);
3164    }
3165
3166    if (query || query_1) {
3167	printf(" SZ:    Pixels          Physical       Refresh\n");
3168	for (i = 0; i < nsize; i++) {
3169	    printf ("%c%-2d %5d x %-5d  (%4dmm x%4dmm )",
3170		    i == current_size ? '*' : ' ',
3171		    i, sizes[i].width, sizes[i].height,
3172		    sizes[i].mwidth, sizes[i].mheight);
3173	    rates = XRRConfigRates (sc, i, &nrate);
3174	    if (nrate) printf ("  ");
3175	    for (j = 0; j < nrate; j++)
3176		printf ("%c%-4d",
3177			i == current_size && rates[j] == current_rate ? '*' : ' ',
3178			rates[j]);
3179	    printf ("\n");
3180	}
3181    }
3182
3183    rotations = XRRConfigRotations(sc, &current_rotation);
3184
3185    rotation = 1 << rot ;
3186    if (query) {
3187    	printf("Current rotation - %s\n",
3188	       rotation_name (current_rotation));
3189
3190	printf("Current reflection - %s\n",
3191	       reflection_name (current_rotation));
3192
3193	printf ("Rotations possible - ");
3194	for (i = 0; i < 4; i ++) {
3195	    if ((rotations >> i) & 1)  printf("%s ", direction[i]);
3196	}
3197	printf ("\n");
3198
3199	printf ("Reflections possible - ");
3200	if (rotations & (RR_Reflect_X|RR_Reflect_Y))
3201	{
3202	    if (rotations & RR_Reflect_X) printf ("X Axis ");
3203	    if (rotations & RR_Reflect_Y) printf ("Y Axis");
3204	}
3205	else
3206	    printf ("none");
3207	printf ("\n");
3208    }
3209
3210    if (verbose) {
3211	printf("Setting size to %d, rotation to %s\n",  size, direction[rot]);
3212
3213	printf ("Setting reflection on ");
3214	if (reflection)
3215	{
3216	    if (reflection & RR_Reflect_X) printf ("X Axis ");
3217	    if (reflection & RR_Reflect_Y) printf ("Y Axis");
3218	}
3219	else
3220	    printf ("neither axis");
3221	printf ("\n");
3222
3223	if (reflection & RR_Reflect_X) printf("Setting reflection on X axis\n");
3224
3225	if (reflection & RR_Reflect_Y) printf("Setting reflection on Y axis\n");
3226    }
3227
3228    /* we should test configureNotify on the root window */
3229    XSelectInput (dpy, root, StructureNotifyMask);
3230
3231    if (setit && !dryrun) XRRSelectInput (dpy, root,
3232			       RRScreenChangeNotifyMask);
3233    if (setit && !dryrun) status = XRRSetScreenConfigAndRate (dpy, sc,
3234						   root,
3235						   (SizeID) size, (Rotation) (rotation | reflection), rate, CurrentTime);
3236
3237    XRRQueryExtension(dpy, &event_base, &error_base);
3238
3239    if (setit && !dryrun && status == RRSetConfigFailed) {
3240	printf ("Failed to change the screen configuration!\n");
3241	ret = 1;
3242    }
3243
3244    if (verbose && setit && !dryrun && size != current_size) {
3245	if (status == RRSetConfigSuccess)
3246	{
3247	    Bool    seen_screen = False;
3248	    while (!seen_screen) {
3249		int spo;
3250		XNextEvent(dpy, (XEvent *) &event);
3251
3252		printf ("Event received, type = %d\n", event.type);
3253		/* update Xlib's knowledge of the event */
3254		XRRUpdateConfiguration (&event);
3255		if (event.type == ConfigureNotify)
3256		    printf("Received ConfigureNotify Event!\n");
3257
3258		switch (event.type - event_base) {
3259		case RRScreenChangeNotify:
3260		    sce = (XRRScreenChangeNotifyEvent *) &event;
3261
3262		    printf("Got a screen change notify event!\n");
3263		    printf(" window = %d\n root = %d\n size_index = %d\n rotation %d\n",
3264			   (int) sce->window, (int) sce->root,
3265			   sce->size_index,  sce->rotation);
3266		    printf(" timestamp = %ld, config_timestamp = %ld\n",
3267			   sce->timestamp, sce->config_timestamp);
3268		    printf(" Rotation = %x\n", sce->rotation);
3269		    printf(" %d X %d pixels, %d X %d mm\n",
3270			   sce->width, sce->height, sce->mwidth, sce->mheight);
3271		    printf("Display width   %d, height   %d\n",
3272			   DisplayWidth(dpy, screen), DisplayHeight(dpy, screen));
3273		    printf("Display widthmm %d, heightmm %d\n",
3274			   DisplayWidthMM(dpy, screen), DisplayHeightMM(dpy, screen));
3275		    spo = sce->subpixel_order;
3276		    if ((spo < 0) || (spo > 5))
3277			printf ("Unknown subpixel order, value = %d\n", spo);
3278		    else printf ("new Subpixel rendering model is %s\n", order[spo]);
3279		    seen_screen = True;
3280		    break;
3281		default:
3282		    if (event.type != ConfigureNotify)
3283			printf("unknown event received, type = %d!\n", event.type);
3284		}
3285	    }
3286	}
3287    }
3288    XRRFreeScreenConfigInfo(sc);
3289    return(ret);
3290}
3291