/*
 * Copyright © 2006 Keith Packard
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of the copyright holders not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  The copyright holders make no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 */

/**************************************************************************/
/*                                                                        */
/* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/.         */
/*                                                                        */
/* NX-X11, NX protocol compression and NX extensions to this software     */
/* are copyright of NoMachine. Redistribution and use of the present      */
/* software is allowed according to terms specified in the file LICENSE   */
/* which comes in the source distribution.                                */
/*                                                                        */
/* Check http://www.nomachine.com/licensing.html for applicability.       */
/*                                                                        */
/* NX and NoMachine are trademarks of Medialogic S.p.A.                   */
/*                                                                        */
/* All rights reserved.                                                   */
/*                                                                        */
/**************************************************************************/

#include "randrstr.h"
#include "swaprep.h"
#include "registry.h"

RESTYPE	RRCrtcType;

/*
 * Notify the CRTC of some change
 */
void
RRCrtcChanged (RRCrtcPtr crtc, Bool layoutChanged)
{
    ScreenPtr	pScreen = crtc->pScreen;

    crtc->changed = TRUE;
    if (pScreen)
    {
	rrScrPriv(pScreen);
    
	pScrPriv->changed = TRUE;
	/*
	 * Send ConfigureNotify on any layout change
	 */
	if (layoutChanged)
	    pScrPriv->layoutChanged = TRUE;
    }
}

/*
 * Create a CRTC
 */
RRCrtcPtr
RRCrtcCreate (ScreenPtr pScreen, void *devPrivate)
{
    RRCrtcPtr	    crtc;
    RRCrtcPtr	    *crtcs;
    rrScrPrivPtr    pScrPriv;

    if (!RRInit())
	return NULL;
    
    pScrPriv = rrGetScrPriv(pScreen);

    /* make space for the crtc pointer */
    if (pScrPriv->numCrtcs)
	crtcs = xrealloc (pScrPriv->crtcs, 
			  (pScrPriv->numCrtcs + 1) * sizeof (RRCrtcPtr));
    else
	crtcs = xalloc (sizeof (RRCrtcPtr));
    if (!crtcs)
	return FALSE;
    pScrPriv->crtcs = crtcs;
    
    crtc = xcalloc (1, sizeof (RRCrtcRec));
    if (!crtc)
	return NULL;
    crtc->id = FakeClientID (0);
    crtc->pScreen = pScreen;
    crtc->mode = NULL;
    crtc->x = 0;
    crtc->y = 0;
    crtc->rotation = RR_Rotate_0;
    crtc->rotations = RR_Rotate_0;
    crtc->outputs = NULL;
    crtc->numOutputs = 0;
    crtc->gammaSize = 0;
    crtc->gammaRed = crtc->gammaBlue = crtc->gammaGreen = NULL;
    crtc->changed = FALSE;
    crtc->devPrivate = devPrivate;

    if (!AddResource (crtc->id, RRCrtcType, (pointer) crtc))
	return NULL;

    /* attach the screen and crtc together */
    crtc->pScreen = pScreen;
    pScrPriv->crtcs[pScrPriv->numCrtcs++] = crtc;
    
    return crtc;
}

/*
 * Set the allowed rotations on a CRTC
 */
void
RRCrtcSetRotations (RRCrtcPtr crtc, Rotation rotations)
{
    crtc->rotations = rotations;
}

/*
 * Notify the extension that the Crtc has been reconfigured,
 * the driver calls this whenever it has updated the mode
 */
