/*_ fgdemo.c */
/* Copyright Prototronics */
/* 6490 Kaniksu Shores Circle */
/* Sandpoint, ID  83864-9445 */
/* Joe Huffman */
/* September 18, 1990 */
/* FAX:   208-263-8772 */
/* email: fg@proto.com */
/* Modified by Joe Huffman March 30, 1991*/

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
#include <assert.h>
#include <string.h>
#include <limits.h>
#include <fg.h>

struct fg_state *demostate_p = &fg;

/**********************************/
/* Useful macros. */

#define arraysize(array)  (sizeof(array) / sizeof(array[0]))
#define key_waiting() kbhit()
#define key_input() getch()

#define ESC 27

typedef struct {fg_coord_t x, y;} point_t;     /* an (x,y) pair        */
typedef struct {fg_coord_t g_x0, g_y0, g_z0, g_x, g_y, g_z;} three_d_t;
fg_box_t inner_box;

fg_color_t next_color (fg_color_t ex_color);
void beam_bounce (void);
void fancy_lines (void);
void lots_of_boxes (void);
void next_color_init (void), next_color_term (void);
void next_color_setcolormap (void);
fg_pbox_t rand_box (fg_pbox_t to_box, fg_pbox_t outer_box);
void press_any (void);

fg_coord_t star_delta (long int, fg_coord_t, fg_coord_t);
void explode_box (fg_const_pbox_t box);
void fixed_stars (int num, fg_box_t clipbox);
void clearscreen(void);
void stars (void);
void terminate (void);
void border (void);
void demo_text (char *);
void dissolve_demo(void);
void bounce_line (fg_box_t clipbox, int erase);
#define BOUNCE_INIT 0
#define BOUNCE_NORMAL 1
#define BOUNCE_ERASE -1

static char press_any_string [] = "Esc to exit, any other key to continue.";
void demo_functions (void);
void ellipse_demo (void);

typedef long int scale_t;
#define SCALE(a) ((scale_t)(a * 200.0))
#define UNSCALE(a) ((scale_t)((a)/200))
scale_t int_sin (int);
scale_t int_cos (int);
int int_arctan (scale_t slope);

void pyramid(int mode,point_t loc, scale_t scale_x, int phi);
void rotate_pyramids (void);
void blit_demo(void);
void mouse_demo(void);
void demo_fill_patterns(void);

main()
{
  if (fg_init_all () == FG_NULL)
  {
    fputs (
#if DOS386  /* 8514A not supported yet. */
"Unable to initialize graphics display device.\n\
This demo requires a CGA, EGA, VGA, TOSHIBA or Hercules graphics board.\n\
If this program was unable to properly sense the correct graphics board\n\
automatically type \"set FG_DISPLAY=XXXX\" at the DOS prompt where XXXX is one\n\
of the following:\n\
\tCGAHIRES\t\tCGAMEDRES\n\
\tEGAECD\t\t\tEGACOLOR\n\
\tEGALOWRES\t\tEVGAHIRES\n\
\tHERC\t\t\tTSENGHIRES\n\
\tPARADISEHIRES\t\tTRIDENTHIRES\n\
\tVGA11\t\t\tVGA12\n\
\tVGA13\t\t\tVEGAVGAHIRES\n\
\tVESA{0|1|2|3|5|7}\tVESA6A\n\
\tTOSHIBA\n\
\tDFIHIRES\n\n\
Where:\tEGAECD means the EGA card with an enhanced color display.\n\
\tEGACOLOR means the EGA card with a color display.\n\
\tVEGAVGAHIRES means Video 7 Vega VGA card in 800 x 600 mode.\n\
\tVGA11 means the VGA card is to be run in mode 11 (hex).\n\
\tVGA12 means the VGA card is to be run in mode 12 (hex).\netc...", stderr);
#else /* DOS386 */
#if M_UNIX || M_XENIX
"Unable to initialize graphics display device.\n\
This demo requires a EGA or VGA graphics board.\n\
If this program was unable to properly sense the correct graphics board\n\
automatically type \"setenv FG_DISPLAY XXXX\" at the shell prompt where\n\
XXXX is one of the following:\n\
\tEGAECD\t\t\tEGACOLOR\n\
\tVGA12\t\t\tVGA13\n\
Where:\tEGAECD means the EGA card with an enhanced color display.\n\
\tEGACOLOR means the EGA card with a color display.\n\
\tVGA12 means the VGA card is to be run in mode 12 (hex).\netc...\n", stderr);
#else /* M_UNIX || M_XENIX */
"Unable to initialize graphics display device.\n\
This demo requires a CGA, EGA, VGA, 8514A, TOSHIBA or Hercules graphics board.\n\
If this program was unable to properly sense the correct graphics board\n\
automatically type \"set FG_DISPLAY=XXXX\" at the DOS prompt where XXXX is one\n\
of the following:\n\
\tCGAHIRES\t\tCGAMEDRES\n\
\tEGAECD\t\t\tEGACOLOR\n\
\tEGALOWRES\t\tEVGAHIRES\n\
\tHERC\t\t\tTSENGHIRES\n\
\tPARADISEHIRES\t\tTRIDENTHIRES\n\
\tVGA11\t\t\tVGA12\n\
\tVGA13\t\t\tVEGAVGAHIRES\n\
\tVESA{0|1|2|3|5|7}\tVESA6A\n\
\tTOSHIBA\t\t\t8514A\n\
\tDFIHIRES\n\n\
Where:\tEGAECD means the EGA card with an enhanced color display.\n\
\tEGACOLOR means the EGA card with a color display.\n\
\tVEGAVGAHIRES means Video 7 Vega VGA card in 800 x 600 mode.\n\
\tVGA11 means the VGA card is to be run in mode 11 (hex).\n\
\tVGA12 means the VGA card is to be run in mode 12 (hex).\netc...", stderr);
#endif  /* M_UNIX || M_XENIX */
#endif  /* DOS386 */
    exit (1);
  }

  next_color_init ();
  demo_text (press_any_string);
  ellipse_demo ();
  blit_demo();
  clearscreen();
  demo_fill_patterns();
  mouse_demo();
  clearscreen();
  lots_of_boxes ();
  clearscreen();
  border ();
  fixed_stars (200, inner_box);
  stars ();
  dissolve_demo();
  clearscreen();
  rotate_pyramids ();

  demo_text ("Press any key to exit...");
  terminate ();
  return 0;
}

