/*
**	This utility attempts to reduce or even prevent colormap
**	flashing by over-riding most of the X colour manipulation
**	routines without the application being aware.
**
**	HISTORY
**
**	26th March 1997.
**	Written by David Tong, Sun Microsystems Inc.
**	Performs a closest match when allocation fails.
**
**	17th August 1997.
**	Override XCreateColormap and other functions to protect
**	the lower pixels when an application allocates its own colormap
**
**	19th September 1997.
**	Let's not allow the user to allocate any read-write colours.
**	Instead, lets give him a fake pixel value, greater than 256.
**	When the user tries to set the colour of that pixel we just
**	change our look up table so it points to the nearest match 
**	in the default colormap.
**
**	For this to work we need to trap every place where a pixel
**	value is used and convert it as needed. Hopefully we can
**	just do this by manipulating a few calls:
**	XCreateGC, XChangeGC, XSetBackground, XSetForeground
**
**	January 1998
**	Discovered that the FFB can report a colormap size of less than 256
**	when using an 8 bit visual with overlays. Implemented a fix for this.
**	Now I'm wondering whether it may be possible to take advantage of it.
**
**	---------------------------------------------------------------------
**
**	KNOWN BUGS
**	Doesn't work well with Appl*x
**	Causes Pro/Engineer to crash
**	Impacts performance. This is minor, but can probably be improved.
**
**	---------------------------------------------------------------------
**
**	To use, first build and then
**		setenv LD_PRELOAD /<full path name>/libnoflash.so.1
**
**	Environmental variables:
**	NOFLASH_DEBUG	Used by me when investigating what a program is doing
**	NOFLASH_FORCE	Forces closest matching on all read-only colours.
**					This prevents the default colormap from being consumed.
**	NOFLASH_CUBE	Allocate the 4x4x4 read-only color cube
**	NOFLASH_PROTECT	How many colours to protect from flashing (default 64)
**	NOFLASH_NOALLOC	Don't pre-allocate the protected colours
**					as this upsets tools such as xcolor and stdinage.
**	NOFLASH_NONAME	Don't set defaults based on the program name.
**					By default, noflash sets reset variables according
**					to the name of the executable.
**	NOFLASH_DISABLE	Turn off the functionality without changing LD_PRELOAD
** 
**  Acknowledgement:
**  The closest match algorithm used here was not developed by me.   
**  Its origins are obscure, but it is probably a derivation of the  
**  algorithm created by Jason Patterson (jason@reflections.com.au) 
**  and donated to the libXpm distribution. Thanks, Jason. 
**
**	Makefile follows:
**
all:	libnoflash.so.1

libnoflash.so.1:	preload.c
	cc -g -K PIC preload.c -c -I. -I/usr/openwin/include
	ld -G -ztext preload.o -R/usr/lib -ldl -lc -o libnoflash.so
	mv libnoflash.so libnoflash.so.1
**
*/

#include	<sys/types.h>
#include	<sys/timeb.h>
#include	<X11/StringDefs.h>
#include	<X11/Intrinsic.h>
#include	<X11/IntrinsicP.h>
#define NEED_REPLIES
#include	<X11/Xlibint.h>

#include <fcntl.h>
#include <sys/procfs.h>
#include <stdio.h>
#include <dlfcn.h>

void *X11 = NULL;
int PROTECT = 64;
int NOALLOC = 0;
int DEBUG = 0;
int DISABLED = 0;
int FORCE = 0;
int LOOKUP = 1;

Colormap default_cmap = NULL;

typedef struct defaults {
	char *proc_name;
	int noalloc;
	int protect;
	int force;
	int lookup;
} Defaults;

/*
**	Here we set up default values for non-standard applications.
**	Remember, we are changing the way the X server behaves.
**	Applications may make assumptions that we render invalid.
*/

