/*
 *	Xgraf.c
 *
 *	X11 implementation of Xgraf (the simple Modula-2 graphics library)
 */

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> 
#include "bool.h"
#include "Xcolours.h"
#include "Xgraf.h"
#include "Xgrafprivate.h"



typedef enum { debug_none,debug_important,debug_all } debug_type;


#define WS_MIN      1
#define WS_MAX      2
#define WS_BACKINGSTORE 4
#define WS_SAVEUNDER    8
#define WS_MAPWIN   16
#define WS_BGCOL    32


typedef struct{
  int   mask;
  int   minw,minh;
  int   maxw,maxh;
  int   eventmask;
  Colour    bgcol;
} windowsettings;


typedef struct{
  int         size,used;
  int         current;
  char        **name;         /* fontnames */
  XFontStruct **font;     /* all the fonts */
} fontstruct;


typedef struct{
  Window    win;
  int   w,h;
} windowstruct;


typedef struct{
  int       size,used;
  windowstruct  *data;          /* array of window sizes */
} winsizedata;


static Window         rootwin;        /* the root window */
static Display        *dpy;           /* the display */
static int            thescreen;  
static Window         currentwin;     /* the window */
static winsizedata    winsize;        /* window sizes */
static GC             gc;             /* graphics context */
static PlotAttribute  current_pen;    /* colour of current pen */
static int            planes;         /* no. of colour planes */
static int            maxcolours;     /* max possible # colours */
static int            ncolours;       /* # of colour names above */
static int            tablesize;      /* size of colour name table */
static unsigned long  *colourvector;  /* vector of pixel values */
static Colormap       cmap;           /* the colour map */
static Cursor         user_cursor; 
static Cursor         busy_cursor;    
static windowsettings wsettings;      /* per window settings */
static unsigned long  fg,bg;         /* foregound and background */
static bool           shared_colour_map=TRUE; /* use the default? */
static bool           use_rgb_colour_map=FALSE;/* using RGB ? */
static fontstruct    fonts;           /* all the fonts */
static debug_type    debugging = debug_none; 

/*
 *  Allow all events by default; naive users can ignore them by simply
 *  not doing anything when they arrive
 */
#define DEFAULT_EVENTMASK   (ExposureMask|KeyPressMask|KeyReleaseMask|               ButtonPressMask|ButtonReleaseMask|              EnterWindowMask|LeaveWindowMask|                ButtonMotionMask|PointerMotionMask|                 StructureNotifyMask)

/*
 *	arbitrary limit for size of polygon handled by FillPolygon,
 *	to make memory allocation simpler
 *	(we should probably do it with malloc() followed by check for success,
 *	which would not impose an arbitrary limit - but this is simpler)
 */
#define FillPolygon_MaxNoOfVerticesInAPolygon	1000

#define min(x,y)    ((x)<(y)?(x):(y))
#define swap(x,y)   {int t; t=x; x=y; y=t;}


static void ResetWindowSettings(void);
static void InitialiseFonts(int);
static void FreeFonts(void);
static void InitialiseColour(void);
static void setRWcolours(void);
static void setROcolours(void);
static void FreeColours(void);
static void On(void);
static void Off(void);
static void Invert(void);
static void InitialiseCursors(void);
static void FreeCursors(void);
static void InitialisePixmaps(void);
static void FreePixmaps(void);
static void InitialiseClipList(int);
static void AddToClipList(int,XRectangle);
static void FreeClipList(void);
static void InitialiseWinList(int);
static void AddWin(Window,int,int);
static Window DeleteWin(Window);
static int FindWin(Window,int *,int *);
static void SetWinSize(Window,int,int);
static void FreeWinList(void);
static void fatal(char *);
static void fatal_int(char *,int);
static void fatal_str(char *,char *);



/* ------------------------ Starting and Stopping ---------------------- */


void EnquireSystem(int *xpixels,int *ypixels,float *xsize,float *ysize){
  *xpixels    = DisplayWidth(dpy,thescreen);
  *ypixels    = DisplayHeight(dpy,thescreen);
  *xsize      = (float)*xpixels / (float)DPI_GUESS;
  *ysize      = (float)*ypixels / (float)DPI_GUESS;
}


void UseSharedColourMap(void){
  shared_colour_map = TRUE;
  use_rgb_colour_map = FALSE;
}


void UseOwnColourMap(void){
  shared_colour_map = FALSE;
  use_rgb_colour_map = FALSE;
}


void UseRGBColourMap(void){
  shared_colour_map = FALSE;
  use_rgb_colour_map = TRUE;
}


void DebugImportant(void){
  debugging = debug_important;
}


void DebugAll(void){
  debugging = debug_all;
}