int _FAR dissolve_stop(void)
{
  if(key_waiting())
  {
    do
    {
      if (key_input () == ESC)
        terminate ();
    } while (key_waiting ());

    return 0;   /* Stop. */
  }
  return 1;     /* Keep going. */
}
/****************************************************************************
Helper function for dissolve_demo().  Returns non-zero if specified bit is 
set.
March 30, 1991
****************************************************************************/
int matrix_bit(char *m_p, fg_const_pbox_t box, fg_coord_t x, fg_coord_t y)
{
  int offset = y * ((fg_box_width(box) + 7)/CHAR_BIT) + x/CHAR_BIT;
  unsigned char mask = 1 << ((CHAR_BIT - 1) - (x % CHAR_BIT));

  return m_p[offset] & mask;
}
void dissolve_demo()
{
  static fg_box_t box_dissolve = {0,0,88,16};
#define BOX_WIDTH 89
#define BOX_HEIGHT 17
#define BOX_AREA (BOX_WIDTH * BOX_HEIGHT)
  static char matrix_dissolve [204] =
  {
    0xff,0x06,0x0f,0xc1,0xf8,0x3f,0x83,0x00,0xc0,0x18,0x7f,0x80,	/* XXXXXXXX     XX     XXXXXX     XXXXXX     XXXXXXX     XX        XX         XX    XXXXXXXX */
    0xff,0x86,0x1f,0xc3,0xf8,0x7f,0xc3,0x00,0xc0,0x18,0x7f,0x80,	/* XXXXXXXXX    XX    XXXXXXX    XXXXXXX    XXXXXXXXX    XX        XX         XX    XXXXXXXX */
    0xc1,0x86,0x18,0x03,0x00,0x60,0xc3,0x00,0xc0,0x18,0x60,0x00,	/* XX     XX    XX    XX         XX         XX     XX    XX        XX         XX    XX       */
    0xc1,0x86,0x18,0x03,0x00,0x60,0xc3,0x00,0x60,0x30,0x60,0x00,	/* XX     XX    XX    XX         XX         XX     XX    XX         XX       XX     XX       */
    0xc1,0x86,0x18,0x03,0x00,0x60,0xc3,0x00,0x60,0x30,0x60,0x00,	/* XX     XX    XX    XX         XX         XX     XX    XX         XX       XX     XX       */
    0xc1,0x86,0x18,0x03,0x00,0x60,0xc3,0x00,0x60,0x30,0x60,0x00,	/* XX     XX    XX    XX         XX         XX     XX    XX         XX       XX     XX       */
    0xc1,0x86,0x18,0x03,0x00,0x60,0xc3,0x00,0x30,0x60,0x60,0x00,	/* XX     XX    XX    XX         XX         XX     XX    XX          XX     XX      XX       */
    0xc1,0x86,0x18,0x03,0x00,0x60,0xc3,0x00,0x30,0x60,0x60,0x00,	/* XX     XX    XX    XX         XX         XX     XX    XX          XX     XX      XX       */
    0xc1,0x86,0x1f,0xc3,0xf8,0x60,0xc3,0x00,0x30,0x60,0x7c,0x00,	/* XX     XX    XX    XXXXXXX    XXXXXXX    XX     XX    XX          XX     XX      XXXXX    */
    0xc1,0x86,0x00,0xc0,0x18,0x60,0xc3,0x00,0x18,0xc0,0x60,0x00,	/* XX     XX    XX         XX         XX    XX     XX    XX           XX   XX       XX       */
    0xc1,0x86,0x00,0xc0,0x18,0x60,0xc3,0x00,0x18,0xc0,0x60,0x00,	/* XX     XX    XX         XX         XX    XX     XX    XX           XX   XX       XX       */
    0xc1,0x86,0x00,0xc0,0x18,0x60,0xc3,0x00,0x18,0xc0,0x60,0x00,	/* XX     XX    XX         XX         XX    XX     XX    XX           XX   XX       XX       */
    0xc1,0x86,0x00,0xc0,0x18,0x60,0xc3,0x00,0x0d,0x80,0x60,0x00,	/* XX     XX    XX         XX         XX    XX     XX    XX            XX XX        XX       */
    0xc1,0x86,0x00,0xc0,0x18,0x60,0xc3,0x00,0x0d,0x80,0x60,0x00,	/* XX     XX    XX         XX         XX    XX     XX    XX            XX XX        XX       */
    0xc1,0x86,0x00,0xc0,0x18,0x60,0xc3,0x00,0x0d,0x80,0x60,0x00,	/* XX     XX    XX         XX         XX    XX     XX    XX            XX XX        XX       */
    0xff,0x86,0x1f,0xc3,0xf8,0x7f,0xc3,0xfe,0x07,0x00,0x7f,0x80,	/* XXXXXXXXX    XX    XXXXXXX    XXXXXXX    XXXXXXXXX    XXXXXXXXX      XXX         XXXXXXXX */
    0xff,0x06,0x1f,0x83,0xf0,0x3f,0x83,0xfe,0x07,0x00,0x7f,0x80,	/* XXXXXXXX     XX    XXXXXX     XXXXXX      XXXXXXX     XXXXXXXXX      XXX         XXXXXXXX */
    };
  fg_coord_t x, y;
  fg_box_t box;
  fg_color_t text[BOX_AREA], back_color, fore_color;

  back_color = FG_LIGHT_BLUE == -1? FG_BLACK: FG_LIGHT_BLUE;
  fore_color = FG_LIGHT_WHITE;

  fg_fillboxdissolve(FG_WHITE,FG_MODE_SET,~0,fg.displaybox,1000,dissolve_stop);

  for(y = 0; y < BOX_HEIGHT; y++)
  {
    for(x = 0; x < BOX_WIDTH; x++)
    {
      fg_color_t *t_p = &text[y * BOX_WIDTH + x];

      if(matrix_bit(matrix_dissolve,box_dissolve,x,box_dissolve[FG_Y2] - y))
        *t_p = fore_color;
      else
        *t_p = back_color;
    }
  }

  fg_box_cpy(box,box_dissolve);
  x = (fg_box_width(fg.displaybox) - fg_box_width(box_dissolve))/2;
  box[FG_X1] += x;
  box[FG_X2] += x;
  box[FG_Y1] += y = fg_box_height(fg.displaybox)/2;
  box[FG_Y2] += y;

  fg_writeboxdissolve(box, text, 1, dissolve_stop);
  fg_fillboxdissolve(back_color,FG_MODE_SET,~0,fg.displaybox,1000,dissolve_stop);

#undef BOX_WIDTH
#undef BOX_HEIGHT
#undef BOX_AREA
}
void blit_demo(void)
{
  fg_line_t line;
  fg_box_t box;
  fg_coord_t delta_x, x, delta_y, y;
  fg_color_t color = 0;

  clearscreen();
  line[FG_X1] = fg_box_width(fg.displaybox)/2;
  line[FG_Y1] = fg_box_height(fg.displaybox)/2;

  for(y = 0; y <= fg.displaybox[FG_Y2]; y += 10)
  {
    line[FG_Y2] = y;
    line[FG_X2] = 0;
    fg_drawline (next_color (FG_BLACK), FG_MODE_SET, ~0, FG_LINE_SOLID, line);

    line[FG_X2] = fg.displaybox[FG_X2];
    fg_drawline (next_color (FG_BLACK), FG_MODE_SET, ~0, FG_LINE_SOLID, line);
  }

  for(x = 0; x <= fg.displaybox[FG_X2]; x += 10)
  {
    line[FG_X2] = x;
    line[FG_Y2] = 0;
    fg_drawline (next_color (FG_BLACK), FG_MODE_SET, ~0, FG_LINE_SOLID, line);

    line[FG_Y2] = fg.displaybox[FG_Y2];
    fg_drawline (next_color (FG_BLACK), FG_MODE_SET, ~0, FG_LINE_SOLID, line);
  }

  delta_x = fg_box_width(fg.displaybox)/8;
  delta_y = fg_box_height(fg.displaybox)/50;

  for(x = 0; !key_waiting() && x <= fg.displaybox[FG_X2]; x += delta_x)
  {
    fg_box_cpy(box,fg.displaybox);
    box[FG_X1] = x;
    box[FG_X2] = x + delta_x;
    if(box[FG_X2] > fg.displaybox[FG_X2])
      box[FG_X2] = fg.displaybox[FG_X2];
    box[FG_Y1] = box[FG_Y2] - delta_y;
    fg_fillbox(FG_BLACK, FG_MODE_SET, ~0, box);

    box[FG_Y1] = delta_y;

    do
    {
      fg_blit(box,box[FG_X1],0,0,0);
      box[FG_Y2] -= delta_y;
    } while (box[FG_Y2] >= box[FG_Y1] && !key_waiting());
  }
  while(key_waiting())
  {
    if (key_input () == ESC)
      terminate ();
  }
}
void center_string(fg_coord_t y, char _FAR *string)
{
  char _FAR *p = string;

  while(*p) /* Find the length of the string. */
    p++;

  fg_puts(FG_HIGHLIGHT,FG_MODE_SET, ~0, FG_ROT0,
    (fg.displaybox[FG_X2] - ((p - string) * fg_box_width(fg.charbox)))/2, y,
    string, fg.displaybox);
}


void rand_fill_pattern(void)
{
  fg_pattern_t p;
  int i = 15;
  do
  {
    p[i] = rand() & 1? ~rand(): rand();
  } while (i-- >= 0);

  fg_setfillpattern(FG_FILL_USER_DEFINED,p);
}
void polygon_translate(fg_coord_t *pdest,fg_coord_t *psrc,int vertices,
                       fg_coord_t x, fg_coord_t y)
{
  while(vertices-- >= 0)
  {
    *pdest++ = *psrc++ + x;
    *pdest++ = *psrc++ + y;
  }
}

void demo_fill_patterns()
{
  fg_coord_t p0[8], pout[8];
  int fill_type = FG_FILL_PATTERN_MAX;

  /* Initialize the user definable pattern. */
  rand_fill_pattern();

  /* create a origin based polygon (triangle with one corner at 0,0). */
  memset(p0,0,sizeof(p0));
  p0[2] = fg_box_width(fg_displaybox)/5;
  p0[4] = p0[2]/2;
  p0[5] = fg_box_height(fg_displaybox)/4;

  do
  {
    fg_color_t color, erase_color;
    fg_coord_t x, y;
    char *type_p;

    switch(fill_type)
    {
    default:              /* This will initialize things. */
      fill_type = FG_FILL_SOLID;
      type_p = "SOLID";
      x = 0;
      y = 0;
      break;

    case FG_FILL_SOLID:    fill_type = FG_FILL_BSLASH;  type_p = "BSLASH";
      break;

    case FG_FILL_BSLASH:   fill_type = FG_FILL_SLASH;   type_p = "SLASH";
      break;

    case FG_FILL_SLASH:    fill_type = FG_FILL_HATCH;   type_p = "HATCH";
      break;

    case FG_FILL_HATCH:    fill_type = FG_FILL_LINE;    type_p = "LINE";
      x = 0;
      y += fg_box_height(fg_displaybox)/3;
      break;

    case FG_FILL_LINE:     fill_type = FG_FILL_THICK_X; type_p = "THICK_X";
      break;

    case FG_FILL_THICK_X:  fill_type = FG_FILL_THIN_X;  type_p = "THIN_X";
      break;

    case FG_FILL_THIN_X:   fill_type = FG_FILL_SCALE;   type_p = "SCALE";
      break;

    case FG_FILL_SCALE:    fill_type = FG_FILL_WIDEDOT; type_p = "WIDEDOT";
      x = 0;
      y += fg_box_height(fg_displaybox)/3;
      break;

    case FG_FILL_WIDEDOT:  fill_type = FG_FILL_CLOSEDOT;type_p = "CLOSEDOT";
      break;

    case FG_FILL_CLOSEDOT: fill_type = FG_FILL_DIAMOND; type_p = "DIAMOND";
      break;

    case FG_FILL_DIAMOND:  fill_type = FG_FILL_USER_DEFINED; type_p = "USER";
      break;
    }

    fg_puts(FG_HIGHLIGHT,FG_MODE_SET,~0,FG_ROT0,x,y,type_p,fg.displaybox);
    polygon_translate(pout,p0,3,x,y + fg_box_height(fg.charbox) + 2);
    fg_fillpolygonpattern(FG_HIGHLIGHT,FG_MODE_SET,~0,3,pout,fg.displaybox,
      fill_type);

    color = erase_color = 0;
    while(fill_type == FG_FILL_USER_DEFINED && !key_waiting())
    {
      rand_fill_pattern();
      color = (color + 1) % fg.nsimulcolor;

      if(color == erase_color)
      {
        /* Test for a signed or signed number underflow. */
        if(--erase_color < 0 || erase_color > fg.nsimulcolor)
          erase_color = fg.nsimulcolor - 1;
      }
      fg_flush();
      sleep(2);
      fg_fillpolygon(erase_color,FG_MODE_SET,~0,3,pout,fg.displaybox);
      fg_fillpolygonpattern(color,FG_MODE_SET,~0,3,pout,fg.displaybox,
        fill_type);
    }
    x += fg_box_width(fg.displaybox)/4;
  } while(fill_type != FG_FILL_USER_DEFINED);

  do
  {
    if (key_input () == ESC)
      terminate ();
  } while (key_waiting ());

  return;
}
#ifdef __OS2__  /* No support for mouse under OS2. */