Defaults def[] = {
	/*	Applix does some very odd tricks, and there's no easy way round. */
	/*	The only solution is that Applix should be started first	*/
{ "axmain", 		1, 0, 0, 0 },		/* Applix */
	/*	Don't even THINK about trying to fix Doom. */
{ "sundgadoom",		1, 0, 0, 0 },		/* DOOM */
{ "sunxdoom",		1, 0, 0, 0 },		/* DOOM */
	/*	Xcolor assumes that the whole 256 colours are its for the taking */
{ "xcolor",			1, 64, 0, 0 },		/* Xcolor */
	/*	As do imagetool and sdtimage (basically the same code) */
{ "imagetool",		1, 64, 0, 0 },		/* OpenWin imagetool */
{ "sdtimage",		1, 64, 0, 0 },		/* CDE imagetool */
	/*	I think Wabi does too, but as yet it's untested */
{ "wabi",			1, 64, 0, 1 }		/* Wabi */
};

typedef struct myXcolor {
	long pixel;
	long flags;
} MyXcolor;

/*	TABLESIZE msut be a power of 2 minimum 256, eg 256, 512, 1024 */
#define	TABLESIZE	256

MyXcolor *lookup_table = NULL;
Colormap private_colormap = NULL;

#define	DEFMAX (sizeof(def)/sizeof(Defaults))

void init();

/*	Declare the functions to be over-ridden */

Status (*_XAllocColor) (Display*, Colormap, XColor*);
Status (*_XAllocNamedColor) (Display*, Colormap, char *, XColor*, XColor*);
Status (*_XAllocColorCells) (Display *, Colormap, Bool,
	long[], int, long[], int);
int (*_XFreeColors) (Display*, Colormap, long*, int, long);
Colormap (*_XCreateColormap) (Display*, Window, Visual*, int);

int (*_XQueryColor) (Display *, Colormap, XColor*);
int (*_XQueryColors) (Display *, Colormap, XColor[], int);

int (*_XStoreColor) (Display *, Colormap, XColor*);
int (*_XStoreColors) (Display *, Colormap, XColor[], int);
int (*_XStoreNamedColor) (Display *, Colormap, char *, long, int);

int (*_XSetForeground) (Display *, GC, long);
int (*_XSetBackground) (Display *, GC, long);
int (*_XSetWindowForeground) (Display *, Window, long);
int (*_XSetWindowBackground) (Display *, Window, long);

GC (*_XCreateGC) (Display *, Drawable, unsigned long, XGCValues *);
int (*_XChangeGC) (Display *, GC, unsigned long, XGCValues *);

#define COLOR_FACTOR       3
#define BRIGHTNESS_FACTOR  1

Status XAllocColorCells(Display *dpy, Colormap map, Bool contig,
	long *plane_masks, int nplanes,
	long *pixels, int npixels)
{
	Status stat;
	int i, j;

	if (DEBUG) fprintf(stderr," > XAllocColorCells: %d cells\n", npixels);

	if (X11 == NULL) {
		if (DEBUG) fprintf(stderr,"INIT \n");
		init(dpy);
	}

	if ((map != private_colormap) || (npixels == 0) || (lookup_table == NULL)) {
		if (DEBUG) fprintf(stderr," >>> Call _XAllocColorCells\n");
		stat = _XAllocColorCells(dpy, map, contig, plane_masks, nplanes, pixels, npixels);

#if 0
		if (stat)
			return stat;
		else {
			private_colormap = default_cmap;
			lookup_table = (MyXcolor * )calloc(TABLESIZE, sizeof(MyXcolor));
			memset(lookup_table, -1, TABLESIZE * sizeof(MyXcolor));
		}
#else
		return stat;
#endif

	}
/*
**	We have a private colormap, a lookup table, and a request for pixels.
**	Look through the lookup table and see if we have space to allocate
**	the new entries. If so, we do this by setting the flags in the table.
**	The pixel values we return are the array indices plus the table size.
**	This is fine, because a pixel is a long; we are unlikely to exceed a short.
*/
	/* First see if we can */
	for (i = 0, j = 0; i < npixels; i++) {
		if (j >= TABLESIZE) {
			if (DEBUG) fprintf(stderr," >>> (1) Return 0\n");
			return 0;
		}
		while (lookup_table[j].flags > 0) {
			if (++j >= TABLESIZE) {
				if (DEBUG) fprintf(stderr," >>> (2) Return 0\n");
				return 0;
			}
		}
		j++;
	}
/*
**	There is enough space in the lookup table.
**	Now do the "allocation"
*/
	for (i = 0, j = 0; i < npixels; i++) {
		while (lookup_table[j].flags > 0) {
			j++;
		}
		pixels[i] = j + TABLESIZE;
		lookup_table[j].flags = 1;
		j++;
	}
	stat = j;

	if (DEBUG) fprintf(stderr," >>> Return %d \n", stat);
	return stat;
}

