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;
45 static XWindowAttributes xgwa;
47 static Window g_window;
48 static Display *g_dpy;
50 static XImage *orig_map, *buffer_map;
53 static int ****from_array;
54 static void (*effect) (int) = NULL;
55 static void move_lense(int);
56 static void swamp_thing(int);
57 static void new_rnd_coo(int);
58 static void init_round_lense(void);
60 static void init_distort(Display *dpy, Window window)
69 delay = get_integer_resource ("delay", "Integer");
70 radius = get_integer_resource ("radius", "Integer");
71 speed = get_integer_resource ("speed", "Integer");
72 number = get_integer_resource ("number", "Integer");
74 #ifdef HAVE_XSHM_EXTENSION
75 use_shm = get_boolean_resource("useSHM", "Boolean");
76 #endif /* HAVE_XSHM_EXTENSION */
78 blackhole = get_boolean_resource("blackhole", "Boolean");
79 vortex = get_boolean_resource("vortex", "Boolean");
80 magnify = get_boolean_resource("magnify", "Boolean");
82 if (get_boolean_resource ("swamp", "Boolean"))
83 effect = &swamp_thing;
84 if (get_boolean_resource ("bounce", "Boolean"))
87 if (effect == NULL && radius == 0 && speed == 0 && number == 0
88 && !blackhole && !vortex && !magnify) {
89 /* if no cmdline options are given, randomly choose one of:
90 * -radius 50 -number 4 -speed 1 -bounce
91 * -radius 50 -number 4 -speed 1 -blackhole
92 * -radius 50 -number 4 -speed 1 -vortex
93 * -radius 50 -number 4 -speed 1 -vortex -magnify
94 * -radius 50 -number 4 -speed 1 -vortex -magnify -blackhole
95 * -radius 100 -number 1 -speed 2 -bounce
96 * -radius 100 -number 1 -speed 2 -blackhole
97 * -radius 100 -number 1 -speed 2 -vortex
98 * -radius 100 -number 1 -speed 2 -vortex -magnify
99 * -radius 100 -number 1 -speed 2 -vortex -magnify -blackhole
100 * -radius 50 -number 4 -speed 2 -swamp
101 * -radius 50 -number 4 -speed 2 -swamp -blackhole
102 * -radius 50 -number 4 -speed 2 -swamp -vortex
103 * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify
104 * -radius 50 -number 4 -speed 2 -swamp -vortex -magnify -blackhole
111 radius=50;number=4;speed=1;
112 effect=&move_lense;break;
114 radius=50;number=4;speed=1;blackhole=1;
115 effect=&move_lense;break;
117 radius=50;number=4;speed=1;vortex=1;
118 effect=&move_lense;break;
120 radius=50;number=4;speed=1;vortex=1;magnify=1;
121 effect=&move_lense;break;
123 radius=50;number=4;speed=1;vortex=1;magnify=1;blackhole=1;
124 effect=&move_lense;break;
126 radius=100;number=1;speed=2;
127 effect=&move_lense;break;
129 radius=100;number=1;speed=2;blackhole=1;
130 effect=&move_lense;break;
132 radius=100;number=1;speed=2;vortex=1;
133 effect=&move_lense;break;
135 radius=100;number=1;speed=2;vortex=1;magnify=1;
136 effect=&move_lense;break;
138 radius=100;number=1;speed=2;vortex=1;magnify=1;blackhole=1;
139 effect=&move_lense;break;
141 radius=50;number=4;speed=2;
142 effect=&swamp_thing;break;
144 radius=50;number=4;speed=2;blackhole=1;
145 effect=&swamp_thing;break;
147 radius=50;number=4;speed=2;vortex=1;
148 effect=&swamp_thing;break;
150 radius=50;number=4;speed=2;vortex=1;magnify=1;
151 effect=&swamp_thing;break;
153 radius=50;number=4;speed=2;vortex=1;magnify=1;blackhole=1;
154 effect=&swamp_thing;break;
170 effect = &move_lense;
172 XGetWindowAttributes (dpy, window, &xgwa);
174 gcv.function = GXcopy;
175 gcv.subwindow_mode = IncludeInferiors;
176 gcflags = GCForeground |GCFunction;
177 if (use_subwindow_mode_p(xgwa.screen, window)) /* see grabscreen.c */
178 gcflags |= GCSubwindowMode;
179 gc = XCreateGC (dpy, window, gcflags, &gcv);
181 grab_screen_image (xgwa.screen, window);
184 orig_map = XGetImage(dpy, window, 0, 0, xgwa.width, xgwa.height,
187 # ifdef HAVE_XSHM_EXTENSION
191 buffer_map = create_xshm_image(dpy, xgwa.visual, orig_map->depth,
192 ZPixmap, 0, &shm_info,
193 2*radius + speed + 2,
194 2*radius + speed + 2);
198 # endif /* HAVE_XSHM_EXTENSION */
202 buffer_map = XCreateImage(dpy, xgwa.visual,
203 orig_map->depth, ZPixmap, 0, 0,
204 2*radius + speed + 2, 2*radius + speed + 2,
206 buffer_map->data = (char *)
207 calloc(buffer_map->height, buffer_map->bytes_per_line);
212 for (i = 0; i < number; i++) {
215 xy_coo[i].r = (i*radius)/(number-1); /* "randomize" initial */
218 xy_coo[i].r_change = speed + (i%2)*2*(-speed); /* values a bit */
219 xy_coo[i].xmove = speed + (i%2)*2*(-speed);
220 xy_coo[i].ymove = speed + (i%2)*2*(-speed);
224 /* example: initializes a "see-trough" matrix */
225 /* static void make_null_lense(void)
228 for (i = 0; i < 2*radius+speed+2; i++) {
229 for (j = 0 ; j < 2*radius+speed+2 ; j++) {
237 /* makes a lense with the Radius=loop and centred in
238 * the point (radius, radius)
240 static void make_round_lense(int radius, int loop)
244 for (i = 0; i < 2*radius+speed+2; i++) {
245 for(j = 0; j < 2*radius+speed+2; j++) {
247 r = sqrt ((i-radius)*(i-radius)+(j-radius)*(j-radius));
252 if (vortex) { /* vortex-twist effect */
254 /* this one-line formula for getting a nice rotation angle is borrowed
255 * (with permission) from the whirl plugin for gimp,
256 * Copyright (C) 1996 Federico Mena Quintero
258 /* 2.5 is just a constant used because it looks good :) */
259 angle = 2.5*(1-r/loop)*(1-r/loop);
261 from[i][j][0] = radius + cos(angle -
262 atan2(radius-j,-(radius-i)))*r;
263 from[i][j][1] = radius + sin(angle -
264 atan2(radius-j,-(radius-i)))*r;
268 if (blackhole && r != 0) /* blackhole effect */
270 from[i][j][0] = radius + (from[i][j][0]-radius)*r;
271 from[i][j][1] = radius + (from[i][j][1]-radius)*r;
273 } else { /* default is to magnify */
276 /* raising r to different power here gives different amounts of
277 * distortion, a negative value sucks everything into a black hole
280 if (blackhole) /* blackhole effect */
282 /* bubble effect (and blackhole) */
283 from[i][j][0] = radius + (i-radius)*r;
284 from[i][j][1] = radius + (j-radius)*r;
286 } else { /* not inside loop */
294 static void allocate_lense(void)
297 /* maybe this should be redone so that from[][][] is in one block;
298 * then pointers could be used instead of arrays in some places (and
299 * maybe give a speedup - maybe also consume less memory)
302 from = (int ***)malloc((2*radius+speed+2) * sizeof(int **));
307 for (i = 0; i < 2*radius+speed+2; i++) {
308 from[i] = (int **)malloc((2*radius+speed+2) * sizeof(int *));
309 if (from[i] == NULL) {
313 for (j = 0; j < 2*radius+speed+2; j++) {
314 from[i][j] = (int *)malloc(2 * sizeof(int));
315 if (from[i][j] == NULL) {
323 /* from_array in an array containing precalculated from matrices,
324 * this is a double faced mem vs speed trade, it's faster, but eats
325 * _a lot_ of mem for large radius (is there a bug here? I can't see it)
327 static void init_round_lense(void)
331 if (effect == &swamp_thing) {
332 from_array = (int ****)malloc((radius+1)*sizeof(int ***));
333 for (k=0; k <= radius; k++) {
335 make_round_lense(radius, k);
336 from_array[k] = from;
338 } else { /* just allocate one from[][][] */
340 make_round_lense(radius,radius);
345 /* generate an XImage of from[][][] and draw it on the screen */
349 for(i = 0 ; i < 2*radius+speed+2; i++) {
350 for(j = 0 ; j < 2*radius+speed+2 ; j++) {
351 if (xy_coo[k].x+from[i][j][0] >= 0 &&
352 xy_coo[k].x+from[i][j][0] < xgwa.width &&
353 xy_coo[k].y+from[i][j][1] >= 0 &&
354 xy_coo[k].y+from[i][j][1] < xgwa.height)
355 XPutPixel(buffer_map, i, j,
357 xy_coo[k].x+from[i][j][0],
358 xy_coo[k].y+from[i][j][1]));
362 XPutImage(g_dpy, g_window, gc, buffer_map, 0, 0, xy_coo[k].x, xy_coo[k].y,
363 2*radius+speed+2, 2*radius+speed+2);
366 /* create a new, random coordinate, that won't interfer with any other
367 * coordinates, as the drawing routines would be significantly slowed
368 * down if they were to handle serveral layers of distortions
370 static void new_rnd_coo(int k)
374 xy_coo[k].x = (random() % (xgwa.width-2*radius));
375 xy_coo[k].y = (random() % (xgwa.height-2*radius));
377 for (i = 0; i < number; i++) {
379 if ((abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius+speed+2)
380 && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius+speed+2)) {
381 xy_coo[k].x = (random() % (xgwa.width-2*radius));
382 xy_coo[k].y = (random() % (xgwa.height-2*radius));
389 /* move lens and handle bounces with walls and other lenses */
390 static void move_lense(int k)
394 if (xy_coo[k].x + 4*radius/2 >= xgwa.width)
395 xy_coo[k].xmove = -abs(xy_coo[k].xmove);
396 if (xy_coo[k].x <= speed)
397 xy_coo[k].xmove = abs(xy_coo[k].xmove);
398 if (xy_coo[k].y + 4*radius/2 >= xgwa.height)
399 xy_coo[k].ymove = -abs(xy_coo[k].ymove);
400 if (xy_coo[k].y <= speed)
401 xy_coo[k].ymove = abs(xy_coo[k].ymove);
403 xy_coo[k].x = xy_coo[k].x + xy_coo[k].xmove;
404 xy_coo[k].y = xy_coo[k].y + xy_coo[k].ymove;
406 for (i = 0; i < number; i++) {
409 /* This commented test is for rectangular lenses (not presently used) and
410 * the one used is for circular ones
411 && (abs(xy_coo[k].x - xy_coo[i].x) <= 2*radius)
412 && (abs(xy_coo[k].y - xy_coo[i].y) <= 2*radius)) { */
414 && ((xy_coo[k].x - xy_coo[i].x)*(xy_coo[k].x - xy_coo[i].x)
415 + (xy_coo[k].y - xy_coo[i].y)*(xy_coo[k].y - xy_coo[i].y)
416 <= 2*radius*2*radius)) {
421 xy_coo[k].xmove = xy_coo[i].xmove;
422 xy_coo[k].ymove = xy_coo[i].ymove;
430 /* make xy_coo[k] grow/shrink */
431 void swamp_thing(int k)
433 if (xy_coo[k].r >= radius)
434 xy_coo[k].r_change = -abs(xy_coo[k].r_change);
436 if (xy_coo[k].r <= 0) {
437 from = from_array[0];
439 xy_coo[k].r_change = abs(xy_coo[k].r_change);
441 xy_coo[k].r=xy_coo[k].r_change;
445 xy_coo[k].r = xy_coo[k].r + xy_coo[k].r_change;
447 if (xy_coo[k].r >= radius)
448 xy_coo[k].r = radius;
449 if (xy_coo[k].r <= 0)
452 from = from_array[xy_coo[k].r];
458 char *progclass = "Distort";
460 char *defaults [] = {
461 "*dontClearRoot: True",
462 #ifdef __sgi /* really, HAVE_READ_DISPLAY_EXTENSION */
475 #ifdef HAVE_XSHM_EXTENSION
476 "*useSHM: False", /* xshm turns out not to help. */
477 #endif /* HAVE_XSHM_EXTENSION */
481 XrmOptionDescRec options [] = {
482 { "-delay", ".delay", XrmoptionSepArg, 0 },
483 { "-radius", ".radius", XrmoptionSepArg, 0 },
484 { "-speed", ".speed", XrmoptionSepArg, 0 },
485 { "-number", ".number", XrmoptionSepArg, 0 },
486 { "-swamp", ".swamp", XrmoptionNoArg, "True" },
487 { "-bounce", ".bounce", XrmoptionNoArg, "True" },
488 { "-vortex", ".vortex", XrmoptionNoArg, "True" },
489 { "-magnify", ".magnify", XrmoptionNoArg, "True" },
490 { "-blackhole", ".blackhole", XrmoptionNoArg, "True" },
491 #ifdef HAVE_XSHM_EXTENSION
492 { "-shm", ".useSHM", XrmoptionNoArg, "True" },
493 { "-no-shm", ".useSHM", XrmoptionNoArg, "False" },
494 #endif /* HAVE_XSHM_EXTENSION */
499 void screenhack(Display *dpy, Window window)
503 init_distort (dpy, window);
505 for (k = 0; k < number; k++) {
511 if (delay) usleep(delay);