// xgrp.c  MR Nov 100
//
// Auxiliary functions for X-Windows graphics.
//
// Uses only a few simple EXISTING primary colors, doesn't 
// allocate/initialize/use a new, newly allocated, color-map.  

#include "xgrp.h"


// --------------------------------------------------------------------------
//                   graphics stuff, part 1:  COLORS 
// --------------------------------------------------------------------------


#define IABS( a ) ( (a) >= 0 ? (a) : -(a) )

unsigned long get_colorpixel( Display * pD,
	int targ_r, 
	int targ_g,
	int targ_b )
	// Get the pixel value to use to produce the color nearest
	//  the specified RGB value.  We use the ''default color map''
	//  of the Display pD in unchanged form.
	// Values of targ_r/g/b lie inside [ 0, 65535 ].
	// E.g.: max intensity white = (65535/65535/65535).
	{
	XColor clr;
	int m = 65535 + 65535 + 65535;
	unsigned long mycolor = 0;
	unsigned long i;
	unsigned long imax = 1L;
	int depth = DefaultDepth( pD, DefaultScreen(pD) );

	imax <<= depth;
	//DEBUG printf( "getcolor : depth=%d\n", depth );
	//DEBUG printf( "getcolor : imax=%lu\n", imax );

	for ( i = 0; i < imax; i++ )
		{
		int newm;

		clr.pixel = i;
		XQueryColor( pD, 
			DefaultColormap( pD, DefaultScreen(pD) ),
			&clr );
		
		newm = IABS( targ_r - (int)(clr.red) ) + 
			IABS( targ_g - (int)(clr.green) ) + 
			IABS( targ_b - (int)(clr.blue) );
		if ( newm < m )
			{ mycolor = i; m = newm; }
		}

	//DEBUG printf( "getcolor : i_final=%d\n", mycolor );
	return mycolor;
	}


// Global color pixel-values array:
// (global to ALL files in the program)
long GLOB_colorpixel[MAXCOLOR];



void initcolors( Display * pD )
	// This function initializes the global array GLOB_colorpixel
	{
	int i;
	unsigned long grey = get_colorpixel( pD, 30000, 30000, 30000 );
	for ( i = 0; i < MAXCOLOR; i++ )
		{
		GLOB_colorpixel[i] = grey;
		}

	GLOB_colorpixel[CLR_BLACK] = get_colorpixel( pD, 0, 0, 0 );
	GLOB_colorpixel[CLR_WHITE] = get_colorpixel( pD, 65535, 65535, 65535 );

	GLOB_colorpixel[CLR_LGREY] = get_colorpixel( pD, 50000, 50000, 50000 );
	GLOB_colorpixel[CLR_DGREY] = get_colorpixel( pD, 20000, 20000, 20000 );
	GLOB_colorpixel[CLR_GREY] = grey;

	GLOB_colorpixel[CLR_RED   ] = get_colorpixel( pD, 65535, 0, 0 );
	GLOB_colorpixel[CLR_ORANGE] = get_colorpixel( pD, 65535, 40000, 0 );
	GLOB_colorpixel[CLR_YELLOW] = get_colorpixel( pD, 65535, 65535, 0 );
	GLOB_colorpixel[CLR_YELGR ] = get_colorpixel( pD, 40000, 65535, 0 );
	GLOB_colorpixel[CLR_GREEN ] = get_colorpixel( pD, 0, 65535, 0 );
	//GLOB_colorpixel[CLR_CYAN  ] = get_colorpixel( pD, 0, 40000, 40000 );
	GLOB_colorpixel[CLR_CYAN  ] = get_colorpixel( pD, 0, 30000, 65535 );
	GLOB_colorpixel[CLR_BLUE  ] = get_colorpixel( pD, 0, 0, 65535 );
	GLOB_colorpixel[CLR_PURPLE] = get_colorpixel( pD, 40000, 0, 65535 );
	}



// --------------------------------------------------------------------------
//           graphics stuff part 2: drawing lines etc. on the screen
// --------------------------------------------------------------------------


//
// Funcs & structs for use inside 'drawMyPicture()' :
//



