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

			       XCopilot

This code is part of XCopilot, a port of copilot

     Portions of this code are Copyright (C) 1997 Ivan A. Curtis
		       icurtis@radlogic.com.au

The original MS-Windows95 copilot emulator was written by Greg Hewgill.
The following copyright notice appeared on the original copilot sources:

		  Copyright (c) 1996 Greg Hewgill

Some of the code in this file was derived from code in which
the following copyright notice appeared:

		     XWindow Support for QuickCam
		   by Paul Chinn <loomer@svpal.org>
	      Modified by Scott Laird <scott@laird.com>
 
	 I took a bunch of this code from the source for VGB
	  "Virtual GameBoy" emulator by Marat Fayzullin and
			    Elan Feingold

 MC68000 Emulation code is from Bernd Schmidt's Unix Amiga Emulator.
       The following copyright notice appeared in those files:

	  Original UAE code Copyright (c) 1995 Bernd Schmidt

This code must not be distributed without these copyright notices intact.

*******************************************************************************
*******************************************************************************

Filename:	display.c

Description:	Display module for xcopilot emulator

Update History:   (most recent first)
   I. Curtis   9-Apr-97 11:43 -- 16bpp and keboard events courtesy of
   	Andrew Pfiffer <andyp@co.intel.com> 
   I. Curtis   5-Mar-97 20:33 -- added key event processing code
   I. Curtis  25-Feb-97 20:17 -- major tidy up
   I. Curtis  23-Feb-97 21:17 -- Created.

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/xpm.h>
#include <X11/extensions/XShm.h> /* MIT Shared Memory Extensions */


#include "sysdeps.h"
#include "shared.h"
#include "dragonball.h"
#include "case.xpm"

#include "libmx.h"

/*****************************************************************************
 *                                                                           *
 * 			   Global Variables                                  *
 *                                                                           *
 *****************************************************************************/
XShmSegmentInfo xcpSHMInfo;	/* info about shm pixmap (if using shm) */
int xcpUsingShm;		/* set True if we are using MIT shm ext. */
int xcpStarted = 0;		/* set True once XCPilot is started */
int xcpQuit = 0;		/* set True if XCopilot must quit */
Display *xcpDisplay;		/* X Display connection */
Screen *xcpScreen;		/* X screen */
int xcpScreenNum;		/* screen number */
Window xcpLCDWindow;		/* window for Copilot LCD image */
Window xcpCaseWindow;		/* window for case */
XImage *xcpLCDImage;		/* XImage of LCD screen */
GC xcpLCDgc;			/* GC for rendering LCD XImage */
unsigned long xcpOffPixel,	/* Pixels for LCD screen */
  xcpOnPixel;
int xcpDepth;			/* Pixel depth of XImage */


/*************************************
 * Menu Items                        *
 *                                   *
 * These structures hold the entries *
 * for menus                         *
 *************************************/
#define MAIN_N_ITEMS 5		/* Main Menu */
mx_menu_item main_items[] = {
  {MXItemFlag_Center, "XCopilot", 0},
  {MXItemFlag_Left, "Load app..", 0},
  {MXItemFlag_Left, "About..", 0},
  {MXItemFlag_Left | MXItemFlag_Disabled, " ", 0},
  {MXItemFlag_Left, "Quit", 0},
};

#define ABOUT_N_ITEMS 6		/* About panel */
mx_menu_item about_items[] = {
  {MXItemFlag_Center, NULL, 0},
  {MXItemFlag_Left | MXItemFlag_Disabled, " ", 0},
  {MXItemFlag_Center, "  Unix/X Port by Ivan A. Curtis  ", 0},
  {MXItemFlag_Left | MXItemFlag_Disabled, " ", 0},
  {MXItemFlag_Center, "Copilot by Greg Hewgill", 0},
  {MXItemFlag_Left | MXItemFlag_Disabled, " ", 0},
};

#define APPOK_N_ITEMS 4		/* App load OK status panel */
mx_menu_item appok_items[] = {
  {MXItemFlag_Center, "XCopilot", 0},
  {MXItemFlag_Left | MXItemFlag_Disabled, " ", 0},
  {MXItemFlag_Center, "  App Loaded  ", 0},
  {MXItemFlag_Left | MXItemFlag_Disabled, " ", 0},
};

#define APPFAIL_N_ITEMS 4	/* App load fail panel */
mx_menu_item appfail_items[] = {
  {MXItemFlag_Center, "XCopilot", 0},
  {MXItemFlag_Left | MXItemFlag_Disabled, " ", 0},
  {MXItemFlag_Center, "  Load App Failed  ", 0},
  {MXItemFlag_Left | MXItemFlag_Disabled, " ", 0},
};

