#ident "@(#)exec.c	1.17 91/04/05 XGRASP"
/*-
 * exec.c - grasp language execution routines.
 *
 * Copyright (c) 1991 by Patrick J. Naughton
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * This file is provided AS IS with no warranties of any kind. The author
 * shall have no liability with respect to the infringement of copyrights,
 * trade secrets or any patents by this file or any part thereof. In no
 * event will the author be liable for any lost revenue or profits or
 * other special, indirect and consequential damages.
 *
 * Comments and additions should be sent to the author:
 *
 * Patrick J. Naughton
 * Sun Microsystems
 * 2550 Garcia Ave, MS 10-20
 * Mountain View, CA 94043
 * (415) 336-1080
 *
 */

#include <sys/time.h>
#include <math.h>
#include "grasp.h"
#include <X11/Xatom.h>

#define ESCAPE	-3
#define DONE	-2
#define CONT	-1

#define intarg(ex,index) (ex)->Code[(index)].val.i
#define strarg(ex,index) (ex)->Code[(index)].val.s
#define imgarg(ex,index) (ex)->Code[(index)].val.image
#define fntarg(ex,index) (ex)->Code[(index)].val.font
#define excarg(ex,index) (ex)->Code[(index)].val.exec

Colormap    EGAcmap = (Colormap) 0;

XRectangle  window = {
    0, 0, 0, 0
};

typedef struct {
    int         count;
    int         ipaddr;
}           stackent;
#define STACKSIZE 16

stackent    loopstack[STACKSIZE];
int         loopstackptr = 0;

int         ipstack[STACKSIZE];
int         ipstackptr = 0;

ImageStruct *picreg[16];
ImageStruct *clipreg[128];
FontStruct *fontreg[16];
FontStruct *currentfont = 0;
int         fstyle = 0;
int         chargap = 1;
int         spacegap = -1;
int         currentcolor = 1;
int         currentbgcolor = 0;
Colormap    installedcmap;
int         palettenum = 0;
int         tranval = -1;
int         keypressed = -1;
int         videomode;


long
hundredthsofseconds()
{
    struct timeval now;

    gettimeofday(&now, (struct timezone *) 0);
    return now.tv_sec * 100 + now.tv_usec / 10000;
}

void
ExitEvent(ev)
    XClientMessageEvent *ev;
{
    if (ev->message_type == protocol_atom &&
	    ev->data.l[0] == kill_atom)
	exit(0);
}

void
exitcheck()
{
    while (XPending(dsp)) {
	XEvent      ev;
	XNextEvent(dsp, &ev);
	switch (ev.type) {
	case ClientMessage:
	    ExitEvent(&ev);
	    break;
	}
    }
}

/*
 * delay sleeps for 'd'/100'ths of a second
 * usleep() will have to be provided on non-sun platforms.
 */
void
delay(d)
    int         d;
{
    int         endtime = hundredthsofseconds() + d;
    while (1) {
	exitcheck();
	usleep(10000);	/* sleep for 1/100th of a second */
	if (hundredthsofseconds() > endtime)
	    return;
    }
}


/*
 * if ip is pointing at a wildcard '*' then resolvewild returns a pointer
 * to the object which is referenced by the current data pointer.
 * otherwise it checks the type of the ip to make sure it matches what
 * the caller expected, plus a little hack for zero's mistyped as oh's.
 */
void       *
resolvewild(ex, ip, type)
    ExecStruct *ex;
    int         ip;
    int         type;
{
    int         i;

    if (ex->Code[ip].token == WILDTYPE) {
	if (ex->currentdataptr == -1)
	    error("%s: resolvewild no data\n");
	i = ex->currentdataptr++;
    } else {
	i = ip;
	if (ex->Code[i].token != type) {
	    /* hack for mistyped zero's as Oh's */
	    if (ex->Code[i].token == STRING && ex->Code[i].val.s[0] == 'o') {
		ex->Code[i].token = INTEGER;
		ex->Code[i].val.i = 0;
	    } else
		error("%s: resolvewild type mismatch.\n");
	}
    }

    switch (type) {
    case STRING:
	return (void *) ex->Code[i].val.s;
	break;
    case INTEGER:
	return (void *) ex->Code[i].val.i;
	break;
    case IMAGE:
	if (ex->Code[i].token == STRING)
	    stringtoimage(i, EXT_CLP);
	return (void *) ex->Code[i].val.image;
	break;
    case FONTTYPE:
	if (ex->Code[i].token == STRING)
	    stringtofont(i);
	return (void *) ex->Code[i].val.font;
	break;
    default:
	error("%s: resolvewild type failure\n");
    }
}

/*
 * installs the colormap associated with the image argument.
 */
void
installcmap(i)
{
    ImageStruct *im;

    palettenum = i;
    im = picreg[i];
    if (im) {
	installedcmap = im->cmap;
	XSetWindowColormap(dsp, win, im->cmap);
    } else {
	printf("no image at %d for colormap setting\n", i);
	installedcmap = (Colormap) 0;
    }
}

