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@jwz.org> |
---|
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 | screenhack_handle_events (dpy); |
---|
218 | if (delay) |
---|
219 | usleep(delay); |
---|
220 | } |
---|
221 | } |
---|
222 | |
---|
223 | /** |
---|
224 | * paint adds a new layer of multicolored rectangles within a grid of |
---|
225 | * randomly generated size. Each row of rectangles is the same color, |
---|
226 | * but colors vary slightly from row to row. Each rectangle is placed |
---|
227 | * within a regularly-sized cell, but each rectangle is sized and |
---|
228 | * placed randomly within that cell. |
---|
229 | * |
---|
230 | * @param g the Graphics coordinate in which to draw |
---|
231 | * @see #genNewColor |
---|
232 | **/ |
---|
233 | static void paint(void) |
---|
234 | { |
---|
235 | int i; |
---|
236 | int cellsWide, cellsHigh, cellWidth, cellHeight; |
---|
237 | static int width, height; |
---|
238 | static int size_check = 1; |
---|
239 | |
---|
240 | if (--size_check <= 0) |
---|
241 | { |
---|
242 | XWindowAttributes xgwa; |
---|
243 | XGetWindowAttributes (dpy, window, &xgwa); |
---|
244 | width = xgwa.width; |
---|
245 | height = xgwa.height; |
---|
246 | size_check = 1000; |
---|
247 | } |
---|
248 | |
---|
249 | /* How many cells wide the grid is (equal to gridSize +/- (gridSize / 2)) |
---|
250 | */ |
---|
251 | cellsWide = c_tweak(gridSize, gridSize / 2); |
---|
252 | /* How many cells high the grid is (equal to gridSize +/- (gridSize / 2)) |
---|
253 | */ |
---|
254 | cellsHigh = c_tweak(gridSize, gridSize / 2); |
---|
255 | /* How wide each cell in the grid is */ |
---|
256 | cellWidth = width / cellsWide; |
---|
257 | /* How tall each cell in the grid is */ |
---|
258 | cellHeight = height / cellsHigh; |
---|
259 | |
---|
260 | /* Ensure that each cell is above a certain minimum size */ |
---|
261 | |
---|
262 | if (cellWidth < MINCELLSIZE) { |
---|
263 | cellWidth = MINCELLSIZE; |
---|
264 | cellsWide = width / cellWidth; |
---|
265 | } |
---|
266 | |
---|
267 | if (cellHeight < MINCELLSIZE) { |
---|
268 | cellHeight = MINCELLSIZE; |
---|
269 | cellsHigh = width / cellWidth; |
---|
270 | } |
---|
271 | |
---|
272 | /* fill the grid with randomly-generated cells */ |
---|
273 | for(i = 0; i < cellsHigh; i++) { |
---|
274 | int j; |
---|
275 | |
---|
276 | /* Each row is a different color, randomly generated (but constrained) */ |
---|
277 | if (!mono_p) |
---|
278 | { |
---|
279 | int c = genNewColor(); |
---|
280 | XSetForeground(dpy, fg_gc, colors[c].pixel); |
---|
281 | } |
---|
282 | |
---|
283 | for(j = 0; j < cellsWide; j++) { |
---|
284 | int curWidth, curHeight, curX, curY; |
---|
285 | |
---|
286 | /* Generate a random height for a rectangle and make sure that */ |
---|
287 | /* it's above a certain minimum size */ |
---|
288 | curHeight = random() % (cellHeight - shadowWidth); |
---|
289 | if (curHeight < MINRECTSIZE) |
---|
290 | curHeight = MINRECTSIZE; |
---|
291 | /* Generate a random width for a rectangle and make sure that |
---|
292 | it's above a certain minimum size */ |
---|
293 | curWidth = random() % (cellWidth - shadowWidth); |
---|
294 | if (curWidth < MINRECTSIZE) |
---|
295 | curWidth = MINRECTSIZE; |
---|
296 | /* Figure out a random place to locate the rectangle within the |
---|
297 | cell */ |
---|
298 | curY = (i * cellHeight) + (random() % ((cellHeight - curHeight) - |
---|
299 | shadowWidth)); |
---|
300 | curX = (j * cellWidth) + (random() % ((cellWidth - curWidth) - |
---|
301 | shadowWidth)); |
---|
302 | |
---|
303 | /* Draw the shadow */ |
---|
304 | if (elevation > 0) |
---|
305 | XFillRectangle(dpy, window, shadow_gc, |
---|
306 | curX + elevation, curY + elevation, |
---|
307 | curWidth, curHeight); |
---|
308 | |
---|
309 | /* Draw the edge */ |
---|
310 | if (shadowWidth > 0) |
---|
311 | XFillRectangle(dpy, window, bg_gc, |
---|
312 | curX + shadowWidth, curY + shadowWidth, |
---|
313 | curWidth, curHeight); |
---|
314 | |
---|
315 | XFillRectangle(dpy, window, fg_gc, curX, curY, curWidth, curHeight); |
---|
316 | |
---|
317 | /* Draw a 1-pixel black border around the rectangle */ |
---|
318 | XDrawRectangle(dpy, window, bg_gc, curX, curY, curWidth, curHeight); |
---|
319 | } |
---|
320 | |
---|
321 | } |
---|
322 | } |
---|
323 | |
---|
324 | |
---|
325 | /** |
---|
326 | * genNewColor returns a new color, gradually mutating the colors and |
---|
327 | * occasionally returning a totally random color, just for variety. |
---|
328 | * |
---|
329 | * @return the new color |
---|
330 | **/ |
---|
331 | static int genNewColor(void) |
---|
332 | { |
---|
333 | /* These lines handle "sway", or the gradual random changing of */ |
---|
334 | /* colors. After genNewColor() has been called a given number of */ |
---|
335 | /* times (specified by a random permutation of the tweak variable), */ |
---|
336 | /* take whatever color has been most recently randomly generated and */ |
---|
337 | /* make it the new base color. */ |
---|
338 | if (timeLeft == 0) { |
---|
339 | timeLeft = c_tweak(sway, sway / 3); |
---|
340 | curColor = curBase; |
---|
341 | } else { |
---|
342 | timeLeft--; |
---|
343 | } |
---|
344 | |
---|
345 | /* If a randomly generated number is less than the threshold value, |
---|
346 | produce a "sport" color value that is completely unrelated to the |
---|
347 | current palette. */ |
---|
348 | if (0 == (random() % THRESHOLD)) { |
---|
349 | return (random() % ncolors); |
---|
350 | } else { |
---|
351 | curBase = genConstrainedColor(curColor, tweak); |
---|
352 | return curBase; |
---|
353 | } |
---|
354 | |
---|
355 | } |
---|
356 | |
---|
357 | /** |
---|
358 | * genConstrainedColor creates a random new color within a certain |
---|
359 | * range of an existing color. Right now this works with RGB color |
---|
360 | * values, but a future version of the program will most likely use HSV |
---|
361 | * colors, which should generate a more pleasing progression of values. |
---|
362 | * |
---|
363 | * @param base the color on which the new color will be based |
---|
364 | * @param tweak the amount that the new color can be tweaked |
---|
365 | * @return a new constrained color |
---|
366 | * @see #genNewColor |
---|
367 | **/ |
---|
368 | static int genConstrainedColor(int base, int tweak) |
---|
369 | { |
---|
370 | int i = 1 + (random() % tweak); |
---|
371 | if (random() & 1) |
---|
372 | i = -i; |
---|
373 | i = (base + i) % ncolors; |
---|
374 | while (i < 0) |
---|
375 | i += ncolors; |
---|
376 | return i; |
---|
377 | } |
---|
378 | |
---|
379 | /** |
---|
380 | * Utility function to generate a tweaked color value |
---|
381 | * |
---|
382 | * @param base the byte value on which the color is based |
---|
383 | * @param tweak the amount the value will be skewed |
---|
384 | * @see #tweak |
---|
385 | * @return the tweaked byte |
---|
386 | **/ |
---|
387 | static int c_tweak(int base, int tweak) |
---|
388 | { |
---|
389 | int ranTweak = (random() % (2 * tweak)); |
---|
390 | int n = (base + (ranTweak - tweak)); |
---|
391 | if (n < 0) n = -n; |
---|
392 | return (n < 255 ? n : 255); |
---|
393 | } |
---|