#define OK_N_ITEMS 1		/* an OK button */
mx_menu_item ok_items[] = {
  {MXItemFlag_Center, "OK", 0},
};

/***************************************
 * This is a signal handler. It simply *
 * sets xcpQuit True, which causes the *
 * event loop to terminate             *
 ***************************************/
void xcpSignalQuit(int unused)
{
  xcpQuit = 1;
}
 
/********************************************************
 * Check if the X Shared Memory extension is available. *
 * Return:  0 = not available                           *
 *   1 = shared XImage support available                *
 *   2 = shared Pixmap support available also           *
 ********************************************************/
int xcpCheckXshm(Display *display)
{
  int major, minor, ignore;
  int pixmaps;

  if (XQueryExtension(display, "MIT-SHM", &ignore, &ignore, &ignore)) {
    if (XShmQueryVersion(display, &major, &minor, &pixmaps) == True) {
      return (pixmaps == True) ? 2 : 1;
    } else {
      return 0;
    }
  } else {
    return 0;
  }
}

/************************************************
 * Initialize xwindows, and prepare a shared    *
 * memory buffer for the LCD image. width and   *
 * height are the dimensions of the LCD screen. *
 * Returns a pointer to shared memory buffer    *
 * or regular ximage buffer.                    *
 ************************************************/
