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