/* Copyright (c) 1993, 2015, Oracle and/or its affiliates. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */


/*
 * cmap_grab.c - the client side code for DGA colormap grabber
 */


/*
 *
 *	Shared colormap synchronization routines.
 *
 *	Client side
 *
 *
 * Functions:
 *
 *    Dga_cmap
 *    dga_cm_grab(devfd, filesuffix)
 *		int		devfd ;
 *		unsigned long	filesuffix ;
 *
 *	Grab a colormap.  'filesuffix' is the handle returned by
 *	XDgaGrabColormap.  'devfd' is the file descriptor of the frame buffer
 *	if any, -1 otherwise.  If you specify -1, dga_cm_grab will open
 *	the frame buffer.  The frame buffer fd may be inquired from
 *	the returned dga_cmap structure.
 *
 *	Returns a pointer to a dga_cmap structure on success,
 *	NULL on failure.
 *
 *
 *    void
 *    dga_cm_ungrab(cginfo,cflag)
 *		Dga_cmap	cginfo ;
 *		int		cflag ;
 *
 *	Release a colormap.  All resources allocated by dga_cm_grab are
 *	freed.  The application should call XDgaUnGrabColormap after calling
 *	dga_cm_ungrab() so that the server may free the colormap info page
 *	at the other end.
 *
 *	if cflag is nonzero, the framebuffr fd described in the info page
 *	is also closed.  The info page is invalid after this call and
 *	references to it will probably result in a SIGSEGV.
 *
 *
 *
 *
 *    void
 *    dga_cm_get(cginfo,index,count, red,green,blue)
 *		Dga_cmap	cginfo ;
 *		int		index, count ;
 *		u_char		*red, *green, *blue ;
 *
 *	Read colormap values and return them to the application.
 *
 *
 *    void
 *    dga_cm_put(cginfo,index,count, red,green,blue)
 *		Dga_cmap	cginfo ;
 *		int		index, count ;
 *		u_char		*red, *green, *blue ;
 *
 *	Write colormap to hardware if colormap is installed, otherwise
 *	save them in shared memory.
 *
 *
 *
 *    void
 *    dga_cm_write(cginfo,index,count, red,green,blue, putfunc)
 *      Dga_cmap    cginfo ;
 *      int     index, count ;
 *      u_char      *red, *green, *blue ;
 * 		int		(*putfunc)();
 *
 *  Write colormap to hardware by calling the user supplied putfunc 
 *  if colormap is installed, otherwise save them in shared memory.
 * 
 **    void
 *    dga_cm_read(cginfo,index,count, red,green,blue)
 *      Dga_cmap    cginfo ;
 *      int     index, count ;
 *      u_char      *red, *green, *blue ;
 * 		int		(*putfunc)();
 *
 *	Read colormap values and return them to the application
 *
 *	int
 *	dga_cm_get_devfd(cginfo)
 *	Dga_cmap cginfo;
 *
 *	Return the fd of the device associated with cginfo
 *
 *	void
 *	dga_cm_get_devinfo(cginfo)
 *	Dga_cmap cginfo;
 *
 *	Returns pointer to the device specific info associated with
 *	cginfo. Used to communicate info bet server and client
 *
 *	void
 *	dga_cm_set_client_infop(cginfo, client_info_ptr)
 *	Dga_cmap cginfo;
 *	void*  client_info_ptr;
 *
 *	Sets pointer to client specificr-data associated with cginfo.
 *
 *	void *
 *	dga_cm_get_client_infop(cginfo)
 *	Dga_cmap cginfo;
 *
 *	Returns the client specific data pointer associated with cginfo

 ****/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <errno.h>
#include <X11/Xlib.h>

#ifdef SVR4
#include <sys/fbio.h>
#ifdef SUNWINDOWS	/* see note later */
#include <sys/cms.h>
#include <sys/rect.h>
#include <sys/win_screen.h>
#include <sys/win_input.h>
#include <sys/win_ioctl.h>
#endif /* SUNWINDOWS */
#include <unistd.h>
#else
#include <sun/fbio.h>
#include <sunwindow/cms.h>
#include <sunwindow/rect.h>
#include <sunwindow/win_screen.h>
#include <sunwindow/win_input.h>
#include <sunwindow/win_ioctl.h>
#endif

#ifndef FB_CLUTPOST
struct  fb_clut {
        unsigned int    flags;          /* flags                        */
        int             index;          /* CLUT id                      */
        int             offset;         /* offset within the CLUT       */
        int             count;          /* nbr of entries to be posted  */
        unsigned char   *red;           /* pointer to red table         */
        unsigned char   *green;         /* pointer to green table       */
        unsigned char   *blue;          /* pointer to blue table        */
};

#ifdef SVR4
#define FB_CLUTPOST	(('t' << 8) | 4)
#else
#define FB_CLUTPOST     _IOW(t,   4, struct fb_clut)
#endif
#endif