#define CASEWIDTH 221
#define CASEHEIGHT 337
#define LCDWIDTH 160
#define LCDHEIGHT 160
char *xcpInitialize(int no_x_shm)
{
  int depth;
  char *sbuf;
  XEvent ev;

  /*
   * Attempt to open a display connection
   * to the default display
   */
  xcpDisplay = XOpenDisplay(NULL);
  if(!xcpDisplay) {
    printf("E - open display failed\n");
    return NULL;
  }

  /*
   * Get some information about the
   * display
   */
  xcpScreen = DefaultScreenOfDisplay(xcpDisplay);
  xcpScreenNum = DefaultScreen(xcpDisplay);
  depth  = DefaultDepthOfScreen(xcpScreen);
  xcpDepth = depth;

  /*
   * Create a window for the case
   */
  xcpCaseWindow = XCreateSimpleWindow(xcpDisplay, DefaultRootWindow(xcpDisplay),
				    0, 0, CASEWIDTH, CASEHEIGHT, 0,
				    XWhitePixel(xcpDisplay, xcpScreenNum),
				    XBlackPixel(xcpDisplay, xcpScreenNum));
  if(!xcpCaseWindow) {
    (void)printf("E - create case window failed\n");
    return(NULL); 
  }

  /*
   * Create a window for the LCD
   * make it a child of the case window
   * offset it by (32, 33) from TL of case window
   */
  xcpOffPixel = XWhitePixel(xcpDisplay, xcpScreenNum);
  xcpOnPixel = XBlackPixel(xcpDisplay, xcpScreenNum);
  xcpLCDWindow = XCreateSimpleWindow(xcpDisplay, xcpCaseWindow, 32, 33,
				     LCDWIDTH, LCDHEIGHT, 0, 
				     xcpOffPixel, xcpOnPixel);
  if(!xcpLCDWindow) {
    (void)printf("E - create LCD window failed\n");
    return(NULL); 
  }

  /*
   * Tell window manager about our window
   * Fill out the Hints structure and hand
   * it to the window manager
   */
  {
    XSizeHints hints;
    XWMHints wmhints;
    char *window_name = "CoPilot/X";
    char *icon_name = "CoPilot/X";
    XTextProperty windowName, iconName;
    
    hints.flags = PSize | PMaxSize | PMinSize;
    hints.min_width = hints.max_width = hints.width = CASEWIDTH;
    hints.min_height = hints.max_height = hints.height = CASEHEIGHT;
    wmhints.input = True;
    wmhints.flags = InputHint;
    XStringListToTextProperty(&window_name, 1, &windowName);
    XStringListToTextProperty(&icon_name  , 1, &iconName);
    XSetWMProperties(xcpDisplay, xcpCaseWindow, &windowName, &iconName,
		     NULL, 0, &hints, &wmhints, NULL);
  }

  /*
   * Set the event mask for the case window
   * and raise it. Note that we don't expect
   * events from the LCD window, we get all
   * events from the case window
   */
  XSelectInput(xcpDisplay, xcpCaseWindow, ExposureMask | OwnerGrabButtonMask |
	       ButtonPressMask | ButtonMotionMask | ButtonReleaseMask |
	       KeyPressMask);
  XMapRaised(xcpDisplay, xcpCaseWindow);
  XNextEvent(xcpDisplay, &ev);	/* always toss first event */

  /*
   * Display the pixmap of the case
   */
  {
    XpmColorSymbol symbols[10];
    XpmAttributes case_attributes;
    Pixmap case_bitmap, case_mask;
    int status;
    case_attributes.colorsymbols = symbols;
    case_attributes.numsymbols   = 0;
    case_attributes.valuemask    = 0;
    case_attributes.valuemask   |= XpmReturnPixels;
    case_attributes.valuemask   |= XpmReturnInfos;
    case_attributes.valuemask   |= XpmReturnExtensions;
    case_attributes.valuemask   |= XpmCloseness; /* close enough..   */
    case_attributes.closeness    = 40000;        /* ..is good enough */

    status = XpmCreatePixmapFromData(xcpDisplay, xcpCaseWindow, case_xpm,
				     &case_bitmap, &case_mask,
				     &case_attributes);
    if (status != 0) {
      fprintf(stderr, "X - XpmCreatePixmapFromData failed. Sorry, no case.\n");
    } else {
      XSetWindowBackgroundPixmap(xcpDisplay, xcpCaseWindow, case_bitmap);
      XFreePixmap(xcpDisplay, case_bitmap); /* Free Pixmap since we're done */
    }
    XClearWindow(xcpDisplay, xcpCaseWindow);
  }
 /*
  * Check for availability of MIT-SHM
  * if we are allowed!
  */  
  if (no_x_shm) {
    xcpUsingShm = 0;
  } else {
    xcpUsingShm = (xcpCheckXshm(xcpDisplay) == 2);
  }
  if (xcpUsingShm) {
    /*
    fprintf(stderr, "X - Shared mem available, using Shared Mem Images...\n");
     */
    xcpLCDImage = XShmCreateImage(xcpDisplay,
				  DefaultVisual(xcpDisplay, xcpScreenNum), 
				  depth, ZPixmap, NULL, &xcpSHMInfo,
				  LCDWIDTH, LCDHEIGHT);
    if(!xcpLCDImage) {
      (void)printf("E - XShmCreateImage Failed\n");
      return(NULL);
    }
    xcpSHMInfo.shmid = shmget(IPC_PRIVATE,
			   xcpLCDImage->bytes_per_line * xcpLCDImage->height,
			   IPC_CREAT | 0777);
    if(xcpSHMInfo.shmid < 0) {
      fprintf(stderr, "E - shmget failed\n");
      return(NULL);
    }
 
    sbuf = xcpLCDImage->data = xcpSHMInfo.shmaddr = shmat(xcpSHMInfo.shmid, NULL, 0);
    XShmAttach(xcpDisplay, &xcpSHMInfo);
  } else { /* no MIT Shared Mem Extension available -- use regular stuff!! */
    /*
    fprintf(stderr, "X - Shared memory unavailable, using regular images\n");
    */
    sbuf = (char *)malloc(((depth + 7) / 8) * LCDWIDTH * LCDHEIGHT);
    xcpLCDImage = XCreateImage(xcpDisplay,
			       DefaultVisual(xcpDisplay, xcpScreenNum), 
			       depth, ZPixmap, 0, sbuf, LCDWIDTH, LCDHEIGHT,
			       depth, LCDWIDTH * ((depth + 7) / 8));
    if(!xcpLCDImage) {
      (void)printf("E - XCreateImage Failed\n");
      return(NULL);
    }
  }
  
  /*
   * Build the LCD GC (for rendering in the LCD window)
   */
  {
    XGCValues gc_values;
    gc_values.foreground = xcpOffPixel;
    gc_values.background = xcpOnPixel;
    xcpLCDgc = XCreateGC(xcpDisplay, xcpLCDWindow, 0, &gc_values);
  }

  /*
   * Raise the LCD window. Note that we only expect
   * Exposure events from this window
   */
  XSelectInput(xcpDisplay, xcpLCDWindow, ExposureMask);
  XMapRaised(xcpDisplay, xcpLCDWindow);
  XNextEvent(xcpDisplay, &ev);

  /*
   * Install signal handlers
   * and flag xcpStarted
   */
  signal(SIGHUP, xcpSignalQuit); 
  signal(SIGINT, xcpSignalQuit);
  signal(SIGQUIT, xcpSignalQuit); 
  signal(SIGTERM, xcpSignalQuit);
  xcpStarted = 1;

  return sbuf;
}

