
/*      file    turtlegr.c                              *
 *      Copyright (C) 1992      sjm@ee.tut.fi           *
 *                              jtl@cc.tut.fi           *
 *                                                      *
 *      Turtlegraphics primitives for the               *
 *      SCM interpreter by Aubrey Jaffer                *
 *                                                      *
 *      Last modification: 13.10.1992                   *
 *                                                      *
 *      Versions:                                       *
 *      12.3.1992       The first version.              *
 *      13.3.1992       Added the possibility to pass   *
 *                      floating point args.            *
 *      15.3.1992       Graphics cards other than EGA   *
 *                      are now supported.              *
 *      9.4.1992        The internal representation     *
 *                      of X & Y is now float.          *
 *      13.10.1992      Added X11 support.              *
 *                      A major rewrite of certain      *
 *                      parts.                          *
 *                      Put -DX11 -DFLOATS to CFLAGS    *
 *                      in Makefile to get it.          *
 *                                                      *
 *      REMEMBER to define INITS=init_turtlegr()        *
 *      in the Makefile.                                *
 *                                                      */

/*                                                                      *
 *       This code tries to compromise between two very different       *
 *      systems: MSDOS and UNIX with the X11 windowing system.          *
 *      The MSDOS version was build first and it really shows.  :)      *
 *      The X port is a partial rewrite of the old MSDOS stuff          *
 *      and plays around with #ifdef's a lot.  The result is,           *
 *      eventually, a C source which is expected to compile             *
 *      under both MSDOS and UNIX (X11).                                *
 *       The X code handles colors emulating CGA palette. It tries      *
 *      to act sensibly even on a monochrome screen and when the        *
 *      color palette is full.                                          *
 *       X event handling is implemented with polling whenever          *
 *      appropriate. This is not The Right Way to do it in X, but       *
 *      it was easiest to adopt in this case.                           *
 *       Another solution would have been to make the X graphics        *
 *      a separate process, but I didn't want to because I wanted       *
 *      to keep it simple. I can't tell how good an example of porting  *
 *      MSDOS software to X this is, but it works.                      *
 *                                                                      *
 *       This has been tested with SunOs 4.1.2 with X11R5, Linux 0.98.1 *
 *      with Xfree86 1.1 (X11R5 port) and in MSDOS with BC 3.1.         *
 *      Because the code uses only the basic Xlib calls, it should      *
 *      compile without problems under _any_ UNIX with X11R4 or newer.  *
 *                                                                      *
 *      Please send bugreports to sjm@ee.tut.fi.                        *
 *      I'm especially interested in hearing about ports to other       *
 *      platforms than those tested by me.                              *
 *                                                                      *
 *      - sjm                                                           *
 *                                                                      */


/****************************************************/
/*****  GENERIC includes & defines              *****/
/****************************************************/
#include        "scm.h"         /* includes scmfig.h as well    */
#include        "patchlvl.h"    /* Guess...                     */
#include        <math.h>        /* sin(), cos(), fmod()         */
#include	<stdlib.h>	/* atexit()			*/

/****************************************************/
/*****  X11 specific includes & defines         *****/
/****************************************************/
#ifdef X11

/* Xlib include files */
# include <X11/Xlib.h>
# include <X11/Xutil.h>
# include <X11/Xatom.h>
# include <stdio.h>

# include "turtle"
# define BITMAPDEPTH 1

# define PROGNAME        "scm"
# define CLASSNAME       "Scm"
# define WINDOWNAME      "TurtleSCM graphics window"
# define ICONNAME        "TurtleSCM"

# define GR_MAX_XSIZE    1024
# define GR_MAX_YSIZE    1024
# define GR_DEF_XSIZE    640
# define GR_DEF_YSIZE    480
# define GR_MIN_XSIZE    64
# define GR_MIN_YSIZE    64

/* Fake CGA colormap with X - yuk!                              */
# define GR_COLORS       16              /* CGA/EGA counterpart  */
# define GR_COLOR00      "black"         /* black                */
# define GR_COLOR01      "blue2"         /* blue                 */
# define GR_COLOR02      "green2"        /* green                */
# define GR_COLOR03      "cyan2"         /* cyan                 */
# define GR_COLOR04      "red3"          /* red                  */
# define GR_COLOR05      "magenta2"      /* magenta              */
# define GR_COLOR06      "yellow2"       /* brown                */
# define GR_COLOR07      "light gray"    /* white                */
# define GR_COLOR08      "gray"          /* gray                 */
# define GR_COLOR09      "blue1"         /* light blue           */
# define GR_COLOR10      "green1"        /* light green          */
# define GR_COLOR11      "cyan1"         /* light cyan           */
# define GR_COLOR12      "red1"          /* light red            */
# define GR_COLOR13      "magenta1"      /* light magenta        */
# define GR_COLOR14      "yellow1"       /* yellow               */
# define GR_COLOR15      "white"         /* bright white         */

# ifdef  __STDC__
static void     gr_events( int );
# else
static void     gr_events();
# endif

#else
/****************************************************/
/*****  PC specific includes & defines          *****/
/****************************************************/
# include        <graphics.h>
# include        <stdlib.h>      /* for getenv()                 */
# include        <stdio.h>       /* for fputs()                  */
# define         BGIDIR_ENVSTRING        "BGIDIR"
#endif

