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