static char *SccsId = "@(#)setcmap.c 4.2 (TU-Delft) 02/12/91";
/**********************************************************

Name/Version      : cacdcmap/4.2

Language          : C
Operating system  : BSD4.2 | BSD4.3 | SYS5
Host machine      : HP9000s[358]00 | GOULD PN6000 | APOLLO DN[34]000

Author(s)         : P. Bingley
Creation date     : 18-Aug-1988
Modified by       : P. Bingley
Modification date : 09-Jan-1990


        Delft University of Technology
        Department of Electrical Engineering
        Network Theory Section
        Mekelweg 4 - P.O.Box 5031
        2600 GA DELFT
        The Netherlands

        Phone : 015 - 781708

        COPYRIGHT (C) 1988-1989, All rights reserved
**********************************************************/

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include "cacdcmap.h"

/* External Declarations */

/* Global Declarations */
int	set_cacd_cmap();

/* Local Declarations */
static	int	print_cacd_cmap();
static	int	remove_cacd_cmap();
static	int	create_cacd_cmap();

/*********************************************************/


int set_cacd_cmap(dpy, s_nr, ctab, planes, nplanes, print,
		     remove, force, silent, verbose)
Display	*dpy;
int	s_nr;
float	**ctab;
int	*planes;
int	nplanes;
int	print;
int	remove;
int	force;
int	silent;
int	verbose;
{
    Atom		atom;
    XStandardColormap	scmap;

    /* if the cacd colormap exists */
    if((atom = XInternAtom(dpy, RGB_CACD_MAP, True)) &&
	XGetStandardColormap(dpy, RootWindow(dpy, s_nr), &scmap, atom)) {

	/* replace ? */
	if(force) {
	    /* first remove old colormap */
	    if(!remove_cacd_cmap(dpy, s_nr, &scmap, silent, atom)) return(0);

	    /* then create new colormap */
	    if(!create_cacd_cmap(dpy, s_nr, ctab, planes,
	    		nplanes, silent, verbose, atom)) {
		return(0);
	    }

	    /* get new scmap ? */
	    if(print || remove) {
		XGetStandardColormap(dpy, RootWindow(dpy, s_nr), &scmap, atom);
	    }
	}

	/* print ? */
	if(print && !print_cacd_cmap(dpy, &scmap)) return(0);

	/* remove ? */
	if(remove && !remove_cacd_cmap(dpy, s_nr, &scmap, silent, atom)) {
	    return(0);
	}

	if(verbose && !force && !print && !remove) {
	    fprintf(stderr, "setcmap: Cacd cmap already present\n");
	}

	/* all went well */
	return(1);
    }
    else {	/* the cacd colormap does not exist */
	if(remove) {
	    if(!silent) {
		fprintf(stderr, "setcmap: No cacd cmap present\n");
	    }
	    return(1);
	}

	/* create atom if necessary */
	if(!atom && (atom = XInternAtom(dpy, RGB_CACD_MAP, False)) == 0) {
	    if(!silent) {
		fprintf(stderr, "setcmap: Cannot create atom\n");
	    }
	    return(0);
	}

	/* create new colormap */
	if(!print || force) {
	    if(!create_cacd_cmap(dpy, s_nr, ctab, planes,
			nplanes, silent, verbose, atom)) {
		return(0);
	    }
	}

	/* print ? */
	if(print) {
	    if(force) {
		XGetStandardColormap(dpy, RootWindow(dpy, s_nr), &scmap, atom);
		if(!print_cacd_cmap(dpy, &scmap)) return(0);
	    }
	    else {
		if(!silent) {
		    fprintf(stderr, "setcmap: No cacd cmap present\n");
		}
		return(0);
	    }
	}

	/* all went well */
	return(1);
    }
}


/* convert virtual cmap indices ('ind') to real pixels values */
static unsigned long int_to_pixel(ind, mask, base)
register unsigned long	ind;
register unsigned long	mask;
unsigned long		base;
{
    register unsigned long	pixel = 0;
    register unsigned long	real_ind = 1;

    while(ind) {
	while(!(mask & real_ind)) real_ind <<= 1;

	if(ind & 0x1) pixel |= real_ind;

	ind >>= 1;
	real_ind <<= 1;
    }
    return(pixel | base);
}

