DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T c

⟦8962c2458⟧ TextFile

    Length: 15344 (0x3bf0)
    Types: TextFile
    Names: »clover.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/X/Clover/clover.c« 

TextFile

/* $Header$ */

/* 
 * clover.c - Gosper's LispM "smoking clover" color table hack
 * 
 * Author:	Christopher A. Kent
 * 		Western Research Laboratory
 * 		Digital Equipment Corporation
 * Date:	Wed Feb 22 1989
 */

/*
 * $Log$
 */

static char rcs_ident[] = "$Header$";

#include <stdio.h>

#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/Xresource.h>
#include <X11/StringDefs.h>
#include <X11/Intrinsic.h>
#include <X11/cursorfont.h>

#define	NCOLORS	16
#define	R	5432
#define S	4321

#define	MIN(x, y)	((x) < (y))?(x):(y)
#define	MAX(x, y)	((x) > (y))?(x):(y)
#define	CEIL(a, b)	((a)+(b)-1)/(b)
#define	FLOOR(a, b)	CEIL((a)-(b), (b))

Display		*dpy;			/* the display */
Window		w;			/* the window */
int		screen;			/* the screen to use */
Colormap	colorMap;		/* private Colormap resource */
XColor		*colors;		/* color sets */
int		cmap = 0;		/* which color set to use now */
Visual		*v;			/* the appropriate visual */
int		nColors;		/* how many colors (per v) */
Pixel		fg_pixel;		/* specified fg color (ignored) */
Pixel		bg_pixel;		/* specified bg color */
Pixel		borderColor;		/* ... border color */
Cardinal	borderWidth;		/* ... border width */
String		geometry;		/* ... geometry */
int		posX, posY;		/* ... position */
int		width, height;		/* ... sizes */
int		one = 1;
int		radius = R;		/* ... 'radius' of clover */
int		speed = S;		/* ... initial speed */
int		maxColors = NCOLORS;	/* ... maximum colors to use */

char		*malloc();

#define	XtNgeometry	"geometry"
#define	XtCGeometry	"Geometry"
#define	XtNcolors	"colors"
#define	XtCColors	"Colors"
#define	XtNradius	"radius"
#define	XtCRadius	"Radius"
#define	XtNspeed	"speed"
#define	XtCSpeed	"Speed"

static XrmOptionDescRec opTable[] = {
{"-geometry",   "*geometry",	XrmoptionSepArg,  (caddr_t) NULL},
{"-colors",	"*colors",	XrmoptionSepArg,  (caddr_t) NULL}, 
{"-radius",	"*radius",	XrmoptionSepArg,  (caddr_t) NULL},
{"-speed",	"*speed",	XrmoptionSepArg,  (caddr_t) NULL}, 
};

static XtResource resources[] = {	/* don't really need some of these */
        {XtNforeground,  XtCForeground,  XtRPixel,  sizeof(Pixel), 
         (Cardinal)&fg_pixel,  XtRString,  "black"}, 
        {XtNbackground,  XtCBackground,  XtRPixel,  sizeof(Pixel), 
         (Cardinal)&bg_pixel,  XtRString,  "white"}, 
        {XtNborderWidth,  XtCBorderWidth,  XtRInt,  sizeof(int), 
         (Cardinal)&borderWidth,  XtRInt,  (caddr_t) &one}, 
        {XtNborder,  XtCBorderColor,  XtRPixel,  sizeof(Pixel), 
         (Cardinal)&borderColor,  XtRString,  "black"},
        {XtNgeometry,  XtCGeometry,  XtRString,  sizeof(char *), 
         (Cardinal)&geometry,  XtRString,  (caddr_t) NULL}, 
        {XtNcolors,  XtCColors,  XtRInt,  sizeof(int), 
         (Cardinal)&maxColors,  XtRInt,  (caddr_t) &maxColors}, 
        {XtNradius,  XtCRadius,  XtRInt,  sizeof(int), 
         (Cardinal)&radius,  XtRInt,  (caddr_t) &radius}, 
        {XtNspeed,  XtCSpeed,  XtRInt,  sizeof(int), 
         (Cardinal)&speed,  XtRInt,  (caddr_t) &speed}, 
};

