/* $Id: xwin.c,v 1.49 1994/08/25 03:58:15 mjl Exp $
 * $Log: xwin.c,v $
 * Revision 1.49  1994/08/25  03:58:15  mjl
 * Change to use default visual for now, since otherwise the current
 * procedure results in a BadMatch when calling XCreateWindow on some systems
 * (Suns).  To really get it right, XGetRGBColormaps or something similar
 * must be used to pair the visual with a compatible colormap.
 *
 * Revision 1.48  1994/08/05  09:25:05  mjl
 * Added the static variable "synchronize", to be set when you want
 * synchronous X operation.  Only for debugging, otherwise it's too slow.
 *
 * Revision 1.47  1994/07/27  22:18:01  mjl
 * Fixed bug in window cleanup and termination that was causing a core dump
 * for apps using multiple plframe widgets.
 *
 * Revision 1.46  1994/07/22  22:20:36  mjl
 * Fixed bug in WM hints, introduced during the last bug fix.  It was causing
 * the window to come up without user intervention and in a poor location for
 * tvtwm.  Now it should get BOTH the default and the user specified cases
 * right regardless of window manager (I hope).
 *
 * Revision 1.45  1994/07/19  22:30:32  mjl
 * All device drivers: enabling macro renamed to PLD_<driver>, where <driver>
 * is xwin, ps, etc.  See plDevs.h for more detail.
 *
 * Revision 1.44  1994/07/18  20:28:46  mjl
 * Fixed a cast.
 *
 * Revision 1.43  1994/07/01  22:38:04  mjl
 * All display-dependent data broken off into a separate structure.  The X
 * driver now saves only one of these XwDisplay structs for each physical
 * X display.  These are then shared between all PLplot streams.  This
 * ensures that using multiple streams -> multiple X windows does not cause
 * abnormal termination due to running out of resources since most are now
 * shared (allocated colors, graphics contexts, etc).  The drawback is that
 * colors are no longer independent between windows, if created by a single
 * process (but this can also be an advantage).
 *
 * Revision 1.42  1994/06/17  21:21:11  mjl
 * Fixed background color for mono displays, should now come up white (much
 * easier to see).  Middle mouse button press now returns world coordinates
 * at cursor, but only correct for last plot on the page (submitted by
 * Mark Glover glover@zombi.res.utc.com).
 *
 * Revision 1.41  1994/06/09  20:05:58  mjl
 * Fixed Visual handling to support grayscale and mono displays.  Enabled
 * exporting of colormap hack flag for other files (plframe.c) that need it.
 *
 * Revision 1.40  1994/05/27  22:19:17  mjl
 * Window now placed properly when offsets (+x+y) are specified.  Thanks
 * to Mark S. Glover (glover@zombi.res.utc.com) for the fix.
*/

/*	xwin.c

	PLPLOT X-windows device driver.
*/
#include "plDevs.h"

#ifdef PLD_xwin

#include "plplotP.h"
#include "plplotX.h"
#include "drivers.h"
#include "plevent.h"

static int synchronize = 0;	/* change to 1 for synchronized operation */
				/* for debugging only */

/* When DEFAULT_VISUAL is 1, DefaultVisual() is used to get the visual.
 * Otherwise, the visual is obtained using XGetVisualInfo() to make a
 * match.
 */

#define DEFAULT_VISUAL 1

/* Set constants for dealing with colormap.  In brief:
 *
 * ccmap		When set, turns on custom color map
 *
 * XWM_COLORS		Number of low "pixel" values to copy.  
 * CMAP0_COLORS		Color map 0 entries.  
 * CMAP1_COLORS		Color map 1 entries.  
 * MAX_COLORS		Maximum colors period.
 *
 * See Init_CustomCmap() and  Init_DefaultCmap() for more info.
 * Set ccmap at your own risk -- still under development.
 */

int plplot_ccmap = 0;

#define XWM_COLORS 70
#define CMAP0_COLORS 16
#define CMAP1_COLORS 100
#define MAX_COLORS 256

/* Variables to hold RGB components of given colormap. */
/* Used in an ugly hack to get past some X11R5 and TK limitations. */

static int  sxwm_colors_set;
static XColor sxwm_colors[256];

/* Keep pointers to all the displays in use */

static XwDisplay *xwDisplay[PLXDISPLAYS];

/* Function prototypes */

static void  Init		(PLStream *pls);
static void  Init_main		(PLStream *pls);
static void  Init_Colors	(PLStream *pls);
static void  Init_CustomCmap	(PLStream *pls);
static void  Init_DefaultCmap	(PLStream *pls);
static void  Map_main		(PLStream *pls);
static void  WaitForPage	(PLStream *pls);
static void  HandleEvents	(PLStream *pls);
static void  ColorInit		(PLStream *pls);
static void  Cmap0Init		(PLStream *pls);
static void  Cmap1Init		(PLStream *pls);
static void  CreatePixmap	(PLStream *pls);
static void  fill_polygon	(PLStream *pls);
static void  Colorcpy		(XColor *, XColor *);

static void  MasterEH		(PLStream *, XEvent *);
static void  KeyEH		(PLStream *, XEvent *);
static void  MouseEH		(PLStream *, XEvent *);
static void  ExposeEH		(PLStream *, XEvent *);
static void  ResizeEH		(PLStream *, XEvent *);

static void  ExposeCmd		(PLStream *);
static void  ResizeCmd		(PLStream *, PLWindow *);
static void  RedrawCmd		(PLStream *);

/*----------------------------------------------------------------------*\
 * plD_init_xw()
 *
 * Initialize device.
 * X-dependent stuff done in Init().
\*----------------------------------------------------------------------*/

