/*
 *	X Version 11 - S Graphics Device Driver
 *
 *	Copyright 1988, 1989 by The Massachusetts Institute of Technology
 *
 *	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, and that the name of
 *	M.I.T. not be used in advertising or publicity pertaining to
 *	distribution of the software without specific, written prior
 *	permission.  M.I.T. makes no representations about the
 *	suitability of this software for any purpose.  It is provided
 *	"as is" without express or implied warranty.
 *
 */

	/* Drawing multiple lines kills the R3 Sun server */
	/* Your mileage may vary */

#define BRAIN_DEAD_SERVER

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <stdio.h>
#include <math.h>

#undef Complex
#ifdef OLD
#include "cdefs.h"
#include "structure.h"
#else
#include "S.h"
#endif

#include "device.h"

#define Bitmap_width 16
#define Bitmap_height 16

typedef char Shade16[32];
Shade16 Shades[] = {
	/* White */
	{
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	},

	/* Black */
	{
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	},

	/* Diag2 */
	{
	0xEE, 0xEE, 0x55, 0x55, 0xBB, 0xBB, 0x55, 0x55,
	0xEE, 0xEE, 0x55, 0x55, 0xBB, 0xBB, 0x55, 0x55,
	0xEE, 0xEE, 0x55, 0x55, 0xBB, 0xBB, 0x55, 0x55,
	0xEE, 0xEE, 0x55, 0x55, 0xBB, 0xBB, 0x55, 0x55,
	},

	/* DarkGrey */
	{
	0xEE, 0xEE, 0xFF, 0xFF, 0xBB, 0xBB, 0xFF, 0xFF,
	0xEE, 0xEE, 0xFF, 0xFF, 0xBB, 0xBB, 0xFF, 0xFF,
	0xEE, 0xEE, 0xFF, 0xFF, 0xBB, 0xBB, 0xFF, 0xFF,
	0xEE, 0xEE, 0xFF, 0xFF, 0xBB, 0xBB, 0xFF, 0xFF
	},

	/* Grey */
	{
	0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA,
	0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA,
	0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA,
	0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA,
	},

	/* 2 Pixel Checkerboard */
	{
	0xCC, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0x33,
	0xCC, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0x33,
	0xCC, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0x33,
	0xCC, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0x33,
	},

	/* Diag1 */
	{
	0x11, 0x11, 0xAA, 0xAA, 0x44, 0x44, 0xAA, 0xAA,
	0x11, 0x11, 0xAA, 0xAA, 0x44, 0x44, 0xAA, 0xAA,
	0x11, 0x11, 0xAA, 0xAA, 0x44, 0x44, 0xAA, 0xAA,
	0x11, 0x11, 0xAA, 0xAA, 0x44, 0x44, 0xAA, 0xAA,
	},

	/* LightGrey */
	{
	0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44,
	0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44,
	0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44,
	0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44,
	},

	/* VeryLightGrey */
	{
	0x11, 0x11, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00,
	0x11, 0x11, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00,
	0x11, 0x11, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00,
	0x11, 0x11, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00,
	},
	
	/* X Background Inverted */
	{
	0x11, 0x11, 0x22, 0x22, 0x88, 0x88, 0x44, 0x44,
	0x11, 0x11, 0x22, 0x22, 0x88, 0x88, 0x44, 0x44,
	0x11, 0x11, 0x22, 0x22, 0x88, 0x88, 0x44, 0x44,
	0x11, 0x11, 0x22, 0x22, 0x88, 0x88, 0x44, 0x44,
	},
	
	/* X Background */
	{
	0xee, 0xee, 0xdd, 0xdd, 0x77, 0x77, 0xbb, 0xbb,
	0xee, 0xee, 0xdd, 0xdd, 0x77, 0x77, 0xbb, 0xbb,
	0xee, 0xee, 0xdd, 0xdd, 0x77, 0x77, 0xbb, 0xbb,
	0xee, 0xee, 0xdd, 0xdd, 0x77, 0x77, 0xbb, 0xbb,
	},
};

#define NSHADES sizeof(Shades)/sizeof(Shade16)
Pixmap ShadePixmap[NSHADES];