int XFreeColors(Display *dpy, Colormap map, long* pixels, int count,
	long planes)
{
	int stat = 1;
	int j, k;
	long *l, *lp1, *lp2;

	if (DEBUG) fprintf(stderr," > XFreeColors (%d) \n", count);

	if (X11 == NULL)
		init(dpy);
/*
**	Look for pixels >= TABLESIZE in our colormap.
**	Those are not real, so we free them by clearing the flag
**	our lookup table. Any other pixels must be freed normally.
*/

	if ((map == private_colormap) && (lookup_table != NULL) && (count > 0)) {
		lp1 = pixels;
		lp2 = l = (long * )calloc(count, sizeof(long));
		for (j = 0, k = 0; j < count; j++) {
			if (*lp1 >= TABLESIZE) {
				*lp2 = lookup_table[(*lp1) - TABLESIZE].pixel;
				if (*lp2 >= 0) {
					lp2++;
					k++;	/* Has been used; need to call XFreeColors */
				}
				lookup_table[(*lp1) - TABLESIZE].flags = 0;
				lookup_table[(*lp1) - TABLESIZE].pixel = -1;
			} else {
				if (*lp1 >= 0) {
					*lp2++ = *lp1;
					k++;
				}
			}
			lp1++;
		}
		if (k)
			stat = _XFreeColors(dpy, map, l, k, planes);
		free(l);
	} else
		stat = _XFreeColors(dpy, map, pixels, count, planes);
	return stat;
}

XQueryColor(Display *dpy, Colormap map, XColor* colour) 
{
	if (DEBUG) fprintf(stderr," > XQueryColor\n");

	if (X11 == NULL)
		init(dpy);
/*
**	First, check if this is one of our "fake" pixels.
*/

	if ((map == private_colormap) && (lookup_table != NULL)) {
		int offset = colour->pixel - TABLESIZE;
		if ((offset >= 0) && (offset < TABLESIZE)) {
			int save_pixel = colour->pixel;
			colour->pixel = lookup_table[offset].pixel;
			_XQueryColor(dpy, map, colour);
			colour->pixel = save_pixel;
			return 1;
		}
	}  

	return _XQueryColor(dpy, map, colour);
}

XQueryColors(Display *dpy, Colormap map, XColor *colours, int count)
{
	if (DEBUG) fprintf(stderr," > XQueryColors\n");

	if (X11 == NULL)
		init(dpy);

	if ((map == private_colormap) && (lookup_table != NULL)) {
		long *savepixel = (long)calloc(count, sizeof(long));
		int i;

		XColor *c = colours;
		long *l = savepixel;

		for (i = 0; i < count; i++, l++, c++) {
			int offset = c->pixel - TABLESIZE;
			*l = c->pixel;
			if ((offset >= 0) && (offset < TABLESIZE))
				c->pixel = lookup_table[offset].pixel;
		}

		_XQueryColors(dpy, map, colours, count);

		l = savepixel;
		c = colours;
		for (i = 0; i < count; i++, l++, c++) {
			c->pixel = *l;
		}
		free (savepixel);
	} else
		_XQueryColors(dpy, map, colours, count);

	return 1;
}