#include "cmap_grab.h"


/* externs */
#if 0
extern	char	*_SysErrorMsg[] ;
#endif

extern int _dga_winlockat(u_long cookie, int **lockp, int **unlockp);
extern int _dga_winlockdt(int *lockp, int *unlockp);

#define	GRABFILE	"/tmp/cm"
#define	NDIGITS		8

#define	MINSHMEMSIZE	(8*1024)
#define	CG_PAGESZ	(8*1024)

static	Dga_cmap	errret() ;
static	int		sunwindows_open();
static	void		sunwindows_close();

static	Dga_cmap	grabbed_list = NULL ;

static	int		cleanup_lockpages();

/*ARGSUSED*/
Dga_cmap
dga_cm_grab(fd, filesuffix)
int		fd ;
Dga_token	filesuffix ;
{
    Grabbedcmap	*infop, tmp ;
    _Dga_cmap	cmap_grab ;
    char		fn[sizeof(GRABFILE)+NDIGITS+1];
    char		*lock, *unlock ;
    int		lockfd,			/* lock device */
		devfd,			/* framebuffer */
		infofd,			/* shared memory file */
		sunwindows_def_fd,	/* /dev/winXX fd */
		sunwindows_inst_fd ;	/* /dev/winXX fd */
    int		locktype = 0 ;		/* use /dev/winlock */
    int		cflag = 0 ;		/* close devfd */
    u_long	cookie;
    int		cmaplen ;
    int		filelen ;
    int		ok;
    struct fbtype   fbd_type;

#ifdef MT
    mutex_lock(&dgaGlobalMutex);
#endif
    /* first and foremost, is this a nested grab? */
    for(cmap_grab = grabbed_list; cmap_grab != NULL;
		     cmap_grab = cmap_grab->cm_next) {
    	if (cmap_grab->cm_token == filesuffix) {
    		++cmap_grab->cm_grab_count;
#ifdef MT
		mutex_unlock(&dgaGlobalMutex);
#endif
    		return cmap_grab;
    	}
    }

    if((cmap_grab = (_Dga_cmap)calloc(1,sizeof(struct dga_cmap))) == NULL ) {
#ifdef MT
	    mutex_unlock(&dgaGlobalMutex);
#endif
	    return errret("malloc failed", fn) ;
    }

    cmap_grab->cm_infofd =
    cmap_grab->cm_devfd =
    cmap_grab->cm_lockfd =
    cmap_grab->cm_sunwindows_def_fd =
    cmap_grab->cm_sunwindows_inst_fd = -1 ;
    cmap_grab->cm_grab_count = 1 ;
    cmap_grab->cm_next = grabbed_list ;
    grabbed_list = cmap_grab ;

    /* open the file.  Read in enough data to find out long it is. */

    sprintf(fn, "%s%08x", GRABFILE, filesuffix) ;

    if ((infofd = open(fn,O_RDWR,0666))<0) {
	cleanup_lockpages(cmap_grab,0,0,0) ;
#ifdef MT
	mutex_unlock(&dgaGlobalMutex);
#endif
	return errret("can't open info page, ", fn) ;
    }

    if( read(infofd, &tmp, sizeof(tmp)) != sizeof(tmp) ) {
	(void) close(infofd) ;
	cleanup_lockpages(cmap_grab,0,0,0) ;
#ifdef MT
	mutex_unlock(&dgaGlobalMutex);
#endif
	return errret("info page too short", fn) ;
    }

    if( tmp.cm_magic != CMMAGIC ) {
	(void) close(infofd) ;
	cleanup_lockpages(cmap_grab,0,0,0) ;
#ifdef MT
	mutex_unlock(&dgaGlobalMutex);
#endif
	return errret("invalid magic number", fn) ;
    }

    cookie = tmp.cm_cookie;
    cmaplen = tmp.cm_reallen ;

    filelen = tmp.cm_shadowofs + 3*cmaplen*sizeof(short) + 4*cmaplen;

    /* map the colormap info area */

    infop = (Grabbedcmap *)mmap(0,
			filelen,
			PROT_READ|PROT_WRITE,
			MAP_SHARED,
			infofd,
			(off_t)0);

    /* we're all done with infofd now.  If the info file becomes
     * subject to growth later, we'll want to keep it open.
     */
    (void) close(infofd) ;
    cmap_grab->cm_infofd = -1 ;

    if (infop == (Grabbedcmap *)-1) {
	cleanup_lockpages(cmap_grab,0,filelen,0) ;
#ifdef MT
	mutex_unlock(&dgaGlobalMutex);
#endif
        return errret("can't map info page", fn) ;
    }
    cmap_grab->cm_info = (void *) infop ;


    /* Open framebuffer if not provided by caller */

    if( fd == -1 ) {
	devfd = open(infop->cm_devname, O_RDWR,0666) ;
	if( devfd < 0 ) {
#ifdef	DEBUG
	    fprintf(stderr, "wx_cm_grab: cannot open %s, %s\n",
		infop->cm_devname, strerror(errno)) ;
#endif	/* DEBUG */
	    cleanup_lockpages(cmap_grab,0,filelen,0) ;
#ifdef MT
	    mutex_unlock(&dgaGlobalMutex);
#endif
	    return NULL ;
	}
	cflag = 1 ;
    } else
	  devfd = fd ;

    cmap_grab->cm_devfd = devfd ;

    locktype = infop->cm_version >= 1 ? infop->cm_locktype : CM_LOCKDEV ;

    switch( locktype ) {
	case CM_LOCKDEV:
	    /* if lockdevice explicitly specified by server, we need to
	     * open it here, even if the calling routine has opened the
	     * framebuffer for us.  Otherwise, we use the framebuffer device.
	     */

	    if( infop->cm_lockdevname[0] != '\0' ) {
		lockfd = open(infop->cm_lockdevname, O_RDWR,0666) ;
		if( lockfd < 0 ) {
#ifdef	DEBUG
		    fprintf(stderr, "wx_cm_grab: cannot open %s, %s\n",
		    infop->cm_lockdevname, strerror(errno)) ;
#endif	/* DEBUG */
		    cleanup_lockpages(cmap_grab,cflag,filelen,locktype) ;
#ifdef MT
		    mutex_unlock(&dgaGlobalMutex);
#endif
		    return NULL ;
		}
	    } else
		lockfd = devfd ;

	    cmap_grab->cm_lockfd = lockfd ;

	    /* map the lock page */
	    lock = (char *)mmap(0, 
		    CM_PAGESZ, 
		    PROT_READ|PROT_WRITE, 
		    MAP_SHARED,
		    lockfd, (off_t)cookie);

	    if (lock == (char *)-1) {
		cleanup_lockpages(cmap_grab,cflag,filelen,0) ;
#ifdef MT
		mutex_unlock(&dgaGlobalMutex);
#endif
		return errret("can't map lock page",tmp.cm_devname) ;
	    }
	    cmap_grab->cm_lockp = (u_int *) lock ;

	    /* map the unlock page */
	    unlock = (char *) mmap(0,
		      CM_PAGESZ, 
		      PROT_READ|PROT_WRITE, 
		      MAP_SHARED,
		      lockfd,(off_t)cookie) ;

	    if( unlock == (char *)-1 ) {
		cleanup_lockpages(cmap_grab,cflag,filelen,0) ;
#ifdef MT
		mutex_unlock(&dgaGlobalMutex);
#endif
		return errret("can't map unlock page", tmp.cm_devname) ;
	    }
	    cmap_grab->cm_unlockp = (u_int *) unlock ;
        break ;
	case CM_WINLOCK:
	    if( _dga_winlockat(cookie, &lock, &unlock) != 0 ) {
	      cleanup_lockpages(cmap_grab,cflag,filelen,locktype) ;
#ifdef MT
	      mutex_unlock(&dgaGlobalMutex);
#endif
	      return errret("can't get lock pages", fn) ;
	    }
	    cmap_grab->cm_lockp = (u_int *) lock ;
	    cmap_grab->cm_unlockp = (u_int *) unlock ;
        break ;
    }

    /* fill in the misc stuff */

    sunwindows_def_fd = sunwindows_inst_fd = -1;
    ok = 1;
    if (infop->cm_default && (infop->cm_sunwindows_def_devname[0] != '\0')) {
	if ((sunwindows_def_fd = sunwindows_open(
			infop->cm_sunwindows_def_devname)) < 0)
	    ok = 0;
    }
    cmap_grab->cm_sunwindows_def_fd = sunwindows_def_fd ;
    if (ok && (infop->cm_sunwindows_inst_devname[0] != '\0')) {
        if ((sunwindows_inst_fd = sunwindows_open(
    		infop->cm_sunwindows_inst_devname)) < 0)
        ok = 0;
    }
    cmap_grab->cm_sunwindows_inst_fd = sunwindows_inst_fd ;

    if(!ok) {
        cleanup_lockpages(cmap_grab,cflag,filelen,locktype) ;
#ifdef MT
	mutex_unlock(&dgaGlobalMutex);
#endif
        return errret("/dev/winxx open failed", fn) ;
    }

    cmap_grab->cm_token = filesuffix;
    cmap_grab->cm_client = NULL ;
    cmap_grab->c_chngcnt = -1 ;	/* flag all changes as not seen */
    cmap_grab->s_chngcnt_p = (u_int *) &(infop->cm_count);
    cmap_grab->cm_lockcnt = 0 ;
    cmap_grab->cm_use_new_ioctl = 1 ;	/* try to use new ioctl */

#ifdef MT
    mutex_unlock(&dgaGlobalMutex);
#endif
    return((Dga_cmap)cmap_grab);
}