static int		window_width;
static int		window_height;
static int		ask;
static char*		display_name;
static char*		font_name;
static int		star_xbias;
static int		star_ybias;
static int		resize = 0;

static Display*		display = NULL;	/* Display */
static int		screen;		/* Screen */
static int		depth;		/* Pixmap depth */
static Window		root_window;	/* Root Window */
static Window		window;		/* Current Graphics Window */
static Window*		window_list;	/* Graphics Window List */
static Cursor		gcursor;	/* Graphics Cursor */
static GC		gc_erase;	/* for Rotated Text */
static GC		gc_text;	/* for drawing Text */
static GC		gc_lines;	/* for drawing lines */
static GC		gc_solid;	/* for drawing lines */
static GC		gc_fill;	/* for area fills */
static GC		gc_dashes;	/* for drawing lines */
static XEvent		event;		/* Event */
static Font		font_id;	/* Font */
static XFontStruct*	font_str;	/* Font info */
static int		blackpixel;	/* Black */
static int		whitepixel;	/* White */
 
#define am(i)		F77_COM(bgrp)[(i)-1]
#define UxR(i)		((i)*am(37)+am(36)+0.5)
#define UyR(i)		(window_height-((i)*am(39)+am(38)+0.5))

long			F77_COM(reshap);
extern float		F77_COM(bgrp)[];
char*			malloc();
vector*			x11();


		/* S Entry Points */

static vector	*wrap(),
		*flush(),
		*signalled(),
		*points(),
		*lines(),
		*polygon(),
		*text(),
		*segments(),
		*clear(),
		*readpen();

		/* Internally Used Functions */

static		event_handler(),
		font_orientation(),
		init_display(),
		init_graphics(),
		init_text(),
		init_window(),
		rotchar(),
		set_clip_region(),
		set_line_style();

static double	pixelHeight(),
		pixelWidth();

static device d_X = {
	FALSE, 0, NULL, 0, NULL,
	{
	x11, wrap, flush, signalled, points, lines,
	polygon, text, segments, clear, readpen, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL
	}
};


vector *x11(width,height,pask,pdisplay_name,pfont_name,pstar_xbias,pstar_ybias)
float*	width;
float*	height;
long*	pask;
char**	pdisplay_name;
char**	pfont_name;
long*	pstar_xbias;
long*	pstar_ybias;
{
	device *d, *new_device();
	double pixelWidth();
	double pixelHeight();
	int i;

	ask = *pask;
	display_name = *pdisplay_name;
	font_name = *pfont_name;
	star_xbias = *pstar_xbias;
	star_ybias = *pstar_ybias;

	d = new_device(&d_X,0L);
	set_device(d->which);

	init_display();
	init_text();
	init_graphics();

	window_width = *width/pixelWidth();
	window_height = *height/pixelHeight();

	init_window();

	for( i=0 ; i<40 ; i++ ) am(i) = 0.0;

		/* nominal character extents */

	am(20) = font_str->max_bounds.width;
	am(21) = font_str->max_bounds.ascent+font_str->max_bounds.descent;

		/* display extents */

	am(22) = 0.0;
	am(23) = window_width;
	am(24) = 0.0;
	am(25) = window_height;

		/* character addressing offsets */

	am(26) = 0.0;
	am(27) = 0.5;

		/* raster size in inches */

	am(28) = pixelWidth();
	am(29) = pixelHeight();

		/* device ID */

	am(30) = -1954;
	am(1) = 0.0;		/* fixed size characters */
	am(31) = 1.0;		/* allow character rotation */

	F77_SUB(defltz)();

	return S_void;
}


static init_display()
{
		/* open the default display */

	if( strlen(display_name) )
		display=XOpenDisplay(display_name);
	else 
		display=XOpenDisplay(NULL);

	if(display==NULL)
		Recover("Couldn't open X display",S_void);

		/* Default Screen and Root Window */

	screen = XDefaultScreen(display);
	/*
	if( XDoesBackingStore(screen)==NotUseful ) {
		fprintf(stderr,"Warning: window contents will not be maintained by the X server\n");
		fflush(stderr);
	}
	*/
	depth = XDefaultDepth(display,screen);
	root_window = XDefaultRootWindow(display);

		/* Get black and white pixel values */

	blackpixel = BlackPixel(display,screen);
	whitepixel = WhitePixel(display,screen);
}