void grp_makenew( grp_t * pgrp,
	Display * pD, 
	Drawable d )
	//Fill *pgrp struct with values for Display 'pD', Drawable 'd'
	// and with a newly allocated and initialized ''GC'' with suitable 
	// default settings.
	//Set origin to (0,0) default, and set scale to 1.0 default.
	{
	XGCValues xgcvals; 

	pgrp->pD = pD;
	pgrp->d = d; //grp.w = bb; //grp.w = w;

	// Initialize ''gc'' with our ''default'' settings
	xgcvals.function = GXcopy; //GXset; //GXxor; 
	xgcvals.plane_mask = GLOB_colorpixel[ CLR_WHITE ];  
	            //This 'plane_mask' determines the color
	xgcvals.foreground = 0xffffffffL;
	xgcvals.background = 0xffffffffL;
	xgcvals.line_width = 1;
	xgcvals.line_style = LineSolid;
	pgrp->gc = XCreateGC( 
	   pD, 
	   d, //Drawable
	   GCFunction | GCPlaneMask | 
	    GCLineWidth | GCForeground | GCBackground |
	    GCLineStyle /*| GCClipMask | GCGraphicsExposures */,
	   &xgcvals );
	}
void grp_delete( grp_t * pgrp )
	{
	XFreeGC( pgrp->pD, pgrp->gc );
	}


void grp_setoperation( grp_t * pgrp,
	int function ) // One of the GX... defines (e.g. XGset, XGclear)
	{
	XGCValues xgcvals; 
	xgcvals.function = function;
	XChangeGC( pgrp->pD, pgrp->gc, 
	   GCFunction,
	   &xgcvals );
	}
void grp_setcolor( grp_t * pgrp,
	long pm_color )  //One of the PM_... defines
	{
	XGCValues xgcvals; 
	xgcvals.plane_mask = pm_color;  //This determines the color
	XChangeGC( pgrp->pD, pgrp->gc, 
	   GCPlaneMask, 
	   &xgcvals );
	}
void grp_setlinewidth( grp_t * pgrp,
	int linewidth )
	{
	XGCValues xgcvals; 
	xgcvals.line_width = linewidth;
	XChangeGC( pgrp->pD, pgrp->gc, 
	   GCLineWidth,
	   &xgcvals );
	}


#if 0
void grp_clearscreen( grp_t * pgrp )
	{
	XClearWindow( pgrp->pD, pgrp->w ); 
	}
#endif

void grp_drawline( grp_t * pgrp,
	int x0, int y0, 
	int x1, int y1 )
	{
	XDrawLine( pgrp->pD, pgrp->d, pgrp->gc, 
	    x0, y0, x1, y1 );
	}
void grp_paintline( grp_t * pgrp,
	int x0, int y0, 
	int x1, int y1,
	long color )
	// First clear, then draw
	{
	grp_setoperation( pgrp, GXclear );
	grp_setcolor( pgrp, 0xffffffff );
	XDrawLine( pgrp->pD, pgrp->d, pgrp->gc, x0, y0, x1, y1 );

	grp_setoperation( pgrp, GXset );
	grp_setcolor( pgrp, color );
	XDrawLine( pgrp->pD, pgrp->d, pgrp->gc, x0, y0, x1, y1 );
	}



void grp_drawcircle( grp_t * pgrp,
	int x, int y, int radius )
	{
	XDrawArc( 
	   pgrp->pD, pgrp->d, pgrp->gc, 
	   x - radius,
	   y - radius,
           2 * radius,
           2 * radius,
	   0, 64*360 );
	}
void grp_fillcircle( grp_t * pgrp,
	int x, int y, int radius )
	{
	XFillArc( 
	   pgrp->pD, pgrp->d, pgrp->gc, 
	   x - radius,
	   y - radius,
           2 * radius,
           2 * radius,
	   0, 64*360 );
	}

void grp_paintcircle( grp_t * pgrp,
	int x, int y, int radius,
	long color )
	// First clear, then fill
	{
	grp_setoperation( pgrp, GXclear );
	grp_setcolor( pgrp, 0xffffffff );
	grp_fillcircle( pgrp, x, y, radius );

	grp_setoperation( pgrp, GXset );
	grp_setcolor( pgrp, color );
	grp_fillcircle( pgrp, x, y, radius );
	}