static int print_cacd_cmap(dpy, scmap)
Display			*dpy;
XStandardColormap	*scmap;
{
    unsigned long	i;
    unsigned long	ncolors, basesh, planes, planemask;
    unsigned long	basep;
    Colormap		cmap;
    XColor		col;

    cmap = scmap->colormap;
    basep = scmap->base_pixel;
    basesh = scmap->red_mult;
    planes = scmap->blue_mult;
    planemask = scmap->blue_max;
    ncolors = 1 << planes;

    /* print these figures */
    printf("#planes = %d, mask = 0x%x, #colors = %u, basepix = %u\n",
		planes, planemask, ncolors, basep);
    printf("    contig = %d, baseshift = %u, Xid = 0x%x\n",
		scmap->red_max, basesh, scmap->green_mult);

    /* retrieve all colors from the colormap */
    for(i = 0; i < ncolors; ++i) {
	col.pixel = int_to_pixel(i, planemask, basep);

	XQueryColor(dpy, cmap, &col);

	/* print the result */
	printf("pixel = %3u, red = %f, green = %f, blue = %f\n",
	    col.pixel, (float) (col.red / 65535.0),
	    (float) (col.green / 65535.0), (float) (col.blue / 65535.0));
    }

    return(1);
}

static int remove_cacd_cmap(dpy, s_nr, scmap, silent, atom)
Display			*dpy;
int			s_nr;
XStandardColormap	*scmap;
int			silent;
Atom			atom;
{
/* free colormap and the colors in the colormap */

    /* Would like to do this but we are not allowed to */
    /* since we are not owner of the resources. Therefore */
    /* we created the (unused) Xid so we can 'kill' the */
    /* 'resources' which we are allowed */
    /*
    unsigned long planes = (((1 << scmap->blue_mult) - 1) << scmap->red_mult);
    XFreeColors(dpy, scmap->colormap, &scmap->base_pixel, 1, planes);
    */

/* use XKILLWORKAROUND when you cannot 'kill the resources' */
/* of a client by using the Xid */
#ifdef XKILLWORKAROUND
    XKillClient(dpy, AllTemporary);
#else
    if(scmap->green_mult) {
	XKillClient(dpy, (XID) scmap->green_mult);
    }
    else if(!silent) {
	fprintf(stderr, "setcmap: Warning: Cannot free resources\n");
    }
#endif

/* now remove the standard cacd colormap */

    /* grab the server to make this an atomair operation */
    XGrabServer(dpy);

    /* remove the standard colormap */
    XDeleteProperty(dpy, RootWindow(dpy, s_nr), atom);

    /* ungrab the server */
    XUngrabServer(dpy);

    /* sync X (to make sure that the cacd colormap is removed) */
    XSync(dpy, 0);

    /* tell user about it */
    if(!silent) fprintf(stderr, "setcmap: cacd colormap removed\n");

    return(1);
}

static int create_cacd_cmap(dpy, s_nr, ctab, nplanesp,
			nplanes, silent, verbose, atom)
