
// The X11 graphics module:
//
// This module has been greatly enhanced by the content of "Xlib by Example",
// by Cui-Qinq Yang and Mahir S. Ali.


#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

#include "x11_framebuf.h"
#include "cursor.h"

#ifndef True
#define	True	1
#endif
#ifndef False
#define False	0
#endif

// Not in any header file...
extern "C" {
	extern int XShmQueryExtension(Display *dpy);
}
#if defined(_SGI_SOURCE) || defined(_INCLUDE_HPUX_SOURCE)
extern void usleep(unsigned long usec);
#endif

/* Shared memory error handler routine */
static int haderror;
static int (*origerrorhandler)(Display *, XErrorEvent *);
static int shmerrorhandler(Display *d, XErrorEvent *e)
{
	haderror++;
	if(e->error_code==BadAccess)
		fprintf(stderr,"X: failed to attach shared memory\n");
	else (*origerrorhandler)(d,e);
	return(0);
} 

X11_FrameBuf:: X11_FrameBuf(char **icon, 
		unsigned short width, unsigned short height, char *title) :
					FrameBuf(icon, width, height, title)
{
	XSetWindowAttributes winattr;
	XVisualInfo     vinfo_return;
	XGCValues       gcv;
	XKeyboardState  kbd_state;
	char           *window_Name = title;
        XTextProperty   windowName;
	XWMHints       *wmhints;
        XClassHint     *classhints;
        XSizeHints     *sizehints;
	XpmAttributes   attributes;
	Pixmap		icon_pixmap;
	Pixmap		cursor_pixmap;
	Pixmap		cursor_mask;
	Pixmap		nocursor_pixmap;
	Pixmap		nocursor_mask;
	XColor		black, white;
        unsigned long   valuemask=0L;
	int		XpmErrorStatus;
	XEvent          event;
	char           *argv[2] = { title, NULL };

	/* Open our DISPLAY */
	if ( (display=XOpenDisplay(NULL)) == NULL ) {
		fprintf(stderr, "X: Can't open display...\n");
		exit(3);
	}

	/* Make sure all is destroyed if killed off */
	XSetCloseDownMode(display, DestroyAll);

	/* Try to allocate an 8-bit PseudoColor display.
	 * If one isn't available, try to get a 16 bit TrueColor visual.
	 * Lastly, try to get a 24 bit TrueColor visual.
	 */
	if ( XMatchVisualInfo(display, DefaultScreen(display), 
					8, PseudoColor, &vinfo_return) ) {
#ifdef DEBUG
		fprintf(stdout, "X: 8 bit PsuedoColor\n");
#endif
		truecolor = 0;
		bpp = 1;
	} else if ( XMatchVisualInfo(display, DefaultScreen(display), 
					15, TrueColor, &vinfo_return) ) {
#ifdef DEBUG
		fprintf(stdout, "X: 15 bit TrueColor\n");
#endif
		truecolor = 1;
		bpp = 2;
	} else if ( XMatchVisualInfo(display, DefaultScreen(display),
				     16, TrueColor, &vinfo_return) ) {
#ifdef DEBUG
		fprintf(stdout, "X: 16 bit TrueColor\n");
#endif
		truecolor = 1;
		bpp = 2;
	} else if ( XMatchVisualInfo(display, DefaultScreen(display),
				     24, TrueColor, &vinfo_return) ) {
#ifdef DEBUG
		fprintf(stdout, "X: 24 bit TrueColor\n");
#endif
		truecolor = 1;
		bpp = 3;
	} else {
		fprintf(stderr,
		"X: Screen doesn't support PseudoColor or TrueColor!\n");
		exit(3);
	}
	if ( truecolor ) {
		get_color_info(vinfo_return.red_mask,&red_shift,&red_prec);
		get_color_info(vinfo_return.green_mask, &green_shift,
								&green_prec);
		get_color_info(vinfo_return.blue_mask,&blue_shift,&blue_prec);
	}
	colormap = DefaultColormap(display, DefaultScreen(display));
	private_cmap = 0;

	/* Check to see if the extensions are supported */
	if (!XShmQueryExtension(display)) {
		fprintf(stderr, "X server doesn't support MITSHM extension.\n");
		exit(3);
	}
#ifdef USE_BG_PIXMAP		// Turns out, shared mem XImage is faster.
	if (!XShmPixmapFormat(display)) {
#ifdef IMAGE_DEBUG
		fprintf(stderr, "X server doesn't do shared memory pixmaps.\n");
#endif
		use_ximage = 1;
	} else
		use_ximage = 0;
#endif

	/* Create the main window */
	Black = BlackPixel(display, DefaultScreen(display));
	MainWin = XCreateSimpleWindow(display, DefaultRootWindow(display),
				0, 0, width, height, 1, Black, Black);

	/* Get our shared screen memory */
	shared_len = (width * height);	// 8 bits per pixel
	backbuf = new unsigned char[shared_len];
	if ( truecolor )
		shared_len *= (bpp == 3 ? sizeof(unsigned long) : bpp);
	shminfo.shmid = shmget(IPC_PRIVATE, shared_len, IPC_CREAT|0777);
	if ( shminfo.shmid < 0 ) {
		fprintf(stderr, "Couldn't get X shared memory.\n");
		exit(3);
	}
/* We need to protect our shared memory against orphanage if we
   are killed in this function.  */

	shminfo.shmaddr = (char *) shmat(shminfo.shmid, 0, 0);
	shared_mem = (unsigned char *) shminfo.shmaddr;
	if ( shminfo.shmaddr == (char *)-1 ) {
		fprintf(stderr, "Couldn't attach to X shared memory.\n");
		shmctl(shminfo.shmid, IPC_RMID, NULL);
		exit(3);
	}
	shminfo.readOnly = False;

	/* Now try to attach it to the X Server */
	haderror = False;
	origerrorhandler = XSetErrorHandler(shmerrorhandler);
	XShmAttach (display, &shminfo);
	XSync(display,True);  /* wait for error or ok */
	XSetErrorHandler(origerrorhandler);

	/* After the X server attaches, remove the public ID */
	if ( shmctl(shminfo.shmid, IPC_RMID, NULL) < 0 )
		perror("X: Warning: shmctl rmid() error");

	if( haderror /* In shared memory attach procedure */ ){
		if ( shmdt(shminfo.shmaddr) < 0 )
			perror("X shmdt() error");
		exit(3);
	}

	/* Create the shared memory Pixmap */
#ifdef USE_BG_PIXMAP
	if ( use_ximage ) {
		X_image = XShmCreateImage(display, vinfo_return.visual,
				vinfo_return.depth, ZPixmap, shminfo.shmaddr,
						&shminfo, width, height);
	} else {
		X_pixmap = XShmCreatePixmap(display, MainWin, shminfo.shmaddr,
				&shminfo, width, height, vinfo_return.depth);
		XSetWindowBackgroundPixmap(display, MainWin, X_pixmap);
	}
#else
	X_image = XShmCreateImage(display, vinfo_return.visual,
				vinfo_return.depth, ZPixmap, shminfo.shmaddr,
						&shminfo, width, height);
#endif /* USE_BG_PIXMAP */

	/* If truecolor, we will present a fake 8-bit pixmap to the
	   rest of the program, and then copy it to the true pixmap
	   on updates.
	*/
	memset((void *)shared_mem, Black, shared_len);
	if ( truecolor ) {	// Create the false 8-bit buffer
		trueshared_mem = shared_mem;
		shared_len /= bpp;
		shared_mem = new unsigned char[shared_len];
		memset((void *)shared_mem, Black, shared_len);
	}
	memset((void *)backbuf, Black, shared_len);

	/* Create the icon window, and give it the icon colormap */
	wmhints = XAllocWMHints();
	if ( icon ) {
		/* Allocate the pixmap for the icon */
		attributes.valuemask = XpmColormap;
		attributes.colormap = colormap;
        	XpmErrorStatus = XpmCreatePixmapFromData(display, MainWin, 
					icon, &icon_pixmap, NULL, &attributes);
		/* Ignore a failed pixmap creation */
		if ( XpmErrorStatus != XpmSuccess ) {
			fprintf(stderr, 
				"Warning: Couldn't create color icon.\n");
		}
		XpmFreeAttributes(&attributes);

		wmhints->icon_pixmap 	= icon_pixmap;
		wmhints->flags = IconPixmapHint;
	} else
		wmhints->flags = 0;

	/* Create the normal cursor (Mac cursor) */
	cursor_pixmap = XCreatePixmapFromBitmapData(display, MainWin, 
			cursor_bits, cursor_width, cursor_height, 1L, 0L, 1);
	cursor_mask = XCreatePixmapFromBitmapData(display, MainWin, 
			cursorm_bits, cursorm_width, cursorm_height, 1L, 0L, 1);
	black.red   = 0x0000;
	black.green = 0x0000;
	black.blue  = 0x0000;
	white.red   = 0xFFFF;
	white.green = 0xFFFF;
	white.blue  = 0xFFFF;
	cursor  = XCreatePixmapCursor(display, cursor_pixmap,
					cursor_mask, &black, &white, 0, 0);
	XFreePixmap(display, cursor_pixmap);
	XFreePixmap(display, cursor_mask);

	/* Create the hidden cursor */
	black.red   = 0;
	black.green = 0;
	black.blue  = 0;
	attributes.valuemask = (XpmColormap|XpmDepth);
	attributes.colormap = colormap;
	attributes.depth = 1;
	XpmCreatePixmapFromData(display, MainWin, invis_cursor_xpm, 
				&nocursor_pixmap, &nocursor_mask, &attributes);
	XpmFreeAttributes(&attributes);
	nocursor  = XCreatePixmapCursor(display, nocursor_pixmap,
					nocursor_mask, &black, &black, 0, 0);
	XFreePixmap(display, nocursor_pixmap);
	XFreePixmap(display, nocursor_mask);

	/* Set the cursor */
	hidden_cursor = 1;
	Show_Cursor();

	/* Various window manager settings */
        wmhints->initial_state   = NormalState;
        wmhints->input           = True;     
        wmhints->flags |= StateHint | InputHint;

	/* Set the class for this program */
	classhints = XAllocClassHint();
	classhints->res_name     = title;
	classhints->res_class    = title;

        /* Setup the max and minimum size that the window will be */
	sizehints = XAllocSizeHints();
        sizehints->flags         = PSize | PMinSize | PMaxSize;
        sizehints->min_width     = width;
        sizehints->min_height    = height;
        sizehints->max_width     = width;
        sizehints->max_height    = height;
	WIDTH = width;
	HEIGHT = height;

	/* Create the window/icon name properties */
        if (XStringListToTextProperty(&window_Name, 1, &windowName) == 0) {
		fprintf(stderr, "X: Cannot create window name resource!\n");
		exit(3);
	}

        /* Now set the window manager properties */
        XSetWMProperties(display, MainWin, &windowName, &windowName,
				argv, 1, sizehints, wmhints, classhints);
	XFree(wmhints);
	XFree(classhints);
	XFree(sizehints);

  	/*  Enable the delete window protocol (taken from 'rxvt' -- Thanks!) */
	wm_del_win = XInternAtom(display,"WM_DELETE_WINDOW",False);
	XSetWMProtocols(display, MainWin, &wm_del_win, 1);
  
        /* Check if the server allows backing store */
        if (DoesBackingStore(XDefaultScreenOfDisplay(display)) == Always)
        {
                /* Ok we want backing store as it is very useful */
                valuemask |= CWBackingStore;
                winattr.backing_store = Always;
        }
	XChangeWindowAttributes(display, MainWin, valuemask, &winattr);

	/* Set up graphics contexts */
	gcv.graphics_exposures = False;
        if (!(gc = XCreateGC(display, MainWin, GCGraphicsExposures, &gcv))) {
		fprintf(stderr, "X: Couldn't create graphics context.\n");
		exit(3);
	}

	/* Turn off autorepeat */
	XGetKeyboardControl(display, &kbd_state);
	was_repeating = (kbd_state.global_auto_repeat != AutoRepeatModeOff);
	XAutoRepeatOff(display);

	/* Set up the events we wait for */
	Event_mask =    KeyPressMask | KeyReleaseMask |
                        ButtonPressMask | ButtonReleaseMask |
                        // PointerMotionMask |
                        ExposureMask | StructureNotifyMask;
	XSelectInput(display, MainWin, Event_mask);

	/* Actually map the main window */
	XMapWindow(display, MainWin);

	/* Loop, waiting for our window to be mapped */
	do {
		/* Get the next event */
		XNextEvent(display, &event);
	}
	while (event.type != MapNotify);
}

