1 /* fuzzyflakes, Copyright (c) 2004
2 * Barry Dmytro <badcherry@mailc.net>
5 * ! - Added support for resizing
6 * ! - Added a color scheme generation algorithm
7 * ! Thanks to <ZoeB> from #vegans@irc.blitzed.org
8 * ! - Added random color generation
9 * ! - Fixed errors in the xml config file
10 * ! - Cleaned up a few inconsistencies in the code
11 * ! - Changed the default color to #EFBEA5
14 * ! -original creation
16 * Permission to use, copy, modify, distribute, and sell this software and its
17 * documentation for any purpose is hereby granted without fee, provided that
18 * the above copyright notice appear in all copies and that both that
19 * copyright notice and this permission notice appear in supporting
20 * documentation. No representations are made about the suitability of this
21 * software for any purpose. It is provided "as is" without express or
26 #include "screenhack.h"
28 /* I have need of 1/3 and 2/3 constants later on */
29 #define N1_3 0.3333333333
30 #define N2_3 0.6666666666
32 typedef struct _flake_var
41 /* Struct containing the atrributes to our flakes */
51 unsigned long BordColor;
52 unsigned long ForeColor;
53 unsigned long BackColor;
65 /* a dynamic array containing positions of all the flakes */
66 FlakeVariable ***Flakes;
68 unsigned long GCFlags;
70 XWindowAttributes XGWA;
79 *This gets the pixel resource for a color: #ffffff
82 FuzzyFlakesColorResource(Flake *flake, char *Color)
86 if (!XParseColor(flake->dpy, flake->XGWA.colormap, Color, &color))
88 fprintf(stderr, "%s: can't parse color %s", progname, Color);
91 if (!XAllocColor(flake->dpy, flake->XGWA.colormap, &color))
93 fprintf(stderr, "%s: can't allocate color %s", progname, Color);
100 * This is a great color matching algorithm that I got from
101 * a friend of mine on #vegans@irc.blitzed.org
102 * She wrote it in PHP and I ported it over to C
103 * her site is http://beautifulfreak.net/
106 FuzzyFlakesColorHelper(Flake *flake)
108 unsigned int iR, iG, iB;
109 unsigned int iR0, iG0, iB0;
110 unsigned int iR1, iG1, iB1;
112 float Max = 0, Min = 0, Lig, Sat;
113 float Hue, Hue0, Hue1;
121 /* First convert from hex to dec */
122 /* while splitting up the RGB values */
123 if (!XParseColor(flake->dpy, flake->XGWA.colormap,
124 flake->Colors.Back, &color))
126 fprintf(stderr, "%s: can't parse color %s", progname,
131 iG = color.green >> 8;
132 iB = color.blue >> 8;
134 /* Convert from int to float */
139 /* convert from 0-255 to 0-1 */
144 /* work out the lightness */
145 if (fR >= fG && fR >= fB)
147 if (fG >= fR && fG >= fB)
149 if (fB >= fR && fB >= fG)
152 if (fR <= fG && fR <= fB)
154 if (fG <= fR && fG <= fB)
156 if (fB <= fR && fB <= fG)
159 Lig = (Max + Min) / 2;
161 /* work out the saturation */
167 Sat = (Max - Min) / (Max + Min);
169 Sat = (Max - Min) / (2 - Max - Min);
173 * if our satration is too low we won't be
174 * able to see any objects
181 /* work out the hue */
183 Hue = (fG - fB) / (Max - Min);
185 Hue = 2 + (fB - fR) / (Max - Min);
187 Hue = 4 + (fR - fG) / (Max - Min);
191 /* fine two equidistant hues */
199 /* convert the colors into hex codes */
201 f2 = Lig * (1 + Sat);
203 f2 = (Lig + Sat) - (Lig * Sat);
207 fR0 = (Hue0 + 1) / 3;
208 fR1 = (Hue1 + 1) / 3;
211 fB0 = (Hue0 - 1) / 3;
212 fB1 = (Hue1 - 1) / 3;
241 nR0 = f1 + (f2 - f1) * 6 * fR0;
242 else if (2 * fR0 < 1)
244 else if (3 * fR0 < 2)
245 nR0 = f1 + (f2 - f1) * (N2_3 - fR0) * 6;
250 nG0 = f1 + (f2 - f1) * 6 * fG0;
251 else if (2 * fG0 < 1)
253 else if (3 * fG0 < 2)
254 nG0 = f1 + (f2 - f1) * (N2_3 - fG0) * 6;
259 nB0 = f1 + (f2 - f1) * 6 * fB0;
260 else if (2 * fB0 < 1)
262 else if (3 * fB0 < 2)
263 nB0 = f1 + (f2 - f1) * (N2_3 - fB0) * 6;
268 nR1 = f1 + (f2 - f1) * 6 * fR1;
269 else if (2 * fR1 < 1)
271 else if (3 * fR1 < 2)
272 nR1 = f1 + (f2 - f1) * (N2_3 - fR1) * 6;
277 nG1 = f1 + (f2 - f1) * 6 * fG1;
278 else if (2 * fG1 < 1)
280 else if (3 * fG1 < 2)
281 nG1 = f1 + (f2 - f1) * (N2_3 - fG1) * 6;
286 nB1 = f1 + (f2 - f1) * 6 * fB1;
287 else if (2 * fB1 < 1)
289 else if (3 * fB1 < 2)
290 nB1 = f1 + (f2 - f1) * (N2_3 - fB1) * 6;
294 /* at last convert them to a hex string */
303 flake->Colors.Fore = malloc(sizeof(unsigned char) * 8);
304 flake->Colors.Bord = malloc(sizeof(unsigned char) * 8);
306 sprintf(flake->Colors.Fore, "#%02X%02X%02X", iR0, iG0, iB0);
307 sprintf(flake->Colors.Bord, "#%02X%02X%02X", iR1, iG1, iB1);
313 FuzzyFlakesInit(Flake *flake)
316 XWindowAttributes xgwa;
319 XGetWindowAttributes(flake->dpy, flake->window, &xgwa);
320 cmap = xgwa.colormap;
322 flake->DB.b = flake->DB.ba = flake->DB.bb = 0;
323 flake->DB.dbuf = get_boolean_resource(flake->dpy, "doubleBuffer", "Boolean");
325 # ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
326 flake->DB.dbuf = False;
332 XCreatePixmap(flake->dpy, flake->window, xgwa.width, xgwa.height, xgwa.depth);
334 XCreatePixmap(flake->dpy, flake->window, xgwa.width, xgwa.height, xgwa.depth);
335 flake->DB.b = flake->DB.ba;
339 flake->DB.b = flake->window;
342 flake->Arms = get_integer_resource(flake->dpy, "arms", "Integer");
343 flake->Thickness = get_integer_resource(flake->dpy, "thickness", "Integer");
344 flake->BorderThickness = get_integer_resource(flake->dpy, "bthickness", "Integer");
345 flake->Radius = get_integer_resource(flake->dpy, "radius", "Integer");
347 flake->Density = get_integer_resource(flake->dpy, "density", "Integer");
348 flake->Layers = get_integer_resource(flake->dpy, "layers", "Integer");
349 flake->FallingSpeed = get_integer_resource(flake->dpy, "fallingspeed", "Integer");
350 flake->Delay = get_integer_resource(flake->dpy, "delay", "Integer");
351 if (flake->RandomColors == True)
352 flake->RandomColors = get_boolean_resource(flake->dpy, "randomColors", "Boolean");
354 if (flake->Delay < 0)
357 if (!flake->Colors.Back)
359 flake->Colors.Back = get_string_resource(flake->dpy, "color", "Color");
360 if (!FuzzyFlakesColorResource(flake, flake->Colors.Back))
362 fprintf(stderr, " reverting to random\n");
363 flake->RandomColors = True;
366 if (flake->RandomColors)
368 if (flake->Colors.Back)
369 free(flake->Colors.Back);
370 flake->Colors.Back = malloc(sizeof(unsigned char) * 8);
371 sprintf(flake->Colors.Back, "#%X%X%X%X%X%X", random() % 16,
372 random() % 16, random() % 16, random() % 16, random() % 16,
377 * Here we establish our colormap based on what is in
380 if (FuzzyFlakesColorHelper(flake))
382 fprintf(stderr, " reverting to random\n");
383 if (flake->Colors.Back)
384 free(flake->Colors.Back);
385 flake->Colors.Back = malloc(sizeof(unsigned char) * 8);
386 sprintf(flake->Colors.Back, "#%X%X%X%X%X%X", random() % 16,
387 random() % 16, random() % 16, random() % 16, random() % 16,
389 FuzzyFlakesColorHelper(flake);
392 flake->ForeColor = FuzzyFlakesColorResource(flake, flake->Colors.Fore);
393 flake->BackColor = FuzzyFlakesColorResource(flake, flake->Colors.Back);
394 flake->BordColor = FuzzyFlakesColorResource(flake, flake->Colors.Bord);
396 flake->GCValues.foreground = flake->ForeColor;
397 flake->GCValues.background = flake->BackColor;
398 flake->RandomColors = False;
401 flake->GCValues.line_width = flake->Thickness;
402 flake->GCValues.cap_style = CapProjecting;
403 flake->GCValues.join_style = JoinMiter;
404 flake->GCFlags |= (GCLineWidth | GCCapStyle | GCJoinStyle);
407 XCreateGC(flake->dpy, flake->window, flake->GCFlags,
410 flake->Density = flake->XGWA.width / 200 * flake->Density;
411 flake->Flakes = malloc(sizeof(FlakeVariable **) * flake->Layers);
412 for (i = 1; i <= flake->Layers; i++)
414 flake->Flakes[i - 1] = malloc(sizeof(FlakeVariable *) * flake->Density);
415 for (j = 0; j < flake->Density; j++)
417 flake->Flakes[i - 1][j] = malloc(sizeof(FlakeVariable));
418 flake->Flakes[i - 1][j]->XPos = random() % flake->XGWA.width;
419 flake->Flakes[i - 1][j]->YPos = random() % flake->XGWA.height;
420 flake->Flakes[i - 1][j]->Angle = random() % 360 * (M_PI / 180);
421 flake->Flakes[i - 1][j]->Ticks = random() % 360;
422 flake->Flakes[i - 1][j]->XOffset = random() % flake->XGWA.height;
428 FuzzyFlakesFreeFlake(Flake *flake)
432 for (i = 1; i <= flake->Layers; i++)
434 for (j = 0; j < flake->Density; j++)
436 free(flake->Flakes[i - 1][j]);
438 free(flake->Flakes[i - 1]);
441 if (flake->DB.bb) XFreePixmap(flake->dpy, flake->DB.bb);
442 if (flake->DB.ba) XFreePixmap(flake->dpy, flake->DB.ba);
446 FuzzyFlakesMove(Flake *flake)
450 for (i = 1; i <= flake->Layers; i++)
452 for (j = 0; j < flake->Density; j++)
454 FlakeVariable *FlakeVar;
456 FlakeVar = flake->Flakes[i - 1][j];
459 FlakeVar->YPos + ((double)flake->FallingSpeed) / 10 / i;
463 FlakeVar->Ticks * (M_PI / 180) * ((double)flake->FallingSpeed /
464 10))) * 10 + FlakeVar->XPos;
466 FlakeVar->Angle + 0.005 * ((double)flake->FallingSpeed / 10);
467 if (FlakeVar->YPos - flake->Radius > flake->XGWA.height)
470 FlakeVar->YPos = 0 - flake->Radius;
477 FuzzyFlakesDrawFlake(Flake *flake, int XPos, int YPos, double AngleOffset, int Layer)
480 double x, y, Angle, Radius;
482 /* calculate the shrink factor debending on which layer we are drawing atm */
483 Radius = (double)(flake->Radius - Layer * 5);
484 /* draw the flake one arm at a time */
485 for (i = 1; i <= flake->Arms; i++)
489 Diameter = (flake->BorderThickness * 2 + flake->Thickness) / Layer;
490 /* compute the angle of this arm of the flake */
491 Angle = ((2 * M_PI) / flake->Arms) * i + AngleOffset;
492 /* calculate the x and y dispositions for this arm */
493 y = (int)(sin(Angle) * Radius);
494 x = (int)(cos(Angle) * Radius);
495 /* draw the base for the arm */
496 flake->GCValues.line_width = Diameter;
497 XFreeGC(flake->dpy, flake->GCVar);
499 XCreateGC(flake->dpy, flake->DB.b, flake->GCFlags,
501 XSetForeground(flake->dpy, flake->GCVar, flake->BordColor);
502 XDrawLine(flake->dpy, flake->DB.b, flake->GCVar, XPos, YPos,
505 /* draw the flake one arm at a time */
506 for (i = 1; i <= flake->Arms; i++)
508 /* compute the angle of this arm of the flake */
509 Angle = ((2 * M_PI) / flake->Arms) * i + AngleOffset;
510 /* calculate the x and y dispositions for this arm */
511 y = (int)(sin(Angle) * Radius);
512 x = (int)(cos(Angle) * Radius);
513 /* draw the inside of the arm */
514 flake->GCValues.line_width = flake->Thickness / Layer;
515 XFreeGC(flake->dpy, flake->GCVar);
517 XCreateGC(flake->dpy, flake->DB.b, flake->GCFlags,
519 XSetForeground(flake->dpy, flake->GCVar, flake->ForeColor);
520 XDrawLine(flake->dpy, flake->DB.b, flake->GCVar, XPos, YPos,
526 FuzzyFlakes(Flake *flake)
530 FuzzyFlakesMove(flake);
531 XSetForeground(flake->dpy, flake->GCVar, flake->BackColor);
532 XFillRectangle(flake->dpy, flake->DB.b, flake->GCVar, 0, 0,
533 flake->XGWA.width, flake->XGWA.height);
534 for (i = flake->Layers; i >= 1; i--)
536 for (j = 0; j < flake->Density; j++)
538 FuzzyFlakesDrawFlake(flake,
539 flake->Flakes[i - 1][j]->TrueX,
540 flake->Flakes[i - 1][j]->YPos,
541 flake->Flakes[i - 1][j]->Angle, i);
548 fuzzyflakes_init (Display *dpy, Window window)
550 Flake *flake = (Flake *) calloc (1, sizeof(*flake));
552 flake->window = window;
554 /* This is needed even if it is going to be set to false */
555 flake->RandomColors = True;
557 /* set up our colors amoung many other things */
558 FuzzyFlakesInit(flake);
564 fuzzyflakes_draw (Display *dpy, Window window, void *closure)
566 Flake *flake = (Flake *) closure;
571 XCopyArea(flake->dpy, flake->DB.b, flake->window,
572 flake->GCVar, 0, 0, flake->XGWA.width, flake->XGWA.height,
575 (flake->DB.b == flake->DB.ba ? flake->DB.bb : flake->DB.ba);
582 fuzzyflakes_reshape (Display *dpy, Window window, void *closure,
583 unsigned int w, unsigned int h)
585 Flake *flake = (Flake *) closure;
587 if (flake->XGWA.width != w || flake->XGWA.height != h)
589 FuzzyFlakesFreeFlake(flake);
590 FuzzyFlakesInit(flake);
595 fuzzyflakes_event (Display *dpy, Window window, void *closure, XEvent *event)
601 fuzzyflakes_free (Display *dpy, Window window, void *closure)
603 Flake *flake = (Flake *) closure;
604 FuzzyFlakesFreeFlake(flake);
609 static const char *fuzzyflakes_defaults[] = {
619 "*doubleBuffer: True",
620 "*randomColors: False",
624 static XrmOptionDescRec fuzzyflakes_options[] = {
625 { "-color", ".color", XrmoptionSepArg, 0},
626 { "-arms", ".arms", XrmoptionSepArg, 0},
627 { "-thickness", ".thickness", XrmoptionSepArg, 0},
628 { "-bthickness", ".bthickness", XrmoptionSepArg, 0},
629 { "-radius", ".radius", XrmoptionSepArg, 0},
630 { "-layers", ".layers", XrmoptionSepArg, 0},
631 { "-density", ".density", XrmoptionSepArg, 0},
632 { "-speed", ".fallingspeed", XrmoptionSepArg, 0},
633 { "-delay", ".delay", XrmoptionSepArg, 0},
634 { "-db", ".doubleBuffer", XrmoptionNoArg, "True"},
635 { "-no-db", ".doubleBuffer", XrmoptionNoArg, "False"},
636 { "-random-colors", ".randomColors", XrmoptionNoArg, "True"},
641 XSCREENSAVER_MODULE ("FuzzyFlakes", fuzzyflakes)