1/***********************************************************
2
3Copyright (c) 1987  X Consortium
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of the X Consortium shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from the X Consortium.
25
26
27Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
28
29                        All Rights Reserved
30
31Permission to use, copy, modify, and distribute this software and its
32documentation for any purpose and without fee is hereby granted,
33provided that the above copyright notice appear in all copies and that
34both that copyright notice and this permission notice appear in
35supporting documentation, and that the name of Digital not be
36used in advertising or publicity pertaining to distribution of the
37software without specific, written prior permission.
38
39DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
40ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
41DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
42ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
43WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
44ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
45SOFTWARE.
46
47******************************************************************/
48
49/******************************************************************************
50 * Description
51 *	Display a wire-frame rotating icosahedron, with hidden lines removed
52 *
53 * Arguments:
54 *	-r		display on root window instead of creating a new one
55 * (plus a host of others, try -help)
56 *****************************************************************************/
57/* Additions by jimmc@sci:
58 *  faces and colors
59 *  double buffering on the display
60 *  additional polyhedra
61 *  sleep switch
62 */
63
64/*
65 * multi-thread version by Stephen Gildea, January 1992
66 */
67
68/* Additions by Carlos A M dos Santos, XFree86 project, September 1999:
69 *  use of "q" to quit threads
70 *  support for ICCCM delete window message
71 *  better thread support - mutex and condition to control termination
72 */
73
74#ifdef HAVE_CONFIG_H
75#include "config.h"
76
77#include <X11/XlibConf.h>
78#ifdef XTHREADS
79# define MULTITHREAD
80#endif
81#endif /* HAVE_CONFIG_H / autoconf */
82
83#include <math.h>
84#include <X11/Xlib.h>
85#include <X11/Xatom.h>
86#include <X11/Xutil.h>
87#include <X11/Xfuncs.h>
88#include <X11/keysym.h>
89#include <stdio.h>
90#include <stdarg.h>
91#ifdef MULTIBUFFER
92#include <X11/extensions/multibuf.h>
93#endif /* MULTIBUFFER */
94#ifdef MULTITHREAD
95#include <X11/Xthreads.h>
96#endif
97#include <X11/Xos.h>
98
99#define MIN_ICO_WIDTH 5
100#define MIN_ICO_HEIGHT 5
101#define DEFAULT_ICO_WIDTH 150
102#define DEFAULT_ICO_HEIGHT 150
103#define DEFAULT_DELTAX 13
104#define DEFAULT_DELTAY 9
105
106#include "polyinfo.h"	/* define format of one polyhedron */
107
108/* Now include all the files which define the actual polyhedra */
109static Polyinfo polygons[] = {
110#include "allobjs.h"
111};
112#define NumberPolygons sizeof(polygons)/sizeof(polygons[0])
113
114#include <stdlib.h>
115#include <time.h>	/* for time_t */
116#include <sys/time.h>	/* for struct timeval */
117
118typedef double Transform3D[4][4];
119
120typedef struct {
121	int prevX, prevY;
122	unsigned long *plane_masks;	/* points into dbpair.plane_masks */
123	unsigned long enplanemask;	/* what we enable for drawing */
124	XColor *colors;		/* size = 2 ** totalplanes */
125	unsigned long *pixels;	/* size = 2 ** planesperbuf */
126} DBufInfo;
127
128/* variables that need to be per-thread */
129
130struct closure {
131    /* these elements were originally in DBufPair, a struct type */
132    int planesperbuf;
133    int pixelsperbuf;	/* = 1<<planesperbuf */
134    int totalplanes;	/* = 2*planesperbuf */
135    int totalpixels;	/* = 1<<totalplanes */
136    unsigned long *plane_masks;	/* size = totalplanes */
137    unsigned long pixels[1];
138    int dbufnum;
139    DBufInfo bufs[2];
140    DBufInfo *drawbuf, *dpybuf;
141    /* end of old DBufPair dbpair */
142    /* these elements were originally global variables */
143    Window win, draw_window;
144    int winW, winH;
145    Colormap cmap;
146    GC gcontext;
147#ifdef MULTIBUFFER
148    Multibuffer multibuffers[2];
149#endif /* MULTIBUFFER */
150    int nplanesets;
151    /* items needed by drawPoly */
152    char drawn[MAXNV][MAXNV];
153    Transform3D xform;
154    Point3D xv[2][MAXNV];
155    int xv_buffer;
156    double wo2, ho2;
157#ifdef MULTITHREAD
158    int thread_num;
159#endif
160};
161
162
163/* The display is shared and writable, but Xlib locks it as necessary */
164
165static Display *dpy;
166
167/* This atom will be used to catch the ICCCM "delete window" message. It will
168 * be allocated once and used in read-only mode by threads, so it can be a
169 * global variable */
170
171static Atom wm_delete_window;
172
173/*
174 * variables that are not set except maybe in initialization before
175 * any additional threads are created
176 */
177
178static const char *Primaries[] = {
179    "red", "green", "blue", "yellow", "cyan", "magenta"
180};
181#define NumberPrimaries 6
182
183static const char *help_message =
184"where options include:\n"
185"    -display host:dpy       X server to use\n"
186"    -geometry geom          geometry of window to use\n"
187"    -size WxH               size of object to rotate\n"
188"    -delta +X+Y             amount by which to move object\n"
189"    -r                      draw in the root window\n"
190"    -d number               dashed line pattern for wire frames\n"
191"    -bg color               background color\n"
192"    -colors color ...       codes to use on sides\n"
193"    -p#                     use # (1 through 8) primary colors\n"
194#ifdef MULTIBUFFER
195"    -dbl                    use double buffering extension (if present)\n"
196#else
197"    -dbl                    use double buffering (software only)\n"
198#endif
199"    -softdbl                use software double buffering\n"
200"    -noedges                don't draw wire frame edges\n"
201"    -faces                  draw faces\n"
202"    -copy                   use multibuffer update action Copied\n"
203"    -untouched              use multibuffer update action Untouched\n"
204"    -undefined              use multibuffer update action Undefined\n"
205"    -lw number              line width to use\n"
206"    -i                      invert\n"
207"    -sleep number           seconds to sleep in between draws\n"
208"    -obj objname            type of polyhedral object to draw\n"
209"    -objhelp                list polyhedral objects available\n"
210#ifdef MULTITHREAD
211"    -threads number         number of windows, each its own thread\n"
212#endif
213"    -version                print program version\n"
214;
215
216static const char *ProgramName;	/* argv[0] */
217
218/*
219 * variables set by command-line options
220 */
221static const char *geom = NULL;	/* -geometry: window geometry */
222static int useRoot = 0;		/* -r */
223static int dash = 0;		/* -d: dashed line pattern */
224static const char **colornames;	/* -colors (points into argv) */
225#ifdef MULTIBUFFER
226static int update_action = MultibufferUpdateActionBackground;
227#endif
228static int linewidth = 0;	/* -lw */
229static int multibufext = 0;	/* -dbl: use Multi-Buffering extension */
230static int dblbuf = 0;		/* -dbl or -softdbl: double buffering */
231static int numcolors = 0;	/* -p: number of primary colors to use */
232static const char *background_colorname = NULL; /* -bg */
233static int doedges = 1;		/* -noedges turns this off */
234static int dofaces = 0;		/* -faces */
235static int invert = 0;		/* -i */
236static const char *ico_geom = NULL;	/* -size: size of object in window */
237static const char *delta_geom = NULL;	/* -delta: amount by which to move object */
238static Polyinfo *polyobj;	/* -obj: the poly to draw */
239static int dsync = 0;		/* -dsync */
240static int xsync = 0;		/* -sync */
241static int msleepcount = 0;	/* -sleep value in milliseconds*/
242#ifdef MULTITHREAD
243static int thread_count;
244#ifdef XMUTEX_INITIALIZER
245static xmutex_rec count_mutex = XMUTEX_INITIALIZER;
246#else
247static xmutex_rec count_mutex;
248#endif
249static xcondition_rec count_cond;/* Xthreads doesn't define an equivalent to
250				 * PTHREAD_COND_INITIALIZER, so we must call
251				 * xcondition_init later */
252#endif
253
254/******************************************************************************
255 * Description
256 *	Error handling
257 *****************************************************************************/
258
259
260static void _X_NORETURN _X_ATTRIBUTE_PRINTF(1, 2)
261icoFatal(const char *fmt, ...)
262{
263	va_list args;
264
265	fprintf(stderr, "%s: ", ProgramName);
266	va_start(args, fmt);
267	vfprintf(stderr, fmt, args);
268	va_end(args);
269	fprintf(stderr, "\n");
270	exit(1);
271}
272
273
274/******************************************************************************
275 * Description
276 *	Memory allocation
277 *****************************************************************************/
278
279static char *
280xalloc(unsigned int nbytes)
281{
282        char *p;
283
284	p = malloc(nbytes);
285	if (p)
286		return p;
287
288	fprintf(stderr, "%s: no more memory\n", ProgramName);
289	exit(1);
290}
291
292
293/******************************************************************************
294 * Description
295 *	Sleep a certain number of milliseconds
296 *****************************************************************************/
297
298static void
299msleep(unsigned int msecs)
300{
301	struct timeval timeout;
302
303	timeout.tv_sec = msecs / 1000; timeout.tv_usec = (msecs % 1000) * 1000;
304	select(1,NULL,NULL,NULL,&timeout);
305}
306
307
308/******************************************************************************
309 * Description
310 *	Format a 4x4 identity matrix.
311 *
312 * Output
313 *	*m		Formatted identity matrix
314 *****************************************************************************/
315
316static void
317IdentMat(Transform3D m)
318{
319	int i;
320	int j;
321
322	for (i = 3; i >= 0; --i) {
323		for (j = 3; j >= 0; --j)
324			m[i][j] = 0.0;
325		m[i][i] = 1.0;
326	}
327}
328
329
330/******************************************************************************
331 * Description
332 *	Concatenate two 4-by-4 transformation matrices.
333 *
334 * Input
335 *	l		multiplicand (left operand)
336 *	r		multiplier (right operand)
337 *
338 * Output
339 *	*m		Result matrix
340 *****************************************************************************/
341
342static void
343ConcatMat(Transform3D l, Transform3D r, Transform3D m)
344{
345	int i;
346	int j;
347
348	for (i = 0; i < 4; ++i)
349		for (j = 0; j < 4; ++j)
350			m[i][j] = l[i][0] * r[0][j]
351			    + l[i][1] * r[1][j]
352			    + l[i][2] * r[2][j]
353			    + l[i][3] * r[3][j];
354}
355
356
357/******************************************************************************
358 * Description
359 *	Format a matrix that will perform a rotation transformation
360 *	about the specified axis.  The rotation angle is measured
361 *	counterclockwise about the specified axis when looking
362 *	at the origin from the positive axis.
363 *
364 * Input
365 *	axis		Axis ('x', 'y', 'z') about which to perform rotation
366 *	angle		Angle (in radians) of rotation
367 *	A		Pointer to rotation matrix
368 *
369 * Output
370 *	*m		Formatted rotation matrix
371 *****************************************************************************/
372
373static void
374FormatRotateMat(char axis, double angle, Transform3D m)
375{
376	double s, c;
377
378	IdentMat(m);
379
380	s = sin(angle);
381	c = cos(angle);
382
383	switch (axis)
384	{
385		case 'x':
386			m[1][1] = m[2][2] = c;
387			m[1][2] = s;
388			m[2][1] = -s;
389			break;
390		case 'y':
391			m[0][0] = m[2][2] = c;
392			m[2][0] = s;
393			m[0][2] = -s;
394			break;
395		case 'z':
396			m[0][0] = m[1][1] = c;
397			m[0][1] = s;
398			m[1][0] = -s;
399			break;
400	}
401}
402
403
404/******************************************************************************
405 * Description
406 *	Perform a partial transform on non-homogeneous points.
407 *	Given an array of non-homogeneous (3-coordinate) input points,
408 *	this routine multiplies them by the 3-by-3 upper left submatrix
409 *	of a standard 4-by-4 transform matrix.  The resulting non-homogeneous
410 *	points are returned.
411 *
412 * Input
413 *	n		number of points to transform
414 *	m		4-by-4 transform matrix
415 *	in		array of non-homogeneous input points
416 *
417 * Output
418 *	*out		array of transformed non-homogeneous output points
419 *****************************************************************************/
420
421static void
422PartialNonHomTransform(int n, Transform3D m, const Point3D *in, Point3D *out)
423{
424	for (; n > 0; --n, ++in, ++out) {
425		out->x = in->x * m[0][0] + in->y * m[1][0] + in->z * m[2][0];
426		out->y = in->x * m[0][1] + in->y * m[1][1] + in->z * m[2][1];
427		out->z = in->x * m[0][2] + in->y * m[1][2] + in->z * m[2][2];
428	}
429}
430
431
432/*
433 * Unfortunately we can not use XWindowEvent and XCheckWindowEvent to get
434 * ClientMessage events, because there is no corresponding event mask. We must
435 * use XIfEvent and XCheckIfEvent and this function as a predicate. Better if
436 * Xlib had some kind of XWindowAnyEvent and XCheckWindowEvent. -- Casantos.
437 */
438
439static Bool
440predicate(_X_UNUSED Display *display, XEvent *event, XPointer args)
441{
442    Window w = (Window) args;
443    return event->xany.window == w;
444}
445
446/******************************************************************************
447 * Description
448 *	Icosahedron animator.
449 *****************************************************************************/
450
451static void
452icoClearArea(struct closure *closure, int x, int y, int w, int h)
453{
454	if (multibufext && dblbuf)
455		return;
456
457	if (dblbuf || dofaces) {
458		XSetForeground(dpy,
459			closure->gcontext,
460			closure->drawbuf->pixels[0]);
461
462		/* use background as foreground color for fill */
463		XFillRectangle(dpy,closure->win,closure->gcontext,x,y,w,h);
464	} else {
465		XClearArea(dpy,closure->win,x,y,w,h,0);
466	}
467}
468
469/* Set up points, transforms, etc.  */
470
471static void
472initPoly(struct closure *closure, Polyinfo *poly, int icoW, int icoH)
473{
474    const Point3D *vertices = poly->v;
475    int NV = poly->numverts;
476    Transform3D r1;
477    Transform3D r2;
478
479    FormatRotateMat('x', 5 * 3.1416 / 180.0, r1);
480    FormatRotateMat('y', 5 * 3.1416 / 180.0, r2);
481    ConcatMat(r1, r2, closure->xform);
482
483    memcpy(closure->xv[0], vertices, NV * sizeof(Point3D));
484    closure->xv_buffer = 0;
485
486    closure->wo2 = icoW / 2.0;
487    closure->ho2 = icoH / 2.0;
488}
489
490static void
491setDrawBuf (struct closure *closure, int n)
492{
493    XGCValues xgcv;
494    unsigned long mask;
495
496#ifdef MULTIBUFFER
497    if (multibufext && dblbuf) {
498	closure->win = closure->multibuffers[n];
499	n = 0;
500    }
501#endif /* MULTIBUFFER */
502
503    closure->drawbuf = closure->bufs+n;
504    xgcv.foreground = closure->drawbuf->pixels[closure->pixelsperbuf-1];
505    xgcv.background = closure->drawbuf->pixels[0];
506    mask = GCForeground | GCBackground;
507    if (dblbuf && !multibufext) {
508	xgcv.plane_mask = closure->drawbuf->enplanemask;
509	mask |= GCPlaneMask;
510    }
511    XChangeGC(dpy, closure->gcontext, mask, &xgcv);
512}
513
514static void
515setDisplayBuf(struct closure *closure, int n,
516#ifndef MULTIBUFFER
517              _X_UNUSED
518#endif
519              int firsttime)
520{
521#ifdef MULTIBUFFER
522	if (multibufext && dblbuf) {
523		XmbufDisplayBuffers (dpy, 1, &closure->multibuffers[n], msleepcount, 0);
524		if (!firsttime)
525			return;
526		n = 0;
527	}
528#endif
529	closure->dpybuf = closure->bufs+n;
530	if (closure->totalpixels > 2)
531	    XStoreColors(dpy,closure->cmap,closure->dpybuf->colors,closure->totalpixels);
532}
533
534static void
535setBufColor(struct closure *closure, int n, XColor *color)
536{
537	int i,j,cx;
538	DBufInfo *b;
539	unsigned long pix;
540
541	for (i=0; i<closure->nplanesets; i++) {
542		b = closure->bufs+i;
543		for (j=0; j<(dblbuf&&!multibufext?closure->pixelsperbuf:1); j++) {
544			cx = n + j*closure->pixelsperbuf;
545			pix = b->colors[cx].pixel;
546			b->colors[cx] = *color;
547			b->colors[cx].pixel = pix;
548			b->colors[cx].flags = DoRed | DoGreen | DoBlue;
549		}
550	}
551}
552
553/******************************************************************************
554 * Description
555 *	Undraw previous polyhedron (by erasing its bounding box).
556 *	Rotate and draw the new polyhedron.
557 *
558 * Input
559 *	poly		the polyhedron to draw
560 *	gc		X11 graphics context to be used for drawing
561 *	icoX, icoY	position of upper left of bounding-box
562 *	icoW, icoH	size of bounding-box
563 *	prevX, prevY	position of previous bounding-box
564 *****************************************************************************/
565
566static void
567drawPoly(struct closure *closure, Polyinfo *poly, GC gc,
568	 int icoX, int icoY, int icoW, int icoH, int prevX, int prevY)
569{
570	const int *f = poly->f;
571	int NV = poly->numverts;
572	int NF = poly->numfaces;
573
574	int p0;
575	int p1;
576	XPoint *pv2;
577	XSegment *pe;
578	Point3D *pxv;
579	XPoint v2[MAXNV];
580	XSegment edges[MAXEDGES];
581	int i;
582	int j,k;
583	const int *pf;
584	int facecolor;
585
586	int pcount;
587	XPoint ppts[MAXEDGESPERPOLY];
588
589	/* Switch double-buffer and rotate vertices */
590
591	closure->xv_buffer = !closure->xv_buffer;
592	PartialNonHomTransform(NV, closure->xform,
593		closure->xv[!closure->xv_buffer],
594		closure->xv[closure->xv_buffer]);
595
596
597	/* Convert 3D coordinates to 2D window coordinates: */
598
599	pxv = closure->xv[closure->xv_buffer];
600	pv2 = v2;
601	for (i = NV - 1; i >= 0; --i) {
602		pv2->x = (int) ((pxv->x + 1.0) * closure->wo2) + icoX;
603		pv2->y = (int) ((pxv->y + 1.0) * closure->ho2) + icoY;
604		++pxv;
605		++pv2;
606	}
607
608
609	/* Accumulate edges to be drawn, eliminating duplicates for speed: */
610
611	pxv = closure->xv[closure->xv_buffer];
612	pv2 = v2;
613	pf = f;
614	pe = edges;
615	bzero(closure->drawn, sizeof(closure->drawn));
616
617	if (dblbuf)
618		setDrawBuf(closure, closure->dbufnum);
619			/* switch drawing buffers if double buffered */
620	/* for faces, need to clear before FillPoly */
621	if (dofaces && !(multibufext && dblbuf)) {
622		/* multibuf uses update background */
623		if (dblbuf)
624			icoClearArea(closure,
625				closure->drawbuf->prevX - linewidth/2,
626				closure->drawbuf->prevY - linewidth/2,
627				icoW + linewidth + 1, icoH + linewidth + 1);
628		icoClearArea(closure,
629			prevX - linewidth/2, prevY - linewidth/2,
630			icoW + linewidth + 1, icoH + linewidth + 1);
631	}
632
633	if (dsync)
634		XSync(dpy, 0);
635
636	for (i = NF - 1; i >= 0; --i, pf += pcount) {
637		double pxvz = 0.0;
638
639		pcount = *pf++;	/* number of edges for this face */
640
641		for (j=0; j<pcount; j++) {
642			p0 = pf[j];
643			pxvz += pxv[p0].z;
644		}
645
646		/* If facet faces away from viewer, don't consider it: */
647		if (pxvz<0.0)
648			continue;
649
650		if (dofaces) {
651			if (numcolors)
652				facecolor = i%numcolors + 1;
653			else
654				facecolor = 1;
655			XSetForeground(dpy, gc,
656				closure->drawbuf->pixels[facecolor]);
657			for (j=0; j<pcount; j++) {
658				p0 = pf[j];
659				ppts[j].x = pv2[p0].x;
660				ppts[j].y = pv2[p0].y;
661			}
662			XFillPolygon(dpy, closure->win, gc, ppts, pcount,
663				Convex, CoordModeOrigin);
664		}
665
666		if (doedges) {
667			for (j=0; j<pcount; j++) {
668				if (j<pcount-1) k=j+1;
669				else k=0;
670				p0 = pf[j];
671				p1 = pf[k];
672				if (!closure->drawn[p0][p1]) {
673					closure->drawn[p0][p1] = 1;
674					closure->drawn[p1][p0] = 1;
675					pe->x1 = pv2[p0].x;
676					pe->y1 = pv2[p0].y;
677					pe->x2 = pv2[p1].x;
678					pe->y2 = pv2[p1].y;
679					++pe;
680				}
681			}
682		}
683	}
684
685	/* Erase previous, draw current icosahedrons; sync for smoothness. */
686
687	if (doedges) {
688		if (dofaces) {
689			XSetForeground(dpy, gc, closure->drawbuf->pixels[0]);
690				/* use background as foreground color */
691		} else {
692			if (dblbuf && !multibufext)
693				icoClearArea(closure,
694					closure->drawbuf->prevX - linewidth/2,
695					closure->drawbuf->prevY - linewidth/2,
696					icoW + linewidth + 1,
697					icoH + linewidth + 1);
698			if (!(multibufext && dblbuf))
699				icoClearArea(closure,
700					prevX - linewidth/2,
701					prevY - linewidth/2,
702					icoW + linewidth + 1,
703					icoH + linewidth + 1);
704			if (dblbuf || dofaces) {
705				XSetForeground(dpy, gc, closure->drawbuf->pixels[
706					closure->pixelsperbuf-1]);
707			}
708		}
709		XDrawSegments(dpy, closure->win, gc, edges, pe - edges);
710	}
711
712	if (dsync)
713		XSync(dpy, 0);
714
715	if (dblbuf) {
716		closure->drawbuf->prevX = icoX;
717		closure->drawbuf->prevY = icoY;
718		setDisplayBuf(closure, closure->dbufnum, 0);
719	}
720	if (dblbuf)
721		closure->dbufnum = 1 - closure->dbufnum;
722	if (!(multibufext && dblbuf) && msleepcount > 0)
723		msleep(msleepcount);
724}
725
726static void
727initDBufs(struct closure *closure, unsigned long fg, unsigned long bg,
728          int planesperbuf)
729{
730	int i,j,jj,j0,j1,k,m;
731	DBufInfo *b;
732	XColor bgcolor, fgcolor;
733
734	closure->nplanesets = (dblbuf && !multibufext ? 2 : 1);
735
736	closure->planesperbuf = planesperbuf;
737	closure->pixelsperbuf = 1<<planesperbuf;
738	closure->totalplanes = closure->nplanesets * planesperbuf;
739	closure->totalpixels = 1<<closure->totalplanes;
740	closure->plane_masks = (unsigned long *)
741		xalloc(closure->totalplanes * sizeof(unsigned long));
742	closure->dbufnum = 0;
743	for (i=0; i < closure->nplanesets; i++) {
744		b = closure->bufs+i;
745		b->plane_masks = closure->plane_masks + (i*planesperbuf);
746		b->colors = (XColor *)
747			xalloc(closure->totalpixels * sizeof(XColor));
748		b->pixels = (unsigned long *)
749			xalloc(closure->pixelsperbuf * sizeof(unsigned long));
750	}
751
752	if (closure->totalplanes == 1) {
753	    closure->pixels[0] = bg;
754	    closure->plane_masks[0] = fg ^ bg;
755	} else {
756	    int t = XAllocColorCells(dpy, closure->cmap, 0,
757				     closure->plane_masks, closure->totalplanes,
758				     closure->pixels, 1);
759			    /* allocate color planes */
760	    if (t==0) {
761		    icoFatal("can't allocate enough color planes");
762	    }
763	}
764
765	fgcolor.pixel = fg;
766	bgcolor.pixel = bg;
767	XQueryColor(dpy,closure->cmap,&fgcolor);
768	XQueryColor(dpy,closure->cmap,&bgcolor);
769
770	setBufColor(closure, 0,&bgcolor);
771	setBufColor(closure, 1,&fgcolor);
772	for (i=0; i<closure->nplanesets; i++) {
773		b = closure->bufs+i;
774		for (j0=0; j0<(dblbuf&&!multibufext?closure->pixelsperbuf:1); j0++) {
775		    for (j1=0; j1<closure->pixelsperbuf; j1++) {
776			j = (j0<<closure->planesperbuf)|j1;
777			if (i==0) jj=j;
778			else jj= (j1<<closure->planesperbuf)|j0;
779			b->colors[jj].pixel = closure->pixels[0];
780			for (k=0, m=j; m; k++, m=m>>1) {
781				if (m&1)
782				   b->colors[jj].pixel ^= closure->plane_masks[k];
783			}
784			b->colors[jj].flags = DoRed | DoGreen | DoBlue;
785		    }
786		}
787		b->prevX = b->prevY = 0;
788		b->enplanemask = 0;
789		for (j=0; j<planesperbuf; j++) {
790			b->enplanemask |= b->plane_masks[j];
791		}
792		for (j=0; j<closure->pixelsperbuf; j++) {
793			b->pixels[j] = closure->pixels[0];
794			for (k=0, m=j; m; k++, m=m>>1) {
795				if (m&1)
796				   b->pixels[j] ^= b->plane_masks[k];
797			}
798		}
799	}
800
801	if (!(multibufext && dblbuf)) {
802	    setDrawBuf(closure, 0);
803	    XSetBackground(dpy, closure->gcontext, closure->bufs[0].pixels[0]);
804	    XSetWindowBackground(dpy, closure->draw_window, closure->bufs[0].pixels[0]);
805	    XSetPlaneMask(dpy, closure->gcontext, AllPlanes);
806	    icoClearArea(closure, 0, 0, closure->winW, closure->winH); /* clear entire window */
807	}
808}
809
810static void
811setBufColname(struct closure *closure, int n, const char *colname)
812{
813	int t;
814	XColor dcolor, color;
815
816	t = XLookupColor(dpy,closure->cmap,colname,&dcolor,&color);
817	if (t==0) {	/* no such color */
818		icoFatal("no such color %s",colname);
819	}
820	setBufColor(closure, n,&color);
821}
822
823
824/* function to create and run an ico window */
825static void *
826do_ico_window(void *ptr)
827{
828	unsigned long fg, bg;
829	XSetWindowAttributes xswa;
830	XWindowAttributes xwa;
831	XEvent xev;
832	int icoX, icoY;
833	unsigned long vmask;
834	XGCValues xgcv;
835	int initcolors = 0;
836	int icoDeltaX = DEFAULT_DELTAX, icoDeltaY = DEFAULT_DELTAY;
837	int icodeltax2, icodeltay2;
838	Bool blocking = False;
839	int winX, winY;
840	int icoW = 0, icoH = 0;
841	KeySym ksym;
842	Bool do_it = True;
843	char buf[20];
844	struct closure *closure = ptr;
845#ifdef MULTITHREAD
846	int len;
847#endif
848
849#ifdef DEBUG
850	printf("thread %x starting\n", xthread_self());
851#endif
852	closure->cmap = XDefaultColormap(dpy,DefaultScreen(dpy));
853	if (!closure->cmap) {
854		icoFatal("no default colormap!");
855	}
856
857	fg = WhitePixel(dpy, DefaultScreen(dpy));
858	bg = BlackPixel(dpy, DefaultScreen(dpy));
859	if (background_colorname) {
860	    XColor cdef, igndef;
861
862	    if (XAllocNamedColor (dpy, closure->cmap, background_colorname,
863				  &cdef, &igndef))
864	      bg = cdef.pixel;
865	    else
866	      icoFatal("background: no such color \"%s\"",background_colorname);
867	}
868	if (numcolors && (!dofaces || numcolors == 1)) {
869	    XColor cdef, igndef;
870
871	    if (XAllocNamedColor (dpy, closure->cmap, colornames[0], &cdef, &igndef))
872	      fg = cdef.pixel;
873	    else
874	      icoFatal("face: no such color \"%s\"", colornames[0]);
875	}
876
877	if (invert) {
878	    unsigned long tmp = fg;
879	    fg = bg;
880	    bg = tmp;
881	}
882
883	/* Set up window parameters, create and map window if necessary */
884
885	if (useRoot) {
886		closure->draw_window = DefaultRootWindow(dpy);
887		winX = 0;
888		winY = 0;
889		closure->winW = DisplayWidth(dpy, DefaultScreen(dpy));
890		closure->winH = DisplayHeight(dpy, DefaultScreen(dpy));
891	} else {
892		closure->winW = closure->winH = (multibufext&&dblbuf ? 300 : 600);
893		winX = (DisplayWidth(dpy, DefaultScreen(dpy)) - closure->winW) >> 1;
894		winY = (DisplayHeight(dpy, DefaultScreen(dpy)) - closure->winH) >> 1;
895		if (geom)
896			XParseGeometry(geom, &winX, &winY,
897				       (unsigned int *)&closure->winW,
898				       (unsigned int *)&closure->winH);
899
900		xswa.event_mask = ExposureMask |
901				  StructureNotifyMask |
902				  KeyPressMask;
903		xswa.background_pixel = bg;
904		xswa.border_pixel = fg;
905
906		closure->draw_window = XCreateWindow(dpy,
907		    DefaultRootWindow(dpy),
908		    winX, winY, closure->winW, closure->winH, 0,
909		    DefaultDepth(dpy, DefaultScreen(dpy)),
910		    InputOutput, DefaultVisual(dpy, DefaultScreen(dpy)),
911		    CWEventMask | CWBackPixel | CWBorderPixel, &xswa);
912#ifdef MULTITHREAD
913		len = sprintf(buf, "Ico: thread %d", closure->thread_num);
914		XChangeProperty(dpy, closure->draw_window,
915				XA_WM_NAME, XA_STRING, 8,
916				PropModeReplace, (unsigned char *)buf, len);
917#else
918		XChangeProperty(dpy, closure->draw_window,
919				XA_WM_NAME, XA_STRING, 8,
920				PropModeReplace, (unsigned char *)"Ico", 3);
921#endif
922		(void) XSetWMProtocols (dpy, closure->draw_window,
923					&wm_delete_window, 1);
924		XMapWindow(dpy, closure->draw_window);
925#ifdef DEBUG
926		printf("thread %x waiting for Expose\n", xthread_self());
927#endif
928		for (;;) {
929		    XIfEvent(dpy, &xev, predicate, (XPointer) closure->draw_window);
930		    if (xev.type == Expose)
931			break;
932		}
933#ifdef DEBUG
934		printf("thread %x got Expose\n", xthread_self());
935#endif
936		if (XGetWindowAttributes(dpy,closure->draw_window,&xwa)==0) {
937			icoFatal("cannot get window attributes (size)");
938		}
939		closure->winW = xwa.width;
940		closure->winH = xwa.height;
941	}
942
943	if (ico_geom)
944	  XParseGeometry (ico_geom, &icoX, &icoY,
945			  (unsigned int *)&icoW,
946			  (unsigned int *)&icoH);
947	if (icoW <= 0) icoW = DEFAULT_ICO_WIDTH;
948	if (icoH <= 0) icoH = DEFAULT_ICO_HEIGHT;
949	if (icoW < MIN_ICO_WIDTH) icoW = MIN_ICO_WIDTH;
950	if (icoH < MIN_ICO_HEIGHT) icoH = MIN_ICO_HEIGHT;
951
952	if (delta_geom) {
953	    unsigned int junk;
954
955	    XParseGeometry (delta_geom, &icoDeltaX, &icoDeltaY, &junk, &junk);
956	    if (icoDeltaX == 0 && icoDeltaY == 0) {
957		icoDeltaX = DEFAULT_DELTAX;
958		icoDeltaY = DEFAULT_DELTAY;
959	    }
960	}
961
962	closure->win = None;
963
964#ifdef MULTIBUFFER
965	if (multibufext && dblbuf) {
966	    if (XmbufCreateBuffers (dpy, closure->draw_window, 2, update_action,
967				    MultibufferUpdateHintFrequent,
968				    closure->multibuffers) == 2) {
969		XCopyArea (dpy, closure->draw_window, closure->multibuffers[1],
970			   DefaultGC(dpy, DefaultScreen(dpy)),
971			   0, 0, closure->winW, closure->winH, 0, 0);
972		closure->win = closure->multibuffers[1];
973	    } else
974	      icoFatal ("unable to obtain 2 buffers");
975	}
976#endif /* MULTIBUFFER */
977	if (closure->win == None) closure->win = closure->draw_window;
978
979	/* Set up a graphics context */
980
981	vmask = (GCBackground | GCForeground | GCLineWidth);
982	xgcv.background = bg;
983	xgcv.foreground = fg;
984	xgcv.line_width = linewidth;
985	if (dash) {
986	    xgcv.line_style = LineDoubleDash;
987	    xgcv.dashes = dash;
988	    vmask |= (GCLineStyle | GCDashList);
989	}
990	closure->gcontext = XCreateGC (dpy, closure->draw_window, vmask, &xgcv);
991
992	if (dofaces && numcolors>1) {
993	    int i,t,bits;
994		bits = 0;
995		for (t=numcolors; t; t=t>>1) bits++;
996		initDBufs(closure, fg,bg,bits);
997		/* don't set the background color */
998		for (i=0; i<numcolors; i++) {
999			setBufColname(closure, i+1,colornames[i]);
1000		}
1001		initcolors = 1;
1002	}
1003	else if (dblbuf || dofaces) {
1004		initDBufs(closure, fg,bg,1);
1005		initcolors = 1;
1006	}
1007	if (initcolors) {
1008	    setDisplayBuf(closure, dblbuf?1:0, 1); /* insert new colors */
1009	}
1010
1011	if (dsync)
1012		XSync(dpy, 0);
1013
1014	/* Get the initial position, size, and speed of the bounding-box */
1015
1016	srand((int) time((time_t *)0) % 231);
1017	icoX = ((closure->winW - icoW) * (rand() & 0xFF)) >> 8;
1018	icoY = ((closure->winH - icoH) * (rand() & 0xFF)) >> 8;
1019
1020
1021	/* Bounce the box in the window */
1022
1023	icodeltax2 = icoDeltaX * 2;
1024	icodeltay2 = icoDeltaY * 2;
1025	initPoly(closure, polyobj, icoW, icoH);
1026
1027	while (do_it) {
1028		int prevX;
1029		int prevY;
1030		Bool do_event;
1031
1032		/*
1033		 * This is not a good example of how to do event reading
1034		 * in multi-threaded programs.  More commonly there would
1035		 * be one thread reading all events and dispatching them
1036		 * to the appropriate thread.  However, the threaded version
1037		 * of ico was developed to test the MT Xlib implementation,
1038		 * so it is useful to have it behave a little oddly.
1039		 * For a discussion of how to write multi-threaded X programs,
1040		 * see Gildea, S., "Multi-Threaded Xlib", The X Resource,
1041		 * Issue 5, January 1993, pp. 159-166.
1042		 */
1043		if (blocking) {
1044		    XIfEvent(dpy, &xev, predicate, (XPointer) closure->win);
1045		    do_event = True;
1046		} else
1047		    do_event = XCheckIfEvent(dpy, &xev, predicate,
1048			    (XPointer) closure->win);
1049		if (do_event) {
1050		    switch (xev.type) {
1051		      case ConfigureNotify:
1052#ifdef DEBUG
1053			printf("thread %x configure\n", xthread_self());
1054#endif
1055			if (xev.xconfigure.width != closure->winW ||
1056			    xev.xconfigure.height != closure->winH)
1057			  icoX = icoY = 1;
1058			closure->winW = xev.xconfigure.width;
1059			closure->winH = xev.xconfigure.height;
1060			break;
1061		      case KeyPress:
1062#ifdef DEBUG
1063			printf("thread %x keypress\n", xthread_self());
1064#endif
1065			XLookupString(&xev.xkey, buf, 10, &ksym, NULL);
1066			do_it = ((ksym != XK_Q) && ksym != XK_q);
1067			break;
1068		      case MapNotify:
1069			blocking = False;
1070#ifdef DEBUG
1071			printf("thread %x unblocking\n", xthread_self());
1072#endif
1073			break;
1074		      case UnmapNotify:
1075			blocking = True;
1076#ifdef DEBUG
1077			printf("thread %x blocking\n", xthread_self());
1078#endif
1079			break;
1080		      case ClientMessage:
1081#ifdef DEBUG
1082			printf("thread %x message\n", xthread_self());
1083#endif
1084			if ((Atom) xev.xclient.data.l[0] == wm_delete_window)
1085			    do_it = False;
1086			else
1087			    XBell (dpy, 0);
1088			continue;
1089		    }
1090		}
1091
1092		prevX = icoX;
1093		prevY = icoY;
1094
1095		icoX += icoDeltaX;
1096		if (icoX < 0 || icoX + icoW > closure->winW) {
1097			icoX -= icodeltax2;
1098			icoDeltaX = - icoDeltaX;
1099			icodeltax2 = icoDeltaX * 2;
1100		}
1101		icoY += icoDeltaY;
1102		if (icoY < 0 || icoY + icoH > closure->winH) {
1103			icoY -= icodeltay2;
1104			icoDeltaY = - icoDeltaY;
1105			icodeltay2 = icoDeltaY * 2;
1106		}
1107
1108		drawPoly(closure, polyobj, closure->gcontext,
1109			 icoX, icoY, icoW, icoH, prevX, prevY);
1110	}
1111	XDestroyWindow(dpy, closure->win);
1112#ifdef MULTITHREAD
1113	xmutex_lock(&count_mutex);
1114	thread_count--;
1115	if (thread_count == 0) {
1116	    xcondition_broadcast(&count_cond);
1117	}
1118	xmutex_unlock(&count_mutex);
1119#endif
1120	return NULL;
1121}
1122
1123/******************************************************************************
1124 * Description
1125 *	Main routine.  Process command-line arguments, then bounce a bounding
1126 *	box inside the window.  Call DrawIco() to redraw the icosahedron.
1127 *****************************************************************************/
1128
1129static void
1130giveObjHelp(void)
1131{
1132	unsigned int i;
1133
1134	printf("%-16s%-12s  #Vert.  #Edges  #Faces  %-16s\n",
1135		"Name", "ShortName", "Dual");
1136	for (i=0; i<NumberPolygons; i++) {
1137		Polyinfo *poly = polygons+i;
1138
1139		printf("%-16s%-12s%6d%8d%8d    %-16s\n",
1140			poly->longname, poly->shortname,
1141			poly->numverts, poly->numedges, poly->numfaces,
1142			poly->dual);
1143	}
1144}
1145
1146static Polyinfo *
1147findpoly(const char *name)
1148{
1149	unsigned int i;
1150
1151	for (i=0; i<NumberPolygons; i++) {
1152		Polyinfo *poly = polygons+i;
1153
1154		if (strcmp(name,poly->longname)==0 || strcmp(name,poly->shortname)==0)
1155			return poly;
1156	}
1157	icoFatal("can't find object %s", name);
1158}
1159
1160int main(int argc, const char **argv)
1161{
1162	const char *display = NULL;
1163#ifdef MULTIBUFFER
1164	int mbevbase, mberrbase;
1165#endif
1166#ifdef MULTITHREAD
1167	int nthreads = 1;	/* -threads: number of windows */
1168	int i;
1169#endif
1170	struct closure *closure;
1171
1172	ProgramName = argv[0];
1173
1174	/* Process arguments: */
1175
1176	polyobj = findpoly("icosahedron");	/* default */
1177
1178	for (argv++, argc--; argc > 0; argv++, argc--) {
1179		if (!strcmp (*argv, "-display")) {
1180			if (argc < 2)
1181				icoFatal("missing argument for %s", *argv);
1182			display = *++argv; argc--;
1183		} else if (!strncmp (*argv, "-g", 2)) {
1184			if (argc < 2)
1185				icoFatal("missing argument for %s", *argv);
1186			geom = *++argv; argc--;
1187		} else if (!strcmp(*argv, "-r"))
1188			useRoot = 1;
1189		else if (!strcmp (*argv, "-d")) {
1190			if (argc < 2)
1191				icoFatal("missing argument for %s", *argv);
1192			dash = atoi(*++argv); argc--;
1193		}
1194#ifdef MULTITHREAD
1195		else if (!strcmp(*argv, "-threads")) {
1196			if (argc < 2)
1197				icoFatal("missing argument for %s", *argv);
1198		        nthreads = atoi(*++argv); argc--;
1199		}
1200#endif
1201		else if (!strcmp(*argv, "-colors")) {
1202			if (argc < 2)
1203				icoFatal("missing argument for %s", *argv);
1204			colornames = ++argv; argc--; numcolors = 0;
1205			for ( ; argc > 0 && argv[0][0]!='-'; argv++, argc--, numcolors++) ;
1206			argv--; argc++;
1207		}
1208		else if (!strcmp (*argv, "-copy")) {
1209#ifdef MULTIBUFFER
1210			update_action = MultibufferUpdateActionCopied;
1211#endif
1212		}
1213		else if (!strcmp (*argv, "-untouched")) {
1214#ifdef MULTIBUFFER
1215			update_action = MultibufferUpdateActionUntouched;
1216#endif
1217		}
1218		else if (!strcmp (*argv, "-undefined")) {
1219#ifdef MULTIBUFFER
1220			update_action = MultibufferUpdateActionUndefined;
1221#endif
1222		} else if (!strcmp (*argv, "-lw")) {
1223			if (argc < 2)
1224				icoFatal("missing argument for %s", *argv);
1225			linewidth = atoi(*++argv); argc--;
1226		} else if (!strcmp (*argv, "-dbl")) {
1227			dblbuf = 1;
1228#ifdef MULTIBUFFER
1229			multibufext = 1;
1230#endif
1231		}
1232		else if (!strcmp(*argv, "-softdbl")) {
1233		        dblbuf = 1;
1234			multibufext = 0;
1235		}
1236		else if (!strncmp(*argv, "-p", 2)) {
1237			numcolors = atoi(argv[0]+2);
1238			if (numcolors < 1 || numcolors > NumberPrimaries)
1239				numcolors = NumberPrimaries;
1240			colornames = Primaries;
1241			dofaces = 1;
1242		}
1243		else if (!strcmp(*argv, "-bg")) {
1244			if (argc < 2)
1245				icoFatal("missing argument for %s", *argv);
1246			background_colorname = *++argv; argc--;
1247		} else if (!strcmp(*argv, "-noedges"))
1248			doedges = 0;
1249		else if (!strcmp(*argv, "-faces"))
1250			dofaces = 1;
1251		else if (!strcmp(*argv, "-i"))
1252			invert = 1;
1253		else if (!strcmp(*argv, "-size")) {
1254			if (argc < 2)
1255				icoFatal("missing argument for %s", *argv);
1256			ico_geom = *++argv; argc--;
1257		} else if (!strcmp(*argv, "-delta")) {
1258			if (argc < 2)
1259				icoFatal("missing argument for %s", *argv);
1260			delta_geom = *++argv; argc--;
1261		} else if (!strcmp (*argv, "-sleep")) {
1262			float f;
1263			if (argc < 2)
1264				icoFatal("missing argument for %s", *argv);
1265			if (sscanf (*++argv, "%f", &f) < 1)
1266				icoFatal("invalid argument for %s", argv[-1]);
1267			msleepcount = (int) (f * 1000.0);
1268			argc--;
1269		} else if (!strcmp (*argv, "-obj")) {
1270			if (argc < 2)
1271				icoFatal("missing argument for %s", *argv);
1272			polyobj = findpoly(*++argv); argc--;
1273		} else if (!strcmp(*argv, "-dsync"))
1274			dsync = 1;
1275		else if (!strncmp(*argv, "-sync",  5))
1276			xsync = 1;
1277		else if (!strcmp(*argv, "-objhelp")) {
1278			giveObjHelp();
1279			exit(1);
1280		}
1281		else if (strcmp(*argv, "-version") == 0) {
1282			puts(PACKAGE_STRING);
1283			exit(0);
1284		}
1285		else {	/* unknown arg */
1286			fprintf (stderr, "%s: unrecognized argument %s\n\n",
1287				 ProgramName, *argv);
1288			fprintf (stderr, "usage:  %s [options]\n\n%s",
1289			         ProgramName, help_message);
1290			exit (1);
1291		}
1292	}
1293
1294	if (!dofaces && !doedges)
1295		icoFatal("nothing to draw");
1296
1297#ifdef MULTITHREAD
1298	XInitThreads();
1299#endif
1300	if (!(dpy = XOpenDisplay(display)))
1301	    icoFatal("cannot open display \"%s\"", XDisplayName(display));
1302    	wm_delete_window = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
1303	if (xsync)
1304	    XSynchronize(dpy, True);
1305
1306#ifdef MULTIBUFFER
1307	if (multibufext && !XmbufQueryExtension (dpy, &mbevbase, &mberrbase)) {
1308	    multibufext = 0;
1309	}
1310#endif
1311
1312#ifdef MULTITHREAD
1313#ifndef XMUTEX_INITIALIZER
1314	xmutex_init(&count_mutex);
1315#endif
1316	xcondition_init(&count_cond);
1317
1318	/* start all threads here */
1319	thread_count = nthreads;
1320	for (i=1; i <= nthreads; i++) {
1321	    closure = (struct closure *) xalloc(sizeof(struct closure));
1322	    closure->thread_num = i;
1323	    xthread_fork(do_ico_window, closure);
1324	}
1325	/* wait until all theads terminate */
1326	xmutex_lock(&count_mutex);
1327	xcondition_wait(&count_cond, &count_mutex);
1328	xmutex_unlock(&count_mutex);
1329#else
1330	/* start the animation */
1331	closure = (struct closure *) xalloc(sizeof(struct closure));
1332	do_ico_window(closure);
1333#endif
1334	XCloseDisplay(dpy);
1335	return 0;
1336}
1337