/*********************************
 * Release allocated X resources *
 * in preparation for exit       *
 *********************************/
void xcpCleanup(void)
{
  if (xcpStarted) {
    if (xcpLCDImage) {
      XDestroyImage(xcpLCDImage);
    }
    if (xcpUsingShm) {
      XShmDetach(xcpDisplay, &xcpSHMInfo);
      if (xcpSHMInfo.shmaddr) {
	shmdt(xcpSHMInfo.shmaddr);
      }
      if (xcpSHMInfo.shmid > 0) {
	shmctl(xcpSHMInfo.shmid, IPC_RMID, 0);
      }
    }
  }
}

/***************************************
 * Put the LCD image on the LCD window *
 ***************************************/
int xcpPutImage(void) {
  if (xcpUsingShm) {
    XShmPutImage(xcpDisplay, xcpLCDWindow, xcpLCDgc, xcpLCDImage, 0, 0, 0, 0, 
		 LCDWIDTH, LCDHEIGHT, False);
  } else {
    XPutImage(xcpDisplay, xcpLCDWindow, xcpLCDgc, xcpLCDImage,  0, 0, 0, 0,
	      LCDWIDTH, LCDHEIGHT);
  }
  return 1;
}

/**********************************************
 * Update the LCD screen                      *
 * sbuf is the pointer to the XImage storage, *
 * screenmap is a pointer to the screen ram   *
 * area of the Pilot.                         *
 **********************************************/
void xcpUpdateLCD(char *sbuf, unsigned char *screenmap)
{
  int i, bit, mask;
  /*
   * Convolutions here are to byte-swap and bit-reverse data!
   */
#ifdef __BIG_ENDIAN__
  for (i = 0; i < LCDWIDTH * LCDHEIGHT / 8; i += 2) {
    for (bit = 7, mask = 1; bit >= 0; bit--, mask <<= 1) {
      if (screenmap[i] & mask) {
	sbuf[8 * i + bit] = xcpOnPixel;
      } else {
	sbuf[8 * i + bit] = xcpOffPixel;
      }
    }
    for (bit = 7, mask = 1; bit >= 0; bit--, mask <<= 1) {
      if (screenmap[i + 1] & mask) {
	sbuf[8 * (i + 1) + bit] = xcpOnPixel;
      } else {
	sbuf[8 * (i + 1) + bit] = xcpOffPixel;
      }
    }
  }
#else
  if (xcpDepth == 16) {
    register unsigned short *wbuf;
    register int n;
 
    wbuf = (unsigned short *) sbuf;
    n = LCDWIDTH * LCDHEIGHT / 8;
 
    for (i = 0; i < n; i += 2) {
      for (bit = 0, mask = 1 << 7; bit < 8; bit++, mask >>= 1) {
	if (screenmap[i + 1] & mask) {
	  wbuf[8 * i + bit] = xcpOnPixel;
	} else {
	  wbuf[8 * i + bit] = xcpOffPixel;
	}
	if (screenmap[i] & mask) {
	  wbuf[8 * (i + 1) + bit] = xcpOnPixel;
	} else {
	  wbuf[8 * (i + 1) + bit] = xcpOffPixel;
	}
      }
    }
  } else {			/* assume depth = 8 */
    for (i = 0; i < LCDWIDTH * LCDHEIGHT / 8; i += 2) {
      for (bit = 0, mask = 1 << 7; bit < 8; bit++, mask >>= 1) {
	if (screenmap[i + 1] & mask) {
	  sbuf[8 * i + bit] = xcpOnPixel;
	} else {
	  sbuf[8 * i + bit] = xcpOffPixel;
	}
      }
      for (bit = 0, mask = 1 << 7; bit < 8; bit++, mask >>= 1) {
	if (screenmap[i] & mask) {
	  sbuf[8 * (i + 1) + bit] = xcpOnPixel;
	} else {
	  sbuf[8 * (i + 1) + bit] = xcpOffPixel;
	}
      }
    }
  }
#endif
  xcpPutImage();
}

#define UTIMER 10000		/* how long between updates (useconds) */
extern void *CPU_getmemptr(CPTR addr);
extern int dbg_loadapp(FILE *out, FILE *in, char *cmd, char *line,
		       shared_img *shptr);

