2a8fafacb0ca5e1b6cad8ff4f8a22a0fe38d476a
[xscreensaver] / hacks / lissie.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* lissie --- the Lissajous worm */
3
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)lissie.c      4.04 97/07/28 xlockmore";
6 #endif
7
8 /*-
9  * lissie.c - The Lissajous worm for xlock, the X Window System
10  *               lockscreen.
11  *
12  * Copyright (c) 1996 by Alexander Jolk <ub9x@rz.uni-karlsruhe.de>
13  *
14  * Permission to use, copy, modify, and distribute this software and its
15  * documentation for any purpose and without fee is hereby granted,
16  * provided that the above copyright notice appear in all copies and that
17  * both that copyright notice and this permission notice appear in
18  * supporting documentation.
19  *
20  * This file is provided AS IS with no warranties of any kind.  The author
21  * shall have no liability with respect to the infringement of copyrights,
22  * trade secrets or any patents by this file or any part thereof.  In no
23  * event will the author be liable for any lost revenue or profits or
24  * other special, indirect and consequential damages.
25  *
26  * Revision History:
27  * 10-May-97: Compatible with xscreensaver
28  * 18-Aug-96: added refresh-hook.
29  * 01-May-96: written.
30  */
31
32 #ifdef STANDALONE
33 # define PROGCLASS "Lissie"
34 # define HACK_INIT init_lissie
35 # define HACK_DRAW draw_lissie
36 # define lissie_opts xlockmore_opts
37 # define DEFAULTS       "*delay:   10000 \n"    \
38                                         "*count:   1     \n"    \
39                                         "*cycles:  2000  \n"    \
40                                         "*size:   -200   \n"    \
41                                         "*ncolors: 64    \n"
42 # define SMOOTH_COLORS
43 # include "xlockmore.h"         /* in xscreensaver distribution */
44 #else /* STANDALONE */
45 # include "xlock.h"             /* in xlockmore distribution */
46 #endif /* STANDALONE */
47
48 ModeSpecOpt lissie_opts =
49 {0, NULL, 0, NULL, NULL};
50
51 #define MINSIZE 1
52
53 #define Lissie(n)\
54         if (lissie->loc[(n)].x > 0 && lissie->loc[(n)].y > 0 &&\
55                 lissie->loc[(n)].x <= lp->width && lissie->loc[(n)].y <= lp->height) {\
56                 if (lissie->ri < 2)\
57                         XDrawPoint(display, MI_WINDOW(mi),\
58                                 gc, lissie->loc[(n)].x, lissie->loc[(n)].y);\
59                 else\
60                         XDrawArc(display, MI_WINDOW(mi), gc,\
61                                 lissie->loc[(n)].x - lissie->ri / 2,\
62                                 lissie->loc[(n)].y - lissie->ri / 2,\
63                                 lissie->ri, lissie->ri, 0, 23040);\
64         }
65
66 #define FLOATRAND(min,max)      ((min)+(LRAND()/MAXRAND)*((max)-(min)))
67 #define INTRAND(min,max)     ((min)+NRAND((max)-(min)+1))
68
69 #define MINDT  0.01
70 #define MAXDT  0.15
71
72 #define MAXLISSIELEN  100
73 #define MINLISSIELEN  10
74 #define MINLISSIES 1
75
76 /* How many segments to draw per cycle when redrawing */
77 #define REDRAWSTEP 3
78
79 typedef struct {
80         double      tx, ty, dtx, dty;
81         int         xi, yi, ri, rx, ry, len, pos;
82         int         redrawing, redrawpos;
83         XPoint      loc[MAXLISSIELEN];
84         int         color;
85 } lissiestruct;
86
87 typedef struct {
88         int         width, height;
89         int         nlissies;
90         lissiestruct *lissie;
91         int         loopcount;
92 } lissstruct;
93
94 static lissstruct *lisses = NULL;
95
96
97 static void
98 drawlissie(ModeInfo * mi, lissiestruct * lissie)
99 {
100         Display    *display = MI_DISPLAY(mi);
101         GC          gc = MI_GC(mi);
102         lissstruct *lp = &lisses[MI_SCREEN(mi)];
103         int         p = (++lissie->pos) % MAXLISSIELEN;
104         int         oldp = (lissie->pos - lissie->len + MAXLISSIELEN) % MAXLISSIELEN;
105
106         /* Let time go by ... */
107         lissie->tx += lissie->dtx;
108         lissie->ty += lissie->dty;
109         if (lissie->tx > 2 * M_PI)
110                 lissie->tx -= 2 * M_PI;
111         if (lissie->ty > 2 * M_PI)
112                 lissie->ty -= 2 * M_PI;
113
114         /* vary both (x/y) speeds by max. 1% */
115         lissie->dtx *= FLOATRAND(0.99, 1.01);
116         lissie->dty *= FLOATRAND(0.99, 1.01);
117         if (lissie->dtx < MINDT)
118                 lissie->dtx = MINDT;
119         else if (lissie->dtx > MAXDT)
120                 lissie->dtx = MAXDT;
121         if (lissie->dty < MINDT)
122                 lissie->dty = MINDT;
123         else if (lissie->dty > MAXDT)
124                 lissie->dty = MAXDT;
125
126         lissie->loc[p].x = lissie->xi + (int) (sin(lissie->tx) * lissie->rx);
127         lissie->loc[p].y = lissie->yi + (int) (sin(lissie->ty) * lissie->ry);
128
129         /* Mask */
130         XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
131         Lissie(oldp);
132
133         /* Redraw */
134         if (MI_NPIXELS(mi) > 2) {
135                 XSetForeground(display, gc, MI_PIXEL(mi, lissie->color));
136                 if (++lissie->color >= MI_NPIXELS(mi))
137                         lissie->color = 0;
138         } else
139                 XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
140         Lissie(p);
141         if (lissie->redrawing) {
142                 int         i;
143
144                 lissie->redrawpos++;
145                 /* This compensates for the changed p
146                    since the last callback. */
147
148                 for (i = 0; i < REDRAWSTEP; i++) {
149                         Lissie((p - lissie->redrawpos + MAXLISSIELEN) % MAXLISSIELEN);
150                         if (++(lissie->redrawpos) >= lissie->len) {
151                                 lissie->redrawing = 0;
152                                 break;
153                         }
154                 }
155         }
156 }
157
158 static void
159 initlissie(ModeInfo * mi, lissiestruct * lissie)
160 {
161         lissstruct *lp = &lisses[MI_SCREEN(mi)];
162         int         size = MI_SIZE(mi);
163         int         i;
164
165         if (MI_NPIXELS(mi) > 2)
166                 lissie->color = NRAND(MI_NPIXELS(mi));
167         else
168                 lissie->color = MI_WIN_WHITE_PIXEL(mi);
169         /* Initialize parameters */
170         if (size < -MINSIZE)
171                 lissie->ri = NRAND(MIN(-size, MAX(MINSIZE,
172                    MIN(lp->width, lp->height) / 4)) - MINSIZE + 1) + MINSIZE;
173         else if (size < MINSIZE) {
174                 if (!size)
175                         lissie->ri = MAX(MINSIZE, MIN(lp->width, lp->height) / 4);
176                 else
177                         lissie->ri = MINSIZE;
178         } else
179                 lissie->ri = MIN(size, MAX(MINSIZE, MIN(lp->width, lp->height) / 4));
180         lissie->xi = INTRAND(lp->width / 4 + lissie->ri,
181                              lp->width * 3 / 4 - lissie->ri);
182         lissie->yi = INTRAND(lp->height / 4 + lissie->ri,
183                              lp->height * 3 / 4 - lissie->ri);
184         lissie->rx = INTRAND(lp->width / 4,
185                    MIN(lp->width - lissie->xi, lissie->xi)) - 2 * lissie->ri;
186         lissie->ry = INTRAND(lp->height / 4,
187                   MIN(lp->height - lissie->yi, lissie->yi)) - 2 * lissie->ri;
188         lissie->len = INTRAND(MINLISSIELEN, MAXLISSIELEN - 1);
189         lissie->pos = 0;
190
191         lissie->redrawing = 0;
192
193         lissie->tx = FLOATRAND(0, 2 * M_PI);
194         lissie->ty = FLOATRAND(0, 2 * M_PI);
195         lissie->dtx = FLOATRAND(MINDT, MAXDT);
196         lissie->dty = FLOATRAND(MINDT, MAXDT);
197
198         for (i = 0; i < MAXLISSIELEN; i++)
199                 lissie->loc[i].x = lissie->loc[i].y = 0;
200         /* Draw lissie */
201         drawlissie(mi, lissie);
202 }
203
204 void
205 init_lissie(ModeInfo * mi)
206 {
207         lissstruct *lp;
208         unsigned char ball;
209
210         if (lisses == NULL) {
211                 if ((lisses = (lissstruct *) calloc(MI_NUM_SCREENS(mi),
212                                                sizeof (lissstruct))) == NULL)
213                         return;
214         }
215         lp = &lisses[MI_SCREEN(mi)];
216
217         lp->width = MI_WIN_WIDTH(mi);
218         lp->height = MI_WIN_HEIGHT(mi);
219
220         lp->nlissies = MI_BATCHCOUNT(mi);
221         if (lp->nlissies < -MINLISSIES) {
222                 if (lp->lissie) {
223                         (void) free((void *) lp->lissie);
224                         lp->lissie = NULL;
225                 }
226                 lp->nlissies = NRAND(-lp->nlissies - MINLISSIES + 1) + MINLISSIES;
227         } else if (lp->nlissies < MINLISSIES)
228                 lp->nlissies = MINLISSIES;
229
230         lp->loopcount = 0;
231
232         if (!lp->lissie)
233                 lp->lissie = (lissiestruct *) calloc(lp->nlissies, sizeof (lissiestruct));
234         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
235         for (ball = 0; ball < (unsigned char) lp->nlissies; ball++)
236                 initlissie(mi, &lp->lissie[ball]);
237
238 }
239
240 void
241 draw_lissie(ModeInfo * mi)
242 {
243         lissstruct *lp = &lisses[MI_SCREEN(mi)];
244         register unsigned char ball;
245
246         if (++lp->loopcount > MI_CYCLES(mi))
247                 init_lissie(mi);
248         else
249                 for (ball = 0; ball < (unsigned char) lp->nlissies; ball++)
250                         drawlissie(mi, &lp->lissie[ball]);
251 }
252
253 void
254 release_lissie(ModeInfo * mi)
255 {
256         if (lisses != NULL) {
257                 int         screen;
258
259                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
260                         lissstruct *lp = &lisses[screen];
261
262                         if (lp->lissie) {
263                                 (void) free((void *) lp->lissie);
264                                 lp->lissie = NULL;
265                         }
266                 }
267                 (void) free((void *) lisses);
268                 lisses = NULL;
269         }
270 }
271
272 void
273 refresh_lissie(ModeInfo * mi)
274 {
275         if (lisses != NULL) {
276                 lissstruct *lp = &lisses[MI_SCREEN(mi)];
277                 int         i;
278
279                 for (i = 0; i < lp->nlissies; i++) {
280                         lp->lissie[i].redrawing = 1;
281                         lp->lissie[i].redrawpos = 0;
282                 }
283         }
284 }