XStoreColor(Display *dpy, Colormap map, XColor* colour) 
{
	if (DEBUG) fprintf(stderr," > XStoreColor: Pixel %x \n", colour->pixel);

	if (X11 == NULL)
		init(dpy);
/*
**	First, check if this is one of our "fake" pixels.
*/

	if ((map == private_colormap) && (lookup_table != NULL)) {
		int pixel = colour->pixel;
		int offset = pixel - TABLESIZE;
		colour->pixel = 0;
		if ((offset >= 0) && (offset < TABLESIZE)) {
			if (lookup_table[offset].pixel >= 0)
				_XFreeColors(dpy, map, &lookup_table[offset].pixel, 1, NULL);
			XAllocColor(dpy, map, colour);
			if ((colour->pixel >= 0) && (colour->pixel < 256)) {
					if (DEBUG) fprintf(stderr," > Storing colour %X%X%X pixel %x at %x\n",
						colour->red, colour->green, colour->blue,
						colour->pixel, offset);
				lookup_table[offset].pixel = colour->pixel;
				colour->pixel = pixel;
			}
			return 1;
		}
	}

/*
**	If the colour cell is one of the ones we are protecting
**	we just ignore the request
*/

	if ((map == default_cmap) || (colour->pixel >= PROTECT))
		return _XStoreColor(dpy, map, colour);

	return 1;
}

XStoreColors(Display *dpy, Colormap map, XColor *colours, int count)
{
	int i;

	if (DEBUG) fprintf(stderr," > XStoreColors: Requesting %d Colours\n", count);

	if (X11 == NULL)
		init(dpy);

	if ((map == private_colormap) && (lookup_table != NULL)) {
		long *free_list = (long)calloc(count, sizeof(long));
		long *f = free_list;
		int free_count = 0;

		for (i = 0; i < count; i++, colours++) {
			int pixel = colours->pixel;
			int offset = pixel - TABLESIZE;
			colours->pixel = 0;
			XAllocColor(dpy, map, colours);
			if ((offset >= 0) && (offset < TABLESIZE)) {
				if (lookup_table[offset].pixel >= 0) {
					*f++ = lookup_table[offset].pixel;
					free_count++;
				}
				if ((colours->pixel >= 0) && (colours->pixel < 256)) {
					if (DEBUG) fprintf(stderr," > Storing colour %X%X%X pixel %x at %x\n",
						colours->red, colours->green, colours->blue,
						colours->pixel, offset);
					lookup_table[offset].pixel = colours->pixel;
					colours->pixel = pixel;
				}
			}
		}

		if (free_count > 0)
			_XFreeColors(dpy, map, free_list, free_count, NULL);
		free(free_list);
	} else {
		for (i = 0; i < count; i++, colours++) {
			XStoreColor(dpy, map, colours);
		}
		if (DEBUG) fprintf(stderr," > XStoreColors: Optimise this bit \n");
	}
	return 1;
}

XStoreNamedColor(Display *dpy, Colormap map, char *name, long pixel, int flag)
{
	if (DEBUG) fprintf(stderr," > XStoreNamedColor\n");

	if (X11 == NULL)
		init(dpy);
/*
**	First, check if this is one of our "fake" pixels.
*/

	if ((map == private_colormap) && (lookup_table != NULL)) {
		if (pixel >= TABLESIZE) {
			XColor newcolour1, newcolour2;
			int offset = pixel - TABLESIZE;

			if (lookup_table[offset].pixel >= 0)
				_XFreeColors(dpy, map, &lookup_table[offset].pixel, 1, NULL);

			XAllocNamedColor(dpy, map, name, &newcolour1, &newcolour2);
			if ((newcolour1.pixel >= 0) && (newcolour1.pixel < 256))
				lookup_table[offset].pixel = newcolour1.pixel;
			return 1;
		}
	}

/*
**	If the colour cell is one of the ones we are protecting
**	we just ignore the request
*/

	if ((map == default_cmap) || (pixel >= PROTECT))
		return _XStoreNamedColor(dpy, map, name, pixel, flag);
	return 1;
}