X11_FrameBuf:: ~X11_FrameBuf()
{
	XUnmapWindow(display,  MainWin);
#ifdef USE_BG_PIXMAP
	if ( use_ximage )
		XDestroyImage(X_image);
	else
		XFreePixmap(display, X_pixmap);
#else
	XDestroyImage(X_image);
#endif
	XFreeCursor(display, cursor);
	XFreeCursor(display, nocursor);
	if ( truecolor )
		delete shared_mem;
	delete backbuf;
	XSync(display, False);
	XShmDetach (display, &shminfo);
	XSync(display, False);	/* need server to detach so can remove id */
	if ( shmdt(shminfo.shmaddr) < 0 )
		perror("X shmdt() error");
	if ( was_repeating )
		XAutoRepeatOn(display);
	XCloseDisplay(display);
}

/* Just try to allocate a full colormap */
/* This code assumes a psuedo-color (256 color) display */
int
X11_FrameBuf:: Alloc_Cmap(int polite, Color Cmap[NUM_COLORS])
{
	int i, c, r, g, b;
	static int alloct[NUM_COLORS], nalloct=0;
	unsigned long pix;
	XColor xcol, cmap[NUM_COLORS];

	/* Most of the mapping code is adapted from 'xv' -- Thanks! :) */
	int ri, gi, bi;			/* The color shades we want */
	int rd, gd, bd;			/* The color shades we have */
	int mdist, close, d;		/* Distance and closest pixel */

	/* Truecolor displays can get their own "colormap" */
	if ( truecolor )
		return(Alloc_Private_Cmap(Cmap));

	if ( nalloct ) {  /* Free a previously allocated colormap */
		for ( i=0; i<NUM_COLORS; ++i ) {
			if ( alloct[i] ) {
				pix = i;
				XFreeColors(display, colormap, &pix, 1, 0L);
			}
		}
	}

	/* Shed light on a standard colormap, if we can */
	for ( i=0; i<NUM_COLORS; ++i )
		alloct[i] = 0;

	for ( r=0; r<6; ++r ) {
		for ( g=0; g<6; ++g ) {
			for ( b=0; b<6; ++b ) {
				xcol.red =   (((r*255)/5)<<8);
				xcol.green = (((g*255)/5)<<8);
				xcol.blue =  (((b*255)/5)<<8);
				xcol.flags = (DoRed|DoGreen|DoBlue);
				if ( XAllocColor(display, colormap, &xcol) ) {
					alloct[xcol.pixel]=1;
					++nalloct;
				}
			}
		}
	}
#ifdef DEBUG
fprintf(stderr, "Phase 1: nalloct = %d\n", nalloct);
#endif

	/* If we couldn't allocate an orthogonal plane of color */
	if ( (nalloct < (6*6*6)) && (nalloct > 128) ) {
		/* Free allocated colors and allocate a more limited range */
		for ( i=0; i<NUM_COLORS; ++i ) {
			if ( alloct[i] ) {
				pix = i;
				XFreeColors(display, colormap, &pix, 1, 0L);
				alloct[i]=0;
				--nalloct;
			}
		}
		for ( r=0; r<4; ++r ) {
			for ( g=0; g<8; ++g ) {
				for ( b=0; b<4; ++b ) {
					xcol.red =   (((r*255)/3)<<8);
					xcol.green = (((g*255)/7)<<8);
					xcol.blue =  (((b*255)/3)<<8);
					xcol.flags = (DoRed|DoGreen|DoBlue);
					if ( XAllocColor(display,
							colormap, &xcol) ) {
						alloct[xcol.pixel]=1;
						++nalloct;
					}
				}
			}
		}
	}
#ifdef DEBUG
fprintf(stderr, "Phase 2: nalloct = %d\n", nalloct);
#endif

	/* Find out what kind of colormap we have now */
	for ( i=0; i<NUM_COLORS; ++i ) {
		cmap[i].pixel = i;
		cmap[i].flags = (DoRed|DoGreen|DoBlue);
	}
	XQueryColors(display, colormap, cmap, NUM_COLORS);

	/* Map the whole colormap */
	for ( i=0; i<NUM_COLORS; ++i ) {
		mdist = 100000; close=0;

		ri = (Cmap[i].red >> 8);
		gi = (Cmap[i].green >> 8);
		bi = (Cmap[i].blue >> 8);

		/* Cycle through the colors we have */
		for ( c=0; c<NUM_COLORS; ++c ) {
			rd = ri - (cmap[c].red >> 8);
			gd = gi - (cmap[c].green >> 8);
			bd = bi - (cmap[c].blue >> 8);
			d = rd*rd + gd*gd + bd*bd;

			if ( d < mdist ) {
				mdist = d;
				close = c;
			}
		}
#ifdef DEBUG
fprintf(stderr, "Desired RGB (%d,%d,%d) mapped to existing RGB (%d,%d,%d)\n",
				ri, gi, bi, cmap[close].red>>8,
				cmap[close].green>>8, cmap[close].blue>>8);
#endif
		/* Lock down and register the color we want */
		if ( ! XAllocColor(display, colormap, &cmap[close]) ) {
			fprintf(stderr,
	"X11_FrameBuf: Warning: couldn't allocate an existing color!\n");
		}
		Pixel_colors[i] = cmap[close].pixel;
		Color_Map[i].red   = cmap[close].red;
		Color_Map[i].green = cmap[close].green;
		Color_Map[i].blue  = cmap[close].blue;
	}
	return(NUM_COLORS);
}

