From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / interference.c
1 /* interference.c --- colored fields via decaying sinusoidal waves.
2  * An entry for the RHAD Labs Screensaver Contest.
3  *
4  * Author: Hannu Mallat <hmallat@cs.hut.fi>
5  *
6  * Copyright (C) 1998 Hannu Mallat.
7  *
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notice appear in all copies and that both that
11  * copyright notice and this permission notice appear in supporting
12  * documentation.  No representations are made about the suitability of this
13  * software for any purpose.  It is provided "as is" without express or 
14  * implied warranty.
15  *
16  * decaying sinusoidal waves, which extend spherically from their
17  * respective origins, move around the plane. a sort of interference
18  * between them is calculated and the resulting twodimensional wave
19  * height map is plotted in a grid, using softly changing colours.
20  *
21  * not physically (or in any sense) accurate, but fun to look at for 
22  * a while. you may tune the speed/resolution/interestingness tradeoff 
23  * with X resources, see below.
24  *
25  * Created      : Wed Apr 22 09:30:30 1998, hmallat
26  * Last modified: Wed Apr 22 09:30:30 1998, hmallat
27  * Last modified: Sun Aug 31 23:40:14 2003, 
28  *              david slimp <rock808@DavidSlimp.com>
29  *              added -hue option to specify base color hue
30  * Last modified: Wed May 15 00:04:43 2013,
31  *              Dave Odell <dmo2118@gmail.com>
32  *              Tuned performance; double-buffering is now off by default.
33  *              Made animation speed independent of FPS.
34  *              Added cleanup code, fixed a few glitches.
35  *              Added gratuitous #ifdefs.
36  * Last modified: Fri Feb 21 02:14:29 2014, <dmo2118@gmail.com>
37  *              Added support for SMP rendering.
38  *              Tweaked math a bit re: performance.
39  * Last modified: Tue Dec 30 16:43:33 2014, <dmo2118@gmail.com>
40  *              Killed the black margin on the right and bottom.
41  *              Reduced the default grid size to 2.
42  * Last modified: Sun Oct 9 11:20:48 2016, <dmo2118@gmail.com>
43  *              Updated for new xshm.c.
44  *              Ditched USE_BIG_XIMAGE.
45  */
46
47 #include <math.h>
48 #include <errno.h>
49
50 #include "screenhack.h"
51
52 #include "thread_util.h"
53
54 #ifdef HAVE_INTTYPES_H
55 # include <inttypes.h>
56 #else
57
58 typedef unsigned int uint32_t;
59 typedef unsigned short uint16_t;
60 typedef unsigned char uint8_t;
61
62 #endif
63
64 /*
65 Tested on an Intel(R) Pentium(R) 4 CPU 3.00GHz (family 15, model 6, 2 cores),
66 1 GB PC2-4200, nouveau - Gallium 0.4 on NV44, X.Org version: 1.13.3. A very
67 modest system by current standards.
68
69 Does double-buffering make sense? (gridsize = 2)
70 USE_XIMAGE is off: Yes (-db: 4.1 FPS, -no-db: 2.9 FPS)
71 XPutImage in strips: No (-db: 35.9 FPS, -no-db: 38.7 FPS)
72 XPutImage, whole image: No (-db: 32.3 FPS, -no-db: 33.7 FPS)
73 MIT-SHM, whole image: Doesn't work anyway: (-no-db: 37.3 FPS)
74
75 If gridsize = 1, XPutImage is slow when the XImage is one line at a time.
76 XPutImage in strips: -db: 21.2 FPS, -no-db: 19.7 FPS
77 XPutimage, whole image: -db: 23.2 FPS, -no-db: 23.4 FPS
78 MIT-SHM: 26.0 FPS
79
80 So XPutImage in strips is very slightly faster when gridsize >= 2, but
81 quite a bit worse when gridsize = 1.
82 */
83
84 /* I thought it would be faster this way, but it turns out not to be... -jwz */
85 /* It's a lot faster for me, though - D.O. */
86 #define USE_XIMAGE
87
88 /* Numbers are wave_table size, measured in # of unsigned integers.
89  * FPS/radius = 50/radius = 800/radius = 1500/Big-O memory usage
90  *
91  * Use at most one of the following:
92  * Both off = regular sqrt() - 13.5 FPS, 50/800/1500. */
93
94 /* #define USE_FAST_SQRT_HACKISH */ /* 17.8 FPS/2873/4921/5395/O(lg(radius)) */
95 #define USE_FAST_SQRT_BIGTABLE2 /* 26.1 FPS/156/2242/5386/O(radius^2) */
96
97 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
98 # include "xdbe.h"
99 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
100
101 #ifdef USE_XIMAGE
102 # include "xshm.h"
103 #endif /* USE_XIMAGE */
104
105 static const char *interference_defaults [] = {
106   ".background:  black",
107   ".foreground:  white",
108   "*count:       3",     /* number of waves */
109   "*gridsize:    2",     /* pixel size, smaller values for better resolution */
110   "*ncolors:     192",   /* number of colours used */
111   "*hue:         0",     /* hue to use for base color (0-360) */
112   "*speed:       30",    /* speed of wave origins moving around */
113   "*delay:       30000", /* or something */
114   "*color-shift: 60",    /* h in hsv space, smaller values for smaller
115                           * color gradients */
116   "*radius:      800",   /* wave extent */
117   "*gray:        false", /* color or grayscale */
118   "*mono:        false", /* monochrome, not very much fun */
119
120   "*doubleBuffer: False", /* doubleBuffer slows things down for me - D.O. */
121 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
122   "*useDBE:      True", /* use double buffering extension */
123 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
124
125 #ifdef HAVE_XSHM_EXTENSION
126   "*useSHM:      True", /* use shared memory extension */
127 #endif /*  HAVE_XSHM_EXTENSION */
128 #ifdef HAVE_MOBILE
129   "*ignoreRotation: True",
130 #endif
131   THREAD_DEFAULTS
132   0
133 };
134
135 static XrmOptionDescRec interference_options [] = {
136   { "-count",       ".count",       XrmoptionSepArg, 0 }, 
137   { "-ncolors",     ".ncolors",     XrmoptionSepArg, 0 }, 
138   { "-gridsize",    ".gridsize",    XrmoptionSepArg, 0 }, 
139   { "-hue",         ".hue",         XrmoptionSepArg, 0 },
140   { "-speed",       ".speed",       XrmoptionSepArg, 0 },
141   { "-delay",       ".delay",       XrmoptionSepArg, 0 },
142   { "-color-shift", ".color-shift", XrmoptionSepArg, 0 },
143   { "-radius",      ".radius",      XrmoptionSepArg, 0 },
144   { "-gray",        ".gray",        XrmoptionNoArg,  "True" },
145   { "-mono",        ".mono",        XrmoptionNoArg,  "True" },
146   { "-db",          ".doubleBuffer", XrmoptionNoArg,  "True" },
147   { "-no-db",       ".doubleBuffer", XrmoptionNoArg,  "False" },
148 #ifdef HAVE_XSHM_EXTENSION
149   { "-shm",     ".useSHM",      XrmoptionNoArg, "True" },
150   { "-no-shm",  ".useSHM",      XrmoptionNoArg, "False" },
151 #endif /*  HAVE_XSHM_EXTENSION */
152   THREAD_OPTIONS
153   { 0, 0, 0, 0 }
154 };
155
156 struct inter_source {
157   int x;
158   int y;
159   double x_theta;
160   double y_theta;
161 };
162
163 struct inter_context {
164   /*
165    * Display-related entries
166    */
167   Display* dpy;
168   Window   win;
169
170 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
171   XdbeBackBuffer back_buf;
172 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
173   Pixmap   pix_buf;
174
175   GC       copy_gc;
176 #ifdef USE_XIMAGE
177   XImage  *ximage;
178
179   Bool shm_can_draw;
180   XShmSegmentInfo shm_info;
181 #endif /* USE_XIMAGE */
182
183   /*
184    * Resources
185    */
186   int count;
187   int grid_size;
188   int colors;
189   float hue;
190   int speed;
191   int delay;
192   int shift;
193
194   /*
195    * Drawing-related entries
196    */
197   int w;
198   int h;
199   unsigned w_div_g, h_div_g;
200   Colormap cmap;
201   Screen *screen;
202   unsigned bits_per_pixel;
203   XColor* pal;
204 #ifndef USE_XIMAGE
205   GC* gcs;
206 #endif
207   int radius; /* Not always the same as the X resource. */
208   double last_frame;
209
210   struct threadpool threadpool;
211
212   /*
213    * lookup tables
214    */
215   unsigned* wave_height;
216     
217   /*
218    * Interference sources
219    */
220   struct inter_source* source;
221 };
222
223 struct inter_thread
224 {
225   const struct inter_context *context;
226   unsigned thread_id;
227
228 #ifdef USE_XIMAGE
229   uint32_t* row;
230 #endif
231
232   unsigned* result_row;
233 };
234
235 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
236 # define TARGET(c) ((c)->back_buf ? (c)->back_buf : \
237                     (c)->pix_buf ? (c)->pix_buf : (c)->win)
238 #else  /* HAVE_DOUBLE_BUFFER_EXTENSION */
239 # define TARGET(c) ((c)->pix_buf ? (c)->pix_buf : (c)->win)
240 #endif /* !HAVE_DOUBLE_BUFFER_EXTENSION */
241
242 #ifdef USE_FAST_SQRT_HACKISH
243 /* Based loosely on code from Wikipedia:
244  * https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Approximations_that_depend_on_IEEE_representation
245  */
246
247 /* FAST_SQRT_EXTRA_BITS = 3: Smallest useful value
248  * = 5/6: A little bit of banding, wave_height table is on par with regular
249  *        sqrt() code.
250  * = 7: No apparent difference with original @ radius = 800.
251  * = 8: One more just to be comfortable.
252  */
253
254 # define FAST_SQRT_EXTRA_BITS 8
255
256 union int_float
257 {
258   uint32_t i;
259   float f;
260 };
261
262 static unsigned fast_log2(unsigned x)
263 {
264   union int_float u;
265   if(!x)
266     return x;
267   u.f = x;
268   return ((u.i - 0x3f800000) >> (23 - FAST_SQRT_EXTRA_BITS)) + 1;
269 }
270
271 static float fast_inv_log2(unsigned x)
272 {
273   union int_float u;
274   if(!x)
275     return 0.0f;
276   u.i = ((x - 1) << (23 - FAST_SQRT_EXTRA_BITS)) + 0x3f800000;
277   return u.f;
278 }
279
280 #endif
281
282 #ifdef USE_FAST_SQRT_BIGTABLE2
283
284 /* I eyeballed these figures. They could be improved. - D.O. */
285
286 # define FAST_SQRT_DISCARD_BITS1 4
287 /* = 5: Dot in center is almost invisible at radius = 800. */
288 /* = 4: Dot in center looks OK at radius = 50. */
289
290 /* 156/2740/9029 */
291 /* # define FAST_SQRT_DISCARD_BITS2 8 */
292 /* # define FAST_SQRT_CUTOFF 64 * 64 */
293
294 /* 156/2242/5386 */
295 # define FAST_SQRT_DISCARD_BITS2 9
296 # define FAST_SQRT_CUTOFF 128 * 128
297
298 /*
299  * This is a little faster:
300  * 44.5 FPS, 19/5000/17578
301  *
302  * # define FAST_SQRT_DISCARD_BITS1 7
303  * # define FAST_SQRT_DISCARD_BITS2 7
304  * # define FAST_SQRT_CUTOFF 0
305  *
306  * For radius = 800, FAST_SQRT_DISCARD_BITS2 =
307  * = 9/10: Approximately the original table size, some banding near origins.
308  * = 7: wave_height is 20 KB, and just fits inside a 32K L1 cache.
309  * = 6: Nearly indistinguishable from original
310  */
311
312 /*
313   FAST_TABLE(x) is equivalent to, but slightly faster than:
314   x < FAST_SQRT_CUTOFF ?
315     (x >> FAST_SQRT_DISCARD_BITS1) :
316     ((x - FAST_SQRT_CUTOFF) >> FAST_SQRT_DISCARD_BITS2) +
317       (FAST_SQRT_CUTOFF >> FAST_SQRT_DISCARD_BITS1);
318 */
319
320 #define FAST_TABLE(x) \
321   ((x) < FAST_SQRT_CUTOFF ? \
322     ((x) >> FAST_SQRT_DISCARD_BITS1) : \
323     (((x) + \
324       ((FAST_SQRT_CUTOFF << (FAST_SQRT_DISCARD_BITS2 - \
325         FAST_SQRT_DISCARD_BITS1)) - FAST_SQRT_CUTOFF)) >> \
326       FAST_SQRT_DISCARD_BITS2))
327
328 static double fast_inv_table(unsigned x)
329 {
330   return x < (FAST_SQRT_CUTOFF >> FAST_SQRT_DISCARD_BITS1) ?
331     (x << FAST_SQRT_DISCARD_BITS1) :
332     ((x - (FAST_SQRT_CUTOFF >> FAST_SQRT_DISCARD_BITS1)) <<
333       FAST_SQRT_DISCARD_BITS2) + FAST_SQRT_CUTOFF;
334 }
335
336 #endif
337
338 static void destroy_image(Display* dpy, struct inter_context* c)
339 {
340 #ifdef USE_XIMAGE
341   if(c->ximage) {
342     destroy_xshm_image(dpy, c->ximage, &c->shm_info);
343   }
344 #endif
345
346   if(c->threadpool.count)
347   {
348     threadpool_destroy(&c->threadpool);
349     c->threadpool.count = 0;
350   }
351 }
352
353 static void inter_free(Display* dpy, struct inter_context* c)
354 {
355 #ifndef USE_XIMAGE
356   unsigned i;
357 #endif
358
359   if(c->pix_buf)
360     XFreePixmap(dpy, c->pix_buf);
361
362   if(c->copy_gc)
363     XFreeGC(dpy, c->copy_gc);
364
365   destroy_image(dpy, c);
366
367   if(c->colors <= 2)
368     free(c->pal);
369   else if(c->pal)
370     free_colors(c->screen, c->cmap, c->pal, c->colors);
371
372 #ifndef USE_XIMAGE
373   for(i = 0; i != c->colors; ++i)
374     XFreeGC(dpy, c->gcs[i]);
375   free(c->gcs);
376 #endif
377
378   free(c->wave_height);
379   free(c->source);
380 }
381
382 static void abort_on_error(int error)
383 {
384   fprintf(stderr, "interference: %s\n", strerror(error));
385   exit(1);
386 }
387
388 static void abort_no_mem(void)
389 {
390   abort_on_error(ENOMEM);
391 }
392
393 static void check_no_mem(Display* dpy, struct inter_context* c, void* ptr)
394 {
395   if(!ptr) {
396     inter_free(dpy, c);
397     abort_no_mem();
398   }
399 }
400
401 static int inter_thread_create(
402   void* self_raw,
403   struct threadpool* pool,
404   unsigned id)
405 {
406   struct inter_thread* self = (struct inter_thread*)self_raw;
407   const struct inter_context* c = GET_PARENT_OBJ(struct inter_context, threadpool, pool);
408
409   self->context = c;
410   self->thread_id = id;
411
412   self->result_row = malloc(c->w_div_g * sizeof(unsigned));
413   if(!self->result_row)
414     return ENOMEM;
415
416 #ifdef USE_XIMAGE
417   self->row = malloc(c->w_div_g * sizeof(uint32_t));
418   if(!self->row) {
419     free(self->result_row);
420     return ENOMEM;
421   }
422 #endif
423
424   return 0;
425 }
426
427 static void inter_thread_destroy(void* self_raw)
428 {
429   struct inter_thread* self = (struct inter_thread*)self_raw;
430 #ifdef USE_XIMAGE
431   free(self->row);
432 #endif
433   free(self->result_row);
434 }
435
436 /*
437 A higher performance design would have input and output queues, so that when
438 worker threads finish with one frame, they can pull the next work order from
439 the queue and get started on it immediately, rather than going straight to
440 sleep. The current "single-buffered" design still provides reasonable
441 performance at low frame rates; high frame rates are noticeably less efficient.
442 */
443
444 static void inter_thread_run(void* self_raw)
445 {
446   struct inter_thread* self = (struct inter_thread*)self_raw;
447   const struct inter_context* c = self->context;
448
449   int i, j, k;
450   unsigned result;
451   int dist1;
452   int g = c->grid_size;
453
454   int dx, dy, g2 = 2 * g * g;
455   int px, py, px2g;
456
457   int dist0, ddist;
458
459 #ifdef USE_XIMAGE
460   unsigned img_y = g * self->thread_id;
461   void *scanline = c->ximage->data + c->ximage->bytes_per_line * g * self->thread_id;
462 #endif
463
464   for(j = self->thread_id; j < c->h_div_g; j += c->threadpool.count) {
465     px = g/2;
466     py = j*g + px;
467
468     memset(self->result_row, 0, c->w_div_g * sizeof(unsigned));
469
470     for(k = 0; k < c->count; k++) {
471
472       dx = px - c->source[k].x;
473       dy = py - c->source[k].y;
474
475       dist0 = dx*dx + dy*dy;
476       ddist = -2 * g * c->source[k].x;
477
478       /* px2g = g*(px*2 + g); */
479       px2g = g2;
480
481       for(i = 0; i < c->w_div_g; i++) {
482         /*
483          * Discarded possibilities for improving performance here:
484          * 1. Using octagon-based distance estimation
485          *    (Which causes giant octagons to appear.)
486          * 2. Square root approximation by reinterpret-casting IEEE floats to
487          *    integers.
488          *    (Which causes angles to appear when two waves interfere.)
489          */
490
491 /*      int_float u;
492         u.f = dx*dx + dy*dy;
493         u.i = (1 << 29) + (u.i >> 1) - (1 << 22);
494         dist = u.f; */
495
496 #if defined USE_FAST_SQRT_BIGTABLE2
497         dist1 = FAST_TABLE(dist0);
498 #elif defined USE_FAST_SQRT_HACKISH
499         dist1 = fast_log2(dist0);
500 #else
501         dist1 = sqrt(dist0);
502 #endif
503
504         if(dist1 < c->radius)
505           self->result_row[i] += c->wave_height[dist1];
506
507         dist0 += px2g + ddist;
508         px2g += g2;
509       }
510     }
511
512     for(i = 0; i < c->w_div_g; i++) {
513
514       result = self->result_row[i];
515
516       /* It's slightly faster to do a subtraction or two before calculating the
517        * modulus. - D.O. */
518       if(result >= c->colors)
519       {
520         result -= c->colors;
521         if(result >= c->colors)
522           result %= (unsigned)c->colors;
523       }
524
525 #ifdef USE_XIMAGE
526       self->row[i] = c->pal[result].pixel;
527 #else
528       XFillRectangle(c->dpy, TARGET(c), c->gcs[result], g*i, g*j, g, g);
529 #endif /* USE_XIMAGE */
530     }
531
532 #ifdef USE_XIMAGE
533     /* Fill in these `gridsize' horizontal bits in the scanline */
534     if(c->ximage->bits_per_pixel == 32)
535     {
536       uint32_t *ptr = (uint32_t *)scanline;
537       for(i = 0; i < c->w_div_g; i++) {
538         for(k = 0; k < g; k++)
539           ptr[g*i+k] = self->row[i];
540       }
541     }
542     else if(c->ximage->bits_per_pixel == 24)
543     {
544       uint8_t *ptr = (uint8_t *)scanline;
545       for(i = 0; i < c->w_div_g; i++) {
546         for(k = 0; k < g; k++) {
547           uint32_t pixel = self->row[i];
548           /* Might not work on big-endian. */
549           ptr[0] = pixel;
550           ptr[1] = (pixel & 0x0000ff00) >> 8;
551           ptr[2] = (pixel & 0x00ff0000) >> 16;
552           ptr += 3;
553         }
554       }
555     }
556     else if(c->ximage->bits_per_pixel == 16)
557     {
558       uint16_t *ptr = (uint16_t *)scanline;
559       for(i = 0; i < c->w_div_g; i++) {
560         for(k = 0; k < g; k++)
561           ptr[g*i+k] = self->row[i];
562       }
563     }
564     else if(c->ximage->bits_per_pixel == 8)
565     {
566       uint8_t *ptr = (uint8_t *)scanline;
567       for(i = 0; i < c->w_div_g; i++) {
568         for(k = 0; k < g; k++)
569           ptr[g*i+k] = self->row[i];
570       }
571     }
572     else
573     {
574       for(i = 0; i < c->w_div_g; i++) {
575         for(k = 0; k < g; k++)
576           /* XPutPixel is thread safe as long as the XImage didn't have its
577            * bits_per_pixel changed. */
578           XPutPixel(c->ximage, (g*i)+k, img_y, self->row[i]);
579       }
580     }
581
582     /* Only the first scanline of the image has been filled in; clone that
583        scanline to the rest of the `gridsize' lines in the ximage */
584     for(k = 0; k < (g-1); k++)
585       memcpy(c->ximage->data + (c->ximage->bytes_per_line * (img_y + k + 1)),
586              c->ximage->data + (c->ximage->bytes_per_line * img_y),
587              c->ximage->bytes_per_line);
588
589     scanline = (char *)scanline +
590                  c->ximage->bytes_per_line * g * c->threadpool.count;
591     img_y += g * c->threadpool.count;
592
593 #endif /* USE_XIMAGE */
594   }
595 }
596
597 /* On allocation error, c->ximage == NULL. */
598 static void create_image(
599   Display* dpy, 
600   struct inter_context* c, 
601   const XWindowAttributes* xgwa)
602 {
603 #ifdef USE_XIMAGE
604
605   /* Set the width so that each thread can work on a different line. */
606   unsigned align = thread_memory_alignment(dpy) * 8 - 1;
607   unsigned wbits, w, h;
608 #endif /* USE_XIMAGE */
609
610   c->w = xgwa->width;
611   c->h = xgwa->height;
612   c->w_div_g = (c->w + c->grid_size - 1) / c->grid_size;
613   c->h_div_g = (c->h + c->grid_size - 1) / c->grid_size;
614
615 #ifdef USE_XIMAGE
616   w = c->w_div_g * c->grid_size;
617   h = c->h_div_g * c->grid_size;
618
619   /* The width of a scan line, in *bits*. */
620   wbits = (w * c->bits_per_pixel + align) & ~align;
621
622   /* This uses a lot more RAM than the single line approach. Users without
623    * enough RAM to fit even a single framebuffer should consider an upgrade for
624    * their 386. - D.O.
625    */
626
627   c->ximage = create_xshm_image(dpy, xgwa->visual, xgwa->depth,
628                                 ZPixmap, &c->shm_info,
629                                 wbits / c->bits_per_pixel, h);
630
631   c->shm_can_draw = True;
632
633   check_no_mem(dpy, c, c->ximage);
634 #endif /* USE_XIMAGE */
635
636   {
637     static const struct threadpool_class cls =
638     {
639       sizeof(struct inter_thread),
640       inter_thread_create,
641       inter_thread_destroy
642     };
643
644     int error = threadpool_create(
645       &c->threadpool,
646       &cls,
647       dpy,
648 #ifdef USE_XIMAGE
649       hardware_concurrency(dpy)
650 #else
651       1
652       /* At least two issues with threads without USE_XIMAGE:
653        * 1. Most of Xlib isn't thread safe without XInitThreads.
654        * 2. X(Un)LockDisplay would need to be called for each line, which is
655        *    terrible.
656        */
657 #endif
658       );
659
660     if(error) {
661       c->threadpool.count = 0; /* See the note in thread_util.h. */
662       inter_free(dpy, c);
663       abort_on_error(error);
664     }
665   }
666 }
667
668 static void create_pix_buf(Display* dpy, Window win, struct inter_context *c,
669                            const XWindowAttributes* xgwa)
670 {
671 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
672   if(c->back_buf)
673     return;
674 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
675   c->pix_buf = XCreatePixmap(dpy, win, xgwa->width, xgwa->height, xgwa->depth);
676 }
677
678 static double float_time(void)
679 {
680   struct timeval result;
681   gettimeofday(
682     &result
683 #ifdef GETTIMEOFDAY_TWO_ARGS
684     , NULL
685 #endif
686     );
687
688   return result.tv_usec * 1.0e-6 + result.tv_sec;
689 }
690
691 static void inter_init(Display* dpy, Window win, struct inter_context* c) 
692 {
693   XWindowAttributes xgwa;
694   double H[3], S[3], V[3];
695   int i;
696   int mono;
697   int gray;
698   int radius;
699   XGCValues val;
700   Bool dbuf = get_boolean_resource (dpy, "doubleBuffer", "Boolean");
701
702 #ifndef USE_XIMAGE
703   unsigned long valmask = 0;
704 #endif
705
706 # ifdef HAVE_JWXYZ      /* Don't second-guess Quartz's double-buffering */
707   dbuf = False;
708 # endif
709
710   memset (c, 0, sizeof(*c));
711
712   c->dpy = dpy;
713   c->win = win;
714
715
716   c->delay = get_integer_resource(dpy, "delay", "Integer");
717
718   XGetWindowAttributes(c->dpy, c->win, &xgwa);
719   c->cmap = xgwa.colormap;
720   c->screen = xgwa.screen;
721   c->bits_per_pixel = visual_pixmap_depth(xgwa.screen, xgwa.visual);
722   check_no_mem(dpy, c, (void *)(ptrdiff_t)c->bits_per_pixel);
723
724   val.function = GXcopy;
725   c->copy_gc = XCreateGC(c->dpy, TARGET(c), GCFunction, &val);
726
727   c->count = get_integer_resource(dpy, "count", "Integer");
728   if(c->count < 1)
729     c->count = 1;
730   c->grid_size = get_integer_resource(dpy, "gridsize", "Integer");
731   if(c->grid_size < 1)
732     c->grid_size = 1;
733   mono = get_boolean_resource(dpy, "mono", "Boolean");
734   if(!mono) {
735     c->colors = get_integer_resource(dpy, "ncolors", "Integer");
736     if(c->colors < 2)
737       c->colors = 2;
738   }
739   c->hue = get_integer_resource(dpy, "hue", "Float");
740   while (c->hue < 0 || c->hue >= 360)
741     c->hue = frand(360.0);
742   c->speed = get_integer_resource(dpy, "speed", "Integer");
743   c->shift = get_float_resource(dpy, "color-shift", "Float");
744   while(c->shift >= 360.0)
745     c->shift -= 360.0;
746   while(c->shift <= -360.0)
747     c->shift += 360.0;
748   radius = get_integer_resource(dpy, "radius", "Integer");;
749   if(radius < 1)
750     radius = 1;
751
752   create_image(dpy, c, &xgwa);
753
754   if(!mono) {
755     c->pal = calloc(c->colors, sizeof(XColor));
756     check_no_mem(dpy, c, c->pal);
757
758     gray = get_boolean_resource(dpy, "gray", "Boolean");
759     if(!gray) {
760       H[0] = c->hue;
761       H[1] = H[0] + c->shift < 360.0 ? H[0]+c->shift : H[0] + c->shift-360.0; 
762       H[2] = H[1] + c->shift < 360.0 ? H[1]+c->shift : H[1] + c->shift-360.0; 
763       S[0] = S[1] = S[2] = 1.0;
764       V[0] = V[1] = V[2] = 1.0;
765     } else {
766       H[0] = H[1] = H[2] = 0.0;
767       S[0] = S[1] = S[2] = 0.0;
768       V[0] = 1.0; V[1] = 0.5; V[2] = 0.0;
769     }
770
771     make_color_loop(c->screen, xgwa.visual, c->cmap, 
772                     H[0], S[0], V[0], 
773                     H[1], S[1], V[1], 
774                     H[2], S[2], V[2], 
775                     c->pal, &(c->colors), 
776                     True, False);
777     if(c->colors < 2) { /* color allocation failure */
778       mono = 1;
779       free(c->pal);
780     }
781   }
782
783   if(mono) { /* DON'T else this with the previous if! */
784     c->colors = 2;
785     c->pal = calloc(2, sizeof(XColor));
786     check_no_mem(dpy, c, c->pal);
787     c->pal[0].pixel = BlackPixel(c->dpy, DefaultScreen(c->dpy));
788     c->pal[1].pixel = WhitePixel(c->dpy, DefaultScreen(c->dpy));
789   }
790
791 #ifdef USE_XIMAGE
792   dbuf = False;
793   /* Double-buffering doesn't work with MIT-SHM: XShmPutImage must draw to the
794    * window. Otherwise, XShmCompletion events will have the XAnyEvent::window
795    * field set to the back buffer, and XScreenSaver will ignore the event.
796    */
797 #endif
798
799   if (dbuf)
800     {
801 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
802       c->back_buf = xdbe_get_backbuffer (c->dpy, c->win, XdbeUndefined);
803 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
804
805       create_pix_buf(dpy, win, c, &xgwa);
806     }
807
808 #ifndef USE_XIMAGE
809   valmask = GCForeground;
810   c->gcs = calloc(c->colors, sizeof(GC));
811   check_no_mem(dpy, c, c->gcs);
812   for(i = 0; i < c->colors; i++) {
813     val.foreground = c->pal[i].pixel;    
814     c->gcs[i] = XCreateGC(c->dpy, TARGET(c), valmask, &val);
815   }
816 #endif
817
818 #if defined USE_FAST_SQRT_HACKISH
819   c->radius = fast_log2(radius * radius);
820 #elif defined USE_FAST_SQRT_BIGTABLE2
821   c->radius = radius * radius;
822   c->radius = FAST_TABLE(c->radius);
823 #else
824   c->radius = radius;
825 #endif
826
827   if (c->radius < 1) c->radius = 1;
828   c->wave_height = calloc(c->radius, sizeof(unsigned));
829   check_no_mem(dpy, c, c->wave_height);
830
831   for(i = 0; i < c->radius; i++) {
832     float max, fi;
833 #if defined USE_FAST_SQRT_HACKISH
834     fi = sqrt(fast_inv_log2(i));
835 #elif defined USE_FAST_SQRT_BIGTABLE2
836     fi = sqrt(fast_inv_table(i));
837 #else
838     fi = i;
839 #endif
840     max = 
841       ((float)c->colors) * 
842       ((float)radius - fi) /
843       ((float)radius);
844     c->wave_height[i] = 
845       (unsigned)
846       ((max + max*cos(fi/50.0)) / 2.0);
847   }
848
849   c->source = calloc(c->count, sizeof(struct inter_source));
850   check_no_mem(dpy, c, c->source);
851
852   for(i = 0; i < c->count; i++) {
853     c->source[i].x_theta = frand(2.0)*3.14159;
854     c->source[i].y_theta = frand(2.0)*3.14159;
855   }
856
857   c->last_frame = float_time();
858 }
859
860 #define source_x(c, i) \
861   (c->w/2 + ((int)(cos(c->source[i].x_theta)*((float)c->w/2.0))))
862 #define source_y(c, i) \
863   (c->h/2 + ((int)(cos(c->source[i].y_theta)*((float)c->h/2.0))))
864
865 /*
866  * This is somewhat suboptimal. Calculating the distance per-pixel is going to
867  * be a lot slower than using now-ubiquitous SIMD CPU instructions to do four
868  * or eight pixels at a time.
869  */
870
871 static unsigned long do_inter(struct inter_context* c)
872 {
873   int i;
874
875   double now;
876   float elapsed;
877
878 #ifdef USE_XIMAGE
879   /* Wait a little while for the XServer to become ready if necessary. */
880   if(!c->shm_can_draw)
881     return 2000;
882 #endif
883
884   now = float_time();
885   elapsed = (now - c->last_frame) * 10.0;
886
887   c->last_frame = now;
888
889   for(i = 0; i < c->count; i++) {
890     c->source[i].x_theta += (elapsed*c->speed/1000.0);
891     if(c->source[i].x_theta > 2.0*3.14159)
892       c->source[i].x_theta -= 2.0*3.14159;
893     c->source[i].y_theta += (elapsed*c->speed/1000.0);
894     if(c->source[i].y_theta > 2.0*3.14159)
895       c->source[i].y_theta -= 2.0*3.14159;
896     c->source[i].x = source_x(c, i);
897     c->source[i].y = source_y(c, i);
898   }
899
900   threadpool_run(&c->threadpool, inter_thread_run);
901   threadpool_wait(&c->threadpool);
902
903 #ifdef USE_XIMAGE
904   put_xshm_image(c->dpy, c->win, c->copy_gc, c->ximage, 0, 0, 0, 0,
905                  c->ximage->width, c->ximage->height, &c->shm_info);
906   /* c->shm_can_draw = False; */
907 #endif
908
909 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
910   if (c->back_buf)
911     {
912       XdbeSwapInfo info[1];
913       info[0].swap_window = c->win;
914       info[0].swap_action = XdbeUndefined;
915       XdbeSwapBuffers(c->dpy, info, 1);
916     }
917   else
918 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
919     if (c->pix_buf)
920       {
921         XCopyArea (c->dpy, c->pix_buf, c->win, c->copy_gc,
922                    0, 0, c->w, c->h, 0, 0);
923       }
924
925   return c->delay;
926 }
927
928 static void *
929 interference_init (Display *dpy, Window win)
930 {
931   struct inter_context *c = (struct inter_context *) calloc (1, sizeof(*c));
932   if(!c)
933     abort_no_mem();
934   inter_init(dpy, win, c);
935   return c;
936 }
937
938 static unsigned long
939 interference_draw (Display *dpy, Window win, void *closure)
940 {
941   struct inter_context *c = (struct inter_context *) closure;
942   return do_inter(c);
943 }
944
945 static void
946 interference_reshape (Display *dpy, Window window, void *closure, 
947                  unsigned int w, unsigned int h)
948 {
949   struct inter_context *c = (struct inter_context *) closure;
950   XWindowAttributes xgwa;
951   Bool dbuf = (!!c->pix_buf
952 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
953                || c->back_buf
954 # endif
955                );
956
957 #ifdef USE_XIMAGE
958   destroy_image(dpy, c);
959   c->ximage = 0;
960 #endif
961
962   if(c->pix_buf)
963     XFreePixmap(dpy, c->pix_buf);
964   c->pix_buf = None;
965
966   XGetWindowAttributes(dpy, window, &xgwa);
967   xgwa.width = w;
968   xgwa.height = h;
969   create_image(dpy, c, &xgwa);
970   if(dbuf)
971     create_pix_buf(dpy, window, c, &xgwa);
972 }
973
974 static Bool
975 interference_event (Display *dpy, Window window, void *closure, XEvent *event)
976 {
977 #if HAVE_XSHM_EXTENSION
978   struct inter_context *c = (struct inter_context *) closure;
979
980   if(event->type == XShmGetEventBase(dpy) + ShmCompletion)
981   {
982     c->shm_can_draw = True;
983     return True;
984   }
985 #endif
986   return False;
987 }
988
989 static void
990 interference_free (Display *dpy, Window window, void *closure)
991 {
992   struct inter_context *c = (struct inter_context *) closure;
993   inter_free(dpy, c);
994 }
995
996 XSCREENSAVER_MODULE ("Interference", interference)
997