/*
** SRGP Lite - a subset of SRGP
** re-implemented by Allan Christian Long, Jr.
** Fall 1990
**
** revisited by Roddy And Dennis, Last Week In May, 1992
*/

# define SRGP_BOSS
# define SRGPLITE

# include <stdio.h>
# include <conio.h>
# include <stdlib.h>
# include <alloc.h>
# include <time.h>
# include <graphics.h>
# include "srgp.h"
# include "dynarray.h"
# include "mouse.h"

# undef rectangle

/*
** Globals:
*/
# define TRACE_FILE "SRGPlog"
# define REVERSEY(oldy) (current_attributes.clip_rectangle.top_right.y - \
                         current_attributes.clip_rectangle.bottom_left.y \
				   	     - (oldy))
# define MAPX(oldx) ((oldx)-current_attributes.clip_rectangle.bottom_left.x)
# define MAPY(oldy) ((oldy)-current_attributes.clip_rectangle.bottom_left.y)
/*
# define MAPX(x) (x)
# define MAPY(y) (y)
*/

FILE *SRGP_logStream;
char *prog_name;
int screen_height, screen_width, linestyle, linethickness;
srgp__attribute_group current_attributes;
typedef unsigned char eightchar[8];
eightchar *srgp__bitmapPatternTable;
boolean srgp__traceDisabled = TRUE;
int input_mode[2]; /* contains INACTIVE, SAMPLE, or EVENT; indexed by
              KEYBOARD, LOCATOR */
int last_button_chord[3] = {0, 0, 0};
int graphics_mode, graphics_driver = DETECT;
char graphics_driver_path[256] = "";
int SRGP_errorOccurred;
int user_screen_width, user_screen_height;


/*
** Private routines:
*/

int debug_indent_level = -1;

/* debugging routines: */
void enter (char *s)
{
  int i;

  if (!srgp__traceDisabled) {
    debug_indent_level++;
    for (i = 0; i < debug_indent_level; i++)
      fputs("  ",SRGP_logStream);
    fprintf(SRGP_logStream,"Entering %s\n",s);
  }
}

void leave (char *s)
{
  int i;

  if (!srgp__traceDisabled) {
    for (i = 1; i < debug_indent_level; i++)
      fputs("  ",SRGP_logStream);
    fprintf(SRGP_logStream,"Leaving %s\n",s);
    debug_indent_level--;
  }
}

/* event queue routines: */

typedef struct {
  int device; /* LOCATOR, KEYBOARD */
  union {
    srgp__deluxe_keyboard_measure keyboard;
    srgp__deluxe_locator_measure  locator;
  } u;
} event_type;

DynArray event_queue;
event_type *current_event = NULL;
int numevents = 0;

void create_queue (void)
{
  static int size = sizeof (event_type);

  event_queue = DynCreate (size, size * 5);
  numevents = 0;
}

void add_event (event_type event)
{
  event_type *e;

  DynAdd(event_queue, &event);
  numevents++;
}

event_type *get_event (void)
{
  event_type *temp, *result;

  temp = DynGet (event_queue, 0);
  if (temp == NULL)
    printf("ERROR: temp = NULL\n");

  result = (event_type *) malloc (sizeof(event_type));
  if (result == NULL)
    printf("ERROR: result = NULL\n");

  *result = *temp;

  DynDelete (event_queue, 0);
  numevents--;

  return result;
}



# define KEYINTR 0X09 /* keyboard interrupt */

/* keyboard interrupt routines: */

void interrupt (*oldkeyboard_handler)(void);

void install_keyboard_interrupt (void interrupt (*keyboard_handler)(void))
/*
   Install the passed keyboard handler and store the old one as
   oldkeyboard_handler.
*/
{
  oldkeyboard_handler = getvect (KEYINTR);
  setvect (KEYINTR, keyboard_handler);
}

void restore_keyboard_interrupt (void)
/*
   Restore the original keyboard interrupt handler.
*/
{
  setvect (KEYINTR, oldkeyboard_handler);
}

/*
  This function converts from a scan code to an ASCII character
*/
char QwertyToASCII (int code)
{
   switch (code) {
      case 16: return 'q';  case 17: return 'w';  case 18: return 'e';
      case 19: return 'r';  case 20: return 't';  case 21: return 'y';
      case 22: return 'u';  case 23: return 'i';  case 24: return 'o';
      case 25: return 'p';  case 30: return 'a';  case 31: return 's';
      case 32: return 'd';  case 33: return 'f';  case 34: return 'g';
      case 35: return 'h';  case 36: return 'j';  case 37: return 'k';
      case 38: return 'l';  case 44: return 'z';  case 45: return 'x';
      case 46: return 'c';  case 47: return 'v';  case 48: return 'b';
      case 49: return 'n';  case 50: return 'm';
    }
    switch (code) {
      case 120: return '1'; case 121: return '2'; case 122: return '3';
      case 123: return '4'; case 124: return '5'; case 125: return '6';
      case 126: return '7'; case 127: return '8'; case 128: return '9';
      case 129: return '0'; case 130: return '-'; case 131: return '=';
      default: return '?';
   }
}

/*
  This function modifies a character by the shift key.
*/
char ShiftPlusChar (char c)
{
     if (c >= 'a' && c <= 'z')
          return 'A'+c-'a';
    switch (c) {
        case '1': return '!';  case '2': return '@';  case '3': return '#';
        case '4': return '$';  case '5': return '%';  case '6': return '^';
        case '7': return '&';  case '8': return '*';  case '9': return '(';
        case '0': return ')';  case '-': return '_';  case '=': return '+';
        case '[': return '{';  case ']': return '}';  case ';': return ':';
        case '\'': return '"'; case '`': return '~';  case '\\': return '|';
        case ',': return '<';  case '.': return '>';  case '/': return '?';
    }
    /* will never get past here */
    return 0;  /* to please the compiler */
}

