/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ /* Copyright (c) 2008-2014 Oleksandr Shneyder */ /* Copyright (c) 2011-2016 Mike Gabriel */ /* Copyright (c) 2014-2016 Mihai Moldovan */ /* Copyright (c) 2014-2016 Ulrich Sibiller */ /* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ /* */ /* nx-X11, NX protocol compression and NX extensions to this software */ /* are copyright of the aforementioned persons and companies. */ /* */ /* Redistribution and use of the present software is allowed according */ /* to terms specified in the file LICENSE which comes in the source */ /* distribution. */ /* */ /* All rights reserved. */ /* */ /* NOTE: This software has received contributions from various other */ /* contributors, only the core maintainers and supporters are listed as */ /* copyright holders. Please contact us, if you feel you should be listed */ /* as copyright holder, as well. */ /* */ /**************************************************************************/ /* * 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; 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 *) (char *) &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); #ifndef NXAGENT_SERVER rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); #else pWin = SecurityLookupWindow(stuff->window, client, DixReadAccess); rc = pWin ? Success : BadWindow; #endif 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); #ifndef NXAGENT_SERVER rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); #else pWin = SecurityLookupWindow(stuff->window, client, DixReadAccess); rc = pWin ? Success : BadWindow; #endif 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 (mode) { int source_width = mode->mode.width; int source_height = mode->mode.height; Rotation rotation = crtc->rotation; if (rotation == RR_Rotate_90 || rotation == 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) #ifndef NXAGENT_SERVER 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 masters and all their output slaves */ 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->output_slave_list, output_head) { 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 = calloc(1,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->output_slave_list, output_head) { 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; } #endif /* !defined(NXAGENT_SERVER) */ 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); #ifndef NXAGENT_SERVER rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); #else pWin = SecurityLookupWindow(stuff->window, client, DixReadAccess); rc = pWin ? Success : BadWindow; #endif if (rc != Success) return rc; pScreen = pWin->drawable.pScreen; pScrPriv = rrGetScrPriv(pScreen); if (query && pScrPriv) if (!RRGetInfo(pScreen, query)) return BadAlloc; #ifndef NXAGENT_SERVER if (!xorg_list_is_empty(&pScreen->output_slave_list)) return rrGetMultiScreenResources(client, query, pScreen); #endif 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, 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); #ifndef NXAGENT_SERVER rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); #else pWin = SecurityLookupWindow(stuff->window, client, DixReadAccess); rc = pWin ? Success : BadWindow; #endif 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 *) calloc(1, 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; } #ifndef NXAGENT_SERVER rc = dixLookupDrawable(&pDraw, stuff->drawable, client, 0, DixWriteAccess); #else /* !defined(NXAGENT_SERVER) */ pDraw = SecurityLookupDrawable(stuff->drawable, client, DixWriteAccess); rc = pDraw ? Success : BadDrawable; #endif /* !defined(NXAGENT_SERVER) */ 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; }