/* $Xorg: cmsCmap.c,v 1.3 2000/08/17 19:45:09 cpqbld Exp $ */

/*
 * Code and supporting documentation (c) Copyright 1990 1991 Tektronix, Inc.
 * 	All Rights Reserved
 * 
 * This file is a component of an X Window System-specific implementation
 * of Xcms based on the TekColor Color Management System.  Permission is
 * hereby granted to use, copy, modify, sell, and otherwise distribute this
 * software and its documentation for any purpose and without fee, provided
 * that this copyright, permission, and disclaimer notice is reproduced in
 * all copies of this software and in supporting documentation.  TekColor
 * is a trademark of Tektronix, Inc.
 * 
 * Tektronix makes no representation about the suitability of this software
 * for any purpose.  It is provided "as is" and with all faults.
 * 
 * TEKTRONIX DISCLAIMS ALL WARRANTIES APPLICABLE TO THIS SOFTWARE,
 * INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE.  IN NO EVENT SHALL TEKTRONIX 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 THE PERFORMANCE OF THIS SOFTWARE.
 *
 *
 *	NAME
 *		XcmsCmap.c - Client Colormap Management Routines
 *
 *	DESCRIPTION
 *		Routines that store additional information about
 *		colormaps being used by the X Client.
 *
 *
 */

#define NEED_EVENTS
#define NEED_REPLIES
#include "Xlibint.h"
#include "Xcmsint.h"
#include "Xutil.h"

/*
 *      FORWARD DECLARATIONS
 */
XcmsCmapRec *_XcmsAddCmapRec();
static void _XcmsFreeClientCmaps();


/************************************************************************
 *									*
 *			PRIVATE INTERFACES				*
 *									*
 ************************************************************************/

/*
 *	NAME
 *		CmapRecForColormap
 *
 *	SYNOPSIS
 */
static XcmsCmapRec *
CmapRecForColormap(dpy, cmap)
    Display *dpy;
    Colormap cmap;