/* Allocate a private colormap :) */
int
X11_FrameBuf:: Alloc_Private_Cmap(Color Cmap[NUM_COLORS])
{
	static int alloct=0;
	int i;
        XColor xcols[NUM_COLORS];

	if ( truecolor ) {
		for (i = 0; i < NUM_COLORS; i++) {
			if ( !Cmap[i].red && !Cmap[i].green && !Cmap[i].blue )
				Black = i;
			Color_Map[i].red = (Cmap[i].red << 8);
			Color_Map[i].green = (Cmap[i].green << 8);
			Color_Map[i].blue = (Cmap[i].blue << 8);

			Pixel_colors[i] = i;
			True_Colors[i] = 
				(((Cmap[i].red>>(red_prec+8))<<red_shift)+
				((Cmap[i].green>>(green_prec+8))<<green_shift)+
				((Cmap[i].blue>>(blue_prec+8))<<blue_shift));
		}
		if ( ! alloct ) {
			memset((void *)backbuf, Black, shared_len);
			memset((void *)shared_mem, Black, shared_len);
			memset((void *)trueshared_mem, True_Colors[Black],
							shared_len*bpp);
			alloct = 1;
		}
	} else {
		/* Create a custom visual colormap, if needed */
		if ( ! alloct ) {
			private_cmap = XCreateColormap(display, MainWin,
				DefaultVisual(display, XDefaultScreen(display)),
							AllocAll);
			alloct = 1;
		}

		/* Allocate custom colors... */
		for(i=0;i<NUM_COLORS;i++)
			xcols[i].pixel = i;
		for(i=0;i<NUM_COLORS;i++) {
			if ( !Cmap[i].red && !Cmap[i].green && !Cmap[i].blue )
				Black = i;
			Pixel_colors[i] = i;
			Color_Map[i].red = xcols[i].red   = Cmap[i].red<<8;
			Color_Map[i].green = xcols[i].green = Cmap[i].green<<8;
			Color_Map[i].blue = xcols[i].blue  = Cmap[i].blue<<8;
			xcols[i].flags = (DoRed|DoGreen|DoBlue);
		}
		memset((void *)backbuf, Black, shared_len);
		XStoreColors(display, private_cmap, xcols, NUM_COLORS);
		XSetWindowColormap(display, MainWin, private_cmap);
	}
	return(NUM_COLORS);
}