main(argc, argv)
char	**argv;
{
	Widget		widg;
	XEvent		xev;
	XSizeHints	hint;
	char		text[10];
	int		i;
	KeySym		key;
	Pixmap		p, cloverPixmap();
	XWindowAttributes	attr;
	Bool		mapped = False;
	Cursor		spiral;
	int		cycleDelay, cycleDecrement = 0, currentSpeed;

	/*
	 * We cheat here by using the Toolkit to do the initialization work.
	 * We just ignore the top-level widget that gets created.
	 */

	widg = XtInitialize("clover", "clover", opTable, XtNumber(opTable), 
			&argc, argv);
	dpy = XtDisplay(widg);
	screen = DefaultScreen(dpy);

	XtGetApplicationResources(widg, (caddr_t) NULL, resources, 
				XtNumber(resources), NULL, (Cardinal) 0);

	posX = posY = -1;
	width = height = 0;
	if (geometry) {
		int	mask, gx, gy, gw, gh;

		mask = XParseGeometry(geometry, &gx, &gy, &gw, &gh);
		if (mask & WidthValue)
			width = gw;
		if (mask & HeightValue)
			height = gh;
		if (mask & XValue)
			if (mask & XNegative)
				posX = DisplayWidth(dpy, screen) - 
					width + posX;
			else
				posX = gx;
		if (mask & YValue)
			if (mask & YNegative)
				posY = DisplayHeight(dpy, screen) -
					height + posY;
			else
				posY = gy;
	}

	hint.width = width = width ? width : DisplayWidth(dpy, screen);
	hint.height = height = height ? height : DisplayHeight(dpy, screen);
	hint.x = posX >= 0 ? posX : (DisplayWidth(dpy, screen) - width)/2;
	hint.y = posY >= 0 ? posY : (DisplayHeight(dpy, screen) - height)/2;
	hint.flags = PPosition | PSize;

	w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 
			hint.x, hint.y, 
			hint.width, hint.height, 
			borderWidth, 
			BlackPixel(dpy, screen), 
			WhitePixel(dpy, screen));
	XSetStandardProperties(dpy, w, "Smoking Clover", "clover", None, 
				argv, argc, &hint);
	XSelectInput(dpy, w, 
		ButtonPressMask | ExposureMask | 
		KeyPressMask | StructureNotifyMask);

	buildColormaps();

	p = cloverPixmap(radius);
	
	spiral = XCreateFontCursor(dpy, XC_box_spiral);
	
	XSynchronize(dpy, True);
	XMapRaised(dpy, w);

	cycleDelay = speed;
	currentSpeed = speed;
	while(1) {
		if (mapped && !XPending(dpy)) {
			if (--cycleDelay <= 0) {
				cycle();
				cycleDelay = currentSpeed;
				if (++cycleDecrement%16 == 0)
					currentSpeed >>= 1;
			}
			continue;
		}

		XNextEvent(dpy, &xev);
		switch(xev.type) {
		case Expose:
		case MapNotify:
			mapped = True;
			break;

		case ConfigureNotify:
			XSynchronize(dpy, True);
			XDefineCursor(dpy, w, spiral);
			XGetWindowAttributes(dpy, w, &attr);
			width = attr.width;
			height = attr.height;
			XFreePixmap(dpy, p);
			p = cloverPixmap(radius);
			XClearWindow(dpy, w);
			XUndefineCursor(dpy, w);
			XSynchronize(dpy, False);
			break;
			
		case UnmapNotify:
			mapped = False;
			break;

		case MappingNotify:
			XRefreshKeyboardMapping(&xev);
			break;

		case ButtonPress:
			currentSpeed = speed;
			break;

		case KeyPress:
			i = XLookupString(&xev, text, 10, &key, 0);
			if (i == 1 && text[0] == 'q')
				exit(0);
			break;
		}
	}
}

