From http://www.jwz.org/xscreensaver/xscreensaver-5.27.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  */
40
41 #include <math.h>
42 #include <errno.h>
43
44 #include "screenhack.h"
45
46 #include "thread_util.h"
47
48 #ifdef HAVE_STDINT_H
49 # include <stdint.h>
50 #else
51
52 typedef unsigned int uint32_t;
53 typedef unsigned short uint16_t;
54 typedef unsigned char uint8_t;
55
56 #endif
57
58 /*
59 Tested on an Intel(R) Pentium(R) 4 CPU 3.00GHz (family 15, model 6, 2 cores),
60 1 GB PC2-4200, nouveau - Gallium 0.4 on NV44, X.Org version: 1.13.3. A very
61 modest system by current standards.
62
63 Does double-buffering make sense? (gridsize = 2)
64 USE_XIMAGE is off: Yes (-db: 4.1 FPS, -no-db: 2.9 FPS)
65 XPutImage in strips: No (-db: 35.9 FPS, -no-db: 38.7 FPS)
66 XPutImage, whole image: No (-db: 32.3 FPS, -no-db: 33.7 FPS)
67 MIT-SHM, whole image: Doesn't work anyway: (-no-db: 37.3 FPS)
68
69 If gridsize = 1, XPutImage is slow when the XImage is one line at a time.
70 XPutImage in strips: -db: 21.2 FPS, -no-db: 19.7 FPS
71 XPutimage, whole image: -db: 23.2 FPS, -no-db: 23.4 FPS
72 MIT-SHM: 26.0 FPS
73
74 So XPutImage in strips is very slightly faster when gridsize >= 2, but
75 quite a bit worse when gridsize = 1.
76 */
77
78 /* I thought it would be faster this way, but it turns out not to be... -jwz */
79 /* It's a lot faster for me, though - D.O. */
80 #define USE_XIMAGE
81
82 /* i.e. make the XImage the size of the screen. This is much faster when
83  * gridsize = 1. (And SHM is turned off.) */
84 #define USE_BIG_XIMAGE
85
86 /* Numbers are wave_table size, measured in # of unsigned integers.
87  * FPS/radius = 50/radius = 800/radius = 1500/Big-O memory usage
88  *
89  * Use at most one of the following:
90  * Both off = regular sqrt() - 13.5 FPS, 50/800/1500. */
91
92 /* #define USE_FAST_SQRT_HACKISH */ /* 17.8 FPS/2873/4921/5395/O(lg(radius)) */
93 #define USE_FAST_SQRT_BIGTABLE2 /* 26.1 FPS/156/2242/5386/O(radius^2) */
94
95 #ifndef USE_XIMAGE
96 # undef HAVE_XSHM_EXTENSION  /* only applicable when using XImages */
97 #endif /* USE_XIMAGE */
98
99 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
100 # include "xdbe.h"
101 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
102
103 #ifdef HAVE_XSHM_EXTENSION
104 # include "xshm.h"
105 #endif /* HAVE_XSHM_EXTENSION */
106
107 static const char *interference_defaults [] = {
108   ".background:  black",
109   ".foreground:  white",
110   "*count:       3",     /* number of waves */
111   "*gridsize:    4",     /* pixel size, smaller values for better resolution */
112   "*ncolors:     128",   /* number of colours used */
113   "*hue:         0",     /* hue to use for base color (0-360) */
114   "*speed:       30",    /* speed of wave origins moving around */
115   "*delay:       30000", /* or something */
116   "*color-shift: 60",    /* h in hsv space, smaller values for smaller
117                           * color gradients */
118   "*radius:      800",   /* wave extent */
119   "*gray:        false", /* color or grayscale */
120   "*mono:        false", /* monochrome, not very much fun */
121
122   "*doubleBuffer: False", /* doubleBuffer slows things down for me - D.O. */
123 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
124   "*useDBE:      True", /* use double buffering extension */
125 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
126
127 #ifdef HAVE_XSHM_EXTENSION
128   "*useSHM:      True", /* use shared memory extension */
129 #endif /*  HAVE_XSHM_EXTENSION */
130 #ifdef USE_IPHONE
131   "*ignoreRotation: True",
132 #endif
133   THREAD_DEFAULTS
134   0
135 };
136
137 static XrmOptionDescRec interference_options [] = {
138   { "-count",       ".count",       XrmoptionSepArg, 0 }, 
139   { "-ncolors",     ".ncolors",     XrmoptionSepArg, 0 }, 
140   { "-gridsize",    ".gridsize",    XrmoptionSepArg, 0 }, 
141   { "-hue",         ".hue",         XrmoptionSepArg, 0 },
142   { "-speed",       ".speed",       XrmoptionSepArg, 0 },
143   { "-delay",       ".delay",       XrmoptionSepArg, 0 },
144   { "-color-shift", ".color-shift", XrmoptionSepArg, 0 },
145   { "-radius",      ".radius",      XrmoptionSepArg, 0 },
146   { "-gray",        ".gray",        XrmoptionNoArg,  "True" },
147   { "-mono",        ".mono",        XrmoptionNoArg,  "True" },
148   { "-db",          ".doubleBuffer", XrmoptionNoArg,  "True" },
149   { "-no-db",       ".doubleBuffer", XrmoptionNoArg,  "False" },
150 #ifdef HAVE_XSHM_EXTENSION
151   { "-shm",     ".useSHM",      XrmoptionNoArg, "True" },
152   { "-no-shm",  ".useSHM",      XrmoptionNoArg, "False" },
153 #endif /*  HAVE_XSHM_EXTENSION */
154   THREAD_OPTIONS
155   { 0, 0, 0, 0 }
156 };
157
158 struct inter_source {
159   int x;
160   int y;
161   double x_theta;
162   double y_theta;
163 };
164
165 struct inter_context {
166   /*
167    * Display-related entries
168    */
169   Display* dpy;
170   Window   win;
171
172 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
173   XdbeBackBuffer back_buf;
174 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
175   Pixmap   pix_buf;
176
177   GC       copy_gc;
178 #ifdef USE_XIMAGE
179   XImage  *ximage;
180 #endif /* USE_XIMAGE */
181
182 #ifdef HAVE_XSHM_EXTENSION
183   Bool use_shm, shm_can_draw;
184   XShmSegmentInfo shm_info;
185 #endif /* HAVE_XSHM_EXTENSION */
186
187   /*
188    * Resources
189    */
190   int count;
191   int grid_size;
192   int colors;
193   float hue;
194   int speed;
195   int delay;
196   int shift;
197
198   /*
199    * Drawing-related entries
200    */
201   int w;
202   int h;
203   Colormap cmap;
204   Screen *screen;
205   unsigned bits_per_pixel;
206   XColor* pal;
207 #ifndef USE_XIMAGE
208   GC* gcs;
209 #endif
210   int radius; /* Not always the same as the X resource. */
211   double last_frame;
212
213   struct threadpool threadpool;
214
215   /*
216    * lookup tables
217    */
218   unsigned* wave_height;
219     
220   /*
221    * Interference sources
222    */
223   struct inter_source* source;
224 };
225
226 struct inter_thread
227 {
228   const struct inter_context *context;
229   unsigned thread_id;
230
231 #ifdef USE_XIMAGE
232   uint32_t* row;
233 #endif
234
235   unsigned* result_row;
236 };
237
238 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
239 # define TARGET(c) ((c)->back_buf ? (c)->back_buf : \
240                     (c)->pix_buf ? (c)->pix_buf : (c)->win)
241 #else  /* HAVE_DOUBLE_BUFFER_EXTENSION */
242 # define TARGET(c) ((c)->pix_buf ? (c)->pix_buf : (c)->win)
243 #endif /* !HAVE_DOUBLE_BUFFER_EXTENSION */
244
245 #ifdef USE_FAST_SQRT_HACKISH
246 /* Based loosely on code from Wikipedia:
247  * https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Approximations_that_depend_on_IEEE_representation
248  */
249
250 /* FAST_SQRT_EXTRA_BITS = 3: Smallest useful value
251  * = 5/6: A little bit of banding, wave_height table is on par with regular
252  *        sqrt() code.
253  * = 7: No apparent difference with original @ radius = 800.
254  * = 8: One more just to be comfortable.
255  */
256
257 # define FAST_SQRT_EXTRA_BITS 8
258
259 union int_float
260 {
261   uint32_t i;
262   float f;
263 };
264
265 static unsigned fast_log2(unsigned x)
266 {
267   union int_float u;
268   if(!x)
269     return x;
270   u.f = x;
271   return ((u.i - 0x3f800000) >> (23 - FAST_SQRT_EXTRA_BITS)) + 1;
272 }
273
274 static float fast_inv_log2(unsigned x)
275 {
276   union int_float u;
277   if(!x)
278     return 0.0f;
279   u.i = ((x - 1) << (23 - FAST_SQRT_EXTRA_BITS)) + 0x3f800000;
280   return u.f;
281 }
282
283 #endif
284
285 #ifdef USE_FAST_SQRT_BIGTABLE2
286
287 /* I eyeballed these figures. They could be improved. - D.O. */
288
289 # define FAST_SQRT_DISCARD_BITS1 4
290 /* = 5: Dot in center is almost invisible at radius = 800. */
291 /* = 4: Dot in center looks OK at radius = 50. */
292
293 /* 156/2740/9029 */
294 /* # define FAST_SQRT_DISCARD_BITS2 8 */
295 /* # define FAST_SQRT_CUTOFF 64 * 64 */
296
297 /* 156/2242/5386 */
298 # define FAST_SQRT_DISCARD_BITS2 9
299 # define FAST_SQRT_CUTOFF 128 * 128
300
301 /*
302  * This is a little faster:
303  * 44.5 FPS, 19/5000/17578
304  *
305  * # define FAST_SQRT_DISCARD_BITS1 7
306  * # define FAST_SQRT_DISCARD_BITS2 7
307  * # define FAST_SQRT_CUTOFF 0
308  *
309  * For radius = 800, FAST_SQRT_DISCARD_BITS2 =
310  * = 9/10: Approximately the original table size, some banding near origins.
311  * = 7: wave_height is 20 KB, and just fits inside a 32K L1 cache.
312  * = 6: Nearly indistinguishable from original
313  */
314
315 /*
316   FAST_TABLE(x) is equivalent to, but slightly faster than:
317   x < FAST_SQRT_CUTOFF ?
318     (x >> FAST_SQRT_DISCARD_BITS1) :
319     ((x - FAST_SQRT_CUTOFF) >> FAST_SQRT_DISCARD_BITS2) +
320       (FAST_SQRT_CUTOFF >> FAST_SQRT_DISCARD_BITS1);
321 */
322
323 #define FAST_TABLE(x) \
324   ((x) < FAST_SQRT_CUTOFF ? \
325     ((x) >> FAST_SQRT_DISCARD_BITS1) : \
326     (((x) + \
327       ((FAST_SQRT_CUTOFF << (FAST_SQRT_DISCARD_BITS2 - \
328         FAST_SQRT_DISCARD_BITS1)) - FAST_SQRT_CUTOFF)) >> \
329       FAST_SQRT_DISCARD_BITS2))
330
331 static double fast_inv_table(unsigned x)
332 {
333   return x < (FAST_SQRT_CUTOFF >> FAST_SQRT_DISCARD_BITS1) ?
334     (x << FAST_SQRT_DISCARD_BITS1) :
335     ((x - (FAST_SQRT_CUTOFF >> FAST_SQRT_DISCARD_BITS1)) <<
336       FAST_SQRT_DISCARD_BITS2) + FAST_SQRT_CUTOFF;
337 }
338
339 #endif
340
341 static void destroy_image(Display* dpy, struct inter_context* c)
342 {
343 #ifdef USE_XIMAGE
344   if(c->ximage) {
345 # ifdef HAVE_XSHM_EXTENSION
346     if(c->use_shm) {
347       destroy_xshm_image(dpy, c->ximage, &c->shm_info);
348     } else
349 # endif
350     {
351       /* Don't let XDestroyImage free c->ximage->data. */
352       thread_free(c->ximage->data);
353       c->ximage->data = NULL;
354       XDestroyImage(c->ximage);
355     }
356   }
357 #endif
358
359   if(c->threadpool.count)
360   {
361     threadpool_destroy(&c->threadpool);
362     c->threadpool.count = 0;
363   }
364 }
365
366 static void inter_free(Display* dpy, struct inter_context* c)
367 {
368 #ifndef USE_XIMAGE
369   unsigned i;
370 #endif
371
372   if(c->pix_buf)
373     XFreePixmap(dpy, c->pix_buf);
374
375   if(c->copy_gc)
376     XFreeGC(dpy, c->copy_gc);
377
378   destroy_image(dpy, c);
379
380   if(c->colors <= 2)
381     free(c->pal);
382   else
383     free_colors(c->screen, c->cmap, c->pal, c->colors);
384
385 #ifndef USE_XIMAGE
386   for(i = 0; i != c->colors; ++i)
387     XFreeGC(dpy, c->gcs[i]);
388   free(c->gcs);
389 #endif
390
391   free(c->wave_height);
392   free(c->source);
393 }
394
395 static void abort_on_error(int error)
396 {
397   fprintf(stderr, "interference: %s\n", strerror(error));
398   exit(1);
399 }
400
401 static void abort_no_mem(void)
402 {
403   abort_on_error(ENOMEM);
404 }
405
406 static void check_no_mem(Display* dpy, struct inter_context* c, void* ptr)
407 {
408   if(!ptr) {
409     inter_free(dpy, c);
410     abort_no_mem();
411   }
412 }
413
414 static int inter_thread_create(
415   void* self_raw,
416   struct threadpool* pool,
417   unsigned id)
418 {
419   struct inter_thread* self = (struct inter_thread*)self_raw;
420   const struct inter_context* c = GET_PARENT_OBJ(struct inter_context, threadpool, pool);
421
422   self->context = c;
423   self->thread_id = id;
424
425   self->result_row = malloc((c->w / c->grid_size) * sizeof(unsigned));
426   if(!self->result_row)
427     return ENOMEM;
428
429 #ifdef USE_XIMAGE
430   self->row = malloc((c->w / c->grid_size) * sizeof(uint32_t));
431   if(!self->row) {
432     free(self->result_row);
433     return ENOMEM;
434   }
435 #endif
436
437   return 0;
438 }
439
440 static void inter_thread_destroy(void* self_raw)
441 {
442   struct inter_thread* self = (struct inter_thread*)self_raw;
443 #ifdef USE_XIMAGE
444   free(self->row);
445 #endif
446   free(self->result_row);
447 }
448
449 /*
450 A higher performance design would have input and output queues, so that when
451 worker threads finish with one frame, they can pull the next work order from
452 the queue and get started on it immediately, rather than going straight to
453 sleep. The current "single-buffered" design still provides reasonable
454 performance at low frame rates; high frame rates are noticeably less efficient.
455 */
456
457 static void inter_thread_run(void* self_raw)
458 {
459   struct inter_thread* self = (struct inter_thread*)self_raw;
460   const struct inter_context* c = self->context;
461
462   int i, j, k;
463   unsigned result;
464   int dist1;
465   int g = c->grid_size;
466   unsigned w_div_g = c->w/g;
467
468   int dx, dy, g2 = 2 * g * g;
469   int px, py, px2g;
470
471   int dist0, ddist;
472
473 #ifdef USE_XIMAGE
474   unsigned img_y = g * self->thread_id;
475   void *scanline = c->ximage->data + c->ximage->bytes_per_line * g * self->thread_id;
476 #endif
477
478   for(j = self->thread_id; j < c->h/g; j += c->threadpool.count) {
479     px = g/2;
480     py = j*g + px;
481
482     memset(self->result_row, 0, w_div_g * sizeof(unsigned));
483
484     for(k = 0; k < c->count; k++) {
485
486       dx = px - c->source[k].x;
487       dy = py - c->source[k].y;
488
489       dist0 = dx*dx + dy*dy;
490       ddist = -2 * g * c->source[k].x;
491
492       /* px2g = g*(px*2 + g); */
493       px2g = g2;
494
495       for(i = 0; i < w_div_g; i++) {
496         /*
497          * Discarded possibilities for improving performance here:
498          * 1. Using octagon-based distance estimation
499          *    (Which causes giant octagons to appear.)
500          * 2. Square root approximation by reinterpret-casting IEEE floats to
501          *    integers.
502          *    (Which causes angles to appear when two waves interfere.)
503          */
504
505 /*      int_float u;
506         u.f = dx*dx + dy*dy;
507         u.i = (1 << 29) + (u.i >> 1) - (1 << 22);
508         dist = u.f; */
509
510 #if defined USE_FAST_SQRT_BIGTABLE2
511         dist1 = FAST_TABLE(dist0);
512 #elif defined USE_FAST_SQRT_HACKISH
513         dist1 = fast_log2(dist0);
514 #else
515         dist1 = sqrt(dist0);
516 #endif
517
518         if(dist1 < c->radius)
519           self->result_row[i] += c->wave_height[dist1];
520
521         dist0 += px2g + ddist;
522         px2g += g2;
523       }
524     }
525
526     for(i = 0; i < w_div_g; i++) {
527
528       result = self->result_row[i];
529
530       /* It's slightly faster to do a subtraction or two before calculating the
531        * modulus. - D.O. */
532       if(result >= c->colors)
533       {
534         result -= c->colors;
535         if(result >= c->colors)
536           result %= (unsigned)c->colors;
537       }
538
539 #ifdef USE_XIMAGE
540       self->row[i] = c->pal[result].pixel;
541 #else
542       XFillRectangle(c->dpy, TARGET(c), c->gcs[result], g*i, g*j, g, g);
543 #endif /* USE_XIMAGE */
544     }
545
546 #ifdef USE_XIMAGE
547     /* Fill in these `gridsize' horizontal bits in the scanline */
548     if(c->ximage->bits_per_pixel == 32)
549     {
550       uint32_t *ptr = (uint32_t *)scanline;
551       for(i = 0; i < w_div_g; i++) {
552         for(k = 0; k < g; k++)
553           ptr[g*i+k] = self->row[i];
554       }
555     }
556     else if(c->ximage->bits_per_pixel == 24)
557     {
558       uint8_t *ptr = (uint8_t *)scanline;
559       for(i = 0; i < w_div_g; i++) {
560         for(k = 0; k < g; k++) {
561           uint32_t pixel = self->row[i];
562           /* Might not work on big-endian. */
563           ptr[0] = pixel;
564           ptr[1] = (pixel & 0x0000ff00) >> 8;
565           ptr[2] = (pixel & 0x00ff0000) >> 16;
566           ptr += 3;
567         }
568       }
569     }
570     else if(c->ximage->bits_per_pixel == 16)
571     {
572       uint16_t *ptr = (uint16_t *)scanline;
573       for(i = 0; i < w_div_g; i++) {
574         for(k = 0; k < g; k++)
575           ptr[g*i+k] = self->row[i];
576       }
577     }
578     else if(c->ximage->bits_per_pixel == 8)
579     {
580       uint8_t *ptr = (uint8_t *)scanline;
581       for(i = 0; i < w_div_g; i++) {
582         for(k = 0; k < g; k++)
583           ptr[g*i+k] = self->row[i];
584       }
585     }
586     else
587     {
588       for(i = 0; i < w_div_g; i++) {
589         for(k = 0; k < g; k++)
590           /* XPutPixel is thread safe as long as the XImage didn't have its
591            * bits_per_pixel changed. */
592           XPutPixel(c->ximage, (g*i)+k, img_y, self->row[i]);
593       }
594     }
595
596     /* Only the first scanline of the image has been filled in; clone that
597        scanline to the rest of the `gridsize' lines in the ximage */
598     for(k = 0; k < (g-1); k++)
599       memcpy(c->ximage->data + (c->ximage->bytes_per_line * (img_y + k + 1)),
600              c->ximage->data + (c->ximage->bytes_per_line * img_y),
601              c->ximage->bytes_per_line);
602
603 # ifndef USE_BIG_XIMAGE
604     /* Move the bits for this horizontal stripe to the server. */
605 #  ifdef HAVE_XSHM_EXTENSION
606     if (!c->use_shm)
607 #  endif /*  HAVE_XSHM_EXTENSION */
608       XPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage,
609                 0, 0, 0, g*j, c->ximage->width, c->ximage->height);
610 # endif
611
612 # if defined HAVE_XSHM_EXTENSION && !defined USE_BIG_XIMAGE
613     if (c->use_shm)
614 # endif
615     {
616 # if defined HAVE_XSHM_EXTENSION || defined USE_BIG_XIMAGE
617       scanline = (char *)scanline + c->ximage->bytes_per_line * g * c->threadpool.count;
618       img_y += g * c->threadpool.count;
619 # endif
620     }
621
622 #endif /* USE_XIMAGE */
623   }
624 }
625
626 /* On allocation error, c->ximage == NULL. */
627 static void create_image(
628   Display* dpy, 
629   struct inter_context* c, 
630   const XWindowAttributes* xgwa)
631 {
632 #ifdef USE_XIMAGE
633
634   /* Set the width so that each thread can work on a different line. */
635   unsigned align = thread_memory_alignment(dpy) * 8 - 1;
636   /* The width of a scan line, in *bits*. */
637   unsigned width = (xgwa->width * c->bits_per_pixel + align) & ~align;
638
639 # ifdef HAVE_XSHM_EXTENSION
640   /*
641    * Interference used to put one row at a time to the X server. This changes
642    * today.
643    *
644    * XShmPutImage is asynchronous; the contents of the XImage must not be
645    * modified until the server has placed the data on the screen. Waiting for
646    * an XShmCompletionEvent after every line of pixels is a little nutty, so
647    * shared-memory XImages will cover the entire screen, and it only has to be
648    * sent once per frame.
649    *
650    * The non-SHM code, on the other hand is noticeably slower when
651    * gridsize = 1 with one row at a time. If, on the other hand, gridsize >= 2,
652    * there's a slight speed increase with one row at a time.
653    *
654    * This uses a lot more RAM than the single line approach. Users with only
655    * 4 MB of RAM may wish to disable USE_BIG_XIMAGE and specify -no-shm on the
656    * command line. Since this is 2013 and desktop computers are shipping with
657    * 8 GB of RAM, I doubt that this will be a major issue. - D.O.
658    */
659
660   if (c->use_shm)
661     {
662       c->ximage = create_xshm_image(dpy, xgwa->visual, xgwa->depth,
663                                     ZPixmap, 0, &c->shm_info,
664                                     width / c->bits_per_pixel, xgwa->height);
665       if (!c->ximage)
666         c->use_shm = False;
667       /* If create_xshm_image fails, it will not be attempted again. */
668
669       c->shm_can_draw = True;
670     }
671 # endif /* HAVE_XSHM_EXTENSION */
672
673   if (!c->ximage)
674     {
675       c->ximage =
676         XCreateImage(dpy, xgwa->visual,
677                      xgwa->depth, ZPixmap, 0, 0, /* depth, fmt, offset, data */
678                      xgwa->width,                /* width */
679 # ifdef USE_BIG_XIMAGE
680                      xgwa->height,               /* height */
681 # else
682                      c->grid_size,               /* height */
683 # endif
684                      8, width / 8);              /* pad, bpl */
685
686       if(c->ximage)
687         {
688           if(thread_malloc((void **)&c->ximage->data, dpy,
689                            c->ximage->height * c->ximage->bytes_per_line))
690             {
691               XFree(c->ximage);
692               c->ximage = NULL;
693             }
694         }
695     }
696
697   check_no_mem(dpy, c, c->ximage);
698 #endif /* USE_XIMAGE */
699
700   {
701     static const struct threadpool_class cls =
702     {
703       sizeof(struct inter_thread),
704       inter_thread_create,
705       inter_thread_destroy
706     };
707
708     int error = threadpool_create(
709       &c->threadpool,
710       &cls,
711       dpy,
712 #if defined USE_XIMAGE && defined USE_BIG_XIMAGE
713       hardware_concurrency(dpy)
714 #else
715       1
716       /* At least three issues with threads without USE_BIG_XIMAGE:
717        * 1. Most of Xlib isn't thread safe without XInitThreads.
718        * 2. X(Un)LockDisplay would need to be called for each line, which is terrible.
719        * 3. There's only one XImage buffer at the moment.
720        */
721 #endif
722       );
723
724     if(error) {
725       c->threadpool.count = 0; /* See the note in thread_util.h. */
726       inter_free(dpy, c);
727       abort_on_error(error);
728     }
729   }
730 }
731
732 static void create_pix_buf(Display* dpy, Window win, struct inter_context *c,
733                            const XWindowAttributes* xgwa)
734 {
735 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
736   if(c->back_buf)
737     return;
738 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
739   c->pix_buf = XCreatePixmap(dpy, win, xgwa->width, xgwa->height, xgwa->depth);
740 }
741
742 static double float_time(void)
743 {
744   struct timeval result;
745   gettimeofday(
746     &result
747 #ifdef GETTIMEOFDAY_TWO_ARGS
748     , NULL
749 #endif
750     );
751
752   return result.tv_usec * 1.0e-6 + result.tv_sec;
753 }
754
755 static void inter_init(Display* dpy, Window win, struct inter_context* c) 
756 {
757   XWindowAttributes xgwa;
758   double H[3], S[3], V[3];
759   int i;
760   int mono;
761   int gray;
762   int radius;
763   XGCValues val;
764   Bool dbuf = get_boolean_resource (dpy, "doubleBuffer", "Boolean");
765
766 #ifndef USE_XIMAGE
767   unsigned long valmask = 0;
768 #endif
769
770 # ifdef HAVE_COCOA      /* Don't second-guess Quartz's double-buffering */
771   dbuf = False;
772 # endif
773
774   memset (c, 0, sizeof(*c));
775
776   c->dpy = dpy;
777   c->win = win;
778
779
780   c->delay = get_integer_resource(dpy, "delay", "Integer");
781
782   XGetWindowAttributes(c->dpy, c->win, &xgwa);
783   c->w = xgwa.width;
784   c->h = xgwa.height;
785   c->cmap = xgwa.colormap;
786   c->screen = xgwa.screen;
787   c->bits_per_pixel = get_bits_per_pixel(c->dpy, xgwa.depth);
788   check_no_mem(dpy, c, (void *)(ptrdiff_t)c->bits_per_pixel);
789
790 #ifdef HAVE_XSHM_EXTENSION
791   c->use_shm = get_boolean_resource(dpy, "useSHM", "Boolean");
792 #endif /*  HAVE_XSHM_EXTENSION */
793
794   val.function = GXcopy;
795   c->copy_gc = XCreateGC(c->dpy, TARGET(c), GCFunction, &val);
796
797   c->count = get_integer_resource(dpy, "count", "Integer");
798   if(c->count < 1)
799     c->count = 1;
800   c->grid_size = get_integer_resource(dpy, "gridsize", "Integer");
801   if(c->grid_size < 1)
802     c->grid_size = 1;
803   mono = get_boolean_resource(dpy, "mono", "Boolean");
804   if(!mono) {
805     c->colors = get_integer_resource(dpy, "ncolors", "Integer");
806     if(c->colors < 2)
807       c->colors = 2;
808   }
809   c->hue = get_integer_resource(dpy, "hue", "Float");
810   while (c->hue < 0 || c->hue >= 360)
811     c->hue = frand(360.0);
812   c->speed = get_integer_resource(dpy, "speed", "Integer");
813   c->shift = get_float_resource(dpy, "color-shift", "Float");
814   while(c->shift >= 360.0)
815     c->shift -= 360.0;
816   while(c->shift <= -360.0)
817     c->shift += 360.0;
818   radius = get_integer_resource(dpy, "radius", "Integer");;
819   if(radius < 1)
820     radius = 1;
821
822   create_image(dpy, c, &xgwa);
823
824   if(!mono) {
825     c->pal = calloc(c->colors, sizeof(XColor));
826     check_no_mem(dpy, c, c->pal);
827
828     gray = get_boolean_resource(dpy, "gray", "Boolean");
829     if(!gray) {
830       H[0] = c->hue;
831       H[1] = H[0] + c->shift < 360.0 ? H[0]+c->shift : H[0] + c->shift-360.0; 
832       H[2] = H[1] + c->shift < 360.0 ? H[1]+c->shift : H[1] + c->shift-360.0; 
833       S[0] = S[1] = S[2] = 1.0;
834       V[0] = V[1] = V[2] = 1.0;
835     } else {
836       H[0] = H[1] = H[2] = 0.0;
837       S[0] = S[1] = S[2] = 0.0;
838       V[0] = 1.0; V[1] = 0.5; V[2] = 0.0;
839     }
840
841     make_color_loop(c->screen, xgwa.visual, c->cmap, 
842                     H[0], S[0], V[0], 
843                     H[1], S[1], V[1], 
844                     H[2], S[2], V[2], 
845                     c->pal, &(c->colors), 
846                     True, False);
847     if(c->colors < 2) { /* color allocation failure */
848       mono = 1;
849       free(c->pal);
850     }
851   }
852
853   if(mono) { /* DON'T else this with the previous if! */
854     c->colors = 2;
855     c->pal = calloc(2, sizeof(XColor));
856     check_no_mem(dpy, c, c->pal);
857     c->pal[0].pixel = BlackPixel(c->dpy, DefaultScreen(c->dpy));
858     c->pal[1].pixel = WhitePixel(c->dpy, DefaultScreen(c->dpy));
859   }
860
861 #ifdef HAVE_XSHM_EXTENSION
862   if(c->use_shm)
863     dbuf = False;
864   /* Double-buffering doesn't work with MIT-SHM: XShmPutImage must draw to the
865    * window. Otherwise, XShmCompletion events will have the XAnyEvent::window
866    * field set to the back buffer, and XScreenSaver will ignore the event. */
867 #endif
868
869   if (dbuf)
870     {
871 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
872       c->back_buf = xdbe_get_backbuffer (c->dpy, c->win, XdbeUndefined);
873 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
874
875       create_pix_buf(dpy, win, c, &xgwa);
876     }
877
878 #ifndef USE_XIMAGE
879   valmask = GCForeground;
880   c->gcs = calloc(c->colors, sizeof(GC));
881   check_no_mem(dpy, c, c->gcs);
882   for(i = 0; i < c->colors; i++) {
883     val.foreground = c->pal[i].pixel;    
884     c->gcs[i] = XCreateGC(c->dpy, TARGET(c), valmask, &val);
885   }
886 #endif
887
888 #if defined USE_FAST_SQRT_HACKISH
889   c->radius = fast_log2(radius * radius);
890 #elif defined USE_FAST_SQRT_BIGTABLE2
891   c->radius = radius * radius;
892   c->radius = FAST_TABLE(c->radius);
893 #else
894   c->radius = radius;
895 #endif
896
897   c->wave_height = calloc(c->radius, sizeof(unsigned));
898   check_no_mem(dpy, c, c->wave_height);
899
900   for(i = 0; i < c->radius; i++) {
901     float max, fi;
902 #if defined USE_FAST_SQRT_HACKISH
903     fi = sqrt(fast_inv_log2(i));
904 #elif defined USE_FAST_SQRT_BIGTABLE2
905     fi = sqrt(fast_inv_table(i));
906 #else
907     fi = i;
908 #endif
909     max = 
910       ((float)c->colors) * 
911       ((float)radius - fi) /
912       ((float)radius);
913     c->wave_height[i] = 
914       (unsigned)
915       ((max + max*cos(fi/50.0)) / 2.0);
916   }
917
918   c->source = calloc(c->count, sizeof(struct inter_source));
919   check_no_mem(dpy, c, c->source);
920
921   for(i = 0; i < c->count; i++) {
922     c->source[i].x_theta = frand(2.0)*3.14159;
923     c->source[i].y_theta = frand(2.0)*3.14159;
924   }
925
926   c->last_frame = float_time();
927 }
928
929 #define source_x(c, i) \
930   (c->w/2 + ((int)(cos(c->source[i].x_theta)*((float)c->w/2.0))))
931 #define source_y(c, i) \
932   (c->h/2 + ((int)(cos(c->source[i].y_theta)*((float)c->h/2.0))))
933
934 /*
935  * This is somewhat suboptimal. Calculating the distance per-pixel is going to
936  * be a lot slower than using now-ubiquitous SIMD CPU instructions to do four
937  * or eight pixels at a time.
938  */
939
940 #ifdef TEST_PATTERN
941 static uint32_t
942 _alloc_color(struct inter_context *c, uint16_t r, uint16_t g, uint16_t b)
943 {
944   XColor color;
945   color.red = r;
946   color.green = g;
947   color.blue = b;
948   XAllocColor(c->dpy, c->cmap, &color);
949   return color.pixel;
950 }
951
952 static void _copy_test(Display *dpy, Drawable src, Drawable dst, GC gc, int x, int y, uint32_t cells)
953 {
954   XCopyArea(dpy, src, dst, gc, 0, 0, 3, 2, x, y);
955
956   {
957     XImage *image = XGetImage(dpy, src, 0, 0, 3, 2, cells, ZPixmap);
958     XPutImage(dpy, dst, gc, image, 0, 0, x, y + 2, 3, 2);
959     XDestroyImage(image);
960   }
961 }
962
963 static void _test_pattern(Display *dpy, Drawable d, GC gc, const uint32_t *rgb)
964 {
965   unsigned x;
966   for(x = 0; x != 3; ++x)
967   {
968     XSetForeground(dpy, gc, rgb[x]);
969     XDrawPoint(dpy, d, gc, x, 0);
970     XSetForeground(dpy, gc, rgb[2 - x]);
971     XFillRectangle(dpy, d, gc, x, 1, 1, 1);
972   }
973
974   _copy_test(dpy, d, d, gc, 0, 2, rgb[0] | rgb[1] | rgb[2]);
975 }
976 #endif /* TEST_PATTERN */
977
978 static unsigned long do_inter(struct inter_context* c)
979 {
980   int i;
981
982   double now;
983   float elapsed;
984
985 #if defined USE_XIMAGE && defined HAVE_XSHM_EXTENSION
986   /* Wait a little while for the XServer to become ready if necessary. */
987   if(c->use_shm && !c->shm_can_draw)
988     return 2000;
989 #endif
990
991   now = float_time();
992   elapsed = (now - c->last_frame) * 10.0;
993
994   c->last_frame = now;
995
996   for(i = 0; i < c->count; i++) {
997     c->source[i].x_theta += (elapsed*c->speed/1000.0);
998     if(c->source[i].x_theta > 2.0*3.14159)
999       c->source[i].x_theta -= 2.0*3.14159;
1000     c->source[i].y_theta += (elapsed*c->speed/1000.0);
1001     if(c->source[i].y_theta > 2.0*3.14159)
1002       c->source[i].y_theta -= 2.0*3.14159;
1003     c->source[i].x = source_x(c, i);
1004     c->source[i].y = source_y(c, i);
1005   }
1006
1007   threadpool_run(&c->threadpool, inter_thread_run);
1008   threadpool_wait(&c->threadpool);
1009
1010 #ifdef HAVE_XSHM_EXTENSION
1011   if (c->use_shm)
1012   {
1013     XShmPutImage(c->dpy, c->win, c->copy_gc, c->ximage,
1014                  0, 0, 0, 0, c->ximage->width, c->ximage->height,
1015                  True);
1016     c->shm_can_draw = False;
1017   }
1018 #endif
1019 #if defined HAVE_XSHM_EXTENSION && defined USE_BIG_XIMAGE
1020   else
1021 #endif
1022 #ifdef USE_BIG_XIMAGE
1023   {
1024     XPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage,
1025               0, 0, 0, 0, c->ximage->width, c->ximage->height);
1026   }
1027 #endif
1028
1029 #ifdef TEST_PATTERN
1030   {
1031 /*  XWindowAttributes xgwa;
1032     XGetWindowAttributes(c->dpy, c->win, &xgwa); */
1033
1034     /* if(xgwa.width >= 9 && xgwa.height >= 10) */
1035     {
1036       Screen *screen = ScreenOfDisplay(c->dpy, DefaultScreen(c->dpy));
1037       Visual *visual = DefaultVisualOfScreen(screen);
1038       Pixmap pixmap = XCreatePixmap(c->dpy, TARGET(c), 3, 10, visual_depth(screen, visual));
1039
1040       {
1041         XSetForeground(c->dpy, c->copy_gc, _alloc_color(c, 0xffff, 0x7fff, 0x7fff));
1042         XDrawPoint(c->dpy, TARGET(c), c->copy_gc, 0, c->h - 1);
1043       }
1044
1045       uint32_t rgb[3], cells;
1046       rgb[0] = _alloc_color(c, 0xffff, 0, 0);
1047       rgb[1] = _alloc_color(c, 0, 0xffff, 0);
1048       rgb[2] = _alloc_color(c, 0, 0, 0xffff);
1049       cells = rgb[0] | rgb[1] | rgb[2];
1050
1051       _test_pattern(c->dpy, TARGET(c), c->copy_gc, rgb);
1052       _test_pattern(c->dpy, pixmap, c->copy_gc, rgb);
1053       /* Here's a good spot to verify that the pixmap contains the right colors
1054        * at the top. */
1055       _copy_test(c->dpy, TARGET(c), pixmap, c->copy_gc, 0, 6, cells);
1056
1057       XCopyArea(c->dpy, pixmap, TARGET(c), c->copy_gc, 0, 0, 3, 10, 3, 0);
1058       {
1059         XImage *image = XGetImage(c->dpy, pixmap, 0, 0, 3, 10, cells, ZPixmap);
1060         XPutImage(c->dpy, TARGET(c), c->copy_gc, image, 0, 0, 6, 0, 3, 10);
1061         XDestroyImage(image);
1062       }
1063
1064       XFreePixmap(c->dpy, pixmap);
1065       XSync(c->dpy, False);
1066     }
1067   }
1068 #endif /* TEST_PATTERN */
1069
1070 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1071   if (c->back_buf)
1072     {
1073       XdbeSwapInfo info[1];
1074       info[0].swap_window = c->win;
1075       info[0].swap_action = XdbeUndefined;
1076       XdbeSwapBuffers(c->dpy, info, 1);
1077     }
1078   else
1079 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
1080     if (c->pix_buf)
1081       {
1082         XCopyArea (c->dpy, c->pix_buf, c->win, c->copy_gc,
1083                    0, 0, c->w, c->h, 0, 0);
1084       }
1085
1086   return c->delay;
1087 }
1088
1089 static void *
1090 interference_init (Display *dpy, Window win)
1091 {
1092   struct inter_context *c = (struct inter_context *) calloc (1, sizeof(*c));
1093   if(!c)
1094     abort_no_mem();
1095   inter_init(dpy, win, c);
1096   return c;
1097 }
1098
1099 static unsigned long
1100 interference_draw (Display *dpy, Window win, void *closure)
1101 {
1102   struct inter_context *c = (struct inter_context *) closure;
1103   return do_inter(c);
1104 }
1105
1106 static void
1107 interference_reshape (Display *dpy, Window window, void *closure, 
1108                  unsigned int w, unsigned int h)
1109 {
1110   struct inter_context *c = (struct inter_context *) closure;
1111   XWindowAttributes xgwa;
1112   Bool dbuf = (c->pix_buf
1113 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1114                || c->back_buf
1115 # endif
1116                );
1117
1118   c->w = w;
1119   c->h = h;
1120
1121 #ifdef USE_XIMAGE
1122   destroy_image(dpy, c);
1123   c->ximage = 0;
1124 #endif
1125
1126   if(c->pix_buf)
1127     XFreePixmap(dpy, c->pix_buf);
1128   c->pix_buf = None;
1129
1130   XGetWindowAttributes(dpy, window, &xgwa);
1131   xgwa.width = w;
1132   xgwa.height = h;
1133   create_image(dpy, c, &xgwa);
1134   if(dbuf)
1135     create_pix_buf(dpy, window, c, &xgwa);
1136 }
1137
1138 static Bool
1139 interference_event (Display *dpy, Window window, void *closure, XEvent *event)
1140 {
1141 #if HAVE_XSHM_EXTENSION
1142   struct inter_context *c = (struct inter_context *) closure;
1143
1144   if(c->use_shm && event->type == XShmGetEventBase(dpy) + ShmCompletion)
1145   {
1146     c->shm_can_draw = True;
1147     return True;
1148   }
1149 #endif
1150   return False;
1151 }
1152
1153 static void
1154 interference_free (Display *dpy, Window window, void *closure)
1155 {
1156   struct inter_context *c = (struct inter_context *) closure;
1157   inter_free(dpy, c);
1158 }
1159
1160 XSCREENSAVER_MODULE ("Interference", interference)
1161