xenocara/xserver/randr/rrcrtc.c

1430 lines
38 KiB
C

/*
* Copyright © 2006 Keith Packard
* Copyright 2010 Red Hat, Inc
*
* 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.
*/
#include "randrstr.h"
#include "swaprep.h"
#include "mipointer.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 = realloc(pScrPriv->crtcs,
(pScrPriv->numCrtcs + 1) * sizeof(RRCrtcPtr));
else
crtcs = malloc(sizeof(RRCrtcPtr));
if (!crtcs)
return FALSE;
pScrPriv->crtcs = crtcs;
crtc = calloc(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;
RRTransformInit(&crtc->client_pending_transform);
RRTransformInit(&crtc->client_current_transform);
pixman_transform_init_identity(&crtc->transform);
pixman_f_transform_init_identity(&crtc->f_transform);
pixman_f_transform_init_identity(&crtc->f_inverse);
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;
}
/*
* Set whether transforms are allowed on a CRTC
*/
void
RRCrtcSetTransformSupport(RRCrtcPtr crtc, Bool transforms)
{
crtc->transforms = transforms;
}
/*
* 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,
RRTransformPtr transform, 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 = realloc(crtc->outputs,
numOutputs * sizeof(RROutputPtr));
else
newoutputs = malloc(numOutputs * sizeof(RROutputPtr));
if (!newoutputs)
return FALSE;
}
else {
free(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);
}
if (!RRTransformEqual(transform, &crtc->client_current_transform)) {
RRTransformCopy(&crtc->client_current_transform, transform);
RRCrtcChanged(crtc, TRUE);
}
if (crtc->changed && mode) {
RRTransformCompute(x, y,
mode->mode.width, mode->mode.height,
rotation,
&crtc->client_current_transform,
&crtc->transform, &crtc->f_transform,
&crtc->f_inverse);
}
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.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;
}
static void
crtc_bounds(RRCrtcPtr crtc, int *left, int *right, int *top, int *bottom)
{
*left = crtc->x;
*top = crtc->y;
switch (crtc->rotation) {
case RR_Rotate_0:
case RR_Rotate_180:
default:
*right = crtc->x + crtc->mode->mode.width;
*bottom = crtc->y + crtc->mode->mode.height;
return;
case RR_Rotate_90:
case RR_Rotate_270:
*right = crtc->x + crtc->mode->mode.height;
*bottom = crtc->y + crtc->mode->mode.width;
return;
}
}
/* overlapping counts as adjacent */
static Bool
crtcs_adjacent(const RRCrtcPtr a, const RRCrtcPtr b)
{
/* left, right, top, bottom... */
int al, ar, at, ab;
int bl, br, bt, bb;
int cl, cr, ct, cb; /* the overlap, if any */
crtc_bounds(a, &al, &ar, &at, &ab);
crtc_bounds(b, &bl, &br, &bt, &bb);
cl = max(al, bl);
cr = min(ar, br);
ct = max(at, bt);
cb = min(ab, bb);
return (cl <= cr) && (ct <= cb);
}
/* Depth-first search and mark all CRTCs reachable from cur */
static void
mark_crtcs(rrScrPrivPtr pScrPriv, int *reachable, int cur)
{
int i;
reachable[cur] = TRUE;
for (i = 0; i < pScrPriv->numCrtcs; ++i) {
if (reachable[i] || !pScrPriv->crtcs[i]->mode)
continue;
if (crtcs_adjacent(pScrPriv->crtcs[cur], pScrPriv->crtcs[i]))
mark_crtcs(pScrPriv, reachable, i);
}
}
static void
RRComputeContiguity(ScreenPtr pScreen)
{
rrScrPriv(pScreen);
Bool discontiguous = TRUE;
int i, n = pScrPriv->numCrtcs;
int *reachable = calloc(n, sizeof(int));
if (!reachable)
goto out;
/* Find first enabled CRTC and start search for reachable CRTCs from it */
for (i = 0; i < n; ++i) {
if (pScrPriv->crtcs[i]->mode) {
mark_crtcs(pScrPriv, reachable, i);
break;
}
}
/* Check that all enabled CRTCs were marked as reachable */
for (i = 0; i < n; ++i)
if (pScrPriv->crtcs[i]->mode && !reachable[i])
goto out;
discontiguous = FALSE;
out:
free(reachable);
pScrPriv->discontiguous = discontiguous;
}
/*
* 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;
Bool recompute = TRUE;
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) && !RRCrtcPendingTransform(crtc)) {
recompute = FALSE;
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, NULL, 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, NULL, 1,
outputs);
RRScreenSizeNotify(pScreen);
}
}
}
#endif
}
if (ret) {
int o;
RRTellChanged(pScreen);
for (o = 0; o < numOutputs; o++)
RRPostPendingProperties(outputs[o]);
}
}
if (recompute)
RRComputeContiguity(pScreen);
return ret;
}
/*
* Return crtc transform
*/
RRTransformPtr
RRCrtcGetTransform(RRCrtcPtr crtc)
{
RRTransformPtr transform = &crtc->client_pending_transform;
if (pixman_transform_is_identity(&transform->transform))
return NULL;
return transform;
}
/*
* Check whether the pending and current transforms are the same
*/
Bool
RRCrtcPendingTransform(RRCrtcPtr crtc)
{
return memcmp(&crtc->client_current_transform.transform,
&crtc->client_pending_transform.transform,
sizeof(PictTransform)) != 0;
}
/*
* 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;
}
}
}
free(crtc->gammaRed);
if (crtc->mode)
RRModeDestroy(crtc->mode);
free(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;
}
/*
* Request current gamma back from the DDX (if possible).
* This includes gamma size.
*/
Bool
RRCrtcGammaGet(RRCrtcPtr crtc)
{
Bool ret = TRUE;
#if RANDR_12_INTERFACE
ScreenPtr pScreen = crtc->pScreen;
#endif
#if RANDR_12_INTERFACE
if (pScreen) {
rrScrPriv(pScreen);
if (pScrPriv->rrCrtcGetGamma)
ret = (*pScrPriv->rrCrtcGetGamma) (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 */
}
static void
RRModeGetScanoutSize(RRModePtr mode, PictTransformPtr transform,
int *width, int *height)
{
BoxRec box;
if (mode == NULL) {
*width = 0;
*height = 0;
return;
}
box.x1 = 0;
box.y1 = 0;
box.x2 = mode->mode.width;
box.y2 = mode->mode.height;
pixman_transform_bounds(transform, &box);
*width = box.x2 - box.x1;
*height = box.y2 - box.y1;
}
/**
* Returns the width/height that the crtc scans out from the framebuffer
*/
void
RRCrtcGetScanoutSize(RRCrtcPtr crtc, int *width, int *height)
{
RRModeGetScanoutSize(crtc->mode, &crtc->transform, width, height);
}
/*
* 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 = malloc(size * 3 * sizeof(CARD16));
if (!gamma)
return FALSE;
}
else
gamma = NULL;
free(crtc->gammaRed);
crtc->gammaRed = gamma;
crtc->gammaGreen = gamma + size;
crtc->gammaBlue = gamma + size * 2;
crtc->gammaSize = size;
return TRUE;
}
/*
* Set the pending CRTC transformation
*/
int
RRCrtcTransformSet(RRCrtcPtr crtc,
PictTransformPtr transform,
struct pixman_f_transform *f_transform,
struct pixman_f_transform *f_inverse,
char *filter_name,
int filter_len, xFixed * params, int nparams)
{
PictFilterPtr filter = NULL;
int width = 0, height = 0;
if (!crtc->transforms)
return BadValue;
if (filter_len) {
filter = PictureFindFilter(crtc->pScreen, filter_name, filter_len);
if (!filter)
return BadName;
if (filter->ValidateParams) {
if (!filter->ValidateParams(crtc->pScreen, filter->id,
params, nparams, &width, &height))
return BadMatch;
}
else {
width = filter->width;
height = filter->height;
}
}
else {
if (nparams)
return BadMatch;
}
if (!RRTransformSetFilter(&crtc->client_pending_transform,
filter, params, nparams, width, height))
return BadAlloc;
crtc->client_pending_transform.transform = *transform;
crtc->client_pending_transform.f_transform = *f_transform;
crtc->client_pending_transform.f_inverse = *f_inverse;
return Success;
}
/*
* Initialize crtc type
*/
Bool
RRCrtcInit(void)
{
RRCrtcType = CreateNewResourceType(RRCrtcDestroyResource, "CRTC");
if (!RRCrtcType)
return FALSE;
return TRUE;
}
/*
* Initialize crtc type error value
*/
void
RRCrtcInitErrorValue(void)
{
SetResourceTypeErrorValue(RRCrtcType, RRErrorBase + BadRRCrtc);
}
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;
int width, height;
BoxRec panned_area;
REQUEST_SIZE_MATCH(xRRGetCrtcInfoReq);
VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess);
/* 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;
if (pScrPriv->rrGetPanning &&
pScrPriv->rrGetPanning(pScreen, crtc, &panned_area, NULL, NULL) &&
(panned_area.x2 > panned_area.x1) && (panned_area.y2 > panned_area.y1))
{
rep.x = panned_area.x1;
rep.y = panned_area.y1;
rep.width = panned_area.x2 - panned_area.x1;
rep.height = panned_area.y2 - panned_area.y1;
}
else {
RRCrtcGetScanoutSize(crtc, &width, &height);
rep.x = crtc->x;
rep.y = crtc->y;
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 = malloc(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]);
}
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]);
k++;
}
if (client->swapped) {
swaps(&rep.sequenceNumber);
swapl(&rep.length);
swapl(&rep.timestamp);
swaps(&rep.x);
swaps(&rep.y);
swaps(&rep.width);
swaps(&rep.height);
swapl(&rep.mode);
swaps(&rep.rotation);
swaps(&rep.rotations);
swaps(&rep.nOutput);
swaps(&rep.nPossibleOutput);
}
WriteToClient(client, sizeof(xRRGetCrtcInfoReply), (char *) &rep);
if (extraLen) {
WriteToClient(client, extraLen, (char *) extra);
free(extra);
}
return Success;
}
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 rc, i, j;
REQUEST_AT_LEAST_SIZE(xRRSetCrtcConfigReq);
numOutputs = (stuff->length - bytes_to_int32(SIZEOF(xRRSetCrtcConfigReq)));
VERIFY_RR_CRTC(stuff->crtc, crtc, DixSetAttrAccess);
if (stuff->mode == None) {
mode = NULL;
if (numOutputs > 0)
return BadMatch;
}
else {
VERIFY_RR_MODE(stuff->mode, mode, DixSetAttrAccess);
if (numOutputs == 0)
return BadMatch;
}
if (numOutputs) {
outputs = malloc(numOutputs * sizeof(RROutputPtr));
if (!outputs)
return BadAlloc;
}
else
outputs = NULL;
outputIds = (RROutput *) (stuff + 1);
for (i = 0; i < numOutputs; i++) {
rc = dixLookupResourceByType((pointer *) (outputs + i), outputIds[i],
RROutputType, client, DixSetAttrAccess);
if (rc != Success) {
free(outputs);
return rc;
}
/* 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) {
free(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) {
free(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) {
free(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;
}
/*
* 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;
free(outputs);
return BadValue;
}
if (mode) {
if ((~crtc->rotations) & rotation) {
/*
* requested rotation or reflection not supported by screen
*/
client->errorValue = stuff->rotation;
free(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 the driver supports transforms,
* then it must allow crtcs to display a subset of the screen, so
* only do this check for drivers without transform support.
*/
if (pScrPriv->rrScreenSetSize && !crtc->transforms) {
int source_width;
int source_height;
PictTransform transform;
struct pixman_f_transform f_transform, f_inverse;
RRTransformCompute(stuff->x, stuff->y,
mode->mode.width, mode->mode.height,
rotation,
&crtc->client_pending_transform,
&transform, &f_transform, &f_inverse);
RRModeGetScanoutSize(mode, &transform, &source_width,
&source_height);
if (stuff->x + source_width > pScreen->width) {
client->errorValue = stuff->x;
free(outputs);
return BadValue;
}
if (stuff->y + source_height > pScreen->height) {
client->errorValue = stuff->y;
free(outputs);
return BadValue;
}
}
#endif
}
if (!RRCrtcSet(crtc, mode, stuff->x, stuff->y,
rotation, numOutputs, outputs)) {
rep.status = RRSetConfigFailed;
goto sendReply;
}
rep.status = RRSetConfigSuccess;
pScrPriv->lastSetTime = time;
sendReply:
free(outputs);
rep.type = X_Reply;
/* rep.status has already been filled in */
rep.length = 0;
rep.sequenceNumber = client->sequence;
rep.newTimestamp = pScrPriv->lastSetTime.milliseconds;
if (client->swapped) {
swaps(&rep.sequenceNumber);
swapl(&rep.length);
swapl(&rep.newTimestamp);
}
WriteToClient(client, sizeof(xRRSetCrtcConfigReply), (char *) &rep);
return Success;
}
int
ProcRRGetPanning(ClientPtr client)
{
REQUEST(xRRGetPanningReq);
xRRGetPanningReply rep;
RRCrtcPtr crtc;
ScreenPtr pScreen;
rrScrPrivPtr pScrPriv;
BoxRec total;
BoxRec tracking;
INT16 border[4];
REQUEST_SIZE_MATCH(xRRGetPanningReq);
VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess);
/* All crtcs must be associated with screens before client
* requests are processed
*/
pScreen = crtc->pScreen;
pScrPriv = rrGetScrPriv(pScreen);
if (!pScrPriv)
return RRErrorBase + BadRRCrtc;
memset(&rep, 0, sizeof(rep));
rep.type = X_Reply;
rep.status = RRSetConfigSuccess;
rep.sequenceNumber = client->sequence;
rep.length = 1;
rep.timestamp = pScrPriv->lastSetTime.milliseconds;
if (pScrPriv->rrGetPanning &&
pScrPriv->rrGetPanning(pScreen, crtc, &total, &tracking, border)) {
rep.left = total.x1;
rep.top = total.y1;
rep.width = total.x2 - total.x1;
rep.height = total.y2 - total.y1;
rep.track_left = tracking.x1;
rep.track_top = tracking.y1;
rep.track_width = tracking.x2 - tracking.x1;
rep.track_height = tracking.y2 - tracking.y1;
rep.border_left = border[0];
rep.border_top = border[1];
rep.border_right = border[2];
rep.border_bottom = border[3];
}
if (client->swapped) {
swaps(&rep.sequenceNumber);
swapl(&rep.length);
swapl(&rep.timestamp);
swaps(&rep.left);
swaps(&rep.top);
swaps(&rep.width);
swaps(&rep.height);
swaps(&rep.track_left);
swaps(&rep.track_top);
swaps(&rep.track_width);
swaps(&rep.track_height);
swaps(&rep.border_left);
swaps(&rep.border_top);
swaps(&rep.border_right);
swaps(&rep.border_bottom);
}
WriteToClient(client, sizeof(xRRGetPanningReply), (char *) &rep);
return Success;
}
int
ProcRRSetPanning(ClientPtr client)
{
REQUEST(xRRSetPanningReq);
xRRSetPanningReply rep;
RRCrtcPtr crtc;
ScreenPtr pScreen;
rrScrPrivPtr pScrPriv;
TimeStamp time;
BoxRec total;
BoxRec tracking;
INT16 border[4];
REQUEST_SIZE_MATCH(xRRSetPanningReq);
VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess);
/* All crtcs must be associated with screens before client
* requests are processed
*/
pScreen = crtc->pScreen;
pScrPriv = rrGetScrPriv(pScreen);
if (!pScrPriv) {
time = currentTime;
rep.status = RRSetConfigFailed;
goto sendReply;
}
time = ClientTimeToServerTime(stuff->timestamp);
if (!pScrPriv->rrGetPanning)
return RRErrorBase + BadRRCrtc;
total.x1 = stuff->left;
total.y1 = stuff->top;
total.x2 = total.x1 + stuff->width;
total.y2 = total.y1 + stuff->height;
tracking.x1 = stuff->track_left;
tracking.y1 = stuff->track_top;
tracking.x2 = tracking.x1 + stuff->track_width;
tracking.y2 = tracking.y1 + stuff->track_height;
border[0] = stuff->border_left;
border[1] = stuff->border_top;
border[2] = stuff->border_right;
border[3] = stuff->border_bottom;
if (!pScrPriv->rrSetPanning(pScreen, crtc, &total, &tracking, border))
return BadMatch;
pScrPriv->lastSetTime = time;
rep.status = RRSetConfigSuccess;
sendReply:
rep.type = X_Reply;
rep.sequenceNumber = client->sequence;
rep.length = 0;
rep.newTimestamp = pScrPriv->lastSetTime.milliseconds;
if (client->swapped) {
swaps(&rep.sequenceNumber);
swapl(&rep.length);
swapl(&rep.newTimestamp);
}
WriteToClient(client, sizeof(xRRSetPanningReply), (char *) &rep);
return Success;
}
int
ProcRRGetCrtcGammaSize(ClientPtr client)
{
REQUEST(xRRGetCrtcGammaSizeReq);
xRRGetCrtcGammaSizeReply reply;
RRCrtcPtr crtc;
REQUEST_SIZE_MATCH(xRRGetCrtcGammaSizeReq);
VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess);
/* Gamma retrieval failed, any better error? */
if (!RRCrtcGammaGet(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);
swapl(&reply.length);
swaps(&reply.size);
}
WriteToClient(client, sizeof(xRRGetCrtcGammaSizeReply), (char *) &reply);
return Success;
}
int
ProcRRGetCrtcGamma(ClientPtr client)
{
REQUEST(xRRGetCrtcGammaReq);
xRRGetCrtcGammaReply reply;
RRCrtcPtr crtc;
unsigned long len;
char *extra = NULL;
REQUEST_SIZE_MATCH(xRRGetCrtcGammaReq);
VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess);
/* Gamma retrieval failed, any better error? */
if (!RRCrtcGammaGet(crtc))
return RRErrorBase + BadRRCrtc;
len = crtc->gammaSize * 3 * 2;
if (crtc->gammaSize) {
extra = malloc(len);
if (!extra)
return BadAlloc;
}
reply.type = X_Reply;
reply.sequenceNumber = client->sequence;
reply.length = bytes_to_int32(len);
reply.size = crtc->gammaSize;
if (client->swapped) {
swaps(&reply.sequenceNumber);
swapl(&reply.length);
swaps(&reply.size);
}
WriteToClient(client, sizeof(xRRGetCrtcGammaReply), (char *) &reply);
if (crtc->gammaSize) {
memcpy(extra, crtc->gammaRed, len);
client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write;
WriteSwappedDataToClient(client, len, extra);
free(extra);
}
return Success;
}
int
ProcRRSetCrtcGamma(ClientPtr client)
{
REQUEST(xRRSetCrtcGammaReq);
RRCrtcPtr crtc;
unsigned long len;
CARD16 *red, *green, *blue;
REQUEST_AT_LEAST_SIZE(xRRSetCrtcGammaReq);
VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess);
len = client->req_len - bytes_to_int32(sizeof(xRRSetCrtcGammaReq));
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;
}
/* Version 1.3 additions */
int
ProcRRSetCrtcTransform(ClientPtr client)
{
REQUEST(xRRSetCrtcTransformReq);
RRCrtcPtr crtc;
PictTransform transform;
struct pixman_f_transform f_transform, f_inverse;
char *filter;
int nbytes;
xFixed *params;
int nparams;
REQUEST_AT_LEAST_SIZE(xRRSetCrtcTransformReq);
VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess);
PictTransform_from_xRenderTransform(&transform, &stuff->transform);
pixman_f_transform_from_pixman_transform(&f_transform, &transform);
if (!pixman_f_transform_invert(&f_inverse, &f_transform))
return BadMatch;
filter = (char *) (stuff + 1);
nbytes = stuff->nbytesFilter;
params = (xFixed *) (filter + pad_to_int32(nbytes));
nparams = ((xFixed *) stuff + client->req_len) - params;
if (nparams < 0)
return BadLength;
return RRCrtcTransformSet(crtc, &transform, &f_transform, &f_inverse,
filter, nbytes, params, nparams);
}
#define CrtcTransformExtra (SIZEOF(xRRGetCrtcTransformReply) - 32)
static int
transform_filter_length(RRTransformPtr transform)
{
int nbytes, nparams;
if (transform->filter == NULL)
return 0;
nbytes = strlen(transform->filter->name);
nparams = transform->nparams;
return pad_to_int32(nbytes) + (nparams * sizeof(xFixed));
}
static int
transform_filter_encode(ClientPtr client, char *output,
CARD16 *nbytesFilter,
CARD16 *nparamsFilter, RRTransformPtr transform)
{
int nbytes, nparams;
if (transform->filter == NULL) {
*nbytesFilter = 0;
*nparamsFilter = 0;
return 0;
}
nbytes = strlen(transform->filter->name);
nparams = transform->nparams;
*nbytesFilter = nbytes;
*nparamsFilter = nparams;
memcpy(output, transform->filter->name, nbytes);
while ((nbytes & 3) != 0)
output[nbytes++] = 0;
memcpy(output + nbytes, transform->params, nparams * sizeof(xFixed));
if (client->swapped) {
swaps(nbytesFilter);
swaps(nparamsFilter);
SwapLongs((CARD32 *) (output + nbytes), nparams);
}
nbytes += nparams * sizeof(xFixed);
return nbytes;
}
static void
transform_encode(ClientPtr client, xRenderTransform * wire,
PictTransform * pict)
{
xRenderTransform_from_PictTransform(wire, pict);
if (client->swapped)
SwapLongs((CARD32 *) wire, bytes_to_int32(sizeof(xRenderTransform)));
}
int
ProcRRGetCrtcTransform(ClientPtr client)
{
REQUEST(xRRGetCrtcTransformReq);
xRRGetCrtcTransformReply *reply;
RRCrtcPtr crtc;
int nextra;
RRTransformPtr current, pending;
char *extra;
REQUEST_SIZE_MATCH(xRRGetCrtcTransformReq);
VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess);
pending = &crtc->client_pending_transform;
current = &crtc->client_current_transform;
nextra = (transform_filter_length(pending) +
transform_filter_length(current));
reply = malloc(sizeof(xRRGetCrtcTransformReply) + nextra);
if (!reply)
return BadAlloc;
extra = (char *) (reply + 1);
reply->type = X_Reply;
reply->sequenceNumber = client->sequence;
reply->length = bytes_to_int32(CrtcTransformExtra + nextra);
reply->hasTransforms = crtc->transforms;
transform_encode(client, &reply->pendingTransform, &pending->transform);
extra += transform_filter_encode(client, extra,
&reply->pendingNbytesFilter,
&reply->pendingNparamsFilter, pending);
transform_encode(client, &reply->currentTransform, &current->transform);
extra += transform_filter_encode(client, extra,
&reply->currentNbytesFilter,
&reply->currentNparamsFilter, current);
if (client->swapped) {
swaps(&reply->sequenceNumber);
swapl(&reply->length);
}
WriteToClient(client, sizeof(xRRGetCrtcTransformReply) + nextra,
(char *) reply);
free(reply);
return Success;
}
void
RRConstrainCursorHarder(DeviceIntPtr pDev, ScreenPtr pScreen, int mode, int *x,
int *y)
{
rrScrPriv(pScreen);
int i;
/* intentional dead space -> let it float */
if (pScrPriv->discontiguous)
return;
/* if we're moving inside a crtc, we're fine */
for (i = 0; i < pScrPriv->numCrtcs; i++) {
RRCrtcPtr crtc = pScrPriv->crtcs[i];
int left, right, top, bottom;
if (!crtc->mode)
continue;
crtc_bounds(crtc, &left, &right, &top, &bottom);
if ((*x >= left) && (*x < right) && (*y >= top) && (*y < bottom))
return;
}
/* if we're trying to escape, clamp to the CRTC we're coming from */
for (i = 0; i < pScrPriv->numCrtcs; i++) {
RRCrtcPtr crtc = pScrPriv->crtcs[i];
int nx, ny;
int left, right, top, bottom;
if (!crtc->mode)
continue;
crtc_bounds(crtc, &left, &right, &top, &bottom);
miPointerGetPosition(pDev, &nx, &ny);
if ((nx >= left) && (nx < right) && (ny >= top) && (ny < bottom)) {
if (*x < left)
*x = left;
if (*x >= right)
*x = right - 1;
if (*y < top)
*y = top;
if (*y >= bottom)
*y = bottom - 1;
return;
}
}
}