1 /* -*- mode: C; tab-width: 4 -*-
2 * xscreensaver, Copyright (c) 1992, 1993, 1994, 1996, 1997, 1998
3 * Jamie Zawinski <jwz@jwz.org>
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
15 * by Jonas Munsin (jmunsin@iki.fi) and Jamie Zawinski <jwz@jwz.org>
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 * program idea borrowed from a screensaver on a non-*NIX OS,
27 #include "screenhack.h"
28 #include <X11/Xutil.h>
30 #ifdef HAVE_XSHM_EXTENSION
33 static XShmSegmentInfo shm_info;
34 #endif /* HAVE_XSHM_EXTENSION */
42 static struct coo xy_coo[10];
44 static int delay, radius, speed, number, blackhole, vortex, magnify, reflect;
45 static XWindowAttributes xgwa;
47 static Window g_window;
48 static Display *g_dpy;
49 static unsigned long black_pixel;
51 static XImage *orig_map, *buffer_map;
54 static int ****from_array;
55 static void (*effect) (int) = NULL;
56 static void move_lense(int);
57 static void swamp_thing(int);
58 static void new_rnd_coo(int);
59 static void init_round_lense(void);
60 static void (*draw) (int) = NULL;
61 static void reflect_draw(int);
62 static void plain_draw(int);
64 static void init_distort(Display *dpy, Window window)
73 delay = get_integer_resource("delay", "Integer");
74 radius = get_integer_resource("radius", "Integer");
75 speed = get_integer_resource("speed", "Integer");
76 number = get_integer_resource("number", "Integer");
78 #ifdef HAVE_XSHM_EXTENSION
79 use_shm = get_boolean_resource("useSHM", "Boolean");
80 #endif /* HAVE_XSHM_EXTENSION */
82 blackhole = get_boolean_resource("blackhole", "Boolean");
83 vortex = get_boolean_resource("vortex", "Boolean");
84 magnify = get_boolean_resource("magnify", "Boolean");
85 reflect = get_boolean_resource("reflect", "Boolean");
87 if (get_boolean_resource("swamp", "Boolean"))
88 effect = &swamp_thing;
89 if (get_boolean_resource("bounce", "Boolean"))
92 if (effect == NULL && radius == 0 && speed == 0 && number == 0
93 && !blackhole && !vortex && !magnify && !reflect) {
94 /* if no cmdline options are given, randomly choose one of:
95 * -radius 50 -number 4 -speed 1 -bounce
96 * -radius 50 -number 4 -speed 1 -blackhole
97 * -radius 50 -number 4 -speed 1 -vortex
98 * -radius 50 -number 4 -speed 1 -vortex -magnify
99 * -radius 50 -number 4 -speed 1 -vortex -magnify -blackhole
100 * -radius 100 -number 1 -speed 2 -bounce
101 * -radius 100 -number 1 -speed 2 -blackhole
102 * -radius 100 -number 1 -speed 2 -vortex
103 * -radius 100 -number 1 -speed 2 -vortex -magnify
104 * -radius 100 -number 1 -speed 2 -vortex -magnify -blackhole
105 * -radius 50 -number 4 -speed 2 -swamp
106 * -radius 50 -number 4 -speed 2 -swamp -blackhole
107 * -radius 50 -number 4 -speed 2 -swamp -vortex
108 * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify
109 * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify -blackhole
110 * -radius 80 -number 1 -speed 2 -reflect
111 * -radius 50 -number 3 -speed 2 -reflect
120 radius=50;number=4;speed=1;
121 effect=&move_lense;break;
123 radius=50;number=4;speed=1;blackhole=1;
124 effect=&move_lense;break;
126 radius=50;number=4;speed=1;vortex=1;
127 effect=&move_lense;break;
129 radius=50;number=4;speed=1;vortex=1;magnify=1;
130 effect=&move_lense;break;
132 radius=50;number=4;speed=1;vortex=1;magnify=1;blackhole=1;
133 effect=&move_lense;break;
135 radius=100;number=1;speed=2;
136 effect=&move_lense;break;
138 radius=100;number=1;speed=2;blackhole=1;
139 effect=&move_lense;break;
141 radius=100;number=1;speed=2;vortex=1;
142 effect=&move_lense;break;
144 radius=100;number=1;speed=2;vortex=1;magnify=1;
145 effect=&move_lense;break;
147 radius=100;number=1;speed=2;vortex=1;magnify=1;blackhole=1;
148 effect=&move_lense;break;
150 radius=50;number=4;speed=2;
151 effect=&swamp_thing;break;
153 radius=50;number=4;speed=2;blackhole=1;
154 effect=&swamp_thing;break;
156 radius=50;number=4;speed=2;vortex=1;
157 effect=&swamp_thing;break;
159 radius=50;number=4;speed=2;vortex=1;magnify=1;
160 effect=&swamp_thing;break;
162 radius=50;number=4;speed=2;vortex=1;magnify=1;blackhole=1;
163 effect=&swamp_thing;break;
165 radius=80;number=1;speed=2;reflect=1;
166 draw = &reflect_draw;effect = &move_lense;break;
168 radius=50;number=4;speed=2;reflect=1;
169 draw = &reflect_draw;effect = &move_lense;break;
185 effect = &move_lense;
187 draw = &reflect_draw;
188 effect = &move_lense;
193 XGetWindowAttributes (dpy, window, &xgwa);
194 black_pixel = BlackPixelOfScreen( xgwa.screen );
196 gcv.function = GXcopy;
197 gcv.subwindow_mode = IncludeInferiors;
198 gcflags = GCForeground |GCFunction;
199 if (use_subwindow_mode_p(xgwa.screen, window)) /* see grabscreen.c */
200 gcflags |= GCSubwindowMode;
201 gc = XCreateGC (dpy, window, gcflags, &gcv);
203 grab_screen_image (xgwa.screen, window);
206 orig_map = XGetImage(dpy, window, 0, 0, xgwa.width, xgwa.height,
209 # ifdef HAVE_XSHM_EXTENSION
213 buffer_map = create_xshm_image(dpy, xgwa.visual, orig_map->depth,
214 ZPixmap, 0, &shm_info,
215 2*radius + speed + 2,
216 2*radius + speed + 2);
220 # endif /* HAVE_XSHM_EXTENSION */
224 buffer_map = XCreateImage(dpy, xgwa.visual,
225 orig_map->depth, ZPixmap, 0, 0,
226 2*radius + speed + 2, 2*radius + speed + 2,
228 buffer_map->data = (char *)
229 calloc(buffer_map->height, buffer_map->bytes_per_line);
234 for (i = 0; i < number; i++) {
237 xy_coo[i].r = (i*radius)/(number-1); /* "randomize" initial */
240 xy_coo[i].r_change = speed + (i%2)*2*(-speed); /* values a bit */
241 xy_coo[i].xmove = speed + (i%2)*2*(-speed);
242 xy_coo[i].ymove = speed + (i%2)*2*(-speed);
246 /* example: initializes a "see-trough" matrix */
247 /* static void make_null_lense(void)
250 for (i = 0; i < 2*radius+speed+2; i++) {
251 for (j = 0 ; j < 2*radius+speed+2 ; j++) {
259 /* makes a lense with the Radius=loop and centred in
260 * the point (radius, radius)
262 static void make_round_lense(int radius, int loop)
266 for (i = 0; i < 2*radius+speed+2; i++) {
267 for(j = 0; j < 2*radius+speed+2; j++) {
269 r = sqrt ((i-radius)*(i-radius)+(j-radius)*(j-radius));
277 if (vortex) { /* vortex-twist effect */
279 /* this one-line formula for getting a nice rotation angle is borrowed
280 * (with permission) from the whirl plugin for gimp,
281 * Copyright (C) 1996 Federico Mena Quintero
283 /* 2.5 is just a constant used because it looks good :) */
284 angle = 2.5*(1-d)*(1-d);
286 /* Avoid atan2: DOMAIN error message */
287 if ((radius-j) == 0.0 && (radius-i) == 0.0) {
288 from[i][j][0] = radius + cos(angle)*r;
289 from[i][j][1] = radius + sin(angle)*r;
291 from[i][j][0] = radius +
292 cos(angle - atan2(radius-j, -(radius-i)))*r;
293 from[i][j][1] = radius +
294 sin(angle - atan2(radius-j, -(radius-i)))*r;
298 if (blackhole && r != 0) /* blackhole effect */
300 from[i][j][0] = radius + (from[i][j][0]-radius)*r;
301 from[i][j][1] = radius + (from[i][j][1]-radius)*r;
303 } else { /* default is to magnify */
306 /* raising r to different power here gives different amounts of
307 * distortion, a negative value sucks everything into a black hole
310 if (blackhole && r != 0) /* blackhole effect */
312 /* bubble effect (and blackhole) */
313 from[i][j][0] = radius + (i-radius)*r;
314 from[i][j][1] = radius + (j-radius)*r;
316 } else { /* not inside loop */
325 # define EXIT_FAILURE -1
328 static void allocate_lense(void)
331 /* maybe this should be redone so that from[][][] is in one block;
332 * then pointers could be used instead of arrays in some places (and
333 * maybe give a speedup - maybe also consume less memory)
336 from = (int ***)malloc((2*radius+speed+2) * sizeof(int **));
341 for (i = 0; i < 2*radius+speed+2; i++) {
342 from[i] = (int **)malloc((2*radius+speed+2) * sizeof(int *));
343 if (from[i] == NULL) {
347 for (j = 0; j < 2*radius+speed+2; j++) {
348 from[i][j] = (int *)malloc(2 * sizeof(int));
349 if (from[i][j] == NULL) {
357 /* from_array in an array containing precalculated from matrices,
358 * this is a double faced mem vs speed trade, it's faster, but eats
359 * _a lot_ of mem for large radius (is there a bug here? I can't see it)
361 static void init_round_lense(void)
365 if (effect == &swamp_thing) {
366 from_array = (int ****)malloc((radius+1)*sizeof(int ***));
367 for (k=0; k <= radius; k++) {
369 make_round_lense(radius, k);
370 from_array[k] = from;
372 } else { /* just allocate one from[][][] */
374 make_round_lense(radius,radius);
379 /* generate an XImage of from[][][] and draw it on the screen */
380 void plain_draw(int k)
383 for(i = 0 ; i < 2*radius+speed+2; i++) {
384 for(j = 0 ; j < 2*radius+speed+2 ; j++) {
385 if (xy_coo[k].x+from[i][j][0] >= 0 &&
386 xy_coo[k].x+from[i][j][0] < xgwa.width &&
387 xy_coo[k].y+from[i][j][1] >= 0 &&
388 xy_coo[k].y+from[i][j][1] < xgwa.height)
389 XPutPixel(buffer_map, i, j,
391 xy_coo[k].x+from[i][j][0],
392 xy_coo[k].y+from[i][j][1]));
396 XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
397 2*radius+speed+2, 2*radius+speed+2);
400 /* generate an XImage from the reflect algoritm submitted by
401 * Randy Zack <randy@acucorp.com>
402 * draw really got too big and ugly so I split it up
403 * it should be possible to use the from[][] to speed it up
404 * (once I figure out the algorithm used :)
406 void reflect_draw(int k)
410 int ly, lysq, lx, ny, dist, rsq = radius * radius;
413 if (xy_coo[k].ymove > 0)
415 if (xy_coo[k].xmove > 0)
418 for(i = 0 ; i < 2*radius+speed+2; i++) {
421 ny = xy_coo[k].y + i;
422 for(j = 0 ; j < 2*radius+speed+2 ; j++) {
424 dist = lx * lx + lysq;
426 ly < -radius || ly > radius ||
427 lx < -radius || lx > radius)
428 XPutPixel( buffer_map, j, i,
429 XGetPixel( orig_map, xy_coo[k].x + j, ny ));
431 XPutPixel( buffer_map, j, i, black_pixel );
433 int x = xy_coo[k].x + cx + (lx * rsq / dist);
434 int y = xy_coo[k].y + cy + (ly * rsq / dist);
435 if (x < 0 || x > xgwa.width ||
436 y < 0 || y > xgwa.height)
437 XPutPixel( buffer_map, j, i, black_pixel );
439 XPutPixel( buffer_map, j, i,
440 XGetPixel( orig_map, x, y ));
445 XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
446 2*radius+speed+2, 2*radius+speed+2);
449 /* create a new, random coordinate, that won't interfer with any other
450 * coordinates, as the drawing routines would be significantly slowed
451 * down if they were to handle serveral layers of distortions
453 static void new_rnd_coo(int k)
457 xy_coo[k].x = (random() % (xgwa.width-2*radius));
458 xy_coo[k].y = (random() % (xgwa.height-2*radius));
460 for (i = 0; i < number; i++) {
462 if ((abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius+speed+2)
463 && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius+speed+2)) {
464 xy_coo[k].x = (random() % (xgwa.width-2*radius));
465 xy_coo[k].y = (random() % (xgwa.height-2*radius));
472 /* move lens and handle bounces with walls and other lenses */
473 static void move_lense(int k)
477 if (xy_coo[k].x + 2*radius + speed + 2 >= xgwa.width)
478 xy_coo[k].xmove = -abs(xy_coo[k].xmove);
479 if (xy_coo[k].x <= speed)
480 xy_coo[k].xmove = abs(xy_coo[k].xmove);
481 if (xy_coo[k].y + 2*radius + speed + 2 >= xgwa.height)
482 xy_coo[k].ymove = -abs(xy_coo[k].ymove);
483 if (xy_coo[k].y <= speed)
484 xy_coo[k].ymove = abs(xy_coo[k].ymove);
486 xy_coo[k].x = xy_coo[k].x + xy_coo[k].xmove;
487 xy_coo[k].y = xy_coo[k].y + xy_coo[k].ymove;
489 for (i = 0; i < number; i++) {
492 /* This commented test is for rectangular lenses (not currently used) and
493 * the one used is for circular ones
494 && (abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius)
495 && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius)) { */
497 && ((xy_coo[k].x - xy_coo[i].x)*(xy_coo[k].x - xy_coo[i].x)
498 + (xy_coo[k].y - xy_coo[i].y)*(xy_coo[k].y - xy_coo[i].y)
499 <= 2*radius*2*radius)) {
504 xy_coo[k].xmove = xy_coo[i].xmove;
505 xy_coo[k].ymove = xy_coo[i].ymove;
513 /* make xy_coo[k] grow/shrink */
514 void swamp_thing(int k)
516 if (xy_coo[k].r >= radius)
517 xy_coo[k].r_change = -abs(xy_coo[k].r_change);
519 if (xy_coo[k].r <= 0) {
520 from = from_array[0];
522 xy_coo[k].r_change = abs(xy_coo[k].r_change);
524 xy_coo[k].r=xy_coo[k].r_change;
528 xy_coo[k].r = xy_coo[k].r + xy_coo[k].r_change;
530 if (xy_coo[k].r >= radius)
531 xy_coo[k].r = radius;
532 if (xy_coo[k].r <= 0)
535 from = from_array[xy_coo[k].r];
541 char *progclass = "Distort";
543 char *defaults [] = {
544 "*dontClearRoot: True",
545 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
559 #ifdef HAVE_XSHM_EXTENSION
560 "*useSHM: False", /* xshm turns out not to help. */
561 #endif /* HAVE_XSHM_EXTENSION */
565 XrmOptionDescRec options [] = {
566 { "-delay", ".delay", XrmoptionSepArg, 0 },
567 { "-radius", ".radius", XrmoptionSepArg, 0 },
568 { "-speed", ".speed", XrmoptionSepArg, 0 },
569 { "-number", ".number", XrmoptionSepArg, 0 },
570 { "-swamp", ".swamp", XrmoptionNoArg, "True" },
571 { "-bounce", ".bounce", XrmoptionNoArg, "True" },
572 { "-reflect", ".reflect", XrmoptionNoArg, "True" },
573 { "-vortex", ".vortex", XrmoptionNoArg, "True" },
574 { "-magnify", ".magnify", XrmoptionNoArg, "True" },
575 { "-blackhole", ".blackhole", XrmoptionNoArg, "True" },
576 #ifdef HAVE_XSHM_EXTENSION
577 { "-shm", ".useSHM", XrmoptionNoArg, "True" },
578 { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
579 #endif /* HAVE_XSHM_EXTENSION */
584 void screenhack(Display *dpy, Window window)
588 init_distort (dpy, window);
590 for (k = 0; k < number; k++) {
596 if (delay) usleep(delay);