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