static int
cleanup_lockpages(cginfo,cflag,filelen,locktype)
    _Dga_cmap	cginfo ;
    int		cflag, filelen, locktype ;
{
    int	error = 0 ;
    _Dga_cmap	p1, p2 ;

    if (cginfo->cm_sunwindows_def_fd >= 0)
        sunwindows_close(cginfo->cm_sunwindows_def_fd);
    if (cginfo->cm_sunwindows_inst_fd >= 0)
        sunwindows_close(cginfo->cm_sunwindows_inst_fd);
    switch( locktype ) {
	case CM_LOCKDEV:
	    if( cginfo->cm_unlockp != NULL )
	      error |= munmap((char *)cginfo->cm_unlockp,CM_PAGESZ) < 0 ;
	    if( cginfo->cm_lockp != NULL )
	      error |= munmap((char *)cginfo->cm_lockp,CM_PAGESZ) < 0 ;
	    if( cginfo->cm_lockfd != -1 &&
		cginfo->cm_lockfd != cginfo->cm_devfd )
	      error |= close(cginfo->cm_lockfd) ;
	break ;
	case CM_WINLOCK:
	   error |= _dga_winlockdt(cginfo->cm_lockp, cginfo->cm_unlockp) < 0 ;
	break ;
    }
    if( cginfo->cm_info != NULL )
	error |= munmap(cginfo->cm_info, filelen) < 0 ;
    if( cginfo->cm_infofd != -1 )
	    error |= close(cginfo->cm_infofd) < 0 ;
    if( cflag )
	  error |= close(cginfo->cm_devfd) < 0 ;

    for( p1 = grabbed_list, p2 = NULL ; p1 != NULL  &&  p1 != cginfo;
			p2 = p1, p1=p1->cm_next) ;
    if( p1 == NULL )		/* not found?? */
	error = 1 ;
    else {
	if( p2 == NULL )
	    grabbed_list = p1->cm_next ;
	  else
	    p2->cm_next = p1->cm_next ;
    }
    free(cginfo);
    return error ? -1 : 0 ;
}