/*
 * Decide how many colors to use (depends on the available Visuals).  Create a
 * private Colormap and that many sets of color cells.  Load the first set with
 * a random selection of colors, and the rest so that the colors slide through
 * the pixel values.
 *
 * It might seem more straightforward to allocate nColors Colormaps, load them
 * and explicitly install them, but well-behaved clients should not install
 * Colormaps explicitly.  Rather, they should change the Colormap attribute and
 * wait for the window manager to do the installation.  This slows things down
 * a lot, so we do explict XStoreColors calls to cycle through the color sets.
 *
 */

/*
 * There are nColors sets of (nColors+1) colors each.  Slots [0..nColors-1]
 * rotate in subsequent sets; slot nColors is always the background color.
 */

#define	COLOR(a, b)	colors[((a)*(nColors+1))+(b)]

buildColormaps()
{
	int		i, j;
	XColor		*c1, *c2, tempColor, bgColor;
	Visual		*findVisual();

	v = findVisual();
	if (v == NULL) {
		printf("Sorry, clover needs a writable colormap to run\n");
		exit(0);
	}

	/*
	 * Find out the color values of the specified background...
	 */

	bgColor.pixel = bg_pixel;
	XQueryColor(dpy, DefaultColormap(dpy, screen), &bgColor);
	
	colors = (XColor *) malloc((nColors+1) * nColors * sizeof(XColor));

	colorMap = XCreateColormap(dpy, w, v, AllocAll);

	/*
	 * allocate random colors into first color map, with bg at the end
	 */
	
	for (i = 0; i < nColors; i++) {
		c1 = &COLOR(0, i);
		c1->pixel = i;
		if (i == 0) {
			c1->red = 0;
			c1->green = (unsigned short) -65535/6;
			c1->blue = 65535/6;
		} else {
			c2 = &COLOR(0, i-1);
			c1->red = c2->red - random()/6;
			c1->green = c2->green - random()/6;
			c1->blue = c2->blue - random()/6;
		}
		c1->flags = DoRed | DoGreen | DoBlue;

		for (j = 1; j < nColors; j++)
			COLOR(j, i).pixel = i;
	}
	c1 = &COLOR(0, nColors);
	c1->pixel = nColors;
	c1->red = bgColor.red;
	c1->green = bgColor.green;
	c1->blue = bgColor.blue;
	c1->flags = DoRed | DoGreen | DoBlue;
	XStoreColors(dpy, colorMap, colors, nColors+1);


	/* rotate colors (but not pixels) through other colors */
	for (i = 1; i < nColors; i++) {
		tempColor = COLOR(i-1, 0);
		for (j = 0; j < nColors-1; j++) {
			c1 = &COLOR(i, j);
			c2 = &COLOR(i-1, j+1);
			c1->red = c2->red;
			c1->green = c2->green;
			c1->blue = c2->blue;
			c1->flags = c2->flags;
		}
		c1 = &COLOR(i, nColors-1);
		c2 = &tempColor;
		c1->red = c2->red;
		c1->green = c2->green;
		c1->blue = c2->blue;
		c1->flags = c2->flags;

		COLOR(i, nColors) = COLOR(i-1, nColors);
	}
}

/*
 * Create the image in memory. Assumes several things have already been set up
 * in global variables.
 */

char	*bits;
int	trace = 0;
#define	getPixel(x, y)		bits[(y)*width+(x)]
#define	putPixel(x, y, v)	bits[(y)*width+(x)] = (v)%nColors;
xputPixel(x, y, v)
{
	printf("%d, %d -> %d/%d\n", x, y, v, v%nColors);
	xputPixel(x, y, v);
}
 
Pixmap
cloverPixmap(r)
{
	XImage	*im, *simpleImage(), *cloverImage();
	Pixmap	p;
	XSetWindowAttributes	attr;

	im = cloverImage(r);
	p = XCreatePixmap(dpy, w, width, height, XDefaultDepth(dpy, screen));
	XPutImage(dpy, p, DefaultGC(dpy, screen), im,
					0, 0, 0, 0, width, height);
	XDestroyImage(im);

	attr.background_pixmap = p;
	attr.colormap = colorMap;
	attr.bit_gravity = CenterGravity;
	XChangeWindowAttributes(dpy, w, 
			CWBackPixmap | CWColormap | CWBitGravity,
			&attr);

	return p;
}