void
plD_init_xw(PLStream *pls)
{
    XwDev *dev;
    int xmin = 0;
    int xmax = PIXELS_X - 1;
    int ymin = 0;
    int ymax = PIXELS_Y - 1;

    float pxlx = (double) PIXELS_X / (double) LPAGE_X;
    float pxly = (double) PIXELS_Y / (double) LPAGE_Y;

    dbug_enter("plD_init_xw");

    pls->termin = 1;		/* is an interactive terminal */
    pls->icol0 = 1;
    pls->width = 1;
    pls->bytecnt = 0;
    pls->page = 0;
    pls->plbuf_write = 1;
    pls->dev_flush = 1;		/* Want to handle our own flushes */
    pls->dev_fill0 = 1;		/* Can do solid fills */

/* Allocate and initialize device-specific data */

    if (pls->dev != NULL)
	free((void *) pls->dev);

    pls->dev = calloc(1, (size_t) sizeof(XwDev));
    if (pls->dev == NULL)
	plexit("plD_init_xw: Out of memory.");

    dev = (XwDev *) pls->dev;

/* Set up flags that determine what we are writing to */
/* If nopixmap is set, ignore db */

    if (pls->nopixmap) {
	dev->write_to_pixmap = 0;
	pls->db = 0;
    }
    else
	dev->write_to_pixmap = 1;

    dev->write_to_window = ! pls->db;

/* X-specific initialization */

    Init(pls);

/* Get ready for plotting */

    dev->xlen = xmax - xmin;
    dev->ylen = ymax - ymin;

    dev->xscale_init = (double) (dev->init_width-1) / (double) dev->xlen;
    dev->yscale_init = (double) (dev->init_height-1) / (double) dev->ylen;

    dev->xscale = dev->xscale_init;
    dev->yscale = dev->yscale_init;

    plP_setpxl(pxlx, pxly);
    plP_setphy(xmin, xmax, ymin, ymax);
}

/*----------------------------------------------------------------------*\
 * plD_line_xw()
 *
 * Draw a line in the current color from (x1,y1) to (x2,y2).
\*----------------------------------------------------------------------*/

void
plD_line_xw(PLStream *pls, short x1a, short y1a, short x2a, short y2a)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    int x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a;
    static long count = 0, max_count = 20;

    if (dev->is_main) {
	if ( (++count/max_count)*max_count == count) {
	    count = 0;
	    HandleEvents(pls);	/* Check for events */
	}
    }

    y1 = dev->ylen - y1;
    y2 = dev->ylen - y2;

    x1 = x1 * dev->xscale;
    x2 = x2 * dev->xscale;
    y1 = y1 * dev->yscale;
    y2 = y2 * dev->yscale;

    if (dev->write_to_window)
	XDrawLine(xwd->display, dev->window, xwd->gc, x1, y1, x2, y2);

    if (dev->write_to_pixmap)
	XDrawLine(xwd->display, dev->pixmap, xwd->gc, x1, y1, x2, y2);
}

/*----------------------------------------------------------------------*\
 * plD_polyline_xw()
 *
 * Draw a polyline in the current color from (x1,y1) to (x2,y2).
\*----------------------------------------------------------------------*/

void
plD_polyline_xw(PLStream *pls, short *xa, short *ya, PLINT npts)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    PLINT i;
    XPoint pts[PL_MAXPOLY];
    static long count = 0, max_count = 10;

    if (dev->is_main) {
	if ( (++count/max_count)*max_count == count) {
	    count = 0;
	    HandleEvents(pls);	/* Check for events */
	}
    }

    if (npts > PL_MAXPOLY)
	plexit("Error -- too many points in polyline\n");

    for (i = 0; i < npts; i++) {
	pts[i].x = dev->xscale * xa[i];
	pts[i].y = dev->yscale * (dev->ylen - ya[i]);
    }

    if (dev->write_to_window)
	XDrawLines(xwd->display, dev->window, xwd->gc, pts, npts,
		   CoordModeOrigin);

    if (dev->write_to_pixmap)
	XDrawLines(xwd->display, dev->pixmap, xwd->gc, pts, npts,
		   CoordModeOrigin);
}

/*----------------------------------------------------------------------*\
 * plD_eop_xw()
 *
 * End of page.  User must click left mouse button to continue.
\*----------------------------------------------------------------------*/

void
plD_eop_xw(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    dbug_enter("plD_eop_xw");

    XFlush(xwd->display);
    if (pls->db)
	ExposeCmd(pls);
	
    if ( ! pls->nopause)
	WaitForPage(pls);
}

/*----------------------------------------------------------------------*\
 * plD_bop_xw()
 *
 * Set up for the next page.
\*----------------------------------------------------------------------*/

void
plD_bop_xw(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    dbug_enter("plD_bop_xw");

    if (dev->write_to_window) {
	XClearWindow(xwd->display, dev->window);
    }
    if (dev->write_to_pixmap) {
	XSetForeground(xwd->display, xwd->gc, xwd->cmap0[0].pixel);
	XFillRectangle(xwd->display, dev->pixmap, xwd->gc, 0, 0,
		       dev->width, dev->height);
	XSetForeground(xwd->display, xwd->gc, xwd->curcolor.pixel);
    }
    XSync(xwd->display, 0);
    pls->page++;
}

/*----------------------------------------------------------------------*\
 * plD_tidy_xw()
 *
 * Close graphics file
\*----------------------------------------------------------------------*/

void
plD_tidy_xw(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    dbug_enter("plD_tidy_xw");

    if (dev->is_main) {
	XDestroyWindow(xwd->display, dev->window);
	if (dev->write_to_pixmap) 
	    XFreePixmap(xwd->display, dev->pixmap);
    }

    xwd->count--;
    if (xwd->count == 0) {
	XFreeGC(xwd->display, xwd->gc);
	XCloseDisplay(xwd->display);
	free_mem(xwDisplay[xwd->ixwd]);
    }
    pls->plbuf_write = 0;
}

/*----------------------------------------------------------------------*\
 * plD_state_xw()
 *
 * Handle change in PLStream state (color, pen width, fill attribute, etc).
\*----------------------------------------------------------------------*/

void 
plD_state_xw(PLStream *pls, PLINT op)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    dbug_enter("plD_state_xw");

    if (dev->is_main) {
	HandleEvents(pls);	/* Check for events */
    }

    switch (op) {

    case PLSTATE_WIDTH:
	break;

    case PLSTATE_COLOR0:{
	int icol0 = pls->icol0;
	if ( ! xwd->color) {
	    xwd->curcolor.pixel = xwd->fgcolor.pixel;
	    XSetForeground(xwd->display, xwd->gc, xwd->curcolor.pixel);
	}
	else {
	    if (icol0 == PL_RGB_COLOR) {
		PLColor_to_XColor(&pls->curcolor, &xwd->curcolor);

		if ( ! XAllocColor(xwd->display, xwd->map, &xwd->curcolor)) {
		    fprintf(stderr, "Warning: could not allocate color\n");
		    xwd->curcolor.pixel = xwd->fgcolor.pixel;
		}

		XSetForeground(xwd->display, xwd->gc, xwd->curcolor.pixel);
	    }
	    else
		XSetForeground(xwd->display, xwd->gc, xwd->cmap0[icol0].pixel);
	}
	break;
    }

    case PLSTATE_COLOR1:{
	int icol1 = (pls->icol1 * (xwd->ncol1-1)) / (pls->ncol1-1);
	if ( ! xwd->color) {
	    xwd->curcolor.pixel = xwd->fgcolor.pixel;
	    XSetForeground(xwd->display, xwd->gc, xwd->curcolor.pixel);
	}
	else {
	    XSetForeground(xwd->display, xwd->gc, xwd->cmap1[icol1].pixel);
	}
	break;
    }

    case PLSTATE_CMAP0:
	Cmap0Init(pls);
	break;

    case PLSTATE_CMAP1:
	Cmap1Init(pls);
	break;
    }
}