void mouse_demo(void)
{
}

#else

/****************************************************************************
A simple demo of the mouse capabilities.
April 10, 1990
****************************************************************************/

char bat_matrix[48] =
{
 /* This is drawn with the X's set to WHITE, blanks are unchanged. */

  0x00,0x0a,0x00,0x00,  /*             X X             */
  0x00,0x0e,0x00,0x00,  /*             XXX             */
  0x02,0x15,0x08,0x00,  /*       X    X X X    X       */
  0x07,0x9f,0x3c,0x00,  /*      XXXX  XXXXX  XXXX      */
  0x0f,0xff,0xfe,0x00,  /*     XXXXXXXXXXXXXXXXXXX     */
  0x1f,0xff,0xff,0x00,  /*    XXXXXXXXXXXXXXXXXXXXX    */
  0x3f,0xff,0xff,0x80,  /*   XXXXXXXXXXXXXXXXXXXXXXX   */
  0x7f,0xff,0xff,0xc0,  /*  XXXXXXXXXXXXXXXXXXXXXXXXX  */
  0xc7,0x3b,0x9c,0x60,  /* XX   XXX  XXX XXX  XXX   XX */
  0x82,0x11,0x08,0x20,  /* X     X    X   X    X     X */
  0x00,0x11,0x00,0x00,  /*            X   X            */
  0x00,0x2a,0x80,0x00   /*           X X X X           */
};

char arrow_matrix[15] =
{
  0x80,  /* X        */
  0xc0,  /* XX       */
  0xe0,  /* XXX      */
  0xf0,  /* XXXX     */
  0xf8,  /* XXXXX    */
  0xfc,  /* XXXXXX   */
  0xfe,  /* XXXXXXX  */
  0xff,  /* XXXXXXXX */
  0xfc,  /* XXXXXX   */
  0xf8,  /* XXXXX    */
  0xf8,  /* XXXXX    */
  0xcc,  /* XX  XX   */
  0x8c,  /* X   XX   */
  0x06,  /*      XX  */
  0x06,  /*      XX  */
};

fg_msm_cursor_t bat  = {bat_matrix, {0,0,26,11}, 13, 11};
fg_msm_cursor_t arrow = {arrow_matrix,{0,0,7,14}, 0, 14};

void mouse_demo(void)
{
  clearscreen();

  if(fg.msm)
  {
    unsigned int status, last_status = 0xffff;
    fg_box_t status_box;
    static char bat_string[] = "BAT";
    static char arrow_string[] = "ARROW";
    fg_box_t bat_select, arrow_select;
    fg_coord_t x, y, temp;
    int in_char, active_page = 0;
    char done = 0;

    /* Make a box for selecting the bat cursor. */
    bat_select[FG_X1] = 0;
    bat_select[FG_X2] = sizeof(bat_string) * fg_box_width(fg.charbox);

    bat_select[FG_Y2] = fg.displaybox[FG_Y2] - 2 * fg_box_height(fg.charbox);;
    bat_select[FG_Y1] = bat_select[FG_Y2] - fg_box_height(fg.charbox);

    /* Make a box for selecting the arrow cursor. */
    fg_box_cpy(arrow_select, bat_select);
    arrow_select[FG_X2] = sizeof(arrow_string) * fg_box_width(fg.charbox);

    /* Put the boxes at the correct locations. */
    temp = fg_box_width(fg.displaybox)/2;
    bat_select[FG_X1] += temp;
    bat_select[FG_X2] += temp;
    temp -= arrow_select[FG_X2] + fg_box_width(fg.charbox);

    arrow_select[FG_X1] += temp;
    arrow_select[FG_X2] += temp;

    /* Fill in the cursor select boxes on the screen. */
    fg_fillbox(FG_HIGHLIGHT, FG_MODE_SET, ~0, arrow_select);
    fg_fillbox(FG_HIGHLIGHT, FG_MODE_SET, ~0, bat_select);

    temp = fg_box_width(fg.charbox)/2;
    fg_puts(FG_HIGHLIGHT, FG_MODE_XOR, ~0, FG_ROT0, arrow_select[FG_X1] + temp,
      arrow_select[FG_Y1], arrow_string, fg.displaybox);
    fg_puts(FG_HIGHLIGHT, FG_MODE_XOR, ~0, FG_ROT0, bat_select[FG_X1] + temp,
      bat_select[FG_Y1], bat_string, fg.displaybox);

    center_string(fg.displaybox[FG_Y2]-fg_box_height(fg.charbox),
                  "Left button selects cursor.");
    center_string(3 * fg_box_height(fg.charbox), 
"Press F1, F2, F3 to query presses.");
    center_string(2 * fg_box_height(fg.charbox), 
"Press ^F1, ^F2, ^F3 to query releases.");
    center_string(fg_box_height(fg.charbox),
                  "Press mouse button(s) to change status.");

    fg_msm_setcurpos(fg_box_width(fg_displaybox)/2,
      fg_box_height(fg_displaybox)/2);
    fg_msm_showcursor();
    fg_flush();

    fg_box_cpy(status_box,fg.displaybox);
    status_box[FG_Y2] = fg.charbox[FG_Y2];

    do
    {
      static char press_format[] = "status %d, %u presses, last @%d,%d.";
      static char release_format[] = "status %d, %u releases, last @%d,%d.";
      char out_string[50];
      unsigned int count;
      static unsigned int ratio_x = 8, ratio_y = 16;

      do
      {
        status = fg_msm_getstatus(&x, &y);

        if(status != last_status)
        {
          static char motion = 1;   /* The default. */

          if(status & FG_MSM_RIGHT)
          {
            motion = ++motion % 2;
            fg_msm_motion(motion);
          }

          if(status & FG_MSM_LEFT)
          {
            if(fg_pt_inbox(bat_select,x,y))
            {
              fg_msm_setcursor(bat);
            }else
            if(fg_pt_inbox(arrow_select,x,y))
            {
              fg_msm_setcursor(arrow);
            }
          }

          sprintf(out_string, "status:%x, motion:%d, x:%d, y:%d", status,
            motion,x,y);
          fg_fillbox(FG_BLACK,FG_MODE_SET, ~0, status_box);
          center_string(0, out_string);
          fg_flush();
          last_status = status;
        }
      } while(!key_waiting());

      in_char = key_input();
      if (in_char == 0)             /* Extended key, function key, whatever. */
        in_char = key_input() << 8;

#define F1     (59 << 8)
#define F2     (60 << 8)
#define F3     (61 << 8)
#define F4     (62 << 8)
#define CTRLF1 (94 << 8)
#define CTRLF2 (95 << 8)
#define CTRLF3 (96 << 8)
#define CTRLF4 (97 << 8)
#define UP     (72 << 8)
#define DOWN   (80 << 8)

      out_string[0] = '\0';
      switch (in_char)
      {
      case ESC:
        terminate();
        break;
      case F1:
        count = FG_MSM_LEFT;
        status = fg_msm_getpress(&count, &x, &y);
        sprintf(out_string, press_format, status, count, x, y);
        break;
      case F2:
        count = FG_MSM_MIDDLE;
        status = fg_msm_getpress(&count, &x, &y);
        sprintf(out_string, press_format, status, count, x, y);
        break;
      case F3:
        count = FG_MSM_RIGHT;
        status = fg_msm_getpress(&count, &x, &y);
        sprintf(out_string, press_format, status, count, x, y);
        break;
      case F4:
        active_page = (active_page + 1) % fg.numpages;
        fg_setactivepage(active_page);
        fg_flush();
        break;
      case CTRLF4:
        fg_setdisplaypage(active_page);
        fg_flush();
        break;
      case CTRLF1:
        count = FG_MSM_LEFT;
        status = fg_msm_getrelease(&count, &x, &y);
        sprintf(out_string, release_format, status, count, x, y);
        break;
      case CTRLF2:
        count = FG_MSM_MIDDLE;
        status = fg_msm_getrelease(&count, &x, &y);
        sprintf(out_string, release_format, status, count, x, y);
        break;
      case CTRLF3:
        count = FG_MSM_RIGHT;
        status = fg_msm_getrelease(&count, &x, &y);
        sprintf(out_string, release_format, status, count, x, y);
        break;
      case UP:      /* Higher numbers mean slower mouse. */
        if(ratio_y < UINT_MAX/2)
        {
          ratio_x *= 2;
          ratio_y *= 2;
        }
        fg_msm_setratio(ratio_x, ratio_y);
        break;
      case DOWN:    /* Lower numbers mean faster mouse. */
        if(ratio_x > 1)
        {
          ratio_x /= 2;
          ratio_y /= 2;
        }
        fg_msm_setratio(ratio_x, ratio_y);
        break;
      default:
        done = 1;
        break;
      }
      if(out_string[0])
      {
        fg_fillbox(FG_BLACK,FG_MODE_SET, ~0, status_box);
        center_string(0, out_string);
        fg_flush();
      }
    } while(!done);

    fg_msm_hidecursor();

    while (key_waiting ())
    {
      if (key_input () == ESC)
        terminate ();
    }
  }
  else
  {
    static char no_mouse[] = "No mouse present.";
    fg_puts(FG_HIGHLIGHT, FG_MODE_SET, ~0, FG_ROT0, 
      (fg.displaybox[FG_X2] -
      (sizeof(no_mouse) - 1) * fg_box_width(fg.charbox))/2,
      fg_box_height(fg.charbox), no_mouse, fg.displaybox);

    if (key_input () == ESC)
      terminate ();
  }
}
#endif /* __OS2__ */

