rrscreen.c revision b86d567b
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	if (has_rate)
669		extraLen += rep.nrateEnts * sizeof (CARD16);
670
671	if (extraLen)
672	{
673	    extra = (CARD8 *) xalloc (extraLen);
674	    if (!extra)
675	    {
676		xfree (pData);
677		return BadAlloc;
678	    }
679	}
680	else
681	    extra = NULL;
682
683	/*
684	 * First comes the size information
685	 */
686	size = (xScreenSizes *) extra;
687	rates = (CARD16 *) (size + rep.nSizes);
688	for (i = 0; i < pData->nsize; i++)
689	{
690	    pSize = &pData->sizes[i];
691	    size->widthInPixels = pSize->width;
692	    size->heightInPixels = pSize->height;
693	    size->widthInMillimeters = pSize->mmWidth;
694	    size->heightInMillimeters = pSize->mmHeight;
695	    if (client->swapped)
696	    {
697	        swaps (&size->widthInPixels, n);
698	        swaps (&size->heightInPixels, n);
699	        swaps (&size->widthInMillimeters, n);
700	        swaps (&size->heightInMillimeters, n);
701	    }
702	    size++;
703	    if (has_rate)
704	    {
705		*rates = pSize->nRates;
706		if (client->swapped)
707		{
708		    swaps (rates, n);
709		}
710		rates++;
711		for (j = 0; j < pSize->nRates; j++)
712		{
713		    *rates = pSize->pRates[j].rate;
714		    if (client->swapped)
715		    {
716			swaps (rates, n);
717		    }
718		    rates++;
719		}
720	    }
721	}
722        xfree (pData);
723
724	data8 = (CARD8 *) rates;
725
726	if (data8 - (CARD8 *) extra != extraLen)
727	    FatalError ("RRGetScreenInfo bad extra len %ld != %ld\n",
728			(unsigned long)(data8 - (CARD8 *) extra), extraLen);
729	rep.length =  (extraLen + 3) >> 2;
730    }
731    if (client->swapped) {
732	swaps(&rep.sequenceNumber, n);
733	swapl(&rep.length, n);
734	swapl(&rep.timestamp, n);
735	swaps(&rep.rotation, n);
736	swaps(&rep.nSizes, n);
737	swaps(&rep.sizeID, n);
738	swaps(&rep.rate, n);
739	swaps(&rep.nrateEnts, n);
740    }
741    WriteToClient(client, sizeof(xRRGetScreenInfoReply), (char *)&rep);
742    if (extraLen)
743    {
744	WriteToClient (client, extraLen, (char *) extra);
745	xfree (extra);
746    }
747    return (client->noClientException);
748}
749
750int
751ProcRRSetScreenConfig (ClientPtr client)
752{
753    REQUEST(xRRSetScreenConfigReq);
754    xRRSetScreenConfigReply rep;
755    DrawablePtr		    pDraw;
756    int			    n, rc;
757    ScreenPtr		    pScreen;
758    rrScrPrivPtr	    pScrPriv;
759    TimeStamp		    time;
760    int			    i;
761    Rotation		    rotation;
762    int			    rate;
763    Bool		    has_rate;
764    RROutputPtr		    output;
765    RRCrtcPtr		    crtc;
766    RRModePtr		    mode;
767    RR10DataPtr		    pData = NULL;
768    RRScreenSizePtr    	    pSize;
769    int			    width, height;
770
771    UpdateCurrentTime ();
772
773    if (RRClientKnowsRates (client))
774    {
775	REQUEST_SIZE_MATCH (xRRSetScreenConfigReq);
776	has_rate = TRUE;
777    }
778    else
779    {
780	REQUEST_SIZE_MATCH (xRR1_0SetScreenConfigReq);
781	has_rate = FALSE;
782    }
783
784    rc = dixLookupDrawable(&pDraw, stuff->drawable, client, 0, DixWriteAccess);
785    if (rc != Success)
786	return rc;
787
788    pScreen = pDraw->pScreen;
789
790    pScrPriv = rrGetScrPriv(pScreen);
791
792    time = ClientTimeToServerTime(stuff->timestamp);
793
794    if (!pScrPriv)
795    {
796	time = currentTime;
797	rep.status = RRSetConfigFailed;
798	goto sendReply;
799    }
800    if (!RRGetInfo (pScreen, FALSE))
801	return BadAlloc;
802
803    output = RRFirstOutput (pScreen);
804    if (!output)
805    {
806	time = currentTime;
807	rep.status = RRSetConfigFailed;
808	goto sendReply;
809    }
810
811    crtc = output->crtc;
812
813    /*
814     * If the client's config timestamp is not the same as the last config
815     * timestamp, then the config information isn't up-to-date and
816     * can't even be validated.
817     *
818     * Note that the client only knows about the milliseconds part of the
819     * timestamp, so using CompareTimeStamps here would cause randr to suddenly
820     * stop working after several hours have passed (freedesktop bug #6502).
821     */
822    if (stuff->configTimestamp != pScrPriv->lastConfigTime.milliseconds)
823    {
824	rep.status = RRSetConfigInvalidConfigTime;
825	goto sendReply;
826    }
827
828    pData = RR10GetData (pScreen, output);
829    if (!pData)
830	return BadAlloc;
831
832    if (stuff->sizeID >= pData->nsize)
833    {
834	/*
835	 * Invalid size ID
836	 */
837	client->errorValue = stuff->sizeID;
838	xfree (pData);
839	return BadValue;
840    }
841    pSize = &pData->sizes[stuff->sizeID];
842
843    /*
844     * Validate requested rotation
845     */
846    rotation = (Rotation) stuff->rotation;
847
848    /* test the rotation bits only! */
849    switch (rotation & 0xf) {
850    case RR_Rotate_0:
851    case RR_Rotate_90:
852    case RR_Rotate_180:
853    case RR_Rotate_270:
854	break;
855    default:
856	/*
857	 * Invalid rotation
858	 */
859	client->errorValue = stuff->rotation;
860	xfree (pData);
861	return BadValue;
862    }
863
864    if ((~crtc->rotations) & rotation)
865    {
866	/*
867	 * requested rotation or reflection not supported by screen
868	 */
869	client->errorValue = stuff->rotation;
870	xfree (pData);
871	return BadMatch;
872    }
873
874    /*
875     * Validate requested refresh
876     */
877    if (has_rate)
878	rate = (int) stuff->rate;
879    else
880	rate = 0;
881
882    if (rate)
883    {
884	for (i = 0; i < pSize->nRates; i++)
885	{
886	    if (pSize->pRates[i].rate == rate)
887		break;
888	}
889	if (i == pSize->nRates)
890	{
891	    /*
892	     * Invalid rate
893	     */
894	    client->errorValue = rate;
895	    xfree (pData);
896	    return BadValue;
897	}
898	mode = pSize->pRates[i].mode;
899    }
900    else
901	mode = pSize->pRates[0].mode;
902
903    /*
904     * Make sure the requested set-time is not older than
905     * the last set-time
906     */
907    if (CompareTimeStamps (time, pScrPriv->lastSetTime) < 0)
908    {
909	rep.status = RRSetConfigInvalidTime;
910	goto sendReply;
911    }
912
913    /*
914     * If the screen size is changing, adjust all of the other outputs
915     * to fit the new size, mirroring as much as possible
916     */
917    width = mode->mode.width;
918    height = mode->mode.height;
919    if (rotation & (RR_Rotate_90|RR_Rotate_270))
920    {
921	width = mode->mode.height;
922	height = mode->mode.width;
923    }
924    if (width != pScreen->width || height != pScreen->height)
925    {
926	int	c;
927
928	for (c = 0; c < pScrPriv->numCrtcs; c++)
929	{
930	    if (!RRCrtcSet (pScrPriv->crtcs[c], NULL, 0, 0, RR_Rotate_0,
931			    0, NULL))
932	    {
933		rep.status = RRSetConfigFailed;
934		/* XXX recover from failure */
935		goto sendReply;
936	    }
937	}
938	if (!RRScreenSizeSet (pScreen, width, height,
939			      pScreen->mmWidth, pScreen->mmHeight))
940	{
941	    rep.status = RRSetConfigFailed;
942	    /* XXX recover from failure */
943	    goto sendReply;
944	}
945    }
946
947    if (!RRCrtcSet (crtc, mode, 0, 0, stuff->rotation, 1, &output))
948	rep.status = RRSetConfigFailed;
949    else {
950	pScrPriv->lastSetTime = time;
951	rep.status = RRSetConfigSuccess;
952    }
953
954    /*
955     * XXX Configure other crtcs to mirror as much as possible
956     */
957
958sendReply:
959
960    if (pData)
961	xfree (pData);
962
963    rep.type = X_Reply;
964    /* rep.status has already been filled in */
965    rep.length = 0;
966    rep.sequenceNumber = client->sequence;
967
968    rep.newTimestamp = pScrPriv->lastSetTime.milliseconds;
969    rep.newConfigTimestamp = pScrPriv->lastConfigTime.milliseconds;
970    rep.root = WindowTable[pDraw->pScreen->myNum]->drawable.id;
971
972    if (client->swapped)
973    {
974    	swaps(&rep.sequenceNumber, n);
975    	swapl(&rep.length, n);
976	swapl(&rep.newTimestamp, n);
977	swapl(&rep.newConfigTimestamp, n);
978	swapl(&rep.root, n);
979    }
980    WriteToClient(client, sizeof(xRRSetScreenConfigReply), (char *)&rep);
981
982    return (client->noClientException);
983}
984
985static CARD16
986RR10CurrentSizeID (ScreenPtr pScreen)
987{
988    CARD16	sizeID = 0xffff;
989    RROutputPtr output = RRFirstOutput (pScreen);
990
991    if (output)
992    {
993	RR10DataPtr data = RR10GetData (pScreen, output);
994	if (data)
995	{
996	    int i;
997	    for (i = 0; i < data->nsize; i++)
998		if (data->sizes[i].width == pScreen->width &&
999		    data->sizes[i].height == pScreen->height)
1000		{
1001		    sizeID = (CARD16) i;
1002		    break;
1003		}
1004	    xfree (data);
1005	}
1006    }
1007    return sizeID;
1008}
1009