/*----------------------------------------------------------------------*\
 * plD_esc_xw()
 *
 * Escape function.
 *
 * Functions:
 *
 *	PLESC_EXPOSE	Force an expose
 *	PLESC_RESIZE	Force a resize
 *	PLESC_REDRAW	Force a redraw
 *	PLESC_FLUSH	Flush X event buffer
 *	PLESC_FILL	Fill polygon
 *	PLESC_EH	Handle events only
 *
\*----------------------------------------------------------------------*/

void
plD_esc_xw(PLStream *pls, PLINT op, void *ptr)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    dbug_enter("plD_esc_xw");

    switch (op) {
    case PLESC_EXPOSE:
	ExposeCmd(pls);
	break;

    case PLESC_RESIZE:
	ResizeCmd(pls, (PLWindow *) ptr);
	break;

    case PLESC_REDRAW:
	RedrawCmd(pls);
	break;

    case PLESC_FLUSH:
	HandleEvents(pls);
	XFlush(xwd->display);
	break;

    case PLESC_FILL:
	fill_polygon(pls);
	break;

    case PLESC_EH:
	HandleEvents(pls);
	break;
    }
}

/*----------------------------------------------------------------------*\
 * fill_polygon()
 *
 * Fill polygon described in points pls->dev_x[] and pls->dev_y[].
 * Only solid color fill supported.
\*----------------------------------------------------------------------*/

static void
fill_polygon(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;
    XPoint pts[PL_MAXPOLY];

    static long count = 0, max_count = 10;
    int i, outline_only=0;

    if (dev->is_main) {
	if ( (++count/max_count)*max_count == count) {
	    count = 0;
	    HandleEvents(pls);	/* Check for events */
	}
    }

    if (pls->dev_npts > PL_MAXPOLY)
	plexit("Error -- too many points in polygon\n");

    for (i = 0; i < pls->dev_npts; i++) {
	pts[i].x = dev->xscale * pls->dev_x[i];
	pts[i].y = dev->yscale * (dev->ylen - pls->dev_y[i]);
    }

/*
 * For an interesting effect during fills, turn on outline_only.
 * Mostly for debugging.
 */

    if (outline_only) {
	if (dev->write_to_window)
	    XDrawLines(xwd->display, dev->window, xwd->gc, pts, pls->dev_npts,
		       CoordModeOrigin);

	if (dev->write_to_pixmap)
	    XDrawLines(xwd->display, dev->pixmap, xwd->gc, pts, pls->dev_npts,
		       CoordModeOrigin);
    }
    else {
	if (dev->write_to_window)
	    XFillPolygon(xwd->display, dev->window, xwd->gc,
			 pts, pls->dev_npts, Nonconvex, CoordModeOrigin);

	if (dev->write_to_pixmap)
	    XFillPolygon(xwd->display, dev->pixmap, xwd->gc,
			 pts, pls->dev_npts, Nonconvex, CoordModeOrigin);
    }
}

/*----------------------------------------------------------------------*\
 * Init()
 *
 * Xlib initialization routine.
 *
 * Controlling routine for X window creation and/or initialization.
 * The user may customize the window in the following ways:
 *
 * display:	pls->OutFile (use plsfnam() or -display option) 
 * size:	pls->xlength, pls->ylength (use plspage() or -geo option)
 * bg color:	pls->cmap0[0] (use plscolbg() or -bg option)
\*----------------------------------------------------------------------*/

static void
Init(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd;

    Window root;
    int i, x, y;

    dbug_enter("Init");

/* See if display matches any already in use, and if so use that */

    dev->xwd = NULL;
    for (i = 0; i < PLXDISPLAYS; i++) {
	if (xwDisplay[i] == NULL)
	    continue;

	if (strcmp(xwDisplay[i]->displayName, pls->FileName) == 0) {
	    dev->xwd = xwDisplay[i];
	    break;
	}
    }

/* If no display matched, create a new one */

    if (dev->xwd == NULL) {
	dev->xwd = (XwDisplay *) calloc(1, (size_t) sizeof(XwDisplay));
	if (dev->xwd == NULL)
	    plexit("Init: Out of memory.");

	for (i = 0; i < PLXDISPLAYS; i++) {
	    if (xwDisplay[i] == NULL)
		break;
	}
	if (i == PLXDISPLAYS) 
	    plexit("Init: Out of xwDisplay's.");

	xwDisplay[i] = xwd = (XwDisplay *) dev->xwd;
	xwd->count = 1;
    }
    else {
	xwd = (XwDisplay *) dev->xwd;
	xwd->count++;
    }
    xwd->ixwd = i;
	
/* Open display, initialize color map and color values */

    if (xwd->count == 1) {
	xwd->display = XOpenDisplay(pls->FileName);
	if (xwd->display == NULL) {
	    fprintf(stderr, "Can't open display\n");
	    exit(1);
	}
	xwd->displayName = pls->FileName;
	xwd->screen = DefaultScreen(xwd->display);
	if (synchronize) 
	    XSynchronize(xwd->display, 1);

	Init_Colors(pls);
    }

/* If not plotting into a child window, need to create main window now */

    if (pls->window_id == 0) {
	dev->is_main = TRUE;
	Init_main(pls);
    }
    else {
	dev->is_main = FALSE;
	dev->window = pls->window_id;
    }
    XSetWindowColormap( xwd->display, dev->window, xwd->map );

/* GC creation and initialization */

    if (xwd->count == 1) {
	xwd->gc = XCreateGC(xwd->display, dev->window, 0, 0);
    }

/* Get initial drawing area dimensions */

    (void) XGetGeometry(xwd->display, dev->window, &root, &x, &y,
			&dev->width, &dev->height, &dev->border, &xwd->depth);

    dev->init_width = dev->width;
    dev->init_height = dev->height;

/* Create pixmap for holding plot image (for expose events). */

    if (dev->write_to_pixmap) 
	CreatePixmap(pls);

/* Set drawing color */

    plD_state_xw(pls, PLSTATE_COLOR0);

    XSetWindowBackground(xwd->display, dev->window, xwd->cmap0[0].pixel);
    XSetBackground(xwd->display, xwd->gc, xwd->cmap0[0].pixel);

/* If main window, need to map it and wait for exposure */

    if (dev->is_main) 
	Map_main(pls);
}

