From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / bouboule.c
1 /* -*- Mode: C; tab-width: 4 -*-
2    Ported from xlockmore 4.03a12 to be a standalone program and thus usable
3    with xscreensaver by Jamie Zawinski <jwz@jwz.org> on 15-May-97.
4
5    Original copyright notice from xlock.c:
6
7     * Copyright (c) 1988-91 by Patrick J. Naughton.
8     *
9     * Permission to use, copy, modify, and distribute this software and its
10     * documentation for any purpose and without fee is hereby granted,
11     * provided that the above copyright notice appear in all copies and that
12     * both that copyright notice and this permission notice appear in
13     * supporting documentation.
14     *
15     * This file is provided AS IS with no warranties of any kind.  The author
16     * shall have no liability with respect to the infringement of copyrights,
17     * trade secrets or any patents by this file or any part thereof.  In no
18     * event will the author be liable for any lost revenue or profits or
19     * other special, indirect and consequential damages.
20  */
21
22 #if 0
23 static const char sccsid[] = "@(#)bouboule.c    4.00 97/01/01 xlockmore";
24 #endif
25
26 /*-
27  * bouboule.c (bouboule mode for xlockmore)
28  *
29  * Sort of starfield for xlockmore. I found that making a starfield for
30  * a 3D engine and thought it could be a nice lock mode. For a real starfield,
31  * I only scale the sort of sphere you see to the whole sky and clip the stars
32  * to the camera screen.
33  *
34  *   Code Copyright 1996 by Jeremie PETIT (jeremie_petit@geocities.com)
35  *
36  *   Use: batchcount is the number of stars.
37  *        cycles is the maximum size for a star
38  *
39  * 15-May-97: jwz@jwz.org: turned into a standalone program.
40  * 04-Sep-96: Added 3d support (Henrik Theiling, theiling@coli-uni-sb.de)
41  * 20-Feb-96: Added tests so that already malloced objects are not
42  *            malloced twice, thanks to the report from <mccomb@interport.net>
43  * 01-Feb-96: Patched by Jouk Jansen <joukj@alpha.chem.uva.nl> for VMS
44  *            Patched by <bagleyd@bigfoot.com> for TrueColor displays
45  * 30-Jan-96: Wrote all that I wanted to.
46  *
47  * DONE: Build up a XArc list and Draw everything once with XFillArcs
48  *       That idea came from looking at swarm code.
49  * DONE: Add an old arcs list for erasing.
50  * DONE: Make center of starfield SinVariable.
51  * DONE: Add some random in the sinvary() function.
52  * DONE: check time for erasing the stars with the two methods and use the
53  *       better one. Note that sometimes the time difference between
54  *       beginning of erasing and its end is negative! I check this, and
55  *       do not use this result when it occurs. If all values are negative,
56  *       the erasing will continue being done in the currently tested mode.
57  * DONE: Allow stars size customization.
58  * DONE: Make sizey be no less than half sizex or no bigger than twice sizex.
59  *
60  * IDEA: A simple check can be performed to know which stars are "behind"
61  *       and which are "in front". So is possible to very simply change
62  *       the drawing mode for these two sorts of stars. BUT: this would lead
63  *       to a rewrite of the XArc list code because drawing should be done
64  *       in two steps: "behind" stars then "in front" stars. Also, what could
65  *       be the difference between the rendering of these two types of stars?
66  * IDEA: Calculate the distance of each star to the "viewer" and render the
67  *       star accordingly to this distance. Same remarks as for previous
68  *       ideas can be pointed out. This would even lead to reget the old stars
69  *       drawing code, that has been replaced by the XFillArcs. On another
70  *       hand, this would allow particular stars (own color, shape...), as
71  *       far as they would be individually drawn. One should be careful to
72  *       draw them according to their distance, that is not drawing a far
73  *       star after a close one.
74  */
75
76 #ifdef STANDALONE
77 # define DEFAULTS       "*count:                100     \n"                     \
78                                         "*size:                 15      \n"                     \
79                                         "*delay:                20000   \n"                     \
80                                         "*ncolors:              64      \n"                     \
81                                         "*use3d:                True    \n"                     \
82                                         "*delta3d:              1.5             \n"                     \
83                                         "*right3d:              red             \n"                     \
84                                         "*left3d:               blue    \n"                     \
85                                         "*both3d:               magenta \n"                     \
86                                         "*none3d:               black   \n"                     \
87                                         "*fpsSolid:             true    \n"                     \
88                                         "*ignoreRotation: True  \n"
89
90 # define SMOOTH_COLORS
91 # define release_bouboule 0
92 # define bouboule_handle_event 0
93 # include "xlockmore.h"                         /* from the xscreensaver distribution */
94 #else  /* !STANDALONE */
95 # include "xlock.h"                                     /* from the xlockmore distribution */
96 # define ENTRYPOINT /**/
97 #endif /* !STANDALONE */
98
99 ENTRYPOINT ModeSpecOpt bouboule_opts = {
100   0, NULL, 0, NULL, NULL };
101
102 #define USEOLDXARCS  1          /* If 1, we use old xarcs list for erasing.
103                                    * else we just roughly erase the window.
104                                    * This mainly depends on the number of stars,
105                                    * because when they are many, it is faster to
106                                    * erase the whole window than to erase each star
107                                  */
108
109 #if HAVE_GETTIMEOFDAY
110 #define ADAPT_ERASE  1          /* If 1, then we try ADAPT_CHECKS black XFillArcs,
111                                    * and after, ADAPT_CHECKS XFillRectangle.
112                                    * We check which method seems better, knowing that
113                                    * XFillArcs is generally visually better. So we
114                                    * consider that XFillArcs is still better if its time
115                                    * is about XFillRectangle * ADAPT_ARC_PREFERED
116                                    * We need gettimeofday
117                                    * for this... Does it exist on other systems ? Do we
118                                    * have to use another function for others ?
119                                    * This value overrides USEOLDXARCS.
120                                  */
121
122 #ifdef USE_XVMSUTILS
123 # if 0
124 #  include "../xvmsutils/unix_time.h"
125 # else
126 #  include <X11/unix_time.h>
127 # endif
128 #endif
129
130 #include <sys/time.h>
131
132 #define ADAPT_CHECKS 50
133 #define ADAPT_ARC_PREFERED 150  /* Maybe the value that is the most important
134                                    * for adapting to a system */
135 #endif
136
137 #define dtor(x)    (((x) * M_PI) / 180.0)       /* Degrees to radians */
138
139 #define MINSTARS      1
140 #define MINSIZE       1
141 /* jwz: I think slower color changes look better */
142 #define COLOR_CHANGES 50                /* How often we change colors (1 = always)
143                                    * This value should be tuned accordingly to
144                                    * the number of stars */
145 #define MAX_SIZEX_SIZEY 2.      /* This controls whether the sphere can be very
146                                    * very large and have a small height (or the
147                                    * opposite) or no. */
148
149 #define THETACANRAND  80        /* percentage of changes for the speed of
150                                    * change of the 3 theta values */
151 #define SIZECANRAND   80        /* percentage of changes for the speed of
152                                    * change of the sizex and sizey values */
153 #define POSCANRAND    80        /* percentage of changes for the speed of
154                                    * change of the x and y values */
155 /* Note that these XXXCANRAND values can be 0, that is no rand acceleration *
156    variation. */
157
158 #define VARRANDALPHA (NRAND((int) (M_PI * 1000.0))/1000.0)
159 #define VARRANDSTEP  (M_PI/(NRAND(100)+100.0))
160 #define VARRANDMIN   (-70.0)
161 #define VARRANDMAX   70.0
162
163 #define MINZVAL   100           /* stars can come this close */
164 #define SCREENZ  2000           /* this is where the screen is */
165 #define MAXZVAL 10000           /* stars can go this far away */
166
167 #define GETZDIFF(z) ((MI_DELTA3D(mi))*20.0*(1.0-(SCREENZ)/(z+1000)))
168 #define MAXDIFF  MAX(-GETZDIFF(MINZVAL),GETZDIFF(MAXZVAL))
169
170 /* These values are the variation parameters of the acceleration variation *
171    of the SinVariables that are randomized. */
172
173 /******************************/
174 typedef struct SinVariableStruct
175 /******************************/
176 {
177         double      alpha;      /* 
178                                  * Alpha is the current state of the sinvariable
179                                  * alpha should be initialized to a value between
180                                  * 0.0 and 2 * M_PI
181                                  */
182         double      step;       /*
183                                  * Speed of evolution of alpha. It should be a reasonable
184                                  * fraction of 2 * M_PI. This value directly influence
185                                  * the variable speed of variation.
186                                  */
187         double      minimum;    /* Minimum value for the variable */
188         double      maximum;    /* Maximum value for the variable */
189         double      value;      /* Current value */
190         int         mayrand;    /* Flag for knowing whether some randomization can be
191                                  * applied to the variable */
192         struct SinVariableStruct *varrand;      /* Evolving Variable: the variation of
193                                                    * alpha */
194 } SinVariable;
195
196 /***********************/
197 typedef struct StarStruct
198 /***********************/
199 {
200         double      x, y, z;    /* Position of the star */
201         short       size;       /* Try to guess */
202 } Star;
203
204 /****************************/
205 typedef struct StarFieldStruct
206 /****************************/
207 {
208         short       width, height;      /* width and height of the starfield window */
209         short       max_star_size;      /* Maximum radius for stars. stars radius will
210                                          * vary from 1 to MAX_STAR_SIZE */
211         SinVariable x;          /* Evolving variables:               */
212         SinVariable y;          /* Center of the field on the screen */
213         SinVariable z;
214         SinVariable sizex;      /* Evolving variable: half width  of the field */
215         SinVariable sizey;      /* Evolving variable: half height of the field */
216         SinVariable thetax;     /* Evolving Variables:              */
217         SinVariable thetay;     /* rotation angles of the starfield */
218         SinVariable thetaz;     /* around x, y and z local axis     */
219         Star       *star;       /* List of stars */
220         XArc       *xarc;       /* Current List of arcs */
221         XArc       *xarcleft;   /* additional list for the left arcs */
222 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
223         XArc       *oldxarc;    /* Old list of arcs */
224         XArc       *oldxarcleft;
225 #endif
226         unsigned long color;    /* Current color of the starfield */
227         int         colorp;     /* Pointer to color of the starfield */
228         int         NbStars;    /* Number of stars */
229         short       colorchange;        /* Counter for the color change */
230 #if (ADAPT_ERASE == 1)
231         short       hasbeenchecked;
232         long        rect_time;
233         long        xarc_time;
234 #endif
235 } StarField;
236
237 static StarField *starfield = NULL;
238
239 /*********/
240 static void
241 sinvary(SinVariable * v)
242 /*********/
243
244 {
245         v->value = v->minimum +
246                 (v->maximum - v->minimum) * (sin(v->alpha) + 1.0) / 2.0;
247
248         if (v->mayrand == 0)
249                 v->alpha += v->step;
250         else {
251                 int         vaval = NRAND(100);
252
253                 if (vaval <= v->mayrand)
254                         sinvary(v->varrand);
255                 v->alpha += (100.0 + (v->varrand->value)) * v->step / 100.0;
256         }
257
258         if (v->alpha > 2 * M_PI)
259                 v->alpha -= 2 * M_PI;
260 }
261
262 /*************************************************/
263 static void
264 sininit(SinVariable * v,
265         double alpha, double step, double minimum, double maximum,
266         short int mayrand)
267 {
268         v->alpha = alpha;
269         v->step = step;
270         v->minimum = minimum;
271         v->maximum = maximum;
272         v->mayrand = mayrand;
273         if (mayrand != 0) {
274                 if (v->varrand == NULL)
275                         v->varrand = (SinVariable *) calloc(1, sizeof (SinVariable));
276                 sininit(v->varrand,
277                         VARRANDALPHA,
278                         VARRANDSTEP,
279                         VARRANDMIN,
280                         VARRANDMAX,
281                         0);
282                 sinvary(v->varrand);
283         }
284         /* We calculate the values at least once for initialization */
285         sinvary(v);
286 }
287
288 static void
289 sinfree(SinVariable * point)
290 {
291         SinVariable *temp, *next;
292
293         next = point->varrand;
294         while (next) {
295                 temp = next;
296                 next = temp->varrand;
297                 (void) free((void *) temp);
298         }
299 }
300
301 static void free_bouboule(ModeInfo * mi);
302
303
304 /***************/
305 ENTRYPOINT void
306 init_bouboule(ModeInfo * mi)
307 /***************/
308
309 /*-
310  *  The stars init part was first inspirated from the net3d game starfield
311  * code.  But net3d starfield is not really 3d starfield, and I needed real 3d,
312  * so only remains the net3d starfield initialization main idea, that is
313  * the stars distribution on a sphere (theta and omega computing)
314  */
315 {
316         StarField  *sp;
317         int         size = MI_SIZE(mi);
318         int         i;
319         double      theta, omega;
320
321         MI_INIT (mi, starfield, free_bouboule);
322         sp = &starfield[MI_SCREEN(mi)];
323
324         sp->width = MI_WIN_WIDTH(mi);
325         sp->height = MI_WIN_HEIGHT(mi);
326
327         /* use the right `black' pixel values: */
328         if (MI_WIN_IS_INSTALL(mi) && MI_WIN_IS_USE3D(mi)) {
329                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_NONE_COLOR(mi));
330                 XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
331                                0, 0, sp->width, sp->height);
332         } else
333                 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
334
335         if (size < -MINSIZE)
336                 sp->max_star_size = NRAND(-size - MINSIZE + 1) + MINSIZE;
337         else if (size < MINSIZE)
338                 sp->max_star_size = MINSIZE;
339         else
340                 sp->max_star_size = size;
341
342         sp->NbStars = MI_BATCHCOUNT(mi);
343         if (sp->NbStars < -MINSTARS) {
344                 if (sp->star) {
345                         (void) free((void *) sp->star);
346                         sp->star = NULL;
347                 }
348                 if (sp->xarc) {
349                         (void) free((void *) sp->xarc);
350                         sp->xarc = NULL;
351                 }
352                 if (sp->xarcleft) {
353                         (void) free((void *) sp->xarcleft);
354                         sp->xarcleft = NULL;
355                 }
356 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
357                 if (sp->oldxarc) {
358                         (void) free((void *) sp->oldxarc);
359                         sp->oldxarc = NULL;
360                 }
361                 if (sp->oldxarcleft) {
362                         (void) free((void *) sp->oldxarcleft);
363                         sp->oldxarcleft = NULL;
364                 }
365 #endif
366                 sp->NbStars = NRAND(-sp->NbStars - MINSTARS + 1) + MINSTARS;
367         } else if (sp->NbStars < MINSTARS)
368                 sp->NbStars = MINSTARS;
369
370         /* We get memory for lists of objects */
371         if (sp->star == NULL)
372                 sp->star = (Star *) malloc(sp->NbStars * sizeof (Star));
373         if (sp->xarc == NULL)
374                 sp->xarc = (XArc *) malloc(sp->NbStars * sizeof (XArc));
375         if (MI_WIN_IS_USE3D(mi) && sp->xarcleft == NULL)
376                 sp->xarcleft = (XArc *) malloc(sp->NbStars * sizeof (XArc));
377 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
378         if (sp->oldxarc == NULL)
379                 sp->oldxarc = (XArc *) malloc(sp->NbStars * sizeof (XArc));
380         if (MI_WIN_IS_USE3D(mi) && sp->oldxarcleft == NULL)
381                 sp->oldxarcleft = (XArc *) malloc(sp->NbStars * sizeof (XArc));
382 #endif
383
384         {
385                 /* We initialize evolving variables */
386                 sininit(&sp->x,
387                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
388                         ((double) sp->width) / 4.0,
389                         3.0 * ((double) sp->width) / 4.0,
390                         POSCANRAND);
391                 sininit(&sp->y,
392                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
393                         ((double) sp->height) / 4.0,
394                         3.0 * ((double) sp->height) / 4.0,
395                         POSCANRAND);
396
397                 /* for z, we have to ensure that the bouboule does not get behind */
398                 /* the eyes of the viewer.  His/Her eyes are at 0.  Because the */
399                 /* bouboule uses the x-radius for the z-radius, too, we have to */
400                 /* use the x-values. */
401                 sininit(&sp->z,
402                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
403                         ((double) sp->width / 2.0 + MINZVAL),
404                         ((double) sp->width / 2.0 + MAXZVAL),
405                         POSCANRAND);
406
407
408                 sininit(&sp->sizex,
409                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
410                         MIN(((double) sp->width) - sp->x.value,
411                             sp->x.value) / 5.0,
412                         MIN(((double) sp->width) - sp->x.value,
413                             sp->x.value),
414                         SIZECANRAND);
415
416                 sininit(&sp->sizey,
417                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
418                         MAX(sp->sizex.value / MAX_SIZEX_SIZEY,
419                             sp->sizey.maximum / 5.0),
420                         MIN(sp->sizex.value * MAX_SIZEX_SIZEY,
421                             MIN(((double) sp->height) -
422                                 sp->y.value,
423                                 sp->y.value)),
424                         SIZECANRAND);
425
426                 sininit(&sp->thetax,
427                         NRAND(3142) / 1000.0, M_PI / (NRAND(200) + 200.0),
428                         -M_PI, M_PI,
429                         THETACANRAND);
430                 sininit(&sp->thetay,
431                         NRAND(3142) / 1000.0, M_PI / (NRAND(200) + 200.0),
432                         -M_PI, M_PI,
433                         THETACANRAND);
434                 sininit(&sp->thetaz,
435                         NRAND(3142) / 1000.0, M_PI / (NRAND(400) + 400.0),
436                         -M_PI, M_PI,
437                         THETACANRAND);
438         }
439         for (i = 0; i < sp->NbStars; i++) {
440                 Star       *star;
441                 XArc       *arc = NULL, *arcleft = NULL;
442 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
443         XArc       *oarc = NULL, *oarcleft = NULL;
444 #endif
445
446                 star = &(sp->star[i]);
447                 arc = &(sp->xarc[i]);
448                 if (MI_WIN_IS_USE3D(mi))
449                         arcleft = &(sp->xarcleft[i]);
450 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
451                 oarc = &(sp->oldxarc[i]);
452                 if (MI_WIN_IS_USE3D(mi))
453                         oarcleft = &(sp->oldxarcleft[i]);
454 #endif
455                 /* Elevation and bearing of the star */
456                 theta = dtor((NRAND(1800)) / 10.0 - 90.0);
457                 omega = dtor((NRAND(3600)) / 10.0 - 180.0);
458
459                 /* Stars coordinates in a 3D space */
460                 star->x = cos(theta) * sin(omega);
461                 star->y = sin(omega) * sin(theta);
462                 star->z = cos(omega);
463
464                 /* We set the stars size */
465                 star->size = NRAND(2 * sp->max_star_size);
466                 if (star->size < sp->max_star_size)
467                         star->size = 0;
468                 else
469                         star->size -= sp->max_star_size;
470
471                 /* We set default values for the XArc lists elements */
472                 arc->x = arc->y = 0;
473                 if (MI_WIN_IS_USE3D(mi)) {
474                         arcleft->x = arcleft->y = 0;
475                 }
476 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
477                 oarc->x = oarc->y = 0;
478                 if (MI_WIN_IS_USE3D(mi)) {
479                         oarcleft->x = oarcleft->y = 0;
480                 }
481 #endif
482                 arc->width = 2 + star->size;
483                 arc->height = 2 + star->size;
484                 if (MI_WIN_IS_USE3D(mi)) {
485                         arcleft->width = 2 + star->size;
486                         arcleft->height = 2 + star->size;
487                 }
488 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
489                 oarc->width = 2 + star->size;
490                 oarc->height = 2 + star->size;
491                 if (MI_WIN_IS_USE3D(mi)) {
492                         oarcleft->width = 2 + star->size;
493                         oarcleft->height = 2 + star->size;
494                 }
495 #endif
496
497                 arc->angle1 = 0;
498                 arc->angle2 = 360 * 64;
499                 if (MI_WIN_IS_USE3D(mi)) {
500                         arcleft->angle1 = 0;
501                         arcleft->angle2 = 360 * 64;
502                 }
503 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
504                 oarc->angle1 = 0;
505                 oarc->angle2 = 360 * 64;        /* ie. we draw whole disks:
506                                                  * from 0 to 360 degrees */
507                 if (MI_WIN_IS_USE3D(mi)) {
508                         oarcleft->angle1 = 0;
509                         oarcleft->angle2 = 360 * 64;
510                 }
511 #endif
512         }
513
514         if (MI_NPIXELS(mi) > 2)
515                 sp->colorp = NRAND(MI_NPIXELS(mi));
516         /* We set up the starfield color */
517         if (!MI_WIN_IS_USE3D(mi) && MI_NPIXELS(mi) > 2)
518                 sp->color = MI_PIXEL(mi, sp->colorp);
519         else
520                 sp->color = MI_WIN_WHITE_PIXEL(mi);
521
522 #if (ADAPT_ERASE == 1)
523         /* We initialize the adaptation code for screen erasing */
524         sp->hasbeenchecked = ADAPT_CHECKS * 2;
525         sp->rect_time = 0;
526         sp->xarc_time = 0;
527 #endif
528 }
529
530 /****************/
531 ENTRYPOINT void
532 draw_bouboule(ModeInfo * mi)
533 /****************/
534
535 {
536         Display    *display = MI_DISPLAY(mi);
537         Window      window = MI_WINDOW(mi);
538         GC          gc = MI_GC(mi);
539         StarField  *sp = &starfield[MI_SCREEN(mi)];
540         int         i, diff = 0;
541         double      CX, CY, CZ, SX, SY, SZ;
542         Star       *star;
543         XArc       *arc = NULL, *arcleft = NULL;
544
545 #ifdef HAVE_JWXYZ       /* Don't second-guess Quartz's double-buffering */
546     XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
547 #endif
548
549 #if (ADAPT_ERASE == 1)
550         struct timeval tv1;
551         struct timeval tv2;
552
553 #endif
554
555 #if ((USEOLDXARCS == 0) || (ADAPT_ERASE == 1))
556         short       x_1, y_1, x_2, y_2;
557
558         /* bounding rectangle around the old starfield,
559          * for erasing with the smallest rectangle
560          * instead of filling the whole screen */
561         int         maxdiff = 0;        /* maximal distance between left and right */
562
563         /* star in 3d mode, otherwise 0 */
564 #endif
565
566 #if ((USEOLDXARCS == 0) || (ADAPT_ERASE == 1))
567         if (MI_WIN_IS_USE3D(mi)) {
568                 maxdiff = (int) MAXDIFF;
569         }
570         x_1 = (int) sp->x.value - (int) sp->sizex.value -
571                 sp->max_star_size - maxdiff;
572         y_1 = (int) sp->y.value - (int) sp->sizey.value -
573                 sp->max_star_size;
574         x_2 = 2 * ((int) sp->sizex.value + sp->max_star_size + maxdiff);
575         y_2 = 2 * ((int) sp->sizey.value + sp->max_star_size);
576 #endif
577         /* We make variables vary. */
578         sinvary(&sp->thetax);
579         sinvary(&sp->thetay);
580         sinvary(&sp->thetaz);
581
582         sinvary(&sp->x);
583         sinvary(&sp->y);
584         if (MI_WIN_IS_USE3D(mi))
585                 sinvary(&sp->z);
586
587         /* A little trick to prevent the bouboule from being
588          * bigger than the screen */
589         sp->sizex.maximum =
590                 MIN(((double) sp->width) - sp->x.value,
591                     sp->x.value);
592         sp->sizex.minimum = sp->sizex.maximum / 3.0;
593
594         /* Another trick to make the ball not too flat */
595         sp->sizey.minimum =
596                 MAX(sp->sizex.value / MAX_SIZEX_SIZEY,
597                     sp->sizey.maximum / 3.0);
598         sp->sizey.maximum =
599                 MIN(sp->sizex.value * MAX_SIZEX_SIZEY,
600                     MIN(((double) sp->height) - sp->y.value,
601                         sp->y.value));
602
603         sinvary(&sp->sizex);
604         sinvary(&sp->sizey);
605
606         /*
607          * We calculate the rotation matrix values. We just make the
608          * rotation on the fly, without using a matrix.
609          * Star positions are recorded as unit vectors pointing in various
610          * directions. We just make them all rotate.
611          */
612         CX = cos(sp->thetax.value);
613         SX = sin(sp->thetax.value);
614         CY = cos(sp->thetay.value);
615         SY = sin(sp->thetay.value);
616         CZ = cos(sp->thetaz.value);
617         SZ = sin(sp->thetaz.value);
618
619         for (i = 0; i < sp->NbStars; i++) {
620                 star = &(sp->star[i]);
621                 arc = &(sp->xarc[i]);
622                 if (MI_WIN_IS_USE3D(mi)) {
623                         arcleft = &(sp->xarcleft[i]);
624                         /* to help the eyes, the starfield is always as wide as */
625                         /* deep, so .sizex.value can be used. */
626                         diff = (int) GETZDIFF(sp->sizex.value *
627                                       ((SY * CX) * star->x + (SX) * star->y +
628                                        (CX * CY) * star->z) + sp->z.value);
629                 }
630                 arc->x = (short) ((sp->sizex.value *
631                                    ((CY * CZ - SX * SY * SZ) * star->x +
632                                     (-CX * SZ) * star->y +
633                                     (SY * CZ + SZ * SX * CY) * star->z) +
634                                    sp->x.value));
635                 arc->y = (short) ((sp->sizey.value *
636                                    ((CY * SZ + SX * SY * CZ) * star->x +
637                                     (CX * CZ) * star->y +
638                                     (SY * SZ - SX * CY * CZ) * star->z) +
639                                    sp->y.value));
640
641                 if (MI_WIN_IS_USE3D(mi)) {
642                         arcleft->x = (short) ((sp->sizex.value *
643                                         ((CY * CZ - SX * SY * SZ) * star->x +
644                                          (-CX * SZ) * star->y +
645                                          (SY * CZ + SZ * SX * CY) * star->z) +
646                                                sp->x.value));
647                         arcleft->y = (short) ((sp->sizey.value *
648                                         ((CY * SZ + SX * SY * CZ) * star->x +
649                                          (CX * CZ) * star->y +
650                                          (SY * SZ - SX * CY * CZ) * star->z) +
651                                                sp->y.value));
652                         arc->x += diff;
653                         arcleft->x -= diff;
654                 }
655                 if (star->size != 0) {
656                         arc->x -= star->size;
657                         arc->y -= star->size;
658                         if (MI_WIN_IS_USE3D(mi)) {
659                                 arcleft->x -= star->size;
660                                 arcleft->y -= star->size;
661                         }
662                 }
663         }
664
665         /* First, we erase the previous starfield */
666         if (MI_WIN_IS_INSTALL(mi) && MI_WIN_IS_USE3D(mi))
667                 XSetForeground(display, gc, MI_NONE_COLOR(mi));
668         else
669                 XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
670
671 #if (ADAPT_ERASE == 1)
672         if (sp->hasbeenchecked == 0) {
673                 /* We just calculate which method is the faster and eventually free
674                  * the oldxarc list */
675                 if (sp->xarc_time >
676                     ADAPT_ARC_PREFERED * sp->rect_time) {
677                         sp->hasbeenchecked = -2;        /* XFillRectangle mode */
678                         (void) free((void *) sp->oldxarc);
679                         sp->oldxarc = NULL;
680                         if (MI_WIN_IS_USE3D(mi)) {
681                                 (void) free((void *) sp->oldxarcleft);
682                           sp->oldxarcleft = NULL;
683                         }
684                 } else {
685                         sp->hasbeenchecked = -1;        /* XFillArcs mode */
686                 }
687         }
688         if (sp->hasbeenchecked == -2) {
689                 /* Erasing is done with XFillRectangle */
690                 XFillRectangle(display, window, gc,
691                                x_1, y_1, x_2, y_2);
692         } else if (sp->hasbeenchecked == -1) {
693                 /* Erasing is done with XFillArcs */
694                 XFillArcs(display, window, gc,
695                           sp->oldxarc, sp->NbStars);
696                 if (MI_WIN_IS_USE3D(mi))
697                         XFillArcs(display, window, gc,
698                                   sp->oldxarcleft, sp->NbStars);
699         } else {
700                 long        usec;
701
702                 if (sp->hasbeenchecked > ADAPT_CHECKS) {
703 #ifdef GETTIMEOFDAY_TWO_ARGS
704                         (void) gettimeofday(&tv1, NULL);
705 #else
706                         (void) gettimeofday(&tv1);
707 #endif
708                         XFillRectangle(display, window, gc,
709                                        x_1, y_1, x_2, y_2);
710 #ifdef GETTIMEOFDAY_TWO_ARGS
711                         (void) gettimeofday(&tv2, NULL);
712 #else
713                         (void) gettimeofday(&tv2);
714 #endif
715                         usec = (tv2.tv_sec - tv1.tv_sec) * 1000000;
716                         if (usec + tv2.tv_usec - tv1.tv_usec > 0) {
717                                 sp->rect_time += usec + tv2.tv_usec - tv1.tv_usec;
718                                 sp->hasbeenchecked--;
719                         }
720                 } else {
721 #ifdef GETTIMEOFDAY_TWO_ARGS
722                         (void) gettimeofday(&tv1, NULL);
723 #else
724                         (void) gettimeofday(&tv1);
725 #endif
726                         XFillArcs(display, window, gc,
727                                   sp->oldxarc, sp->NbStars);
728                         if (MI_WIN_IS_USE3D(mi))
729                                 XFillArcs(display, window, gc,
730                                           sp->oldxarcleft, sp->NbStars);
731 #ifdef GETTIMEOFDAY_TWO_ARGS
732                         (void) gettimeofday(&tv2, NULL);
733 #else
734                         (void) gettimeofday(&tv2);
735 #endif
736                         usec = (tv2.tv_sec - tv1.tv_sec) * 1000000;
737                         if (usec + tv2.tv_usec - tv1.tv_usec > 0) {
738                                 sp->xarc_time += usec + tv2.tv_usec - tv1.tv_usec;
739                                 sp->hasbeenchecked--;
740                         }
741                 }
742         }
743 #else
744 #if (USEOLDXARCS == 1)
745         XFillArcs(display, window, gc,
746                   sp->oldxarc, sp->NbStars);
747         if (MI_WIN_IS_USE3D(mi))
748                 XFillArcs(display, window, gc,
749                           sp->oldxarcleft, sp->NbStars);
750 #else
751         XFillRectangle(display, window, gc,
752                        x_1, y_1, x_2, y_2);
753 #endif
754 #endif
755
756         /* Then we draw the new one */
757         if (MI_WIN_IS_USE3D(mi)) {
758                 if (MI_WIN_IS_INSTALL(mi))
759                         XSetFunction(display, gc, GXor);
760                 XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
761                 XFillArcs(display, window, gc, sp->xarc, sp->NbStars);
762                 XSetForeground(display, gc, MI_LEFT_COLOR(mi));
763                 XFillArcs(display, window, gc, sp->xarcleft, sp->NbStars);
764                 if (MI_WIN_IS_INSTALL(mi))
765                         XSetFunction(display, gc, GXcopy);
766         } else {
767                 XSetForeground(display, gc, sp->color);
768                 XFillArcs(display, window, gc, sp->xarc, sp->NbStars);
769         }
770
771 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
772 #if (ADAPT_ERASE == 1)
773         if (sp->hasbeenchecked >= -1) {
774                 arc = sp->xarc;
775                 sp->xarc = sp->oldxarc;
776                 sp->oldxarc = arc;
777                 if (MI_WIN_IS_USE3D(mi)) {
778                         arcleft = sp->xarcleft;
779                         sp->xarcleft = sp->oldxarcleft;
780                         sp->oldxarcleft = arcleft;
781                 }
782         }
783 #else
784         arc = sp->xarc;
785         sp->xarc = sp->oldxarc;
786         sp->oldxarc = arc;
787         if (MI_WIN_IS_USE3D(mi)) {
788                 arcleft = sp->xarcleft;
789                 sp->xarcleft = sp->oldxarcleft;
790                 sp->oldxarcleft = arcleft;
791         }
792 #endif
793 #endif
794
795         /* We set up the color for the next drawing */
796         if (!MI_WIN_IS_USE3D(mi) && MI_NPIXELS(mi) > 2 &&
797             (++sp->colorchange >= COLOR_CHANGES)) {
798                 sp->colorchange = 0;
799                 if (++sp->colorp >= MI_NPIXELS(mi))
800                         sp->colorp = 0;
801                 sp->color = MI_PIXEL(mi, sp->colorp);
802         }
803 }
804
805 static void
806 free_bouboule(ModeInfo * mi)
807 {
808         StarField  *sp = &starfield[MI_SCREEN(mi)];
809
810         if (sp->star)
811                 (void) free((void *) sp->star);
812         if (sp->xarc)
813                 (void) free((void *) sp->xarc);
814         if (sp->xarcleft)
815                 (void) free((void *) sp->xarcleft);
816 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
817         if (sp->oldxarc)
818                 (void) free((void *) sp->oldxarc);
819         if (sp->oldxarcleft)
820                 (void) free((void *) sp->oldxarcleft);
821 #endif
822         sinfree(&(sp->x));
823         sinfree(&(sp->y));
824         sinfree(&(sp->z));
825         sinfree(&(sp->sizex));
826         sinfree(&(sp->sizey));
827         sinfree(&(sp->thetax));
828         sinfree(&(sp->thetay));
829         sinfree(&(sp->thetaz));
830 }
831
832 ENTRYPOINT void
833 reshape_bouboule(ModeInfo * mi, int width, int height)
834 {
835         StarField  *sp = &starfield[MI_SCREEN(mi)];
836         sp->width  = width;
837         sp->height = height;
838     sininit(&sp->x,
839         sp->x.alpha, sp->x.step,
840         ((double) sp->width) / 4.0,
841         3.0 * ((double) sp->width) / 4.0,
842         POSCANRAND);
843     sininit(&sp->y,
844         sp->y.alpha, sp->y.step,
845         ((double) sp->height) / 4.0,
846         3.0 * ((double) sp->height) / 4.0,
847         POSCANRAND);
848 }
849
850 ENTRYPOINT void
851 refresh_bouboule(ModeInfo * mi)
852 {
853         /* Do nothing, it will refresh by itself */
854 }
855
856 XSCREENSAVER_MODULE ("Bouboule", bouboule)