/* * Copyright (c) 1998-2001 by The XFree86 Project, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of the copyright holder(s) * and author(s) shall not be used in advertising or otherwise to promote * the sale, use or other dealings in this Software without prior written * authorization from the copyright holder(s) and author(s). */ #ifdef HAVE_XORG_CONFIG_H #include #endif #include "misc.h" #include "xf86.h" #include #include "scrnintstr.h" #include "regionstr.h" #include "xf86fbman.h" /* #define DEBUG */ static DevPrivateKeyRec xf86FBManagerKeyRec; static DevPrivateKey xf86FBManagerKey; Bool xf86RegisterOffscreenManager(ScreenPtr pScreen, FBManagerFuncsPtr funcs) { xf86FBManagerKey = &xf86FBManagerKeyRec; if (!dixRegisterPrivateKey(&xf86FBManagerKeyRec, PRIVATE_SCREEN, 0)) return FALSE; dixSetPrivate(&pScreen->devPrivates, xf86FBManagerKey, funcs); return TRUE; } Bool xf86FBManagerRunning(ScreenPtr pScreen) { if (xf86FBManagerKey == NULL) return FALSE; if (!dixLookupPrivate(&pScreen->devPrivates, xf86FBManagerKey)) return FALSE; return TRUE; } Bool xf86RegisterFreeBoxCallback(ScreenPtr pScreen, FreeBoxCallbackProcPtr FreeBoxCallback, void *devPriv) { FBManagerFuncsPtr funcs; if (xf86FBManagerKey == NULL) return FALSE; if (!(funcs = (FBManagerFuncsPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBManagerKey))) return FALSE; return (*funcs->RegisterFreeBoxCallback) (pScreen, FreeBoxCallback, devPriv); } FBAreaPtr xf86AllocateOffscreenArea(ScreenPtr pScreen, int w, int h, int gran, MoveAreaCallbackProcPtr moveCB, RemoveAreaCallbackProcPtr removeCB, void *privData) { FBManagerFuncsPtr funcs; if (xf86FBManagerKey == NULL) return NULL; if (!(funcs = (FBManagerFuncsPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBManagerKey))) return NULL; return (*funcs->AllocateOffscreenArea) (pScreen, w, h, gran, moveCB, removeCB, privData); } FBLinearPtr xf86AllocateOffscreenLinear(ScreenPtr pScreen, int length, int gran, MoveLinearCallbackProcPtr moveCB, RemoveLinearCallbackProcPtr removeCB, void *privData) { FBManagerFuncsPtr funcs; if (xf86FBManagerKey == NULL) return NULL; if (!(funcs = (FBManagerFuncsPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBManagerKey))) return NULL; return (*funcs->AllocateOffscreenLinear) (pScreen, length, gran, moveCB, removeCB, privData); } void xf86FreeOffscreenArea(FBAreaPtr area) { FBManagerFuncsPtr funcs; if (!area) return; if (xf86FBManagerKey == NULL) return; if (! (funcs = (FBManagerFuncsPtr) dixLookupPrivate(&area->pScreen->devPrivates, xf86FBManagerKey))) return; (*funcs->FreeOffscreenArea) (area); return; } void xf86FreeOffscreenLinear(FBLinearPtr linear) { FBManagerFuncsPtr funcs; if (!linear) return; if (xf86FBManagerKey == NULL) return; if (! (funcs = (FBManagerFuncsPtr) dixLookupPrivate(&linear->pScreen->devPrivates, xf86FBManagerKey))) return; (*funcs->FreeOffscreenLinear) (linear); return; } Bool xf86ResizeOffscreenArea(FBAreaPtr resize, int w, int h) { FBManagerFuncsPtr funcs; if (!resize) return FALSE; if (xf86FBManagerKey == NULL) return FALSE; if (! (funcs = (FBManagerFuncsPtr) dixLookupPrivate(&resize->pScreen->devPrivates, xf86FBManagerKey))) return FALSE; return (*funcs->ResizeOffscreenArea) (resize, w, h); } Bool xf86ResizeOffscreenLinear(FBLinearPtr resize, int size) { FBManagerFuncsPtr funcs; if (!resize) return FALSE; if (xf86FBManagerKey == NULL) return FALSE; if (! (funcs = (FBManagerFuncsPtr) dixLookupPrivate(&resize->pScreen->devPrivates, xf86FBManagerKey))) return FALSE; return (*funcs->ResizeOffscreenLinear) (resize, size); } Bool xf86QueryLargestOffscreenArea(ScreenPtr pScreen, int *w, int *h, int gran, int preferences, int severity) { FBManagerFuncsPtr funcs; *w = 0; *h = 0; if (xf86FBManagerKey == NULL) return FALSE; if (!(funcs = (FBManagerFuncsPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBManagerKey))) return FALSE; return (*funcs->QueryLargestOffscreenArea) (pScreen, w, h, gran, preferences, severity); } Bool xf86QueryLargestOffscreenLinear(ScreenPtr pScreen, int *size, int gran, int severity) { FBManagerFuncsPtr funcs; *size = 0; if (xf86FBManagerKey == NULL) return FALSE; if (!(funcs = (FBManagerFuncsPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBManagerKey))) return FALSE; return (*funcs->QueryLargestOffscreenLinear) (pScreen, size, gran, severity); } Bool xf86PurgeUnlockedOffscreenAreas(ScreenPtr pScreen) { FBManagerFuncsPtr funcs; if (xf86FBManagerKey == NULL) return FALSE; if (!(funcs = (FBManagerFuncsPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBManagerKey))) return FALSE; return (*funcs->PurgeOffscreenAreas) (pScreen); } /************************************************************\ Below is a specific implementation of an offscreen manager. \************************************************************/ static DevPrivateKeyRec xf86FBScreenKeyRec; #define xf86FBScreenKey (&xf86FBScreenKeyRec) typedef struct _FBLink { FBArea area; struct _FBLink *next; } FBLink, *FBLinkPtr; typedef struct _FBLinearLink { FBLinear linear; int free; /* need to add free here as FBLinear is publicly accessible */ FBAreaPtr area; /* only used if allocation came from XY area */ struct _FBLinearLink *next; } FBLinearLink, *FBLinearLinkPtr; typedef struct { ScreenPtr pScreen; RegionPtr InitialBoxes; RegionPtr FreeBoxes; FBLinkPtr UsedAreas; int NumUsedAreas; FBLinearLinkPtr LinearAreas; CloseScreenProcPtr CloseScreen; int NumCallbacks; FreeBoxCallbackProcPtr *FreeBoxesUpdateCallback; DevUnion *devPrivates; } FBManager, *FBManagerPtr; static void SendCallFreeBoxCallbacks(FBManagerPtr offman) { int i = offman->NumCallbacks; while (i--) { (*offman->FreeBoxesUpdateCallback[i]) (offman->pScreen, offman->FreeBoxes, offman->devPrivates[i].ptr); } } static Bool localRegisterFreeBoxCallback(ScreenPtr pScreen, FreeBoxCallbackProcPtr FreeBoxCallback, void *devPriv) { FBManagerPtr offman; FreeBoxCallbackProcPtr *newCallbacks; DevUnion *newPrivates; offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); newCallbacks = reallocarray(offman->FreeBoxesUpdateCallback, offman->NumCallbacks + 1, sizeof(FreeBoxCallbackProcPtr)); if (!newCallbacks) return FALSE; else offman->FreeBoxesUpdateCallback = newCallbacks; newPrivates = reallocarray(offman->devPrivates, offman->NumCallbacks + 1, sizeof(DevUnion)); if (!newPrivates) return FALSE; else offman->devPrivates = newPrivates; offman->FreeBoxesUpdateCallback[offman->NumCallbacks] = FreeBoxCallback; offman->devPrivates[offman->NumCallbacks].ptr = devPriv; offman->NumCallbacks++; SendCallFreeBoxCallbacks(offman); return TRUE; } static FBAreaPtr AllocateArea(FBManagerPtr offman, int w, int h, int granularity, MoveAreaCallbackProcPtr moveCB, RemoveAreaCallbackProcPtr removeCB, void *privData) { ScreenPtr pScreen = offman->pScreen; FBLinkPtr link = NULL; FBAreaPtr area = NULL; RegionRec NewReg; int i, x = 0, num; BoxPtr boxp; if (granularity <= 1) granularity = 0; boxp = RegionRects(offman->FreeBoxes); num = RegionNumRects(offman->FreeBoxes); /* look through the free boxes */ for (i = 0; i < num; i++, boxp++) { x = boxp->x1; if (granularity > 1) x = ((x + granularity - 1) / granularity) * granularity; if (((boxp->y2 - boxp->y1) < h) || ((boxp->x2 - x) < w)) continue; link = malloc(sizeof(FBLink)); if (!link) return NULL; area = &(link->area); link->next = offman->UsedAreas; offman->UsedAreas = link; offman->NumUsedAreas++; break; } /* try to boot a removeable one out if we are not expendable ourselves */ if (!area && !removeCB) { link = offman->UsedAreas; while (link) { if (!link->area.RemoveAreaCallback) { link = link->next; continue; } boxp = &(link->area.box); x = boxp->x1; if (granularity > 1) x = ((x + granularity - 1) / granularity) * granularity; if (((boxp->y2 - boxp->y1) < h) || ((boxp->x2 - x) < w)) { link = link->next; continue; } /* bye, bye */ (*link->area.RemoveAreaCallback) (&link->area); RegionInit(&NewReg, &(link->area.box), 1); RegionUnion(offman->FreeBoxes, offman->FreeBoxes, &NewReg); RegionUninit(&NewReg); area = &(link->area); break; } } if (area) { area->pScreen = pScreen; area->granularity = granularity; area->box.x1 = x; area->box.x2 = x + w; area->box.y1 = boxp->y1; area->box.y2 = boxp->y1 + h; area->MoveAreaCallback = moveCB; area->RemoveAreaCallback = removeCB; area->devPrivate.ptr = privData; RegionInit(&NewReg, &(area->box), 1); RegionSubtract(offman->FreeBoxes, offman->FreeBoxes, &NewReg); RegionUninit(&NewReg); } return area; } static FBAreaPtr localAllocateOffscreenArea(ScreenPtr pScreen, int w, int h, int gran, MoveAreaCallbackProcPtr moveCB, RemoveAreaCallbackProcPtr removeCB, void *privData) { FBManagerPtr offman; FBAreaPtr area = NULL; offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); if ((area = AllocateArea(offman, w, h, gran, moveCB, removeCB, privData))) SendCallFreeBoxCallbacks(offman); return area; } static void localFreeOffscreenArea(FBAreaPtr area) { FBManagerPtr offman; FBLinkPtr pLink, pLinkPrev = NULL; RegionRec FreedRegion; ScreenPtr pScreen; pScreen = area->pScreen; offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); pLink = offman->UsedAreas; if (!pLink) return; while (&(pLink->area) != area) { pLinkPrev = pLink; pLink = pLink->next; if (!pLink) return; } /* put the area back into the pool */ RegionInit(&FreedRegion, &(pLink->area.box), 1); RegionUnion(offman->FreeBoxes, offman->FreeBoxes, &FreedRegion); RegionUninit(&FreedRegion); if (pLinkPrev) pLinkPrev->next = pLink->next; else offman->UsedAreas = pLink->next; free(pLink); offman->NumUsedAreas--; SendCallFreeBoxCallbacks(offman); } static Bool localResizeOffscreenArea(FBAreaPtr resize, int w, int h) { FBManagerPtr offman; ScreenPtr pScreen; BoxRec OrigArea; RegionRec FreedReg; FBAreaPtr area = NULL; FBLinkPtr pLink, newLink, pLinkPrev = NULL; pScreen = resize->pScreen; offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); /* find this link */ if (!(pLink = offman->UsedAreas)) return FALSE; while (&(pLink->area) != resize) { pLinkPrev = pLink; pLink = pLink->next; if (!pLink) return FALSE; } OrigArea.x1 = resize->box.x1; OrigArea.x2 = resize->box.x2; OrigArea.y1 = resize->box.y1; OrigArea.y2 = resize->box.y2; /* if it's smaller, this is easy */ if ((w <= (resize->box.x2 - resize->box.x1)) && (h <= (resize->box.y2 - resize->box.y1))) { RegionRec NewReg; resize->box.x2 = resize->box.x1 + w; resize->box.y2 = resize->box.y1 + h; if ((resize->box.y2 == OrigArea.y2) && (resize->box.x2 == OrigArea.x2)) return TRUE; RegionInit(&FreedReg, &OrigArea, 1); RegionInit(&NewReg, &(resize->box), 1); RegionSubtract(&FreedReg, &FreedReg, &NewReg); RegionUnion(offman->FreeBoxes, offman->FreeBoxes, &FreedReg); RegionUninit(&FreedReg); RegionUninit(&NewReg); SendCallFreeBoxCallbacks(offman); return TRUE; } /* otherwise we remove the old region */ RegionInit(&FreedReg, &OrigArea, 1); RegionUnion(offman->FreeBoxes, offman->FreeBoxes, &FreedReg); /* remove the old link */ if (pLinkPrev) pLinkPrev->next = pLink->next; else offman->UsedAreas = pLink->next; /* and try to add a new one */ if ((area = AllocateArea(offman, w, h, resize->granularity, resize->MoveAreaCallback, resize->RemoveAreaCallback, resize->devPrivate.ptr))) { /* copy data over to our link and replace the new with old */ memcpy(resize, area, sizeof(FBArea)); pLinkPrev = NULL; newLink = offman->UsedAreas; while (&(newLink->area) != area) { pLinkPrev = newLink; newLink = newLink->next; } if (pLinkPrev) pLinkPrev->next = newLink->next; else offman->UsedAreas = newLink->next; pLink->next = offman->UsedAreas; offman->UsedAreas = pLink; free(newLink); /* AllocateArea added one but we really only exchanged one */ offman->NumUsedAreas--; } else { /* reinstate the old region */ RegionSubtract(offman->FreeBoxes, offman->FreeBoxes, &FreedReg); RegionUninit(&FreedReg); pLink->next = offman->UsedAreas; offman->UsedAreas = pLink; return FALSE; } RegionUninit(&FreedReg); SendCallFreeBoxCallbacks(offman); return TRUE; } static Bool localQueryLargestOffscreenArea(ScreenPtr pScreen, int *width, int *height, int granularity, int preferences, int severity) { FBManagerPtr offman; RegionPtr newRegion = NULL; BoxPtr pbox; int nbox; int x, w, h, area, oldArea; *width = *height = oldArea = 0; if (granularity <= 1) granularity = 0; if ((preferences < 0) || (preferences > 3)) return FALSE; offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); if (severity < 0) severity = 0; if (severity > 2) severity = 2; switch (severity) { case 2: if (offman->NumUsedAreas) { FBLinkPtr pLink; RegionRec tmpRegion; newRegion = RegionCreate(NULL, 1); RegionCopy(newRegion, offman->InitialBoxes); pLink = offman->UsedAreas; while (pLink) { if (!pLink->area.RemoveAreaCallback) { RegionInit(&tmpRegion, &(pLink->area.box), 1); RegionSubtract(newRegion, newRegion, &tmpRegion); RegionUninit(&tmpRegion); } pLink = pLink->next; } nbox = RegionNumRects(newRegion); pbox = RegionRects(newRegion); break; } case 1: if (offman->NumUsedAreas) { FBLinkPtr pLink; RegionRec tmpRegion; newRegion = RegionCreate(NULL, 1); RegionCopy(newRegion, offman->FreeBoxes); pLink = offman->UsedAreas; while (pLink) { if (pLink->area.RemoveAreaCallback) { RegionInit(&tmpRegion, &(pLink->area.box), 1); RegionAppend(newRegion, &tmpRegion); RegionUninit(&tmpRegion); } pLink = pLink->next; } nbox = RegionNumRects(newRegion); pbox = RegionRects(newRegion); break; } default: nbox = RegionNumRects(offman->FreeBoxes); pbox = RegionRects(offman->FreeBoxes); break; } while (nbox--) { x = pbox->x1; if (granularity > 1) x = ((x + granularity - 1) / granularity) * granularity; w = pbox->x2 - x; h = pbox->y2 - pbox->y1; area = w * h; if (w > 0) { Bool gotIt = FALSE; switch (preferences) { case FAVOR_AREA_THEN_WIDTH: if ((area > oldArea) || ((area == oldArea) && (w > *width))) gotIt = TRUE; break; case FAVOR_AREA_THEN_HEIGHT: if ((area > oldArea) || ((area == oldArea) && (h > *height))) gotIt = TRUE; break; case FAVOR_WIDTH_THEN_AREA: if ((w > *width) || ((w == *width) && (area > oldArea))) gotIt = TRUE; break; case FAVOR_HEIGHT_THEN_AREA: if ((h > *height) || ((h == *height) && (area > oldArea))) gotIt = TRUE; break; } if (gotIt) { *width = w; *height = h; oldArea = area; } } pbox++; } if (newRegion) RegionDestroy(newRegion); return TRUE; } static Bool localPurgeUnlockedOffscreenAreas(ScreenPtr pScreen) { FBManagerPtr offman; FBLinkPtr pLink, tmp, pPrev = NULL; RegionRec FreedRegion; Bool anyUsed = FALSE; offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); pLink = offman->UsedAreas; if (!pLink) return TRUE; while (pLink) { if (pLink->area.RemoveAreaCallback) { (*pLink->area.RemoveAreaCallback) (&pLink->area); RegionInit(&FreedRegion, &(pLink->area.box), 1); RegionAppend(offman->FreeBoxes, &FreedRegion); RegionUninit(&FreedRegion); if (pPrev) pPrev->next = pLink->next; else offman->UsedAreas = pLink->next; tmp = pLink; pLink = pLink->next; free(tmp); offman->NumUsedAreas--; anyUsed = TRUE; } else { pPrev = pLink; pLink = pLink->next; } } if (anyUsed) { RegionValidate(offman->FreeBoxes, &anyUsed); SendCallFreeBoxCallbacks(offman); } return TRUE; } static void LinearMoveCBWrapper(FBAreaPtr from, FBAreaPtr to) { /* this will never get called */ } static void LinearRemoveCBWrapper(FBAreaPtr area) { FBManagerPtr offman; FBLinearLinkPtr pLink, pLinkPrev = NULL; ScreenPtr pScreen = area->pScreen; offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); pLink = offman->LinearAreas; if (!pLink) return; while (pLink->area != area) { pLinkPrev = pLink; pLink = pLink->next; if (!pLink) return; } /* give the user the callback it is expecting */ (*pLink->linear.RemoveLinearCallback) (&(pLink->linear)); if (pLinkPrev) pLinkPrev->next = pLink->next; else offman->LinearAreas = pLink->next; free(pLink); } static void DumpDebug(FBLinearLinkPtr pLink) { #ifdef DEBUG if (!pLink) ErrorF("MMmm, PLINK IS NULL!\n"); while (pLink) { ErrorF(" Offset:%08x, Size:%08x, %s,%s\n", pLink->linear.offset, pLink->linear.size, pLink->free ? "Free" : "Used", pLink->area ? "Area" : "Linear"); pLink = pLink->next; } #endif } static FBLinearPtr AllocateLinear(FBManagerPtr offman, int size, int granularity, void *privData) { ScreenPtr pScreen = offman->pScreen; FBLinearLinkPtr linear = NULL; FBLinearLinkPtr newlink = NULL; int offset, end; if (size <= 0) return NULL; if (!offman->LinearAreas) return NULL; linear = offman->LinearAreas; while (linear) { /* Make sure we get a free area that's not an XY fallback case */ if (!linear->area && linear->free) { offset = linear->linear.offset; if (granularity > 1) offset = ((offset + granularity - 1) / granularity) * granularity; end = offset + size; if (end <= (linear->linear.offset + linear->linear.size)) break; } linear = linear->next; } if (!linear) return NULL; /* break left */ if (offset > linear->linear.offset) { newlink = malloc(sizeof(FBLinearLink)); if (!newlink) return NULL; newlink->area = NULL; newlink->linear.offset = offset; newlink->linear.size = linear->linear.size - (offset - linear->linear.offset); newlink->free = 1; newlink->next = linear->next; linear->linear.size -= newlink->linear.size; linear->next = newlink; linear = newlink; } /* break right */ if (size < linear->linear.size) { newlink = malloc(sizeof(FBLinearLink)); if (!newlink) return NULL; newlink->area = NULL; newlink->linear.offset = offset + size; newlink->linear.size = linear->linear.size - size; newlink->free = 1; newlink->next = linear->next; linear->linear.size = size; linear->next = newlink; } /* p = middle block */ linear->linear.granularity = granularity; linear->free = 0; linear->linear.pScreen = pScreen; linear->linear.MoveLinearCallback = NULL; linear->linear.RemoveLinearCallback = NULL; linear->linear.devPrivate.ptr = NULL; DumpDebug(offman->LinearAreas); return &(linear->linear); } static FBLinearPtr localAllocateOffscreenLinear(ScreenPtr pScreen, int length, int gran, MoveLinearCallbackProcPtr moveCB, RemoveLinearCallbackProcPtr removeCB, void *privData) { FBManagerPtr offman; FBLinearLinkPtr link; FBAreaPtr area; FBLinearPtr linear = NULL; BoxPtr extents; int w, h, pitch; offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); /* Try to allocate from linear memory first...... */ DebugF("ALLOCATING LINEAR\n"); if ((linear = AllocateLinear(offman, length, gran, privData))) return linear; DebugF("NOPE, ALLOCATING AREA\n"); if (!(link = malloc(sizeof(FBLinearLink)))) return NULL; /* No linear available, so try and pinch some from the XY areas */ extents = RegionExtents(offman->InitialBoxes); pitch = extents->x2 - extents->x1; if (gran > 1) { if (gran > pitch) { /* we can't match the specified alignment with XY allocations */ free(link); return NULL; } if (pitch % gran) { /* pitch and granularity aren't a perfect match, let's allocate * a bit more so we can align later on */ length += gran - 1; } } if (length < pitch) { /* special case */ w = length; h = 1; } else { w = pitch; h = (length + pitch - 1) / pitch; } if ((area = localAllocateOffscreenArea(pScreen, w, h, gran, moveCB ? LinearMoveCBWrapper : NULL, removeCB ? LinearRemoveCBWrapper : NULL, privData))) { link->area = area; link->free = 0; link->next = offman->LinearAreas; offman->LinearAreas = link; linear = &(link->linear); linear->pScreen = pScreen; linear->size = h * w; linear->offset = (pitch * area->box.y1) + area->box.x1; if (gran > 1) linear->offset = ((linear->offset + gran - 1) / gran) * gran; linear->granularity = gran; linear->MoveLinearCallback = moveCB; linear->RemoveLinearCallback = removeCB; linear->devPrivate.ptr = privData; } else free(link); DumpDebug(offman->LinearAreas); return linear; } static void localFreeOffscreenLinear(FBLinearPtr linear) { FBManagerPtr offman; FBLinearLinkPtr pLink, pLinkPrev = NULL; ScreenPtr pScreen = linear->pScreen; offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); pLink = offman->LinearAreas; if (!pLink) return; while (&(pLink->linear) != linear) { pLinkPrev = pLink; pLink = pLink->next; if (!pLink) return; } if (pLink->area) { /* really an XY area */ DebugF("FREEING AREA\n"); localFreeOffscreenArea(pLink->area); if (pLinkPrev) pLinkPrev->next = pLink->next; else offman->LinearAreas = pLink->next; free(pLink); DumpDebug(offman->LinearAreas); return; } pLink->free = 1; if (pLink->next && pLink->next->free) { FBLinearLinkPtr p = pLink->next; pLink->linear.size += p->linear.size; pLink->next = p->next; free(p); } if (pLinkPrev) { if (pLinkPrev->next && pLinkPrev->next->free && !pLinkPrev->area) { FBLinearLinkPtr p = pLinkPrev->next; pLinkPrev->linear.size += p->linear.size; pLinkPrev->next = p->next; free(p); } } DebugF("FREEING LINEAR\n"); DumpDebug(offman->LinearAreas); } static Bool localResizeOffscreenLinear(FBLinearPtr resize, int length) { FBManagerPtr offman; FBLinearLinkPtr pLink; ScreenPtr pScreen = resize->pScreen; offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); pLink = offman->LinearAreas; if (!pLink) return FALSE; while (&(pLink->linear) != resize) { pLink = pLink->next; if (!pLink) return FALSE; } /* This could actually be alot smarter and try to move allocations from XY to linear when available. For now if it was XY, we keep it XY */ if (pLink->area) { /* really an XY area */ BoxPtr extents; int pitch, w, h; extents = RegionExtents(offman->InitialBoxes); pitch = extents->x2 - extents->x1; if (length < pitch) { /* special case */ w = length; h = 1; } else { w = pitch; h = (length + pitch - 1) / pitch; } if (localResizeOffscreenArea(pLink->area, w, h)) { resize->size = h * w; resize->offset = (pitch * pLink->area->box.y1) + pLink->area->box.x1; return TRUE; } } else { /* TODO!!!! resize the linear area */ } return FALSE; } static Bool localQueryLargestOffscreenLinear(ScreenPtr pScreen, int *size, int gran, int priority) { FBManagerPtr offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); FBLinearLinkPtr pLink; FBLinearLinkPtr pLinkRet; *size = 0; pLink = offman->LinearAreas; if (pLink && !pLink->area) { pLinkRet = pLink; while (pLink) { if (pLink->free) { if (pLink->linear.size > pLinkRet->linear.size) pLinkRet = pLink; } pLink = pLink->next; } if (pLinkRet->free) { *size = pLinkRet->linear.size; return TRUE; } } else { int w, h; if (localQueryLargestOffscreenArea(pScreen, &w, &h, gran, FAVOR_WIDTH_THEN_AREA, priority)) { BoxPtr extents; extents = RegionExtents(offman->InitialBoxes); if ((extents->x2 - extents->x1) == w) *size = w * h; return TRUE; } } return FALSE; } static FBManagerFuncs xf86FBManFuncs = { localAllocateOffscreenArea, localFreeOffscreenArea, localResizeOffscreenArea, localQueryLargestOffscreenArea, localRegisterFreeBoxCallback, localAllocateOffscreenLinear, localFreeOffscreenLinear, localResizeOffscreenLinear, localQueryLargestOffscreenLinear, localPurgeUnlockedOffscreenAreas }; static Bool xf86FBCloseScreen(ScreenPtr pScreen) { FBLinkPtr pLink, tmp; FBLinearLinkPtr pLinearLink, tmp2; FBManagerPtr offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); pScreen->CloseScreen = offman->CloseScreen; pLink = offman->UsedAreas; while (pLink) { tmp = pLink; pLink = pLink->next; free(tmp); } pLinearLink = offman->LinearAreas; while (pLinearLink) { tmp2 = pLinearLink; pLinearLink = pLinearLink->next; free(tmp2); } RegionDestroy(offman->InitialBoxes); RegionDestroy(offman->FreeBoxes); free(offman->FreeBoxesUpdateCallback); free(offman->devPrivates); free(offman); dixSetPrivate(&pScreen->devPrivates, xf86FBScreenKey, NULL); return (*pScreen->CloseScreen) (pScreen); } Bool xf86InitFBManager(ScreenPtr pScreen, BoxPtr FullBox) { ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); RegionRec ScreenRegion; RegionRec FullRegion; BoxRec ScreenBox; Bool ret; ScreenBox.x1 = 0; ScreenBox.y1 = 0; ScreenBox.x2 = pScrn->virtualX; ScreenBox.y2 = pScrn->virtualY; if ((FullBox->x1 > ScreenBox.x1) || (FullBox->y1 > ScreenBox.y1) || (FullBox->x2 < ScreenBox.x2) || (FullBox->y2 < ScreenBox.y2)) { return FALSE; } if (FullBox->y2 < FullBox->y1) return FALSE; if (FullBox->x2 < FullBox->x1) return FALSE; RegionInit(&ScreenRegion, &ScreenBox, 1); RegionInit(&FullRegion, FullBox, 1); RegionSubtract(&FullRegion, &FullRegion, &ScreenRegion); ret = xf86InitFBManagerRegion(pScreen, &FullRegion); RegionUninit(&ScreenRegion); RegionUninit(&FullRegion); return ret; } Bool xf86InitFBManagerArea(ScreenPtr pScreen, int PixelArea, int Verbosity) { ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); xRectangle Rect[3]; RegionPtr pRegion, pScreenRegion; int nRect; Bool ret = FALSE; if (PixelArea < (pScrn->displayWidth * pScrn->virtualY)) return FALSE; Rect[0].x = Rect[0].y = 0; Rect[0].width = pScrn->displayWidth; Rect[0].height = PixelArea / pScrn->displayWidth; nRect = 1; /* Add a possible partial scanline */ if ((Rect[1].height = Rect[1].width = PixelArea % pScrn->displayWidth)) { Rect[1].x = 0; Rect[1].y = Rect[0].height; Rect[1].height = 1; nRect++; } /* Factor out virtual resolution */ pRegion = RegionFromRects(nRect, Rect, 0); if (pRegion) { if (!RegionNar(pRegion)) { Rect[2].x = Rect[2].y = 0; Rect[2].width = pScrn->virtualX; Rect[2].height = pScrn->virtualY; pScreenRegion = RegionFromRects(1, &Rect[2], 0); if (pScreenRegion) { if (!RegionNar(pScreenRegion)) { RegionSubtract(pRegion, pRegion, pScreenRegion); ret = xf86InitFBManagerRegion(pScreen, pRegion); if (ret && xf86GetVerbosity() >= Verbosity) { int scrnIndex = pScrn->scrnIndex; xf86DrvMsgVerb(scrnIndex, X_INFO, Verbosity, "Largest offscreen areas (with overlaps):\n"); if (Rect[2].width < Rect[0].width) { xf86DrvMsgVerb(scrnIndex, X_INFO, Verbosity, "\t%d x %d rectangle at %d,0\n", Rect[0].width - Rect[2].width, Rect[0].height, Rect[2].width); } if (Rect[2].width < Rect[1].width) { xf86DrvMsgVerb(scrnIndex, X_INFO, Verbosity, "\t%d x %d rectangle at %d,0\n", Rect[1].width - Rect[2].width, Rect[0].height + Rect[1].height, Rect[2].width); } if (Rect[2].height < Rect[0].height) { xf86DrvMsgVerb(scrnIndex, X_INFO, Verbosity, "\t%d x %d rectangle at 0,%d\n", Rect[0].width, Rect[0].height - Rect[2].height, Rect[2].height); } if (Rect[1].height) { xf86DrvMsgVerb(scrnIndex, X_INFO, Verbosity, "\t%d x %d rectangle at 0,%d\n", Rect[1].width, Rect[0].height - Rect[2].height + Rect[1].height, Rect[2].height); } } } RegionDestroy(pScreenRegion); } } RegionDestroy(pRegion); } return ret; } Bool xf86InitFBManagerRegion(ScreenPtr pScreen, RegionPtr FullRegion) { FBManagerPtr offman; if (RegionNil(FullRegion)) return FALSE; if (!dixRegisterPrivateKey(&xf86FBScreenKeyRec, PRIVATE_SCREEN, 0)) return FALSE; if (!xf86RegisterOffscreenManager(pScreen, &xf86FBManFuncs)) return FALSE; offman = malloc(sizeof(FBManager)); if (!offman) return FALSE; dixSetPrivate(&pScreen->devPrivates, xf86FBScreenKey, offman); offman->CloseScreen = pScreen->CloseScreen; pScreen->CloseScreen = xf86FBCloseScreen; offman->InitialBoxes = RegionCreate(NULL, 1); offman->FreeBoxes = RegionCreate(NULL, 1); RegionCopy(offman->InitialBoxes, FullRegion); RegionCopy(offman->FreeBoxes, FullRegion); offman->pScreen = pScreen; offman->UsedAreas = NULL; offman->LinearAreas = NULL; offman->NumUsedAreas = 0; offman->NumCallbacks = 0; offman->FreeBoxesUpdateCallback = NULL; offman->devPrivates = NULL; return TRUE; } Bool xf86InitFBManagerLinear(ScreenPtr pScreen, int offset, int size) { FBManagerPtr offman; FBLinearLinkPtr link; FBLinearPtr linear; if (size <= 0) return FALSE; /* we expect people to have called the Area setup first for pixmap cache */ if (!dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey)) return FALSE; offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); offman->LinearAreas = malloc(sizeof(FBLinearLink)); if (!offman->LinearAreas) return FALSE; link = offman->LinearAreas; link->area = NULL; link->next = NULL; link->free = 1; linear = &(link->linear); linear->pScreen = pScreen; linear->size = size; linear->offset = offset; linear->granularity = 0; linear->MoveLinearCallback = NULL; linear->RemoveLinearCallback = NULL; linear->devPrivate.ptr = NULL; return TRUE; } /* This is an implementation specific function and should disappear after the next release. People should use the real linear functions instead */ FBAreaPtr xf86AllocateLinearOffscreenArea(ScreenPtr pScreen, int length, int gran, MoveAreaCallbackProcPtr moveCB, RemoveAreaCallbackProcPtr removeCB, void *privData) { FBManagerFuncsPtr funcs; FBManagerPtr offman; BoxPtr extents; int w, h; if (xf86FBManagerKey == NULL) return NULL; if (!(funcs = (FBManagerFuncsPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBManagerKey))) return NULL; offman = (FBManagerPtr) dixLookupPrivate(&pScreen->devPrivates, xf86FBScreenKey); extents = RegionExtents(offman->InitialBoxes); w = extents->x2 - extents->x1; if (gran > 1) { if (gran > w) return NULL; if (w % gran) length += gran - 1; } if (length <= w) { /* special case */ h = 1; w = length; } else { h = (length + w - 1) / w; } return (*funcs->AllocateOffscreenArea) (pScreen, w, h, gran, moveCB, removeCB, privData); }