/*
   This routine gets the current character with bioskey (0) and the
   modifiers with bioskey (2).  The current character is returned as
   the function value, and the modifiers are returned in the array.
*/
char getKey (buttonStatus modifier_chord[3])
{
  int modifiers, c;

  c = bioskey (0);
  modifiers = bioskey (2);
  modifier_chord[SHIFT]   = (modifiers & 0x03) != 0;
  modifier_chord[CONTROL] = (modifiers & 0x04) != 0;
  modifier_chord[META]    = (modifiers & 0x08) != 0;
  if (modifier_chord[META] && ((c & 0xFF) == 0)) {
    c = QwertyToASCII (c >> 8);
    if (modifier_chord[SHIFT])
      c = ShiftPlusChar (c);
  }
  return c;
}


void interrupt our_keyboard_handler (void)
/*
   This routine intercepts all keyboard interrupts, and puts keyboard
   events into the event queue.
*/
{
  static double tick_factor = 60 / CLK_TCK;
  clock_t clk;
  event_type event;
  int modifiers;
  locator_measure mouse;

  if ((input_mode[KEYBOARD] == EVENT) && (bioskey (1))) {
    clk = clock ();
    event.device = KEYBOARD;
    event.u.keyboard.timestamp.seconds = (int) (clk / CLK_TCK);
    event.u.keyboard.timestamp.ticks = (int)
      ((clk - event.u.keyboard.timestamp.seconds * CLK_TCK) * tick_factor);
    event.u.keyboard.buffer = malloc (sizeof(char) * 2);
    event.u.keyboard.buffer[0] = getKey (event.u.keyboard.modifier_chord);
    event.u.keyboard.buffer[1] = '\0';
    SRGP_sampleLocator (&mouse);
    event.u.keyboard.position = mouse.position;
    add_event (event);
  }
  oldkeyboard_handler ();
}

/* locator interrupt routines: */

void restore_locator_interrupt (void)
{
  mouse_install ();
}


void our_locator_handler (void)
{
  static double tick_factor = 60 / CLK_TCK;
  clock_t clk;
  event_type event;
  int modifiers;

  if (input_mode[LOCATOR] == EVENT) {
    clk = clock ();
    modifiers = bioskey (2);
    event.u.locator.timestamp.seconds = (int) (clk / CLK_TCK);
    event.u.locator.timestamp.ticks = (int)
      ((clk - event.u.locator.timestamp.seconds * CLK_TCK) * tick_factor);
    event.device = LOCATOR;
    event.u.locator.modifier_chord[SHIFT]   = (modifiers & 0x03) != 0;
    event.u.locator.modifier_chord[CONTROL] = (modifiers & 0x04) != 0;
    event.u.locator.modifier_chord[META]    = (modifiers & 0x08) != 0;

    mouse_pos_button (&(event.u.locator.position.x),
              &(event.u.locator.position.y),
	      (int *) &(event.u.locator.button_chord[LEFT_BUTTON]),
	      (int *) &(event.u.locator.button_chord[RIGHT_BUTTON]));
    event.u.locator.position.y = REVERSEY(event.u.locator.position.y);
    event.u.locator.button_chord[MIDDLE_BUTTON] = 0;
    event.u.locator.button_of_last_transition =
      (last_button_chord[LEFT_BUTTON] ^
       event.u.locator.button_chord[LEFT_BUTTON])
      ? (LEFT_BUTTON) : (RIGHT_BUTTON);
    last_button_chord[LEFT_BUTTON] = event.u.locator.button_chord[LEFT_BUTTON];
    last_button_chord[MIDDLE_BUTTON] = event.u.locator.button_chord[MIDDLE_BUTTON];
    last_button_chord[RIGHT_BUTTON]= event.u.locator.button_chord[RIGHT_BUTTON];
    add_event (event);
  }
}

/*
** Definitions of public routines (by category):
*/

/* States of the System: */

void SRGP_begin (char *name, int width, int height, int planes, boolean please_trace)
{
  int errorcode;
  srgp__rectangle initCR;  /* the initial clipping rectangle */

  /* initialize graphics and local variables  */
  initgraph (&graphics_driver, &graphics_mode, graphics_driver_path);

  /* read result of initialization */
  errorcode = graphresult ();
  if (errorcode != grOk)  /* an error occurred */
    fprintf (stderr,"Graphics error: %s\n", grapherrormsg (errorcode));

  /* set up misc variables */
  prog_name = name;
  screen_width = getmaxx ();
  screen_height = getmaxy ();

  /* for returning the real screen size -- Matt Conway 20 Aug 1992 */
  user_screen_width = width;
  user_screen_height = height;
  

  /* initialize the graphics state */
  SRGP_WHITE = 1; /* Reversing these causes the 'invisible mouse' effect. */
  SRGP_BLACK = 0;
  SRGP_loadCommonColor(SRGP_WHITE,"white");
  SRGP_loadCommonColor(SRGP_BLACK,"black");
  SRGP_setBackgroundColor(SRGP_BLACK);
  SRGP_setColor(SRGP_WHITE);
  SRGP_setFillStyle(SOLID);
  SRGP_setLineStyle(SOLID);
  SRGP_setLineWidth(1);
  SRGP_setPenStyle(SOLID);
  SRGP_setWriteMode(WRITE_REPLACE);
  initCR.bottom_left.x = 0;
  initCR.bottom_left.y = 0;
  initCR.top_right.x = screen_width;
  initCR.top_right.y = screen_height;
  SRGP_setClipRectangle(initCR);

  /* SRGP__initDefaultPatterns(); */

  /* install input drivers */
  install_keyboard_interrupt (our_keyboard_handler);
  mouse_resident (14, our_locator_handler);

  /* display the mouse cursor */
  mouse_show ();

  /* set the mouse screen size to the actual screen size */
  mouse_set_horizontal_limits (0, screen_width);
  mouse_set_vertical_limits (0, screen_height);
  mouse_set_screen_size (graphics_driver, screen_width, screen_height);

  /* initialize input device modes */
  input_mode[KEYBOARD] = INACTIVE;
  input_mode[LOCATOR] = INACTIVE;
  create_queue (); /* initialize the event queue */

  SRGP_tracing (please_trace);
}