void InitialiseGraf(void){
  XGCValues       values;

  /* Open the X connection */
  if ((dpy=XOpenDisplay(NULL)) == NULL){
	  fatal("Can't open connection to X-server");
	  /*NOTREACHED*/
  }

  thescreen = DefaultScreen(dpy);
  currentwin = rootwin = RootWindow(dpy,thescreen);
  planes = DefaultDepth(dpy,thescreen);
  gc = XCreateGC(dpy,currentwin,0L,&values);

  InitialiseColour();     /* build colour map */
  InitialiseFonts(10);    /* initialise fonts */
  InitialiseCursors();    /* define some cursors */
  InitialisePixmaps();    /* define some fill patterns */
  InitialiseClipList(10); /* initialise cliplist structure */
  InitialiseWinList(10);  /* initialise the window list */
  ResetWindowSettings();  /* set default window settings */

  fg = colourvector[(int)Black];  /* setup GC */
  bg = colourvector[(int)White];
  (void)XSetFillStyle(dpy,gc,FillSolid);
  On();       
}


void CloseDownGraf(void){
  /* Fix to bug discovered by Nick Williams,zmacu19 */
  /* if the display queue still contains requests at this   */
  /* point,the freeing of the font without flushing causes */
  /* an X protocol error.                                   */

  /* clean up any remaining display requests */
  (void)XSync(dpy,True);

  FreeColours();
  FreeFonts();
  FreeCursors();
  FreePixmaps();
  FreeClipList();
  FreeWinList();

  (void)XCloseDisplay(dpy);
}


Display *GetDisplay(void){
  return dpy;
}


/* -------------------------- Windows -------------------------------- */


Window RootWin(void){
  return rootwin;
}


/*
 *  Reset mask and eventmask to defaults
 */

static void ResetWindowSettings(void){
  wsettings.mask      = WS_MAPWIN;
  wsettings.eventmask = DEFAULT_EVENTMASK;
}


void SetEventMask(EventMask mask){
  wsettings.eventmask = ExposureMask;
  if ((mask & MaskKeypress) != 0){
    wsettings.eventmask |= KeyPressMask;
  }
  if ((mask & MaskKeyrelease) != 0){
    wsettings.eventmask |= KeyReleaseMask;
  }
  if ((mask & MaskMouseDown) != 0) {
    wsettings.eventmask |= ButtonPressMask;
  }
  if ((mask & MaskMouseUp) != 0){
    wsettings.eventmask |= ButtonReleaseMask;
  }
  if ((mask & MaskEnter) != 0){
    wsettings.eventmask |= EnterWindowMask;
  }
  if ((mask & MaskLeave) != 0){
    wsettings.eventmask |= LeaveWindowMask;
  }
  if ((mask & MaskButtonMotion) != 0) {
    wsettings.eventmask |= ButtonMotionMask;
    wsettings.eventmask &= ~ PointerMotionMask;
  }
  if ((mask & MaskAllMotion) != 0){
    wsettings.eventmask |= ButtonMotionMask|PointerMotionMask;
  }
  if ((mask & MaskResize) != 0){
    wsettings.eventmask |= StructureNotifyMask;
  }
}


void SetWBackground(Colour col){
  wsettings.mask |= WS_BGCOL;
  wsettings.bgcol = col;
}


void SetWMinSize(int w,int h){
  wsettings.mask |= WS_MIN;
  wsettings.minw = w;
  wsettings.minh = h;
}


void SetWMaxSize(int w,int h){
  wsettings.mask |= WS_MAX;
  wsettings.maxw = w;
  wsettings.maxh = h;
}


void SetWBackingStore(void){
  wsettings.mask |= WS_BACKINGSTORE;
}


void SetWSaveUnder(void){
    wsettings.mask |= WS_SAVEUNDER;
}


void SetWNotShown(void){
  wsettings.mask &= ~WS_MAPWIN;
}


