
/*
 * emulate gpr/ftn on top of X11.
 *
 * this is a gross crock, and I make no apologies for it..
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

unsigned long colortrans[15];

typedef long bitmap_desc_t;
typedef unsigned long plane_t;
typedef unsigned long raster_op_t;
typedef long coord_t;
typedef long pixel_t;

typedef struct {
    coord_t x, y;
} position_t;

typedef struct {
    coord_t x, y;
} offset_t;

typedef struct {
    coord_t x_coord_l, x_coord_r, y_coord;
} horiz_seg_t;

typedef struct {
    horiz_seg_t top, bot;
} trap_t;

typedef struct {
    position_t base;
    offset_t size;
} window_t;

Display *d;
Window w;
GC FillGC, DrawGC, TextGC, EraseGC;

#define MAX_COLORS 16

void gprinqconfig_ (config, status)
    long *config;
    long *status;
{
    XSetWindowAttributes attr;
    XColor color;
    Cursor c;
    Font f;
    XGCValues xgcv;
    unsigned long fcolor, bcolor;
    Colormap cmap;
    XColor exact, closest;
    int i, ncolors = MAX_COLORS;

    /* These colors chosen at random */
    static char *ColorName[] = {"Black", "White", "Red", "Blue","Yellow",
	"Green", "Orange", "Sea Green", "Coral", "Maroon",
	"Orange Red", "Sky Blue", "Violet Red", "Brown", "Lime Green", "Gold"};
    
    d = XOpenDisplay (0);
    if (d == 0) {
	printf("can't open display! bye.\n");
	exit(1);
    }

    colortrans[1] = fcolor = WhitePixelOfScreen(DefaultScreenOfDisplay(d));
    colortrans[0] = bcolor = BlackPixelOfScreen(DefaultScreenOfDisplay(d));
    
    *config = DisplayPlanes(d,0);
    if (*config != 1) {
	cmap = XDefaultColormap(d,0);
	for (i = 0; i < MAX_COLORS; i++) {
		if (!XParseColor(d, cmap, ColorName[i], &exact)) {
			fprintf(stderr,"XParseColor: color name %s \
			is not in database\n", ColorName[i]);
			exit(1);
		}
		if (!XAllocColor(d,cmap, &exact)) {
			fprintf(stderr, "XAllocColor: Couldn't allocate %s\n",
				ColorName[i]);
			exit(1);
		}
		colortrans[i] = exact.pixel;
	}
    }

    
    w = XCreateSimpleWindow (d, DefaultRootWindow(d),
			     0, 0,
			     WidthOfScreen(DefaultScreenOfDisplay(d))-4,
			     HeightOfScreen(DefaultScreenOfDisplay(d))-4,
			     2,
			     fcolor,
			     bcolor);
    attr.override_redirect = True;
    XChangeWindowAttributes (d, w, CWOverrideRedirect, &attr);
    xgcv.background = bcolor;
    xgcv.foreground = fcolor;
    FillGC = XCreateGC(d, w, GCForeground | GCBackground, &xgcv);
    xgcv.background = bcolor;
    xgcv.foreground = bcolor;
    EraseGC = XCreateGC(d, w, GCForeground | GCBackground, &xgcv);
    xgcv.background = bcolor;
    xgcv.foreground = fcolor;
    DrawGC = XCreateGC(d, w, GCForeground | GCBackground, &xgcv);
    TextGC = XCreateGC(d, w, GCForeground | GCBackground, &xgcv);
    f = XLoadFont (d, "fixed");
    c = XCreateGlyphCursor (d, f, f, ' ', ' ',
			    &color, &color);
    XDefineCursor(d, w, c);
    XSetGraphicsExposures (d, FillGC, False);
    XSetGraphicsExposures (d, DrawGC, False);
    XSetGraphicsExposures (d, TextGC, False);    
    *status = 0;
}

void gprinit_ (flag, xx, size, yy, bitmapdesc, status)
    long *status;
{
    XEvent ev;
    XSelectInput(d, w, ExposureMask|PointerMotionMask|
		 ButtonPressMask|ButtonReleaseMask|KeyPressMask);
    XMapWindow(d, w);
    XWindowEvent(d, w, ExposureMask, &ev);
#if 0
    while (XGrabPointer (d, w, False,
			 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
			 GrabModeAsync,
			 GrabModeAsync,
			 w, None, ev.xmotion.time) != GrabSuccess) {
	printf("couldn't grab cursor.. waiting 1 second..\n");
	sleep(1);
    }
    printf("cursor grabbed..\n");
#endif
    XGrabServer(d);
    *status = 0;		/* XXX most args */
}