static init_window()
{
	XWindowAttributes	getattr;
	XSetWindowAttributes	attributes;

		/* Try to create a simple window */
		/* Want to know about exposures */
		/* and window-resizes and locations */

	attributes.background_pixel = whitepixel;
	attributes.border_pixel = blackpixel;
	attributes.backing_store = Always;
	attributes.event_mask = ButtonPressMask
			      | ExposureMask
			      | StructureNotifyMask;

	if((window = XCreateWindow( display, root_window,
		DisplayWidth(display,screen)-window_width-10,		/* window coordinates */
		10,
		window_width, window_height, 1,
		DefaultDepth(display,screen), InputOutput,
		DefaultVisual(display,screen),
		CWEventMask | CWBackPixel | CWBorderPixel | CWBackingStore,
		&attributes ))==0)
			Recover("couldn't create graphics window",S_void);

	gcursor = XCreateFontCursor(display,XC_crosshair);
	XChangeProperty( display, window,
		XA_WM_NAME, XA_STRING, 8, PropModeReplace, "S Graphics", 10);

	XDefineCursor(display,window,gcursor);

	XSelectInput( display, window,
		ExposureMask|ButtonPressMask|StructureNotifyMask);

		/* Map the window */

	XMapWindow(display, window);
	XNextEvent(display,&event);
	while( event.type != ConfigureNotify )
		XNextEvent(display,&event);
	window_width = event.xconfigure.width;
	window_height = event.xconfigure.height;
}


	/* Structures used in character rotation */

static Pixmap c_pixmap;
static XImage *c_image;
static XPoint *c_points;
static int c_width = 32;
static int c_height = 32;


static init_text()
{
	if( !(font_str=XLoadQueryFont(display,font_name)) )
		Recover("couldn't load graphics font",S_void);
	font_id = font_str->fid;

	gc_text= XCreateGC(display,root_window,0,NULL);
	XSetState(display,gc_text,blackpixel,whitepixel,GXcopy,AllPlanes);
	XSetFont(display,gc_text,font_id);

	c_pixmap = XCreatePixmap(display,root_window,c_width,c_height,depth);
	c_points = (XPoint*)malloc((unsigned)(c_width*c_height*sizeof(XPoint)));
	gc_erase = XCreateGC(display,root_window,0,NULL);
	XSetState(display,gc_erase,whitepixel,whitepixel,GXcopy,AllPlanes);
}


static int dash_mode;

static unsigned char
	short_dash[] = { 4, 4 },
	dotted[]     = { 1, 3 },
	dot_dash[]   = { 4, 3, 1, 3 },
	long_dash[]  = { 7, 4};


static init_graphics()
{
	int i;

	gc_solid = XCreateGC( display, root_window, 0, NULL);
	XSetState( display, gc_solid, blackpixel, whitepixel, GXcopy, AllPlanes);
	gc_lines = gc_solid;

	gc_dashes = XCreateGC( display, root_window, 0, NULL);
	XSetState( display, gc_dashes, blackpixel, whitepixel, GXcopy, AllPlanes);
	XSetLineAttributes(display, gc_dashes, 0, LineOnOffDash, CapButt, JoinMiter);
	XSetDashes( display, gc_dashes, 0, short_dash, 2 );
	dash_mode = 2;

	gc_fill = XCreateGC( display, root_window, 0, NULL );
	XSetState( display, gc_fill, blackpixel, whitepixel, GXcopy, AllPlanes);

	for( i=0 ; i<NSHADES ; i++ )
		ShadePixmap[i] = XCreateBitmapFromData(display,root_window,
					Shades[i],Bitmap_width,Bitmap_height);
	XSetFillStyle(display,gc_fill,FillStippled);
}


int olty = 1; /* solid */

static set_line_style()
{
	int lty = am(8);

	if( lty != olty ) {
		switch( lty ) {
			case 1:
				gc_lines = gc_solid;
				break;
			case 2:
				XSetDashes( display, gc_dashes, 0, dotted, 2 );
				gc_lines = gc_dashes;
				break;
			case 3:
				XSetDashes( display, gc_dashes, 0, long_dash, 2 );
				gc_lines = gc_dashes;
				break;
			case 4:
				XSetDashes( display, gc_dashes, 0, short_dash, 2 );
				gc_lines = gc_dashes;
				break;
			case 5:
				XSetDashes( display, gc_dashes, 0, dot_dash, 4 );
				gc_lines = gc_dashes;
				break;
		}
		olty = lty;
	}
}