Window MakeWindow(Window parent,char *title,int winX,int winY,int winW,int winH){
  unsigned int        xswamask;
  XSetWindowAttributes    xswa;
  XSizeHints      size_h;

  /* set up the Window Attributes structure & colourmaps */
  xswa.background_pixel   = bg = colourvector[(int)White];
  xswa.border_pixel   = fg = colourvector[(int)Black];
  xswamask        = CWBackPixel | CWBorderPixel;

  if (! shared_colour_map){
    xswa.colormap   = cmap;
    xswamask    |= CWColormap;
    if (debugging == debug_all){
      (void)fprintf(stderr,"MakeWindow: own colour map desired\n");
    }
  }

  if ((wsettings.mask & WS_BACKINGSTORE) != 0){
    xswamask       |= CWBackingStore;
    xswa.backing_store = WhenMapped;
    if (debugging == debug_all){
      (void)fprintf(stderr,"MakeWindow: backing store desired\n");
    }
  }
  if ((wsettings.mask & WS_SAVEUNDER) != 0){
    xswamask       |= CWSaveUnder;
    xswa.save_under    = True;
    if (debugging == debug_all){
      (void)fprintf(stderr,"MakeWindow: saveunder desired\n");
    }
  }

  /* the window itself */
  currentwin = XCreateWindow(dpy,parent,
                              winX,winY,winW,winH,
                              2,CopyFromParent,CopyFromParent,
                              CopyFromParent,xswamask,&xswa);
  
  if (! shared_colour_map){
      /* use this colour map */
    (void)XSetWindowColormap(dpy,currentwin,cmap);
  }

  (void)XSelectInput(dpy,currentwin,wsettings.eventmask);
  
    /* set the window name  */
  (void)XChangeProperty(dpy,currentwin,XA_WM_NAME,XA_STRING,8,
                         PropModeReplace,title,strlen(title));

  /*
   *  Tell the window manager about the size and location of the windows.
   */
  
  size_h.flags    = USSize;
  size_h.flags    |= (winX == -1 || winY == -1) ? PPosition : USPosition;
  size_h.x        = winX;
  size_h.y         = winY;
  size_h.width    = winW;
  size_h.height   = winH;

  if (debugging == debug_all){
    (void)fprintf(stderr,"MakeWindow: creating window width (%d,%d)\n",winW,winH);
    (void)fprintf(stderr,"MakeWindow: window position is %s (%d,%d)\n",
                   (winX == -1 || winY == -1) ? "PPosition" : "USPosition",
                   winX,winY);
  }

  if ((wsettings.mask & WS_MIN) != 0) {
    size_h.flags       |= PMinSize;
    size_h.min_width   = wsettings.minw;
    size_h.min_height  = wsettings.minh;
    if (debugging == debug_all){
      (void)fprintf(stderr,"MakeWindow: min geom (%d,%d) desired\n",
                     wsettings.minw,wsettings.minh);
    }
  }
  if ((wsettings.mask & WS_MAX)!= 0){
    size_h.flags       |= PMaxSize;
    size_h.max_width   = wsettings.maxw;
    size_h.max_height  = wsettings.maxh;
    if (debugging == debug_all){
      (void)fprintf(stderr,"MakeWindow: max geom (%d,%d) desired\n",
                     wsettings.maxw,wsettings.maxh);
    }
  }

  XSetWMNormalHints(dpy,currentwin,&size_h);

  if ((wsettings.mask & WS_MAPWIN) != 0){
      /* try to make the window appear */
    (void)XMapWindow(dpy,currentwin);
    if (debugging == debug_all){
      (void)fprintf(stderr,"MakeWindow: map window desired\n");
    }
  } 
  else if (debugging == debug_all) {
    (void)fprintf(stderr,"MakeWindow: map window not desired\n");
  }

  if ((wsettings.mask & WS_BGCOL) != 0){
    (void)XSetWindowBackground(dpy,currentwin,wsettings.bgcol);
    if (debugging == debug_all){
      (void)fprintf(stderr,"MakeWindow: background colour = %d\n",
                     wsettings.bgcol);
    }
  }

  ResetWindowSettings();  /* for next call to MakeWindow */
  AddWin(currentwin,winW,winH);
  return currentwin;
}


void SelectWindow(Window w){
  currentwin = w;
}

void ShowWindow(void){
  (void)XMapWindow(dpy,currentwin);
}

void HideWindow(void){
  (void)XUnmapWindow(dpy,currentwin);
}

void MoveResizeWindow(int x,int y,int w,int h){
  (void)XMoveResizeWindow(dpy,currentwin,x,y,w,h);
}

void MoveWindow(int x,int y){
  (void)XMoveWindow(dpy,currentwin,x,y);
}

void ResizeWindow(int w,int h){
  (void)XResizeWindow(dpy,currentwin,w,h);
}

void CloseWindow(void){
  (void)XDestroyWindow(dpy,currentwin);
  currentwin = DeleteWin(currentwin);
}

void ClearWindow(void){
  (void)XClearWindow(dpy,currentwin);
}

/* --------------------------- Fonts -------------------------------- */

static void InitialiseFonts(int numfonts){
  fonts.size = numfonts;
  fonts.font = (XFontStruct **) malloc(numfonts * sizeof(XFontStruct *));
  fonts.name = (char **) malloc(numfonts * sizeof(char *));
  fonts.name[0] = strdup(FONT);
  if ((fonts.font[0] = XLoadQueryFont(dpy,FONT))==NULL){
    fatal_str("Display doesn't know default font",FONT);
      /*NOTREACHED*/
  }
  fonts.used = 1;
  SelectFont(0);
}


Font LoadFont(char *name){
  XFontStruct *f;

  if (fonts.size == fonts.used){
    fonts.size += 5;
    fonts.font = (XFontStruct **) realloc((char *) fonts.font,
                                           fonts.size * sizeof(XFontStruct *));
    fonts.name = (char **) realloc((char *) fonts.name,
                                    fonts.size * sizeof(char *));
  }

  f = XLoadQueryFont(dpy,name);
  if (debugging == debug_all){
    (void)fprintf(stderr,
                   "LoadFont: loaded font '%s',XFontStruct * = %X\n",
                   name,(int) f);
  }
  if (f != NULL){
    fonts.font[fonts.used] = f;
    fonts.name[fonts.used] = strdup(name);
    return fonts.used++;
  }
  if (debugging != debug_none){
    (void)fprintf(stderr,"LoadFont: unknown font '%s'\n",name);
  }
  return 0;
}


Font DefaultFont(void){
  return 0;
}


void SelectFont(Font f){
  (void)XSetFont(dpy,gc,fonts.font[f]->fid);
  fonts.current = f;
}


