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 | * |
---|
31 | * TODO: |
---|
32 | * |
---|
33 | * This really needs to be sped up. |
---|
34 | * |
---|
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.) |
---|
38 | * |
---|
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. |
---|
41 | * |
---|
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.) |
---|
46 | * |
---|
47 | * -- jwz, 4-Jun-98 |
---|
48 | */ |
---|
49 | |
---|
50 | #include <math.h> |
---|
51 | |
---|
52 | #include "screenhack.h" |
---|
53 | |
---|
54 | # include <X11/Xutil.h> |
---|
55 | |
---|
56 | /* I thought it would be faster this way, but it turns out not to be... -jwz */ |
---|
57 | #undef USE_XIMAGE |
---|
58 | |
---|
59 | #ifndef USE_XIMAGE |
---|
60 | # undef HAVE_XSHM_EXTENSION /* only applicable when using XImages */ |
---|
61 | #endif /* USE_XIMAGE */ |
---|
62 | |
---|
63 | |
---|
64 | #ifdef HAVE_DOUBLE_BUFFER_EXTENSION |
---|
65 | # include "xdbe.h" |
---|
66 | #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ |
---|
67 | |
---|
68 | #ifdef HAVE_XSHM_EXTENSION |
---|
69 | # include "xshm.h" |
---|
70 | #endif /* HAVE_XSHM_EXTENSION */ |
---|
71 | |
---|
72 | char *progclass="Interference"; |
---|
73 | |
---|
74 | char *defaults [] = { |
---|
75 | ".background: black", |
---|
76 | ".foreground: white", |
---|
77 | "*count: 3", /* number of waves */ |
---|
78 | "*gridsize: 4", /* pixel size, smaller values for better resolution */ |
---|
79 | "*ncolors: 128", /* number of colours used */ |
---|
80 | "*hue: 0", /* hue to use for base color (0-360) */ |
---|
81 | "*speed: 30", /* speed of wave origins moving around */ |
---|
82 | "*delay: 30000", /* or something */ |
---|
83 | "*color-shift: 60", /* h in hsv space, smaller values for smaller |
---|
84 | * color gradients */ |
---|
85 | "*radius: 800", /* wave extent */ |
---|
86 | "*gray: false", /* color or grayscale */ |
---|
87 | "*mono: false", /* monochrome, not very much fun */ |
---|
88 | |
---|
89 | "*doubleBuffer: True", |
---|
90 | #ifdef HAVE_DOUBLE_BUFFER_EXTENSION |
---|
91 | "*useDBE: True", /* use double buffering extension */ |
---|
92 | #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ |
---|
93 | |
---|
94 | #ifdef HAVE_XSHM_EXTENSION |
---|
95 | "*useSHM: True", /* use shared memory extension */ |
---|
96 | #endif /* HAVE_XSHM_EXTENSION */ |
---|
97 | 0 |
---|
98 | }; |
---|
99 | |
---|
100 | XrmOptionDescRec options [] = { |
---|
101 | { "-count", ".count", XrmoptionSepArg, 0 }, |
---|
102 | { "-ncolors", ".ncolors", XrmoptionSepArg, 0 }, |
---|
103 | { "-gridsize", ".gridsize", XrmoptionSepArg, 0 }, |
---|
104 | { "-hue", ".hue", XrmoptionSepArg, 0 }, |
---|
105 | { "-speed", ".speed", XrmoptionSepArg, 0 }, |
---|
106 | { "-delay", ".delay", XrmoptionSepArg, 0 }, |
---|
107 | { "-color-shift", ".color-shift", XrmoptionSepArg, 0 }, |
---|
108 | { "-radius", ".radius", XrmoptionSepArg, 0 }, |
---|
109 | { "-gray", ".gray", XrmoptionNoArg, "True" }, |
---|
110 | { "-mono", ".mono", XrmoptionNoArg, "True" }, |
---|
111 | { "-db", ".doubleBuffer", XrmoptionNoArg, "True" }, |
---|
112 | { "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" }, |
---|
113 | #ifdef HAVE_XSHM_EXTENSION |
---|
114 | { "-shm", ".useSHM", XrmoptionNoArg, "True" }, |
---|
115 | { "-no-shm", ".useSHM", XrmoptionNoArg, "False" }, |
---|
116 | #endif /* HAVE_XSHM_EXTENSION */ |
---|
117 | { 0, 0, 0, 0 } |
---|
118 | }; |
---|
119 | |
---|
120 | int options_size = (sizeof (options) / sizeof (XrmOptionDescRec)); |
---|
121 | |
---|
122 | struct inter_source { |
---|
123 | int x; |
---|
124 | int y; |
---|
125 | double x_theta; |
---|
126 | double y_theta; |
---|
127 | }; |
---|
128 | |
---|
129 | struct inter_context { |
---|
130 | /* |
---|
131 | * Display-related entries |
---|
132 | */ |
---|
133 | Display* dpy; |
---|
134 | Window win; |
---|
135 | |
---|
136 | #ifdef HAVE_DOUBLE_BUFFER_EXTENSION |
---|
137 | XdbeBackBuffer back_buf; |
---|
138 | #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ |
---|
139 | Pixmap pix_buf; |
---|
140 | |
---|
141 | GC copy_gc; |
---|
142 | #ifdef USE_XIMAGE |
---|
143 | XImage *ximage; |
---|
144 | #endif /* USE_XIMAGE */ |
---|
145 | |
---|
146 | #ifdef HAVE_XSHM_EXTENSION |
---|
147 | Bool use_shm; |
---|
148 | XShmSegmentInfo shm_info; |
---|
149 | #endif /* HAVE_XSHM_EXTENSION */ |
---|
150 | |
---|
151 | /* |
---|
152 | * Resources |
---|
153 | */ |
---|
154 | int count; |
---|
155 | int grid_size; |
---|
156 | int colors; |
---|
157 | float hue; |
---|
158 | int speed; |
---|
159 | int delay; |
---|
160 | int shift; |
---|
161 | int radius; |
---|
162 | |
---|
163 | /* |
---|
164 | * Drawing-related entries |
---|
165 | */ |
---|
166 | int w; |
---|
167 | int h; |
---|
168 | Colormap cmap; |
---|
169 | XColor* pal; |
---|
170 | GC* gcs; |
---|
171 | |
---|
172 | /* |
---|
173 | * lookup tables |
---|
174 | */ |
---|
175 | int* wave_height; |
---|
176 | |
---|
177 | /* |
---|
178 | * Interference sources |
---|
179 | */ |
---|
180 | struct inter_source* source; |
---|
181 | }; |
---|
182 | |
---|
183 | #ifdef HAVE_DOUBLE_BUFFER_EXTENSION |
---|
184 | # define TARGET(c) ((c)->back_buf ? (c)->back_buf : \ |
---|
185 | (c)->pix_buf ? (c)->pix_buf : (c)->win) |
---|
186 | #else /* HAVE_DOUBLE_BUFFER_EXTENSION */ |
---|
187 | # define TARGET(c) ((c)->pix_buf ? (c)->pix_buf : (c)->win) |
---|
188 | #endif /* !HAVE_DOUBLE_BUFFER_EXTENSION */ |
---|
189 | |
---|
190 | void inter_init(Display* dpy, Window win, struct inter_context* c) |
---|
191 | { |
---|
192 | XWindowAttributes xgwa; |
---|
193 | double H[3], S[3], V[3]; |
---|
194 | int i; |
---|
195 | int mono; |
---|
196 | int gray; |
---|
197 | XGCValues val; |
---|
198 | unsigned long valmask = 0; |
---|
199 | Bool dbuf = get_boolean_resource ("doubleBuffer", "Boolean"); |
---|
200 | |
---|
201 | memset (c, 0, sizeof(*c)); |
---|
202 | |
---|
203 | c->dpy = dpy; |
---|
204 | c->win = win; |
---|
205 | |
---|
206 | XGetWindowAttributes(c->dpy, c->win, &xgwa); |
---|
207 | c->w = xgwa.width; |
---|
208 | c->h = xgwa.height; |
---|
209 | c->cmap = xgwa.colormap; |
---|
210 | |
---|
211 | #ifdef HAVE_XSHM_EXTENSION |
---|
212 | c->use_shm = get_boolean_resource("useSHM", "Boolean"); |
---|
213 | #endif /* HAVE_XSHM_EXTENSION */ |
---|
214 | |
---|
215 | if (dbuf) |
---|
216 | { |
---|
217 | #ifdef HAVE_DOUBLE_BUFFER_EXTENSION |
---|
218 | c->back_buf = xdbe_get_backbuffer (c->dpy, c->win, XdbeUndefined); |
---|
219 | #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ |
---|
220 | |
---|
221 | #ifdef HAVE_DOUBLE_BUFFER_EXTENSION |
---|
222 | if (!c->back_buf) |
---|
223 | #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ |
---|
224 | c->pix_buf = XCreatePixmap (dpy, win, xgwa.width, xgwa.height, |
---|
225 | xgwa.depth); |
---|
226 | } |
---|
227 | |
---|
228 | val.function = GXcopy; |
---|
229 | c->copy_gc = XCreateGC(c->dpy, TARGET(c), GCFunction, &val); |
---|
230 | |
---|
231 | c->count = get_integer_resource("count", "Integer"); |
---|
232 | if(c->count < 1) |
---|
233 | c->count = 1; |
---|
234 | c->grid_size = get_integer_resource("gridsize", "Integer"); |
---|
235 | if(c->grid_size < 1) |
---|
236 | c->grid_size = 1; |
---|
237 | mono = get_boolean_resource("mono", "Boolean"); |
---|
238 | if(!mono) { |
---|
239 | c->colors = get_integer_resource("ncolors", "Integer"); |
---|
240 | if(c->colors < 2) |
---|
241 | c->colors = 2; |
---|
242 | } |
---|
243 | c->hue = get_integer_resource("hue", "Float"); |
---|
244 | while (c->hue < 0 || c->hue >= 360) |
---|
245 | c->hue = frand(360.0); |
---|
246 | c->speed = get_integer_resource("speed", "Integer"); |
---|
247 | c->shift = get_float_resource("color-shift", "Float"); |
---|
248 | while(c->shift >= 360.0) |
---|
249 | c->shift -= 360.0; |
---|
250 | while(c->shift <= -360.0) |
---|
251 | c->shift += 360.0; |
---|
252 | c->radius = get_integer_resource("radius", "Integer");; |
---|
253 | if(c->radius < 1) |
---|
254 | c->radius = 1; |
---|
255 | |
---|
256 | #ifdef USE_XIMAGE |
---|
257 | |
---|
258 | c->ximage = 0; |
---|
259 | |
---|
260 | # ifdef HAVE_XSHM_EXTENSION |
---|
261 | if (c->use_shm) |
---|
262 | { |
---|
263 | c->ximage = create_xshm_image(dpy, xgwa.visual, xgwa.depth, |
---|
264 | ZPixmap, 0, &c->shm_info, |
---|
265 | xgwa.width, c->grid_size); |
---|
266 | if (!c->ximage) |
---|
267 | c->use_shm = False; |
---|
268 | } |
---|
269 | # endif /* HAVE_XSHM_EXTENSION */ |
---|
270 | |
---|
271 | if (!c->ximage) |
---|
272 | { |
---|
273 | c->ximage = |
---|
274 | XCreateImage (dpy, xgwa.visual, |
---|
275 | xgwa.depth, ZPixmap, 0, 0, /* depth, fmt, offset, data */ |
---|
276 | xgwa.width, c->grid_size, /* width, height */ |
---|
277 | 8, 0); /* pad, bpl */ |
---|
278 | c->ximage->data = (unsigned char *) |
---|
279 | calloc(c->ximage->height, c->ximage->bytes_per_line); |
---|
280 | } |
---|
281 | #endif /* USE_XIMAGE */ |
---|
282 | |
---|
283 | if(!mono) { |
---|
284 | c->pal = calloc(c->colors, sizeof(XColor)); |
---|
285 | |
---|
286 | gray = get_boolean_resource("gray", "Boolean"); |
---|
287 | if(!gray) { |
---|
288 | H[0] = c->hue; |
---|
289 | H[1] = H[0] + c->shift < 360.0 ? H[0]+c->shift : H[0] + c->shift-360.0; |
---|
290 | H[2] = H[1] + c->shift < 360.0 ? H[1]+c->shift : H[1] + c->shift-360.0; |
---|
291 | S[0] = S[1] = S[2] = 1.0; |
---|
292 | V[0] = V[1] = V[2] = 1.0; |
---|
293 | } else { |
---|
294 | H[0] = H[1] = H[2] = 0.0; |
---|
295 | S[0] = S[1] = S[2] = 0.0; |
---|
296 | V[0] = 1.0; V[1] = 0.5; V[2] = 0.0; |
---|
297 | } |
---|
298 | |
---|
299 | make_color_loop(c->dpy, c->cmap, |
---|
300 | H[0], S[0], V[0], |
---|
301 | H[1], S[1], V[1], |
---|
302 | H[2], S[2], V[2], |
---|
303 | c->pal, &(c->colors), |
---|
304 | True, False); |
---|
305 | if(c->colors < 2) { /* color allocation failure */ |
---|
306 | mono = 1; |
---|
307 | free(c->pal); |
---|
308 | } |
---|
309 | } |
---|
310 | |
---|
311 | if(mono) { /* DON'T else this with the previous if! */ |
---|
312 | c->colors = 2; |
---|
313 | c->pal = calloc(2, sizeof(XColor)); |
---|
314 | c->pal[0].pixel = BlackPixel(c->dpy, DefaultScreen(c->dpy)); |
---|
315 | c->pal[1].pixel = WhitePixel(c->dpy, DefaultScreen(c->dpy)); |
---|
316 | } |
---|
317 | |
---|
318 | valmask = GCForeground; |
---|
319 | c->gcs = calloc(c->colors, sizeof(GC)); |
---|
320 | for(i = 0; i < c->colors; i++) { |
---|
321 | val.foreground = c->pal[i].pixel; |
---|
322 | c->gcs[i] = XCreateGC(c->dpy, TARGET(c), valmask, &val); |
---|
323 | } |
---|
324 | |
---|
325 | c->wave_height = calloc(c->radius, sizeof(int)); |
---|
326 | for(i = 0; i < c->radius; i++) { |
---|
327 | float max = |
---|
328 | ((float)c->colors) * |
---|
329 | ((float)c->radius - (float)i) / |
---|
330 | ((float)c->radius); |
---|
331 | c->wave_height[i] = |
---|
332 | (int) |
---|
333 | ((max + max*cos((double)i/50.0)) / 2.0); |
---|
334 | } |
---|
335 | |
---|
336 | c->source = calloc(c->count, sizeof(struct inter_source)); |
---|
337 | for(i = 0; i < c->count; i++) { |
---|
338 | c->source[i].x_theta = frand(2.0)*3.14159; |
---|
339 | c->source[i].y_theta = frand(2.0)*3.14159; |
---|
340 | } |
---|
341 | |
---|
342 | } |
---|
343 | |
---|
344 | #define source_x(c, i) \ |
---|
345 | (c->w/2 + ((int)(cos(c->source[i].x_theta)*((float)c->w/2.0)))) |
---|
346 | #define source_y(c, i) \ |
---|
347 | (c->h/2 + ((int)(cos(c->source[i].y_theta)*((float)c->h/2.0)))) |
---|
348 | |
---|
349 | /* |
---|
350 | * this is rather suboptimal. the sqrt() doesn't seem to be a big |
---|
351 | * performance hit, but all those separate XFillRectangle()'s are. |
---|
352 | * hell, it's just a quick hack anyway -- if someone wishes to tune |
---|
353 | * it, go ahead! |
---|
354 | */ |
---|
355 | |
---|
356 | void do_inter(struct inter_context* c) |
---|
357 | { |
---|
358 | int i, j, k; |
---|
359 | int result; |
---|
360 | int dist; |
---|
361 | int g; |
---|
362 | |
---|
363 | int dx, dy; |
---|
364 | |
---|
365 | for(i = 0; i < c->count; i++) { |
---|
366 | c->source[i].x_theta += (c->speed/1000.0); |
---|
367 | if(c->source[i].x_theta > 2.0*3.14159) |
---|
368 | c->source[i].x_theta -= 2.0*3.14159; |
---|
369 | c->source[i].y_theta += (c->speed/1000.0); |
---|
370 | if(c->source[i].y_theta > 2.0*3.14159) |
---|
371 | c->source[i].y_theta -= 2.0*3.14159; |
---|
372 | c->source[i].x = source_x(c, i); |
---|
373 | c->source[i].y = source_y(c, i); |
---|
374 | } |
---|
375 | |
---|
376 | g = c->grid_size; |
---|
377 | |
---|
378 | for(j = 0; j < c->h/g; j++) { |
---|
379 | for(i = 0; i < c->w/g; i++) { |
---|
380 | result = 0; |
---|
381 | for(k = 0; k < c->count; k++) { |
---|
382 | dx = i*g + g/2 - c->source[k].x; |
---|
383 | dy = j*g + g/2 - c->source[k].y; |
---|
384 | dist = sqrt(dx*dx + dy*dy); /* what's the performance penalty here? */ |
---|
385 | result += (dist > c->radius ? 0 : c->wave_height[dist]); |
---|
386 | } |
---|
387 | result %= c->colors; |
---|
388 | |
---|
389 | #ifdef USE_XIMAGE |
---|
390 | /* Fill in these `gridsize' horizontal bits in the scanline */ |
---|
391 | for(k = 0; k < g; k++) |
---|
392 | XPutPixel(c->ximage, (g*i)+k, 0, c->pal[result].pixel); |
---|
393 | |
---|
394 | #else /* !USE_XIMAGE */ |
---|
395 | XFillRectangle(c->dpy, TARGET(c), c->gcs[result], g*i, g*j, g, g); |
---|
396 | #endif /* !USE_XIMAGE */ |
---|
397 | } |
---|
398 | |
---|
399 | #ifdef USE_XIMAGE |
---|
400 | |
---|
401 | /* Only the first scanline of the image has been filled in; clone that |
---|
402 | scanline to the rest of the `gridsize' lines in the ximage */ |
---|
403 | for(k = 0; k < (g-1); k++) |
---|
404 | memcpy(c->ximage->data + (c->ximage->bytes_per_line * (k + 1)), |
---|
405 | c->ximage->data + (c->ximage->bytes_per_line * k), |
---|
406 | c->ximage->bytes_per_line); |
---|
407 | |
---|
408 | /* Move the bits for this horizontal stripe to the server. */ |
---|
409 | # ifdef HAVE_XSHM_EXTENSION |
---|
410 | if (c->use_shm) |
---|
411 | XShmPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage, |
---|
412 | 0, 0, 0, g*j, c->ximage->width, c->ximage->height, |
---|
413 | False); |
---|
414 | else |
---|
415 | # endif /* HAVE_XSHM_EXTENSION */ |
---|
416 | XPutImage(c->dpy, TARGET(c), c->copy_gc, c->ximage, |
---|
417 | 0, 0, 0, g*j, c->ximage->width, c->ximage->height); |
---|
418 | |
---|
419 | #endif /* USE_XIMAGE */ |
---|
420 | } |
---|
421 | |
---|
422 | #ifdef HAVE_DOUBLE_BUFFER_EXTENSION |
---|
423 | if (c->back_buf) |
---|
424 | { |
---|
425 | XdbeSwapInfo info[1]; |
---|
426 | info[0].swap_window = c->win; |
---|
427 | info[0].swap_action = XdbeUndefined; |
---|
428 | XdbeSwapBuffers(c->dpy, info, 1); |
---|
429 | } |
---|
430 | else |
---|
431 | #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */ |
---|
432 | if (c->pix_buf) |
---|
433 | { |
---|
434 | XCopyArea (c->dpy, c->pix_buf, c->win, c->copy_gc, |
---|
435 | 0, 0, c->w, c->h, 0, 0); |
---|
436 | } |
---|
437 | |
---|
438 | XSync(c->dpy, False); |
---|
439 | } |
---|
440 | |
---|
441 | void screenhack(Display *dpy, Window win) |
---|
442 | { |
---|
443 | struct inter_context c; |
---|
444 | int delay; |
---|
445 | |
---|
446 | delay = get_integer_resource("delay", "Integer"); |
---|
447 | |
---|
448 | inter_init(dpy, win, &c); |
---|
449 | while(1) { |
---|
450 | do_inter(&c); |
---|
451 | screenhack_handle_events (dpy); |
---|
452 | if(delay) usleep(delay); |
---|
453 | } |
---|
454 | } |
---|