1/*
2 * Copyright © 2000 Compaq Computer Corporation
3 * Copyright © 2002 Hewlett-Packard Company
4 * Copyright © 2006 Intel Corporation
5 * Copyright © 2017 Keith Packard
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that copyright
10 * notice and this permission notice appear in supporting documentation, and
11 * that the name of the copyright holders not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission.  The copyright holders make no representations
14 * about the suitability of this software for any purpose.  It is provided "as
15 * is" without express or implied warranty.
16 *
17 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
23 * OF THIS SOFTWARE.
24 *
25 * Author:  Jim Gettys, Hewlett-Packard Company, Inc.
26 *	    Keith Packard, Intel Corporation
27 */
28
29#ifdef HAVE_DIX_CONFIG_H
30#include <dix-config.h>
31#endif
32
33#include "randrstr.h"
34#include "extinit.h"
35
36/* From render.h */
37#ifndef SubPixelUnknown
38#define SubPixelUnknown 0
39#endif
40
41#define RR_VALIDATE
42static int RRNScreens;
43
44#define wrap(priv,real,mem,func) {\
45    priv->mem = real->mem; \
46    real->mem = func; \
47}
48
49#define unwrap(priv,real,mem) {\
50    real->mem = priv->mem; \
51}
52
53static int ProcRRDispatch(ClientPtr pClient);
54static int SProcRRDispatch(ClientPtr pClient);
55
56int RREventBase;
57int RRErrorBase;
58RESTYPE RRClientType, RREventType;      /* resource types for event masks */
59DevPrivateKeyRec RRClientPrivateKeyRec;
60
61DevPrivateKeyRec rrPrivKeyRec;
62
63static void
64RRClientCallback(CallbackListPtr *list, void *closure, void *data)
65{
66    NewClientInfoRec *clientinfo = (NewClientInfoRec *) data;
67    ClientPtr pClient = clientinfo->client;
68
69    rrClientPriv(pClient);
70    RRTimesPtr pTimes = (RRTimesPtr) (pRRClient + 1);
71    int i;
72
73    pRRClient->major_version = 0;
74    pRRClient->minor_version = 0;
75    for (i = 0; i < screenInfo.numScreens; i++) {
76        ScreenPtr pScreen = screenInfo.screens[i];
77
78        rrScrPriv(pScreen);
79
80        if (pScrPriv) {
81            pTimes[i].setTime = pScrPriv->lastSetTime;
82            pTimes[i].configTime = pScrPriv->lastConfigTime;
83        }
84    }
85}
86
87static Bool
88RRCloseScreen(ScreenPtr pScreen)
89{
90    rrScrPriv(pScreen);
91    int j;
92    RRLeasePtr lease, next;
93
94    unwrap(pScrPriv, pScreen, CloseScreen);
95
96    xorg_list_for_each_entry_safe(lease, next, &pScrPriv->leases, list)
97        RRTerminateLease(lease);
98    for (j = pScrPriv->numCrtcs - 1; j >= 0; j--)
99        RRCrtcDestroy(pScrPriv->crtcs[j]);
100    for (j = pScrPriv->numOutputs - 1; j >= 0; j--)
101        RROutputDestroy(pScrPriv->outputs[j]);
102
103    if (pScrPriv->provider)
104        RRProviderDestroy(pScrPriv->provider);
105
106    RRMonitorClose(pScreen);
107
108    free(pScrPriv->crtcs);
109    free(pScrPriv->outputs);
110    free(pScrPriv);
111    RRNScreens -= 1;            /* ok, one fewer screen with RandR running */
112    return (*pScreen->CloseScreen) (pScreen);
113}
114
115static void
116SRRScreenChangeNotifyEvent(xRRScreenChangeNotifyEvent * from,
117                           xRRScreenChangeNotifyEvent * to)
118{
119    to->type = from->type;
120    to->rotation = from->rotation;
121    cpswaps(from->sequenceNumber, to->sequenceNumber);
122    cpswapl(from->timestamp, to->timestamp);
123    cpswapl(from->configTimestamp, to->configTimestamp);
124    cpswapl(from->root, to->root);
125    cpswapl(from->window, to->window);
126    cpswaps(from->sizeID, to->sizeID);
127    cpswaps(from->subpixelOrder, to->subpixelOrder);
128    cpswaps(from->widthInPixels, to->widthInPixels);
129    cpswaps(from->heightInPixels, to->heightInPixels);
130    cpswaps(from->widthInMillimeters, to->widthInMillimeters);
131    cpswaps(from->heightInMillimeters, to->heightInMillimeters);
132}
133
134static void
135SRRCrtcChangeNotifyEvent(xRRCrtcChangeNotifyEvent * from,
136                         xRRCrtcChangeNotifyEvent * to)
137{
138    to->type = from->type;
139    to->subCode = from->subCode;
140    cpswaps(from->sequenceNumber, to->sequenceNumber);
141    cpswapl(from->timestamp, to->timestamp);
142    cpswapl(from->window, to->window);
143    cpswapl(from->crtc, to->crtc);
144    cpswapl(from->mode, to->mode);
145    cpswaps(from->rotation, to->rotation);
146    /* pad1 */
147    cpswaps(from->x, to->x);
148    cpswaps(from->y, to->y);
149    cpswaps(from->width, to->width);
150    cpswaps(from->height, to->height);
151}
152
153static void
154SRROutputChangeNotifyEvent(xRROutputChangeNotifyEvent * from,
155                           xRROutputChangeNotifyEvent * to)
156{
157    to->type = from->type;
158    to->subCode = from->subCode;
159    cpswaps(from->sequenceNumber, to->sequenceNumber);
160    cpswapl(from->timestamp, to->timestamp);
161    cpswapl(from->configTimestamp, to->configTimestamp);
162    cpswapl(from->window, to->window);
163    cpswapl(from->output, to->output);
164    cpswapl(from->crtc, to->crtc);
165    cpswapl(from->mode, to->mode);
166    cpswaps(from->rotation, to->rotation);
167    to->connection = from->connection;
168    to->subpixelOrder = from->subpixelOrder;
169}
170
171static void
172SRROutputPropertyNotifyEvent(xRROutputPropertyNotifyEvent * from,
173                             xRROutputPropertyNotifyEvent * to)
174{
175    to->type = from->type;
176    to->subCode = from->subCode;
177    cpswaps(from->sequenceNumber, to->sequenceNumber);
178    cpswapl(from->window, to->window);
179    cpswapl(from->output, to->output);
180    cpswapl(from->atom, to->atom);
181    cpswapl(from->timestamp, to->timestamp);
182    to->state = from->state;
183    /* pad1 */
184    /* pad2 */
185    /* pad3 */
186    /* pad4 */
187}
188
189static void
190SRRProviderChangeNotifyEvent(xRRProviderChangeNotifyEvent * from,
191                         xRRProviderChangeNotifyEvent * to)
192{
193    to->type = from->type;
194    to->subCode = from->subCode;
195    cpswaps(from->sequenceNumber, to->sequenceNumber);
196    cpswapl(from->timestamp, to->timestamp);
197    cpswapl(from->window, to->window);
198    cpswapl(from->provider, to->provider);
199}
200
201static void
202SRRProviderPropertyNotifyEvent(xRRProviderPropertyNotifyEvent * from,
203                               xRRProviderPropertyNotifyEvent * to)
204{
205    to->type = from->type;
206    to->subCode = from->subCode;
207    cpswaps(from->sequenceNumber, to->sequenceNumber);
208    cpswapl(from->window, to->window);
209    cpswapl(from->provider, to->provider);
210    cpswapl(from->atom, to->atom);
211    cpswapl(from->timestamp, to->timestamp);
212    to->state = from->state;
213    /* pad1 */
214    /* pad2 */
215    /* pad3 */
216    /* pad4 */
217}
218
219static void _X_COLD
220SRRResourceChangeNotifyEvent(xRRResourceChangeNotifyEvent * from,
221                             xRRResourceChangeNotifyEvent * to)
222{
223    to->type = from->type;
224    to->subCode = from->subCode;
225    cpswaps(from->sequenceNumber, to->sequenceNumber);
226    cpswapl(from->timestamp, to->timestamp);
227    cpswapl(from->window, to->window);
228}
229
230static void _X_COLD
231SRRLeaseNotifyEvent(xRRLeaseNotifyEvent * from,
232                    xRRLeaseNotifyEvent * to)
233{
234    to->type = from->type;
235    to->subCode = from->subCode;
236    cpswaps(from->sequenceNumber, to->sequenceNumber);
237    cpswapl(from->timestamp, to->timestamp);
238    cpswapl(from->window, to->window);
239    cpswapl(from->lease, to->lease);
240    to->created = from->created;
241}
242
243static void _X_COLD
244SRRNotifyEvent(xEvent *from, xEvent *to)
245{
246    switch (from->u.u.detail) {
247    case RRNotify_CrtcChange:
248        SRRCrtcChangeNotifyEvent((xRRCrtcChangeNotifyEvent *) from,
249                                 (xRRCrtcChangeNotifyEvent *) to);
250        break;
251    case RRNotify_OutputChange:
252        SRROutputChangeNotifyEvent((xRROutputChangeNotifyEvent *) from,
253                                   (xRROutputChangeNotifyEvent *) to);
254        break;
255    case RRNotify_OutputProperty:
256        SRROutputPropertyNotifyEvent((xRROutputPropertyNotifyEvent *) from,
257                                     (xRROutputPropertyNotifyEvent *) to);
258        break;
259    case RRNotify_ProviderChange:
260        SRRProviderChangeNotifyEvent((xRRProviderChangeNotifyEvent *) from,
261                                   (xRRProviderChangeNotifyEvent *) to);
262        break;
263    case RRNotify_ProviderProperty:
264        SRRProviderPropertyNotifyEvent((xRRProviderPropertyNotifyEvent *) from,
265                                       (xRRProviderPropertyNotifyEvent *) to);
266        break;
267    case RRNotify_ResourceChange:
268        SRRResourceChangeNotifyEvent((xRRResourceChangeNotifyEvent *) from,
269                                   (xRRResourceChangeNotifyEvent *) to);
270        break;
271    case RRNotify_Lease:
272        SRRLeaseNotifyEvent((xRRLeaseNotifyEvent *) from,
273                            (xRRLeaseNotifyEvent *) to);
274        break;
275    default:
276        break;
277    }
278}
279
280static int RRGeneration;
281
282Bool
283RRInit(void)
284{
285    if (RRGeneration != serverGeneration) {
286        if (!RRModeInit())
287            return FALSE;
288        if (!RRCrtcInit())
289            return FALSE;
290        if (!RROutputInit())
291            return FALSE;
292        if (!RRProviderInit())
293            return FALSE;
294        if (!RRLeaseInit())
295            return FALSE;
296        RRGeneration = serverGeneration;
297    }
298    if (!dixRegisterPrivateKey(&rrPrivKeyRec, PRIVATE_SCREEN, 0))
299        return FALSE;
300
301    return TRUE;
302}
303
304Bool
305RRScreenInit(ScreenPtr pScreen)
306{
307    rrScrPrivPtr pScrPriv;
308
309    if (!RRInit())
310        return FALSE;
311
312    pScrPriv = (rrScrPrivPtr) calloc(1, sizeof(rrScrPrivRec));
313    if (!pScrPriv)
314        return FALSE;
315
316    SetRRScreen(pScreen, pScrPriv);
317
318    /*
319     * Calling function best set these function vectors
320     */
321    pScrPriv->rrGetInfo = 0;
322    pScrPriv->maxWidth = pScrPriv->minWidth = pScreen->width;
323    pScrPriv->maxHeight = pScrPriv->minHeight = pScreen->height;
324
325    pScrPriv->width = pScreen->width;
326    pScrPriv->height = pScreen->height;
327    pScrPriv->mmWidth = pScreen->mmWidth;
328    pScrPriv->mmHeight = pScreen->mmHeight;
329#if RANDR_12_INTERFACE
330    pScrPriv->rrScreenSetSize = NULL;
331    pScrPriv->rrCrtcSet = NULL;
332    pScrPriv->rrCrtcSetGamma = NULL;
333#endif
334#if RANDR_10_INTERFACE
335    pScrPriv->rrSetConfig = 0;
336    pScrPriv->rotations = RR_Rotate_0;
337    pScrPriv->reqWidth = pScreen->width;
338    pScrPriv->reqHeight = pScreen->height;
339    pScrPriv->nSizes = 0;
340    pScrPriv->pSizes = NULL;
341    pScrPriv->rotation = RR_Rotate_0;
342    pScrPriv->rate = 0;
343    pScrPriv->size = 0;
344#endif
345
346    /*
347     * This value doesn't really matter -- any client must call
348     * GetScreenInfo before reading it which will automatically update
349     * the time
350     */
351    pScrPriv->lastSetTime = currentTime;
352    pScrPriv->lastConfigTime = currentTime;
353
354    wrap(pScrPriv, pScreen, CloseScreen, RRCloseScreen);
355
356    pScreen->ConstrainCursorHarder = RRConstrainCursorHarder;
357    pScreen->ReplaceScanoutPixmap = RRReplaceScanoutPixmap;
358    pScrPriv->numOutputs = 0;
359    pScrPriv->outputs = NULL;
360    pScrPriv->numCrtcs = 0;
361    pScrPriv->crtcs = NULL;
362
363    xorg_list_init(&pScrPriv->leases);
364
365    RRMonitorInit(pScreen);
366
367    RRNScreens += 1;            /* keep count of screens that implement randr */
368    return TRUE;
369}
370
371 /*ARGSUSED*/ static int
372RRFreeClient(void *data, XID id)
373{
374    RREventPtr pRREvent;
375    WindowPtr pWin;
376    RREventPtr *pHead, pCur, pPrev;
377
378    pRREvent = (RREventPtr) data;
379    pWin = pRREvent->window;
380    dixLookupResourceByType((void **) &pHead, pWin->drawable.id,
381                            RREventType, serverClient, DixDestroyAccess);
382    if (pHead) {
383        pPrev = 0;
384        for (pCur = *pHead; pCur && pCur != pRREvent; pCur = pCur->next)
385            pPrev = pCur;
386        if (pCur) {
387            if (pPrev)
388                pPrev->next = pRREvent->next;
389            else
390                *pHead = pRREvent->next;
391        }
392    }
393    free((void *) pRREvent);
394    return 1;
395}
396
397 /*ARGSUSED*/ static int
398RRFreeEvents(void *data, XID id)
399{
400    RREventPtr *pHead, pCur, pNext;
401
402    pHead = (RREventPtr *) data;
403    for (pCur = *pHead; pCur; pCur = pNext) {
404        pNext = pCur->next;
405        FreeResource(pCur->clientResource, RRClientType);
406        free((void *) pCur);
407    }
408    free((void *) pHead);
409    return 1;
410}
411
412void
413RRExtensionInit(void)
414{
415    ExtensionEntry *extEntry;
416
417    if (RRNScreens == 0)
418        return;
419
420    if (!dixRegisterPrivateKey(&RRClientPrivateKeyRec, PRIVATE_CLIENT,
421                               sizeof(RRClientRec) +
422                               screenInfo.numScreens * sizeof(RRTimesRec)))
423        return;
424    if (!AddCallback(&ClientStateCallback, RRClientCallback, 0))
425        return;
426
427    RRClientType = CreateNewResourceType(RRFreeClient, "RandRClient");
428    if (!RRClientType)
429        return;
430    RREventType = CreateNewResourceType(RRFreeEvents, "RandREvent");
431    if (!RREventType)
432        return;
433    extEntry = AddExtension(RANDR_NAME, RRNumberEvents, RRNumberErrors,
434                            ProcRRDispatch, SProcRRDispatch,
435                            NULL, StandardMinorOpcode);
436    if (!extEntry)
437        return;
438    RRErrorBase = extEntry->errorBase;
439    RREventBase = extEntry->eventBase;
440    EventSwapVector[RREventBase + RRScreenChangeNotify] = (EventSwapPtr)
441        SRRScreenChangeNotifyEvent;
442    EventSwapVector[RREventBase + RRNotify] = (EventSwapPtr)
443        SRRNotifyEvent;
444
445    RRModeInitErrorValue();
446    RRCrtcInitErrorValue();
447    RROutputInitErrorValue();
448    RRProviderInitErrorValue();
449#ifdef PANORAMIX
450    RRXineramaExtensionInit();
451#endif
452}
453
454void
455RRResourcesChanged(ScreenPtr pScreen)
456{
457    rrScrPriv(pScreen);
458    pScrPriv->resourcesChanged = TRUE;
459
460    RRSetChanged(pScreen);
461}
462
463static void
464RRDeliverResourceEvent(ClientPtr client, WindowPtr pWin)
465{
466    ScreenPtr pScreen = pWin->drawable.pScreen;
467
468    rrScrPriv(pScreen);
469
470    xRRResourceChangeNotifyEvent re = {
471        .type = RRNotify + RREventBase,
472        .subCode = RRNotify_ResourceChange,
473        .timestamp = pScrPriv->lastSetTime.milliseconds,
474        .window = pWin->drawable.id
475    };
476
477    WriteEventsToClient(client, 1, (xEvent *) &re);
478}
479
480static int
481TellChanged(WindowPtr pWin, void *value)
482{
483    RREventPtr *pHead, pRREvent;
484    ClientPtr client;
485    ScreenPtr pScreen = pWin->drawable.pScreen;
486    ScreenPtr iter;
487    rrScrPrivPtr pSecondaryScrPriv;
488
489    rrScrPriv(pScreen);
490    int i;
491
492    dixLookupResourceByType((void **) &pHead, pWin->drawable.id,
493                            RREventType, serverClient, DixReadAccess);
494    if (!pHead)
495        return WT_WALKCHILDREN;
496
497    for (pRREvent = *pHead; pRREvent; pRREvent = pRREvent->next) {
498        client = pRREvent->client;
499        if (client == serverClient || client->clientGone)
500            continue;
501
502        if (pRREvent->mask & RRScreenChangeNotifyMask)
503            RRDeliverScreenEvent(client, pWin, pScreen);
504
505        if (pRREvent->mask & RRCrtcChangeNotifyMask) {
506            for (i = 0; i < pScrPriv->numCrtcs; i++) {
507                RRCrtcPtr crtc = pScrPriv->crtcs[i];
508
509                if (crtc->changed)
510                    RRDeliverCrtcEvent(client, pWin, crtc);
511            }
512
513            xorg_list_for_each_entry(iter, &pScreen->secondary_list, secondary_head) {
514                if (!iter->is_output_secondary)
515                    continue;
516
517                pSecondaryScrPriv = rrGetScrPriv(iter);
518                for (i = 0; i < pSecondaryScrPriv->numCrtcs; i++) {
519                    RRCrtcPtr crtc = pSecondaryScrPriv->crtcs[i];
520
521                    if (crtc->changed)
522                        RRDeliverCrtcEvent(client, pWin, crtc);
523                }
524            }
525        }
526
527        if (pRREvent->mask & RROutputChangeNotifyMask) {
528            for (i = 0; i < pScrPriv->numOutputs; i++) {
529                RROutputPtr output = pScrPriv->outputs[i];
530
531                if (output->changed)
532                    RRDeliverOutputEvent(client, pWin, output);
533            }
534
535            xorg_list_for_each_entry(iter, &pScreen->secondary_list, secondary_head) {
536                if (!iter->is_output_secondary)
537                    continue;
538
539                pSecondaryScrPriv = rrGetScrPriv(iter);
540                for (i = 0; i < pSecondaryScrPriv->numOutputs; i++) {
541                    RROutputPtr output = pSecondaryScrPriv->outputs[i];
542
543                    if (output->changed)
544                        RRDeliverOutputEvent(client, pWin, output);
545                }
546            }
547        }
548
549        if (pRREvent->mask & RRProviderChangeNotifyMask) {
550            xorg_list_for_each_entry(iter, &pScreen->secondary_list, secondary_head) {
551                pSecondaryScrPriv = rrGetScrPriv(iter);
552                if (pSecondaryScrPriv->provider->changed)
553                    RRDeliverProviderEvent(client, pWin, pSecondaryScrPriv->provider);
554            }
555        }
556
557        if (pRREvent->mask & RRResourceChangeNotifyMask) {
558            if (pScrPriv->resourcesChanged) {
559                RRDeliverResourceEvent(client, pWin);
560            }
561        }
562
563        if (pRREvent->mask & RRLeaseNotifyMask) {
564            if (pScrPriv->leasesChanged) {
565                RRDeliverLeaseEvent(client, pWin);
566            }
567        }
568    }
569    return WT_WALKCHILDREN;
570}
571
572void
573RRSetChanged(ScreenPtr pScreen)
574{
575    /* set changed bits on the primary screen only */
576    ScreenPtr primary;
577    rrScrPriv(pScreen);
578    rrScrPrivPtr primarysp;
579
580    if (pScreen->isGPU) {
581        primary = pScreen->current_primary;
582        if (!primary)
583            return;
584        primarysp = rrGetScrPriv(primary);
585    }
586    else {
587        primary = pScreen;
588        primarysp = pScrPriv;
589    }
590
591    primarysp->changed = TRUE;
592}
593
594/*
595 * Something changed; send events and adjust pointer position
596 */
597void
598RRTellChanged(ScreenPtr pScreen)
599{
600    ScreenPtr primary;
601    rrScrPriv(pScreen);
602    rrScrPrivPtr primarysp;
603    int i;
604    ScreenPtr iter;
605    rrScrPrivPtr pSecondaryScrPriv;
606
607    if (pScreen->isGPU) {
608        primary = pScreen->current_primary;
609        if (!primary)
610            return;
611        primarysp = rrGetScrPriv(primary);
612    }
613    else {
614        primary = pScreen;
615        primarysp = pScrPriv;
616    }
617
618    /* If there's no root window yet, can't send events */
619    if (!primary->root)
620        return;
621
622    xorg_list_for_each_entry(iter, &primary->secondary_list, secondary_head) {
623        pSecondaryScrPriv = rrGetScrPriv(iter);
624
625        if (!iter->is_output_secondary)
626            continue;
627
628        if (CompareTimeStamps(primarysp->lastSetTime,
629                              pSecondaryScrPriv->lastSetTime) == EARLIER) {
630            primarysp->lastSetTime = pSecondaryScrPriv->lastSetTime;
631        }
632    }
633
634    if (primarysp->changed) {
635        UpdateCurrentTimeIf();
636        if (primarysp->configChanged) {
637            primarysp->lastConfigTime = currentTime;
638            primarysp->configChanged = FALSE;
639        }
640        pScrPriv->changed = FALSE;
641        primarysp->changed = FALSE;
642
643        WalkTree(primary, TellChanged, (void *) primary);
644
645        primarysp->resourcesChanged = FALSE;
646
647        for (i = 0; i < pScrPriv->numOutputs; i++)
648            pScrPriv->outputs[i]->changed = FALSE;
649        for (i = 0; i < pScrPriv->numCrtcs; i++)
650            pScrPriv->crtcs[i]->changed = FALSE;
651
652        xorg_list_for_each_entry(iter, &primary->secondary_list, secondary_head) {
653            pSecondaryScrPriv = rrGetScrPriv(iter);
654            pSecondaryScrPriv->provider->changed = FALSE;
655            if (iter->is_output_secondary) {
656                for (i = 0; i < pSecondaryScrPriv->numOutputs; i++)
657                    pSecondaryScrPriv->outputs[i]->changed = FALSE;
658                for (i = 0; i < pSecondaryScrPriv->numCrtcs; i++)
659                    pSecondaryScrPriv->crtcs[i]->changed = FALSE;
660            }
661        }
662
663        if (primarysp->layoutChanged) {
664            pScrPriv->layoutChanged = FALSE;
665            RRPointerScreenConfigured(primary);
666            RRSendConfigNotify(primary);
667        }
668    }
669}
670
671/*
672 * Return the first output which is connected to an active CRTC
673 * Used in emulating 1.0 behaviour
674 */
675RROutputPtr
676RRFirstOutput(ScreenPtr pScreen)
677{
678    rrScrPriv(pScreen);
679    RROutputPtr output;
680    int i, j;
681
682    if (!pScrPriv)
683        return NULL;
684
685    if (pScrPriv->primaryOutput && pScrPriv->primaryOutput->crtc)
686        return pScrPriv->primaryOutput;
687
688    for (i = 0; i < pScrPriv->numCrtcs; i++) {
689        RRCrtcPtr crtc = pScrPriv->crtcs[i];
690
691        for (j = 0; j < pScrPriv->numOutputs; j++) {
692            output = pScrPriv->outputs[j];
693            if (output->crtc == crtc)
694                return output;
695        }
696    }
697    return NULL;
698}
699
700RRCrtcPtr
701RRFirstEnabledCrtc(ScreenPtr pScreen)
702{
703    rrScrPriv(pScreen);
704    RROutputPtr output;
705    int i, j;
706
707    if (!pScrPriv)
708        return NULL;
709
710    if (pScrPriv->primaryOutput && pScrPriv->primaryOutput->crtc &&
711        pScrPriv->primaryOutput->pScreen == pScreen)
712        return pScrPriv->primaryOutput->crtc;
713
714    for (i = 0; i < pScrPriv->numCrtcs; i++) {
715        RRCrtcPtr crtc = pScrPriv->crtcs[i];
716
717        for (j = 0; j < pScrPriv->numOutputs; j++) {
718            output = pScrPriv->outputs[j];
719            if (output->crtc == crtc && crtc->mode)
720                return crtc;
721        }
722    }
723    return NULL;
724}
725
726
727CARD16
728RRVerticalRefresh(xRRModeInfo * mode)
729{
730    CARD32 refresh;
731    CARD32 dots = mode->hTotal * mode->vTotal;
732
733    if (!dots)
734        return 0;
735    refresh = (mode->dotClock + dots / 2) / dots;
736    if (refresh > 0xffff)
737        refresh = 0xffff;
738    return (CARD16) refresh;
739}
740
741static int
742ProcRRDispatch(ClientPtr client)
743{
744    REQUEST(xReq);
745    if (stuff->data >= RRNumberRequests || !ProcRandrVector[stuff->data])
746        return BadRequest;
747    UpdateCurrentTimeIf();
748    return (*ProcRandrVector[stuff->data]) (client);
749}
750
751static int _X_COLD
752SProcRRDispatch(ClientPtr client)
753{
754    REQUEST(xReq);
755    if (stuff->data >= RRNumberRequests || !SProcRandrVector[stuff->data])
756        return BadRequest;
757    UpdateCurrentTimeIf();
758    return (*SProcRandrVector[stuff->data]) (client);
759}
760