void
setvideomode(c)
    char        c;
{
    int         w, h;

    if (c >= 'A' && c <= 'Z')
	c += 'a' - 'A';

    if (EGAcmap == (Colormap) 0)
	EGAcmap = CreateEGAcmap();

    videomode = c;
    switch (c) {
    case '1':	/* 80x25 text 16 color */
	w = 80 * 8;
	h = 25 * 13 + 4;
	installedcmap = (Colormap) 0;
	palettenum = 0;
	XSetWindowColormap(dsp, win, EGAcmap);
	break;
    case 'a':	/* 320x200 4 color */
	w = 320;
	h = 200;
	break;
    case 'c':	/* 640x200 2 color */
	w = 640;
	h = 200;
	break;
    case 'e':	/* 640x350 2 color */
	w = 640;
	h = 350;
	break;
    case 'g':	/* 640x350 16 colors */
	w = 640;
	h = 350;
	break;
    case 'h':	/* 720x384 2 colors */
	w = 720;
	h = 384;
	break;
    case 'i':	/* 640x350 16 colors */
	w = 640;
	h = 350;
	break;
    case 'j':	/* 640x480 16 colors */
	w = 640;
	h = 480;
	break;
    case 'k':	/* 640x350 16 colors */
	w = 640;
	h = 350;
	break;
    default:
    case 'l':	/* 320x200 256 color */
	w = 320;
	h = 200;
	break;
    case 'm':	/* 640x480 256 color */
	w = 640;
	h = 480;
	break;
    case 'n':	/* 720x348 16 color */
	w = 720;
	h = 348;
	break;
    case 'o':	/* 640x480 2 color */
	w = 640;
	h = 480;
	break;
    case 'p':	/* 800x600 2 color */
	w = 800;
	h = 600;
	break;
    case 'q':	/* 800x600 16 color */
	w = 800;
	h = 600;
	break;
    case 'r':	/* 800x600 256 color */
	w = 640;
	h = 350;
	break;
    case 's':	/* 800x600 256 color */
	w = 640;
	h = 480;
	break;
    case 't':	/* 800x600 256 color */
	w = 800;
	h = 600;
	break;
    case 'w':	/* 360x480 256 color */
	w = 360;
	h = 480;
	break;
    }

    if (picreg[0] == 0 || picreg[0]->w != w || picreg[0]->h != h) {
	if (picreg[0] != 0) {
	    XFreePixmap(dsp, picreg[0]->pix);
	    if (picreg[0]->cmap)
		XFreeColormap(dsp, picreg[0]->cmap);
	    free(picreg[0]);
	}
	picreg[0] = (ImageStruct *) malloc(sizeof(ImageStruct));
	picreg[0]->name = "background";
	picreg[0]->type = EXT_PIC;
	picreg[0]->w = w;
	picreg[0]->h = h;
	picreg[0]->d = 8;
	picreg[0]->xoff = 0;
	picreg[0]->yoff = 0;
	picreg[0]->pix = XCreatePixmap(dsp, win, w, h, 8);
	XSetForeground(dsp, gc, 0);
	XFillRectangle(dsp, picreg[0]->pix, gc, 0, 0, w, h);
	picreg[0]->cmap = (Colormap) 0;
	picreg[0]->cmaplen = 0;

	XResizeWindow(dsp, win, w, h);
	while (1) {
	    XEvent      ev;
	    XNextEvent(dsp, &ev);
	    if (ev.type == ConfigureNotify)
		break;
	}
    }
    window.x = window.y = 0;
    window.width = w;
    window.height = h;

    XSetClipMask(dsp, gc, None);
    XCopyArea(dsp, picreg[0]->pix, win, gc,
	      0, 0,
	      picreg[0]->w,
	      picreg[0]->h,
	      0, 0);
    XMapWindow(dsp, win);

    XSync(dsp, False);
}

int
unimplemented(ex, ip)
    ExecStruct *ex;
    int         ip;
{
    printf("%s: ", tokens[ex->Code[ip - 1].token]);
    printf("unimplemented operator.\n");
    return CONT;
}

#define defrect(r, X, Y, W, H) \
	(r).x = X, (r).y = Y, (r).width = W, (r).height = H

void
drawWideRect(dpy, win, gc, x, y, w, h, thick)
    Display    *dpy;
    Window      win;
    GC          gc;
    int         x, y, w, h;
    int         thick;
{
    XRectangle  rects[4];
    int         nrects, doublethick;

    if (w == 0 && h == 0)
	return;

    doublethick = 2 * thick;

    /*
     * if too small for box just draw one solid rect
     */
    if (w <= doublethick || h <= doublethick) {
	defrect(rects[0], x, y, w, h);
	nrects = 1;
	/* else draw all 4 rects for the box */
    } else {
	defrect(rects[0], x, y, w, thick);
	defrect(rects[1], x, y + h - thick, w, thick);
	defrect(rects[2], x, y + thick, thick, h - doublethick);
	defrect(rects[3], x + w - thick, y + thick, thick, h - doublethick);
	nrects = 4;
    }
    XFillRectangles(dpy, win, gc, rects, nrects);
}




/*-
 * BOX x1 y1 x2 y2 [thickness]
 * draws a hollow rectangle in the current color as thick as specified.
 */

int
f_box(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         x1 = intarg(ex, ip);
    int         y1 = intarg(ex, ip + 1);
    int         x2 = intarg(ex, ip + 2);
    int         y2 = intarg(ex, ip + 3);
    int         th = 1;

    if (nargs > 4)
	th = intarg(ex, ip + 4);

    XSetForeground(dsp, gc, currentcolor);
    drawWideRect(dsp, win, gc, x1, YFLIP(y2), x2 - x1, y2 - y1, th);
    return CONT;
}

/*-
 * BREAK label
 * break out of a loop.
 */
int
f_break(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}


/*-
 * CALL file [label]
 */
int
f_call(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    void        execfile();

    execfile(excarg(ex, ip), (nargs > 1) ? intarg(ex, ip + 1) : 0);
    return CONT;
}

/*-
 * CFADE fadenumber x y [buffernumber] [speed] [delay]
 * fade the given buffer using the given fade number at x,y.
 */
int
f_cfade(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         fadestyle = intarg(ex, ip);
    int         x = intarg(ex, ip + 1);
    int         y = intarg(ex, ip + 2);
    int         buf = 1;
    int         speed = 0;
    int         d = 10;
    ImageStruct *im;

    switch (nargs) {
    case 6:
	d = intarg(ex, ip + 5);
    case 5:
	speed = intarg(ex, ip + 4);
    case 4:
	buf = intarg(ex, ip + 3);
	break;
    case 3:
    case 2:
    case 1:
	break;
    default:
	error("%s: argcount mismatch\n");
    }
    im = clipreg[buf];
    imagefade(fadestyle, clipreg[buf], x + im->xoff, y + im->xoff, speed);

    delay(d);
    return CONT;
}

/*-
 * CFREE buffer [buffer] ...
 * unload a clipping
 */
int
f_cfree(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    /* NOP */
    return CONT;
}

/*-
 * CGETBUF n [x1 y1 x2 y2] [noshift] [tran]
 * copy an area of the screen into a clip buffer
 */