void grp_fillrect( grp_t * pgrp,
	int x, int y, int width, int height )
	{
	XFillRectangle(
	   pgrp->pD, pgrp->d, pgrp->gc, 
	   x, y, width, height );
	}
void grp_paintrect( grp_t * pgrp,
	int x, int y, int width, int height,
	long color )
	// First clear, then fill
	{
	grp_setoperation( pgrp, GXclear );
	grp_setcolor( pgrp, 0xffffffff );
	grp_fillrect( pgrp, x, y, width, height );

	grp_setoperation( pgrp, GXset );
	grp_setcolor( pgrp, color );
	grp_fillrect( pgrp, x, y, width, height );
	}



void grp_paintbox( grp_t * pgrp,
	int x, int y, int radius,
	long color )
	//Draw an outlined (not filled) box 
	{
	int x0 = x-radius;
	int x1 = x+radius;
	int y0 = y-radius;
	int y1 = y+radius;
	grp_paintline( pgrp, x0, y0, x1, y0, color );
	grp_paintline( pgrp, x1, y0, x1, y1, color );
	grp_paintline( pgrp, x1, y1, x0, y1, color );
	grp_paintline( pgrp, x0, y1, x0, y0, color );
	}



// --------------------------------------------------------------------------
//           graphics stuff part 3: Ascii bitmap font data 
// --------------------------------------------------------------------------

// ''Global'' with FILE scope