static vector *points(x,y,n)
float *x, *y;
long *n;
{
	XCharStruct inf;
	int dret, asc, desc;
	int i, ix, iy, ixoffset, iyoffset;
	long lch;
	char ch;

	lch = ch = am(15);
	if( lch<32 ) {
		F77_SUB(dmarkz)(x,y,n,&lch);
	}
	else {
		XQueryTextExtents(display,font_id,&ch,1,&dret,&asc,&desc,&inf);
		ixoffset = -0.5*inf.width;
		iyoffset =  0.5*(inf.ascent+inf.descent);
		if( ch=='*' ) {
			iyoffset += star_ybias;
			ixoffset += star_xbias;
		}
		for( i=0 ; i<*n ; i++ ) {
			ix = UxR(x[i])+ixoffset;
			iy = UyR(y[i])+iyoffset;
			XDrawString(display,window,gc_text,ix,iy,&ch,1);
		}
	}
	XSync(display,0);
	return S_void;
}


static vector *lines(x,y,pn)
float *x, *y;
long *pn;
{
	int i, n = *pn;
#ifdef BRAIN_DEAD_SERVER
	int ix0, ix1, iy0, iy1;

	set_line_style();
	set_clip_region();
	for( i=1 ; i<n ; i++ ) {
		ix0 = UxR(x[i-1]);
		iy0 = UyR(y[i-1]);
		ix1 = UxR(x[i]);
		iy1 = UyR(y[i]);
		XDrawLine(display,window,gc_lines,ix0,iy0,ix1,iy1);
	}
#else
	XPoint *points;

	set_line_style();
	set_clip_region();
	points = (XPoint *)malloc((unsigned)(n*sizeof(XPoint)));
	for( i=0 ; i<n ; i++ ) {
		points[i].x = UxR(x[i]);
		points[i].y = UyR(y[i]);
	}
	XDrawLines(display,window,gc_lines,points,n,CoordModeOrigin);
	free((char*)points);
#endif
	XSync(display,0);
	return S_void;
}


static set_clip_region()
{
	XRectangle cliprect;

	if( am(65)>0.5 ) {
		cliprect.x = 0;
		cliprect.width = window_width;
		cliprect.y = 0;
		cliprect.height = window_height;
	}
	else {
		cliprect.x = UxR(am(61));
		cliprect.width = UxR(am(62))-cliprect.x+1.0;
		cliprect.y = UyR(am(64));
		cliprect.height = UyR(am(63))-cliprect.y+1.0;
	}
	XSetClipRectangles(display,gc_lines,0,0,&cliprect,1,Unsorted);
}


static vector *segments(x0,y0,x1,y1,pn)
float *x0, *y0, *x1, *y1;
long *pn;
{
	int ix0, ix1, iy0, iy1;
	int i, n = *pn;

	set_line_style();
	set_clip_region();
	for( i=0 ; i<n ; i++ ) {
		ix0 = UxR(x0[i]);
		iy0 = UyR(y0[i]);
		ix1 = UxR(x1[i]);
		iy1 = UyR(y1[i]);
		XDrawLine(display,window,gc_lines,ix0,iy0,ix1,iy1);
	}
	XSync(display,0);
	return S_void;
}


int tile = -1;


static vector *polygon(x,y,pn)
float *x, *y;
long *pn;
{
	XPoint *points;
	int i;
	int n = *pn;
	int col = am(10);

	if( n<=1 )
		return S_void;

	points = (XPoint *)malloc((unsigned)(n*sizeof(XPoint)));
	for( i=0 ; i<n ; i++ ) {
		points[i].x = UxR(x[i]);
		points[i].y = UyR(y[i]);
	}

	if( col>=0 && col<NSHADES ) {
		if( tile!=col )
			XSetStipple(display,gc_fill,ShadePixmap[col]);
		XFillPolygon(display,window,gc_fill,points,n,Nonconvex,CoordModeOrigin);
	}

	free((char*)points);
	XSync(display,0);


	return S_void;
}