int
f_cgetbuf(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         clip = intarg(ex, ip);
    int         x1 = intarg(ex, ip + 1);
    int         y1 = intarg(ex, ip + 2);
    int         x2 = intarg(ex, ip + 3);
    int         y2 = intarg(ex, ip + 4);
    ImageStruct *im;
    XImage     *xim;

    im = (ImageStruct *) malloc(sizeof(ImageStruct));
    im->name = "getbuf";
    im->w = x2 - x1;
    im->h = y2 - y1;
    im->d = 8;
    im->cmap = (Colormap) 0;
    im->cmaplen = 0;
    im->pix = XCreatePixmap(dsp, win, im->w, im->h, 8);
    xim = XGetImage(dsp, win, x1, y1, im->w, im->h, 0xff, ZPixmap);
    XPutImage(dsp, im->pix, gc, xim, 0, 0, 0, 0, im->w, im->h);
    clipreg[clip] = im;
    free(xim->data);
    free(xim);
    return CONT;
}

/*-
 * CHGCOLOR slot val [slot val] ...
 * change palette colors in EGA mode.
 */
int
f_chgcolor(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         i;
    u_long      pmasks;
    u_long      pixels[16];
    ImageStruct *im = picreg[0];

    im->cmaplen = 16;	/* EGA hard-coded value */
    if (!im->cmap) {
	im->cmap = XCreateColormap(dsp, win, vis, AllocNone);
	XAllocColorCells(dsp, im->cmap, True, &pmasks, 0, pixels, im->cmaplen);
    }
    for (i = 0; i < nargs; i += 2) {
	int         slot = intarg(ex, ip + i);
	int         pal = intarg(ex, ip + i + 1);
	im->colors[slot].pixel = slot;
	im->colors[slot].red = decodepal(pal, 0x20, 0x04) << 8;
	im->colors[slot].green = decodepal(pal, 0x10, 0x02) << 8;
	im->colors[slot].blue = decodepal(pal, 0x08, 0x01) << 8;
	im->colors[slot].flags = DoRed | DoGreen | DoBlue;
	XStoreColor(dsp, im->cmap, &(im->colors[slot]));
    }
    installcmap(0);
    return CONT;
}

/*-
 * CIRCLE x y xr [yr] [iris]
 * draw an ellipse
 */
