/*----------------------------------------------------------------------*\
 | Graphical MCSST retrieval program -- Unix X11 version				|
 |																		|
 | Author: Peter N. Schweitzer (MS 955, USGS, Reston VA 22092)			|
 | Language: Standard C (Developed on GNU gcc under DG/UX 4.31, X11R4)	|
 |																		|
 | Input argument: Name of file containing a list of MCSST files.		|
 | The maximum number of MCSST files that can be simultaneously used	|
 | is twelve; ordinarily one would choose monthly files.				|
 | The file names must end with extensions indicating the region of		|
 | interest, as in the original MCSST files.							|
\*----------------------------------------------------------------------*/

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/keysym.h>
#include <X11/Xresource.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <pwd.h>

#ifdef __hpux
#undef FILENAME_MAX
#define FILENAME_MAX 1024
typedef XPointer caddr_t;
#endif

#define see_width 40
#define see_height 40
static char see_bits[] = {
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x80,0x03,0x00,0x00,0x00,0x40,0x04,0x00,0x00,0x00,0x40,0x04,
	0x00,0x00,0x00,0x40,0x06,0x00,0x00,0x00,0x40,0x04,0x00,0x00,
	0x00,0x40,0x04,0x00,0x00,0x00,0x40,0x05,0x00,0x00,0x00,0x40,
	0x07,0x00,0x00,0x00,0x40,0x05,0x00,0x00,0x00,0x40,0x05,0x00,
	0x21,0x84,0x50,0x45,0x08,0x52,0x4a,0x49,0xa7,0x94,0x8c,0x31,
	0x46,0x15,0x63,0x00,0x00,0x40,0x05,0x00,0x21,0x84,0x50,0x45,
	0x08,0x52,0x4a,0x49,0xa7,0x94,0x8c,0x31,0x46,0x15,0x63,0x00,
	0x00,0x40,0x05,0x00,0x21,0x84,0x50,0x45,0x08,0x52,0x4a,0x49,
	0xa7,0x94,0x8c,0x31,0x46,0x05,0x63,0x00,0x00,0x20,0x09,0x00,
	0x21,0x84,0x90,0x53,0x08,0x52,0x4a,0xd1,0x97,0x94,0x8c,0x31,
	0xd6,0x17,0x63,0x00,0x00,0xd0,0x17,0x00,0x21,0x84,0x90,0x53,
	0x08,0x52,0x4a,0x21,0x88,0x94,0x8c,0x31,0xc6,0x07,0x63,0x00,
	0x00,0x00,0x00,0x00,0x21,0x84,0x10,0x42,0x08,0x52,0x4a,0x29,
	0xa5,0x94,0x8c,0x31,0xc6,0x18,0x63,0x00,0x00,0x00,0x00,0x00,
	0x21,0x84,0x10,0x42,0x08,0x52,0x4a,0x29,0xa5,0x94,0x8c,0x31,
	0xc6,0x18,0x63,0x00,0x00,0x00,0x00,0x00
	};

#define MAXLEN 256

/*----------------------------------------------------------------------*/

#ifndef LSBFirst
#define LSBFirst	0
#endif

#ifndef MSBFirst
#define MSBFirst	1
#endif

static int byte_order;

static void get_byte_order (void) {
	short w = 255;
	unsigned char *s = (unsigned char *) &w;
	if (*s) byte_order = LSBFirst;
	else byte_order = MSBFirst;
	}

/*----------------------------------------------------------------------*\
 | Static data															|
\*----------------------------------------------------------------------*/

static Display *display;
static int screen;
static Colormap cmap = (Colormap)0;
static GC gc;
static GC ggc;
static GC pgc;
static GC rgc;
static XFontStruct *font_info;
static int font_height,font_width;
static unsigned g_width,g_height;
static unsigned t_width,t_height;
static unsigned p_width,p_height;
static Window t_win;
static Window g_win;
static Window p_win;
static unsigned long foreground,background;
static unsigned long white_pixel,black_pixel;
static XSizeHints size_hints;
static XrmDatabase commandlineDB, rDB;
static unsigned border_width;
static char window_name[64];
static char program[64];
static char name_of_file[128] = "";

static int tabstop = 4;
static char tab_character = 0x1F;
static char tab_fill_character = 0x20;

static unsigned long color[256];
static unsigned long missing_color,land_color;

static int mouse_x = 0, mouse_y = 0;
static unsigned char profile[12] = {252,252,252,252,252,252,252,252,252,252,252,252};

struct origin {
	char *ext;
	double latitude;
	double longitude;
	char *name;
	};

struct origin *origin;

static struct origin regions[] = {
	{"ag",  23.291015625, 335.654296875, "Agulhas Region"},
	{"io",  30.322265625,  31.904296875, "Indian Ocean"},
	{"na",  72.509765625, 261.826171875, "Western North Atlantic"},
	{"nae", 72.509765625, 313.505859375, "Eastern North Atlantic and Mediterranean"},
	{"nep", 68.994140625, 171.474609375, "Northeastern Pacific"},
	{"nwp", 68.994140625, 115.224609375, "Northwestern Pacific"},
	{"sa",  23.291015625, 290.654296875, "South Atlantic"},
	{"sep", 23.291015625, 201.005859375, "Southeastern Pacific"},
	{"swp", 23.291015625, 111.005859375, "Southwestern Pacific"},
	{NULL,             0,             0, "None"}
	};


struct sst_file {
	struct sst_file *prev;
	struct sst_file *next;
	char name[64];
	unsigned char *sst;
	XImage *image;
	};

static struct sst_file *first = NULL;
static struct sst_file *current = NULL;
static char *image_name = NULL;

static int grid = 0;

static int mx0 = 0, my0 = 0;

static short *depth = NULL;

static char *month_name[] = {
	"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
	};

static int p1x,p1y,p2x,p2y;
static double upa = 0, upb = 0, upc = 0, upd = 0;

#define nint(x) ((x - floor(x) < 0.5) ? (int)(x) : (int)(x + 0.5))

static char *help[] = {
	"File selection    ",
	"  PgUp  = Previous",
	"  PgDn  = Next    ",
	"  Home  = First   ",
	"  End   = Last    ",
	"  Enter = View    ",
	"                  ",
	"Query Points      ",
	"  Mouse = Select  ",
	"  Left  = West    ",
	"  Right = East    ",
	"  Up    = North   ",
	"  Down  = South   ",
	"                  ",
	"Grid on/off       ",
	"  G = toggle grid ",
	"                  ",
	"Exit              ",
	"  Alt-X           ",
	"  Alt-Q           ",
	NULL
	};