void gprterminate_ (flag, status)
    long *status;
{
    XUngrabServer(d);
    XUngrabPointer(d, CurrentTime);
    XCloseDisplay(d);
    *status = 0;
}

void caldecodelocaltime_ (array)
    long *array;
{
    time_t clock;
    struct tm *lt;

    (void) time(&clock);
    lt = localtime(&clock);
    array[0] = lt->tm_year + 1900;
    array[1] = lt->tm_mon;
    array[2] = lt->tm_mday;
}

void gprsettextfont_ (font, status)
    long *font;
    long *status;
{
    /* XXX fill in font here */
    XSetFont(d, TextGC, *font);
    *status = 0;
}
    
XPoint curpoint;
XRectangle clipr;

void gprmove_ (x, y, status)
    long *x;
    long *y;
    long *status;
{
    curpoint.x = *x;
    if (curpoint.x == 0)
	curpoint.x = *(long *)x;
    curpoint.y = *y;
    if (curpoint.y == 0)
	curpoint.y = *(long *)y;
    *status = 0;
}

void gprtext_ (string, nchars, status)
    char *string;
    long *nchars;
    long *status;
{
    int count = *nchars;
    if (count == 0)
	count = *(long *)nchars;
#if 0
    printf("%d: (%.*s) show at %d, %d\n", count, count, string,
	   curpoint.x, curpoint.y);
#endif
    XDrawImageString (d, w, TextGC, curpoint.x, curpoint.y, string, count);
    *status = 0;
}

/* polyline does a series of lines, starting with the cp */

void gprpolyline_(x, y, number, status)
    long *x;
    long *y;
    long *number;
    long *status;
{
    int i, count;
    XPoint *points;
    *status = 0;
    count = *number;

    /*
     * gross hack.  we may be passed an integer*2 or an integer*4;
     * neither is likely to be greater than 2<<16
     */
    if (count == 0)
	count = *((long *)number);
	
    points = (XPoint *) malloc (sizeof(XPoint) * (count + 1));
    points[0] = curpoint;
    for (i=0; i<count; i++) {
	points[i+1].x = x[i];
	points[i+1].y = y[i];
    }
    curpoint = points[count];
    XDrawLines(d, w, DrawGC, points, count+1, CoordModeOrigin);
    free(points);
}

/* "multiline draws a series of alternate moves and lines" */

void gprmultiline_(x, y, number, status)
    long *x;
    long *y;
    long *number;
    long *status;
{
    int i, count;
    XSegment *segments;
    *status = 0;
    count = *number;
    /*
     * gross hack.  we may be passed an integer*2 or an integer*4;
     * neither is likely to be greater than 2<<16
     */
    if (count == 0)
	count = *((long *)number);

    if (count & 1)
	printf("odd multiline??\n");
    /* not exactly what you want.. */
    segments = (XSegment *)malloc (sizeof(XSegment) * (count/2));
    for (i=0; i<count; i+= 2) {
	segments[i/2].x1 = x[i];
	segments[i/2].y1 = y[i];
	segments[i/2].x2 = x[i+1];
	segments[i/2].y2 = y[i+1];
    }
    curpoint.x = segments[count/2].x2;
    curpoint.y = segments[count/2].y2;    
    XDrawSegments (d, w, DrawGC, segments, count/2);
    free(segments);
}

static long lop;
void gprbitblt_ (bitmap, window, plane, dsto, dstp, status)
bitmap_desc_t *bitmap;
window_t *window;
plane_t *plane;
position_t *dsto;
plane_t *dstp;
long *status;
{
  
    if(lop==6) XFillRectangle(d,w,EraseGC,dsto->x, dsto->y,window->size.x, window->size.y);
    else {
	XCopyArea (d, w, w, FillGC,
		window->base.x, window->base.y,
		window->size.x, window->size.y,
		dsto->x, dsto->y);
    }
    *status = 0;
}