Bool
RRCrtcNotify (RRCrtcPtr	    crtc,
	      RRModePtr	    mode,
	      int	    x,
	      int	    y,
	      Rotation	    rotation,
	      int	    numOutputs,
	      RROutputPtr   *outputs)
{
    int	    i, j;
    
    /*
     * Check to see if any of the new outputs were
     * not in the old list and mark them as changed
     */
    for (i = 0; i < numOutputs; i++)
    {
	for (j = 0; j < crtc->numOutputs; j++)
	    if (outputs[i] == crtc->outputs[j])
		break;
	if (j == crtc->numOutputs)
	{
	    outputs[i]->crtc = crtc;
	    RROutputChanged (outputs[i], FALSE);
	    RRCrtcChanged (crtc, FALSE);
	}
    }
    /*
     * Check to see if any of the old outputs are
     * not in the new list and mark them as changed
     */
    for (j = 0; j < crtc->numOutputs; j++)
    {
	for (i = 0; i < numOutputs; i++)
	    if (outputs[i] == crtc->outputs[j])
		break;
	if (i == numOutputs)
	{
	    if (crtc->outputs[j]->crtc == crtc)
		crtc->outputs[j]->crtc = NULL;
	    RROutputChanged (crtc->outputs[j], FALSE);
	    RRCrtcChanged (crtc, FALSE);
	}
    }
    /*
     * Reallocate the crtc output array if necessary
     */
    if (numOutputs != crtc->numOutputs)
    {
	RROutputPtr *newoutputs;
	
	if (numOutputs)
	{
	    if (crtc->numOutputs)
		newoutputs = xrealloc (crtc->outputs,
				    numOutputs * sizeof (RROutputPtr));
	    else
		newoutputs = xalloc (numOutputs * sizeof (RROutputPtr));
	    if (!newoutputs)
		return FALSE;
	}
	else
	{
	    if (crtc->outputs)
		xfree (crtc->outputs);
	    newoutputs = NULL;
	}
	crtc->outputs = newoutputs;
	crtc->numOutputs = numOutputs;
    }
    /*
     * Copy the new list of outputs into the crtc
     */
    memcpy (crtc->outputs, outputs, numOutputs * sizeof (RROutputPtr));
    /*
     * Update remaining crtc fields
     */
    if (mode != crtc->mode)
    {
	if (crtc->mode)
	    RRModeDestroy (crtc->mode);
	crtc->mode = mode;
	if (mode != NULL)
	    mode->refcnt++;
	RRCrtcChanged (crtc, TRUE);
    }
    if (x != crtc->x)
    {
	crtc->x = x;
	RRCrtcChanged (crtc, TRUE);
    }
    if (y != crtc->y)
    {
	crtc->y = y;
	RRCrtcChanged (crtc, TRUE);
    }
    if (rotation != crtc->rotation)
    {
	crtc->rotation = rotation;
	RRCrtcChanged (crtc, TRUE);
    }
    return TRUE;
}

void
RRDeliverCrtcEvent (ClientPtr client, WindowPtr pWin, RRCrtcPtr crtc)
{
    ScreenPtr pScreen = pWin->drawable.pScreen;
    rrScrPriv (pScreen);
    xRRCrtcChangeNotifyEvent	ce;
    RRModePtr	mode = crtc->mode;
    
    ce.type = RRNotify + RREventBase;
    ce.subCode = RRNotify_CrtcChange;
    ce.sequenceNumber = client->sequence;
    ce.timestamp = pScrPriv->lastSetTime.milliseconds;
    ce.window = pWin->drawable.id;
    ce.crtc = crtc->id;
    ce.rotation = crtc->rotation;
    if (mode)
    {
	ce.mode = mode->mode.id;
	ce.x = crtc->x;
	ce.y = crtc->y;
	ce.width = mode->mode.width;
	ce.height = mode->mode.height;
    }
    else
    {
	ce.mode = None;
	ce.x = 0;
	ce.y = 0;
	ce.width = 0;
	ce.height = 0;
    }
    WriteEventsToClient (client, 1, (xEvent *) &ce);
}

static Bool
RRCrtcPendingProperties (RRCrtcPtr crtc)
{
    ScreenPtr	pScreen = crtc->pScreen;
    rrScrPriv(pScreen);
    int		o;

    for (o = 0; o < pScrPriv->numOutputs; o++)
    {
	RROutputPtr output = pScrPriv->outputs[o];
	if (output->crtc == crtc && output->pendingProperties)
	    return TRUE;
    }
    return FALSE;
}

/*
 * Request that the Crtc be reconfigured
 */
