http://packetstorm.tacticalflex.com/UNIX/admin/xscreensaver-3.27.tar.gz
[xscreensaver] / hacks / xrayswarm.c
1 /*
2  * Copyright (c) 2000 by Chris Leger (xrayjones@users.sourceforge.net)
3  *
4  * xrayswarm - a shameless ripoff of the 'swarm' screensaver on SGI
5  *   boxes.
6  *
7  * Version 1.0 - initial release.  doesn't read any special command-line
8  *   options, and only supports the variable 'delay' via Xresources.
9  *   (the delay resouces is most useful on systems w/o gettimeofday, in
10  *   which case automagical level-of-detail for FPS maintainance can't
11  *   be used.)
12  *
13  *   The code isn't commented, but isn't too ugly. It should be pretty
14  *   easy to understand, with the exception of the colormap stuff.
15  *
16  */
17 /*
18 Permission is hereby granted, free of charge, to any person obtaining
19 a copy of this software and associated documentation files (the
20 "Software"), to deal in the Software without restriction, including
21 without limitation the rights to use, copy, modify, merge, publish,
22 distribute, sublicense, and/or sell copies of the Software, and to
23 permit persons to whom the Software is furnished to do so, subject to
24 the following conditions:
25
26 The above copyright notice and this permission notice shall be included
27 in all copies or substantial portions of the Software.
28
29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
30 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
32 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
33 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
34 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
35 OTHER DEALINGS IN THE SOFTWARE.
36
37 Except as contained in this notice, the name of the X Consortium shall
38 not be used in advertising or otherwise to promote the sale, use or
39 other dealings in this Software without prior written authorization
40 from the X Consortium.
41 */
42
43 #include <math.h>
44 #include <sys/time.h>
45 #include "screenhack.h"
46 #include "config.h"
47
48
49 /**********************************************************************
50  *                                                                     
51  * window crap 
52  *
53  **********************************************************************/
54
55 char *progclass="xrayswarm";
56
57 char *defaults [] ={
58         ".background:           black",
59         "*delay:                0",
60         0
61 };
62
63 XrmOptionDescRec options [] = {
64         {"-delay",".delay",XrmoptionSepArg,0},
65         {0,0,0,0}
66 };
67
68 static char colors[768];
69
70 static Display *dpy;
71 static Window win;
72 static GC fgc[256];
73 static GC cgc;
74 static int xsize, ysize;
75 static int xc, yc;
76 static unsigned long delay;
77 static float maxx, maxy;
78
79 /**********************************************************************
80  *                                                                     
81  * bug structs & variables
82  *
83  **********************************************************************/
84 #define MAX_TRAIL_LEN 60
85 #define MAX_BUGS 100
86 #define MAX_TARGETS 10
87 #define sq(x) ((x)*(x))
88
89 #define MAX_FPS 150
90 #define MIN_FPS 16
91 #define DESIRED_DT 0.2
92
93 typedef struct _sbug {
94   float pos[3];
95   int hist[MAX_TRAIL_LEN][2];
96   float vel[2];
97   struct _sbug *closest;
98 } bug;
99
100 #define GRAY_TRAILS 0
101 #define GRAY_SCHIZO 1
102 #define COLOR_TRAILS 2
103 #define RANDOM_TRAILS 3
104 #define RANDOM_SCHIZO 4
105 #define COLOR_SCHIZO 5
106 #define NUM_SCHEMES 6 /* too many schizos; don't use last 2 */  
107
108 static float dt = 0.3;
109 static float targetVel = 0.03;
110 static float targetAcc = 0.02;
111 static float maxVel = 0.05;
112 static float maxAcc = 0.03;
113 static float noise = 0.01;
114 static float minVelMultiplier = 0.5;
115
116 static int nbugs = -1;
117 static int ntargets = -1;
118 static int trailLen = -1;
119
120 /* vars dependent on those above */
121 static float dtInv;
122 static float halfDtSq;
123 static float targetVelSq;
124 static float maxVelSq;
125 static float minVelSq;
126 static float minVel;
127
128 static bug bugs[MAX_BUGS];
129 static bug targets[MAX_TARGETS];
130 static int head = 0;
131 static int tail = 0;
132 static int colorScheme = -1;
133 static float changeProb = 0.08;
134
135 static int grayIndex[MAX_TRAIL_LEN];
136 static int redIndex[MAX_TRAIL_LEN];
137 static int blueIndex[MAX_TRAIL_LEN];
138 static int graySIndex[MAX_TRAIL_LEN];
139 static int redSIndex[MAX_TRAIL_LEN];
140 static int blueSIndex[MAX_TRAIL_LEN];
141 static int randomIndex[MAX_TRAIL_LEN];
142 static int numColors;
143 static int numRandomColors;
144
145 typedef struct {
146   float dt;
147   float targetVel;
148   float targetAcc;
149   float maxVel;
150   float maxAcc;
151   float noise;
152
153   int nbugs;
154   int ntargets;
155   int trailLen;
156   int colorScheme;
157   int changeProb;
158 } bugParams;
159
160 bugParams good1 = {
161   0.3,  /* dt */
162   0.03,  /* targetVel */
163   0.02,  /* targetAcc */
164   0.05,  /* maxVel */
165   0.03,  /* maxAcc */
166   0.01,  /* noise */
167   -1,    /* nbugs */
168   -1,     /* ntargets */
169   60,    /* trailLen */
170   2,     /* colorScheme */
171   0.15  /* changeProb */
172 };
173
174 bugParams *goodParams[] = { 
175 &good1
176 };
177
178 int numParamSets = 1;
179
180 void initCMap(void) {
181   int i, n;
182   int temp;
183
184   n = 0;
185
186   /* color 0 is black */
187   colors[n++] = 0;
188   colors[n++] = 0;
189   colors[n++] = 0;
190
191   /* color 1 is red */
192   colors[n++] = 255;
193   colors[n++] = 0;
194   colors[n++] = 0;
195
196   /* color 2 is green */
197   colors[n++] = 255;
198   colors[n++] = 0;
199   colors[n++] = 0;
200
201   /* color 3 is blue */
202   colors[n++] = 255;
203   colors[n++] = 0;
204   colors[n++] = 0;
205
206   /* start greyscale colors at 4; 16 levels */
207   for (i = 0; i < 16; i++) {
208     temp = i*16;
209     if (temp > 255) temp = 255;
210     colors[n++] = 255 - temp;
211     colors[n++] = 255 - temp;
212     colors[n++] = 255 - temp;
213   }
214
215   /* start red fade at 20; 16 levels */
216   for (i = 0; i < 16; i++) {
217     temp = i*16;
218     if (temp > 255) temp = 255;
219     colors[n++] = 255 - temp;
220     colors[n++] = 255 - pow(i/16.0+0.001, 0.3)*255;
221     colors[n++] = 65 - temp/4;
222   }
223
224   /* start blue fade at 36; 16 levels */
225   for (i = 0; i < 16; i++) {
226     temp = i*16;
227     if (temp > 255) temp = 255;
228     colors[n++] = 32 - temp/8;
229     colors[n++] = 180 - pow(i/16.0+0.001, 0.3)*180;
230     colors[n++] = 255 - temp;
231   }
232
233   /* random colors start at 52 */
234   numRandomColors = MAX_TRAIL_LEN;
235
236   colors[n++] = random()&255;
237   colors[n++] = random()&255;
238   colors[n++] = colors[n-2]/2 + colors[n-3]/2;
239
240   for (i = 0; i < numRandomColors; i++) {
241     colors[n++] = (colors[n-3] + (random()&31) - 16)&255;
242     colors[n++] = (colors[n-3] + (random()&31) - 16)&255;
243     colors[n++] = colors[n-2]/(float)(i+2) + colors[n-3]/(float)(i+2);
244   }
245   
246   numColors = n/3 + 1;
247 }
248
249 static int initGraphics(void) {
250   XGCValues xgcv;
251   XWindowAttributes xgwa;
252   XSetWindowAttributes xswa;
253   Colormap cmap;
254   XColor color;
255   int n, i;
256   
257   initCMap();
258
259   XGetWindowAttributes(dpy,win,&xgwa);
260   cmap=xgwa.colormap;
261   xswa.backing_store=Always;
262   XChangeWindowAttributes(dpy,win,CWBackingStore,&xswa);
263   xgcv.function=GXcopy;
264
265   delay = get_integer_resource("delay","Integer");
266   
267   xgcv.foreground=get_pixel_resource ("background", "Background", dpy, cmap);
268   fgc[0]=XCreateGC(dpy, win, GCForeground|GCFunction,&xgcv);
269   
270   n=0;
271   if (mono_p) {
272     xgcv.foreground=get_pixel_resource ("foreground", "Foreground", dpy, cmap);
273     fgc[1]=XCreateGC(dpy,win,GCForeground|GCFunction,&xgcv);
274     for (i=0;i<numColors;i+=2) fgc[i]=fgc[0];
275     for (i=1;i<numColors;i+=2) fgc[i]=fgc[1];
276   } else {
277     for (i = 0; i < numColors; i++) {
278       color.red=colors[n++]<<8;
279       color.green=colors[n++]<<8;
280       color.blue=colors[n++]<<8;
281       color.flags=DoRed|DoGreen|DoBlue;
282       XAllocColor(dpy,cmap,&color);
283       xgcv.foreground=color.pixel;
284       fgc[i] = XCreateGC(dpy, win, GCForeground | GCFunction,&xgcv);
285     }
286   }
287   cgc = XCreateGC(dpy,win,GCForeground|GCFunction,&xgcv);
288   XSetGraphicsExposures(dpy,cgc,False);
289
290   xsize = xgwa.width;
291   ysize = xgwa.height;
292   xc = xsize >> 1;
293   yc = ysize >> 1;
294
295   maxx = 1.0;
296   maxy = ysize/(float)xsize;
297
298   if (colorScheme < 0) colorScheme = random()%NUM_SCHEMES;
299
300   return True;
301 }
302
303 static void initBugs(void) {
304   register bug *b;
305   int i;
306
307   head = tail = 0;
308
309   memset((char *)bugs, 0,MAX_BUGS*sizeof(bug));
310   memset((char *)targets, 0, MAX_TARGETS*sizeof(bug));
311
312   if (ntargets < 0) ntargets = (0.25+frand(0.75)*frand(1))*MAX_TARGETS;
313   if (ntargets < 1) ntargets = 1;
314
315   if (nbugs < 0) nbugs = (0.25+frand(0.75)*frand(1))*MAX_BUGS;
316   if (nbugs <= ntargets) nbugs = ntargets+1;
317
318   if (trailLen < 0) {
319     trailLen = (1.0 - frand(0.6)*frand(1))*MAX_TRAIL_LEN;
320   }
321
322   if (nbugs > MAX_BUGS) nbugs = MAX_BUGS;
323   if (ntargets > MAX_TARGETS) ntargets = MAX_TARGETS;
324   if (trailLen > MAX_TRAIL_LEN) trailLen = MAX_TRAIL_LEN;
325
326   b = bugs;
327   for (i = 0; i < nbugs; i++, b++) {
328     b->pos[0] = frand(maxx);
329     b->pos[1] = frand(maxy);
330     b->vel[0] = frand(maxVel/2);
331     b->vel[1] = frand(maxVel/2);
332
333     b->hist[head][0] = b->pos[0]*xsize;
334     b->hist[head][1] = b->pos[1]*xsize;
335     b->closest = &targets[random()%ntargets];
336   }
337
338   b = targets;
339   for (i = 0; i < ntargets; i++, b++) {
340     b->pos[0] = frand(maxx);
341     b->pos[1] = frand(maxy);
342
343     b->vel[0] = frand(targetVel/2);
344     b->vel[1] = frand(targetVel/2);
345
346     b->hist[head][0] = b->pos[0]*xsize;
347     b->hist[head][1] = b->pos[1]*xsize;
348   }
349 }
350
351 static void pickNewTargets(void) {
352   register int i;
353   register bug *b;
354
355   b = bugs;
356   for (i = 0; i < nbugs; i++, b++) {
357     b->closest = &targets[random()%ntargets];
358   }
359 }
360
361 #if 0
362 static void addBugs(int numToAdd) {
363   register bug *b;
364   int i;
365
366   if (numToAdd + nbugs > MAX_BUGS) numToAdd = MAX_BUGS-nbugs;
367   else if (numToAdd < 0) numToAdd = 0;
368
369   for (i = 0; i < numToAdd; i++) {
370     b = &bugs[random()%nbugs];
371     memcpy((char *)&bugs[nbugs+i], (char *)b, sizeof(bug));
372     b->closest = &targets[random()%ntargets];
373   }
374
375   nbugs += numToAdd;
376 }
377
378 static void addTargets(int numToAdd) {
379   register bug *b;
380   int i;
381
382   if (numToAdd + ntargets > MAX_TARGETS) numToAdd = MAX_TARGETS-ntargets;
383   else if (numToAdd < 0) numToAdd = 0;
384
385   for (i = 0; i < numToAdd; i++) {
386     b = &targets[random()%ntargets];
387     memcpy((char *)&targets[ntargets+i], (char *)b, sizeof(bug));
388     b->closest = &targets[random()%ntargets];
389   }
390
391   ntargets += numToAdd;
392 }
393 #endif
394
395 static void computeConstants(void) {
396   halfDtSq = dt*dt*0.5;
397   dtInv = 1.0/dt;
398   targetVelSq = targetVel*targetVel;
399   maxVelSq = maxVel*maxVel;
400   minVel = maxVel*minVelMultiplier;
401   minVelSq = minVel*minVel;
402 }
403
404 void computeColorIndices(void) {
405   int i;
406   int schizoLength;
407
408   /* note: colors are used in *reverse* order! */
409
410   /* grayscale */
411   for (i = 0; i < trailLen; i++) {
412     grayIndex[trailLen-1-i] = 4 + i*16.0/trailLen + 0.5;
413     if (grayIndex[trailLen-1-i] > 19) grayIndex[trailLen-1-i] = 19;
414   }
415
416   /* red */
417   for (i = 0; i < trailLen; i++) {
418     redIndex[trailLen-1-i] = 20 + i*16.0/trailLen + 0.5;
419     if (redIndex[trailLen-1-i] > 35) redIndex[trailLen-1-i] = 35;
420   }
421
422   /* blue */
423   for (i = 0; i < trailLen; i++) {
424     blueIndex[trailLen-1-i] = 36 + i*16.0/trailLen + 0.5;
425     if (blueIndex[trailLen-1-i] > 51) blueIndex[trailLen-1-i] = 51;
426   }
427
428   /* gray schizo - same as gray*/
429   for (i = 0; i < trailLen; i++) {
430     graySIndex[trailLen-1-i] = 4 + i*16.0/trailLen + 0.5;
431     if (graySIndex[trailLen-1-i] > 19) graySIndex[trailLen-1-i] = 19;
432   }
433
434   schizoLength = trailLen/4;
435   if (schizoLength < 3) schizoLength = 3;
436   /* red schizo */
437   for (i = 0; i < trailLen; i++) {
438     /*    redSIndex[trailLen-1-i] = 
439           20 + 16.0*(i%schizoLength)/(schizoLength-1.0) + 0.5;*/
440     redSIndex[trailLen-1-i] = 20 + i*16.0/trailLen + 0.5;
441     if (redSIndex[trailLen-1-i] > 35) redSIndex[trailLen-1-i] = 35;
442   }
443
444   schizoLength = trailLen/2;
445   if (schizoLength < 3) schizoLength = 3;
446   /* blue schizo is next */
447   for (i = 0; i < trailLen; i++) {
448     blueSIndex[trailLen-1-i] = 
449       36 + 16.0*(i%schizoLength)/(schizoLength-1.0) + 0.5;
450     if (blueSIndex[trailLen-1-i] > 51) blueSIndex[trailLen-1-i] = 51;
451   }
452
453   /* random is next */
454   for (i = 0; i < trailLen; i++) {
455     randomIndex[i] = 52 + random()%(numRandomColors);
456   }
457 }
458
459 #if 0
460 static void setParams(bugParams *p) {
461   dt = p->dt;
462   targetVel = p->targetVel;
463   targetAcc = p->targetAcc;
464   maxVel = p->maxVel;
465   maxAcc = p->maxAcc;
466   noise = p->noise;
467
468   nbugs = p->nbugs;
469   ntargets = p->ntargets;
470   trailLen = p->trailLen;
471   colorScheme = p->colorScheme;
472   changeProb = p->changeProb;
473   computeConstants();
474   computeColorIndices();
475 }
476 #endif
477
478 static void drawBugs(int *tColorIdx, int tci0, int tnc,
479                      int *colorIdx, int ci0, int nc) {
480   register bug *b;
481   register int i, j;
482   int temp;
483
484   if (((head+1)%trailLen) == tail) {
485     /* first, erase last segment of bugs if necessary */
486     temp = (tail+1) % trailLen;
487     
488     b = bugs;
489     for (i = 0; i < nbugs; i++, b++) {
490       XDrawLine(dpy, win, fgc[0],
491                 b->hist[tail][0], b->hist[tail][1],
492                 b->hist[temp][0], b->hist[temp][1]);
493     }
494   
495     b = targets;
496     for (i = 0; i < ntargets; i++, b++) {
497       XDrawLine(dpy, win, fgc[0],
498                 b->hist[tail][0], b->hist[tail][1],
499                 b->hist[temp][0], b->hist[temp][1]);
500     }
501     tail = (tail+1)%trailLen;
502   }
503   
504   for (j = tail; j != head; j = temp) {
505     temp = (j+1)%trailLen;
506
507     b = bugs;
508     for (i = 0; i < nbugs; i++, b++) {
509       XDrawLine(dpy, win, fgc[colorIdx[ci0]],
510                 b->hist[j][0], b->hist[j][1],
511                 b->hist[temp][0], b->hist[temp][1]);
512     }
513     
514     b = targets;
515     for (i = 0; i < ntargets; i++, b++) {
516       XDrawLine(dpy, win, fgc[tColorIdx[tci0]],
517                 b->hist[j][0], b->hist[j][1],
518                 b->hist[temp][0], b->hist[temp][1]);
519     }
520     ci0 = (ci0+1)%nc;  
521     tci0 = (tci0+1)%tnc;  
522   }
523 }
524
525 static void clearBugs(void) {
526   register bug *b;
527   register int i, j;
528   int temp;
529
530   tail = tail-1;
531   if (tail < 0) tail = trailLen-1;
532
533   if (((head+1)%trailLen) == tail) {
534     /* first, erase last segment of bugs if necessary */
535     temp = (tail+1) % trailLen;
536     
537     b = bugs;
538     for (i = 0; i < nbugs; i++, b++) {
539       XDrawLine(dpy, win, fgc[0],
540                 b->hist[tail][0], b->hist[tail][1],
541                 b->hist[temp][0], b->hist[temp][1]);
542     }
543   
544     b = targets;
545     for (i = 0; i < ntargets; i++, b++) {
546       XDrawLine(dpy, win, fgc[0],
547                 b->hist[tail][0], b->hist[tail][1],
548                 b->hist[temp][0], b->hist[temp][1]);
549     }
550     tail = (tail+1)%trailLen;
551   }
552   
553   for (j = tail; j != head; j = temp) {
554     temp = (j+1)%trailLen;
555
556     b = bugs;
557     for (i = 0; i < nbugs; i++, b++) {
558       XDrawLine(dpy, win, fgc[0],
559                 b->hist[j][0], b->hist[j][1],
560                 b->hist[temp][0], b->hist[temp][1]);
561     }
562     
563     b = targets;
564     for (i = 0; i < ntargets; i++, b++) {
565       XDrawLine(dpy, win, fgc[0],
566                 b->hist[j][0], b->hist[j][1],
567                 b->hist[temp][0], b->hist[temp][1]);
568     }
569   }
570 }
571
572 void updateState(void) {
573   register int i;
574   register bug *b;
575   register float ax, ay, temp;
576   float theta;
577   static int checkIndex = 0;
578   bug *b2;
579   int j;
580
581   head = (head+1)%trailLen;
582   
583   for (j = 0; j < 5; j++) {
584     /* update closets bug for the bug indicated by checkIndex */
585     checkIndex = (checkIndex+1)%nbugs;
586     b = &bugs[checkIndex];
587
588     ax = b->closest->pos[0] - b->pos[0];
589     ay = b->closest->pos[1] - b->pos[1];
590     temp = ax*ax + ay*ay;
591     for (i = 0; i < ntargets; i++) {
592       b2 = &targets[i];
593       if (b2 == b->closest) continue;
594       ax = b2->pos[0] - b->pos[0];
595       ay = b2->pos[1] - b->pos[1];
596       theta = ax*ax + ay*ay;
597       if (theta < temp*2) {
598         b->closest = b2;
599         temp = theta;
600       }
601     }
602   }
603   
604   /* update target state */
605
606   b = targets;
607   for (i = 0; i < ntargets; i++, b++) {
608     theta = frand(6.28);
609     ax = targetAcc*cos(theta);
610     ay = targetAcc*sin(theta);
611
612     b->vel[0] += ax*dt;
613     b->vel[1] += ay*dt;
614
615     /* check velocity */
616     temp = sq(b->vel[0]) + sq(b->vel[1]);
617     if (temp > targetVelSq) {
618       temp = targetVel/sqrt(temp);
619       /* save old vel for acc computation */
620       ax = b->vel[0];
621       ay = b->vel[1];
622
623       /* compute new velocity */
624       b->vel[0] *= temp;
625       b->vel[1] *= temp;
626       
627       /* update acceleration */
628       ax = (b->vel[0]-ax)*dtInv;
629       ay = (b->vel[1]-ay)*dtInv;
630     }
631
632     /* update position */
633     b->pos[0] += b->vel[0]*dt + ax*halfDtSq;
634     b->pos[1] += b->vel[1]*dt + ay*halfDtSq;
635
636     /* check limits on targets */
637     if (b->pos[0] < 0) {
638       /* bounce */
639       b->pos[0] = -b->pos[0];
640       b->vel[0] = -b->vel[0];
641     } else if (b->pos[0] >= maxx) {
642       /* bounce */
643       b->pos[0] = 2*maxx-b->pos[0];
644       b->vel[0] = -b->vel[0];
645     }
646     if (b->pos[1] < 0) {
647       /* bounce */
648       b->pos[1] = -b->pos[1];
649       b->vel[1] = -b->vel[1];
650     } else if (b->pos[1] >= maxy) {
651       /* bounce */
652       b->pos[1] = 2*maxy-b->pos[1];
653       b->vel[1] = -b->vel[1];
654     }
655
656     b->hist[head][0] = b->pos[0]*xsize;
657     b->hist[head][1] = b->pos[1]*xsize;
658   }
659
660   /* update bug state */
661   b = bugs;
662   for (i = 0; i < nbugs; i++, b++) {
663     theta = atan2(b->closest->pos[1] - b->pos[1] + frand(noise),
664                   b->closest->pos[0] - b->pos[0] + frand(noise));
665     ax = maxAcc*cos(theta);
666     ay = maxAcc*sin(theta);
667
668     b->vel[0] += ax*dt;
669     b->vel[1] += ay*dt;
670
671     /* check velocity */
672     temp = sq(b->vel[0]) + sq(b->vel[1]);
673     if (temp > maxVelSq) {
674       temp = maxVel/sqrt(temp);
675
676       /* save old vel for acc computation */
677       ax = b->vel[0];
678       ay = b->vel[1];
679
680       /* compute new velocity */
681       b->vel[0] *= temp;
682       b->vel[1] *= temp;
683       
684       /* update acceleration */
685       ax = (b->vel[0]-ax)*dtInv;
686       ay = (b->vel[1]-ay)*dtInv;
687     } else if (temp < minVelSq) {
688       temp = minVel/sqrt(temp);
689
690       /* save old vel for acc computation */
691       ax = b->vel[0];
692       ay = b->vel[1];
693
694       /* compute new velocity */
695       b->vel[0] *= temp;
696       b->vel[1] *= temp;
697       
698       /* update acceleration */
699       ax = (b->vel[0]-ax)*dtInv;
700       ay = (b->vel[1]-ay)*dtInv;
701     }
702
703     /* update position */
704     b->pos[0] += b->vel[0]*dt + ax*halfDtSq;
705     b->pos[1] += b->vel[1]*dt + ay*halfDtSq;
706
707     /* check limits on targets */
708     if (b->pos[0] < 0) {
709       /* bounce */
710       b->pos[0] = -b->pos[0];
711       b->vel[0] = -b->vel[0];
712     } else if (b->pos[0] >= maxx) {
713       /* bounce */
714       b->pos[0] = 2*maxx-b->pos[0];
715       b->vel[0] = -b->vel[0];
716     }
717     if (b->pos[1] < 0) {
718       /* bounce */
719       b->pos[1] = -b->pos[1];
720       b->vel[1] = -b->vel[1];
721     } else if (b->pos[1] >= maxy) {
722       /* bounce */
723       b->pos[1] = 2*maxy-b->pos[1];
724       b->vel[1] = -b->vel[1];
725     }
726
727     b->hist[head][0] = b->pos[0]*xsize;
728     b->hist[head][1] = b->pos[1]*xsize;
729   }
730 }
731
732 void mutateBug(int which) {
733   int i, j;
734
735   if (which == 0) {
736     /* turn bug into target */
737     if (ntargets < MAX_TARGETS-1 && nbugs > 1) {
738       i = random() % nbugs;
739       memcpy((char *)&targets[ntargets], (char *)&bugs[i], sizeof(bug));
740       memcpy((char *)&bugs[i], (char *)&bugs[nbugs-1], sizeof(bug));
741       targets[ntargets].pos[0] = frand(maxx);
742       targets[ntargets].pos[1] = frand(maxy);
743       nbugs--;
744       ntargets++;
745
746       for (i = 0; i < nbugs; i += ntargets) {
747         bugs[i].closest = &targets[ntargets-1];
748       }
749     }
750   } else {
751     /* turn target into bug */
752     if (ntargets > 1 && nbugs < MAX_BUGS-1) {
753       /* pick a target */
754       i = random() % ntargets;
755
756       /* copy state into a new bug */
757       memcpy((char *)&bugs[nbugs], (char *)&targets[i], sizeof(bug));
758       ntargets--;
759
760       /* pick a target for the new bug */
761       bugs[nbugs].closest = &targets[random()%ntargets];
762
763       for (j = 0; j < nbugs; j++) {
764         if (bugs[j].closest == &targets[ntargets]) {
765           bugs[j].closest = &targets[i];
766         } else if (bugs[j].closest == &targets[i]) {
767           bugs[j].closest = &targets[random()%ntargets];
768         }
769       }
770       nbugs++;
771       
772       /* copy the last ntarget into the one we just deleted */
773       memcpy(&targets[i], (char *)&targets[ntargets], sizeof(bug));
774     }
775   }
776 }
777
778 void mutateParam(float *param) {
779   *param *= 0.75+frand(0.5);
780 }
781
782 void randomSmallChange(void) {
783   int whichCase = 0;
784   static int callDepth = 0;
785
786   whichCase = random()%11;
787
788   if (++callDepth > 10) {
789     callDepth--;
790     return;
791   }
792   
793   switch(whichCase) {
794   case 0:
795     /* acceleration */
796     mutateParam(&maxAcc);
797     break;
798
799   case 1:
800     /* target acceleration */
801     mutateParam(&targetAcc);
802     break;
803
804   case 2:
805     /* velocity */
806     mutateParam(&maxVel);
807     break;
808
809   case 3: 
810     /* target velocity */
811     mutateParam(&targetVel);
812     break;
813
814   case 4:
815     /* noise */
816     mutateParam(&noise);
817     break;
818
819   case 5:
820     /* minVelMultiplier */
821     mutateParam(&minVelMultiplier);
822     break;
823     
824   case 6:
825   case 7:
826     /* target to bug */
827     if (ntargets < 2) break;
828     mutateBug(1);
829     break;
830
831   case 8:
832     /* bug to target */
833     if (nbugs < 2) break;
834     mutateBug(0);
835     if (nbugs < 2) break;
836     mutateBug(0);
837     break;
838
839   case 9:
840     /* color scheme */
841     colorScheme = random()%NUM_SCHEMES;
842     if (colorScheme == RANDOM_SCHIZO || colorScheme == COLOR_SCHIZO) {
843       /* don't use these quite as much */
844       colorScheme = random()%NUM_SCHEMES;
845     }
846     break;
847
848   default:
849     randomSmallChange();
850     randomSmallChange();
851     randomSmallChange();
852     randomSmallChange();
853   }
854
855   if (minVelMultiplier < 0.3) minVelMultiplier = 0.3;
856   else if (minVelMultiplier > 0.9) minVelMultiplier = 0.9;
857   if (noise < 0.01) noise = 0.01;
858   if (maxVel < 0.02) maxVel = 0.02;
859   if (targetVel < 0.02) targetVel = 0.02;
860   if (targetAcc > targetVel*0.7) targetAcc = targetVel*0.7;
861   if (maxAcc > maxVel*0.7) maxAcc = maxVel*0.7;
862   if (targetAcc > targetVel*0.7) targetAcc = targetVel*0.7;
863   if (maxAcc < 0.01) maxAcc = 0.01;
864   if (targetAcc < 0.005) targetAcc = 0.005;
865
866   computeConstants();
867   callDepth--;
868 }
869
870 void randomBigChange(void) {
871   static int whichCase = 0;
872   static int callDepth = 0;
873   int temp;
874
875   whichCase = random()%4;
876   
877   if (++callDepth > 3) {
878     callDepth--;
879     return;
880   }
881
882   switch(whichCase) {
883   case 0:
884     /* trail length */
885     temp = (random()%(MAX_TRAIL_LEN-25)) + 25;
886     clearBugs();
887     trailLen = temp;
888     computeColorIndices();
889     initBugs();
890     break;
891
892   case 1:  
893     /* Whee! */
894     randomSmallChange();
895     randomSmallChange();
896     randomSmallChange();
897     randomSmallChange();
898     randomSmallChange();
899     randomSmallChange();
900     randomSmallChange();
901     randomSmallChange();
902     break;
903
904   case 2:
905     clearBugs();
906     initBugs();
907     break;
908     
909   case 3:
910     pickNewTargets();
911     break;
912     
913   default:
914     temp = random()%ntargets;
915     targets[temp].pos[0] += frand(maxx/4)-maxx/8;
916     targets[temp].pos[1] += frand(maxy/4)-maxy/8;
917     /* updateState() will fix bounds */
918     break;
919   }
920
921   callDepth--;
922 }
923
924 void updateColorIndex(int **tColorIdx, int *tci0, int *tnc,
925                       int **colorIdx, int *ci0, int *nc) {
926   switch(colorScheme) {
927   case COLOR_TRAILS:
928     *tColorIdx = redIndex;
929     *tci0 = 0;
930     *tnc = trailLen;
931     *colorIdx = blueIndex;
932     *ci0 = 0;
933     *nc = trailLen;
934     break;
935
936   case GRAY_SCHIZO:
937     *tColorIdx = graySIndex;
938     *tci0 = head;
939     *tnc = trailLen;
940     *colorIdx = graySIndex;
941     *ci0 = head;
942     *nc = trailLen;
943     break;
944
945   case COLOR_SCHIZO:
946     *tColorIdx = redSIndex;
947     *tci0 = head;
948     *tnc = trailLen;
949     *colorIdx = blueSIndex;
950     *ci0 = head;
951     *nc = trailLen;
952     break;
953
954   case GRAY_TRAILS:
955     *tColorIdx = grayIndex;
956     *tci0 = 0;
957     *tnc = trailLen;
958     *colorIdx = grayIndex;
959     *ci0 = 0;
960     *nc = trailLen;
961     break;
962
963   case RANDOM_TRAILS:
964     *tColorIdx = redIndex;
965     *tci0 = 0;
966     *tnc = trailLen;
967     *colorIdx = randomIndex;
968     *ci0 = 0;
969     *nc = trailLen;
970     break;
971
972   case RANDOM_SCHIZO:
973     *tColorIdx = redIndex;
974     *tci0 = head;
975     *tnc = trailLen;
976     *colorIdx = randomIndex;
977     *ci0 = head;
978     *nc = trailLen;
979     break;
980   }
981 }
982
983 #if HAVE_GETTIMEOFDAY
984 static struct timeval startupTime;
985 static void initTime(void) {
986 #if GETTIMEOFDAY_TWO_ARGS
987   gettimeofday(&startupTime, NULL);
988 #else
989   gettimeofday(&startupTime);
990 #endif
991 }
992
993 static double getTime(void) {
994   struct timeval t;
995   float f;
996 #if GETTIMEOFDAY_TWO_ARGS
997   gettimeofday(&t, NULL);
998 #else
999   gettimeofday(&t);
1000 #endif
1001   t.tv_sec -= startupTime.tv_sec;
1002   f = ((double)t.tv_sec) + t.tv_usec*1e-6;
1003   return f;
1004 }
1005 #endif
1006
1007 void screenhack(Display *d, Window w) {
1008   int nframes, i;
1009   float fps;
1010   float timePerFrame, elapsed;
1011   int *targetColorIndex, *colorIndex;
1012   int targetStartColor, targetNumColors;
1013   int startColor, numColors;
1014   double start, end;
1015   int cnt;
1016   int sleepCount = 0;
1017   int delayAccum = 0;
1018
1019   dpy = d;
1020   win = w;
1021
1022   if (!initGraphics()) return;
1023
1024   computeConstants();
1025   initBugs();
1026   initTime();
1027   computeColorIndices();
1028
1029   if (changeProb > 0) {
1030     for (i = random()%5+5; i >= 0; i--) {
1031       randomSmallChange();
1032     }
1033   }
1034
1035   nframes = 0;
1036 #if HAVE_GETTIMEOFDAY
1037   start = getTime();
1038 #endif
1039
1040   while (1) {
1041     if (delay > 0) {
1042       cnt = 2;
1043       dt = DESIRED_DT/2;
1044     } else {
1045       cnt = 1;
1046       dt = DESIRED_DT;
1047     }
1048
1049     for (; cnt > 0; cnt--) {
1050       updateState();
1051       updateColorIndex(&targetColorIndex, &targetStartColor, &targetNumColors,
1052                        &colorIndex, &startColor, &numColors);
1053       drawBugs(targetColorIndex, targetStartColor, targetNumColors,
1054                colorIndex, startColor, numColors);
1055       XSync(dpy, False);
1056       screenhack_handle_events (dpy);
1057     }
1058 #if HAVE_GETTIMEOFDAY
1059     end = getTime();
1060     nframes++;
1061
1062     if (end > start+0.5) {
1063       if (frand(1.0) < changeProb) randomSmallChange();
1064       if (frand(1.0) < changeProb*0.3) randomBigChange();
1065       elapsed = end-start;
1066         
1067       timePerFrame = elapsed/nframes - delay*1e-6;
1068       fps = nframes/elapsed;
1069       /*
1070       printf("elapsed: %.3f\n", elapsed);
1071       printf("fps: %.1f  secs per frame: %.3f  delay: %f\n", 
1072              fps, timePerFrame, delay);
1073       */
1074
1075       if (fps > MAX_FPS) {
1076         delay = (1.0/MAX_FPS - (timePerFrame + delay*1e-6))*1e6;
1077       } else if (dt*fps < MIN_FPS*DESIRED_DT) {
1078         /* need to speed things up somehow */
1079         if (0 && nbugs > 10) {
1080           /*printf("reducing bugs to improve speed.\n");*/
1081           clearBugs();
1082           nbugs *= fps/MIN_FPS;
1083           if (ntargets >= nbugs/2) mutateBug(1);
1084         } else if (0 && dt < 0.3) {
1085           /*printf("increasing dt to improve speed.\n");*/
1086           dt *= MIN_FPS/fps;
1087           computeConstants();
1088         } else if (trailLen > 10) {
1089           /*printf("reducing trail length to improve speed.\n");*/
1090           clearBugs();
1091           trailLen = trailLen * (fps/MIN_FPS);
1092           if (trailLen < 10) trailLen = 10;
1093           computeColorIndices();
1094           initBugs();
1095         }
1096       }
1097
1098       start = getTime();
1099       nframes = 0;
1100     }
1101 #else
1102     if (frand(1) < changeProb*2/100.0) randomSmallChange();
1103     if (frand(1) < changeProb*0.3/100.0) randomBigChange();
1104 #endif
1105     
1106     if (delay > 10000) usleep(delay);
1107     else {
1108       delayAccum += delay;
1109       if (delayAccum > 10000) {
1110         usleep(delayAccum);
1111         delayAccum = 0;
1112         sleepCount = 0;
1113       }
1114       if (++sleepCount > 2) {
1115         sleepCount = 0;
1116         usleep(10000);
1117       }
1118     }
1119   }
1120 }