void
dga_cm_ungrab(cginfoi, cflag)
Dga_cmap	cginfoi ;
int		cflag ;
{
    _Dga_cmap cginfo = (struct dga_cmap *)cginfoi;
    Grabbedcmap  *infop = (Grabbedcmap *) cginfo->cm_info ;
    int	cmaplen, filelen ;
    int	locktype ;

#ifdef MT
    mutex_lock(&dgaGlobalMutex);
#endif
    if( --cginfo->cm_grab_count > 0 ) {
#ifdef MT
	  mutex_unlock(&dgaGlobalMutex);
#endif
	  return;
    }

    cmaplen = infop->cm_reallen ;
    filelen = infop->cm_shadowofs + 3*cmaplen*sizeof(short) + 4*cmaplen ;

    cleanup_lockpages(cginfo,cflag,filelen,
	      infop->cm_version >= 1 ? infop->cm_locktype : CM_LOCKDEV ) ;
#ifdef MT
    mutex_unlock(&dgaGlobalMutex);
#endif
}

static Dga_cmap
errret(msg,file)
char	*msg, *file ;
{
#ifdef	DEBUG
	fprintf(stderr, "colormap-grab: %s: %s, %s\n",
		msg, file, strerror(errno)) ;
#endif	/* DEBUG */
	return NULL ;
}



/*
 * Read colormap from shared memory.
 * Shared memory should always be in sync
 * with server's idea of this X11 colormap's
 * contents.
 */

void
dga_cm_get(cginfoi,index,count, red,green,blue)
Dga_cmap	cginfoi ;
int		index, count ;
u_char		*red, *green, *blue ;
{
    _Dga_cmap cginfo = (struct dga_cmap *)cginfoi;
    Grabbedcmap	*infop = (Grabbedcmap *) cginfo->cm_info ;

#ifdef MT
    mutex_lock(&dgaGlobalMutex);
#endif
    wx_cm_lock(cginfo) ;

    /* REMIND: Currently, we don't need to test for any changes in the
     * shared memory page; but if we did, right here is where we'd do
     * it.  We compare infop->cm_count against cginfo->c_chngcnt and
     * react if there was a difference.  After handling the change,
     * we'd copy infop->cm_count to cginfo->c_chngcnt.
     */

    if( index+count > infop->cm_reallen )
      count = infop->cm_reallen - index ;

    if( count > 0 ) {

	/* copy from shared memory */
	short *shadow ;
	short *ip ;
	u_char *op ;
	int n ;

	shadow = (short *) ( (u_char *)infop + infop->cm_shadowofs ) ;
	shadow += index ;
	for(n=count, op=red, ip=shadow ; --n >= 0 ; *op++ = *ip++>>8 ) ;
	shadow += infop->cm_reallen ;
	for(n=count, op=green, ip=shadow ; --n >= 0 ; *op++ = *ip++>>8 ) ;
	shadow += infop->cm_reallen ;
	for(n=count, op=blue, ip=shadow ; --n >= 0 ; *op++ = *ip++>>8 ) ;
    }
    wx_cm_unlock(cginfo) ;
#ifdef MT
    mutex_unlock(&dgaGlobalMutex);
#endif
}
/* This is the new interface that will be publicly exposed */
void
dga_cm_read(cginfo,index,count, red,green,blue)
Dga_cmap cginfo;
int             index, count ;
u_char          *red, *green, *blue ;
{
    dga_cm_get(cginfo,index,count, red,green,blue);
}