Colormap XCreateColormap(register Display *dpy, Window w, Visual *visual, int alloc)
{
	Colormap cmap;
	XColor *colours;
	long *pixels;
	int depth, ncolours, i;
	XVisualInfo visualTemp, *visualInfo;
	int staticVisual;

	Visual *viz;
	XVisualInfo vinfo_tmpl;
	XVisualInfo *return_vinfo = NULL;
	long vinfo_mask = VisualIDMask;
	int num_vinfos;

	if (DEBUG) fprintf(stderr," > XCreateColormap\n");

	if (X11 == NULL)
		init(dpy);

	visualTemp.visualid = XVisualIDFromVisual(visual);
	visualInfo = XGetVisualInfo(dpy, VisualIDMask, &visualTemp, &i);
	staticVisual = ((visualInfo->class % 2) == 0);
	depth = DefaultDepth(dpy, DefaultScreen(dpy));

/*
**	Create a new colormap, exactly as requested.
**	We aren't interested in static or 24 bit visuals yet.
*/
	if ((staticVisual) || (depth == 24)) {
	cmap = _XCreateColormap(dpy, w, visual, alloc);
		return cmap;
	}

	if (LOOKUP > 0) {
		if (DEBUG) fprintf(stderr," > XCreateColormap: Using lookup table\n");
		private_colormap = default_cmap;
		lookup_table = (MyXcolor * )calloc(TABLESIZE, sizeof(MyXcolor));
		memset(lookup_table, -1, TABLESIZE * sizeof(MyXcolor));
	} else {
		if (DEBUG) fprintf(stderr," > XCreateColormap: NOT using lookup table\n");
		private_colormap = _XCreateColormap(dpy, w, visual, AllocNone);

/*
**	The following is necessary because FFB2 can have a colormap size less than 256 -
**	I think it's 224 + 32 for overlay.
*/
		viz = DefaultVisual(dpy, DefaultScreen(dpy));
		vinfo_tmpl.visualid = XVisualIDFromVisual(viz);
		return_vinfo = XGetVisualInfo(dpy, vinfo_mask, &vinfo_tmpl, &num_vinfos);
		ncolours = return_vinfo->colormap_size;

		colours = (XColor * )calloc(ncolours, sizeof(XColor));
		pixels = (long * )calloc(ncolours, sizeof(long));
		for (i = 0; i < ncolours; i++) {
			colours[i].pixel = i;
		}
		_XQueryColors(dpy, default_cmap, colours, ncolours);

		if (_XAllocColorCells(dpy, private_colormap, True, NULL, 0, pixels, ncolours))
			_XStoreColors(dpy, private_colormap, &colours[0], ncolours);
		if (alloc == AllocNone) {
			_XFreeColors(dpy, private_colormap, pixels, ncolours, 0);
			if (!NOALLOC)
				_XAllocColorCells(dpy, private_colormap, True, NULL, 0, pixels, PROTECT);
		}

		free(colours);
		free(pixels);
	}
	return private_colormap;
}

Status XAllocNamedColor(Display* dpy, Colormap map, char *c, XColor*x1, XColor*x2)
{
	Status retstat;

	if (DEBUG) fprintf(stderr," > XAllocNamedColor %s\n", c);

	if (X11 == NULL)
		init(dpy);

/*
**	This bit hasn't been well tested.
**	I'm hoping that x2 gets updated with the right colours
**	even if the allocation fails.
*/

    if (!(retstat = _XAllocNamedColor(dpy, map, c, x1, x2))) {
        if (retstat = XLookupColor(dpy, map, c, x2, x1)) {
            x1->red = x2->red;
            x1->blue = x2->blue;
            x1->green = x2->green;
			x1->flags = DoRed | DoGreen | DoBlue;
            retstat = XAllocColor(dpy, map, x1);
        }
    }    
	if (DEBUG) fprintf(stderr," Wanted Colour 0x%x%x%x\n",
		x2->red, x2->green, x2->blue);
	if (DEBUG) fprintf(stderr," Closest Match 0x%x%x%x Pixel %d\n",
		x1->red, x1->green, x1->blue, x1->pixel);
    return retstat;
}