/********************************************/
/***** GENERIC code, declarations       *****/
/********************************************/
#define         SIN( x )                \
                sin( ((x)/180.0) * M_PI )
#define         COS( x )                \
                cos( ((x)/180.0) * M_PI )

static  int             gr_graphicsavail = 0;
static  int             gr_grmode_on = 0;
static  float           gr_dir = 0.0;
static  int             gr_max_x=0, gr_max_y=0, gr_max_color=0;
static  float           gr_x=0.0, gr_y=0.0;
static  int             gr_color = 0;

static  char    s_gr_draw[]             = "draw";
static  char    s_gr_move[]             = "move";
static  char    s_gr_setcolor[]         = "set-color!";
static  char    s_gr_turnright[]        = "turn-right";
static  char    s_gr_turnleft[]         = "turn-left";
static  char    s_gr_turnto[]           = "turn-to!";

static  char    s_gr_getdot[]           = "get-dot";
static  char    s_gr_drawTo[]           = "draw-to!";
static  char    s_gr_drawto[]           = "draw-to";
static  char    s_gr_moveTo[]           = "move-to!";

static  char    s_gr_setdot[]           = "set-dot!";
static  char    s_gr_validXYC[]         = "valid-xyc?";

#ifdef __GNUC__
inline
#else
static
#endif
int      valid_XYC( x, y, color )
int     x, y, color;
{
#ifdef X11
        /* Check for changed window size */
        gr_events(0);
#endif
        if( (x <= gr_max_x) && (y <= gr_max_y) && (color <= gr_max_color)
            && (x >= 0) && (y >= 0) && (color >= 0) )
                return( 1 );
        else
                return( 0 );
} /* valid_XYC() */


/********************************************************************/
/*****  X11 specific variable and function declarations         *****/
/********************************************************************/
#ifdef X11
static Display          *gr_display;            /* The X display        */
static int              gr_screen;              /* The X screen number  */
static Window           gr_win;                 /* The drawable Window  */
static GC               gr_gc;                  /* Graphics Context     */
static unsigned long    gr_colortbl[GR_COLORS]; /* Color table          */
static XEvent           gr_event;               /* Event structure      */

/* These are needed for XSetWMProperties */
static char     *gr_windowname  = WINDOWNAME;
static char     *gr_iconname    = ICONNAME;
static char     gr_progname[]   = PROGNAME;
static char     gr_classname[]  = CLASSNAME;
static int      gr_argc         = 1;
static char     *gr_argv[]      = { gr_progname, NULL };

static void     gr_eventhandler( event )
XEvent          event;
{
          switch( event.type ) {

          case ConfigureNotify:
# ifdef TESTING
            fputs( "Received ConfigureNotify event\n", stderr );
# endif
            gr_max_x = event.xconfigure.width - 1;
            gr_max_y = event.xconfigure.height - 1;
            break;

          case MapNotify:
# ifdef TESTING
            fputs( "Received MapNotify event\n", stderr );
# endif
            break;

          case DestroyNotify:
# ifdef TESTING
            fputs( "Received DestroyNotify event\n", stderr );
# endif
            break;

          case UnmapNotify:
# ifdef TESTING
            fputs( "Received UnmapNotify event\n", stderr );
# endif
            break;

          case Expose:
# ifdef TESTING
            fputs( "Received Expose event\n", stderr );
# endif
            if( event.xexpose.count != 0 )
              break;
            break;

          case ClientMessage:
# ifdef TESTING
            fputs( "Received ClientMessage event\n", stderr );
# endif
            break;

          default:
            /* Throw away any unknown events */
            break;

          } /* switch */
}

static void     gr_events( expected )
int             expected;
{
int             i;

        /* Get at least 'expected' events */
        for( i = 0; i < expected; ++i ) {
          XNextEvent( gr_display, &gr_event );
          gr_eventhandler( gr_event );
        }
        /* Handle all remaining events if there are any */
        /* XPending will call XFlush() if it doesn't find events at once */
        while( XPending(gr_display) ) {
          XNextEvent( gr_display, &gr_event );
          gr_eventhandler( gr_event );
        } /* while */
} /* gr_events() */

static void     gr_typedevent( type )
int             type;
{
        do {
          XNextEvent( gr_display, &gr_event );
          gr_eventhandler( gr_event );
        } while( gr_event.type != type );
        /* Handle all remaining events if there are any */
        /* XPending will call XFlush() if it doesn't find events at once */
        while( XPending(gr_display) ) {
          XNextEvent( gr_display, &gr_event );
          gr_eventhandler( gr_event );
        } /* while */
}


/********************************************************************/
/*****  PC specific variable and function declarations          *****/
/********************************************************************/
#else

static  int             gr_max_display_mode;
static  int             gr_drivernum;

#endif


/********************************************************************/
/********************************************************************/
/***    User callable SCM routines begin here           ***
 ***                                                    ***
 ***                                                    ***/