static vector *clear()
{
	long x0, x1, y0, y1;
	extern zzdevz_();

	if( ask ) {
		(void)fprintf(stderr,"GO? ");
		fflush(stderr);
		while( getchar()!='\n' );
	}
	XClearWindow(display,window);
	/* or init_window(); */
	event_handler();
	if( resize ) {
		am(22) = x0 = 0;
		am(23) = x1 = window_width;
		am(24) = y0 = 0;
		am(25) = y1 = window_height;
		F77_COM(reshap) = 1;
		zzdevz_(&x0,&x1,&y0,&y1);
		resize = 0;
	}
	XSync(display,0);
	return S_void;
}


static vector *wrap()
{
	int i;

	free(c_points);
	XFreeCursor(display,gcursor);
	XFreeFont(display,font_str);
	XFreeGC(display,gc_text);
	XFreeGC(display,gc_erase);
	XFreeGC(display,gc_solid);
	XFreeGC(display,gc_dashes);
	XFreeGC(display,gc_fill);
	XFreePixmap(display,c_pixmap);
	for( i=0 ; i<NSHADES ; i++ )
		XFreePixmap(display,ShadePixmap[i]);
	XDestroyWindow(display,window);
	XCloseDisplay(display);
	return S_void;
}


static vector *flush()
{
	return S_void;
}


static vector *signalled()
{
	return S_void;
}


#define MM_PER_INCH 25.4


static double pixelWidth()
{
	double width, widthMM;
	width = DisplayWidth(display,screen);
	widthMM = DisplayWidthMM(display,screen);
	return (widthMM/width)/MM_PER_INCH;
}

static double pixelHeight()
{
	double height, heightMM;
	height = DisplayHeight(display,screen);
	heightMM = DisplayHeightMM(display,screen);
	return (heightMM/height)/MM_PER_INCH;
}


static event_handler()
{
	while( XPending(display) ) {
		XNextEvent(display,&event);
		if( event.xany.type==Expose ) {
			while( event.xexpose.count ) {
				XNextEvent(display,&event);
			}
		}
		else if( event.type==ConfigureNotify ) {
			window_width = event.xconfigure.width;
			window_height = event.xconfigure.height;
			resize = 1;
		}
	}
}

/*
 *    o	Ideal String Placement
 * 
 *	String origins are taken to be at the lower left corner.  If
 *	the string extents are (len,hgt) the rotated string is offset
 *	by:
 *
 *		-adj * ( len, hgt ) * (  cos_rot  sin_rot )
 *				      ( -sin_rot  cos_rot )
 *
 *	before drawing (the reversal of sign on the y-axis must be
 *	dealt with separately).
 *
 *
 *    o	Practical Difficulty
 *
 *	Because characters cannot be rotated arbitrarily the the above
 *	description doesn't quite work.  For example consider "rot" in
 *	the quadrant [-45,45].  To avoid characters getting squished
 *	together as rotation occurs we have to arrange that the width
 *	of the string projected on the x-axis is constant.  This means
 *	that the top right coordinate of the rotated string (before
 *	adjustment) is in fact at
 *		( len,  len * tan(rot) + hgt )
 *	Thus when we adjust the string, we offset the origin by an
 *	amount
 *		adj * ( len,  len * tan(rot) + hgt )
 *	A similar kludge must be applied in the other quadrants.
 *	I agree, its as clear as mud, but its the right thing to do.
 */


#define DEG_TO_RAD	0.01745329251994329576		/* 2 Pi / 360 */
#define TANDEG(x)	tan((x)*DEG_TO_RAD)

