/*
 * $XConsortium: sunCG.c,v 1.7 92/11/18 14:10:34 rws Exp $
 *
 * Copyright 1990 Massachusetts Institute of Technology
 *
 * 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 M.I.T. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
 * 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.
 *
 * Author:  Keith Packard, MIT X Consortium
 */

/*
 * sunCG.c
 *
 * Routines for managing a sun colormap
 */

#include    "sun.h"

#include    <sys/mman.h>
#ifdef SVR4
#include    <sys/memreg.h>
#else
#include    <pixrect/memreg.h>
#include    <struct.h>
#endif
#include    "colormapst.h"
#include    "resource.h"

extern int TellLostMap(), TellGainedMap();

static void
sunCGUpdateColormap(pScreen, index, count, rmap, gmap, bmap)
    ScreenPtr	pScreen;
    int		index, count;
    u_char	*rmap, *gmap, *bmap;
{
    struct fbcmap sunCmap;

    sunCmap.index = index;
    sunCmap.count = count;
    sunCmap.red = &rmap[index];
    sunCmap.green = &gmap[index];
    sunCmap.blue = &bmap[index];

#ifdef SUN_WINDOWS
    if (sunUseSunWindows()) {
	static Pixwin *pw = 0;

	if (! pw) {
	    if ( ! (pw = pw_open(windowFd)) )
		FatalError( "sunCGUpdateColormap: pw_open failed\n" );
	    pw_setcmsname(pw, "X.V11");
	}
	pw_putcolormap(
	    pw, index, count, &rmap[index], &gmap[index], &bmap[index]);
    }
#endif /* SUN_WINDOWS */

    if (ioctl(sunFbs[pScreen->myNum].fd, FBIOPUTCMAP, &sunCmap) < 0) {
	perror("sunCGUpdateColormap");
	FatalError( "sunCGUpdateColormap: FBIOPUTCMAP failed\n" );
    }
}


/*-
 *-----------------------------------------------------------------------
 * sunCGInstallColormapPseudo --
 *	Install given colormap for Pseudo hardware.
 *
 * Results:
 *	None
 *
 * Side Effects:
 *	Existing map is uninstalled.
 *	All clients requesting ColormapNotify are notified
 *
 *-----------------------------------------------------------------------
 */
static void
sunCGInstallColormapPseudo(cmap)
    ColormapPtr	cmap;
{
    SetupScreen(cmap->pScreen);
    register int i;
    register Entry *pent;
    register VisualPtr pVisual = cmap->pVisual;
    u_char	  rmap[256], gmap[256], bmap[256];

    if (cmap == pPrivate->installedMap)
	return;
    if (pPrivate->installedMap)
	WalkTree(pPrivate->installedMap->pScreen, TellLostMap,
		 (pointer) &(pPrivate->installedMap->mid));
    if ((pVisual->class | DynamicClass) == DirectColor) {
	for (i = 0; i < 256; i++) {
	    pent = &cmap->red[(i & pVisual->redMask) >>
			      pVisual->offsetRed];
	    rmap[i] = pent->co.local.red >> 8;
	    pent = &cmap->green[(i & pVisual->greenMask) >>
				pVisual->offsetGreen];
	    gmap[i] = pent->co.local.green >> 8;
	    pent = &cmap->blue[(i & pVisual->blueMask) >>
			       pVisual->offsetBlue];
	    bmap[i] = pent->co.local.blue >> 8;
	}
    } else {
	for (i = 0, pent = cmap->red;
	     i < pVisual->ColormapEntries;
	     i++, pent++) {
	    if (pent->fShared) {
		rmap[i] = pent->co.shco.red->color >> 8;
		gmap[i] = pent->co.shco.green->color >> 8;
		bmap[i] = pent->co.shco.blue->color >> 8;
	    }
	    else {
		rmap[i] = pent->co.local.red >> 8;
		gmap[i] = pent->co.local.green >> 8;
		bmap[i] = pent->co.local.blue >> 8;
	    }
	}
    }
    pPrivate->installedMap = cmap;
    (*pPrivate->UpdateColormap) (cmap->pScreen, 0, 256, rmap, gmap, bmap);
    WalkTree(cmap->pScreen, TellGainedMap, (pointer) &(cmap->mid));
}