SCM     gr_helpgr()
{
        fputs( "\
Ret   Name               nargs    args        returns\n\
---------------------------------------------------------\n\
B  graphics-avail?         0       -          #t if graphics available\n\
B  graphics-mode!          0       -          #f if no graphics\n\
B  text-mode!              0       -          #t on success\n\
B  clear-graphics!         0       -          #f if not in graphics mode\n\
i  max-x                   0       -          maximum value of x\n\
i  max-y                   0       -          maximum value of y\n\
i  max-color               0       -          maximum value of color\n\
B  valid-xyc?              3       x y color  #t if valid\n\
B  set-dot!                3       x y color  #t on success\n\
i  get-dot                 2       x y        color of the dot in (x,y)\n\
                                              or #f if (x,y) not legal\n\
\n\
NOTE: Origin (0,0) is in the upper left corner.\n\n\
", stdout );
        return  BOOL_T;
} /* gr_helpgr() */


SCM     gr_helpturtlegr()
{
        fputs( "\
Ret   Name               nargs    args        returns\n\
---------------------------------------------------------\n\
B  goto-home!              0       -          #f if not in graphics mode\n\
B  goto-center!            0       -          #f if not in graphics mode\n\
B  goto-nw!                0       -          #f if not in graphics mode\n\
B  goto-ne!                0       -          #f if not in graphics mode\n\
B  goto-sw!                0       -          #f if not in graphics mode\n\
B  goto-se!                0       -          #f if not in graphics mode\n\
B  draw                    1       length     #t if target within drawing area\n\
B  draw-to                 2       x y        #t if (x,y) within drawing area\n\
B  draw-to!                2       x y        #t if (x,y) within drawing area\n\
B  move                    1       length     #t if target within drawing area\n\
B  move-to!                2       x y        #t if (x,y) within drawing area\n\
i  where-x                 0       -          current x-coordinate\n\
i  where-y                 0       -          current y-coordinate\n\
i  turn-right              1       angle      drawing direction in degrees\n\
i  turn-left               1       angle      drawing direction in degrees\n\
i  turn-to!                1       angle      drawing direction in degrees\n\
i  what-direction          0       -          drawing direction in degrees\n\
B  set-color!              1       color      #t if color valid\n\
i  what-color              0       -          current drawing color\n\n\
", stdout );
        return  BOOL_T;
} /* gr_helpturtlegr() */


SCM     gr_available()
{
        if( gr_graphicsavail )
                return  BOOL_T;
        else
                return  BOOL_F;
} /* gr_available() */


SCM     gr_maxx()
{
        if( !gr_grmode_on )
                return  BOOL_F;
#ifdef X11
        /* Check for changed window size */
        gr_events(0);
#endif
        return  MAKINUM( (long)gr_max_x );
} /* gr_maxx() */


SCM     gr_maxy()
{
        if( !gr_grmode_on )
                return  BOOL_F;
#ifdef X11
        /* Check for changed window size */
        gr_events(0);
#endif
        return  MAKINUM( (long)gr_max_y );
} /* gr_maxy() */

SCM     gr_maxc()
{
        if( !gr_grmode_on )
                return  BOOL_F;
        return  MAKINUM( (long)gr_max_color );
} /* gr_maxc() */


SCM     gr_validXYC( x, y, c )
SCM     x, y, c;
{
int     xi, yi, ci;

        ASRTER( NUMBERP(x), x, ARG1, s_gr_validXYC );
        ASRTER( NUMBERP(y), y, ARG2, s_gr_validXYC );
        ASRTER( NUMBERP(c), c, ARG3, s_gr_validXYC );
        if( !gr_grmode_on )
                return  BOOL_F;

        if( INUMP(x) )
                xi = (int)(INUM(x));
        else
                xi = (int)(REALPART(x));

        if( INUMP(y) )
                yi = (int)(INUM(y));
        else
                yi = (int)(REALPART(y));

        if( INUMP(c) )
                ci = (int)(INUM(c));
        else
                ci = (int)(REALPART(c));

/* valid_XYC() calls gr_events() */

        if( valid_XYC( xi, yi, ci ) )
                return  BOOL_T;
        else
                return  BOOL_F;
} /* gr_validXYC() */


SCM     gr_grmode()
{
        if( !gr_graphicsavail )
                return  BOOL_F;
#ifdef  X11
        /* bwuah... but it works :) */
        if( !gr_grmode_on ) {
            XMapWindow( gr_display, gr_win );
            gr_typedevent( MapNotify );
        }
#else   /* PC version */
        setgraphmode( gr_max_display_mode );
#endif
        gr_grmode_on = 1;
        return  BOOL_T;
} /* gr_grmode() */

SCM     gr_txtmode()
{
        if( !gr_graphicsavail )
                return  BOOL_F;
#ifdef  X11
        /* bwuah... but it works :) */
        if( gr_grmode_on ) {
            XUnmapWindow( gr_display, gr_win );
            gr_typedevent( UnmapNotify );
        }
#else   /* PC version */
        restorecrtmode();
#endif
        gr_grmode_on = 0;
        return  BOOL_T;
} /* gr_txtmode() */


SCM     gr_cleargraph()
{
        if( !gr_grmode_on )
                return  BOOL_F;
#ifdef  X11
        XClearWindow( gr_display, gr_win );
        gr_events(0);
#else   /* PC version */
        cleardevice();
#endif
        return  BOOL_T;
} /* gr_cleargraph() */