Display	*dpy;
int	s_nr;
float	**ctab;
int	*nplanesp;
int	nplanes;
int	silent;
int	verbose;
Atom	atom;
{
    register int	i, j, last, basesh;
    float		*cacdcmap;
    Colormap		cmap;
    unsigned long	low, basep, planes[MAXPLANES];
    unsigned long	planemask = 0;
    int			contig = 1;
    Pixmap		pixmap;
    XColor		col;
    XStandardColormap	scmap;
    int			apollo_14 = 0;

    /* get the colormap to allocate from */
    cmap = DefaultColormap(dpy, s_nr);

    /* grab the server to make this an atomair operation */
    XGrabServer(dpy);

    /* now create pixmap as XID to identify colorcells for remove */
    if((pixmap = XCreatePixmap(dpy, RootWindow(dpy, s_nr), (unsigned) 1,
		(unsigned) 1, (unsigned) DefaultDepth(dpy, s_nr))) == None) {
	if(!silent) {
	    fprintf(stderr, "setcmap: Warning: Cannot create pixmap Id\n");
	}
    }
    else if(verbose) {
	fprintf(stderr, "setcmap: pixmap Id created (0x%x)\n", pixmap);
    }

    /* try to allocate the largest amount of planes */
    /* if this is an apollo; forget it, since even trying it */
    /* (which results in failure) causes the Xapollo server */
    /* to go into '8 color' mode */
    if(DisplayCells(dpy, s_nr) != 14) {		/* it's not an apollo */
	for(i = 0; i < nplanes; ++i) {
	    if(!XAllocColorCells(dpy, cmap, contig, planes,
				nplanesp[i], &basep, 1)) {
		if(!silent) {
		    fprintf(stderr, "setcmap: Cannot allocate %d bitplanes\n",
				nplanesp[i]);
		}
	    }
	    else {
		if(verbose) {
		    fprintf(stderr, "setcmap: allocated %d planes\n",
				nplanesp[i]);
		}
		break;
	    }
	}
    }
    else {	/* it's an apollo (at least something with 14 colors) */
	/* check if we can create 3-bitplane apollo colormap */
	/* and if the caller wants us to */
	if(nplanesp[nplanes-1] < DisplayPlanes(dpy, s_nr) &&
	    create_apollo_14_cmap(dpy, s_nr, ctab[nplanes - 1],
				silent, verbose, atom)) {
	/* set some values for this colormap */
	    i = nplanes - 1;
	    apollo_14 = 1;
	    basep = 0;
	    basesh = 0;
	    nplanes = 3;
	    contig = 0;
	    planemask = 0x0d;
	}
	else {
	    i = nplanes;
	    if(!silent) {
		fprintf(stderr, "setcmap: Cannot allocate %d bitplanes\n",
			    nplanesp[nplanes-1]);
	    }
	}
    }

    /* if all allocations failed */
    if(i >= nplanes) {
	/* remove pixmap ID */
	if(pixmap) XFreePixmap(dpy, pixmap);

	/* ungrab the server !! */
	XUngrabServer(dpy);

	if(verbose) fprintf(stderr, "setcmap: Removed pixmap Id\n");

	return(0);
    }

    if(!apollo_14) {
	nplanes = nplanesp[i];
	cacdcmap = ctab[i];

	/* find lowest plane and build planemask */
	for(low = ~0, i = 0; i < nplanes; ++i) {
	    low = ((low < planes[i]) ? low : planes[i]);
	    planemask |= planes[i];
	}

	if(verbose) fprintf(stderr, "planes = %d, planemask = 0x%x, low = %u, ",
				nplanes, planemask, low);

	/* calculate shift from low */
	for(basesh = -1; low; ++basesh) low >>= 1;

	if(verbose) {
	    fprintf(stderr, "baseshift = %d, basepixel = %u\n", basesh, basep);
	}

	/* set last color and color flags */
	last = 1 << nplanes;
	col.flags = (char) (DoRed | DoGreen | DoBlue);
	    
	/* store all colors in the colormap */
	for(i = 0, j = -1; i < last; ++i) {
	    col.pixel = (unsigned long) ((i << basesh) | basep);
	    col.red   = (unsigned short) (cacdcmap[++j] * 65535);
	    col.green = (unsigned short) (cacdcmap[++j] * 65535);
	    col.blue  = (unsigned short) (cacdcmap[++j] * 65535);
	    XStoreColor (dpy, cmap, &col);
	}
    }

    /* now set the standard colormap information */
    scmap.colormap   = cmap;
    scmap.red_max    = (unsigned long) contig;
    scmap.red_mult   = (unsigned long) basesh;
    scmap.green_max  = (unsigned long) 1;
    scmap.green_mult = (unsigned long) pixmap;
    scmap.blue_max   = planemask;
    scmap.blue_mult  = (unsigned long) nplanes;
    scmap.base_pixel = basep;

    /* set the standard colormap */
    XSetStandardColormap(dpy, RootWindow(dpy, s_nr), &scmap, atom);
    
    if(!silent && verbose) {
	fprintf(stderr, "setcmap: cacd colormap initialized\n");
    }

    /* do not destroy allocated colormap on exit */
#ifdef XKILLWORKAROUND
    XSetCloseDownMode(dpy, RetainTemporary);
#else
    XSetCloseDownMode(dpy, RetainPermanent);
#endif

    /* sync X (to make sure that the cacd colormap arrives in the server) */
    XSync(dpy, 0);

    /* ungrab the server */
    XUngrabServer(dpy);

    /* all is well */
    return(1);
}

