rrscreen.c revision 8223e2f2
1/*
2 * Copyright © 2006 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include "randrstr.h"
24
25static const int padlength[4] = {0, 3, 2, 1};
26
27static CARD16
28RR10CurrentSizeID (ScreenPtr pScreen);
29
30/*
31 * Edit connection information block so that new clients
32 * see the current screen size on connect
33 */
34static void
35RREditConnectionInfo (ScreenPtr pScreen)
36{
37    xConnSetup	    *connSetup;
38    char	    *vendor;
39    xPixmapFormat   *formats;
40    xWindowRoot	    *root;
41    xDepth	    *depth;
42    xVisualType	    *visual;
43    int		    screen = 0;
44    int		    d;
45
46    connSetup = (xConnSetup *) ConnectionInfo;
47    vendor = (char *) connSetup + sizeof (xConnSetup);
48    formats = (xPixmapFormat *) ((char *) vendor +
49				 connSetup->nbytesVendor +
50				 padlength[connSetup->nbytesVendor & 3]);
51    root = (xWindowRoot *) ((char *) formats +
52			    sizeof (xPixmapFormat) * screenInfo.numPixmapFormats);
53    while (screen != pScreen->myNum)
54    {
55	depth = (xDepth *) ((char *) root +
56			    sizeof (xWindowRoot));
57	for (d = 0; d < root->nDepths; d++)
58	{
59	    visual = (xVisualType *) ((char *) depth +
60				      sizeof (xDepth));
61	    depth = (xDepth *) ((char *) visual +
62				depth->nVisuals * sizeof (xVisualType));
63	}
64	root = (xWindowRoot *) ((char *) depth);
65	screen++;
66    }
67    root->pixWidth = pScreen->width;
68    root->pixHeight = pScreen->height;
69    root->mmWidth = pScreen->mmWidth;
70    root->mmHeight = pScreen->mmHeight;
71}
72
73void
74RRSendConfigNotify (ScreenPtr pScreen)
75{
76    WindowPtr	pWin = pScreen->root;
77    xEvent	event;
78
79    event.u.u.type = ConfigureNotify;
80    event.u.configureNotify.window = pWin->drawable.id;
81    event.u.configureNotify.aboveSibling = None;
82    event.u.configureNotify.x = 0;
83    event.u.configureNotify.y = 0;
84
85    /* XXX xinerama stuff ? */
86
87    event.u.configureNotify.width = pWin->drawable.width;
88    event.u.configureNotify.height = pWin->drawable.height;
89    event.u.configureNotify.borderWidth = wBorderWidth (pWin);
90    event.u.configureNotify.override = pWin->overrideRedirect;
91    DeliverEvents(pWin, &event, 1, NullWindow);
92}
93
94void
95RRDeliverScreenEvent (ClientPtr client, WindowPtr pWin, ScreenPtr pScreen)
96{
97    rrScrPriv (pScreen);
98    xRRScreenChangeNotifyEvent	se;
99    RRCrtcPtr	crtc = pScrPriv->numCrtcs ? pScrPriv->crtcs[0] : NULL;
100    WindowPtr	pRoot = pScreen->root;
101
102    se.type = RRScreenChangeNotify + RREventBase;
103    se.rotation = (CARD8) (crtc ? crtc->rotation : RR_Rotate_0);
104    se.timestamp = pScrPriv->lastSetTime.milliseconds;
105    se.configTimestamp = pScrPriv->lastConfigTime.milliseconds;
106    se.root =  pRoot->drawable.id;
107    se.window = pWin->drawable.id;
108    se.subpixelOrder = PictureGetSubpixelOrder (pScreen);
109
110    se.sizeID = RR10CurrentSizeID (pScreen);
111
112    if (se.rotation & (RR_Rotate_90 | RR_Rotate_270)) {
113	se.widthInPixels = pScreen->height;
114	se.heightInPixels = pScreen->width;
115	se.widthInMillimeters = pScreen->mmHeight;
116	se.heightInMillimeters = pScreen->mmWidth;
117    } else {
118	se.widthInPixels = pScreen->width;
119	se.heightInPixels = pScreen->height;
120	se.widthInMillimeters = pScreen->mmWidth;
121	se.heightInMillimeters = pScreen->mmHeight;
122    }
123
124    WriteEventsToClient (client, 1, (xEvent *) &se);
125}
126
127/*
128 * Notify the extension that the screen size has been changed.
129 * The driver is responsible for calling this whenever it has changed
130 * the size of the screen
131 */
132void
133RRScreenSizeNotify (ScreenPtr	pScreen)
134{
135    rrScrPriv(pScreen);
136    /*
137     * Deliver ConfigureNotify events when root changes
138     * pixel size
139     */
140    if (pScrPriv->width == pScreen->width &&
141	pScrPriv->height == pScreen->height &&
142	pScrPriv->mmWidth == pScreen->mmWidth &&
143	pScrPriv->mmHeight == pScreen->mmHeight)
144	return;
145
146    pScrPriv->width = pScreen->width;
147    pScrPriv->height = pScreen->height;
148    pScrPriv->mmWidth = pScreen->mmWidth;
149    pScrPriv->mmHeight = pScreen->mmHeight;
150    pScrPriv->changed = TRUE;
151/*    pScrPriv->sizeChanged = TRUE; */
152
153    RRTellChanged (pScreen);
154    RRSendConfigNotify (pScreen);
155    RREditConnectionInfo (pScreen);
156
157    RRPointerScreenConfigured (pScreen);
158    /*
159     * Fix pointer bounds and location
160     */
161    ScreenRestructured (pScreen);
162}
163
164/*
165 * Request that the screen be resized
166 */
167Bool
168RRScreenSizeSet (ScreenPtr  pScreen,
169		 CARD16	    width,
170		 CARD16	    height,
171		 CARD32	    mmWidth,
172		 CARD32	    mmHeight)
173{
174    rrScrPriv(pScreen);
175
176#if RANDR_12_INTERFACE
177    if (pScrPriv->rrScreenSetSize)
178    {
179	return (*pScrPriv->rrScreenSetSize) (pScreen,
180					     width, height,
181					     mmWidth, mmHeight);
182    }
183#endif
184#if RANDR_10_INTERFACE
185    if (pScrPriv->rrSetConfig)
186    {
187	return TRUE;	/* can't set size separately */
188    }
189#endif
190    return FALSE;
191}
192
193/*
194 * Retrieve valid screen size range
195 */
196int
197ProcRRGetScreenSizeRange (ClientPtr client)
198{
199    REQUEST(xRRGetScreenSizeRangeReq);
200    xRRGetScreenSizeRangeReply	rep;
201    WindowPtr			pWin;
202    ScreenPtr			pScreen;
203    rrScrPrivPtr		pScrPriv;
204    int				rc;
205
206    REQUEST_SIZE_MATCH(xRRGetScreenInfoReq);
207    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
208    if (rc != Success)
209	return rc;
210
211    pScreen = pWin->drawable.pScreen;
212    pScrPriv = rrGetScrPriv(pScreen);
213
214    rep.type = X_Reply;
215    rep.pad = 0;
216    rep.sequenceNumber = client->sequence;
217    rep.length = 0;
218
219    if (pScrPriv)
220    {
221	if (!RRGetInfo (pScreen, FALSE))
222	    return BadAlloc;
223	rep.minWidth  = pScrPriv->minWidth;
224	rep.minHeight = pScrPriv->minHeight;
225	rep.maxWidth  = pScrPriv->maxWidth;
226	rep.maxHeight = pScrPriv->maxHeight;
227    }
228    else
229    {
230	rep.maxWidth  = rep.minWidth  = pScreen->width;
231	rep.maxHeight = rep.minHeight = pScreen->height;
232    }
233    if (client->swapped)
234    {
235	int n;
236
237    	swaps(&rep.sequenceNumber, n);
238    	swapl(&rep.length, n);
239	swaps(&rep.minWidth, n);
240	swaps(&rep.minHeight, n);
241	swaps(&rep.maxWidth, n);
242	swaps(&rep.maxHeight, n);
243    }
244    WriteToClient(client, sizeof(xRRGetScreenSizeRangeReply), (char *)&rep);
245    return Success;
246}
247
248int
249ProcRRSetScreenSize (ClientPtr client)
250{
251    REQUEST(xRRSetScreenSizeReq);
252    WindowPtr		pWin;
253    ScreenPtr		pScreen;
254    rrScrPrivPtr	pScrPriv;
255    int			i, rc;
256
257    REQUEST_SIZE_MATCH(xRRSetScreenSizeReq);
258    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
259    if (rc != Success)
260	return rc;
261
262    pScreen = pWin->drawable.pScreen;
263    pScrPriv = rrGetScrPriv(pScreen);
264    if (stuff->width < pScrPriv->minWidth || pScrPriv->maxWidth < stuff->width)
265    {
266	client->errorValue = stuff->width;
267	return BadValue;
268    }
269    if (stuff->height < pScrPriv->minHeight ||
270	pScrPriv->maxHeight < stuff->height)
271    {
272	client->errorValue = stuff->height;
273	return BadValue;
274    }
275    for (i = 0; i < pScrPriv->numCrtcs; i++)
276    {
277	RRCrtcPtr   crtc = pScrPriv->crtcs[i];
278	RRModePtr   mode = crtc->mode;
279	if (mode)
280	{
281	    int		source_width = mode->mode.width;
282	    int		source_height = mode->mode.height;
283	    Rotation	rotation = crtc->rotation;
284
285	    if (rotation == RR_Rotate_90 || rotation == RR_Rotate_270)
286	    {
287		source_width = mode->mode.height;
288		source_height = mode->mode.width;
289	    }
290
291	    if (crtc->x + source_width > stuff->width ||
292		crtc->y + source_height > stuff->height)
293	    return BadMatch;
294	}
295    }
296    if (stuff->widthInMillimeters == 0 || stuff->heightInMillimeters == 0)
297    {
298	client->errorValue = 0;
299	return BadValue;
300    }
301    if (!RRScreenSizeSet (pScreen,
302			  stuff->width, stuff->height,
303			  stuff->widthInMillimeters,
304			  stuff->heightInMillimeters))
305    {
306	return BadMatch;
307    }
308    return Success;
309}
310
311static int
312rrGetScreenResources(ClientPtr client, Bool query)
313{
314    REQUEST(xRRGetScreenResourcesReq);
315    xRRGetScreenResourcesReply  rep;
316    WindowPtr			pWin;
317    ScreenPtr			pScreen;
318    rrScrPrivPtr		pScrPriv;
319    CARD8			*extra;
320    unsigned long		extraLen;
321    int				i, n, rc, has_primary = 0;
322    RRCrtc			*crtcs;
323    RROutput			*outputs;
324    xRRModeInfo			*modeinfos;
325    CARD8			*names;
326
327    REQUEST_SIZE_MATCH(xRRGetScreenResourcesReq);
328    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
329    if (rc != Success)
330	return rc;
331
332    pScreen = pWin->drawable.pScreen;
333    pScrPriv = rrGetScrPriv(pScreen);
334    rep.pad = 0;
335
336    if (query && pScrPriv)
337	if (!RRGetInfo (pScreen, query))
338	    return BadAlloc;
339
340    if (!pScrPriv)
341    {
342	rep.type = X_Reply;
343	rep.sequenceNumber = client->sequence;
344	rep.length = 0;
345	rep.timestamp = currentTime.milliseconds;
346	rep.configTimestamp = currentTime.milliseconds;
347	rep.nCrtcs = 0;
348	rep.nOutputs = 0;
349	rep.nModes = 0;
350	rep.nbytesNames = 0;
351	extra = NULL;
352	extraLen = 0;
353    }
354    else
355    {
356	RRModePtr   *modes;
357	int	    num_modes;
358
359	modes = RRModesForScreen (pScreen, &num_modes);
360	if (!modes)
361	    return BadAlloc;
362
363	rep.type = X_Reply;
364	rep.sequenceNumber = client->sequence;
365	rep.length = 0;
366	rep.timestamp = pScrPriv->lastSetTime.milliseconds;
367	rep.configTimestamp = pScrPriv->lastConfigTime.milliseconds;
368	rep.nCrtcs = pScrPriv->numCrtcs;
369	rep.nOutputs = pScrPriv->numOutputs;
370	rep.nModes = num_modes;
371	rep.nbytesNames = 0;
372
373	for (i = 0; i < num_modes; i++)
374	    rep.nbytesNames += modes[i]->mode.nameLength;
375
376	rep.length = (pScrPriv->numCrtcs +
377		      pScrPriv->numOutputs +
378		      num_modes * bytes_to_int32(SIZEOF(xRRModeInfo)) +
379		      bytes_to_int32(rep.nbytesNames));
380
381	extraLen = rep.length << 2;
382	if (extraLen)
383	{
384	    extra = malloc(extraLen);
385	    if (!extra)
386	    {
387		free(modes);
388		return BadAlloc;
389	    }
390	}
391	else
392	    extra = NULL;
393
394	crtcs = (RRCrtc *) extra;
395	outputs = (RROutput *) (crtcs + pScrPriv->numCrtcs);
396	modeinfos = (xRRModeInfo *) (outputs + pScrPriv->numOutputs);
397	names = (CARD8 *) (modeinfos + num_modes);
398
399	if (pScrPriv->primaryOutput && pScrPriv->primaryOutput->crtc)
400	{
401	    has_primary = 1;
402	    crtcs[0] = pScrPriv->primaryOutput->crtc->id;
403	    if (client->swapped)
404		swapl (&crtcs[0], n);
405	}
406
407	for (i = 0; i < pScrPriv->numCrtcs; i++)
408	{
409	    if (has_primary &&
410		pScrPriv->primaryOutput->crtc == pScrPriv->crtcs[i])
411	    {
412		has_primary = 0;
413		continue;
414	    }
415	    crtcs[i + has_primary] = pScrPriv->crtcs[i]->id;
416	    if (client->swapped)
417		swapl (&crtcs[i + has_primary], n);
418	}
419
420	for (i = 0; i < pScrPriv->numOutputs; i++)
421	{
422	    outputs[i] = pScrPriv->outputs[i]->id;
423	    if (client->swapped)
424		swapl (&outputs[i], n);
425	}
426
427	for (i = 0; i < num_modes; i++)
428	{
429	    RRModePtr	mode = modes[i];
430	    modeinfos[i] = mode->mode;
431	    if (client->swapped)
432	    {
433		swapl (&modeinfos[i].id, n);
434		swaps (&modeinfos[i].width, n);
435		swaps (&modeinfos[i].height, n);
436		swapl (&modeinfos[i].dotClock, n);
437		swaps (&modeinfos[i].hSyncStart, n);
438		swaps (&modeinfos[i].hSyncEnd, n);
439		swaps (&modeinfos[i].hTotal, n);
440		swaps (&modeinfos[i].hSkew, n);
441		swaps (&modeinfos[i].vSyncStart, n);
442		swaps (&modeinfos[i].vSyncEnd, n);
443		swaps (&modeinfos[i].vTotal, n);
444		swaps (&modeinfos[i].nameLength, n);
445		swapl (&modeinfos[i].modeFlags, n);
446	    }
447	    memcpy (names, mode->name,
448		    mode->mode.nameLength);
449	    names += mode->mode.nameLength;
450	}
451        free(modes);
452	assert (bytes_to_int32((char *) names - (char *) extra) == rep.length);
453    }
454
455    if (client->swapped) {
456	swaps(&rep.sequenceNumber, n);
457	swapl(&rep.length, n);
458	swapl(&rep.timestamp, n);
459	swapl(&rep.configTimestamp, n);
460	swaps(&rep.nCrtcs, n);
461	swaps(&rep.nOutputs, n);
462	swaps(&rep.nModes, n);
463	swaps(&rep.nbytesNames, n);
464    }
465    WriteToClient(client, sizeof(xRRGetScreenResourcesReply), (char *)&rep);
466    if (extraLen)
467    {
468	WriteToClient (client, extraLen, (char *) extra);
469	free(extra);
470    }
471    return Success;
472}
473
474int
475ProcRRGetScreenResources (ClientPtr client)
476{
477    return rrGetScreenResources(client, TRUE);
478}
479
480int
481ProcRRGetScreenResourcesCurrent (ClientPtr client)
482{
483    return rrGetScreenResources(client, FALSE);
484}
485
486typedef struct _RR10Data {
487    RRScreenSizePtr sizes;
488    int		    nsize;
489    int		    nrefresh;
490    int		    size;
491    CARD16	    refresh;
492} RR10DataRec, *RR10DataPtr;
493
494/*
495 * Convert 1.2 monitor data into 1.0 screen data
496 */
497static RR10DataPtr
498RR10GetData (ScreenPtr pScreen, RROutputPtr output)
499{
500    RR10DataPtr	    data;
501    RRScreenSizePtr size;
502    int		    nmode = output->numModes + output->numUserModes;
503    int		    o, os, l, r;
504    RRScreenRatePtr refresh;
505    CARD16	    vRefresh;
506    RRModePtr	    mode;
507    Bool	    *used;
508
509    /* Make sure there is plenty of space for any combination */
510    data = malloc (sizeof (RR10DataRec) +
511		   sizeof (RRScreenSize) * nmode +
512		   sizeof (RRScreenRate) * nmode +
513		   sizeof (Bool) * nmode);
514    if (!data)
515	return NULL;
516    size = (RRScreenSizePtr) (data + 1);
517    refresh = (RRScreenRatePtr) (size + nmode);
518    used = (Bool *) (refresh + nmode);
519    memset (used, '\0', sizeof (Bool) * nmode);
520    data->sizes = size;
521    data->nsize = 0;
522    data->nrefresh = 0;
523    data->size = 0;
524    data->refresh = 0;
525
526    /*
527     * find modes not yet listed
528     */
529    for (o = 0; o < output->numModes + output->numUserModes; o++)
530    {
531	if (used[o]) continue;
532
533	if (o < output->numModes)
534	    mode = output->modes[o];
535	else
536	    mode = output->userModes[o - output->numModes];
537
538	l = data->nsize;
539	size[l].id = data->nsize;
540	size[l].width = mode->mode.width;
541	size[l].height = mode->mode.height;
542	if (output->mmWidth && output->mmHeight) {
543	    size[l].mmWidth = output->mmWidth;
544	    size[l].mmHeight = output->mmHeight;
545	} else {
546	    size[l].mmWidth = pScreen->mmWidth;
547	    size[l].mmHeight = pScreen->mmHeight;
548	}
549	size[l].nRates = 0;
550	size[l].pRates = &refresh[data->nrefresh];
551	data->nsize++;
552
553	/*
554	 * Find all modes with matching size
555	 */
556	for (os = o; os < output->numModes + output->numUserModes; os++)
557	{
558	    if (os < output->numModes)
559		mode = output->modes[os];
560	    else
561		mode = output->userModes[os - output->numModes];
562	    if (mode->mode.width == size[l].width &&
563		mode->mode.height == size[l].height)
564	    {
565		vRefresh = RRVerticalRefresh (&mode->mode);
566		used[os] = TRUE;
567
568		for (r = 0; r < size[l].nRates; r++)
569		    if (vRefresh == size[l].pRates[r].rate)
570			break;
571		if (r == size[l].nRates)
572		{
573		    size[l].pRates[r].rate = vRefresh;
574		    size[l].pRates[r].mode = mode;
575		    size[l].nRates++;
576		    data->nrefresh++;
577		}
578		if (mode == output->crtc->mode)
579		{
580		    data->size = l;
581		    data->refresh = vRefresh;
582		}
583	    }
584	}
585    }
586    return data;
587}
588
589int
590ProcRRGetScreenInfo (ClientPtr client)
591{
592    REQUEST(xRRGetScreenInfoReq);
593    xRRGetScreenInfoReply   rep;
594    WindowPtr	    	    pWin;
595    int			    n, rc;
596    ScreenPtr		    pScreen;
597    rrScrPrivPtr	    pScrPriv;
598    CARD8		    *extra;
599    unsigned long	    extraLen;
600    RROutputPtr		    output;
601
602    REQUEST_SIZE_MATCH(xRRGetScreenInfoReq);
603    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
604    if (rc != Success)
605	return rc;
606
607    pScreen = pWin->drawable.pScreen;
608    pScrPriv = rrGetScrPriv(pScreen);
609    rep.pad = 0;
610
611    if (pScrPriv)
612	if (!RRGetInfo (pScreen, TRUE))
613	    return BadAlloc;
614
615    output = RRFirstOutput (pScreen);
616
617    if (!pScrPriv || !output)
618    {
619	rep.type = X_Reply;
620	rep.setOfRotations = RR_Rotate_0;
621	rep.sequenceNumber = client->sequence;
622	rep.length = 0;
623	rep.root = pWin->drawable.pScreen->root->drawable.id;
624	rep.timestamp = currentTime.milliseconds;
625	rep.configTimestamp = currentTime.milliseconds;
626	rep.nSizes = 0;
627	rep.sizeID = 0;
628	rep.rotation = RR_Rotate_0;
629	rep.rate = 0;
630	rep.nrateEnts = 0;
631	extra = 0;
632	extraLen = 0;
633    }
634    else
635    {
636	int			i, j;
637	xScreenSizes		*size;
638	CARD16			*rates;
639	CARD8			*data8;
640	Bool			has_rate = RRClientKnowsRates (client);
641	RR10DataPtr		pData;
642	RRScreenSizePtr		pSize;
643
644	pData = RR10GetData (pScreen, output);
645	if (!pData)
646	    return BadAlloc;
647
648	rep.type = X_Reply;
649	rep.setOfRotations = output->crtc->rotations;
650	rep.sequenceNumber = client->sequence;
651	rep.length = 0;
652	rep.root = pWin->drawable.pScreen->root->drawable.id;
653	rep.timestamp = pScrPriv->lastSetTime.milliseconds;
654	rep.configTimestamp = pScrPriv->lastConfigTime.milliseconds;
655	rep.rotation = output->crtc->rotation;
656	rep.nSizes = pData->nsize;
657        rep.nrateEnts = pData->nrefresh + pData->nsize;
658	rep.sizeID = pData->size;
659	rep.rate = pData->refresh;
660
661	extraLen = rep.nSizes * sizeof (xScreenSizes);
662	if (has_rate)
663		extraLen += rep.nrateEnts * sizeof (CARD16);
664
665	if (extraLen)
666	{
667	    extra = (CARD8 *) malloc(extraLen);
668	    if (!extra)
669	    {
670		free(pData);
671		return BadAlloc;
672	    }
673	}
674	else
675	    extra = NULL;
676
677	/*
678	 * First comes the size information
679	 */
680	size = (xScreenSizes *) extra;
681	rates = (CARD16 *) (size + rep.nSizes);
682	for (i = 0; i < pData->nsize; i++)
683	{
684	    pSize = &pData->sizes[i];
685	    size->widthInPixels = pSize->width;
686	    size->heightInPixels = pSize->height;
687	    size->widthInMillimeters = pSize->mmWidth;
688	    size->heightInMillimeters = pSize->mmHeight;
689	    if (client->swapped)
690	    {
691	        swaps (&size->widthInPixels, n);
692	        swaps (&size->heightInPixels, n);
693	        swaps (&size->widthInMillimeters, n);
694	        swaps (&size->heightInMillimeters, n);
695	    }
696	    size++;
697	    if (has_rate)
698	    {
699		*rates = pSize->nRates;
700		if (client->swapped)
701		{
702		    swaps (rates, n);
703		}
704		rates++;
705		for (j = 0; j < pSize->nRates; j++)
706		{
707		    *rates = pSize->pRates[j].rate;
708		    if (client->swapped)
709		    {
710			swaps (rates, n);
711		    }
712		    rates++;
713		}
714	    }
715	}
716        free(pData);
717
718	data8 = (CARD8 *) rates;
719
720	if (data8 - (CARD8 *) extra != extraLen)
721	    FatalError ("RRGetScreenInfo bad extra len %ld != %ld\n",
722			(unsigned long)(data8 - (CARD8 *) extra), extraLen);
723	rep.length =  bytes_to_int32(extraLen);
724    }
725    if (client->swapped) {
726	swaps(&rep.sequenceNumber, n);
727	swapl(&rep.length, n);
728	swapl(&rep.timestamp, n);
729	swaps(&rep.rotation, n);
730	swaps(&rep.nSizes, n);
731	swaps(&rep.sizeID, n);
732	swaps(&rep.rate, n);
733	swaps(&rep.nrateEnts, n);
734    }
735    WriteToClient(client, sizeof(xRRGetScreenInfoReply), (char *)&rep);
736    if (extraLen)
737    {
738	WriteToClient (client, extraLen, (char *) extra);
739	free(extra);
740    }
741    return Success;
742}
743
744int
745ProcRRSetScreenConfig (ClientPtr client)
746{
747    REQUEST(xRRSetScreenConfigReq);
748    xRRSetScreenConfigReply rep;
749    DrawablePtr		    pDraw;
750    int			    n, rc;
751    ScreenPtr		    pScreen;
752    rrScrPrivPtr	    pScrPriv;
753    TimeStamp		    time;
754    int			    i;
755    Rotation		    rotation;
756    int			    rate;
757    Bool		    has_rate;
758    RROutputPtr		    output;
759    RRCrtcPtr		    crtc;
760    RRModePtr		    mode;
761    RR10DataPtr		    pData = NULL;
762    RRScreenSizePtr    	    pSize;
763    int			    width, height;
764
765    UpdateCurrentTime ();
766
767    if (RRClientKnowsRates (client))
768    {
769	REQUEST_SIZE_MATCH (xRRSetScreenConfigReq);
770	has_rate = TRUE;
771    }
772    else
773    {
774	REQUEST_SIZE_MATCH (xRR1_0SetScreenConfigReq);
775	has_rate = FALSE;
776    }
777
778    rc = dixLookupDrawable(&pDraw, stuff->drawable, client, 0, DixWriteAccess);
779    if (rc != Success)
780	return rc;
781
782    pScreen = pDraw->pScreen;
783
784    pScrPriv = rrGetScrPriv(pScreen);
785
786    time = ClientTimeToServerTime(stuff->timestamp);
787
788    if (!pScrPriv)
789    {
790	time = currentTime;
791	rep.status = RRSetConfigFailed;
792	goto sendReply;
793    }
794    if (!RRGetInfo (pScreen, FALSE))
795	return BadAlloc;
796
797    output = RRFirstOutput (pScreen);
798    if (!output)
799    {
800	time = currentTime;
801	rep.status = RRSetConfigFailed;
802	goto sendReply;
803    }
804
805    crtc = output->crtc;
806
807    /*
808     * If the client's config timestamp is not the same as the last config
809     * timestamp, then the config information isn't up-to-date and
810     * can't even be validated.
811     *
812     * Note that the client only knows about the milliseconds part of the
813     * timestamp, so using CompareTimeStamps here would cause randr to suddenly
814     * stop working after several hours have passed (freedesktop bug #6502).
815     */
816    if (stuff->configTimestamp != pScrPriv->lastConfigTime.milliseconds)
817    {
818	rep.status = RRSetConfigInvalidConfigTime;
819	goto sendReply;
820    }
821
822    pData = RR10GetData (pScreen, output);
823    if (!pData)
824	return BadAlloc;
825
826    if (stuff->sizeID >= pData->nsize)
827    {
828	/*
829	 * Invalid size ID
830	 */
831	client->errorValue = stuff->sizeID;
832	free(pData);
833	return BadValue;
834    }
835    pSize = &pData->sizes[stuff->sizeID];
836
837    /*
838     * Validate requested rotation
839     */
840    rotation = (Rotation) stuff->rotation;
841
842    /* test the rotation bits only! */
843    switch (rotation & 0xf) {
844    case RR_Rotate_0:
845    case RR_Rotate_90:
846    case RR_Rotate_180:
847    case RR_Rotate_270:
848	break;
849    default:
850	/*
851	 * Invalid rotation
852	 */
853	client->errorValue = stuff->rotation;
854	free(pData);
855	return BadValue;
856    }
857
858    if ((~crtc->rotations) & rotation)
859    {
860	/*
861	 * requested rotation or reflection not supported by screen
862	 */
863	client->errorValue = stuff->rotation;
864	free(pData);
865	return BadMatch;
866    }
867
868    /*
869     * Validate requested refresh
870     */
871    if (has_rate)
872	rate = (int) stuff->rate;
873    else
874	rate = 0;
875
876    if (rate)
877    {
878	for (i = 0; i < pSize->nRates; i++)
879	{
880	    if (pSize->pRates[i].rate == rate)
881		break;
882	}
883	if (i == pSize->nRates)
884	{
885	    /*
886	     * Invalid rate
887	     */
888	    client->errorValue = rate;
889	    free(pData);
890	    return BadValue;
891	}
892	mode = pSize->pRates[i].mode;
893    }
894    else
895	mode = pSize->pRates[0].mode;
896
897    /*
898     * Make sure the requested set-time is not older than
899     * the last set-time
900     */
901    if (CompareTimeStamps (time, pScrPriv->lastSetTime) < 0)
902    {
903	rep.status = RRSetConfigInvalidTime;
904	goto sendReply;
905    }
906
907    /*
908     * If the screen size is changing, adjust all of the other outputs
909     * to fit the new size, mirroring as much as possible
910     */
911    width = mode->mode.width;
912    height = mode->mode.height;
913    if (rotation & (RR_Rotate_90|RR_Rotate_270))
914    {
915	width = mode->mode.height;
916	height = mode->mode.width;
917    }
918
919    if (width < pScrPriv->minWidth || pScrPriv->maxWidth < width) {
920	client->errorValue = width;
921	free(pData);
922	return BadValue;
923    }
924    if (height < pScrPriv->minHeight || pScrPriv->maxHeight < height) {
925	client->errorValue = height;
926	free(pData);
927	return BadValue;
928    }
929
930    if (width != pScreen->width || height != pScreen->height)
931    {
932	int	c;
933
934	for (c = 0; c < pScrPriv->numCrtcs; c++)
935	{
936	    if (!RRCrtcSet (pScrPriv->crtcs[c], NULL, 0, 0, RR_Rotate_0,
937			    0, NULL))
938	    {
939		rep.status = RRSetConfigFailed;
940		/* XXX recover from failure */
941		goto sendReply;
942	    }
943	}
944	if (!RRScreenSizeSet (pScreen, width, height,
945			      pScreen->mmWidth, pScreen->mmHeight))
946	{
947	    rep.status = RRSetConfigFailed;
948	    /* XXX recover from failure */
949	    goto sendReply;
950	}
951    }
952
953    if (!RRCrtcSet (crtc, mode, 0, 0, stuff->rotation, 1, &output))
954	rep.status = RRSetConfigFailed;
955    else {
956	pScrPriv->lastSetTime = time;
957	rep.status = RRSetConfigSuccess;
958    }
959
960    /*
961     * XXX Configure other crtcs to mirror as much as possible
962     */
963
964sendReply:
965
966    free(pData);
967
968    rep.type = X_Reply;
969    /* rep.status has already been filled in */
970    rep.length = 0;
971    rep.sequenceNumber = client->sequence;
972
973    rep.newTimestamp = pScrPriv->lastSetTime.milliseconds;
974    rep.newConfigTimestamp = pScrPriv->lastConfigTime.milliseconds;
975    rep.root = pDraw->pScreen->root->drawable.id;
976
977    if (client->swapped)
978    {
979    	swaps(&rep.sequenceNumber, n);
980    	swapl(&rep.length, n);
981	swapl(&rep.newTimestamp, n);
982	swapl(&rep.newConfigTimestamp, n);
983	swapl(&rep.root, n);
984    }
985    WriteToClient(client, sizeof(xRRSetScreenConfigReply), (char *)&rep);
986
987    return Success;
988}
989
990static CARD16
991RR10CurrentSizeID (ScreenPtr pScreen)
992{
993    CARD16	sizeID = 0xffff;
994    RROutputPtr output = RRFirstOutput (pScreen);
995
996    if (output)
997    {
998	RR10DataPtr data = RR10GetData (pScreen, output);
999	if (data)
1000	{
1001	    int i;
1002	    for (i = 0; i < data->nsize; i++)
1003		if (data->sizes[i].width == pScreen->width &&
1004		    data->sizes[i].height == pScreen->height)
1005		{
1006		    sizeID = (CARD16) i;
1007		    break;
1008		}
1009	    free(data);
1010	}
1011    }
1012    return sizeID;
1013}
1014