105b261ecSmrg/*
205b261ecSmrg * Copyright © 2006 Keith Packard
305b261ecSmrg *
405b261ecSmrg * Permission to use, copy, modify, distribute, and sell this software and its
505b261ecSmrg * documentation for any purpose is hereby granted without fee, provided that
605b261ecSmrg * the above copyright notice appear in all copies and that both that copyright
705b261ecSmrg * notice and this permission notice appear in supporting documentation, and
805b261ecSmrg * that the name of the copyright holders not be used in advertising or
905b261ecSmrg * publicity pertaining to distribution of the software without specific,
1005b261ecSmrg * written prior permission.  The copyright holders make no representations
1105b261ecSmrg * about the suitability of this software for any purpose.  It is provided "as
1205b261ecSmrg * is" without express or implied warranty.
1305b261ecSmrg *
1405b261ecSmrg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1505b261ecSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1605b261ecSmrg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1705b261ecSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1805b261ecSmrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1905b261ecSmrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
2005b261ecSmrg * OF THIS SOFTWARE.
2105b261ecSmrg */
2205b261ecSmrg
2305b261ecSmrg#include "randrstr.h"
2405b261ecSmrg
2535c4bbdfSmrgRESTYPE RRModeType;
2605b261ecSmrg
2705b261ecSmrgstatic Bool
2835c4bbdfSmrgRRModeEqual(xRRModeInfo * a, xRRModeInfo * b)
2905b261ecSmrg{
3035c4bbdfSmrg    if (a->width != b->width)
3135c4bbdfSmrg        return FALSE;
3235c4bbdfSmrg    if (a->height != b->height)
3335c4bbdfSmrg        return FALSE;
3435c4bbdfSmrg    if (a->dotClock != b->dotClock)
3535c4bbdfSmrg        return FALSE;
3635c4bbdfSmrg    if (a->hSyncStart != b->hSyncStart)
3735c4bbdfSmrg        return FALSE;
3835c4bbdfSmrg    if (a->hSyncEnd != b->hSyncEnd)
3935c4bbdfSmrg        return FALSE;
4035c4bbdfSmrg    if (a->hTotal != b->hTotal)
4135c4bbdfSmrg        return FALSE;
4235c4bbdfSmrg    if (a->hSkew != b->hSkew)
4335c4bbdfSmrg        return FALSE;
4435c4bbdfSmrg    if (a->vSyncStart != b->vSyncStart)
4535c4bbdfSmrg        return FALSE;
4635c4bbdfSmrg    if (a->vSyncEnd != b->vSyncEnd)
4735c4bbdfSmrg        return FALSE;
4835c4bbdfSmrg    if (a->vTotal != b->vTotal)
4935c4bbdfSmrg        return FALSE;
5035c4bbdfSmrg    if (a->nameLength != b->nameLength)
5135c4bbdfSmrg        return FALSE;
5235c4bbdfSmrg    if (a->modeFlags != b->modeFlags)
5335c4bbdfSmrg        return FALSE;
5405b261ecSmrg    return TRUE;
5505b261ecSmrg}
5605b261ecSmrg
5705b261ecSmrg/*
5805b261ecSmrg * Keep a list so it's easy to find modes in the resource database.
5905b261ecSmrg */
6035c4bbdfSmrgstatic int num_modes;
6135c4bbdfSmrgstatic RRModePtr *modes;
6205b261ecSmrg
6305b261ecSmrgstatic RRModePtr
6435c4bbdfSmrgRRModeCreate(xRRModeInfo * modeInfo, const char *name, ScreenPtr userScreen)
6505b261ecSmrg{
6635c4bbdfSmrg    RRModePtr mode, *newModes;
6705b261ecSmrg
6835c4bbdfSmrg    if (!RRInit())
6935c4bbdfSmrg        return NULL;
7035c4bbdfSmrg
7135c4bbdfSmrg    mode = malloc(sizeof(RRModeRec) + modeInfo->nameLength + 1);
7205b261ecSmrg    if (!mode)
7335c4bbdfSmrg        return NULL;
7405b261ecSmrg    mode->refcnt = 1;
7505b261ecSmrg    mode->mode = *modeInfo;
7605b261ecSmrg    mode->name = (char *) (mode + 1);
7735c4bbdfSmrg    memcpy(mode->name, name, modeInfo->nameLength);
7805b261ecSmrg    mode->name[modeInfo->nameLength] = '\0';
7905b261ecSmrg    mode->userScreen = userScreen;
8005b261ecSmrg
8105b261ecSmrg    if (num_modes)
8235c4bbdfSmrg        newModes = reallocarray(modes, num_modes + 1, sizeof(RRModePtr));
8305b261ecSmrg    else
8435c4bbdfSmrg        newModes = malloc(sizeof(RRModePtr));
8505b261ecSmrg
8635c4bbdfSmrg    if (!newModes) {
8735c4bbdfSmrg        free(mode);
8835c4bbdfSmrg        return NULL;
8905b261ecSmrg    }
9005b261ecSmrg
9105b261ecSmrg    mode->mode.id = FakeClientID(0);
9235c4bbdfSmrg    if (!AddResource(mode->mode.id, RRModeType, (void *) mode)) {
9335c4bbdfSmrg        free(newModes);
9435c4bbdfSmrg        return NULL;
9535c4bbdfSmrg    }
9605b261ecSmrg    modes = newModes;
9705b261ecSmrg    modes[num_modes++] = mode;
9835c4bbdfSmrg
9905b261ecSmrg    /*
10005b261ecSmrg     * give the caller a reference to this mode
10105b261ecSmrg     */
10205b261ecSmrg    ++mode->refcnt;
10305b261ecSmrg    return mode;
10405b261ecSmrg}
10505b261ecSmrg
10605b261ecSmrgstatic RRModePtr
10735c4bbdfSmrgRRModeFindByName(const char *name, CARD16 nameLength)
10805b261ecSmrg{
10935c4bbdfSmrg    int i;
11035c4bbdfSmrg    RRModePtr mode;
11135c4bbdfSmrg
11235c4bbdfSmrg    for (i = 0; i < num_modes; i++) {
11335c4bbdfSmrg        mode = modes[i];
11435c4bbdfSmrg        if (mode->mode.nameLength == nameLength &&
11535c4bbdfSmrg            !memcmp(name, mode->name, nameLength)) {
11635c4bbdfSmrg            return mode;
11735c4bbdfSmrg        }
11805b261ecSmrg    }
11905b261ecSmrg    return NULL;
12005b261ecSmrg}
12105b261ecSmrg
12205b261ecSmrgRRModePtr
12335c4bbdfSmrgRRModeGet(xRRModeInfo * modeInfo, const char *name)
12405b261ecSmrg{
12535c4bbdfSmrg    int i;
12635c4bbdfSmrg
12735c4bbdfSmrg    for (i = 0; i < num_modes; i++) {
12835c4bbdfSmrg        RRModePtr mode = modes[i];
12935c4bbdfSmrg
13035c4bbdfSmrg        if (RRModeEqual(&mode->mode, modeInfo) &&
13135c4bbdfSmrg            !memcmp(name, mode->name, modeInfo->nameLength)) {
13235c4bbdfSmrg            ++mode->refcnt;
13335c4bbdfSmrg            return mode;
13435c4bbdfSmrg        }
13505b261ecSmrg    }
13605b261ecSmrg
13735c4bbdfSmrg    return RRModeCreate(modeInfo, name, NULL);
13805b261ecSmrg}
13905b261ecSmrg
14005b261ecSmrgstatic RRModePtr
14135c4bbdfSmrgRRModeCreateUser(ScreenPtr pScreen,
14235c4bbdfSmrg                 xRRModeInfo * modeInfo, const char *name, int *error)
14305b261ecSmrg{
14435c4bbdfSmrg    RRModePtr mode;
14505b261ecSmrg
14635c4bbdfSmrg    mode = RRModeFindByName(name, modeInfo->nameLength);
14735c4bbdfSmrg    if (mode) {
14835c4bbdfSmrg        *error = BadName;
14935c4bbdfSmrg        return NULL;
15005b261ecSmrg    }
15135c4bbdfSmrg
15235c4bbdfSmrg    mode = RRModeCreate(modeInfo, name, pScreen);
15335c4bbdfSmrg    if (!mode) {
15435c4bbdfSmrg        *error = BadAlloc;
15535c4bbdfSmrg        return NULL;
15605b261ecSmrg    }
15705b261ecSmrg    *error = Success;
15805b261ecSmrg    return mode;
15905b261ecSmrg}
16005b261ecSmrg
16105b261ecSmrgRRModePtr *
16235c4bbdfSmrgRRModesForScreen(ScreenPtr pScreen, int *num_ret)
16305b261ecSmrg{
16405b261ecSmrg    rrScrPriv(pScreen);
16535c4bbdfSmrg    int o, c, m;
16635c4bbdfSmrg    RRModePtr *screen_modes;
16735c4bbdfSmrg    int num_screen_modes = 0;
16805b261ecSmrg
16935c4bbdfSmrg    screen_modes = xallocarray((num_modes ? num_modes : 1), sizeof(RRModePtr));
1704642e01fSmrg    if (!screen_modes)
17135c4bbdfSmrg        return NULL;
17235c4bbdfSmrg
17305b261ecSmrg    /*
17405b261ecSmrg     * Add modes from all outputs
17505b261ecSmrg     */
17635c4bbdfSmrg    for (o = 0; o < pScrPriv->numOutputs; o++) {
17735c4bbdfSmrg        RROutputPtr output = pScrPriv->outputs[o];
17835c4bbdfSmrg        int n;
17935c4bbdfSmrg
18035c4bbdfSmrg        for (m = 0; m < output->numModes + output->numUserModes; m++) {
18135c4bbdfSmrg            RRModePtr mode = (m < output->numModes ?
18235c4bbdfSmrg                              output->modes[m] :
18335c4bbdfSmrg                              output->userModes[m - output->numModes]);
18435c4bbdfSmrg            for (n = 0; n < num_screen_modes; n++)
18535c4bbdfSmrg                if (screen_modes[n] == mode)
18635c4bbdfSmrg                    break;
18735c4bbdfSmrg            if (n == num_screen_modes)
18835c4bbdfSmrg                screen_modes[num_screen_modes++] = mode;
18935c4bbdfSmrg        }
19005b261ecSmrg    }
19105b261ecSmrg    /*
19205b261ecSmrg     * Add modes from all crtcs. The goal is to
19305b261ecSmrg     * make sure all available and active modes
19405b261ecSmrg     * are visible to the client
19505b261ecSmrg     */
19635c4bbdfSmrg    for (c = 0; c < pScrPriv->numCrtcs; c++) {
19735c4bbdfSmrg        RRCrtcPtr crtc = pScrPriv->crtcs[c];
19835c4bbdfSmrg        RRModePtr mode = crtc->mode;
19935c4bbdfSmrg        int n;
20035c4bbdfSmrg
20135c4bbdfSmrg        if (!mode)
20235c4bbdfSmrg            continue;
20335c4bbdfSmrg        for (n = 0; n < num_screen_modes; n++)
20435c4bbdfSmrg            if (screen_modes[n] == mode)
20535c4bbdfSmrg                break;
20635c4bbdfSmrg        if (n == num_screen_modes)
20735c4bbdfSmrg            screen_modes[num_screen_modes++] = mode;
20805b261ecSmrg    }
20905b261ecSmrg    /*
21005b261ecSmrg     * Add all user modes for this screen
21105b261ecSmrg     */
21235c4bbdfSmrg    for (m = 0; m < num_modes; m++) {
21335c4bbdfSmrg        RRModePtr mode = modes[m];
21435c4bbdfSmrg        int n;
21535c4bbdfSmrg
21635c4bbdfSmrg        if (mode->userScreen != pScreen)
21735c4bbdfSmrg            continue;
21835c4bbdfSmrg        for (n = 0; n < num_screen_modes; n++)
21935c4bbdfSmrg            if (screen_modes[n] == mode)
22035c4bbdfSmrg                break;
22135c4bbdfSmrg        if (n == num_screen_modes)
22235c4bbdfSmrg            screen_modes[num_screen_modes++] = mode;
22305b261ecSmrg    }
22435c4bbdfSmrg
22505b261ecSmrg    *num_ret = num_screen_modes;
22605b261ecSmrg    return screen_modes;
22705b261ecSmrg}
22805b261ecSmrg
22905b261ecSmrgvoid
23035c4bbdfSmrgRRModeDestroy(RRModePtr mode)
23105b261ecSmrg{
23235c4bbdfSmrg    int m;
23335c4bbdfSmrg
23405b261ecSmrg    if (--mode->refcnt > 0)
23535c4bbdfSmrg        return;
23635c4bbdfSmrg    for (m = 0; m < num_modes; m++) {
23735c4bbdfSmrg        if (modes[m] == mode) {
23835c4bbdfSmrg            memmove(modes + m, modes + m + 1,
23935c4bbdfSmrg                    (num_modes - m - 1) * sizeof(RRModePtr));
24035c4bbdfSmrg            num_modes--;
24135c4bbdfSmrg            if (!num_modes) {
24235c4bbdfSmrg                free(modes);
24335c4bbdfSmrg                modes = NULL;
24435c4bbdfSmrg            }
24535c4bbdfSmrg            break;
24635c4bbdfSmrg        }
24705b261ecSmrg    }
24835c4bbdfSmrg
2496747b715Smrg    free(mode);
25005b261ecSmrg}
25105b261ecSmrg
25205b261ecSmrgstatic int
25335c4bbdfSmrgRRModeDestroyResource(void *value, XID pid)
25405b261ecSmrg{
25535c4bbdfSmrg    RRModeDestroy((RRModePtr) value);
25605b261ecSmrg    return 1;
25705b261ecSmrg}
25805b261ecSmrg
2596747b715Smrg/*
2606747b715Smrg * Initialize mode type
2616747b715Smrg */
26205b261ecSmrgBool
26335c4bbdfSmrgRRModeInit(void)
26405b261ecSmrg{
26535c4bbdfSmrg    assert(num_modes == 0);
26635c4bbdfSmrg    assert(modes == NULL);
26735c4bbdfSmrg    RRModeType = CreateNewResourceType(RRModeDestroyResource, "MODE");
26805b261ecSmrg    if (!RRModeType)
26935c4bbdfSmrg        return FALSE;
27035c4bbdfSmrg
27105b261ecSmrg    return TRUE;
27205b261ecSmrg}
27305b261ecSmrg
2746747b715Smrg/*
2756747b715Smrg * Initialize mode type error value
2766747b715Smrg */
2776747b715Smrgvoid
2786747b715SmrgRRModeInitErrorValue(void)
2796747b715Smrg{
2806747b715Smrg    SetResourceTypeErrorValue(RRModeType, RRErrorBase + BadRRMode);
2816747b715Smrg}
2826747b715Smrg
28305b261ecSmrgint
28435c4bbdfSmrgProcRRCreateMode(ClientPtr client)
28505b261ecSmrg{
28605b261ecSmrg    REQUEST(xRRCreateModeReq);
28735c4bbdfSmrg    xRRCreateModeReply rep;
28835c4bbdfSmrg    WindowPtr pWin;
28935c4bbdfSmrg    ScreenPtr pScreen;
29035c4bbdfSmrg    xRRModeInfo *modeInfo;
29135c4bbdfSmrg    long units_after;
29235c4bbdfSmrg    char *name;
29335c4bbdfSmrg    int error, rc;
29435c4bbdfSmrg    RRModePtr mode;
29535c4bbdfSmrg
29635c4bbdfSmrg    REQUEST_AT_LEAST_SIZE(xRRCreateModeReq);
2976747b715Smrg    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
29805b261ecSmrg    if (rc != Success)
29935c4bbdfSmrg        return rc;
30005b261ecSmrg
30105b261ecSmrg    pScreen = pWin->drawable.pScreen;
30235c4bbdfSmrg
30305b261ecSmrg    modeInfo = &stuff->modeInfo;
30405b261ecSmrg    name = (char *) (stuff + 1);
30535c4bbdfSmrg    units_after = (stuff->length - bytes_to_int32(sizeof(xRRCreateModeReq)));
30605b261ecSmrg
30705b261ecSmrg    /* check to make sure requested name fits within the data provided */
3086747b715Smrg    if (bytes_to_int32(modeInfo->nameLength) > units_after)
30935c4bbdfSmrg        return BadLength;
31005b261ecSmrg
31135c4bbdfSmrg    mode = RRModeCreateUser(pScreen, modeInfo, name, &error);
31205b261ecSmrg    if (!mode)
31335c4bbdfSmrg        return error;
31435c4bbdfSmrg
31535c4bbdfSmrg    rep = (xRRCreateModeReply) {
31635c4bbdfSmrg        .type = X_Reply,
31735c4bbdfSmrg        .sequenceNumber = client->sequence,
31835c4bbdfSmrg        .length = 0,
31935c4bbdfSmrg        .mode = mode->mode.id
32035c4bbdfSmrg	};
32135c4bbdfSmrg    if (client->swapped) {
32235c4bbdfSmrg        swaps(&rep.sequenceNumber);
32335c4bbdfSmrg        swapl(&rep.length);
32435c4bbdfSmrg        swapl(&rep.mode);
32505b261ecSmrg    }
32635c4bbdfSmrg    WriteToClient(client, sizeof(xRRCreateModeReply), &rep);
3274642e01fSmrg    /* Drop out reference to this mode */
32835c4bbdfSmrg    RRModeDestroy(mode);
3296747b715Smrg    return Success;
33005b261ecSmrg}
33105b261ecSmrg
33205b261ecSmrgint
33335c4bbdfSmrgProcRRDestroyMode(ClientPtr client)
33405b261ecSmrg{
33505b261ecSmrg    REQUEST(xRRDestroyModeReq);
33635c4bbdfSmrg    RRModePtr mode;
33735c4bbdfSmrg
33805b261ecSmrg    REQUEST_SIZE_MATCH(xRRDestroyModeReq);
3396747b715Smrg    VERIFY_RR_MODE(stuff->mode, mode, DixDestroyAccess);
3406747b715Smrg
34105b261ecSmrg    if (!mode->userScreen)
34235c4bbdfSmrg        return BadMatch;
34305b261ecSmrg    if (mode->refcnt > 1)
34435c4bbdfSmrg        return BadAccess;
34535c4bbdfSmrg    FreeResource(stuff->mode, 0);
34605b261ecSmrg    return Success;
34705b261ecSmrg}
34805b261ecSmrg
34905b261ecSmrgint
35035c4bbdfSmrgProcRRAddOutputMode(ClientPtr client)
35105b261ecSmrg{
35205b261ecSmrg    REQUEST(xRRAddOutputModeReq);
35335c4bbdfSmrg    RRModePtr mode;
35435c4bbdfSmrg    RROutputPtr output;
35535c4bbdfSmrg
35605b261ecSmrg    REQUEST_SIZE_MATCH(xRRAddOutputModeReq);
3576747b715Smrg    VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
3586747b715Smrg    VERIFY_RR_MODE(stuff->mode, mode, DixUseAccess);
35935c4bbdfSmrg
3601b5d61b8Smrg    if (RROutputIsLeased(output))
3611b5d61b8Smrg        return BadAccess;
3621b5d61b8Smrg
36335c4bbdfSmrg    return RROutputAddUserMode(output, mode);
36405b261ecSmrg}
36505b261ecSmrg
36605b261ecSmrgint
36735c4bbdfSmrgProcRRDeleteOutputMode(ClientPtr client)
36805b261ecSmrg{
36905b261ecSmrg    REQUEST(xRRDeleteOutputModeReq);
37035c4bbdfSmrg    RRModePtr mode;
37135c4bbdfSmrg    RROutputPtr output;
37235c4bbdfSmrg
37305b261ecSmrg    REQUEST_SIZE_MATCH(xRRDeleteOutputModeReq);
3746747b715Smrg    VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
3756747b715Smrg    VERIFY_RR_MODE(stuff->mode, mode, DixUseAccess);
37635c4bbdfSmrg
3771b5d61b8Smrg    if (RROutputIsLeased(output))
3781b5d61b8Smrg        return BadAccess;
3791b5d61b8Smrg
38035c4bbdfSmrg    return RROutputDeleteUserMode(output, mode);
38105b261ecSmrg}
382