void
X11_FrameBuf:: Hide_Cursor(void)
{
	if ( hidden_cursor ==  0 ) {
		XDefineCursor(display, MainWin, nocursor);
		hidden_cursor = 1;
	}
}

void
X11_FrameBuf:: Show_Cursor(void)
{
	if ( hidden_cursor == 1 ) {
		XDefineCursor(display, MainWin, cursor);
		hidden_cursor = 0;
	}
}

/* This function was adapted from 'xscreensaver', by Jamie Zawinski 

	-- Thanks! :)
*/
void
X11_FrameBuf:: Fade(int steps)
{
	static int      state = XFADE_IN;
	static XColor   orig_colors[NUM_COLORS];
	XColor          curr_colors[NUM_COLORS];
	static long     orig_truecolors[NUM_COLORS];
	int             i, j;
	const int       true_slowfactor=4;
	static Colormap *fade_cmap, new_cmap;

#ifdef _SGI_SOURCE
	return;		/* Not working yet? */
#endif

	/* Find out the state of the fade */
	state = ( (state == XFADE_IN) ? XFADE_OUT : XFADE_IN );

	/* Truecolor screens don't fade using a colormap */
	if ( truecolor ) {  /* Do it a different way... */
		steps /= true_slowfactor;
		if ( state == XFADE_OUT	) {
			for ( j=0; j<NUM_COLORS; ++j ) {
				orig_colors[j].red = Color_Map[j].red;
				orig_colors[j].green = Color_Map[j].green;
				orig_colors[j].blue = Color_Map[j].blue;
			}
			memcpy((void *)orig_truecolors, (void *)True_Colors,
						NUM_COLORS*sizeof(long));
			for ( i=steps-1; i >= 0; i-- ) {
				for (j = 0; j < NUM_COLORS; j++) {
					curr_colors[j].red   = 
						(orig_colors[j].red*i)/steps;
					curr_colors[j].green = 
						(orig_colors[j].green*i)/steps;
					curr_colors[j].blue  = 
						(orig_colors[j].blue*i)/steps;
					True_Colors[j] = 
			(((curr_colors[j].red>>(red_prec+8))<<red_shift)+
			((curr_colors[j].green>>(green_prec+8))<<green_shift)+
			((curr_colors[j].blue>>(blue_prec+8))<<blue_shift));
				}
				Refresh();
				usleep(5);
				XSync (display, False);
			}
			memset((void *)True_Colors, 0, NUM_COLORS*sizeof(long));
		} else {
			for ( i=0; i < steps; i++ ) {
				for (j = 0; j < NUM_COLORS; j++) {
					curr_colors[j].red   = 
						(orig_colors[j].red*i)/steps;
					curr_colors[j].green = 
						(orig_colors[j].green*i)/steps;
					curr_colors[j].blue  = 
						(orig_colors[j].blue*i)/steps;
					True_Colors[j] = 
			(((curr_colors[j].red>>(red_prec+8))<<red_shift)+
			((curr_colors[j].green>>(green_prec+8))<<green_shift)+
			((curr_colors[j].blue>>(blue_prec+8))<<blue_shift));
				}
				Refresh();
				usleep(5);
				XSync (display, False);
			}
			memcpy((void *)True_Colors, (void *)orig_truecolors,
						NUM_COLORS*sizeof(long));
		}
		Refresh();
		usleep(5);
		XSync (display, False);
		return;
	}
		

	/* Set the requested pixels */
	for ( i=0; i<NUM_COLORS; ++i )
		orig_colors[i].pixel = i;

	/* Make a copy of the default colormap and use that in fade */
	if ( private_cmap ) {
		if ( state == XFADE_OUT ) {
			XQueryColors(display, private_cmap, orig_colors, 
								NUM_COLORS);
			fade_cmap = &private_cmap;
		}
	} else {
		XQueryColors(display, colormap, orig_colors, NUM_COLORS);
		if ( state == XFADE_OUT ) {
			new_cmap = XCreateColormap(display,
				DefaultRootWindow(display),
				DefaultVisual(display, XDefaultScreen(display)),
								AllocAll);
			XStoreColors(display,new_cmap,orig_colors,NUM_COLORS);
			XGrabServer(display);
			XInstallColormap(display, new_cmap);
			fade_cmap = &new_cmap;
		}
	}
	XSync(display, False);

	memcpy(curr_colors, orig_colors, NUM_COLORS*sizeof(XColor));
	if ( state == XFADE_OUT ) {
		for ( i=steps-1; i >= 0; i-- ) {
			for (j = 0; j < NUM_COLORS; j++) {
				curr_colors[j].red   = 
					orig_colors[j].red   * i / steps;
				curr_colors[j].green = 
					orig_colors[j].green * i / steps;
				curr_colors[j].blue  = 
					orig_colors[j].blue  * i / steps;
			}
			XStoreColors (display, *fade_cmap, 
						curr_colors, NUM_COLORS);
			usleep(5);
			XSync (display, False);
		}
	} else {
		for ( i=0; i < steps; i++ ) {
			for (j = 0; j < NUM_COLORS; j++) {
				curr_colors[j].red   = 
					orig_colors[j].red   * i / steps;
				curr_colors[j].green = 
					orig_colors[j].green * i / steps;
				curr_colors[j].blue  = 
					orig_colors[j].blue  * i / steps;
			}
			XStoreColors (display, *fade_cmap, 
						curr_colors, NUM_COLORS);
			usleep(5);
			XSync (display, False);
		}
	}
	
	if ( state == XFADE_IN ) {
		/* Restore the original colormap */
		if ( private_cmap ) {
			XStoreColors (display, private_cmap, 
						orig_colors, NUM_COLORS);
		} else {
			XInstallColormap(display, colormap);
			XUngrabServer(display);
			XFreeColormap(display, *fade_cmap);
		}
		XSync(display, False);
	}
}

