1 /* xscreensaver, Copyright (c) 1997-2008 Jamie Zawinski <jwz@jwz.org>
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
13 #include "screenhack.h"
18 /* This is pretty compute-intensive, probably due to the large number of
19 polygon fills. I tried introducing a scaling factor to make the spline
20 code emit fewer line segments, but that made the edges very rough.
21 However, tuning *maxVelocity, *elasticity and *delay can result in much
22 smoother looking animation. I tuned these for a 1280x1024 Indy display,
23 but I don't know whether these values will be reasonable for a slower
26 The more planes the better -- SGIs have a 12-bit pseudocolor display
27 (4096 colormap cells) which is mostly useless, except for this program,
28 where it means you can have 11 or 12 mutually-transparent objects instead
29 of only 7 or 8. But, if you are using the 12-bit visual, you should crank
30 down the velocity and elasticity, or server slowness will cause the
31 animation to look jerky (yes, it's sad but true, SGI's X server is
32 perceptibly slower when using plane masks on a 12-bit visual than on an
33 8-bit visual.) Using -max-velocity 0.5 -elasticity 0.9 seems to work ok
34 on my Indy R5k with visual 0x27 and the bottom-of-the-line 24-bit graphics
37 It might look better if each blob had an outline, which was a *slightly*
38 darker color than the center, to give them a bit more definition -- but
39 that would mean using two planes per blob. (Or maybe allocating the
40 outline colors outside of the plane-space? Then the outlines wouldn't be
41 transparent, but maybe that wouldn't be so noticeable?)
43 Oh, for an alpha channel... maybe I should rewrite this in GL. Then the
44 blobs could have thickness, and curved edges with specular reflections...
47 #define SCALE 10000 /* fixed-point math, for sub-pixel motion */
48 #define DEF_COUNT 12 /* When planes and count are 0, how many blobs. */
50 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
51 #define RANDSIGN() ((random() & 1) ? 1 : -1)
54 long x, y; /* position of midpoint */
55 long dx, dy; /* velocity and direction */
56 double torque; /* rotational speed */
57 double th; /* angle of rotation */
58 long elasticity; /* how fast they deform */
59 long max_velocity; /* speed limit */
60 long min_r, max_r; /* radius range */
61 int npoints; /* control points */
85 struct layer **layers;
86 unsigned long background;
97 make_blob (Display *dpy, int maxx, int maxy, int size)
99 struct blob *b = (struct blob *) calloc(1, sizeof(*b));
110 if (b->min_r < (5*SCALE)) b->min_r = (5*SCALE);
111 mid = ((b->min_r + b->max_r) / 2);
113 b->torque = get_float_resource (dpy, "torque", "Torque");
114 b->elasticity = SCALE * get_float_resource (dpy, "elasticity", "Elasticity");
115 b->max_velocity = SCALE * get_float_resource (dpy, "maxVelocity", "MaxVelocity");
120 b->dx = RAND(b->max_velocity) * RANDSIGN();
121 b->dy = RAND(b->max_velocity) * RANDSIGN();
122 b->th = frand(M_PI+M_PI) * RANDSIGN();
123 b->npoints = (random() % 5) + 5;
125 b->spline = make_spline (b->npoints);
126 b->r = (long *) malloc (sizeof(*b->r) * b->npoints);
127 for (i = 0; i < b->npoints; i++)
128 b->r[i] = (long) ((random() % mid) + (mid/2)) * RANDSIGN();
133 free_blob(struct blob *blob)
135 free_spline(blob->spline);
141 throb_blob (struct blob *b)
144 double frac = ((M_PI+M_PI) / b->npoints);
146 for (i = 0; i < b->npoints; i++)
149 long ra = (r > 0 ? r : -r);
150 double th = (b->th > 0 ? b->th : -b->th);
153 /* place control points evenly around perimiter, shifted by theta */
154 x = b->x + ra * cos (i * frac + th);
155 y = b->y + ra * sin (i * frac + th);
157 b->spline->control_x[i] = x / SCALE;
158 b->spline->control_y[i] = y / SCALE;
160 /* alter the radius by a random amount, in the direction in which
161 it had been going (the sign of the radius indicates direction.) */
162 ra += (RAND(b->elasticity) * (r > 0 ? 1 : -1));
163 r = ra * (r >= 0 ? 1 : -1);
165 /* If we've reached the end (too long or too short) reverse direction. */
166 if ((ra > b->max_r && r >= 0) ||
167 (ra < b->min_r && r < 0))
169 /* And reverse direction in mid-course once every 50 times. */
170 else if (! (random() % 50))
178 move_blob (struct blob *b, int maxx, int maxy)
186 /* If we've reached the edge of the box, reverse direction. */
187 if ((b->x > maxx && b->dx >= 0) ||
188 (b->x < 0 && b->dx < 0))
192 if ((b->y > maxy && b->dy >= 0) ||
193 (b->y < 0 && b->dy < 0))
198 /* Alter velocity randomly. */
199 if (! (random() % 10))
201 b->dx += (RAND(b->max_velocity/2) * RANDSIGN());
202 b->dy += (RAND(b->max_velocity/2) * RANDSIGN());
204 /* Throttle velocity */
205 if (b->dx > b->max_velocity || b->dx < -b->max_velocity)
207 if (b->dy > b->max_velocity || b->dy < -b->max_velocity)
213 double d = (b->torque == 0 ? 0 : frand(b->torque));
219 if (th > (M_PI+M_PI))
224 b->th = (b->th > 0 ? th : -th);
227 /* Alter direction of rotation randomly. */
228 if (! (random() % 100))
233 draw_blob (Display *dpy, Drawable drawable, GC gc, struct blob *b,
236 compute_closed_spline (b->spline);
240 for (i = 0; i < b->npoints; i++)
241 XDrawLine (dpy, drawable, gc, b->x/SCALE, b->y/SCALE,
242 b->spline->control_x[i], b->spline->control_y[i]);
246 XFillPolygon (dpy, drawable, gc, b->spline->points, b->spline->n_points,
247 Nonconvex, CoordModeOrigin);
250 XDrawLines (dpy, drawable, gc, b->spline->points, b->spline->n_points,
255 static struct layer *
256 make_layer (Display *dpy, Window window, int width, int height, int nblobs)
259 struct layer *layer = (struct layer *) calloc(1, sizeof(*layer));
260 int blob_min, blob_max;
262 layer->nblobs = nblobs;
264 layer->blobs = (struct blob **) malloc(sizeof(*layer->blobs)*layer->nblobs);
266 blob_max = (width < height ? width : height) / 2;
267 blob_min = (blob_max * 2) / 3;
268 for (i = 0; i < layer->nblobs; i++){
269 int j = blob_max - blob_min;
270 layer->blobs[i] = make_blob (dpy, width, height,
271 (j ? random() % j : 0) + blob_min);
274 layer->pixmap = XCreatePixmap (dpy, window, width, height, 1);
275 layer->gc = XCreateGC (dpy, layer->pixmap, 0, &gcv);
278 jwxyz_XSetAlphaAllowed (dpy, layer->gc, True);
279 # endif /* HAVE_COCOA */
285 free_layer(struct layer *layer, Display *dpy)
288 for (i = 0; i < layer->nblobs; i++){
289 free_blob(layer->blobs[i]);
292 XFreeGC(dpy, layer->gc);
299 draw_layer_plane (Display *dpy, struct layer *layer, int width, int height)
302 for (i = 0; i < layer->nblobs; i++)
304 throb_blob (layer->blobs[i]);
305 move_blob (layer->blobs[i], width, height);
306 draw_blob (dpy, layer->pixmap, layer->gc, layer->blobs[i], True);
309 #endif /* !HAVE_COCOA */
313 draw_layer_blobs (Display *dpy, Drawable drawable, GC gc,
314 struct layer *layer, int width, int height,
318 for (i = 0; i < layer->nblobs; i++)
320 draw_blob (dpy, drawable, gc, layer->blobs[i], fill_p);
321 throb_blob (layer->blobs[i]);
322 move_blob (layer->blobs[i], width, height);
328 make_goop (Screen *screen, Visual *visual, Window window, Colormap cmap,
329 int width, int height, long depth)
331 Display *dpy = DisplayOfScreen (screen);
333 struct goop *goop = (struct goop *) calloc(1, sizeof(*goop));
335 int nblobs = get_integer_resource (dpy, "count", "Count");
337 unsigned long *plane_masks = 0;
339 unsigned long base_pixel = 0;
343 s = get_string_resource (dpy, "mode", "Mode");
344 goop->mode = transparent;
345 if (!s || !*s || !strcasecmp (s, "transparent"))
347 else if (!strcasecmp (s, "opaque"))
349 else if (!strcasecmp (s, "xor"))
352 fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
355 goop->delay = get_integer_resource (dpy, "delay", "Integer");
358 goop->height = height;
360 goop->nlayers = get_integer_resource (dpy, "planes", "Planes");
361 if (goop->nlayers <= 0)
362 goop->nlayers = (random() % (depth-2)) + 2;
364 goop->layers = (struct layer **)
365 malloc(sizeof(*goop->layers)*goop->nlayers);
367 goop->additive_p = get_boolean_resource (dpy, "additive", "Additive");
368 goop->cmap_p = has_writable_cells (screen, visual);
370 if (mono_p && goop->mode == transparent)
374 /* Try to allocate some color planes before committing to nlayers.
376 if (goop->mode == transparent)
378 int nplanes = goop->nlayers;
379 allocate_alpha_colors (screen, visual, cmap,
380 &nplanes, goop->additive_p, &plane_masks,
383 goop->nlayers = nplanes;
387 "%s: couldn't allocate any color planes; turning transparency off.\n",
392 # endif /* !HAVE_COCOA */
396 int total = DEF_COUNT;
397 memset (lblobs, 0, sizeof(lblobs));
400 for (i = 0; total && i < goop->nlayers; i++)
401 lblobs[i]++, total--;
402 for (i = 0; i < goop->nlayers; i++)
403 goop->layers[i] = make_layer (dpy, window, width, height,
404 (nblobs > 0 ? nblobs : lblobs[i]));
408 if (goop->mode == transparent && plane_masks)
410 for (i = 0; i < goop->nlayers; i++)
411 goop->layers[i]->pixel = base_pixel | plane_masks[i];
412 goop->background = base_pixel;
414 # endif /* !HAVE_COCOA */
420 if (goop->mode != transparent)
421 # endif /* !HAVE_COCOA */
424 color.flags = DoRed|DoGreen|DoBlue;
427 get_pixel_resource (dpy,cmap, "background", "Background");
429 for (i = 0; i < goop->nlayers; i++)
431 int H = random() % 360; /* range 0-360 */
432 double S = ((double) (random()%70) + 30)/100.0; /* range 30%-100% */
433 double V = ((double) (random()%34) + 66)/100.0; /* range 66%-100% */
434 hsv_to_rgb (H, S, V, &color.red, &color.green, &color.blue);
435 if (XAllocColor (dpy, cmap, &color))
436 goop->layers[i]->pixel = color.pixel;
438 goop->layers[i]->pixel =
439 WhitePixelOfScreen(DefaultScreenOfDisplay(dpy));
441 if (goop->mode == transparent)
443 /* give a non-opaque alpha to the color */
444 unsigned long pixel = goop->layers[i]->pixel;
445 unsigned long amask = BlackPixelOfScreen (0);
446 unsigned long a = (0xBBBBBBBB & amask);
447 pixel = (pixel & (~amask)) | a;
448 goop->layers[i]->pixel = pixel;
450 # endif /* HAVE_COCOA */
454 goop->pixmap = XCreatePixmap (dpy, window, width, height,
455 (goop->mode == xor ? 1L : depth));
457 gcv.background = goop->background;
458 gcv.foreground = get_pixel_resource (dpy, cmap, "foreground", "Foreground");
459 gcv.line_width = get_integer_resource (dpy, "thickness","Thickness");
460 goop->pixmap_gc = XCreateGC (dpy, goop->pixmap, GCLineWidth, &gcv);
461 goop->window_gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
464 jwxyz_XSetAlphaAllowed (dpy, goop->pixmap_gc, True);
465 # endif /* HAVE_COCOA */
470 /* Well, the naming of this function is
471 confusing with goop_free()... */
473 free_goop (struct goop *goop, Display *dpy)
476 for (i = 0; i < goop->nlayers; i++){
477 struct layer * layer = goop->layers[i];
478 free_layer(layer, dpy);
481 XFreeGC(dpy, goop->pixmap_gc);
482 XFreeGC(dpy, goop->window_gc);
486 goop_init (Display *dpy, Window window)
488 XWindowAttributes xgwa;
489 XGetWindowAttributes (dpy, window, &xgwa);
490 return make_goop (xgwa.screen, xgwa.visual, window, xgwa.colormap,
491 xgwa.width, xgwa.height, xgwa.depth);
495 goop_draw (Display *dpy, Window window, void *closure)
497 struct goop *goop = (struct goop *) closure;
505 for (i = 0; i < goop->nlayers; i++)
506 draw_layer_plane (dpy, goop->layers[i], goop->width, goop->height);
508 XSetForeground (dpy, goop->pixmap_gc, goop->background);
509 XSetFunction (dpy, goop->pixmap_gc, GXcopy);
510 XSetPlaneMask (dpy, goop->pixmap_gc, AllPlanes);
511 XFillRectangle (dpy, goop->pixmap, goop->pixmap_gc, 0, 0,
512 goop->width, goop->height);
514 XSetForeground (dpy, goop->pixmap_gc, ~0L);
516 if (!goop->cmap_p && !goop->additive_p)
519 for (i = 0; i < goop->nlayers; i++)
520 for (j = 0; j < goop->layers[i]->nblobs; j++)
521 draw_blob (dpy, goop->pixmap, goop->pixmap_gc,
522 goop->layers[i]->blobs[j], True);
523 XSetFunction (dpy, goop->pixmap_gc, GXclear);
526 for (i = 0; i < goop->nlayers; i++)
528 XSetPlaneMask (dpy, goop->pixmap_gc, goop->layers[i]->pixel);
529 draw_layer_blobs (dpy, goop->pixmap, goop->pixmap_gc,
530 goop->layers[i], goop->width, goop->height,
533 XCopyArea (dpy, goop->pixmap, window, goop->window_gc, 0, 0,
534 goop->width, goop->height, 0, 0);
536 #endif /* !HAVE_COCOA */
539 XSetFunction (dpy, goop->pixmap_gc, GXcopy);
540 XSetForeground (dpy, goop->pixmap_gc, 0);
541 XFillRectangle (dpy, goop->pixmap, goop->pixmap_gc, 0, 0,
542 goop->width, goop->height);
543 XSetFunction (dpy, goop->pixmap_gc, GXxor);
544 XSetForeground (dpy, goop->pixmap_gc, 1);
545 for (i = 0; i < goop->nlayers; i++)
546 draw_layer_blobs (dpy, goop->pixmap, goop->pixmap_gc,
547 goop->layers[i], goop->width, goop->height,
548 (goop->mode != outline));
549 XCopyPlane (dpy, goop->pixmap, window, goop->window_gc, 0, 0,
550 goop->width, goop->height, 0, 0, 1L);
558 XSetForeground (dpy, goop->pixmap_gc, goop->background);
559 XFillRectangle (dpy, goop->pixmap, goop->pixmap_gc, 0, 0,
560 goop->width, goop->height);
561 for (i = 0; i < goop->nlayers; i++)
563 XSetForeground (dpy, goop->pixmap_gc, goop->layers[i]->pixel);
564 draw_layer_blobs (dpy, goop->pixmap, goop->pixmap_gc,
565 goop->layers[i], goop->width, goop->height,
566 (goop->mode != outline));
568 XCopyArea (dpy, goop->pixmap, window, goop->window_gc, 0, 0,
569 goop->width, goop->height, 0, 0);
580 goop_reshape (Display *dpy, Window window, void *closure,
581 unsigned int w, unsigned int h)
583 struct goop *goop = (struct goop *) closure;
585 struct goop *goop2 = goop_init (dpy, window);
586 free_goop(goop, dpy);
587 memcpy (goop, goop2, sizeof(*goop));
592 goop_event (Display *dpy, Window window, void *closure, XEvent *event)
598 goop_free (Display *dpy, Window window, void *closure)
600 struct goop *goop = (struct goop *) closure;
601 free_goop(goop, dpy);
608 static const char *goop_defaults [] = {
609 ".background: black",
610 ".foreground: yellow",
613 "*mode: transparent",
621 "*ignoreRotation: True",
626 static XrmOptionDescRec goop_options [] = {
627 { "-delay", ".delay", XrmoptionSepArg, 0 },
628 { "-count", ".count", XrmoptionSepArg, 0 },
629 { "-planes", ".planes", XrmoptionSepArg, 0 },
630 { "-mode", ".mode", XrmoptionSepArg, 0 },
631 { "-xor", ".mode", XrmoptionNoArg, "xor" },
632 { "-transparent", ".mode", XrmoptionNoArg, "transparent" },
633 { "-opaque", ".mode", XrmoptionNoArg, "opaque" },
634 { "-additive", ".additive", XrmoptionNoArg, "True" },
635 { "-subtractive", ".additive", XrmoptionNoArg, "false" },
636 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
637 { "-torque", ".torque", XrmoptionSepArg, 0 },
638 { "-elasticity", ".elasticity", XrmoptionSepArg, 0 },
639 { "-max-velocity", ".maxVelocity", XrmoptionSepArg, 0 },
643 XSCREENSAVER_MODULE ("Goop", goop)