Status XAllocColor(register Display *dpy, Colormap cmap, XColor *xcolor)
{
	Status retstat = 0;
	Visual *viz;
	XVisualInfo vinfo_tmpl;
	XVisualInfo *return_vinfo = NULL;
	long vinfo_mask = VisualIDMask;
	int num_vinfos;

	if (DEBUG) fprintf(stderr," > XAllocColor 0x%x%x%x\n",
		xcolor->red, xcolor->green, xcolor->blue);

	if (X11 == NULL)
		init(dpy);

	if (!FORCE)
		retstat = _XAllocColor(dpy, cmap, xcolor);

	if (!retstat) {
		XColor *colours;
		int ncolours, i, closepix;
		long int closediff;

/*
**	The following is necessary because FFB2 can have a colormap size less than 256 -
**	I think it's 224 + 32 for overlay.
*/
		viz = DefaultVisual(dpy, DefaultScreen(dpy));
		vinfo_tmpl.visualid = XVisualIDFromVisual(viz);
		return_vinfo = XGetVisualInfo(dpy, vinfo_mask, &vinfo_tmpl, &num_vinfos);
		ncolours = return_vinfo->colormap_size;

/*
**	Have to do this every time, because we don't know what has changed.
*/

		colours = (XColor * )calloc(ncolours, sizeof(XColor));
		for (i = 0; i < ncolours; ++i) 
			colours[i].pixel = i;
		XQueryColors(dpy, cmap, colours, ncolours);

/*
**	Sooner or later this has to match against something,
**	even if it's a choice between black or white!
*/
		do {
		    for (i = 0, closediff = 0x7FFFFFFF; i < ncolours; ++i) {

				long int	newclosediff = 
				  COLOR_FACTOR *(
					abs((long)xcolor->red   -(long)colours[i].red)   + 
					abs((long)xcolor->green -(long)colours[i].green) + 
					abs((long)xcolor->blue  -(long)colours[i].blue)) + 
				  BRIGHTNESS_FACTOR *abs(
					((long)xcolor->red + (long)xcolor->green + (long)xcolor->blue) -
					((long)colours[i].red + (long)colours[i].green + (long)colours[i].blue));

				if (newclosediff < closediff) { 
					closepix = i; 
					closediff = newclosediff; 
				}
		    }

		    xcolor->red   = colours[closepix].red;
		    xcolor->green = colours[closepix].green;
		    xcolor->blue  = colours[closepix].blue;
		    /*
		    ** Now paint it black so we don't loop
		    ** in the case that this colour is read-write.
		    */
		    colours[closepix].red = 0;
		    colours[closepix].green = 0;
		    colours[closepix].blue = 0;
		    retstat = _XAllocColor(dpy, cmap, xcolor);
		} while (retstat == 0);
		free(colours);
	}
	if (DEBUG) fprintf(stderr," Closest Match 0x%x%x%x Pixel %d\n",
		xcolor->red, xcolor->green, xcolor->blue, xcolor->pixel);
	return(retstat);
}

XSetForeground(Display *dpy, GC gc, long pixel) {
	int offset = pixel - TABLESIZE;

	if (DEBUG) fprintf(stderr," > XSetForeground: pixel = %x \n", pixel);

	if (X11 == NULL)
		init(dpy);

	if ((lookup_table) && (offset >= 0) && (offset < TABLESIZE)) {
		if (DEBUG) fprintf(stderr," >>> Lookup: %x becomes %x \n", 
			pixel, lookup_table[offset].pixel);
		pixel = lookup_table[offset].pixel;
	}
	_XSetForeground(dpy, gc, pixel);
}

XSetBackground(Display *dpy, GC gc, long pixel) {
	int offset = pixel - TABLESIZE;

	if (DEBUG) fprintf(stderr," > XSetBackground: pixel = %x \n", pixel);

	if (X11 == NULL)
		init(dpy);

	if ((lookup_table) && (offset >= 0) && (offset < TABLESIZE)) {
		if (DEBUG) fprintf(stderr," >>> Lookup: %x becomes %x \n", 
			pixel, lookup_table[offset].pixel);
		pixel = lookup_table[offset].pixel;
	}
	_XSetBackground(dpy, gc, pixel);
}

XSetWindowForeground(Display *dpy, Window w, long pixel) {
	int offset = pixel - TABLESIZE;

	if (DEBUG) fprintf(stderr," > XSetWindowForeground: pixel = %x \n", pixel);

	if (X11 == NULL)
		init(dpy);

	if ((lookup_table) && (offset >= 0) && (offset < TABLESIZE)) {
		if (DEBUG) fprintf(stderr," >>> Lookup: %x becomes %x \n", 
			pixel, lookup_table[offset].pixel);
		pixel = lookup_table[offset].pixel;
	}
	_XSetWindowForeground(dpy, w, pixel);
}