Bool
RRCrtcSet (RRCrtcPtr    crtc,
	   RRModePtr	mode,
	   int		x,
	   int		y,
	   Rotation	rotation,
	   int		numOutputs,
	   RROutputPtr  *outputs)
{
    ScreenPtr	pScreen = crtc->pScreen;
    Bool	ret = FALSE;
    rrScrPriv(pScreen);

    /* See if nothing changed */
    if (crtc->mode == mode &&
	crtc->x == x &&
	crtc->y == y &&
	crtc->rotation == rotation &&
	crtc->numOutputs == numOutputs &&
	!memcmp (crtc->outputs, outputs, numOutputs * sizeof (RROutputPtr)) &&
	!RRCrtcPendingProperties (crtc))
    {
	ret = TRUE;
    }
    else
    {
#if RANDR_12_INTERFACE
	if (pScrPriv->rrCrtcSet)
	{
	    ret = (*pScrPriv->rrCrtcSet) (pScreen, crtc, mode, x, y, 
					  rotation, numOutputs, outputs);
	}
	else
#endif
	{
#if RANDR_10_INTERFACE
	    if (pScrPriv->rrSetConfig)
	    {
		RRScreenSize	    size;
		RRScreenRate	    rate;

		if (!mode)
		{
		    RRCrtcNotify (crtc, NULL, x, y, rotation, 0, NULL);
		    ret = TRUE;
		}
		else
		{
		    size.width = mode->mode.width;
		    size.height = mode->mode.height;
		    if (outputs[0]->mmWidth && outputs[0]->mmHeight)
		    {
			size.mmWidth = outputs[0]->mmWidth;
			size.mmHeight = outputs[0]->mmHeight;
		    }
		    else
		    {
			size.mmWidth = pScreen->mmWidth;
			size.mmHeight = pScreen->mmHeight;
		    }
		    size.nRates = 1;
		    rate.rate = RRVerticalRefresh (&mode->mode);
		    size.pRates = &rate;
		    ret = (*pScrPriv->rrSetConfig) (pScreen, rotation, rate.rate, &size);
		    /*
		     * Old 1.0 interface tied screen size to mode size
		     */
		    if (ret)
		    {
			RRCrtcNotify (crtc, mode, x, y, rotation, 1, outputs);
			RRScreenSizeNotify (pScreen);
		    }
		}
	    }
#endif
	}
	if (ret)
	{
	    int	o;
	    RRTellChanged (pScreen);

	    for (o = 0; o < numOutputs; o++)
		RRPostPendingProperties (outputs[o]);
	}
    }
    return ret;
}

/*
 * Destroy a Crtc at shutdown
 */
void
RRCrtcDestroy (RRCrtcPtr crtc)
{
    FreeResource (crtc->id, 0);
}

static int
RRCrtcDestroyResource (pointer value, XID pid)
{
    RRCrtcPtr	crtc = (RRCrtcPtr) value;
    ScreenPtr	pScreen = crtc->pScreen;

    if (pScreen)
    {
	rrScrPriv(pScreen);
	int		i;
    
	for (i = 0; i < pScrPriv->numCrtcs; i++)
	{
	    if (pScrPriv->crtcs[i] == crtc)
	    {
		memmove (pScrPriv->crtcs + i, pScrPriv->crtcs + i + 1,
			 (pScrPriv->numCrtcs - (i + 1)) * sizeof (RRCrtcPtr));
		--pScrPriv->numCrtcs;
		break;
	    }
	}
    }
    if (crtc->gammaRed)
	xfree (crtc->gammaRed);
    if (crtc->mode)
	RRModeDestroy (crtc->mode);
    xfree (crtc);
    return 1;
}

/*
 * Request that the Crtc gamma be changed
 */

Bool
RRCrtcGammaSet (RRCrtcPtr   crtc,
		CARD16	    *red,
		CARD16	    *green,
		CARD16	    *blue)
{
    Bool	ret = TRUE;
#if RANDR_12_INTERFACE
    ScreenPtr	pScreen = crtc->pScreen;
#endif
    
    memcpy (crtc->gammaRed, red, crtc->gammaSize * sizeof (CARD16));
    memcpy (crtc->gammaGreen, green, crtc->gammaSize * sizeof (CARD16));
    memcpy (crtc->gammaBlue, blue, crtc->gammaSize * sizeof (CARD16));
#if RANDR_12_INTERFACE
    if (pScreen)
    {
	rrScrPriv(pScreen);
	if (pScrPriv->rrCrtcSetGamma)
	    ret = (*pScrPriv->rrCrtcSetGamma) (pScreen, crtc);
    }
#endif
    return ret;
}