/*----------------------------------------------------------------------*\
 * Init_main()
 *
 * Create main window using standard Xlib calls.
\*----------------------------------------------------------------------*/

static void
Init_main(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    Window root;
    XSizeHints hint;
    int x, y;
    U_INT width, height, border, depth;
    char header[80];
    Cursor cross_cursor;

    dbug_enter("Init_main");

/* Get root window geometry */

    (void) XGetGeometry(xwd->display, DefaultRootWindow(xwd->display),
			&root, &x, &y, &width, &height, &border, &depth);

    dev->border = 5;
    hint.flags = 0;

/* Set window size */
/* Need to be careful to set XSizeHints flags correctly */

    if (pls->xlength == 0 && pls->ylength == 0)
	hint.flags |= PSize;
    else
	hint.flags |= USSize;

    if (pls->xlength == 0)
	pls->xlength = width * 0.75;
    if (pls->ylength == 0)
	pls->ylength = height * 0.75;

    if (pls->xlength > width)
	pls->xlength = width - dev->border * 2;
    if (pls->ylength > height)
	pls->ylength = height - dev->border * 2;

    hint.width = (int) pls->xlength;
    hint.height = (int) pls->ylength;

/* Set window position if specified by the user. */
/* Otherwise leave up to the window manager */

    if (pls->xoffset != 0 || pls->yoffset != 0) {
	hint.flags |= USPosition;
	hint.x = (int) pls->xoffset;
	hint.y = (int) pls->yoffset;
    }

/* Window title */

    sprintf(header, "plplot");

/* Window creation */

    dev->window =
	XCreateWindow( xwd->display,
		       DefaultRootWindow(xwd->display),
		       hint.x, hint.y, hint.width, hint.height,
		       dev->border, xwd->depth,
		       InputOutput, xwd->visual,
		       0, NULL );

    XSetStandardProperties(xwd->display, dev->window, header, header,
			   None, 0, 0, &hint);

/* Set cursor to crosshair */

    cross_cursor = XCreateFontCursor(xwd->display, XC_crosshair);
    XDefineCursor(xwd->display, dev->window, cross_cursor);
}

/*----------------------------------------------------------------------*\
 * Map_main()
 *
 * Sets up event handlers, maps main window and waits for exposure.
\*----------------------------------------------------------------------*/

static void
Map_main(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    XEvent the_event;

    dbug_enter("Map_main");

/* Input event selection */

    XSelectInput(xwd->display, dev->window,
		 ButtonPressMask | KeyPressMask |
		 ExposureMask | StructureNotifyMask);

/* Window mapping */

    XMapRaised(xwd->display, dev->window);

/* Wait for exposure */
/* Remove extraneous expose events from the event queue */

    for (;;) {
	XNextEvent(xwd->display, &the_event);
	if (the_event.type == Expose) {
	    while (XCheckMaskEvent(xwd->display, ExposureMask, &the_event));
	    break;
	}
    }
}

/*----------------------------------------------------------------------*\
 * WaitForPage()
 *
 * This routine waits for the user to advance the plot, while handling
 * all other events.
\*----------------------------------------------------------------------*/

static void
WaitForPage(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    XEvent event;

    dbug_enter("WaitForPage");

    if ( ! dev->is_main) {
	HandleEvents(pls);	/* Check for events */
	return;
    }

    while ( ! dev->exit_eventloop) {
	XNextEvent(xwd->display, &event);
	MasterEH(pls, &event);
    }
    dev->exit_eventloop = FALSE;
}

/*----------------------------------------------------------------------*\
 * HandleEvents()
 *
 * Just a front-end to MasterEH(), for use when not actually waiting for
 * an event but only checking the event queue.  
\*----------------------------------------------------------------------*/

static void
HandleEvents(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    XEvent event;

    if (XCheckMaskEvent(xwd->display, ButtonPressMask | KeyPressMask |
			ExposureMask | StructureNotifyMask, &event))
	MasterEH(pls, &event);
}

/*----------------------------------------------------------------------*\
 * MasterEH()
 *
 * Master X event handler routine.
 * Redirects control to routines to handle:
 *    - keyboard events
 *    - mouse events
 *    - expose events
 *    - resize events
 *
 * By supplying a master event handler, the user can take over all event
 * processing.  If events other than those trapped by plplot need handling,
 * just call XSelectInput with the appropriate flags.  The default PLPlot
 * event handling can be modified arbitrarily by changing the event struct.
\*----------------------------------------------------------------------*/

static void
MasterEH(PLStream *pls, XEvent *event)
{
    XwDev *dev = (XwDev *) pls->dev;

    if (dev->MasterEH != NULL)
	(*dev->MasterEH) (pls, event);

    switch (event->type) {
      case KeyPress:
	KeyEH(pls, event);
	break;

      case ButtonPress:
	MouseEH(pls, event);
	break;

      case Expose:
	ExposeEH(pls, event);
	break;

      case ConfigureNotify:
	ResizeEH(pls, event);
	break;
    }
}

/*----------------------------------------------------------------------*\
 * KeyEH()
 *
 * Event handler routine for keyboard events.
\*----------------------------------------------------------------------*/

