2003-10-13 00:19:58 +00:00
|
|
|
/*
|
|
|
|
|
* $Id$
|
|
|
|
|
*
|
|
|
|
|
* Copyright © 2003 Anders Carlsson
|
|
|
|
|
*
|
|
|
|
|
* 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 Anders Carlsson not be used in
|
|
|
|
|
* advertising or publicity pertaining to distribution of the software without
|
|
|
|
|
* specific, written prior permission. Anders Carlsson makes no
|
|
|
|
|
* representations about the suitability of this software for any purpose. It
|
|
|
|
|
* is provided "as is" without express or implied warranty.
|
|
|
|
|
*
|
|
|
|
|
* ANDERS CARLSSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
|
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
|
|
|
* EVENT SHALL ANDERS CARLSSON 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.
|
|
|
|
|
*/
|
|
|
|
|
/* $Header$ */
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
#include "kdrive.h"
|
|
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
#define DEBUG_OFFSCREEN 0
|
|
|
|
|
#if DEBUG_OFFSCREEN
|
|
|
|
|
#define DBG_OFFSCREEN(a) ErrorF a
|
|
|
|
|
#else
|
|
|
|
|
#define DBG_OFFSCREEN(a)
|
|
|
|
|
#endif
|
|
|
|
|
|
2003-10-13 00:19:58 +00:00
|
|
|
typedef struct _RealOffscreenArea {
|
|
|
|
|
KdOffscreenArea area;
|
|
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
KdOffscreenSaveProc save;
|
2003-10-13 00:19:58 +00:00
|
|
|
|
|
|
|
|
Bool locked;
|
|
|
|
|
|
|
|
|
|
struct _RealOffscreenArea *prev;
|
2003-10-14 21:10:53 +00:00
|
|
|
struct _RealOffscreenArea *next;
|
2003-10-13 00:19:58 +00:00
|
|
|
} RealOffscreenArea;
|
|
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
#if DEBUG_OFFSCREEN
|
|
|
|
|
static void
|
|
|
|
|
KdOffscreenValidate (ScreenPtr pScreen)
|
|
|
|
|
{
|
|
|
|
|
KdScreenPriv (pScreen);
|
|
|
|
|
RealOffscreenArea *prev = 0, *area;
|
|
|
|
|
|
|
|
|
|
assert (pScreenPriv->screen->off_screen_areas->area.offset == 0);
|
|
|
|
|
for (area = pScreenPriv->screen->off_screen_areas; area; area = area->next)
|
|
|
|
|
{
|
|
|
|
|
if (prev)
|
|
|
|
|
assert (prev->area.offset + prev->area.size == area->area.offset);
|
|
|
|
|
|
|
|
|
|
prev = area;
|
|
|
|
|
}
|
|
|
|
|
assert (prev->area.offset + prev->area.size == pScreenPriv->screen->off_screen_size);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
#define KdOffscreenValidate(s)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
KdOffscreenKickOut (KdOffscreenArea *area)
|
|
|
|
|
{
|
|
|
|
|
RealOffscreenArea *real_area = (RealOffscreenArea *) area;
|
|
|
|
|
KdCheckSync (area->screen);
|
|
|
|
|
if (real_area->save)
|
|
|
|
|
(*real_area->save) (area);
|
|
|
|
|
KdOffscreenFree (area);
|
|
|
|
|
}
|
|
|
|
|
|
2003-10-13 00:19:58 +00:00
|
|
|
KdOffscreenArea *
|
|
|
|
|
KdOffscreenAlloc (ScreenPtr pScreen, int size, int align,
|
|
|
|
|
Bool locked,
|
2003-10-14 21:10:53 +00:00
|
|
|
KdOffscreenSaveProc save,
|
2003-10-13 00:19:58 +00:00
|
|
|
pointer privData)
|
|
|
|
|
{
|
2003-10-14 21:10:53 +00:00
|
|
|
RealOffscreenArea *area, **prev;
|
2003-10-13 00:19:58 +00:00
|
|
|
KdScreenPriv (pScreen);
|
|
|
|
|
int tmp, real_size;
|
|
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
KdOffscreenValidate (pScreen);
|
2003-10-14 05:07:39 +00:00
|
|
|
if (!align)
|
|
|
|
|
align = 1;
|
|
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
if (!size)
|
|
|
|
|
{
|
|
|
|
|
DBG_OFFSCREEN (("Alloc 0x%x -> EMPTY\n", size));
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* throw out requests that cannot fit */
|
|
|
|
|
if (size > pScreenPriv->screen->off_screen_size)
|
|
|
|
|
{
|
|
|
|
|
DBG_OFFSCREEN (("Alloc 0x%x -> TOBIG\n", size));
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retry:
|
|
|
|
|
|
2003-10-13 00:19:58 +00:00
|
|
|
/* Go through the areas */
|
2003-10-14 21:10:53 +00:00
|
|
|
for (area = pScreenPriv->screen->off_screen_areas; area; area = area->next)
|
2003-10-13 00:19:58 +00:00
|
|
|
{
|
2003-10-14 21:10:53 +00:00
|
|
|
/* skip allocated areas */
|
|
|
|
|
if (area->area.screen)
|
2003-10-13 00:19:58 +00:00
|
|
|
continue;
|
|
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
/* adjust size to match alignment requirement */
|
2003-10-13 00:19:58 +00:00
|
|
|
real_size = size;
|
2003-10-14 21:10:53 +00:00
|
|
|
tmp = area->area.offset % align;
|
2003-10-13 00:19:58 +00:00
|
|
|
if (tmp)
|
|
|
|
|
real_size += (align - tmp);
|
|
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
/* does it fit? */
|
2003-10-13 00:19:58 +00:00
|
|
|
if (real_size <= area->area.size)
|
|
|
|
|
{
|
|
|
|
|
RealOffscreenArea *new_area;
|
|
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
/* save extra space in new area */
|
|
|
|
|
if (real_size < area->area.size)
|
2003-10-13 00:19:58 +00:00
|
|
|
{
|
2003-10-14 21:10:53 +00:00
|
|
|
new_area = xalloc (sizeof (RealOffscreenArea));
|
|
|
|
|
if (!new_area)
|
|
|
|
|
return NULL;
|
|
|
|
|
new_area->area.offset = area->area.offset + real_size;
|
|
|
|
|
new_area->area.size = area->area.size - real_size;
|
|
|
|
|
new_area->area.screen = 0;
|
|
|
|
|
new_area->locked = FALSE;
|
|
|
|
|
new_area->save = 0;
|
|
|
|
|
if ((new_area->next = area->next))
|
|
|
|
|
new_area->next->prev = new_area;
|
|
|
|
|
new_area->prev = area;
|
|
|
|
|
area->next = new_area;
|
|
|
|
|
area->area.size = real_size;
|
2003-10-13 00:19:58 +00:00
|
|
|
}
|
2003-10-14 21:10:53 +00:00
|
|
|
area->area.screen = pScreen;
|
|
|
|
|
area->area.privData = privData;
|
|
|
|
|
area->locked = locked;
|
|
|
|
|
area->save = save;
|
2003-10-13 00:19:58 +00:00
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
KdOffscreenValidate (pScreen);
|
|
|
|
|
|
|
|
|
|
DBG_OFFSCREEN (("Alloc 0x%x -> 0x%x\n", size, area->area.offset));
|
|
|
|
|
return &area->area;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Kick out existing users. This is pretty simplistic; it just
|
|
|
|
|
* keeps deleting areas until the first area is free and has enough room
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
prev = (RealOffscreenArea **) &pScreenPriv->screen->off_screen_areas;
|
|
|
|
|
while ((area = *prev))
|
|
|
|
|
{
|
|
|
|
|
if (area->area.screen && !area->locked)
|
|
|
|
|
{
|
|
|
|
|
KdOffscreenKickOut (&area->area);
|
|
|
|
|
continue;
|
2003-10-13 00:19:58 +00:00
|
|
|
}
|
2003-10-14 21:10:53 +00:00
|
|
|
/* adjust size to match alignment requirement */
|
|
|
|
|
real_size = size;
|
|
|
|
|
tmp = area->area.offset % align;
|
|
|
|
|
if (tmp)
|
|
|
|
|
real_size += (align - tmp);
|
2003-10-13 00:19:58 +00:00
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
/* does it fit? */
|
|
|
|
|
if (real_size <= area->area.size)
|
|
|
|
|
goto retry;
|
|
|
|
|
|
|
|
|
|
/* kick out the next area */
|
2003-10-13 00:19:58 +00:00
|
|
|
area = area->next;
|
2003-10-14 21:10:53 +00:00
|
|
|
if (!area)
|
|
|
|
|
break;
|
|
|
|
|
/* skip over locked areas */
|
|
|
|
|
if (area->locked)
|
|
|
|
|
{
|
|
|
|
|
prev = &area->next;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
assert (area->area.screen);
|
|
|
|
|
KdOffscreenKickOut (&area->area);
|
2003-10-13 00:19:58 +00:00
|
|
|
}
|
2003-10-14 21:10:53 +00:00
|
|
|
|
|
|
|
|
DBG_OFFSCREEN (("Alloc 0x%x -> NOSPACE\n", size));
|
2003-10-13 00:19:58 +00:00
|
|
|
/* Could not allocate memory */
|
2003-10-14 21:10:53 +00:00
|
|
|
KdOffscreenValidate (pScreen);
|
2003-10-13 00:19:58 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
KdOffscreenSwapOut (ScreenPtr pScreen)
|
|
|
|
|
{
|
|
|
|
|
KdScreenPriv (pScreen);
|
|
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
KdOffscreenValidate (pScreen);
|
|
|
|
|
/* loop until a single free area spans the space */
|
|
|
|
|
for (;;)
|
2003-10-13 00:19:58 +00:00
|
|
|
{
|
2003-10-14 21:10:53 +00:00
|
|
|
RealOffscreenArea *area = pScreenPriv->screen->off_screen_areas;
|
|
|
|
|
|
|
|
|
|
if (!area)
|
|
|
|
|
break;
|
|
|
|
|
if (area->area.screen)
|
|
|
|
|
{
|
|
|
|
|
KdOffscreenKickOut (&area->area);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2003-10-13 00:19:58 +00:00
|
|
|
area = area->next;
|
2003-10-14 21:10:53 +00:00
|
|
|
if (!area)
|
|
|
|
|
break;
|
|
|
|
|
assert (area->area.screen);
|
|
|
|
|
KdOffscreenKickOut (&area->area);
|
|
|
|
|
KdOffscreenValidate (pScreen);
|
2003-10-13 00:19:58 +00:00
|
|
|
}
|
2003-10-14 21:10:53 +00:00
|
|
|
KdOffscreenValidate (pScreen);
|
2003-10-13 00:19:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
KdOffscreenSwapIn (ScreenPtr pScreen)
|
|
|
|
|
{
|
2003-10-14 21:10:53 +00:00
|
|
|
/* nothing to do here; page in on usage */
|
|
|
|
|
}
|
2003-10-13 00:19:58 +00:00
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
/* merge the next free area into this one */
|
|
|
|
|
static void
|
|
|
|
|
KdOffscreenMerge (KdOffscreenArea *area)
|
|
|
|
|
{
|
|
|
|
|
RealOffscreenArea *real_area = (RealOffscreenArea *) area;
|
|
|
|
|
RealOffscreenArea *next = real_area->next;
|
|
|
|
|
|
|
|
|
|
/* account for space */
|
|
|
|
|
real_area->area.size += next->area.size;
|
|
|
|
|
/* frob pointers */
|
|
|
|
|
if ((real_area->next = next->next))
|
|
|
|
|
real_area->next->prev = real_area;
|
|
|
|
|
xfree (next);
|
2003-10-13 00:19:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
KdOffscreenFree (KdOffscreenArea *area)
|
|
|
|
|
{
|
2003-10-14 21:10:53 +00:00
|
|
|
ScreenPtr pScreen = area->screen;
|
|
|
|
|
RealOffscreenArea *real_area = (RealOffscreenArea *) area;
|
|
|
|
|
RealOffscreenArea *next = real_area->next;
|
|
|
|
|
RealOffscreenArea *prev = real_area->prev;
|
2003-10-13 00:19:58 +00:00
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
DBG_OFFSCREEN (("Free 0x%x -> 0x%x\n", area->size, area->offset));
|
|
|
|
|
KdOffscreenValidate (pScreen);
|
2003-10-13 00:19:58 +00:00
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
area->screen = NULL;
|
2003-10-13 00:19:58 +00:00
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
/* link with next area if free */
|
|
|
|
|
if (next && !next->area.screen)
|
|
|
|
|
KdOffscreenMerge (&real_area->area);
|
2003-10-13 00:19:58 +00:00
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
/* link with prev area if free */
|
|
|
|
|
if (prev && !prev->area.screen)
|
|
|
|
|
KdOffscreenMerge (&prev->area);
|
|
|
|
|
|
|
|
|
|
KdOffscreenValidate (pScreen);
|
2003-10-13 00:19:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Bool
|
|
|
|
|
KdOffscreenInit (ScreenPtr pScreen)
|
|
|
|
|
{
|
|
|
|
|
KdScreenPriv (pScreen);
|
|
|
|
|
RealOffscreenArea *area;
|
|
|
|
|
|
|
|
|
|
/* Allocate a big free area */
|
|
|
|
|
area = xalloc (sizeof (RealOffscreenArea));
|
|
|
|
|
|
|
|
|
|
if (!area)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
area->area.screen = NULL;
|
|
|
|
|
area->area.offset = pScreenPriv->screen->off_screen_base;
|
|
|
|
|
area->area.size = pScreenPriv->screen->off_screen_size;
|
2003-10-14 21:10:53 +00:00
|
|
|
area->save = 0;
|
|
|
|
|
area->locked = FALSE;
|
2003-10-13 00:19:58 +00:00
|
|
|
area->next = NULL;
|
|
|
|
|
area->prev = NULL;
|
|
|
|
|
|
|
|
|
|
/* Add it to the free areas */
|
|
|
|
|
pScreenPriv->screen->off_screen_areas = area;
|
|
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
KdOffscreenValidate (pScreen);
|
|
|
|
|
|
2003-10-13 00:19:58 +00:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2003-10-14 21:10:53 +00:00
|
|
|
void
|
|
|
|
|
KdOffscreenFini (ScreenPtr pScreen)
|
|
|
|
|
{
|
|
|
|
|
KdScreenPriv (pScreen);
|
|
|
|
|
RealOffscreenArea *area;
|
|
|
|
|
|
|
|
|
|
/* just free all of the area records */
|
|
|
|
|
while ((area = pScreenPriv->screen->off_screen_areas))
|
|
|
|
|
{
|
|
|
|
|
pScreenPriv->screen->off_screen_areas = area->next;
|
|
|
|
|
xfree (area);
|
|
|
|
|
}
|
|
|
|
|
}
|