/* BTW, you _can_ pass negative x0 and y0 values to this function */
void
X11_FrameBuf:: RefreshArea(int x0, int y0, int width, int height)
{
	/* Do bounds checking */
	if ( y0 < 0 ) {
		if ( (height += y0) <= 0 )
			return;
		y0 = 0;
	}
	if ( (y0 + height) >= HEIGHT ) {
		if ( y0 > HEIGHT )
			return;
		height = (HEIGHT-y0);
	}
	if ( x0 < 0 ) {
		if ( (width += x0) <= 0 )
			return;
		x0 = 0;
	}
	if ( (x0 + width) >= WIDTH ) {
		if ( x0 > WIDTH )
			return;
		width = (WIDTH-x0);
	}

	if ( truecolor ) {	// Copy the fake pixmap to the real one
		int rows, cols;
		int offset;

		switch ( bpp ) {
			case 2:
	{ unsigned short *smem = (unsigned short *) trueshared_mem;
		for ( rows = height-1; rows>=0; --rows ) {
			offset = ((y0+rows)*WIDTH)+x0;
			for ( cols = width; cols; --cols ) {
				smem[offset] = (unsigned short)
						True_Colors[shared_mem[offset]];
				++offset;
			}
		}
	}
	break;
			case 3:
	{ unsigned long *smem = (unsigned long *) trueshared_mem;
		for ( rows = height-1; rows>=0; --rows ) {
			offset = ((y0+rows)*WIDTH)+x0;
			for ( cols = width; cols; --cols ) {
				smem[offset] = True_Colors[shared_mem[offset]];
				++offset;
			}
		}
	}
	break;
			default:
				fprintf(stderr, "Unknown bits-per-pixel!\n");
				break;
		}
	}
#ifdef USE_BG_PIXMAP
	if ( use_ximage ) {
		XShmPutImage(display, MainWin, gc, X_image, x0, y0, x0, y0,
             						width, height, False);
	} else
		XClearArea(display, MainWin, x0, y0, width, height, False);
#else
	XShmPutImage(display, MainWin, gc, X_image, x0, y0, x0, y0,
             						width, height, False);
#endif
}