/*
 *	DESCRIPTION
 *		Find the corresponding XcmsCmapRec for cmap.  In not found
 *		this routines attempts to create one.
 *
 *	RETURNS
 *		Returns NULL if failed; otherwise the address to
 *		the corresponding XcmsCmapRec.
 *
 */
{
    XcmsCmapRec *pRec;
    int nScrn;
    int i, j;
    XVisualInfo visualTemplate;	/* Template of the visual we want */
    XVisualInfo *visualList;	/* List for visuals that match */
    int nVisualsMatched;	/* Number of visuals that match */
    Window tmpWindow;
    Visual *vp;
    unsigned long border = 0;
    _XAsyncHandler async;
    _XAsyncErrorState async_state;

    for (pRec = (XcmsCmapRec *)dpy->cms.clientCmaps; pRec != NULL;
	    pRec = pRec->pNext) {
	if (pRec->cmapID == cmap) {
	    return(pRec);
	}
    }

    /*
     * Can't find an XcmsCmapRec associated with cmap in our records.
     * Let's try to see if its a default colormap
     */
    nScrn = ScreenCount(dpy);
    for (i = 0; i < nScrn; i++) {
	if (cmap == DefaultColormap(dpy, i)) {
	    /* It is ... lets go ahead and store that info */
	    if ((pRec = _XcmsAddCmapRec(dpy, cmap, RootWindow(dpy, i),
		    DefaultVisual(dpy, i))) == NULL) {
		return((XcmsCmapRec *)NULL);
	    }
	    pRec->ccc = XcmsCreateCCC(
		    dpy,
		    i,			/* screenNumber */
		    DefaultVisual(dpy, i),
		    (XcmsColor *)NULL,	/* clientWhitePt */
		    (XcmsCompressionProc)NULL,  /* gamutCompProc */
		    (XPointer)NULL,	/* gamutCompClientData */
		    (XcmsWhiteAdjustProc)NULL,  /* whitePtAdjProc */
		    (XPointer)NULL	/* whitePtAdjClientData */
		    );
	    return(pRec);
	}
    }

    /*
     * Nope, its not a default colormap, so it's probably a foreign color map
     * of which we have no specific details.  Let's go through the
     * rigorous process of finding this colormap:
     *        for each screen
     *            for each screen's visual types
     *                create a window with cmap specified as the colormap
     *                if successful
     *                    Add a CmapRec
     *                    Create an XcmsCCC
     *                    return the CmapRec
     *                else
     *                    continue
     */

    async_state.error_code = 0; /* don't care */
    async_state.major_opcode = X_CreateWindow;
    async_state.minor_opcode = 0;
    for (i = 0; i < nScrn; i++) {
	visualTemplate.screen = i;
	visualList = XGetVisualInfo(dpy, VisualScreenMask, &visualTemplate,
	    &nVisualsMatched);
	if (nVisualsMatched == 0) {
	    continue;
	}

	/*
	 * Attempt to create a window with cmap
	 */
	j = 0;
	do {
	    vp = (visualList+j)->visual;
	    LockDisplay(dpy);
	    {
		register xCreateWindowReq *req;

		GetReq(CreateWindow, req);
		async_state.min_sequence_number = dpy->request;
		async_state.max_sequence_number = dpy->request;
		async_state.error_count = 0;
		async.next = dpy->async_handlers;
		async.handler = _XAsyncErrorHandler;
		async.data = (XPointer)&async_state;
		dpy->async_handlers = &async;
		req->parent = RootWindow(dpy, i);
		req->x = 0;
		req->y = 0;
		req->width = 1;
		req->height = 1;
		req->borderWidth = 0;
		req->depth = (visualList+j)->depth;
		req->class = CopyFromParent;
		req->visual = vp->visualid;
		tmpWindow = req->wid = XAllocID(dpy);
		req->mask = CWBorderPixel | CWColormap;
		req->length += 2;
		Data32 (dpy, (long *) &border, 4);
		Data32 (dpy, (long *) &cmap, 4);
	    }
	    {
		xGetInputFocusReply rep;
		register xReq *req;

		GetEmptyReq(GetInputFocus, req);
		(void) _XReply (dpy, (xReply *)&rep, 0, xTrue);
	    }
	    DeqAsyncHandler(dpy, &async);
	    UnlockDisplay(dpy);
	    SyncHandle();
	} while (async_state.error_count > 0 && ++j < nVisualsMatched);

	Xfree((char *)visualList);

	/*
	 * if successful
	 */
	if (j < nVisualsMatched) {
	    if ((pRec = _XcmsAddCmapRec(dpy, cmap, tmpWindow, vp)) == NULL)
		return((XcmsCmapRec *)NULL);
	    pRec->ccc = XcmsCreateCCC(
		    dpy,
		    i,			/* screenNumber */
		    vp,
		    (XcmsColor *)NULL,	/* clientWhitePt */
		    (XcmsCompressionProc)NULL,  /* gamutCompProc */
		    (XPointer)NULL,	/* gamutCompClientData */
		    (XcmsWhiteAdjustProc)NULL,  /* whitePtAdjProc */
		    (XPointer)NULL	/* whitePtAdjClientData */
		    );
	    XDestroyWindow(dpy, tmpWindow);
	    return(pRec);
	}
    }

    return(NULL);
}



/************************************************************************
 *									*
 *			API PRIVATE INTERFACES				*
 *									*
 ************************************************************************/

/*
 *	NAME
 *		_XcmsAddCmapRec
 *
 *	SYNOPSIS
 */
XcmsCmapRec *
_XcmsAddCmapRec(dpy, cmap, windowID, visual)
    Display *dpy;
    Colormap cmap;
    Window windowID;
    Visual *visual;