void clearscreen(void)
{
    fg_fillbox (FG_BLACK, FG_MODE_SET, ~0, fg.displaybox);
}

/**********************************
Ask the user to press a key to continue.
**********************************/
void press_any (void)
{
  fg_color_t color;
  fg_box_t string_box;

  fg_box_cpy (string_box, fg.charbox);

  string_box [FG_X1] = (fg_box_width (fg.displaybox) -
      sizeof (press_any_string) * fg_box_width (fg.charbox))/2;
  if (string_box[FG_X1] < fg.displaybox[FG_X1])
    string_box[FG_X1] = fg.displaybox[FG_X1];

  string_box [FG_X2] = (fg.displaybox [FG_X2] +
      sizeof (press_any_string) * fg_box_width (fg.charbox))/2;
  if (string_box[FG_X2] > fg.displaybox[FG_X2])
    string_box[FG_X2] = fg.displaybox[FG_X2];

  fg_fillbox (FG_BLACK, FG_MODE_SET, ~0, string_box);

  fg_puts (FG_HIGHLIGHT, FG_MODE_SET, ~0, FG_ROT0, string_box [FG_X1], 0,
         press_any_string, fg.displaybox);

  fg_flush ();
  do
  {
    if (key_input () == ESC)
      terminate ();
  } while (key_waiting ());
}
/**********************************
A demo of ellispes...
**********************************/
void ellipse_demo (void)
{
  fg_coord_t x, y, x_radius, y_radius;
  fg_box_t small_box;

  clearscreen();

  small_box [FG_X1] = fg_box_width (fg.displaybox)/4;
  small_box [FG_X2] = 3 * small_box [FG_X1];

  small_box [FG_Y1] = fg_box_height (fg.displaybox)/4;
  small_box [FG_Y2] = 3 * small_box [FG_Y1];
  fg_drawbox (FG_HIGHLIGHT,FG_MODE_SET, ~0,
     FG_LINE_DASH_DOTTED, small_box, fg.displaybox);

  x_radius = fg_box_width (small_box)/2;

  x = fg.displaybox [FG_X2]/2;
  y = fg.displaybox [FG_Y2]/2;

  do
  {
    long int temp;

    x_radius -= 5;
    y -= 2;

    temp = ((long int) (x_radius)) * fg.pixelx;
    y_radius = temp/((long int)fg.pixely);

    fg_drawellipse (next_color (FG_BLACK), FG_MODE_SET, ~0, x, y, x_radius,
       y_radius, 0, 3600, small_box);
    fg_flush();
  } while (x_radius > 0 && !key_waiting ());

  press_any ();

  clearscreen();
  x = fg_box_width (fg.displaybox)/2;
  y = fg_box_height (fg.displaybox)/5;
  x_radius = 0;

  do
  {
    fg_color_t color = next_color (FG_BLACK);
    long int temp = ((long int) (x_radius)) * fg.pixelx;
    y_radius = temp/((long int)fg.pixely);

    fg_drawellipse (color, FG_MODE_SET, ~0, x, y, x_radius,
       y_radius/2, 1800, 3600, fg.displaybox);

    fg_drawellipse (color, FG_MODE_SET, ~0, x, y, x_radius,
       y_radius * 2, 0, 1800, fg.displaybox);

    x_radius += 3;
    fg_flush();
  } while (x_radius < fg.displaybox [FG_X2]/2 &&
           y_radius < fg.displaybox [FG_Y2]/4 && !key_waiting ());

  beam_bounce ();   /* Continues until a key is input. */

  do
  {
    if (key_input () == ESC)
      terminate ();
  } while (key_waiting ());
}
/**********************************
"Radar" beam bouncing around.  Continues until a key is input.
**********************************/
void beam_bounce (void)
{
  scale_t slope;
  int start_angle, end_angle;
  fg_coord_t radius, x, y, diag_len;

  slope = SCALE (fg_box_height (fg.displaybox));
  slope /= fg_box_width (fg.displaybox);
  diag_len = SCALE(fg_box_width (fg.displaybox))/int_cos (int_arctan (slope));

  do
  {
    next_color_init ();

    start_angle = int_arctan (slope) * 10;
    end_angle = start_angle + 100;
    start_angle -= 100;

    x = fg.displaybox [FG_X1];
    y = fg.displaybox [FG_Y1];

    radius = 0;
    do
    {
      fg_drawarc (next_color (FG_BLACK), FG_MODE_XOR, ~0, x, y, radius,
        start_angle, end_angle, fg.displaybox);
      fg_flush();
      radius += 10;
    } while (radius <= diag_len && radius < 720);

    x = fg.displaybox [FG_X2];
    y = fg.displaybox [FG_Y2];

    radius = 0;
    do
    {
      fg_drawarc (next_color (FG_BLACK), FG_MODE_XOR, ~0, x, y, radius,
        2600, 2700, fg.displaybox);

      fg_flush();
      radius += 10;
    } while (radius <= fg.displaybox [FG_Y2]);


    x = fg.displaybox [FG_X2];
    y = fg.displaybox [FG_Y1];

    start_angle = int_arctan (-slope) * 10;
    end_angle = start_angle + 100;
    start_angle -= 100;

    radius = 0;
    do
    {
      fg_drawarc (next_color (FG_BLACK), FG_MODE_XOR, ~0, x, y, radius,
        start_angle, end_angle, fg.displaybox);

      fg_flush();
      radius += 10;
    } while (radius <= diag_len && radius < 720);

    x = fg.displaybox [FG_X1];
    y = fg.displaybox [FG_Y2];

    radius = 0;

    do
    {
      fg_drawarc (next_color (FG_BLACK), FG_MODE_XOR, ~0, x, y, radius,
        2700, 2800, fg.displaybox);

      fg_flush();
      radius += 10;
    } while (radius <= fg.displaybox [FG_Y2]);

    fg_flush ();
  } while (!key_waiting ());
}
/**********************************
Lots of boxes, very fast.
**********************************/
void lots_of_boxes (void)
{
  fg_box_t box;
  const int n_col = fg.nsimulcolor;

  do
  {
    fg_fillbox (rand() % n_col, FG_MODE_SET, ~0, rand_box(box, fg.displaybox));
    fg_flush ();
  } while (!key_waiting ());

  do
  {
    if (key_input () == ESC)
      terminate ();
  } while (key_waiting ());
}
/**********************************
Create a new box that fits inside of outer_box but of random size.
**********************************/
fg_pbox_t rand_box (to_box, outer_box)
fg_pbox_t to_box, outer_box;
{
  to_box [FG_X2] = (rand () % (fg_box_width (outer_box) - 1)) + 1;
  to_box [FG_Y2] = (rand () % (fg_box_height (outer_box) - 1)) + 1;

  to_box [FG_X1] = rand () % to_box [FG_X2];
  to_box [FG_Y1] = rand () % to_box [FG_Y2];

  to_box [FG_X1] += outer_box [FG_X1];
  to_box [FG_X2] += outer_box [FG_X1];
  to_box [FG_Y1] += outer_box [FG_Y1];
  to_box [FG_Y2] += outer_box [FG_Y1];

  return to_box;
}
/**********************************
Some fancy looking lines.
**********************************/
void fancy_lines (void)
{
  fg_line_t line;

  border ();

  line [FG_X1] = line [FG_X2] = inner_box [FG_X1];
  line [FG_Y1] = inner_box [FG_Y1];
  line [FG_Y2] = inner_box [FG_Y2];

  do
  {
    fg_drawline (next_color (FG_BLACK), FG_MODE_SET, ~0, FG_LINE_SOLID, line);
    line [FG_X2] += 10;
    line [FG_Y1] += 10;
  } while (line [FG_X2] < inner_box [FG_X2] &&
           line [FG_Y1] < inner_box [FG_Y2]);

  line [FG_X1] = line [FG_X2] = inner_box [FG_X2];
  line [FG_Y1] = inner_box [FG_Y1];
  line [FG_Y2] = inner_box [FG_Y2];

  do
  {
    fg_drawline (next_color (FG_BLACK), FG_MODE_SET, ~0, FG_LINE_SOLID, line);
    line [FG_X1] -= 10;
    line [FG_Y2] -= 10;
  } while (line [FG_X1] > inner_box [FG_X1] &&
           line [FG_Y2] > inner_box [FG_Y1]);
}
/**********************************
The title of the program.
**********************************/
void demo_text (press_any_string)
char *press_any_string;
{
  static fg_box_t flash_box = {0,0,65,12};
  static char flash_matrix[117] =
{
0x7f,0xe2,0x00,0x00,0x40,0x0f,0xf0,0x80,0x40,	/*  XXXXXXXXXX   X                  X          XXXXXXXX    X        X */
0x80,0x02,0x00,0x00,0x40,0x10,0x08,0x80,0x40,	/* X             X                  X         X        X   X        X */
0x80,0x02,0x00,0x00,0xa0,0x10,0x08,0x80,0x40,	/* X             X                 X X        X        X   X        X */
0x80,0x02,0x00,0x00,0xa0,0x10,0x00,0x80,0x40,	/* X             X                 X X        X            X        X */
0x80,0x02,0x00,0x01,0x10,0x10,0x00,0x80,0x40,	/* X             X                X   X       X            X        X */
0x80,0x02,0x00,0x01,0x10,0x10,0x00,0x80,0x40,	/* X             X                X   X       X            X        X */
0xff,0x02,0x00,0x02,0x08,0x0f,0xf0,0xff,0xc0,	/* XXXXXXXX      X               X     X       XXXXXXXX    XXXXXXXXXX */
0x80,0x02,0x00,0x03,0xf8,0x00,0x08,0x80,0x40,	/* X             X               XXXXXXX               X   X        X */
0x80,0x02,0x00,0x04,0x04,0x00,0x08,0x80,0x40,	/* X             X              X       X              X   X        X */
0x80,0x02,0x00,0x04,0x04,0x00,0x08,0x80,0x40,	/* X             X              X       X              X   X        X */
0x80,0x02,0x00,0x08,0x02,0x10,0x08,0x80,0x40,	/* X             X             X         X    X        X   X        X */
0x80,0x02,0x00,0x08,0x02,0x10,0x08,0x80,0x40,	/* X             X             X         X    X        X   X        X */
0x80,0x01,0xff,0x10,0x01,0x0f,0xf0,0x80,0x40,	/* X              XXXXXXXXX   X           X    XXXXXXXX    X        X */
};

static fg_box_t graphics_box = {0,0,92,12};
static char graphics_matrix[156] =
{
0x7f,0xc1,0xfc,0x00,0x80,0x3f,0x84,0x04,0x7c,0x3f,0x07,0xf0,	/*  XXXXXXXXX     XXXXXXX          X         XXXXXXX    X       X   XXXXX    XXXXXX     XXXXXXX  */
0x80,0x22,0x02,0x01,0x40,0x20,0x44,0x04,0x10,0x40,0x88,0x08,	/* X         X   X       X        X X        X      X   X       X     X     X      X   X       X */
0x80,0x22,0x02,0x01,0x40,0x20,0x44,0x04,0x10,0x40,0x08,0x00,	/* X         X   X       X        X X        X      X   X       X     X     X          X         */
0x80,0x02,0x02,0x02,0x20,0x20,0x44,0x04,0x10,0x40,0x08,0x00,	/* X             X       X       X   X       X      X   X       X     X     X          X         */
0x80,0x02,0x02,0x02,0x20,0x20,0x44,0x04,0x10,0x40,0x08,0x00,	/* X             X       X       X   X       X      X   X       X     X     X          X         */
0x80,0x02,0x02,0x04,0x10,0x20,0x44,0x04,0x10,0x40,0x08,0x00,	/* X             X       X      X     X      X      X   X       X     X     X          X         */
0x80,0x03,0xfc,0x04,0x10,0x3f,0x87,0xfc,0x10,0x40,0x07,0xf0,	/* X             XXXXXXXX       X     X      XXXXXXX    XXXXXXXXX     X     X           XXXXXXX  */
0x83,0xc2,0x02,0x0f,0xf8,0x20,0x04,0x04,0x10,0x40,0x00,0x08,	/* X     XXXX    X       X     XXXXXXXXX     X          X       X     X     X                  X */
0x80,0x22,0x02,0x08,0x08,0x20,0x04,0x04,0x10,0x40,0x00,0x08,	/* X         X   X       X     X       X     X          X       X     X     X                  X */
0x80,0x22,0x02,0x10,0x04,0x20,0x04,0x04,0x10,0x40,0x00,0x08,	/* X         X   X       X    X         X    X          X       X     X     X                  X */
0x80,0x22,0x02,0x10,0x04,0x20,0x04,0x04,0x10,0x40,0x00,0x08,	/* X         X   X       X    X         X    X          X       X     X     X                  X */
0x80,0x22,0x02,0x20,0x02,0x20,0x04,0x04,0x10,0x40,0x80,0x08,	/* X         X   X       X   X           X   X          X       X     X     X      X           X */
0x7f,0xc2,0x02,0x20,0x02,0x20,0x04,0x04,0x7c,0x3f,0x07,0xf0,	/*  XXXXXXXXX    X       X   X           X   X          X       X   XXXXX    XXXXXX     XXXXXXX  */
};

#if M_UNIX || M_XENIX
  static char support_string [] = "Supports EGA and VGA.";
#else
#if !__OS2__
  static char support_string [] = "Supports [C|E|V]GA, TIGA, and Hercules.";
#else
  static char support_string [] = "Supports CGA, EGA, VGA, and Hercules.";
#endif
#endif
  static char demo_string [] = "Demo program for:";
  fg_coord_t x, y;
  char *cptr, out_str [81];

  clearscreen();
  fancy_lines ();

  x = (fg.displaybox [FG_X2] -
      arraysize (demo_string) * fg_box_width(fg.charbox))/2;
  y = fg_box_height (fg.displaybox)/4;
  y *= 3;

  fg_puts (FG_HIGHLIGHT, FG_MODE_SET, ~0, FG_ROT0, x, y, demo_string, 
    fg.displaybox);

  x = (fg_box_width (fg.displaybox) - fg_box_width (flash_box))/2;
  y -= fg_box_height (flash_box) + fg_box_height (fg.charbox)/2;
  fg_drawmatrix (next_color (FG_BLACK), FG_MODE_SET, ~0, FG_ROT0, x, y,
      flash_matrix, flash_box, fg.displaybox);

  x = (fg_box_width (fg.displaybox) - fg_box_width (graphics_box))/2;
  y -= fg_box_height (graphics_box) + fg_box_height (fg.charbox)/2;
  fg_drawmatrix (next_color (FG_BLACK), FG_MODE_SET, ~0, FG_ROT0, x, y,
      graphics_matrix, graphics_box, fg.displaybox);

  x = (fg_box_width (fg.displaybox) -
      sizeof (support_string) * fg_box_width (fg.charbox))/2;
  y = fg_box_height (fg.displaybox)/2;

  fg_puts (FG_HIGHLIGHT, FG_MODE_SET, ~0, FG_ROT0, x, y, support_string,
    fg.displaybox);

  switch (fg.display)
  {
#if !(M_UNIX || M_XENIX || __OS2__)
  case FG_ATI61:  /* ATI Wonder: 640 x 400 x 256 colors (mode 0x61). */
    cptr = "ATI Wonder+ mode 0x61 (640 x 400 x 256 colors)";
    break;
  case FG_ATI62:  /* ATI Wonderboard: 640 x 480 x 256 colors (mode 0x62). */
    cptr = "ATI Wonder+ mode 0x62 (640 x 480 x 256 colors)";
    break;
  case FG_ATI63:  /* ATI Wonderboard: 800 x 600 x 256 colors (mode 0x63). */
    cptr = "ATI Wonder+ mode 0x63 (800 x 600 x 256 colors)";
    break;
  case FG_CGAMEDRES:  /* IBM CGA in 320*200 color (mode 0x04)         */
    cptr = "CGA in med res mode";
    break;
  case FG_EGALOWRES:  /* IBM EGA in 320*200 color (mode 0x0D)         */
    cptr = "EGA in low res mode";
    break;
  case FG_VGA13:      /* IBM VGA in 320*200, 256 colors               */
    cptr = "VGA mode 13";
    break;
  case FG_HERCHALF:   /* Hercules monochrome, 1 display page          */
    cptr = "Hercules in Half (one page) mode";
    break;
  case FG_HERCFULL:   /* Hercules monochrome, 2 display pages         */
    cptr = "Hercules in Full (two page) mode";
    break;
  case FG_TOSHIBA:    /* Toshiba J-3100                       */
    cptr = "Toshiba";
    break;
  case FG_8514A:
    cptr = "IBM 8514A";
    break;
  case FG_EVGAHIRES:
    cptr = "Everex EVGA board 800 x 600 mode";
    break;
  case FG_VEGAVGAHIRES:
    cptr = "Video 7 Vega VGA 800 x 600 mode";
    break;
  case FG_PARADISEHIRES:
    cptr = "Paradise VGA 800 x 600 mode";
    break;
  case FG_TRIDENTHIRES:
    cptr = "Trident VGA 800 x 600 mode";
    break;
  case FG_TIGA:
    cptr = "TIGA";
    break;
  case FG_TSENGHIRES:
    cptr = "Tseng Lab's VGA 800 x 600 x 16 colors mode";
    break;
  case FG_DFIHIRES:
    cptr = "Diamond Flower Instruments VGA 800 x 600 mode";
    break;
  case FG_VESA0:      /* VESA super VGA in 640*400, 256 colors         */
    cptr = "VESA VGA mode 100, 640 x 400 x 256 colors";
    break;
  case FG_VESA1:      /* VESA super VGA in 640*480, 256 colors         */
    cptr = "VESA VGA mode 101, 640 x 480 x 256 colors";
    break;
  case FG_VESA2:      /* VESA super VGA in 800*600, 16 colors         */
    cptr = "VESA VGA mode 102, 800 x 600 x 16 colors";
    break;
  case FG_VESA3:      /* VESA super VGA in 800*600, 256 colors         */
    cptr = "VESA VGA mode 103, 800 x 600 x 256 colors";
    break;
  case FG_VESA5:      /* VESA super VGA in 1024*768, 256 colors         */
    cptr = "VESA VGA mode 105, 1024 x 768 x 256 colors";
    break;
  case FG_VESA7:      /* VESA super VGA in 1280*1024, 256 colors         */
    cptr = "VESA VGA mode 107, 1280 x 1024 x 256 colors";
    break;
  case FG_VESA6A:      /* VESA super VGA in 800*600, 16 colors         */
    cptr = "VESA VGA mode 6A, 800 x 600 x 16 colors";
    break;
#endif /* UNIX || XENIX || __OS2__ */

  case FG_CGAHIRES:   /* IBM CGA in 640*200 (mode 0x06)               */
    cptr = "CGA high resolution mode";
    break;
  case FG_EGACOLOR:   /* IBM EGA with regular color display (mode 0x0E)  */
    cptr = "EGA with a IBM Color Display";
    break;
  case FG_EGAMONO:    /* IBM EGA with IBM monochrome monitor (mode 0x0F) */
    cptr = "EGA with a Monochrome Display";
    break;
  case FG_EGAECD:     /* IBM EGA with enhanced color display (mode 0x10) */
    cptr = "EGA with an Enhanced Color Display";
    break;
  case FG_VGA11:      /* IBM VGA in 640*480, 1 color                  */
    cptr = "VGA mode 11, 2 color";
    break;
  case FG_VGA12:      /* IBM VGA in 640*480, 16 colors                */
    cptr = "VGA mode 12, 16 colors";
    break;
  default:
    assert (0);
  }

  sprintf (out_str, "Graphics device: %s.", cptr);
  y -= 2 * fg_box_height (fg.charbox);
  center_string(y, out_str);

  y -= 2 * fg_box_height (fg.charbox);
  center_string(y, "FG library version:");

  y -= 2 * fg_box_height (fg.charbox);
  center_string(y, fg.version);

  y -= 2 * fg_box_height (fg.charbox);
  center_string(y, press_any_string);

#if M_UNIX || M_XENIX
  y -= 2 * fg_box_height (fg.charbox);
  center_string(y, "Use 'Ctrl-Alt Fn' to change multiscreen.");
#endif

  fg_flush ();

  bounce_line (fg.displaybox, BOUNCE_INIT);
  while (!key_waiting ())
    bounce_line (fg.displaybox, BOUNCE_NORMAL);

  do
  {
    if (key_input () == ESC)
      terminate ();
  } while (key_waiting ());
}
/**********************************
And now, an attempt to be clever.
**********************************/
void rotate_pyramids (void)
{
  scale_t size;
  int page;
  int phi;
  point_t loc1, loc2;

  page = 0;
  loc1.x = 0;
  loc2.x = fg.displaybox [FG_X2];
  loc1.y = fg.displaybox [FG_Y2]/2;
  loc2.y = fg.displaybox [FG_Y2]/2;
  phi = 0;

  size = 1;
  while (!key_waiting () && loc2.x > 0)
  {
    if (fg.numpages > 1)    /* If more than one page swap back and forth.*/
    {
      page ^= 1;
      fg_setactivepage (page);  /* Swap output pages. */
    }

    clearscreen();
    pyramid (FG_MODE_SET, loc1, size, phi);
    pyramid (FG_MODE_SET, loc2, size, -phi);
#if 0
    pyramid (FG_MODE_SET, loc1, size/4, -2 * phi);
    pyramid (FG_MODE_SET, loc2, size/4, 2 * phi);
#endif

    if (fg.numpages > 1)    /* If more than one page swap back and forth. */
      fg_setdisplaypage (page);
    loc1.x += 2;
    loc2.x -= 2;
    phi += 5;
    if (phi > 360)
      phi -= 360;

    if (loc2.x > fg.displaybox [FG_X2]/2)
      size++;
    else
      size--;
  }

  while (key_waiting ())
  {
    if (key_input () == ESC)
      terminate ();
  }
}