/* write colormap to shared memory, and to DACS if appropriate. */

void
dga_cm_put(cginfoi,index,count, red,green,blue)
Dga_cmap cginfoi ;
int index, count ;
u_char *red, *green, *blue ;
{
    _Dga_cmap cginfo = (struct dga_cmap *)cginfoi;
    Grabbedcmap	*infop = (Grabbedcmap *) cginfo->cm_info ;
    short		*shadow ;
    u_char		*ip ;		/* in pointer */
    short		*op ;		/* out pointer */
    u_char		*fp ;		/* flag pointer */
    int		n ;

#ifdef MT
    mutex_lock(&dgaGlobalMutex);
#endif
    wx_cm_lock(cginfo) ;

    if( index+count > infop->cm_reallen )
	count = infop->cm_reallen - index ;   
    if( count > 0 ) {
	if( index < infop->cm_start )
	    infop->cm_start = index ;
	if( index + count > infop->cm_start + infop->cm_len )
	    infop->cm_len = index + count -infop->cm_start ;

	/* copy to shared memory shadow of grabbed color map */
	/* "stutter" the 8-bit values into 16 bits */

	shadow = (short *) ( (u_char *)infop + infop->cm_shadowofs ) ;
	shadow += index ;
	for(n=count, ip=red, op=shadow ; --n >= 0 ; *op++, *ip++)
		*op = *ip | (*ip << 8);
	shadow += infop->cm_reallen ;
	for(n=count, ip=green, op=shadow ; --n >= 0 ; *op++, *ip++)
		*op = *ip | (*ip << 8);
	shadow += infop->cm_reallen ;
	for(n=count, ip=blue, op=shadow ; --n >= 0 ; *op++, *ip++)
		*op = *ip | (*ip << 8);
	fp = (u_char *)infop + infop->cm_shadowofs +
		3 * infop->cm_reallen * sizeof(short) + index ;
	for(n=count ; --n >= 0 ; *fp++ = 1 ) ;

	if( infop->cm_installed ) {
#ifdef COMMENT
	    /* We no longer need the hw shadow cmap but have to keep
	     * the structures the same size and so we will just stop
	     * updating it
	     */
	    u_char *hwshadow;
	
	    /* copy to shared memory shadow of hardware color map */
	    hwshadow = (u_char *)infop + infop->cm_shadowofs +
		3 * infop->cm_reallen * sizeof(short) +
		infop->cm_reallen + index;
	    for (n=count, ip=red, fp=hwshadow; --n >= 0; *fp++ = *ip++);
	    hwshadow += infop->cm_reallen;
	    for (n=count, ip=green, fp=hwshadow; --n >= 0; *fp++ = *ip++);
	    hwshadow += infop->cm_reallen;
	    for (n=count, ip=blue, fp=hwshadow; --n >= 0; *fp++ = *ip++);
	    hwshadow -= index + 2 * infop->cm_reallen;
#endif
	    switch(infop->cm_load_method) {

#ifdef SUNWINDOWS 
		/* Note from V4 on, SUnwindows include files, support no
		 * longer exist. I am commenting out the code instead of
		 * removing it only for completeness. After Mars, time to
		 * move away from sunwindows....
		*/
		case SUNWINDOWS_IOCTL:
		{
		    struct cmschange cmschange;

		    if( cginfo->cm_sunwindows_def_fd >= 0 ) {
			strcpy(cmschange.cc_cms.cms_name,
			    infop->cm_sunwindows_def_cmapname);
			cmschange.cc_cms.cms_addr = 0;
			cmschange.cc_cms.cms_size =
			    infop->cm_sunwindows_def_cmapsize;
			cmschange.cc_map.cm_red = hwshadow;
			cmschange.cc_map.cm_green = 
			    hwshadow + infop->cm_reallen;
			cmschange.cc_map.cm_blue = 
			hwshadow + 2 * infop->cm_reallen;

		    /* HACK ALERT */
		    /* Adjust SUNWINDOWS cms segment 1st colr != last color */
		    if ((cmschange.cc_map.cm_red[0] ==
		      cmschange.cc_map.cm_red[cmschange.cc_cms.cms_size-1]) &&
		      (cmschange.cc_map.cm_green[0] ==
		      cmschange.cc_map.cm_green[cmschange.cc_cms.cms_size-1]) &&
		      (cmschange.cc_map.cm_blue[0] ==
		      cmschange.cc_map.cm_blue[cmschange.cc_cms.cms_size-1]))
			{
			if (cmschange.cc_map.cm_blue[0] > 0)
			    cmschange.cc_map.cm_blue[0]--;
			else
			    cmschange.cc_map.cm_blue[0] = 1;
			}

		    ioctl(cginfo->cm_sunwindows_def_fd, WINSETCMS, &cmschange);
		    }

		if( cginfo->cm_sunwindows_inst_fd >= 0 )
		    {
		    strcpy(cmschange.cc_cms.cms_name,
			infop->cm_sunwindows_inst_cmapname);
		    cmschange.cc_cms.cms_addr = 0;
		    cmschange.cc_cms.cms_size =
			infop->cm_sunwindows_inst_cmapsize;
		    cmschange.cc_map.cm_red = hwshadow;
		    cmschange.cc_map.cm_green = hwshadow + infop->cm_reallen;
		    cmschange.cc_map.cm_blue = 
			hwshadow + 2 * infop->cm_reallen;

		    /* HACK ALERT */
		    /* Adjust SUNWINDOWS cms segment 1st colr != last color */
		    if ((cmschange.cc_map.cm_red[0] ==
		      cmschange.cc_map.cm_red[cmschange.cc_cms.cms_size-1]) &&
		      (cmschange.cc_map.cm_green[0] ==
		      cmschange.cc_map.cm_green[cmschange.cc_cms.cms_size-1]) &&
		      (cmschange.cc_map.cm_blue[0] ==
		      cmschange.cc_map.cm_blue[cmschange.cc_cms.cms_size-1])) {
			if (cmschange.cc_map.cm_blue[0] > 0)
			    cmschange.cc_map.cm_blue[0]--;
			else
			    cmschange.cc_map.cm_blue[0] = 1;
		    }

		    ioctl(cginfo->cm_sunwindows_inst_fd,WINSETCMS,&cmschange);
		}
	    }
	    break;
#endif /* SUNWINDOWS */
	    case HW_DEVICE_DIRECT:	/* could have device-specific
					   routines here; just fall
					   through to device ioctl
					   for now */

	    case HW_DEVICE_IOCTL:
	    default:
#ifdef	FBIOPUTCMAPI
		if( cginfo->cm_use_new_ioctl) {
		    /* try new ioctl */
		    struct fbcmap_i cmap;

		    cmap.flags = 0 ;
		    cmap.id = infop->cm_cmapnum ;
		    cmap.index = infop->cm_ioctlbits | index;
		    cmap.count = count;
		    cmap.red   = red;
		    cmap.green = green;
		    cmap.blue  = blue;
		    if( ioctl(cginfo->cm_devfd, FBIOPUTCMAPI, &cmap) == 0 )
		    break;
		}
#endif	/* FBIOPUTCMAPI */

		if( cginfo->cm_use_new_ioctl ) {	
		    /* try FB_CLUTPOST ioctl */
		    struct fb_clut cmap;

		    cmap.flags = 0 ;
		    cmap.index = infop->cm_cmapnum;
		    cmap.offset = index;
		    cmap.count = count;
		    cmap.red   = red;
		    cmap.green = green;
		    cmap.blue  = blue;
		    if( ioctl(cginfo->cm_devfd, FB_CLUTPOST, &cmap) == 0 )
			break;
		
		    cginfo->cm_use_new_ioctl = 0;
               }

	       if (cginfo->cm_use_new_ioctl == 0) {
		    struct fbcmap cmap;

		    cmap.index = infop->cm_ioctlbits | index;
		    cmap.count = count;
		    cmap.red   = red;
		    cmap.green = green;
		    cmap.blue  = blue;
		    ioctl(cginfo->cm_devfd, FBIOPUTCMAP, &cmap);
		}
		break;
	    }
	}

	/* We've changed the shared memory page, flag this fact to
	* the server and to any other clients
	*/
	cginfo->c_chngcnt = ++infop->cm_count ;
    }
    wx_cm_unlock(cginfo) ;
#ifdef MT
    mutex_unlock(&dgaGlobalMutex);
#endif
}

