de2a914471cee22244bfcce6c3e13bd670c45558
[xscreensaver] / hacks / laser.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * laser --- draws swinging laser beams.
3  */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)laser.c       4.00 97/01/01 xlockmore";
6 #endif
7
8 /* Copyright (c) 1995 Pascal Pensa <pensa@aurora.unice.fr>
9  *
10  * Permission to use, copy, modify, distribute, and sell this software and its
11  * documentation for any purpose is hereby granted without fee, provided that
12  * the above copyright notice appear in all copies and that both that
13  * copyright notice and this permission notice appear in supporting
14  * documentation.  No representations are made about the suitability of this
15  * software for any purpose.  It is provided "as is" without express or
16  * implied warranty.
17  *
18  * Revision History:
19  * 10-May-97: jwz@jwz.org: turned into a standalone program.
20  */
21
22 #ifdef STANDALONE
23 # define PROGCLASS                                      "Laser"
24 # define HACK_INIT                                      init_laser
25 # define HACK_DRAW                                      draw_laser
26 # define laser_opts                                     xlockmore_opts
27 # define DEFAULTS       "*count:                10    \n"                       \
28                                         "*cycles:               200   \n"                       \
29                                         "*delay:                40000 \n"                       \
30                                         "*ncolors:              64   \n"
31 # define SMOOTH_COLORS
32 # include "xlockmore.h"                         /* from the xscreensaver distribution */
33 #else  /* !STANDALONE */
34 # include "xlock.h"                                     /* from the xlockmore distribution */
35 #endif /* !STANDALONE */
36
37 ModeSpecOpt laser_opts = {
38   0, NULL, 0, NULL, NULL };
39
40 #define MINREDRAW 3             /* Number of redrawn on each frame */
41 #define MAXREDRAW 8
42
43 #define MINLASER  1             /* Laser number */
44
45 #define MINWIDTH  2             /* Laser ray width range */
46 #define MAXWIDTH 40
47
48 #define MINSPEED  2             /* Speed range */
49 #define MAXSPEED 17
50
51 #define MINDIST  10             /* Minimal distance from edges */
52
53 #define COLORSTEP 2             /* Laser color step */
54
55 #define RANGE_RAND(min,max) ((min) + LRAND() % ((max) - (min)))
56
57 typedef enum {
58         TOP, RIGHT, BOTTOM, LEFT
59 } border;
60
61 typedef struct {
62         int         bx;         /* border x */
63         int         by;         /* border y */
64         border      bn;         /* active border */
65         int         dir;        /* direction */
66         int         speed;      /* laser velocity from MINSPEED to MAXSPEED */
67         int         sx[MAXWIDTH];       /* x stack */
68         int         sy[MAXWIDTH];       /* x stack */
69         XGCValues   gcv;        /* for color */
70 } laserstruct;
71
72 typedef struct {
73         int         width;
74         int         height;
75         int         cx;         /* center x */
76         int         cy;         /* center y */
77         int         lw;         /* laser width */
78         int         ln;         /* laser number */
79         int         lr;         /* laser redraw */
80         int         sw;         /* stack width */
81         int         so;         /* stack offset */
82         int         time;       /* up time */
83         GC          stippledGC;
84         XGCValues   gcv_black;  /* for black color */
85         laserstruct *laser;
86 } lasersstruct;
87
88 static lasersstruct *lasers = NULL;
89
90
91 void
92 init_laser(ModeInfo * mi)
93 {
94         int         i, c = 0;
95         lasersstruct *lp;
96
97         if (lasers == NULL) {
98                 if ((lasers = (lasersstruct *) calloc(MI_NUM_SCREENS(mi),
99                                              sizeof (lasersstruct))) == NULL)
100                         return;
101         }
102         lp = &lasers[MI_SCREEN(mi)];
103
104         lp->width = MI_WIN_WIDTH(mi);
105         lp->height = MI_WIN_HEIGHT(mi);
106         lp->time = 0;
107
108         lp->ln = MI_BATCHCOUNT(mi);
109         if (lp->ln < -MINLASER) {
110                 /* if lp->ln is random ... the size can change */
111                 if (lp->laser != NULL) {
112                         (void) free((void *) lp->laser);
113                         lp->laser = NULL;
114                 }
115                 lp->ln = NRAND(-lp->ln - MINLASER + 1) + MINLASER;
116         } else if (lp->ln < MINLASER)
117                 lp->ln = MINLASER;
118
119         if (!lp->laser) {
120                 lp->laser = (laserstruct *) malloc(lp->ln * sizeof (laserstruct));
121         }
122         if (lp->stippledGC == NULL) {
123                 XGCValues   gcv;
124
125                 gcv.foreground = MI_WIN_WHITE_PIXEL(mi);
126                 gcv.background = MI_WIN_BLACK_PIXEL(mi);
127                 lp->gcv_black.foreground = MI_WIN_BLACK_PIXEL(mi);
128                 lp->stippledGC = XCreateGC(MI_DISPLAY(mi), MI_WINDOW(mi),
129                                            GCForeground | GCBackground, &gcv);
130         }
131         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
132
133         if (MINDIST < lp->width - MINDIST)
134                 lp->cx = RANGE_RAND(MINDIST, lp->width - MINDIST);
135         else
136                 lp->cx = RANGE_RAND(0, lp->width);
137         if (MINDIST < lp->height - MINDIST)
138                 lp->cy = RANGE_RAND(MINDIST, lp->height - MINDIST);
139         else
140                 lp->cy = RANGE_RAND(0, lp->height);
141         lp->lw = RANGE_RAND(MINWIDTH, MAXWIDTH);
142         lp->lr = RANGE_RAND(MINREDRAW, MAXREDRAW);
143         lp->sw = 0;
144         lp->so = 0;
145
146         if (MI_NPIXELS(mi) > 2)
147                 c = NRAND(MI_NPIXELS(mi));
148
149         for (i = 0; i < lp->ln; i++) {
150                 laserstruct *l = &lp->laser[i];
151
152                 l->bn = (border) NRAND(4);
153
154                 switch (l->bn) {
155                         case TOP:
156                                 l->bx = NRAND(lp->width);
157                                 l->by = 0;
158                                 break;
159                         case RIGHT:
160                                 l->bx = lp->width;
161                                 l->by = NRAND(lp->height);
162                                 break;
163                         case BOTTOM:
164                                 l->bx = NRAND(lp->width);
165                                 l->by = lp->height;
166                                 break;
167                         case LEFT:
168                                 l->bx = 0;
169                                 l->by = NRAND(lp->height);
170                 }
171
172                 l->dir = LRAND() & 1;
173                 l->speed = ((RANGE_RAND(MINSPEED, MAXSPEED) * lp->width) / 1000) + 1;
174                 if (MI_NPIXELS(mi) > 2) {
175                         l->gcv.foreground = MI_PIXEL(mi, c);
176                         c = (c + COLORSTEP) % MI_NPIXELS(mi);
177                 } else
178                         l->gcv.foreground = MI_WIN_WHITE_PIXEL(mi);
179         }
180 }
181
182 static void
183 draw_laser_once(ModeInfo * mi)
184 {
185         Display    *display = MI_DISPLAY(mi);
186         lasersstruct *lp = &lasers[MI_SCREEN(mi)];
187         int         i;
188
189         for (i = 0; i < lp->ln; i++) {
190                 laserstruct *l = &lp->laser[i];
191
192                 if (lp->sw >= lp->lw) {
193                         XChangeGC(display, lp->stippledGC, GCForeground, &(lp->gcv_black));
194                         XDrawLine(display, MI_WINDOW(mi), lp->stippledGC,
195                                   lp->cx, lp->cy,
196                                   l->sx[lp->so], l->sy[lp->so]);
197                 }
198                 if (l->dir) {
199                         switch (l->bn) {
200                                 case TOP:
201                                         l->bx -= l->speed;
202                                         if (l->bx < 0) {
203                                                 l->by = -l->bx;
204                                                 l->bx = 0;
205                                                 l->bn = LEFT;
206                                         }
207                                         break;
208                                 case RIGHT:
209                                         l->by -= l->speed;
210                                         if (l->by < 0) {
211                                                 l->bx = lp->width + l->by;
212                                                 l->by = 0;
213                                                 l->bn = TOP;
214                                         }
215                                         break;
216                                 case BOTTOM:
217                                         l->bx += l->speed;
218                                         if (l->bx >= lp->width) {
219                                                 l->by = lp->height - l->bx % lp->width;
220                                                 l->bx = lp->width;
221                                                 l->bn = RIGHT;
222                                         }
223                                         break;
224                                 case LEFT:
225                                         l->by += l->speed;
226                                         if (l->by >= lp->height) {
227                                                 l->bx = l->by % lp->height;
228                                                 l->by = lp->height;
229                                                 l->bn = BOTTOM;
230                                         }
231                         }
232                 } else {
233                         switch (l->bn) {
234                                 case TOP:
235                                         l->bx += l->speed;
236                                         if (l->bx >= lp->width) {
237                                                 l->by = l->bx % lp->width;
238                                                 l->bx = lp->width;
239                                                 l->bn = RIGHT;
240                                         }
241                                         break;
242                                 case RIGHT:
243                                         l->by += l->speed;
244                                         if (l->by >= lp->height) {
245                                                 l->bx = lp->width - l->by % lp->height;
246                                                 l->by = lp->height;
247                                                 l->bn = BOTTOM;
248                                         }
249                                         break;
250                                 case BOTTOM:
251                                         l->bx -= l->speed;
252                                         if (l->bx < 0) {
253                                                 l->by = lp->height + l->bx;
254                                                 l->bx = 0;
255                                                 l->bn = LEFT;
256                                         }
257                                         break;
258                                 case LEFT:
259                                         l->by -= l->speed;
260                                         if (l->by < 0) {
261                                                 l->bx = -l->bx;
262                                                 l->by = 0;
263                                                 l->bn = TOP;
264                                         }
265                         }
266                 }
267
268                 XChangeGC(display, lp->stippledGC, GCForeground, &l->gcv);
269                 XDrawLine(display, MI_WINDOW(mi), lp->stippledGC,
270                           lp->cx, lp->cy, l->bx, l->by);
271
272                 l->sx[lp->so] = l->bx;
273                 l->sy[lp->so] = l->by;
274
275         }
276
277         if (lp->sw < lp->lw)
278                 ++lp->sw;
279
280         lp->so = (lp->so + 1) % lp->lw;
281 }
282
283 void
284 draw_laser(ModeInfo * mi)
285 {
286         lasersstruct *lp = &lasers[MI_SCREEN(mi)];
287         int         i;
288
289         for (i = 0; i < lp->lr; i++)
290                 draw_laser_once(mi);
291
292         if (++lp->time > MI_CYCLES(mi))
293                 init_laser(mi);
294 }
295
296 void
297 release_laser(ModeInfo * mi)
298 {
299         if (lasers != NULL) {
300                 int         screen;
301
302                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
303                         lasersstruct *lp = &lasers[screen];
304
305                         if (lp->laser != NULL)
306                                 (void) free((void *) lp->laser);
307                         if (lp->stippledGC != NULL)
308                                 XFreeGC(MI_DISPLAY(mi), lp->stippledGC);
309                 }
310                 (void) free((void *) lasers);
311                 lasers = NULL;
312         }
313 }
314
315 void
316 refresh_laser(ModeInfo * mi)
317 {
318         /* Do nothing, it will refresh by itself */
319 }