void
X11_FrameBuf:: Refresh(void)
{ 
	if ( truecolor ) {	// Copy the fake pixmap to the real one
		int offset, end;

		end = WIDTH * HEIGHT;
		switch ( bpp ) {
			case 2:
		{ unsigned short *smem = (unsigned short *) trueshared_mem;
		for ( offset = end-1; offset>=0; --offset )
			smem[offset] = (unsigned short)
					True_Colors[shared_mem[offset]];
		}
		break;
			case 3:
		{ unsigned long *smem = (unsigned long *) trueshared_mem;
		for ( offset = end-1; offset>=0; --offset )
			smem[offset] = True_Colors[shared_mem[offset]];
		}
		break;
			default:
				fprintf(stderr, "Unknown bits-per-pixel!\n");
				break;
		}
	}
#ifdef USE_BG_PIXMAP
	if ( use_ximage ) {
		XShmPutImage(display, MainWin, gc, X_image, 0, 0, 0, 0,
             						WIDTH, HEIGHT, False);
		XSync(display, False);
	} else
		XClearWindow(display, MainWin);
#else
	XShmPutImage(display, MainWin, gc, X_image, 0, 0, 0, 0,
             						WIDTH, HEIGHT, False);
	XSync(display, False);
#endif
}

void
X11_FrameBuf:: FlushEvents(void)
{
	int    numevents;
	XEvent event;

	for ( numevents=XPending(display); numevents > 0; --numevents )
		GetEvent(&event);
}
	