static vector *text(x,y,str,pl,padj)
float *x, *y, *padj;
char *str;
long *pl;
{
	int i, orientation, l, len, hgt;
	double t, rot;
	int xloc, yloc;
	double adj = *padj;

	rot = am(48);
	orientation = font_orientation(rot);
	l = *pl;
	xloc = UxR(*x);
	yloc = UyR(*y);
	len = XTextWidth(font_str,str,l);
	hgt = font_str->max_bounds.ascent;

	switch( orientation ) {
		case 0:
			xloc -= adj*len;
			yloc += adj*(len*TANDEG(rot)+hgt);
			break;
		case 1:
			xloc += adj*(len*TANDEG(rot-90.0)+hgt);
			yloc += adj*len;
			break;
		case 2:
			xloc += adj*len;
			yloc -= adj*(len*TANDEG(rot-180.0)+hgt);
			break;
		case 3:
			xloc -= adj*(len*TANDEG(rot-270.0)+hgt);
			yloc -= adj*len;
			break;
	}

	for( i=0 ; i<l ; i++ ) {
		rotchar(str[i],xloc,yloc,orientation);
		t = XTextWidth(font_str,&str[i],1);
		switch( orientation ) {
			case 0:
				xloc += t;
				yloc -= t*TANDEG(rot);
				break;
			case 1:
				xloc -= t*TANDEG(rot-90.0);
				yloc -= t;
				break;
			case 2:
				xloc -= t;
				yloc += t*TANDEG(rot-180.0);
				break;
			case 3:
				xloc += t*TANDEG(rot-270.0);
				yloc += t;
				break;
		}
	}
	XSync(display,0);
	return S_void;
}


static font_orientation( angle )
double angle;
{
	while( angle<0.0 ) angle += 360.0;
	while( angle>360.0 ) angle -= 360.0;

	/* bias toward horizontal is deliberate */

	if(  45.0<angle && angle<135.0 )
		return 1;
	if( 135.0<=angle && angle<=225.0 )
		return 2;
	if( 225.0<angle && angle<315.0 )
		return 3;
	return 0;
}


	/* Draw a rotated character - idea lifted from xlispstat */
	/* Write the character into a pixmap in the server */
	/* Transfer the bits to the client using XGetImage */
	/* Record the rotated coordinates of the black pixels */
	/* Draw these coordinates using XDrawPoints */
	/* God help network administrators when X gets loose */

static rotchar(ch, x, y, orientation)
char ch;
int x, y;
int orientation;
{
	int i, j, n;
	int ascent = font_str->max_bounds.ascent;
	int width = font_str->max_bounds.width;
	int height = font_str->max_bounds.ascent + font_str->max_bounds.descent;

	if( orientation ) {
		XFillRectangle( display, c_pixmap, gc_erase, 0, 0, width, height);
		XDrawString( display, c_pixmap, gc_text, 0, ascent, &ch, 1);
		c_image = XGetImage( display, c_pixmap, 0, 0, width, height,
			AllPlanes, ZPixmap);
		switch( orientation ) {
		case 1:
			for(i = 0, n = 0; i < width; i++)
				for(j = 0; j < height; j++)
					if(blackpixel==XGetPixel(c_image,i,j)) {
						c_points[n].x = x - ascent + j;
						c_points[n].y = y - i;
						n++;
					}
			break;
		case 2:
			for(i = 0, n = 0; i < width; i++)
				for(j = 0; j < height; j++)
					if(blackpixel==XGetPixel(c_image,i,j)) {
						c_points[n].x = x - i;
						c_points[n].y = y + ascent - j;
						n++;
					}
			break;
		case 3:
			for(i = 0, n = 0; i < width; i++)
				for(j = 0; j < height; j++)
					if(blackpixel==XGetPixel(c_image,i,j)) {
						c_points[n].x = x + ascent - j;
						c_points[n].y = y + i;
						n++;
					}
			break;
		}
		XDrawPoints(display,window,gc_text,c_points,n,CoordModeOrigin);
		XDestroyImage(c_image);
	}
	else
		XDrawString(display,window,gc_text,x,y,&ch,1);
}


static vector *readpen(x,y,n,nxy)
float *x, *y;
long *n, *nxy;
{
	int i;

	event_handler();
	XSync(display,1);

	for( i=0 ; i<*nxy ; i++ ) {
		XNextEvent(display,&event);
		if( event.xbutton.button==Button1 ) {
			x[i] = (event.xbutton.x-am(36))/am(37);
			y[i] = (window_height-event.xbutton.y-am(38))/am(39);
			(void)fprintf(stderr,"\07");
			(void)fflush(stderr);
		}
		else
			break;
	}
	*n = i;
	XSync(display,0);
	return S_void;
}
