http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.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 PROGCLASS                                      "Bouboule"
78 # define HACK_INIT                                      init_bouboule
79 # define HACK_DRAW                                      draw_bouboule
80 # define bouboule_opts                          xlockmore_opts
81 # define DEFAULTS       "*count:                100     \n"                     \
82                                         "*size:                 15      \n"                     \
83                                         "*delay:                5000    \n"                     \
84                                         "*ncolors:              64      \n"                     \
85                                         "*use3d:                False   \n"                     \
86                                         "*delta3d:              1.5             \n"                     \
87                                         "*right3d:              red             \n"                     \
88                                         "*left3d:               blue    \n"                     \
89                                         "*both3d:               magenta \n"                     \
90                                         "*none3d:               black   \n"
91
92 # define SMOOTH_COLORS
93 # include "xlockmore.h"                         /* from the xscreensaver distribution */
94 #else  /* !STANDALONE */
95 # include "xlock.h"                                     /* from the xlockmore distribution */
96 #endif /* !STANDALONE */
97
98 ModeSpecOpt bouboule_opts = {
99   0, NULL, 0, NULL, NULL };
100
101 #define USEOLDXARCS  1          /* If 1, we use old xarcs list for erasing.
102                                    * else we just roughly erase the window.
103                                    * This mainly depends on the number of stars,
104                                    * because when they are many, it is faster to
105                                    * erase the whole window than to erase each star
106                                  */
107
108 #if HAVE_GETTIMEOFDAY
109 #define ADAPT_ERASE  1          /* If 1, then we try ADAPT_CHECKS black XFillArcs,
110                                    * and after, ADAPT_CHECKS XFillRectangle.
111                                    * We check which method seems better, knowing that
112                                    * XFillArcs is generally visually better. So we
113                                    * consider that XFillArcs is still better if its time
114                                    * is about XFillRectangle * ADAPT_ARC_PREFERED
115                                    * We need gettimeofday
116                                    * for this... Does it exist on other systems ? Do we
117                                    * have to use another function for others ?
118                                    * This value overrides USEOLDXARCS.
119                                  */
120
121 #ifdef USE_XVMSUTILS
122 # if 0
123 #  include "../xvmsutils/unix_time.h"
124 # else
125 #  include <X11/unix_time.h>
126 # endif
127 #endif
128
129 #include <sys/time.h>
130
131 #define ADAPT_CHECKS 50
132 #define ADAPT_ARC_PREFERED 150  /* Maybe the value that is the most important
133                                    * for adapting to a system */
134 #endif
135
136 #define dtor(x)    (((x) * M_PI) / 180.0)       /* Degrees to radians */
137
138 #define MINSTARS      1
139 #define MINSIZE       1
140 /* jwz: I think slower color changes look better */
141 #define COLOR_CHANGES 50                /* How often we change colors (1 = always)
142                                    * This value should be tuned accordingly to
143                                    * the number of stars */
144 #define MAX_SIZEX_SIZEY 2.      /* This controls whether the sphere can be very
145                                    * very large and have a small height (or the
146                                    * opposite) or no. */
147
148 #define THETACANRAND  80        /* percentage of changes for the speed of
149                                    * change of the 3 theta values */
150 #define SIZECANRAND   80        /* percentage of changes for the speed of
151                                    * change of the sizex and sizey values */
152 #define POSCANRAND    80        /* percentage of changes for the speed of
153                                    * change of the x and y values */
154 /* Note that these XXXCANRAND values can be 0, that is no rand acceleration *
155    variation. */
156
157 #define VARRANDALPHA (NRAND((int) (M_PI * 1000.0))/1000.0)
158 #define VARRANDSTEP  (M_PI/(NRAND(100)+100.0))
159 #define VARRANDMIN   (-70.0)
160 #define VARRANDMAX   70.0
161
162 #define MINZVAL   100           /* stars can come this close */
163 #define SCREENZ  2000           /* this is where the screen is */
164 #define MAXZVAL 10000           /* stars can go this far away */
165
166 #define GETZDIFF(z) ((MI_DELTA3D(mi))*20.0*(1.0-(SCREENZ)/(z+1000)))
167 #define MAXDIFF  MAX(-GETZDIFF(MINZVAL),GETZDIFF(MAXZVAL))
168
169 /* These values are the variation parameters of the acceleration variation *
170    of the SinVariables that are randomized. */
171
172 /******************************/
173 typedef struct SinVariableStruct
174 /******************************/
175 {
176         double      alpha;      /* 
177                                  * Alpha is the current state of the sinvariable
178                                  * alpha should be initialized to a value between
179                                  * 0.0 and 2 * M_PI
180                                  */
181         double      step;       /*
182                                  * Speed of evolution of alpha. It should be a reasonable
183                                  * fraction of 2 * M_PI. This value directly influence
184                                  * the variable speed of variation.
185                                  */
186         double      minimum;    /* Minimum value for the variable */
187         double      maximum;    /* Maximum value for the variable */
188         double      value;      /* Current value */
189         int         mayrand;    /* Flag for knowing whether some randomization can be
190                                  * applied to the variable */
191         struct SinVariableStruct *varrand;      /* Evolving Variable: the variation of
192                                                    * alpha */
193 } SinVariable;
194
195 /***********************/
196 typedef struct StarStruct
197 /***********************/
198 {
199         double      x, y, z;    /* Position of the star */
200         short       size;       /* Try to guess */
201 } Star;
202
203 /****************************/
204 typedef struct StarFieldStruct
205 /****************************/
206 {
207         short       width, height;      /* width and height of the starfield window */
208         short       max_star_size;      /* Maximum radius for stars. stars radius will
209                                          * vary from 1 to MAX_STAR_SIZE */
210         SinVariable x;          /* Evolving variables:               */
211         SinVariable y;          /* Center of the field on the screen */
212         SinVariable z;
213         SinVariable sizex;      /* Evolving variable: half width  of the field */
214         SinVariable sizey;      /* Evolving variable: half height of the field */
215         SinVariable thetax;     /* Evolving Variables:              */
216         SinVariable thetay;     /* rotation angles of the starfield */
217         SinVariable thetaz;     /* around x, y and z local axis     */
218         Star       *star;       /* List of stars */
219         XArc       *xarc;       /* Current List of arcs */
220         XArc       *xarcleft;   /* additional list for the left arcs */
221 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
222         XArc       *oldxarc;    /* Old list of arcs */
223         XArc       *oldxarcleft;
224 #endif
225         unsigned long color;    /* Current color of the starfield */
226         int         colorp;     /* Pointer to color of the starfield */
227         int         NbStars;    /* Number of stars */
228         short       colorchange;        /* Counter for the color change */
229 #if (ADAPT_ERASE == 1)
230         short       hasbeenchecked;
231         long        rect_time;
232         long        xarc_time;
233 #endif
234 } StarField;
235
236 static StarField *starfield = NULL;
237
238 /*********/
239 static void
240 sinvary(SinVariable * v)
241 /*********/
242
243 {
244         v->value = v->minimum +
245                 (v->maximum - v->minimum) * (sin(v->alpha) + 1.0) / 2.0;
246
247         if (v->mayrand == 0)
248                 v->alpha += v->step;
249         else {
250                 int         vaval = NRAND(100);
251
252                 if (vaval <= v->mayrand)
253                         sinvary(v->varrand);
254                 v->alpha += (100.0 + (v->varrand->value)) * v->step / 100.0;
255         }
256
257         if (v->alpha > 2 * M_PI)
258                 v->alpha -= 2 * M_PI;
259 }
260
261 /*************************************************/
262 static void
263 sininit(SinVariable * v,
264         double alpha, double step, double minimum, double maximum,
265         short int mayrand)
266 {
267         v->alpha = alpha;
268         v->step = step;
269         v->minimum = minimum;
270         v->maximum = maximum;
271         v->mayrand = mayrand;
272         if (mayrand != 0) {
273                 if (v->varrand == NULL)
274                         v->varrand = (SinVariable *) calloc(1, sizeof (SinVariable));
275                 sininit(v->varrand,
276                         VARRANDALPHA,
277                         VARRANDSTEP,
278                         VARRANDMIN,
279                         VARRANDMAX,
280                         0);
281                 sinvary(v->varrand);
282         }
283         /* We calculate the values at least once for initialization */
284         sinvary(v);
285 }
286
287 static void
288 sinfree(SinVariable * point)
289 {
290         SinVariable *temp, *next;
291
292         next = point->varrand;
293         while (next) {
294                 temp = next;
295                 next = temp->varrand;
296                 (void) free((void *) temp);
297         }
298 }
299
300 /***************/
301 void
302 init_bouboule(ModeInfo * mi)
303 /***************/
304
305 /*-
306  *  The stars init part was first inspirated from the net3d game starfield
307  * code.  But net3d starfield is not really 3d starfield, and I needed real 3d,
308  * so only remains the net3d starfield initialization main idea, that is
309  * the stars distribution on a sphere (theta and omega computing)
310  */
311 {
312         StarField  *sp;
313         int         size = MI_SIZE(mi);
314         int         i;
315         double      theta, omega;
316
317         if (starfield == NULL) {
318                 if ((starfield = (StarField *) calloc(MI_NUM_SCREENS(mi),
319                                                 sizeof (StarField))) == NULL)
320                         return;
321         }
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 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 #if (ADAPT_ERASE == 1)
546         struct timeval tv1;
547         struct timeval tv2;
548
549 #endif
550
551 #if ((USEOLDXARCS == 0) || (ADAPT_ERASE == 1))
552         short       x_1, y_1, x_2, y_2;
553
554         /* bounding rectangle around the old starfield,
555          * for erasing with the smallest rectangle
556          * instead of filling the whole screen */
557         int         maxdiff = 0;        /* maximal distance between left and right */
558
559         /* star in 3d mode, otherwise 0 */
560 #endif
561
562 #if ((USEOLDXARCS == 0) || (ADAPT_ERASE == 1))
563         if (MI_WIN_IS_USE3D(mi)) {
564                 maxdiff = (int) MAXDIFF;
565         }
566         x_1 = (int) sp->x.value - (int) sp->sizex.value -
567                 sp->max_star_size - maxdiff;
568         y_1 = (int) sp->y.value - (int) sp->sizey.value -
569                 sp->max_star_size;
570         x_2 = 2 * ((int) sp->sizex.value + sp->max_star_size + maxdiff);
571         y_2 = 2 * ((int) sp->sizey.value + sp->max_star_size);
572 #endif
573         /* We make variables vary. */
574         sinvary(&sp->thetax);
575         sinvary(&sp->thetay);
576         sinvary(&sp->thetaz);
577
578         sinvary(&sp->x);
579         sinvary(&sp->y);
580         if (MI_WIN_IS_USE3D(mi))
581                 sinvary(&sp->z);
582
583         /* A little trick to prevent the bouboule from being
584          * bigger than the screen */
585         sp->sizex.maximum =
586                 MIN(((double) sp->width) - sp->x.value,
587                     sp->x.value);
588         sp->sizex.minimum = sp->sizex.maximum / 3.0;
589
590         /* Another trick to make the ball not too flat */
591         sp->sizey.minimum =
592                 MAX(sp->sizex.value / MAX_SIZEX_SIZEY,
593                     sp->sizey.maximum / 3.0);
594         sp->sizey.maximum =
595                 MIN(sp->sizex.value * MAX_SIZEX_SIZEY,
596                     MIN(((double) sp->height) - sp->y.value,
597                         sp->y.value));
598
599         sinvary(&sp->sizex);
600         sinvary(&sp->sizey);
601
602         /*
603          * We calculate the rotation matrix values. We just make the
604          * rotation on the fly, without using a matrix.
605          * Star positions are recorded as unit vectors pointing in various
606          * directions. We just make them all rotate.
607          */
608         CX = cos(sp->thetax.value);
609         SX = sin(sp->thetax.value);
610         CY = cos(sp->thetay.value);
611         SY = sin(sp->thetay.value);
612         CZ = cos(sp->thetaz.value);
613         SZ = sin(sp->thetaz.value);
614
615         for (i = 0; i < sp->NbStars; i++) {
616                 star = &(sp->star[i]);
617                 arc = &(sp->xarc[i]);
618                 if (MI_WIN_IS_USE3D(mi)) {
619                         arcleft = &(sp->xarcleft[i]);
620                         /* to help the eyes, the starfield is always as wide as */
621                         /* deep, so .sizex.value can be used. */
622                         diff = (int) GETZDIFF(sp->sizex.value *
623                                       ((SY * CX) * star->x + (SX) * star->y +
624                                        (CX * CY) * star->z) + sp->z.value);
625                 }
626                 arc->x = (short) ((sp->sizex.value *
627                                    ((CY * CZ - SX * SY * SZ) * star->x +
628                                     (-CX * SZ) * star->y +
629                                     (SY * CZ + SZ * SX * CY) * star->z) +
630                                    sp->x.value));
631                 arc->y = (short) ((sp->sizey.value *
632                                    ((CY * SZ + SX * SY * CZ) * star->x +
633                                     (CX * CZ) * star->y +
634                                     (SY * SZ - SX * CY * CZ) * star->z) +
635                                    sp->y.value));
636
637                 if (MI_WIN_IS_USE3D(mi)) {
638                         arcleft->x = (short) ((sp->sizex.value *
639                                         ((CY * CZ - SX * SY * SZ) * star->x +
640                                          (-CX * SZ) * star->y +
641                                          (SY * CZ + SZ * SX * CY) * star->z) +
642                                                sp->x.value));
643                         arcleft->y = (short) ((sp->sizey.value *
644                                         ((CY * SZ + SX * SY * CZ) * star->x +
645                                          (CX * CZ) * star->y +
646                                          (SY * SZ - SX * CY * CZ) * star->z) +
647                                                sp->y.value));
648                         arc->x += diff;
649                         arcleft->x -= diff;
650                 }
651                 if (star->size != 0) {
652                         arc->x -= star->size;
653                         arc->y -= star->size;
654                         if (MI_WIN_IS_USE3D(mi)) {
655                                 arcleft->x -= star->size;
656                                 arcleft->y -= star->size;
657                         }
658                 }
659         }
660
661         /* First, we erase the previous starfield */
662         if (MI_WIN_IS_INSTALL(mi) && MI_WIN_IS_USE3D(mi))
663                 XSetForeground(display, gc, MI_NONE_COLOR(mi));
664         else
665                 XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
666
667 #if (ADAPT_ERASE == 1)
668         if (sp->hasbeenchecked == 0) {
669                 /* We just calculate which method is the faster and eventually free
670                  * the oldxarc list */
671                 if (sp->xarc_time >
672                     ADAPT_ARC_PREFERED * sp->rect_time) {
673                         sp->hasbeenchecked = -2;        /* XFillRectangle mode */
674                         (void) free((void *) sp->oldxarc);
675                         sp->oldxarc = NULL;
676                         if (MI_WIN_IS_USE3D(mi)) {
677                                 (void) free((void *) sp->oldxarcleft);
678                           sp->oldxarcleft = NULL;
679                         }
680                 } else {
681                         sp->hasbeenchecked = -1;        /* XFillArcs mode */
682                 }
683         }
684         if (sp->hasbeenchecked == -2) {
685                 /* Erasing is done with XFillRectangle */
686                 XFillRectangle(display, window, gc,
687                                x_1, y_1, x_2, y_2);
688         } else if (sp->hasbeenchecked == -1) {
689                 /* Erasing is done with XFillArcs */
690                 XFillArcs(display, window, gc,
691                           sp->oldxarc, sp->NbStars);
692                 if (MI_WIN_IS_USE3D(mi))
693                         XFillArcs(display, window, gc,
694                                   sp->oldxarcleft, sp->NbStars);
695         } else {
696                 long        usec;
697
698                 if (sp->hasbeenchecked > ADAPT_CHECKS) {
699 #ifdef GETTIMEOFDAY_TWO_ARGS
700                         (void) gettimeofday(&tv1, NULL);
701 #else
702                         (void) gettimeofday(&tv1);
703 #endif
704                         XFillRectangle(display, window, gc,
705                                        x_1, y_1, x_2, y_2);
706 #ifdef GETTIMEOFDAY_TWO_ARGS
707                         (void) gettimeofday(&tv2, NULL);
708 #else
709                         (void) gettimeofday(&tv2);
710 #endif
711                         usec = (tv2.tv_sec - tv1.tv_sec) * 1000000;
712                         if (usec + tv2.tv_usec - tv1.tv_usec > 0) {
713                                 sp->rect_time += usec + tv2.tv_usec - tv1.tv_usec;
714                                 sp->hasbeenchecked--;
715                         }
716                 } else {
717 #ifdef GETTIMEOFDAY_TWO_ARGS
718                         (void) gettimeofday(&tv1, NULL);
719 #else
720                         (void) gettimeofday(&tv1);
721 #endif
722                         XFillArcs(display, window, gc,
723                                   sp->oldxarc, sp->NbStars);
724                         if (MI_WIN_IS_USE3D(mi))
725                                 XFillArcs(display, window, gc,
726                                           sp->oldxarcleft, sp->NbStars);
727 #ifdef GETTIMEOFDAY_TWO_ARGS
728                         (void) gettimeofday(&tv2, NULL);
729 #else
730                         (void) gettimeofday(&tv2);
731 #endif
732                         usec = (tv2.tv_sec - tv1.tv_sec) * 1000000;
733                         if (usec + tv2.tv_usec - tv1.tv_usec > 0) {
734                                 sp->xarc_time += usec + tv2.tv_usec - tv1.tv_usec;
735                                 sp->hasbeenchecked--;
736                         }
737                 }
738         }
739 #else
740 #if (USEOLDXARCS == 1)
741         XFillArcs(display, window, gc,
742                   sp->oldxarc, sp->NbStars);
743         if (MI_WIN_IS_USE3D(mi))
744                 XFillArcs(display, window, gc,
745                           sp->oldxarcleft, sp->NbStars);
746 #else
747         XFillRectangle(display, window, gc,
748                        x_1, y_1, x_2, y_2);
749 #endif
750 #endif
751
752         /* Then we draw the new one */
753         if (MI_WIN_IS_USE3D(mi)) {
754                 if (MI_WIN_IS_INSTALL(mi))
755                         XSetFunction(display, gc, GXor);
756                 XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
757                 XFillArcs(display, window, gc, sp->xarc, sp->NbStars);
758                 XSetForeground(display, gc, MI_LEFT_COLOR(mi));
759                 XFillArcs(display, window, gc, sp->xarcleft, sp->NbStars);
760                 if (MI_WIN_IS_INSTALL(mi))
761                         XSetFunction(display, gc, GXcopy);
762         } else {
763                 XSetForeground(display, gc, sp->color);
764                 XFillArcs(display, window, gc, sp->xarc, sp->NbStars);
765         }
766
767 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
768 #if (ADAPT_ERASE == 1)
769         if (sp->hasbeenchecked >= -1) {
770                 arc = sp->xarc;
771                 sp->xarc = sp->oldxarc;
772                 sp->oldxarc = arc;
773                 if (MI_WIN_IS_USE3D(mi)) {
774                         arcleft = sp->xarcleft;
775                         sp->xarcleft = sp->oldxarcleft;
776                         sp->oldxarcleft = arcleft;
777                 }
778         }
779 #else
780         arc = sp->xarc;
781         sp->xarc = sp->oldxarc;
782         sp->oldxarc = arc;
783         if (MI_WIN_IS_USE3D(mi)) {
784                 arcleft = sp->xarcleft;
785                 sp->xarcleft = sp->oldxarcleft;
786                 sp->oldxarcleft = arcleft;
787         }
788 #endif
789 #endif
790
791         /* We set up the color for the next drawing */
792         if (!MI_WIN_IS_USE3D(mi) && MI_NPIXELS(mi) > 2 &&
793             (++sp->colorchange >= COLOR_CHANGES)) {
794                 sp->colorchange = 0;
795                 if (++sp->colorp >= MI_NPIXELS(mi))
796                         sp->colorp = 0;
797                 sp->color = MI_PIXEL(mi, sp->colorp);
798         }
799 }
800
801 void
802 release_bouboule(ModeInfo * mi)
803 {
804         if (starfield != NULL) {
805                 int         screen;
806
807                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
808                         StarField  *sp = &starfield[screen];
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                 (void) free((void *) starfield);
832                 starfield = NULL;
833         }
834 }
835
836 void
837 refresh_bouboule(ModeInfo * mi)
838 {
839         /* Do nothing, it will refresh by itself */
840 }