void gprsetrasterop_ (plane, op, status)
    plane_t *plane;
    raster_op_t *op;
    long *status;
{
    lop = *op;
#if 0
    
#endif
    /*
     * When we invert, invert only one bit.
     */
    if (lop == 10) {
	XSetPlaneMask(d, FillGC, 0x00000001);
	XSetPlaneMask(d, TextGC, 0x00000001);
    }
    else {
	XSetPlaneMask(d, FillGC, 0xffffffff);
	XSetPlaneMask(d, TextGC, 0xffffffff);
    }
	
    XSetFunction(d, FillGC, lop);
    XSetFunction(d, TextGC, lop);
#if 0
    XSetFunction(d, DrawGC, *op);
#endif
}

void gprtrapezoid_ (trap, status)
    trap_t *trap;
    long *status;
{
    XPoint tr[4];
    tr[0].x = trap->top.x_coord_l;
    tr[0].y = trap->top.y_coord;
    tr[1].x = trap->top.x_coord_r;
    tr[1].y = trap->top.y_coord;    
    tr[2].x = trap->bot.x_coord_r;
    tr[2].y = trap->bot.y_coord;    
    tr[3].x = trap->bot.x_coord_l;
    tr[3].y = trap->bot.y_coord;    
    XFillPolygon (d, w, FillGC, tr, 4, Convex, CoordModeOrigin);
}

void gprsetclippingactive_ (flag, status)
    long *flag;
    long *status;
{
    if (*flag) {
	XSetClipRectangles (d, FillGC, 0, 0, &clipr, 1, YXBanded);
	XSetClipRectangles (d, DrawGC, 0, 0, &clipr, 1, YXBanded);
	XSetClipRectangles (d, TextGC, 0, 0, &clipr, 1, YXBanded);
    } else {
	XSetClipMask (d, FillGC, None);
	XSetClipMask (d, DrawGC, None);
	XSetClipMask (d, TextGC, None);		
    }
}

void tonetime_ (time)
    long *time;
{
    XBell(d, 0);
}

void timeclock_ (clock)
    long *clock;
{
    struct timeval tv;

    XSync(d,0);
    gettimeofday(&tv, 0);

    clock[0] = tv.tv_sec >> 16;
    clock[1] = tv.tv_sec & 0xffff;
    clock[2] = tv.tv_usec/4;
}

void timewait_ (delta, clock, status)
    long *clock;
    long *status;
{
    struct timeval tv;
    XSync(d, 0);
    tv.tv_sec = ((clock[0] <<16) + clock[1])>>1;
    tv.tv_usec = clock[2] * 4;
    select (0, 0, 0, 0, &tv);
}

void calgetlocaltime_(clock)
    long *clock;
{
    struct timeval tv;
    gettimeofday(&tv, 0);

    clock[0] = tv.tv_sec >> 16;
    clock[1] = tv.tv_sec & 0xffff;
    clock[2] = tv.tv_usec/4;
}

position_t mouse_posn;

void gprinqcursor_ (cursor, ops, active, posn, origin, status)
long *active;
position_t *posn;
position_t *origin;
long *status;
{
    *posn = mouse_posn;
    *status = 0;
}

int gprcondeventwait_ (event, key, posn, status)
char *key;
position_t *posn;
long *status;
{
    static unsigned long flag[16]={0,0,0,0};
    XEvent ev;
    XFlush(d);
    while (XPending(d)) {
	XNextEvent(d, &ev);
	switch(ev.type) {
	    case MotionNotify:
		mouse_posn.x = ev.xmotion.x;
		mouse_posn.y = ev.xmotion.y;
		break;

	    case ButtonPress:
		mouse_posn.x = ev.xbutton.x;
		mouse_posn.y = ev.xbutton.y;	    
		*key = ev.xbutton.button + 'a' - 1;
		flag[ev.xbutton.button]=1;
		*posn = mouse_posn;
		return 1;

	    case ButtonRelease:
		mouse_posn.x = ev.xbutton.x;
		mouse_posn.y = ev.xbutton.y;
		*key = ev.xbutton.button + 'A' - 1;
		flag[ev.xbutton.button]=0;
		*posn = mouse_posn;
		return 1;
	    case KeyPress:
		{
		    XKeyEvent *keyEvent = (XKeyEvent *) &ev;
		    char buf[128];
		    KeySym ks;
		    XComposeStatus status;
		    buf[0] = '\0';

		    XLookupString(keyEvent,buf,128,&ks,&status);
		    switch (buf[0]) {
		    case 'q':
		    case 'Q':
			exit(0);
			break;
		    default:
			return 0;
		    }
		}
		return 0;
	    }
    }
    *posn = mouse_posn;
    if(flag[1]&&flag[3]) exit(0);
    return 0;
}