/************************************************************
 * Handle a pen event                                       *
 * img is a pointer to the shared memory structure which    *
 * holds the custom registers which are shared between the  *
 * CPU and Display processes. down is 1 if the pen is down, *
 * x and y are the coordinates of the pen                   *
 ************************************************************/
void xcpPenEvent(shared_img *img, int down, int x, int y)
{
  if (!img->pendown && down) {	/* pen going down */
    img->pen = 1;
    img->run_updateisr = 1;
  } else if (img->pendown && !down) { /* pen coming up */
    img->pen = 0;
    img->run_updateisr = 1;
  }
  img->pendown = down;
  img->penx = x - 30;
  img->peny = y - 30;
}

/************************************************************
 * Handle a key event                                       *
 * img is a pointer to the shared memory structure which    *
 * holds the custom registers which are shared between the  *
 * CPU and Display processes. down is 1 if the key is down, *
 * key is the number of the key                             *
 ************************************************************/
void xcpKeyEvent(shared_img *img, int down, int key)
{
  img->keydown = down;
  img->key = key;
  img->run_updateisr = 1;
}

/*********************************************
 * Convert a coordinate to a key number. The *
 * coordinates are for the realistic looking *
 * pilot case pixmap.                        *
 * If the coordinates do not correspond to a *
 * key, then -1 is returned                  *
 *********************************************/
int xcpKeyFromCoord(int x, int y)
{
  int key = -1;
  if (y >= 276 && y < 318) {
    if (x >= 0 && x < 17) {
      key = 0; /* power */
    } else if (x >= 25 && x < 53) {
      key = 3; /* datebook */
    } else if (x >= 64 && x < 92) {
      key = 4; /* phone */
    } else if (x >= 100 && x < 125) {
      if (y >= 280 && y < 292) {
	key = 1; /* up */
      } else if (y >= 302 && y < 314) {
	key = 2; /* down */
      }
    } else if (x >= 135 && x < 162) {
      key = 5; /* todo */
    } else if (x >= 174 && x < 200) {
      key = 6; /* memo */
    }
  }
  /*
  fprintf(stderr, "X - Key is %d (%d, %d)\n", key, x, y);
  fflush(stderr);
  */
  return key;
}


void xcpKBtoASCII(shared_img *shptr, XEvent *event)
{
  static KeySym		keysym;
  static XComposeStatus	compose;
  int			count, bufsize, in;
  char			buffer[8];

  bufsize = 8;
  count = XLookupString((XKeyEvent *) event,
			buffer,
			bufsize,
			&keysym,
			&compose);
  if (count > 0) {
    if (buffer[0] == '\r') {
      buffer[0] = '\n';
    }
    in = shptr->kbin;
    shptr->kb[in] = buffer[0];
    shptr->kbin = (in + 1) & 7;
  }
}

/***********************************************
 * This is the main event loop                 *
 * We loop in here updating the LCD screen     *
 * while mapping mouse events to xcpPenEvent's *
 ***********************************************/