/*-
 *-----------------------------------------------------------------------
 * sunCGInstallColormapDirect --
 *	Install given colormap for Direct hardware.
 *
 * Results:
 *	None
 *
 * Side Effects:
 *	Existing map is uninstalled.
 *	All clients requesting ColormapNotify are notified
 *
 *-----------------------------------------------------------------------
 */
static void
sunCGInstallColormapDirect(cmap)
    ColormapPtr	cmap;
{
    SetupScreen(cmap->pScreen);
    register int i;
    register VisualPtr pVisual = cmap->pVisual;
    u_char	  rmap[256], gmap[256], bmap[256];

    if (cmap == pPrivate->installedMap)
	return;
    if (pPrivate->installedMap)
	WalkTree(pPrivate->installedMap->pScreen, TellLostMap,
		 (pointer) &(pPrivate->installedMap->mid));
    for (i = 0; i < 256; i++) {
	rmap[i] = cmap->red[i].co.local.red >> 8;
	gmap[i] = cmap->green[i].co.local.green >> 8;
	bmap[i] = cmap->blue[i].co.local.blue >> 8;
    }
    pPrivate->installedMap = cmap;
    (*pPrivate->UpdateColormap) (cmap->pScreen, 0, 256, rmap, gmap, bmap);
    WalkTree(cmap->pScreen, TellGainedMap, (pointer) &(cmap->mid));
}

/*-
 *-----------------------------------------------------------------------
 * sunCGUninstallColormap --
 *	Uninstall given colormap.
 *
 * Results:
 *	None
 *
 * Side Effects:
 *	default map is installed
 *	All clients requesting ColormapNotify are notified
 *
 *-----------------------------------------------------------------------
 */
static void
sunCGUninstallColormap(cmap)
    ColormapPtr	cmap;
{
    SetupScreen(cmap->pScreen);
    if (cmap == pPrivate->installedMap) {
	Colormap defMapID = cmap->pScreen->defColormap;

	if (cmap->mid != defMapID) {
	    ColormapPtr defMap = (ColormapPtr) LookupIDByType(defMapID,
							      RT_COLORMAP);

	    if (defMap)
		(*cmap->pScreen->InstallColormap)(defMap);
	    else
	        ErrorF("sunCG: Can't find default colormap\n");
	}
    }
}

/*-
 *-----------------------------------------------------------------------
 * sunCGListInstalledColormaps --
 *	Fills in the list with the IDs of the installed maps
 *
 * Results:
 *	Returns the number of IDs in the list
 *
 * Side Effects:
 *	None
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
static int
sunCGListInstalledColormaps(pScreen, pCmapList)
    ScreenPtr	pScreen;
    Colormap	*pCmapList;
{
    SetupScreen(pScreen);
    *pCmapList = pPrivate->installedMap->mid;
    return (1);
}


/*-
 *-----------------------------------------------------------------------
 * sunCGStoreColorsPseudo --
 *	Sets the pixels in pdefs into the specified map for Pseudo hardware.
 *
 * Results:
 *	None
 *
 * Side Effects:
 *	None
 *
 *-----------------------------------------------------------------------
 */