static void
KeyEH(PLStream *pls, XEvent *event)
{
    XwDev *dev = (XwDev *) pls->dev;

    KeySym keysym;
    PLKey key;
    int count;
    XComposeStatus cs;

    dbug_enter("KeyEH");

/* Translate event into string */

    count = XLookupString((XKeyEvent *) event, key.string, PL_NKEYSTRING,
			  &keysym, &cs);
    key.string[count] = '\0';
    key.code = (U_LONG) keysym;

#ifdef DEBUG
    printf("Keysym %x, translation: %s\n", keysym, key.string);
#endif

/* Call user keypress event handler */
/* Since this is called first, the user can disable all plplot internal
   event handling by setting key.code to 0 and key.string to '\0' */

    if (pls->KeyEH != NULL)
	(*pls->KeyEH) (&key, pls->KeyEH_data, &dev->exit_eventloop);

/* Handle internal events */

/* Advance to next page (i.e. terminate event loop) on a <eol> */
/* Check for both <CR> and <LF> for portability, also a <Page Down> */

    if (key.code == PLK_Return ||
	key.code == PLK_Linefeed ||
	key.code == PLK_Next)
	dev->exit_eventloop = TRUE;

/* Terminate on a 'Q' (not 'q', since it's too easy to hit by mistake) */

    if (key.string[0] == 'Q') {
	pls->nopause = TRUE;
	plexit("");
    }
}

/*----------------------------------------------------------------------*\
 * MouseEH()
 *
 * Event handler routine for mouse events.
 * On:
 *   Button1: nothing for now
 *   Button2: spit out device space coordinates
 *   Button3: set page advance flag for later processing
\*----------------------------------------------------------------------*/

static void
MouseEH(PLStream *pls, XEvent *event)
{
    XwDev *dev = (XwDev *) pls->dev;

    PLMouse mouse;
    float xcoord, ycoord;

    dbug_enter("MouseEH");

    mouse.button = event->xbutton.button;
    mouse.state = event->xbutton.state;
    mouse.x = (PLFLT)event->xbutton.x / pls->xlength;
    mouse.y = 1.0 - (PLFLT)event->xbutton.y / pls->ylength;
      
/* Call user event handler */
/* Since this is called first, the user can disable all plplot internal
   event handling by setting mouse.button to 0 */

    if (pls->MouseEH != NULL)
	(*pls->MouseEH) (&mouse, pls->MouseEH_data, &dev->exit_eventloop);

    switch (mouse.button) {
    case Button1:
	break;

    case Button2:
	printf("%f\t%f\n", mouse.x, mouse.y);
        xcoord=((event->xbutton.x+1-(pls->vpdxmi)*(pls->xlength))/
                ((pls->vpdxma-pls->vpdxmi)*pls->xlength))*pls->vpwxma;
        ycoord=((pls->ylength-event->xbutton.y+1-(pls->vpdymi)*(pls->ylength))/
                ((pls->vpdyma-pls->vpdymi)*pls->ylength))*pls->vpwyma;
	printf("%f\t%f\n", xcoord, ycoord);
#ifdef DEBUG
        printf("[xwin.c:MouseEH:] world X coord = %f\n",
               xcoord);
        printf("[xwin.c:MouseEH:] world Y coord = %f\n",
               ycoord);
#endif /* DEBUG */
	break;

    case Button3:
	dev->exit_eventloop = TRUE;
	break;
    }
}

/*----------------------------------------------------------------------*\
 * ExposeEH()
 *
 * Event handler routine for expose events.
 * Front end to ExposeCmd() to deal with wierdnesses of Xlib.
\*----------------------------------------------------------------------*/

static void
ExposeEH(PLStream *pls, XEvent *event)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    dbug_enter("ExposeEH");

/* Handle expose */

    ExposeCmd(pls);

/* Remove extraneous expose events from the event queue */

    while (XCheckMaskEvent(xwd->display, ExposureMask, event))
	;
}

/*----------------------------------------------------------------------*\
 * ResizeEH()
 *
 * Event handler routine for resize events.
 * Front end to ResizeCmd() to deal with wierdnesses of Xlib.
\*----------------------------------------------------------------------*/

static void
ResizeEH(PLStream *pls, XEvent *event)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    PLWindow newwin;

    dbug_enter("ResizeEH");

    newwin.width = event->xconfigure.width;
    newwin.height = event->xconfigure.height;

/* Only need to resize if size is actually changed */

    if (newwin.width == dev->width && newwin.height == dev->height)
	return;

    ResizeCmd(pls, &newwin);

/* Remove extraneous expose events from the event queue */
/* These are not needed since we've redrawn the whole plot */

    while (XCheckMaskEvent(xwd->display, ExposureMask, event))
	;
}

/*----------------------------------------------------------------------*\
 * ExposeCmd()
 *
 * Event handler routine for expose events.
 * These are "pure" exposures (no resize), so don't need to clear window.
\*----------------------------------------------------------------------*/

static void
ExposeCmd(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    dbug_enter("ExposeCmd");

/* Return if plD_init_xw hasn't been called yet */

    if (dev == NULL) {
	plwarn("ExposeCmd: Illegal call -- driver uninitialized");
	return;
    }

/* Usual case: refresh window from pixmap */

    XSync(xwd->display, 0);
    if (dev->write_to_pixmap) {
	XCopyArea(xwd->display, dev->pixmap, dev->window, xwd->gc, 0, 0,
		  dev->width, dev->height, 0, 0);
	XSync(xwd->display, 0);
    }
    else {
	plRemakePlot(pls);
	XFlush(xwd->display);
    }
}

/*----------------------------------------------------------------------*\
 * ResizeCmd()
 *
 * Event handler routine for resize events.
 * Note: this function is callable from the outside world.
\*----------------------------------------------------------------------*/

static void
ResizeCmd(PLStream *pls, PLWindow *window)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    dbug_enter("ResizeCmd");

/* Return if plD_init_xw hasn't been called yet */

    if (dev == NULL) {
	plwarn("ResizeCmd: Illegal call -- driver uninitialized");
	return;
    }

/* Return if pointer to window not specified. */

    if (window == NULL) {
	plwarn("ResizeCmd: Illegal call -- window pointer uninitialized");
	return;
    }

/* Reset current window bounds */

    dev->width = window->width;
    dev->height = window->height;

    dev->xscale = (double) (dev->width-1) / (double) dev->init_width;
    dev->yscale = (double) (dev->height-1) / (double) dev->init_height;

    dev->xscale = dev->xscale * dev->xscale_init;
    dev->yscale = dev->yscale * dev->yscale_init;

/* Need to regenerate pixmap copy of window using new dimensions */

    if (dev->write_to_pixmap) {
	XSync(xwd->display, 0);
	XFreePixmap(xwd->display, dev->pixmap);
	CreatePixmap(pls);
    }

/* Now do a redraw using the new size */

    RedrawCmd(pls);
}