/* This is the interfce that will be publicly exposed */
void
dga_cm_write(cginfoi,index,count, red,green,blue, putfunc)
Dga_cmap   cginfoi ;
int     index, count ;
u_char  *red, *green, *blue ;
void		(*putfunc)();
{
    _Dga_cmap cginfo = (struct dga_cmap *)cginfoi;
    Grabbedcmap    *infop = (Grabbedcmap *) cginfo->cm_info ;
    short       *shadow ;
    u_char     *ip ;       /* in pointer */
    short      *op ;       /* out pointer */
    u_char     *fp ;       /* flag pointer */
    int        n ;
 
#ifdef MT
    mutex_lock(&dgaGlobalMutex);
#endif
    wx_cm_lock(cginfo) ;
 
    if( index+count > infop->cm_reallen )
      count = infop->cm_reallen - index ;
 
    if( count > 0 ) {
	if( index < infop->cm_start )
	    infop->cm_start = index ;
 
	if( index + count > infop->cm_start + infop->cm_len )
	    infop->cm_len = index + count -infop->cm_start ;
 
	/* copy to shared memory shadow of grabbed color map */
	/* "stutter" the 8-bit values into 16 bits */
 
	shadow = (short *) ( (u_char *)infop + infop->cm_shadowofs ) ;
	shadow += index ;
	for(n=count, ip=red, op=shadow ; --n >= 0 ; *op++, *ip++)
	    *op = *ip | (*ip << 8);
	shadow += infop->cm_reallen ;
	for(n=count, ip=green, op=shadow ; --n >= 0 ; *op++, *ip++)
	    *op = *ip | (*ip << 8);
	shadow += infop->cm_reallen ;
	for(n=count, ip=blue, op=shadow ; --n >= 0 ; *op++, *ip++)
	    *op = *ip | (*ip << 8);
	fp = (u_char *)infop + infop->cm_shadowofs +
	    3 * infop->cm_reallen * sizeof(short) + index ;
	for(n=count ; --n >= 0 ; *fp++ = 1 ) ;
        
	if( infop->cm_installed ) {
#ifdef COMMENT
	    /* We dont need to update thie any more */
	    u_char *hwshadow;
 
	    /* copy to shared memory shadow of hardware color map */
	    hwshadow = (u_char *)infop + infop->cm_shadowofs +
		3 * infop->cm_reallen * sizeof(short) +
	    infop->cm_reallen + index;
	    for (n=count, ip=red, fp=hwshadow; --n >= 0; *fp++ = *ip++);
	    hwshadow += infop->cm_reallen;
	    for (n=count, ip=green, fp=hwshadow; --n >= 0; *fp++ = *ip++);
	    hwshadow += infop->cm_reallen;
	    for (n=count, ip=blue, fp=hwshadow; --n >= 0; *fp++ = *ip++);
	    hwshadow -= index + 2 * infop->cm_reallen;
 
	    for (n=count, ip=blue, fp=hwshadow; --n >= 0; *fp++ = *ip++);
	    hwshadow -= index + 2 * infop->cm_reallen;
#endif
	    /* Now call the user supplied function for actually
	    * posting the cmap
	    */
	    (*putfunc)( cginfo, index, count, red, green, blue);
      }
         
      /* We've changed the shared memory page, flag this fact to
       * the server and to any other clients
       */
 
      cginfo->c_chngcnt = ++infop->cm_count ;
    }
    wx_cm_unlock(cginfo) ;
#ifdef MT
    mutex_unlock(&dgaGlobalMutex);
#endif
}    