static int opTableEntries = 19;
static XrmOptionDescRec opTable[] = {
{"-background",		"*background",					XrmoptionSepArg,	(caddr_t) NULL},
{"-bd",				"*borderColor",					XrmoptionSepArg,	(caddr_t) NULL},
{"-bg",				"*background",					XrmoptionSepArg,	(caddr_t) NULL},
{"-borderwidth",	"*TopLevelShell.borderWidth",	XrmoptionSepArg,	(caddr_t) NULL},
{"-bordercolor",	"*borderColor",					XrmoptionSepArg,	(caddr_t) NULL},
{"-bw",				"*TopLevelShell.borderWidth",	XrmoptionSepArg,	(caddr_t) NULL},
{"-display",		".display",						XrmoptionSepArg,	(caddr_t) NULL},
{"-fg",				"*foreground",					XrmoptionSepArg,	(caddr_t) NULL},
{"-fn",				"*font",						XrmoptionSepArg,	(caddr_t) NULL},
{"-font",			"*font",						XrmoptionSepArg,	(caddr_t) NULL},
{"-foreground",		"*foreground",					XrmoptionSepArg,	(caddr_t) NULL},
{"-geometry",		".TopLevelShell.geometry",		XrmoptionSepArg,	(caddr_t) NULL},
{"-iconic",			".TopLevelShell.iconic",		XrmoptionNoArg,		(caddr_t) "on"},
{"-name",			".name",						XrmoptionSepArg,	(caddr_t) NULL},
{"-reverse",		"*reverseVideo",				XrmoptionNoArg,		(caddr_t) "on"},
{"-rv",				"*reverseVideo",				XrmoptionNoArg,		(caddr_t) "on"},
{"-force",			"*forceColors",					XrmoptionSepArg,	(caddr_t) NULL},
{"-title",			".TopLevelShell.title",			XrmoptionSepArg,	(caddr_t) NULL},
{"-xrm",			NULL,							XrmoptionResArg,	(caddr_t) NULL}
};

/*----------------------------------------------------------------------*\
 | Procedures. All are static except main().							|
\*----------------------------------------------------------------------*/

static void HSV_to_RGB (double *r, double *g, double *b, double h, double s, double v);

static void create_colormap (void) {
	int i;
	int colormap_size;
	double h,s,v,r,g,b,delta;
	XColor cell;

	if (!cmap) cmap = XDefaultColormap (display,screen);

	if (cmap != XDefaultColormap (display,screen))
		for (i=0; i < 60; i++) {
			cell.pixel = i;
			cell.flags = DoRed | DoGreen | DoBlue;
			XQueryColor (display,XDefaultColormap (display,screen),&cell);
			XAllocColor (display,cmap,&cell);
			}

	s = v = 1.0;
	delta = 360.0/192.0;
	for (i=0; i < 192; i++) {
		h = 360.0 - delta * (double) (i+32);
		if (h < 0.0) h += 360.0;
		HSV_to_RGB (&r,&g,&b,h,s,v);
		cell.pixel = i+60;
		cell.flags = DoRed | DoGreen | DoBlue;
		cell.red   = (unsigned short) (r * 65535);
		cell.green = (unsigned short) (g * 65535);
		cell.blue  = (unsigned short) (b * 65535);
		if (XAllocColor (display,cmap,&cell)) color[i] = cell.pixel;
		else {
			fprintf (stderr,"Warning: could not allocate color (0x%04X,0x%04X,0x%04X)\n",cell.red,cell.green,cell.blue);
			color[i] = i;
			}
		}
	cell.pixel = i++;
	cell.flags = DoRed | DoGreen | DoBlue;
	cell.red   = 0xA000;
	cell.green = 0x8000;
	cell.blue  = 0x0000;
	if (XAllocColor (display,cmap,&cell)) color[255] = cell.pixel;
	else color[255] = BlackPixel(display,screen);

	cell.pixel = i++;
	cell.flags = DoRed | DoGreen | DoBlue;
	cell.red   = 0x0000;
	cell.green = 0x0000;
	cell.blue  = 0x0000;
	if (XAllocColor (display,cmap,&cell)) color[254] = cell.pixel;
	else color[254] = BlackPixel(display,screen);
	black_pixel = color[254];

	cell.pixel = i++;
	cell.flags = DoRed | DoGreen | DoBlue;
	cell.red   = 0xFFFF;
	cell.green = 0xFFFF;
	cell.blue  = 0xFFFF;
	if (XAllocColor (display,cmap,&cell)) color[252] = cell.pixel;
	else color[252] = WhitePixel(display,screen);
	white_pixel = color[252];
	}

static void parseOpenDisp (int *argc, char *argv[]) {
	XrmValue value;
	char *str_type[20];
	char myDisplayName[128];
	char str_name[128];

	myDisplayName[0] = 0;

	XrmParseCommand (&commandlineDB,opTable,opTableEntries,argv[0],argc,argv);

	if (*argc > 1) strcpy (name_of_file,argv[1]);

	strcpy (str_name,program);
	strcat (str_name,".display");

	if (XrmGetResource (commandlineDB,str_name,"See.Display",str_type,&value) == True)
		strncpy (myDisplayName,value.addr,(int)value.size);

	if (!(display = XOpenDisplay (myDisplayName))) {
		fprintf (stderr,"%s: Can't open display '%s'\n",argv[0],XDisplayName (myDisplayName));
		exit (1);
		}

	screen = DefaultScreen (display);
	}

static char *GetHomeDir (char *dest) {
	uid_t uid;
	extern uid_t getuid (void);
	struct passwd *pw;
	char *ptr;

	if (ptr = getenv ("HOME")) strcpy (dest,ptr);
	else {
		if (ptr = getenv ("USER")) pw = getpwnam (ptr);
		else {
			uid = getuid();
			pw = getpwuid (uid);
			}
		if (pw) strcpy (dest,pw->pw_dir);
		else strcpy (dest," ");
		}
	return (dest);
	}

static void getUsersDatabase (void) {
	XrmDatabase homeDB, serverDB, applicationDB;

	char filenamebuf[1024];
	char *filename = filenamebuf;
	char *environment;
	char *classname = "See";
	char name[255];
	char *string;

	strcpy (name,"/usr/lib/X11/app-defaults/");
	strcat (name,classname);
	applicationDB = XrmGetFileDatabase (name);
	XrmMergeDatabases (applicationDB,&rDB);

	if (string = XResourceManagerString (display))
		serverDB = XrmGetStringDatabase (string);
	else {
		GetHomeDir (filename);
		strcat (filename,".Xdefaults");
		serverDB = XrmGetFileDatabase (filename);
		}
	XrmMergeDatabases (serverDB,&rDB);

	if ((environment = getenv ("XENVIRONMENT")) == NULL) {
		int len;
		environment = GetHomeDir (filename);
		strcat (environment,".Xdefaults");
		len = strlen (environment);
		gethostname (environment+len,1024-len);
		}
	homeDB = XrmGetFileDatabase (environment);
	XrmMergeDatabases (homeDB,&rDB);
	}