void SRGP_tracing (boolean enable_trace)
{
  if (enable_trace && srgp__traceDisabled) {
    if ((SRGP_logStream = fopen (TRACE_FILE,"w")) == NULL)
      fprintf(stderr,"error: Can't open file SRGPlog\n");
  }
  else if (!enable_trace && srgp__traceDisabled)
    fclose (SRGP_logStream);
  srgp__traceDisabled = !enable_trace;
}


void SRGP_allowResize (boolean dummy)
{
  enter ("SRGP_allowResize");
  leave ("SRGP_allowResize");
}

static int (*srgp__resizeCallback) (int, int);

void SRGP_registerResizeCallback (int callback (int, int))
{
  enter ("SRGP_registerResizeCallback");
  srgp__resizeCallback = callback;
  leave ("SRGP_registerResizeCallback");
}


void SRGP_end (void)
{
  SRGP_tracing (FALSE);
  restore_keyboard_interrupt ();
  restore_locator_interrupt ();
  closegraph ();
  clrscr ();
}

/* Canvases */

int SRGP_createCanvas (int width, int height)
{
  enter ("SRGP_createCanvas");
  leave ("SRGP_createCanvas");
  return 0;
}

void SRGP_deleteCanvas (int canvasID)
{
  enter ("SRGP_deleteCanvas");
  leave ("SRGP_deleteCanvas");
}

void SRGP_useCanvas (int canvasID)
{
  enter ("SRGP_useCanvas");
  leave ("SRGP_useCanvas");
}


/* Output: */

/* geometric data types */


srgp__point SRGP_defPoint (int x, int y)
{
  srgp__point point;

  enter ("SRGP_defPoint");
  point.x = x;
  point.y = y;

  leave ("SRGP_defPoint");
  return point;
}

srgp__rectangle SRGP_defRectangle (int left_x, int lower_y, int right_x, int upper_y)
{
  srgp__rectangle rect;

  enter ("SRGP_defRectangle");

  rect.top_right = SRGP_defPoint (right_x, upper_y);
  rect.bottom_left = SRGP_defPoint (left_x, lower_y);

  leave ("SRGP_defRectangle");
  return rect;
}


/* control of the pattern and font tables */

/*
int  SRGP_loadBitmapPatternsFromFile (FILE *stream)
{
  enter ("SRGP_loadBitmapPatternsFromFile");
  leave ("SRGP_loadBitmapPatternsFromFile");
  return 1;
}
*/

int  SRGP_loadPixmapPatternsFromFile (FILE *stream)
{
  enter ("SRGP_loadPixmapPatternsFromFile");
  leave ("SRGP_loadPixmapPatternsFromFile");
  return 1;
}

/*
void SRGP_loadBitmapPattern (int index, char *data)
{
  enter ("SRGP_loadBitmapPattern");
  leave ("SRGP_loadBitmapPattern");
}
*/

void SRGP_loadPixmapPattern (int index, int *data)
{
  enter ("SRGP_loadPixmapPattern");
  leave ("SRGP_loadPixmapPattern");
}

void SRGP_loadFont (int font_index, char *filename)
{
  enter ("SRGP_loadFont");
  leave ("SRGP_loadFont");
}


/* control of attributes affecting output */


void SRGP_setWriteMode (writeMode mode)
{
  enter ("SRGP_setWriteMode");
  current_attributes.write_mode = mode;
  switch (mode) {
    case WRITE_XOR : setwritemode (1);
		     break;
    default        : setwritemode(0);
  }
  leave ("SRGP_setWriteMode");
}

void SRGP_setClipRectangle (srgp__rectangle value)
{
  enter ("SRGP_setClipRectangle");
  if (value.top_right.y > screen_height)
    value.top_right.y = screen_height;
  if (value.bottom_left.y < 0)
    value.bottom_left.y = 0;
  if (value.top_right.x > screen_width)
    value.top_right.x = screen_width;
  if (value.bottom_left.x < 0)
    value.bottom_left.x = 0;
  current_attributes.clip_rectangle = value;
  setviewport(value.bottom_left.x,screen_height - (value.top_right.y),
  		      value.top_right.x,screen_height - (value.bottom_left.y),1);
/*  if (graphresult() == -11) {
    fprintf(stderr,"Error: Invalid Clipping Coordinates: (%d,%d),(%d,%d) %d\n",
	    value.bottom_left.x,value.bottom_left.y,
		    value.top_right.x,value.top_right.y,screen_height);
    fprintf (stderr, "I really tried (%d %d %d %d) [%d %d]\n",
	value.bottom_left.x,screen_height - (value.top_right.y),
		      value.top_right.x,screen_height - (value.bottom_left.y),
		      getmaxx(),getmaxy());
   }*/
  leave ("SRGP_setClipRectangle");
}

void SRGP_setFont (int font_index)
{
  enter ("SRGP_setFont");
  leave ("SRGP_setFont");
}

void SRGP_setMarkerSize (int size)
{
  enter ("SRGP_setMarkerSize");
  leave ("SRGP_setMarkerSize");
}

void SRGP_setMarkerStyle (markerStyle style)
{
  enter ("SRGP_setMarkerStyle");
  leave ("SRGP_setMarkerStyle");
}

