/* * 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" #include "swaprep.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(void *devPrivate) { RRCrtcPtr crtc; if (!RRInit()) return NULL; crtc = malloc(sizeof(RRCrtcRec)); if (!crtc) return NULL; crtc->id = FakeClientID(0); crtc->pScreen = NULL; 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; return crtc; } /* * Set the allowed rotations on a CRTC */ void RRCrtcSetRotations(RRCrtcPtr crtc, Rotation rotations) { crtc->rotations = rotations; } /* * Attach a Crtc to a screen. This is done as a separate step * so that an xf86-based driver can create CRTCs in PreInit * before the Screen has been created */ Bool RRCrtcAttachScreen(RRCrtcPtr crtc, ScreenPtr pScreen) { rrScrPriv(pScreen); RRCrtcPtr *crtcs; /* 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; /* attach the screen and crtc together */ crtc->pScreen = pScreen; pScrPriv->crtcs = crtcs; pScrPriv->crtcs[pScrPriv->numCrtcs++] = crtc; RRCrtcChanged(crtc, TRUE); return TRUE; } /* * 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) { 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) { 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 { if (crtc->outputs) 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); } 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); } /* * 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; /* 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))) { return TRUE; } if (pScreen) { #if RANDR_12_INTERFACE rrScrPriv(pScreen); if (pScrPriv->rrCrtcSet) { return (*pScrPriv->rrCrtcSet) (pScreen, crtc, mode, x, y, rotation, numOutputs, outputs); } #endif #if RANDR_10_INTERFACE if (pScrPriv->rrSetConfig) { RRScreenSize size; RRScreenRate rate; if (!mode) { RRCrtcNotify(crtc, NULL, x, y, rotation, 0, NULL); return TRUE; } Bool ret; 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); } return ret; } #endif RRTellChanged(pScreen); } return FALSE; } /* * 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) free(crtc->gammaRed); 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; } /* * 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 = malloc(size * 3 * sizeof(CARD16)); if (!gamma) return FALSE; } else gamma = NULL; if (crtc->gammaRed) free(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; #ifdef XResExtension RegisterResourceName(RRCrtcType, "CRTC"); #endif 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; REQUEST_SIZE_MATCH(xRRGetCrtcInfoReq); crtc = LookupCrtc(client, stuff->crtc, SecurityReadAccess); 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; rep.width = mode ? mode->mode.width : 0; rep.height = mode ? mode->mode.height : 0; 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 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 = malloc(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) free(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) free(outputs); return BadMatch; } /* validate mode for this output */ for (j = 0; j < outputs[i]->numModes; j++) if (outputs[i]->modes[j] == mode) break; if (j == outputs[i]->numModes) { if (outputs) free(outputs); return BadMatch; } } pScreen = crtc->pScreen; pScrPriv = rrGetScrPriv(pScreen); time = ClientTimeToServerTime(stuff->timestamp); configTime = ClientTimeToServerTime(stuff->configTimestamp); if (!pScrPriv) { rep.status = RRSetConfigFailed; goto sendReply; } /* * 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; } /* * 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) free(outputs); return BadValue; } if (mode) { if ((~crtc->rotations) & rotation) { /* * requested rotation or reflection not supported by screen */ client->errorValue = stuff->rotation; if (outputs) 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 (pScrPriv->rrScreenSetSize) { int source_width = mode->mode.width; int source_height = mode->mode.height; if (rotation == RR_Rotate_90 || rotation == 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) free(outputs); return BadValue; } if (stuff->y + source_height > pScreen->height) { client->errorValue = stuff->y; if (outputs) free(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; } rep.status = RRSetConfigSuccess; sendReply: if (outputs) free(outputs); rep.type = X_Reply; /* rep.status has already been filled in */ rep.length = 0; rep.sequenceNumber = client->sequence; rep.newTimestamp = pScrPriv->lastConfigTime.milliseconds; if (client->swapped) { swaps(&rep.sequenceNumber); swapl(&rep.length); swapl(&rep.newTimestamp); } WriteToClient(client, sizeof(xRRSetCrtcConfigReply), (char *) &rep); return client->noClientException; } int ProcRRGetCrtcGammaSize(ClientPtr client) { REQUEST(xRRGetCrtcGammaSizeReq); xRRGetCrtcGammaSizeReply reply; RRCrtcPtr crtc; REQUEST_SIZE_MATCH(xRRGetCrtcGammaSizeReq); crtc = LookupCrtc(client, stuff->crtc, SecurityReadAccess); 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); swapl(&reply.length); swaps(&reply.size); } WriteToClient(client, sizeof(xRRGetCrtcGammaSizeReply), (char *) &reply); return client->noClientException; } int ProcRRGetCrtcGamma(ClientPtr client) { REQUEST(xRRGetCrtcGammaReq); xRRGetCrtcGammaReply reply; RRCrtcPtr crtc; unsigned long len; REQUEST_SIZE_MATCH(xRRGetCrtcGammaReq); crtc = LookupCrtc(client, stuff->crtc, SecurityReadAccess); if (!crtc) return RRErrorBase + BadRRCrtc; len = crtc->gammaSize * 3 * 2; reply.type = X_Reply; reply.sequenceNumber = client->sequence; reply.length = (len + 3) >> 2; 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) { client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write; WriteSwappedDataToClient(client, len, (char *) crtc->gammaRed); } 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, SecurityWriteAccess); 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; }