#include <math.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/Xfuncs.h>
#include <X11/keysym.h>
#include <stdio.h>
#ifdef MULTIBUFFER
#include <X11/extensions/multibuf.h>
#endif
#ifdef MULTITHREAD
#include <X11/Xthreads.h>
#endif
#include <X11/Xos.h>
#define MIN_ICO_WIDTH 5
#define MIN_ICO_HEIGHT 5
#define DEFAULT_ICO_WIDTH 150
#define DEFAULT_ICO_HEIGHT 150
#define DEFAULT_DELTAX 13
#define DEFAULT_DELTAY 9
#include "polyinfo.h"
Polyinfo polygons[] = {
#include "allobjs.h"
};
#define NumberPolygons sizeof(polygons)/sizeof(polygons[0])
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
typedef double Transform3D[4][4];
typedef struct {
int prevX, prevY;
unsigned long *plane_masks;
unsigned long enplanemask;
XColor *colors;
unsigned long *pixels;
} DBufInfo;
struct closure {
int planesperbuf;
int pixelsperbuf;
int totalplanes;
int totalpixels;
unsigned long *plane_masks;
unsigned long pixels[1];
int dbufnum;
DBufInfo bufs[2];
DBufInfo *drawbuf, *dpybuf;
Window win, draw_window;
int winW, winH;
Colormap cmap;
GC gcontext;
#ifdef MULTIBUFFER
Multibuffer multibuffers[2];
#endif
int nplanesets;
char drawn[MAXNV][MAXNV];
Transform3D xform;
Point3D xv[2][MAXNV];
int xv_buffer;
double wo2, ho2;
#ifdef MULTITHREAD
int thread_num;
#endif
};
Display *dpy;
Atom wm_delete_window;
char *Primaries[] = {
"red", "green", "blue", "yellow", "cyan", "magenta"
};
#define NumberPrimaries 6
const char *help_message[] = {
"where options include:",
"-display host:dpy X server to use",
" -geometry geom geometry of window to use",
" -size WxH size of object to rotate",
" -delta +X+Y amount by which to move object",
" -r draw in the root window",
" -d number dashed line pattern for wire frames",
" -bg color background color",
" -colors color ... codes to use on sides",
" -p# use # (1 through 8) primary colors",
#ifdef MULTIBUFFER
" -dbl use double buffering (extension if present)",
#else
" -dbl use double buffering (software only)",
#endif
" -softdbl use software double buffering",
" -noedges don't draw wire frame edges",
" -faces draw faces",
" -copy use multibuffer update action Copied",
" -untouched use multibuffer update action Untouched",
" -undefined use multibuffer update action Undefined",
" -lw number line width to use",
" -i invert",
" -sleep number seconds to sleep in between draws",
" -obj objname type of polyhedral object to draw",
" -objhelp list polyhedral objects available",
#ifdef MULTITHREAD
" -threads number number of windows, each its own thread",
#endif
NULL};
const char *ProgramName;
const char *geom = NULL;
int useRoot = 0;
int dash = 0;
char **colornames;
#ifdef MULTIBUFFER
int update_action = MultibufferUpdateActionBackground;
#endif
int linewidth = 0;
int multibufext = 0;
int dblbuf = 0;
int numcolors = 0;
const char *background_colorname = NULL;
int doedges = 1;
int dofaces = 0;
int invert = 0;
const char *ico_geom = NULL;
const char *delta_geom = NULL;
Polyinfo *poly;
int dsync = 0;
int msleepcount = 0;
#ifdef MULTITHREAD
int thread_count;
#ifdef XMUTEX_INITIALIZER
xmutex_rec count_mutex = XMUTEX_INITIALIZER;
#else
xmutex_rec count_mutex;
#endif
xcondition_rec count_cond;
#endif
#if defined(__GNUC__) && \
((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 7)))
void icoFatal (const char *fmt, const char *a0) __attribute__((__noreturn__));
#endif
void
icoFatal(fmt,a0)
const char *fmt;
const char *a0;
{
fprintf(stderr, "%s: ", ProgramName);
fprintf(stderr, fmt, a0);
fprintf(stderr, "\n");
exit(1);
}
static char *
xalloc(unsigned int nbytes)
{
char *p;
p = malloc(nbytes);
if (p)
return p;
fprintf(stderr, "%s: no more memory\n", ProgramName);
exit(1);
}
static void
msleep(unsigned int msecs)
{
struct timeval timeout;
timeout.tv_sec = msecs / 1000; timeout.tv_usec = (msecs % 1000) * 1000;
select(1,NULL,NULL,NULL,&timeout);
}
static void
IdentMat(Transform3D m)
{
int i;
int j;
for (i = 3; i >= 0; --i) {
for (j = 3; j >= 0; --j)
m[i][j] = 0.0;
m[i][i] = 1.0;
}
}
static void
ConcatMat(Transform3D l, Transform3D r, Transform3D m)
{
int i;
int j;
for (i = 0; i < 4; ++i)
for (j = 0; j < 4; ++j)
m[i][j] = l[i][0] * r[0][j]
+ l[i][1] * r[1][j]
+ l[i][2] * r[2][j]
+ l[i][3] * r[3][j];
}
static void
FormatRotateMat(char axis, double angle, Transform3D m)
{
double s, c;
IdentMat(m);
s = sin(angle);
c = cos(angle);
switch (axis)
{
case 'x':
m[1][1] = m[2][2] = c;
m[1][2] = s;
m[2][1] = -s;
break;
case 'y':
m[0][0] = m[2][2] = c;
m[2][0] = s;
m[0][2] = -s;
break;
case 'z':
m[0][0] = m[1][1] = c;
m[0][1] = s;
m[1][0] = -s;
break;
}
}
static void
PartialNonHomTransform(int n, Transform3D m, const Point3D *in, Point3D *out)
{
for (; n > 0; --n, ++in, ++out) {
out->x = in->x * m[0][0] + in->y * m[1][0] + in->z * m[2][0];
out->y = in->x * m[0][1] + in->y * m[1][1] + in->z * m[2][1];
out->z = in->x * m[0][2] + in->y * m[1][2] + in->z * m[2][2];
}
}
static Bool
predicate(Display *display, XEvent *event, XPointer args)
{
Window w = (Window) args;
return event->xany.window == w;
}
static void
icoClearArea(struct closure *closure, int x, int y, int w, int h)
{
if (multibufext && dblbuf)
return;
if (dblbuf || dofaces) {
XSetForeground(dpy,
closure->gcontext,
closure->drawbuf->pixels[0]);
XFillRectangle(dpy,closure->win,closure->gcontext,x,y,w,h);
} else {
XClearArea(dpy,closure->win,x,y,w,h,0);
}
}
static void
initPoly(struct closure *closure, Polyinfo *poly, int icoW, int icoH)
{
Point3D *vertices = poly->v;
int NV = poly->numverts;
Transform3D r1;
Transform3D r2;
FormatRotateMat('x', 5 * 3.1416 / 180.0, r1);
FormatRotateMat('y', 5 * 3.1416 / 180.0, r2);
ConcatMat(r1, r2, closure->xform);
memcpy((char *)closure->xv[0], (char *)vertices, NV * sizeof(Point3D));
closure->xv_buffer = 0;
closure->wo2 = icoW / 2.0;
closure->ho2 = icoH / 2.0;
}
static void
setDrawBuf (struct closure *closure, int n)
{
XGCValues xgcv;
unsigned long mask;
#ifdef MULTIBUFFER
if (multibufext && dblbuf) {
closure->win = closure->multibuffers[n];
n = 0;
}
#endif
closure->drawbuf = closure->bufs+n;
xgcv.foreground = closure->drawbuf->pixels[closure->pixelsperbuf-1];
xgcv.background = closure->drawbuf->pixels[0];
mask = GCForeground | GCBackground;
if (dblbuf && !multibufext) {
xgcv.plane_mask = closure->drawbuf->enplanemask;
mask |= GCPlaneMask;
}
XChangeGC(dpy, closure->gcontext, mask, &xgcv);
}
static void
setDisplayBuf(struct closure *closure, int n, int firsttime)
{
#ifdef MULTIBUFFER
if (multibufext && dblbuf) {
XmbufDisplayBuffers (dpy, 1, &closure->multibuffers[n], msleepcount, 0);
if (!firsttime)
return;
n = 0;
}
#endif
closure->dpybuf = closure->bufs+n;
if (closure->totalpixels > 2)
XStoreColors(dpy,closure->cmap,closure->dpybuf->colors,closure->totalpixels);
}
static void
setBufColor(struct closure *closure, int n, XColor *color)
{
int i,j,cx;
DBufInfo *b;
unsigned long pix;
for (i=0; i<closure->nplanesets; i++) {
b = closure->bufs+i;
for (j=0; j<(dblbuf&&!multibufext?closure->pixelsperbuf:1); j++) {
cx = n + j*closure->pixelsperbuf;
pix = b->colors[cx].pixel;
b->colors[cx] = *color;
b->colors[cx].pixel = pix;
b->colors[cx].flags = DoRed | DoGreen | DoBlue;
}
}
}
static void
drawPoly(struct closure *closure, Polyinfo *poly, GC gc,
int icoX, int icoY, int icoW, int icoH, int prevX, int prevY)
{
int *f = poly->f;
int NV = poly->numverts;
int NF = poly->numfaces;
int p0;
int p1;
XPoint *pv2;
XSegment *pe;
Point3D *pxv;
XPoint v2[MAXNV];
XSegment edges[MAXEDGES];
int i;
int j,k;
int *pf;
int facecolor;
int pcount;
double pxvz;
XPoint ppts[MAXEDGESPERPOLY];
closure->xv_buffer = !closure->xv_buffer;
PartialNonHomTransform(NV, closure->xform,
closure->xv[!closure->xv_buffer],
closure->xv[closure->xv_buffer]);
pxv = closure->xv[closure->xv_buffer];
pv2 = v2;
for (i = NV - 1; i >= 0; --i) {
pv2->x = (int) ((pxv->x + 1.0) * closure->wo2) + icoX;
pv2->y = (int) ((pxv->y + 1.0) * closure->ho2) + icoY;
++pxv;
++pv2;
}
pxv = closure->xv[closure->xv_buffer];
pv2 = v2;
pf = f;
pe = edges;
bzero(closure->drawn, sizeof(closure->drawn));
if (dblbuf)
setDrawBuf(closure, closure->dbufnum);
if (dofaces && !(multibufext && dblbuf)) {
if (dblbuf)
icoClearArea(closure,
closure->drawbuf->prevX - linewidth/2,
closure->drawbuf->prevY - linewidth/2,
icoW + linewidth + 1, icoH + linewidth + 1);
icoClearArea(closure,
prevX - linewidth/2, prevY - linewidth/2,
icoW + linewidth + 1, icoH + linewidth + 1);
}
if (dsync)
XSync(dpy, 0);
for (i = NF - 1; i >= 0; --i, pf += pcount) {
pcount = *pf++;
pxvz = 0.0;
for (j=0; j<pcount; j++) {
p0 = pf[j];
pxvz += pxv[p0].z;
}
if (pxvz<0.0)
continue;
if (dofaces) {
if (numcolors)
facecolor = i%numcolors + 1;
else
facecolor = 1;
XSetForeground(dpy, gc,
closure->drawbuf->pixels[facecolor]);
for (j=0; j<pcount; j++) {
p0 = pf[j];
ppts[j].x = pv2[p0].x;
ppts[j].y = pv2[p0].y;
}
XFillPolygon(dpy, closure->win, gc, ppts, pcount,
Convex, CoordModeOrigin);
}
if (doedges) {
for (j=0; j<pcount; j++) {
if (j<pcount-1) k=j+1;
else k=0;
p0 = pf[j];
p1 = pf[k];
if (!closure->drawn[p0][p1]) {
closure->drawn[p0][p1] = 1;
closure->drawn[p1][p0] = 1;
pe->x1 = pv2[p0].x;
pe->y1 = pv2[p0].y;
pe->x2 = pv2[p1].x;
pe->y2 = pv2[p1].y;
++pe;
}
}
}
}
if (doedges) {
if (dofaces) {
XSetForeground(dpy, gc, closure->drawbuf->pixels[0]);
} else {
if (dblbuf && !multibufext)
icoClearArea(closure,
closure->drawbuf->prevX - linewidth/2,
closure->drawbuf->prevY - linewidth/2,
icoW + linewidth + 1,
icoH + linewidth + 1);
if (!(multibufext && dblbuf))
icoClearArea(closure,
prevX - linewidth/2,
prevY - linewidth/2,
icoW + linewidth + 1,
icoH + linewidth + 1);
if (dblbuf || dofaces) {
XSetForeground(dpy, gc, closure->drawbuf->pixels[
closure->pixelsperbuf-1]);
}
}
XDrawSegments(dpy, closure->win, gc, edges, pe - edges);
}
if (dsync)
XSync(dpy, 0);
if (dblbuf) {
closure->drawbuf->prevX = icoX;
closure->drawbuf->prevY = icoY;
setDisplayBuf(closure, closure->dbufnum, 0);
}
if (dblbuf)
closure->dbufnum = 1 - closure->dbufnum;
if (!(multibufext && dblbuf) && msleepcount > 0)
msleep(msleepcount);
}
static void
initDBufs(struct closure *closure, int fg, int bg, int planesperbuf)
{
int i,j,jj,j0,j1,k,m,t;
DBufInfo *b;
XColor bgcolor, fgcolor;
closure->nplanesets = (dblbuf && !multibufext ? 2 : 1);
closure->planesperbuf = planesperbuf;
closure->pixelsperbuf = 1<<planesperbuf;
closure->totalplanes = closure->nplanesets * planesperbuf;
closure->totalpixels = 1<<closure->totalplanes;
closure->plane_masks = (unsigned long *)
xalloc(closure->totalplanes * sizeof(unsigned long));
closure->dbufnum = 0;
for (i=0; i < closure->nplanesets; i++) {
b = closure->bufs+i;
b->plane_masks = closure->plane_masks + (i*planesperbuf);
b->colors = (XColor *)
xalloc(closure->totalpixels * sizeof(XColor));
b->pixels = (unsigned long *)
xalloc(closure->pixelsperbuf * sizeof(unsigned long));
}
if (closure->totalplanes == 1) {
closure->pixels[0] = bg;
closure->plane_masks[0] = fg ^ bg;
} else {
t = XAllocColorCells(dpy,closure->cmap,0,
closure->plane_masks,closure->totalplanes, closure->pixels,1);
if (t==0) {
icoFatal("can't allocate enough color planes", NULL);
}
}
fgcolor.pixel = fg;
bgcolor.pixel = bg;
XQueryColor(dpy,closure->cmap,&fgcolor);
XQueryColor(dpy,closure->cmap,&bgcolor);
setBufColor(closure, 0,&bgcolor);
setBufColor(closure, 1,&fgcolor);
for (i=0; i<closure->nplanesets; i++) {
b = closure->bufs+i;
for (j0=0; j0<(dblbuf&&!multibufext?closure->pixelsperbuf:1); j0++) {
for (j1=0; j1<closure->pixelsperbuf; j1++) {
j = (j0<<closure->planesperbuf)|j1;
if (i==0) jj=j;
else jj= (j1<<closure->planesperbuf)|j0;
b->colors[jj].pixel = closure->pixels[0];
for (k=0, m=j; m; k++, m=m>>1) {
if (m&1)
b->colors[jj].pixel ^= closure->plane_masks[k];
}
b->colors[jj].flags = DoRed | DoGreen | DoBlue;
}
}
b->prevX = b->prevY = 0;
b->enplanemask = 0;
for (j=0; j<planesperbuf; j++) {
b->enplanemask |= b->plane_masks[j];
}
for (j=0; j<closure->pixelsperbuf; j++) {
b->pixels[j] = closure->pixels[0];
for (k=0, m=j; m; k++, m=m>>1) {
if (m&1)
b->pixels[j] ^= b->plane_masks[k];
}
}
}
if (!(multibufext && dblbuf)) {
setDrawBuf(closure, 0);
XSetBackground(dpy, closure->gcontext, closure->bufs[0].pixels[0]);
XSetWindowBackground(dpy, closure->draw_window, closure->bufs[0].pixels[0]);
XSetPlaneMask(dpy, closure->gcontext, AllPlanes);
icoClearArea(closure, 0, 0, closure->winW, closure->winH);
}
}
static void
setBufColname(struct closure *closure, int n, char *colname)
{
int t;
XColor dcolor, color;
t = XLookupColor(dpy,closure->cmap,colname,&dcolor,&color);
if (t==0) {
icoFatal("no such color %s",colname);
}
setBufColor(closure, n,&color);
}
static void *
do_ico_window(void *ptr)
{
int fg, bg;
XSetWindowAttributes xswa;
XWindowAttributes xwa;
XEvent xev;
int icoX, icoY;
unsigned long vmask;
XGCValues xgcv;
int initcolors = 0;
int icoDeltaX = DEFAULT_DELTAX, icoDeltaY = DEFAULT_DELTAY;
int icodeltax2, icodeltay2;
Bool blocking = False;
int winX, winY;
int icoW = 0, icoH = 0;
KeySym ksym;
Bool do_it = True;
char buf[20];
struct closure *closure = ptr;
#ifdef MULTITHREAD
int len;
#endif
#ifdef DEBUG
printf("thread %x starting\n", xthread_self());
#endif
closure->cmap = XDefaultColormap(dpy,DefaultScreen(dpy));
if (!closure->cmap) {
icoFatal("no default colormap!", NULL);
}
fg = WhitePixel(dpy, DefaultScreen(dpy));
bg = BlackPixel(dpy, DefaultScreen(dpy));
if (background_colorname) {
XColor cdef, igndef;
if (XAllocNamedColor (dpy, closure->cmap, background_colorname,
&cdef, &igndef))
bg = cdef.pixel;
else
icoFatal("background: no such color \"%s\"",background_colorname);
}
if (numcolors && (!dofaces || numcolors == 1)) {
XColor cdef, igndef;
if (XAllocNamedColor (dpy, closure->cmap, colornames[0], &cdef, &igndef))
fg = cdef.pixel;
else
icoFatal("face: no such color \"%s\"", colornames[0]);
}
if (invert) {
unsigned long tmp = fg;
fg = bg;
bg = tmp;
}
if (useRoot) {
closure->draw_window = DefaultRootWindow(dpy);
winX = 0;
winY = 0;
closure->winW = DisplayWidth(dpy, DefaultScreen(dpy));
closure->winH = DisplayHeight(dpy, DefaultScreen(dpy));
} else {
closure->winW = closure->winH = (multibufext&&dblbuf ? 300 : 600);
winX = (DisplayWidth(dpy, DefaultScreen(dpy)) - closure->winW) >> 1;
winY = (DisplayHeight(dpy, DefaultScreen(dpy)) - closure->winH) >> 1;
if (geom)
XParseGeometry(geom, &winX, &winY,
(unsigned int *)&closure->winW,
(unsigned int *)&closure->winH);
xswa.event_mask = ExposureMask |
StructureNotifyMask |
KeyPressMask;
xswa.background_pixel = bg;
xswa.border_pixel = fg;
closure->draw_window = XCreateWindow(dpy,
DefaultRootWindow(dpy),
winX, winY, closure->winW, closure->winH, 0,
DefaultDepth(dpy, DefaultScreen(dpy)),
InputOutput, DefaultVisual(dpy, DefaultScreen(dpy)),
CWEventMask | CWBackPixel | CWBorderPixel, &xswa);
#ifdef MULTITHREAD
len = sprintf(buf, "Ico: thread %d", closure->thread_num);
XChangeProperty(dpy, closure->draw_window,
XA_WM_NAME, XA_STRING, 8,
PropModeReplace, (unsigned char *)buf, len);
#else
XChangeProperty(dpy, closure->draw_window,
XA_WM_NAME, XA_STRING, 8,
PropModeReplace, (unsigned char *)"Ico", 3);
#endif
(void) XSetWMProtocols (dpy, closure->draw_window,
&wm_delete_window, 1);
XMapWindow(dpy, closure->draw_window);
#ifdef DEBUG
printf("thread %x waiting for Expose\n", xthread_self());
#endif
for (;;) {
XNextEvent(dpy, &xev);
if (xev.type == Expose)
break;
}
#ifdef DEBUG
printf("thread %x got Expose\n", xthread_self());
#endif
if (XGetWindowAttributes(dpy,closure->draw_window,&xwa)==0) {
icoFatal("cannot get window attributes (size)", NULL);
}
closure->winW = xwa.width;
closure->winH = xwa.height;
}
if (ico_geom)
XParseGeometry (ico_geom, &icoX, &icoY,
(unsigned int *)&icoW,
(unsigned int *)&icoH);
if (icoW <= 0) icoW = DEFAULT_ICO_WIDTH;
if (icoH <= 0) icoH = DEFAULT_ICO_HEIGHT;
if (icoW < MIN_ICO_WIDTH) icoW = MIN_ICO_WIDTH;
if (icoH < MIN_ICO_HEIGHT) icoH = MIN_ICO_HEIGHT;
if (delta_geom) {
unsigned int junk;
XParseGeometry (delta_geom, &icoDeltaX, &icoDeltaY, &junk, &junk);
if (icoDeltaX == 0 && icoDeltaY == 0) {
icoDeltaX = DEFAULT_DELTAX;
icoDeltaY = DEFAULT_DELTAY;
}
}
closure->win = None;
#ifdef MULTIBUFFER
if (multibufext && dblbuf) {
if (XmbufCreateBuffers (dpy, closure->draw_window, 2, update_action,
MultibufferUpdateHintFrequent,
closure->multibuffers) == 2) {
XCopyArea (dpy, closure->draw_window, closure->multibuffers[1],
DefaultGC(dpy, DefaultScreen(dpy)),
0, 0, closure->winW, closure->winH, 0, 0);
closure->win = closure->multibuffers[1];
} else
icoFatal ("unable to obtain 2 buffers", NULL);
}
#endif
if (closure->win == None) closure->win = closure->draw_window;
vmask = (GCBackground | GCForeground | GCLineWidth);
xgcv.background = bg;
xgcv.foreground = fg;
xgcv.line_width = linewidth;
if (dash) {
xgcv.line_style = LineDoubleDash;
xgcv.dashes = dash;
vmask |= (GCLineStyle | GCDashList);
}
closure->gcontext = XCreateGC (dpy, closure->draw_window, vmask, &xgcv);
if (dofaces && numcolors>1) {
int i,t,bits;
bits = 0;
for (t=numcolors; t; t=t>>1) bits++;
initDBufs(closure, fg,bg,bits);
for (i=0; i<numcolors; i++) {
setBufColname(closure, i+1,colornames[i]);
}
initcolors = 1;
}
else if (dblbuf || dofaces) {
initDBufs(closure, fg,bg,1);
initcolors = 1;
}
if (initcolors) {
setDisplayBuf(closure, dblbuf?1:0, 1);
}
if (dsync)
XSync(dpy, 0);
srand((int) time((time_t *)0) % 231);
icoX = ((closure->winW - icoW) * (rand() & 0xFF)) >> 8;
icoY = ((closure->winH - icoH) * (rand() & 0xFF)) >> 8;
icodeltax2 = icoDeltaX * 2;
icodeltay2 = icoDeltaY * 2;
initPoly(closure, poly, icoW, icoH);
while (do_it) {
int prevX;
int prevY;
Bool do_event;
if (blocking) {
XIfEvent(dpy, &xev, predicate, (XPointer) closure->win);
do_event = True;
} else
do_event = XCheckIfEvent(dpy, &xev, predicate,
(XPointer) closure->win);
if (do_event) {
switch (xev.type) {
case ConfigureNotify:
#ifdef DEBUG
printf("thread %x configure\n", xthread_self());
#endif
if (xev.xconfigure.width != closure->winW ||
xev.xconfigure.height != closure->winH)
icoX = icoY = 1;
closure->winW = xev.xconfigure.width;
closure->winH = xev.xconfigure.height;
break;
case KeyPress:
#ifdef DEBUG
printf("thread %x keypress\n", xthread_self());
#endif
XLookupString(&xev.xkey, buf, 10, &ksym, NULL);
do_it = ((ksym != XK_Q) && ksym != XK_q);
break;
case MapNotify:
blocking = False;
#ifdef DEBUG
printf("thread %x unblocking\n", xthread_self());
#endif
break;
case UnmapNotify:
blocking = True;
#ifdef DEBUG
printf("thread %x blocking\n", xthread_self());
#endif
break;
case ClientMessage:
#ifdef DEBUG
printf("thread %x message\n", xthread_self());
#endif
if (xev.xclient.data.l[0] == wm_delete_window)
do_it = False;
else
XBell (dpy, 0);
continue;
}
}
prevX = icoX;
prevY = icoY;
icoX += icoDeltaX;
if (icoX < 0 || icoX + icoW > closure->winW) {
icoX -= icodeltax2;
icoDeltaX = - icoDeltaX;
icodeltax2 = icoDeltaX * 2;
}
icoY += icoDeltaY;
if (icoY < 0 || icoY + icoH > closure->winH) {
icoY -= icodeltay2;
icoDeltaY = - icoDeltaY;
icodeltay2 = icoDeltaY * 2;
}
drawPoly(closure, poly, closure->gcontext,
icoX, icoY, icoW, icoH, prevX, prevY);
}
XDestroyWindow(dpy, closure->win);
#ifdef MULTITHREAD
xmutex_lock(&count_mutex);
thread_count--;
if (thread_count == 0) {
xcondition_broadcast(&count_cond);
}
xmutex_unlock(&count_mutex);
#endif
return NULL;
}
static void
giveObjHelp(void)
{
int i;
Polyinfo *poly;
printf("%-16s%-12s #Vert. #Edges #Faces %-16s\n",
"Name", "ShortName", "Dual");
for (i=0; i<NumberPolygons; i++) {
poly = polygons+i;
printf("%-16s%-12s%6d%8d%8d %-16s\n",
poly->longname, poly->shortname,
poly->numverts, poly->numedges, poly->numfaces,
poly->dual);
}
}
static Polyinfo *
findpoly(const char *name)
{
int i;
Polyinfo *poly;
for (i=0; i<NumberPolygons; i++) {
poly = polygons+i;
if (strcmp(name,poly->longname)==0 || strcmp(name,poly->shortname)==0)
return poly;
}
icoFatal("can't find object %s", name);
}
int main(argc, argv)
int argc;
char **argv;
{
const char *display = NULL;
#ifdef MULTIBUFFER
int mbevbase, mberrbase;
#endif
#ifdef MULTITHREAD
int nthreads = 1;
int i;
#endif
struct closure *closure;
ProgramName = argv[0];
poly = findpoly("icosahedron");
for (argv++, argc--; argc > 0; argv++, argc--) {
if (!strcmp (*argv, "-display")) {
if (argc < 2)
icoFatal("missing argument for %s", *argv);
display = *++argv; argc--;
} else if (!strncmp (*argv, "-g", 2)) {
if (argc < 2)
icoFatal("missing argument for %s", *argv);
geom = *++argv; argc--;
} else if (!strcmp(*argv, "-r"))
useRoot = 1;
else if (!strcmp (*argv, "-d")) {
if (argc < 2)
icoFatal("missing argument for %s", *argv);
dash = atoi(*++argv); argc--;
}
#ifdef MULTITHREAD
else if (!strcmp(*argv, "-threads")) {
if (argc < 2)
icoFatal("missing argument for %s", *argv);
nthreads = atoi(*++argv); argc--;
}
#endif
else if (!strcmp(*argv, "-colors")) {
if (argc < 2)
icoFatal("missing argument for %s", *argv);
colornames = ++argv; argc--; numcolors = 0;
for ( ; argc > 0 && argv[0][0]!='-'; argv++, argc--, numcolors++) ;
argv--; argc++;
}
else if (!strcmp (*argv, "-copy")) {
#ifdef MULTIBUFFER
update_action = MultibufferUpdateActionCopied;
#endif
}
else if (!strcmp (*argv, "-untouched")) {
#ifdef MULTIBUFFER
update_action = MultibufferUpdateActionUntouched;
#endif
}
else if (!strcmp (*argv, "-undefined")) {
#ifdef MULTIBUFFER
update_action = MultibufferUpdateActionUndefined;
#endif
} else if (!strcmp (*argv, "-lw")) {
if (argc < 2)
icoFatal("missing argument for %s", *argv);
linewidth = atoi(*++argv); argc--;
} else if (!strcmp (*argv, "-dbl")) {
dblbuf = 1;
#ifdef MULTIBUFFER
multibufext = 1;
#endif
}
else if (!strcmp(*argv, "-softdbl")) {
dblbuf = 1;
multibufext = 0;
}
else if (!strncmp(*argv, "-p", 2)) {
numcolors = atoi(argv[0]+2);
if (numcolors < 1 || numcolors > NumberPrimaries)
numcolors = NumberPrimaries;
colornames = Primaries;
dofaces = 1;
}
else if (!strcmp(*argv, "-bg")) {
if (argc < 2)
icoFatal("missing argument for %s", *argv);
background_colorname = *++argv; argc--;
} else if (!strcmp(*argv, "-noedges"))
doedges = 0;
else if (!strcmp(*argv, "-faces"))
dofaces = 1;
else if (!strcmp(*argv, "-i"))
invert = 1;
else if (!strcmp(*argv, "-size")) {
if (argc < 2)
icoFatal("missing argument for %s", *argv);
ico_geom = *++argv; argc--;
} else if (!strcmp(*argv, "-delta")) {
if (argc < 2)
icoFatal("missing argument for %s", *argv);
delta_geom = *++argv; argc--;
} else if (!strcmp (*argv, "-sleep")) {
float f;
if (argc < 2)
icoFatal("missing argument for %s", *argv);
if (sscanf (*++argv, "%f", &f) < 1)
icoFatal("invalid argument for %s", argv[-1]);
msleepcount = (int) (f * 1000.0);
argc--;
} else if (!strcmp (*argv, "-obj")) {
if (argc < 2)
icoFatal("missing argument for %s", *argv);
poly = findpoly(*++argv); argc--;
} else if (!strcmp(*argv, "-dsync"))
dsync = 1;
else if (!strncmp(*argv, "-sync", 5))
_Xdebug = 1;
else if (!strcmp(*argv, "-objhelp")) {
giveObjHelp();
exit(1);
}
else {
const char **cpp;
fprintf (stderr, "usage: %s [options]\n\n",
ProgramName);
for (cpp = help_message; *cpp; cpp++)
fprintf (stderr, "%s\n", *cpp);
exit (1);
}
}
if (!dofaces && !doedges)
icoFatal("nothing to draw", NULL);
#ifdef MULTITHREAD
XInitThreads();
#endif
if (!(dpy = XOpenDisplay(display)))
icoFatal("cannot open display \"%s\"", XDisplayName(display));
wm_delete_window = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
#ifdef MULTIBUFFER
if (multibufext && !XmbufQueryExtension (dpy, &mbevbase, &mberrbase)) {
multibufext = 0;
}
#endif
#ifdef MULTITHREAD
#ifndef XMUTEX_INITIALIZER
xmutex_init(&count_mutex);
#endif
xcondition_init(&count_cond);
thread_count = nthreads;
for (i=1; i <= nthreads; i++) {
closure = (struct closure *) xalloc(sizeof(struct closure));
closure->thread_num = i;
xthread_fork(do_ico_window, closure);
}
xmutex_lock(&count_mutex);
xcondition_wait(&count_cond, &count_mutex);
xmutex_unlock(&count_mutex);
#else
closure = (struct closure *) xalloc(sizeof(struct closure));
do_ico_window(closure);
#endif
XCloseDisplay(dpy);
return 0;
}