http://ftp.x.org/contrib/applications/xscreensaver-2.23.tar.gz
[xscreensaver] / hacks / cynosure.c
1 /* cynosure --- draw some rectangles
2  *
3  * 01-aug-96: written in Java by ozymandias G desiderata <ogd@organic.com>
4  * 25-dec-97: ported to C and XScreenSaver by Jamie Zawinski <jwz@netscape.com>
5  *
6  * Original version:
7  *   http://www.organic.com/staff/ogd/java/cynosure.html
8  *   http://www.organic.com/staff/ogd/java/source/cynosure/Cynosure-java.txt
9  *
10  * Original comments and copyright:
11  *
12  *   Cynosure.java
13  *   A Java implementation of Stephen Linhart's Cynosure screen-saver as a
14  *   drop-in class.
15  *
16  *   Header: /home/ogd/lib/cvs/aoaioxxysz/graphics/Cynosure.java,v 1.2 1996/08/02 02:41:21 ogd Exp 
17  *
18  *   ozymandias G desiderata <ogd@organic.com>
19  *   Thu Aug  1 1996
20  *
21  *   COPYRIGHT NOTICE
22  *
23  *   Copyright 1996 ozymandias G desiderata. Title, ownership rights, and
24  *   intellectual property rights in and to this software remain with
25  *   ozymandias G desiderata. This software may be copied, modified,
26  *   or used as long as this copyright is retained. Use this code at your
27  *   own risk.
28  *
29  *   Revision: 1.2 
30  *
31  *   Log: Cynosure.java,v 
32  *   Revision 1.2  1996/08/02 02:41:21  ogd
33  *   Added a few more comments, fixed messed-up header.
34  *
35  *   Revision 1.1.1.1  1996/08/02 02:30:45  ogd
36  *   First version
37  */
38
39 #include "screenhack.h"
40 static Display *dpy;
41 static Window window;
42 static XColor *colors;
43 static int ncolors;
44 static int fg_pixel, bg_pixel;
45 static GC fg_gc, bg_gc, shadow_gc;
46
47 static void paint(void);
48 static int genNewColor(void);
49 static int genConstrainedColor(int base, int tweak);
50 static int c_tweak(int base, int tweak);
51
52 /**
53  * The current color that is being tweaked to create the
54  * rectangles.
55  **/
56 static int curColor;
57
58 /**
59  * A variable used for the progression of the colors (yes, I know
60  * that's a lame explanation, but if your read the source, it should
61  * become obvious what I'm doing with this variable).
62  **/
63 static int curBase;
64
65 /**
66  * The width of the right and bottom edges of the rectangles.
67  **/
68 static int   shadowWidth;
69
70 /* The offset of the dropshadow beneath the rectangles. */
71 static int   elevation;
72
73 /**
74  * The approximate amount of time that will elapse before the base
75  * color is permanently changed.
76  *
77  * @see #tweak
78  **/
79 static int   sway;
80
81 /**
82  * The counter of time left until the base color value used. This class
83  * variable is necessary because Java doesn't support static method
84  * variables (grr grr).
85  **/
86 static int   timeLeft;
87
88 /**
89  * The amount by which the color of the polygons drawn will vary.
90  *
91  * @see #sway;
92  **/
93 static int   tweak;
94
95 /**
96  * The smallest size for an individual cell.
97  **/
98 #define MINCELLSIZE 16
99
100 /**
101  * The narrowest a rectangle can be.
102  **/
103 #define MINRECTSIZE 6
104
105 /**
106  * The size of the grid that the rectangles are placed within.
107  **/
108 static int gridSize;
109
110 /**
111  * Every so often genNewColor() generates a completely random
112  * color. This variable sets how frequently that happens. It's
113  * currently set to happen 1% of the time.
114  *
115  * @see #genNewColor
116  **/
117 #define THRESHOLD 100 /*0.01*/
118
119
120 char *progclass = "Cynosure";
121 char *defaults [] = {
122   ".background:         black",
123   ".foreground:         white",
124   "*delay:              500000",
125   "*colors:             128",
126   "*iterations:         100",
127   "*shadowWidth:        2",
128   "*elevation:          5",
129   "*sway:               30",
130   "*tweak:              20",
131   "*gridSize:           12",
132   0
133 };
134
135 XrmOptionDescRec options [] = {
136   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
137   { "-ncolors",         ".colors",      XrmoptionSepArg, 0 },
138   { "-iterations",      ".iterations",  XrmoptionSepArg, 0 },
139   { 0, 0, 0, 0 }
140 };
141
142
143 void screenhack(Display *d, Window w) 
144 {
145   XWindowAttributes xgwa;
146   XGCValues gcv;
147   int delay;
148   int i, iterations;
149
150   dpy = d;
151   window = w;
152
153   curColor    = 0;
154   curBase     = curColor;
155   shadowWidth = get_integer_resource ("shadowWidth", "Integer");
156   elevation   = get_integer_resource ("elevation", "Integer");
157   sway        = get_integer_resource ("sway", "Integer");
158   tweak       = get_integer_resource ("tweak", "Integer");
159   gridSize    = get_integer_resource ("gridSize", "Integer");
160   timeLeft    = 0;
161
162   XGetWindowAttributes (dpy, window, &xgwa);
163
164   ncolors = get_integer_resource ("colors", "Colors");
165   if (ncolors < 2) ncolors = 2;
166   if (ncolors <= 2) mono_p = True;
167
168   if (mono_p)
169     colors = 0;
170   else
171     colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
172
173   if (mono_p)
174     ;
175   else {
176     make_smooth_colormap (dpy, xgwa.visual, xgwa.colormap, colors, &ncolors,
177                           True, 0, True);
178     if (ncolors <= 2) {
179       mono_p = True;
180       ncolors = 2;
181       if (colors) free(colors);
182       colors = 0;
183     }
184   }
185
186   bg_pixel = get_pixel_resource("background", "Background", dpy,
187                                 xgwa.colormap);
188   fg_pixel = get_pixel_resource("foreground", "Foreground", dpy,
189                                 xgwa.colormap);
190
191   gcv.foreground = fg_pixel;
192   fg_gc = XCreateGC(dpy, window, GCForeground, &gcv);
193   gcv.foreground = bg_pixel;
194   bg_gc = XCreateGC(dpy, window, GCForeground, &gcv);
195
196   gcv.fill_style = FillStippled;
197   gcv.stipple = XCreateBitmapFromData(dpy, window, "\125\252", 8, 2);
198   shadow_gc = XCreateGC(dpy, window, GCForeground|GCFillStyle|GCStipple, &gcv);
199   XFreePixmap(dpy, gcv.stipple);
200
201   delay = get_integer_resource ("delay", "Delay");
202   iterations = get_integer_resource ("iterations", "Iterations");
203
204   i = 0;
205   while (1)
206     {
207       if (iterations > 0 && ++i >= iterations)
208         {
209           i = 0;
210           if (!mono_p)
211             XSetWindowBackground(dpy, window,
212                                  colors[random() % ncolors].pixel);
213           XClearWindow(dpy, window);
214         }
215       paint();
216       XSync(dpy, False);
217       if (delay)
218         usleep(delay);
219     }
220 }
221
222 /**
223  * paint adds a new layer of multicolored rectangles within a grid of
224  * randomly generated size. Each row of rectangles is the same color,
225  * but colors vary slightly from row to row. Each rectangle is placed
226  * within a regularly-sized cell, but each rectangle is sized and
227  * placed randomly within that cell.
228  *
229  * @param g      the Graphics coordinate in which to draw
230  * @see #genNewColor
231  **/
232 static void paint(void)
233 {
234     int i;
235     int cellsWide, cellsHigh, cellWidth, cellHeight;
236     static int width, height;
237     static int size_check = 1;
238
239     if (--size_check <= 0)
240       {
241         XWindowAttributes xgwa;
242         XGetWindowAttributes (dpy, window, &xgwa);
243         width = xgwa.width;
244         height = xgwa.height;
245         size_check = 1000;
246       }
247
248     /* How many cells wide the grid is (equal to gridSize +/- (gridSize / 2))
249      */
250     cellsWide  = c_tweak(gridSize, gridSize / 2);
251     /* How many cells high the grid is (equal to gridSize +/- (gridSize / 2))
252      */
253     cellsHigh  = c_tweak(gridSize, gridSize / 2);
254     /* How wide each cell in the grid is */
255     cellWidth  = width  / cellsWide;
256     /* How tall each cell in the grid is */
257     cellHeight = height / cellsHigh;
258
259     /* Ensure that each cell is above a certain minimum size */
260
261     if (cellWidth < MINCELLSIZE) {
262       cellWidth  = MINCELLSIZE;
263       cellsWide  = width / cellWidth;
264     }
265
266     if (cellHeight < MINCELLSIZE) {
267       cellHeight = MINCELLSIZE;
268       cellsHigh  = width / cellWidth;
269     }
270
271     /* fill the grid with randomly-generated cells */
272     for(i = 0; i < cellsHigh; i++) {
273       int j;
274
275       /* Each row is a different color, randomly generated (but constrained) */
276       if (!mono_p)
277         {
278           int c = genNewColor();
279           XSetForeground(dpy, fg_gc, colors[c].pixel);
280         }
281
282       for(j = 0; j < cellsWide; j++) {
283         int curWidth, curHeight, curX, curY;
284
285         /* Generate a random height for a rectangle and make sure that */
286         /* it's above a certain minimum size */
287         curHeight = random() % (cellHeight - shadowWidth);
288         if (curHeight < MINRECTSIZE)
289           curHeight = MINRECTSIZE;
290         /* Generate a random width for a rectangle and make sure that
291            it's above a certain minimum size */
292         curWidth  = random() % (cellWidth  - shadowWidth);
293         if (curWidth < MINRECTSIZE)
294           curWidth = MINRECTSIZE;
295         /* Figure out a random place to locate the rectangle within the
296            cell */
297         curY      = (i * cellHeight) + (random() % ((cellHeight - curHeight) -
298                                                     shadowWidth));
299         curX      = (j * cellWidth) +  (random() % ((cellWidth  - curWidth) -
300                                                     shadowWidth));
301
302         /* Draw the shadow */
303         if (elevation > 0)
304           XFillRectangle(dpy, window, shadow_gc,
305                          curX + elevation, curY + elevation,
306                          curWidth, curHeight);
307
308         /* Draw the edge */
309         if (shadowWidth > 0)
310           XFillRectangle(dpy, window, bg_gc,
311                          curX + shadowWidth, curY + shadowWidth,
312                          curWidth, curHeight);
313
314         XFillRectangle(dpy, window, fg_gc, curX, curY, curWidth, curHeight);
315
316         /* Draw a 1-pixel black border around the rectangle */
317         XDrawRectangle(dpy, window, bg_gc, curX, curY, curWidth, curHeight);
318       }
319
320     }
321 }
322
323
324 /**
325  * genNewColor returns a new color, gradually mutating the colors and
326  * occasionally returning a totally random color, just for variety.
327  *
328  * @return the new color
329  **/
330 static int genNewColor(void)
331 {
332     /* These lines handle "sway", or the gradual random changing of */
333     /* colors. After genNewColor() has been called a given number of */
334     /* times (specified by a random permutation of the tweak variable), */
335     /* take whatever color has been most recently randomly generated and */
336     /* make it the new base color. */
337     if (timeLeft == 0) {
338       timeLeft = c_tweak(sway, sway / 3);
339       curColor = curBase;
340     } else {
341       timeLeft--;
342     }
343      
344     /* If a randomly generated number is less than the threshold value,
345        produce a "sport" color value that is completely unrelated to the 
346        current palette. */
347     if (0 == (random() % THRESHOLD)) {
348       return (random() % ncolors);
349     } else {
350       curBase = genConstrainedColor(curColor, tweak);
351       return curBase;
352     }
353
354 }
355
356 /**
357  * genConstrainedColor creates a random new color within a certain
358  * range of an existing color. Right now this works with RGB color
359  * values, but a future version of the program will most likely use HSV
360  * colors, which should generate a more pleasing progression of values.
361  *
362  * @param base  the color on which the new color will be based
363  * @param tweak the amount that the new color can be tweaked
364  * @return a new constrained color
365  * @see #genNewColor
366  **/
367 static int genConstrainedColor(int base, int tweak) 
368 {
369     int i = 1 + (random() % tweak);
370     if (random() & 1)
371       i = -i;
372     i = (base + i) % ncolors;
373     while (i < 0)
374       i += ncolors;
375     return i;
376 }
377
378 /**
379  * Utility function to generate a tweaked color value
380  *
381  * @param  base   the byte value on which the color is based
382  * @param  tweak  the amount the value will be skewed
383  * @see    #tweak
384  * @return the tweaked byte
385  **/
386 static int c_tweak(int base, int tweak) 
387 {
388     int ranTweak = (random() % (2 * tweak));
389     int n = (base + (ranTweak - tweak));
390     if (n < 0) n = -n;
391     return (n < 255 ? n : 255);
392 }