SCM     gr_setdot( x, y, c )
SCM     x, y, c;
{
int     xi, yi, ci;

        ASRTER( NUMBERP(x), x, ARG1, s_gr_setdot );
        ASRTER( NUMBERP(y), y, ARG2, s_gr_setdot );
        ASRTER( NUMBERP(c), c, ARG3, s_gr_setdot );
        if( !gr_grmode_on )
                return  BOOL_F;

        if( INUMP(x) )
                xi = (int)(INUM(x));
        else
                xi = (int)(REALPART(x));

        if( INUMP(y) )
                yi = (int)(INUM(y));
        else
                yi = (int)(REALPART(y));

        if( INUMP(c) )
                ci = (int)(INUM(c));
        else
                ci = (int)(REALPART(c));
#ifdef TESTING
        fprintf( stderr, "set-dot! called (%d,%d,%d)\n", xi, yi, ci );
#endif
        if( !valid_XYC( xi, yi, ci ) )
                return  BOOL_F;
#ifdef  X11
        /* Set the drawing color */
        XSetForeground( gr_display, gr_gc, gr_colortbl[ ci ] );
        XDrawPoint( gr_display, gr_win, gr_gc, xi, yi );
        /* Restore the drawing color */
        XSetForeground( gr_display, gr_gc, gr_colortbl[ gr_color ] );
        gr_events(0);
#else   /* PC version */
        putpixel( xi, yi, ci );
#endif
        return  BOOL_T;
} /* gr_setdot() */


SCM     gr_getdot( x, y )
SCM     x, y;
{
int                     xi, yi;
#ifdef  X11
XImage                  *xim;
XWindowAttributes       wattr;
unsigned long           dot;
int                     i;
#endif
        ASRTER( NUMBERP(x), x, ARG1, s_gr_getdot );
        ASRTER( NUMBERP(y), y, ARG2, s_gr_getdot );
        if( !gr_grmode_on )
                return  BOOL_F;
        if( INUMP(x) )
                xi = (int)(INUM(x));
        else
                xi = (int)(REALPART(x));

        if( INUMP(y) )
                yi = (int)(INUM(y));
        else
                yi = (int)(REALPART(y));
#ifdef TESTING
        fprintf( stderr, "get-dot called (%d,%d)\n", xi, yi );
#endif
        if( !valid_XYC( xi, yi, 0 ) )
                return  BOOL_F;
#ifdef  X11
        /* Now, this IS ugly. But it's there if you need it.            */

        /* Have to make sure that the window is mapped.  Tough...       */
        XGetWindowAttributes( gr_display, gr_win, &wattr );
        if( wattr.map_state == IsUnmapped ) {
            XMapWindow( gr_display, gr_win );
            gr_typedevent( MapNotify );
        }
        /* I KNOW this sucks.                                           */
        xim = XGetImage( gr_display, gr_win, xi, yi, 1, 1, AllPlanes, XYPixmap );
        dot = XGetPixel( xim, 0, 0 );
        for( i = 0; i < GR_COLORS; ++i ) {
            if( gr_colortbl[i] == dot )
                return MAKINUM( (long)i );
        }
        /* This should never happen. There's garbage in the window!     */
        fprintf( stderr, "%s: %s: Got an illegal pixel value %lu. \
Is there garbage?\n", gr_progname, s_gr_getdot, dot );
        return BOOL_F;
#else   /* PC version */
        return MAKINUM( (long)getpixel( xi, yi ) );
#endif
} /* gr_getdot() */

SCM     gr_draw( S )
SCM     S;
{
float   xf, yf;
float   sf;
int     ok;

        ASRTER( NUMBERP(S), S, ARG1, s_gr_draw );
        if( !gr_grmode_on )
                return  BOOL_F;
        if( INUMP(S) )
                sf = (float)(INUM(S));
        else
                sf = REALPART(S);
#ifdef TESTING
        fprintf( stderr, "draw called (%f)\n", sf );
#endif
        ok = 1;
        xf = gr_x + ( COS( gr_dir ) * sf );
        yf = gr_y + ( SIN( gr_dir ) * sf );
        if( (int)xf > gr_max_x ) {
                xf = (float)gr_max_x;
                ok = 0;
        }
        else if( xf < 0.0 ) {
                xf = 0.0;
                ok = 0;
        }
        if( (int)yf > gr_max_y ) {
                yf = (float)gr_max_y;
                ok = 0;
        }
        else if( yf < 0.0 ) {
                yf = 0.0;
                ok = 0;
        }
#ifdef  X11
        XDrawLine( gr_display, gr_win, gr_gc,
                   (int)gr_x, (int)gr_y,
                   (int)xf, (int)yf );
        gr_events(0);
#else   /* PC version */
        line( (int)gr_x, (int)gr_y, (int)xf, (int)yf );
#endif
        gr_x = xf;
        gr_y = yf;
        if( ok )
                return  BOOL_T;
        else
                return  BOOL_F;
} /* gr_draw() */