XSetWindowBackground(Display *dpy, Window w, long pixel) {
	int offset = pixel - TABLESIZE;

	if (DEBUG) fprintf(stderr," > XSetWindowBackground: pixel = %x \n", pixel);

	if (X11 == NULL)
		init(dpy);

	if ((lookup_table) && (offset >= 0) && (offset < TABLESIZE)) {
		if (DEBUG) fprintf(stderr," >>> Lookup: %x becomes %x \n", 
			pixel, lookup_table[offset].pixel);
		pixel = lookup_table[offset].pixel;
	}
	_XSetWindowBackground(dpy, w, pixel);
}

GC XCreateGC(Display *dpy, Drawable d, long mask, XGCValues *values) {
	int offset;

	if (DEBUG) fprintf(stderr," > XCreateGC\n");

	if (X11 == NULL)
		init(dpy);

	if (values) {
		if (mask & GCForeground) {
			if (DEBUG) fprintf(stderr," >>> Set foreground to %x\n", values->foreground);
			if (values->foreground == 0xffffffff) {
				values->foreground = 0;
			}

			offset = values->foreground - TABLESIZE;
			if ((lookup_table) && (offset >= 0) && (offset < TABLESIZE)) {
				if (DEBUG) fprintf(stderr," >>> Lookup: %x becomes %x \n", 
					values->foreground, lookup_table[offset].pixel);
				values->foreground = lookup_table[offset].pixel;
			}
		}

		if (mask & GCBackground) {
			if (DEBUG) fprintf(stderr," >>> Set background to %x\n", values->background);
			if (values->background == 0xffffffff)
				values->background = 0;

			offset = values->background - TABLESIZE;
			if ((lookup_table) && (offset >= 0) && (offset < TABLESIZE)) {
				if (DEBUG) fprintf(stderr," >>> Lookup: %x becomes %x \n", 
					values->background, lookup_table[offset].pixel);
				values->background = lookup_table[offset].pixel;
			}
		}
	}

	return _XCreateGC(dpy, d, mask, values);
}

XChangeGC(Display *dpy, GC gc, long mask, XGCValues *values) {
	int offset;

	if (DEBUG) fprintf(stderr," > XChangeGC\n");

	if (X11 == NULL)
		init(dpy);

	if (values) {
		if (mask & GCForeground) {
			if (DEBUG) fprintf(stderr," >>> Set foreground to %x\n", values->foreground);
			if (values->foreground == 0xffffffff)
				values->foreground = 0;

			offset = values->foreground - TABLESIZE;
			if ((lookup_table) && (offset >= 0) && (offset < TABLESIZE)) {
				if (DEBUG) fprintf(stderr," >>> Lookup: %x becomes %x \n", 
					values->foreground, lookup_table[offset].pixel);
				values->foreground = lookup_table[offset].pixel;
			}
		}

		if (mask & GCBackground) {
			if (DEBUG) fprintf(stderr," >>> Set background to %x\n", values->background);
			if (values->background == 0xffffffff)
				values->background = 0;

			offset = values->background - TABLESIZE;
			if ((lookup_table) && (offset >= 0) && (offset < TABLESIZE)) {
				if (DEBUG) fprintf(stderr," >>> Lookup: %x becomes %x \n", 
					values->background, lookup_table[offset].pixel);
				values->background = lookup_table[offset].pixel;
			}
		}
	}

	return _XChangeGC(dpy, gc, mask, values);
}

