/* * Copyright © 2000 Compaq Computer Corporation * Copyright © 2002 Hewlett-Packard Company * Copyright © 2006 Intel Corporation * * 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. * * Author: Jim Gettys, Hewlett-Packard Company, Inc. * Keith Packard, Intel Corporation */ #ifdef HAVE_DIX_CONFIG_H #include <dix-config.h> #endif #include "randrstr.h" #include "extinit.h" /* From render.h */ #ifndef SubPixelUnknown #define SubPixelUnknown 0 #endif #define RR_VALIDATE static int RRNScreens; #define wrap(priv,real,mem,func) {\ priv->mem = real->mem; \ real->mem = func; \ } #define unwrap(priv,real,mem) {\ real->mem = priv->mem; \ } static int ProcRRDispatch(ClientPtr pClient); static int SProcRRDispatch(ClientPtr pClient); int RREventBase; int RRErrorBase; RESTYPE RRClientType, RREventType; /* resource types for event masks */ DevPrivateKeyRec RRClientPrivateKeyRec; DevPrivateKeyRec rrPrivKeyRec; static void RRClientCallback(CallbackListPtr *list, pointer closure, pointer data) { NewClientInfoRec *clientinfo = (NewClientInfoRec *) data; ClientPtr pClient = clientinfo->client; rrClientPriv(pClient); RRTimesPtr pTimes = (RRTimesPtr) (pRRClient + 1); int i; pRRClient->major_version = 0; pRRClient->minor_version = 0; for (i = 0; i < screenInfo.numScreens; i++) { ScreenPtr pScreen = screenInfo.screens[i]; rrScrPriv(pScreen); if (pScrPriv) { pTimes[i].setTime = pScrPriv->lastSetTime; pTimes[i].configTime = pScrPriv->lastConfigTime; } } } static Bool RRCloseScreen(ScreenPtr pScreen) { rrScrPriv(pScreen); int j; unwrap(pScrPriv, pScreen, CloseScreen); for (j = pScrPriv->numCrtcs - 1; j >= 0; j--) RRCrtcDestroy(pScrPriv->crtcs[j]); for (j = pScrPriv->numOutputs - 1; j >= 0; j--) RROutputDestroy(pScrPriv->outputs[j]); if (pScrPriv->provider) RRProviderDestroy(pScrPriv->provider); free(pScrPriv->crtcs); free(pScrPriv->outputs); free(pScrPriv); RRNScreens -= 1; /* ok, one fewer screen with RandR running */ return (*pScreen->CloseScreen) (pScreen); } static void SRRScreenChangeNotifyEvent(xRRScreenChangeNotifyEvent * from, xRRScreenChangeNotifyEvent * to) { to->type = from->type; to->rotation = from->rotation; cpswaps(from->sequenceNumber, to->sequenceNumber); cpswapl(from->timestamp, to->timestamp); cpswapl(from->configTimestamp, to->configTimestamp); cpswapl(from->root, to->root); cpswapl(from->window, to->window); cpswaps(from->sizeID, to->sizeID); cpswaps(from->subpixelOrder, to->subpixelOrder); cpswaps(from->widthInPixels, to->widthInPixels); cpswaps(from->heightInPixels, to->heightInPixels); cpswaps(from->widthInMillimeters, to->widthInMillimeters); cpswaps(from->heightInMillimeters, to->heightInMillimeters); } static void SRRCrtcChangeNotifyEvent(xRRCrtcChangeNotifyEvent * from, xRRCrtcChangeNotifyEvent * to) { to->type = from->type; to->subCode = from->subCode; cpswaps(from->sequenceNumber, to->sequenceNumber); cpswapl(from->timestamp, to->timestamp); cpswapl(from->window, to->window); cpswapl(from->crtc, to->crtc); cpswapl(from->mode, to->mode); cpswaps(from->rotation, to->rotation); /* pad1 */ cpswaps(from->x, to->x); cpswaps(from->y, to->y); cpswaps(from->width, to->width); cpswaps(from->height, to->height); } static void SRROutputChangeNotifyEvent(xRROutputChangeNotifyEvent * from, xRROutputChangeNotifyEvent * to) { to->type = from->type; to->subCode = from->subCode; cpswaps(from->sequenceNumber, to->sequenceNumber); cpswapl(from->timestamp, to->timestamp); cpswapl(from->configTimestamp, to->configTimestamp); cpswapl(from->window, to->window); cpswapl(from->output, to->output); cpswapl(from->crtc, to->crtc); cpswapl(from->mode, to->mode); cpswaps(from->rotation, to->rotation); to->connection = from->connection; to->subpixelOrder = from->subpixelOrder; } static void SRROutputPropertyNotifyEvent(xRROutputPropertyNotifyEvent * from, xRROutputPropertyNotifyEvent * to) { to->type = from->type; to->subCode = from->subCode; cpswaps(from->sequenceNumber, to->sequenceNumber); cpswapl(from->window, to->window); cpswapl(from->output, to->output); cpswapl(from->atom, to->atom); cpswapl(from->timestamp, to->timestamp); to->state = from->state; /* pad1 */ /* pad2 */ /* pad3 */ /* pad4 */ } static void SRRProviderChangeNotifyEvent(xRRProviderChangeNotifyEvent * from, xRRProviderChangeNotifyEvent * to) { to->type = from->type; to->subCode = from->subCode; cpswaps(from->sequenceNumber, to->sequenceNumber); cpswapl(from->timestamp, to->timestamp); cpswapl(from->window, to->window); cpswapl(from->provider, to->provider); } static void SRRProviderPropertyNotifyEvent(xRRProviderPropertyNotifyEvent * from, xRRProviderPropertyNotifyEvent * to) { to->type = from->type; to->subCode = from->subCode; cpswaps(from->sequenceNumber, to->sequenceNumber); cpswapl(from->window, to->window); cpswapl(from->provider, to->provider); cpswapl(from->atom, to->atom); cpswapl(from->timestamp, to->timestamp); to->state = from->state; /* pad1 */ /* pad2 */ /* pad3 */ /* pad4 */ } static void SRRResourceChangeNotifyEvent(xRRResourceChangeNotifyEvent * from, xRRResourceChangeNotifyEvent * to) { to->type = from->type; to->subCode = from->subCode; cpswaps(from->sequenceNumber, to->sequenceNumber); cpswapl(from->timestamp, to->timestamp); cpswapl(from->window, to->window); } static void SRRNotifyEvent(xEvent *from, xEvent *to) { switch (from->u.u.detail) { case RRNotify_CrtcChange: SRRCrtcChangeNotifyEvent((xRRCrtcChangeNotifyEvent *) from, (xRRCrtcChangeNotifyEvent *) to); break; case RRNotify_OutputChange: SRROutputChangeNotifyEvent((xRROutputChangeNotifyEvent *) from, (xRROutputChangeNotifyEvent *) to); break; case RRNotify_OutputProperty: SRROutputPropertyNotifyEvent((xRROutputPropertyNotifyEvent *) from, (xRROutputPropertyNotifyEvent *) to); break; case RRNotify_ProviderChange: SRRProviderChangeNotifyEvent((xRRProviderChangeNotifyEvent *) from, (xRRProviderChangeNotifyEvent *) to); break; case RRNotify_ProviderProperty: SRRProviderPropertyNotifyEvent((xRRProviderPropertyNotifyEvent *) from, (xRRProviderPropertyNotifyEvent *) to); break; case RRNotify_ResourceChange: SRRResourceChangeNotifyEvent((xRRResourceChangeNotifyEvent *) from, (xRRResourceChangeNotifyEvent *) to); default: break; } } static int RRGeneration; Bool RRInit(void) { if (RRGeneration != serverGeneration) { if (!RRModeInit()) return FALSE; if (!RRCrtcInit()) return FALSE; if (!RROutputInit()) return FALSE; if (!RRProviderInit()) return FALSE; RRGeneration = serverGeneration; } if (!dixRegisterPrivateKey(&rrPrivKeyRec, PRIVATE_SCREEN, 0)) return FALSE; return TRUE; } Bool RRScreenInit(ScreenPtr pScreen) { rrScrPrivPtr pScrPriv; if (!RRInit()) return FALSE; pScrPriv = (rrScrPrivPtr) calloc(1, sizeof(rrScrPrivRec)); if (!pScrPriv) return FALSE; SetRRScreen(pScreen, pScrPriv); /* * Calling function best set these function vectors */ pScrPriv->rrGetInfo = 0; pScrPriv->maxWidth = pScrPriv->minWidth = pScreen->width; pScrPriv->maxHeight = pScrPriv->minHeight = pScreen->height; pScrPriv->width = pScreen->width; pScrPriv->height = pScreen->height; pScrPriv->mmWidth = pScreen->mmWidth; pScrPriv->mmHeight = pScreen->mmHeight; #if RANDR_12_INTERFACE pScrPriv->rrScreenSetSize = NULL; pScrPriv->rrCrtcSet = NULL; pScrPriv->rrCrtcSetGamma = NULL; #endif #if RANDR_10_INTERFACE pScrPriv->rrSetConfig = 0; pScrPriv->rotations = RR_Rotate_0; pScrPriv->reqWidth = pScreen->width; pScrPriv->reqHeight = pScreen->height; pScrPriv->nSizes = 0; pScrPriv->pSizes = NULL; pScrPriv->rotation = RR_Rotate_0; pScrPriv->rate = 0; pScrPriv->size = 0; #endif /* * This value doesn't really matter -- any client must call * GetScreenInfo before reading it which will automatically update * the time */ pScrPriv->lastSetTime = currentTime; pScrPriv->lastConfigTime = currentTime; wrap(pScrPriv, pScreen, CloseScreen, RRCloseScreen); pScreen->ConstrainCursorHarder = RRConstrainCursorHarder; pScreen->ReplaceScanoutPixmap = RRReplaceScanoutPixmap; pScrPriv->numOutputs = 0; pScrPriv->outputs = NULL; pScrPriv->numCrtcs = 0; pScrPriv->crtcs = NULL; RRNScreens += 1; /* keep count of screens that implement randr */ return TRUE; } /*ARGSUSED*/ static int RRFreeClient(pointer data, XID id) { RREventPtr pRREvent; WindowPtr pWin; RREventPtr *pHead, pCur, pPrev; pRREvent = (RREventPtr) data; pWin = pRREvent->window; dixLookupResourceByType((pointer *) &pHead, pWin->drawable.id, RREventType, serverClient, DixDestroyAccess); if (pHead) { pPrev = 0; for (pCur = *pHead; pCur && pCur != pRREvent; pCur = pCur->next) pPrev = pCur; if (pCur) { if (pPrev) pPrev->next = pRREvent->next; else *pHead = pRREvent->next; } } free((pointer) pRREvent); return 1; } /*ARGSUSED*/ static int RRFreeEvents(pointer data, XID id) { RREventPtr *pHead, pCur, pNext; pHead = (RREventPtr *) data; for (pCur = *pHead; pCur; pCur = pNext) { pNext = pCur->next; FreeResource(pCur->clientResource, RRClientType); free((pointer) pCur); } free((pointer) pHead); return 1; } void RRExtensionInit(void) { ExtensionEntry *extEntry; if (RRNScreens == 0) return; if (!dixRegisterPrivateKey(&RRClientPrivateKeyRec, PRIVATE_CLIENT, sizeof(RRClientRec) + screenInfo.numScreens * sizeof(RRTimesRec))) return; if (!AddCallback(&ClientStateCallback, RRClientCallback, 0)) return; RRClientType = CreateNewResourceType(RRFreeClient, "RandRClient"); if (!RRClientType) return; RREventType = CreateNewResourceType(RRFreeEvents, "RandREvent"); if (!RREventType) return; extEntry = AddExtension(RANDR_NAME, RRNumberEvents, RRNumberErrors, ProcRRDispatch, SProcRRDispatch, NULL, StandardMinorOpcode); if (!extEntry) return; RRErrorBase = extEntry->errorBase; RREventBase = extEntry->eventBase; EventSwapVector[RREventBase + RRScreenChangeNotify] = (EventSwapPtr) SRRScreenChangeNotifyEvent; EventSwapVector[RREventBase + RRNotify] = (EventSwapPtr) SRRNotifyEvent; RRModeInitErrorValue(); RRCrtcInitErrorValue(); RROutputInitErrorValue(); RRProviderInitErrorValue(); #ifdef PANORAMIX RRXineramaExtensionInit(); #endif } static int TellChanged(WindowPtr pWin, pointer value) { RREventPtr *pHead, pRREvent; ClientPtr client; ScreenPtr pScreen = pWin->drawable.pScreen; rrScrPriv(pScreen); int i; dixLookupResourceByType((pointer *) &pHead, pWin->drawable.id, RREventType, serverClient, DixReadAccess); if (!pHead) return WT_WALKCHILDREN; for (pRREvent = *pHead; pRREvent; pRREvent = pRREvent->next) { client = pRREvent->client; if (client == serverClient || client->clientGone) continue; if (pRREvent->mask & RRScreenChangeNotifyMask) RRDeliverScreenEvent(client, pWin, pScreen); if (pRREvent->mask & RRCrtcChangeNotifyMask) { for (i = 0; i < pScrPriv->numCrtcs; i++) { RRCrtcPtr crtc = pScrPriv->crtcs[i]; if (crtc->changed) RRDeliverCrtcEvent(client, pWin, crtc); } } if (pRREvent->mask & RROutputChangeNotifyMask) { for (i = 0; i < pScrPriv->numOutputs; i++) { RROutputPtr output = pScrPriv->outputs[i]; if (output->changed) RRDeliverOutputEvent(client, pWin, output); } } } return WT_WALKCHILDREN; } void RRSetChanged(ScreenPtr pScreen) { /* set changed bits on the master screen only */ ScreenPtr master; rrScrPriv(pScreen); rrScrPrivPtr mastersp; if (pScreen->isGPU) { master = pScreen->current_master; if (!master) return; mastersp = rrGetScrPriv(master); } else { master = pScreen; mastersp = pScrPriv; } mastersp->changed = TRUE; } /* * Something changed; send events and adjust pointer position */ void RRTellChanged(ScreenPtr pScreen) { ScreenPtr master; rrScrPriv(pScreen); rrScrPrivPtr mastersp; int i; if (pScreen->isGPU) { master = pScreen->current_master; mastersp = rrGetScrPriv(master); } else { master = pScreen; mastersp = pScrPriv; } if (mastersp->changed) { UpdateCurrentTimeIf(); if (mastersp->configChanged) { mastersp->lastConfigTime = currentTime; mastersp->configChanged = FALSE; } pScrPriv->changed = FALSE; mastersp->changed = FALSE; WalkTree(master, TellChanged, (pointer) master); for (i = 0; i < pScrPriv->numOutputs; i++) pScrPriv->outputs[i]->changed = FALSE; for (i = 0; i < pScrPriv->numCrtcs; i++) pScrPriv->crtcs[i]->changed = FALSE; if (mastersp->layoutChanged) { pScrPriv->layoutChanged = FALSE; RRPointerScreenConfigured(master); RRSendConfigNotify(master); } } } /* * Return the first output which is connected to an active CRTC * Used in emulating 1.0 behaviour */ RROutputPtr RRFirstOutput(ScreenPtr pScreen) { rrScrPriv(pScreen); RROutputPtr output; int i, j; if (!pScrPriv) return NULL; if (pScrPriv->primaryOutput && pScrPriv->primaryOutput->crtc) return pScrPriv->primaryOutput; for (i = 0; i < pScrPriv->numCrtcs; i++) { RRCrtcPtr crtc = pScrPriv->crtcs[i]; for (j = 0; j < pScrPriv->numOutputs; j++) { output = pScrPriv->outputs[j]; if (output->crtc == crtc) return output; } } return NULL; } CARD16 RRVerticalRefresh(xRRModeInfo * mode) { CARD32 refresh; CARD32 dots = mode->hTotal * mode->vTotal; if (!dots) return 0; refresh = (mode->dotClock + dots / 2) / dots; if (refresh > 0xffff) refresh = 0xffff; return (CARD16) refresh; } static int ProcRRDispatch(ClientPtr client) { REQUEST(xReq); if (stuff->data >= RRNumberRequests || !ProcRandrVector[stuff->data]) return BadRequest; return (*ProcRandrVector[stuff->data]) (client); } static int SProcRRDispatch(ClientPtr client) { REQUEST(xReq); if (stuff->data >= RRNumberRequests || !ProcRandrVector[stuff->data]) return BadRequest; return (*SProcRandrVector[stuff->data]) (client); }