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);
61 static void init_distort(Display *dpy, Window window)
70 delay = get_integer_resource ("delay", "Integer");
71 radius = get_integer_resource ("radius", "Integer");
72 speed = get_integer_resource ("speed", "Integer");
73 number = get_integer_resource ("number", "Integer");
75 #ifdef HAVE_XSHM_EXTENSION
76 use_shm = get_boolean_resource("useSHM", "Boolean");
77 #endif /* HAVE_XSHM_EXTENSION */
79 blackhole = get_boolean_resource("blackhole", "Boolean");
80 vortex = get_boolean_resource("vortex", "Boolean");
81 magnify = get_boolean_resource("magnify", "Boolean");
82 reflect = get_boolean_resource("reflect", "Boolean");
84 if (get_boolean_resource ("swamp", "Boolean"))
85 effect = &swamp_thing;
86 if (get_boolean_resource ("bounce", "Boolean") || reflect)
89 if (effect == NULL && radius == 0 && speed == 0 && number == 0
90 && !blackhole && !vortex && !magnify && !reflect) {
91 /* if no cmdline options are given, randomly choose one of:
92 * -radius 50 -number 4 -speed 1 -bounce
93 * -radius 50 -number 4 -speed 1 -blackhole
94 * -radius 50 -number 4 -speed 1 -vortex
95 * -radius 50 -number 4 -speed 1 -vortex -magnify
96 * -radius 50 -number 4 -speed 1 -vortex -magnify -blackhole
97 * -radius 100 -number 1 -speed 2 -bounce
98 * -radius 100 -number 1 -speed 2 -blackhole
99 * -radius 100 -number 1 -speed 2 -vortex
100 * -radius 100 -number 1 -speed 2 -vortex -magnify
101 * -radius 100 -number 1 -speed 2 -vortex -magnify -blackhole
102 * -radius 50 -number 4 -speed 2 -swamp
103 * -radius 50 -number 4 -speed 2 -swamp -blackhole
104 * -radius 50 -number 4 -speed 2 -swamp -vortex
105 * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify
106 * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify -blackhole
113 radius=50;number=4;speed=1;
114 effect=&move_lense;break;
116 radius=50;number=4;speed=1;blackhole=1;
117 effect=&move_lense;break;
119 radius=50;number=4;speed=1;vortex=1;
120 effect=&move_lense;break;
122 radius=50;number=4;speed=1;vortex=1;magnify=1;
123 effect=&move_lense;break;
125 radius=50;number=4;speed=1;vortex=1;magnify=1;blackhole=1;
126 effect=&move_lense;break;
128 radius=100;number=1;speed=2;
129 effect=&move_lense;break;
131 radius=100;number=1;speed=2;blackhole=1;
132 effect=&move_lense;break;
134 radius=100;number=1;speed=2;vortex=1;
135 effect=&move_lense;break;
137 radius=100;number=1;speed=2;vortex=1;magnify=1;
138 effect=&move_lense;break;
140 radius=100;number=1;speed=2;vortex=1;magnify=1;blackhole=1;
141 effect=&move_lense;break;
143 radius=50;number=4;speed=2;
144 effect=&swamp_thing;break;
146 radius=50;number=4;speed=2;blackhole=1;
147 effect=&swamp_thing;break;
149 radius=50;number=4;speed=2;vortex=1;
150 effect=&swamp_thing;break;
152 radius=50;number=4;speed=2;vortex=1;magnify=1;
153 effect=&swamp_thing;break;
155 radius=50;number=4;speed=2;vortex=1;magnify=1;blackhole=1;
156 effect=&swamp_thing;break;
172 effect = &move_lense;
174 XGetWindowAttributes (dpy, window, &xgwa);
175 black_pixel = BlackPixelOfScreen( xgwa.screen );
177 gcv.function = GXcopy;
178 gcv.subwindow_mode = IncludeInferiors;
179 gcflags = GCForeground |GCFunction;
180 if (use_subwindow_mode_p(xgwa.screen, window)) /* see grabscreen.c */
181 gcflags |= GCSubwindowMode;
182 gc = XCreateGC (dpy, window, gcflags, &gcv);
184 grab_screen_image (xgwa.screen, window);
187 orig_map = XGetImage(dpy, window, 0, 0, xgwa.width, xgwa.height,
190 # ifdef HAVE_XSHM_EXTENSION
194 buffer_map = create_xshm_image(dpy, xgwa.visual, orig_map->depth,
195 ZPixmap, 0, &shm_info,
196 2*radius + speed + 2,
197 2*radius + speed + 2);
201 # endif /* HAVE_XSHM_EXTENSION */
205 buffer_map = XCreateImage(dpy, xgwa.visual,
206 orig_map->depth, ZPixmap, 0, 0,
207 2*radius + speed + 2, 2*radius + speed + 2,
209 buffer_map->data = (char *)
210 calloc(buffer_map->height, buffer_map->bytes_per_line);
215 for (i = 0; i < number; i++) {
218 xy_coo[i].r = (i*radius)/(number-1); /* "randomize" initial */
221 xy_coo[i].r_change = speed + (i%2)*2*(-speed); /* values a bit */
222 xy_coo[i].xmove = speed + (i%2)*2*(-speed);
223 xy_coo[i].ymove = speed + (i%2)*2*(-speed);
227 /* example: initializes a "see-trough" matrix */
228 /* static void make_null_lense(void)
231 for (i = 0; i < 2*radius+speed+2; i++) {
232 for (j = 0 ; j < 2*radius+speed+2 ; j++) {
240 /* makes a lense with the Radius=loop and centred in
241 * the point (radius, radius)
243 static void make_round_lense(int radius, int loop)
248 for (i = 0; i < 2*radius+speed+2; i++) {
249 for(j = 0; j < 2*radius+speed+2; j++) {
251 r = sqrt ((i-radius)*(i-radius)+(j-radius)*(j-radius));
259 if (vortex) { /* vortex-twist effect */
261 /* this one-line formula for getting a nice rotation angle is borrowed
262 * (with permission) from the whirl plugin for gimp,
263 * Copyright (C) 1996 Federico Mena Quintero
265 /* 2.5 is just a constant used because it looks good :) */
266 angle = 2.5*(1-d)*(1-d);
269 /* Avoid atan2: DOMAIN error message */
270 if ((radius-j) == 0.0 && (radius-i) == 0.0)
273 theta = atan2(radius-j, radius-i);
274 from[i][j][0] = radius +
275 cos(angle - theta)*r;
276 from[i][j][1] = radius +
277 sin(angle - theta)*r;
281 if (blackhole && r != 0) /* blackhole effect */
283 from[i][j][0] = radius + (from[i][j][0]-radius)*r;
284 from[i][j][1] = radius + (from[i][j][1]-radius)*r;
286 } else { /* default is to magnify */
289 /* raising r to different power here gives different amounts of
290 * distortion, a negative value sucks everything into a black hole
293 if (blackhole) /* blackhole effect */
295 /* bubble effect (and blackhole) */
296 from[i][j][0] = radius + (i-radius)*r;
297 from[i][j][1] = radius + (j-radius)*r;
299 } else { /* not inside loop */
308 # define EXIT_FAILURE -1
311 static void allocate_lense(void)
314 /* maybe this should be redone so that from[][][] is in one block;
315 * then pointers could be used instead of arrays in some places (and
316 * maybe give a speedup - maybe also consume less memory)
319 from = (int ***)malloc((2*radius+speed+2) * sizeof(int **));
324 for (i = 0; i < 2*radius+speed+2; i++) {
325 from[i] = (int **)malloc((2*radius+speed+2) * sizeof(int *));
326 if (from[i] == NULL) {
330 for (j = 0; j < 2*radius+speed+2; j++) {
331 from[i][j] = (int *)malloc(2 * sizeof(int));
332 if (from[i][j] == NULL) {
340 /* from_array in an array containing precalculated from matrices,
341 * this is a double faced mem vs speed trade, it's faster, but eats
342 * _a lot_ of mem for large radius (is there a bug here? I can't see it)
344 static void init_round_lense(void)
348 if (effect == &swamp_thing) {
349 from_array = (int ****)malloc((radius+1)*sizeof(int ***));
350 for (k=0; k <= radius; k++) {
352 make_round_lense(radius, k);
353 from_array[k] = from;
355 } else { /* just allocate one from[][][] */
357 make_round_lense(radius,radius);
362 /* generate an XImage of from[][][] and draw it on the screen */
367 int ly, lysq, lx, ny, dist, rsq = radius * radius;
370 if (xy_coo[k].ymove > 0)
372 if (xy_coo[k].xmove > 0)
375 for(i = 0 ; i < 2*radius+speed+2; i++) {
379 ny = xy_coo[k].y + i;
381 for(j = 0 ; j < 2*radius+speed+2 ; j++) {
384 dist = lx * lx + lysq;
386 ly < -radius || ly > radius ||
387 lx < -radius || lx > radius)
388 XPutPixel( buffer_map, j, i,
389 XGetPixel( orig_map, xy_coo[k].x + j, ny ));
391 XPutPixel( buffer_map, j, i, black_pixel );
393 int x = xy_coo[k].x + cx + (lx * rsq / dist);
394 int y = xy_coo[k].y + cy + (ly * rsq / dist);
395 if (x < 0 || x > xgwa.width ||
396 y < 0 || y > xgwa.height)
397 XPutPixel( buffer_map, j, i, black_pixel );
399 XPutPixel( buffer_map, j, i,
400 XGetPixel( orig_map, x, y ));
402 } else if (xy_coo[k].x+from[i][j][0] >= 0 &&
403 xy_coo[k].x+from[i][j][0] < xgwa.width &&
404 xy_coo[k].y+from[i][j][1] >= 0 &&
405 xy_coo[k].y+from[i][j][1] < xgwa.height) {
406 XPutPixel(buffer_map, i, j,
408 xy_coo[k].x+from[i][j][0],
409 xy_coo[k].y+from[i][j][1]));
414 XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
415 2*radius+speed+2, 2*radius+speed+2);
418 /* create a new, random coordinate, that won't interfer with any other
419 * coordinates, as the drawing routines would be significantly slowed
420 * down if they were to handle serveral layers of distortions
422 static void new_rnd_coo(int k)
426 xy_coo[k].x = (random() % (xgwa.width-2*radius));
427 xy_coo[k].y = (random() % (xgwa.height-2*radius));
429 for (i = 0; i < number; i++) {
431 if ((abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius+speed+2)
432 && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius+speed+2)) {
433 xy_coo[k].x = (random() % (xgwa.width-2*radius));
434 xy_coo[k].y = (random() % (xgwa.height-2*radius));
441 /* move lens and handle bounces with walls and other lenses */
442 static void move_lense(int k)
446 if (xy_coo[k].x + 2*radius + speed + 2 >= xgwa.width)
447 xy_coo[k].xmove = -abs(xy_coo[k].xmove);
448 if (xy_coo[k].x <= speed)
449 xy_coo[k].xmove = abs(xy_coo[k].xmove);
450 if (xy_coo[k].y + 2*radius + speed + 2 >= xgwa.height)
451 xy_coo[k].ymove = -abs(xy_coo[k].ymove);
452 if (xy_coo[k].y <= speed)
453 xy_coo[k].ymove = abs(xy_coo[k].ymove);
455 xy_coo[k].x = xy_coo[k].x + xy_coo[k].xmove;
456 xy_coo[k].y = xy_coo[k].y + xy_coo[k].ymove;
458 for (i = 0; i < number; i++) {
461 /* This commented test is for rectangular lenses (not presently used) and
462 * the one used is for circular ones
463 && (abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius)
464 && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius)) { */
466 && ((xy_coo[k].x - xy_coo[i].x)*(xy_coo[k].x - xy_coo[i].x)
467 + (xy_coo[k].y - xy_coo[i].y)*(xy_coo[k].y - xy_coo[i].y)
468 <= 2*radius*2*radius)) {
473 xy_coo[k].xmove = xy_coo[i].xmove;
474 xy_coo[k].ymove = xy_coo[i].ymove;
482 /* make xy_coo[k] grow/shrink */
483 void swamp_thing(int k)
485 if (xy_coo[k].r >= radius)
486 xy_coo[k].r_change = -abs(xy_coo[k].r_change);
488 if (xy_coo[k].r <= 0) {
489 from = from_array[0];
491 xy_coo[k].r_change = abs(xy_coo[k].r_change);
493 xy_coo[k].r=xy_coo[k].r_change;
497 xy_coo[k].r = xy_coo[k].r + xy_coo[k].r_change;
499 if (xy_coo[k].r >= radius)
500 xy_coo[k].r = radius;
501 if (xy_coo[k].r <= 0)
504 from = from_array[xy_coo[k].r];
510 char *progclass = "Distort";
512 char *defaults [] = {
513 "*dontClearRoot: True",
514 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
528 #ifdef HAVE_XSHM_EXTENSION
529 "*useSHM: False", /* xshm turns out not to help. */
530 #endif /* HAVE_XSHM_EXTENSION */
534 XrmOptionDescRec options [] = {
535 { "-delay", ".delay", XrmoptionSepArg, 0 },
536 { "-radius", ".radius", XrmoptionSepArg, 0 },
537 { "-speed", ".speed", XrmoptionSepArg, 0 },
538 { "-number", ".number", XrmoptionSepArg, 0 },
539 { "-swamp", ".swamp", XrmoptionNoArg, "True" },
540 { "-bounce", ".bounce", XrmoptionNoArg, "True" },
541 { "-reflect", ".reflect", XrmoptionNoArg, "True" },
542 { "-vortex", ".vortex", XrmoptionNoArg, "True" },
543 { "-magnify", ".magnify", XrmoptionNoArg, "True" },
544 { "-blackhole", ".blackhole", XrmoptionNoArg, "True" },
545 #ifdef HAVE_XSHM_EXTENSION
546 { "-shm", ".useSHM", XrmoptionNoArg, "True" },
547 { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
548 #endif /* HAVE_XSHM_EXTENSION */
553 void screenhack(Display *dpy, Window window)
557 init_distort (dpy, window);
559 for (k = 0; k < number; k++) {
565 if (delay) usleep(delay);