void GetFontName(Font f,char *name){
  strcpy(name,fonts.name[f]);
}

void CharSize(char ch,int *w,int *h){
  char        msg[2];
  XFontStruct *f = fonts.font[fonts.current];
  
  msg[0] = ch;
  msg[1] = '\0';

  *h = f->max_bounds.ascent + f->max_bounds.descent;
  *w = XTextWidth(f,msg,1);
}


void StringSize(char *msg,int *w,int *h){
  XFontStruct *f = fonts.font[fonts.current];

  *h = f->max_bounds.ascent + f->max_bounds.descent;
  *w = XTextWidth(f,msg,strlen(msg));
}

static void FreeFonts(void){
  int i;

  for (i = 0; i < fonts.used; i++){
    (void)XFreeFont(dpy,fonts.font[i]);
    free(fonts.name[i]);
  }
}


/* --------------------------- Colours -------------------------------- */


/*
 *  InitialiseColour either sets up a new colour map,or uses the default one.
 *  Then it allocates as many of the named colours as will fit.
 */

static void InitialiseColour(void){
  if (use_rgb_colour_map && planes == 1){
    (void) fprintf(stderr,"InitialiseGraf: No RGB colour map on a monochrome screen!\n");
    use_rgb_colour_map = FALSE;
  }

    /* count the colours in the table */
  for (tablesize=0; colour_names[tablesize] != NULL; ++tablesize);

  if (shared_colour_map){
    cmap = DefaultColormap(dpy,thescreen);

  /* find the maximum number of colours available in the default colour map */
    maxcolours = DisplayCells(dpy,thescreen);
  } 
  else{
    cmap = XCreateColormap(dpy,rootwin,
                            DefaultVisual(dpy,thescreen),
                            use_rgb_colour_map ? AllocAll : AllocNone);

      /* find the maximum possible number of colours on this screen*/

    maxcolours = 1<<planes;
  }
  ncolours = min(maxcolours,tablesize);

  if (debugging == debug_all){
    (void) fprintf(stderr,"tablesize  = %d\n",tablesize);
    (void) fprintf(stderr,"maxcolours = %d\n",maxcolours);
    (void) fprintf(stderr,"ncolours   = %d\n",ncolours);
  }

    /* allocate the colour vector */
  colourvector = (unsigned long *)
    malloc((unsigned) (maxcolours * sizeof(unsigned long)));

    /* allocate as many as poss of the named colours to pixel values
     * mapped through the colour map
     */

  if (use_rgb_colour_map){
    setRWcolours();
  } 
  else {
    setROcolours();
  }
}


static void setRWcolours(void){
  int i;
  XColor dummy,hw;

  for (i=0; i<maxcolours; ++i){
    colourvector[i] = i;
  }

  for (i=0; i<ncolours; ++i){
    if (XLookupColor(dpy,cmap,colour_names[i],&dummy,&hw) == 0){
      if (debugging != debug_none){
        (void) fprintf(stderr,"lookup for colour '%s' failed - using %s\n",
                       colour_names[i],"black");
      }
      hw.red = hw.green = hw.blue = 0;
    }
    hw.flags = (char)(DoRed | DoGreen | DoBlue);
    hw.pixel = colourvector[i];
    (void)XStoreColor(dpy,cmap,&hw);
  }
}


static void setROcolours(void){
  int i;
  XColor hw,rgb;

  for (i=0; i<ncolours; ++i){
    if (XAllocNamedColor(dpy,cmap,colour_names[i],&hw,&rgb) != 0) {
      colourvector[i] = hw.pixel;
      if (debugging == debug_all) {
        (void) fprintf(stderr,"Logical colour %d is physical pixel %ld\n",
                        i,colourvector[i]);
      }
    } 
    else {
      if (debugging != debug_none){
        (void) fprintf(stderr,"Colour allocation failed at %s\n",
                        colour_names[i]);
      }
      ncolours = i;
    }
  }
  
  if (debugging == debug_all){
    (void) fprintf(stderr,"final ncolours = %d\n",ncolours);
  }
}


void SetRGB(Colour col,int r,int g,int b){
  XColor rgb;

  if (! use_rgb_colour_map){
    fatal("You can only set RGB with an RGB colour map!\n");
      /*NOTREACHED*/
  }

  rgb.red   = r;
  rgb.green = g;
  rgb.blue  = b;
  rgb.flags = (char)(DoRed | DoGreen | DoBlue);
  rgb.pixel = colourvector[(int)col];
  (void)XStoreColor(dpy,cmap,&rgb);
}


void GetRGB(Colour col,int *r,int *g,int *b){
  XColor rgb;

  if (! use_rgb_colour_map){
    fatal("You can only get RGB from an RGB colour map!\n");
      /*NOTREACHED*/
  }

  rgb.pixel = colourvector[(int)col];

  (void)XQueryColor(dpy,cmap,&rgb);
  *r = rgb.red;
  *g = rgb.green;
  *b = rgb.blue;
}