/*
 *	DESCRIPTION
 *		Create an XcmsCmapRec for the specified cmap, windowID,
 *		and visual, then adds it to its list of CmapRec's.
 *
 *	RETURNS
 *		Returns NULL if failed; otherwise the address to
 *		the added XcmsCmapRec.
 *
 */
{
    XcmsCmapRec *pNew;

    if ((pNew = (XcmsCmapRec *) Xcalloc(1, (unsigned) sizeof(XcmsCmapRec)))
	    == NULL) {
	return((XcmsCmapRec *)NULL);
    }

    pNew->cmapID = cmap;
    pNew->dpy = dpy;
    pNew->windowID = windowID;
    pNew->visual = visual;
    pNew->pNext = (XcmsCmapRec *)dpy->cms.clientCmaps;
    dpy->cms.clientCmaps = (XPointer)pNew;
    dpy->free_funcs->clientCmaps = _XcmsFreeClientCmaps;

    /*
     * Note, we don't create the XcmsCCC for pNew->ccc here because
     * it may require the use of XGetWindowAttributes (a round trip request)
     * to determine the screen.
     */
    return(pNew);
}


/*
 *	NAME
 *		_XcmsCopyCmapRecAndFree
 *
 *	SYNOPSIS
 */
XcmsCmapRec *
_XcmsCopyCmapRecAndFree(dpy, src_cmap, copy_cmap)
    Display *dpy;
    Colormap src_cmap;
    Colormap copy_cmap;
/*
 *	DESCRIPTION
 *		Augments Xlib's XCopyColormapAndFree() to copy
 *		XcmsCmapRecs.
 *
 *	RETURNS
 *		Returns NULL if failed; otherwise the address to
 *		the copy XcmsCmapRec.
 *
 */
{
    XcmsCmapRec *pRec_src;
    XcmsCmapRec *pRec_copy;

    if ((pRec_src = CmapRecForColormap(dpy, src_cmap)) != NULL) {
	pRec_copy =_XcmsAddCmapRec(dpy, copy_cmap, pRec_src->windowID,
		pRec_src->visual);
	if (pRec_copy != NULL && pRec_src->ccc) {
	    pRec_copy->ccc = (XcmsCCC)Xcalloc(1, (unsigned) sizeof(XcmsCCCRec));
	    memcpy((char *)pRec_copy->ccc, (char *)pRec_src->ccc,
		   sizeof(XcmsCCCRec));
	}
	return(pRec_copy);
    }
    return((XcmsCmapRec *)NULL);
}


/*
 *	NAME
 *		_XcmsDeleteCmapRec
 *
 *	SYNOPSIS
 */
void
_XcmsDeleteCmapRec(dpy, cmap)
    Display *dpy;
    Colormap cmap;
/*
 *	DESCRIPTION
 *		Removes and frees the specified XcmsCmapRec structure
 *		from the linked list of structures.
 *
 *	RETURNS
 *		void
 *
 */
{
    XcmsCmapRec **pPrevPtr;
    XcmsCmapRec *pRec;
    int scr;

    /* If it is the default cmap for a screen, do not delete it,
     * because the server will not actually free it */
    for (scr = ScreenCount(dpy); --scr >= 0; ) {
	if (cmap == DefaultColormap(dpy, scr))
	    return;
    }

    /* search for it in the list */
    pPrevPtr = (XcmsCmapRec **)&dpy->cms.clientCmaps;
    while ((pRec = *pPrevPtr) && (pRec->cmapID != cmap)) {
	pPrevPtr = &pRec->pNext;
    }

    if (pRec) {
	if (pRec->ccc) {
	    XcmsFreeCCC(pRec->ccc);
	}
	*pPrevPtr = pRec->pNext;
	Xfree((char *)pRec);
    }
}


/*
 *	NAME
 *		_XcmsFreeClientCmaps
 *
 *	SYNOPSIS
 */
static void
_XcmsFreeClientCmaps(dpy)
    Display *dpy;