void xcpEventLoop(char *sbuf, shared_img *shptr)
{
  extern char *id_version;
  XEvent event;
  int cleared = 0;
  int PenTracking = 0;
  int KeyTracking = 0;
  /*
   * These flags are set if we are tracking a pen event or a key event
   * respectively. That is, if the mouse button went down on the pen
   * area or on a key. When the button goes up, regardless of where the
   * mouse currently is, we must release the last event.
   */
  int LastKey = -1;
  /*
   * This is the key number of the last key event. When we do a key up,
   * we must use the key number that we generated a key down event for
   */

  struct timeval tv, otv;
  long ut;
  unsigned char *scrnmap;
  mx_appearance *app;		/* appearance structure form menu */
				/* panels for menus */
  mx_panel *panel_main, *panel_about, *panel_ok;
  mx_panel *panel_appok, *panel_appfail;
  int x, y;

  app = mx_appearance_create(xcpDisplay, xcpScreenNum, xcpCaseWindow,
			     "lucidasans-14", 2, 3, 0, 0, NULL, xcpPutImage);
  panel_main = mx_panel_create(xcpDisplay, app, MAIN_N_ITEMS, main_items);
  about_items[0].text = id_version;
  panel_about = mx_panel_create(xcpDisplay, app, ABOUT_N_ITEMS, about_items);
  panel_ok = mx_panel_create(xcpDisplay, app, OK_N_ITEMS, ok_items);
  panel_appok = mx_panel_create(xcpDisplay, app, APPOK_N_ITEMS, appok_items);
  panel_appfail = mx_panel_create(xcpDisplay, app, APPFAIL_N_ITEMS, appfail_items);

  gettimeofday(&otv, NULL);
  while (!xcpQuit && shptr->CpuReq != cpuExit) {
    if (shptr->LcdPower == lcdOff) {
      /*
       * When screen is turned off, make everything "black".
       */
      if (cleared == 0) {
	XClearWindow (xcpDisplay, xcpLCDWindow);
	cleared ++;
      }
    } else {
      /*
       * Get current location of screen ram from
       * dragonball register LSSA, then update LCD
       */
      scrnmap = (unsigned char *)CPU_getmemptr(shptr->lssa);
      xcpUpdateLCD(sbuf, scrnmap);
      cleared = 0;
    }

    /*
     * maybe ring the bell
     */
    if (shptr->LcdReq == lcdBell) {
      XBell(xcpDisplay, 50);
      shptr->LcdReq = lcdNone;
    }

    /*
     * Check for pending events
     */
    while (XEventsQueued(xcpDisplay, QueuedAfterReading)) {
      XNextEvent(xcpDisplay, &event);
      switch(event.type) {
      case KeyPress:
	xcpKBtoASCII(shptr, &event);
	break;
      case Expose:
	break;
      case ButtonPress:		/* a mouse button was pressed */
	x = event.xbutton.x_root;
	y = event.xbutton.y_root;
	if (event.xbutton.button == 1) { /* left button - pen down */
	  PenTracking = 0;
	  KeyTracking = 0;
	  if (event.xbutton.y < 260) {	/* pen digitizer event */
	    xcpPenEvent(shptr, 1, event.xbutton.x, event.xbutton.y);
	    PenTracking = 1;
	  } else {
	    LastKey = xcpKeyFromCoord(event.xbutton.x, event.xbutton.y);
	    if (LastKey >= 0) {
	      xcpKeyEvent(shptr, 1, LastKey);
	      KeyTracking = 1;
	    }
	  }
	} else if (event.xbutton.button == 3) { /* right button - exit */
	  int choice;
	  choice = mx_popup_menu(xcpDisplay, xcpScreenNum, 
				 panel_main, &x, &y, True);
	  switch (choice) {
	    char *appname;
	    int rx, ry;
	  case 1:		/* Load App.. */
	    rx = x;
	    ry = y;
	    appname = mx_popup_filesel(xcpDisplay, xcpScreenNum,
				       app, &rx, &ry);
	    if (appname != NULL) {
	      if (dbg_loadapp(NULL, NULL, NULL, appname, shptr) == 0) {
		mx_popup_alert(xcpDisplay, xcpScreenNum,
			       panel_appok, panel_ok, &x, &y);
	      } else {
		mx_popup_alert(xcpDisplay, xcpScreenNum,
			       panel_appfail, panel_ok, &x, &y);
	      }
	    }
	    break;
	  case 2:		/* About.. */
	    mx_popup_alert(xcpDisplay, xcpScreenNum,
			   panel_about, panel_ok, &x, &y);
	    break;
	  case 4:		/* Quit */
	    shptr->CpuReq = cpuExit;
	    break;
	  default:
	  }
	}
	break;
      case ButtonRelease:		/* a mouse button was released */
	if (event.xbutton.button == 1) { /* left button - pen up */
	  if (PenTracking) {
	    xcpPenEvent(shptr, 0, event.xbutton.x, event.xbutton.y);
	  } else if (KeyTracking) {
	    xcpKeyEvent(shptr, 0, LastKey);
	  }
	}
	break;
      case MotionNotify:	/* mouse moved with button down */
	xcpPenEvent(shptr, 1, event.xbutton.x, event.xbutton.y);
	break;
      default:
	break;
      }
    }
    /*
     * now work out how long to delay for
     * so we don't refresh display too fast
     */
    XFlush(xcpDisplay);
    gettimeofday(&tv, NULL);
    ut = tv.tv_usec - otv.tv_usec;
    if(tv.tv_sec > otv.tv_sec)
      ut += 1000000;
    if(UTIMER - ut > 0) {
      unsigned long usecs = UTIMER - ut;
      tv.tv_sec  = usecs / 1000000L;
      tv.tv_usec = usecs % 1000000L;
      (void) select (0, 0, 0, 0, &tv);
    }
    gettimeofday(&otv, 0);
  }

  /*
   * Once we drop out the event loop
   * it means we are quitting..
   */
  /*
  fprintf(stderr, "X - Exiting\n");
  */
  xcpCleanup();
  shptr->CpuReq = cpuExit;
  return;
}