XImage *
simpleImage()
{
	int	i, j;
	XImage	*im;

	bits = malloc(width * height);

	for (i = 0; i < height; i++)
		for (j = 0; j< width; j++)
			bits[i * width + j] = i % nColors;

	im = XCreateImage(dpy, v, 
			XDefaultDepth(dpy, screen), 
			ZPixmap, 0, bits, width, height, 0, width);
	free(bits);
	return im;
}

/*
 * Basically the algorithm is to draw a series of Bresenham lines from the
 * center.  The "interference pattern" is built by incrementing the pixel value
 * of (x,y) every time it's touched; the resulting pattern is a product of the
 * vagaries of integer arithmetic.
 */

XImage *
cloverImage(r)
{
	XImage	*im;
	int	maxX, maxY, midX, midY, x, f, y;
	int	v, yy, x1, y1;
	int	i, o;
	char	*b;

	bits = malloc(width * height);
	if (bits == NULL) {
		perror("No memory");
		exit(-1);
	}

	maxX = width - 1;
	maxY = height - 1;
	midX = maxX / 2;
	midY = maxY / 2;

	for (y = 0; y < height; y++) {
		b = &bits[y*width];
		for (x = 0; x < width; x++)
			*b++ = nColors;		/* fill in background */
	}

	/*
	 * Fill in the first semi-quadrant.
	 */

	x = r;
	f = 0;
	for (y = 0; y < x; y++) {
		if (f > x) {
			x--;
			f = f-x - (x-1);
		}
		clipLine(midX, midY, x+midX, y+midY, 0, 0, maxX, maxY);
		f = f+y + y+1;
	}

	/*
	 * Copy to the other seven, adjusting the horizontal and diagonal.
	 */

	for (x = midX; x < maxX; x++) {
/*		putPixel(x, midY, (getPixel(x, midY) << 1) - 1);*/
		if (x - midX + midY <= maxY)
			putPixel(x, x-midX+midY, 
				(getPixel(x, x-midX+midY) << 1) - 1);
		yy = MIN(maxY, x + midY - midX);
		for (y = midY; y <= yy; y++) {
			v = getPixel(x, y);
			x1 = x;
			y1 = y;
			for (i = 0; i < 4; i++) {
				if ((y1 < maxY) && (y1 > 0)) {
					putPixel(midX + midX - x1, y1, v);
					putPixel(x1, y1, v);
				}
				o = x1;
				x1 = midX + midY - y1;
				y1 = midY + o - midX;
			}
		}
	}

	im = XCreateImage(dpy, v, 
			XDefaultDepth(dpy, screen), 
			ZPixmap, 0, bits, width, height, 0, width);
	free(bits);
	return im;
}

/*
 * (xe, ye) and (xf, yf) are the corners of a rectangle to clip a line to.
 * (x0, y0) and (xn, yn) are the endpoints of the line to clip.
 * The function argument that's being computed is the semi-quadrant;
 *  dx and dy are used to determine whether we're above or below the diagonal,
 *  since (x0, y0) is always the midpoint of the pattern.
 * (The LispM has the origin at lower left, instead of upper left, so
 * the numbers don't correspond to the normal Cartesian plane quadrants.)
 *
 * This routine is very general, but the calling code only builds lines in the
 * first semi-quadrant and then copies them everywhere else.
 */

clipLine(x0, y0, xn, yn, xe, ye, xf, yf)
{
	int	dx, dy;

	dx = abs(xn - x0);
	dy = abs(yn - y0);

	if (xn > x0) {				/* moving right */
		if (yn >= y0) {			/* moving up */
			if (dx > dy)		/* below diagonal */
				line(0, x0, y0, dx, dy, xe, ye, xf, yf);
			else
				line(1, y0, x0, dy, dx, ye, xe, yf, xf);
		} else {
			if (dx > dy)
				line(7, x0, -y0, dx, dy, xe, -yf, xf, -ye);
			else
				line(6, -y0, x0, dy, dx, -yf, xe, -ye, xf);
		}
	} else {
		if (yn >= y0) {
			if (dx > dy)
				line(3, -x0, y0, dx, dy, -xf, ye, -xe, yf);
			else
				line(2, y0, -x0, dy, dx, ye, -xf, yf, -xe);
		} else {
			if (dx > dy)
				line(4, -x0, -y0, dx, dy, -xf, -yf, -xe, -ye);
			else
				line(5, -y0, -x0, dy, dx, -yf, -xf, -ye, -xe);
		}
	}
}