/* X rgb on apollo (4-planes) ranges from 0 to 65535 in 4096 increments */
#define	RGB_MIN	4000
#define RGB_MAX	61000

static unsigned long	app14_pixs[8] = { 0, 1,  4, 5,  8, 9,  12, 13 };

static int create_apollo_14_cmap(dpy, s_nr, cacdcmap,
			silent, verbose, atom)
Display	*dpy;
int	s_nr;
float	*cacdcmap;
int	silent;
int	verbose;
Atom	atom;
{
    int		i, j;
    XColor	col;
    Colormap	cmap;
    int		cmask = DoRed | DoGreen | DoBlue;
    FILE	*ps;
    int		mixed_mode = 0;
    char	ps_line[128];	/* should be enough */

    /* Check if this baby is running mixed mode dm-Xapollo */
    /* If it is, setcmap will not fail on the checks it does on the colour table */
    /* It will not even try allocating new colours */
    /* It's all up to Apollo "lcm" command */

    ps = popen("ps -x", "r");
    while  (fscanf(ps, "%s", ps_line) != EOF)
	if  (strcmp(ps_line, "dm") == 0)  {
	    mixed_mode = 1;
	    if  (verbose)  {
		fprintf(stderr, "running mixed mode dm/Xapollo !!\n");
		fprintf(stderr, "load the colour map with lcm command\n");
	    }
	}
    if  (mixed_mode)  return(1);

    /* get the colormap to allocate from */
    cmap = DefaultColormap(dpy, s_nr);

    /* enough planes and colors ? */
    if(DisplayPlanes(dpy, s_nr) != 4 || DisplayCells(dpy, s_nr) != 14)
			return(0);

    /* check pixel value 0 (should be black) */
    col.pixel = (unsigned long) 0;
    XQueryColor(dpy, cmap, &col);
    if(col.red > RGB_MIN || col.green > RGB_MIN ||
	col.blue > RGB_MIN || (col.flags & cmask) != cmask) {

	if(verbose) fprintf(stderr,
		"pixel = %2u, red = %f, green = %f, blue = %f, flags = 0x%x\n",
		col.pixel, (float) (col.red / 65535.0),
		(float) (col.green / 65535.0),
		(float) (col.blue / 65535.0), col.flags);

	return(0);
    }

    /* check pixel value 1 (should be red) */
    col.pixel = (unsigned long) 1;
    XQueryColor(dpy, cmap, &col);
    if(col.red < RGB_MAX || col.green > RGB_MIN ||
	col.blue > RGB_MIN || (col.flags & cmask) != cmask) {

	if(verbose) fprintf(stderr,
		"pixel = %2u, red = %f, green = %f, blue = %f, flags = 0x%x\n",
		col.pixel, (float) (col.red / 65535.0),
		(float) (col.green / 65535.0),
		(float) (col.blue / 65535.0), col.flags);

	return(0);
    }

    /* alloc the additional colors (read-only) */
    for(i = 2, j = 6; i < 8; ++i, j+=3) {

	if(!get_apollo_14_color(dpy, cmap, silent, verbose,
		app14_pixs[i],
		(int) (cacdcmap[j] * 65535.0),
		(int) (cacdcmap[j+1] * 65535.0),
		(int) (cacdcmap[j+2] * 65535.0))) {

	    if(verbose) fprintf(stderr,
		"Cannot allocate pixel %d (red = %f, green = %f, blue = %f)\n",
		    app14_pixs[i], cacdcmap[j], cacdcmap[j+1], cacdcmap[j+2]);

	    return(0);
	}
    }
    (void) free_apollo_14_dummies(dpy, cmap, silent, verbose);
    return(1);
}


static unsigned long	dum_arr[16];
static int		nr_dummies = 0;
static int		first_get_color = 1;
static unsigned long	last_color;

static int get_apollo_14_color(dpy, cmap,
		    silent, verbose, pixel, red, green, blue)
