1 /* xscreensaver, Copyright (c) 1992-2013 Jamie Zawinski <jwz@jwz.org>
2 *
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
9 * implied warranty.
10 */
11
12 /* 18-Sep-97: Johannes Keukelaar <johannes@nada.kth.se>: Added some color.
13 * Using -mono gives the old behaviour. (Modified by jwz.)
14 */
15 /* Flying through an asteroid field. Based on TI Explorer Lisp code by
16 John Nguyen <johnn@hx.lcs.mit.edu>
17 */
18
19 #include <stdio.h>
20 #include <math.h>
21 #include "screenhack.h"
22
23 #define MIN_ROCKS 1
24 #define MIN_DEPTH 2 /* rocks disappear when they get this close */
25 #define MAX_DEPTH 60 /* this is where rocks appear */
26 #define MIN_SIZE 3 /* how small where pixmaps are not used */
27 #define MAX_SIZE 200 /* how big (in pixels) rocks are at depth 1 */
28 #define DEPTH_SCALE 100 /* how many ticks there are between depths */
29 #define SIN_RESOLUTION 1000
30
31 #define MAX_DEP 0.3 /* how far the displacement can be (percent) */
32 #define DIRECTION_CHANGE_RATE 60
33 #define MAX_DEP_SPEED 5 /* Maximum speed for movement */
34 #define MOVE_STYLE 0 /* Only 0 and 1. Distinguishes the fact that
35 these are the rocks that are moving (1)
36 or the rocks source (0). */
37
38 /* there's not much point in the above being user-customizable, but those
39 numbers might want to be tweaked for displays with an order of magnitude
40 higher resolution or compute power.
41 */
42
43 struct state {
44 Display *dpy;
45 Window window;
46
47 double sins [SIN_RESOLUTION];
48 double coss [SIN_RESOLUTION];
49 double depths [(MAX_DEPTH + 1) * DEPTH_SCALE];
50
51 int width, height, midx, midy;
52 int dep_x, dep_y;
53 int ncolors;
54 XColor *colors;
55 float max_dep;
56 GC erase_gc;
57 GC *draw_gcs;
58 Bool rotate_p;
59 Bool move_p;
60 int speed;
61 Bool threed;
62 GC threed_left_gc, threed_right_gc;
63 double threed_delta;
64
65 struct rock *rocks;
66 int nrocks;
67 Pixmap pixmaps [MAX_SIZE];
68 int delay;
69
70 int move_current_dep[2];
71 int move_speed[2];
72 short move_direction[2];
73 int move_limit[2];
74
75 int current_delta; /* observer Z rotation */
76 int new_delta;
77 int dchange_tick;
78 };
79
80
81
82 #define GETZDIFF(z) \
83 (st->threed_delta * 40.0 * \
84 (1.0 - ((MAX_DEPTH * DEPTH_SCALE / 2) / \
85 ((z) + 20.0 * DEPTH_SCALE))))
86
87 struct rock {
88 int real_size;
89 int r;
90 int theta;
91 int depth;
92 int size, x, y;
93 int diff;
94 int color;
95 };
96
97 static void rock_compute (struct state *, struct rock *);
98 static void rock_draw (struct state *, struct rock *, Bool draw_p);
99
100 static void
rock_reset(struct state * st,struct rock * rock)101 rock_reset (struct state *st, struct rock *rock)
102 {
103 rock->real_size = MAX_SIZE;
104 rock->r = (SIN_RESOLUTION * 0.7) + (random () % (30 * SIN_RESOLUTION));
105 rock->theta = random () % SIN_RESOLUTION;
106 rock->depth = MAX_DEPTH * DEPTH_SCALE;
107 rock->color = random() % st->ncolors;
108 rock_compute (st, rock);
109 rock_draw (st, rock, True);
110 }
111
112 static void
rock_tick(struct state * st,struct rock * rock,int d)113 rock_tick (struct state *st, struct rock *rock, int d)
114 {
115 if (rock->depth > 0)
116 {
117 rock_draw (st, rock, False);
118 rock->depth -= st->speed;
119 if (st->rotate_p)
120 {
121 rock->theta = (rock->theta + d) % SIN_RESOLUTION;
122 }
123 while (rock->theta < 0)
124 rock->theta += SIN_RESOLUTION;
125 if (rock->depth < (MIN_DEPTH * DEPTH_SCALE))
126 rock->depth = 0;
127 else
128 {
129 rock_compute (st, rock);
130 rock_draw (st, rock, True);
131 }
132 }
133 else if ((random () % 40) == 0)
134 rock_reset (st, rock);
135 }
136
137 static void
rock_compute(struct state * st,struct rock * rock)138 rock_compute (struct state *st, struct rock *rock)
139 {
140 double factor = st->depths [rock->depth];
141 double rsize = rock->real_size * factor;
142
143 rock->size = (int) (rsize + 0.5);
144 rock->diff = (int) GETZDIFF(rock->depth);
145 rock->x = st->midx + (st->coss [rock->theta] * rock->r * factor);
146 rock->y = st->midy + (st->sins [rock->theta] * rock->r * factor);
147
148 if (st->move_p)
149 {
150 double move_factor = (((double) MOVE_STYLE) -
151 (((double) rock->depth) /
152 (((double) (MAX_DEPTH + 1)) *
153 ((double) DEPTH_SCALE))));
154 /* move_factor is 0 when the rock is close, 1 when far */
155 rock->x += (((double) st->dep_x) * move_factor);
156 rock->y += (((double) st->dep_y) * move_factor);
157 }
158 }
159
160 static void
rock_draw(struct state * st,struct rock * rock,Bool draw_p)161 rock_draw (struct state *st, struct rock *rock, Bool draw_p)
162 {
163 GC gc = (draw_p
164 ? (st->threed ? st->erase_gc : st->draw_gcs[rock->color])
165 : st->erase_gc);
166
167 if (rock->x <= 0 || rock->y <= 0 || rock->x >= st->width || rock->y >= st->height)
168 {
169 /* this means that if a rock were to go off the screen at 12:00, but
170 would have been visible at 3:00, it won't come back once the observer
171 rotates around so that the rock would have been visible again.
172 Oh well.
173 */
174 if (!st->move_p)
175 rock->depth = 0;
176 return;
177 }
178 if (rock->size <= 1)
179 {
180 if (st->threed)
181 {
182 if (draw_p) gc = st->threed_left_gc;
183 XDrawPoint (st->dpy, st->window, gc, rock->x - rock->diff, rock->y);
184 if (draw_p) gc = st->threed_right_gc;
185 XDrawPoint (st->dpy, st->window, gc, rock->x + rock->diff, rock->y);
186 }
187 else
188 {
189 XDrawPoint (st->dpy, st->window, gc, rock->x, rock->y);
190 }
191 }
192 else if (rock->size <= MIN_SIZE || !draw_p)
193 {
194 if (st->threed)
195 {
196 if (draw_p) gc = st->threed_left_gc;
197 XFillRectangle(st->dpy, st->window, gc,
198 rock->x - rock->size / 2 - rock->diff,
199 rock->y - rock->size / 2,
200 rock->size, rock->size);
201 if (draw_p) gc = st->threed_right_gc;
202 XFillRectangle(st->dpy, st->window, gc,
203 rock->x - rock->size / 2 + rock->diff,
204 rock->y - rock->size / 2,
205 rock->size, rock->size);
206 }
207 else
208 {
209 XFillRectangle (st->dpy, st->window, gc,
210 rock->x - rock->size/2, rock->y - rock->size/2,
211 rock->size, rock->size);
212 }
213 }
214 else if (rock->size < MAX_SIZE)
215 {
216 if (st->threed)
217 {
218 gc = st->threed_left_gc;
219 XCopyPlane(st->dpy, st->pixmaps[rock->size], st->window, gc,
220 0, 0, rock->size, rock->size,
221 rock->x - rock->size / 2 - rock->diff,
222 rock->y - rock->size / 2, 1L);
223 gc = st->threed_right_gc;
224 XCopyPlane(st->dpy, st->pixmaps[rock->size], st->window, gc,
225 0, 0, rock->size, rock->size,
226 rock->x - rock->size / 2 + rock->diff,
227 rock->y - rock->size / 2, 1L);
228 }
229 else
230 {
231 XCopyPlane (st->dpy, st->pixmaps [rock->size], st->window, gc,
232 0, 0, rock->size, rock->size,
233 rock->x - rock->size/2, rock->y - rock->size/2,
234 1L);
235 }
236 }
237 }
238
239
240 static void
init_pixmaps(struct state * st)241 init_pixmaps (struct state *st)
242 {
243 int i;
244 XGCValues gcv;
245 GC fg_gc = 0, bg_gc = 0;
246 st->pixmaps [0] = st->pixmaps [1] = 0;
247 for (i = MIN_DEPTH; i < MAX_SIZE; i++)
248 {
249 int w = (1+(i/32))<<5; /* server might be faster if word-aligned */
250 int h = i;
251 Pixmap p = XCreatePixmap (st->dpy, st->window, w, h, 1);
252 XPoint points [7];
253 st->pixmaps [i] = p;
254 if (! p)
255 {
256 fprintf (stderr, "%s: couldn't allocate pixmaps", progname);
257 exit (1);
258 }
259 if (! fg_gc)
260 { /* must use drawable of pixmap, not window (fmh) */
261 gcv.foreground = 1;
262 fg_gc = XCreateGC (st->dpy, p, GCForeground, &gcv);
263 gcv.foreground = 0;
264 bg_gc = XCreateGC (st->dpy, p, GCForeground, &gcv);
265 }
266 XFillRectangle (st->dpy, p, bg_gc, 0, 0, w, h);
267 points [0].x = i * 0.15; points [0].y = i * 0.85;
268 points [1].x = i * 0.00; points [1].y = i * 0.20;
269 points [2].x = i * 0.30; points [2].y = i * 0.00;
270 points [3].x = i * 0.40; points [3].y = i * 0.10;
271 points [4].x = i * 0.90; points [4].y = i * 0.10;
272 points [5].x = i * 1.00; points [5].y = i * 0.55;
273 points [6].x = i * 0.45; points [6].y = i * 1.00;
274 XFillPolygon (st->dpy, p, fg_gc, points, 7, Nonconvex, CoordModeOrigin);
275 }
276 XFreeGC (st->dpy, fg_gc);
277 XFreeGC (st->dpy, bg_gc);
278 }
279
280
281 static int
compute_move(struct state * st,int axe)282 compute_move(struct state *st, int axe) /* 0 for x, 1 for y */
283 {
284 int change = 0;
285
286 st->move_limit[0] = st->midx;
287 st->move_limit[1] = st->midy;
288
289 st->move_current_dep[axe] += st->move_speed[axe]; /* We adjust the displacement */
290
291 if (st->move_current_dep[axe] > (int) (st->move_limit[axe] * st->max_dep))
292 {
293 if (st->move_current_dep[axe] > st->move_limit[axe])
294 st->move_current_dep[axe] = st->move_limit[axe];
295 st->move_direction[axe] = -1;
296 } /* This is when we reach the upper screen limit */
297 if (st->move_current_dep[axe] < (int) (-st->move_limit[axe] * st->max_dep))
298 {
299 if (st->move_current_dep[axe] < -st->move_limit[axe])
300 st->move_current_dep[axe] = -st->move_limit[axe];
301 st->move_direction[axe] = 1;
302 } /* This is when we reach the lower screen limit */
303 if (st->move_direction[axe] == 1) /* We adjust the speed */
304 st->move_speed[axe] += 1;
305 else if (st->move_direction[axe] == -1)
306 st->move_speed[axe] -= 1;
307
308 if (st->move_speed[axe] > MAX_DEP_SPEED)
309 st->move_speed[axe] = MAX_DEP_SPEED;
310 else if (st->move_speed[axe] < -MAX_DEP_SPEED)
311 st->move_speed[axe] = -MAX_DEP_SPEED;
312
313 if (st->move_p && !(random() % DIRECTION_CHANGE_RATE))
314 {
315 /* We change direction */
316 change = random() & 1;
317 if (change != 1)
318 {
319 if (st->move_direction[axe] == 0)
320 st->move_direction[axe] = change - 1; /* 0 becomes either 1 or -1 */
321 else
322 st->move_direction[axe] = 0; /* -1 or 1 become 0 */
323 }
324 }
325 return (st->move_current_dep[axe]);
326 }
327
328 static void
tick_rocks(struct state * st,int d)329 tick_rocks (struct state *st, int d)
330 {
331 int i;
332
333 if (st->move_p)
334 {
335 st->dep_x = compute_move(st, 0);
336 st->dep_y = compute_move(st, 1);
337 }
338
339 for (i = 0; i < st->nrocks; i++)
340 rock_tick (st, &st->rocks [i], d);
341 }
342
343
344 static unsigned long
rocks_draw(Display * dpy,Window window,void * closure)345 rocks_draw (Display *dpy, Window window, void *closure)
346 {
347 struct state *st = (struct state *) closure;
348 if (st->current_delta != st->new_delta)
349 {
350 if (st->dchange_tick++ == 5)
351 {
352 st->dchange_tick = 0;
353 if (st->current_delta < st->new_delta)
354 st->current_delta++;
355 else
356 st->current_delta--;
357 }
358 }
359 else
360 {
361 if (! (random() % 50))
362 {
363 st->new_delta = ((random() % 11) - 5);
364 if (! (random() % 10))
365 st->new_delta *= 5;
366 }
367 }
368 tick_rocks (st, st->current_delta);
369
370 return st->delay;
371 }
372
373 static void *
rocks_init(Display * d,Window w)374 rocks_init (Display *d, Window w)
375 {
376 struct state *st = (struct state *) calloc (1, sizeof(*st));
377 int i;
378 XGCValues gcv;
379 Colormap cmap;
380 XWindowAttributes xgwa;
381 unsigned int bg;
382 st->dpy = d;
383 st->window = w;
384 XGetWindowAttributes (st->dpy, st->window, &xgwa);
385
386 st->width = xgwa.width;
387 st->height = xgwa.height;
388 st->midx = st->width/2;
389 st->midy = st->height/2;
390
391 cmap = xgwa.colormap;
392 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
393 if (st->delay < 0) st->delay = 0;
394 st->speed = get_integer_resource (st->dpy, "speed", "Integer");
395 if (st->speed < 1) st->speed = 1;
396 if (st->speed > 100) st->speed = 100;
397 st->rotate_p = get_boolean_resource (st->dpy, "rotate", "Boolean");
398 st->move_p = get_boolean_resource (st->dpy, "move", "Boolean");
399 if (mono_p)
400 st->ncolors = 2;
401 else
402 st->ncolors = get_integer_resource (st->dpy, "colors", "Colors");
403
404 if (st->ncolors < 2)
405 {
406 st->ncolors = 2;
407 mono_p = True;
408 }
409
410 st->colors = (XColor *) malloc(st->ncolors * sizeof(*st->colors));
411 st->draw_gcs = (GC *) malloc(st->ncolors * sizeof(*st->draw_gcs));
412
413 bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
414 st->colors[0].pixel = bg;
415 st->colors[0].flags = DoRed|DoGreen|DoBlue;
416 XQueryColor(st->dpy, cmap, &st->colors[0]);
417
418 st->ncolors--;
419 make_random_colormap(xgwa.screen, xgwa.visual, cmap,
420 st->colors+1, &st->ncolors, True,
421 True, 0, True);
422 st->ncolors++;
423
424 if (st->ncolors < 2)
425 {
426 st->ncolors = 2;
427 mono_p = True;
428 }
429
430 if (mono_p)
431 {
432 unsigned int fg = get_pixel_resource(st->dpy, cmap, "foreground", "Foreground");
433 st->colors[1].pixel = fg;
434 st->colors[1].flags = DoRed|DoGreen|DoBlue;
435 XQueryColor(st->dpy, cmap, &st->colors[1]);
436 gcv.foreground = fg;
437 gcv.background = bg;
438 st->draw_gcs[0] = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
439 st->draw_gcs[1] = st->draw_gcs[0];
440 }
441 else
442 for( i = 0; i < st->ncolors; i++ )
443 {
444 gcv.foreground = st->colors[i].pixel;
445 gcv.background = bg;
446 st->draw_gcs[i] = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
447 }
448
449 gcv.foreground = bg;
450 st->erase_gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
451
452 st->max_dep = (st->move_p ? MAX_DEP : 0);
453
454 for (i = 0; i < SIN_RESOLUTION; i++)
455 {
456 st->sins [i] = sin ((((double) i) / (SIN_RESOLUTION / 2)) * M_PI);
457 st->coss [i] = cos ((((double) i) / (SIN_RESOLUTION / 2)) * M_PI);
458 }
459 /* we actually only need i/speed of these, but wtf */
460 for (i = 1; i < (sizeof (st->depths) / sizeof (st->depths[0])); i++)
461 st->depths [i] = atan (((double) 0.5) / (((double) i) / DEPTH_SCALE));
462 st->depths [0] = M_PI/2; /* avoid division by 0 */
463
464 st->threed = get_boolean_resource(st->dpy, "use3d", "Boolean");
465 if (st->threed)
466 {
467 gcv.background = bg;
468 gcv.foreground = get_pixel_resource (st->dpy, cmap, "left3d", "Foreground");
469 st->threed_left_gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground,&gcv);
470 gcv.foreground = get_pixel_resource (st->dpy, cmap, "right3d", "Foreground");
471 st->threed_right_gc = XCreateGC (st->dpy, st->window,GCForeground|GCBackground,&gcv);
472 st->threed_delta = get_float_resource(st->dpy, "delta3d", "Integer");
473 }
474
475 /* don't want any exposure events from XCopyPlane */
476 for( i = 0; i < st->ncolors; i++)
477 XSetGraphicsExposures (st->dpy, st->draw_gcs[i], False);
478 XSetGraphicsExposures (st->dpy, st->erase_gc, False);
479
480 st->nrocks = get_integer_resource (st->dpy, "count", "Count");
481 if (st->nrocks < 1) st->nrocks = 1;
482 st->rocks = (struct rock *) calloc (st->nrocks, sizeof (struct rock));
483 init_pixmaps (st);
484 XClearWindow (st->dpy, st->window);
485 return st;
486 }
487
488 static void
rocks_reshape(Display * dpy,Window window,void * closure,unsigned int w,unsigned int h)489 rocks_reshape (Display *dpy, Window window, void *closure,
490 unsigned int w, unsigned int h)
491 {
492 struct state *st = (struct state *) closure;
493 st->width = w;
494 st->height = h;
495 st->midx = st->width/2;
496 st->midy = st->height/2;
497 }
498
499 static Bool
rocks_event(Display * dpy,Window window,void * closure,XEvent * event)500 rocks_event (Display *dpy, Window window, void *closure, XEvent *event)
501 {
502 return False;
503 }
504
505 static void
rocks_free(Display * dpy,Window window,void * closure)506 rocks_free (Display *dpy, Window window, void *closure)
507 {
508 struct state *st = (struct state *) closure;
509 int i;
510 for (i = 0; i < MAX_SIZE; i++)
511 if (st->pixmaps[i]) XFreePixmap (dpy, st->pixmaps[i]);
512 for (i = 0; i < st->ncolors; i++)
513 if (st->draw_gcs[i]) XFreeGC (dpy, st->draw_gcs[i]);
514 XFreeGC (dpy, st->erase_gc);
515 free (st->colors);
516 free (st->rocks);
517 free (st->draw_gcs);
518 free (st);
519 }
520
521
522
523 static const char *rocks_defaults [] = {
524 ".lowrez: true",
525 ".background: Black",
526 ".foreground: #E9967A",
527 "*fpsSolid: true",
528 "*colors: 5",
529 "*count: 100",
530 "*delay: 50000",
531 "*speed: 100",
532 "*rotate: true",
533 "*move: true",
534 "*use3d: False",
535 "*left3d: Blue",
536 "*right3d: Red",
537 "*delta3d: 1.5",
538 #ifdef HAVE_MOBILE
539 "*ignoreRotation: True",
540 #endif
541 0
542 };
543
544 static XrmOptionDescRec rocks_options [] = {
545 { "-count", ".count", XrmoptionSepArg, 0 },
546 { "-rotate", ".rotate", XrmoptionNoArg, "true" },
547 { "-no-rotate", ".rotate", XrmoptionNoArg, "false" },
548 { "-move", ".move", XrmoptionNoArg, "true" },
549 { "-no-move", ".move", XrmoptionNoArg, "false" },
550 { "-delay", ".delay", XrmoptionSepArg, 0 },
551 { "-speed", ".speed", XrmoptionSepArg, 0 },
552 {"-3d", ".use3d", XrmoptionNoArg, "True"},
553 {"-no-3d", ".use3d", XrmoptionNoArg, "False"},
554 {"-left3d", ".left3d", XrmoptionSepArg, 0 },
555 {"-right3d", ".right3d", XrmoptionSepArg, 0 },
556 {"-delta3d", ".delta3d", XrmoptionSepArg, 0 },
557 { "-colors", ".colors", XrmoptionSepArg, 0 },
558 { 0, 0, 0, 0 }
559 };
560
561 XSCREENSAVER_MODULE ("Rocks", rocks)
562