From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / hacks / rotor.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* rotor --- a swirly rotor */
3
4 #if 0
5 static const char sccsid[] = "@(#)rotor.c       5.00 2000/11/01 xlockmore";
6 #endif
7
8 /*-
9  * Copyright (c) 1991 by Patrick J. Naughton.
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  * 08-Mar-1995: CAT stuff for ## was tripping up some C compilers.  Removed.
27  * 01-Dec-1993: added patch for AIXV3 from Tom McConnell
28  *              <tmcconne@sedona.intel.com>
29  * 11-Nov-1990: put into xlock by Steve Zellers <zellers@sun.com>
30  * 16-Oct-1990: Received from Tom Lawrence (tcl@cs.brown.edu: 'flight'
31  *               simulator)
32  */
33
34 #ifdef STANDALONE
35 #define MODE_rotor
36 #define DEFAULTS        "*delay: 10000 \n" \
37                                         "*count: 4 \n" \
38                                         "*cycles: 20 \n" \
39                                         "*size: -6 \n" \
40                                         "*ncolors: 200 \n" \
41                                         "*fpsSolid: true \n" \
42                                     "*lowrez: True \n" \
43
44 # define SMOOTH_COLORS
45 # define release_rotor 0
46 # define reshape_rotor 0
47 # define rotor_handle_event 0
48 # include "xlockmore.h"         /* in xscreensaver distribution */
49 #else /* STANDALONE */
50 # include "xlock.h"             /* in xlockmore distribution */
51 #endif /* STANDALONE */
52
53 #ifdef MODE_rotor
54
55 ENTRYPOINT ModeSpecOpt rotor_opts =
56 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
57
58 #ifdef USE_MODULES
59 ModStruct   rotor_description =
60 {"rotor", "init_rotor", "draw_rotor", (char *) NULL,
61  "refresh_rotor", "init_rotor", "free_rotor", &rotor_opts,
62  100, 4, 100, -6, 64, 0.3, "",
63  "Shows Tom's Roto-Rooter", 0, NULL};
64
65 #endif
66
67 /*-
68  * A 'batchcount' of 3 or 4 works best!
69  */
70
71 #define MAXANGLE        3000.0  /* irrectangular  (was 10000.0) */
72
73 /* How many segments to draw per cycle when redrawing */
74 #define REDRAWSTEP 3
75
76 typedef struct {
77         float       angle;
78         float       radius;
79         float       start_radius;
80         float       end_radius;
81         float       radius_drift_max;
82         float       radius_drift_now;
83
84         float       ratio;
85         float       start_ratio;
86         float       end_ratio;
87         float       ratio_drift_max;
88         float       ratio_drift_now;
89 } elem;
90
91 typedef struct {
92         int         pix;
93         int         lastx, lasty;
94         int         num, rotor, prev;
95         int         nsave;
96         float       angle;
97         int         centerx, centery;
98         int         prevcenterx, prevcentery;
99         unsigned char firsttime;
100         unsigned char iconifiedscreen;  /* for iconified view */
101         unsigned char forward;
102         unsigned char unused;
103         elem       *elements;
104         XPoint     *save;
105         int         redrawing, redrawpos;
106         int         linewidth;
107 } rotorstruct;
108
109 static rotorstruct *rotors = (rotorstruct *) NULL;
110
111 ENTRYPOINT void
112 free_rotor(ModeInfo * mi)
113 {
114         rotorstruct *rp = &rotors[MI_SCREEN(mi)];
115         if (rp->elements != NULL) {
116                 (void) free((void *) rp->elements);
117                 rp->elements = (elem *) NULL;
118         }
119         if (rp->save != NULL) {
120                 (void) free((void *) rp->save);
121                 rp->save = (XPoint *) NULL;
122         }
123 }
124
125 ENTRYPOINT void
126 init_rotor (ModeInfo * mi)
127 {
128         int         x;
129         elem       *pelem;
130         unsigned char wasiconified;
131         rotorstruct *rp;
132
133         MI_INIT (mi, rotors);
134         rp = &rotors[MI_SCREEN(mi)];
135
136 #ifdef HAVE_JWXYZ
137     jwxyz_XSetAntiAliasing (MI_DISPLAY(mi), MI_GC(mi),  False);
138 #endif
139
140         rp->prevcenterx = rp->centerx;
141         rp->prevcentery = rp->centery;
142
143         rp->centerx = MI_WIDTH(mi) / 2;
144         rp->centery = MI_HEIGHT(mi) / 2;
145
146         rp->redrawing = 0;
147         /*
148          * sometimes, you go into iconified view, only to see a really whizzy pattern
149          * that you would like to look more closely at. Normally, clicking in the
150          * icon reinitializes everything - but I don't, cuz I'm that kind of guy.
151          * HENCE, the wasiconified stuff you see here.
152          */
153
154         wasiconified = rp->iconifiedscreen;
155         rp->iconifiedscreen = MI_IS_ICONIC(mi);
156
157         if (wasiconified && !rp->iconifiedscreen)
158                 rp->firsttime = True;
159         else {
160
161                 /* This is a fudge is needed since prevcenter may not be set when it comes
162                    from the the random mode and return is pressed (and its not the first
163                    mode that was running). This assumes that the size of the lock screen
164                    window / size of the icon window = 12 */
165                 if (!rp->prevcenterx)
166                         rp->prevcenterx = rp->centerx * 12;
167                 if (!rp->prevcentery)
168                         rp->prevcentery = rp->centery * 12;
169
170                 rp->num = MI_COUNT(mi);
171                 if (rp->num < 0) {
172                         rp->num = NRAND(-rp->num) + 1;
173                         if (rp->elements != NULL) {
174                                 (void) free((void *) rp->elements);
175                                 rp->elements = (elem *) NULL;
176                         }
177                 }
178                 if (rp->elements == NULL)
179                         if ((rp->elements = (elem *) calloc(rp->num,
180                                         sizeof (elem))) == NULL) {
181                                 free_rotor(mi);
182                                 return;
183                         }
184                 rp->nsave = MI_CYCLES(mi);
185                 if (rp->nsave <= 1)
186                         rp->nsave = 2;
187                 if (rp->save == NULL)
188                         if ((rp->save = (XPoint *) malloc(rp->nsave *
189                                         sizeof (XPoint))) == NULL) {
190                                 free_rotor(mi);
191                                 return;
192                         }
193                 for (x = 0; x < rp->nsave; x++) {
194                         rp->save[x].x = rp->centerx;
195                         rp->save[x].y = rp->centery;
196                 }
197
198                 pelem = rp->elements;
199
200                 for (x = rp->num; --x >= 0; pelem++) {
201                         pelem->radius_drift_max = 1.0;
202                         pelem->radius_drift_now = 1.0;
203
204                         pelem->end_radius = 100.0;
205
206                         pelem->ratio_drift_max = 1.0;
207                         pelem->ratio_drift_now = 1.0;
208                         pelem->end_ratio = 10.0;
209                 }
210                 if (MI_NPIXELS(mi) > 2)
211                         rp->pix = NRAND(MI_NPIXELS(mi));
212
213                 rp->rotor = 0;
214                 rp->prev = 1;
215                 rp->lastx = rp->centerx;
216                 rp->lasty = rp->centery;
217                 rp->angle = (float) NRAND((long) MAXANGLE) / 3.0;
218                 rp->forward = rp->firsttime = True;
219         }
220         rp->linewidth = MI_SIZE(mi);
221
222         if (rp->linewidth == 0)
223                 rp->linewidth = 1;
224         if (rp->linewidth < 0)
225                 rp->linewidth = NRAND(-rp->linewidth) + 1;
226
227         MI_CLEARWINDOW(mi);
228 }
229
230 ENTRYPOINT void
231 draw_rotor (ModeInfo * mi)
232 {
233         Display    *display = MI_DISPLAY(mi);
234         GC          gc = MI_GC(mi);
235         register elem *pelem;
236         int         thisx, thisy;
237         int         i;
238         int         x_1, y_1, x_2, y_2;
239         rotorstruct *rp;
240
241         if (rotors == NULL)
242                 return;
243         rp = &rotors[MI_SCREEN(mi)];
244         if (rp->elements == NULL)
245                 return;
246
247         MI_IS_DRAWN(mi) = True;
248         if (!rp->iconifiedscreen) {
249                 thisx = rp->centerx;
250                 thisy = rp->centery;
251         } else {
252                 thisx = rp->prevcenterx;
253                 thisy = rp->prevcentery;
254         }
255         XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), rp->linewidth,
256                            LineSolid, CapButt, JoinMiter);
257         for (i = rp->num, pelem = rp->elements; --i >= 0; pelem++) {
258                 if (pelem->radius_drift_max <= pelem->radius_drift_now) {
259                         pelem->start_radius = pelem->end_radius;
260                         pelem->end_radius = (float) NRAND(40000) / 100.0 - 200.0;
261                         pelem->radius_drift_max = (float) NRAND(100000) + 10000.0;
262                         pelem->radius_drift_now = 0.0;
263                 }
264                 if (pelem->ratio_drift_max <= pelem->ratio_drift_now) {
265                         pelem->start_ratio = pelem->end_ratio;
266                         pelem->end_ratio = (float) NRAND(2000) / 100.0 - 10.0;
267                         pelem->ratio_drift_max = (float) NRAND(100000) + 10000.0;
268                         pelem->ratio_drift_now = 0.0;
269                 }
270                 pelem->ratio = pelem->start_ratio +
271                         (pelem->end_ratio - pelem->start_ratio) /
272                         pelem->ratio_drift_max * pelem->ratio_drift_now;
273                 pelem->angle = rp->angle * pelem->ratio;
274                 pelem->radius = pelem->start_radius +
275                         (pelem->end_radius - pelem->start_radius) /
276                         pelem->radius_drift_max * pelem->radius_drift_now;
277
278                 thisx += (int) (COSF(pelem->angle) * pelem->radius);
279                 thisy += (int) (SINF(pelem->angle) * pelem->radius);
280
281                 pelem->ratio_drift_now += 1.0;
282                 pelem->radius_drift_now += 1.0;
283         }
284         if (rp->firsttime)
285                 rp->firsttime = False;
286         else {
287                 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
288
289                 x_1 = (int) rp->save[rp->rotor].x;
290                 y_1 = (int) rp->save[rp->rotor].y;
291                 x_2 = (int) rp->save[rp->prev].x;
292                 y_2 = (int) rp->save[rp->prev].y;
293
294                 if (rp->iconifiedscreen) {
295                         x_1 = x_1 * rp->centerx / rp->prevcenterx;
296                         x_2 = x_2 * rp->centerx / rp->prevcenterx;
297                         y_1 = y_1 * rp->centery / rp->prevcentery;
298                         y_2 = y_2 * rp->centery / rp->prevcentery;
299                 }
300                 XDrawLine(display, MI_WINDOW(mi), gc, x_1, y_1, x_2, y_2);
301
302                 if (MI_NPIXELS(mi) > 2) {
303                         XSetForeground(display, gc, MI_PIXEL(mi, rp->pix));
304                         if (++rp->pix >= MI_NPIXELS(mi))
305                                 rp->pix = 0;
306                 } else
307                         XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
308
309                 x_1 = rp->lastx;
310                 y_1 = rp->lasty;
311                 x_2 = thisx;
312                 y_2 = thisy;
313
314                 if (rp->iconifiedscreen) {
315                         x_1 = x_1 * rp->centerx / rp->prevcenterx;
316                         x_2 = x_2 * rp->centerx / rp->prevcenterx;
317                         y_1 = y_1 * rp->centery / rp->prevcentery;
318                         y_2 = y_2 * rp->centery / rp->prevcentery;
319                 }
320                 XDrawLine(display, MI_WINDOW(mi), gc, x_1, y_1, x_2, y_2);
321         }
322         rp->save[rp->rotor].x = rp->lastx = thisx;
323         rp->save[rp->rotor].y = rp->lasty = thisy;
324
325         ++rp->rotor;
326         rp->rotor %= rp->nsave;
327         ++rp->prev;
328         rp->prev %= rp->nsave;
329         if (rp->forward) {
330                 rp->angle += 0.01;
331                 if (rp->angle >= MAXANGLE) {
332                         rp->angle = MAXANGLE;
333                         rp->forward = False;
334                 }
335         } else {
336                 rp->angle -= 0.1;
337                 if (rp->angle <= 0) {
338                         rp->angle = 0.0;
339                         rp->forward = True;
340                 }
341         }
342         if (rp->redrawing) {
343                 int         j;
344
345                 for (i = 0; i < REDRAWSTEP; i++) {
346                         j = (rp->rotor - rp->redrawpos + rp->nsave) % rp->nsave;
347
348                         x_1 = (int) rp->save[j].x;
349                         y_1 = (int) rp->save[j].y;
350                         x_2 = (int) rp->save[(j - 1 + rp->nsave) % rp->nsave].x;
351                         y_2 = (int) rp->save[(j - 1 + rp->nsave) % rp->nsave].y;
352
353                         if (rp->iconifiedscreen) {
354                                 x_1 = x_1 * rp->centerx / rp->prevcenterx;
355                                 x_2 = x_2 * rp->centerx / rp->prevcenterx;
356                                 y_1 = y_1 * rp->centery / rp->prevcentery;
357                                 y_2 = y_2 * rp->centery / rp->prevcentery;
358                         }
359                         XDrawLine(display, MI_WINDOW(mi), gc, x_1, y_1, x_2, y_2);
360
361                         if (++(rp->redrawpos) >= rp->nsave) {
362                                 rp->redrawing = 0;
363                                 break;
364                         }
365                 }
366         }
367         XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
368                            LineSolid, CapButt, JoinMiter);
369 }
370
371 #ifndef STANDALONE
372 ENTRYPOINT void
373 refresh_rotor (ModeInfo * mi)
374 {
375         rotorstruct *rp;
376
377         if (rotors == NULL)
378                 return;
379         rp = &rotors[MI_SCREEN(mi)];
380
381         MI_CLEARWINDOW(mi);
382         rp->redrawing = 1;
383         rp->redrawpos = 1;
384 }
385 #endif
386
387 XSCREENSAVER_MODULE ("Rotor", rotor)
388
389 #endif /* MODE_rotor */