Display		*dpy;
Colormap	cmap;
int		silent;
int		verbose;
unsigned long	pixel;
int		red;
int		green;
int		blue;
{
    unsigned long	pixs, planes;
    XColor		col;

    col.red = red;
    col.green = green;
    col.blue = blue;
    col.flags = DoRed | DoGreen | DoBlue;

    /* get first color writable to see where we are in the color table */
    /* we can then decide to either use it or make it a dummy */
    /* or abort if the color table is filled to far already */
    if(first_get_color) {
	first_get_color = 0;
#ifdef DEBUG
fprintf(stderr, "Firstime: color %2u (r = %5d, g = %5d, b = %5d)\n",
	pixel, col.red, col.green, col.blue);
#endif /* DEBUG */

	if(!XAllocColorCells(dpy, cmap, 0, &planes, 0, &pixs, 1)
		|| pixs > pixel) return(0);
#ifdef DEBUG
fprintf(stderr, "We got %u\n", pixs);
#endif /* DEBUG */

	last_color = pixs;

	/* Hey! we can use this */
	if(pixel == pixs) {
#ifdef DEBUG
fprintf(stderr, "Storing it\n");
#endif /* DEBUG */
	    col.pixel = pixs;
	    XStoreColor(dpy, cmap, &col);
	    return(1);
	}

#ifdef DEBUG
fprintf(stderr, "Making it dummy\n");
#endif /* DEBUG */
	/* this will be a dummy */
	dum_arr[nr_dummies] = pixs;
	++nr_dummies;
    }

    /* allocate dummies */
    if(last_color < pixel - 1) {
	create_apollo_14_dummies(dpy, cmap, silent, verbose, pixel);
    }
    if(last_color >= pixel) return(0);

#ifdef DEBUG
fprintf(stderr, "Allocating color %2u (r = %5d, g = %5d, b = %5d)\n",
	pixel, col.red, col.green, col.blue);
#endif /* DEBUG */
    /* try to get the color read-only */
    if(!XAllocColor(dpy, cmap, &col) || pixel < col.pixel) return(0);

#ifdef DEBUG
fprintf(stderr, "Got pixel %u\n", col.pixel);
#endif /* DEBUG */
    /* we got the right one ! */
    if(pixel == col.pixel) {
	last_color = pixel;
	return(1);
    }

    /* else (pixel > col.pixel) we need to allocate writable */
    /* do not free the incorrect pixel here but do it later with the dummies */
#ifdef DEBUG
fprintf(stderr, "Making it dummy; trying writable color\n");
#endif /* DEBUG */
    dum_arr[nr_dummies] = col.pixel;
    ++nr_dummies;

    if(!XAllocColorCells(dpy, cmap, 0, &planes, 0, &pixs, 1)
		|| pixel != pixs) return(0);
#ifdef DEBUG
fprintf(stderr, "Got writable pixel %u\n", pixs);
#endif /* DEBUG */

    /* pixel == pixs */
    last_color = col.pixel = pixs;
    XStoreColor(dpy, cmap, &col);
    return(1);
}

static int create_apollo_14_dummies(dpy, cmap, silent, verbose, pixel)
Display		*dpy;
Colormap	cmap;
int		silent;
int		verbose;
int		pixel;
{
    unsigned long	pixs, planes;

    while(last_color < pixel - 1) {
#ifdef DEBUG
fprintf(stderr, "Getting dummy: ");
#endif /* DEBUG */

	if(!XAllocColorCells(dpy, cmap, 0, &planes, 0, &pixs, 1)) return(0);
#ifdef DEBUG
fprintf(stderr, "Got dummy %u\n", pixs);
#endif /* DEBUG */

	last_color = dum_arr[nr_dummies] = pixs;
	++nr_dummies;
    }
}

static int free_apollo_14_dummies(dpy, cmap, silent, verbose)
Display		*dpy;
Colormap	cmap;
int		silent;
int		verbose;
{
#ifdef DEBUG
    int	i;

    fprintf(stderr, "Freeing dummy pixels: ");
    for(i = 0; i < nr_dummies; ++i) {
        fprintf(stderr, "%d, ", dum_arr[i]);
    }
    fprintf(stderr, "\n");
#endif /* DEBUG */

    XFreeColors(dpy, cmap, dum_arr, nr_dummies, 0);
    return(1);
}