SCM     gr_move( S )
SCM     S;
{
float   xf, yf;
float   sf;
int     ok;

        ASRTER( NUMBERP(S), S, ARG1, s_gr_move );
        if( !gr_grmode_on )
                return  BOOL_F;
        if( INUMP(S) )
                sf = (float)(INUM(S));
        else
                sf = REALPART(S);
#ifdef TESTING
        fprintf( stderr, "move called (%f)\n", sf );
#endif
        ok = 1;
        xf = gr_x + ( COS( gr_dir ) * sf );
        yf = gr_y + ( SIN( gr_dir ) * sf );

        if( (int)xf > gr_max_x ) {
                xf = (float)gr_max_x;
                ok = 0;
        }
        else if( xf < 0.0 ) {
                xf = 0.0;
                ok = 0;
        }
        if( (int)yf > gr_max_y ) {
                yf = (float)gr_max_y;
                ok = 0;
        }
        else if( yf < 0.0 ) {
                yf = 0.0;
                ok = 0;
        }
        gr_x = xf;
        gr_y = yf;
        if( ok )
                return  BOOL_T;
        else
                return  BOOL_F;
} /* gr_move() */


SCM     gr_drawto( x, y )
SCM     x, y;
{
int     xi, yi;

        ASRTER( NUMBERP(x), x, ARG1, s_gr_drawto );
        ASRTER( NUMBERP(y), y, ARG2, s_gr_drawto );
        if( !gr_grmode_on )
                return  BOOL_F;
        if( INUMP(x) )
                xi = (int)(INUM(x));
        else
                xi = (int)(REALPART(x));

        if( INUMP(y) )
                yi = (int)(INUM(y));
        else
                yi = (int)(REALPART(y));
#ifdef TESTING
        fprintf( stderr, "draw-to called (%d,%d)\n", xi, yi );
#endif
        if( !valid_XYC( xi, yi, 0 ) )
                return  BOOL_F;
#ifdef  X11
        XDrawLine( gr_display, gr_win, gr_gc,
                   (int)gr_x, (int)gr_y, xi, yi );
        gr_events(0);
#else   /* PC version */
        line( (int)gr_x, (int)gr_y, xi, yi );
#endif
        return  BOOL_T;
} /* gr_drawto() */


SCM     gr_drawTo( x, y )
SCM     x, y;
{
float   xf, yf;

        ASRTER( NUMBERP(x), x, ARG1, s_gr_drawTo );
        ASRTER( NUMBERP(y), y, ARG2, s_gr_drawTo );
        if( !gr_grmode_on )
                return  BOOL_F;
        if( INUMP(x) )
                xf = (float)(INUM(x));
        else
                xf = (REALPART(x));

        if( INUMP(y) )
                yf = (float)(INUM(y));
        else
                yf = (REALPART(y));
#ifdef TESTING
        fprintf( stderr, "draw-to! called (%d,%d)\n", (int)xf, (int)yf );
#endif
        if( !valid_XYC( (int)xf, (int)yf, 0 ) )
                return  BOOL_F;
#ifdef  X11
        XDrawLine( gr_display, gr_win, gr_gc,
                   (int)gr_x, (int)gr_y,
                   (int)xf, (int)yf );
        gr_events(0);
#else   /* PC version */
        line( (int)gr_x, (int)gr_y, (int)xf, (int)yf );
#endif
        gr_x = xf;
        gr_y = yf;
        return  BOOL_T;
} /* gr_drawTo() */


SCM     gr_moveTo( x, y )
SCM     x, y;
{
float   xf, yf;

        ASRTER( NUMBERP(x), x, ARG1, s_gr_moveTo );
        ASRTER( NUMBERP(y), y, ARG2, s_gr_moveTo );
        if( !gr_grmode_on )
                return  BOOL_F;
        if( INUMP(x) )
                xf = (float)(INUM(x));
        else
                xf = (REALPART(x));

        if( INUMP(y) )
                yf = (float)(INUM(y));
        else
                yf = (REALPART(y));
#ifdef TESTING
        fprintf( stderr, "move-to! called (%d,%d)\n", (int)xf, (int)yf );
#endif
        if( !valid_XYC( (int)xf, (int)yf, 0 ) )
                return  BOOL_F;
        gr_x = xf;
        gr_y = yf;
        return  BOOL_T;
} /* gr_moveTo() */


SCM     gr_setcolor( c )
SCM     c;
{
int     color;

        ASRTER( NUMBERP(c), c, ARG1, s_gr_setcolor );
        if( !gr_grmode_on )
                return  BOOL_F;
        if( INUMP(c) )
                color = (int)(INUM(c));
        else
                color = (int)(REALPART(c));
#ifdef TESTING
        fprintf( stderr, "set-color! called (%d)\n", color );
#endif
        if( !valid_XYC( 0, 0, color ) )
                return  BOOL_F;
        gr_color = color;
#ifdef  X11
        /* Set the drawing color */
        XSetForeground( gr_display, gr_gc, gr_colortbl[ gr_color ] );
        gr_events(0);
#else   /* PC version */
        setcolor( gr_color );
#endif
        return  BOOL_T;
} /* gr_setcolor() */


SCM     gr_turnright( d )
SCM     d;
{
float   df;

        ASRTER( NUMBERP(d), d, ARG1, s_gr_turnright );
        if( !gr_grmode_on )
                return  BOOL_F;
        if( INUMP(d) )
                df = (float)(INUM(d));
        else
                df = REALPART(d);
        df = fmod( df, 360.0 );
        gr_dir -= df;
        gr_dir = fmod( gr_dir, 360.0 );
        return MAKINUM( (long)(gr_dir+.5) );
} /* gr_turnright() */