/*
 * Notify the extension that the Crtc gamma has been changed
 * The driver calls this whenever it has changed the gamma values
 * in the RRCrtcRec
 */

Bool
RRCrtcGammaNotify (RRCrtcPtr	crtc)
{
    return TRUE;    /* not much going on here */
}

/**
 * Returns the width/height that the crtc scans out from the framebuffer
 */
void
RRCrtcGetScanoutSize(RRCrtcPtr crtc, int *width, int *height)
{
    if (crtc->mode == NULL) {
	*width = 0;
	*height = 0;
	return;
    }

    switch (crtc->rotation & 0xf) {
    case RR_Rotate_0:
    case RR_Rotate_180:
	*width = crtc->mode->mode.width;
	*height = crtc->mode->mode.height;
	break;
    case RR_Rotate_90:
    case RR_Rotate_270:
	*width = crtc->mode->mode.height;
	*height = crtc->mode->mode.width;
	break;
    }
}

/*
 * Set the size of the gamma table at server startup time
 */

Bool
RRCrtcGammaSetSize (RRCrtcPtr	crtc,
		    int		size)
{
    CARD16  *gamma;

    if (size == crtc->gammaSize)
	return TRUE;
    if (size)
    {
	gamma = xalloc (size * 3 * sizeof (CARD16));
	if (!gamma)
	    return FALSE;
    }
    else
	gamma = NULL;
    if (crtc->gammaRed)
	xfree (crtc->gammaRed);
    crtc->gammaRed = gamma;
    crtc->gammaGreen = gamma + size;
    crtc->gammaBlue = gamma + size*2;
    crtc->gammaSize = size;
    return TRUE;
}

/*
 * Initialize crtc type
 */
Bool
RRCrtcInit (void)
{
    RRCrtcType = CreateNewResourceType (RRCrtcDestroyResource);
    if (!RRCrtcType)
	return FALSE;
    RegisterResourceName (RRCrtcType, "CRTC");
    return TRUE;
}

int
ProcRRGetCrtcInfo (ClientPtr client)
{
    REQUEST(xRRGetCrtcInfoReq);
    xRRGetCrtcInfoReply	rep;
    RRCrtcPtr			crtc;
    CARD8			*extra;
    unsigned long		extraLen;
    ScreenPtr			pScreen;
    rrScrPrivPtr		pScrPriv;
    RRModePtr			mode;
    RROutput			*outputs;
    RROutput			*possible;
    int				i, j, k, n;
    int				width, height;
    
    REQUEST_SIZE_MATCH(xRRGetCrtcInfoReq);
    crtc = LookupCrtc(client, stuff->crtc, DixReadAccess);

    if (!crtc)
	return RRErrorBase + BadRRCrtc;

    /* All crtcs must be associated with screens before client
     * requests are processed
     */
    pScreen = crtc->pScreen;
    pScrPriv = rrGetScrPriv(pScreen);

    mode = crtc->mode;
    
    rep.type = X_Reply;
    rep.status = RRSetConfigSuccess;
    rep.sequenceNumber = client->sequence;
    rep.length = 0;
    rep.timestamp = pScrPriv->lastSetTime.milliseconds;
    rep.x = crtc->x;
    rep.y = crtc->y;
    RRCrtcGetScanoutSize (crtc, &width, &height);
    rep.width = width;
    rep.height = height;
    rep.mode = mode ? mode->mode.id : 0;
    rep.rotation = crtc->rotation;
    rep.rotations = crtc->rotations;
    rep.nOutput = crtc->numOutputs;
    k = 0;
    for (i = 0; i < pScrPriv->numOutputs; i++)
	for (j = 0; j < pScrPriv->outputs[i]->numCrtcs; j++)
	    if (pScrPriv->outputs[i]->crtcs[j] == crtc)
		k++;
    rep.nPossibleOutput = k;
    
    rep.length = rep.nOutput + rep.nPossibleOutput;

    extraLen = rep.length << 2;
    if (extraLen)
    {
	extra = xalloc (extraLen);
	if (!extra)
	    return BadAlloc;
    }
    else
	extra = NULL;

    outputs = (RROutput *) extra;
    possible = (RROutput *) (outputs + rep.nOutput);
    
    for (i = 0; i < crtc->numOutputs; i++)
    {
	outputs[i] = crtc->outputs[i]->id;
	if (client->swapped)
	    swapl (&outputs[i], n);
    }
    k = 0;
    for (i = 0; i < pScrPriv->numOutputs; i++)
	for (j = 0; j < pScrPriv->outputs[i]->numCrtcs; j++)
	    if (pScrPriv->outputs[i]->crtcs[j] == crtc)
	    {
		possible[k] = pScrPriv->outputs[i]->id;
		if (client->swapped)
		    swapl (&possible[k], n);
		k++;
	    }
    
    if (client->swapped) {
	swaps(&rep.sequenceNumber, n);
	swapl(&rep.length, n);
	swapl(&rep.timestamp, n);
	swaps(&rep.x, n);
	swaps(&rep.y, n);
	swaps(&rep.width, n);
	swaps(&rep.height, n);
	swapl(&rep.mode, n);
	swaps(&rep.rotation, n);
	swaps(&rep.rotations, n);
	swaps(&rep.nOutput, n);
	swaps(&rep.nPossibleOutput, n);
    }
    WriteToClient(client, sizeof(xRRGetCrtcInfoReply), (char *)&rep);
    if (extraLen)
    {
	WriteToClient (client, extraLen, (char *) extra);
	xfree (extra);
    }
    
    return client->noClientException;
}