/*----------------------------------------------------------------------*\
 * RedrawCmd()
 *
 * Handles page redraw without resize (pixmap does not get reallocated).
 * Calling this makes sure all necessary housekeeping gets done.
\*----------------------------------------------------------------------*/

static void
RedrawCmd(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    dbug_enter("RedrawCmd");

/* Return if plD_init_xw hasn't been called yet */

    if (dev == NULL) {
	plwarn("RedrawCmd: Illegal call -- driver uninitialized");
	return;
    }

/* Initialize & redraw to pixmap.  Then fake an expose. */

    if (dev->write_to_pixmap)
	dev->write_to_window = 0;

    XSync(xwd->display, 0);
    plD_bop_xw(pls);
    plRemakePlot(pls);

    XSync(xwd->display, 0);
    if (dev->write_to_pixmap) {
	XCopyArea(xwd->display, dev->pixmap, dev->window, xwd->gc, 0, 0,
		  dev->width, dev->height, 0, 0);
	XSync(xwd->display, 0);
    }
    else {
	plRemakePlot(pls);
	XFlush(xwd->display);
    }

    dev->write_to_window = ! pls->db;
}

/*----------------------------------------------------------------------*\
 * CreatePixmapErrorHandler()
 *
 * Error handler used in CreatePixmap() to catch errors in allocating
 * storage for pixmap.  This way we can nicely substitute redraws for
 * pixmap copies if the server has insufficient memory.
\*----------------------------------------------------------------------*/

static unsigned char CreatePixmapStatus;

static int
CreatePixmapErrorHandler(Display *display, XErrorEvent *error)
{
    if (error->error_code == BadAlloc) {
	CreatePixmapStatus = error->error_code;
    }
    else
	fprintf(stderr, "Something bad just happened.\n");

    return(1);
}	

/*----------------------------------------------------------------------*\
 * CreatePixmap()
 *
 * This routine creates a pixmap, doing error trapping in case there
 * isn't enough memory on the server.
\*----------------------------------------------------------------------*/

static void
CreatePixmap(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    int (*oldErrorHandler)();

    oldErrorHandler = XSetErrorHandler(CreatePixmapErrorHandler);

    CreatePixmapStatus = Success;
#ifdef DEBUG
    fprintf(stderr, "Creating pixmap of width %d, height %d, depth %d\n",
	    dev->width, dev->height, xwd->depth);
#endif
    dev->pixmap = XCreatePixmap(xwd->display, dev->window,
				dev->width, dev->height, xwd->depth);
    XSync(xwd->display, 0);
    if (CreatePixmapStatus != Success) {
	dev->write_to_pixmap = 0;
	dev->write_to_window = 1;
	pls->db = 0;
	fprintf(stderr, "\n\
Warning: pixmap could not be allocated (insufficient memory on server).\n\
Driver will redraw the entire plot to handle expose events.\n");
    }

    XSetErrorHandler(oldErrorHandler);
}

/*----------------------------------------------------------------------*\
 * Init_Colors()
 *
 * Does all color initialization.
\*----------------------------------------------------------------------*/

static void
Init_Colors(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    Colormap default_map;
    PLColor fgcolor;
    int gslevbg, gslevfg;
    int visuals_matched = 0;

    dbug_enter("Init_Colors");

/* Grab default color map */

    default_map = DefaultColormap(xwd->display, xwd->screen);

/* Get visual info.
 * 
 * In order to safely use a visual other than that of the parent (which
 * hopefully is that returned by DefaultVisual), you must first find
 * (using XGetRGBColormaps) or create a colormap matching this visual and
 * then set the colormap window attribute in the XCreateWindow attributes
 * and valuemask arguments.  I don't do this right now, so this is turned
 * off by default.
 */

#if DEFAULT_VISUAL == 0
    {
	XVisualInfo vTemplate, *visualList;

/* Try for an 8 plane display, if unavailable go for the default */

	vTemplate.screen = xwd->screen;
	vTemplate.depth = 8;

	visualList = XGetVisualInfo( xwd->display,
				     VisualScreenMask | VisualDepthMask,
				     &vTemplate, &visuals_matched );

	if (visuals_matched) {
	    xwd->visual = visualList->visual;	/* Choose first match. */
	    xwd->depth = vTemplate.depth;
	}
    }
#endif

    if ( ! visuals_matched) {
	xwd->visual = DefaultVisual( xwd->display, xwd->screen );
	xwd->depth = DefaultDepth( xwd->display, xwd->screen );
    }

/*
 * Figure out if we have a color display or not.
 * Default is color IF the user hasn't specified and IF the output device is
 * not grayscale.  
 */

    if (pls->colorset)
	xwd->color = pls->color;
    else {
	pls->color = 1;
	xwd->color = ! pl_AreWeGrayscale(xwd->display);
    }

/*
 * Set background color.
 *
 * Background defaults to black on color screens, white on grayscale (many
 * grayscale monitors have poor contrast, and black-on-white looks better).
 */

    if ( ! xwd->color) {
	pls->cmap0[0].r = pls->cmap0[0].g = pls->cmap0[0].b = 0xFF;
    }
    gslevbg = ((long) pls->cmap0[0].r +
	       (long) pls->cmap0[0].g +
	       (long) pls->cmap0[0].b) / 3;

/*
 * Set foreground color.
 *
 * Used for grayscale output, since otherwise the plots can become nearly
 * unreadable (i.e. if colors get mapped onto grayscale values).  In this
 * case it becomes the grayscale level for all draws, and is taken to be
 * black if the background is light, and white if the background is dark.
 * Note that white/black allocations never fail.
 */

    if (gslevbg > 0x7F) 
	gslevfg = 0;
    else 
	gslevfg = 0xFF;

    fgcolor.r = fgcolor.g = fgcolor.b = gslevfg;

    PLColor_to_XColor(&fgcolor, &xwd->fgcolor);

/* If we're not on a color system, just allocate bg & fg then return */

    if ( ! xwd->color) {
	PLColor_to_XColor(&pls->cmap0[0], &xwd->cmap0[0]);
	XAllocColor(xwd->display, default_map, &xwd->cmap0[0]);
	XAllocColor(xwd->display, default_map, &xwd->fgcolor);
	return;
    }

/* Create custom color map and initialize cmap0 */

    if (plplot_ccmap) 
	Init_CustomCmap(pls);
    else 
	Init_DefaultCmap(pls);
}