int
f_circle(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * CLEARSCR
 * paint entire screen
 */
int
f_clearscr(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    if (picreg[0] == 0)
	setvideomode('l');
    XSetForeground(dsp, gc, currentcolor);
    XFillRectangle(dsp, picreg[0]->pix, gc,
		   0, 0, picreg[0]->w, picreg[0]->h);
    XCopyArea(dsp, picreg[0]->pix, win, gc,
	      0, 0, picreg[0]->w, picreg[0]->h, 0, 0);
    return CONT;
}

/*-
 * CLOAD name [buffer] [noshift] [tran]
 * load a clipping
 */
int
f_cload(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    ImageStruct *im;
    int         clip;

    im = (ImageStruct *) resolvewild(ex, ip, IMAGE);
    clip = (int) resolvewild(ex, ip + 1, INTEGER);

    clipreg[clip] = im;

    return CONT;
}

/*-
 * CLOSEGL
 * close a library file
 */
int
f_closegl(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * COLOR color1 [R] [color2]
 * set the drawing color, (not sure what the R is...)
 */
int
f_color(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    currentcolor = intarg(ex, ip);
    if (nargs > 1)
	currentbgcolor = intarg(ex, ip + 1);

    return CONT;
}

/*-
 * CYCLE cycles start number [time]
 * rotate palette colors.
 */
int
f_cycle(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         cycles = intarg(ex, ip);
    int         start = intarg(ex, ip + 1);
    int         number = intarg(ex, ip + 2);
    int         d = 0;
    int         i;
    int         j;
    int         end = start + number - 1;
    XColor     *c = picreg[palettenum]->colors;
    unsigned long buffer;

    if (nargs > 3)
	d = intarg(ex, ip + 3);
    for (i = 0; i < cycles; i++) {
	buffer = c[start].pixel;
	for (j = start; j < end; j++)
	    c[j].pixel = c[j + 1].pixel;
	c[end].pixel = buffer;
	XStoreColors(dsp, installedcmap, &c[start], number);
	XSync(dsp, False);
	if (d)
	    delay(d);
    }
    return CONT;
}

/*-
 * DATA item [item] ...
 * define data elements
 */
int
f_data(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    ex->currentdataptr = ip;
    return CONT;
}

/*-
 * DATABEGIN
 * define data elements (multiple lines)
 */
int
f_databegin(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    if (nargs == 1) {
	ex->currentdataptr = intarg(ex, ip);
	return CONT;
    } else
	ex->currentdataptr = ip;
    return CONT;
}

/*-
 * DATAEND
 * mark the end of a data block
 */
int
f_dataend(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    ex->currentdataend = ip - 2;
    return CONT;
}

/*-
 * DATASKIP n
 * skip n data elements in a block.
 */
f_dataskip(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    ex->currentdataptr += intarg(ex, ip);
    return unimplemented(ex, ip);
}

/*-
 * DFREE buffer [buffer] ...
 *
 */
int
f_dfree(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * DLOAD name [buffer] [disk]
 * load a differential animation file.
 */
int
f_dload(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * EDGE setting [color]
 * turn leading edge for fades on or off.
 */
int
f_edge(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * ELSE
 * target for IF condition not met.
 */
int
f_else(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * ENDLFLOAT
 * clear the float background buffer.
 */
int
f_endlfloat(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * ENDIF
 * mark the end of an if-else block
 */
int
f_endif(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * EXEC name [options]
 * run a non-grasp program.
 */
int
f_exec(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * EXIT [value]
 * exit grasp or subprogram.
 */
int
f_exit(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    delay(100);
    return DONE;
}

/*-
 * FFREE buffer [buffer] ...
 * unload a font
 */
int
f_ffree(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    /* NOP */
    return CONT;
}

/*-
 * FGAPS char [space]
 * set letter and word spacing
 */
int
f_fgaps(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    chargap = intarg(ex, ip);
    spacegap = intarg(ex, ip + 1);

    return CONT;
}

/*-
 * FLOAD name [buffer]
 * load a font
 */
int
f_fload(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    FontStruct *f = fntarg(ex, ip);
    int         reg = 1;
    if (nargs > 1)
	reg = intarg(ex, ip + 1);

    fontreg[reg] = f;
    currentfont = f;

    chargap = 1;
    spacegap = f->glyphs[' '].width;

    return CONT;
}

/*-
 * FLOAT x1 y1 x2 y2 step delay buf [buf] ...
 * animate a clipping and preserve the background.
 */
int
f_float(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         x1 = intarg(ex, ip);
    int         y1 = intarg(ex, ip + 1);
    int         x2 = intarg(ex, ip + 2);
    int         y2 = intarg(ex, ip + 3);
    int         step = intarg(ex, ip + 4);
    int         d = intarg(ex, ip + 5);
    int         images = nargs - 6;
    int         image;

    if (x1 == x2 && y1 == y2) {
	for (image = 0; image < images; image++) {
	    ImageStruct *im = clipreg[intarg(ex, ip + 6 + image)];
	    XCopyArea(dsp, im->pix, win, gc, 0, 0, im->w, im->h,
		      x1, YFLIPIM(y1, im));
	    XSync(dsp, False);
	    delay(d);
	}
    } else {
	int         i;
	ImageStruct *im = clipreg[intarg(ex, ip + 6)];
	float       x = x1;
	float       y = YFLIP(y1) - im->h;
	float       dx = (x2 - x1);
	float       dy = (y2 - y1);
	int         count = sqrt(dx * dx + dy * dy) / step;
	Window      floatwin;
	XSetWindowAttributes xswa;

	xswa.backing_store = Always;
	floatwin = XCreateWindow(dsp, win, (int) x, (int) y, im->w, im->h, 0, 8,
				 InputOutput, vis, CWBackingStore, &xswa);

	dx /= count;
	dy /= count;
	image = 0;
	XCopyArea(dsp, im->pix, floatwin, gc, 0, 0, im->w, im->h, 0, 0);
	XMapWindow(dsp, floatwin);
	for (i = 0; i <= count; i++) {
	    if (images > 1) {
		im = clipreg[intarg(ex, ip + 6 + image)];
		if (++image >= images)
		    image = 0;
		XCopyArea(dsp, im->pix, floatwin, gc,
			  0, 0, im->w, im->h, 0, 0);
	    }
	    XMoveWindow(dsp, floatwin, (int) x, (int) y);
	    x += dx;
	    y += dy;
	    XSync(dsp, False);
	    delay(d);
	}
	XUnmapWindow(dsp, floatwin);
	XDestroyWindow(dsp, floatwin);
    }
    return CONT;
}

/*-
 * FLY x1 y1 x2 y2 step delay buf [buf] ...
 * animate a clipping
 */
int
f_fly(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         x1 = intarg(ex, ip);
    int         y1 = intarg(ex, ip + 1);
    int         x2 = intarg(ex, ip + 2);
    int         y2 = intarg(ex, ip + 3);
    int         step = intarg(ex, ip + 4);
    int         d = intarg(ex, ip + 5);
    int         i;
    int         x = x1;
    int         y = y1;
    int         dx = step ? (x2 - x1) / step : 0;
    int         dy = step ? (y2 - y1) / step : 0;

    for (i = 6; i < nargs; i++) {
	ImageStruct *im = clipreg[intarg(ex, ip + i)];
	XCopyArea(dsp, im->pix, win, gc, 0, 0, im->w, im->h,
		  x, YFLIPIM(y, im));
	x += dx;
	y += dy;
	XSync(dsp, False);
	delay(d);
    }
    return CONT;
}

/*-
 * FONT [buffer]
 * select a font
 */
int
f_font(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    currentfont = fontreg[intarg(ex, ip)];
    return CONT;
}

/*-
 * FSTYLE dir off1 [off2]
 * set character shading... (I think this changed between 1.1 and 3.0)
 */
int
f_fstyle(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    fstyle = intarg(ex, ip);
    return CONT;
}

/*-
 * GETCOLOR x y
 * set the drawing color equal to the screen pixel.
 */
int
f_getcolor(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         x = intarg(ex, ip);
    int         y = YFLIP(intarg(ex, ip + 1));
    XImage     *xim = XGetImage(dsp, win, x, y, 1, 1, 0xff, ZPixmap);

    currentcolor = XGetPixel(xim, 0, 0);
    printf("getcolor %d,%d = %d\n", x, y, currentcolor);
    XDestroyImage(xim);
    return CONT;
}

/*-
 * GETKEY name
 * set a variable equal to a keystroke
 */
int
f_getkey(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * GOSUB label [val] ...
 * execute a subroutine
 */
int
f_gosub(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    ipstack[loopstackptr] = ip + 1;
    if (++loopstackptr >= STACKSIZE)
	error("%s: ipstack overflow\n");
    return intarg(ex, ip);
}

/*-
 * GOTO label
 * jump to a label
 */
int
f_goto(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return intarg(ex, ip);
}

/*-
 * IF exp [label]
 * jump if condition is met, or start if-else block if no label.
 */
int
f_if(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * IFKEY key [label [key label] ... ]
 * check for specific keypress
 */
int
f_ifkey(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         i;

    for (i = 0; i < nargs; i += 2) {
	char       *key = strarg(ex, ip + i);
	if (*key == keypressed)
	    return intarg(ex, ip + i + 1);
    }
    return CONT;
}

/*-
 * IFMEM mem [label]
 * check available memory.
 */
int
f_ifmem(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    /* ignore the memory value */

    return (nargs > 1) ? intarg(ex, ip + 1) : CONT;
}

/*-
 * IFMOUSE button [label1] [x y x1 y1] [color] [wait] [label2]
 * check for a mouse click (no idea how this one is supposed to work).
 */
int
f_ifmouse(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * IFVIDEO mode [label]
 * check whether video mode is available
 */
int
f_ifvideo(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    /* ignore the mode value */

    return (nargs > 1) ? intarg(ex, ip + 1) : CONT;
}

/*-
 * INT num [ax] [bx] [cx] [dx] [si] [di] [ds] [es]
 * call an MSDOS interrupt... (yeah, right!)
 */
int
f_int(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

static int  linex1 = 0;
static int  liney1 = 0;
static int  linex2 = 0;
static int  liney2 = 0;

/*-
 * LINE x1 y1 x2 y2 [R]
 * draw a line, (possibly relative to the current point).
 */
int
f_line(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         x1 = intarg(ex, ip);
    int         y1 = intarg(ex, ip + 1);
    int         x2 = intarg(ex, ip + 2);
    int         y2 = intarg(ex, ip + 3);

    if (nargs > 4) {	/* assume there's an R out there */
	linex1 += x1;
	liney1 += y1;
	linex2 += x2;
	liney2 += y2;
    } else {
	linex1 = x1;
	liney1 = y1;
	linex2 = x2;
	liney2 = y2;
    }

    XSetForeground(dsp, gc, currentcolor);
    XDrawLine(dsp, win, gc, linex1, YFLIP(liney1) - 1,
	      linex2, YFLIP(liney2) - 1);
    XSync(dsp, False);
    return CONT;
}

/*-
 * LINK name [label]
 * jump to another program
 */
int
f_link(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    execfile(excarg(ex, ip), (nargs > 1) ? intarg(ex, ip + 1) : 0);
    return DONE;
}

/*-
 * LOCAL var value
 * define a local variable
 */
int
f_local(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * LOOP
 * define the end of a MARK'ed loop.
 */
int
f_loop(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    if (--loopstack[loopstackptr - 1].count == 0) {
	--loopstackptr;

	return CONT;
    }
    return loopstack[loopstackptr - 1].ipaddr;
}

/*-
 * MARK count [rand]
 * define the beggining of a loop. (not sure what rand is supposed to do.)
 */
int
f_mark(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    loopstack[loopstackptr].count = intarg(ex, ip);
    loopstack[loopstackptr].ipaddr = ip + 1;
    if (++loopstackptr >= STACKSIZE)
	error("%s: stack overflow\n");
    return CONT;
}

/*-
 * MERGE name
 * add lines to the current program from another
 * (why is this different from CALL?)
 */
int
f_merge(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * MODE color
 * change colors in 2 color CGA mode.
 */
int
f_mode(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         color = intarg(ex, ip);
    int         pal = 0;
    int         i;
    u_long      pmasks;
    u_long      pixels[4];
    ImageStruct *im = picreg[0];

    im->cmaplen = 4;
    if (nargs > 1)
	pal = intarg(ex, ip + 1);
    if (!im->cmap) {
	im->cmap = XCreateColormap(dsp, win, vis, AllocNone);
	XAllocColorCells(dsp, im->cmap, True, &pmasks, 0, pixels, im->cmaplen);
    }
    i = 0;
    im->colors[i].pixel = i;
    im->colors[i].red = egapal[color][0];
    im->colors[i].green = egapal[color][1];
    im->colors[i].blue = egapal[color][2];
    im->colors[i].flags = DoRed | DoGreen | DoBlue;

    for (i = 1; i < 4; i++) {
	color = cgapal[i - 1][pal];
	im->colors[i].pixel = i;
	im->colors[i].red = egapal[color][0];
	im->colors[i].green = egapal[color][1];
	im->colors[i].blue = egapal[color][2];
	im->colors[i].flags = DoRed | DoGreen | DoBlue;
    }
    XStoreColors(dsp, im->cmap, im->colors, im->cmaplen);
    installcmap(0);
    return CONT;
}

/*-
 * MOUSE setting
 * turn mouse on/off
 */
int
f_mouse(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * MOVE x1 y1 x2 y2 x3 y3
 * move an area of the screen
 */
int
f_move(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         x1 = intarg(ex, ip);
    int         y1 = YFLIP(intarg(ex, ip + 1));
    int         x2 = intarg(ex, ip + 2);
    int         y2 = YFLIP(intarg(ex, ip + 3));
    int         w = x2 - x1;
    int         h = y2 - y1;
    int         x3 = intarg(ex, ip + 4);
    int         y3 = YFLIP(intarg(ex, ip + 5)) - h;

    XCopyArea(dsp, win, win, gc, x1, y1, w, h, x3, y3);
    return CONT;
}

/*-
 * NOISE n m time
 * create a sound
 */
int
f_noise(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    /* NOP */
    return CONT;
}

/*-
 * NOTE val tone time [R]
 * play a note
 */
int
f_note(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    /* NOP */
    return CONT;
}

/*-
 * OFFSET x y [R]
 * change the screen coords for some other commands.
 */
int
f_offset(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * OPENGL name
 * use files in a library file.
 */
int
f_opengl(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * OUT dx al [ah]
 * output a value to an IBM PC hardware IO port (yeah, right!)
 */
int
f_out(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * PALETTE buffer
 * set palette colors to match a given picture.
 */
int
f_palette(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    installcmap(intarg(ex, ip));
    return CONT;
}

/*-
 * PAN [x1 y1] x2 y2 [R] [speed]
 * pan across large picture in EGA mode.
 */
int
f_pan(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * PFADE fade [buffer] [speed] [delay]
 * display a picture.
 */
int
f_pfade(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         fadestyle = (int) resolvewild(ex, ip, INTEGER);
    int         buf = 0;
    int         speed = 0;
    int         d = 10;
    ImageStruct *im;

    switch (nargs) {
    case 4:
	d = (int) resolvewild(ex, ip + 3, INTEGER);
    case 3:
	speed = (int) resolvewild(ex, ip + 2, INTEGER);
    case 2:
	buf = (int) resolvewild(ex, ip + 1, INTEGER);
	break;
    case 1:
	break;
    default:
	error("%s: argcount mismatch\n");
    }

    im = picreg[buf];

    if (buf == 0) {
	XSetForeground(dsp, gc, currentcolor);
	XFillRectangle(dsp, im->pix, gc, 0, 0, im->w, im->h);
    }
    if (im)
	imagefade(fadestyle, im, 0, 0, speed);

    delay(d);
    return CONT;
}

/*-
 * PFREE buffer [buffer] ...
 * unload a picture.
 */
int
f_pfree(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    /* NOP */
    return CONT;
}

/*-
 * PGETBUF n [x1 y1 x2 y2]
 * copy the screen into a picture buffer, (same as CGETBUF?)
 */
int
f_pgetbuf(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * PLOAD name [buffer]
 * load a picture.
 */
int
f_pload(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    ImageStruct *im = imgarg(ex, ip);
    int         regnum;

    if (nargs > 1)
	regnum = intarg(ex, ip + 1);
    else
	regnum = 1;
    picreg[regnum] = im;
    return CONT;
}

/*-
 * PNEWBUF buffer [x y]
 * create an empty picture buffer. (what is x, y? offset?)
 */
int
f_pnewbuf(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * POINT x y [rx ry]
 * draw a point (what are rx,ry?)
 */
int
f_point(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         x = intarg(ex, ip);
    int         y = YFLIP(intarg(ex, ip + 1));

    XSetForeground(dsp, gc, currentcolor);
    XDrawPoint(dsp, win, gc, x, y);
    XSync(dsp, False);
    return CONT;
}

/*-
 * POKE seg off byte [byte] ...
 * change 8-bit memory given 8088 seg:off address (yeah, right!)
 */
int
f_poke(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * POKEL seg off byte [byte] ...
 * change 32-bit memory given 8088 seg:off address (yeah, right!)
 */
int
f_pokel(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * POKEW seg off byte [byte] ...
 * change 16-bit memory given 8088 seg:off address (yeah, right!)
 */
int
f_pokew(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * POP label
 * leave a subroutine and branch on return.
 */
int
f_pop(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * POSITION buffer x y [R]
 * alter picture placement on the screen.
 */
int
f_position(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         buf = intarg(ex, ip);
    int         x = intarg(ex, ip + 1);
    int         y = intarg(ex, ip + 2);
    int         rel = 0;

    if (nargs > 3)
	rel = 1;	/* assume it's an 'r' */

    picreg[buf]->xoff = (rel ? picreg[buf]->xoff : 0) + x;
    picreg[buf]->yoff = (rel ? picreg[buf]->yoff : 0) + y;

    return CONT;
}

/*-
 * PSAVE name [buffer]
 * save picture buffer to disk.
 */
int
f_psave(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * PSETBUF [buffer]
 * draw to picture buffer instead of screen.
 */
int
f_psetbuf(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * PUTDFF [buffer] [delay] [start] [end] [x y]
 * play a differential animation file.
 */
int
f_putdff(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * PUTUP x y [buffer] [delay]
 * display a clipping
 */
int
f_putup(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         x = (int) resolvewild(ex, ip, INTEGER);
    int         y = (int) resolvewild(ex, ip + 1, INTEGER);
    int         clip = (int) resolvewild(ex, ip + 2, INTEGER);
    ImageStruct *im = clipreg[clip];
    int         d = 1;

    if (nargs > 3)
	d = intarg(ex, ip + 3);

    x += im->xoff;
    y += im->yoff;
    XCopyArea(dsp, im->pix, win, gc, 0, 0, im->w, im->h, x, YFLIPIM(y, im));
    XSync(dsp, False);
    delay(d);
    return CONT;
}

/*-
 * RECT x1 y1 x2 y2
 * draw a filled rectangle;
 */
int
f_rect(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         x1 = intarg(ex, ip);
    int         y1 = intarg(ex, ip + 1);
    int         x2 = intarg(ex, ip + 2);
    int         y2 = intarg(ex, ip + 3);

    XSetForeground(dsp, gc, currentcolor);
    XFillRectangle(dsp, win, gc, x1, YFLIP(y2), x2 - x2, y2 - y1);

    return CONT;
}

/*-
 * RESETGL
 * close a library
 */
int
f_resetgl(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * RESETSCR
 * reset normal screen size.
 */
int
f_resetscr(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * RETURN [val]
 * return from a subroutine.
 */
int
f_return(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    if (--loopstackptr < 0)
	error("%s: ipstack underflow\n");

    return ipstack[loopstackptr];
}

/*-
 * REVPAGE
 * reverse viewing and drawing pages (for double buffering?)
 */
int
f_revpage(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * SEND device string
 * send a character string to a device (yeah, right!)
 */
int
f_send(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * SET function|feature|var value
 * alter system characteristics or alter text features or defines variables...
 */
int
f_set(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * SETCOLOR val0 val1 val2 ... val16
 * define palette in EGA mode
 */
int
f_setcolor(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         i;
    u_long      pmasks;
    u_long      pixels[16];
    ImageStruct *im = picreg[0];

    if (nargs != 16)
	error("%s: not 16 args to setcolor\n");

    im->cmaplen = nargs;

    if (!im->cmap) {
	im->cmap = XCreateColormap(dsp, win, vis, AllocNone);
	XAllocColorCells(dsp, im->cmap, True, &pmasks, 0, pixels, im->cmaplen);
    }
    for (i = 0; i < im->cmaplen; i++) {
	int         pal = intarg(ex, ip + i);
	im->colors[i].pixel = i;
	im->colors[i].red = decodepal(pal, 0x20, 0x04) << 8;
	im->colors[i].green = decodepal(pal, 0x10, 0x02) << 8;
	im->colors[i].blue = decodepal(pal, 0x08, 0x01) << 8;
	im->colors[i].flags = DoRed | DoGreen | DoBlue;
    }
    XStoreColors(dsp, im->cmap, im->colors, im->cmaplen);
    installcmap(0);
    return CONT;
}

/*-
 * SETPAGE view draw
 * define viewing and drawing pages for double buffering.
 */
int
f_setpage(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * SETRGB start r g b [R] rand
 * change color in VGA mode (not sure what R or rand do...)
 */
int
f_setrgb(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         start = intarg(ex, ip);
    int         r = intarg(ex, ip + 1);
    int         g = intarg(ex, ip + 2);
    int         b = intarg(ex, ip + 3);
    XColor      c;

    c.pixel = picreg[palettenum]->colors[start].pixel;
    c.red = r << 8;
    c.green = g << 8;
    c.blue = b << 8;
    c.flags = DoRed | DoGreen | DoBlue;

    XStoreColors(dsp, installedcmap, &c, 1);

    return CONT;
}

/*-
 * SETUPSCR buffer
 * create a virtual screen for panning.
 */
int
f_setupscr(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * SPLIT line [R]
 * divide an EGA screen into two independant areas.
 */
int
f_split(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * SPREAD [pal1] pal2 [steps]
 * cross-fade between two VGA palettes.
 */
int
f_spread(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         r1 = intarg(ex, ip);
    int         r2 = intarg(ex, ip + 1);
    ImageStruct *p1 = picreg[r1];
    ImageStruct *p2 = picreg[r2];
    int         n = 1;
    XColor      colors[256];
    int         i;
    int         j;
    Colormap    cmap;
    unsigned long pmasks;
    u_long      pixels[256];
    int         len;

    if (nargs == 3)
	n = intarg(ex, ip + 2);
    n = (n < 0) ? -n : n;

    len = p1->cmaplen;

    if (len != p2->cmaplen)
	error("%s: %d,%d spread length mismatch\n", len, p2->cmaplen);

    cmap = XCreateColormap(dsp, win, vis, AllocNone);
    XAllocColorCells(dsp, cmap, True, &pmasks, 0, pixels, len);
    for (i = 0; i < len; i++) {
	colors[i].pixel = pixels[i];
	colors[i].red = p1->colors[i].red;
	colors[i].green = p1->colors[i].green;
	colors[i].blue = p1->colors[i].blue;
	colors[i].flags = DoRed | DoGreen | DoBlue;
    }
    XStoreColors(dsp, cmap, p1->colors, p1->cmaplen);
    XSetWindowColormap(dsp, win, cmap);

#define lerp(a,b,step,nsteps) ((a)<(b)			\
    ? (a) + ((b) - (a)) * (step) / (nsteps)		\
    : (b) + ((a) - (b)) * (step) / (nsteps))

    for (j = 1; j <= n; j++) {
	for (i = 0; i < len; i++) {
	    XColor     *c1 = &(p1->colors[i]);
	    XColor     *c2 = &(p2->colors[i]);
	    colors[i].red = lerp(c1->red, c2->red, j, n);
	    colors[i].green = lerp(c1->green, c2->green, j, n);
	    colors[i].red = lerp(c1->red, c2->red, j, n);
	}
	XStoreColors(dsp, cmap, colors, p1->cmaplen);
	XSync(dsp, False);
    }
    installcmap(r2);
    XFreeColormap(dsp, cmap);
    XSync(dsp, False);
    return -1;
}

void
displaystring(s, x, y)
    char       *s;
    int         x, y;
{
    int         i;

    for (i = 0; i < strlen(s); i++) {
	GlyphStruct *g = &currentfont->glyphs[s[i]];
	if (g->pix) {
	    XSetStipple(dsp, gc, g->pix);
	    XSetTSOrigin(dsp, gc, x - g->lbearing, y);
	    XFillRectangle(dsp, win, gc, x, y, g->width, currentfont->height);
	    x += g->width + chargap;
	}
    }
}

/*-
 * TEXT [x y] string [delay]
 * print characters on the screen.
 */
int
f_text(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         x = intarg(ex, ip);
    int         y = intarg(ex, ip + 1);
    char       *s = strarg(ex, ip + 2);
    int         d = 1;
    int         i;


    if (nargs > 3)
	d = intarg(ex, ip + 3);
    if (!currentfont) {
	/* hack there should be a default font. */
	XSetForeground(dsp, gc, currentcolor);
	XSetBackground(dsp, gc, currentbgcolor);
	XDrawString(dsp, win, gc, x, YFLIP(y), s, strlen(s));
	goto text_exit;
    }
    currentfont->glyphs[' '].width = spacegap;
    y = YFLIP(y) - currentfont->height - 2;
    XSetFillStyle(dsp, gc, FillStippled);
    XSetForeground(dsp, gc, (fstyle > 2) ? currentbgcolor : currentcolor);
    switch (fstyle) {
    case 0:
	break;
    case 1:
	displaystring(s, x, y - 1);	/* bold up */
	break;
    case 2:
	displaystring(s, x + 1, y);	/* bold right */
	break;
    case 3:
	displaystring(s, x + 1, y - 1);	/* shadow up right */
	break;
    case 4:
	displaystring(s, x - 1, y - 1);	/* shadow up left */
	break;
    case 5:
	displaystring(s, x + 2, y - 2);	/* shadow up right 2 pixels */
	break;
    case 6:
	displaystring(s, x - 2, y - 2);	/* shadow up left 2 pixels */
	break;
    }
    XSetForeground(dsp, gc, currentcolor);
    displaystring(s, x, y);
    XSetFillStyle(dsp, gc, FillSolid);
text_exit:
    XSync(dsp, False);
    delay(d);
    return CONT;
}

/*-
 * TILE buffer [bleed]
 * fill screen with copies of the clipping.
 */
int
f_tile(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    ImageStruct *im = clipreg[intarg(ex, ip)];
    int         x;
    int         y;

    for (y = 0; y <= picreg[0]->h - im->h; y += im->h)
	for (x = 0; x <= picreg[0]->w - im->w; x += im->w)
	    XCopyArea(dsp, im->pix, win, gc, 0, 0, im->w, im->h, x, y);
    delay(10);
    return CONT;
}

/*-
 * TIMER
 * set the system clock for execution timing (huh?)
 */
int
f_timer(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * TRAN set [color]
 * set = "on"|"off", make color be transparent in clippings.
 */
int
f_tran(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         tranval = intarg(ex, ip);
    return -1;
}

/*-
 * VIDEO mode [x y] [init]
 * set the display mode (not sure what x and y and init are for)
 */
int
f_video(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    char       *s = strarg(ex, ip);
    char        c = s[0];

    setvideomode(c);
    return CONT;
}


int
KeyEvent(ev)
    XKeyEvent  *ev;
{
    KeySym      keysym;
    XComposeStatus status;
    char        string[2];
    int         len = XLookupString(ev, string, 1, &keysym, &status);
    keypressed = string[0];
    if (keypressed == 27)	/* Esc Key */
	return -1;
    return len;
}


/*-
 * WAITKEY [time] [label]
 * wait for a keystroke or a given time, and jump to label on timeout.
 */
int
f_waitkey(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         endtime;
    int         retaddr = CONT;

    if (nargs > 0)
	endtime = hundredthsofseconds() + intarg(ex, ip);
    if (nargs > 1)
	retaddr = intarg(ex, ip + 1);
    while (1) {
	if (nargs == 0 || XPending(dsp)) {
	    XEvent      ev;
	    int         key;
	    XNextEvent(dsp, &ev);
	    switch (ev.type) {
	    case KeyPress:
		key = KeyEvent(&ev);
		if (key > 0)
		    return CONT;
		if (key == -1)
		    return ESCAPE;
	    case ClientMessage:
		ExitEvent(&ev);
		break;
	    }
	} else {
	    usleep(10000);	/* sleep for 1/100th of a second */
	    if (hundredthsofseconds() > endtime)
		return retaddr;
	}
    }
}


/*-
 * WHEN key [command]
 * set up an automatic response for given keystroke.
 */
int
f_when(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    return unimplemented(ex, ip);
}

/*-
 * WINDOW x1 y1 x2 y2 [R]
 * limit screen changes to given area.
 */
int
f_window(ex, ip, nargs)
    ExecStruct *ex;
    int         ip;
    int         nargs;
{
    int         x1 = intarg(ex, ip);
    int         y1 = intarg(ex, ip + 1);
    int         x2 = intarg(ex, ip + 2);
    int         y2 = intarg(ex, ip + 3);

    if (nargs > 4) {
	/* relative */
	int         oy2 = YFLIP(window.y);
	y2 += oy2;
	y1 += oy2 - window.height;
	x1 += window.x;
	x2 += window.x + window.width;
    }
    window.x = x1;
    window.y = YFLIP(y2);
    window.width = x2 - x1;
    window.height = y2 - y1;

    return CONT;
}

int         (*funcs[]) () = {
    0,
    f_box,
    f_break,
    f_call,
    f_cfade,
    f_cfree,
    f_cgetbuf,
    f_chgcolor,
    f_circle,
    f_clearscr,
    f_cload,
    f_closegl,
    f_color,
    f_cycle,
    f_data,
    f_databegin,
    f_dataend,
    f_dataskip,
    f_dfree,
    f_dload,
    f_edge,
    f_else,
    f_endlfloat,
    f_endif,
    f_exec,
    f_exit,
    f_ffree,
    f_fgaps,
    f_fload,
    f_float,
    f_fly,
    f_font,
    f_fstyle,
    f_getcolor,
    f_getkey,
    f_gosub,
    f_goto,
    f_if,
    f_ifkey,
    f_ifmem,
    f_ifmouse,
    f_ifvideo,
    f_int,
    f_line,
    f_link,
    f_local,
    f_loop,
    f_mark,
    f_merge,
    f_mode,
    f_mouse,
    f_move,
    f_noise,
    f_note,
    f_offset,
    f_opengl,
    f_out,
    f_palette,
    f_pan,
    f_pfade,
    f_pfree,
    f_pgetbuf,
    f_pload,
    f_pnewbuf,
    f_point,
    f_poke,
    f_pokel,
    f_pokew,
    f_pop,
    f_position,
    f_psave,
    f_psetbuf,
    f_putdff,
    f_putup,
    f_rect,
    f_resetgl,
    f_resetscr,
    f_return,
    f_revpage,
    f_send,
    f_set,
    f_setcolor,
    f_setpage,
    f_setrgb,
    f_setupscr,
    f_split,
    f_spread,
    f_text,
    f_tile,
    f_timer,
    f_tran,
    f_video,
    f_waitkey,
    f_when,
    f_window,
    f_pfade,
    f_waitkey
};


void
printexec(ex, nargs)
    ExecStruct *ex;
    int         nargs;
{
    int         i;

    printf("executing: %s", tokens[ex->Code[ex->ip].token]);
    for (i = 1; i <= nargs; i++)
	switch (ex->Code[ex->ip + i].token) {
	case STRING:
	    printf(" %s", ex->Code[ex->ip + i].val.s);
	    break;
	case INTEGER:
	    printf(" %d", ex->Code[ex->ip + i].val.i);
	    break;
	case IMAGE:
	    printf(" %s", ex->Code[ex->ip + i].val.image->name);
	    break;
	case FONTTYPE:
	    if (ex->Code[ex->ip + i].val.font)
		printf(" %s", ex->Code[ex->ip + i].val.font->name);
	    else
		printf(" (nil)");
	    break;
	case EXECTYPE:
	    printf(" %s", ex->Code[ex->ip + i].val.exec->name);
	    break;
	case WILDTYPE:
	    printf(" @");
	    break;
	default:
	    error("%s: bogus token type.\n");
	}
    printf("\n");
}


void
execfile(ex, ip)
    ExecStruct *ex;
    int         ip;
{
    ex->ip = ip;
    ex->currentdataptr = -1;
    ex->currentdataend = -1;
    while (1) {
	int         nargs = ex->Code[ex->ip].val.i;
	int         i = ex->Code[ex->ip].token;
	int         retval;

	if (i > NTOKENS) {
	    printf("skipping bogus token %s\n", ex->Code[ex->ip].val.s);
	    ex->ip++;
	    continue;
	}
	if (verbose)
	    printexec(ex, nargs);
	switch (retval = funcs[i] (ex, ex->ip + 1, nargs)) {
	case DONE:
	    return;
	case ESCAPE:
	    exit(0);
	case CONT:
	    if ((ex->ip += (nargs + 1)) >= ex->numcodes)
		return;
	    break;
	default:
	    ex->ip = retval;
	}
	exitcheck();
    }
}
