http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.01.tar.gz
[xscreensaver] / hacks / laser.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* laser --- spinning lasers */
3
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)laser.c       5.00 2000/11/01 xlockmore";
6
7 #endif
8
9 /*-
10  * Copyright (c) 1995 Pascal Pensa <pensa@aurora.unice.fr>
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * Revision History:
25  * 01-Nov-2000: Allocation checks
26  * 10-May-1997: Compatible with xscreensaver
27  * 1995: Written.
28  */
29
30 #ifdef STANDALONE
31 #define MODE_laser
32 #define PROGCLASS "Laser"
33 #define HACK_INIT init_laser
34 #define HACK_DRAW draw_laser
35 #define laser_opts xlockmore_opts
36 #define DEFAULTS "*delay: 40000 \n" \
37  "*count: 10 \n" \
38  "*cycles: 200 \n" \
39  "*ncolors: 64 \n"
40 #define BRIGHT_COLORS
41 #include "xlockmore.h"          /* in xscreensaver distribution */
42 #else /* STANDALONE */
43 #include "xlock.h"              /* in xlockmore distribution */
44 #endif /* STANDALONE */
45
46 #ifdef MODE_laser
47
48 ModeSpecOpt laser_opts =
49 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
50
51 #ifdef USE_MODULES
52 ModStruct   laser_description =
53 {"laser", "init_laser", "draw_laser", "release_laser",
54  "refresh_laser", "init_laser", (char *) NULL, &laser_opts,
55  20000, -10, 200, 1, 64, 1.0, "",
56  "Shows spinning lasers", 0, NULL};
57
58 #endif
59
60 #define MINREDRAW 3             /* Number of redrawn on each frame */
61 #define MAXREDRAW 8
62
63 #define MINLASER  1             /* Laser number */
64
65 #define MINWIDTH  2             /* Laser ray width range */
66 #define MAXWIDTH 40
67
68 #define MINSPEED  2             /* Speed range */
69 #define MAXSPEED 17
70
71 #define MINDIST  10             /* Minimal distance from edges */
72
73 #define COLORSTEP 2             /* Laser color step */
74
75 #define RANGE_RAND(min,max) (int) ((min) + LRAND() % ((max) - (min)))
76
77 typedef enum {
78         TOP, RIGHT, BOTTOM, LEFT
79 } border;
80
81 typedef struct {
82         int         bx;         /* border x */
83         int         by;         /* border y */
84         border      bn;         /* active border */
85         int         dir;        /* direction */
86         int         speed;      /* laser velocity from MINSPEED to MAXSPEED */
87         int         sx[MAXWIDTH];       /* x stack */
88         int         sy[MAXWIDTH];       /* x stack */
89         XGCValues   gcv;        /* for color */
90 } laserstruct;
91
92 typedef struct {
93         int         width;
94         int         height;
95         int         cx;         /* center x */
96         int         cy;         /* center y */
97         int         lw;         /* laser width */
98         int         ln;         /* laser number */
99         int         lr;         /* laser redraw */
100         int         sw;         /* stack width */
101         int         so;         /* stack offset */
102         int         time;       /* up time */
103         GC          stippledGC;
104         XGCValues   gcv_black;  /* for black color */
105         laserstruct *laser;
106 } lasersstruct;
107
108 static lasersstruct *lasers = (lasersstruct *) NULL;
109
110 static void
111 free_laser(Display *display, lasersstruct *lp)
112 {
113         if (lp->laser != NULL) {
114                 (void) free((void *) lp->laser);
115                 lp->laser = (laserstruct *) NULL;
116         }
117         if (lp->stippledGC != None) {
118                 XFreeGC(display, lp->stippledGC);
119                 lp->stippledGC = None;
120         }
121 }
122
123 void
124 init_laser(ModeInfo * mi)
125 {
126         Display *display = MI_DISPLAY(mi);
127         int         i, c = 0;
128         lasersstruct *lp;
129
130         if (lasers == NULL) {
131                 if ((lasers = (lasersstruct *) calloc(MI_NUM_SCREENS(mi),
132                                              sizeof (lasersstruct))) == NULL)
133                         return;
134         }
135         lp = &lasers[MI_SCREEN(mi)];
136
137         lp->width = MI_WIDTH(mi);
138         lp->height = MI_HEIGHT(mi);
139         lp->time = 0;
140
141         lp->ln = MI_COUNT(mi);
142         if (lp->ln < -MINLASER) {
143                 /* if lp->ln is random ... the size can change */
144                 if (lp->laser != NULL) {
145                         (void) free((void *) lp->laser);
146                         lp->laser = (laserstruct *) NULL;
147                 }
148                 lp->ln = NRAND(-lp->ln - MINLASER + 1) + MINLASER;
149         } else if (lp->ln < MINLASER)
150                 lp->ln = MINLASER;
151
152         if (lp->laser == NULL) {
153                 if ((lp->laser = (laserstruct *) malloc(lp->ln *
154                                 sizeof (laserstruct))) == NULL) {
155                         free_laser(display, lp);
156                         return;
157                 }
158         }
159         if (lp->stippledGC == None) {
160                 XGCValues   gcv;
161
162                 gcv.foreground = MI_WHITE_PIXEL(mi);
163                 gcv.background = MI_BLACK_PIXEL(mi);
164                 lp->gcv_black.foreground = MI_BLACK_PIXEL(mi);
165                 if ((lp->stippledGC = XCreateGC(display, MI_WINDOW(mi),
166                                 GCForeground | GCBackground, &gcv)) == None) {
167                         free_laser(display, lp);
168                         return;
169                 }
170         }
171         MI_CLEARWINDOW(mi);
172
173         if (MINDIST < lp->width - MINDIST)
174                 lp->cx = RANGE_RAND(MINDIST, lp->width - MINDIST);
175         else
176                 lp->cx = RANGE_RAND(0, lp->width);
177         if (MINDIST < lp->height - MINDIST)
178                 lp->cy = RANGE_RAND(MINDIST, lp->height - MINDIST);
179         else
180                 lp->cy = RANGE_RAND(0, lp->height);
181         lp->lw = RANGE_RAND(MINWIDTH, MAXWIDTH);
182         lp->lr = RANGE_RAND(MINREDRAW, MAXREDRAW);
183         lp->sw = 0;
184         lp->so = 0;
185
186         if (MI_NPIXELS(mi) > 2)
187                 c = NRAND(MI_NPIXELS(mi));
188
189         for (i = 0; i < lp->ln; i++) {
190                 laserstruct *l = &lp->laser[i];
191
192                 l->bn = (border) NRAND(4);
193
194                 switch (l->bn) {
195                         case TOP:
196                                 l->bx = NRAND(lp->width);
197                                 l->by = 0;
198                                 break;
199                         case RIGHT:
200                                 l->bx = lp->width;
201                                 l->by = NRAND(lp->height);
202                                 break;
203                         case BOTTOM:
204                                 l->bx = NRAND(lp->width);
205                                 l->by = lp->height;
206                                 break;
207                         case LEFT:
208                                 l->bx = 0;
209                                 l->by = NRAND(lp->height);
210                 }
211
212                 l->dir = (int) (LRAND() & 1);
213                 l->speed = ((RANGE_RAND(MINSPEED, MAXSPEED) * lp->width) / 1000) + 1;
214                 if (MI_NPIXELS(mi) > 2) {
215                         l->gcv.foreground = MI_PIXEL(mi, c);
216                         c = (c + COLORSTEP) % MI_NPIXELS(mi);
217                 } else
218                         l->gcv.foreground = MI_WHITE_PIXEL(mi);
219         }
220 }
221
222 static void
223 draw_laser_once(ModeInfo * mi)
224 {
225         Display    *display = MI_DISPLAY(mi);
226         lasersstruct *lp = &lasers[MI_SCREEN(mi)];
227         int         i;
228
229         for (i = 0; i < lp->ln; i++) {
230                 laserstruct *l = &lp->laser[i];
231
232                 if (lp->sw >= lp->lw) {
233                         XChangeGC(display, lp->stippledGC, GCForeground, &(lp->gcv_black));
234                         XDrawLine(display, MI_WINDOW(mi), lp->stippledGC,
235                                   lp->cx, lp->cy,
236                                   l->sx[lp->so], l->sy[lp->so]);
237                 }
238                 if (l->dir) {
239                         switch (l->bn) {
240                                 case TOP:
241                                         l->bx -= l->speed;
242                                         if (l->bx < 0) {
243                                                 l->by = -l->bx;
244                                                 l->bx = 0;
245                                                 l->bn = LEFT;
246                                         }
247                                         break;
248                                 case RIGHT:
249                                         l->by -= l->speed;
250                                         if (l->by < 0) {
251                                                 l->bx = lp->width + l->by;
252                                                 l->by = 0;
253                                                 l->bn = TOP;
254                                         }
255                                         break;
256                                 case BOTTOM:
257                                         l->bx += l->speed;
258                                         if (l->bx >= lp->width) {
259                                                 l->by = lp->height - l->bx % lp->width;
260                                                 l->bx = lp->width;
261                                                 l->bn = RIGHT;
262                                         }
263                                         break;
264                                 case LEFT:
265                                         l->by += l->speed;
266                                         if (l->by >= lp->height) {
267                                                 l->bx = l->by % lp->height;
268                                                 l->by = lp->height;
269                                                 l->bn = BOTTOM;
270                                         }
271                         }
272                 } else {
273                         switch (l->bn) {
274                                 case TOP:
275                                         l->bx += l->speed;
276                                         if (l->bx >= lp->width) {
277                                                 l->by = l->bx % lp->width;
278                                                 l->bx = lp->width;
279                                                 l->bn = RIGHT;
280                                         }
281                                         break;
282                                 case RIGHT:
283                                         l->by += l->speed;
284                                         if (l->by >= lp->height) {
285                                                 l->bx = lp->width - l->by % lp->height;
286                                                 l->by = lp->height;
287                                                 l->bn = BOTTOM;
288                                         }
289                                         break;
290                                 case BOTTOM:
291                                         l->bx -= l->speed;
292                                         if (l->bx < 0) {
293                                                 l->by = lp->height + l->bx;
294                                                 l->bx = 0;
295                                                 l->bn = LEFT;
296                                         }
297                                         break;
298                                 case LEFT:
299                                         l->by -= l->speed;
300                                         if (l->by < 0) {
301                                                 l->bx = -l->bx;
302                                                 l->by = 0;
303                                                 l->bn = TOP;
304                                         }
305                         }
306                 }
307
308                 XChangeGC(display, lp->stippledGC, GCForeground, &l->gcv);
309                 XDrawLine(display, MI_WINDOW(mi), lp->stippledGC,
310                           lp->cx, lp->cy, l->bx, l->by);
311
312                 l->sx[lp->so] = l->bx;
313                 l->sy[lp->so] = l->by;
314
315         }
316
317         if (lp->sw < lp->lw)
318                 ++lp->sw;
319
320         lp->so = (lp->so + 1) % lp->lw;
321 }
322
323 void
324 draw_laser(ModeInfo * mi)
325 {
326         int         i;
327         lasersstruct *lp;
328
329         if (lasers == NULL)
330                 return;
331         lp = &lasers[MI_SCREEN(mi)];
332         if (lp->laser == NULL)
333                 return;
334
335         MI_IS_DRAWN(mi) = True;
336         for (i = 0; i < lp->lr; i++)
337                 draw_laser_once(mi);
338
339         if (++lp->time > MI_CYCLES(mi))
340                 init_laser(mi);
341 }
342
343 void
344 release_laser(ModeInfo * mi)
345 {
346         if (lasers != NULL) {
347                 int         screen;
348
349                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
350                         free_laser(MI_DISPLAY(mi), &lasers[screen]);
351                 (void) free((void *) lasers);
352                 lasers = (lasersstruct *) NULL;
353         }
354 }
355
356 void
357 refresh_laser(ModeInfo * mi)
358 {
359         MI_CLEARWINDOW(mi);
360 }
361
362 #endif /* MODE_laser */