void SRGP_setLineStyle (lineStyle linestyletype)
{
  int style;

  enter ("SRGP_setLineStyle");
  switch (linestyletype) {
    case CONTINUOUS : style = SOLID_LINE;
		      break;
    case DOTTED     : style = DOTTED_LINE;
		      break;
    case DOT_DASHED : style = CENTER_LINE;
		      break;
    case DASHED     : style = DASHED_LINE;
		      break;
  }
  current_attributes.line_style = style; /* set global variable */
  setlinestyle (style,0,current_attributes.line_width);
  leave ("SRGP_setLineStyle");
}

void SRGP_setLineWidth (int width_in_pixels)
{
  enter ("SRGP_setLineWidth");
  /* Turbo C++ only supports widths 1 and 3 */
  if ((width_in_pixels == 1) || (width_in_pixels == 3)) {
    current_attributes.line_width = width_in_pixels; /* set global variable */
    setlinestyle (current_attributes.line_style,0,
                  current_attributes.line_width);
  }
  leave ("SRGP_setLineWidth");
}

# define CLAMPCOLOR if (pixelvalue >= 1) pixelvalue = WHITE

void SRGP_setColor (int pixelvalue)
{
  enter ("SRGP_setColor");
  /*CLAMPCOLOR;*/
  setcolor (pixelvalue);
  if (current_attributes.fill_style == SOLID)
    setfillstyle (SOLID_FILL,pixelvalue);
  else
    setfillstyle (current_attributes.fill_bitmap_pattern_id,pixelvalue);
  current_attributes.color = pixelvalue;
  leave ("SRGP_setColor");
}

void SRGP_setBackgroundColor (int pixelvalue)
{
  enter ("SRGP_setBackgroundColor");
  /*CLAMPCOLOR;*/
  current_attributes.background_color = pixelvalue;
  if (current_attributes.fill_style == SOLID)
    setfillstyle (SOLID_FILL,pixelvalue);
  else
    setfillstyle (current_attributes.fill_bitmap_pattern_id,pixelvalue);
  leave ("SRGP_setBackgroundColor");
}

void SRGP_setPlaneMask (int bitmask)
{
  enter ("SRGP_setPlaneMask");
  leave ("SRGP_setPlaneMask");
}

void SRGP_setFillStyle (drawStyle value /* SOLID, BITMAP_PATTERN_OPAQUE, BITMAP_PATTERN_TRANSPARENT, PIXMAP_PATTERN */)
{
  enter ("SRGP_setFillStyle");
  current_attributes.fill_style = value;
  switch (value) {
    case SOLID                      :
	     setfillstyle(SOLID_FILL,current_attributes.color);
	     break;
    case BITMAP_PATTERN_OPAQUE      :
    case BITMAP_PATTERN_TRANSPARENT :
	     setfillstyle(current_attributes.fill_bitmap_pattern_id,
		              current_attributes.color);
		 break;
    case PIXMAP_PATTERN             : break;
  }
  leave ("SRGP_setFillStyle");
}

void SRGP_setPenStyle (drawStyle value /* SOLID, BITMAP_PATTERN_OPAQUE, BITMAP_PATTERN_TRANSPARENT, PIXMAP_PATTERN */)
{
  enter ("SRGP_setPenStyle");
  leave ("SRGP_setPenStyle");
}

void SRGP_setFillBitmapPattern (int index)
{
  enter ("SRGP_setFillBitmapPattern");
  current_attributes.fill_bitmap_pattern_id = index;
  setfillpattern(srgp__bitmapPatternTable[index], current_attributes.color);
  leave ("SRGP_setFillBitmapPattern");
}

void SRGP_setFillPixmapPattern (int index)
{
  enter ("SRGP_setFillPixmapPattern");
  leave ("SRGP_setFillPixmapPattern");
}

void SRGP_setPenBitmapPattern (int index)
{
  enter ("SRGP_setPenBitmapPattern");
  leave ("SRGP_setPenBitmapPattern");
}

void SRGP_setPenPixmapPattern (int index)
{
  enter ("SRGP_setPenPixmapPattern");
  leave ("SRGP_setPenPixmapPattern");
}

void printAttributes (srgp__attribute_group *group)
{
  printf("color=%d\n",current_attributes.color);
  printf("background color=%d\n",current_attributes.background_color);
  printf("line style=%d\n",current_attributes.line_style);
  printf("line width=%d\n",current_attributes.line_width);
  printf("write mode=%d\n",current_attributes.write_mode);
}

void SRGP_setAttributes (srgp__attribute_group *group)
{
  enter ("SRGP_setAttributes");
  current_attributes = *group;
  SRGP_setColor (current_attributes.color);
  SRGP_setBackgroundColor (current_attributes.background_color);
  SRGP_setLineStyle (current_attributes.line_style);
  SRGP_setLineWidth (current_attributes.line_width);
  SRGP_setWriteMode (current_attributes.write_mode);
  SRGP_setClipRectangle (current_attributes.clip_rectangle);
  leave ("SRGP_setAttributes");
}

void SRGP_setVideoMode (int mode)
{
  graphics_mode = mode;
}

void SRGP_setVideoDriver (int driver, char *driver_path)
{
  graphics_driver = driver;
  strcpy(graphics_driver_path,driver_path);
}


/* generation of primitives */

void SRGP_refresh (void)
{
  enter ("SRGP_refresh");
  leave ("SRGP_refresh");
}

void SRGP_point (srgp__point pt)
{
  enter ("SRGP_point");
  SRGP_pointCoord(pt.x, pt.y);
  leave ("SRGP_point");
}

void SRGP_pointCoord (int x, int y)
{
  enter ("SRGP_point");
  mouse_hide ();
  putpixel (MAPX(x), REVERSEY(MAPY(y)), current_attributes.color);
  mouse_show ();
  leave ("SRGP_point");
}