int
ProcRRSetCrtcConfig (ClientPtr client)
{
    REQUEST(xRRSetCrtcConfigReq);
    xRRSetCrtcConfigReply   rep;
    ScreenPtr		    pScreen;
    rrScrPrivPtr	    pScrPriv;
    RRCrtcPtr		    crtc;
    RRModePtr		    mode;
    int			    numOutputs;
    RROutputPtr		    *outputs = NULL;
    RROutput		    *outputIds;
    TimeStamp		    configTime;
    TimeStamp		    time;
    Rotation		    rotation;
    int			    i, j;
    
    REQUEST_AT_LEAST_SIZE(xRRSetCrtcConfigReq);
    numOutputs = (stuff->length - (SIZEOF (xRRSetCrtcConfigReq) >> 2));
    
    crtc = LookupIDByType (stuff->crtc, RRCrtcType);
    if (!crtc)
    {
	client->errorValue = stuff->crtc;
	return RRErrorBase + BadRRCrtc;
    }
    if (stuff->mode == None)
    {
	mode = NULL;
	if (numOutputs > 0)
	    return BadMatch;
    }
    else
    {
	mode = LookupIDByType (stuff->mode, RRModeType);
	if (!mode)
	{
	    client->errorValue = stuff->mode;
	    return RRErrorBase + BadRRMode;
	}
	if (numOutputs == 0)
	    return BadMatch;
    }
    if (numOutputs)
    {
	outputs = xalloc (numOutputs * sizeof (RROutputPtr));
	if (!outputs)
	    return BadAlloc;
    }
    else
	outputs = NULL;
    
    outputIds = (RROutput *) (stuff + 1);
    for (i = 0; i < numOutputs; i++)
    {
	outputs[i] = (RROutputPtr) LookupIDByType (outputIds[i], RROutputType);
	if (!outputs[i])
	{
	    client->errorValue = outputIds[i];
	    if (outputs)
		xfree (outputs);
	    return RRErrorBase + BadRROutput;
	}
	/* validate crtc for this output */
	for (j = 0; j < outputs[i]->numCrtcs; j++)
	    if (outputs[i]->crtcs[j] == crtc)
		break;
	if (j == outputs[i]->numCrtcs)
	{
	    if (outputs)
		xfree (outputs);
	    return BadMatch;
	}
	/* validate mode for this output */
	for (j = 0; j < outputs[i]->numModes + outputs[i]->numUserModes; j++)
	{
	    RRModePtr	m = (j < outputs[i]->numModes ? 
			     outputs[i]->modes[j] :
			     outputs[i]->userModes[j - outputs[i]->numModes]);
	    if (m == mode)
		break;
	}
	if (j == outputs[i]->numModes + outputs[i]->numUserModes)
	{
	    if (outputs)
		xfree (outputs);
	    return BadMatch;
	}
    }
    /* validate clones */
    for (i = 0; i < numOutputs; i++)
    {
	for (j = 0; j < numOutputs; j++)
	{
	    int k;
	    if (i == j)
		continue;
	    for (k = 0; k < outputs[i]->numClones; k++)
	    {
		if (outputs[i]->clones[k] == outputs[j])
		    break;
	    }
	    if (k == outputs[i]->numClones)
	    {
		if (outputs)
		    xfree (outputs);
		return BadMatch;
	    }
	}
    }

    pScreen = crtc->pScreen;
    pScrPriv = rrGetScrPriv(pScreen);
    
    time = ClientTimeToServerTime(stuff->timestamp);
    configTime = ClientTimeToServerTime(stuff->configTimestamp);
    
    if (!pScrPriv)
    {
	time = currentTime;
	rep.status = RRSetConfigFailed;
	goto sendReply;
    }
    
#if 0
    /*
     * if the client's config timestamp is not the same as the last config
     * timestamp, then the config information isn't up-to-date and
     * can't even be validated
     */
    if (CompareTimeStamps (configTime, pScrPriv->lastConfigTime) != 0)
    {
	rep.status = RRSetConfigInvalidConfigTime;
	goto sendReply;
    }
#endif
    
    /*
     * Validate requested rotation
     */
    rotation = (Rotation) stuff->rotation;

    /* test the rotation bits only! */
    switch (rotation & 0xf) {
    case RR_Rotate_0:
    case RR_Rotate_90:
    case RR_Rotate_180:
    case RR_Rotate_270:
	break;
    default:
	/*
	 * Invalid rotation
	 */
	client->errorValue = stuff->rotation;
	if (outputs)
	    xfree (outputs);
	return BadValue;
    }

    if (mode)
    {
	if ((~crtc->rotations) & rotation)
	{
	    /*
	     * requested rotation or reflection not supported by screen
	     */
	    client->errorValue = stuff->rotation;
	    if (outputs)
		xfree (outputs);
	    return BadMatch;
	}
    
#ifdef RANDR_12_INTERFACE
	/*
	 * Check screen size bounds if the DDX provides a 1.2 interface
	 * for setting screen size. Else, assume the CrtcSet sets
	 * the size along with the mode
	 */
	if (pScrPriv->rrScreenSetSize)
	{
	    int source_width = mode->mode.width;
	    int	source_height = mode->mode.height;

	    if ((rotation & 0xf) == RR_Rotate_90 || (rotation & 0xf) == RR_Rotate_270)
	    {
		source_width = mode->mode.height;
		source_height = mode->mode.width;
	    }
	    if (stuff->x + source_width > pScreen->width)
	    {
		client->errorValue = stuff->x;
		if (outputs)
		    xfree (outputs);
		return BadValue;
	    }
	    
	    if (stuff->y + source_height > pScreen->height)
	    {
		client->errorValue = stuff->y;
		if (outputs)
		    xfree (outputs);
		return BadValue;
	    }
	}
#endif
    }
    
    /*
     * Make sure the requested set-time is not older than
     * the last set-time
     */
    if (CompareTimeStamps (time, pScrPriv->lastSetTime) < 0)
    {
	rep.status = RRSetConfigInvalidTime;
	goto sendReply;
    }

    if (!RRCrtcSet (crtc, mode, stuff->x, stuff->y,
		   rotation, numOutputs, outputs))
    {
	rep.status = RRSetConfigFailed;
	goto sendReply;
    }
    #ifdef NXAGENT_SERVER /* Bug 21987 */
    pScrPriv->lastSetTime = time;
    #endif
    rep.status = RRSetConfigSuccess;
    
sendReply:
    if (outputs)
	xfree (outputs);
    
    rep.type = X_Reply;
    /* rep.status has already been filled in */
    rep.length = 0;
    rep.sequenceNumber = client->sequence;
    #ifndef NXAGENT_SERVER /* Bug 21987 */
    rep.newTimestamp = pScrPriv->lastConfigTime.milliseconds;
    #else
    rep.newTimestamp = pScrPriv->lastSetTime.milliseconds;
    #endif

    if (client->swapped) 
    {
	int n;
    	swaps(&rep.sequenceNumber, n);
    	swapl(&rep.length, n);
	swapl(&rep.newTimestamp, n);
    }
    WriteToClient(client, sizeof(xRRSetCrtcConfigReply), (char *)&rep);
    
    return client->noClientException;
}