void SetColour(Colour colour){
  if ((int)colour < 0){
    fatal_int("SetColour: bad colour number",(int)colour);
      /*NOTREACHED*/
  }
  if ((int)colour >= ncolours && ! use_rgb_colour_map) {
    if (debugging != debug_none){
      (void) fprintf(stderr,"Attempt to set colour %d - only %d colours!\n",
                      (int)colour,ncolours);
    }
    colour = (Colour) (ncolours-1);
  }
  (void)XSetForeground(dpy,gc,fg = colourvector[(int)colour]);
}


void SetBackColour(Colour colour){
  if ((int)colour < 0){
    fatal_int("SetBackColour: bad colour number",
              (int)colour);
      /*NOTREACHED*/
  }
  if ((int)colour >= ncolours && ! use_rgb_colour_map){
    if (debugging != debug_none){
      (void)fprintf(stderr,"Attempt to set back colour %d - only %d colours\n",
                    (int)colour,ncolours);
    }
    colour = (Colour) (ncolours-1);
  }
  (void)XSetBackground(dpy,gc,bg = colourvector[(int)colour]);
  (void)XSetWindowBackground(dpy,currentwin,bg);
}


void GetColourName(Colour colour,char *name){
  if ((int)colour < 0){
    fatal_int("GetColourName: bad colour name",(int)colour);
      /*NOTREACHED*/
  }
  if ((int)colour >= ncolours) colour = (Colour) (ncolours-1);
  strcpy(name,colour_names[(int)colour]);
}


Colour FirstColour(void){
  return (Colour) 0;
}

Colour LastColour(void){
  return (Colour) (use_rgb_colour_map ? maxcolours-1 : ncolours-1);
}

int NumColours(void){
  return use_rgb_colour_map ? maxcolours : ncolours;
}

static void FreeColours(void){
    /* destroy the shared colourmap to use a private one */
  if (! shared_colour_map){
    (void)XFreeColormap(dpy,cmap);
  }
}


/* -------------------------- Events -------------------------------- */


static bool allownoevent = FALSE;

void AllowNoEvent(void){
  allownoevent = TRUE;
}


void GetEvent(Window *win,EventType *event,EventData *data){
  XEvent      xev;
  EventData   useifdatanull;

    /* Enhancement: These could result in null pointer dereferences */
    /* make sure that they don't                    */
  if (event == NULL){
    fatal("Can't use GetEvent without an event");
      /*NOTREACHED*/
  }
  *event = NoEvent;

  if (data == NULL){
      /* a warning in case they didn't realise */
    if (debugging == debug_all){
      (void) fprintf(stderr,"data parameter to GetEvent is NULL\n");
    }
    data = &useifdatanull;
  }
  data->x     = 0;
  data->y     = 0;
  data->ch    = '\0';
  data->button    = 0;

  do {
    (void)XNextEvent(dpy,&xev);
    *win = xev.xany.window;

    switch (xev.type){
    /* EXPOSE EVENT */
        case Expose:
        {
          XRectangle  r;
          XExposeEvent    *xexp = &xev.xexpose;

            /* debugging code */
          if (debugging == debug_all) {
            (void) fprintf(stderr,"Event was an Expose: win =%ld,count =%d\n",
                            xexp->window,xexp->count);
          }

          r.x  = xexp->x;
          r.y  = xexp->y;
          r.width  = xexp->width;
          r.height = xexp->height;

          AddToClipList(xexp->count,r);

          if (xexp->count == 0){
            *event = EventExpose;
          }
        }
        break;
        
          /* BUTTONRELEASE and BUTTONPRESS*/
        case ButtonRelease:
        case ButtonPress:
        {
          XButtonEvent *xevb = &xev.xbutton;

          data->x = xevb->x;
          data->y = xevb->y;
          switch (xevb->button) {
              case Button1:
                data->button = 1; break;
              case Button2:
                data->button = 2; break;
              default:
                data->button = 3; break;
          }
          *event = xevb->type == ButtonPress ?
            EventMouseDown : EventMouseUp;

            /* debugging code */
          if (debugging == debug_all){
            (void) fprintf(stderr,"Event was a %s: on button %d at (%d,%d)\n",
                            xevb->type == ButtonPress?"ButtonPress":
                            "ButtonRelease",
                            data->button,xevb->x,xevb->y);
          }
        }
        break;

          /* MOTIONNOTIFY */
        case MotionNotify:
        {
          XMotionEvent *xevm = &xev.xmotion;
          data->x = xevm->x;
          data->y = xevm->y;
          *event = EventMove;

            /* debugging code */
          if (debugging == debug_all) {
            (void) fprintf(stderr,
                            "Event was a Motion: to (%d,%d)\n",xevm->x,xevm->y);
          }

        }
        break;

        /* KEYPRESS/KEYRELEASE */
        case KeyPress:
        case KeyRelease:
        {
          XKeyEvent   *xevk = &xev.xkey;
          KeySym      keysym;
          XComposeStatus  st;
          char    buffer[30];
          int ret = XLookupString(xevk,buffer,29,&keysym,&st);

          if (debugging == debug_all){
            (void) fprintf(stderr,"Event was a %s: keysym is %u,state is %u\n",
                            xevk->type == KeyPress?"KeyPress":
                            "KeyRelease",
                            (unsigned int)keysym,xevk->state);
          }
          if (ret == 1) {
            if (debugging == debug_all) {
              (void) fprintf(stderr,"%s: Key is '%c' (%d)\n",
                              xevk->type == KeyPress?"KeyPress":
                              "KeyRelease",
                              (char)keysym,(int)keysym);
            }

            data->ch = keysym;
          } 
          else if (debugging == debug_all){
            (void) fprintf(stderr,"%s: keysym only\n",
                            xevk->type == KeyPress?"KeyPress":
                            "KeyRelease");
            data->ch = '\0';
          }
          data->keycode   = keysym;
          data->keystate  = xevk->state;
          *event = xevk->type == KeyPress ?
            EventKeypress : EventKeyrelease;
        }
        break;

        /* CONFIGURENOTIFY*/
        case ConfigureNotify:
        {
          XConfigureEvent *xevc = &xev.xconfigure;
          int oldw,oldh;
          (void) FindWin(xevc->window,&oldw,&oldh);

          if (oldw != xevc->width || oldh != xevc->height){
            data->w = xevc->width;
            data->h = xevc->height;
            *event  = EventResize;
            SetWinSize(xevc->window,xevc->width,
                        xevc->height);
              /* debugging code */
            if (debugging == debug_all){
              (void) fprintf(stderr,"Event was a Resize: (%d,%d)\n",
                              data->w,data->h);
            }
          } 
          else {
              /* debugging code */
            if (debugging == debug_all){
              (void) fprintf(stderr,"Event was a Configure: %s\n",
                              "discarded (no size change)");
            }
          }

          
        }
        break;
		  
    }
  }
  while (!allownoevent && *event == NoEvent);
}


