1 /* xscreensaver, Copyright (c) 1992 Jamie Zawinski <jwz@lucid.com>
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
12 #include "screenhack.h"
33 static GC draw_gc, erase_gc;
34 static unsigned int default_fg_pixel;
35 static int maxx, maxy, max_spread, max_size, color_shift;
36 static Bool random_p, solid_p, xor_p, transparent_p;
40 static unsigned long base_pixel;
45 get_geom (dpy, window)
49 XWindowAttributes xgwa;
50 XGetWindowAttributes (dpy, window, &xgwa);
56 init_one_qix (dpy, window, nlines)
62 struct qix *qix = (struct qix *) calloc (1, sizeof (struct qix));
64 qix->lines = (struct qline *) calloc (qix->nlines, sizeof (struct qline));
66 if (!mono_p && !transparent_p)
68 hsv_to_rgb (random () % 360, frand (1.0), frand (0.5) + 0.5,
69 &qix->lines[0].color.red, &qix->lines[0].color.green,
70 &qix->lines[0].color.blue);
71 if (!XAllocColor (dpy, cmap, &qix->lines[0].color))
73 qix->lines[0].color.pixel = default_fg_pixel;
74 XQueryColor (dpy, cmap, &qix->lines[0].color);
75 if (!XAllocColor (dpy, cmap, &qix->lines[0].color))
79 qix->lines[0].p1.x = random () % maxx;
80 qix->lines[0].p1.y = random () % maxy;
83 qix->lines[0].p2.x = random () % maxx;
84 qix->lines[0].p2.y = random () % maxy;
88 qix->lines[0].p2.x = qix->lines[0].p1.x + (random () % (max_size/2));
89 qix->lines[0].p2.y = qix->lines[0].p1.y + (random () % (max_size/2));
90 if (qix->lines[0].p2.x > maxx) qix->lines[0].p2.x = maxx;
91 if (qix->lines[0].p2.y > maxy) qix->lines[0].p2.y = maxy;
93 qix->lines[0].p1.dx = (random () % (max_spread + 1)) - (max_spread / 2);
94 qix->lines[0].p1.dy = (random () % (max_spread + 1)) - (max_spread / 2);
95 qix->lines[0].p2.dx = (random () % (max_spread + 1)) - (max_spread / 2);
96 qix->lines[0].p2.dy = (random () % (max_spread + 1)) - (max_spread / 2);
97 qix->lines[0].dead = True;
98 for (i = 1; i < qix->nlines; i++)
100 qix->lines[i] = qix->lines[0];
101 if (!mono_p && !transparent_p)
102 if (!XAllocColor (dpy, cmap, &qix->lines[i].color))
108 /* I don't believe this fucking language doesn't have builtin exponentiation.
109 I further can't believe that the fucking ^ character means fucking XOR!! */
110 static int i_exp(i,j)
120 merge_colors (argc, argv, into_color, mask, increment_p)
128 *into_color = *argv [0];
129 into_color->pixel |= mask;
130 #define SHORT_INC(x,y) (x = ((((x)+(y)) > 0xFFFF) ? 0xFFFF : ((x)+(y))))
131 #define SHORT_DEC(x,y) (x = ((((x)-(y)) < 0) ? 0 : ((x)-(y))))
132 for (j = 1; j < argc; j++)
135 SHORT_INC (into_color->red, argv[j]->red);
136 SHORT_INC (into_color->green, argv[j]->green);
137 SHORT_INC (into_color->blue, argv[j]->blue);
141 SHORT_DEC (into_color->red, argv[j]->red);
142 SHORT_DEC (into_color->green, argv[j]->green);
143 SHORT_DEC (into_color->blue, argv[j]->blue);
149 /* fill in all the permutations of colors that XAllocColorCells() has
150 allocated for us. Thanks Ron, you're an additive kind of guy. */
152 permute_colors (pcolors, colors, count, plane_masks, increment_p)
153 XColor *pcolors, *colors;
155 unsigned long *plane_masks;
159 int max = i_exp (2, count);
160 if (count > 31) abort ();
161 for (out = 1; out < max; out++)
167 for (bit = 0; bit < 32; bit++)
170 argv [argc++] = &pcolors [bit];
171 this_mask |= plane_masks [bit];
173 merge_colors (argc, argv, &colors [out-1], this_mask, increment_p);
179 init_qix (dpy, window)
186 XWindowAttributes xgwa;
187 XGetWindowAttributes (dpy, window, &xgwa);
188 cmap = xgwa.colormap;
189 count = get_integer_resource ("count", "Integer");
190 if (count <= 0) count = 1;
191 nlines = get_integer_resource ("segments", "Integer");
192 if (nlines <= 0) nlines = 20;
193 get_geom (dpy, window);
194 max_spread = get_integer_resource ("spread", "Integer");
195 if (max_spread <= 0) max_spread = 10;
196 max_size = get_integer_resource ("size", "Integer");
197 if (max_size < 0) max_size = 0;
198 random_p = get_boolean_resource ("random", "Boolean");
199 solid_p = get_boolean_resource ("solid", "Boolean");
200 xor_p = get_boolean_resource ("xor", "Boolean");
201 transparent_p = get_boolean_resource ("transparent", "Boolean");
202 delay = get_integer_resource ("delay", "Integer");
203 color_shift = get_integer_resource ("colorShift", "Integer");
204 if (color_shift < 0 || color_shift >= 360) color_shift = 5;
205 if (delay < 0) delay = 0;
207 if (count == 1 && transparent_p)
208 transparent_p = False; /* it's a no-op */
210 if (transparent_p && CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
212 fprintf (stderr, "%s: -transparent only works on color displays.\n",
214 transparent_p = False;
217 if (xor_p && !transparent_p)
221 gcv.foreground = default_fg_pixel =
222 get_pixel_resource ("foreground", "Foreground", dpy, cmap);
226 Bool increment_p = get_boolean_resource ("additive", "Boolean");
227 unsigned long plane_masks [32];
228 XColor *pcolors, *colors;
232 /* permutations would be harder if the number of planes didn't fit
233 in an int. Who has >32-bit displays anyway... */
234 if (nplanes > 31) nplanes = 31;
236 while (nplanes > 1 &&
237 !XAllocColorCells (dpy, cmap, False, plane_masks, nplanes,
244 "%s: couldn't allocate any color planes; turning -transparent off.\n",
246 transparent_p = False;
248 goto NON_TRANSPARENT_XOR;
250 goto NON_TRANSPARENT;
252 else if (nplanes != count)
255 "%s: only allocated %d color planes (instead of %d).\n",
256 progname, nplanes, count);
260 gcs[0] = (GC *) malloc (count * sizeof (GC));
261 gcs[1] = xor_p ? gcs[0] : (GC *) malloc (count * sizeof (GC));
262 total_colors = i_exp (2, count);
263 pcolors = (XColor *) calloc (count, sizeof (XColor));
264 colors = (XColor *) calloc (total_colors, sizeof (XColor));
265 for (i = 0; i < count; i++)
267 gcv.plane_mask = plane_masks [i];
271 gcv.function = GXxor;
272 gcs [0][i] = XCreateGC (dpy, window,
273 GCForeground|GCFunction|GCPlaneMask,
278 gcs [0][i] = XCreateGC (dpy, window, GCForeground|GCPlaneMask,
281 gcs [1][i] = XCreateGC (dpy, window, GCForeground|GCPlaneMask,
285 /* pick the "primary" (not in that sense) colors.
286 If we are in subtractive mode, pick higher intensities. */
287 hsv_to_rgb (random () % 360, frand (1.0),
288 frand (0.5) + (increment_p ? 0.2 : 0.5),
289 &pcolors[i].red, &pcolors[i].green, &pcolors[i].blue);
291 pcolors [i].flags = DoRed | DoGreen | DoBlue;
292 pcolors [i].pixel = base_pixel | plane_masks [i];
294 permute_colors (pcolors, colors, count, plane_masks, increment_p);
295 /* clone the default background of the window into our "base" pixel */
296 colors [total_colors - 1].pixel =
297 get_pixel_resource ("background", "Background", dpy, cmap);
298 XQueryColor (dpy, cmap, &colors [total_colors - 1]);
299 colors [total_colors - 1].pixel = base_pixel;
300 XStoreColors (dpy, cmap, colors, total_colors);
301 XSetWindowBackground (dpy, window, base_pixel);
302 XClearWindow (dpy, window);
307 gcv.function = GXxor;
309 (default_fg_pixel ^ get_pixel_resource ("background", "Background",
311 draw_gc = erase_gc = XCreateGC(dpy,window,GCForeground|GCFunction,&gcv);
316 draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
317 gcv.foreground = get_pixel_resource ("background", "Background",
319 erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
322 qixes = (struct qix **) malloc ((count + 1) * sizeof (struct qix *));
326 qixes [count] = init_one_qix (dpy, window, nlines);
327 qixes [count]->id = count;
333 free_qline (dpy, window, cmap, qline, prev, qix)
337 struct qline *qline, *prev;
340 if (qline->dead || !prev)
345 points [0].x = qline->p1.x; points [0].y = qline->p1.y;
346 points [1].x = qline->p2.x; points [1].y = qline->p2.y;
347 points [2].x = prev->p2.x; points [2].y = prev->p2.y;
348 points [3].x = prev->p1.x; points [3].y = prev->p1.y;
349 XFillPolygon (dpy, window, (transparent_p ? gcs[1][qix->id] : erase_gc),
350 points, 4, Complex, CoordModeOrigin);
353 XDrawLine (dpy, window, (transparent_p ? gcs[1][qix->id] : erase_gc),
354 qline->p1.x, qline->p1.y, qline->p2.x, qline->p2.y);
356 if (!mono_p && !transparent_p)
357 XFreeColors (dpy, cmap, &qline->color.pixel, 1, 0);
363 add_qline (dpy, window, cmap, qline, prev_qline, qix)
367 struct qline *qline, *prev_qline;
370 *qline = *prev_qline;
372 #define wiggle(point,delta,max) \
373 if (random_p) delta += (random () % 3) - 1; \
374 if (delta > max_spread) delta = max_spread; \
375 else if (delta < -max_spread) delta = -max_spread; \
377 if (point < 0) point = 0, delta = -delta, point += delta<<1; \
378 else if (point > max) point = max, delta = -delta, point += delta<<1;
380 wiggle (qline->p1.x, qline->p1.dx, maxx);
381 wiggle (qline->p1.y, qline->p1.dy, maxy);
382 wiggle (qline->p2.x, qline->p2.dx, maxx);
383 wiggle (qline->p2.y, qline->p2.dy, maxy);
387 if (qline->p1.x - qline->p2.x > max_size)
388 qline->p1.x = qline->p2.x + max_size
389 - (random_p ? random() % max_spread : 0);
390 else if (qline->p2.x - qline->p1.x > max_size)
391 qline->p2.x = qline->p1.x + max_size
392 - (random_p ? random() % max_spread : 0);
393 if (qline->p1.y - qline->p2.y > max_size)
394 qline->p1.y = qline->p2.y + max_size
395 - (random_p ? random() % max_spread : 0);
396 else if (qline->p2.y - qline->p1.y > max_size)
397 qline->p2.y = qline->p1.y + max_size
398 - (random_p ? random() % max_spread : 0);
401 if (!mono_p && !transparent_p)
403 cycle_hue (&qline->color, color_shift);
404 qline->color.flags = DoRed | DoGreen | DoBlue;
405 if (!XAllocColor (dpy, cmap, &qline->color))
407 qline->color = prev_qline->color;
408 if (!XAllocColor (dpy, cmap, &qline->color))
409 abort (); /* same color should work */
411 XSetForeground (dpy, draw_gc, qline->color.pixel);
414 XDrawLine (dpy, window, (transparent_p ? gcs[0][qix->id] : draw_gc),
415 qline->p1.x, qline->p1.y, qline->p2.x, qline->p2.y);
416 else if (!prev_qline->dead)
419 points [0].x = qline->p1.x; points [0].y = qline->p1.y;
420 points [1].x = qline->p2.x; points [1].y = qline->p2.y;
421 points [2].x = prev_qline->p2.x; points [2].y = prev_qline->p2.y;
422 points [3].x = prev_qline->p1.x; points [3].y = prev_qline->p1.y;
423 XFillPolygon (dpy, window, (transparent_p ? gcs[0][qix->id] : draw_gc),
424 points, 4, Complex, CoordModeOrigin);
431 qix1 (dpy, window, qix)
436 int ofp = qix->fp - 1;
437 static int gtick = 0;
439 get_geom (dpy, window), gtick = 0;
440 if (ofp < 0) ofp = qix->nlines - 1;
441 free_qline (dpy, window, cmap, &qix->lines [qix->fp],
442 &qix->lines[(qix->fp + 1) % qix->nlines], qix);
443 add_qline (dpy, window, cmap, &qix->lines[qix->fp], &qix->lines[ofp], qix);
444 if ((++qix->fp) >= qix->nlines)
449 char *progclass = "Qix";
451 char *defaults [] = {
452 "*background: black",
453 "*foreground: white",
463 "*transparent:false",
468 XrmOptionDescRec options [] = {
469 { "-count", ".count", XrmoptionSepArg, 0 },
470 { "-segments", ".segments", XrmoptionSepArg, 0 },
471 { "-spread", ".spread", XrmoptionSepArg, 0 },
472 { "-size", ".size", XrmoptionSepArg, 0 },
473 { "-delay", ".delay", XrmoptionSepArg, 0 },
474 { "-color-shift", ".colorShift", XrmoptionSepArg, 0 },
475 { "-random", ".random", XrmoptionNoArg, "true" },
476 { "-linear", ".random", XrmoptionNoArg, "false" },
477 { "-solid", ".solid", XrmoptionNoArg, "true" },
478 { "-hollow", ".solid", XrmoptionNoArg, "false" },
479 { "-xor", ".xor", XrmoptionNoArg, "true" },
480 { "-no-xor", ".xor", XrmoptionNoArg, "false" },
481 { "-transparent", ".transparent", XrmoptionNoArg, "true" },
482 { "-non-transparent", ".transparent", XrmoptionNoArg, "false" },
483 { "-additive", ".additive", XrmoptionNoArg, "true" },
484 { "-subtractive", ".additive", XrmoptionNoArg, "false" },
486 int options_size = (sizeof (options) / sizeof (options[0]));
489 screenhack (dpy, window)
493 struct qix **q1 = init_qix (dpy, window);
496 for (qn = q1; *qn; qn++)
498 qix1 (dpy, window, *qn);
500 if (delay) usleep (delay);