/* Interfaces that will be exposed to the public */
int
dga_cm_devdfd(cginfoi)
Dga_cmap   cginfoi ;
{

    return (((struct dga_cmap *)cginfoi)->cm_devfd);
}

/* This device info is shared between the server and client */
void *
dga_cm_devinfo(cginfoi)
Dga_cmap   cginfoi ;
{
    _Dga_cmap cginfo = (struct dga_cmap *)cginfoi;
    return ((void *)(((char *)cginfo->cm_info) 
			+ CM_INFOP(cginfo)->device_offset));

}

/* Each client may store private info in the client info ptr */
void
dga_cm_set_client_infop(cginfoi, client_info_ptr)
Dga_cmap cginfoi;
void* client_info_ptr;
{
    _Dga_cmap cginfo = (struct dga_cmap *)cginfoi;
    
    cginfo->cm_client = client_info_ptr;
}

void *
dga_cm_get_client_infop(cginfoi) 
Dga_cmap cginfoi; 
{
    _Dga_cmap cginfo = (struct dga_cmap *)cginfoi;
        
    return ((void *)cginfo->cm_client); 
} 

static Cmap_Devlist *devlist = NULL;

static	int
sunwindows_open(devname)
char *devname;
{
	Cmap_Devlist *dlist = devlist;
	int fd;

	while (dlist)
		{
		if (strcmp(devname, dlist->devname) == 0)
			{
			dlist->refcnt++;
			return(dlist->fd);
			}
		dlist = dlist->next;
		}
	if ((fd = open(devname, O_RDWR, 0666)) < 0)
		{
#ifdef	DEBUG
		fprintf(stderr, "colormap-grab: open failed %s, %s\n",
		    devname, strerror(errno)) ;
#endif	/* DEBUG */
		return(-1);
		}
	if((dlist = (Cmap_Devlist *)malloc(sizeof(Cmap_Devlist))) == NULL )
		{
		close(fd);
#ifdef	DEBUG
		fprintf(stderr, "colormap-grab: malloc failed %s, %s\n",
		    devname, strerror(errno)) ;
#endif	/* DEBUG */
		return(-1);
		}
	dlist->next = devlist;
	devlist = dlist;
	dlist->refcnt = 1;
	dlist->fd = fd;
	strcpy(dlist->devname, devname);
	return(fd);
}