void init(Display *dpy)
{
	char *c;
	char pname[256];
	int procfd;
	prpsinfo_t info;
	char *name = NULL;
	int i;

	if (!getenv("NOFLASH_NONAME")) {
		sprintf(pname, "/proc/%05ld", getpid());

		if ((procfd = open(pname, O_RDONLY)) > 0) {
			if (ioctl(procfd, PIOCPSINFO, (char *) &info) != -1) {
				name = strdup(info.pr_psargs);
				if (c = strchr(name, ' '))
					*c = '\0';
				if ((c = strrchr(name, '/')) != NULL)
					name = ++c;
			}
		}
		(void) close(procfd);
	}

	printf("NoFlash V1.1.12 dated 98/03/14: Copyright 1997 David Tong and Sun Microsystems Inc.\n", 0, 0, 0, 0);
	printf("\nThis is a BETA VERSION and is made available for testing purposes ONLY\n");
	printf("NoFlash is the subject of a patent application. In using this program you agree \n");
	printf("to abide by the terms and conditions of Sun's standard non-disclosure agreement.\n");
	printf("Unauthorised distribution of the source or binary is strictly prohibited.\n");
	printf("Please send feedback to david.tong@corp.sun.com\n");

	if (name) {
		printf("Running %s\n", name);

		for (i = 0; i < DEFMAX; i++) {
			if (!strcmp(name, def[i].proc_name)) {
				NOALLOC = def[i].noalloc;
				PROTECT = def[i].protect;
				FORCE = def[i].force;
				LOOKUP = def[i].lookup;
				break;
			}
		}
	}

	/* Tip from john.m.martin@Central - don't hard-code the library path */

	/* C compiler hates this next bit, but it's good enough */

	_XAllocColor		= dlsym(RTLD_NEXT, "XAllocColor");
	_XAllocNamedColor	= dlsym(RTLD_NEXT, "XAllocNamedColor");
	_XAllocColorCells	= dlsym(RTLD_NEXT, "XAllocColorCells");
	_XQueryColors		= dlsym(RTLD_NEXT, "XQueryColors");
	_XQueryColor		= dlsym(RTLD_NEXT, "XQueryColor");
	_XStoreColors		= dlsym(RTLD_NEXT, "XStoreColors");
	_XStoreColor		= dlsym(RTLD_NEXT, "XStoreColor");
	_XStoreNamedColor	= dlsym(RTLD_NEXT, "XStoreNamedColor");
	_XCreateColormap	= dlsym(RTLD_NEXT, "XCreateColormap");
	_XFreeColors		= dlsym(RTLD_NEXT, "XFreeColors");

	_XSetForeground		= dlsym(RTLD_NEXT, "XSetForeground");
	_XSetBackground		= dlsym(RTLD_NEXT, "XSetBackground");
	_XSetWindowForeground		= dlsym(RTLD_NEXT, "XSetWindowForeground");
	_XSetWindowBackground		= dlsym(RTLD_NEXT, "XSetWindowBackground");

	_XCreateGC			= dlsym(RTLD_NEXT, "XCreateGC");
	_XChangeGC			= dlsym(RTLD_NEXT, "XChangeGC");

	X11 = (void *) 1;

	if (c = getenv("NOFLASH_DISABLE")) {
		DISABLED = c;
		return;
	}
	if (c = getenv("NOFLASH_DEBUG"))
		DEBUG = c;
	if (c = getenv("NOFLASH_NOALLOC"))
		NOALLOC = c;
	if (c = getenv("NOFLASH_FORCE"))
		FORCE = c;
	if (c = getenv("NOFLASH_PROTECT"))
		PROTECT = atoi(c);
	default_cmap = DefaultColormap(dpy, DefaultScreen(dpy));

/*
**	Try and create a 4x4x4 color cube
**	If the cube exists we will match those colours.
*/
	
	if (getenv("NOFLASH_CUBE")) {
		int depth = DefaultDepth(dpy, DefaultScreen(dpy));
		int x, y, z;
		XColor colour;

		if (depth == 24)
			return;

		for (x = 0; x < 4; x++) {
			for (y = 0; y < 4; y++) {
				for (z = 0; z < 4; z++) {
					colour.flags = DoRed | DoGreen | DoBlue;
					colour.red = 21845 *x;
					colour.blue = 21845 *y;
					colour.green = 21845 *z;
					if (_XAllocColor(dpy, default_cmap, &colour)) {
						if (colour.pixel > PROTECT) {
							PROTECT = colour.pixel;
						}
					}
				}
			}
		}
	}

/*
**	The old-fashioned way - hard-code the library path
**
**	if ((X11 = dlopen("/usr/openwin/lib/libX11.so.4", RTLD_LAZY)) == 0) {
**		printf("%s\n", dlerror());
**		exit(1);
**	}
**
**	_XAllocColor = dlsym(X11, "XAllocColor");
**	_XFreeColors = dlsym(X11, "XFreeColors");
*/

}