bool EventPending(void){
  return (XPending(dpy) != 0);
}


/* -------------------------- Plot Attributes ---------------------------- */


void SetPlotAttribute(PlotAttribute p){
  if (current_pen != p){
    switch (p){
        case OnPixel:
          On();
          break;
        case OffPixel:
          Off();
          break;
        case InvertPixel:
          Invert();
          break;
        default:
          fatal("SetPlotAttribute: invalid value of P");
            /*NOTREACHED*/
    }
  }
}


static void On(void){
  XGCValues   xgcv;

  current_pen = OnPixel;
  (void)XSetPlaneMask(dpy,gc,AllPlanes);
  xgcv.function = GXcopy;
  (void)XChangeGC(dpy,gc,GCFunction,&xgcv);
  (void)XSetForeground(dpy,gc,fg);
  (void)XSetBackground(dpy,gc,bg);
}


static void Off(void){
  XGCValues   xgcv;

  current_pen = OffPixel;
  (void)XSetPlaneMask(dpy,gc,AllPlanes);
  xgcv.function = GXcopy;
  (void)XChangeGC(dpy,gc,GCFunction,&xgcv);
  (void)XSetForeground(dpy,gc,bg);
  (void)XSetBackground(dpy,gc,fg);
}


static void Invert(void){
  XGCValues   xgcv;

  current_pen = InvertPixel;
  (void)XSetPlaneMask(dpy,gc,1L);
  xgcv.function = GXinvert;
  (void)XChangeGC(dpy,gc,GCFunction,&xgcv);
  (void)XSetForeground(dpy,gc,fg);
  (void)XSetBackground(dpy,gc,bg);
}


/* ---------------------- Line Thickness and Style ------------------------ */


void SetLineThickness(int w){
    XGCValues xgcv;
    xgcv.line_width = w;
    (void)XChangeGC(dpy,gc,GCLineWidth,&xgcv);
}


void SetLineStyle(LineStyle s){
  XGCValues xgcv;
  switch (s){
      case LineSolidStyle:
        xgcv.line_style = LineSolid;
        break;
      case LineDashStyle:
        xgcv.line_style = LineOnOffDash;
        break;
      case LineDoubleDashStyle:
        xgcv.line_style = LineDoubleDash;
        break;
      default:
        fatal("SetLineStyle: invalid linestyle");
          /*NOTREACHED*/
  }
  (void)XChangeGC(dpy,gc,GCLineStyle,&xgcv);
}


void ResetLineThicknessAndStyle(void){
  XGCValues xgcv;
  xgcv.line_width = 0;
  xgcv.line_style = LineSolid;
  (void)XChangeGC(dpy,gc,GCLineWidth|GCLineStyle,&xgcv);
}


/* -------------------------- Filling Styles ---------------------------- */


void SetFillStyle(FillingStyle style){
  if (style == SolidStyle){
    (void)XSetFillStyle(dpy,gc,FillSolid);
  } 
  else {
    (void)XSetFillStyle(dpy,gc,FillOpaqueStippled);
    (void)XSetStipple(dpy,gc,pattern[(int)style-1].pixmap);
  }
}


FillingStyle FirstStyle(void){
  return SolidStyle;
}