#define	plot(x, y)	putPixel((x), (y), getPixel((x), (y))+1)

/*
 * Clip symmetric segment (x0, y0) thru (xn, yn) to the rectangle 
 * (xe, ye) < (xf, yf).
 *
 * The original says:
 *
 * "This routine incorrectly assumes that the subsegment starts prior to the
 * midpoint of the supersegment.  The 'divide for nearest integer' (i.e.,
 * divide for remainder of minimum magnitude), which is simulated by the FLOOR
 * and CEIL of num and (dx <<1), always rounds up on the half integer case, but
 * should round down (for symmetry) if startup is in 2nd half. It would be
 * nice to have these other flavors of divide.'
 */

line(fun, x0, y0, dx, dy, xe, ye, xf, yf)
{
	int	x, num, lx;
	int	xx, y, x00, f;
	int	x11;

	x = MAX(x0,  MAX(xe, 
			(dy == 0)? xe :
				   x0 + CEIL(dx * (((ye - y0)<<1) - 1), 
						(dy << 1))));
	num = dx + 2*dy*(x - x0);
	lx = MIN(xf, (dy == 0) ? xf :
				x0 + CEIL(dx * (((yf - y0)<<1) - 1), 
						(dy << 1)));
	xx = MIN(lx, x0 + (dx>>1));
	y = y0 + FLOOR(num, (dx<<1));
	f = (FLOOR(num, (dx<<1)) - dx) >> 1;

	for (x00 = x; x00 < xx; x00++,f+=dy) {
		if (f+f > dx) {
			f -= dx;
			y++;
		}
		switch(fun) {
		case 0:	plot(x00, y);	break;
		case 1:	plot(y, x00);	break;
		case 2:	plot(-y, x00);	break;
		case 3:	plot(-x00, y);	break;
		case 4:	plot(-x00, -y);	break;
		case 5:	plot(-y, -x00);	break;
		case 6:	plot(y, -x00);	break;
		case 7:	plot(x00, -y);	break;
		}
	}

	for (x11 = x00; x11 < lx; x11++, f+=dy) {
		if (f + f > dx) {
			f -= dx;
			y++;
		}
		switch(fun) {
		case 0:	plot(x11, y);	break;
		case 1:	plot(y, x11);	break;
		case 2:	plot(-y, x11);	break;
		case 3:	plot(-x11, y);	break;
		case 4:	plot(-x11, -y);	break;
		case 5:	plot(-y, -x11);	break;
		case 6:	plot(y, -x11);	break;
		case 7:	plot(x11, -y);	break;
		}
	}
}

/*
 * Install the next set of colors in the cycle.
 */

cycle()
{
	cmap = ++cmap % nColors;
	XStoreColors(dpy, colorMap, &colors[cmap * (nColors+1)], nColors+1);
}

/*
 * Find an appropriate visual (and set the screen and nColors as a side effect)
 * to run on.
 */

int	classes[] = {
	PseudoColor,
	DirectColor,
	GrayScale, 
	0
};

Visual *
findVisual()
{
	int		howMany, i, max, *class;
	XVisualInfo	*vip, vTemplate;

	for (class = classes; *class; class++) {
		vTemplate.class = *class;
		vip = XGetVisualInfo(dpy, VisualClassMask, 
					&vTemplate, &howMany);
		if (vip) {
			max = 0;
			for (i = 0; i < howMany; i++) {
				if (vip->colormap_size > max)
					v = vip->visual;
					max = vip->colormap_size;
			}
			screen = vip->screen;
			nColors = MIN(maxColors, vip->colormap_size);
			nColors--;
			return v;
		}
	}
	return NULL;
}