static const char GLOB_font5x9[96*9] = {

//
// 0x20 ... 3f (interpunction and numbers)
//

/*0x20*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
/*0x21*/ 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x00, 0x00, 
/*0x22*/ 0xd8, 0xd8, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
/*0x23*/ 0x50, 0x50, 0xf8, 0x50, 0xf8, 0x50, 0x50, 0x00, 0x00, 
/*0x24*/ 0x20, 0x78, 0xa0, 0x70, 0x28, 0xf0, 0x20, 0x00, 0x00, 
/*0x25*/ 0xc8, 0xd0, 0x10, 0x20, 0x40, 0x58, 0x98, 0x00, 0x00, 
/*0x26*/ 0x60, 0x90, 0x60, 0x40, 0xa8, 0x90, 0x68, 0x00, 0x00, 
/*0x27*/ 0x30, 0x18, 0x30, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 
/*0x28*/ 0x10, 0x20, 0x40, 0x40, 0x40, 0x20, 0x10, 0x00, 0x00, 
/*0x29*/ 0x40, 0x20, 0x10, 0x10, 0x10, 0x20, 0x40, 0x00, 0x00, 
/*0x2a*/ 0x00, 0x20, 0xa8, 0x70, 0xa8, 0x20, 0x00, 0x00, 0x00, 
/*0x2b*/ 0x00, 0x20, 0x20, 0xf8, 0x20, 0x20, 0x00, 0x00, 0x00, 
/*0x2c*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x30, 0x40, 
/*0x2d*/ 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 
/*0x2e*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x70, 0x20, 0x00, 
/*0x2f*/ 0x08, 0x10, 0x10, 0x20, 0x40, 0x40, 0x80, 0x00, 0x00, 

/*0x30*/ 0x70, 0x88, 0x88, 0xa8, 0x88, 0x88, 0x70, 0x00, 0x00, 
/*0x31*/ 0x20, 0x60, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 
/*0x32*/ 0x70, 0x88, 0x08, 0x10, 0x20, 0x40, 0xf8, 0x00, 0x00, 
/*0x33*/ 0xf0, 0x08, 0x08, 0x70, 0x08, 0x08, 0xf0, 0x00, 0x00, 
/*0x34*/ 0x10, 0x30, 0x50, 0x90, 0xf8, 0x10, 0x10, 0x00, 0x00, 
/*0x35*/ 0xf8, 0x80, 0x80, 0xf0, 0x08, 0x08, 0xf0, 0x00, 0x00, 
/*0x36*/ 0x30, 0x40, 0x80, 0xf0, 0x88, 0x88, 0x70, 0x00, 0x00, 
/*0x37*/ 0xf8, 0x08, 0x10, 0x20, 0x20, 0x40, 0x40, 0x00, 0x00, 
/*0x38*/ 0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x70, 0x00, 0x00, 
/*0x39*/ 0x70, 0x88, 0x88, 0x78, 0x08, 0x10, 0x60, 0x00, 0x00, 
/*0x3a*/ 0x20, 0x70, 0x20, 0x00, 0x00, 0x20, 0x70, 0x20, 0x00, 
/*0x3b*/ 0x20, 0x70, 0x20, 0x00, 0x00, 0x30, 0x18, 0x30, 0x40, 
/*0x3c*/ 0x00, 0x10, 0x20, 0x40, 0x20, 0x10, 0x00, 0x00, 0x00, 
/*0x3d*/ 0x00, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 
/*0x3e*/ 0x00, 0x40, 0x20, 0x10, 0x20, 0x40, 0x00, 0x00, 0x00, 
/*0x3f*/ 0x70, 0x88, 0x10, 0x20, 0x20, 0x00, 0x20, 0x00, 0x00, 

//
// 0x40 ... 0x5f (capitals)
//

/*0x41*/ 0x70, 0x88, 0xb8, 0xa8, 0xb8, 0x80, 0x78, 0x00, 0x00, 
/*0x41*/ 0x20, 0x50, 0x88, 0x88, 0xf8, 0x88, 0x88, 0x00, 0x00, 
/*0x42*/ 0xf0, 0x88, 0x88, 0xf0, 0x88, 0x88, 0xf0, 0x00, 0x00, 
/*0x43*/ 0x70, 0x88, 0x80, 0x80, 0x80, 0x88, 0x70, 0x00, 0x00, 
/*0x44*/ 0xf0, 0x88, 0x88, 0x88, 0x88, 0x88, 0xf0, 0x00, 0x00, 
/*0x45*/ 0xf8, 0x80, 0x80, 0xf0, 0x80, 0x80, 0xf8, 0x00, 0x00, 
/*0x46*/ 0xf8, 0x80, 0x80, 0xf0, 0x80, 0x80, 0x80, 0x00, 0x00, 
/*0x47*/ 0x70, 0x88, 0x80, 0x98, 0x88, 0x88, 0x78, 0x00, 0x00, 
/*0x48*/ 0x88, 0x88, 0x88, 0xf8, 0x88, 0x88, 0x88, 0x00, 0x00, 
/*0x49*/ 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 
/*0x4a*/ 0x38, 0x10, 0x10, 0x10, 0x10, 0x90, 0x60, 0x00, 0x00, 
/*0x4b*/ 0x88, 0x90, 0xa0, 0xc0, 0xa0, 0x90, 0x88, 0x00, 0x00, 
/*0x4c*/ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf8, 0x00, 0x00, 
/*0x4d*/ 0x88, 0xd8, 0xe8, 0xa8, 0xa8, 0x88, 0x88, 0x00, 0x00, 
/*0x4e*/ 0x88, 0xc8, 0xa8, 0xa8, 0x98, 0x88, 0x88, 0x00, 0x00, 
/*0x4f*/ 0x70, 0x98, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 
/*0x50*/ 0xf0, 0x88, 0x88, 0xf0, 0x80, 0x80, 0x80, 0x00, 0x00, 
/*0x51*/ 0x70, 0x88, 0x88, 0x88, 0xa8, 0xa8, 0x70, 0x08, 0x00, 
/*0x52*/ 0xf0, 0x88, 0x88, 0xf0, 0xa0, 0x90, 0x88, 0x00, 0x00, 
/*0x53*/ 0x70, 0x88, 0x80, 0x70, 0x08, 0x88, 0x70, 0x00, 0x00, 
/*0x54*/ 0xf8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 
/*0x55*/ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 
/*0x56*/ 0x88, 0x88, 0x88, 0x88, 0x50, 0x50, 0x20, 0x00, 0x00, 
/*0x57*/ 0x88, 0x88, 0xa8, 0xa8, 0xa8, 0x50, 0x50, 0x00, 0x00, 
/*0x58*/ 0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0x88, 0x00, 0x00, 
/*0x59*/ 0x88, 0x88, 0x88, 0x50, 0x20, 0x20, 0x20, 0x00, 0x00, 
/*0x5a*/ 0xf8, 0x08, 0x10, 0x20, 0x40, 0x80, 0xf8, 0x00, 0x00, 
/*0x5b*/ 0x70, 0x40, 0x40, 0x40, 0x40, 0x40, 0x70, 0x00, 0x00, 
/*0x5c*/ 0x80, 0x40, 0x40, 0x20, 0x10, 0x10, 0x08, 0x00, 0x00, 
/*0x5d*/ 0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x70, 0x00, 0x00, 
/*0x5e*/ 0x20, 0x50, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
/*0x5f*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 

//
// 0x60 ... 0x7f (small letters)
//

/*0x60*/ 0x30, 0x60, 0x30, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 
/*0x61*/ 0x00, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00, 
/*0x62*/ 0x80, 0x80, 0xf0, 0x88, 0x88, 0x88, 0xf0, 0x00, 0x00, 
/*0x63*/ 0x00, 0x00, 0x78, 0x80, 0x80, 0x80, 0x78, 0x00, 0x00, 
/*0x64*/ 0x08, 0x08, 0x78, 0x88, 0x88, 0x88, 0x78, 0x00, 0x00, 
/*0x65*/ 0x00, 0x00, 0x70, 0x88, 0xf8, 0x80, 0x70, 0x00, 0x00, 
/*0x66*/ 0x38, 0x40, 0x40, 0xf0, 0x40, 0x40, 0x40, 0x00, 0x00, 
/*0x67*/ 0x00, 0x00, 0x78, 0x88, 0x70, 0x08, 0x78, 0x88, 0xf0, 
/*0x68*/ 0x80, 0x80, 0xf0, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 
/*0x69*/ 0x20, 0x00, 0x60, 0x20, 0x20, 0x20, 0xf0, 0x00, 0x00, 
/*0x6a*/ 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x90, 0x60, 
/*0x6b*/ 0x80, 0x80, 0x98, 0xb0, 0xe0, 0x90, 0x88, 0x00, 0x00, 
/*0x6c*/ 0x30, 0x48, 0x50, 0x60, 0xc0, 0x40, 0x30, 0x00, 0x00, 
/*0x6d*/ 0x00, 0x00, 0xd0, 0xa8, 0xa8, 0xa8, 0xa8, 0x00, 0x00, 
/*0x6e*/ 0x00, 0x00, 0xf0, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 
/*0x6f*/ 0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 
/*0x70*/ 0x00, 0x00, 0xf0, 0x88, 0x88, 0x88, 0xf0, 0x80, 0x80, 
/*0x71*/ 0x00, 0x00, 0x78, 0x88, 0x88, 0x88, 0x78, 0x08, 0x08, 
/*0x72*/ 0x00, 0x00, 0xb0, 0xc8, 0x80, 0x80, 0x80, 0x00, 0x00, 
/*0x73*/ 0x00, 0x00, 0x78, 0x80, 0x70, 0x08, 0xf0, 0x00, 0x00, 
/*0x74*/ 0x00, 0x40, 0xf8, 0x40, 0x40, 0x40, 0x30, 0x00, 0x00, 
/*0x76*/ 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, 0x78, 0x00, 0x00, 
/*0x76*/ 0x00, 0x00, 0x88, 0x88, 0x50, 0x50, 0x20, 0x00, 0x00, 
/*0x77*/ 0x00, 0x00, 0x88, 0xa8, 0xa8, 0xd0, 0x50, 0x00, 0x00, 
/*0x78*/ 0x00, 0x00, 0x88, 0x50, 0x20, 0x50, 0x88, 0x00, 0x00, 
/*0x79*/ 0x00, 0x00, 0x88, 0x88, 0x48, 0x30, 0x20, 0x40, 0x80, 
/*0x7a*/ 0x00, 0x00, 0xf8, 0x10, 0x20, 0x40, 0xf8, 0x00, 0x00, 
/*0x7b*/ 0x18, 0x20, 0x20, 0xc0, 0x20, 0x20, 0x18, 0x00, 0x00, 
/*0x7c*/ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 
/*0x7d*/ 0xc0, 0x20, 0x20, 0x18, 0x20, 0x20, 0xc0, 0x00, 0x00, 
/*0x7e*/ 0x40, 0xa8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
/*0x7f*/ 0xf8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0xf8, 

};//End of GLOB_font5x9[]