FillingStyle LastStyle(void){
  return (FillingStyle) (NUM_PATS-1);
}


/* -------------------------- Draw Operations ---------------------------- */


void DrawPoint(int x,int y){
  (void)XDrawPoint(dpy,currentwin,gc,x,y);
}


void DrawLine(int x1,int y1,int x2,int y2){
  (void)XDrawLine(dpy,currentwin,gc,x1,y1,x2,y2);
}


void DrawRectangle(int x1,int y1,int x2,int y2){
    /* draw the rectangle */
  if (x1 > x2)
    swap(x1,x2);
  if (y1 > y2)
    swap(y1,y2);
  (void)XDrawLine(dpy,currentwin,gc,x1,y1,x2,y1);
  (void)XDrawLine(dpy,currentwin,gc,x2,y1,x2,y2);
  (void)XDrawLine(dpy,currentwin,gc,x2,y2,x1,y2);
  (void)XDrawLine(dpy,currentwin,gc,x1,y2,x1,y1);
}


void FillRectangle(int x1,int y1,int x2,int y2){
  if (x1 > x2)
    swap(x1,x2);
  if (y1 > y2)
    swap(y1,y2);
  (void)XFillRectangle(dpy,currentwin,gc,x1,y1,1+x2-x1,1+y2-y1);
}


void FillPolygon(int *xcoords, int *ycoords, int n){
  XPoint vertex_array[FillPolygon_MaxNoOfVerticesInAPolygon];
  int i;

  if ((n < 0) || (n > FillPolygon_MaxNoOfVerticesInAPolygon)) {
    fatal_int("FillPolygon: max no. of vertices in a polygon =", n);
  }/*endif*/

  for (i=0; i<n; i++) {
    vertex_array[i].x = (short) xcoords[i];
    vertex_array[i].y = (short) ycoords[i];
  }/*endfor*/

  (void)XFillPolygon(dpy, currentwin, gc, vertex_array, n,
                     Complex, CoordModeOrigin);
}


void DrawChar(int x,int y,char ch){
  char msg[2];

  msg[0] = ch;
  msg[1] = '\0';
  (void)XDrawString(dpy,currentwin,gc,x,y,msg,1);
}


void DrawString(int x,int y,char *msg){
  (void)XDrawString(dpy,currentwin,gc,x,y,msg,strlen(msg));
}


void DrawArc(int x,int y,int radius,int sangle,int eangle){

  /* X11 uses 64's of a degree */
  (void)XDrawArc(dpy,currentwin,gc,x - radius,y - radius,2 * radius,
                  2 * radius,sangle,63 + (eangle<<6) - (sangle<<6));
}


void DrawCircle(int x,int y,int radius){
  (void)XDrawArc(dpy,currentwin,gc,x - radius,y - radius,2 * radius,
                  2 * radius,0,360 * 64);
}


void FillArc(int x,int y,int radius,int sangle,int eangle){

  /* X11 uses 64's of a degree */
  (void)XFillArc(dpy,currentwin,gc,x - radius,y - radius,2 * radius,
                  2 * radius,sangle,63 + (eangle<<6) - (sangle<<6));
}


void FillCircle(int x,int y,int radius){
  (void)XFillArc(dpy,currentwin,gc,x - radius,y - radius,
                  2 * radius,2 * radius,0,360 * 64);
}


/* --------------------------- Cursors -------------------------------- */


static void InitialiseCursors(void){
  /* create some cursors */
  user_cursor = XCreateFontCursor(dpy,XC_crosshair);
  busy_cursor = XCreateFontCursor(dpy,XC_watch);
}


static void FreeCursors(void){
  /* clear the cursor bitmaps */
  (void)XFreeCursor(dpy,user_cursor);
  (void)XFreeCursor(dpy,busy_cursor);
}


void OnCursor(void){
  (void)XDefineCursor(dpy,currentwin,user_cursor);
}


void BusyCursor(void){
  (void)XDefineCursor(dpy,currentwin,busy_cursor);
}


void OffCursor(void){
  (void)XUndefineCursor(dpy,currentwin);
}


/* --------------------------- Pixmaps -------------------------------- */


static void InitialisePixmaps(void){
  unsigned int i;
  /* create some pixmaps for later on */
  for (i = 0; i < NUM_PATS; i++) {
    pattern[i].pixmap = XCreatePixmapFromBitmapData(dpy,rootwin,
              pattern[i].data,pattern[i].width,pattern[i].height,0,1,1);
  }
}

/* create a pixmap compatable with the window */
Pixmap CreatePixmap(int width,int height) {
  return  XCreatePixmap(dpy,rootwin,width,height,planes);
}

/* copy from an area of a  pixmap to the window */
void CopyToWin(Pixmap p,int src_x,int src_y,int width,int height,
			   int dest_x,int dest_y){
  XCopyArea(dpy,p,currentwin,gc,src_x,src_y,width,height,dest_x,dest_y);
}

/* from an area of the current window to a pixmap */
void CopyFromWin(Pixmap p,int src_x,int src_y,int width,int height,
			   int dest_x,int dest_y){
  XCopyArea(dpy,currentwin,p,gc,src_x,src_y,width,height,dest_x,dest_y);
}

