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.
42 * Last modified: Sun Oct 9 11:20:48 2016, <dmo2118@gmail.com>
43 * Updated for new xshm.c.
44 * Ditched USE_BIG_XIMAGE.
50 #include "screenhack.h"
52 #include "thread_util.h"
54 #ifdef HAVE_INTTYPES_H
55 # include <inttypes.h>
58 typedef unsigned int uint32_t;
59 typedef unsigned short uint16_t;
60 typedef unsigned char uint8_t;
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.
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)
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
80 So XPutImage in strips is very slightly faster when gridsize >= 2, but
81 quite a bit worse when gridsize = 1.
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. */
88 /* Numbers are wave_table size, measured in # of unsigned integers.
89 * FPS/radius = 50/radius = 800/radius = 1500/Big-O memory usage
91 * Use at most one of the following:
92 * Both off = regular sqrt() - 13.5 FPS, 50/800/1500. */
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) */
97 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
99 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
103 #endif /* USE_XIMAGE */
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
116 "*radius: 800", /* wave extent */
117 "*gray: false", /* color or grayscale */
118 "*mono: false", /* monochrome, not very much fun */
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 */
125 #ifdef HAVE_XSHM_EXTENSION
126 "*useSHM: True", /* use shared memory extension */
127 #endif /* HAVE_XSHM_EXTENSION */
129 "*ignoreRotation: True",
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 */
156 struct inter_source {
163 struct inter_context {
165 * Display-related entries
170 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
171 XdbeBackBuffer back_buf;
172 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
180 XShmSegmentInfo shm_info;
181 #endif /* USE_XIMAGE */
195 * Drawing-related entries
199 unsigned w_div_g, h_div_g;
202 unsigned bits_per_pixel;
207 int radius; /* Not always the same as the X resource. */
210 struct threadpool threadpool;
215 unsigned* wave_height;
218 * Interference sources
220 struct inter_source* source;
225 const struct inter_context *context;
232 unsigned* result_row;
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 */
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
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
250 * = 7: No apparent difference with original @ radius = 800.
251 * = 8: One more just to be comfortable.
254 # define FAST_SQRT_EXTRA_BITS 8
262 static unsigned fast_log2(unsigned x)
268 return ((u.i - 0x3f800000) >> (23 - FAST_SQRT_EXTRA_BITS)) + 1;
271 static float fast_inv_log2(unsigned x)
276 u.i = ((x - 1) << (23 - FAST_SQRT_EXTRA_BITS)) + 0x3f800000;
282 #ifdef USE_FAST_SQRT_BIGTABLE2
284 /* I eyeballed these figures. They could be improved. - D.O. */
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. */
291 /* # define FAST_SQRT_DISCARD_BITS2 8 */
292 /* # define FAST_SQRT_CUTOFF 64 * 64 */
295 # define FAST_SQRT_DISCARD_BITS2 9
296 # define FAST_SQRT_CUTOFF 128 * 128
299 * This is a little faster:
300 * 44.5 FPS, 19/5000/17578
302 * # define FAST_SQRT_DISCARD_BITS1 7
303 * # define FAST_SQRT_DISCARD_BITS2 7
304 * # define FAST_SQRT_CUTOFF 0
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
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);
320 #define FAST_TABLE(x) \
321 ((x) < FAST_SQRT_CUTOFF ? \
322 ((x) >> FAST_SQRT_DISCARD_BITS1) : \
324 ((FAST_SQRT_CUTOFF << (FAST_SQRT_DISCARD_BITS2 - \
325 FAST_SQRT_DISCARD_BITS1)) - FAST_SQRT_CUTOFF)) >> \
326 FAST_SQRT_DISCARD_BITS2))
328 static double fast_inv_table(unsigned x)
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;
338 static void destroy_image(Display* dpy, struct inter_context* c)
342 destroy_xshm_image(dpy, c->ximage, &c->shm_info);
346 if(c->threadpool.count)
348 threadpool_destroy(&c->threadpool);
349 c->threadpool.count = 0;
353 static void inter_free(Display* dpy, struct inter_context* c)
360 XFreePixmap(dpy, c->pix_buf);
363 XFreeGC(dpy, c->copy_gc);
365 destroy_image(dpy, c);
370 free_colors(c->screen, c->cmap, c->pal, c->colors);
373 for(i = 0; i != c->colors; ++i)
374 XFreeGC(dpy, c->gcs[i]);
378 free(c->wave_height);
382 static void abort_on_error(int error)
384 fprintf(stderr, "interference: %s\n", strerror(error));
388 static void abort_no_mem(void)
390 abort_on_error(ENOMEM);
393 static void check_no_mem(Display* dpy, struct inter_context* c, void* ptr)
401 static int inter_thread_create(
403 struct threadpool* pool,
406 struct inter_thread* self = (struct inter_thread*)self_raw;
407 const struct inter_context* c = GET_PARENT_OBJ(struct inter_context, threadpool, pool);
410 self->thread_id = id;
412 self->result_row = malloc(c->w_div_g * sizeof(unsigned));
413 if(!self->result_row)
417 self->row = malloc(c->w_div_g * sizeof(uint32_t));
419 free(self->result_row);
427 static void inter_thread_destroy(void* self_raw)
429 struct inter_thread* self = (struct inter_thread*)self_raw;
433 free(self->result_row);
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.
444 static void inter_thread_run(void* self_raw)
446 struct inter_thread* self = (struct inter_thread*)self_raw;
447 const struct inter_context* c = self->context;
452 int g = c->grid_size;
454 int dx, dy, g2 = 2 * g * g;
460 unsigned img_y = g * self->thread_id;
461 void *scanline = c->ximage->data + c->ximage->bytes_per_line * g * self->thread_id;
464 for(j = self->thread_id; j < c->h_div_g; j += c->threadpool.count) {
468 memset(self->result_row, 0, c->w_div_g * sizeof(unsigned));
470 for(k = 0; k < c->count; k++) {
472 dx = px - c->source[k].x;
473 dy = py - c->source[k].y;
475 dist0 = dx*dx + dy*dy;
476 ddist = -2 * g * c->source[k].x;
478 /* px2g = g*(px*2 + g); */
481 for(i = 0; i < c->w_div_g; i++) {
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
488 * (Which causes angles to appear when two waves interfere.)
493 u.i = (1 << 29) + (u.i >> 1) - (1 << 22);
496 #if defined USE_FAST_SQRT_BIGTABLE2
497 dist1 = FAST_TABLE(dist0);
498 #elif defined USE_FAST_SQRT_HACKISH
499 dist1 = fast_log2(dist0);
504 if(dist1 < c->radius)
505 self->result_row[i] += c->wave_height[dist1];
507 dist0 += px2g + ddist;
512 for(i = 0; i < c->w_div_g; i++) {
514 result = self->result_row[i];
516 /* It's slightly faster to do a subtraction or two before calculating the
518 if(result >= c->colors)
521 if(result >= c->colors)
522 result %= (unsigned)c->colors;
526 self->row[i] = c->pal[result].pixel;
528 XFillRectangle(c->dpy, TARGET(c), c->gcs[result], g*i, g*j, g, g);
529 #endif /* USE_XIMAGE */
533 /* Fill in these `gridsize' horizontal bits in the scanline */
534 if(c->ximage->bits_per_pixel == 32)
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];
542 else if(c->ximage->bits_per_pixel == 24)
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. */
550 ptr[1] = (pixel & 0x0000ff00) >> 8;
551 ptr[2] = (pixel & 0x00ff0000) >> 16;
556 else if(c->ximage->bits_per_pixel == 16)
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];
564 else if(c->ximage->bits_per_pixel == 8)
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];
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]);
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);
589 scanline = (char *)scanline +
590 c->ximage->bytes_per_line * g * c->threadpool.count;
591 img_y += g * c->threadpool.count;
593 #endif /* USE_XIMAGE */
597 /* On allocation error, c->ximage == NULL. */
598 static void create_image(
600 struct inter_context* c,
601 const XWindowAttributes* xgwa)
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 */
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;
616 w = c->w_div_g * c->grid_size;
617 h = c->h_div_g * c->grid_size;
619 /* The width of a scan line, in *bits*. */
620 wbits = (w * c->bits_per_pixel + align) & ~align;
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
627 c->ximage = create_xshm_image(dpy, xgwa->visual, xgwa->depth,
628 ZPixmap, &c->shm_info,
629 wbits / c->bits_per_pixel, h);
631 c->shm_can_draw = True;
633 check_no_mem(dpy, c, c->ximage);
634 #endif /* USE_XIMAGE */
637 static const struct threadpool_class cls =
639 sizeof(struct inter_thread),
644 int error = threadpool_create(
649 hardware_concurrency(dpy)
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
661 c->threadpool.count = 0; /* See the note in thread_util.h. */
663 abort_on_error(error);
668 static void create_pix_buf(Display* dpy, Window win, struct inter_context *c,
669 const XWindowAttributes* xgwa)
671 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
674 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
675 c->pix_buf = XCreatePixmap(dpy, win, xgwa->width, xgwa->height, xgwa->depth);
678 static double float_time(void)
680 struct timeval result;
683 #ifdef GETTIMEOFDAY_TWO_ARGS
688 return result.tv_usec * 1.0e-6 + result.tv_sec;
691 static void inter_init(Display* dpy, Window win, struct inter_context* c)
693 XWindowAttributes xgwa;
694 double H[3], S[3], V[3];
701 Bool dbuf = get_boolean_resource (dpy, "doubleBuffer", "Boolean");
704 unsigned long valmask = 0;
707 # ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */
711 memset (c, 0, sizeof(*c));
717 c->delay = get_integer_resource(dpy, "delay", "Integer");
719 XGetWindowAttributes(c->dpy, c->win, &xgwa);
720 c->cmap = xgwa.colormap;
721 c->screen = xgwa.screen;
722 c->bits_per_pixel = visual_pixmap_depth(xgwa.screen, xgwa.visual);
723 check_no_mem(dpy, c, (void *)(ptrdiff_t)c->bits_per_pixel);
725 val.function = GXcopy;
726 c->copy_gc = XCreateGC(c->dpy, TARGET(c), GCFunction, &val);
728 c->count = get_integer_resource(dpy, "count", "Integer");
731 c->grid_size = get_integer_resource(dpy, "gridsize", "Integer");
734 mono = get_boolean_resource(dpy, "mono", "Boolean");
736 c->colors = get_integer_resource(dpy, "ncolors", "Integer");
740 c->hue = get_integer_resource(dpy, "hue", "Float");
741 while (c->hue < 0 || c->hue >= 360)
742 c->hue = frand(360.0);
743 c->speed = get_integer_resource(dpy, "speed", "Integer");
744 c->shift = get_float_resource(dpy, "color-shift", "Float");
745 while(c->shift >= 360.0)
747 while(c->shift <= -360.0)
749 radius = get_integer_resource(dpy, "radius", "Integer");;
753 if (xgwa.width > 2560) scale = 3.5; /* Retina displays */
756 create_image(dpy, c, &xgwa);
759 c->pal = calloc(c->colors, sizeof(XColor));
760 check_no_mem(dpy, c, c->pal);
762 gray = get_boolean_resource(dpy, "gray", "Boolean");
765 H[1] = H[0] + c->shift < 360.0 ? H[0]+c->shift : H[0] + c->shift-360.0;
766 H[2] = H[1] + c->shift < 360.0 ? H[1]+c->shift : H[1] + c->shift-360.0;
767 S[0] = S[1] = S[2] = 1.0;
768 V[0] = V[1] = V[2] = 1.0;
770 H[0] = H[1] = H[2] = 0.0;
771 S[0] = S[1] = S[2] = 0.0;
772 V[0] = 1.0; V[1] = 0.5; V[2] = 0.0;
775 make_color_loop(c->screen, xgwa.visual, c->cmap,
779 c->pal, &(c->colors),
781 if(c->colors < 2) { /* color allocation failure */
787 if(mono) { /* DON'T else this with the previous if! */
789 c->pal = calloc(2, sizeof(XColor));
790 check_no_mem(dpy, c, c->pal);
791 c->pal[0].pixel = BlackPixel(c->dpy, DefaultScreen(c->dpy));
792 c->pal[1].pixel = WhitePixel(c->dpy, DefaultScreen(c->dpy));
797 /* Double-buffering doesn't work with MIT-SHM: XShmPutImage must draw to the
798 * window. Otherwise, XShmCompletion events will have the XAnyEvent::window
799 * field set to the back buffer, and XScreenSaver will ignore the event.
805 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
806 c->back_buf = xdbe_get_backbuffer (c->dpy, c->win, XdbeUndefined);
807 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
809 create_pix_buf(dpy, win, c, &xgwa);
813 valmask = GCForeground;
814 c->gcs = calloc(c->colors, sizeof(GC));
815 check_no_mem(dpy, c, c->gcs);
816 for(i = 0; i < c->colors; i++) {
817 val.foreground = c->pal[i].pixel;
818 c->gcs[i] = XCreateGC(c->dpy, TARGET(c), valmask, &val);
822 #if defined USE_FAST_SQRT_HACKISH
823 c->radius = fast_log2(radius * radius);
824 #elif defined USE_FAST_SQRT_BIGTABLE2
825 c->radius = radius * radius;
826 c->radius = FAST_TABLE(c->radius);
831 if (c->radius < 1) c->radius = 1;
832 c->wave_height = calloc(c->radius, sizeof(unsigned));
833 check_no_mem(dpy, c, c->wave_height);
835 for(i = 0; i < c->radius; i++) {
837 #if defined USE_FAST_SQRT_HACKISH
838 fi = sqrt(fast_inv_log2(i));
839 #elif defined USE_FAST_SQRT_BIGTABLE2
840 fi = sqrt(fast_inv_table(i));
846 ((float)radius - fi) /
850 ((max + max*cos(fi/(50.0 * scale))) / 2.0);
853 c->source = calloc(c->count, sizeof(struct inter_source));
854 check_no_mem(dpy, c, c->source);
856 for(i = 0; i < c->count; i++) {
857 c->source[i].x_theta = frand(2.0)*3.14159;
858 c->source[i].y_theta = frand(2.0)*3.14159;
861 c->last_frame = float_time();
864 #define source_x(c, i) \
865 (c->w/2 + ((int)(cos(c->source[i].x_theta)*((float)c->w/2.0))))
866 #define source_y(c, i) \
867 (c->h/2 + ((int)(cos(c->source[i].y_theta)*((float)c->h/2.0))))
870 * This is somewhat suboptimal. Calculating the distance per-pixel is going to
871 * be a lot slower than using now-ubiquitous SIMD CPU instructions to do four
872 * or eight pixels at a time.
875 static unsigned long do_inter(struct inter_context* c)
883 /* Wait a little while for the XServer to become ready if necessary. */
889 elapsed = (now - c->last_frame) * 10.0;
893 for(i = 0; i < c->count; i++) {
894 c->source[i].x_theta += (elapsed*c->speed/1000.0);
895 if(c->source[i].x_theta > 2.0*3.14159)
896 c->source[i].x_theta -= 2.0*3.14159;
897 c->source[i].y_theta += (elapsed*c->speed/1000.0);
898 if(c->source[i].y_theta > 2.0*3.14159)
899 c->source[i].y_theta -= 2.0*3.14159;
900 c->source[i].x = source_x(c, i);
901 c->source[i].y = source_y(c, i);
904 threadpool_run(&c->threadpool, inter_thread_run);
905 threadpool_wait(&c->threadpool);
908 put_xshm_image(c->dpy, c->win, c->copy_gc, c->ximage, 0, 0, 0, 0,
909 c->ximage->width, c->ximage->height, &c->shm_info);
910 /* c->shm_can_draw = False; */
913 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
916 XdbeSwapInfo info[1];
917 info[0].swap_window = c->win;
918 info[0].swap_action = XdbeUndefined;
919 XdbeSwapBuffers(c->dpy, info, 1);
922 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
925 XCopyArea (c->dpy, c->pix_buf, c->win, c->copy_gc,
926 0, 0, c->w, c->h, 0, 0);
933 interference_init (Display *dpy, Window win)
935 struct inter_context *c = (struct inter_context *) calloc (1, sizeof(*c));
938 inter_init(dpy, win, c);
943 interference_draw (Display *dpy, Window win, void *closure)
945 struct inter_context *c = (struct inter_context *) closure;
950 interference_reshape (Display *dpy, Window window, void *closure,
951 unsigned int w, unsigned int h)
953 struct inter_context *c = (struct inter_context *) closure;
954 XWindowAttributes xgwa;
955 Bool dbuf = (!!c->pix_buf
956 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
962 destroy_image(dpy, c);
967 XFreePixmap(dpy, c->pix_buf);
970 XGetWindowAttributes(dpy, window, &xgwa);
973 create_image(dpy, c, &xgwa);
975 create_pix_buf(dpy, window, c, &xgwa);
979 interference_event (Display *dpy, Window window, void *closure, XEvent *event)
981 #if HAVE_XSHM_EXTENSION
982 struct inter_context *c = (struct inter_context *) closure;
984 if(event->type == XShmGetEventBase(dpy) + ShmCompletion)
986 c->shm_can_draw = True;
994 interference_free (Display *dpy, Window window, void *closure)
996 struct inter_context *c = (struct inter_context *) closure;
1000 XSCREENSAVER_MODULE ("Interference", interference)