static	void
sunwindows_close(fd)
int fd;
{
	Cmap_Devlist *dlist = devlist;
	Cmap_Devlist **dlptr = &devlist;

	while (dlist)
		{
		if (fd == dlist->fd)
			{
			if (--dlist->refcnt == 0)
				{
				close(fd);
				*dlptr = dlist->next;
				free(dlist);
				}
			return;
			}
		dlptr = &dlist->next;
		dlist = dlist->next;
		}
}






/* /dev/winlock management code.  This is a temporary hack so we can
 * develop the new /dev/winlock interface.  Hopefully, this will become
 * part of the C library someday.
 *
 */


#include <sys/ioccom.h>

	/* structure for allocating lock contexts.  The identification
	 * should be provided as the offset for mmap(2).  The offset is
	 * the byte-offset relative to the start of the page returned
	 * by mmap(2).
	 */

struct	winlockalloc {
	u_long	sy_key ;	/* user-provided key, if any */
	u_long	sy_ident ;	/* system-provided identification */
	} ;

struct	winlocktimeout {
	u_long	sy_ident ;
	u_int	sy_timeout ;
	int	sy_flags ;
	} ;


#ifndef WINLOCKALLOC
#ifdef	SVR4
#define WIOC    ('L'<<8)
#define WINLOCKALLOC            (WIOC|0)
#define WINLOCKFREE             (WIOC|1)
#define WINLOCKSETTIMEOUT       (WIOC|2)
#define WINLOCKGETTIMEOUT       (WIOC|3)
#define WINLOCKDUMP             (WIOC|4)
#else	/* SVR4 */
#define	WINLOCKALLOC	_IOWR(L, 0, struct winlockalloc)
#define	WINLOCKFREE	_IOW(L, 1, u_long)
#define	WINLOCKSETTIMEOUT	_IOW(L, 2, struct winlocktimeout)
#define	WINLOCKGETTIMEOUT	_IOWR(L, 3, struct winlocktimeout)
#define	WINLOCKDUMP	_IO(L, 4)
#endif	/* SVR4 */
#endif	/* WINLOCKALLOC */


/* flag bits */
#define SY_NOTIMEOUT	0x1	/* This client never times out */


static	char	*lockdev_name = "/dev/winlock" ;
static	char	*alt_lockdev_name = "/dev/cgsix0" ;

static	int	lock_fd = -1 ;
static	u_long	pagemask ;
static	u_long	pageoffset ;
static	u_long	pagesize ;



	/* return non-zero if fail */

static int
init()
{
	if( lock_fd == -1 ) {
	  if( (lock_fd = open(lockdev_name, O_RDWR, 0)) == -1  &&
	      (lock_fd = open(alt_lockdev_name, O_RDWR, 0)) == -1 )
	    return 1 ;
#ifdef SVR4
	pagesize = sysconf(_SC_PAGESIZE);
#else
	  pagesize = getpagesize() ;
#endif
	  pageoffset = pagesize - 1 ;
	  pagemask = ~pageoffset ;
	}

	return 0 ;
}


int
_dga_winlockat(cookie, lockp, unlockp)
	u_long	cookie ;
	int	**lockp, **unlockp ;
{
	u_long	ofs ;
	caddr_t	ptr ;

	if( lock_fd == -1  &&  init() )
	{
	  errno = EINVAL ;
	  return -1 ;
	}

	ofs = cookie & pageoffset ;
	cookie &= pagemask ;

	ptr = mmap(0, pagesize, PROT_READ|PROT_WRITE,
		MAP_SHARED, lock_fd, (off_t)cookie) ;
	if( (int)ptr == -1 )
	{
	  errno = ENOSPC ;
	  return -1 ;
	}
	*lockp = (int *) (ptr+ofs) ;

	ptr = mmap(0, pagesize, PROT_READ|PROT_WRITE,
		MAP_SHARED, lock_fd, (off_t)cookie) ;
	if( (int)ptr == -1 )
	{
	  (void) munmap((caddr_t)*lockp, pagesize) ;
	  errno = ENOSPC ;
	  return -1 ;
	}
	*unlockp = (int *) (ptr+ofs) ;

	return 0 ;
}



int
_dga_winlockdt(lockp, unlockp)
	int	*lockp, *unlockp ;
{
	caddr_t	ptr ;

	if( lock_fd == -1  &&  init() )
	{
	  errno = EINVAL ;
	  return -1 ;
	}

	ptr = (caddr_t) ((long)lockp & pagemask) ;
	if( munmap(ptr, pagesize) )
	    perror("_dga_winlockdt: munmap:");

        ptr = (caddr_t) ((long)unlockp & pagemask) ;
        if( munmap(ptr, pagesize) )
            perror("_dga_winlockdt: munmap:");

	return 0 ;
}