SCM     gr_turnleft( d )
SCM     d;
{
float   df;

        ASRTER( NUMBERP(d), d, ARG1, s_gr_turnleft );
        if( !gr_grmode_on )
                return  BOOL_F;
        if( INUMP(d) )
                df = (float)(INUM(d));
        else
                df = REALPART(d);
        df = fmod( df, 360.0 );
        gr_dir += df;
        gr_dir = fmod( gr_dir, 360.0 );
        return MAKINUM( (long)(gr_dir+.5) );
} /* gr_turnleft() */


SCM     gr_turnto( d )
SCM     d;
{
float   df;

        ASRTER( NUMBERP(d), d, ARG1, s_gr_turnto );
        if( !gr_grmode_on )
                return  BOOL_F;
        if( INUMP(d) )
                df = (float)(INUM(d));
        else
                df = REALPART(d);
        df = fmod( df, 360.0 );
        gr_dir = df;
        return MAKINUM( (long)(gr_dir+.5) );
} /* gr_turnto() */


SCM     gr_gotohome()
{
        if( !gr_grmode_on )
                return  BOOL_F;
        gr_x = gr_y = 0.0;
        return  BOOL_T;
} /* gr_gotohome() */


SCM     gr_gotocenter()
{
        if( !gr_grmode_on )
                return  BOOL_F;
#ifdef X11
        /* Check for changed window size */
        gr_events(0);
#endif
        gr_x = ((float)gr_max_x+1.0) / 2.0;
        gr_y = ((float)gr_max_y+1.0) / 2.0;
        return  BOOL_T;
} /* gr_gotocenter() */


SCM     gr_gotonw()
{
        if( !gr_grmode_on )
                return  BOOL_F;
#ifdef X11
        /* Check for changed window size */
        gr_events(0);
#endif
        gr_x = 0.0;
        gr_y = 0.0;
        return  BOOL_T;
} /* gr_gotonw() */


SCM     gr_gotosw()
{
        if( !gr_grmode_on )
                return  BOOL_F;
#ifdef X11
        /* Check for changed window size */
        gr_events(0);
#endif
        gr_x = 0.0;
        gr_y = (float)gr_max_y;
        return  BOOL_T;
} /* gr_gotosw() */


SCM     gr_gotone()
{
        if( !gr_grmode_on )
                return  BOOL_F;
#ifdef X11
        /* Check for changed window size */
        gr_events(0);
#endif
        gr_x = (float)gr_max_x;
        gr_y = 0.0;
        return  BOOL_T;
} /* gr_gotone() */


SCM     gr_gotose()
{
        if( !gr_grmode_on )
                return  BOOL_F;
#ifdef X11
        /* Check for changed window size */
        gr_events(0);
#endif
        gr_x = (float)gr_max_x;
        gr_y = (float)gr_max_y;
        return  BOOL_T;
} /* gr_gotose() */


SCM     gr_whatcolor()
{
        if( !gr_grmode_on )
                return  BOOL_F;
        return  MAKINUM( (long)gr_color );
} /* gr_whatcolor() */


SCM     gr_whatdirection()
{
        if( !gr_grmode_on )
                return  BOOL_F;
        return  MAKINUM( (long)(gr_dir+.5) );
} /* gr_whatdirection() */


SCM     gr_wherex()
{
        if( !gr_grmode_on )
                return  BOOL_F;
        return  MAKINUM( (long)gr_x );
} /* gr_wherex() */


SCM     gr_wherey()
{
        if( !gr_grmode_on )
                return  BOOL_F;
        return  MAKINUM( (long)gr_y );
} /* gr_wherey() */


static iproc    graph0[] = {
                { "help-gr", gr_helpgr },
                { "help-turtlegr", gr_helpturtlegr },
                { "graphics-mode!", gr_grmode },
                { "text-mode!", gr_txtmode },
                { "clear-graphics!", gr_cleargraph },
                { "graphics-avail?", gr_available },
                { "max-x", gr_maxx },
                { "max-y", gr_maxy },
                { "max-color", gr_maxc },
                { "what-color", gr_whatcolor },
                { "what-direction", gr_whatdirection },
                { "where-x", gr_wherex },
                { "where-y", gr_wherey },
                { "goto-home!", gr_gotohome },
                { "goto-center!", gr_gotocenter },
                { "goto-nw!", gr_gotonw },
                { "goto-sw!", gr_gotosw },
                { "goto-ne!", gr_gotone },
                { "goto-se!", gr_gotose },
                {0, 0}
        };

static iproc    graph1[] = {
                { s_gr_draw, gr_draw },
                { s_gr_move, gr_move },
                { s_gr_setcolor, gr_setcolor },
                { s_gr_turnright, gr_turnright },
                { s_gr_turnleft, gr_turnleft },
                { s_gr_turnto, gr_turnto },
                {0, 0}
        };

static iproc    graph2[] = {
                { s_gr_getdot, gr_getdot },
                { s_gr_drawTo, gr_drawTo },
                { s_gr_drawto, gr_drawto },
                { s_gr_moveTo, gr_moveTo },
                {0, 0}
        };

static iproc    graph3[] = {
                { s_gr_setdot, gr_setdot },
                { s_gr_validXYC, gr_validXYC },
                {0, 0}
        };