/**********************************
Draw a three dimensional pyramid.  It is upright when phi is PI/2;
**********************************/
void pyramid (int mode,point_t loc, scale_t scale_x, int phi)
{
#define PYR_POINT 0
#define PYR_BASE1 1
#define PYR_BASE2 2
#define PYR_BASE3 3
#define PYR_BASE4 4
#define PYRAMID_PTS 5

  three_d_t c [PYRAMID_PTS];
  scale_t c_phi, s_phi;
  scale_t scale_y;
  int i, line_style;
  fg_line_t line;
  fg_coord_t d, h, w;

  i = PYRAMID_PTS - 1;
  do
  {
    c[i].g_x0 = c[i].g_y0 = c[i].g_z0 = 0;
  } while (--i >= 0);

  w = fg.displaybox [FG_X2];
  d = fg.displaybox [FG_X2];
  h = fg.displaybox [FG_Y2];

  c [PYR_POINT].g_z0 = h;      /* Point is the height in the z direction. */
  c [PYR_BASE1].g_y0 = d/2;    /* Front corner of the base. */
  c [PYR_BASE3].g_y0 = -d/2;   /* Back corner of the base. */
  c [PYR_BASE4].g_x0 = w/2;    /* Right corner of the base. */
  c [PYR_BASE2].g_x0 = -w/2;   /* Left corner of the base. */

  c_phi = int_cos (phi);
  s_phi = int_sin (phi);

  scale_y = fg.pixelx * scale_x;
  scale_y /= fg.pixely;

  /* Do the coordinate transformation. */
  i = PYRAMID_PTS - 1;
  do
  {
    c[i].g_y = loc.y +
               UNSCALE (scale_y * (UNSCALE (c[i].g_y0 * c_phi) +
               UNSCALE (c[i].g_z0 * s_phi)));
    c[i].g_x = loc.x + UNSCALE (scale_x * c[i].g_x0);
    c[i].g_z = UNSCALE (c[i].g_y0 * s_phi - c[i].g_z0 * c_phi);
  } while (--i >= 0);

  next_color_init ();

  fg_drawellipse (FG_HIGHLIGHT, mode, ~0,
    c[PYR_POINT].g_x, c[PYR_POINT].g_y, (int) (UNSCALE (scale_x * h/16)),
    (int) (UNSCALE (scale_y * h/16)), 0, 3600, fg.displaybox);

#if 0   /* Too slow. */
  fg_fillpattern(c[PYR_POINT].g_x,c[PYR_POINT].g_y,next_color(FG_BLACK),
    FG_HIGHLIGHT,FG_FILL_WIDEDOT);
#endif

  fg_make_line (line, c[PYR_BASE1].g_x, c[PYR_BASE1].g_y, c[PYR_BASE2].g_x,
    c[PYR_BASE2].g_y);
  fg_drawlineclip (next_color (FG_BLACK), mode, ~0, FG_LINE_SOLID, line,
    fg.displaybox);

  fg_make_line (line, c[PYR_BASE1].g_x, c[PYR_BASE1].g_y, c[PYR_BASE4].g_x,
    c[PYR_BASE4].g_y);
  fg_drawlineclip (next_color (FG_BLACK), mode, ~0, FG_LINE_SOLID, line,
    fg.displaybox);

  fg_make_line (line, c[PYR_BASE2].g_x, c[PYR_BASE2].g_y, c[PYR_BASE3].g_x,
    c[PYR_BASE3].g_y);
  fg_drawlineclip (next_color (FG_BLACK), mode, ~0, FG_LINE_SOLID, line,
    fg.displaybox);

  fg_make_line (line, c[PYR_BASE3].g_x, c[PYR_BASE3].g_y, c[PYR_BASE4].g_x,
    c[PYR_BASE4].g_y);
  fg_drawlineclip (next_color (FG_BLACK), mode, ~0, FG_LINE_SOLID, line,
    fg.displaybox);

  if (c[PYR_POINT].g_z <= 0 && c[PYR_BASE1].g_z <= 0)
    line_style = FG_LINE_SPARSE_DOTTED;
  else
    line_style = FG_LINE_SOLID;

  fg_make_line (line, c[PYR_POINT].g_x, c[PYR_POINT].g_y, c[PYR_BASE1].g_x,
    c[PYR_BASE1].g_y);
  fg_drawlineclip (next_color (FG_BLACK), mode, ~0, line_style, line,
    fg.displaybox);

  if (c[PYR_POINT].g_z <= 0 && c[PYR_BASE2].g_z <= 0)
    line_style = FG_LINE_SPARSE_DOTTED;
  else
    line_style = FG_LINE_SOLID;

  fg_make_line (line, c[PYR_POINT].g_x, c[PYR_POINT].g_y, c[PYR_BASE2].g_x,
    c[PYR_BASE2].g_y);
  fg_drawlineclip (next_color (FG_BLACK), mode, ~0, line_style, line,
    fg.displaybox);

  if (c[PYR_POINT].g_z <= 0 && c[PYR_BASE3].g_z <= 0)
    line_style = FG_LINE_SPARSE_DOTTED;
  else
    line_style = FG_LINE_SOLID;

  fg_make_line (line, c[PYR_POINT].g_x, c[PYR_POINT].g_y, c[PYR_BASE3].g_x,
    c[PYR_BASE3].g_y);
  fg_drawlineclip (next_color (FG_BLACK), mode, ~0, line_style, line,
    fg.displaybox);

  if (c[PYR_POINT].g_z <= 0 && c[PYR_BASE4].g_z <= 0)
    line_style = FG_LINE_SPARSE_DOTTED;
  else
    line_style = FG_LINE_SOLID;

  fg_make_line (line, c[PYR_POINT].g_x, c[PYR_POINT].g_y, c[PYR_BASE4].g_x,
    c[PYR_BASE4].g_y);
  fg_drawlineclip (next_color (FG_BLACK), mode, ~0, line_style, line,
    fg.displaybox);

  fg_flush ();
}