static void
sunCGStoreColorsPseudo(pmap, ndef, pdefs)
    ColormapPtr	pmap;
    int		ndef;
    xColorItem	*pdefs;
{
    SetupScreen(pmap->pScreen);
    u_char	rmap[256], gmap[256], bmap[256];
    xColorItem	expanddefs[256];
    register int i;
    register int n;

    if (pmap != pPrivate->installedMap)
	return;
    if ((pmap->pVisual->class | DynamicClass) == DirectColor) {
	ndef = cfbExpandDirectColors(pmap, ndef, pdefs, expanddefs);
	pdefs = expanddefs;
    }
    n = 0;
    while (ndef--) {
	i = pdefs->pixel;
	rmap[i] = pdefs->red >> 8;
	gmap[i] = pdefs->green >> 8;
	bmap[i] = pdefs->blue >> 8;
	n++;
	if (!ndef || pdefs[1].pixel != i + 1) {
	    (*pPrivate->UpdateColormap)(pmap->pScreen, i - n + 1, n,
					rmap, gmap, bmap);
	    n = 0;
	}
	pdefs++;
    }
}

/*-
 *-----------------------------------------------------------------------
 * sunCGStoreColorsDirect --
 *	Sets the pixels in pdefs into the specified map for Direct hardware.
 *
 * Results:
 *	None
 *
 * Side Effects:
 *	None
 *
 *-----------------------------------------------------------------------
 */
static void
sunCGStoreColorsDirect(pmap, ndef, pdefs)
    ColormapPtr	pmap;
    int		ndef;
    xColorItem	*pdefs;
{
    SetupScreen(pmap->pScreen);
    u_char	rmap[256], gmap[256], bmap[256];
    register int i;
    register int redi, greeni, bluei;
    register VisualPtr pVisual;

    if (pmap != pPrivate->installedMap)
	return;
    pVisual = pmap->pVisual;
    while (ndef--) {
	i = pdefs->pixel;
	redi = (i & pVisual->redMask) >> pVisual->offsetRed;
	rmap[redi] = pdefs->red >> 8;
	greeni = (i & pVisual->greenMask) >> pVisual->offsetGreen;
	gmap[greeni] = pdefs->green >> 8;
	bluei = (i & pVisual->blueMask) >> pVisual->offsetBlue;
	bmap[bluei] = pdefs->blue >> 8;
	if (greeni != redi || bluei != redi) {
	    gmap[redi] = pmap->green[redi].co.local.green;
	    bmap[redi] = pmap->blue[redi].co.local.blue;
	}
	(*pPrivate->UpdateColormap) (pmap->pScreen, redi, 1, rmap, gmap, bmap);
	if (greeni != redi) {
	    rmap[greeni] = pmap->red[greeni].co.local.red;
	    bmap[greeni] = pmap->blue[greeni].co.local.blue;
	    (*pPrivate->UpdateColormap) (pmap->pScreen, greeni, 1,
					 rmap, gmap, bmap);
	}
	if (bluei != greeni && bluei != redi) {
	    rmap[bluei] = pmap->red[bluei].co.local.red;
	    gmap[bluei] = pmap->green[bluei].co.local.green;
	    (*pPrivate->UpdateColormap) (pmap->pScreen, bluei, 1,
					 rmap, gmap, bmap);
	}
	pdefs++;
    }
}

sunCGScreenInit (pScreen)
    ScreenPtr	pScreen;
{
#ifndef STATIC_COLOR
    extern Bool	FlipPixels;
    SetupScreen (pScreen);
    pScreen->UninstallColormap = sunCGUninstallColormap;
    pScreen->ListInstalledColormaps = sunCGListInstalledColormaps;
    if (pScreen->rootDepth == 24) {
	pScreen->InstallColormap = sunCGInstallColormapDirect;
	pScreen->StoreColors = sunCGStoreColorsDirect;
    } else {
	pScreen->InstallColormap = sunCGInstallColormapPseudo;
	pScreen->StoreColors = sunCGStoreColorsPseudo;
    }
    pPrivate->UpdateColormap = sunCGUpdateColormap;
    if (FlipPixels)
    {
	pScreen->whitePixel = 1;
	pScreen->blackPixel = 0;
    }
#endif
}