int
X11_FrameBuf:: NumEvents(void)
{
	return(XPending(display));
}

void
X11_FrameBuf:: GetEvent(XEvent *event)
{
	XNextEvent(display, event);

	/* Handle some events internally */
	switch (event->type) {
		/* Are we being sent a message? */
		case ClientMessage:
			if ( event->xclient.format == 32 && 
					event->xclient.data.l[0] == wm_del_win )
			{ /* Window manager wants to delete us */
			  /* Assume that atexit() handles all cleanup */
				exit(0);
			}
			break;

		/* Cover yourself! :) */
		case Expose:
			Refresh();
			break;
	}
}

int
X11_FrameBuf:: KeyToAscii(XEvent *event, char *buf, int buflen, KeySym *key)
{
	return(XLookupString(&event->xkey, buf, buflen, key, NULL));
}

void
X11_FrameBuf:: Flush(int sync)
{
	if ( sync )
		XSync(display, False);
	else
		XFlush(display);
}

void
X11_FrameBuf:: get_color_info(unsigned long mask, unsigned long *shift, 
			       				unsigned long *prec)
{
  	*shift = 0;
  	*prec = 8;

	while (! (mask & 0x1)) {
		(*shift)++;
		mask >>= 1;
	}
	while (mask & 0x1) {
		(*prec)--;
		mask >>= 1;
	}
}