void fixed_stars (num, clipbox)
int num;
fg_box_t clipbox;
{
  fg_coord_t temp1, temp2;
  int star;

  temp1 = clipbox [FG_X2] - clipbox [FG_X1];
  temp2 = clipbox [FG_Y2] - clipbox [FG_Y1];
  for (star = num; star; star--)
  {
    fg_coord_t x, y;

    x = (rand () % temp1) + clipbox [FG_X1];
    y = (rand () % temp2) + clipbox [FG_Y1];
    fg_drawdot (next_color (FG_BLACK), FG_MODE_SET, ~0, x, y);
  }
}
#define NUM_STARS 700
#define STAR_TIME 5

typedef struct star
 {
   fg_coord_t x, y, speed; fg_color_t color; struct star *next_star;
 } star_t;

void stars (void)
{
  clock_t t0, t1;
  star_t *list_p, *s_p;
  fg_coord_t mid_x, mid_y, temp1, temp2;
  int num_stars;

  mid_x = (fg.displaybox [FG_X2] + 1)/2;
  mid_y = (fg.displaybox [FG_Y2] + 1)/2;

  num_stars = NUM_STARS;
  s_p = list_p = calloc (sizeof (star_t), 1);

  while (1)
  {
    s_p->color = next_color (FG_BLACK);  /* Pick a color, excluding black. */
    s_p->x = mid_x;
    s_p->y = mid_y;
    s_p->speed = rand () % 8 + 1;
    if (--num_stars)
    {
      s_p->next_star = calloc (sizeof (star_t), 1);
      s_p = s_p->next_star;
    }
    else
    {
      s_p->next_star = (star_t *) NULL;
      break;
    }
  }

  t0 = clock ();
  do
  {
    t1 = clock () - t0;

    for (s_p = list_p; s_p != NULL; s_p = s_p->next_star)
    {
      fg_coord_t delta_x, delta_y;

      /* Erase the star. */
      fg_drawdot (s_p->color, FG_MODE_XOR, ~0, s_p->x, s_p->y);

      /* Move the star. */
      delta_x = star_delta (t1 * s_p->speed, s_p->x, mid_x);
      delta_y = star_delta (t1 * s_p->speed, s_p->y, mid_y);

      if (delta_x == 0 && delta_y == 0)
      {    /* -4, -3, -2, -1, 0, 1, 2, 3, 4 */
        delta_x = (4 - rand () % 9) * s_p->speed;
        delta_y = (4 - rand () % 9) * s_p->speed;
      }

      s_p->x += delta_x;
      s_p->y += delta_y;

      /* Is it time to kill it? */
      if (s_p->x < inner_box [FG_X1] || s_p->x > inner_box [FG_X2] ||
          s_p->y < inner_box [FG_Y1] || s_p->y > inner_box [FG_Y2])
      {
        /* Kill the star (actually just move it to the center). */
        s_p->x = mid_x;
        s_p->y = mid_y;
      }
      else  /* Draw the star again. */
        fg_drawdot (s_p->color, FG_MODE_XOR, ~0, s_p->x, s_p->y);
    }
   /* Do this for a few seconds. */
  } while (!key_waiting () && t1 >= 0 && t1 < STAR_TIME * CLK_TCK);

  /* Free up the memory and give a "star burst". */
  do
  {
    fg_line_t line;

    s_p = list_p;
    list_p = s_p->next_star;

    line [FG_X1] = s_p->x;
    line [FG_Y1] = s_p->y;
    line [FG_X2] = s_p->x + star_delta (10 * t1, s_p->x, mid_x);
    line [FG_Y2] = s_p->y + star_delta (10 * t1, s_p->y, mid_y);

    fg_drawlineclip (s_p->color, FG_MODE_SET, ~0, FG_LINE_SOLID, line,
      inner_box);

    free (s_p);
  } while (list_p != NULL);

  while (key_waiting ())
  {
    if (key_input () == ESC)
      terminate ();
  }
}
/*******************************
Calculate the delta change in position for this coordinate.
*/
fg_coord_t star_delta (speed, p, mid)
long int speed;
fg_coord_t p, mid;
{
  return (speed * (p - mid))/2000;
}

