From http://www.jwz.org/xscreensaver/xscreensaver-5.37.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         MI_INIT (mi, lasers, 0);
130         lp = &lasers[MI_SCREEN(mi)];
131
132         lp->width = MI_WIDTH(mi);
133         lp->height = MI_HEIGHT(mi);
134         lp->time = 0;
135
136         lp->ln = MI_COUNT(mi);
137         if (lp->ln < -MINLASER) {
138                 /* if lp->ln is random ... the size can change */
139                 if (lp->laser != NULL) {
140                         (void) free((void *) lp->laser);
141                         lp->laser = (laserstruct *) NULL;
142                 }
143                 lp->ln = NRAND(-lp->ln - MINLASER + 1) + MINLASER;
144         } else if (lp->ln < MINLASER)
145                 lp->ln = MINLASER;
146
147         if (lp->laser == NULL) {
148                 if ((lp->laser = (laserstruct *) malloc(lp->ln *
149                                 sizeof (laserstruct))) == NULL) {
150                         free_laser(display, lp);
151                         return;
152                 }
153         }
154         if (lp->stippledGC == None) {
155                 XGCValues   gcv;
156
157                 gcv.foreground = MI_WHITE_PIXEL(mi);
158                 gcv.background = MI_BLACK_PIXEL(mi);
159                 lp->gcv_black.foreground = MI_BLACK_PIXEL(mi);
160                 if ((lp->stippledGC = XCreateGC(display, MI_WINDOW(mi),
161                                 GCForeground | GCBackground, &gcv)) == None) {
162                         free_laser(display, lp);
163                         return;
164                 }
165 # ifdef HAVE_JWXYZ
166     jwxyz_XSetAntiAliasing (MI_DISPLAY(mi), lp->stippledGC, False);
167 # endif
168         }
169         MI_CLEARWINDOW(mi);
170
171         if (MINDIST < lp->width - MINDIST)
172                 lp->cx = RANGE_RAND(MINDIST, lp->width - MINDIST);
173         else
174                 lp->cx = RANGE_RAND(0, lp->width);
175         if (MINDIST < lp->height - MINDIST)
176                 lp->cy = RANGE_RAND(MINDIST, lp->height - MINDIST);
177         else
178                 lp->cy = RANGE_RAND(0, lp->height);
179         lp->lw = RANGE_RAND(MINWIDTH, MAXWIDTH);
180         lp->lr = RANGE_RAND(MINREDRAW, MAXREDRAW);
181         lp->sw = 0;
182         lp->so = 0;
183
184         if (MI_NPIXELS(mi) > 2)
185                 c = NRAND(MI_NPIXELS(mi));
186
187         for (i = 0; i < lp->ln; i++) {
188                 laserstruct *l = &lp->laser[i];
189
190                 l->bn = (border) NRAND(4);
191
192                 switch (l->bn) {
193                         case TOP:
194                                 l->bx = NRAND(lp->width);
195                                 l->by = 0;
196                                 break;
197                         case RIGHT:
198                                 l->bx = lp->width;
199                                 l->by = NRAND(lp->height);
200                                 break;
201                         case BOTTOM:
202                                 l->bx = NRAND(lp->width);
203                                 l->by = lp->height;
204                                 break;
205                         case LEFT:
206                                 l->bx = 0;
207                                 l->by = NRAND(lp->height);
208                 }
209
210                 l->dir = (int) (LRAND() & 1);
211                 l->speed = ((RANGE_RAND(MINSPEED, MAXSPEED) * lp->width) / 1000) + 1;
212                 if (MI_NPIXELS(mi) > 2) {
213                         l->gcv.foreground = MI_PIXEL(mi, c);
214                         c = (c + COLORSTEP) % MI_NPIXELS(mi);
215                 } else
216                         l->gcv.foreground = MI_WHITE_PIXEL(mi);
217         }
218 }
219
220 static void
221 draw_laser_once(ModeInfo * mi)
222 {
223         Display    *display = MI_DISPLAY(mi);
224         lasersstruct *lp = &lasers[MI_SCREEN(mi)];
225         int         i;
226
227         for (i = 0; i < lp->ln; i++) {
228                 laserstruct *l = &lp->laser[i];
229
230                 if (lp->sw >= lp->lw) {
231                         XChangeGC(display, lp->stippledGC, GCForeground, &(lp->gcv_black));
232                         XDrawLine(display, MI_WINDOW(mi), lp->stippledGC,
233                                   lp->cx, lp->cy,
234                                   l->sx[lp->so], l->sy[lp->so]);
235                 }
236                 if (l->dir) {
237                         switch (l->bn) {
238                                 case TOP:
239                                         l->bx -= l->speed;
240                                         if (l->bx < 0) {
241                                                 l->by = -l->bx;
242                                                 l->bx = 0;
243                                                 l->bn = LEFT;
244                                         }
245                                         break;
246                                 case RIGHT:
247                                         l->by -= l->speed;
248                                         if (l->by < 0) {
249                                                 l->bx = lp->width + l->by;
250                                                 l->by = 0;
251                                                 l->bn = TOP;
252                                         }
253                                         break;
254                                 case BOTTOM:
255                                         l->bx += l->speed;
256                                         if (l->bx >= lp->width) {
257                                                 l->by = lp->height - l->bx % lp->width;
258                                                 l->bx = lp->width;
259                                                 l->bn = RIGHT;
260                                         }
261                                         break;
262                                 case LEFT:
263                                         l->by += l->speed;
264                                         if (l->by >= lp->height) {
265                                                 l->bx = l->by % lp->height;
266                                                 l->by = lp->height;
267                                                 l->bn = BOTTOM;
268                                         }
269                         }
270                 } else {
271                         switch (l->bn) {
272                                 case TOP:
273                                         l->bx += l->speed;
274                                         if (l->bx >= lp->width) {
275                                                 l->by = l->bx % lp->width;
276                                                 l->bx = lp->width;
277                                                 l->bn = RIGHT;
278                                         }
279                                         break;
280                                 case RIGHT:
281                                         l->by += l->speed;
282                                         if (l->by >= lp->height) {
283                                                 l->bx = lp->width - l->by % lp->height;
284                                                 l->by = lp->height;
285                                                 l->bn = BOTTOM;
286                                         }
287                                         break;
288                                 case BOTTOM:
289                                         l->bx -= l->speed;
290                                         if (l->bx < 0) {
291                                                 l->by = lp->height + l->bx;
292                                                 l->bx = 0;
293                                                 l->bn = LEFT;
294                                         }
295                                         break;
296                                 case LEFT:
297                                         l->by -= l->speed;
298                                         if (l->by < 0) {
299                                                 l->bx = -l->bx;
300                                                 l->by = 0;
301                                                 l->bn = TOP;
302                                         }
303                         }
304                 }
305
306                 XChangeGC(display, lp->stippledGC, GCForeground, &l->gcv);
307                 XDrawLine(display, MI_WINDOW(mi), lp->stippledGC,
308                           lp->cx, lp->cy, l->bx, l->by);
309
310                 l->sx[lp->so] = l->bx;
311                 l->sy[lp->so] = l->by;
312
313         }
314
315         if (lp->sw < lp->lw)
316                 ++lp->sw;
317
318         lp->so = (lp->so + 1) % lp->lw;
319 }
320
321 ENTRYPOINT void
322 draw_laser(ModeInfo * mi)
323 {
324         int         i;
325         lasersstruct *lp;
326
327         if (lasers == NULL)
328                 return;
329         lp = &lasers[MI_SCREEN(mi)];
330         if (lp->laser == NULL)
331                 return;
332
333         MI_IS_DRAWN(mi) = True;
334         for (i = 0; i < lp->lr; i++)
335                 draw_laser_once(mi);
336
337         if (++lp->time > MI_CYCLES(mi))
338                 init_laser(mi);
339 }
340
341 ENTRYPOINT void
342 release_laser(ModeInfo * mi)
343 {
344         if (lasers != NULL) {
345                 int         screen;
346
347                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
348                         free_laser(MI_DISPLAY(mi), &lasers[screen]);
349                 (void) free((void *) lasers);
350                 lasers = (lasersstruct *) NULL;
351         }
352 }
353
354 ENTRYPOINT void
355 refresh_laser(ModeInfo * mi)
356 {
357         MI_CLEARWINDOW(mi);
358 }
359
360 XSCREENSAVER_MODULE ("Laser", laser)
361
362 #endif /* MODE_laser */