/* * Copyright © 2006 Keith Packard * Copyright © 2008 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" RESTYPE RROutputType; /* * Notify the output of some change */ void RROutputChanged (RROutputPtr output, Bool configChanged) { ScreenPtr pScreen = output->pScreen; output->changed = TRUE; if (pScreen) { rrScrPriv (pScreen); pScrPriv->changed = TRUE; if (configChanged) pScrPriv->configChanged = TRUE; } } /* * Create an output */ RROutputPtr RROutputCreate (ScreenPtr pScreen, const char *name, int nameLength, void *devPrivate) { RROutputPtr output; RROutputPtr *outputs; rrScrPrivPtr pScrPriv; if (!RRInit()) return NULL; pScrPriv = rrGetScrPriv(pScreen); if (pScrPriv->numOutputs) outputs = realloc(pScrPriv->outputs, (pScrPriv->numOutputs + 1) * sizeof (RROutputPtr)); else outputs = malloc(sizeof (RROutputPtr)); if (!outputs) return FALSE; pScrPriv->outputs = outputs; output = malloc(sizeof (RROutputRec) + nameLength + 1); if (!output) return NULL; output->id = FakeClientID (0); output->pScreen = pScreen; output->name = (char *) (output + 1); output->nameLength = nameLength; memcpy (output->name, name, nameLength); output->name[nameLength] = '\0'; output->connection = RR_UnknownConnection; output->subpixelOrder = SubPixelUnknown; output->mmWidth = 0; output->mmHeight = 0; output->crtc = NULL; output->numCrtcs = 0; output->crtcs = NULL; output->numClones = 0; output->clones = NULL; output->numModes = 0; output->numPreferred = 0; output->modes = NULL; output->numUserModes = 0; output->userModes = NULL; output->properties = NULL; output->pendingProperties = FALSE; output->changed = FALSE; output->devPrivate = devPrivate; if (!AddResource (output->id, RROutputType, (pointer) output)) return NULL; pScrPriv->outputs[pScrPriv->numOutputs++] = output; return output; } /* * Notify extension that output parameters have been changed */ Bool RROutputSetClones (RROutputPtr output, RROutputPtr *clones, int numClones) { RROutputPtr *newClones; int i; if (numClones == output->numClones) { for (i = 0; i < numClones; i++) if (output->clones[i] != clones[i]) break; if (i == numClones) return TRUE; } if (numClones) { newClones = malloc(numClones * sizeof (RROutputPtr)); if (!newClones) return FALSE; } else newClones = NULL; free(output->clones); memcpy (newClones, clones, numClones * sizeof (RROutputPtr)); output->clones = newClones; output->numClones = numClones; RROutputChanged (output, TRUE); return TRUE; } Bool RROutputSetModes (RROutputPtr output, RRModePtr *modes, int numModes, int numPreferred) { RRModePtr *newModes; int i; if (numModes == output->numModes && numPreferred == output->numPreferred) { for (i = 0; i < numModes; i++) if (output->modes[i] != modes[i]) break; if (i == numModes) { for (i = 0; i < numModes; i++) RRModeDestroy (modes[i]); return TRUE; } } if (numModes) { newModes = malloc(numModes * sizeof (RRModePtr)); if (!newModes) return FALSE; } else newModes = NULL; if (output->modes) { for (i = 0; i < output->numModes; i++) RRModeDestroy (output->modes[i]); free(output->modes); } memcpy (newModes, modes, numModes * sizeof (RRModePtr)); output->modes = newModes; output->numModes = numModes; output->numPreferred = numPreferred; RROutputChanged (output, TRUE); return TRUE; } int RROutputAddUserMode (RROutputPtr output, RRModePtr mode) { int m; ScreenPtr pScreen = output->pScreen; rrScrPriv(pScreen); RRModePtr *newModes; /* Check to see if this mode is already listed for this output */ for (m = 0; m < output->numModes + output->numUserModes; m++) { RRModePtr e = (m < output->numModes ? output->modes[m] : output->userModes[m - output->numModes]); if (mode == e) return Success; } /* Check with the DDX to see if this mode is OK */ if (pScrPriv->rrOutputValidateMode) if (!pScrPriv->rrOutputValidateMode (pScreen, output, mode)) return BadMatch; if (output->userModes) newModes = realloc(output->userModes, (output->numUserModes + 1) * sizeof (RRModePtr)); else newModes = malloc(sizeof (RRModePtr)); if (!newModes) return BadAlloc; output->userModes = newModes; output->userModes[output->numUserModes++] = mode; ++mode->refcnt; RROutputChanged (output, TRUE); RRTellChanged (pScreen); return Success; } int RROutputDeleteUserMode (RROutputPtr output, RRModePtr mode) { int m; /* Find this mode in the user mode list */ for (m = 0; m < output->numUserModes; m++) { RRModePtr e = output->userModes[m]; if (mode == e) break; } /* Not there, access error */ if (m == output->numUserModes) return BadAccess; /* make sure the mode isn't active for this output */ if (output->crtc && output->crtc->mode == mode) return BadMatch; memmove (output->userModes + m, output->userModes + m + 1, (output->numUserModes - m - 1) * sizeof (RRModePtr)); output->numUserModes--; RRModeDestroy (mode); return Success; } Bool RROutputSetCrtcs (RROutputPtr output, RRCrtcPtr *crtcs, int numCrtcs) { RRCrtcPtr *newCrtcs; int i; if (numCrtcs == output->numCrtcs) { for (i = 0; i < numCrtcs; i++) if (output->crtcs[i] != crtcs[i]) break; if (i == numCrtcs) return TRUE; } if (numCrtcs) { newCrtcs = malloc(numCrtcs * sizeof (RRCrtcPtr)); if (!newCrtcs) return FALSE; } else newCrtcs = NULL; free(output->crtcs); memcpy (newCrtcs, crtcs, numCrtcs * sizeof (RRCrtcPtr)); output->crtcs = newCrtcs; output->numCrtcs = numCrtcs; RROutputChanged (output, TRUE); return TRUE; } Bool RROutputSetConnection (RROutputPtr output, CARD8 connection) { if (output->connection == connection) return TRUE; output->connection = connection; RROutputChanged (output, TRUE); return TRUE; } Bool RROutputSetSubpixelOrder (RROutputPtr output, int subpixelOrder) { if (output->subpixelOrder == subpixelOrder) return TRUE; output->subpixelOrder = subpixelOrder; RROutputChanged (output, FALSE); return TRUE; } Bool RROutputSetPhysicalSize (RROutputPtr output, int mmWidth, int mmHeight) { if (output->mmWidth == mmWidth && output->mmHeight == mmHeight) return TRUE; output->mmWidth = mmWidth; output->mmHeight = mmHeight; RROutputChanged (output, FALSE); return TRUE; } void RRDeliverOutputEvent(ClientPtr client, WindowPtr pWin, RROutputPtr output) { ScreenPtr pScreen = pWin->drawable.pScreen; rrScrPriv (pScreen); xRROutputChangeNotifyEvent oe; RRCrtcPtr crtc = output->crtc; RRModePtr mode = crtc ? crtc->mode : 0; oe.type = RRNotify + RREventBase; oe.subCode = RRNotify_OutputChange; oe.timestamp = pScrPriv->lastSetTime.milliseconds; oe.configTimestamp = pScrPriv->lastConfigTime.milliseconds; oe.window = pWin->drawable.id; oe.output = output->id; if (crtc) { oe.crtc = crtc->id; oe.mode = mode ? mode->mode.id : None; oe.rotation = crtc->rotation; } else { oe.crtc = None; oe.mode = None; oe.rotation = RR_Rotate_0; } oe.connection = output->connection; oe.subpixelOrder = output->subpixelOrder; WriteEventsToClient (client, 1, (xEvent *) &oe); } /* * Destroy a Output at shutdown */ void RROutputDestroy (RROutputPtr output) { FreeResource (output->id, 0); } static int RROutputDestroyResource (pointer value, XID pid) { RROutputPtr output = (RROutputPtr) value; ScreenPtr pScreen = output->pScreen; int m; if (pScreen) { rrScrPriv(pScreen); int i; if (pScrPriv->primaryOutput == output) pScrPriv->primaryOutput = NULL; for (i = 0; i < pScrPriv->numOutputs; i++) { if (pScrPriv->outputs[i] == output) { memmove (pScrPriv->outputs + i, pScrPriv->outputs + i + 1, (pScrPriv->numOutputs - (i + 1)) * sizeof (RROutputPtr)); --pScrPriv->numOutputs; break; } } } if (output->modes) { for (m = 0; m < output->numModes; m++) RRModeDestroy (output->modes[m]); free(output->modes); } for (m = 0; m < output->numUserModes; m++) RRModeDestroy (output->userModes[m]); free(output->userModes); free(output->crtcs); free(output->clones); RRDeleteAllOutputProperties (output); free(output); return 1; } /* * Initialize output type */ Bool RROutputInit (void) { RROutputType = CreateNewResourceType (RROutputDestroyResource, "OUTPUT"); if (!RROutputType) return FALSE; return TRUE; } /* * Initialize output type error value */ void RROutputInitErrorValue(void) { SetResourceTypeErrorValue(RROutputType, RRErrorBase + BadRROutput); } #define OutputInfoExtra (SIZEOF(xRRGetOutputInfoReply) - 32) int ProcRRGetOutputInfo (ClientPtr client) { REQUEST(xRRGetOutputInfoReq); xRRGetOutputInfoReply rep; RROutputPtr output; CARD8 *extra; unsigned long extraLen; ScreenPtr pScreen; rrScrPrivPtr pScrPriv; RRCrtc *crtcs; RRMode *modes; RROutput *clones; char *name; int i, n; REQUEST_SIZE_MATCH(xRRGetOutputInfoReq); VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); pScreen = output->pScreen; pScrPriv = rrGetScrPriv(pScreen); rep.type = X_Reply; rep.sequenceNumber = client->sequence; rep.length = bytes_to_int32(OutputInfoExtra); rep.timestamp = pScrPriv->lastSetTime.milliseconds; rep.crtc = output->crtc ? output->crtc->id : None; rep.mmWidth = output->mmWidth; rep.mmHeight = output->mmHeight; rep.connection = output->connection; rep.subpixelOrder = output->subpixelOrder; rep.nCrtcs = output->numCrtcs; rep.nModes = output->numModes + output->numUserModes; rep.nPreferred = output->numPreferred; rep.nClones = output->numClones; rep.nameLength = output->nameLength; extraLen = ((output->numCrtcs + output->numModes + output->numUserModes + output->numClones + bytes_to_int32(rep.nameLength)) << 2); if (extraLen) { rep.length += bytes_to_int32(extraLen); extra = malloc(extraLen); if (!extra) return BadAlloc; } else extra = NULL; crtcs = (RRCrtc *) extra; modes = (RRMode *) (crtcs + output->numCrtcs); clones = (RROutput *) (modes + output->numModes + output->numUserModes); name = (char *) (clones + output->numClones); for (i = 0; i < output->numCrtcs; i++) { crtcs[i] = output->crtcs[i]->id; if (client->swapped) swapl (&crtcs[i], n); } for (i = 0; i < output->numModes + output->numUserModes; i++) { if (i < output->numModes) modes[i] = output->modes[i]->mode.id; else modes[i] = output->userModes[i - output->numModes]->mode.id; if (client->swapped) swapl (&modes[i], n); } for (i = 0; i < output->numClones; i++) { clones[i] = output->clones[i]->id; if (client->swapped) swapl (&clones[i], n); } memcpy (name, output->name, output->nameLength); if (client->swapped) { swaps(&rep.sequenceNumber, n); swapl(&rep.length, n); swapl(&rep.timestamp, n); swapl(&rep.crtc, n); swapl(&rep.mmWidth, n); swapl(&rep.mmHeight, n); swaps(&rep.nCrtcs, n); swaps(&rep.nModes, n); swaps(&rep.nClones, n); swaps(&rep.nameLength, n); } WriteToClient(client, sizeof(xRRGetOutputInfoReply), (char *)&rep); if (extraLen) { WriteToClient (client, extraLen, (char *) extra); free(extra); } return Success; } static void RRSetPrimaryOutput(ScreenPtr pScreen, rrScrPrivPtr pScrPriv, RROutputPtr output) { if (pScrPriv->primaryOutput == output) return; /* clear the old primary */ if (pScrPriv->primaryOutput) { RROutputChanged(pScrPriv->primaryOutput, 0); pScrPriv->primaryOutput = NULL; } /* set the new primary */ if (output) { pScrPriv->primaryOutput = output; RROutputChanged(output, 0); } pScrPriv->layoutChanged = TRUE; RRTellChanged(pScreen); } int ProcRRSetOutputPrimary(ClientPtr client) { REQUEST(xRRSetOutputPrimaryReq); RROutputPtr output = NULL; WindowPtr pWin; rrScrPrivPtr pScrPriv; int rc; REQUEST_SIZE_MATCH(xRRSetOutputPrimaryReq); rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); if (rc != Success) return rc; if (stuff->output) { VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); if (output->pScreen != pWin->drawable.pScreen) { client->errorValue = stuff->window; return BadMatch; } } pScrPriv = rrGetScrPriv(pWin->drawable.pScreen); RRSetPrimaryOutput(pWin->drawable.pScreen, pScrPriv, output); return Success; } int ProcRRGetOutputPrimary(ClientPtr client) { REQUEST(xRRGetOutputPrimaryReq); WindowPtr pWin; rrScrPrivPtr pScrPriv; xRRGetOutputPrimaryReply rep; RROutputPtr primary = NULL; int rc; REQUEST_SIZE_MATCH(xRRGetOutputPrimaryReq); rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); if (rc != Success) return rc; pScrPriv = rrGetScrPriv(pWin->drawable.pScreen); if (pScrPriv) primary = pScrPriv->primaryOutput; memset(&rep, 0, sizeof(rep)); rep.type = X_Reply; rep.sequenceNumber = client->sequence; rep.output = primary ? primary->id : None; if (client->swapped) { int n; swaps(&rep.sequenceNumber, n); swapl(&rep.output, n); } WriteToClient(client, sizeof(xRRGetOutputPrimaryReply), &rep); return Success; }