/*
 *	DESCRIPTION
 *		Frees all XcmsCmapRec structures in the linked list
 *		and sets dpy->cms.clientCmaps to NULL.
 *
 *	RETURNS
 *		void
 *
 */
{
    XcmsCmapRec *pRecNext, *pRecFree;

    pRecNext = (XcmsCmapRec *)dpy->cms.clientCmaps;
    while (pRecNext != NULL) {
	pRecFree = pRecNext;
	pRecNext = pRecNext->pNext;
	if (pRecFree->ccc) {
	    /* Free the XcmsCCC structure */
	    XcmsFreeCCC(pRecFree->ccc);
	}
	/* Now free the XcmsCmapRec structure */
	Xfree((char *)pRecFree);
    }
    dpy->cms.clientCmaps = (XPointer)NULL;
}



/************************************************************************
 *									*
 *			PUBLIC INTERFACES				*
 *									*
 ************************************************************************/

/*
 *	NAME
 *		XcmsCCCOfColormap
 *
 *	SYNOPSIS
 */
XcmsCCC 
XcmsCCCOfColormap(dpy, cmap)
    Display *dpy;
    Colormap cmap;
/*
 *	DESCRIPTION
 *		Finds the XcmsCCC associated with the specified colormap.
 *
 *	RETURNS
 *		Returns NULL if failed; otherwise the address to
 *		the associated XcmsCCC structure.
 *
 */
{
    XWindowAttributes windowAttr;
    XcmsCmapRec *pRec;
    int nScrn = ScreenCount(dpy);
    int i;

    if ((pRec = CmapRecForColormap(dpy, cmap)) != NULL) {
	if (pRec->ccc) {
	    /* XcmsCmapRec already has a XcmsCCC */
	    return(pRec->ccc);
	}

	/*
	 * The XcmsCmapRec does not have a XcmsCCC yet, so let's create
	 * one.  But first, we need to know the screen associated with
	 * cmap, so use XGetWindowAttributes() to extract that
	 * information.  Unless, of course there is only one screen!!
	 */
	if (nScrn == 1) {
	    /* Assume screenNumber == 0 */
	    return(pRec->ccc = XcmsCreateCCC(
		    dpy,
		    0,			/* screenNumber */
		    pRec->visual,
		    (XcmsColor *)NULL,	/* clientWhitePt */
		    (XcmsCompressionProc)NULL,  /* gamutCompProc */
		    (XPointer)NULL,	/* gamutCompClientData */
		    (XcmsWhiteAdjustProc)NULL,  /* whitePtAdjProc */
		    (XPointer)NULL	/* whitePtAdjClientData */
		    ));
	} else {
	    if (XGetWindowAttributes(dpy, pRec->windowID, &windowAttr)) {
		for (i = 0; i < nScrn; i++) {
		    if (ScreenOfDisplay(dpy, i) == windowAttr.screen) {
			return(pRec->ccc = XcmsCreateCCC(
				dpy,
				i,		   /* screenNumber */
				pRec->visual,
				(XcmsColor *)NULL, /* clientWhitePt */
				(XcmsCompressionProc)NULL, /* gamutCompProc */
				(XPointer)NULL,	   /* gamutCompClientData */
				(XcmsWhiteAdjustProc)NULL, /* whitePtAdjProc */
				(XPointer)NULL	   /* whitePtAdjClientData */
				));
		    }
		}
	    }
	}
    }

    /*
     * No such cmap
     */
    return(NULL);
}

XcmsCCC XcmsSetCCCOfColormap(dpy, cmap, ccc)
    Display *dpy;
    Colormap cmap;
    XcmsCCC ccc;
{
    XcmsCCC prev_ccc = NULL;
    XcmsCmapRec *pRec;

    pRec = CmapRecForColormap(dpy, cmap);
    if (pRec) {
	prev_ccc = pRec->ccc;
	pRec->ccc = ccc;
    }
    return prev_ccc;
}