void calfloatclock_ (clock, floatclock)
    long *clock;
    double *floatclock;
{
    *floatclock = (double) (clock[0] * 65536) +
	(double) (clock[1]) +
	    ((double) (clock [2]) / (1000000/4));
}

void gprsetcursorposition_ (posn, status)
    position_t *posn;
    long *status;
{
    XWarpPointer (d, None, w, 0, 0, 0, 0,
		  posn->x, posn->y);
}

void gprsetcolormap_ (start, nentries, values, status)
    pixel_t *start;
    long *nentries;
    long *values;
    long *status;
{
	Colormap cmap;
	XColor exact, closest;
}

void gprloadfontfile_ (path, pnlen, fontid, status)
    char *path;
    long *pnlen;
    long *fontid;
    long *status;
{
    int i;
   
    static struct {
	char *gprname;
	char *xname;
    } fontmap[] = { {	"/sys/dm/fonts/f5x9", "6x10" },
      {     "/sys/dm/fonts/i.12", "-*-*-medium-i-*-*-14-*-*-*-*-*-*-*"},
      {	    "/sys/dm/fonts/f7x13", "8x13" },
      {     "bzonefont", "-*-times-medium-r-*-*-24-*-*-*-*-*-*-*"},
	  {     0, 0 }};

    for (i=0; fontmap[i].gprname; i++) {
	if (strcmp(path, fontmap[i].gprname) == 0) {
	    *fontid = XLoadFont (d, fontmap[i].xname);
	    return;
	}
    }
    *fontid = XLoadFont(d, "fixed");
}

void gprsettextpath_ (direction, status)
    long *status;
{
    /*XXX noop */
    *status = 0;
}


void gprsettextvalue_ (pixel, status)
    long *pixel;
    long *status;
{
    XSetForeground(d, TextGC, colortrans[*pixel]);
    *status = 0;
}

void gprsettextbackgroundvalue_ (pixel, status)
    long *pixel;
    long *status;
{
    XSetBackground(d, TextGC, colortrans[*pixel]);
    *status = 0;
}

void gprsetfillvalue_ (pixel, status)
long *pixel;
long *status;
{
    XSetForeground(d, FillGC, colortrans[*pixel]);
    *status = 0;
}


void gprsetdrawvalue_ (pixel, status)
    long *pixel;
    long *status;
{
    XSetForeground(d, DrawGC, colortrans[*pixel]);
    *status = 0;
}

void gprcircle_ (center, radius, status)
    position_t *center;
    long *radius;
    long *status;
{
    long size = *radius;
    long moon = 1;
    if (moon) {
	if ((strncmp(ServerVendor(d), "MIT", 3) == 0) &&
	    VendorRelease(d) < 4)
	    return;
    }
    XDrawArc (d, w, DrawGC,
	      center->x - size, center->y - size,
	      size+size, size+size, 0, 360*64);
}

void gprcirclefilled_ (center, radius, status)
    position_t *center;
    long *radius;
    long *status;
{
    long size = *radius;

    XFillArc (d, w, DrawGC,
	      center->x - size, center->y - size,
	      size+size, size+size, 0, 360*64);
}

void gprenableinput_ (event_type, key_set, status)
    long *status;
{
    /* XXX noop */
}

void gprsetclipwindow_ (window, status)
    window_t *window;
    long *status;
{
    clipr.x = window->base.x;
    clipr.y = window->base.y;
    clipr.width = window->size.x;
    clipr.height =  window->size.y;
}

void rand_ (x)
    float *x;
{
    unsigned long r = random();
    double dr = r % 566927;
    
    *x = dr / 566927;
}

#include <pwd.h>
void
getusername_ (name, namlen)
char *name;
long *namlen;
{
    struct passwd *pwd;
    char *tname;
    int i;
    extern char *getenv();
    extern struct passwd *getpwuid();

    if ((pwd = getpwuid(getuid())) != NULL) {
	tname = pwd->pw_name;
	(void) strncpy(name, tname, sizeof(pwd->pw_name));
	name[sizeof(pwd->pw_name)] == '\0';
    }
    else if ((tname = getenv("USER")) == NULL && 
	     (tname = getenv("LOGNAME")) == NULL)
	tname = "intruder";
    (void) strncpy(name, tname, 9);
    name[9] = '\0';
}
