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