|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T c
Length: 15344 (0x3bf0)
Types: TextFile
Names: »clover.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
└─⟦this⟧ »EUUGD18/X/Clover/clover.c«
/* $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;
}