/*
 * Copyright © 2001 Keith Packard
 *
 * Partly based on code that is Copyright © The XFree86 Project 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 Keith Packard not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Keith Packard makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL KEITH PACKARD 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.
 */

/** @file
 * This file covers the initialization and teardown of SAA, and has various
 * functions not responsible for performing rendering, pixmap migration, or
 * memory management.
 */

#include <stdlib.h>

#include "saa_priv.h"
#include <X11/fonts/fontstruct.h>
#include "regionstr.h"
#include "saa.h"
#include "saa_priv.h"

DevPrivateKeyRec saa_screen_index;
DevPrivateKeyRec saa_pixmap_index;
DevPrivateKeyRec saa_gc_index;

/**
 * saa_get_drawable_pixmap() returns a backing pixmap for a given drawable.
 *
 * @param pDrawable the drawable being requested.
 *
 * This function returns the backing pixmap for a drawable, whether it is a
 * redirected window, unredirected window, or already a pixmap.  Note that
 * coordinate translation is needed when drawing to the backing pixmap of a
 * redirected window, and the translation coordinates are provided by calling
 * saa_get_drawable_pixmap() on the drawable.
 */
PixmapPtr
saa_get_drawable_pixmap(DrawablePtr pDrawable)
{
    if (pDrawable->type == DRAWABLE_WINDOW)
	return pDrawable->pScreen->GetWindowPixmap((WindowPtr) pDrawable);
    else
	return (PixmapPtr) pDrawable;
}

/**
 * Sets the offsets to add to coordinates to make them address the same bits in
 * the backing drawable. These coordinates are nonzero only for redirected
 * windows.
 */
void
saa_get_drawable_deltas(DrawablePtr pDrawable, PixmapPtr pPixmap,
			int *xp, int *yp)
{
#ifdef COMPOSITE
    if (pDrawable->type == DRAWABLE_WINDOW) {
	*xp = -pPixmap->screen_x;
	*yp = -pPixmap->screen_y;
	return;
    }
#endif

    *xp = 0;
    *yp = 0;
}

/**
 * Returns the pixmap which backs a drawable, and the offsets to add to
 * coordinates to make them address the same bits in the backing drawable.
 */
PixmapPtr
saa_get_pixmap(DrawablePtr drawable, int *xp, int *yp)
{
    PixmapPtr pixmap = saa_get_drawable_pixmap(drawable);

    saa_get_drawable_deltas(drawable, pixmap, xp, yp);

    return pixmap;
}

static Bool
saa_download_from_hw(PixmapPtr pix, RegionPtr readback)
{
    struct saa_screen_priv *sscreen = saa_screen(pix->drawable.pScreen);
    struct saa_driver *driver = sscreen->driver;
    struct saa_pixmap *spix = saa_pixmap(pix);
    void *addr;
    Bool ret;

    if (spix->mapped_access)
	driver->release_from_cpu(driver, pix, spix->mapped_access);

    ret = driver->download_from_hw(driver, pix, readback);

    if (spix->mapped_access) {
	addr = driver->sync_for_cpu(driver, pix, spix->mapped_access);
	if (addr != NULL)
	    spix->addr = addr;
    }

    return ret;
}

Bool
saa_prepare_access_pixmap(PixmapPtr pix, saa_access_t access,
			  RegionPtr read_reg)
{
    ScreenPtr pScreen = pix->drawable.pScreen;
    struct saa_screen_priv *sscreen = saa_screen(pScreen);
    struct saa_driver *driver = sscreen->driver;
    struct saa_pixmap *spix = saa_pixmap(pix);
    saa_access_t map_access = 0;
    Bool ret = TRUE;

    if (read_reg && REGION_NOTEMPTY(pScreen, read_reg))
	ret = saa_download_from_hw(pix, read_reg);

    if (!ret) {
	LogMessage(X_ERROR, "Prepare access pixmap failed.\n");
	return ret;
    }

    if ((access & SAA_ACCESS_R) != 0 && spix->read_access++ == 0)
	map_access = SAA_ACCESS_R;
    if ((access & SAA_ACCESS_W) != 0 && spix->write_access++ == 0)
	map_access |= SAA_ACCESS_W;

    if (map_access) {
	if (spix->auth_loc != saa_loc_override) {
	    (void)driver->sync_for_cpu(driver, pix, map_access);
	    spix->addr = driver->map(driver, pix, map_access);
	} else
	    spix->addr = spix->override;
	spix->mapped_access |= map_access;
    }

    pix->devPrivate.ptr = spix->addr;
    return TRUE;
}