// --------------------------------------------------------------------------
//           graphics stuff part 4: drawing TEXT on the screen
// --------------------------------------------------------------------------


//
// Our own fonted-text drawing funcs.
//

void grp_putpixel( grp_t * pgrp, int x, int y )
	{
	XDrawPoint( pgrp->pD, pgrp->d, pgrp->gc, x, y );
	}

void grp_putbyteicon( grp_t * pgrp, 
	const char * pdata, // Icon bitmap data.
	int width, /* # of horizontal pixels (max. 8) used in each font byte*/
	int hight, /* # of bytes in array pdata[] */
	int x, int y )
	{
	unsigned char m; int ix, iy;
	for ( iy = 0; iy < hight; iy++ )
		{ 
		for ( ix = 0, m = 0x80; ix < width; ix++, m >>= 1 )
			{
			if ( m & pdata[iy] ) 
				{ grp_putpixel( pgrp, x+ix, y+iy ); }
			}
		}
	}

void grp_putchar( grp_t * pgrp, unsigned char c, int x, int y )
	//Uses font data in GLOB_font5x9[].
	{
	if ( 0x20 <= c && c < 0x7f )
		{
		grp_putbyteicon( pgrp, GLOB_font5x9 + 9*( c - 0x20 ),
		   6, 9, x, y );
		}
	}
