/*
 * $Id: shadow.c,v 1.19 2005-08-08 06:25:22 keithp Exp $
 *
 * Copyright © 2000 Keith Packard
 *
 * 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.
 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include    <X11/X.h>
#include    "scrnintstr.h"
#include    "windowstr.h"
#include    "dixfontstr.h"
#include    "mi.h"
#include    "regionstr.h"
#include    "globals.h"
#include    "gcstruct.h"
#include    "shadow.h"

int shadowScrPrivateIndex;
int shadowGeneration;

#define wrap(priv, real, mem, func) {\
    priv->mem = real->mem; \
    real->mem = func; \
}

#define unwrap(priv, real, mem) {\
    real->mem = priv->mem; \
}

static void
shadowRedisplay (ScreenPtr pScreen)
{
    shadowBuf(pScreen);
    RegionPtr	pRegion;

    if (!pBuf || !pBuf->pDamage)
	return;
    pRegion = DamageRegion (pBuf->pDamage);
    if (REGION_NOTEMPTY (pScreen, pRegion))
    {
	(*pBuf->update) (pScreen, pBuf);
	DamageEmpty (pBuf->pDamage);
    }
}

static void
shadowBlockHandler (pointer	data,
		      OSTimePtr pTimeout,
		      pointer pRead)
{
    ScreenPtr	pScreen = (ScreenPtr) data;

    shadowRedisplay (pScreen);
}

static void
shadowWakeupHandler (pointer data, int i, pointer LastSelectMask)
{
}

static void
shadowGetImage (DrawablePtr pDrawable,
		int sx,
		int sy,
		int w,
		int h,
		unsigned int format,
		unsigned long planeMask,
		char * pdstLine)
{
    ScreenPtr pScreen = pDrawable->pScreen;
    shadowBuf(pScreen);

    /* Many apps use GetImage to sync with the visable frame buffer */
    if (pDrawable->type == DRAWABLE_WINDOW)
	shadowRedisplay (pScreen);
    unwrap (pBuf, pScreen, GetImage);
    (*pScreen->GetImage) (pDrawable, sx, sy, w, h, format, planeMask, pdstLine);
    wrap (pBuf, pScreen, GetImage, shadowGetImage);
}

static Bool
shadowCloseScreen (int i, ScreenPtr pScreen)
{
    shadowBuf(pScreen);

    unwrap (pBuf, pScreen, GetImage);
    unwrap (pBuf, pScreen, CloseScreen);
    shadowUnset (pScreen);
    DamageDestroy (pBuf->pDamage);
    if (pBuf->pPixmap)
	(*pScreen->DestroyPixmap) (pBuf->pPixmap);
    xfree (pBuf);
    return (*pScreen->CloseScreen) (i, pScreen);
}

Bool
shadowSetup (ScreenPtr pScreen)
{
    shadowBufPtr	pBuf;

    if (!DamageSetup (pScreen))
	return FALSE;

    if (shadowGeneration != serverGeneration)
    {
	shadowScrPrivateIndex = AllocateScreenPrivateIndex ();
	if (shadowScrPrivateIndex == -1)
	    return FALSE;
	shadowGeneration = serverGeneration;
    }
    pBuf = (shadowBufPtr) xalloc (sizeof (shadowBufRec));
    if (!pBuf)
	return FALSE;
    pBuf->pDamage = DamageCreate ((DamageReportFunc) 0,
				  (DamageDestroyFunc) 0,
				  DamageReportNone,
				  TRUE,
				  pScreen,
				  pScreen);

    wrap (pBuf, pScreen, CloseScreen, shadowCloseScreen);
    wrap (pBuf, pScreen, GetImage, shadowGetImage);
    pBuf->update = 0;
    pBuf->window = 0;
    pBuf->pPixmap = 0;
    pBuf->closure = 0;
    pBuf->randr = 0;

    pScreen->devPrivates[shadowScrPrivateIndex].ptr = (pointer) pBuf;
    return TRUE;
}

Bool
shadowSet (ScreenPtr	    pScreen,
	   PixmapPtr	    pPixmap,
	   ShadowUpdateProc update,
	   ShadowWindowProc window,
	   int		    randr,
	   void		    *closure)
{
    shadowBuf(pScreen);

    if (!RegisterBlockAndWakeupHandlers (shadowBlockHandler,
					 shadowWakeupHandler,
					 (pointer) pScreen))
	return FALSE;

    /*
     * Map simple rotation values to bitmasks; fortunately,
     * these are all unique
     */
    switch (randr) {
    case 0:
	randr = SHADOW_ROTATE_0;
	break;
    case 90:
	randr = SHADOW_ROTATE_90;
	break;
    case 180:
	randr = SHADOW_ROTATE_180;
	break;
    case 270:
	randr = SHADOW_ROTATE_270;
	break;
    }
    pBuf->update = update;
    pBuf->window = window;
    pBuf->randr = randr;
    pBuf->closure = 0;
    pBuf->pPixmap = pPixmap;
    DamageRegister (&pPixmap->drawable, pBuf->pDamage);
    return TRUE;
}

void
shadowUnset (ScreenPtr pScreen)
{
    shadowBuf(pScreen);

    if (pBuf->pPixmap)
    {
	DamageUnregister (&pBuf->pPixmap->drawable, pBuf->pDamage);
	pBuf->update = 0;
	pBuf->window = 0;
	pBuf->randr = 0;
	pBuf->closure = 0;
	pBuf->pPixmap = 0;
    }
    RemoveBlockAndWakeupHandlers (shadowBlockHandler,
				  shadowWakeupHandler,
				  (pointer) pScreen);
}

Bool
shadowInit (ScreenPtr pScreen, ShadowUpdateProc update, ShadowWindowProc window)
{
    PixmapPtr	pPixmap;
    
    pPixmap = (*pScreen->CreatePixmap) (pScreen, pScreen->width,
					pScreen->height,
					pScreen->rootDepth);
    if (!pPixmap)
	return FALSE;
    
    if (!shadowSetup (pScreen))
    {
	(*pScreen->DestroyPixmap) (pPixmap);
	return FALSE;
    }

    shadowSet (pScreen, pPixmap, update, window, SHADOW_ROTATE_0, 0);

    return TRUE;
}