void
saa_finish_access_pixmap(PixmapPtr pix, saa_access_t access)
{
    struct saa_screen_priv *sscreen = saa_screen(pix->drawable.pScreen);
    struct saa_driver *driver = sscreen->driver;
    struct saa_pixmap *spix = saa_pixmap(pix);
    saa_access_t unmap_access = 0;

    if ((access & SAA_ACCESS_R) != 0 && --spix->read_access == 0)
	unmap_access = SAA_ACCESS_R;
    if ((access & SAA_ACCESS_W) != 0 && --spix->write_access == 0)
	unmap_access |= SAA_ACCESS_W;

    if (spix->read_access < 0)
	LogMessage(X_ERROR, "Incorrect read access.\n");
    if (spix->write_access < 0)
	LogMessage(X_ERROR, "Incorrect write access.\n");

    if (unmap_access) {
	if (spix->auth_loc != saa_loc_override) {
	    driver->unmap(driver, pix, unmap_access);
	    driver->release_from_cpu(driver, pix, unmap_access);
	}
	spix->mapped_access &= ~unmap_access;
    }
    if (!spix->mapped_access) {
	spix->addr = NULL;
	pix->devPrivate.ptr = SAA_INVALID_ADDRESS;
    }
}

/*
 * Callback that is called after a rendering operation. We try to
 * determine whether it's a shadow damage or a hw damage and call the
 * driver callback.
 */

static void
saa_report_damage(DamagePtr damage, RegionPtr reg, void *closure)
{
    PixmapPtr pixmap = (PixmapPtr) closure;
    struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap);
    struct saa_driver *driver = saa_screen(pixmap->drawable.pScreen)->driver;

    if (spix->read_access || spix->write_access)
	LogMessage(X_ERROR, "Damage report inside prepare access.\n");

    driver->operation_complete(driver, pixmap);
    DamageEmpty(damage);
}

/**
 * saa_notify_destroy_damage - Handle destroy damage notification
 *
 * \param damage[in] damage Pointer to damage about to be destroyed
 * \param closure[in] closure Closure.
 *
 * Makes sure that whatever code destroys a damage object clears the
 * saa_pixmap damage pointer. This is extra protection. Typically
 * saa_destroy_pixmap should be correct if the various subsystem
 * DestroyPixmap functions are called in the right order.
 */
static void
saa_notify_destroy_damage(DamagePtr damage, void *closure)
{
    PixmapPtr pixmap = (PixmapPtr) closure;
    struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap);

    if (spix->damage == damage)
	spix->damage = NULL;
}

Bool
saa_add_damage(PixmapPtr pixmap)
{
    ScreenPtr pScreen = pixmap->drawable.pScreen;
    struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap);

    if (spix->damage)
	return TRUE;

    spix->damage = DamageCreate(saa_report_damage, saa_notify_destroy_damage,
				DamageReportRawRegion, TRUE, pScreen, pixmap);
    if (!spix->damage)
	return FALSE;

    DamageRegister(&pixmap->drawable, spix->damage);
    DamageSetReportAfterOp(spix->damage, TRUE);

    return TRUE;
}

static inline RegionPtr
saa_pix_damage_region(struct saa_pixmap *spix)
{
    return (spix->damage ? DamageRegion(spix->damage) : NULL);
}

Bool
saa_pad_read(DrawablePtr draw)
{
    ScreenPtr pScreen = draw->pScreen;
    PixmapPtr pix;
    int xp;
    int yp;
    BoxRec box;
    RegionRec entire;
    Bool ret;

    (void)pScreen;
    pix = saa_get_pixmap(draw, &xp, &yp);

    box.x1 = draw->x + xp;
    box.y1 = draw->y + yp;
    box.x2 = box.x1 + draw->width;
    box.y2 = box.y1 + draw->height;

    REGION_INIT(pScreen, &entire, &box, 1);
    ret = saa_prepare_access_pixmap(pix, SAA_ACCESS_R, &entire);
    REGION_UNINIT(pScreen, &entire);
    return ret;
}

