1 /* -*- Mode: C; tab-width: 2 -*- */
2 /* rain --- show raindrops */
3
4 #if 0
5 static const char sccsid[] = "@(#)rain.c 1.00 2007/05/22 xlockmore";
6 #endif
7
8 /*
9 * Copyright (c) 1999-2007 by Frank Fesevur
10 *
11 * Permission to use, copy, modify, and distribute this software and its
12 * documentation for any purpose and without fee is hereby granted,
13 * provided that the above copywidth notice appear in all copies and that
14 * both that copywidth notice and this permission notice appear in
15 * supporting documentation.
16 *
17 * This file is provided AS IS with no warranties of any kind. The author
18 * shall have no liability with respect to the infringement of copywidths,
19 * trade secrets or any patents by this file or any part thereof. In no
20 * event will the author be liable for any lost revenue or profits or
21 * other special, indirect and consequential damages.
22 *
23 * Revision History:
24 * 24-May-2007: port to xlockmore
25 * Jan-1999: Written to play with a MS-Windows screen saver
26 */
27
28 #ifdef STANDALONE
29 #define MODE_rain
30 #define DEFAULTS "*delay: 35000 \n" \
31 "*ncolors: 64 \n" \
32
33 # define free_rain 0
34 # define reshape_rain 0
35 # define rain_handle_event 0
36 #define SMOOTH_COLORS
37 #if 0
38 #define UNIFORM_COLORS /* To get blue water uncomment, but ... */
39 #endif
40 #include "xlockmore.h" /* in xscreensaver distribution */
41 #else /* STANDALONE */
42 #include "xlock.h" /* in xlockmore distribution */
43 #endif /* STANDALONE */
44
45 #ifdef MODE_rain
46
47 ENTRYPOINT ModeSpecOpt rain_opts =
48 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
49
50 #ifdef USE_MODULES
51 ModStruct rain_description =
52 {
53 "rain", "init_rain", "draw_rain", "release_rain",
54 "refresh_rain", "init_rain", (char *) NULL, &rain_opts,
55 35000, 1, 1, 1, 64, 0.3, "",
56 "It's raining", 0, NULL
57 };
58 #endif
59
60 #define MAX_RADIUS 25 /* Maximum radius of the drop */
61
62 typedef struct _drop
63 {
64 XRectangle drop; /* Rectangle of the falling rain-drop */
65 XPoint offset; /* Speed of the falling rain-drop */
66 XPoint pool; /* Centerpoint of the pool */
67 unsigned long color; /* Color of the drop and pool */
68 int radius; /* Radius of the pool */
69 int radius_step; /* Step-size to increase the pool */
70 int max_radius; /* Maximum width of the pool */
71 struct _drop *next;
72 } dropstruct;
73
74 typedef struct
75 {
76 int direction; /* Left to right (1) or Right to left (-1) */
77 int colored_drops; /* Colored drops? 1 or 0 */
78 int base_color; /* Colored drops, use base color? */
79 dropstruct *drops; /* The drops */
80 } rainstruct;
81
82 static rainstruct *rain = (rainstruct *) NULL;
83
84 /*
85 * Draw an ellipse at 'drop->drop' with a radius of 'drop->radius' and color 'color'
86 * Not using the color of the 'drop' because we have to draw a black one as well.
87 */
88
DrawEllipse(ModeInfo * mi,dropstruct * drop,unsigned long color)89 static void DrawEllipse(ModeInfo * mi, dropstruct *drop, unsigned long color)
90 {
91 Display *display = MI_DISPLAY(mi);
92 Window window = MI_WINDOW(mi);
93 GC gc = MI_GC(mi);
94 int w = 2 * drop->radius;
95 int h = (drop->radius * 2) / 3;
96
97 XSetForeground(display, gc, color);
98 XDrawArc(display, window, gc,
99 drop->drop.x - w, drop->drop.y - h, 2 * w + 1, 2 * h + 1, 0, 360 * 64);
100 }
101
102 /*
103 * Draw a line from (rect.x, rect.height) to (rect.width, rect.y)
104 * with color 'color'
105 */
106
DrawLine(ModeInfo * mi,XRectangle rect,unsigned long color)107 static void DrawLine(ModeInfo * mi, XRectangle rect, unsigned long color)
108 {
109 Display *display = MI_DISPLAY(mi);
110 Window window = MI_WINDOW(mi);
111 GC gc = MI_GC(mi);
112
113 XSetForeground(display, gc, color);
114 XDrawLine(display, window, gc, rect.x, rect.height, rect.width, rect.y);
115 }
116
117 static void
free_rain_screen(rainstruct * rp)118 free_rain_screen(rainstruct *rp)
119 {
120 if (rp == NULL) {
121 return;
122 }
123 if (rp->drops != NULL)
124 {
125 dropstruct *drop, *dropNext;
126 /* Cleanup the drops */
127
128 drop = rp->drops;
129 while (drop != NULL) {
130 dropNext = drop->next;
131 free(drop);
132 drop = dropNext;
133 }
134 rp->drops = NULL;
135 }
136 rp = NULL;
137 }
138
139 /*
140 * Initialise a single drop
141 */
142
InitDropColors(ModeInfo * mi,rainstruct * rp)143 static void InitDropColors(ModeInfo * mi, rainstruct *rp)
144 {
145 /* Once in every 10 times show only white drops */
146 rp->colored_drops = (NRAND(10) != 1);
147
148 /* When using colored, use the all random colors or around the base color */
149 if (rp->colored_drops)
150 {
151 if (NRAND(2) == 1)
152 rp->base_color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
153 else
154 rp->base_color = 0;
155 }
156 }
157
158 /*
159 * Initialise a single drop
160 */
161
InitDrop(ModeInfo * mi,rainstruct * rp,dropstruct * drop)162 static void InitDrop(ModeInfo * mi, rainstruct *rp, dropstruct *drop)
163 {
164 /* Where does the drop fall in the pool?
165 At least 20% of height from top and not completely at the bottom */
166 int yMin = MI_HEIGHT(mi) / 5;
167 int yMax = MI_HEIGHT(mi) - ((MAX_RADIUS * 3) / 2);
168 drop->pool.x = 0;
169 drop->pool.y = yMin + NRAND(yMax - yMin);
170
171 /* How fast does it fall? */
172 drop->offset.x = 5 + NRAND(5);
173 drop->offset.y = 20 + NRAND(20);
174
175 /* Where does the drop start */
176 drop->drop.x = NRAND(MI_WIDTH(mi));
177 drop->drop.height = 0;
178 drop->drop.width = drop->drop.x + drop->offset.x;
179 drop->drop.y = drop->drop.height + drop->offset.y;
180
181 /* How large is the pool and how fast does it grow? */
182 drop->radius = 0;
183 drop->radius_step = 1 + NRAND(2);
184 drop->max_radius = (MAX_RADIUS / 2) + NRAND(MAX_RADIUS / 2);
185
186 /* Colored drops? */
187 if (rp->colored_drops)
188 {
189 if (rp->base_color == 0)
190 drop->color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
191 else
192 drop->color = rp->base_color + (NRAND(2) == 1 ? -1 : 1) * NRAND(12);
193 }
194 else
195 drop->color = MI_WHITE_PIXEL(mi);
196 }
197
198 /*
199 * Initialise the mode
200 */
201
202 ENTRYPOINT void
init_rain(ModeInfo * mi)203 init_rain(ModeInfo * mi)
204 {
205 rainstruct *rp;
206 int i, nr_drops, max_drops;
207 dropstruct *drop;
208
209 /* Allocate the memory */
210 MI_INIT(mi, rain);
211 rp = &rain[MI_SCREEN(mi)];
212 MI_CLEARWINDOW(mi);
213
214 /* "Left to Right" or "Right to Left" */
215 rp->direction = (NRAND(2) == 1 ? -1 : 1);
216 InitDropColors(mi, rp);
217 /* Determine how many drops to show */
218 max_drops = MI_WIDTH(mi) / 50;
219 i = MAX(max_drops / 2, 2);
220 nr_drops = NRAND(i) + i;
221 nr_drops = MAX(1, nr_drops);
222 nr_drops = MIN(nr_drops, max_drops);
223
224 /* Initialise these drops */
225 free_rain_screen(rp);
226 for (i = 0; i < nr_drops; i++)
227 {
228 /* Allocate the memory for the drop */
229 drop = (dropstruct *) malloc(sizeof(dropstruct));
230 if (drop == NULL)
231 return;
232
233 /* Initialise the drop */
234 InitDrop(mi, rp, drop);
235 drop->next = NULL;
236
237 /* Add the drop to the list */
238 if (rp->drops != NULL)
239 drop->next = rp->drops;
240 rp->drops = drop;
241 }
242 }
243
244 /*
245 * Draw the drops
246 */
247
248 ENTRYPOINT void
draw_rain(ModeInfo * mi)249 draw_rain(ModeInfo * mi)
250 {
251 dropstruct *drop;
252 rainstruct *rp;
253
254 if (rain == NULL)
255 return;
256 rp = &rain[MI_SCREEN(mi)];
257 /* Just in case */
258 if (rp->drops == NULL)
259 return;
260
261 if (NRAND(MI_WIDTH(mi) / 4) == 1)
262 InitDropColors(mi, rp);
263
264 for (drop = rp->drops; drop != NULL; drop = drop->next)
265 {
266 /* Show the drop or show the pool */
267 if (drop->drop.width < MI_WIDTH(mi) - drop->max_radius &&
268 drop->drop.width > drop->max_radius &&
269 drop->drop.y < drop->pool.y)
270 {
271 /* Erase the previous drop */
272 DrawLine(mi, drop->drop, MI_BLACK_PIXEL(mi));
273
274 /* Calculate the new position */
275 drop->drop.x = drop->drop.width;
276 drop->drop.height = drop->drop.y;
277 drop->drop.width += rp->direction * drop->offset.x;
278 drop->drop.y += drop->offset.y;
279
280 /* Draw the new drop */
281 DrawLine(mi, drop->drop, drop->color);
282 }
283 else
284 {
285 /* Erase the last drop */
286 if (drop->radius == 0)
287 {
288 DrawLine(mi, drop->drop, MI_BLACK_PIXEL(mi));
289
290 drop->pool.x = drop->drop.width;
291 drop->pool.y = drop->drop.y;
292 }
293
294 /* Erase the previous pool ellipse */
295 DrawEllipse(mi, drop, MI_BLACK_PIXEL(mi));
296
297 /* Was last pool? Initialise again */
298 if (drop->radius > drop->max_radius)
299 InitDrop(mi, rp, drop);
300 else
301 {
302 /* Enlarge the pool ellipse */
303 drop->radius += drop->radius_step;
304
305 /* Draw the new pool */
306 DrawEllipse(mi, drop, drop->color);
307 }
308 }
309 }
310 }
311
312 /*
313 * Clean up the mess
314 */
315
316 ENTRYPOINT void
release_rain(ModeInfo * mi)317 release_rain(ModeInfo * mi)
318 {
319 if (rain != NULL)
320 {
321 int screen;
322
323 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
324 free_rain_screen(&rain[screen]);
325 free(rain);
326 rain = (rainstruct *) NULL;
327 }
328 }
329
330 #ifndef STANDALONE
331 /*
332 *
333 */
334
335 ENTRYPOINT void
refresh_rain(ModeInfo * mi)336 refresh_rain(ModeInfo * mi)
337 {
338 MI_CLEARWINDOW(mi);
339 }
340 #endif
341
342 XSCREENSAVER_MODULE ("Rain", rain)
343
344 #endif /* MODE_rain */
345