void SRGP_line (srgp__point pt1, srgp__point pt2)
{
  enter ("SRGP_line");
  SRGP_lineCoord (pt1.x, pt1.y, pt2.x, pt2.y);
  leave ("SRGP_line");
}

void SRGP_lineCoord (int x1, int y1, int x2, int y2)
{
  enter ("SRGP_line");
  mouse_hide ();
  line (MAPX(x1), REVERSEY(MAPY(y1)), MAPX(x2), REVERSEY(MAPY(y2)));
  mouse_show ();
  leave ("SRGP_line");
}

void SRGP_rectangle (srgp__rectangle rect)
{
  enter ("SRGP_rectangle");
  SRGP_rectanglePt(rect.bottom_left, rect.top_right);
  leave ("SRGP_rectangle");
}

void SRGP_rectanglePt (srgp__point lower_left, srgp__point upper_right)
{
  enter ("SRGP_rectanglePt");
  SRGP_rectangleCoord(lower_left.x, upper_right.y, upper_right.x, lower_left.y);
  leave ("SRGP_rectanglePt");
}

void SRGP_rectangleCoord (int left_x, int lower_y, int right_x, int upper_y)
{
  enter ("SRGP_rectangleCoord");
  mouse_hide ();
  rectangle (MAPX(left_x), REVERSEY(MAPY(upper_y)),
	     MAPX(right_x), REVERSEY(MAPY(lower_y)));
  mouse_show ();
  leave ("SRGP_rectangleCoord");
}

void SRGP_polyPoint (int vert_count, srgp__point *vertices)
{
  int i;

  enter ("SRGP_polyPoint");
  for (i = 0; i < vert_count; i++)
    SRGP_point(vertices[i]);
  leave ("SRGP_polyPoint");
}

void SRGP_polyMarker (int vert_count, srgp__point *vertices)
{
  enter ("SRGP_polyMarker");
  leave ("SRGP_polyMarker");
}

void SRGP_polyLine (int vert_count, srgp__point *vertices)
{
  int i;

  enter ("SRGP_polyLine");
  moveto (MAPX(vertices[0].x), REVERSEY(MAPY(vertices[0].y)));
  mouse_hide ();
  for (i = 1; i < vert_count; i++)
    lineto (MAPX(vertices[i].x), REVERSEY(MAPY(vertices[i].y)));
  mouse_show ();
  leave ("SRGP_polyLine");
}

void SRGP_polygon (int vert_count, srgp__point *vertices)
{
  int i;

  enter ("SRGP_polygon");
  moveto (MAPX(vertices[0].x), REVERSEY(MAPY(vertices[0].y)));
  mouse_hide ();
  for (i = 1; i < vert_count; i++)
    lineto (MAPX(vertices[i].x), REVERSEY(MAPY(vertices[i].y)));
  lineto (MAPX(vertices[0].x), REVERSEY(MAPY(vertices[0].y)));
  mouse_show ();
  leave ("SRGP_polygon");
}

void SRGP_polyPointCoord (int vert_count, int *x_coords, int *y_coords)
{
  int i, color;

  enter ("SRGP_polyPointCoord");
  color = getcolor ();
  mouse_hide ();
  for (i = 0; i < vert_count; i++)
    putpixel (MAPX(x_coords[i]), REVERSEY(MAPY(y_coords[i])), color);
  mouse_show ();
  leave ("SRGP_polyPointCoord");
}

void SRGP_polyMarkerCoord (int vert_count, int *x_coords, int *y_coords)
{
  enter ("SRGP_polyMarkerCoord");
  leave ("SRGP_polyMarkerCoord");
}

void SRGP_polyLineCoord (int vert_count, int *x_coords, int *y_coords)
{
  int i;

  enter ("SRGP_polyLineCoord");
  moveto (MAPX(x_coords[0]), REVERSEY(MAPY(y_coords[0])));
  mouse_hide ();
  for (i = 1; i < vert_count; i++)
    lineto (MAPX(x_coords[i]), REVERSEY(MAPY(y_coords[i])));
  mouse_show ();
  leave ("SRGP_polyLineCoord");
}

void SRGP_polygonCoord (int vert_count, int *x_coords, int *y_coords)
{
  int i;

  enter ("SRGP_polygonCoord");
  moveto (MAPX(x_coords[0]), REVERSEY(MAPY(y_coords[0])));
  mouse_hide ();
  for (i = 1; i < vert_count; i++)
    lineto (MAPX(x_coords[i]), REVERSEY(MAPY(y_coords[i])));
  lineto (MAPX(x_coords[0]), REVERSEY(MAPY(y_coords[0])));
  mouse_show ();
  leave ("SRGP_polygonCoord");
}

# define AVERAGE(a,b) (b + (a-b)/2)

void SRGP_ellipse (srgp__rectangle rect)
{
  register int center_x, center_y;

  enter ("SRGP_ellipse");
  center_x = (int) AVERAGE(rect.top_right.x,rect.bottom_left.x);
  center_y = (int) AVERAGE(rect.top_right.y,rect.bottom_left.y);
  mouse_hide ();
  ellipse(MAPX(center_x), REVERSEY(MAPY(center_y)), 0, 360,
      abs(center_x - rect.top_right.x),
      abs(center_y - rect.top_right.y));
  mouse_show ();
  leave ("SRGP_ellipse");
}

void SRGP_ellipseArc (srgp__rectangle rect, double startangle, double endangle)
{
  register int center_x, center_y;

  enter ("SRGP_ellipse");
  center_x = (int) AVERAGE(rect.top_right.x,rect.bottom_left.x);
  center_y = (int) AVERAGE(rect.top_right.y,rect.bottom_left.y);
  mouse_hide ();
  ellipse(MAPX(center_x), REVERSEY(MAPY(center_y)), startangle, endangle,
          abs(center_x - rect.top_right.x),
	      abs(center_y - rect.top_right.y));
  mouse_show ();
  leave ("SRGP_ellipse");
}