Bool
saa_pad_read_box(DrawablePtr draw, int x, int y, int w, int h)
{
    ScreenPtr pScreen = draw->pScreen;
    PixmapPtr pix;
    int xp;
    int yp;
    BoxRec box;
    RegionRec entire;
    Bool ret;

    (void)pScreen;
    pix = saa_get_pixmap(draw, &xp, &yp);

    box.x1 = x + xp;
    box.y1 = y + yp;
    box.x2 = box.x1 + w;
    box.y2 = box.y1 + h;

    REGION_INIT(pScreen, &entire, &box, 1);
    ret = saa_prepare_access_pixmap(pix, SAA_ACCESS_R, &entire);
    REGION_UNINIT(pScreen, &entire);
    return ret;
}

/**
 * Prepares a drawable destination for access, and maps it read-write.
 * If check_read is TRUE, pGC should point to a valid GC. The drawable
 * may then be mapped write-only if the pending operation admits.
 */

Bool
saa_pad_write(DrawablePtr draw, GCPtr pGC, Bool check_read,
	      saa_access_t * access)
{
    int xp;
    int yp;
    PixmapPtr pixmap = saa_get_pixmap(draw, &xp, &yp);
    struct saa_pixmap *spix = saa_pixmap(pixmap);

    *access = SAA_ACCESS_W;

    /*
     * If the to-be-damaged area doesn't depend at all on previous
     * rendered contents, we don't need to do any readback.
     */

    if (check_read && !saa_gc_reads_destination(draw, pGC))
	return saa_prepare_access_pixmap(pixmap, *access, NULL);

    *access |= SAA_ACCESS_R;

    /*
     * Read back the area to be damaged.
     */

    return saa_prepare_access_pixmap(pixmap, *access,
				     saa_pix_damage_pending(spix));
}

void
saa_fad_read(DrawablePtr draw)
{
    saa_finish_access_pixmap(saa_get_drawable_pixmap(draw), SAA_ACCESS_R);
}

void
saa_fad_write(DrawablePtr draw, saa_access_t access)
{
    PixmapPtr pix = saa_get_drawable_pixmap(draw);
    struct saa_pixmap *spix = saa_pixmap(pix);

    saa_finish_access_pixmap(pix, access);
    if (spix->damage)
	saa_pixmap_dirty(pix, FALSE, saa_pix_damage_pending(spix));
}

Bool
saa_gc_reads_destination(DrawablePtr pDrawable, GCPtr pGC)
{
    return ((pGC->alu != GXcopy && pGC->alu != GXclear && pGC->alu != GXset &&
	     pGC->alu != GXcopyInverted) || pGC->fillStyle == FillStippled ||
	    pGC->clientClip != NULL ||
	    !SAA_PM_IS_SOLID(pDrawable, pGC->planemask));
}

Bool
saa_op_reads_destination(CARD8 op)
{
    /* FALSE (does not read destination) is the list of ops in the protocol
     * document with "0" in the "Fb" column and no "Ab" in the "Fa" column.
     * That's just Clear and Src.  ReduceCompositeOp() will already have
     * converted con/disjoint clear/src to Clear or Src.
     */
    switch (op) {
    case PictOpClear:
    case PictOpSrc:
	return FALSE;
    default:
	return TRUE;
    }
}

