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
33 * This really needs to be sped up.
35 * I've tried making it use XPutPixel/XPutImage instead of XFillRectangle,
36 * but that doesn't seem to help (it's the same speed at gridsize=1, and
37 * it actually makes it slower at larger sizes.)
39 * I played around with shared memory, but clearly I still don't know how
40 * to use the XSHM extension properly, because it doesn't work yet.
42 * Hannu had put in code to use the double-buffering extension, but that
43 * code didn't work for me on Irix. I don't think it would help anyway,
44 * since it's not the swapping of frames that is the bottleneck (or a source
45 * of visible flicker.)
52 #include "screenhack.h"
54 /* I thought it would be faster this way, but it turns out not to be... -jwz */
58 # undef HAVE_XSHM_EXTENSION /* only applicable when using XImages */
59 #endif /* USE_XIMAGE */
62 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
64 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
66 #ifdef HAVE_XSHM_EXTENSION
68 #endif /* HAVE_XSHM_EXTENSION */
70 static const char *interference_defaults [] = {
73 "*count: 3", /* number of waves */
74 "*gridsize: 4", /* pixel size, smaller values for better resolution */
75 "*ncolors: 128", /* number of colours used */
76 "*hue: 0", /* hue to use for base color (0-360) */
77 "*speed: 30", /* speed of wave origins moving around */
78 "*delay: 30000", /* or something */
79 "*color-shift: 60", /* h in hsv space, smaller values for smaller
81 "*radius: 800", /* wave extent */
82 "*gray: false", /* color or grayscale */
83 "*mono: false", /* monochrome, not very much fun */
85 "*doubleBuffer: True",
86 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
87 "*useDBE: True", /* use double buffering extension */
88 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
90 #ifdef HAVE_XSHM_EXTENSION
91 "*useSHM: True", /* use shared memory extension */
92 #endif /* HAVE_XSHM_EXTENSION */
96 static XrmOptionDescRec interference_options [] = {
97 { "-count", ".count", XrmoptionSepArg, 0 },
98 { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
99 { "-gridsize", ".gridsize", XrmoptionSepArg, 0 },
100 { "-hue", ".hue", XrmoptionSepArg, 0 },
101 { "-speed", ".speed", XrmoptionSepArg, 0 },
102 { "-delay", ".delay", XrmoptionSepArg, 0 },
103 { "-color-shift", ".color-shift", XrmoptionSepArg, 0 },
104 { "-radius", ".radius", XrmoptionSepArg, 0 },
105 { "-gray", ".gray", XrmoptionNoArg, "True" },
106 { "-mono", ".mono", XrmoptionNoArg, "True" },
107 { "-db", ".doubleBuffer", XrmoptionNoArg, "True" },
108 { "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" },
109 #ifdef HAVE_XSHM_EXTENSION
110 { "-shm", ".useSHM", XrmoptionNoArg, "True" },
111 { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
112 #endif /* HAVE_XSHM_EXTENSION */
116 struct inter_source {
123 struct inter_context {
125 * Display-related entries
130 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
131 XdbeBackBuffer back_buf;
132 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
138 #endif /* USE_XIMAGE */
140 #ifdef HAVE_XSHM_EXTENSION
142 XShmSegmentInfo shm_info;
143 #endif /* HAVE_XSHM_EXTENSION */
158 * Drawing-related entries
172 * Interference sources
174 struct inter_source* source;
177 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
178 # define TARGET(c) ((c)->back_buf ? (c)->back_buf : \
179 (c)->pix_buf ? (c)->pix_buf : (c)->win)
180 #else /* HAVE_DOUBLE_BUFFER_EXTENSION */
181 # define TARGET(c) ((c)->pix_buf ? (c)->pix_buf : (c)->win)
182 #endif /* !HAVE_DOUBLE_BUFFER_EXTENSION */
184 static void inter_init(Display* dpy, Window win, struct inter_context* c)
186 XWindowAttributes xgwa;
187 double H[3], S[3], V[3];
192 unsigned long valmask = 0;
193 Bool dbuf = get_boolean_resource (dpy, "doubleBuffer", "Boolean");
195 # ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
199 memset (c, 0, sizeof(*c));
204 c->delay = get_integer_resource(dpy, "delay", "Integer");
207 XGetWindowAttributes(c->dpy, c->win, &xgwa);
210 c->cmap = xgwa.colormap;
212 #ifdef HAVE_XSHM_EXTENSION
213 c->use_shm = get_boolean_resource(dpy, "useSHM", "Boolean");
214 #endif /* HAVE_XSHM_EXTENSION */
218 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
219 c->back_buf = xdbe_get_backbuffer (c->dpy, c->win, XdbeUndefined);
220 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
222 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
224 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
225 c->pix_buf = XCreatePixmap (dpy, win, xgwa.width, xgwa.height,
229 val.function = GXcopy;
230 c->copy_gc = XCreateGC(c->dpy, TARGET(c), GCFunction, &val);
232 c->count = get_integer_resource(dpy, "count", "Integer");
235 c->grid_size = get_integer_resource(dpy, "gridsize", "Integer");
238 mono = get_boolean_resource(dpy, "mono", "Boolean");
240 c->colors = get_integer_resource(dpy, "ncolors", "Integer");
244 c->hue = get_integer_resource(dpy, "hue", "Float");
245 while (c->hue < 0 || c->hue >= 360)
246 c->hue = frand(360.0);
247 c->speed = get_integer_resource(dpy, "speed", "Integer");
248 c->shift = get_float_resource(dpy, "color-shift", "Float");
249 while(c->shift >= 360.0)
251 while(c->shift <= -360.0)
253 c->radius = get_integer_resource(dpy, "radius", "Integer");;
261 # ifdef HAVE_XSHM_EXTENSION
264 c->ximage = create_xshm_image(dpy, xgwa.visual, xgwa.depth,
265 ZPixmap, 0, &c->shm_info,
266 xgwa.width, c->grid_size);
270 # endif /* HAVE_XSHM_EXTENSION */
275 XCreateImage (dpy, xgwa.visual,
276 xgwa.depth, ZPixmap, 0, 0, /* depth, fmt, offset, data */
277 xgwa.width, c->grid_size, /* width, height */
278 8, 0); /* pad, bpl */
279 c->ximage->data = (unsigned char *)
280 calloc(c->ximage->height, c->ximage->bytes_per_line);
282 #endif /* USE_XIMAGE */
285 c->pal = calloc(c->colors, sizeof(XColor));
287 gray = get_boolean_resource(dpy, "gray", "Boolean");
290 H[1] = H[0] + c->shift < 360.0 ? H[0]+c->shift : H[0] + c->shift-360.0;
291 H[2] = H[1] + c->shift < 360.0 ? H[1]+c->shift : H[1] + c->shift-360.0;
292 S[0] = S[1] = S[2] = 1.0;
293 V[0] = V[1] = V[2] = 1.0;
295 H[0] = H[1] = H[2] = 0.0;
296 S[0] = S[1] = S[2] = 0.0;
297 V[0] = 1.0; V[1] = 0.5; V[2] = 0.0;
300 make_color_loop(c->dpy, c->cmap,
304 c->pal, &(c->colors),
306 if(c->colors < 2) { /* color allocation failure */
312 if(mono) { /* DON'T else this with the previous if! */
314 c->pal = calloc(2, sizeof(XColor));
315 c->pal[0].pixel = BlackPixel(c->dpy, DefaultScreen(c->dpy));
316 c->pal[1].pixel = WhitePixel(c->dpy, DefaultScreen(c->dpy));
319 valmask = GCForeground;
320 c->gcs = calloc(c->colors, sizeof(GC));
321 for(i = 0; i < c->colors; i++) {
322 val.foreground = c->pal[i].pixel;
323 c->gcs[i] = XCreateGC(c->dpy, TARGET(c), valmask, &val);
326 c->wave_height = calloc(c->radius, sizeof(int));
327 for(i = 0; i < c->radius; i++) {
330 ((float)c->radius - (float)i) /
334 ((max + max*cos((double)i/50.0)) / 2.0);
337 c->source = calloc(c->count, sizeof(struct inter_source));
338 for(i = 0; i < c->count; i++) {
339 c->source[i].x_theta = frand(2.0)*3.14159;
340 c->source[i].y_theta = frand(2.0)*3.14159;
345 #define source_x(c, i) \
346 (c->w/2 + ((int)(cos(c->source[i].x_theta)*((float)c->w/2.0))))
347 #define source_y(c, i) \
348 (c->h/2 + ((int)(cos(c->source[i].y_theta)*((float)c->h/2.0))))
351 * this is rather suboptimal. the sqrt() doesn't seem to be a big
352 * performance hit, but all those separate XFillRectangle()'s are.
353 * hell, it's just a quick hack anyway -- if someone wishes to tune
357 static void do_inter(struct inter_context* c)
366 for(i = 0; i < c->count; i++) {
367 c->source[i].x_theta += (c->speed/1000.0);
368 if(c->source[i].x_theta > 2.0*3.14159)
369 c->source[i].x_theta -= 2.0*3.14159;
370 c->source[i].y_theta += (c->speed/1000.0);
371 if(c->source[i].y_theta > 2.0*3.14159)
372 c->source[i].y_theta -= 2.0*3.14159;
373 c->source[i].x = source_x(c, i);
374 c->source[i].y = source_y(c, i);
379 for(j = 0; j < c->h/g; j++) {
380 for(i = 0; i < c->w/g; i++) {
382 for(k = 0; k < c->count; k++) {
383 dx = i*g + g/2 - c->source[k].x;
384 dy = j*g + g/2 - c->source[k].y;
385 dist = sqrt(dx*dx + dy*dy); /* what's the performance penalty here? */
386 result += (dist >= c->radius ? 0 : c->wave_height[dist]);
391 /* Fill in these `gridsize' horizontal bits in the scanline */
392 for(k = 0; k < g; k++)
393 XPutPixel(c->ximage, (g*i)+k, 0, c->pal[result].pixel);
395 #else /* !USE_XIMAGE */
396 XFillRectangle(c->dpy, TARGET(c), c->gcs[result], g*i, g*j, g, g);
397 #endif /* !USE_XIMAGE */
402 /* Only the first scanline of the image has been filled in; clone that
403 scanline to the rest of the `gridsize' lines in the ximage */
404 for(k = 0; k < (g-1); k++)
405 memcpy(c->ximage->data + (c->ximage->bytes_per_line * (k + 1)),
406 c->ximage->data + (c->ximage->bytes_per_line * k),
407 c->ximage->bytes_per_line);
409 /* Move the bits for this horizontal stripe to the server. */
410 # ifdef HAVE_XSHM_EXTENSION
412 XShmPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage,
413 0, 0, 0, g*j, c->ximage->width, c->ximage->height,
416 # endif /* HAVE_XSHM_EXTENSION */
417 XPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage,
418 0, 0, 0, g*j, c->ximage->width, c->ximage->height);
420 #endif /* USE_XIMAGE */
423 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
426 XdbeSwapInfo info[1];
427 info[0].swap_window = c->win;
428 info[0].swap_action = XdbeUndefined;
429 XdbeSwapBuffers(c->dpy, info, 1);
432 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
435 XCopyArea (c->dpy, c->pix_buf, c->win, c->copy_gc,
436 0, 0, c->w, c->h, 0, 0);
441 interference_init (Display *dpy, Window win)
443 struct inter_context *c = (struct inter_context *) calloc (1, sizeof(*c));
444 inter_init(dpy, win, c);
449 interference_draw (Display *dpy, Window win, void *closure)
451 struct inter_context *c = (struct inter_context *) closure;
457 interference_reshape (Display *dpy, Window window, void *closure,
458 unsigned int w, unsigned int h)
460 struct inter_context *c = (struct inter_context *) closure;
466 interference_event (Display *dpy, Window window, void *closure, XEvent *event)
472 interference_free (Display *dpy, Window window, void *closure)
476 XSCREENSAVER_MODULE ("Interference", interference)