1 /* interference.c --- colored fields via decaying sinusoidal waves.
2 * An entry for the RHAD Labs Screensaver Contest.
4 * Author: Hannu Mallat <hmallat@cs.hut.fi>
6 * Copyright (C) 1998 Hannu Mallat.
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
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.
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.
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.
47 #include "screenhack.h"
49 #include "thread_util.h"
51 #ifdef HAVE_INTTYPES_H
52 # include <inttypes.h>
55 typedef unsigned int uint32_t;
56 typedef unsigned short uint16_t;
57 typedef unsigned char uint8_t;
62 Tested on an Intel(R) Pentium(R) 4 CPU 3.00GHz (family 15, model 6, 2 cores),
63 1 GB PC2-4200, nouveau - Gallium 0.4 on NV44, X.Org version: 1.13.3. A very
64 modest system by current standards.
66 Does double-buffering make sense? (gridsize = 2)
67 USE_XIMAGE is off: Yes (-db: 4.1 FPS, -no-db: 2.9 FPS)
68 XPutImage in strips: No (-db: 35.9 FPS, -no-db: 38.7 FPS)
69 XPutImage, whole image: No (-db: 32.3 FPS, -no-db: 33.7 FPS)
70 MIT-SHM, whole image: Doesn't work anyway: (-no-db: 37.3 FPS)
72 If gridsize = 1, XPutImage is slow when the XImage is one line at a time.
73 XPutImage in strips: -db: 21.2 FPS, -no-db: 19.7 FPS
74 XPutimage, whole image: -db: 23.2 FPS, -no-db: 23.4 FPS
77 So XPutImage in strips is very slightly faster when gridsize >= 2, but
78 quite a bit worse when gridsize = 1.
81 /* I thought it would be faster this way, but it turns out not to be... -jwz */
82 /* It's a lot faster for me, though - D.O. */
85 /* i.e. make the XImage the size of the screen. This is much faster when
86 * gridsize = 1. (And SHM is turned off.) */
87 #define USE_BIG_XIMAGE
89 /* Numbers are wave_table size, measured in # of unsigned integers.
90 * FPS/radius = 50/radius = 800/radius = 1500/Big-O memory usage
92 * Use at most one of the following:
93 * Both off = regular sqrt() - 13.5 FPS, 50/800/1500. */
95 /* #define USE_FAST_SQRT_HACKISH */ /* 17.8 FPS/2873/4921/5395/O(lg(radius)) */
96 #define USE_FAST_SQRT_BIGTABLE2 /* 26.1 FPS/156/2242/5386/O(radius^2) */
99 # undef HAVE_XSHM_EXTENSION /* only applicable when using XImages */
100 #endif /* USE_XIMAGE */
102 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
104 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
106 #ifdef HAVE_XSHM_EXTENSION
108 #endif /* HAVE_XSHM_EXTENSION */
110 static const char *interference_defaults [] = {
111 ".background: black",
112 ".foreground: white",
113 "*count: 3", /* number of waves */
114 "*gridsize: 2", /* pixel size, smaller values for better resolution */
115 "*ncolors: 192", /* number of colours used */
116 "*hue: 0", /* hue to use for base color (0-360) */
117 "*speed: 30", /* speed of wave origins moving around */
118 "*delay: 30000", /* or something */
119 "*color-shift: 60", /* h in hsv space, smaller values for smaller
121 "*radius: 800", /* wave extent */
122 "*gray: false", /* color or grayscale */
123 "*mono: false", /* monochrome, not very much fun */
125 "*doubleBuffer: False", /* doubleBuffer slows things down for me - D.O. */
126 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
127 "*useDBE: True", /* use double buffering extension */
128 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
130 #ifdef HAVE_XSHM_EXTENSION
131 "*useSHM: True", /* use shared memory extension */
132 #endif /* HAVE_XSHM_EXTENSION */
134 "*ignoreRotation: True",
140 static XrmOptionDescRec interference_options [] = {
141 { "-count", ".count", XrmoptionSepArg, 0 },
142 { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
143 { "-gridsize", ".gridsize", XrmoptionSepArg, 0 },
144 { "-hue", ".hue", XrmoptionSepArg, 0 },
145 { "-speed", ".speed", XrmoptionSepArg, 0 },
146 { "-delay", ".delay", XrmoptionSepArg, 0 },
147 { "-color-shift", ".color-shift", XrmoptionSepArg, 0 },
148 { "-radius", ".radius", XrmoptionSepArg, 0 },
149 { "-gray", ".gray", XrmoptionNoArg, "True" },
150 { "-mono", ".mono", XrmoptionNoArg, "True" },
151 { "-db", ".doubleBuffer", XrmoptionNoArg, "True" },
152 { "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" },
153 #ifdef HAVE_XSHM_EXTENSION
154 { "-shm", ".useSHM", XrmoptionNoArg, "True" },
155 { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
156 #endif /* HAVE_XSHM_EXTENSION */
161 struct inter_source {
168 struct inter_context {
170 * Display-related entries
175 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
176 XdbeBackBuffer back_buf;
177 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
183 #endif /* USE_XIMAGE */
185 #ifdef HAVE_XSHM_EXTENSION
186 Bool use_shm, shm_can_draw;
187 XShmSegmentInfo shm_info;
188 #endif /* HAVE_XSHM_EXTENSION */
202 * Drawing-related entries
206 unsigned w_div_g, h_div_g;
209 unsigned bits_per_pixel;
214 int radius; /* Not always the same as the X resource. */
217 struct threadpool threadpool;
222 unsigned* wave_height;
225 * Interference sources
227 struct inter_source* source;
232 const struct inter_context *context;
239 unsigned* result_row;
242 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
243 # define TARGET(c) ((c)->back_buf ? (c)->back_buf : \
244 (c)->pix_buf ? (c)->pix_buf : (c)->win)
245 #else /* HAVE_DOUBLE_BUFFER_EXTENSION */
246 # define TARGET(c) ((c)->pix_buf ? (c)->pix_buf : (c)->win)
247 #endif /* !HAVE_DOUBLE_BUFFER_EXTENSION */
249 #ifdef USE_FAST_SQRT_HACKISH
250 /* Based loosely on code from Wikipedia:
251 * https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Approximations_that_depend_on_IEEE_representation
254 /* FAST_SQRT_EXTRA_BITS = 3: Smallest useful value
255 * = 5/6: A little bit of banding, wave_height table is on par with regular
257 * = 7: No apparent difference with original @ radius = 800.
258 * = 8: One more just to be comfortable.
261 # define FAST_SQRT_EXTRA_BITS 8
269 static unsigned fast_log2(unsigned x)
275 return ((u.i - 0x3f800000) >> (23 - FAST_SQRT_EXTRA_BITS)) + 1;
278 static float fast_inv_log2(unsigned x)
283 u.i = ((x - 1) << (23 - FAST_SQRT_EXTRA_BITS)) + 0x3f800000;
289 #ifdef USE_FAST_SQRT_BIGTABLE2
291 /* I eyeballed these figures. They could be improved. - D.O. */
293 # define FAST_SQRT_DISCARD_BITS1 4
294 /* = 5: Dot in center is almost invisible at radius = 800. */
295 /* = 4: Dot in center looks OK at radius = 50. */
298 /* # define FAST_SQRT_DISCARD_BITS2 8 */
299 /* # define FAST_SQRT_CUTOFF 64 * 64 */
302 # define FAST_SQRT_DISCARD_BITS2 9
303 # define FAST_SQRT_CUTOFF 128 * 128
306 * This is a little faster:
307 * 44.5 FPS, 19/5000/17578
309 * # define FAST_SQRT_DISCARD_BITS1 7
310 * # define FAST_SQRT_DISCARD_BITS2 7
311 * # define FAST_SQRT_CUTOFF 0
313 * For radius = 800, FAST_SQRT_DISCARD_BITS2 =
314 * = 9/10: Approximately the original table size, some banding near origins.
315 * = 7: wave_height is 20 KB, and just fits inside a 32K L1 cache.
316 * = 6: Nearly indistinguishable from original
320 FAST_TABLE(x) is equivalent to, but slightly faster than:
321 x < FAST_SQRT_CUTOFF ?
322 (x >> FAST_SQRT_DISCARD_BITS1) :
323 ((x - FAST_SQRT_CUTOFF) >> FAST_SQRT_DISCARD_BITS2) +
324 (FAST_SQRT_CUTOFF >> FAST_SQRT_DISCARD_BITS1);
327 #define FAST_TABLE(x) \
328 ((x) < FAST_SQRT_CUTOFF ? \
329 ((x) >> FAST_SQRT_DISCARD_BITS1) : \
331 ((FAST_SQRT_CUTOFF << (FAST_SQRT_DISCARD_BITS2 - \
332 FAST_SQRT_DISCARD_BITS1)) - FAST_SQRT_CUTOFF)) >> \
333 FAST_SQRT_DISCARD_BITS2))
335 static double fast_inv_table(unsigned x)
337 return x < (FAST_SQRT_CUTOFF >> FAST_SQRT_DISCARD_BITS1) ?
338 (x << FAST_SQRT_DISCARD_BITS1) :
339 ((x - (FAST_SQRT_CUTOFF >> FAST_SQRT_DISCARD_BITS1)) <<
340 FAST_SQRT_DISCARD_BITS2) + FAST_SQRT_CUTOFF;
345 static void destroy_image(Display* dpy, struct inter_context* c)
349 # ifdef HAVE_XSHM_EXTENSION
351 destroy_xshm_image(dpy, c->ximage, &c->shm_info);
355 /* Don't let XDestroyImage free c->ximage->data. */
356 thread_free(c->ximage->data);
357 c->ximage->data = NULL;
358 XDestroyImage(c->ximage);
363 if(c->threadpool.count)
365 threadpool_destroy(&c->threadpool);
366 c->threadpool.count = 0;
370 static void inter_free(Display* dpy, struct inter_context* c)
377 XFreePixmap(dpy, c->pix_buf);
380 XFreeGC(dpy, c->copy_gc);
382 destroy_image(dpy, c);
387 free_colors(c->screen, c->cmap, c->pal, c->colors);
390 for(i = 0; i != c->colors; ++i)
391 XFreeGC(dpy, c->gcs[i]);
395 free(c->wave_height);
399 static void abort_on_error(int error)
401 fprintf(stderr, "interference: %s\n", strerror(error));
405 static void abort_no_mem(void)
407 abort_on_error(ENOMEM);
410 static void check_no_mem(Display* dpy, struct inter_context* c, void* ptr)
418 static int inter_thread_create(
420 struct threadpool* pool,
423 struct inter_thread* self = (struct inter_thread*)self_raw;
424 const struct inter_context* c = GET_PARENT_OBJ(struct inter_context, threadpool, pool);
427 self->thread_id = id;
429 self->result_row = malloc(c->w_div_g * sizeof(unsigned));
430 if(!self->result_row)
434 self->row = malloc(c->w_div_g * sizeof(uint32_t));
436 free(self->result_row);
444 static void inter_thread_destroy(void* self_raw)
446 struct inter_thread* self = (struct inter_thread*)self_raw;
450 free(self->result_row);
454 A higher performance design would have input and output queues, so that when
455 worker threads finish with one frame, they can pull the next work order from
456 the queue and get started on it immediately, rather than going straight to
457 sleep. The current "single-buffered" design still provides reasonable
458 performance at low frame rates; high frame rates are noticeably less efficient.
461 static void inter_thread_run(void* self_raw)
463 struct inter_thread* self = (struct inter_thread*)self_raw;
464 const struct inter_context* c = self->context;
469 int g = c->grid_size;
471 int dx, dy, g2 = 2 * g * g;
477 unsigned img_y = g * self->thread_id;
478 void *scanline = c->ximage->data + c->ximage->bytes_per_line * g * self->thread_id;
481 for(j = self->thread_id; j < c->h_div_g; j += c->threadpool.count) {
485 memset(self->result_row, 0, c->w_div_g * sizeof(unsigned));
487 for(k = 0; k < c->count; k++) {
489 dx = px - c->source[k].x;
490 dy = py - c->source[k].y;
492 dist0 = dx*dx + dy*dy;
493 ddist = -2 * g * c->source[k].x;
495 /* px2g = g*(px*2 + g); */
498 for(i = 0; i < c->w_div_g; i++) {
500 * Discarded possibilities for improving performance here:
501 * 1. Using octagon-based distance estimation
502 * (Which causes giant octagons to appear.)
503 * 2. Square root approximation by reinterpret-casting IEEE floats to
505 * (Which causes angles to appear when two waves interfere.)
510 u.i = (1 << 29) + (u.i >> 1) - (1 << 22);
513 #if defined USE_FAST_SQRT_BIGTABLE2
514 dist1 = FAST_TABLE(dist0);
515 #elif defined USE_FAST_SQRT_HACKISH
516 dist1 = fast_log2(dist0);
521 if(dist1 < c->radius)
522 self->result_row[i] += c->wave_height[dist1];
524 dist0 += px2g + ddist;
529 for(i = 0; i < c->w_div_g; i++) {
531 result = self->result_row[i];
533 /* It's slightly faster to do a subtraction or two before calculating the
535 if(result >= c->colors)
538 if(result >= c->colors)
539 result %= (unsigned)c->colors;
543 self->row[i] = c->pal[result].pixel;
545 XFillRectangle(c->dpy, TARGET(c), c->gcs[result], g*i, g*j, g, g);
546 #endif /* USE_XIMAGE */
550 /* Fill in these `gridsize' horizontal bits in the scanline */
551 if(c->ximage->bits_per_pixel == 32)
553 uint32_t *ptr = (uint32_t *)scanline;
554 for(i = 0; i < c->w_div_g; i++) {
555 for(k = 0; k < g; k++)
556 ptr[g*i+k] = self->row[i];
559 else if(c->ximage->bits_per_pixel == 24)
561 uint8_t *ptr = (uint8_t *)scanline;
562 for(i = 0; i < c->w_div_g; i++) {
563 for(k = 0; k < g; k++) {
564 uint32_t pixel = self->row[i];
565 /* Might not work on big-endian. */
567 ptr[1] = (pixel & 0x0000ff00) >> 8;
568 ptr[2] = (pixel & 0x00ff0000) >> 16;
573 else if(c->ximage->bits_per_pixel == 16)
575 uint16_t *ptr = (uint16_t *)scanline;
576 for(i = 0; i < c->w_div_g; i++) {
577 for(k = 0; k < g; k++)
578 ptr[g*i+k] = self->row[i];
581 else if(c->ximage->bits_per_pixel == 8)
583 uint8_t *ptr = (uint8_t *)scanline;
584 for(i = 0; i < c->w_div_g; i++) {
585 for(k = 0; k < g; k++)
586 ptr[g*i+k] = self->row[i];
591 for(i = 0; i < c->w_div_g; i++) {
592 for(k = 0; k < g; k++)
593 /* XPutPixel is thread safe as long as the XImage didn't have its
594 * bits_per_pixel changed. */
595 XPutPixel(c->ximage, (g*i)+k, img_y, self->row[i]);
599 /* Only the first scanline of the image has been filled in; clone that
600 scanline to the rest of the `gridsize' lines in the ximage */
601 for(k = 0; k < (g-1); k++)
602 memcpy(c->ximage->data + (c->ximage->bytes_per_line * (img_y + k + 1)),
603 c->ximage->data + (c->ximage->bytes_per_line * img_y),
604 c->ximage->bytes_per_line);
606 # ifndef USE_BIG_XIMAGE
607 /* Move the bits for this horizontal stripe to the server. */
608 # ifdef HAVE_XSHM_EXTENSION
610 # endif /* HAVE_XSHM_EXTENSION */
611 XPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage,
612 0, 0, 0, g*j, c->ximage->width, c->ximage->height);
615 # if defined HAVE_XSHM_EXTENSION && !defined USE_BIG_XIMAGE
619 # if defined HAVE_XSHM_EXTENSION || defined USE_BIG_XIMAGE
620 scanline = (char *)scanline + c->ximage->bytes_per_line * g * c->threadpool.count;
621 img_y += g * c->threadpool.count;
625 #endif /* USE_XIMAGE */
629 /* On allocation error, c->ximage == NULL. */
630 static void create_image(
632 struct inter_context* c,
633 const XWindowAttributes* xgwa)
637 /* Set the width so that each thread can work on a different line. */
638 unsigned align = thread_memory_alignment(dpy) * 8 - 1;
639 unsigned wbits, w, h;
643 c->w_div_g = (c->w + c->grid_size - 1) / c->grid_size;
644 c->h_div_g = (c->h + c->grid_size - 1) / c->grid_size;
645 w = c->w_div_g * c->grid_size;
646 h = c->h_div_g * c->grid_size;
648 /* The width of a scan line, in *bits*. */
649 wbits = (w * c->bits_per_pixel + align) & ~align;
651 # ifdef HAVE_XSHM_EXTENSION
653 * Interference used to put one row at a time to the X server. This changes
656 * XShmPutImage is asynchronous; the contents of the XImage must not be
657 * modified until the server has placed the data on the screen. Waiting for
658 * an XShmCompletionEvent after every line of pixels is a little nutty, so
659 * shared-memory XImages will cover the entire screen, and it only has to be
660 * sent once per frame.
662 * The non-SHM code, on the other hand is noticeably slower when
663 * gridsize = 1 with one row at a time. If, on the other hand, gridsize >= 2,
664 * there's a slight speed increase with one row at a time.
666 * This uses a lot more RAM than the single line approach. Users with only
667 * 4 MB of RAM may wish to disable USE_BIG_XIMAGE and specify -no-shm on the
668 * command line. Since this is 2013 and desktop computers are shipping with
669 * 8 GB of RAM, I doubt that this will be a major issue. - D.O.
674 c->ximage = create_xshm_image(dpy, xgwa->visual, xgwa->depth,
675 ZPixmap, 0, &c->shm_info,
676 wbits / c->bits_per_pixel, h);
679 /* If create_xshm_image fails, it will not be attempted again. */
681 c->shm_can_draw = True;
683 # endif /* HAVE_XSHM_EXTENSION */
688 XCreateImage(dpy, xgwa->visual,
689 xgwa->depth, ZPixmap, 0, 0, /* depth, fmt, offset, data */
691 # ifdef USE_BIG_XIMAGE
694 c->grid_size, /* height */
696 8, wbits / 8); /* pad, bpl */
700 if(thread_malloc((void **)&c->ximage->data, dpy,
701 c->ximage->height * c->ximage->bytes_per_line))
709 check_no_mem(dpy, c, c->ximage);
710 #endif /* USE_XIMAGE */
713 static const struct threadpool_class cls =
715 sizeof(struct inter_thread),
720 int error = threadpool_create(
724 #if defined USE_XIMAGE && defined USE_BIG_XIMAGE
725 hardware_concurrency(dpy)
728 /* At least three issues with threads without USE_BIG_XIMAGE:
729 * 1. Most of Xlib isn't thread safe without XInitThreads.
730 * 2. X(Un)LockDisplay would need to be called for each line, which is terrible.
731 * 3. There's only one XImage buffer at the moment.
737 c->threadpool.count = 0; /* See the note in thread_util.h. */
739 abort_on_error(error);
744 static void create_pix_buf(Display* dpy, Window win, struct inter_context *c,
745 const XWindowAttributes* xgwa)
747 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
750 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
751 c->pix_buf = XCreatePixmap(dpy, win, xgwa->width, xgwa->height, xgwa->depth);
754 static double float_time(void)
756 struct timeval result;
759 #ifdef GETTIMEOFDAY_TWO_ARGS
764 return result.tv_usec * 1.0e-6 + result.tv_sec;
767 static void inter_init(Display* dpy, Window win, struct inter_context* c)
769 XWindowAttributes xgwa;
770 double H[3], S[3], V[3];
776 Bool dbuf = get_boolean_resource (dpy, "doubleBuffer", "Boolean");
779 unsigned long valmask = 0;
782 # ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */
786 memset (c, 0, sizeof(*c));
792 c->delay = get_integer_resource(dpy, "delay", "Integer");
794 XGetWindowAttributes(c->dpy, c->win, &xgwa);
795 c->cmap = xgwa.colormap;
796 c->screen = xgwa.screen;
797 c->bits_per_pixel = get_bits_per_pixel(c->dpy, xgwa.depth);
798 check_no_mem(dpy, c, (void *)(ptrdiff_t)c->bits_per_pixel);
800 #ifdef HAVE_XSHM_EXTENSION
801 c->use_shm = get_boolean_resource(dpy, "useSHM", "Boolean");
802 #endif /* HAVE_XSHM_EXTENSION */
804 val.function = GXcopy;
805 c->copy_gc = XCreateGC(c->dpy, TARGET(c), GCFunction, &val);
807 c->count = get_integer_resource(dpy, "count", "Integer");
810 c->grid_size = get_integer_resource(dpy, "gridsize", "Integer");
813 mono = get_boolean_resource(dpy, "mono", "Boolean");
815 c->colors = get_integer_resource(dpy, "ncolors", "Integer");
819 c->hue = get_integer_resource(dpy, "hue", "Float");
820 while (c->hue < 0 || c->hue >= 360)
821 c->hue = frand(360.0);
822 c->speed = get_integer_resource(dpy, "speed", "Integer");
823 c->shift = get_float_resource(dpy, "color-shift", "Float");
824 while(c->shift >= 360.0)
826 while(c->shift <= -360.0)
828 radius = get_integer_resource(dpy, "radius", "Integer");;
832 create_image(dpy, c, &xgwa);
835 c->pal = calloc(c->colors, sizeof(XColor));
836 check_no_mem(dpy, c, c->pal);
838 gray = get_boolean_resource(dpy, "gray", "Boolean");
841 H[1] = H[0] + c->shift < 360.0 ? H[0]+c->shift : H[0] + c->shift-360.0;
842 H[2] = H[1] + c->shift < 360.0 ? H[1]+c->shift : H[1] + c->shift-360.0;
843 S[0] = S[1] = S[2] = 1.0;
844 V[0] = V[1] = V[2] = 1.0;
846 H[0] = H[1] = H[2] = 0.0;
847 S[0] = S[1] = S[2] = 0.0;
848 V[0] = 1.0; V[1] = 0.5; V[2] = 0.0;
851 make_color_loop(c->screen, xgwa.visual, c->cmap,
855 c->pal, &(c->colors),
857 if(c->colors < 2) { /* color allocation failure */
863 if(mono) { /* DON'T else this with the previous if! */
865 c->pal = calloc(2, sizeof(XColor));
866 check_no_mem(dpy, c, c->pal);
867 c->pal[0].pixel = BlackPixel(c->dpy, DefaultScreen(c->dpy));
868 c->pal[1].pixel = WhitePixel(c->dpy, DefaultScreen(c->dpy));
871 #ifdef HAVE_XSHM_EXTENSION
874 /* Double-buffering doesn't work with MIT-SHM: XShmPutImage must draw to the
875 * window. Otherwise, XShmCompletion events will have the XAnyEvent::window
876 * field set to the back buffer, and XScreenSaver will ignore the event. */
881 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
882 c->back_buf = xdbe_get_backbuffer (c->dpy, c->win, XdbeUndefined);
883 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
885 create_pix_buf(dpy, win, c, &xgwa);
889 valmask = GCForeground;
890 c->gcs = calloc(c->colors, sizeof(GC));
891 check_no_mem(dpy, c, c->gcs);
892 for(i = 0; i < c->colors; i++) {
893 val.foreground = c->pal[i].pixel;
894 c->gcs[i] = XCreateGC(c->dpy, TARGET(c), valmask, &val);
898 #if defined USE_FAST_SQRT_HACKISH
899 c->radius = fast_log2(radius * radius);
900 #elif defined USE_FAST_SQRT_BIGTABLE2
901 c->radius = radius * radius;
902 c->radius = FAST_TABLE(c->radius);
907 if (c->radius < 1) c->radius = 1;
908 c->wave_height = calloc(c->radius, sizeof(unsigned));
909 check_no_mem(dpy, c, c->wave_height);
911 for(i = 0; i < c->radius; i++) {
913 #if defined USE_FAST_SQRT_HACKISH
914 fi = sqrt(fast_inv_log2(i));
915 #elif defined USE_FAST_SQRT_BIGTABLE2
916 fi = sqrt(fast_inv_table(i));
922 ((float)radius - fi) /
926 ((max + max*cos(fi/50.0)) / 2.0);
929 c->source = calloc(c->count, sizeof(struct inter_source));
930 check_no_mem(dpy, c, c->source);
932 for(i = 0; i < c->count; i++) {
933 c->source[i].x_theta = frand(2.0)*3.14159;
934 c->source[i].y_theta = frand(2.0)*3.14159;
937 c->last_frame = float_time();
940 #define source_x(c, i) \
941 (c->w/2 + ((int)(cos(c->source[i].x_theta)*((float)c->w/2.0))))
942 #define source_y(c, i) \
943 (c->h/2 + ((int)(cos(c->source[i].y_theta)*((float)c->h/2.0))))
946 * This is somewhat suboptimal. Calculating the distance per-pixel is going to
947 * be a lot slower than using now-ubiquitous SIMD CPU instructions to do four
948 * or eight pixels at a time.
951 static unsigned long do_inter(struct inter_context* c)
958 #if defined USE_XIMAGE && defined HAVE_XSHM_EXTENSION
959 /* Wait a little while for the XServer to become ready if necessary. */
960 if(c->use_shm && !c->shm_can_draw)
965 elapsed = (now - c->last_frame) * 10.0;
969 for(i = 0; i < c->count; i++) {
970 c->source[i].x_theta += (elapsed*c->speed/1000.0);
971 if(c->source[i].x_theta > 2.0*3.14159)
972 c->source[i].x_theta -= 2.0*3.14159;
973 c->source[i].y_theta += (elapsed*c->speed/1000.0);
974 if(c->source[i].y_theta > 2.0*3.14159)
975 c->source[i].y_theta -= 2.0*3.14159;
976 c->source[i].x = source_x(c, i);
977 c->source[i].y = source_y(c, i);
980 threadpool_run(&c->threadpool, inter_thread_run);
981 threadpool_wait(&c->threadpool);
983 #ifdef HAVE_XSHM_EXTENSION
986 XShmPutImage(c->dpy, c->win, c->copy_gc, c->ximage,
987 0, 0, 0, 0, c->ximage->width, c->ximage->height,
989 c->shm_can_draw = False;
992 #if defined HAVE_XSHM_EXTENSION && defined USE_BIG_XIMAGE
995 #ifdef USE_BIG_XIMAGE
997 XPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage,
998 0, 0, 0, 0, c->ximage->width, c->ximage->height);
1002 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1005 XdbeSwapInfo info[1];
1006 info[0].swap_window = c->win;
1007 info[0].swap_action = XdbeUndefined;
1008 XdbeSwapBuffers(c->dpy, info, 1);
1011 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
1014 XCopyArea (c->dpy, c->pix_buf, c->win, c->copy_gc,
1015 0, 0, c->w, c->h, 0, 0);
1022 interference_init (Display *dpy, Window win)
1024 struct inter_context *c = (struct inter_context *) calloc (1, sizeof(*c));
1027 inter_init(dpy, win, c);
1031 static unsigned long
1032 interference_draw (Display *dpy, Window win, void *closure)
1034 struct inter_context *c = (struct inter_context *) closure;
1039 interference_reshape (Display *dpy, Window window, void *closure,
1040 unsigned int w, unsigned int h)
1042 struct inter_context *c = (struct inter_context *) closure;
1043 XWindowAttributes xgwa;
1044 Bool dbuf = (!!c->pix_buf
1045 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1051 destroy_image(dpy, c);
1056 XFreePixmap(dpy, c->pix_buf);
1059 XGetWindowAttributes(dpy, window, &xgwa);
1062 create_image(dpy, c, &xgwa);
1064 create_pix_buf(dpy, window, c, &xgwa);
1068 interference_event (Display *dpy, Window window, void *closure, XEvent *event)
1070 #if HAVE_XSHM_EXTENSION
1071 struct inter_context *c = (struct inter_context *) closure;
1073 if(c->use_shm && event->type == XShmGetEventBase(dpy) + ShmCompletion)
1075 c->shm_can_draw = True;
1083 interference_free (Display *dpy, Window window, void *closure)
1085 struct inter_context *c = (struct inter_context *) closure;
1089 XSCREENSAVER_MODULE ("Interference", interference)