You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
xserver/randr/rrscreen.c

1158 lines
34 KiB
C

/*
* 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.
*/
#include "randrstr.h"
static CARD16
RR10CurrentSizeID(ScreenPtr pScreen);
/*
* Edit connection information block so that new clients
* see the current screen size on connect
*/
static void
RREditConnectionInfo(ScreenPtr pScreen)
{
xConnSetup *connSetup;
char *vendor;
xPixmapFormat *formats;
xWindowRoot *root;
xDepth *depth;
xVisualType *visual;
int screen = 0;
int d;
if (ConnectionInfo == NULL)
return;
connSetup = (xConnSetup *) ConnectionInfo;
vendor = (char *) connSetup + sizeof(xConnSetup);
formats = (xPixmapFormat *) ((char *) vendor +
pad_to_int32(connSetup->nbytesVendor));
root = (xWindowRoot *) ((char *) formats +
sizeof(xPixmapFormat) *
screenInfo.numPixmapFormats);
while (screen != pScreen->myNum) {
depth = (xDepth *) ((char *) root + sizeof(xWindowRoot));
for (d = 0; d < root->nDepths; d++) {
visual = (xVisualType *) ((char *) depth + sizeof(xDepth));
depth = (xDepth *) ((char *) visual +
depth->nVisuals * sizeof(xVisualType));
}
root = (xWindowRoot *) ((char *) depth);
screen++;
}
root->pixWidth = pScreen->width;
root->pixHeight = pScreen->height;
root->mmWidth = pScreen->mmWidth;
root->mmHeight = pScreen->mmHeight;
}
void
RRSendConfigNotify(ScreenPtr pScreen)
{
WindowPtr pWin = pScreen->root;
xEvent event = {
.u.configureNotify.window = pWin->drawable.id,
.u.configureNotify.aboveSibling = None,
.u.configureNotify.x = 0,
.u.configureNotify.y = 0,
/* XXX xinerama stuff ? */
.u.configureNotify.width = pWin->drawable.width,
.u.configureNotify.height = pWin->drawable.height,
.u.configureNotify.borderWidth = wBorderWidth(pWin),
.u.configureNotify.override = pWin->overrideRedirect
};
event.u.u.type = ConfigureNotify;
DeliverEvents(pWin, &event, 1, NullWindow);
}
void
RRDeliverScreenEvent(ClientPtr client, WindowPtr pWin, ScreenPtr pScreen)
{
rrScrPriv(pScreen);
RRCrtcPtr crtc = pScrPriv->numCrtcs ? pScrPriv->crtcs[0] : NULL;
WindowPtr pRoot = pScreen->root;
xRRScreenChangeNotifyEvent se = {
.type = RRScreenChangeNotify + RREventBase,
.rotation = (CARD8) (crtc ? crtc->rotation : RR_Rotate_0),
.timestamp = pScrPriv->lastSetTime.milliseconds,
.configTimestamp = pScrPriv->lastConfigTime.milliseconds,
.root = pRoot->drawable.id,
.window = pWin->drawable.id,
.subpixelOrder = PictureGetSubpixelOrder(pScreen),
.sizeID = RR10CurrentSizeID(pScreen)
};
if (se.rotation & (RR_Rotate_90 | RR_Rotate_270)) {
se.widthInPixels = pScreen->height;
se.heightInPixels = pScreen->width;
se.widthInMillimeters = pScreen->mmHeight;
se.heightInMillimeters = pScreen->mmWidth;
}
else {
se.widthInPixels = pScreen->width;
se.heightInPixels = pScreen->height;
se.widthInMillimeters = pScreen->mmWidth;
se.heightInMillimeters = pScreen->mmHeight;
}
WriteEventsToClient(client, 1, (xEvent *) &se);
}
/*
* Notify the extension that the screen size has been changed.
* The driver is responsible for calling this whenever it has changed
* the size of the screen
*/
void
RRScreenSizeNotify(ScreenPtr pScreen)
{
rrScrPriv(pScreen);
/*
* Deliver ConfigureNotify events when root changes
* pixel size
*/
if (pScrPriv->width == pScreen->width &&
pScrPriv->height == pScreen->height &&
pScrPriv->mmWidth == pScreen->mmWidth &&
pScrPriv->mmHeight == pScreen->mmHeight)
return;
pScrPriv->width = pScreen->width;
pScrPriv->height = pScreen->height;
pScrPriv->mmWidth = pScreen->mmWidth;
pScrPriv->mmHeight = pScreen->mmHeight;
RRSetChanged(pScreen);
/* pScrPriv->sizeChanged = TRUE; */
RRTellChanged(pScreen);
RRSendConfigNotify(pScreen);
RREditConnectionInfo(pScreen);
RRPointerScreenConfigured(pScreen);
/*
* Fix pointer bounds and location
*/
ScreenRestructured(pScreen);
}
/*
* Request that the screen be resized
*/
Bool
RRScreenSizeSet(ScreenPtr pScreen,
CARD16 width, CARD16 height, CARD32 mmWidth, CARD32 mmHeight)
{
rrScrPriv(pScreen);
#if RANDR_12_INTERFACE
if (pScrPriv->rrScreenSetSize) {
return (*pScrPriv->rrScreenSetSize) (pScreen,
width, height, mmWidth, mmHeight);
}
#endif
#if RANDR_10_INTERFACE
if (pScrPriv->rrSetConfig) {
return TRUE; /* can't set size separately */
}
#endif
return FALSE;
}
/*
* Retrieve valid screen size range
*/
int
ProcRRGetScreenSizeRange(ClientPtr client)
{
REQUEST(xRRGetScreenSizeRangeReq);
xRRGetScreenSizeRangeReply rep;
WindowPtr pWin;
ScreenPtr pScreen;
rrScrPrivPtr pScrPriv;
int rc;
REQUEST_SIZE_MATCH(xRRGetScreenSizeRangeReq);
rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
if (rc != Success)
return rc;
pScreen = pWin->drawable.pScreen;
pScrPriv = rrGetScrPriv(pScreen);
rep = (xRRGetScreenSizeRangeReply) {
.type = X_Reply,
.pad = 0,
.sequenceNumber = client->sequence,
.length = 0
};
if (pScrPriv) {
if (!RRGetInfo(pScreen, FALSE))
return BadAlloc;
rep.minWidth = pScrPriv->minWidth;
rep.minHeight = pScrPriv->minHeight;
rep.maxWidth = pScrPriv->maxWidth;
rep.maxHeight = pScrPriv->maxHeight;
}
else {
rep.maxWidth = rep.minWidth = pScreen->width;
rep.maxHeight = rep.minHeight = pScreen->height;
}
if (client->swapped) {
swaps(&rep.sequenceNumber);
swapl(&rep.length);
swaps(&rep.minWidth);
swaps(&rep.minHeight);
swaps(&rep.maxWidth);
swaps(&rep.maxHeight);
}
WriteToClient(client, sizeof(xRRGetScreenSizeRangeReply), &rep);
return Success;
}
int
ProcRRSetScreenSize(ClientPtr client)
{
REQUEST(xRRSetScreenSizeReq);
WindowPtr pWin;
ScreenPtr pScreen;
rrScrPrivPtr pScrPriv;
int i, rc;
REQUEST_SIZE_MATCH(xRRSetScreenSizeReq);
rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
if (rc != Success)
return rc;
pScreen = pWin->drawable.pScreen;
pScrPriv = rrGetScrPriv(pScreen);
if (!pScrPriv)
return BadMatch;
if (stuff->width < pScrPriv->minWidth || pScrPriv->maxWidth < stuff->width) {
client->errorValue = stuff->width;
return BadValue;
}
if (stuff->height < pScrPriv->minHeight ||
pScrPriv->maxHeight < stuff->height) {
client->errorValue = stuff->height;
return BadValue;
}
for (i = 0; i < pScrPriv->numCrtcs; i++) {
RRCrtcPtr crtc = pScrPriv->crtcs[i];
RRModePtr mode = crtc->mode;
if (!RRCrtcIsLeased(crtc) && mode) {
int source_width = mode->mode.width;
int source_height = mode->mode.height;
Rotation rotation = crtc->rotation;
if (rotation & (RR_Rotate_90 | RR_Rotate_270)) {
source_width = mode->mode.height;
source_height = mode->mode.width;
}
if (crtc->x + source_width > stuff->width ||
crtc->y + source_height > stuff->height)
return BadMatch;
}
}
if (stuff->widthInMillimeters == 0 || stuff->heightInMillimeters == 0) {
client->errorValue = 0;
return BadValue;
}
if (!RRScreenSizeSet(pScreen,
stuff->width, stuff->height,
stuff->widthInMillimeters,
stuff->heightInMillimeters)) {
return BadMatch;
}
return Success;
}
#define update_totals(gpuscreen, pScrPriv) do { \
total_crtcs += pScrPriv->numCrtcs; \
total_outputs += pScrPriv->numOutputs; \
modes = RRModesForScreen(gpuscreen, &num_modes); \
if (!modes) \
return BadAlloc; \
for (j = 0; j < num_modes; j++) \
total_name_len += modes[j]->mode.nameLength; \
total_modes += num_modes; \
free(modes); \
} while(0)
static inline void swap_modeinfos(xRRModeInfo *modeinfos, int i)
{
swapl(&modeinfos[i].id);
swaps(&modeinfos[i].width);
swaps(&modeinfos[i].height);
swapl(&modeinfos[i].dotClock);
swaps(&modeinfos[i].hSyncStart);
swaps(&modeinfos[i].hSyncEnd);
swaps(&modeinfos[i].hTotal);
swaps(&modeinfos[i].hSkew);
swaps(&modeinfos[i].vSyncStart);
swaps(&modeinfos[i].vSyncEnd);
swaps(&modeinfos[i].vTotal);
swaps(&modeinfos[i].nameLength);
swapl(&modeinfos[i].modeFlags);
}
#define update_arrays(gpuscreen, pScrPriv, primary_crtc, has_primary) do { \
for (j = 0; j < pScrPriv->numCrtcs; j++) { \
if (has_primary && \
primary_crtc == pScrPriv->crtcs[j]) { \
has_primary = 0; \
continue; \
}\
crtcs[crtc_count] = pScrPriv->crtcs[j]->id; \
if (client->swapped) \
swapl(&crtcs[crtc_count]); \
crtc_count++; \
} \
for (j = 0; j < pScrPriv->numOutputs; j++) { \
outputs[output_count] = pScrPriv->outputs[j]->id; \
if (client->swapped) \
swapl(&outputs[output_count]); \
output_count++; \
} \
{ \
RRModePtr mode; \
modes = RRModesForScreen(gpuscreen, &num_modes); \
for (j = 0; j < num_modes; j++) { \
mode = modes[j]; \
modeinfos[mode_count] = mode->mode; \
if (client->swapped) { \
swap_modeinfos(modeinfos, mode_count); \
} \
memcpy(names, mode->name, mode->mode.nameLength); \
names += mode->mode.nameLength; \
mode_count++; \
} \
free(modes); \
} \
} while (0)
static int
rrGetMultiScreenResources(ClientPtr client, Bool query, ScreenPtr pScreen)
{
int j;
int total_crtcs, total_outputs, total_modes, total_name_len;
int crtc_count, output_count, mode_count;
ScreenPtr iter;
rrScrPrivPtr pScrPriv;
int num_modes;
RRModePtr *modes;
xRRGetScreenResourcesReply rep;
unsigned long extraLen;
CARD8 *extra;
RRCrtc *crtcs;
RRCrtcPtr primary_crtc = NULL;
RROutput *outputs;
xRRModeInfo *modeinfos;
CARD8 *names;
int has_primary = 0;
/* we need to iterate all the GPU primarys and all their output secondarys */
total_crtcs = 0;
total_outputs = 0;
total_modes = 0;
total_name_len = 0;
pScrPriv = rrGetScrPriv(pScreen);
if (query && pScrPriv)
if (!RRGetInfo(pScreen, query))
return BadAlloc;
update_totals(pScreen, pScrPriv);
xorg_list_for_each_entry(iter, &pScreen->secondary_list, secondary_head) {
if (!iter->is_output_secondary)
continue;
pScrPriv = rrGetScrPriv(iter);
if (query)
if (!RRGetInfo(iter, query))
return BadAlloc;
update_totals(iter, pScrPriv);
}
pScrPriv = rrGetScrPriv(pScreen);
rep = (xRRGetScreenResourcesReply) {
.type = X_Reply,
.sequenceNumber = client->sequence,
.length = 0,
.timestamp = pScrPriv->lastSetTime.milliseconds,
.configTimestamp = pScrPriv->lastConfigTime.milliseconds,
.nCrtcs = total_crtcs,
.nOutputs = total_outputs,
.nModes = total_modes,
.nbytesNames = total_name_len
};
rep.length = (total_crtcs + total_outputs +
total_modes * bytes_to_int32(SIZEOF(xRRModeInfo)) +
bytes_to_int32(total_name_len));
extraLen = rep.length << 2;
if (extraLen) {
extra = malloc(extraLen);
if (!extra) {
return BadAlloc;
}
}
else
extra = NULL;
crtcs = (RRCrtc *)extra;
outputs = (RROutput *)(crtcs + total_crtcs);
modeinfos = (xRRModeInfo *)(outputs + total_outputs);
names = (CARD8 *)(modeinfos + total_modes);
crtc_count = 0;
output_count = 0;
mode_count = 0;
pScrPriv = rrGetScrPriv(pScreen);
if (pScrPriv->primaryOutput && pScrPriv->primaryOutput->crtc) {
has_primary = 1;
primary_crtc = pScrPriv->primaryOutput->crtc;
crtcs[0] = pScrPriv->primaryOutput->crtc->id;
if (client->swapped)
swapl(&crtcs[0]);
crtc_count = 1;
}
update_arrays(pScreen, pScrPriv, primary_crtc, has_primary);
xorg_list_for_each_entry(iter, &pScreen->secondary_list, secondary_head) {
if (!iter->is_output_secondary)
continue;
pScrPriv = rrGetScrPriv(iter);
update_arrays(iter, pScrPriv, primary_crtc, has_primary);
}
assert(bytes_to_int32((char *) names - (char *) extra) == rep.length);
if (client->swapped) {
swaps(&rep.sequenceNumber);
swapl(&rep.length);
swapl(&rep.timestamp);
swapl(&rep.configTimestamp);
swaps(&rep.nCrtcs);
swaps(&rep.nOutputs);
swaps(&rep.nModes);
swaps(&rep.nbytesNames);
}
WriteToClient(client, sizeof(xRRGetScreenResourcesReply), &rep);
if (extraLen) {
WriteToClient(client, extraLen, extra);
free(extra);
}
return Success;
}
static int
rrGetScreenResources(ClientPtr client, Bool query)
{
REQUEST(xRRGetScreenResourcesReq);
xRRGetScreenResourcesReply rep;
WindowPtr pWin;
ScreenPtr pScreen;
rrScrPrivPtr pScrPriv;
CARD8 *extra;
unsigned long extraLen;
int i, rc, has_primary = 0;
RRCrtc *crtcs;
RROutput *outputs;
xRRModeInfo *modeinfos;
CARD8 *names;
REQUEST_SIZE_MATCH(xRRGetScreenResourcesReq);
rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
if (rc != Success)
return rc;
pScreen = pWin->drawable.pScreen;
pScrPriv = rrGetScrPriv(pScreen);
if (query && pScrPriv)
if (!RRGetInfo(pScreen, query))
return BadAlloc;
if (pScreen->output_secondarys)
return rrGetMultiScreenResources(client, query, pScreen);
if (!pScrPriv) {
rep = (xRRGetScreenResourcesReply) {
.type = X_Reply,
.sequenceNumber = client->sequence,
.length = 0,
.timestamp = currentTime.milliseconds,
.configTimestamp = currentTime.milliseconds,
.nCrtcs = 0,
.nOutputs = 0,
.nModes = 0,
.nbytesNames = 0
};
extra = NULL;
extraLen = 0;
}
else {
RRModePtr *modes;
int num_modes;
modes = RRModesForScreen(pScreen, &num_modes);
if (!modes)
return BadAlloc;
rep = (xRRGetScreenResourcesReply) {
.type = X_Reply,
.sequenceNumber = client->sequence,
.length = 0,
.timestamp = pScrPriv->lastSetTime.milliseconds,
.configTimestamp = pScrPriv->lastConfigTime.milliseconds,
.nCrtcs = pScrPriv->numCrtcs,
.nOutputs = pScrPriv->numOutputs,
.nModes = num_modes,
.nbytesNames = 0
};
for (i = 0; i < num_modes; i++)
rep.nbytesNames += modes[i]->mode.nameLength;
rep.length = (pScrPriv->numCrtcs +
pScrPriv->numOutputs +
num_modes * bytes_to_int32(SIZEOF(xRRModeInfo)) +
bytes_to_int32(rep.nbytesNames));
extraLen = rep.length << 2;
if (extraLen) {
extra = calloc(1, extraLen);
if (!extra) {
free(modes);
return BadAlloc;
}
}
else
extra = NULL;
crtcs = (RRCrtc *) extra;
outputs = (RROutput *) (crtcs + pScrPriv->numCrtcs);
modeinfos = (xRRModeInfo *) (outputs + pScrPriv->numOutputs);
names = (CARD8 *) (modeinfos + num_modes);
if (pScrPriv->primaryOutput && pScrPriv->primaryOutput->crtc) {
has_primary = 1;
crtcs[0] = pScrPriv->primaryOutput->crtc->id;
if (client->swapped)
swapl(&crtcs[0]);
}
for (i = 0; i < pScrPriv->numCrtcs; i++) {
if (has_primary &&
pScrPriv->primaryOutput->crtc == pScrPriv->crtcs[i]) {
has_primary = 0;
continue;
}
crtcs[i + has_primary] = pScrPriv->crtcs[i]->id;
if (client->swapped)
swapl(&crtcs[i + has_primary]);
}
for (i = 0; i < pScrPriv->numOutputs; i++) {
outputs[i] = pScrPriv->outputs[i]->id;
if (client->swapped)
swapl(&outputs[i]);
}
for (i = 0; i < num_modes; i++) {
RRModePtr mode = modes[i];
modeinfos[i] = mode->mode;
if (client->swapped) {
swapl(&modeinfos[i].id);
swaps(&modeinfos[i].width);
swaps(&modeinfos[i].height);
swapl(&modeinfos[i].dotClock);
swaps(&modeinfos[i].hSyncStart);
swaps(&modeinfos[i].hSyncEnd);
swaps(&modeinfos[i].hTotal);
swaps(&modeinfos[i].hSkew);
swaps(&modeinfos[i].vSyncStart);
swaps(&modeinfos[i].vSyncEnd);
swaps(&modeinfos[i].vTotal);
swaps(&modeinfos[i].nameLength);
swapl(&modeinfos[i].modeFlags);
}
memcpy(names, mode->name, mode->mode.nameLength);
names += mode->mode.nameLength;
}
free(modes);
assert(bytes_to_int32((char *) names - (char *) extra) == rep.length);
}
if (client->swapped) {
swaps(&rep.sequenceNumber);
swapl(&rep.length);
swapl(&rep.timestamp);
swapl(&rep.configTimestamp);
swaps(&rep.nCrtcs);
swaps(&rep.nOutputs);
swaps(&rep.nModes);
swaps(&rep.nbytesNames);
}
WriteToClient(client, sizeof(xRRGetScreenResourcesReply), (char *) &rep);
if (extraLen) {
WriteToClient(client, extraLen, (char *) extra);
free(extra);
}
return Success;
}
int
ProcRRGetScreenResources(ClientPtr client)
{
return rrGetScreenResources(client, TRUE);
}
int
ProcRRGetScreenResourcesCurrent(ClientPtr client)
{
return rrGetScreenResources(client, FALSE);
}
typedef struct _RR10Data {
RRScreenSizePtr sizes;
int nsize;
int nrefresh;
int size;
CARD16 refresh;
} RR10DataRec, *RR10DataPtr;
/*
* Convert 1.2 monitor data into 1.0 screen data
*/
static RR10DataPtr
RR10GetData(ScreenPtr pScreen, RROutputPtr output)
{
RR10DataPtr data;
RRScreenSizePtr size;
int nmode = output->numModes + output->numUserModes;
int o, os, l, r;
RRScreenRatePtr refresh;
CARD16 vRefresh;
RRModePtr mode;
Bool *used;
/* Make sure there is plenty of space for any combination */
data = malloc(sizeof(RR10DataRec) +
sizeof(RRScreenSize) * nmode +
sizeof(RRScreenRate) * nmode + sizeof(Bool) * nmode);
if (!data)
return NULL;
size = (RRScreenSizePtr) (data + 1);
refresh = (RRScreenRatePtr) (size + nmode);
used = (Bool *) (refresh + nmode);
memset(used, '\0', sizeof(Bool) * nmode);
data->sizes = size;
data->nsize = 0;
data->nrefresh = 0;
data->size = 0;
data->refresh = 0;
/*
* find modes not yet listed
*/
for (o = 0; o < output->numModes + output->numUserModes; o++) {
if (used[o])
continue;
if (o < output->numModes)
mode = output->modes[o];
else
mode = output->userModes[o - output->numModes];
l = data->nsize;
size[l].id = data->nsize;
size[l].width = mode->mode.width;
size[l].height = mode->mode.height;
if (output->mmWidth && output->mmHeight) {
size[l].mmWidth = output->mmWidth;
size[l].mmHeight = output->mmHeight;
}
else {
size[l].mmWidth = pScreen->mmWidth;
size[l].mmHeight = pScreen->mmHeight;
}
size[l].nRates = 0;
size[l].pRates = &refresh[data->nrefresh];
data->nsize++;
/*
* Find all modes with matching size
*/
for (os = o; os < output->numModes + output->numUserModes; os++) {
if (os < output->numModes)
mode = output->modes[os];
else
mode = output->userModes[os - output->numModes];
if (mode->mode.width == size[l].width &&
mode->mode.height == size[l].height) {
vRefresh = RRVerticalRefresh(&mode->mode);
used[os] = TRUE;
for (r = 0; r < size[l].nRates; r++)
if (vRefresh == size[l].pRates[r].rate)
break;
if (r == size[l].nRates) {
size[l].pRates[r].rate = vRefresh;
size[l].pRates[r].mode = mode;
size[l].nRates++;
data->nrefresh++;
}
if (mode == output->crtc->mode) {
data->size = l;
data->refresh = vRefresh;
}
}
}
}
return data;
}
int
ProcRRGetScreenInfo(ClientPtr client)
{
REQUEST(xRRGetScreenInfoReq);
xRRGetScreenInfoReply rep;
WindowPtr pWin;
int rc;
ScreenPtr pScreen;
rrScrPrivPtr pScrPriv;
CARD8 *extra;
unsigned long extraLen;
RROutputPtr output;
REQUEST_SIZE_MATCH(xRRGetScreenInfoReq);
rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
if (rc != Success)
return rc;
pScreen = pWin->drawable.pScreen;
pScrPriv = rrGetScrPriv(pScreen);
if (pScrPriv)
if (!RRGetInfo(pScreen, TRUE))
return BadAlloc;
output = RRFirstOutput(pScreen);
if (!pScrPriv || !output) {
rep = (xRRGetScreenInfoReply) {
.type = X_Reply,
.setOfRotations = RR_Rotate_0,
.sequenceNumber = client->sequence,
.length = 0,
.root = pWin->drawable.pScreen->root->drawable.id,
.timestamp = currentTime.milliseconds,
.configTimestamp = currentTime.milliseconds,
.nSizes = 0,
.sizeID = 0,
.rotation = RR_Rotate_0,
.rate = 0,
.nrateEnts = 0
};
extra = 0;
extraLen = 0;
}
else {
int i, j;
xScreenSizes *size;
CARD16 *rates;
CARD8 *data8;
Bool has_rate = RRClientKnowsRates(client);
RR10DataPtr pData;
RRScreenSizePtr pSize;
pData = RR10GetData(pScreen, output);
if (!pData)
return BadAlloc;
rep = (xRRGetScreenInfoReply) {
.type = X_Reply,
.setOfRotations = output->crtc->rotations,
.sequenceNumber = client->sequence,
.length = 0,
.root = pWin->drawable.pScreen->root->drawable.id,
.timestamp = pScrPriv->lastSetTime.milliseconds,
.configTimestamp = pScrPriv->lastConfigTime.milliseconds,
.rotation = output->crtc->rotation,
.nSizes = pData->nsize,
.nrateEnts = pData->nrefresh + pData->nsize,
.sizeID = pData->size,
.rate = pData->refresh
};
extraLen = rep.nSizes * sizeof(xScreenSizes);
if (has_rate)
extraLen += rep.nrateEnts * sizeof(CARD16);
if (extraLen) {
extra = (CARD8 *) malloc(extraLen);
if (!extra) {
free(pData);
return BadAlloc;
}
}
else
extra = NULL;
/*
* First comes the size information
*/
size = (xScreenSizes *) extra;
rates = (CARD16 *) (size + rep.nSizes);
for (i = 0; i < pData->nsize; i++) {
pSize = &pData->sizes[i];
size->widthInPixels = pSize->width;
size->heightInPixels = pSize->height;
size->widthInMillimeters = pSize->mmWidth;
size->heightInMillimeters = pSize->mmHeight;
if (client->swapped) {
swaps(&size->widthInPixels);
swaps(&size->heightInPixels);
swaps(&size->widthInMillimeters);
swaps(&size->heightInMillimeters);
}
size++;
if (has_rate) {
*rates = pSize->nRates;
if (client->swapped) {
swaps(rates);
}
rates++;
for (j = 0; j < pSize->nRates; j++) {
*rates = pSize->pRates[j].rate;
if (client->swapped) {
swaps(rates);
}
rates++;
}
}
}
free(pData);
data8 = (CARD8 *) rates;
if (data8 - (CARD8 *) extra != extraLen)
FatalError("RRGetScreenInfo bad extra len %ld != %ld\n",
(unsigned long) (data8 - (CARD8 *) extra), extraLen);
rep.length = bytes_to_int32(extraLen);
}
if (client->swapped) {
swaps(&rep.sequenceNumber);
swapl(&rep.length);
swapl(&rep.timestamp);
swapl(&rep.configTimestamp);
swaps(&rep.rotation);
swaps(&rep.nSizes);
swaps(&rep.sizeID);
swaps(&rep.rate);
swaps(&rep.nrateEnts);
}
WriteToClient(client, sizeof(xRRGetScreenInfoReply), &rep);
if (extraLen) {
WriteToClient(client, extraLen, extra);
free(extra);
}
return Success;
}
int
ProcRRSetScreenConfig(ClientPtr client)
{
REQUEST(xRRSetScreenConfigReq);
xRRSetScreenConfigReply rep;
DrawablePtr pDraw;
int rc;
ScreenPtr pScreen;
rrScrPrivPtr pScrPriv;
TimeStamp time;
int i;
Rotation rotation;
int rate;
Bool has_rate;
CARD8 status;
RROutputPtr output;
RRCrtcPtr crtc;
RRModePtr mode;
RR10DataPtr pData = NULL;
RRScreenSizePtr pSize;
int width, height;
UpdateCurrentTime();
if (RRClientKnowsRates(client)) {
REQUEST_SIZE_MATCH(xRRSetScreenConfigReq);
has_rate = TRUE;
}
else {
REQUEST_SIZE_MATCH(xRR1_0SetScreenConfigReq);
has_rate = FALSE;
}
rc = dixLookupDrawable(&pDraw, stuff->drawable, client, 0, DixWriteAccess);
if (rc != Success)
return rc;
pScreen = pDraw->pScreen;
pScrPriv = rrGetScrPriv(pScreen);
time = ClientTimeToServerTime(stuff->timestamp);
if (!pScrPriv) {
time = currentTime;
status = RRSetConfigFailed;
goto sendReply;
}
if (!RRGetInfo(pScreen, FALSE))
return BadAlloc;
output = RRFirstOutput(pScreen);
if (!output) {
time = currentTime;
status = RRSetConfigFailed;
goto sendReply;
}
crtc = output->crtc;
/*
* 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.
*
* Note that the client only knows about the milliseconds part of the
* timestamp, so using CompareTimeStamps here would cause randr to suddenly
* stop working after several hours have passed (freedesktop bug #6502).
*/
if (stuff->configTimestamp != pScrPriv->lastConfigTime.milliseconds) {
status = RRSetConfigInvalidConfigTime;
goto sendReply;
}
pData = RR10GetData(pScreen, output);
if (!pData)
return BadAlloc;
if (stuff->sizeID >= pData->nsize) {
/*
* Invalid size ID
*/
client->errorValue = stuff->sizeID;
free(pData);
return BadValue;
}
pSize = &pData->sizes[stuff->sizeID];
/*
* 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(pData);
return BadValue;
}
if ((~crtc->rotations) & rotation) {
/*
* requested rotation or reflection not supported by screen
*/
client->errorValue = stuff->rotation;
free(pData);
return BadMatch;
}
/*
* Validate requested refresh
*/
if (has_rate)
rate = (int) stuff->rate;
else
rate = 0;
if (rate) {
for (i = 0; i < pSize->nRates; i++) {
if (pSize->pRates[i].rate == rate)
break;
}
if (i == pSize->nRates) {
/*
* Invalid rate
*/
client->errorValue = rate;
free(pData);
return BadValue;
}
mode = pSize->pRates[i].mode;
}
else
mode = pSize->pRates[0].mode;
/*
* Make sure the requested set-time is not older than
* the last set-time
*/
if (CompareTimeStamps(time, pScrPriv->lastSetTime) < 0) {
status = RRSetConfigInvalidTime;
goto sendReply;
}
/*
* If the screen size is changing, adjust all of the other outputs
* to fit the new size, mirroring as much as possible
*/
width = mode->mode.width;
height = mode->mode.height;
if (width < pScrPriv->minWidth || pScrPriv->maxWidth < width) {
client->errorValue = width;
free(pData);
return BadValue;
}
if (height < pScrPriv->minHeight || pScrPriv->maxHeight < height) {
client->errorValue = height;
free(pData);
return BadValue;
}
if (rotation & (RR_Rotate_90 | RR_Rotate_270)) {
width = mode->mode.height;
height = mode->mode.width;
}
if (width != pScreen->width || height != pScreen->height) {
int c;
for (c = 0; c < pScrPriv->numCrtcs; c++) {
if (!RRCrtcSet(pScrPriv->crtcs[c], NULL, 0, 0, RR_Rotate_0,
0, NULL)) {
status = RRSetConfigFailed;
/* XXX recover from failure */
goto sendReply;
}
}
if (!RRScreenSizeSet(pScreen, width, height,
pScreen->mmWidth, pScreen->mmHeight)) {
status = RRSetConfigFailed;
/* XXX recover from failure */
goto sendReply;
}
}
if (!RRCrtcSet(crtc, mode, 0, 0, stuff->rotation, 1, &output))
status = RRSetConfigFailed;
else {
pScrPriv->lastSetTime = time;
status = RRSetConfigSuccess;
}
/*
* XXX Configure other crtcs to mirror as much as possible
*/
sendReply:
free(pData);
rep = (xRRSetScreenConfigReply) {
.type = X_Reply,
.status = status,
.sequenceNumber = client->sequence,
.length = 0,
.newTimestamp = pScrPriv->lastSetTime.milliseconds,
.newConfigTimestamp = pScrPriv->lastConfigTime.milliseconds,
.root = pDraw->pScreen->root->drawable.id,
/* .subpixelOrder = ?? */
};
if (client->swapped) {
swaps(&rep.sequenceNumber);
swapl(&rep.length);
swapl(&rep.newTimestamp);
swapl(&rep.newConfigTimestamp);
swapl(&rep.root);
}
WriteToClient(client, sizeof(xRRSetScreenConfigReply), &rep);
return Success;
}
static CARD16
RR10CurrentSizeID(ScreenPtr pScreen)
{
CARD16 sizeID = 0xffff;
RROutputPtr output = RRFirstOutput(pScreen);
if (output) {
RR10DataPtr data = RR10GetData(pScreen, output);
if (data) {
int i;
for (i = 0; i < data->nsize; i++)
if (data->sizes[i].width == pScreen->width &&
data->sizes[i].height == pScreen->height) {
sizeID = (CARD16) i;
break;
}
free(data);
}
}
return sizeID;
}