static void
saa_validate_gc(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable)
{
    /* fbValidateGC will do direct access to pixmaps if the tiling has changed.
     * Do a few smart things so fbValidateGC can do it's work.
     */

    ScreenPtr pScreen = pDrawable->pScreen;
    struct saa_screen_priv *sscreen = saa_screen(pScreen);
    struct saa_gc_priv *sgc = saa_gc(pGC);
    PixmapPtr pTile = NULL;
    Bool finish_current_tile = FALSE;

    /* Either of these conditions is enough to trigger access to a tile pixmap. */
    /* With pGC->tileIsPixel == 1, you run the risk of dereferencing an invalid tile pixmap pointer. */
    if (pGC->fillStyle == FillTiled
	|| ((changes & GCTile) && !pGC->tileIsPixel)) {
	pTile = pGC->tile.pixmap;

	/* Sometimes tile pixmaps are swapped, you need access to:
	 * - The current tile if it depth matches.
	 * - Or the rotated tile if that one matches depth and !(changes & GCTile).
	 * - Or the current tile pixmap and a newly created one.
	 */
	if (pTile && pTile->drawable.depth != pDrawable->depth
	    && !(changes & GCTile)) {
	    PixmapPtr pRotatedTile = fbGetRotatedPixmap(pGC);

	    if (pRotatedTile
		&& pRotatedTile->drawable.depth == pDrawable->depth)
		pTile = pRotatedTile;
	    else
		finish_current_tile = TRUE;	/* CreatePixmap will be called. */
	}
    }

    if (pGC->stipple && !saa_pad_read(&pGC->stipple->drawable)) {
	LogMessage(X_ERROR, "Failed stipple prepareaccess.\n");
	return;
    }

    if (pTile && !saa_pad_read(&pTile->drawable)) {
	LogMessage(X_ERROR, "Failed stipple prepareaccess.\n");
	goto out_no_tile;
    }

    /* Calls to Create/DestroyPixmap have to be identified as special, so
     * up sscreen->fallback_count.
     */

    sscreen->fallback_count++;
    saa_swap(sgc, pGC, funcs);
    (*pGC->funcs->ValidateGC) (pGC, changes, pDrawable);
    saa_swap(sgc, pGC, funcs);

    if (finish_current_tile && pGC->tile.pixmap)
	saa_fad_write(&pGC->tile.pixmap->drawable, SAA_ACCESS_W);
    sscreen->fallback_count--;

    if (pTile)
	saa_fad_read(&pTile->drawable);
 out_no_tile:
    if (pGC->stipple)
	saa_fad_read(&pGC->stipple->drawable);
}

static void
saa_destroy_gc(GCPtr pGC)
{
    struct saa_gc_priv *sgc = saa_gc(pGC);

    saa_swap(sgc, pGC, funcs);
    (*pGC->funcs->DestroyGC) (pGC);
    saa_swap(sgc, pGC, funcs);
}

static void
saa_change_gc(GCPtr pGC, unsigned long mask)
{
    struct saa_gc_priv *sgc = saa_gc(pGC);

    saa_swap(sgc, pGC, funcs);
    (*pGC->funcs->ChangeGC) (pGC, mask);
    saa_swap(sgc, pGC, funcs);
}

static void
saa_copy_gc(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst)
{
    struct saa_gc_priv *sgc = saa_gc(pGCDst);

    saa_swap(sgc, pGCDst, funcs);
    (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst);
    saa_swap(sgc, pGCDst, funcs);
}

static void
saa_change_clip(GCPtr pGC, int type, void *pvalue, int nrects)
{
    struct saa_gc_priv *sgc = saa_gc(pGC);

    saa_swap(sgc, pGC, funcs);
    (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects);
    saa_swap(sgc, pGC, funcs);
}

static void
saa_copy_clip(GCPtr pGCDst, GCPtr pGCSrc)
{
    struct saa_gc_priv *sgc = saa_gc(pGCDst);

    saa_swap(sgc, pGCDst, funcs);
    (*pGCDst->funcs->CopyClip) (pGCDst, pGCSrc);
    saa_swap(sgc, pGCDst, funcs);
}

static void
saa_destroy_clip(GCPtr pGC)
{
    struct saa_gc_priv *sgc = saa_gc(pGC);

    saa_swap(sgc, pGC, funcs);
    (*pGC->funcs->DestroyClip) (pGC);
    saa_swap(sgc, pGC, funcs);
}

static GCFuncs saa_gc_funcs = {
    saa_validate_gc,
    saa_change_gc,
    saa_copy_gc,
    saa_destroy_gc,
    saa_change_clip,
    saa_destroy_clip,
    saa_copy_clip
};

/**
 * saa_create_gc makes a new GC and hooks up its funcs handler, so that
 * saa_validate_gc() will get called.
 */
int
saa_create_gc(GCPtr pGC)
{
    ScreenPtr pScreen = pGC->pScreen;
    struct saa_screen_priv *sscreen = saa_screen(pScreen);
    struct saa_gc_priv *sgc = saa_gc(pGC);
    Bool ret;

    saa_swap(sscreen, pScreen, CreateGC);
    ret = pScreen->CreateGC(pGC);
    if (ret) {
	saa_wrap(sgc, pGC, funcs, &saa_gc_funcs);
	saa_wrap(sgc, pGC, ops, &saa_gc_ops);
    }
    saa_swap(sscreen, pScreen, CreateGC);

    return ret;
}