/*----------------------------------------------------------------------*\
 * Init_CustomCmap()
 *
 * Initializes custom color map and all the cruft that goes with it.
 *
 * Assuming all color X displays do 256 colors, the breakdown is as follows:
 *
 * XWM_COLORS	Number of low "pixel" values to copy.  These are typically
 *		allocated first, thus are in use by the window manager. I
 *		copy them to reduce flicker.
 *
 * CMAP0_COLORS	Color map 0 entries.  I allocate these both in the default
 *		colormap and the custom colormap to reduce flicker.
 *
 * CMAP1_COLORS	Color map 1 entries.  There should be as many as practical
 *		available for smooth shading.  On the order of 100 is 
 *		pretty reasonable.  You don't really need all 256.  
 *
 * It's important to leave some extra colors unallocated for Tk.  In 
 * particular the palette tools require a fair amount.  I recommend leaving
 * at least 40 or so free.  
\*----------------------------------------------------------------------*/

static void
Init_CustomCmap(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    Colormap default_map;
    XColor xwm_colors[MAX_COLORS];
    int i, j, npixels;
    unsigned long plane_masks[1];
    unsigned long pixel, pixels[MAX_COLORS];

    dbug_enter("Init_Colormap");

/* Grab default color map */

    default_map = DefaultColormap(xwd->display, xwd->screen);

/* Determine current default colors */

    for (i = 0; i < MAX_COLORS; i++) {
	xwm_colors[i].pixel = i;
    }
    XQueryColors(xwd->display, default_map, xwm_colors, MAX_COLORS);

/* Allocate cmap0 colors in the default colormap.
 * The custom cmap0 colors are later stored at the same pixel values.
 * This is a really cool trick to reduce the flicker when changing colormaps.
 */

    npixels = MIN(pls->ncol0, MAX_COLORS);
    while(1) {
	if (XAllocColorCells(xwd->display, default_map, False,
			     plane_masks, 0, pixels, npixels))
	    break;
	npixels--;
	if (npixels == 0)
	    plexit("couldn't allocate any colors");
    }

    xwd->ncol0 = npixels;
    for (i = 0; i < xwd->ncol0; i++) {

	PLColor_to_XColor(&pls->cmap0[i], &xwd->cmap0[i]);

	xwd->cmap0[i].pixel = pixels[i];
	XStoreColor(xwd->display, default_map, &xwd->cmap0[i]);
    }
    XAllocColor(xwd->display, default_map, &xwd->fgcolor);

/* Create color map */

    xwd->map = XCreateColormap( xwd->display, DefaultRootWindow(xwd->display),
				xwd->visual, AllocNone );

/* Now allocate all colors so we can fill the ones we want to copy */

    npixels = MAX_COLORS;
    while(1) {
	if (XAllocColorCells(xwd->display, xwd->map, False,
			     plane_masks, 0, pixels, npixels))
	    break;
	npixels--;
	if (npixels == 0)
	    plexit("couldn't allocate any colors");
    }

/* Fill the low colors since those are in use by the window manager */

    for (i = 0; i < XWM_COLORS; i++) {
	XStoreColor(xwd->display, xwd->map, &xwm_colors[i]);
	pixels[xwm_colors[i].pixel] = 0;
    }

/* Fill the ones we will use in cmap0 */

    for (i = 0; i < xwd->ncol0; i++) {
	XStoreColor(xwd->display, xwd->map, &xwd->cmap0[i]);
	pixels[xwd->cmap0[i].pixel] = 0;
    }

/* Finally, if the colormap was saved by an external agent, see if there are
 * any differences from the current default map and save those!  A very cool
 * (or sick, depending on how you look at it) trick to get over some X and
 * Tk limitations.
 */

    if (sxwm_colors_set) {
	for (i = 0; i < MAX_COLORS; i++) {
	    if ((xwm_colors[i].red != sxwm_colors[i].red) ||
		(xwm_colors[i].green != sxwm_colors[i].green) ||
		(xwm_colors[i].blue != sxwm_colors[i].blue) ) {

		if (pixels[i] != 0) {
		    XStoreColor(xwd->display, xwd->map, &xwm_colors[i]);
		    pixels[i] = 0;
		}
	    }
	}
    }

/* Now free the ones we're not interested in */

    for (i = 0; i < npixels; i++) {
	if (pixels[i] != 0)
	    XFreeColors(xwd->display, xwd->map, &pixels[i], 1, 0);
    }

/* Allocate colors in cmap 1 */

    npixels = MAX(2, MIN(CMAP1_COLORS, pls->ncol1));
    while(1) {
	if (XAllocColorCells(xwd->display, xwd->map, False,
			     plane_masks, 0, pixels, npixels))
	    break;
	npixels--;
	if (npixels == 0)
	    plexit("couldn't allocate any colors");
    }

    fprintf(stderr, "Allocated %d colors in cmap1\n", npixels);
    xwd->ncol1 = npixels;
    for (i = 0; i < xwd->ncol1; i++) {
	xwd->cmap1[i].pixel = pixels[i];
    }

    Cmap1Init(pls);
}

/*----------------------------------------------------------------------*\
 * Init_DefaultCmap()
 *
 * Initializes default color map and all the cruft that goes with it.
 * Have to severely limit number of colors in cmap1 otherwise TK won't
 * have enough.
\*----------------------------------------------------------------------*/