#if defined __STDC__ || defined __TURBOC__
void    close_turtlegr()
{
# ifdef  X11
        gr_events(0);
        XFreeColors( gr_display, DefaultColormap(gr_display, gr_screen),
                     gr_colortbl, GR_COLORS, AllPlanes );
        XFreeGC( gr_display, gr_gc );
        XUnmapWindow( gr_display, gr_win );
        XDestroyWindow( gr_display, gr_win );
# else   /* PC version */
        closegraph();
# endif
} /* close_turtlegr() */
#endif

void init_banner();		/* from scm.c */

void    init_turtlegr() /* detects if graphics is available; must be
                           called among program initializations */
{
#ifdef  X11
  char                  *display_name = NULL;   /* Server to connect to      */
  Pixmap                icon_pixmap;            /* Icon                      */
  XSizeHints            size_hints;             /* Preferred sizes           */
  XSetWindowAttributes  win_attribs;            /* Window attributes         */
  XWMHints              wm_hints;               /* Window manager hints      */
  XClassHint            class_hints;            /* Class hints               */
  XTextProperty         window_name, icon_name; /* Names for Icon & Window   */
  XGCValues             gc_values;              /* Graphics Context values   */
  static char           *colorname[GR_COLORS] = {
                                GR_COLOR00, GR_COLOR01, GR_COLOR02, GR_COLOR03,
                                GR_COLOR04, GR_COLOR05, GR_COLOR06, GR_COLOR07,
                                GR_COLOR08, GR_COLOR09, GR_COLOR10, GR_COLOR11,
                                GR_COLOR12, GR_COLOR13, GR_COLOR14, GR_COLOR15
                        };
  XColor                x_color;                /* X11 Color structure       */
  unsigned long         mask;                   /* Mask for selections       */
  int                   i;                      /* loop counter variable     */

#else   /* PC version */
int     errcode;
#endif

/***************************/
/* generic initializations */
/***************************/
  gr_x = gr_y = gr_dir = 0.0;
  gr_max_x = gr_max_y = gr_max_color = 0;

  gr_graphicsavail = 0; /* DEFAULT is no graphics - you can do without */

/********************************************/
/*****  Initialize X11 turtlegraphics   *****/
/********************************************/
#ifdef X11
        /* connect to X server */
        if( (gr_display = XOpenDisplay(display_name)) != NULL )
        {

          /*****************************/
          /* connection to X server OK */
          /*****************************/

          gr_screen = DefaultScreen( gr_display );      /* X screen number */

          /* Create a window with Black background and border */
          gr_win
            = XCreateSimpleWindow( gr_display,
                                  RootWindow( gr_display, gr_screen),
                                  0, 0, /* initial placement */
                                  GR_DEF_XSIZE, GR_DEF_YSIZE,
                                  3, /* border width */
                                  /* border pixel value */
                                  BlackPixel(gr_display, gr_screen),
                                  /* background pixel value */
                                  BlackPixel(gr_display, gr_screen) );

          /* Select input (events) for the window */
          XSelectInput( gr_display, gr_win,
                        StructureNotifyMask|ExposureMask );

          /* Check for backing store capability */
          if( !DoesBackingStore(DefaultScreenOfDisplay(gr_display)) )
          {
            fprintf( stderr, "%s: Warning: \
X server does not offer backing store capability.\n\
Window cannot be redrawn if obscured. Sorry...\n", gr_progname );
          }
          else
          {
              /* Enable the backing store feature of X server
                 and set bit gravity */
              win_attribs.bit_gravity   = NorthWestGravity;
              win_attribs.backing_store = Always;
              mask                      = CWBitGravity | CWBackingStore;
              XChangeWindowAttributes( gr_display, gr_win, mask, &win_attribs );
          }

          /* Make names of Window and Icon for window manager */
          if( XStringListToTextProperty(&gr_windowname, 1, &window_name) == 0 ) {
            (void)fprintf( stderr, "%s: Structure allocation for windowName\
 failed.\n", gr_progname );
            exit( 42 );
          }
          if( XStringListToTextProperty(&gr_iconname, 1, &icon_name) == 0 ) {
            (void)fprintf( stderr, "%s: Structure allocation for iconName\
 failed.\n", gr_progname );
            exit( 42 );
          }

          /* Create the icon */
          icon_pixmap = XCreateBitmapFromData( gr_display, gr_win, turtle_bits,
                                               turtle_width, turtle_height );

          /* Window size, state, icon etc. hints for the window manager */
          size_hints.flags = PPosition | PMaxSize | PMinSize | USSize;
          /* position and desired size are given to XCreateSimpleWindow call */
          size_hints.min_width          = GR_MIN_XSIZE;
          size_hints.min_height         = GR_MIN_YSIZE;
          size_hints.max_width          = GR_MAX_XSIZE;
          size_hints.max_height         = GR_MAX_YSIZE;
          wm_hints.flags = StateHint | IconPixmapHint | InputHint;
          wm_hints.initial_state        = NormalState;
          wm_hints.input                = False;
          wm_hints.icon_pixmap          = icon_pixmap;
          class_hints.res_name          = gr_progname;
          class_hints.res_class         = gr_classname;
          XSetWMProperties( gr_display, gr_win, &window_name, &icon_name,
                            gr_argv, gr_argc,
                            &size_hints, &wm_hints, &class_hints );


          /* Handle colors; this is quite complicated in X11 */

          if( DefaultDepth( gr_display, gr_screen ) == 1 )
          {
            /* Only 1 bitplane, BW screen */
            /* Emulate colors with 0 as Black and 1-15 White */
            gr_colortbl[0] = BlackPixel( gr_display, gr_screen );
            for( i = 1; i < GR_COLORS; ++i )
              gr_colortbl[i] = WhitePixel( gr_display, gr_screen );
# ifdef TESTING
            fprintf( stderr, "%s: 1-plane system, substituting White for \
colors 1-15.\n", gr_progname );
            fprintf( stderr, "%s: Pixel value is %lu for Black, \
%lu for White\n", gr_progname, gr_colortbl[0], gr_colortbl[1] );
# endif
          }
          else
          {
              /* more than 1 bitplane */
              for( i = 0; i < GR_COLORS; ++i )
              {
                  /* Initialize the colortable using named colors */
                  if( XParseColor( gr_display,
                                   DefaultColormap(gr_display, gr_screen),
                                   colorname[ i ], &x_color ) )
                  {
                      if( !XAllocColor( gr_display,
                                        DefaultColormap(gr_display, gr_screen),
                                        &x_color ) )
                      {
                          fprintf( stderr, "%s: Can't allocate color \
\"%s\" (%d). Substituting White.\n",
                                   gr_progname,
                                   colorname[ i ], i );
                          gr_colortbl[i] = WhitePixel( gr_display, gr_screen );
                      }
                      else
                      {
                          /* succeeded in allocating color */
                          gr_colortbl[ i ] = x_color.pixel;
# ifdef TESTING
                          fprintf( stderr, "%s: Pixel value is %lu for %s.\n",
                                   gr_progname, gr_colortbl[i], colorname[i] );
# endif
                      }
                  }
                  else
                  {
                      /* could not parse color */
                      fprintf( stderr,
                               "%s: Color name \"%s\" (%d) not in database. \
Substituting White.\n",
                               gr_progname, colorname[i], i );
                      gr_colortbl[i] = WhitePixel( gr_display, gr_screen );
                  }
              } /* for */
          } /* else */
          gr_max_color = GR_COLORS - 1;

          /* Create and initialize a default GC */
          gr_gc = XCreateGC( gr_display, gr_win, 0L, &gc_values );

          /* Initialize the drawing color, default's black */
          XSetForeground( gr_display, gr_gc, gr_colortbl[ 0 ] );
          XSetBackground( gr_display, gr_gc, gr_colortbl[ 0 ] );
          gr_color = 0;

          /* OK, we _do_ have graphics available */
          gr_graphicsavail = 1;

# ifdef __STDC__
          /* Let's do the Right Thing if possible :) */
          atexit( close_turtlegr );
# endif
      } /* if */
      else {
          gr_graphicsavail = 0;
      }
/********************************************/
/*****  Initialize PC turtlegraphics    *****/
/********************************************/
#else   /* PC version */
          gr_drivernum = DETECT;

          detectgraph( &gr_drivernum, &gr_max_display_mode );
          if( gr_drivernum != grNotDetected ) {
              if( !getenv( BGIDIR_ENVSTRING ) )
                 fprintf( stderr,
                        "You really should set the %s environment variable.\n",
                        BGIDIR_ENVSTRING );
              initgraph( &gr_drivernum, &gr_max_display_mode,
                         getenv( BGIDIR_ENVSTRING ) );
              errcode = graphresult();
              if( errcode != grOk ) {
                  fputs( "Graphics error: ", stderr );
                  fputs( grapherrormsg( errcode ), stderr );
                  exit( EXIT_FAILURE );
              }
              moveto( 0, 0 );
              gr_x = gr_y = 0.0;
              setcolor( 0 );
              gr_color = 0;
              gr_max_x = getmaxx();
              gr_max_y = getmaxy();
              gr_max_color = getmaxcolor();
              gr_max_display_mode = getmaxmode();
              restorecrtmode();
              gr_graphicsavail = 1;
              atexit( close_turtlegr );
          }
          else {
              gr_graphicsavail = 0;
          }
#endif

/* generic */
        init_iprocs( graph0, tc7_subr_0 );
        init_iprocs( graph1, tc7_subr_1 );
        init_iprocs( graph2, tc7_subr_2 );
        init_iprocs( graph3, tc7_subr_3 );
        gr_grmode_on = 0;

#ifndef X11
  /* PC version clears screen so this must be repeated */
  init_banner();
#endif

  fputs("\nSCM Turtlegraphics Copyright (C) 1992 sjm@cc.tut.fi, jtl@cc.tut.fi\n\
Type `(help-gr)' or `(help-turtlegr)' for a quick reference of\n\
the new primitives.\n", stderr);

  if( !gr_graphicsavail ) {
#ifdef X11
        fprintf( stderr, "%s: No X server found. \
Turtlegraphics not available.\n", gr_progname );
#else
        fputs( "No graphics adapter detected. \
Turtlegraphics not available.\n", stderr );
#endif
  }
  else {
#ifdef X11
        gr_events(0);
#else
        ;
#endif
  }
} /* init_turtlegr() */
