ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[xscreensaver] / hacks / fuzzyflakes.c
1 /* fuzzyflakes, Copyright (c) 2004
2  *  Barry Dmytro <badcherry@mailc.net>
3  *
4  * ! 2004.06.10 21:05
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
12  *
13  * ! 2004.05.?? ??:??
14  * ! -original creation
15  *
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 
22  * implied warranty.
23  */
24
25 #include <math.h>
26 #include "screenhack.h"
27
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
31
32 typedef struct _flake_var
33 {
34    double              Ticks;
35    double              XPos, YPos;
36    double              TrueX;
37    double              XOffset;
38    double              Angle;
39 } FlakeVariable;
40
41 /* Struct containing the atrributes to our flakes */
42 static struct _flake
43 {
44    int                 Arms;
45    int                 Thickness;
46    int                 BorderThickness;
47    int                 Radius;
48    unsigned long       BordColor;
49    unsigned long       ForeColor;
50    unsigned long       BackColor;
51    Bool                RandomColors;
52    int                 Layers;
53    int                 Density;
54    int                 Delay;
55    int                 FallingSpeed;
56    struct _colors
57    {
58       char               *Fore;
59       char               *Bord;
60       char               *Back;
61    } Colors;
62 /* a dynamic array containing positions of all the flakes */
63    FlakeVariable    ***Flakes;
64    XGCValues           GCValues;
65    unsigned long       GCFlags;
66    GC                  GCVar;
67    Display            *DisplayVar;
68    Window              WindowVar;
69    XWindowAttributes   XGWA;
70    struct _dbevar
71    {
72       Bool                dbuf;
73       Pixmap              b, ba, bb;
74    } DB;
75 } Flake;
76
77 /*
78  *This gets the pixel resource for a color: #ffffff
79  */
80 unsigned int
81 FuzzyFlakesColorResource(char *Color)
82 {
83    XColor              color;
84
85    if (!XParseColor(Flake.DisplayVar, Flake.XGWA.colormap, Color, &color))
86      {
87         fprintf(stderr, "%s: can't parse color %s", progname, Color);
88         return 0;
89      }
90    if (!XAllocColor(Flake.DisplayVar, Flake.XGWA.colormap, &color))
91      {
92         fprintf(stderr, "%s: can't allocate color %s", progname, Color);
93         return 0;
94      }
95    return color.pixel;
96 }
97
98 /*
99  * This is a great color matching algorithm that I got from
100  * a friend of mine on #vegans@irc.blitzed.org
101  * She wrote it in PHP and I ported it over to C
102  * her site is http://beautifulfreak.net/
103  */
104 static int
105 FuzzyFlakesColorHelper(void)
106 {
107    unsigned int        iR, iG, iB;
108    unsigned int        iR0, iG0, iB0;
109    unsigned int        iR1, iG1, iB1;
110    float               fR, fG, fB;
111    float               Max = 0, Min = 0, Lig, Sat;
112    float               Hue, Hue0, Hue1;
113    float               f1, f2;
114    float               fR0, fG0, fB0;
115    float               fR1, fG1, fB1;
116    float               nR0, nG0, nB0;
117    float               nR1, nG1, nB1;
118    XColor              color;
119
120    /* First convert from hex to dec */
121    /* while splitting up the RGB values */
122    if (!XParseColor(Flake.DisplayVar, Flake.XGWA.colormap,
123                     Flake.Colors.Back, &color))
124      {
125         fprintf(stderr, "%s: can't parse color %s", progname,
126                 Flake.Colors.Back);
127         return 1;
128      }
129    iR = color.red   >> 8;
130    iG = color.green >> 8;
131    iB = color.blue  >> 8;
132
133    /* Convert from int to float */
134    fR = iR;
135    fG = iG;
136    fB = iB;
137
138    /* convert from 0-255 to 0-1 */
139    fR = fR / 255;
140    fG = fG / 255;
141    fB = fB / 255;
142
143    /* work out the lightness */
144    if (fR >= fG && fR >= fB)
145       Max = fR;
146    if (fG >= fR && fG >= fB)
147       Max = fG;
148    if (fB >= fR && fB >= fG)
149       Max = fB;
150
151    if (fR <= fG && fR <= fB)
152       Min = fR;
153    if (fG <= fR && fG <= fB)
154       Min = fG;
155    if (fB <= fR && fB <= fG)
156       Min = fB;
157
158    Lig = (Max + Min) / 2;
159
160    /* work out the saturation */
161    if (Max == Min)
162       Sat = 0;
163    else
164      {
165         if (Lig < 0.5)
166            Sat = (Max - Min) / (Max + Min);
167         else
168            Sat = (Max - Min) / (2 - Max - Min);
169      }
170
171    /*
172     * if our satration is too low we won't be
173     * able to see any objects
174     */
175    if (Sat < 0.03)
176      {
177         return 1;
178      }
179
180    /* work out the hue */
181    if (fR == Max)
182       Hue = (fG - fB) / (Max - Min);
183    else if (fG == Max)
184       Hue = 2 + (fB - fR) / (Max - Min);
185    else
186       Hue = 4 + (fR - fG) / (Max - Min);
187
188    Hue = Hue / 6;
189
190    /* fine two equidistant hues */
191    Hue0 = Hue + N1_3;
192    if (Hue0 > 1)
193       Hue0 = Hue0 - 1;
194    Hue1 = Hue0 + N1_3;
195    if (Hue1 > 1)
196       Hue1 = Hue1 - 1;
197
198    /* convert the colors into hex codes */
199    if (Lig < 0.5)
200       f2 = Lig * (1 + Sat);
201    else
202       f2 = (Lig + Sat) - (Lig * Sat);
203
204    f1 = (2 * Lig) - f2;
205
206    fR0 = (Hue0 + 1) / 3;
207    fR1 = (Hue1 + 1) / 3;
208    fG0 = Hue0;
209    fG1 = Hue1;
210    fB0 = (Hue0 - 1) / 3;
211    fB1 = (Hue1 - 1) / 3;
212
213    if (fR0 < 0)
214       fR0 = fR0 + 1;
215    if (fR0 > 1)
216       fR0 = fR0 - 1;
217    if (fG0 < 0)
218       fG0 = fG0 + 1;
219    if (fG0 > 1)
220       fG0 = fG0 - 1;
221    if (fB0 < 0)
222       fB0 = fB0 + 1;
223    if (fB0 > 1)
224       fB0 = fB0 - 1;
225
226    if (fR1 < 0)
227       fR1 = fR1 + 1;
228    if (fR1 > 1)
229       fR1 = fR1 - 1;
230    if (fG1 < 0)
231       fG1 = fG1 + 1;
232    if (fG1 > 1)
233       fG1 = fG1 - 1;
234    if (fB1 < 0)
235       fB1 = fB1 + 1;
236    if (fB1 > 1)
237       fB1 = fB1 - 1;
238
239    if (6 * fR0 < 1)
240       nR0 = f1 + (f2 - f1) * 6 * fR0;
241    else if (2 * fR0 < 1)
242       nR0 = f2;
243    else if (3 * fR0 < 2)
244       nR0 = f1 + (f2 - f1) * (N2_3 - fR0) * 6;
245    else
246       nR0 = f1;
247
248    if (6 * fG0 < 1)
249       nG0 = f1 + (f2 - f1) * 6 * fG0;
250    else if (2 * fG0 < 1)
251       nG0 = f2;
252    else if (3 * fG0 < 2)
253       nG0 = f1 + (f2 - f1) * (N2_3 - fG0) * 6;
254    else
255       nG0 = f1;
256
257    if (6 * fB0 < 1)
258       nB0 = f1 + (f2 - f1) * 6 * fB0;
259    else if (2 * fB0 < 1)
260       nB0 = f2;
261    else if (3 * fB0 < 2)
262       nB0 = f1 + (f2 - f1) * (N2_3 - fB0) * 6;
263    else
264       nB0 = f1;
265
266    if (6 * fR1 < 1)
267       nR1 = f1 + (f2 - f1) * 6 * fR1;
268    else if (2 * fR1 < 1)
269       nR1 = f2;
270    else if (3 * fR1 < 2)
271       nR1 = f1 + (f2 - f1) * (N2_3 - fR1) * 6;
272    else
273       nR1 = f1;
274
275    if (6 * fG1 < 1)
276       nG1 = f1 + (f2 - f1) * 6 * fG1;
277    else if (2 * fG1 < 1)
278       nG1 = f2;
279    else if (3 * fG1 < 2)
280       nG1 = f1 + (f2 - f1) * (N2_3 - fG1) * 6;
281    else
282       nG1 = f1;
283
284    if (6 * fB1 < 1)
285       nB1 = f1 + (f2 - f1) * 6 * fB1;
286    else if (2 * fB1 < 1)
287       nB1 = f2;
288    else if (3 * fB1 < 2)
289       nB1 = f1 + (f2 - f1) * (N2_3 - fB1) * 6;
290    else
291       nB1 = f1;
292
293    /* at last convert them to a hex string */
294    iR0 = nR0 * 255;
295    iG0 = nG0 * 255;
296    iB0 = nB0 * 255;
297
298    iR1 = nR1 * 255;
299    iG1 = nG1 * 255;
300    iB1 = nB1 * 255;
301
302    Flake.Colors.Fore = malloc(sizeof(unsigned char) * 8);
303    Flake.Colors.Bord = malloc(sizeof(unsigned char) * 8);
304
305    sprintf(Flake.Colors.Fore, "#%02X%02X%02X", iR0, iG0, iB0);
306    sprintf(Flake.Colors.Bord, "#%02X%02X%02X", iR1, iG1, iB1);
307
308    return 0;
309 }
310
311 static void
312 FuzzyFlakesInit(Display * dpy, Window window)
313 {
314    int                 i, j;
315    XWindowAttributes   xgwa;
316    Colormap            cmap;
317
318    XGetWindowAttributes(dpy, window, &xgwa);
319    cmap = xgwa.colormap;
320    Flake.XGWA = xgwa;
321    Flake.DB.b = Flake.DB.ba = Flake.DB.bb = 0;
322    Flake.DB.dbuf = get_boolean_resource("doubleBuffer", "Boolean");
323
324    if (Flake.DB.dbuf)
325      {
326         Flake.DB.ba =
327            XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
328         Flake.DB.bb =
329            XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
330         Flake.DB.b = Flake.DB.ba;
331      }
332    else
333      {
334         Flake.DB.b = window;
335      }
336
337    Flake.DisplayVar = dpy;
338    Flake.WindowVar = window;
339    Flake.Arms = get_integer_resource("arms", "Integer");
340    Flake.Thickness = get_integer_resource("thickness", "Integer");
341    Flake.BorderThickness = get_integer_resource("bthickness", "Integer");
342    Flake.Radius = get_integer_resource("radius", "Integer");
343
344    Flake.Density = get_integer_resource("density", "Integer");
345    Flake.Layers = get_integer_resource("layers", "Integer");
346    Flake.FallingSpeed = get_integer_resource("fallingspeed", "Integer");
347    Flake.Delay = get_integer_resource("delay", "Integer");
348    if (Flake.RandomColors == True)
349       Flake.RandomColors = get_boolean_resource("randomColors", "Boolean");
350
351    if (Flake.Delay < 0)
352       Flake.Delay = 0;
353
354    if (!Flake.Colors.Back)
355      {
356         Flake.Colors.Back = get_string_resource("color", "Color");
357         if (!FuzzyFlakesColorResource(Flake.Colors.Back))
358           {
359              fprintf(stderr, " reverting to random\n");
360              Flake.RandomColors = True;
361           }
362
363         if (Flake.RandomColors)
364           {
365              if (Flake.Colors.Back)
366                 free(Flake.Colors.Back);
367              Flake.Colors.Back = malloc(sizeof(unsigned char) * 8);
368              sprintf(Flake.Colors.Back, "#%X%X%X%X%X%X", random() % 16,
369                      random() % 16, random() % 16, random() % 16, random() % 16,
370                      random() % 16);
371           }
372
373         /*
374          * Here we establish our colormap based on what is in
375          * Flake.Colors.Back
376          */
377         if (FuzzyFlakesColorHelper())
378           {
379              fprintf(stderr, " reverting to random\n");
380              if (Flake.Colors.Back)
381                 free(Flake.Colors.Back);
382              Flake.Colors.Back = malloc(sizeof(unsigned char) * 8);
383              sprintf(Flake.Colors.Back, "#%X%X%X%X%X%X", random() % 16,
384                      random() % 16, random() % 16, random() % 16, random() % 16,
385                      random() % 16);
386              FuzzyFlakesColorHelper();
387           }
388
389         Flake.ForeColor = FuzzyFlakesColorResource(Flake.Colors.Fore);
390         Flake.BackColor = FuzzyFlakesColorResource(Flake.Colors.Back);
391         Flake.BordColor = FuzzyFlakesColorResource(Flake.Colors.Bord);
392
393         Flake.GCValues.foreground = Flake.ForeColor;
394         Flake.GCValues.background = Flake.BackColor;
395         Flake.RandomColors = False;
396      }
397
398    Flake.GCValues.line_width = Flake.Thickness;
399    Flake.GCValues.line_style = LineSolid;
400    Flake.GCValues.cap_style = CapProjecting;
401    Flake.GCValues.join_style = JoinMiter;
402    Flake.GCFlags |= (GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle);
403
404    Flake.GCVar =
405       XCreateGC(Flake.DisplayVar, Flake.WindowVar, Flake.GCFlags,
406                 &Flake.GCValues);
407
408    Flake.Density = Flake.XGWA.width / 200 * Flake.Density;
409    Flake.Flakes = malloc(sizeof(FlakeVariable **) * Flake.Layers);
410    for (i = 1; i <= Flake.Layers; i++)
411      {
412         Flake.Flakes[i - 1] = malloc(sizeof(FlakeVariable *) * Flake.Density);
413         for (j = 0; j < Flake.Density; j++)
414           {
415              Flake.Flakes[i - 1][j] = malloc(sizeof(FlakeVariable));
416              Flake.Flakes[i - 1][j]->XPos = random() % Flake.XGWA.width;
417              Flake.Flakes[i - 1][j]->YPos = random() % Flake.XGWA.height;
418              Flake.Flakes[i - 1][j]->Angle = random() % 360 * (M_PI / 180);
419              Flake.Flakes[i - 1][j]->Ticks = random() % 360;
420              Flake.Flakes[i - 1][j]->XOffset = random() % Flake.XGWA.height;
421           }
422      }
423 }
424
425 static void
426 FuzzyFlakesFreeFlake(void)
427 {
428    int                 i, j;
429
430    for (i = 1; i <= Flake.Layers; i++)
431      {
432         for (j = 0; j < Flake.Density; j++)
433           {
434              free(Flake.Flakes[i - 1][j]);
435           }
436         free(Flake.Flakes[i - 1]);
437      }
438
439    XFreePixmap(Flake.DisplayVar, Flake.DB.bb);
440    XFreePixmap(Flake.DisplayVar, Flake.DB.ba);
441 }
442
443 static void
444 FuzzyFlakesResizeTest(void)
445 {
446    XWindowAttributes   xgwa;
447
448    XGetWindowAttributes(Flake.DisplayVar, Flake.WindowVar, &xgwa);
449    if (Flake.XGWA.width != xgwa.width || Flake.XGWA.height != xgwa.height)
450      {
451         FuzzyFlakesFreeFlake();
452         FuzzyFlakesInit(Flake.DisplayVar, Flake.WindowVar);
453      }
454 }
455
456 static void
457 FuzzyFlakesMove(void)
458 {
459    int                 i, j;
460
461    for (i = 1; i <= Flake.Layers; i++)
462      {
463         for (j = 0; j < Flake.Density; j++)
464           {
465              FlakeVariable      *FlakeVar;
466
467              FlakeVar = Flake.Flakes[i - 1][j];
468              FlakeVar->Ticks++;
469              FlakeVar->YPos =
470                 FlakeVar->YPos + ((double)Flake.FallingSpeed) / 10 / i;
471              FlakeVar->TrueX =
472                 (sin
473                  (FlakeVar->XOffset +
474                   FlakeVar->Ticks * (M_PI / 180) * ((double)Flake.FallingSpeed /
475                                                     10))) * 10 + FlakeVar->XPos;
476              FlakeVar->Angle =
477                 FlakeVar->Angle + 0.005 * ((double)Flake.FallingSpeed / 10);
478              if (FlakeVar->YPos - Flake.Radius > Flake.XGWA.height)
479                {
480                   FlakeVar->Ticks = 0;
481                   FlakeVar->YPos = 0 - Flake.Radius;
482                }
483           }
484      }
485 }
486
487 static void
488 FuzzyFlakesDrawFlake(int XPos, int YPos, double AngleOffset, int Layer)
489 {
490    int                 i;
491    double              x, y, Angle, Radius;
492
493    /* calculate the shrink factor debending on which layer we are drawing atm */
494    Radius = (double)(Flake.Radius - Layer * 5);
495    /* draw the flake one arm at a time */
496    for (i = 1; i <= Flake.Arms; i++)
497      {
498         int                 Diameter;
499
500         Diameter = (Flake.BorderThickness * 2 + Flake.Thickness) / Layer;
501         /* compute the angle of this arm of the flake */
502         Angle = ((2 * M_PI) / Flake.Arms) * i + AngleOffset;
503         /* calculate the x and y dispositions for this arm */
504         y = (int)(sin(Angle) * Radius);
505         x = (int)(cos(Angle) * Radius);
506         /* draw the base for the arm */
507         Flake.GCValues.line_width = Diameter;
508         XFreeGC(Flake.DisplayVar, Flake.GCVar);
509         Flake.GCVar =
510            XCreateGC(Flake.DisplayVar, Flake.DB.b, Flake.GCFlags,
511                      &Flake.GCValues);
512         XSetForeground(Flake.DisplayVar, Flake.GCVar, Flake.BordColor);
513         XDrawLine(Flake.DisplayVar, Flake.DB.b, Flake.GCVar, XPos, YPos,
514                   XPos + x, YPos + y);
515      }
516    /* draw the flake one arm at a time */
517    for (i = 1; i <= Flake.Arms; i++)
518      {
519         /* compute the angle of this arm of the flake */
520         Angle = ((2 * M_PI) / Flake.Arms) * i + AngleOffset;
521         /* calculate the x and y dispositions for this arm */
522         y = (int)(sin(Angle) * Radius);
523         x = (int)(cos(Angle) * Radius);
524         /* draw the inside of the arm */
525         Flake.GCValues.line_width = Flake.Thickness / Layer;
526         XFreeGC(Flake.DisplayVar, Flake.GCVar);
527         Flake.GCVar =
528            XCreateGC(Flake.DisplayVar, Flake.DB.b, Flake.GCFlags,
529                      &Flake.GCValues);
530         XSetForeground(Flake.DisplayVar, Flake.GCVar, Flake.ForeColor);
531         XDrawLine(Flake.DisplayVar, Flake.DB.b, Flake.GCVar, XPos, YPos,
532                   XPos + x, YPos + y);
533      }
534 }
535
536 static void
537 FuzzyFlakes(Display * dpy, Window window)
538 {
539    int                 i, j;
540
541    FuzzyFlakesMove();
542    XSetForeground(Flake.DisplayVar, Flake.GCVar, Flake.BackColor);
543    XFillRectangle(Flake.DisplayVar, Flake.DB.b, Flake.GCVar, 0, 0,
544                   Flake.XGWA.width, Flake.XGWA.height);
545    for (i = Flake.Layers; i >= 1; i--)
546      {
547         for (j = 0; j < Flake.Density; j++)
548           {
549              FuzzyFlakesDrawFlake(Flake.Flakes[i - 1][j]->TrueX,
550                                   Flake.Flakes[i - 1][j]->YPos,
551                                   Flake.Flakes[i - 1][j]->Angle, i);
552           }
553      }
554
555 }
556
557 char               *progclass = "FuzzyFlakes";
558 char               *defaults[] = {
559    "*color:     #efbea5",
560    "*arms:      5",
561    "*thickness: 10",
562    "*bthickness:        3",
563    "*radius:    20",
564    "*layers:    3",
565    "*density:   5",
566    "*fallingspeed:      10",
567    "*delay:     10000",
568    "*doubleBuffer:      True",
569    "*randomColors:      False",
570    0
571 };
572
573 XrmOptionDescRec    options[] = {
574    {
575     "-color", ".color", XrmoptionSepArg, 0},
576    {
577     "-arms", ".arms", XrmoptionSepArg, 0},
578    {
579     "-thickness", ".thickness", XrmoptionSepArg, 0},
580    {
581     "-bthickness", ".bthickness", XrmoptionSepArg, 0},
582    {
583     "-radius", ".radius", XrmoptionSepArg, 0},
584    {
585     "-layers", ".layers", XrmoptionSepArg, 0},
586    {
587     "-density", ".density", XrmoptionSepArg, 0},
588    {
589     "-speed", ".fallingspeed", XrmoptionSepArg, 0},
590    {
591     "-delay", ".delay", XrmoptionSepArg, 0},
592    {
593     "-db", ".doubleBuffer", XrmoptionNoArg, "True"},
594    {
595     "-no-db", ".doubleBuffer", XrmoptionNoArg, "False"},
596    {
597     "-random-colors", ".randomColors", XrmoptionNoArg, "True"},
598    {
599     0, 0, 0, 0}
600 };
601
602 void
603 screenhack(Display * dpy, Window window)
604 {
605    register int        tick = 0;
606
607    /* This is needed even if it is going to be set to false */
608    Flake.RandomColors = True;
609
610    /* set up our colors amoung many other things */
611    FuzzyFlakesInit(dpy, window);
612
613    while (1)
614      {
615         /* Test every 50 ticks for a screen resize */
616         tick++;
617         if (tick == 50)
618           {
619              FuzzyFlakesResizeTest();
620              tick = 0;
621           }
622         FuzzyFlakes(dpy, Flake.DB.b);
623         if (Flake.DB.dbuf)
624           {
625              XCopyArea(Flake.DisplayVar, Flake.DB.b, Flake.WindowVar,
626                        Flake.GCVar, 0, 0, Flake.XGWA.width, Flake.XGWA.height,
627                        0, 0);
628              Flake.DB.b =
629                 (Flake.DB.b == Flake.DB.ba ? Flake.DB.bb : Flake.DB.ba);
630           }
631         screenhack_handle_events(dpy);
632         XSync(dpy, False);
633         if (Flake.Delay)
634            usleep(Flake.Delay);
635      }
636 }
637
638 /* EOF */