static void mergeOpts (void) {
	char str_name[128];
	char *str_type[20];
	char buffer[20];
	long flags;
	XrmValue value;
	int x,y;
	XColor screen_def;
	char fontname[128];

	XrmMergeDatabases (commandlineDB, &rDB);

	/*------------------------------------------------------------------*\
	 | Determine whether a new colormap should be used.					|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".forceColors");

	if (XrmGetResource (rDB,str_name,"See.ForceColors",str_type,&value) == True)
		strncpy (buffer,value.addr,(int)value.size);
	else
		strcpy (buffer,"False");

	if (memcmp (buffer,"True",4) == 0)
		cmap = XCreateColormap (display,RootWindow(display,screen),DefaultVisual(display,screen),AllocNone);
	else
		cmap = XDefaultColormap (display,screen);

	create_colormap();

	/*------------------------------------------------------------------*\
	 | Get font info from user preference database and load the font.	|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".font");

	if (XrmGetResource (rDB,str_name,"See.Font",str_type,&value) == True)
		strncpy (fontname,value.addr,(int)value.size);
	else
		strcpy (fontname,"9x15bold");

	if ((font_info = XLoadQueryFont (display,fontname)) == NULL) {
		fprintf (stderr,"see: cannot open font %s\n",fontname);
		exit (-1);
		}
	font_height = font_info->max_bounds.ascent + font_info->max_bounds.descent;
	font_width = XTextWidth (font_info," ",1);

	x = 0;
	y = 0;
	t_width  = 700;
	t_height = 800;

	/*------------------------------------------------------------------*\
	 | Get geometry info from user preference database. Assume that the |
	 | width and height figures are specified in characters, so multiply|
	 | the given values by font_width and font_height, respectively.	|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".geometry");

	if (XrmGetResource (rDB,str_name,"See.Geometry",str_type,&value) == True)
		strncpy (buffer,value.addr,(int)value.size);
	else
		buffer[0] = 0;

	if (*buffer) {
		flags = XParseGeometry (buffer,&x,&y,&t_width,&t_height);
		if (XValue & flags)
			if (XNegative & flags)
				x = DisplayWidth (display,screen) + x - t_width;
		if (YValue & flags)
			if (YNegative & flags)
				y = DisplayHeight (display,screen) + y - t_height;
		}

	size_hints.x = x;
	size_hints.y = y;
	size_hints.width = t_width;
	size_hints.height = t_height;

	/*------------------------------------------------------------------*\
	 | Get foreground color info from user database.					|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".foreground");

	if (XrmGetResource (rDB,str_name,"See.Foreground",str_type,&value) == True) {
		strncpy (buffer,value.addr,(int)value.size);
		if (XParseColor (display,cmap,buffer,&screen_def) == 0)
			foreground = black_pixel;
		else {
			XAllocColor (display,cmap,&screen_def);
			foreground = screen_def.pixel;
			}
		}
	else
		foreground = black_pixel;

	/*------------------------------------------------------------------*\
	 | Get background color info from user database.					|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".background");

	if (XrmGetResource (rDB,str_name,"See.Background",str_type,&value) == True) {
		strncpy (buffer,value.addr,(int)value.size);
		if (XParseColor (display,cmap,buffer,&screen_def) == 0)
			background = white_pixel;
		else {
			XAllocColor (display,cmap,&screen_def);
			background = screen_def.pixel;
			}
		}
	else
		background = white_pixel;

	/*------------------------------------------------------------------*\
	 | Get border width info from user database.						|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".borderWidth");

	if (XrmGetResource (rDB,str_name,"See.BorderWidth",str_type,&value) == True) {
		strncpy (buffer,value.addr,(int)value.size);
		border_width = (unsigned) strtoul (buffer,0,0);
		}
	else
		border_width = 4;

	/*------------------------------------------------------------------*\
	 | Get window name from user database.								|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".name");

	if (XrmGetResource (rDB,str_name,"See.Name",str_type,&value) == True)
		strncpy (window_name,value.addr,(int)value.size);
	else
		strcpy (window_name,"MCSST");
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void user_exit (void) {
	XUnloadFont (display,font_info->fid);
	XFreeGC (display,gc);
	XFreeGC (display,rgc);
	XFreeGC (display,ggc);
	XFreeGC (display,pgc);
	XCloseDisplay (display);
	exit (0);
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static int io_error_handler (Display *display) {
	user_exit();
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void spectrum (void);
static void set_origin (char *s);
static void refresh_graphics (XExposeEvent *event);
static void refresh_plot (XExposeEvent *event);
static void refresh_text (XExposeEvent *event);
static void process_key (XKeyEvent *event);
static int read_sst_file (FILE *f, unsigned char *dst);

void main (int argc, char *argv[]) {
	int i,j,k;
	long n;
	int x = 0, y = 0;
	char *icon_name = "see";
	Pixmap icon_pixmap;
	XEvent report;
	unsigned long valuemask = 0;
	XGCValues values;
	XWMHints wmhints;
	FILE *in,*list;
	char *s;
	char filename [FILENAME_MAX];

	strcpy (program,argv[0]);

	XrmInitialize();
	parseOpenDisp (&argc,argv);
	getUsersDatabase();
	mergeOpts();

	get_byte_order();

	/* create window */
	t_win = XCreateSimpleWindow (display,
								 RootWindow(display,screen),
								 x,y,t_width,t_height,
								 border_width,
								 foreground,
								 background);

	/* create pixmap of depth 1 (bitmap) for icon */
	icon_pixmap = XCreateBitmapFromData (display,t_win,see_bits,see_width,see_height);

	/* initialize size hint property for window manager */
	size_hints.flags = PPosition | PSize | PMinSize;
	size_hints.min_width = 0;
	size_hints.min_height = 0;

	/* set properties for window manager (always before mapping) */
	XSetStandardProperties (display,
							t_win,
							window_name,
							icon_name,
							icon_pixmap,
							argv,
							argc,
							&size_hints);

	/* Select event types wanted */
	XSelectInput (display,t_win,
				  ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask);

	/* Create graphics contexts */

	gc = XCreateGC (display,t_win,valuemask,&values);
	XSetFont (display,gc,font_info->fid);
	XSetBackground (display,gc,background);
	XSetForeground (display,gc,foreground);

	rgc = XCreateGC (display,t_win,valuemask,&values);
	XSetFont (display,rgc,font_info->fid);
	XSetBackground (display,rgc,foreground);
	XSetForeground (display,rgc,background);

	wmhints.flags = InputHint | IconPixmapHint;
	wmhints.input = True;
	wmhints.icon_pixmap = icon_pixmap;
	XSetWMHints (display,t_win,&wmhints);

	/* create graphics window */
	x = font_width;
	y = 3 * font_height;
	g_width = 512;
	g_height = 512;
	g_win = XCreateSimpleWindow (display,t_win,x,y,g_width,g_height,1,foreground,background);
	icon_pixmap = XCreateBitmapFromData (display,g_win,see_bits,see_width,see_height);
	XSetStandardProperties (display,g_win,window_name,icon_name,icon_pixmap,argv,argc,&size_hints);
	XSelectInput (display,g_win, ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask);
	XSetWindowColormap (display,t_win,cmap);
	ggc = XCreateGC (display,g_win,valuemask,&values);
	XSetBackground (display,ggc,background);
	XSetFont (display,ggc,font_info->fid);
	XSetForeground (display,ggc,foreground);

	/* create plot window */
	x = font_width;
	y = 3 * font_height + g_height + 2 + font_width;
	p_width = 512;
	p_height = 224;
	p_win = XCreateSimpleWindow (display,t_win,x,y,p_width,p_height,1,foreground,background);
	icon_pixmap = XCreateBitmapFromData (display,p_win,see_bits,see_width,see_height);
	XSetStandardProperties (display,p_win,window_name,icon_name,icon_pixmap,argv,argc,&size_hints);
	XSelectInput (display,p_win, ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask);
	pgc = XCreateGC (display,p_win,valuemask,&values);
	XSetFont (display,pgc,font_info->fid);
	XSetBackground (display,pgc,background);
	XSetForeground (display,pgc,foreground);

	/*------------------------------------------------------------------*\
	 | Allocate space for a linked list of sst files.					|
	\*------------------------------------------------------------------*/

	if (!(first = (struct sst_file *) malloc (sizeof(struct sst_file)))) {
		printf ("Error: unable to allocate memory for first sst_file\n");
		exit (1);
		}
	memset (first,0,sizeof(struct sst_file));

	current = first;

	if (*name_of_file) {

		/*--------------------------------------------------------------*\
		 | Use the input file as a list of file names to read.			|
		\*--------------------------------------------------------------*/

		if (list = fopen (name_of_file,"r")) {
			while (fgets (filename,64,list)) {
				if (s = strrchr (filename,'\n')) *s = 0;
				if (s = strrchr (filename,'\r')) *s = 0;

				/*------------------------------------------------------*\
				 | If the current file's sst array has already been		|
				 | allocated, get a new sst_file structure, and point	|
				 | current to it.										|
				\*------------------------------------------------------*/

				if (current->sst) {
					current->next = (struct sst_file *) malloc (sizeof(struct sst_file));
					if (!current->next) {
						printf ("Error: unable to allocate memory for file %s\n",filename);
						exit (1);
						}
					memset (current->next,0,sizeof(struct sst_file));
					current->next->prev = current;
					current->next->next = NULL;
					current = current->next;
					}

				/*------------------------------------------------------*\
				 | Set current->name to the file name, then allocate the|
				 | sst array, and read the data into it.				|
				\*------------------------------------------------------*/

				strcpy (current->name,filename);
				if (current->sst = (unsigned char *) malloc (512*512))
					memset (current->sst,0,512*512);
				else {
					printf ("Error: unable to allocate memory for sst data for file %s\n",current->name);
					exit (1);
					}

				if (in = fopen (current->name,"r")) {
					read_sst_file (in,current->sst);
					fclose (in);
					}
				}
			fclose (list);
			}
		else {
			printf ("Error: The file list \"%s\" could not be opened.\n",name_of_file);
			exit (1);
			}
		}
	else {
		printf ("Error: You must specify a list file on the command line\n");
		exit (1);
		}
	current = first;
	set_origin (current->name);
	image_name = current->name;

	/*------------------------------------------------------------------*\
	 | Open a depth file in the same place as the SST files				|
	\*------------------------------------------------------------------*/

	strcpy (filename,current->name);
	if (s = strrchr (filename,'/')) *(s+1) = 0;
	else *filename = 0;
	strcat (filename,"etopo5.");
	strcat (filename,origin->ext);
	if (in = fopen (filename,"rb")) {
		if (depth = (short *) malloc (sizeof (short) * 512*512))
			if ((n = fread (depth,sizeof(short),512*512,in)) != 512*512) {
				free (depth);
				depth = NULL;
				}
			else
				if (byte_order != MSBFirst) {
					char *scratch;
					n = 512L * sizeof (short);
					if (scratch = (char *) malloc (n)) {
						s = (char *)depth;
						for (i=0; i < 512; i++) {
							swab (s,scratch,n);
							memcpy (s,scratch,n);
							s += n;
							}
						free (scratch);
						}
					}
		fclose (in);
		}

	/* Display the windows */
	XMapWindow (display,t_win);
	XMapWindow (display,g_win);
	XMapWindow (display,p_win);

	/*------------------------------------------------------------------*\
	 | Set a fatal IO error handler to exit gracefully when the user	|
	 | closes the window through the window manager.					|
	\*------------------------------------------------------------------*/

	XSetIOErrorHandler (io_error_handler);

	/*------------------------------------------------------------------*\
	 | Event loop														|
	\*------------------------------------------------------------------*/

	while (1) {
		XNextEvent (display,&report);
		switch (report.type) {

			case Expose:
				if (report.xexpose.window == t_win) refresh_text (&report.xexpose);
				if (report.xexpose.window == g_win) refresh_graphics (&report.xexpose);
				if (report.xexpose.window == p_win) refresh_plot (&report.xexpose);
				break;

			case ConfigureNotify:
				if (report.xconfigure.window == t_win) {
					t_width = report.xconfigure.width;
					t_height = report.xconfigure.height;
					}
				else
					if (report.xconfigure.window == g_win) {
						g_width = report.xconfigure.width;
						g_height = report.xconfigure.height;
						}
					else
						if (report.xconfigure.window == p_win) {
							p_width = report.xconfigure.width;
							p_height = report.xconfigure.height;
							}
				break;

			case ButtonPress:
				if (report.xbutton.window == g_win) {
					mouse_x = report.xbutton.x;
					mouse_y = report.xbutton.y;
					refresh_plot (0);
					refresh_text (0);
					}
				break;

			case KeyPress:
				process_key (&report.xkey);
				break;

			case MappingNotify:
				XRefreshKeyboardMapping (&report.xmapping);
				break;

			default:
				break;
			}
		}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void HSV_to_RGB (double *r, double *g, double *b, double h, double s, double v) {
	int i;
	double f,p,q,t;
	if (s == 0) {
		if (h < 0) {
			*r = v;
			*g = v;
			*b = v;
			}
		else {
			puts ("s == 0 and h >= 0");
			return;
			}
		}
	else {
		if (h == 360.0) h = 0;
		h /= 60.0;
		i = (int) floor (h);
		f = h - (double)i;
		p = v * (1 - s);
		q = v * (1 - (s*f));
		t = v * (1 - (s * (1 - f)));
		switch (i) {
			case 0: *r = v; *g = t; *b = p; break;
			case 1: *r = q; *g = v; *b = p; break;
			case 2: *r = p; *g = v; *b = t; break;
			case 3: *r = p; *g = q; *b = v; break;
			case 4: *r = t; *g = p; *b = v; break;
			case 5: *r = v; *g = p; *b = q; break;
			default:
				break;
			}
		}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void set_origin (char *s) {
	char *t;
	int i;

	if (t = strrchr (s,'.')) {
		t++;
		for (i=0; regions[i].ext; i++)
			if (strcmp (t,regions[i].ext) == 0) {
				origin = regions + i;
				break;
				}
		}
	else {
		printf ("Error: Unknown region!\n");
		origin = regions + 9;
		}
	}

static void draw_grid (void) {
	int x,y;
	double dlat,dlong;

	XSetForeground (display,ggc,black_pixel);

	dlat = 10 * floor(origin->latitude/10);
	x = 0;
	y = nint((origin->latitude - dlat)/0.1757812);
	while (y < g_height) {
		if (grid) XDrawLine (display,g_win,ggc,0,y,g_width,y);
		else {
			XDrawLine (display,g_win,ggc,x,y,x+8,y);
			XDrawLine (display,g_win,ggc,g_width-8,y,g_width,y);
			}
		dlat -= 10;
		y = nint((origin->latitude - dlat)/0.1757812);
		}

	dlong = 10 * ceil(origin->longitude/10);
	x = nint((dlong - origin->longitude)/.1757812);
	y = 0;
	while (x < g_width) {
		if (grid) XDrawLine (display,g_win,ggc,x,0,x,g_height);
		else {
			XDrawLine (display,g_win,ggc,x,y,x,y+8);
			XDrawLine (display,g_win,ggc,x,g_height-8,x,g_height);
			}
		dlong += 10;
		x = nint((dlong - origin->longitude)/.1757812);
		}
	}

static void hexdump (unsigned char *b, int n) {
	int i = 0;

	if (!n) return;
	do {
		if (i && !(i & 0xF)) printf ("\n");
		printf ("%02X ",b[i++]);
		} while (i < n);
	printf ("\n");
	}

static void describe_image (XImage *image) {
	int i;
	char *format, *byte_order_tag, *bit_order_tag;
	unsigned char *s;

	printf ("XImage at 0x%X = {\n",image);
	printf ("\twidth            = %d\n",image->width);
	printf ("\theight           = %d\n",image->height);
	printf ("\txoffset          = %d\n",image->xoffset);

	if (image->format == XYBitmap) format = "XYBitmap";
	if (image->format == XYPixmap) format = "XYPixmap";
	if (image->format ==  ZPixmap) format =  "ZPixmap";
	printf ("\tformat           = %s\n",format);
	printf ("\tdata address     = 0x%X\n",image->data);

	if (image->byte_order == LSBFirst) byte_order_tag = "LSBFirst";
	if (image->byte_order == MSBFirst) byte_order_tag = "MSBFirst";
	printf ("\tbyte_order       = %s\n",byte_order_tag);
	printf ("\tbitmap_unit      = %d\n",image->bitmap_unit);

	if (image->bitmap_bit_order == LSBFirst) bit_order_tag = "LSBFirst";
	if (image->bitmap_bit_order == MSBFirst) bit_order_tag = "MSBFirst";
	printf ("\tbitmap_bit_order = %s\n",bit_order_tag);
	printf ("\tbitmap_pad       = %d\n",image->bitmap_pad);
	printf ("\tdepth            = %d\n",image->depth);
	printf ("\tbytes_per_line   = %d\n",image->bytes_per_line);
	printf ("\tbits_per_pixel   = %d\n",image->bits_per_pixel);
	printf ("\tred_mask         = 0x%X\n",image->red_mask);
	printf ("\tgreen_mask       = 0x%X\n",image->green_mask);
	printf ("\tblue_mask        = 0x%X\n",image->blue_mask);
	printf ("\tobdata           = 0x%X\n",image->obdata);
	printf ("\tcreate_image()   = 0x%X\n",image->f.create_image);
	printf ("\tdestroy_image()  = 0x%X\n",image->f.destroy_image);
	printf ("\tget_pixel()      = 0x%X\n",image->f.get_pixel);
	printf ("\tput_pixel()      = 0x%X\n",image->f.put_pixel);
	printf ("\tsub_image()      = 0x%X\n",image->f.sub_image);
	printf ("\tadd_pixel()      = 0x%X\n",image->f.add_pixel);
	printf ("\t};\n");

	printf ("image->data at 0x%X = {\n",image->data);
	s = image->data;
	for (i=0; i < image->depth; i++) {
		printf ("bit plane %d:\n",i);
		hexdump (s,image->bytes_per_line);
		s += image->height * image->bytes_per_line;
		}
	printf ("\n};\n");
	}

static void msb_scramble8 (unsigned char *s) {
	unsigned char d[8];

	d[0] = 	(s[0] & 0x80) >> 0 | (s[1] & 0x80) >> 1 | (s[2] & 0x80) >> 2 | (s[3] & 0x80) >> 3 | (s[4] & 0x80) >> 4 | (s[5] & 0x80) >> 5 | (s[6] & 0x80) >> 6 | (s[7] & 0x80) >> 7;
	d[1] = 	(s[0] & 0x40) << 1 | (s[1] & 0x40) >> 0 | (s[2] & 0x40) >> 1 | (s[3] & 0x40) >> 2 | (s[4] & 0x40) >> 3 | (s[5] & 0x40) >> 4 | (s[6] & 0x40) >> 5 | (s[7] & 0x40) >> 6;
	d[2] = 	(s[0] & 0x20) << 2 | (s[1] & 0x20) << 1 | (s[2] & 0x20) >> 0 | (s[3] & 0x20) >> 1 | (s[4] & 0x20) >> 2 | (s[5] & 0x20) >> 3 | (s[6] & 0x20) >> 4 | (s[7] & 0x20) >> 5;
	d[3] = 	(s[0] & 0x10) << 3 | (s[1] & 0x10) << 2 | (s[2] & 0x10) << 1 | (s[3] & 0x10) >> 0 | (s[4] & 0x10) >> 1 | (s[5] & 0x10) >> 2 | (s[6] & 0x10) >> 3 | (s[7] & 0x10) >> 4;
	d[4] = 	(s[0] & 0x08) << 4 | (s[1] & 0x08) << 3 | (s[2] & 0x08) << 2 | (s[3] & 0x08) << 1 | (s[4] & 0x08) >> 0 | (s[5] & 0x08) >> 1 | (s[6] & 0x08) >> 2 | (s[7] & 0x08) >> 3;
	d[5] = 	(s[0] & 0x04) << 5 | (s[1] & 0x04) << 4 | (s[2] & 0x04) << 3 | (s[3] & 0x04) << 2 | (s[4] & 0x04) << 1 | (s[5] & 0x04) >> 0 | (s[6] & 0x04) >> 1 | (s[7] & 0x04) >> 2;
	d[6] = 	(s[0] & 0x02) << 6 | (s[1] & 0x02) << 5 | (s[2] & 0x02) << 4 | (s[3] & 0x02) << 3 | (s[4] & 0x02) << 2 | (s[5] & 0x02) << 1 | (s[6] & 0x02) >> 0 | (s[7] & 0x02) >> 1;
	d[7] = 	(s[0] & 0x01) << 7 | (s[1] & 0x01) << 6 | (s[2] & 0x01) << 5 | (s[3] & 0x01) << 4 | (s[4] & 0x01) << 3 | (s[5] & 0x01) << 2 | (s[6] & 0x01) << 1 | (s[7] & 0x01) << 0;
	s[0] = d[0]; s[1] = d[1]; s[2] = d[2]; s[3] = d[3]; s[4] = d[4]; s[5] = d[5]; s[6] = d[6]; s[7] = d[7];
	}

static void lsb_scramble8 (unsigned char *s) {
	unsigned char d[8];

	d[0] = 	(s[7] & 0x80) >> 0 | (s[6] & 0x80) >> 1 | (s[5] & 0x80) >> 2 | (s[4] & 0x80) >> 3 | (s[3] & 0x80) >> 4 | (s[2] & 0x80) >> 5 | (s[1] & 0x80) >> 6 | (s[0] & 0x80) >> 7;
	d[1] = 	(s[7] & 0x40) << 1 | (s[6] & 0x40) >> 0 | (s[5] & 0x40) >> 1 | (s[4] & 0x40) >> 2 | (s[3] & 0x40) >> 3 | (s[2] & 0x40) >> 4 | (s[1] & 0x40) >> 5 | (s[0] & 0x40) >> 6;
	d[2] = 	(s[7] & 0x20) << 2 | (s[6] & 0x20) << 1 | (s[5] & 0x20) >> 0 | (s[4] & 0x20) >> 1 | (s[3] & 0x20) >> 2 | (s[2] & 0x20) >> 3 | (s[1] & 0x20) >> 4 | (s[0] & 0x20) >> 5;
	d[3] = 	(s[7] & 0x10) << 3 | (s[6] & 0x10) << 2 | (s[5] & 0x10) << 1 | (s[4] & 0x10) >> 0 | (s[3] & 0x10) >> 1 | (s[2] & 0x10) >> 2 | (s[1] & 0x10) >> 3 | (s[0] & 0x10) >> 4;
	d[4] = 	(s[7] & 0x08) << 4 | (s[6] & 0x08) << 3 | (s[5] & 0x08) << 2 | (s[4] & 0x08) << 1 | (s[3] & 0x08) >> 0 | (s[2] & 0x08) >> 1 | (s[1] & 0x08) >> 2 | (s[0] & 0x08) >> 3;
	d[5] = 	(s[7] & 0x04) << 5 | (s[6] & 0x04) << 4 | (s[5] & 0x04) << 3 | (s[4] & 0x04) << 2 | (s[3] & 0x04) << 1 | (s[2] & 0x04) >> 0 | (s[1] & 0x04) >> 1 | (s[0] & 0x04) >> 2;
	d[6] = 	(s[7] & 0x02) << 6 | (s[6] & 0x02) << 5 | (s[5] & 0x02) << 4 | (s[4] & 0x02) << 3 | (s[3] & 0x02) << 2 | (s[2] & 0x02) << 1 | (s[1] & 0x02) >> 0 | (s[0] & 0x02) >> 1;
	d[7] = 	(s[7] & 0x01) << 7 | (s[6] & 0x01) << 6 | (s[5] & 0x01) << 5 | (s[4] & 0x01) << 4 | (s[3] & 0x01) << 3 | (s[2] & 0x01) << 2 | (s[1] & 0x01) << 1 | (s[0] & 0x01) << 0;
	s[0] = d[0]; s[1] = d[1]; s[2] = d[2]; s[3] = d[3]; s[4] = d[4]; s[5] = d[5]; s[6] = d[6]; s[7] = d[7];
	}

static void (*scramble8)(unsigned char *s);

static void convert_image (unsigned char *dst, unsigned char *src) {
	int x,y,k;
	unsigned char b[8];
	unsigned char *s,*d;
	s = src;
	d = dst;

	for (y=0; y < 512; y++)
		for (x=0; x < 512; x += 8) {
			memcpy (b,s,8);
			for (k=0; k < 8; k++) b[k] = color[b[k]];
			scramble8 (b);
			for (k=0; k < 8; k++) *(d + k * 32768) = b[k];
			s += 8;
			d++;
			}
	}

static XImage *create_image (unsigned char *image_data, unsigned width, unsigned height) {
	Visual *p_visual;
	XImage *image;
	unsigned depth;
	int format;
	int offset;
	int bitmap_pad;
	int bytes_per_line;

	depth = 8;
	p_visual = DefaultVisual (display,screen);
	format = XYPixmap;
	offset = 0;
	bitmap_pad = 32;
	bytes_per_line = width/depth;

	image = XCreateImage (display,p_visual,depth,format,offset,image_data,width,height,bitmap_pad,bytes_per_line);

	if (image->bitmap_bit_order == MSBFirst)
		scramble8 = msb_scramble8;
	else
		scramble8 = lsb_scramble8;

	return (image);
	}

#define FAST_IMAGE		1

static void refresh_graphics (XExposeEvent *event) {
	int x,y;
	unsigned w,h;
	unsigned char *s,c;

	if (!current->image) {
		#ifdef FAST_IMAGE
			if (!(s = (unsigned char *) malloc (512*512))) {
				printf ("Error: unable to allocate space for image %s\n",current->name);
				return;
				}
			if (current->image = create_image (s,512,512)) {
				convert_image (s,current->sst);
				/* describe_image (current->image); */
				}
			else {
				printf ("Error: unable to create XImage for %s\n",current->name);
				return;
				}
		#else
			s = current->sst;
			for (y=0; y < g_height; y++)
				for (x=0; x < g_width; x++) {
					XSetForeground (display,ggc,color[*s++]);
					XDrawPoint (display,g_win,ggc,x,y);
					}
			current->image = XGetImage (display,g_win,0,0,g_width,g_height,0xFF,XYPixmap);
			/* describe_image (current->image); */
			draw_grid();
		#endif
		}
	if (event) {
		x = event->x;
		y = event->y;
		w = (unsigned) event->width;
		h = (unsigned) event->height;
		}
	else {
		x = 0;
		y = 0;
		w = 512;
		h = 512;
		}
	XPutImage (display,g_win,ggc,current->image,x,y,x,y,w,h);
	draw_grid();
	image_name = current->name;
	}

static void refresh_plot (XExposeEvent *event) {
	int x,y,x0,y0;
	int i,k,n;
	FILE *in;
	struct sst_file *p;
	int px,py;
	double u1x,u1y,u2x,u2y;
	char string[8];

	if (mouse_x != mx0 || mouse_y != my0) {
		k = 512 * mouse_y + mouse_x;
		for (n=0, p=first; p; p=p->next, n++) profile[n] = (unsigned char) p->sst[k];
		mx0 = mouse_x;
		my0 = mouse_y;
		}

	if (event) {
		XClearWindow (display,p_win);

		/*--------------------------------------------------------------*\
		 | Calculate the transformation for plotting the data.			|
		\*--------------------------------------------------------------*/

		p1x = 5 * font_width;
		p1y = p_height - 2 * font_height;
		p2x = p_width - 2 * font_width;
		p2y = font_height;

		u1x = -0.5;
		u1y = -3.0;
		u2x = 11.5;
		u2y = 35.0;

		upa = ((double) (p2x - p1x)) / (u2x - u1x);
		upb = ((double) p1x) - u1x * upa;
		upc = ((double) (p2y - p1y)) / (u2y - u1y);
		upd = ((double) p1y) - u1y * upc;

		/*--------------------------------------------------------------*\
		 | Axis edges (lower left)										|
		\*--------------------------------------------------------------*/

		XDrawLine (display,p_win,pgc,p1x,p2y,p1x,p1y);
		XDrawLine (display,p_win,pgc,p1x,p1y,p2x,p1y);

		/*--------------------------------------------------------------*\
		 | X axis ticks and labels										|
		\*--------------------------------------------------------------*/

		for (i=0; i < 12; i++) {
			px = nint (upa * (double)i + upb);
			py = p1y;
			XDrawLine (display,p_win,pgc,px,py,px,py+4);
			px -= 3 * font_width/2;
			py += font_height;
			XDrawImageString (display,p_win,pgc,px,py,month_name[i],3);
			}

		/*--------------------------------------------------------------*\
		 | Y axis ticks and labels										|
		\*--------------------------------------------------------------*/

		for (i=0; i < 4; i++) {
			px = p1x;
			py = nint (upc * 10.0 * (double)i + upd);
			XDrawLine (display,p_win,pgc,px,py,px-4,py);
			px -= 7 * font_width/2;
			py += font_height/3;
			sprintf (string,"%2d\260",10*i);
			XDrawImageString (display,p_win,pgc,px,py,string,strlen (string));
			}
		}
	else
		XClearArea (display,p_win,p1x+1,p2y,p2x-p1x,p1y-p2y,False);

	x0 = 0;
	y0 = 0;

	for (i=0; i < 12; i++) {
		px = nint (upa * (double)i + upb);
		py = nint (upc * (-2.1 + 0.2 * (double)profile[i]) + upd);
		if (profile[i] < 252) {
			XFillRectangle (display,p_win,pgc,px-2,py-2,5,5);
			if (y0 > p2y) XDrawLine (display,p_win,pgc,x0,y0,px,py);
			}
		x0 = px;
		y0 = py;
		}
	}

static int expand_tabs (char *t, char *s, int n) {
	int i = 0;
	while (*s && n > 0) {
		if (*s == '\t') {
			*t++ = tab_character;
			i++;
			while (i % tabstop) {
				*t++ = tab_fill_character;
				i++;
				}
			}
		else {
			*t++ = *s;
			i++;
			}
		s++;
		n--;
		if (i > MAXLEN - tabstop) break;
		}
	while (n > 0 && i < MAXLEN) {
		*t++ = ' ';
		i++;
		n--;
		}
	*t = 0;
	return (i);
	}

static void refresh_text (XExposeEvent *event) {
	int i,x,y,n,w;
	char string[MAXLEN];
	double latitude,longitude,temperature;
	char lat_letter,long_letter;
	struct sst_file *p;
	int d;

	x = font_width;
	y = font_height;

	strcpy (string,origin->name);
	strcat (string," (");
	strcat (string,image_name);
	strcat (string,")");
	n = strlen (string);
	w = 1 + t_width/font_width;
	if (n < w) {
		memset (string+n,' ',1 + w - n);
		n += 1 + w - n;
		}
	XDrawImageString (display,t_win,gc,x,y,string,n);

	/*------------------------------------------------------------------*\
	 | List files and data to the right of the image.					|
	\*------------------------------------------------------------------*/

	x = 2 * font_width + 514;
	y = 4 * font_height;

	for (i=0, p=first; p; p=p->next, i++) {
		if (profile[i] < 252) {
			temperature = -2.1 + 0.2 * (unsigned char) profile[i];
			sprintf (string," %s %4.1lf\260C ",p->name,temperature);
			}
		else sprintf (string," %s ------ ",p->name);
		if (p == current)
			XDrawImageString (display,t_win,rgc,x,y,string,strlen(string));
		else
			XDrawImageString (display,t_win,gc,x,y,string,strlen(string));
		y += font_height;
		}

	/*------------------------------------------------------------------*\
	 | Place instructions below file list.								|
	\*------------------------------------------------------------------*/

	if (event) {
		y += font_height;
		for (i=0; help[i]; i++) {
			XDrawImageString (display,t_win,gc,x,y,help[i],strlen(help[i]));
			y += font_height;
			}
		}

	/*------------------------------------------------------------------*\
	 | Describe the position of the most recent mouse button press.		|
	\*------------------------------------------------------------------*/

	x = font_width;
	y = 2 * font_height;

	latitude  = origin->latitude - mouse_y * 0.1757812;
	if (latitude < 0) {
		latitude = -latitude;
		lat_letter = 'S';
		}
	else lat_letter = 'N';

	longitude = origin->longitude + mouse_x * 0.1757812;
	if (longitude > 360) longitude -= 360;
	if (longitude > 180) {
		longitude = 360 - longitude;
		long_letter = 'W';
		}
	else long_letter = 'E';

	if (depth) {
		d = (int) depth [512 * my0 + mx0];
		n = (unsigned char) current->sst[512*mouse_y + mouse_x];
		if (n == 252) {
			if (d > 0)
				sprintf (string,"Lat %4.1lf\260%c  Long %5.1lf\260%c  Elev %5dm (missing)",
						 latitude,lat_letter,longitude,long_letter,abs(d));
			else
				sprintf (string,"Lat %4.1lf\260%c  Long %5.1lf\260%c  Depth %5dm (missing)",
						 latitude,lat_letter,longitude,long_letter,abs(d));
			}
		else
			if (n == 255)
				sprintf (string,"Lat %4.1lf\260%c  Long %5.1lf\260%c  Elev %5dm (land)",
						 latitude,lat_letter,longitude,long_letter,abs(d));
			else {
				temperature = -2.1 + 0.2 * n;
				sprintf (string,
						 "Lat %4.1lf\260%c  Long %5.1lf\260%c  Depth %5dm  SST %4.1lf\260C",
						 latitude,lat_letter,longitude,long_letter,abs(d),temperature);
				}
		}
	else {
		n = (unsigned char) current->sst[512*mouse_y + mouse_x];
		if (n == 252)
			sprintf (string,"Lat %4.1lf\260%c  Long %5.1lf\260%c  (missing)",
					 latitude,lat_letter,longitude,long_letter);
		else
			if (n == 255)
				sprintf (string,"Lat %4.1lf\260%c  Long %5.1lf\260%c  (land)",
						 latitude,lat_letter,longitude,long_letter);
			else {
				temperature = -2.1 + 0.2 * n;
				sprintf (string,
						 "Lat %4.1lf\260%c  Long %5.1lf\260%c  SST %4.1lf\260C",
						 latitude,lat_letter,longitude,long_letter,temperature);
				}
		}
	n = strlen (string);
	w = 1 + t_width/font_width;
	if (n < w) {
		memset (string+n,' ',1 + w - n);
		n += 1 + w - n;
		}
	XDrawImageString (display,t_win,gc,x,y,string,n);
	}

static void process_key (XKeyEvent *event) {
	int i,x,y,n;
	char buffer[20];
	int bufsize = 20;
	KeySym keysym;
	XComposeStatus status;
	int count;
	XEvent next_event;
	long mask;

	count = XLookupString (event,buffer,bufsize,&keysym,&status);

	if (event->state & Mod1Mask)
		switch (keysym) {
		case XK_q:
			user_exit();
			break;
		case XK_x:
			user_exit();
			break;
		default:
			break;
			}

	switch (keysym) {
		case XK_G:
		case XK_g:
			grid = (grid ? 0 : 1);
			refresh_graphics (0);
			break;
		case XK_F2:
			mask = ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask;
			while (!XCheckWindowEvent (display,t_win,mask,&next_event)) {
				if (current->next) current = current->next;
				else current = first;
				refresh_graphics (0);
				refresh_text (0);
				}
			XPutBackEvent (display,&next_event);
			break;
		case XK_KP_8:
		case XK_Up:
			if (mouse_y > 0) mouse_y--;
			refresh_plot (0);
			refresh_text (0);
			break;
		case XK_KP_2:
		case XK_Down:
			if (mouse_y < 511) mouse_y++;
			refresh_plot (0);
			refresh_text (0);
			break;
		case XK_KP_4:
		case XK_Left:
			if (mouse_x > 0) mouse_x--;
			refresh_plot (0);
			refresh_text (0);
			break;
		case XK_KP_6:
		case XK_Right:
			if (mouse_x < 511) mouse_x++;
			refresh_plot (0);
			refresh_text (0);
			break;
		case XK_KP_7:
		case XK_Home:
			if (current != first) {
				current = first;
				refresh_text(0);
				}
			break;
		case XK_KP_1:
		case XK_End:
			for (current=first; current->next; current=current->next);
			refresh_text(0);
			break;
		case XK_KP_9:
		case XK_Prior:
			if (current->prev) current = current->prev;
			refresh_text(0);
			break;
		case XK_KP_3:
		case XK_Next:
			if (current->next) current = current->next;
			refresh_text(0);
			break;
		case XK_Return:
		case XK_KP_Enter:
			refresh_text(0);
			refresh_graphics (0);
			break;
		default:
			break;
		}
	}

/*----------------------------------------------------------------------*\
 | Modified run-length encoding: read									|
\*----------------------------------------------------------------------*/

static int decode_mrle (char *dst, char *src, int len) {
	int c,n;
	char *d,*s,*end;

	d = dst;
	s = src;
	end = src + len;
	while (src < end) {

		/*--------------------------------------------------------------*\
		 | Get character count											|
		\*--------------------------------------------------------------*/

		n = *src++;
		if (n >= 0) {

			/*----------------------------------------------------------*\
			 | If character count is positive repeat next src byte.		|
			\*----------------------------------------------------------*/

			n++;
			c = *src++;
			while (n-- > 0) *d++ = c;
			}
		else {

			/*----------------------------------------------------------*\
			 | If character count is negative, copy count src bytes.	|
			\*----------------------------------------------------------*/

			n = -n;
			while (n-- > 0) *d++ = *src++;
			}
		}
	return (d - dst);
	}

/*----------------------------------------------------------------------*\
 | Modified run-length encoding: skip									|
 | Find the first count byte that is at least len bytes from the start	|
 | of the decoded image.												|
\*----------------------------------------------------------------------*/

static int skip_mrle (char *src, int len) {
	int n;
	char *s;

	s = src;
	while (len > 0) {
		n = *s++;
		if (n >= 0) {
			n++;
			s++;
			}
		else {
			n = -n;
			s += n;
			}
		len -= n;
		}
	return (s - src);
	}

/*----------------------------------------------------------------------*\
 | Read a MCSST file that is packed using modified run-length encoding.	|
 | Skip the three 512-byte header records.								|
\*----------------------------------------------------------------------*/

unsigned char scratch[512*512];

static int read_sst_file (FILE *f, unsigned char *dst) {
	char *src = (char *) scratch;
	int m,n;

	n = fread (src,1,512*512,f);
	m = skip_mrle (src,3*512);
	src += m;
	n   -= m;
	return (decode_mrle ((char *)dst,src,n));
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/