void grp_paintchar( grp_t * pgrp,
	unsigned char c, int x, int y,
	long color )
	//Uses font data in GLOB_font5x9[].
	// First clear, then show character in given color.
	{
	grp_setoperation( pgrp, GXclear );
	grp_setcolor( pgrp, 0xffffffff );
	grp_putchar( pgrp, c, x, y );

	grp_setoperation( pgrp, GXset );
	grp_setcolor( pgrp, color );
	grp_putchar( pgrp, c, x, y );
	}



// #define CHARWID 7  Defined in 'xgrp.h'.
// #define CHARHI 12

#include <assert.h>
#include <stdio.h>  // For 'vsprintf()'.

void grp_strvprintf( grp_t * pgrp, 
	int x, int y, 
	long color,
	const char * pFormat, va_list valist )
	// Extra formatting characters: 
	//    '$' at begin of line = Center this line horizontally.
	{
	char buf[256];
	int nText;
	int i;
	int xstart = x;

	nText = vsprintf( buf, pFormat, valist );
	assert( nText < sizeof(buf) );

	for ( i = 0; i < nText; i++ )
		{
		if ( buf[i] == '$' ) //Center line horizontally
			{
			int n = 1; 
			while ( buf[i+n] != '\n' && i+n < nText ) 
				{ 
				//if ( buf[i+n] == '\t' ) { n += 8; }
				//else { n++; }
				n++;
				}
			x = ( pgrp->ourScreenWidth - n*CHARWID ) / 2;
			}
		else if ( buf[i] == '\t' ) { x += 8 * CHARWID; }
		else if ( buf[i] == '\n' ) { y += CHARHI; x = xstart; }
		else 
			{ 
			if ( 0 <= x && x < pgrp->ourScreenWidth && 
			   0 <= y && y < pgrp->ourScreenHeight )
				{ 
				//grp_putchar( pgrp, buf[i], x, y ); 
				grp_paintchar( pgrp, buf[i], x, y, color ); 
				}
			x += CHARWID;
			}

		if ( y > pgrp->ourScreenHeight ) { break; }
		}
	}


void grp_strprintf( grp_t * pgrp, 
	int x, int y, 
	long color,
	const char * pFormat, ... )
	// Extra formatting characters: 
	//    '$' at begin of line = Center this line horizontally.
	{
	va_list valist;

	va_start( valist, pFormat );
	grp_strvprintf( pgrp, x, y, color, pFormat, valist );
	va_end( valist );
	}


