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