void SRGP_fillPolygon (int vert_count, srgp__point *vertices)
{
  int i;
  srgp__point *v;

  enter ("SRGP_fillPolygon");
  v = (srgp__point *) malloc (sizeof(srgp__point) * vert_count);
  for (i = 0; i < vert_count; i++) {
    v[i].x = MAPX(vertices[i].x);
    v[i].y = REVERSEY(MAPY(vertices[i].y));
  }
  mouse_hide ();
  fillpoly(vert_count, (int far *) v);
/* roddy & Dennis hack-o-rama */
  free(v);
  mouse_show ();
  leave ("SRGP_fillPolygon");
}

void SRGP_fillPolygonCoord (int vert_count, int *x_coords, int *y_coords)
{
  int i;

  srgp__point *v;
  enter ("SRGP_fillPolygonCoord");
  v = (srgp__point *) malloc (sizeof(srgp__point) * vert_count);
  for (i = 0; i < vert_count; i++) {
    v[i].x = MAPX(x_coords[i]);
    v[i].y = REVERSEY(MAPY(y_coords[i]));
  }
  mouse_hide ();
  fillpoly(vert_count, (int far *) v);
/* roddy & dennis hack */
  free(v);
  mouse_show ();
  leave ("SRGP_fillPolygonCoord");
}

void SRGP_fillEllipse (srgp__rectangle rect)
{
  register int center_x, center_y;

  enter ("SRGP_fillEllipse");
  center_x = (int) AVERAGE(rect.top_right.x,rect.bottom_left.x);
  center_y = (int) AVERAGE(rect.top_right.y,rect.bottom_left.y);
  mouse_hide ();
  fillellipse(MAPX(center_x), REVERSEY(MAPY(center_y)),
              abs(center_x - rect.top_right.x),
              abs(center_y - rect.top_right.y));
  mouse_show ();
  leave ("SRGP_fillEllipse");
}

void SRGP_fillEllipseArc (srgp__rectangle rect, double startangle, double endangle)
{
  register int center_x, center_y;

  enter ("SRGP_fillEllipseArc");
  center_x = (int) AVERAGE(rect.top_right.x,rect.bottom_left.x);
  center_y = (int) AVERAGE(rect.top_right.y,rect.bottom_left.y);
  mouse_hide ();
  sector(MAPX(center_x), REVERSEY(MAPY(center_y)), startangle, endangle,
     abs(center_x - rect.top_right.x), abs(center_y - rect.top_right.y));
  mouse_show ();
  leave ("SRGP_fillEllipseArc");
}

void SRGP_fillRectangle (srgp__rectangle rect)
{
  enter ("SRGP_fillRectangle");
  SRGP_fillRectanglePt (rect.bottom_left, rect.top_right);
  leave ("SRGP_fillRectangle");
}

void SRGP_fillRectanglePt (srgp__point lower_left, srgp__point upper_right)
{
  enter ("SRGP_fillRectanglePt");
  SRGP_fillRectangleCoord (lower_left.x, upper_right.y,
                           upper_right.x, lower_left.y);
  leave ("SRGP_fillRectanglePt");
}

/* Roddy And Dennis hack: bar() doesn't draw in xor 
void SRGP_fillRectangleCoord (int left_x, int lower_y, int right_x, int upper_y)
{
  enter ("SRGP_fillRectangleCoord");
  mouse_hide ();
  bar (MAPX(left_x), REVERSEY(MAPY(upper_y)),
       MAPX(right_x), REVERSEY(MAPY(lower_y)));
  mouse_show ();
  leave ("SRGP_fillRectangleCoord");
}
*/

void RODDY_AND_DENNIS_paintVenetianBlinds (int left_x, int lower_y, int right_x, int upper_y, int blindSize) {

	int j, j0, j1;
	int numBlinds;

	numBlinds = ((REVERSEY(upper_y) - REVERSEY(lower_y))/blindSize) + 1;
	for (j0=0; j0<blindSize; j0++) {
		for (j1=0; j1<numBlinds; j1++) {
			j = REVERSEY(lower_y) + REVERSEY((j1*blindSize)+j0);
			if (j<=REVERSEY(upper_y)) {
				line(MAPX(left_x),  REVERSEY(MAPY(j)), 
				     MAPX(right_x), REVERSEY(MAPY(j)));
			}
	   	}
	}
}


void SRGP_fillRectangleCoord (int left_x, int lower_y, int right_x, int upper_y)
{
	int i;

	enter ("SRGP_fillRectangleCoord");
	mouse_hide ();
	SRGP_setLineStyle (CONTINUOUS);
	if ((left_x == 0) && (upper_y == 0)) {
		RODDY_AND_DENNIS_paintVenetianBlinds (left_x, lower_y, right_x, upper_y, 32);
	}
	else if (current_attributes.write_mode == WRITE_XOR) {
		for (i=left_x; i<= right_x; i++) {
			line (MAPX(i), REVERSEY(MAPY(lower_y)), 
			      MAPX(i), REVERSEY(MAPY(upper_y)));
		}
	}
	else {
		bar (MAPX(left_x), REVERSEY(MAPY(upper_y)),
		     MAPX(right_x), REVERSEY(MAPY(lower_y)));

	}
  	mouse_show ();
  	leave ("SRGP_fillRectangleCoord");
}

void SRGP_text (srgp__point origin, char *str)
{
  enter ("SRGP_text");
  mouse_hide ();
  outtextxy(MAPX(origin.x),
            REVERSEY(MAPY(origin.y)) - textheight((char far *) str),
			(char far *) str);
  mouse_show ();
  leave ("SRGP_text");
}


/* audio output */


