1 /***********************************************************
2 
3 Copyright (c) 1987  X Consortium
4 
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11 
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 Except as contained in this notice, the name of the X Consortium shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from the X Consortium.
25 
26 
27 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
28 
29                         All Rights Reserved
30 
31 Permission to use, copy, modify, and distribute this software and its
32 documentation for any purpose and without fee is hereby granted,
33 provided that the above copyright notice appear in all copies and that
34 both that copyright notice and this permission notice appear in
35 supporting documentation, and that the name of Digital not be
36 used in advertising or publicity pertaining to distribution of the
37 software without specific, written prior permission.
38 
39 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
40 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
41 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
42 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
43 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
44 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
45 SOFTWARE.
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 */
109 static 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 
118 typedef double Transform3D[4][4];
119 
120 typedef 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 
130 struct 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 
165 static 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 
171 static 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 
178 static const char *Primaries[] = {
179     "red", "green", "blue", "yellow", "cyan", "magenta"
180 };
181 #define NumberPrimaries 6
182 
183 static 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 
216 static const char *ProgramName;	/* argv[0] */
217 
218 /*
219  * variables set by command-line options
220  */
221 static const char *geom = NULL;	/* -geometry: window geometry */
222 static int useRoot = 0;		/* -r */
223 static int dash = 0;		/* -d: dashed line pattern */
224 static const char **colornames;	/* -colors (points into argv) */
225 #ifdef MULTIBUFFER
226 static int update_action = MultibufferUpdateActionBackground;
227 #endif
228 static int linewidth = 0;	/* -lw */
229 static int multibufext = 0;	/* -dbl: use Multi-Buffering extension */
230 static int dblbuf = 0;		/* -dbl or -softdbl: double buffering */
231 static int numcolors = 0;	/* -p: number of primary colors to use */
232 static const char *background_colorname = NULL; /* -bg */
233 static int doedges = 1;		/* -noedges turns this off */
234 static int dofaces = 0;		/* -faces */
235 static int invert = 0;		/* -i */
236 static const char *ico_geom = NULL;	/* -size: size of object in window */
237 static const char *delta_geom = NULL;	/* -delta: amount by which to move object */
238 static Polyinfo *poly;		/* -obj: the poly to draw */
239 static int dsync = 0;		/* -dsync */
240 static int xsync = 0;		/* -sync */
241 static int msleepcount = 0;	/* -sleep value in milliseconds*/
242 #ifdef MULTITHREAD
243 static int thread_count;
244 #ifdef XMUTEX_INITIALIZER
245 static xmutex_rec count_mutex = XMUTEX_INITIALIZER;
246 #else
247 static xmutex_rec count_mutex;
248 #endif
249 static 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 
260 static void _X_NORETURN _X_ATTRIBUTE_PRINTF(1, 2)
icoFatal(const char * fmt,...)261 icoFatal(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 
279 static char *
xalloc(unsigned int nbytes)280 xalloc(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 
298 static void
msleep(unsigned int msecs)299 msleep(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 
316 static void
IdentMat(Transform3D m)317 IdentMat(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 
342 static void
ConcatMat(Transform3D l,Transform3D r,Transform3D m)343 ConcatMat(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 
373 static void
FormatRotateMat(char axis,double angle,Transform3D m)374 FormatRotateMat(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 
421 static void
PartialNonHomTransform(int n,Transform3D m,const Point3D * in,Point3D * out)422 PartialNonHomTransform(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 
439 static Bool
predicate(_X_UNUSED Display * display,XEvent * event,XPointer args)440 predicate(_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 
451 static void
icoClearArea(struct closure * closure,int x,int y,int w,int h)452 icoClearArea(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 
471 static void
initPoly(struct closure * closure,Polyinfo * poly,int icoW,int icoH)472 initPoly(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 
490 static void
setDrawBuf(struct closure * closure,int n)491 setDrawBuf (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 
514 static void
setDisplayBuf(struct closure * closure,int n,_X_UNUSED int firsttime)515 setDisplayBuf(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 
534 static void
setBufColor(struct closure * closure,int n,XColor * color)535 setBufColor(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 
566 static void
drawPoly(struct closure * closure,Polyinfo * poly,GC gc,int icoX,int icoY,int icoW,int icoH,int prevX,int prevY)567 drawPoly(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 	double pxvz;
588 	XPoint ppts[MAXEDGESPERPOLY];
589 
590 	/* Switch double-buffer and rotate vertices */
591 
592 	closure->xv_buffer = !closure->xv_buffer;
593 	PartialNonHomTransform(NV, closure->xform,
594 		closure->xv[!closure->xv_buffer],
595 		closure->xv[closure->xv_buffer]);
596 
597 
598 	/* Convert 3D coordinates to 2D window coordinates: */
599 
600 	pxv = closure->xv[closure->xv_buffer];
601 	pv2 = v2;
602 	for (i = NV - 1; i >= 0; --i) {
603 		pv2->x = (int) ((pxv->x + 1.0) * closure->wo2) + icoX;
604 		pv2->y = (int) ((pxv->y + 1.0) * closure->ho2) + icoY;
605 		++pxv;
606 		++pv2;
607 	}
608 
609 
610 	/* Accumulate edges to be drawn, eliminating duplicates for speed: */
611 
612 	pxv = closure->xv[closure->xv_buffer];
613 	pv2 = v2;
614 	pf = f;
615 	pe = edges;
616 	bzero(closure->drawn, sizeof(closure->drawn));
617 
618 	if (dblbuf)
619 		setDrawBuf(closure, closure->dbufnum);
620 			/* switch drawing buffers if double buffered */
621 	/* for faces, need to clear before FillPoly */
622 	if (dofaces && !(multibufext && dblbuf)) {
623 		/* multibuf uses update background */
624 		if (dblbuf)
625 			icoClearArea(closure,
626 				closure->drawbuf->prevX - linewidth/2,
627 				closure->drawbuf->prevY - linewidth/2,
628 				icoW + linewidth + 1, icoH + linewidth + 1);
629 		icoClearArea(closure,
630 			prevX - linewidth/2, prevY - linewidth/2,
631 			icoW + linewidth + 1, icoH + linewidth + 1);
632 	}
633 
634 	if (dsync)
635 		XSync(dpy, 0);
636 
637 	for (i = NF - 1; i >= 0; --i, pf += pcount) {
638 
639 		pcount = *pf++;	/* number of edges for this face */
640 		pxvz = 0.0;
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 
726 static void
initDBufs(struct closure * closure,unsigned long fg,unsigned long bg,int planesperbuf)727 initDBufs(struct closure *closure, unsigned long fg, unsigned long bg,
728           int planesperbuf)
729 {
730 	int i,j,jj,j0,j1,k,m,t;
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 	    t = XAllocColorCells(dpy,closure->cmap,0,
757 		    closure->plane_masks,closure->totalplanes, closure->pixels,1);
758 			    /* allocate color planes */
759 	    if (t==0) {
760 		    icoFatal("can't allocate enough color planes");
761 	    }
762 	}
763 
764 	fgcolor.pixel = fg;
765 	bgcolor.pixel = bg;
766 	XQueryColor(dpy,closure->cmap,&fgcolor);
767 	XQueryColor(dpy,closure->cmap,&bgcolor);
768 
769 	setBufColor(closure, 0,&bgcolor);
770 	setBufColor(closure, 1,&fgcolor);
771 	for (i=0; i<closure->nplanesets; i++) {
772 		b = closure->bufs+i;
773 		for (j0=0; j0<(dblbuf&&!multibufext?closure->pixelsperbuf:1); j0++) {
774 		    for (j1=0; j1<closure->pixelsperbuf; j1++) {
775 			j = (j0<<closure->planesperbuf)|j1;
776 			if (i==0) jj=j;
777 			else jj= (j1<<closure->planesperbuf)|j0;
778 			b->colors[jj].pixel = closure->pixels[0];
779 			for (k=0, m=j; m; k++, m=m>>1) {
780 				if (m&1)
781 				   b->colors[jj].pixel ^= closure->plane_masks[k];
782 			}
783 			b->colors[jj].flags = DoRed | DoGreen | DoBlue;
784 		    }
785 		}
786 		b->prevX = b->prevY = 0;
787 		b->enplanemask = 0;
788 		for (j=0; j<planesperbuf; j++) {
789 			b->enplanemask |= b->plane_masks[j];
790 		}
791 		for (j=0; j<closure->pixelsperbuf; j++) {
792 			b->pixels[j] = closure->pixels[0];
793 			for (k=0, m=j; m; k++, m=m>>1) {
794 				if (m&1)
795 				   b->pixels[j] ^= b->plane_masks[k];
796 			}
797 		}
798 	}
799 
800 	if (!(multibufext && dblbuf)) {
801 	    setDrawBuf(closure, 0);
802 	    XSetBackground(dpy, closure->gcontext, closure->bufs[0].pixels[0]);
803 	    XSetWindowBackground(dpy, closure->draw_window, closure->bufs[0].pixels[0]);
804 	    XSetPlaneMask(dpy, closure->gcontext, AllPlanes);
805 	    icoClearArea(closure, 0, 0, closure->winW, closure->winH); /* clear entire window */
806 	}
807 }
808 
809 static void
setBufColname(struct closure * closure,int n,const char * colname)810 setBufColname(struct closure *closure, int n, const char *colname)
811 {
812 	int t;
813 	XColor dcolor, color;
814 
815 	t = XLookupColor(dpy,closure->cmap,colname,&dcolor,&color);
816 	if (t==0) {	/* no such color */
817 		icoFatal("no such color %s",colname);
818 	}
819 	setBufColor(closure, n,&color);
820 }
821 
822 
823 /* function to create and run an ico window */
824 static void *
do_ico_window(void * ptr)825 do_ico_window(void *ptr)
826 {
827 	unsigned long fg, bg;
828 	XSetWindowAttributes xswa;
829 	XWindowAttributes xwa;
830 	XEvent xev;
831 	int icoX, icoY;
832 	unsigned long vmask;
833 	XGCValues xgcv;
834 	int initcolors = 0;
835 	int icoDeltaX = DEFAULT_DELTAX, icoDeltaY = DEFAULT_DELTAY;
836 	int icodeltax2, icodeltay2;
837 	Bool blocking = False;
838 	int winX, winY;
839 	int icoW = 0, icoH = 0;
840 	KeySym ksym;
841 	Bool do_it = True;
842 	char buf[20];
843 	struct closure *closure = ptr;
844 #ifdef MULTITHREAD
845 	int len;
846 #endif
847 
848 #ifdef DEBUG
849 	printf("thread %x starting\n", xthread_self());
850 #endif
851 	closure->cmap = XDefaultColormap(dpy,DefaultScreen(dpy));
852 	if (!closure->cmap) {
853 		icoFatal("no default colormap!");
854 	}
855 
856 	fg = WhitePixel(dpy, DefaultScreen(dpy));
857 	bg = BlackPixel(dpy, DefaultScreen(dpy));
858 	if (background_colorname) {
859 	    XColor cdef, igndef;
860 
861 	    if (XAllocNamedColor (dpy, closure->cmap, background_colorname,
862 				  &cdef, &igndef))
863 	      bg = cdef.pixel;
864 	    else
865 	      icoFatal("background: no such color \"%s\"",background_colorname);
866 	}
867 	if (numcolors && (!dofaces || numcolors == 1)) {
868 	    XColor cdef, igndef;
869 
870 	    if (XAllocNamedColor (dpy, closure->cmap, colornames[0], &cdef, &igndef))
871 	      fg = cdef.pixel;
872 	    else
873 	      icoFatal("face: no such color \"%s\"", colornames[0]);
874 	}
875 
876 	if (invert) {
877 	    unsigned long tmp = fg;
878 	    fg = bg;
879 	    bg = tmp;
880 	}
881 
882 	/* Set up window parameters, create and map window if necessary */
883 
884 	if (useRoot) {
885 		closure->draw_window = DefaultRootWindow(dpy);
886 		winX = 0;
887 		winY = 0;
888 		closure->winW = DisplayWidth(dpy, DefaultScreen(dpy));
889 		closure->winH = DisplayHeight(dpy, DefaultScreen(dpy));
890 	} else {
891 		closure->winW = closure->winH = (multibufext&&dblbuf ? 300 : 600);
892 		winX = (DisplayWidth(dpy, DefaultScreen(dpy)) - closure->winW) >> 1;
893 		winY = (DisplayHeight(dpy, DefaultScreen(dpy)) - closure->winH) >> 1;
894 		if (geom)
895 			XParseGeometry(geom, &winX, &winY,
896 				       (unsigned int *)&closure->winW,
897 				       (unsigned int *)&closure->winH);
898 
899 		xswa.event_mask = ExposureMask |
900 				  StructureNotifyMask |
901 				  KeyPressMask;
902 		xswa.background_pixel = bg;
903 		xswa.border_pixel = fg;
904 
905 		closure->draw_window = XCreateWindow(dpy,
906 		    DefaultRootWindow(dpy),
907 		    winX, winY, closure->winW, closure->winH, 0,
908 		    DefaultDepth(dpy, DefaultScreen(dpy)),
909 		    InputOutput, DefaultVisual(dpy, DefaultScreen(dpy)),
910 		    CWEventMask | CWBackPixel | CWBorderPixel, &xswa);
911 #ifdef MULTITHREAD
912 		len = sprintf(buf, "Ico: thread %d", closure->thread_num);
913 		XChangeProperty(dpy, closure->draw_window,
914 				XA_WM_NAME, XA_STRING, 8,
915 				PropModeReplace, (unsigned char *)buf, len);
916 #else
917 		XChangeProperty(dpy, closure->draw_window,
918 				XA_WM_NAME, XA_STRING, 8,
919 				PropModeReplace, (unsigned char *)"Ico", 3);
920 #endif
921 		(void) XSetWMProtocols (dpy, closure->draw_window,
922 					&wm_delete_window, 1);
923 		XMapWindow(dpy, closure->draw_window);
924 #ifdef DEBUG
925 		printf("thread %x waiting for Expose\n", xthread_self());
926 #endif
927 		for (;;) {
928 		    XIfEvent(dpy, &xev, predicate, (XPointer) closure->draw_window);
929 		    if (xev.type == Expose)
930 			break;
931 		}
932 #ifdef DEBUG
933 		printf("thread %x got Expose\n", xthread_self());
934 #endif
935 		if (XGetWindowAttributes(dpy,closure->draw_window,&xwa)==0) {
936 			icoFatal("cannot get window attributes (size)");
937 		}
938 		closure->winW = xwa.width;
939 		closure->winH = xwa.height;
940 	}
941 
942 	if (ico_geom)
943 	  XParseGeometry (ico_geom, &icoX, &icoY,
944 			  (unsigned int *)&icoW,
945 			  (unsigned int *)&icoH);
946 	if (icoW <= 0) icoW = DEFAULT_ICO_WIDTH;
947 	if (icoH <= 0) icoH = DEFAULT_ICO_HEIGHT;
948 	if (icoW < MIN_ICO_WIDTH) icoW = MIN_ICO_WIDTH;
949 	if (icoH < MIN_ICO_HEIGHT) icoH = MIN_ICO_HEIGHT;
950 
951 	if (delta_geom) {
952 	    unsigned int junk;
953 
954 	    XParseGeometry (delta_geom, &icoDeltaX, &icoDeltaY, &junk, &junk);
955 	    if (icoDeltaX == 0 && icoDeltaY == 0) {
956 		icoDeltaX = DEFAULT_DELTAX;
957 		icoDeltaY = DEFAULT_DELTAY;
958 	    }
959 	}
960 
961 	closure->win = None;
962 
963 #ifdef MULTIBUFFER
964 	if (multibufext && dblbuf) {
965 	    if (XmbufCreateBuffers (dpy, closure->draw_window, 2, update_action,
966 				    MultibufferUpdateHintFrequent,
967 				    closure->multibuffers) == 2) {
968 		XCopyArea (dpy, closure->draw_window, closure->multibuffers[1],
969 			   DefaultGC(dpy, DefaultScreen(dpy)),
970 			   0, 0, closure->winW, closure->winH, 0, 0);
971 		closure->win = closure->multibuffers[1];
972 	    } else
973 	      icoFatal ("unable to obtain 2 buffers");
974 	}
975 #endif /* MULTIBUFFER */
976 	if (closure->win == None) closure->win = closure->draw_window;
977 
978 	/* Set up a graphics context */
979 
980 	vmask = (GCBackground | GCForeground | GCLineWidth);
981 	xgcv.background = bg;
982 	xgcv.foreground = fg;
983 	xgcv.line_width = linewidth;
984 	if (dash) {
985 	    xgcv.line_style = LineDoubleDash;
986 	    xgcv.dashes = dash;
987 	    vmask |= (GCLineStyle | GCDashList);
988 	}
989 	closure->gcontext = XCreateGC (dpy, closure->draw_window, vmask, &xgcv);
990 
991 	if (dofaces && numcolors>1) {
992 	    int i,t,bits;
993 		bits = 0;
994 		for (t=numcolors; t; t=t>>1) bits++;
995 		initDBufs(closure, fg,bg,bits);
996 		/* don't set the background color */
997 		for (i=0; i<numcolors; i++) {
998 			setBufColname(closure, i+1,colornames[i]);
999 		}
1000 		initcolors = 1;
1001 	}
1002 	else if (dblbuf || dofaces) {
1003 		initDBufs(closure, fg,bg,1);
1004 		initcolors = 1;
1005 	}
1006 	if (initcolors) {
1007 	    setDisplayBuf(closure, dblbuf?1:0, 1); /* insert new colors */
1008 	}
1009 
1010 	if (dsync)
1011 		XSync(dpy, 0);
1012 
1013 	/* Get the initial position, size, and speed of the bounding-box */
1014 
1015 	srand((int) time((time_t *)0) % 231);
1016 	icoX = ((closure->winW - icoW) * (rand() & 0xFF)) >> 8;
1017 	icoY = ((closure->winH - icoH) * (rand() & 0xFF)) >> 8;
1018 
1019 
1020 	/* Bounce the box in the window */
1021 
1022 	icodeltax2 = icoDeltaX * 2;
1023 	icodeltay2 = icoDeltaY * 2;
1024 	initPoly(closure, poly, icoW, icoH);
1025 
1026 	while (do_it) {
1027 		int prevX;
1028 		int prevY;
1029 		Bool do_event;
1030 
1031 		/*
1032 		 * This is not a good example of how to do event reading
1033 		 * in multi-threaded programs.  More commonly there would
1034 		 * be one thread reading all events and dispatching them
1035 		 * to the appropriate thread.  However, the threaded version
1036 		 * of ico was developed to test the MT Xlib implementation,
1037 		 * so it is useful to have it behave a little oddly.
1038 		 * For a discussion of how to write multi-threaded X programs,
1039 		 * see Gildea, S., "Multi-Threaded Xlib", The X Resource,
1040 		 * Issue 5, January 1993, pp. 159-166.
1041 		 */
1042 		if (blocking) {
1043 		    XIfEvent(dpy, &xev, predicate, (XPointer) closure->win);
1044 		    do_event = True;
1045 		} else
1046 		    do_event = XCheckIfEvent(dpy, &xev, predicate,
1047 			    (XPointer) closure->win);
1048 		if (do_event) {
1049 		    switch (xev.type) {
1050 		      case ConfigureNotify:
1051 #ifdef DEBUG
1052 			printf("thread %x configure\n", xthread_self());
1053 #endif
1054 			if (xev.xconfigure.width != closure->winW ||
1055 			    xev.xconfigure.height != closure->winH)
1056 			  icoX = icoY = 1;
1057 			closure->winW = xev.xconfigure.width;
1058 			closure->winH = xev.xconfigure.height;
1059 			break;
1060 		      case KeyPress:
1061 #ifdef DEBUG
1062 			printf("thread %x keypress\n", xthread_self());
1063 #endif
1064 			XLookupString(&xev.xkey, buf, 10, &ksym, NULL);
1065 			do_it = ((ksym != XK_Q) && ksym != XK_q);
1066 			break;
1067 		      case MapNotify:
1068 			blocking = False;
1069 #ifdef DEBUG
1070 			printf("thread %x unblocking\n", xthread_self());
1071 #endif
1072 			break;
1073 		      case UnmapNotify:
1074 			blocking = True;
1075 #ifdef DEBUG
1076 			printf("thread %x blocking\n", xthread_self());
1077 #endif
1078 			break;
1079 		      case ClientMessage:
1080 #ifdef DEBUG
1081 			printf("thread %x message\n", xthread_self());
1082 #endif
1083 			if (xev.xclient.data.l[0] == wm_delete_window)
1084 			    do_it = False;
1085 			else
1086 			    XBell (dpy, 0);
1087 			continue;
1088 		    }
1089 		}
1090 
1091 		prevX = icoX;
1092 		prevY = icoY;
1093 
1094 		icoX += icoDeltaX;
1095 		if (icoX < 0 || icoX + icoW > closure->winW) {
1096 			icoX -= icodeltax2;
1097 			icoDeltaX = - icoDeltaX;
1098 			icodeltax2 = icoDeltaX * 2;
1099 		}
1100 		icoY += icoDeltaY;
1101 		if (icoY < 0 || icoY + icoH > closure->winH) {
1102 			icoY -= icodeltay2;
1103 			icoDeltaY = - icoDeltaY;
1104 			icodeltay2 = icoDeltaY * 2;
1105 		}
1106 
1107 		drawPoly(closure, poly, closure->gcontext,
1108 			 icoX, icoY, icoW, icoH, prevX, prevY);
1109 	}
1110 	XDestroyWindow(dpy, closure->win);
1111 #ifdef MULTITHREAD
1112 	xmutex_lock(&count_mutex);
1113 	thread_count--;
1114 	if (thread_count == 0) {
1115 	    xcondition_broadcast(&count_cond);
1116 	}
1117 	xmutex_unlock(&count_mutex);
1118 #endif
1119 	return NULL;
1120 }
1121 
1122 /******************************************************************************
1123  * Description
1124  *	Main routine.  Process command-line arguments, then bounce a bounding
1125  *	box inside the window.  Call DrawIco() to redraw the icosahedron.
1126  *****************************************************************************/
1127 
1128 static void
giveObjHelp(void)1129 giveObjHelp(void)
1130 {
1131 	unsigned int i;
1132 	Polyinfo *poly;
1133 
1134 	printf("%-16s%-12s  #Vert.  #Edges  #Faces  %-16s\n",
1135 		"Name", "ShortName", "Dual");
1136 	for (i=0; i<NumberPolygons; i++) {
1137 		poly = polygons+i;
1138 		printf("%-16s%-12s%6d%8d%8d    %-16s\n",
1139 			poly->longname, poly->shortname,
1140 			poly->numverts, poly->numedges, poly->numfaces,
1141 			poly->dual);
1142 	}
1143 }
1144 
1145 static Polyinfo *
findpoly(const char * name)1146 findpoly(const char *name)
1147 {
1148 	unsigned int i;
1149         Polyinfo *poly;
1150 
1151 	for (i=0; i<NumberPolygons; i++) {
1152 		poly = polygons+i;
1153 		if (strcmp(name,poly->longname)==0 || strcmp(name,poly->shortname)==0)
1154 			return poly;
1155 	}
1156 	icoFatal("can't find object %s", name);
1157 }
1158 
main(int argc,const char ** argv)1159 int main(int argc, const char **argv)
1160 {
1161 	const char *display = NULL;
1162 #ifdef MULTIBUFFER
1163 	int mbevbase, mberrbase;
1164 #endif
1165 #ifdef MULTITHREAD
1166 	int nthreads = 1;	/* -threads: number of windows */
1167 	int i;
1168 #endif
1169 	struct closure *closure;
1170 
1171 	ProgramName = argv[0];
1172 
1173 	/* Process arguments: */
1174 
1175 	poly = findpoly("icosahedron");	/* default */
1176 
1177 	for (argv++, argc--; argc > 0; argv++, argc--) {
1178 		if (!strcmp (*argv, "-display")) {
1179 			if (argc < 2)
1180 				icoFatal("missing argument for %s", *argv);
1181 			display = *++argv; argc--;
1182 		} else if (!strncmp (*argv, "-g", 2)) {
1183 			if (argc < 2)
1184 				icoFatal("missing argument for %s", *argv);
1185 			geom = *++argv; argc--;
1186 		} else if (!strcmp(*argv, "-r"))
1187 			useRoot = 1;
1188 		else if (!strcmp (*argv, "-d")) {
1189 			if (argc < 2)
1190 				icoFatal("missing argument for %s", *argv);
1191 			dash = atoi(*++argv); argc--;
1192 		}
1193 #ifdef MULTITHREAD
1194 		else if (!strcmp(*argv, "-threads")) {
1195 			if (argc < 2)
1196 				icoFatal("missing argument for %s", *argv);
1197 		        nthreads = atoi(*++argv); argc--;
1198 		}
1199 #endif
1200 		else if (!strcmp(*argv, "-colors")) {
1201 			if (argc < 2)
1202 				icoFatal("missing argument for %s", *argv);
1203 			colornames = ++argv; argc--; numcolors = 0;
1204 			for ( ; argc > 0 && argv[0][0]!='-'; argv++, argc--, numcolors++) ;
1205 			argv--; argc++;
1206 		}
1207 		else if (!strcmp (*argv, "-copy")) {
1208 #ifdef MULTIBUFFER
1209 			update_action = MultibufferUpdateActionCopied;
1210 #endif
1211 		}
1212 		else if (!strcmp (*argv, "-untouched")) {
1213 #ifdef MULTIBUFFER
1214 			update_action = MultibufferUpdateActionUntouched;
1215 #endif
1216 		}
1217 		else if (!strcmp (*argv, "-undefined")) {
1218 #ifdef MULTIBUFFER
1219 			update_action = MultibufferUpdateActionUndefined;
1220 #endif
1221 		} else if (!strcmp (*argv, "-lw")) {
1222 			if (argc < 2)
1223 				icoFatal("missing argument for %s", *argv);
1224 			linewidth = atoi(*++argv); argc--;
1225 		} else if (!strcmp (*argv, "-dbl")) {
1226 			dblbuf = 1;
1227 #ifdef MULTIBUFFER
1228 			multibufext = 1;
1229 #endif
1230 		}
1231 		else if (!strcmp(*argv, "-softdbl")) {
1232 		        dblbuf = 1;
1233 			multibufext = 0;
1234 		}
1235 		else if (!strncmp(*argv, "-p", 2)) {
1236 			numcolors = atoi(argv[0]+2);
1237 			if (numcolors < 1 || numcolors > NumberPrimaries)
1238 				numcolors = NumberPrimaries;
1239 			colornames = Primaries;
1240 			dofaces = 1;
1241 		}
1242 		else if (!strcmp(*argv, "-bg")) {
1243 			if (argc < 2)
1244 				icoFatal("missing argument for %s", *argv);
1245 			background_colorname = *++argv; argc--;
1246 		} else if (!strcmp(*argv, "-noedges"))
1247 			doedges = 0;
1248 		else if (!strcmp(*argv, "-faces"))
1249 			dofaces = 1;
1250 		else if (!strcmp(*argv, "-i"))
1251 			invert = 1;
1252 		else if (!strcmp(*argv, "-size")) {
1253 			if (argc < 2)
1254 				icoFatal("missing argument for %s", *argv);
1255 			ico_geom = *++argv; argc--;
1256 		} else if (!strcmp(*argv, "-delta")) {
1257 			if (argc < 2)
1258 				icoFatal("missing argument for %s", *argv);
1259 			delta_geom = *++argv; argc--;
1260 		} else if (!strcmp (*argv, "-sleep")) {
1261 			float f;
1262 			if (argc < 2)
1263 				icoFatal("missing argument for %s", *argv);
1264 			if (sscanf (*++argv, "%f", &f) < 1)
1265 				icoFatal("invalid argument for %s", argv[-1]);
1266 			msleepcount = (int) (f * 1000.0);
1267 			argc--;
1268 		} else if (!strcmp (*argv, "-obj")) {
1269 			if (argc < 2)
1270 				icoFatal("missing argument for %s", *argv);
1271 			poly = findpoly(*++argv); argc--;
1272 		} else if (!strcmp(*argv, "-dsync"))
1273 			dsync = 1;
1274 		else if (!strncmp(*argv, "-sync",  5))
1275 			xsync = 1;
1276 		else if (!strcmp(*argv, "-objhelp")) {
1277 			giveObjHelp();
1278 			exit(1);
1279 		}
1280 		else if (strcmp(*argv, "-version") == 0) {
1281 			puts(PACKAGE_STRING);
1282 			exit(0);
1283 		}
1284 		else {	/* unknown arg */
1285 			fprintf (stderr, "%s: unrecognized argument %s\n\n",
1286 				 ProgramName, *argv);
1287 			fprintf (stderr, "usage:  %s [options]\n\n%s",
1288 			         ProgramName, help_message);
1289 			exit (1);
1290 		}
1291 	}
1292 
1293 	if (!dofaces && !doedges)
1294 		icoFatal("nothing to draw");
1295 
1296 #ifdef MULTITHREAD
1297 	XInitThreads();
1298 #endif
1299 	if (!(dpy = XOpenDisplay(display)))
1300 	    icoFatal("cannot open display \"%s\"", XDisplayName(display));
1301     	wm_delete_window = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
1302 	if (xsync)
1303 	    XSynchronize(dpy, True);
1304 
1305 #ifdef MULTIBUFFER
1306 	if (multibufext && !XmbufQueryExtension (dpy, &mbevbase, &mberrbase)) {
1307 	    multibufext = 0;
1308 	}
1309 #endif
1310 
1311 #ifdef MULTITHREAD
1312 #ifndef XMUTEX_INITIALIZER
1313 	xmutex_init(&count_mutex);
1314 #endif
1315 	xcondition_init(&count_cond);
1316 
1317 	/* start all threads here */
1318 	thread_count = nthreads;
1319 	for (i=1; i <= nthreads; i++) {
1320 	    closure = (struct closure *) xalloc(sizeof(struct closure));
1321 	    closure->thread_num = i;
1322 	    xthread_fork(do_ico_window, closure);
1323 	}
1324 	/* wait until all theads terminate */
1325 	xmutex_lock(&count_mutex);
1326 	xcondition_wait(&count_cond, &count_mutex);
1327 	xmutex_unlock(&count_mutex);
1328 #else
1329 	/* start the animation */
1330 	closure = (struct closure *) xalloc(sizeof(struct closure));
1331 	do_ico_window(closure);
1332 #endif
1333 	XCloseDisplay(dpy);
1334 	return 0;
1335 }
1336