static Bool
saa_prepare_access_window(WindowPtr pWin)
{
    if (pWin->backgroundState == BackgroundPixmap) {
	if (!saa_pad_read(&pWin->background.pixmap->drawable))
	    return FALSE;
    }

    if (pWin->borderIsPixel == FALSE) {
	if (!saa_pad_read(&pWin->border.pixmap->drawable)) {
	    if (pWin->backgroundState == BackgroundPixmap)
		saa_fad_read(&pWin->background.pixmap->drawable);
	    return FALSE;
	}
    }
    return TRUE;
}

static void
saa_finish_access_window(WindowPtr pWin)
{
    if (pWin->backgroundState == BackgroundPixmap)
	saa_fad_read(&pWin->background.pixmap->drawable);

    if (pWin->borderIsPixel == FALSE)
	saa_fad_read(&pWin->border.pixmap->drawable);
}

static Bool
saa_change_window_attributes(WindowPtr pWin, unsigned long mask)
{
    Bool ret;

    if (!saa_prepare_access_window(pWin))
	return FALSE;
    ret = fbChangeWindowAttributes(pWin, mask);
    saa_finish_access_window(pWin);
    return ret;
}

RegionPtr
saa_bitmap_to_region(PixmapPtr pPix)
{
    RegionPtr ret;

    if (!saa_pad_read(&pPix->drawable))
	return NULL;
    ret = fbPixmapToRegion(pPix);
    saa_fad_read(&pPix->drawable);
    return ret;
}

void
saa_set_fallback_debug(ScreenPtr screen, Bool enable)
{
    struct saa_screen_priv *sscreen = saa_screen(screen);

    sscreen->fallback_debug = enable;
}

/**
 * saa_early_close_screen() Makes sure we call saa_destroy_pixmap on the
 * miScreenInit() pixmap _before_ damageCloseScreen, after which it will
 * generate an invalid memory access. Also unwraps the functions we
 * wrapped _after_ DamageSetup().
 */
static Bool
saa_early_close_screen(ScreenPtr pScreen)
{
    struct saa_screen_priv *sscreen = saa_screen(pScreen);

    if (pScreen->devPrivate) {
	/* Destroy the pixmap created by miScreenInit() *before*
	 * chaining up as we finalize ourselves here and so this
	 * is the last chance we have of releasing our resources
	 * associated with the Pixmap. So do it first.
	 */
        dixDestroyPixmap(pScreen->devPrivate, 0);
	pScreen->devPrivate = NULL;
    }

    saa_unwrap_early(sscreen, pScreen, CloseScreen);
    saa_unwrap(sscreen, pScreen, DestroyPixmap);

    return (*pScreen->CloseScreen) (pScreen);
}

/**
 * saa_close_screen() unwraps its wrapped screen functions and tears down SAA's
 * screen private, before calling down to the next CloseScreen.
 */
Bool
saa_close_screen(ScreenPtr pScreen)
{
    struct saa_screen_priv *sscreen = saa_screen(pScreen);
    struct saa_driver *driver = sscreen->driver;

    saa_unwrap(sscreen, pScreen, CloseScreen);
    saa_unwrap(sscreen, pScreen, CreateGC);
    saa_unwrap(sscreen, pScreen, ChangeWindowAttributes);
    saa_unwrap(sscreen, pScreen, CreatePixmap);
    saa_unwrap(sscreen, pScreen, ModifyPixmapHeader);
    saa_unwrap(sscreen, pScreen, BitmapToRegion);
#ifdef RENDER
    saa_render_takedown(pScreen);
#endif
    saa_unaccel_takedown(pScreen);
    driver->takedown(driver);

    free(sscreen);

    return (*pScreen->CloseScreen) (pScreen);
}

struct saa_driver *
saa_get_driver(ScreenPtr pScreen)
{
    return saa_screen(pScreen)->driver;
}