void SRGP_beep (void)
{
  enter ("SRGP_beep");
  sound(440);
  delay(500);
  nosound();
  leave ("SRGP_beep");
}


/* The copyPixel Procedure */


void SRGP_copyPixel (int source_canvas_index, srgp__rectangle source_rect, srgp__point dest_corner) {

	enter ("SRGP_copyPixel");
	/* roddy and dennis hack */
/*
  	if (source_canvas_index == 0) {

		int src_left	= source_rect.bottom_left.x;
		int src_bottom	= source_rect.bottom_left.y;
		int src_right	= source_rect.top_right.x;
		int src_top	= source_rect.top_right.y;

		int width	= src_right - src_left;
		int height 	= src_top - src_bottom;

		int dest_left	= dest_corner.x;
		int dest_bottom	= dest_corner.y;
		int dest_right	= dest_left + width;
		int dest_top	= dest_bottom + height;

		int canvas_size;
		void *temp_canvas;


#ifdef NOT_DEFINED
printf ("src \t= (%d, %d), (%d, %d)\n", src_left, src_bottom, src_right, src_top);
printf ("dest\t= (%d, %d), (%d, %d)\n", dest_left, dest_bottom, dest_right, dest_top);
#endif

		canvas_size = imagesize (src_left, REVERSEY(src_top), src_right, REVERSEY(src_bottom));
		if (canvas_size == -1) {
			fprintf (stderr, "error in SRGP_copyPixel... imagesize");
		}
		else {
			temp_canvas = malloc (canvas_size);
			if (temp_canvas == NULL) {
				fprintf (stderr, "error in SRGP_copyPixel... canvas malloc");
			}
			else {
				getimage (src_left, REVERSEY(src_top), src_right, REVERSEY(src_bottom), temp_canvas);
				putimage (dest_left, REVERSEY(dest_top), temp_canvas, COPY_PUT);
				free (temp_canvas);
			}
		}
  	}
*/
	leave ("SRGP_copyPixel");
}


/* Input */

/* input modes */

void SRGP_setInputMode (inputDevice device /* LOCATOR, KEYBOARD */, inputMode mode /* INACTIVE, SAMPLE, EVENT */)
{
  enter ("SRGP_setInputMode");
  input_mode[device] = mode;
  leave ("SRGP_setInputMode");
}

/* control of attributes */

void SRGP_setLocatorButtonMask (int mask)
{
  enter ("SRGP_setLocatorButtonMask");
  leave ("SRGP_setLocatorButtonMask");
}

void SRGP_setLocatorEchoType (echoType value /* NO_ECHO, CURSOR, RUBBER_LINE, RUBBER_RECT */)
{
  enter ("SRGP_setLocatorEchoType");
  if (value == NO_ECHO) {
    mouse_hide();
  }
  else if (value == CURSOR) {
    mouse_show();
  }
  leave ("SRGP_setLocatorEchoType");
}

void SRGP_loadCursor (int index, int cursor)
{
  enter ("SRGP_loadCursorTable");
  leave ("SRGP_loadCursorTable");
}

void SRGP_setLocatorEchoCursorShape (int cursor_index)
{
  enter ("SRGP_setLocatorEchoCursorShape");
  leave ("SRGP_setLocatorEchoCursorShape");
}

void SRGP_setLocatorEchoRubberAnchor (srgp__point position)
{
  enter ("SRGP_setLocatorEchoRubberAnchor");
  leave ("SRGP_setLocatorEchoRubberAnchor");
}

void SRGP_setKeyboardProcessingMode (keyboardMode value /* EDIT, RAW */)
{
  enter ("SRGP_setKeyboardProcessingMode");
  leave ("SRGP_setKeyboardProcessingMode");
}

void SRGP_setKeyboardEchoColor (int color_index)
{
  enter ("SRGP_setKeyboardEchoColor");
  leave ("SRGP_setKeyboardEchoColor");
}

void SRGP_setKeyboardEchoFont (int font_index)
{
  enter ("SRGP_setKeyboardEchoFont");
  leave ("SRGP_setKeyboardEchoFont");
}

void SRGP_setKeyboardEchoOrigin (srgp__point position)
{
  enter ("SRGP_setKeyboardEchoOrigin");
  leave ("SRGP_setKeyboardEchoOrigin");
}

/* control of measures */

void SRGP_setLocatorMeasure (srgp__point value)
{
  enter ("SRGP_setLocatorMeasure");
  leave ("SRGP_setLocatorMeasure");
}

void SRGP_setKeyboardMeasure (char *value)
{
  enter ("SRGP_setKeyboardMeasure");
  leave ("SRGP_setKeyboardMeasure");
}

/* sample procedures */

void SRGP_sampleLocator (srgp__locator_measure *measure)
{
  mouse_pos_button (&(measure->position.x),
            &(measure->position.y),
	    (int *) &(measure->button_chord[LEFT_BUTTON]),
	    (int *) &(measure->button_chord[RIGHT_BUTTON]));
  measure->position.y = REVERSEY(measure->position.y);
  measure->button_chord[MIDDLE_BUTTON] = 0;
  measure->button_of_last_transition =
    (last_button_chord[LEFT_BUTTON] ^ measure->button_chord[LEFT_BUTTON])
    ? (LEFT_BUTTON) : (RIGHT_BUTTON);
  last_button_chord[LEFT_BUTTON] = measure->button_chord[LEFT_BUTTON];
  last_button_chord[MIDDLE_BUTTON] = measure->button_chord[MIDDLE_BUTTON];
  last_button_chord[RIGHT_BUTTON] = measure->button_chord[RIGHT_BUTTON];
}

void SRGP_sampleKeyboard (char *measure, int bufsize)
{
  measure[0] = bioskey (0) % 0xFF;
  if (bufsize > 1)
    measure[1] = '\0';
}