static void
Init_DefaultCmap(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    Colormap default_map;
    int i, j, npixels;
    unsigned long plane_masks[1];
    unsigned long pixel, pixels[MAX_COLORS];

    dbug_enter("Init_DefaultCmap");

/* Grab default color map */

    xwd->map = DefaultColormap(xwd->display, xwd->screen);

/* Allocate and assign colors in cmap 0 */

    npixels = 16;
    while(1) {
	if (XAllocColorCells(xwd->display, xwd->map, False,
			     plane_masks, 0, pixels, npixels))
	    break;
	npixels--;
	if (npixels == 0)
	    plexit("couldn't allocate any colors");
    }

    xwd->ncol0 = npixels;
    for (i = 0; i < xwd->ncol0; i++) {
	xwd->cmap0[i].pixel = pixels[i];
    }

    Cmap0Init(pls);

/* Allocate colors in cmap 1 */

    npixels = MAX(2, MIN(CMAP1_COLORS, pls->ncol1));
    while(1) {
	if (XAllocColorCells(xwd->display, xwd->map, False,
			     plane_masks, 0, pixels, npixels))
	    break;
	npixels--;
	if (npixels == 0)
	    plexit("couldn't allocate any colors");
    }

/* Now free them all and start with a reduced number */

    XFreeColors(xwd->display, xwd->map, pixels, npixels, 0);

    npixels = MAX(npixels - 30, 10);
    while(1) {
	if (XAllocColorCells(xwd->display, xwd->map, False,
			     plane_masks, 0, pixels, npixels))
	    break;
	npixels--;
	if (npixels == 0)
	    plexit("couldn't allocate any colors");
    }

    fprintf(stderr, "Allocated %d colors in cmap1\n", npixels);
    xwd->ncol1 = npixels;

/* Don't assign pixels sequentially, to avoid strange problems with xor GC's */
/* Skipping by 2 seems to do the job best */

    for (j = i = 0; i < xwd->ncol1; i++) {
	while (pixels[j] == 0) 
	    j++;

	xwd->cmap1[i].pixel = pixels[j];
	pixels[j] = 0;

	j += 2;
	if (j >= xwd->ncol1)
	    j = 0;
    }

    Cmap1Init(pls);
}

/*----------------------------------------------------------------------*\
 * Cmap0Init()
 *
 * Initializes cmap 0
\*----------------------------------------------------------------------*/

static void
Cmap0Init(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;
    int i;

    for (i = 0; i < xwd->ncol0; i++) {
	PLColor_to_XColor(&pls->cmap0[i], &xwd->cmap0[i]);
	XStoreColor(xwd->display, xwd->map, &xwd->cmap0[i]);
    }
}

/*----------------------------------------------------------------------*\
 * Cmap1Init()
 *
 * Initializes cmap 1
\*----------------------------------------------------------------------*/

static void
Cmap1Init(PLStream *pls)
{
    XwDev *dev = (XwDev *) pls->dev;
    XwDisplay *xwd = (XwDisplay *) dev->xwd;

    PLColor cmap1color;
    int i;

    for (i = 0; i < xwd->ncol1; i++) {
	plcol_interp(pls, &cmap1color, i, xwd->ncol1);
	PLColor_to_XColor(&cmap1color, &xwd->cmap1[i]);
	XStoreColor(xwd->display, xwd->map, &xwd->cmap1[i]);
    }
}

/*----------------------------------------------------------------------*\
 * void Colorcpy()
 *
 * Self-explanatory.
\*----------------------------------------------------------------------*/

static void
Colorcpy(XColor *xcolor1, XColor *xcolor2)
{
    xcolor1->red = xcolor2->red;
    xcolor1->green = xcolor2->green;
    xcolor1->blue = xcolor2->blue;
    xcolor1->pixel = xcolor2->pixel;
}

/*----------------------------------------------------------------------*\
 * void PLColor_to_XColor()
 *
 * Copies the supplied PLColor to an XColor, padding with bits as necessary
 * (a PLColor uses 8 bits for color storage, while an XColor uses 16 bits).
 * The argument types follow the same order as in the function name.
\*----------------------------------------------------------------------*/

#define ToXColor(a) (((0xFF & (a)) << 8) | (a))
#define ToPLColor(a) (((U_LONG) a) >> 8)

void
PLColor_to_XColor(PLColor *plcolor, XColor *xcolor)
{
    xcolor->red   = ToXColor(plcolor->r);
    xcolor->green = ToXColor(plcolor->g);
    xcolor->blue  = ToXColor(plcolor->b);
    xcolor->flags = DoRed | DoGreen | DoBlue;
}

/*----------------------------------------------------------------------*\
 * void PLColor_from_XColor()
 *
 * Copies the supplied XColor to a PLColor, stripping off bits as
 * necessary.  See the previous routine for more info.
\*----------------------------------------------------------------------*/

void
PLColor_from_XColor(PLColor *plcolor, XColor *xcolor)
{
    plcolor->r = ToPLColor(xcolor->red);
    plcolor->g = ToPLColor(xcolor->green);
    plcolor->b = ToPLColor(xcolor->blue);
}

/*----------------------------------------------------------------------*\
 * int pl_AreWeGrayscale(Display *display)
 *
 * Determines if we're using a monochrome or grayscale device.
 * gmf 11-8-91; Courtesy of Paul Martz of Evans and Sutherland. 
\*----------------------------------------------------------------------*/

int
pl_AreWeGrayscale(Display *display)
{
#if defined(__cplusplus) || defined(c_plusplus)
#define THING c_class
#else
#define THING class
#endif

    XVisualInfo *visuals;
    int nitems, i;

    /* get a list of info on the visuals available */
    visuals = XGetVisualInfo(display, 0, NULL, &nitems);

    /* check the list looking for non-monochrome visual classes */
    for (i = 0; i < nitems; i++)
	if ((visuals[i].THING != GrayScale) &&
	    (visuals[i].THING != StaticGray))
	    return (0);

    /* if we got this far, only StaticGray and GrayScale classes available */
    return (1);
}

/*----------------------------------------------------------------------*\
 * void PLX_save_colormap()
 *
 * Saves RGB components of given colormap.
 * Used in an ugly hack to get past some X11R5 and TK limitations.
 * This isn't guaranteed to work under all circumstances, but hopefully
 * in the future there will be a nicer way to accomplish the same thing.
 *
 * Note: I tried using XCopyColormapAndFree to do the same thing, but under
 * HPUX 9.01/VUE/X11R5 at least it doesn't preserve the previous read-only
 * color cell allocations made by Tk.  Is this a bug?  Have to look at the
 * source to find out.
\*----------------------------------------------------------------------*/

void
PLX_save_colormap(Display *display, Colormap colormap)
{
    int i;

    if ( ! plplot_ccmap)
	return;

    sxwm_colors_set = 1;
    for (i = 0; i < 256; i++) {
	sxwm_colors[i].pixel = i;
    }
    XQueryColors(display, colormap, sxwm_colors, 256);
/*
    printf("\nAt startup, default colors are: \n\n");
    for (i = 0; i < 256; i++) {
	printf(" i: %d,  pixel: %d,  r: %d,  g: %d,  b: %d\n",
	       i, sxwm_colors[i].pixel,
	       sxwm_colors[i].red, sxwm_colors[i].green, sxwm_colors[i].blue);
    }
 */
}

#else
int
pldummy_xwin()
{
    return 0;
}

#endif				/* PLD_xwin */