/**
 * @param pScreen screen being initialized
 * @param pScreenInfo SAA driver record
 *
 * saa_driver_init sets up SAA given a driver record filled in by the driver.
 * pScreenInfo should have been allocated by saa_driver_alloc().  See the
 * comments in _SaaDriver for what must be filled in and what is optional.
 *
 * @return TRUE if SAA was successfully initialized.
 */
Bool
saa_driver_init(ScreenPtr screen, struct saa_driver * saa_driver)
{
    struct saa_screen_priv *sscreen;

    if (!saa_driver)
	return FALSE;

    if (saa_driver->saa_major != SAA_VERSION_MAJOR ||
	saa_driver->saa_minor > SAA_VERSION_MINOR) {
	LogMessage(X_ERROR,
		   "SAA(%d): driver's SAA version requirements "
		   "(%d.%d) are incompatible with SAA version (%d.%d)\n",
		   screen->myNum, saa_driver->saa_major,
		   saa_driver->saa_minor, SAA_VERSION_MAJOR, SAA_VERSION_MINOR);
	return FALSE;
    }
#if 0
    if (!saa_driver->prepare_solid) {
	LogMessage(X_ERROR,
		   "SAA(%d): saa_driver_t::prepare_solid must be "
		   "non-NULL\n", screen->myNum);
	return FALSE;
    }

    if (!saa_driver->prepare_copy) {
	LogMessage(X_ERROR,
		   "SAA(%d): saa_driver_t::prepare_copy must be "
		   "non-NULL\n", screen->myNum);
	return FALSE;
    }
#endif
    if (!dixRegisterPrivateKey(&saa_screen_index, PRIVATE_SCREEN, 0)) {
	LogMessage(X_ERROR, "Failed to register SAA screen private.\n");
	return FALSE;
    }
    if (!dixRegisterPrivateKey(&saa_pixmap_index, PRIVATE_PIXMAP,
			       saa_driver->pixmap_size)) {
	LogMessage(X_ERROR, "Failed to register SAA pixmap private.\n");
	return FALSE;
    }
    if (!dixRegisterPrivateKey(&saa_gc_index, PRIVATE_GC,
			       sizeof(struct saa_gc_priv))) {
	LogMessage(X_ERROR, "Failed to register SAA gc private.\n");
	return FALSE;
    }

    sscreen = calloc(1, sizeof(*sscreen));

    if (!sscreen) {
	LogMessage(X_WARNING,
		   "SAA(%d): Failed to allocate screen private\n",
		   screen->myNum);
	return FALSE;
    }

    sscreen->driver = saa_driver;
    dixSetPrivate(&screen->devPrivates, &saa_screen_index, sscreen);

    /*
     * Replace various fb screen functions
     */

    saa_wrap(sscreen, screen, CloseScreen, saa_close_screen);
    saa_wrap(sscreen, screen, CreateGC, saa_create_gc);
    saa_wrap(sscreen, screen, ChangeWindowAttributes,
	     saa_change_window_attributes);
    saa_wrap(sscreen, screen, CreatePixmap, saa_create_pixmap);
    saa_wrap(sscreen, screen, ModifyPixmapHeader, saa_modify_pixmap_header);
    saa_wrap(sscreen, screen, BitmapToRegion, saa_bitmap_to_region);
    saa_unaccel_setup(screen);
#ifdef RENDER
    saa_render_setup(screen);
#endif

    /*
     * Correct saa functionality relies on Damage, so set it up now.
     * Note that this must happen _after_ wrapping the rendering functionality
     * so that damage happens outside of saa.
     */
    if (!DamageSetup(screen))
	return FALSE;

    /*
     * Wrap DestroyPixmap after DamageSetup, so that saa_destroy_pixmap is
     * called _before_ damageDestroyPixmap. This is to make damageDestroyPixmap
     * doesn't free objects pointed to by our damage pointers.
     *
     * Also wrap an early CloseScreen to perform actions needed to be done
     * before damageCloseScreen and to unwrap DestroyPixmap correctly.
     */
    saa_wrap(sscreen, screen, DestroyPixmap, saa_destroy_pixmap);
    saa_wrap_early(sscreen, screen, CloseScreen, saa_early_close_screen);

    return TRUE;
}

Bool
saa_resources_init(ScreenPtr screen)
{
/*    if (!saa_glyphs_init(screen))
	return FALSE;
*/
    return TRUE;
}