static void FreePixmaps(void){
  unsigned int i;

  for (i = 0; i < NUM_PATS; i++){
    (void)XFreePixmap(dpy,pattern[i].pixmap);
  }
}

XImage *GetImage(int x,int y,int width,int height,int format) {
  return  XGetImage(dpy,currentwin,x,y,width,height,32,format);
}

void PutImage(XImage * image,int src_x,int src_y,int dest_x,int dest_y,
               int width,int height){
  XPutImage(dpy,currentwin,gc,image,src_x,src_y,dest_x,dest_y,width,height);
}

XImage *CreateImage(unsigned int depth,int format,int offset,
                    char *data,unsigned int width,unsigned int height,
                    int bitmap_pad,int bytes_per_line){

  return XCreateImage(dpy,DefaultVisual(dpy,thescreen),depth,format,offset,
                      data,width,height,bitmap_pad,bytes_per_line);
}


/* Clipping List for efficient redraws on expose events ----------------- */


typedef struct{
  XRectangle *    data;
  int     size,used;
  bool        newseq;
} cliplist;


static cliplist clist;


/* Set the clip list system going.*/

static void InitialiseClipList(int maxsize){
  clist.size  = maxsize;
  clist.data  = (XRectangle *) malloc(clist.size *sizeof(XRectangle));
  clist.used  = 0;
  clist.newseq    = TRUE;
}


/*
 *  Add the  rectangle (r) to the clip list
 *  (starting a new clip list if necessary).
 *
 *  (Called for every underlying X Expose event)
 */

static void AddToClipList(int count,XRectangle r){
  if (clist.newseq) {
    if (count > clist.size){
      clist.size = count + 5;;
      clist.data = (XRectangle *) realloc((char *) clist.data,
                                          clist.size * sizeof(XRectangle));
    }
    clist.used   = 0;
    clist.newseq = FALSE;
  }
  clist.data[ clist.used++ ] = r;

  if (count == 0){
    clist.newseq = TRUE;
      /* For next time*/
  }
}


void SetClipList(void){
  (void)XSetClipRectangles(dpy,gc,0,0,clist.data,clist.used,Unsorted);
}


void ClearClipList(void){
  (void)XSetClipMask(dpy,gc,None);
}


/*
 *  Discard the clip list data.
 */

static void FreeClipList(void){
  free((char *) clist.data);
}


/* ------------------------- The Window List ---------------------- */


static void InitialiseWinList(int n){
  winsize.used = 0;
  winsize.size = n;
  winsize.data = (windowstruct *) malloc(winsize.size *sizeof(windowstruct));
}


static void AddWin(Window win,int w,int h){
  windowstruct *wp;

  if (winsize.used == winsize.size){
    winsize.size += 5;
    winsize.data = (windowstruct *) realloc((char *) winsize.data,
                                   winsize.size * sizeof(windowstruct));
  }
  wp        = &winsize.data[winsize.used];
  wp->win   = win;
  wp->w     = w;
  wp->h     = h;
  winsize.used++;
}


static Window DeleteWin(Window win){
  int i;

  for (i = FindWin(win,NULL,NULL)+1; i < winsize.size; i++) {
    winsize.data[i-1] = winsize.data[i];
  }
  winsize.used--;

  return (winsize.used == 0) ? rootwin : winsize.data[0].win;
}


static int FindWin(Window win,int *wp,int *hp){
  windowstruct    *ptr;
  windowstruct    *beyond;

  beyond = winsize.data + winsize.used;
  for (ptr = winsize.data; ptr < beyond; ptr++){
    if (win == ptr->win){
      if (wp != NULL) *wp = ptr->w;
      if (hp != NULL) *hp = ptr->h;
      return ptr - winsize.data;
    }
  }
  fatal("FindWin: window not found\n");
  return 0;
    /*NOTREACHED*/
}


static void SetWinSize(Window win,int w,int h){
  int pos = FindWin(win,NULL,NULL);

  winsize.data[pos].w = w;
  winsize.data[pos].h = h;
}


static void FreeWinList(void){
  free((char *) winsize.data);
}

/* -------------------------- Miscellaneous ------------------------------ */


void FlushOutput(void){
  (void)XFlush(dpy);
}


/*
 *  Announce a fatal error and die.
 */

static void fatal(char *desc){
  perror("Xgraf: Graphics Library for X11");
  (void) fprintf(stderr,"A fatal error has occurred\nFault is: %s\n",desc);
  abort();
  /*NOTREACHED*/
}

static void fatal_int(char *desc,int n){
  perror("Xgraf: Graphics Library for X11");
  (void) fprintf(stderr,"A fatal error has occurred\nFault is: %s %d\n",desc,n);
  abort();
  /*NOTREACHED*/
}
static void fatal_str(char *desc,char * s){
  perror("Xgraf: Graphics Library for X11");
  (void) fprintf(stderr,"A fatal error has occurred\nFault is: %s %s\n",desc,s);
  abort();
  /*NOTREACHED*/
}


