http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.00.tar.gz
[xscreensaver] / hacks / distort.c
1 /* -*- mode: C; tab-width: 4 -*-
2  * xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997, 1998
3  * Jamie Zawinski <jwz@jwz.org>
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation.  No representations are made about the suitability of this
10  * software for any purpose.  It is provided "as is" without express or 
11  * implied warranty.
12  */
13
14 /* distort
15  * by Jonas Munsin (jmunsin@iki.fi) and Jamie Zawinski <jwz@jwz.org>
16  * TODO:
17  *      -check the allocations in init_round_lense again, maybe make it possible again
18  *       to use swamp without pre-allocating/calculating (although that
19  *       makes it slower) - -swamp is memory hungry
20  *      -more distortion matrices (fortunately, I'm out of ideas :)
21  * Stuff that would be cool but probably too much of a resource hog:
22  *      -some kind of interpolation to avoid jaggies
23  *    -large speed values leaves the image distorted
24  * program idea borrowed from a screensaver on a non-*NIX OS,
25  *
26  * 28 Sep 1999 Jonas Munsin (jmunsin@iki.fi)
27  *    Added about 10x faster algortim for 8, 16 and 32 bpp (modifies pixels
28  *    directly avoiding costly XPutPixle(XGetPixel()) calls, inspired by
29  *    xwhirl made by horvai@clipper.ens.fr (Peter Horvai) and the XFree86
30  *    Xlib sources.
31  *    This piece of code is really horrible, but it works, and at the moment
32  *    I don't have time or inspiration to fix something that works (knock
33  *    on wood).
34  * 08 Oct 1999 Jonas Munsin (jmunsin@iki.fi)
35  *      Corrected several bugs causing references beyond allocated memory.
36  */
37
38 #include <math.h>
39 #include "screenhack.h"
40 #include <X11/Xutil.h>
41 #include <X11/Xmd.h>
42
43 #ifdef HAVE_XSHM_EXTENSION
44 # include "xshm.h"
45 static Bool use_shm = False;
46 static XShmSegmentInfo shm_info;
47 #endif /* HAVE_XSHM_EXTENSION */
48
49 struct coo {
50         int x;
51         int y;
52         int r, r_change;
53         int xmove, ymove;
54 };
55 static struct coo xy_coo[10];
56
57 static int delay, radius, speed, number, blackhole, vortex, magnify, reflect, slow;
58 static XWindowAttributes xgwa;
59 static GC gc;
60 static Window g_window;
61 static Display *g_dpy;
62 static unsigned long black_pixel;
63
64 static XImage *orig_map, *buffer_map;
65 static unsigned long *buffer_map_cache;
66
67 static int ***from;
68 static int ****from_array;
69 static int *fast_from = NULL;
70 static void (*effect) (int) = NULL;
71 static void move_lense(int);
72 static void swamp_thing(int);
73 static void new_rnd_coo(int);
74 static void init_round_lense(void);
75 static void (*draw) (int) = NULL;
76 static void reflect_draw(int);
77 static void plain_draw(int);
78
79 static void (*draw_routine)(XImage *, XImage *, int, int, int *) = NULL;
80 static void fast_draw_8(XImage *, XImage *, int, int, int *);
81 static void fast_draw_16(XImage *, XImage *, int, int, int *);
82 static void fast_draw_32(XImage *, XImage *, int, int, int *);
83 static void generic_draw(XImage *, XImage *, int, int, int *);
84 static int bpp_size = 0;
85
86
87 static void init_distort(Display *dpy, Window window) 
88 {
89         XGCValues gcv;
90         long gcflags;
91         int i;
92
93         g_window=window;
94         g_dpy=dpy;
95
96         delay = get_integer_resource("delay", "Integer");
97         radius = get_integer_resource("radius", "Integer");
98         speed = get_integer_resource("speed", "Integer");
99         number = get_integer_resource("number", "Integer");
100
101 #ifdef HAVE_XSHM_EXTENSION
102         use_shm = get_boolean_resource("useSHM", "Boolean");
103 #endif /* HAVE_XSHM_EXTENSION */
104         
105         blackhole = get_boolean_resource("blackhole", "Boolean");
106         vortex = get_boolean_resource("vortex", "Boolean");
107         magnify = get_boolean_resource("magnify", "Boolean");
108         reflect = get_boolean_resource("reflect", "Boolean");
109         slow = get_boolean_resource("slow", "Boolean");
110         
111         if (get_boolean_resource("swamp", "Boolean"))
112                 effect = &swamp_thing;
113         if (get_boolean_resource("bounce", "Boolean"))
114                 effect = &move_lense;
115
116         XGetWindowAttributes (dpy, window, &xgwa);
117
118         if (effect == NULL && radius == 0 && speed == 0 && number == 0
119                 && !blackhole && !vortex && !magnify && !reflect) {
120 /* if no cmdline options are given, randomly choose one of:
121  * -radius 50 -number 4 -speed 1 -bounce
122  * -radius 50 -number 4 -speed 1 -blackhole
123  * -radius 50 -number 4 -speed 1 -vortex
124  * -radius 50 -number 4 -speed 1 -vortex -magnify
125  * -radius 50 -number 4 -speed 1 -vortex -magnify -blackhole
126  * -radius 100 -number 1 -speed 2 -bounce
127  * -radius 100 -number 1 -speed 2 -blackhole
128  * -radius 100 -number 1 -speed 2 -vortex
129  * -radius 100 -number 1 -speed 2 -vortex -magnify
130  * -radius 100 -number 1 -speed 2 -vortex -magnify -blackhole
131  * -radius 80 -number 1 -speed 2 -reflect
132  * -radius 50 -number 3 -speed 2 -reflect
133  * jwz: not these
134  *   -radius 50 -number 4 -speed 2 -swamp
135  *   -radius 50 -number 4 -speed 2 -swamp -blackhole
136  *   -radius 50 -number 4 -speed 2 -swamp -vortex
137  *   -radius 50 -number 4 -speed 2 -swamp -vortex -magnify
138  *   -radius 50 -number 4 -speed 2 -swamp -vortex -magnify -blackhole
139  */
140                 
141                 i = (random() % 12 /* 17 */);
142
143                 draw = &plain_draw;
144
145                 switch (i) {
146                         case 0:
147                                 radius=50;number=4;speed=1;
148                                 effect=&move_lense;break;
149                         case 1:
150                                 radius=50;number=4;speed=1;blackhole=1;
151                                 effect=&move_lense;break;
152                         case 2:
153                                 radius=50;number=4;speed=1;vortex=1;
154                                 effect=&move_lense;break;
155                         case 3:
156                                 radius=50;number=4;speed=1;vortex=1;magnify=1;
157                                 effect=&move_lense;break;
158                         case 4:
159                                 radius=50;number=4;speed=1;vortex=1;magnify=1;blackhole=1;
160                                 effect=&move_lense;break;
161                         case 5:
162                                 radius=100;number=1;speed=2;
163                                 effect=&move_lense;break;
164                         case 6:
165                                 radius=100;number=1;speed=2;blackhole=1;
166                                 effect=&move_lense;break;
167                         case 7:
168                                 radius=100;number=1;speed=2;vortex=1;
169                                 effect=&move_lense;break;
170                         case 8:
171                                 radius=100;number=1;speed=2;vortex=1;magnify=1;
172                                 effect=&move_lense;break;
173                         case 9:
174                                 radius=100;number=1;speed=2;vortex=1;magnify=1;blackhole=1;
175                                 effect=&move_lense;break;
176
177                         case 10:
178                                 radius=80;number=1;speed=2;reflect=1;
179                                 draw = &reflect_draw;effect = &move_lense;break;
180                         case 11:
181                                 radius=50;number=4;speed=2;reflect=1;
182                                 draw = &reflect_draw;effect = &move_lense;break;
183
184 #if 0 /* jwz: not these */
185                         case 12:
186                                 radius=50;number=4;speed=2;
187                                 effect=&swamp_thing;break;
188                         case 13:
189                                 radius=50;number=4;speed=2;blackhole=1;
190                                 effect=&swamp_thing;break;
191                         case 14:
192                                 radius=50;number=4;speed=2;vortex=1;
193                                 effect=&swamp_thing;break;
194                         case 15:
195                                 radius=50;number=4;speed=2;vortex=1;magnify=1;
196                                 effect=&swamp_thing;break;
197                         case 16:
198                                 radius=50;number=4;speed=2;vortex=1;magnify=1;blackhole=1;
199                                 effect=&swamp_thing;break;
200 #endif
201
202             default:
203                 abort(); break;
204                 }
205
206         /* but if the window is small, reduce default radius */
207         if (xgwa.width < radius * 8)
208           radius = xgwa.width/8;
209         }
210
211         if (delay < 0)
212                 delay = 0;
213         if (radius <= 0)
214                 radius = 60;
215         if (speed <= 0) 
216                 speed = 2;
217         if (number <= 0)
218                 number=1;
219         if (number >= 10)
220                 number=1;
221         if (effect == NULL)
222                 effect = &move_lense;
223         if (reflect) {
224                 draw = &reflect_draw;
225                 effect = &move_lense;
226         }
227         if (draw == NULL)
228                 draw = &plain_draw;
229
230         black_pixel = BlackPixelOfScreen( xgwa.screen );
231
232         gcv.function = GXcopy;
233         gcv.subwindow_mode = IncludeInferiors;
234         gcflags = GCForeground |GCFunction;
235         if (use_subwindow_mode_p(xgwa.screen, window)) /* see grabscreen.c */
236                 gcflags |= GCSubwindowMode;
237         gc = XCreateGC (dpy, window, gcflags, &gcv);
238
239         grab_screen_image (xgwa.screen, window);
240
241         buffer_map = 0;
242         orig_map = XGetImage(dpy, window, 0, 0, xgwa.width, xgwa.height,
243                                                  ~0L, ZPixmap);
244         buffer_map_cache = malloc(sizeof(unsigned long)*(2*radius+speed+2)*(2*radius+speed+2));
245
246         if (buffer_map_cache == NULL) {
247                 perror("distort");
248                 exit(EXIT_FAILURE);
249         }
250
251 # ifdef HAVE_XSHM_EXTENSION
252
253         if (use_shm)
254           {
255                 buffer_map = create_xshm_image(dpy, xgwa.visual, orig_map->depth,
256                                                                            ZPixmap, 0, &shm_info,
257                                                                            2*radius + speed + 2,
258                                                                            2*radius + speed + 2);
259                 if (!buffer_map)
260                   use_shm = False;
261           }
262 # endif /* HAVE_XSHM_EXTENSION */
263
264         if (!buffer_map)
265           {
266                 buffer_map = XCreateImage(dpy, xgwa.visual,
267                                                                   orig_map->depth, ZPixmap, 0, 0,
268                                                                   2*radius + speed + 2, 2*radius + speed + 2,
269                                                                   8, 0);
270                 buffer_map->data = (char *)
271                   calloc(buffer_map->height, buffer_map->bytes_per_line);
272         }
273
274         if ((buffer_map->byte_order == orig_map->byte_order)
275                         && (buffer_map->depth == orig_map->depth)
276                         && (buffer_map->format == ZPixmap)
277                         && (orig_map->format == ZPixmap)
278                         && !slow) {
279                 switch (orig_map->bits_per_pixel) {
280                         case 32:
281                                 draw_routine = &fast_draw_32;
282                                 bpp_size = sizeof(CARD32);
283                                 break;
284                         case 16:
285                                 draw_routine = &fast_draw_16;
286                                 bpp_size = sizeof(CARD16);
287                                 break;
288                         case 8:
289                                 draw_routine = &fast_draw_8;
290                                 bpp_size = sizeof(CARD8);
291                                 break;
292                         default:
293                                 draw_routine = &generic_draw;
294                                 break;
295                 }
296         } else {
297                 draw_routine = &generic_draw;
298         }
299         init_round_lense();
300
301         for (i = 0; i < number; i++) {
302                 new_rnd_coo(i);
303                 if (number != 1)
304                         xy_coo[i].r = (i*radius)/(number-1); /* "randomize" initial */
305                 else
306                          xy_coo[i].r = 0;
307                 xy_coo[i].r_change = speed + (i%2)*2*(-speed);  /* values a bit */
308                 xy_coo[i].xmove = speed + (i%2)*2*(-speed);
309                 xy_coo[i].ymove = speed + (i%2)*2*(-speed);
310         }
311
312 }
313
314 /* example: initializes a "see-trough" matrix */
315 /* static void make_null_lense(void)
316 {
317         int i, j;
318         for (i = 0; i < 2*radius+speed+2; i++) {
319                 for (j = 0 ; j < 2*radius+speed+2 ; j++) {
320                         from[i][j][0]=i;
321                         from[i][j][1]=j;
322                 }
323         } 
324 }
325 */
326 static void convert(void) {
327         int *p;
328         int i, j;
329         fast_from = calloc(1, sizeof(int)*((buffer_map->bytes_per_line/bpp_size)*(2*radius+speed+2) + 2*radius+speed+2));
330         if (fast_from == NULL) {
331                 perror("distort");
332                 exit(EXIT_FAILURE);
333         }
334         p = fast_from;
335         for (i = 0; i < 2*radius+speed+2; i++) {
336                 for (j = 0; j < 2*radius+speed+2; j++) {
337                         *(p + i + j*buffer_map->bytes_per_line/bpp_size)
338                                 = from[i][j][0] + xgwa.width*from[i][j][1];
339                         if (*(p + i + j*buffer_map->bytes_per_line/bpp_size) < 0
340                                         || *(p + i + j*buffer_map->bytes_per_line/bpp_size) >= orig_map->height*orig_map->width) {
341                                 *(p + i + j*buffer_map->bytes_per_line/bpp_size) = 0;
342                         }
343                 }
344         }
345 }
346
347 /* makes a lense with the Radius=loop and centred in
348  * the point (radius, radius)
349  */
350 static void make_round_lense(int radius, int loop)
351 {
352         int i, j;
353
354         for (i = 0; i < 2*radius+speed+2; i++) {
355                 for(j = 0; j < ((0 == bpp_size) ? (2*radius+speed+2) : (buffer_map->bytes_per_line/bpp_size)); j++) {
356                         double r, d;
357                         r = sqrt ((i-radius)*(i-radius)+(j-radius)*(j-radius));
358                         if (loop == 0)
359                           d=0.0;
360                         else
361                           d=r/loop;
362
363                         if (r < loop-1) {
364
365                                 if (vortex) { /* vortex-twist effect */
366                                         double angle;
367                 /* this one-line formula for getting a nice rotation angle is borrowed
368                  * (with permission) from the whirl plugin for gimp,
369                  * Copyright (C) 1996 Federico Mena Quintero
370                  */
371                 /* 5 is just a constant used because it looks good :) */
372                                         angle = 5*(1-d)*(1-d);
373
374         /* Avoid atan2: DOMAIN error message */
375                                         if ((radius-j) == 0.0 && (radius-i) == 0.0) {
376                                                 from[i][j][0] = radius + cos(angle)*r;
377                                                 from[i][j][1] = radius + sin(angle)*r;
378                                         } else {
379                                                 from[i][j][0] = radius +
380                                                         cos(angle - atan2(radius-j, -(radius-i)))*r;
381                                                 from[i][j][1] = radius +
382                                                         sin(angle - atan2(radius-j, -(radius-i)))*r;
383                                         }
384                                         if (magnify) {
385                                                 r = sin(d*M_PI_2);
386                                                 if (blackhole && r != 0) /* blackhole effect */
387                                                         r = 1/r;
388                                                 from[i][j][0] = radius + (from[i][j][0]-radius)*r;
389                                                 from[i][j][1] = radius + (from[i][j][1]-radius)*r;
390                                         }
391                                 } else { /* default is to magnify */
392                                         r = sin(d*M_PI_2);
393                                 
394         /* raising r to different power here gives different amounts of
395          * distortion, a negative value sucks everything into a black hole
396          */
397                                 /*      r = r*r; */
398                                         if (blackhole && r != 0) /* blackhole effect */
399                                                 r = 1/r;
400                                                                         /* bubble effect (and blackhole) */
401                                         from[i][j][0] = radius + (i-radius)*r;
402                                         from[i][j][1] = radius + (j-radius)*r;
403                                 }
404                         } else { /* not inside loop */
405                                 from[i][j][0] = i;
406                                 from[i][j][1] = j;
407                         }
408                 }
409         }
410
411         /* this is really just a quick hack to keep both the compability mode with all depths and still
412          * allow the custom optimized draw routines with the minimum amount of work */
413         if (0 != bpp_size) {
414                 convert();
415         }
416 }
417
418 #ifndef EXIT_FAILURE
419 # define EXIT_FAILURE -1
420 #endif
421
422 static void allocate_lense(void)
423 {
424         int i, j;
425         int s = ((0 != bpp_size) ? (buffer_map->bytes_per_line/bpp_size) : (2*radius+speed+2));
426         /* maybe this should be redone so that from[][][] is in one block;
427          * then pointers could be used instead of arrays in some places (and
428          * maybe give a speedup - maybe also consume less memory)
429          */
430         from = (int ***)malloc(s*sizeof(int **));
431         if (from == NULL) {
432                 perror("distort");
433                 exit(EXIT_FAILURE);
434         }
435         for (i = 0; i < s; i++) {
436                 from[i] = (int **)malloc((2*radius+speed+2) * sizeof(int *));
437                 if (from[i] == NULL) {
438                         perror("distort");
439                         exit(EXIT_FAILURE);
440                 }
441                 for (j = 0; j < s; j++) {
442                         from[i][j] = (int *)malloc(2 * sizeof(int));
443                         if (from[i][j] == NULL) {
444                                 perror("distort");
445                                 exit(EXIT_FAILURE);
446                         }
447                 }
448         }
449 }
450
451 /* from_array in an array containing precalculated from matrices,
452  * this is a double faced mem vs speed trade, it's faster, but eats
453  * _a lot_ of mem for large radius (is there a bug here? I can't see it)
454  */
455 static void init_round_lense(void)
456 {
457         int k;
458
459         if (effect == &swamp_thing) {
460                 from_array = (int ****)malloc((radius+1)*sizeof(int ***));
461                 for (k=0; k <= radius; k++) {
462                         allocate_lense();
463                         make_round_lense(radius, k);
464                         from_array[k] = from;
465                 }
466         } else { /* just allocate one from[][][] */
467                 allocate_lense();
468                 make_round_lense(radius,radius);
469         }
470 }
471
472 /* If fast_draw_8, fast_draw_16 or fast_draw_32 are to be used, the following properties
473  * of the src and dest XImages must hold (otherwise the generic, slooow, method provided
474  * by X is to be used):
475  *      src->byte_order == dest->byte_order
476  *      src->format == ZPixmap && dest->format == ZPixmap
477  *      src->depth == dest->depth == the depth the function in question asumes
478  * x and y is the coordinates in src from where to cut out the image from,
479  * distort_matrix is a precalculated array of how to distort the matrix
480  */
481
482 static void fast_draw_8(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
483         CARD8 *u = (CARD8 *)dest->data;
484         CARD8 *t = (CARD8 *)src->data + x + y*src->bytes_per_line/sizeof(CARD8);
485
486         while (u < (CARD8 *)(dest->data + sizeof(CARD8)*dest->height
487                                 *dest->bytes_per_line/sizeof(CARD8))) {
488                 *u++ = t[*distort_matrix++];
489         }
490 }
491
492 static void fast_draw_16(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
493         CARD16 *u = (CARD16 *)dest->data;
494         CARD16 *t = (CARD16 *)src->data + x + y*src->bytes_per_line/sizeof(CARD16);
495
496         while (u < (CARD16 *)(dest->data + sizeof(CARD16)*dest->height
497                                 *dest->bytes_per_line/sizeof(CARD16))) {
498                 *u++ = t[*distort_matrix++];
499         }
500 }
501
502 static void fast_draw_32(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
503         CARD32 *u = (CARD32 *)dest->data;
504         CARD32 *t = (CARD32 *)src->data + x + y*src->bytes_per_line/sizeof(CARD32);
505
506         while (u < (CARD32 *)(dest->data + sizeof(CARD32)*dest->height
507                                 *dest->bytes_per_line/sizeof(CARD32))) {
508                 *u++ = t[*distort_matrix++];
509         }
510 }
511
512 static void generic_draw(XImage *src, XImage *dest, int x, int y, int *distort_matrix) {
513         int i, j;
514         for (i = 0; i < dest->width; i++)
515                 for (j = 0; j < dest->height; j++)
516                         if (from[i][j][0] + x >= 0 &&
517                                         from[i][j][0] + x < src->width &&
518                                         from[i][j][1] + y >= 0 &&
519                                         from[i][j][1] + y < src->height)
520                                 XPutPixel(dest, i, j,
521                                                 XGetPixel(src,
522                                                         from[i][j][0] + x,
523                                                         from[i][j][1] + y));
524 }
525
526 /* generate an XImage of from[][][] and draw it on the screen */
527 static void plain_draw(int k)
528 {
529         if (xy_coo[k].x+2*radius+speed+2 > orig_map->width ||
530                         xy_coo[k].y+2*radius+speed+2 > orig_map->height)
531                 return;
532
533         draw_routine(orig_map, buffer_map, xy_coo[k].x, xy_coo[k].y, fast_from);
534
535 # ifdef HAVE_XSHM_EXTENSION
536         if (use_shm)
537                 XShmPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
538                                 2*radius+speed+2, 2*radius+speed+2, False);
539         else
540
541         if (!use_shm)
542 # endif
543                 XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
544                                 2*radius+speed+2, 2*radius+speed+2);
545
546 }
547
548
549 /* generate an XImage from the reflect algoritm submitted by
550  * Randy Zack <randy@acucorp.com>
551  * draw really got too big and ugly so I split it up
552  * it should be possible to use the from[][] to speed it up
553  * (once I figure out the algorithm used :)
554  */
555 static void reflect_draw(int k)
556 {
557         int i, j;
558         int     cx, cy;
559         int     ly, lysq, lx, ny, dist, rsq = radius * radius;
560
561         cx = cy = radius;
562         if (xy_coo[k].ymove > 0)
563                 cy += speed;
564         if (xy_coo[k].xmove > 0)
565                 cx += speed;
566
567         for(i = 0 ; i < 2*radius+speed+2; i++) {
568                 ly = i - cy;
569                 lysq = ly * ly;
570                 ny = xy_coo[k].y + i;
571                 for(j = 0 ; j < 2*radius+speed+2 ; j++) {
572                         lx = j - cx;
573                         dist = lx * lx + lysq;
574                         if (dist > rsq ||
575                                 ly < -radius || ly > radius ||
576                                 lx < -radius || lx > radius)
577                                 XPutPixel( buffer_map, j, i,
578                                                    XGetPixel( orig_map, xy_coo[k].x + j, ny ));
579                         else if (dist == 0)
580                                 XPutPixel( buffer_map, j, i, black_pixel );
581                         else {
582                                 int     x = xy_coo[k].x + cx + (lx * rsq / dist);
583                                 int     y = xy_coo[k].y + cy + (ly * rsq / dist);
584                                 if (x < 0 || x >= xgwa.width ||
585                                         y < 0 || y >= xgwa.height)
586                                         XPutPixel( buffer_map, j, i, black_pixel );
587                                 else
588                                         XPutPixel( buffer_map, j, i,
589                                                            XGetPixel( orig_map, x, y ));
590                         }
591                 }
592         }
593
594         XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
595                         2*radius+speed+2, 2*radius+speed+2);
596 }
597
598 /* create a new, random coordinate, that won't interfer with any other
599  * coordinates, as the drawing routines would be significantly slowed
600  * down if they were to handle serveral layers of distortions
601  */
602 static void new_rnd_coo(int k)
603 {
604         int i;
605
606         xy_coo[k].x = (random() % (xgwa.width-2*radius));
607         xy_coo[k].y = (random() % (xgwa.height-2*radius));
608         
609         for (i = 0; i < number; i++) {
610                 if (i != k) {
611                         if ((abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius+speed+2)
612                          && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius+speed+2)) {
613                                 xy_coo[k].x = (random() % (xgwa.width-2*radius));
614                                 xy_coo[k].y = (random() % (xgwa.height-2*radius));
615                                 i=-1; /* ugly */
616                         } 
617                 }
618         }
619 }
620
621 /* move lens and handle bounces with walls and other lenses */
622 static void move_lense(int k)
623 {
624         int i;
625
626         if (xy_coo[k].x + 2*radius + speed + 2 >= xgwa.width)
627                 xy_coo[k].xmove = -abs(xy_coo[k].xmove);
628         if (xy_coo[k].x <= speed) 
629                 xy_coo[k].xmove = abs(xy_coo[k].xmove);
630         if (xy_coo[k].y + 2*radius + speed + 2 >= xgwa.height)
631                 xy_coo[k].ymove = -abs(xy_coo[k].ymove);
632         if (xy_coo[k].y <= speed)
633                 xy_coo[k].ymove = abs(xy_coo[k].ymove);
634
635         xy_coo[k].x = xy_coo[k].x + xy_coo[k].xmove;
636         xy_coo[k].y = xy_coo[k].y + xy_coo[k].ymove;
637
638         /* bounce against othe lenses */
639         for (i = 0; i < number; i++) {
640                 if ((i != k)
641                 
642 /* This commented test is for rectangular lenses (not currently used) and
643  * the one used is for circular ones
644                 && (abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius)
645                 && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius)) { */
646
647                 && ((xy_coo[k].x - xy_coo[i].x)*(xy_coo[k].x - xy_coo[i].x)
648                   + (xy_coo[k].y - xy_coo[i].y)*(xy_coo[k].y - xy_coo[i].y)
649                         <= 2*radius*2*radius)) {
650
651                         int x, y;
652                         x = xy_coo[k].xmove;
653                         y = xy_coo[k].ymove;
654                         xy_coo[k].xmove = xy_coo[i].xmove;
655                         xy_coo[k].ymove = xy_coo[i].ymove;
656                         xy_coo[i].xmove = x;
657                         xy_coo[i].ymove = y;
658                 }
659         }
660
661 }
662
663 /* make xy_coo[k] grow/shrink */
664 static void swamp_thing(int k)
665 {
666         if (xy_coo[k].r >= radius)
667                 xy_coo[k].r_change = -abs(xy_coo[k].r_change);
668         
669         if (xy_coo[k].r <= 0) {
670                 from = from_array[0];
671                 draw(k); 
672                 xy_coo[k].r_change = abs(xy_coo[k].r_change);
673                 new_rnd_coo(k);
674                 xy_coo[k].r=xy_coo[k].r_change;
675                 return;
676         }
677
678         xy_coo[k].r = xy_coo[k].r + xy_coo[k].r_change;
679
680         if (xy_coo[k].r >= radius)
681                 xy_coo[k].r = radius;
682         if (xy_coo[k].r <= 0)
683                 xy_coo[k].r=0;
684
685         from = from_array[xy_coo[k].r];
686 }
687
688
689 \f
690
691 char *progclass = "Distort";
692
693 char *defaults [] = {
694         "*dontClearRoot:                True",
695 #ifdef __sgi    /* really, HAVE_READ_DISPLAY_EXTENSION */
696         "*visualID:                     Best",
697 #endif
698
699         "*delay:                        1000",
700         "*radius:                       0",
701         "*speed:                        0",
702         "*number:                       0",
703         "*vortex:                       False",
704         "*magnify:                      False",
705         "*swamp:                        False",
706         "*bounce:                       False",
707         "*reflect:                      False",
708         "*blackhole:            False",
709 #ifdef HAVE_XSHM_EXTENSION
710         "*useSHM:                       False",         /* xshm turns out not to help. */
711 #endif /* HAVE_XSHM_EXTENSION */
712         0
713 };
714
715 XrmOptionDescRec options [] = {
716         { "-delay",     ".delay",       XrmoptionSepArg, 0 },
717         { "-radius",    ".radius",      XrmoptionSepArg, 0 },
718         { "-speed",     ".speed",       XrmoptionSepArg, 0 },
719         { "-number",    ".number",      XrmoptionSepArg, 0 },
720         { "-swamp",     ".swamp",       XrmoptionNoArg, "True" },
721         { "-bounce",    ".bounce",      XrmoptionNoArg, "True" },
722         { "-reflect",   ".reflect",     XrmoptionNoArg, "True" },
723         { "-vortex",    ".vortex",      XrmoptionNoArg, "True" },
724         { "-magnify",   ".magnify",     XrmoptionNoArg, "True" },
725         { "-blackhole", ".blackhole",   XrmoptionNoArg, "True" },
726         { "-slow",      ".slow",        XrmoptionNoArg, "True" },
727 #ifdef HAVE_XSHM_EXTENSION
728         { "-shm",               ".useSHM",      XrmoptionNoArg, "True" },
729         { "-no-shm",    ".useSHM",      XrmoptionNoArg, "False" },
730 #endif /* HAVE_XSHM_EXTENSION */
731         { 0, 0, 0, 0 }
732 };
733
734
735 void screenhack(Display *dpy, Window window)
736 {
737         int k;
738
739         init_distort (dpy, window);
740         while (1) {
741                 for (k = 0; k < number; k++) {
742                         effect(k);
743                         draw(k);
744                 }
745
746                 XSync(dpy, False);
747         screenhack_handle_events (dpy);
748                 if (delay) usleep(delay);
749         }
750
751 }