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