static int next_color_index = 0;
int next_colormap[32] =
   {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};

/*******************************
Initialize the next color function.
*/
void next_color_init (void)
{
  if (next_color_index == 0)
    next_color_setcolormap ();
  else
    next_color_index = 0;
}
/****************************
Initialize the list of availiable colors.
****************************/
void next_color_setcolormap (void)
{ int i;

  i = 0;
  next_colormap[i++] = FG_BLACK;
  next_colormap[i++] = FG_BLUE;
  next_colormap[i++] = FG_GREEN;
  next_colormap[i++] = FG_CYAN;
/* FG_BLUE_GREEN is the same as CYAN. */
/*  next_colormap[i++] = FG_BLUE_GREEN; */
  next_colormap[i++] = FG_RED;
  next_colormap[i++] = FG_LIGHT_WHITE;
  next_colormap[i++] = FG_MAGENTA;
/* Purple is the same as magenta. */
/*  next_colormap[i++] = FG_PURPLE; */
  next_colormap[i++] = FG_YELLOW;
  next_colormap[i++] = FG_WHITE;

  next_colormap[i++] = FG_GRAY;
  next_colormap[i++] = FG_LIGHT_BLUE;
  next_colormap[i++] = FG_LIGHT_RED;
  next_colormap[i++] = FG_LIGHT_GREEN;
  next_colormap[i++] = FG_LIGHT_CYAN;
/* Light BLUE_GREEN is the same as LIGHT_CYAN. */
/*  next_colormap[i++] = FG_LIGHT_BLUE_GREEN; */
  next_colormap[i++] = FG_LIGHT_MAGENTA;
  next_colormap[i++] = FG_BROWN;

/* normally an intense white  */
/* Same as LIGHT_WHITE */
/*  next_colormap[i++] = FG_HIGHLIGHT; */

/* blinking white   */
  next_colormap[i++] = FG_BLINK;
}
/*******************************
Terminiate the next color package.
*/
void next_color_term (void)
{
  return;
}

/*******************************
 * Pick a color excluding this one color.
 */

fg_color_t next_color (fg_color_t ex_color)
{
  do
  {
    next_color_index++;
    next_color_index %= sizeof(next_colormap)/sizeof(next_colormap[0]);
  } while (next_colormap[next_color_index] == -1 ||
    next_colormap [next_color_index] == ex_color);

  return next_colormap [next_color_index];
}

