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