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.
44 #include "screenhack.h"
46 #include "thread_util.h"
52 typedef unsigned int uint32_t;
53 typedef unsigned short uint16_t;
54 typedef unsigned char uint8_t;
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.
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)
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
74 So XPutImage in strips is very slightly faster when gridsize >= 2, but
75 quite a bit worse when gridsize = 1.
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. */
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
86 /* Numbers are wave_table size, measured in # of unsigned integers.
87 * FPS/radius = 50/radius = 800/radius = 1500/Big-O memory usage
89 * Use at most one of the following:
90 * Both off = regular sqrt() - 13.5 FPS, 50/800/1500. */
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) */
96 # undef HAVE_XSHM_EXTENSION /* only applicable when using XImages */
97 #endif /* USE_XIMAGE */
99 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
101 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
103 #ifdef HAVE_XSHM_EXTENSION
105 #endif /* HAVE_XSHM_EXTENSION */
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
118 "*radius: 800", /* wave extent */
119 "*gray: false", /* color or grayscale */
120 "*mono: false", /* monochrome, not very much fun */
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 */
127 #ifdef HAVE_XSHM_EXTENSION
128 "*useSHM: True", /* use shared memory extension */
129 #endif /* HAVE_XSHM_EXTENSION */
131 "*ignoreRotation: True",
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 */
158 struct inter_source {
165 struct inter_context {
167 * Display-related entries
172 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
173 XdbeBackBuffer back_buf;
174 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
180 #endif /* USE_XIMAGE */
182 #ifdef HAVE_XSHM_EXTENSION
183 Bool use_shm, shm_can_draw;
184 XShmSegmentInfo shm_info;
185 #endif /* HAVE_XSHM_EXTENSION */
199 * Drawing-related entries
205 unsigned bits_per_pixel;
210 int radius; /* Not always the same as the X resource. */
213 struct threadpool threadpool;
218 unsigned* wave_height;
221 * Interference sources
223 struct inter_source* source;
228 const struct inter_context *context;
235 unsigned* result_row;
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 */
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
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
253 * = 7: No apparent difference with original @ radius = 800.
254 * = 8: One more just to be comfortable.
257 # define FAST_SQRT_EXTRA_BITS 8
265 static unsigned fast_log2(unsigned x)
271 return ((u.i - 0x3f800000) >> (23 - FAST_SQRT_EXTRA_BITS)) + 1;
274 static float fast_inv_log2(unsigned x)
279 u.i = ((x - 1) << (23 - FAST_SQRT_EXTRA_BITS)) + 0x3f800000;
285 #ifdef USE_FAST_SQRT_BIGTABLE2
287 /* I eyeballed these figures. They could be improved. - D.O. */
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. */
294 /* # define FAST_SQRT_DISCARD_BITS2 8 */
295 /* # define FAST_SQRT_CUTOFF 64 * 64 */
298 # define FAST_SQRT_DISCARD_BITS2 9
299 # define FAST_SQRT_CUTOFF 128 * 128
302 * This is a little faster:
303 * 44.5 FPS, 19/5000/17578
305 * # define FAST_SQRT_DISCARD_BITS1 7
306 * # define FAST_SQRT_DISCARD_BITS2 7
307 * # define FAST_SQRT_CUTOFF 0
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
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);
323 #define FAST_TABLE(x) \
324 ((x) < FAST_SQRT_CUTOFF ? \
325 ((x) >> FAST_SQRT_DISCARD_BITS1) : \
327 ((FAST_SQRT_CUTOFF << (FAST_SQRT_DISCARD_BITS2 - \
328 FAST_SQRT_DISCARD_BITS1)) - FAST_SQRT_CUTOFF)) >> \
329 FAST_SQRT_DISCARD_BITS2))
331 static double fast_inv_table(unsigned x)
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;
341 static void destroy_image(Display* dpy, struct inter_context* c)
345 # ifdef HAVE_XSHM_EXTENSION
347 destroy_xshm_image(dpy, c->ximage, &c->shm_info);
351 /* Don't let XDestroyImage free c->ximage->data. */
352 thread_free(c->ximage->data);
353 c->ximage->data = NULL;
354 XDestroyImage(c->ximage);
359 if(c->threadpool.count)
361 threadpool_destroy(&c->threadpool);
362 c->threadpool.count = 0;
366 static void inter_free(Display* dpy, struct inter_context* c)
373 XFreePixmap(dpy, c->pix_buf);
376 XFreeGC(dpy, c->copy_gc);
378 destroy_image(dpy, c);
383 free_colors(c->screen, c->cmap, c->pal, c->colors);
386 for(i = 0; i != c->colors; ++i)
387 XFreeGC(dpy, c->gcs[i]);
391 free(c->wave_height);
395 static void abort_on_error(int error)
397 fprintf(stderr, "interference: %s\n", strerror(error));
401 static void abort_no_mem(void)
403 abort_on_error(ENOMEM);
406 static void check_no_mem(Display* dpy, struct inter_context* c, void* ptr)
414 static int inter_thread_create(
416 struct threadpool* pool,
419 struct inter_thread* self = (struct inter_thread*)self_raw;
420 const struct inter_context* c = GET_PARENT_OBJ(struct inter_context, threadpool, pool);
423 self->thread_id = id;
425 self->result_row = malloc((c->w / c->grid_size) * sizeof(unsigned));
426 if(!self->result_row)
430 self->row = malloc((c->w / c->grid_size) * sizeof(uint32_t));
432 free(self->result_row);
440 static void inter_thread_destroy(void* self_raw)
442 struct inter_thread* self = (struct inter_thread*)self_raw;
446 free(self->result_row);
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.
457 static void inter_thread_run(void* self_raw)
459 struct inter_thread* self = (struct inter_thread*)self_raw;
460 const struct inter_context* c = self->context;
465 int g = c->grid_size;
466 unsigned w_div_g = c->w/g;
468 int dx, dy, g2 = 2 * g * g;
474 unsigned img_y = g * self->thread_id;
475 void *scanline = c->ximage->data + c->ximage->bytes_per_line * g * self->thread_id;
478 for(j = self->thread_id; j < c->h/g; j += c->threadpool.count) {
482 memset(self->result_row, 0, w_div_g * sizeof(unsigned));
484 for(k = 0; k < c->count; k++) {
486 dx = px - c->source[k].x;
487 dy = py - c->source[k].y;
489 dist0 = dx*dx + dy*dy;
490 ddist = -2 * g * c->source[k].x;
492 /* px2g = g*(px*2 + g); */
495 for(i = 0; i < w_div_g; i++) {
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
502 * (Which causes angles to appear when two waves interfere.)
507 u.i = (1 << 29) + (u.i >> 1) - (1 << 22);
510 #if defined USE_FAST_SQRT_BIGTABLE2
511 dist1 = FAST_TABLE(dist0);
512 #elif defined USE_FAST_SQRT_HACKISH
513 dist1 = fast_log2(dist0);
518 if(dist1 < c->radius)
519 self->result_row[i] += c->wave_height[dist1];
521 dist0 += px2g + ddist;
526 for(i = 0; i < w_div_g; i++) {
528 result = self->result_row[i];
530 /* It's slightly faster to do a subtraction or two before calculating the
532 if(result >= c->colors)
535 if(result >= c->colors)
536 result %= (unsigned)c->colors;
540 self->row[i] = c->pal[result].pixel;
542 XFillRectangle(c->dpy, TARGET(c), c->gcs[result], g*i, g*j, g, g);
543 #endif /* USE_XIMAGE */
547 /* Fill in these `gridsize' horizontal bits in the scanline */
548 if(c->ximage->bits_per_pixel == 32)
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];
556 else if(c->ximage->bits_per_pixel == 24)
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. */
564 ptr[1] = (pixel & 0x0000ff00) >> 8;
565 ptr[2] = (pixel & 0x00ff0000) >> 16;
570 else if(c->ximage->bits_per_pixel == 16)
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];
578 else if(c->ximage->bits_per_pixel == 8)
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];
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]);
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);
603 # ifndef USE_BIG_XIMAGE
604 /* Move the bits for this horizontal stripe to the server. */
605 # ifdef HAVE_XSHM_EXTENSION
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);
612 # if defined HAVE_XSHM_EXTENSION && !defined USE_BIG_XIMAGE
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;
622 #endif /* USE_XIMAGE */
626 /* On allocation error, c->ximage == NULL. */
627 static void create_image(
629 struct inter_context* c,
630 const XWindowAttributes* xgwa)
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;
639 # ifdef HAVE_XSHM_EXTENSION
641 * Interference used to put one row at a time to the X server. This changes
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.
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.
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.
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);
667 /* If create_xshm_image fails, it will not be attempted again. */
669 c->shm_can_draw = True;
671 # endif /* HAVE_XSHM_EXTENSION */
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 */
682 c->grid_size, /* height */
684 8, width / 8); /* pad, bpl */
688 if(thread_malloc((void **)&c->ximage->data, dpy,
689 c->ximage->height * c->ximage->bytes_per_line))
697 check_no_mem(dpy, c, c->ximage);
698 #endif /* USE_XIMAGE */
701 static const struct threadpool_class cls =
703 sizeof(struct inter_thread),
708 int error = threadpool_create(
712 #if defined USE_XIMAGE && defined USE_BIG_XIMAGE
713 hardware_concurrency(dpy)
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.
725 c->threadpool.count = 0; /* See the note in thread_util.h. */
727 abort_on_error(error);
732 static void create_pix_buf(Display* dpy, Window win, struct inter_context *c,
733 const XWindowAttributes* xgwa)
735 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
738 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
739 c->pix_buf = XCreatePixmap(dpy, win, xgwa->width, xgwa->height, xgwa->depth);
742 static double float_time(void)
744 struct timeval result;
747 #ifdef GETTIMEOFDAY_TWO_ARGS
752 return result.tv_usec * 1.0e-6 + result.tv_sec;
755 static void inter_init(Display* dpy, Window win, struct inter_context* c)
757 XWindowAttributes xgwa;
758 double H[3], S[3], V[3];
764 Bool dbuf = get_boolean_resource (dpy, "doubleBuffer", "Boolean");
767 unsigned long valmask = 0;
770 # ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
774 memset (c, 0, sizeof(*c));
780 c->delay = get_integer_resource(dpy, "delay", "Integer");
782 XGetWindowAttributes(c->dpy, c->win, &xgwa);
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);
790 #ifdef HAVE_XSHM_EXTENSION
791 c->use_shm = get_boolean_resource(dpy, "useSHM", "Boolean");
792 #endif /* HAVE_XSHM_EXTENSION */
794 val.function = GXcopy;
795 c->copy_gc = XCreateGC(c->dpy, TARGET(c), GCFunction, &val);
797 c->count = get_integer_resource(dpy, "count", "Integer");
800 c->grid_size = get_integer_resource(dpy, "gridsize", "Integer");
803 mono = get_boolean_resource(dpy, "mono", "Boolean");
805 c->colors = get_integer_resource(dpy, "ncolors", "Integer");
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)
816 while(c->shift <= -360.0)
818 radius = get_integer_resource(dpy, "radius", "Integer");;
822 create_image(dpy, c, &xgwa);
825 c->pal = calloc(c->colors, sizeof(XColor));
826 check_no_mem(dpy, c, c->pal);
828 gray = get_boolean_resource(dpy, "gray", "Boolean");
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;
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;
841 make_color_loop(c->screen, xgwa.visual, c->cmap,
845 c->pal, &(c->colors),
847 if(c->colors < 2) { /* color allocation failure */
853 if(mono) { /* DON'T else this with the previous if! */
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));
861 #ifdef HAVE_XSHM_EXTENSION
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. */
871 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
872 c->back_buf = xdbe_get_backbuffer (c->dpy, c->win, XdbeUndefined);
873 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
875 create_pix_buf(dpy, win, c, &xgwa);
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);
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);
897 c->wave_height = calloc(c->radius, sizeof(unsigned));
898 check_no_mem(dpy, c, c->wave_height);
900 for(i = 0; i < c->radius; i++) {
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));
911 ((float)radius - fi) /
915 ((max + max*cos(fi/50.0)) / 2.0);
918 c->source = calloc(c->count, sizeof(struct inter_source));
919 check_no_mem(dpy, c, c->source);
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;
926 c->last_frame = float_time();
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))))
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.
942 _alloc_color(struct inter_context *c, uint16_t r, uint16_t g, uint16_t b)
948 XAllocColor(c->dpy, c->cmap, &color);
952 static void _copy_test(Display *dpy, Drawable src, Drawable dst, GC gc, int x, int y, uint32_t cells)
954 XCopyArea(dpy, src, dst, gc, 0, 0, 3, 2, x, y);
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);
963 static void _test_pattern(Display *dpy, Drawable d, GC gc, const uint32_t *rgb)
966 for(x = 0; x != 3; ++x)
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);
974 _copy_test(dpy, d, d, gc, 0, 2, rgb[0] | rgb[1] | rgb[2]);
976 #endif /* TEST_PATTERN */
978 static unsigned long do_inter(struct inter_context* c)
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)
992 elapsed = (now - c->last_frame) * 10.0;
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);
1007 threadpool_run(&c->threadpool, inter_thread_run);
1008 threadpool_wait(&c->threadpool);
1010 #ifdef HAVE_XSHM_EXTENSION
1013 XShmPutImage(c->dpy, c->win, c->copy_gc, c->ximage,
1014 0, 0, 0, 0, c->ximage->width, c->ximage->height,
1016 c->shm_can_draw = False;
1019 #if defined HAVE_XSHM_EXTENSION && defined USE_BIG_XIMAGE
1022 #ifdef USE_BIG_XIMAGE
1024 XPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage,
1025 0, 0, 0, 0, c->ximage->width, c->ximage->height);
1031 /* XWindowAttributes xgwa;
1032 XGetWindowAttributes(c->dpy, c->win, &xgwa); */
1034 /* if(xgwa.width >= 9 && xgwa.height >= 10) */
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));
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);
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];
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
1055 _copy_test(c->dpy, TARGET(c), pixmap, c->copy_gc, 0, 6, cells);
1057 XCopyArea(c->dpy, pixmap, TARGET(c), c->copy_gc, 0, 0, 3, 10, 3, 0);
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);
1064 XFreePixmap(c->dpy, pixmap);
1065 XSync(c->dpy, False);
1068 #endif /* TEST_PATTERN */
1070 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1073 XdbeSwapInfo info[1];
1074 info[0].swap_window = c->win;
1075 info[0].swap_action = XdbeUndefined;
1076 XdbeSwapBuffers(c->dpy, info, 1);
1079 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
1082 XCopyArea (c->dpy, c->pix_buf, c->win, c->copy_gc,
1083 0, 0, c->w, c->h, 0, 0);
1090 interference_init (Display *dpy, Window win)
1092 struct inter_context *c = (struct inter_context *) calloc (1, sizeof(*c));
1095 inter_init(dpy, win, c);
1099 static unsigned long
1100 interference_draw (Display *dpy, Window win, void *closure)
1102 struct inter_context *c = (struct inter_context *) closure;
1107 interference_reshape (Display *dpy, Window window, void *closure,
1108 unsigned int w, unsigned int h)
1110 struct inter_context *c = (struct inter_context *) closure;
1111 XWindowAttributes xgwa;
1112 Bool dbuf = (c->pix_buf
1113 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1122 destroy_image(dpy, c);
1127 XFreePixmap(dpy, c->pix_buf);
1130 XGetWindowAttributes(dpy, window, &xgwa);
1133 create_image(dpy, c, &xgwa);
1135 create_pix_buf(dpy, window, c, &xgwa);
1139 interference_event (Display *dpy, Window window, void *closure, XEvent *event)
1141 #if HAVE_XSHM_EXTENSION
1142 struct inter_context *c = (struct inter_context *) closure;
1144 if(c->use_shm && event->type == XShmGetEventBase(dpy) + ShmCompletion)
1146 c->shm_can_draw = True;
1154 interference_free (Display *dpy, Window window, void *closure)
1156 struct inter_context *c = (struct inter_context *) closure;
1160 XSCREENSAVER_MODULE ("Interference", interference)