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