int
ProcRRGetCrtcGammaSize (ClientPtr client)
{
    REQUEST(xRRGetCrtcGammaSizeReq);
    xRRGetCrtcGammaSizeReply	reply;
    RRCrtcPtr			crtc;
    int				n;

    REQUEST_SIZE_MATCH(xRRGetCrtcGammaSizeReq);
    crtc = LookupCrtc (client, stuff->crtc, DixReadAccess);
    if (!crtc)
	return RRErrorBase + BadRRCrtc;
    
    reply.type = X_Reply;
    reply.sequenceNumber = client->sequence;
    reply.length = 0;
    reply.size = crtc->gammaSize;
    if (client->swapped) {
	swaps (&reply.sequenceNumber, n);
	swapl (&reply.length, n);
	swaps (&reply.size, n);
    }
    WriteToClient (client, sizeof (xRRGetCrtcGammaSizeReply), (char *) &reply);
    return client->noClientException;
}

int
ProcRRGetCrtcGamma (ClientPtr client)
{
    REQUEST(xRRGetCrtcGammaReq);
    xRRGetCrtcGammaReply	reply;
    RRCrtcPtr			crtc;
    int				n;
    unsigned long		len;
    char			*extra;
    
    REQUEST_SIZE_MATCH(xRRGetCrtcGammaReq);
    crtc = LookupCrtc (client, stuff->crtc, DixReadAccess);
    if (!crtc)
	return RRErrorBase + BadRRCrtc;
    
    len = crtc->gammaSize * 3 * 2;
    
    if (crtc->gammaSize) {
	extra = xalloc(len);
	if (!extra)
	    return BadAlloc;
    }

    reply.type = X_Reply;
    reply.sequenceNumber = client->sequence;
    reply.length = (len + 3) >> 2;
    reply.size = crtc->gammaSize;
    if (client->swapped) {
	swaps (&reply.sequenceNumber, n);
	swapl (&reply.length, n);
	swaps (&reply.size, n);
    }
    WriteToClient (client, sizeof (xRRGetCrtcGammaReply), (char *) &reply);
    if (crtc->gammaSize)
    {
	memcpy(extra, crtc->gammaRed, len);
	client->pSwapReplyFunc = (ReplySwapPtr)CopySwap16Write;
	WriteSwappedDataToClient (client, len, extra);
	xfree(extra);
    }
    return client->noClientException;
}

int
ProcRRSetCrtcGamma (ClientPtr client)
{
    REQUEST(xRRSetCrtcGammaReq);
    RRCrtcPtr			crtc;
    unsigned long		len;
    CARD16			*red, *green, *blue;
    
    REQUEST_AT_LEAST_SIZE(xRRSetCrtcGammaReq);
    crtc = LookupCrtc (client, stuff->crtc, DixWriteAccess);
    if (!crtc)
	return RRErrorBase + BadRRCrtc;
    
    len = client->req_len - (sizeof (xRRSetCrtcGammaReq) >> 2);
    if (len < (stuff->size * 3 + 1) >> 1)
	return BadLength;

    if (stuff->size != crtc->gammaSize)
	return BadMatch;
    
    red = (CARD16 *) (stuff + 1);
    green = red + crtc->gammaSize;
    blue = green + crtc->gammaSize;
    
    RRCrtcGammaSet (crtc, red, green, blue);

    return Success;
}