void SRGP_sampleDeluxeKeyboard (srgp__deluxe_keyboard_measure *measure)
{
  static double tick_factor = 60 / CLK_TCK;
  clock_t clk;
  event_type event;
  int modifiers;

  clk = clock ();
/*  modifiers = bioskey (2);*/
  measure->timestamp.seconds = (int) (clk / CLK_TCK);
  measure->timestamp.ticks = (int)
    ((clk - measure->timestamp.seconds * CLK_TCK) * tick_factor);
  *(measure->buffer) = getKey (measure->modifier_chord);
  *(measure->buffer+1) = '\0';
/*  measure->modifier_chord[SHIFT]   = (modifiers & 0x03) != 0;
  measure->modifier_chord[CONTROL] = (modifiers & 0x04) != 0;
  measure->modifier_chord[META]    = (modifiers & 0x08) != 0;*/

}

void SRGP_sampleDeluxeLocator (srgp__deluxe_locator_measure *measure)
{
  static double tick_factor = 60 / CLK_TCK;
  clock_t clk;
  event_type event;
  int modifiers;

  clk = clock ();
  modifiers = bioskey (2);
  measure->timestamp.seconds = (int) (clk / CLK_TCK);
  measure->timestamp.ticks = (int)
    ((clk - measure->timestamp.seconds * CLK_TCK) * tick_factor);
  measure->modifier_chord[SHIFT]   = (modifiers & 0x03) != 0;
  measure->modifier_chord[CONTROL] = (modifiers & 0x04) != 0;
  measure->modifier_chord[META]    = (modifiers & 0x08) != 0;

  mouse_pos_button (&(measure->position.x),
            &(measure->position.y),
	    (int *) &(measure->button_chord[LEFT_BUTTON]),
	    (int *) &(measure->button_chord[RIGHT_BUTTON]));
  measure->position.y = REVERSEY(measure->position.y);
  measure->button_chord[MIDDLE_BUTTON] = 0;
  measure->button_of_last_transition =
    (last_button_chord[LEFT_BUTTON] ^ measure->button_chord[LEFT_BUTTON])
    ? (LEFT_BUTTON) : (RIGHT_BUTTON);
  last_button_chord[LEFT_BUTTON] = measure->button_chord[LEFT_BUTTON];
  last_button_chord[MIDDLE_BUTTON] = measure->button_chord[MIDDLE_BUTTON];
  last_button_chord[RIGHT_BUTTON] = measure->button_chord[RIGHT_BUTTON];
}

/* event procedures */

inputDevice SRGP_waitEvent (int maximum_wait_time)
{
  static factor = 60 / CLK_TCK;
  int timed_out = 0;
  clock_t start = clock ();

  if (maximum_wait_time == 0)
    timed_out = numevents == 0;
  else
    while ((numevents == 0) && !timed_out)
      timed_out = (((clock () - start) * factor) <= maximum_wait_time);

  if (timed_out)
    return NO_DEVICE;
  else {
	/* monster roddy and dennis hackhackhack */
        /* before getting another event, free the current one if not NULL */
	if (current_event != NULL) {
		if (current_event->device == KEYBOARD) {
			free (current_event->u.keyboard.buffer);
		}
		free (current_event);
	}
        current_event = get_event ();
    return (current_event->device);
  }
}

void SRGP_getLocator (srgp__locator_measure *measure)
{
  measure->position = current_event->u.locator.position;
  measure->button_chord[LEFT_BUTTON] =
    current_event->u.locator.button_chord[LEFT_BUTTON];
  measure->button_chord[MIDDLE_BUTTON] = 
    current_event->u.locator.button_chord[MIDDLE_BUTTON];
  measure->button_chord[RIGHT_BUTTON] =
    current_event->u.locator.button_chord[RIGHT_BUTTON];
  measure->button_of_last_transition =
    current_event->u.locator.button_of_last_transition;
}

void SRGP_getKeyboard (char *measure, int bufsize)
{
  measure[0] = *((*current_event).u.keyboard.buffer);
  measure[1] = '\0';
}

void SRGP_getDeluxeLocator (srgp__deluxe_locator_measure *measure)
{
  *measure = (*current_event).u.locator;
}

void SRGP_getDeluxeKeyboard (srgp__deluxe_keyboard_measure *measure)
{
  *measure = (*current_event).u.keyboard;
}


/* Inquiry */


void SRGP_inquireAttributes (srgp__attribute_group *group)
{
  *group = current_attributes;
}

int SRGP_inquireActiveCanvas (void)
{
  return 0;
}

srgp__rectangle SRGP_inquireCanvasExtent (int canvas_index)
{
  srgp__rectangle rect;

  SRGP_inquireCanvasSize (canvas_index, &rect.top_right.x, &rect.top_right.y);
  rect.bottom_left.x = rect.bottom_left.y = 0;

  return rect;
}

void SRGP_inquireCanvasSize (int canvas_index, int *width, int *height)
{
  /* for returning the real screen size -- Matt Conway 20 Aug 1992 */
  *width = user_screen_width;
  *height = user_screen_height;
}

int SRGP_inquireCanvasDepth (void)
{
  return 8; /* monochrome display */
}

void SRGP_inquireTextExtent (char *str, int *width, int *ascent, int *descent)
{
  *width = textwidth ((char * far) str);
  *ascent = textheight ((char * far) str) + 1;
  *descent = 1; /* question: is there a better way? */
}


/* Control of Table Sizes */

/* no headers */


/* Debugging and Optimization Aids */

/* no headers */


void SRGP_changeScreenCanvasSize (int newWidth, int newHeight)
{
  if (srgp__resizeCallback != NULL)
	srgp__resizeCallback (newWidth, newHeight);
}

void SRGP_disableDebugAids ()
{
}


void SRGP_setErrorHandlingMode (errorHandlingMode emode) { }
