1 | /* xscreensaver, Copyright (c) 1993, 1995, 1996, 1997, 1998, 1999, 2003 |
---|
2 | * Jamie Zawinski <jwz@jwz.org> |
---|
3 | * |
---|
4 | * Permission to use, copy, modify, distribute, and sell this software and its |
---|
5 | * documentation for any purpose is hereby granted without fee, provided that |
---|
6 | * the above copyright notice appear in all copies and that both that |
---|
7 | * copyright notice and this permission notice appear in supporting |
---|
8 | * documentation. No representations are made about the suitability of this |
---|
9 | * software for any purpose. It is provided "as is" without express or |
---|
10 | * implied warranty. |
---|
11 | */ |
---|
12 | |
---|
13 | /* I wanted to lay down new circles with TV:ALU-ADD instead of TV:ALU-XOR, |
---|
14 | but X doesn't support arithmetic combinations of pixmaps!! What losers. |
---|
15 | I suppose I could crank out the 2's compliment math by hand, but that's |
---|
16 | a real drag... |
---|
17 | |
---|
18 | This would probably look good with shapes other than circles as well. |
---|
19 | |
---|
20 | */ |
---|
21 | |
---|
22 | #include "screenhack.h" |
---|
23 | #include <stdio.h> |
---|
24 | |
---|
25 | struct circle { |
---|
26 | int x, y, radius; |
---|
27 | int increment; |
---|
28 | int dx, dy; |
---|
29 | }; |
---|
30 | |
---|
31 | static enum color_mode { |
---|
32 | seuss_mode, ramp_mode, random_mode |
---|
33 | } cmode; |
---|
34 | |
---|
35 | |
---|
36 | static struct circle *circles; |
---|
37 | static int count, global_count; |
---|
38 | static Pixmap pixmap, buffer; |
---|
39 | static int width, height, global_inc; |
---|
40 | static int delay, delay2, cycle_delay; |
---|
41 | static unsigned long fg_pixel, bg_pixel; |
---|
42 | static GC draw_gc, erase_gc, copy_gc, merge_gc; |
---|
43 | static Bool anim_p; |
---|
44 | static Colormap cmap; |
---|
45 | |
---|
46 | static int ncolors; |
---|
47 | static XColor *colors; |
---|
48 | static Bool cycle_p; |
---|
49 | static int fg_index; |
---|
50 | static int bg_index; |
---|
51 | |
---|
52 | |
---|
53 | #define min(x,y) ((x)<(y)?(x):(y)) |
---|
54 | #define max(x,y) ((x)>(y)?(x):(y)) |
---|
55 | |
---|
56 | static void |
---|
57 | init_circles_1 (Display *dpy, Window window) |
---|
58 | { |
---|
59 | int i; |
---|
60 | count = (global_count ? global_count |
---|
61 | : (3 + (random () % max (1, (min (width, height) / 50))) |
---|
62 | + (random () % max (1, (min (width, height) / 50))))); |
---|
63 | circles = (struct circle *) malloc (count * sizeof (struct circle)); |
---|
64 | for (i = 0; i < count; i++) |
---|
65 | { |
---|
66 | circles [i].x = 10 + random () % (width - 20); |
---|
67 | circles [i].y = 10 + random () % (height - 20); |
---|
68 | if (global_inc) |
---|
69 | circles [i].increment = global_inc; |
---|
70 | else |
---|
71 | { /* prefer smaller increments to larger ones */ |
---|
72 | int j = 8; |
---|
73 | int inc = ((random()%j) + (random()%j) + (random()%j)) - ((j*3)/2); |
---|
74 | if (inc < 0) inc = -inc + 3; |
---|
75 | circles [i].increment = inc + 3; |
---|
76 | } |
---|
77 | circles [i].radius = random () % circles [i].increment; |
---|
78 | circles [i].dx = ((random () % 3) - 1) * (1 + random () % 5); |
---|
79 | circles [i].dy = ((random () % 3) - 1) * (1 + random () % 5); |
---|
80 | } |
---|
81 | } |
---|
82 | |
---|
83 | static void |
---|
84 | init_circles (Display *dpy, Window window) |
---|
85 | { |
---|
86 | XGCValues gcv; |
---|
87 | XWindowAttributes xgwa; |
---|
88 | char *mode_str = 0; |
---|
89 | XGetWindowAttributes (dpy, window, &xgwa); |
---|
90 | cmap = xgwa.colormap; |
---|
91 | global_count = get_integer_resource ("count", "Integer"); |
---|
92 | if (global_count < 0) global_count = 0; |
---|
93 | global_inc = get_integer_resource ("increment", "Integer"); |
---|
94 | if (global_inc < 0) global_inc = 0; |
---|
95 | anim_p = get_boolean_resource ("animate", "Boolean"); |
---|
96 | delay = get_integer_resource ("delay", "Integer"); |
---|
97 | delay2 = get_integer_resource ("delay2", "Integer") * 1000000; |
---|
98 | cycle_delay = get_integer_resource ("cycleDelay", "Integer"); |
---|
99 | mode_str = get_string_resource ("colorMode", "ColorMode"); |
---|
100 | if (! mode_str) cmode = random_mode; |
---|
101 | else if (!strcmp (mode_str, "seuss")) cmode = seuss_mode; |
---|
102 | else if (!strcmp (mode_str, "ramp")) cmode = ramp_mode; |
---|
103 | else if (!strcmp (mode_str, "random")) cmode = random_mode; |
---|
104 | else { |
---|
105 | fprintf (stderr, |
---|
106 | "%s: colorMode must be seuss, ramp, or random, not \"%s\"\n", |
---|
107 | progname, mode_str); |
---|
108 | exit (1); |
---|
109 | } |
---|
110 | |
---|
111 | if (mono_p) cmode = seuss_mode; |
---|
112 | if (cmode == random_mode) |
---|
113 | cmode = ((random()&3) == 1) ? ramp_mode : seuss_mode; |
---|
114 | |
---|
115 | if (cmode == ramp_mode) |
---|
116 | anim_p = False; /* This combo doesn't work right... */ |
---|
117 | |
---|
118 | ncolors = get_integer_resource ("colors", "Colors"); |
---|
119 | if (ncolors < 2) ncolors = 2; |
---|
120 | if (ncolors <= 2) mono_p = True; |
---|
121 | |
---|
122 | if (mono_p) |
---|
123 | colors = 0; |
---|
124 | else |
---|
125 | colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1)); |
---|
126 | |
---|
127 | cycle_p = mono_p ? False : get_boolean_resource ("cycle", "Cycle"); |
---|
128 | |
---|
129 | /* If the visual isn't color-indexed, don't bother trying to |
---|
130 | allocate writable cells. */ |
---|
131 | if (cycle_p && !has_writable_cells (xgwa.screen, xgwa.visual)) |
---|
132 | cycle_p = False; |
---|
133 | |
---|
134 | |
---|
135 | if (mono_p) |
---|
136 | ; |
---|
137 | else if (random() % (cmode == seuss_mode ? 2 : 10)) |
---|
138 | make_uniform_colormap (dpy, xgwa.visual, cmap, colors, &ncolors, |
---|
139 | True, &cycle_p, True); |
---|
140 | else |
---|
141 | make_smooth_colormap (dpy, xgwa.visual, cmap, colors, &ncolors, |
---|
142 | True, &cycle_p, True); |
---|
143 | |
---|
144 | if (ncolors <= 2) mono_p = True; |
---|
145 | if (mono_p) cycle_p = False; |
---|
146 | if (mono_p) cmode = seuss_mode; |
---|
147 | |
---|
148 | if (mono_p) |
---|
149 | { |
---|
150 | fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy, cmap); |
---|
151 | bg_pixel = get_pixel_resource ("background", "Background", dpy, cmap); |
---|
152 | } |
---|
153 | else |
---|
154 | { |
---|
155 | fg_index = 0; |
---|
156 | bg_index = ncolors / 4; |
---|
157 | if (fg_index == bg_index) bg_index++; |
---|
158 | fg_pixel = colors[fg_index].pixel; |
---|
159 | bg_pixel = colors[bg_index].pixel; |
---|
160 | } |
---|
161 | |
---|
162 | width = max (50, xgwa.width); |
---|
163 | height = max (50, xgwa.height); |
---|
164 | |
---|
165 | #ifdef DEBUG |
---|
166 | width/=2; height/=2; |
---|
167 | #endif |
---|
168 | |
---|
169 | pixmap = XCreatePixmap (dpy, window, width, height, 1); |
---|
170 | if (cmode == seuss_mode) |
---|
171 | buffer = XCreatePixmap (dpy, window, width, height, 1); |
---|
172 | else |
---|
173 | buffer = 0; |
---|
174 | |
---|
175 | gcv.foreground = 1; |
---|
176 | gcv.background = 0; |
---|
177 | draw_gc = XCreateGC (dpy, pixmap, GCForeground | GCBackground, &gcv); |
---|
178 | gcv.foreground = 0; |
---|
179 | erase_gc = XCreateGC (dpy, pixmap, GCForeground, &gcv); |
---|
180 | gcv.foreground = fg_pixel; |
---|
181 | gcv.background = bg_pixel; |
---|
182 | copy_gc = XCreateGC (dpy, window, GCForeground | GCBackground, &gcv); |
---|
183 | |
---|
184 | if (cmode == seuss_mode) |
---|
185 | { |
---|
186 | gcv.foreground = 1; |
---|
187 | gcv.background = 0; |
---|
188 | gcv.function = GXxor; |
---|
189 | merge_gc = XCreateGC (dpy, pixmap, |
---|
190 | GCForeground | GCBackground | GCFunction, &gcv); |
---|
191 | } |
---|
192 | else |
---|
193 | { |
---|
194 | gcv.foreground = fg_pixel; |
---|
195 | gcv.background = bg_pixel; |
---|
196 | gcv.function = GXcopy; |
---|
197 | merge_gc = XCreateGC (dpy, window, |
---|
198 | GCForeground | GCBackground | GCFunction, &gcv); |
---|
199 | } |
---|
200 | |
---|
201 | init_circles_1 (dpy, window); |
---|
202 | XClearWindow (dpy, window); |
---|
203 | if (buffer) XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height); |
---|
204 | } |
---|
205 | |
---|
206 | static void |
---|
207 | run_circles (Display *dpy, Window window) |
---|
208 | { |
---|
209 | int i; |
---|
210 | static int iterations = 0; |
---|
211 | static int oiterations = 0; |
---|
212 | static Bool first_time_p = True; |
---|
213 | Bool done = False; |
---|
214 | Bool inhibit_sleep = False; |
---|
215 | static int clear_tick = 0; |
---|
216 | |
---|
217 | XFillRectangle (dpy, pixmap, erase_gc, 0, 0, width, height); |
---|
218 | for (i = 0; i < count; i++) |
---|
219 | { |
---|
220 | int radius = circles [i].radius; |
---|
221 | int inc = circles [i].increment; |
---|
222 | |
---|
223 | if (! (iterations & 1)) /* never stop on an odd number of iterations */ |
---|
224 | ; |
---|
225 | else if (radius == 0) /* eschew inf */ |
---|
226 | ; |
---|
227 | else if (radius < 0) /* stop when the circles are points */ |
---|
228 | done = True; |
---|
229 | else /* stop when the circles fill the window */ |
---|
230 | { |
---|
231 | /* Probably there's a simpler way to ask the musical question, |
---|
232 | "is this square completely enclosed by this circle," but I've |
---|
233 | forgotten too much trig to know it... (That's not really the |
---|
234 | right question anyway, but the right question is too hard.) */ |
---|
235 | double x1 = ((double) (-circles [i].x)) / ((double) radius); |
---|
236 | double y1 = ((double) (-circles [i].y)) / ((double) radius); |
---|
237 | double x2 = ((double) (width - circles [i].x)) / ((double) radius); |
---|
238 | double y2 = ((double) (height - circles [i].y)) / ((double) radius); |
---|
239 | x1 *= x1; x2 *= x2; y1 *= y1; y2 *= y2; |
---|
240 | if ((x1 + y1) < 1 && (x2 + y2) < 1 && (x1 + y2) < 1 && (x2 + y1) < 1) |
---|
241 | done = True; |
---|
242 | } |
---|
243 | |
---|
244 | if (radius > 0 && |
---|
245 | (cmode == seuss_mode || /* drawing all circles, or */ |
---|
246 | circles [0].increment < 0)) /* on the way back in */ |
---|
247 | { |
---|
248 | XFillArc (dpy, |
---|
249 | (cmode == seuss_mode ? pixmap : window), |
---|
250 | (cmode == seuss_mode ? draw_gc : merge_gc), |
---|
251 | circles [i].x - radius, circles [i].y - radius, |
---|
252 | radius * 2, radius * 2, 0, 360*64); |
---|
253 | } |
---|
254 | circles [i].radius += inc; |
---|
255 | } |
---|
256 | |
---|
257 | if (cycle_p && cmode != seuss_mode) |
---|
258 | { |
---|
259 | struct timeval now; |
---|
260 | static struct timeval then = { 0, 0 }; |
---|
261 | unsigned long diff; |
---|
262 | #ifdef GETTIMEOFDAY_TWO_ARGS |
---|
263 | struct timezone tzp; |
---|
264 | gettimeofday(&now, &tzp); |
---|
265 | #else |
---|
266 | gettimeofday(&now); |
---|
267 | #endif |
---|
268 | diff = (((now.tv_sec - then.tv_sec) * 1000000) + |
---|
269 | (now.tv_usec - then.tv_usec)); |
---|
270 | if (diff > cycle_delay) |
---|
271 | { |
---|
272 | rotate_colors (dpy, cmap, colors, ncolors, 1); |
---|
273 | then = now; |
---|
274 | } |
---|
275 | } |
---|
276 | |
---|
277 | if (anim_p && !first_time_p) |
---|
278 | inhibit_sleep = !done; |
---|
279 | |
---|
280 | if (done) |
---|
281 | { |
---|
282 | if (anim_p) |
---|
283 | { |
---|
284 | first_time_p = False; |
---|
285 | for (i = 0; i < count; i++) |
---|
286 | { |
---|
287 | circles [i].x += circles [i].dx; |
---|
288 | circles [i].y += circles [i].dy; |
---|
289 | circles [i].radius %= circles [i].increment; |
---|
290 | if (circles [i].x < 0 || circles [i].x >= width) |
---|
291 | { |
---|
292 | circles [i].dx = -circles [i].dx; |
---|
293 | circles [i].x += (2 * circles [i].dx); |
---|
294 | } |
---|
295 | if (circles [i].y < 0 || circles [i].y >= height) |
---|
296 | { |
---|
297 | circles [i].dy = -circles [i].dy; |
---|
298 | circles [i].y += (2 * circles [i].dy); |
---|
299 | } |
---|
300 | } |
---|
301 | } |
---|
302 | else if (circles [0].increment < 0) |
---|
303 | { |
---|
304 | /* We've zoomed out and the screen is blank -- re-pick the |
---|
305 | center points, and shift the colors. |
---|
306 | */ |
---|
307 | free (circles); |
---|
308 | init_circles_1 (dpy, window); |
---|
309 | if (! mono_p) |
---|
310 | { |
---|
311 | fg_index = (fg_index + 1) % ncolors; |
---|
312 | bg_index = (fg_index + (ncolors/2)) % ncolors; |
---|
313 | XSetForeground (dpy, copy_gc, colors [fg_index].pixel); |
---|
314 | XSetBackground (dpy, copy_gc, colors [bg_index].pixel); |
---|
315 | } |
---|
316 | } |
---|
317 | /* Sometimes go out from the inside instead of the outside */ |
---|
318 | else if (clear_tick == 0 && ((random () % 3) == 0)) |
---|
319 | { |
---|
320 | iterations = 0; /* ick */ |
---|
321 | for (i = 0; i < count; i++) |
---|
322 | circles [i].radius %= circles [i].increment; |
---|
323 | |
---|
324 | clear_tick = ((random() % 8) + 4) | 1; /* must be odd */ |
---|
325 | } |
---|
326 | else |
---|
327 | { |
---|
328 | oiterations = iterations; |
---|
329 | for (i = 0; i < count; i++) |
---|
330 | { |
---|
331 | circles [i].increment = -circles [i].increment; |
---|
332 | circles [i].radius += (2 * circles [i].increment); |
---|
333 | } |
---|
334 | } |
---|
335 | } |
---|
336 | |
---|
337 | if (buffer) |
---|
338 | XCopyPlane (dpy, pixmap, buffer, merge_gc, 0, 0, width, height, 0, 0, 1); |
---|
339 | else if (cmode != seuss_mode) |
---|
340 | { |
---|
341 | |
---|
342 | if (!mono_p) |
---|
343 | { |
---|
344 | fg_index++; |
---|
345 | bg_index++; |
---|
346 | if (fg_index >= ncolors) fg_index = 0; |
---|
347 | if (bg_index >= ncolors) bg_index = 0; |
---|
348 | XSetForeground (dpy, merge_gc, colors [fg_index].pixel); |
---|
349 | } |
---|
350 | |
---|
351 | if (circles [0].increment >= 0) |
---|
352 | inhibit_sleep = True; |
---|
353 | else if (done && cmode == seuss_mode) |
---|
354 | XFillRectangle (dpy, window, merge_gc, 0, 0, width, height); |
---|
355 | } |
---|
356 | else |
---|
357 | XCopyPlane (dpy, pixmap, window, merge_gc, 0, 0, width, height, 0, 0, 1); |
---|
358 | |
---|
359 | /* buffer is only used in seuss-mode or anim-mode */ |
---|
360 | if (buffer && (anim_p |
---|
361 | ? (done || (first_time_p && (iterations & 1))) |
---|
362 | : (iterations & 1))) |
---|
363 | { |
---|
364 | XCopyPlane (dpy, buffer, window, copy_gc, 0, 0, width, height, 0, 0, 1); |
---|
365 | XSync (dpy, False); |
---|
366 | if (anim_p && done) |
---|
367 | XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height); |
---|
368 | } |
---|
369 | |
---|
370 | #ifdef DEBUG |
---|
371 | XCopyPlane (dpy, pixmap, window, copy_gc, 0,0,width,height,width,height, 1); |
---|
372 | if (buffer) |
---|
373 | XCopyPlane (dpy, buffer, window, copy_gc, 0,0,width,height,0,height, 1); |
---|
374 | XSync (dpy, False); |
---|
375 | #endif |
---|
376 | |
---|
377 | if (done) |
---|
378 | iterations = 0; |
---|
379 | else |
---|
380 | iterations++; |
---|
381 | |
---|
382 | if (delay && !inhibit_sleep) |
---|
383 | { |
---|
384 | static Bool really_first_p = True; |
---|
385 | int direction = 1; |
---|
386 | int d = delay; |
---|
387 | if (done && cycle_p && cmode != seuss_mode && !really_first_p) |
---|
388 | { |
---|
389 | d = delay2; |
---|
390 | if (! (random() % 10)) |
---|
391 | direction = -1; |
---|
392 | } |
---|
393 | |
---|
394 | XSync(dpy, False); |
---|
395 | screenhack_handle_events (dpy); |
---|
396 | |
---|
397 | if (cycle_p && cycle_delay) |
---|
398 | { |
---|
399 | i = 0; |
---|
400 | while (i < d) |
---|
401 | { |
---|
402 | rotate_colors (dpy, cmap, colors, ncolors, direction); |
---|
403 | usleep(cycle_delay); |
---|
404 | screenhack_handle_events (dpy); |
---|
405 | i += cycle_delay; |
---|
406 | } |
---|
407 | } |
---|
408 | else if (cmode != seuss_mode && |
---|
409 | done && !really_first_p && cycle_delay > 0) |
---|
410 | usleep (cycle_delay * 50); |
---|
411 | else |
---|
412 | usleep (d); |
---|
413 | |
---|
414 | if (done) |
---|
415 | really_first_p = False; |
---|
416 | } |
---|
417 | |
---|
418 | if (done && clear_tick > 0) |
---|
419 | { |
---|
420 | clear_tick--; |
---|
421 | if (!clear_tick) |
---|
422 | { |
---|
423 | XClearWindow (dpy, window); |
---|
424 | if (buffer) XFillRectangle (dpy, buffer, erase_gc, 0,0,width,height); |
---|
425 | } |
---|
426 | } |
---|
427 | } |
---|
428 | |
---|
429 | |
---|
430 | char *progclass = "Halo"; |
---|
431 | |
---|
432 | char *defaults [] = { |
---|
433 | ".background: black", |
---|
434 | ".foreground: white", |
---|
435 | "*colorMode: random", |
---|
436 | "*colors: 100", |
---|
437 | "*cycle: true", |
---|
438 | "*count: 0", |
---|
439 | "*delay: 100000", |
---|
440 | "*delay2: 20", |
---|
441 | "*cycleDelay: 100000", |
---|
442 | 0 |
---|
443 | }; |
---|
444 | |
---|
445 | XrmOptionDescRec options [] = { |
---|
446 | { "-count", ".count", XrmoptionSepArg, 0 }, |
---|
447 | { "-delay", ".delay", XrmoptionSepArg, 0 }, |
---|
448 | { "-cycle-delay", ".cycleDelay", XrmoptionSepArg, 0 }, |
---|
449 | { "-animate", ".animate", XrmoptionNoArg, "True" }, |
---|
450 | { "-mode", ".colorMode", XrmoptionSepArg, 0 }, |
---|
451 | { "-colors", ".colors", XrmoptionSepArg, 0 }, |
---|
452 | { "-cycle", ".cycle", XrmoptionNoArg, "True" }, |
---|
453 | { "-no-cycle", ".cycle", XrmoptionNoArg, "False" }, |
---|
454 | { 0, 0, 0, 0 } |
---|
455 | }; |
---|
456 | |
---|
457 | void |
---|
458 | screenhack (Display *dpy, Window window) |
---|
459 | { |
---|
460 | init_circles (dpy, window); |
---|
461 | while (1) |
---|
462 | { |
---|
463 | run_circles (dpy, window); |
---|
464 | screenhack_handle_events (dpy); |
---|
465 | } |
---|
466 | } |
---|