/**************************************
Bounce a line off the edges of the screen.
**************************************/
void bounce_line (clipbox, erase)
fg_box_t clipbox;
int erase;
{
  static fg_line_t old_line, new_line;
  static fg_color_t old_color, new_color;
  static fg_coord_t vel_x1 = 10, vel_x2 = 15, vel_y1 = 10, vel_y2 = 16;

  if (erase == BOUNCE_INIT)
  {
    new_line[FG_X1] = new_line[FG_Y1] = new_line[FG_X2] = new_line[FG_Y2] = 0;
    new_color = FG_BLACK;
  }

  /* Save the last line. */
  fg_line_cpy (old_line, new_line);
  old_color = new_color;

  /* Create the new line. */
  new_color = next_color (FG_BLACK);
  new_line[FG_X1] += vel_x1; new_line[FG_X2] += vel_x2;
  new_line[FG_Y1] += vel_y1; new_line[FG_Y2] += vel_y2;

  if ((unsigned)new_line[FG_X1] > clipbox[FG_X2])
  {
    fg_coord_t temp;

    new_line[FG_X1] = new_line[FG_X1] > 0? clipbox[FG_X2]: 0;

    vel_x1 = (vel_x1 > 0? -1: 1) * (rand () % 32);

    temp = 32 + (vel_x1 > 0? -vel_x1: vel_x1);
    vel_y1 = vel_y1 > 0? temp: -temp;
  }

  if ((unsigned)new_line[FG_Y1] > clipbox[FG_Y2])
  {
    fg_coord_t temp;

    new_line[FG_Y1] = new_line[FG_Y1] > 0? clipbox[FG_Y2]: 0;

    vel_y1 = (vel_y1 > 0? -1: 1) * (rand () % 32);

    temp = 32 + (vel_y1 > 0? -vel_y1: vel_y1);
    vel_x1 = vel_x1 > 0? temp: -temp;
  }

  if ((unsigned)new_line[FG_X2] > clipbox[FG_X2])
  {
    fg_coord_t temp;

    new_line[FG_X2] = new_line[FG_X2] > 0? clipbox[FG_X2]: 0;

    vel_x2 = (vel_x2 > 0? -1: 1) * (rand () % 32);

    temp = 32 + (vel_x2 > 0? -vel_x2: vel_x2);
    vel_y2 = vel_y2 > 0? temp: -temp;
  }

  if ((unsigned) new_line[FG_Y2] > clipbox [FG_Y2])
  {
    fg_coord_t temp;

    new_line[FG_Y2] = new_line[FG_Y2] > 0? clipbox[FG_Y2]: 0;

    vel_y2 = (vel_y2 > 0? -1: 1) * (rand () % 32);

    temp = 32 + (vel_y2 > 0? -vel_y2: vel_y2);
    vel_x2 = vel_x2 > 0? temp: -temp;
  }

  /* Erase the old line. */
  if (erase != BOUNCE_INIT &&
    (old_line[FG_X1] != old_line[FG_X2] || old_line[FG_Y1] != old_line[FG_Y2]))
  {
    fg_drawline (old_color, FG_MODE_XOR, ~0, FG_LINE_SOLID, old_line);
  }

  /* Draw the new line. */
  if (erase != BOUNCE_ERASE &&
     (new_line[FG_X1] != new_line[FG_X2] || new_line[FG_Y1] != new_line[FG_Y2]))
  {
    fg_drawline (new_color, FG_MODE_XOR, ~0, FG_LINE_SOLID, new_line);
  }
  fg_flush ();
}

/****************************
Terminate gracefully.
****************************/
void terminate (void)
{
  fg_term ();
  exit (0);
}
/****************************
Draw a fancy border around the screen to make the screen look like a view port 
from a "space ship".
****************************/
void border (void)
{
  unsigned int radius;
  int x, y;

  fg_drawbox (FG_HIGHLIGHT, FG_MODE_SET, ~0, FG_LINE_SOLID, fg.displaybox,
    fg.displaybox);

  radius = (fg.displaybox[FG_X2] + 1)/128;

  fg_box_cpy (inner_box, fg.displaybox);
  inner_box [FG_X1] += radius;
  inner_box [FG_X2] -= radius;
  inner_box [FG_Y1] += radius;
  inner_box [FG_Y2] -= radius;

  y = fg.displaybox [FG_Y2];

  for (x = fg.displaybox [FG_X2]; x >= 0; x -= 2 * radius)
  {
    fg_drawarc (next_color (FG_BLACK), FG_MODE_SET, ~0, x, 0, radius, 0, 1800,
       fg.displaybox);
    fg_drawarc(next_color(FG_BLACK), FG_MODE_SET, ~0, x, y, radius, 1800, 3600,
       fg.displaybox);
  }

  x = fg.displaybox [FG_X2];

  for (y = fg.displaybox [FG_Y2]; y >= 0; y -= 2 * radius)
  {
    fg_drawarc (next_color(FG_BLACK), FG_MODE_SET, ~0, 0, y, radius, 2700, 900,
      fg.displaybox);
    fg_drawarc (next_color(FG_BLACK), FG_MODE_SET, ~0, x, y, radius, 900, 2700,
      fg.displaybox);
  }
  fg_drawbox (FG_HIGHLIGHT, FG_MODE_SET, ~0, FG_LINE_SOLID, inner_box,
     fg.displaybox);
}
/*********************************
A black box that smoothly explodes.
*********************************/
void explode_box (fg_const_pbox_t box)
{
  int step, x_mod, y_mod;
  fg_box_t b1;

  assert (fg_box_width (box));
  assert (fg_box_height (box));

  b1 [FG_X2] = b1 [FG_X1] = fg_coord_midpoint(box [FG_X1], box [FG_X2]);
  b1 [FG_Y2] = b1 [FG_Y1] = fg_coord_midpoint(box [FG_Y1], box [FG_Y2]);

  /* Increment both x and y such that both complete at about the same time. */
  x_mod = (3 * fg_box_height (box))/fg_box_width (box);
  if (x_mod == 0)
    x_mod = 1;

  y_mod = (3 * fg_box_width (box))/fg_box_height (box);
  if (y_mod == 0)
    y_mod = 1;

  step = 0;

  do
  {
    fg_drawbox (FG_BLACK, FG_MODE_SET, ~0, FG_LINE_SOLID, b1, fg.displaybox);

    if (++step % y_mod == 0)
    {
      if (b1 [FG_Y1] > box [FG_Y1])
        b1 [FG_Y1]--;
      if (b1 [FG_Y2] < box [FG_Y2])
        b1 [FG_Y2]++;
    }

    if (step % x_mod == 0)
    {
      if (b1 [FG_X1] > box [FG_X1])
        b1 [FG_X1]--;

      if (b1 [FG_X2] < box [FG_X2])
        b1 [FG_X2]++;
    }
  } while (b1 [FG_Y1] != box [FG_Y1] || b1 [FG_Y2] != box [FG_Y2] ||
       b1 [FG_X1] != box [FG_X1] || b1 [FG_X2] != box [FG_X2]);

  fg_drawbox (FG_BLACK, FG_MODE_SET, ~0, FG_LINE_SOLID, b1, fg.displaybox);
}

/*********************************
A look-up table for cosine and sine.
*********************************/
scale_t cos_table [19] = 
  { SCALE (1),      /* 0 degrees. */
    SCALE (.99619), /* 5 degrees. */
    SCALE (.98481), /* 10 degrees. */
    SCALE (.96592), /* 15 degrees. */
    SCALE (.93969), /* 20 degrees. */
    SCALE (.90631), /* 25 degrees. */
    SCALE (.86603), /* 30 degrees. */
    SCALE (.81915), /* 35 degrees. */
    SCALE (.76604), /* 40 degrees. */
    SCALE (.70711), /* 45 degrees. */
    SCALE (.64278), /* 50 degrees. */
    SCALE (.57358), /* 55 degrees. */
    SCALE (.50000), /* 60 degrees. */
    SCALE (.42262), /* 65 degrees. */
    SCALE (.34202), /* 70 degrees. */
    SCALE (.25882), /* 75 degrees. */
    SCALE (.17365), /* 80 degrees. */
    SCALE (.08716), /* 85 degrees. */
    SCALE (0)       /* 90 degrees. */
  };
/*********************************
A look-up table version of cosine.
*********************************/
scale_t int_cos (angle)
int angle;
{
  int quadrant, remainder;
  scale_t answer;

  if (angle < 0)
    angle = - angle;

  angle += 2;   /* Round to the nearest 5 degrees. */
  remainder = (angle % 90)/5;
  quadrant = (angle % 360);

  if (quadrant < 90)
  {
    answer = cos_table [remainder];
  } else
  if (quadrant < 180)
  {
    answer = -cos_table [18 - remainder];
  } else
  if (quadrant < 270)
  {
    answer = -cos_table [remainder];
  } else
  if (quadrant < 360)
    answer = cos_table [18 - remainder];
  else
    assert (0);

  return answer;
}
/*********************************
A look-up table version of sine.
*********************************/
scale_t int_sin (angle)
int angle;
{
  return int_cos (angle - 90);
}
/*********************************
Find the arc tangent.
*********************************/
int int_arctan (slope)
scale_t slope;
{
  int angle;

  angle = 0;
  do
  {
    scale_t cos_answer, test_slope;

    cos_answer = int_cos (angle);

    if (cos_answer == 0)
      continue;

    test_slope = SCALE (int_sin (angle));
    test_slope /= cos_answer;

    if (test_slope > slope &&
       (test_slope >= 0 && slope >= 0 || test_slope < 0 && slope < 0))
    {
      break;
    }

  } while ((angle += 5) < 180);

  return angle;
}
void _FAR _cdecl fg_assert (const char _FAR *e, const char _FAR *f, unsigned int line)
{
  fg_term ();
  printf ("FG ASSERT: '%s', %s line %u", (char *)e, (char *)f, line);
  exit (1);
}

