/* -*- Mode: C; tab-width: 4 -*-
Ported from xlockmore 4.03a12 to be a standalone program and thus usable
with xscreensaver by Jamie Zawinski <jwz@jwz.org> on 15-May-97.
Original copyright notice from xlock.c:
* Copyright (c) 1988-91 by Patrick J. Naughton.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* This file is provided AS IS with no warranties of any kind. The author
* shall have no liability with respect to the infringement of copyrights,
* trade secrets or any patents by this file or any part thereof. In no
* event will the author be liable for any lost revenue or profits or
* other special, indirect and consequential damages.
*/
#if 0
static const char sccsid[] = "@(#)bouboule.c 4.00 97/01/01 xlockmore";
#endif
/*-
* bouboule.c (bouboule mode for xlockmore)
*
* Sort of starfield for xlockmore. I found that making a starfield for
* a 3D engine and thought it could be a nice lock mode. For a real starfield,
* I only scale the sort of sphere you see to the whole sky and clip the stars
* to the camera screen.
*
* Code Copyright 1996 by Jeremie PETIT (jeremie_petit@geocities.com)
*
* Use: batchcount is the number of stars.
* cycles is the maximum size for a star
*
* 15-May-97: jwz@jwz.org: turned into a standalone program.
* 04-Sep-96: Added 3d support (Henrik Theiling, theiling@coli-uni-sb.de)
* 20-Feb-96: Added tests so that already malloced objects are not
* malloced twice, thanks to the report from <mccomb@interport.net>
* 01-Feb-96: Patched by Jouk Jansen <joukj@alpha.chem.uva.nl> for VMS
* Patched by <bagleyd@bigfoot.com> for TrueColor displays
* 30-Jan-96: Wrote all that I wanted to.
*
* DONE: Build up a XArc list and Draw everything once with XFillArcs
* That idea came from looking at swarm code.
* DONE: Add an old arcs list for erasing.
* DONE: Make center of starfield SinVariable.
* DONE: Add some random in the sinvary() function.
* DONE: check time for erasing the stars with the two methods and use the
* better one. Note that sometimes the time difference between
* beginning of erasing and its end is negative! I check this, and
* do not use this result when it occurs. If all values are negative,
* the erasing will continue being done in the currently tested mode.
* DONE: Allow stars size customization.
* DONE: Make sizey be no less than half sizex or no bigger than twice sizex.
*
* IDEA: A simple check can be performed to know which stars are "behind"
* and which are "in front". So is possible to very simply change
* the drawing mode for these two sorts of stars. BUT: this would lead
* to a rewrite of the XArc list code because drawing should be done
* in two steps: "behind" stars then "in front" stars. Also, what could
* be the difference between the rendering of these two types of stars?
* IDEA: Calculate the distance of each star to the "viewer" and render the
* star accordingly to this distance. Same remarks as for previous
* ideas can be pointed out. This would even lead to reget the old stars
* drawing code, that has been replaced by the XFillArcs. On another
* hand, this would allow particular stars (own color, shape...), as
* far as they would be individually drawn. One should be careful to
* draw them according to their distance, that is not drawing a far
* star after a close one.
*/
#ifdef STANDALONE
# define DEFAULTS "*count: 100 \n" \
"*size: 15 \n" \
"*delay: 20000 \n" \
"*ncolors: 64 \n" \
"*use3d: True \n" \
"*delta3d: 1.5 \n" \
"*right3d: red \n" \
"*left3d: blue \n" \
"*both3d: magenta \n" \
"*none3d: black \n" \
"*fpsSolid: true \n" \
"*ignoreRotation: True \n"
# define SMOOTH_COLORS
# define release_bouboule 0
# define bouboule_handle_event 0
# include "xlockmore.h" /* from the xscreensaver distribution */
#else /* !STANDALONE */
# include "xlock.h" /* from the xlockmore distribution */
# define ENTRYPOINT /**/
#endif /* !STANDALONE */
ENTRYPOINT ModeSpecOpt bouboule_opts = {
0, NULL, 0, NULL, NULL };
#define USEOLDXARCS 1 /* If 1, we use old xarcs list for erasing.
* else we just roughly erase the window.
* This mainly depends on the number of stars,
* because when they are many, it is faster to
* erase the whole window than to erase each star
*/
#if HAVE_GETTIMEOFDAY
#define ADAPT_ERASE 1 /* If 1, then we try ADAPT_CHECKS black XFillArcs,
* and after, ADAPT_CHECKS XFillRectangle.
* We check which method seems better, knowing that
* XFillArcs is generally visually better. So we
* consider that XFillArcs is still better if its time
* is about XFillRectangle * ADAPT_ARC_PREFERED
* We need gettimeofday
* for this... Does it exist on other systems ? Do we
* have to use another function for others ?
* This value overrides USEOLDXARCS.
*/
#ifdef USE_XVMSUTILS
# if 0
# include "../xvmsutils/unix_time.h"
# else
# include <X11/unix_time.h>
# endif
#endif
#include <sys/time.h>
#define ADAPT_CHECKS 50
#define ADAPT_ARC_PREFERED 150 /* Maybe the value that is the most important
* for adapting to a system */
#endif
#define dtor(x) (((x) * M_PI) / 180.0) /* Degrees to radians */
#define MINSTARS 1
#define MINSIZE 1
/* jwz: I think slower color changes look better */
#define COLOR_CHANGES 50 /* How often we change colors (1 = always)
* This value should be tuned accordingly to
* the number of stars */
#define MAX_SIZEX_SIZEY 2. /* This controls whether the sphere can be very
* very large and have a small height (or the
* opposite) or no. */
#define THETACANRAND 80 /* percentage of changes for the speed of
* change of the 3 theta values */
#define SIZECANRAND 80 /* percentage of changes for the speed of
* change of the sizex and sizey values */
#define POSCANRAND 80 /* percentage of changes for the speed of
* change of the x and y values */
/* Note that these XXXCANRAND values can be 0, that is no rand acceleration *
variation. */
#define VARRANDALPHA (NRAND((int) (M_PI * 1000.0))/1000.0)
#define VARRANDSTEP (M_PI/(NRAND(100)+100.0))
#define VARRANDMIN (-70.0)
#define VARRANDMAX 70.0
#define MINZVAL 100 /* stars can come this close */
#define SCREENZ 2000 /* this is where the screen is */
#define MAXZVAL 10000 /* stars can go this far away */
#define GETZDIFF(z) ((MI_DELTA3D(mi))*20.0*(1.0-(SCREENZ)/(z+1000)))
#define MAXDIFF MAX(-GETZDIFF(MINZVAL),GETZDIFF(MAXZVAL))
/* These values are the variation parameters of the acceleration variation *
of the SinVariables that are randomized. */
/******************************/
typedef struct SinVariableStruct
/******************************/
{
double alpha; /*
* Alpha is the current state of the sinvariable
* alpha should be initialized to a value between
* 0.0 and 2 * M_PI
*/
double step; /*
* Speed of evolution of alpha. It should be a reasonable
* fraction of 2 * M_PI. This value directly influence
* the variable speed of variation.
*/
double minimum; /* Minimum value for the variable */
double maximum; /* Maximum value for the variable */
double value; /* Current value */
int mayrand; /* Flag for knowing whether some randomization can be
* applied to the variable */
struct SinVariableStruct *varrand; /* Evolving Variable: the variation of
* alpha */
} SinVariable;
/***********************/
typedef struct StarStruct
/***********************/
{
double x, y, z; /* Position of the star */
short size; /* Try to guess */
} Star;
/****************************/
typedef struct StarFieldStruct
/****************************/
{
short width, height; /* width and height of the starfield window */
short max_star_size; /* Maximum radius for stars. stars radius will
* vary from 1 to MAX_STAR_SIZE */
SinVariable x; /* Evolving variables: */
SinVariable y; /* Center of the field on the screen */
SinVariable z;
SinVariable sizex; /* Evolving variable: half width of the field */
SinVariable sizey; /* Evolving variable: half height of the field */
SinVariable thetax; /* Evolving Variables: */
SinVariable thetay; /* rotation angles of the starfield */
SinVariable thetaz; /* around x, y and z local axis */
Star *star; /* List of stars */
XArc *xarc; /* Current List of arcs */
XArc *xarcleft; /* additional list for the left arcs */
#if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
XArc *oldxarc; /* Old list of arcs */
XArc *oldxarcleft;
#endif
unsigned long color; /* Current color of the starfield */
int colorp; /* Pointer to color of the starfield */
int NbStars; /* Number of stars */
short colorchange; /* Counter for the color change */
#if (ADAPT_ERASE == 1)
short hasbeenchecked;
long rect_time;
long xarc_time;
#endif
} StarField;
static StarField *starfield = NULL;
/*********/
static void
sinvary(SinVariable * v)
/*********/
{
v->value = v->minimum +
(v->maximum - v->minimum) * (sin(v->alpha) + 1.0) / 2.0;
if (v->mayrand == 0)
v->alpha += v->step;
else {
int vaval = NRAND(100);
if (vaval <= v->mayrand)
sinvary(v->varrand);
v->alpha += (100.0 + (v->varrand->value)) * v->step / 100.0;
}
if (v->alpha > 2 * M_PI)
v->alpha -= 2 * M_PI;
}
/*************************************************/
static void
sininit(SinVariable * v,
double alpha, double step, double minimum, double maximum,
short int mayrand)
{
v->alpha = alpha;
v->step = step;
v->minimum = minimum;
v->maximum = maximum;
v->mayrand = mayrand;
if (mayrand != 0) {
if (v->varrand == NULL)
v->varrand = (SinVariable *) calloc(1, sizeof (SinVariable));
sininit(v->varrand,
VARRANDALPHA,
VARRANDSTEP,
VARRANDMIN,
VARRANDMAX,
0);
sinvary(v->varrand);
}
/* We calculate the values at least once for initialization */
sinvary(v);
}
static void
sinfree(SinVariable * point)
{
SinVariable *temp, *next;
next = point->varrand;
while (next) {
temp = next;
next = temp->varrand;
(void) free((void *) temp);
}
}
/***************/
ENTRYPOINT void
init_bouboule(ModeInfo * mi)
/***************/
/*-
* The stars init part was first inspirated from the net3d game starfield
* code. But net3d starfield is not really 3d starfield, and I needed real 3d,
* so only remains the net3d starfield initialization main idea, that is
* the stars distribution on a sphere (theta and omega computing)
*/
{
StarField *sp;
int size = MI_SIZE(mi);
int i;
double theta, omega;
if (MI_WIDTH(mi) > 2560) size *= 2; /* Retina displays */
MI_INIT (mi, starfield);
sp = &starfield[MI_SCREEN(mi)];
sp->width = MI_WIN_WIDTH(mi);
sp->height = MI_WIN_HEIGHT(mi);
/* use the right `black' pixel values: */
if (MI_WIN_IS_INSTALL(mi) && MI_WIN_IS_USE3D(mi)) {
XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_NONE_COLOR(mi));
XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
0, 0, sp->width, sp->height);
} else
XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
if (size < -MINSIZE)
sp->max_star_size = NRAND(-size - MINSIZE + 1) + MINSIZE;
else if (size < MINSIZE)
sp->max_star_size = MINSIZE;
else
sp->max_star_size = size;
sp->NbStars = MI_BATCHCOUNT(mi);
if (sp->NbStars < -MINSTARS) {
if (sp->star) {
(void) free((void *) sp->star);
sp->star = NULL;
}
if (sp->xarc) {
(void) free((void *) sp->xarc);
sp->xarc = NULL;
}
if (sp->xarcleft) {
(void) free((void *) sp->xarcleft);
sp->xarcleft = NULL;
}
#if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
if (sp->oldxarc) {
(void) free((void *) sp->oldxarc);
sp->oldxarc = NULL;
}
if (sp->oldxarcleft) {
(void) free((void *) sp->oldxarcleft);
sp->oldxarcleft = NULL;
}
#endif
sp->NbStars = NRAND(-sp->NbStars - MINSTARS + 1) + MINSTARS;
} else if (sp->NbStars < MINSTARS)
sp->NbStars = MINSTARS;
/* We get memory for lists of objects */
if (sp->star == NULL)
sp->star = (Star *) malloc(sp->NbStars * sizeof (Star));
if (sp->xarc == NULL)
sp->xarc = (XArc *) malloc(sp->NbStars * sizeof (XArc));
if (MI_WIN_IS_USE3D(mi) && sp->xarcleft == NULL)
sp->xarcleft = (XArc *) malloc(sp->NbStars * sizeof (XArc));
#if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
if (sp->oldxarc == NULL)
sp->oldxarc = (XArc *) malloc(sp->NbStars * sizeof (XArc));
if (MI_WIN_IS_USE3D(mi) && sp->oldxarcleft == NULL)
sp->oldxarcleft = (XArc *) malloc(sp->NbStars * sizeof (XArc));
#endif
{
/* We initialize evolving variables */
sininit(&sp->x,
NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
((double) sp->width) / 4.0,
3.0 * ((double) sp->width) / 4.0,
POSCANRAND);
sininit(&sp->y,
NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
((double) sp->height) / 4.0,
3.0 * ((double) sp->height) / 4.0,
POSCANRAND);
/* for z, we have to ensure that the bouboule does not get behind */
/* the eyes of the viewer. His/Her eyes are at 0. Because the */
/* bouboule uses the x-radius for the z-radius, too, we have to */
/* use the x-values. */
sininit(&sp->z,
NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
((double) sp->width / 2.0 + MINZVAL),
((double) sp->width / 2.0 + MAXZVAL),
POSCANRAND);
sininit(&sp->sizex,
NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
MIN(((double) sp->width) - sp->x.value,
sp->x.value) / 5.0,
MIN(((double) sp->width) - sp->x.value,
sp->x.value),
SIZECANRAND);
sininit(&sp->sizey,
NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
MAX(sp->sizex.value / MAX_SIZEX_SIZEY,
sp->sizey.maximum / 5.0),
MIN(sp->sizex.value * MAX_SIZEX_SIZEY,
MIN(((double) sp->height) -
sp->y.value,
sp->y.value)),
SIZECANRAND);
sininit(&sp->thetax,
NRAND(3142) / 1000.0, M_PI / (NRAND(200) + 200.0),
-M_PI, M_PI,
THETACANRAND);
sininit(&sp->thetay,
NRAND(3142) / 1000.0, M_PI / (NRAND(200) + 200.0),
-M_PI, M_PI,
THETACANRAND);
sininit(&sp->thetaz,
NRAND(3142) / 1000.0, M_PI / (NRAND(400) + 400.0),
-M_PI, M_PI,
THETACANRAND);
}
for (i = 0; i < sp->NbStars; i++) {
Star *star;
XArc *arc = NULL, *arcleft = NULL;
#if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
XArc *oarc = NULL, *oarcleft = NULL;
#endif
star = &(sp->star[i]);
arc = &(sp->xarc[i]);
if (MI_WIN_IS_USE3D(mi))
arcleft = &(sp->xarcleft[i]);
#if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
oarc = &(sp->oldxarc[i]);
if (MI_WIN_IS_USE3D(mi))
oarcleft = &(sp->oldxarcleft[i]);
#endif
/* Elevation and bearing of the star */
theta = dtor((NRAND(1800)) / 10.0 - 90.0);
omega = dtor((NRAND(3600)) / 10.0 - 180.0);
/* Stars coordinates in a 3D space */
star->x = cos(theta) * sin(omega);
star->y = sin(omega) * sin(theta);
star->z = cos(omega);
/* We set the stars size */
star->size = NRAND(2 * sp->max_star_size);
if (star->size < sp->max_star_size)
star->size = 0;
else
star->size -= sp->max_star_size;
/* We set default values for the XArc lists elements */
arc->x = arc->y = 0;
if (MI_WIN_IS_USE3D(mi)) {
arcleft->x = arcleft->y = 0;
}
#if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
oarc->x = oarc->y = 0;
if (MI_WIN_IS_USE3D(mi)) {
oarcleft->x = oarcleft->y = 0;
}
#endif
arc->width = 2 + star->size;
arc->height = 2 + star->size;
if (MI_WIN_IS_USE3D(mi)) {
arcleft->width = 2 + star->size;
arcleft->height = 2 + star->size;
}
#if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
oarc->width = 2 + star->size;
oarc->height = 2 + star->size;
if (MI_WIN_IS_USE3D(mi)) {
oarcleft->width = 2 + star->size;
oarcleft->height = 2 + star->size;
}
#endif
arc->angle1 = 0;
arc->angle2 = 360 * 64;
if (MI_WIN_IS_USE3D(mi)) {
arcleft->angle1 = 0;
arcleft->angle2 = 360 * 64;
}
#if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
oarc->angle1 = 0;
oarc->angle2 = 360 * 64; /* ie. we draw whole disks:
* from 0 to 360 degrees */
if (MI_WIN_IS_USE3D(mi)) {
oarcleft->angle1 = 0;
oarcleft->angle2 = 360 * 64;
}
#endif
}
if (MI_NPIXELS(mi) > 2)
sp->colorp = NRAND(MI_NPIXELS(mi));
/* We set up the starfield color */
if (!MI_WIN_IS_USE3D(mi) && MI_NPIXELS(mi) > 2)
sp->color = MI_PIXEL(mi, sp->colorp);
else
sp->color = MI_WIN_WHITE_PIXEL(mi);
#if (ADAPT_ERASE == 1)
/* We initialize the adaptation code for screen erasing */
sp->hasbeenchecked = ADAPT_CHECKS * 2;
sp->rect_time = 0;
sp->xarc_time = 0;
#endif
}
/****************/
ENTRYPOINT void
draw_bouboule(ModeInfo * mi)
/****************/
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
StarField *sp = &starfield[MI_SCREEN(mi)];
int i, diff = 0;
double CX, CY, CZ, SX, SY, SZ;
Star *star;
XArc *arc = NULL, *arcleft = NULL;
#ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */
XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
#endif
#if (ADAPT_ERASE == 1)
struct timeval tv1;
struct timeval tv2;
#endif
#if ((USEOLDXARCS == 0) || (ADAPT_ERASE == 1))
short x_1, y_1, x_2, y_2;
/* bounding rectangle around the old starfield,
* for erasing with the smallest rectangle
* instead of filling the whole screen */
int maxdiff = 0; /* maximal distance between left and right */
/* star in 3d mode, otherwise 0 */
#endif
#if ((USEOLDXARCS == 0) || (ADAPT_ERASE == 1))
if (MI_WIN_IS_USE3D(mi)) {
maxdiff = (int) MAXDIFF;
}
x_1 = (int) sp->x.value - (int) sp->sizex.value -
sp->max_star_size - maxdiff;
y_1 = (int) sp->y.value - (int) sp->sizey.value -
sp->max_star_size;
x_2 = 2 * ((int) sp->sizex.value + sp->max_star_size + maxdiff);
y_2 = 2 * ((int) sp->sizey.value + sp->max_star_size);
#endif
/* We make variables vary. */
sinvary(&sp->thetax);
sinvary(&sp->thetay);
sinvary(&sp->thetaz);
sinvary(&sp->x);
sinvary(&sp->y);
if (MI_WIN_IS_USE3D(mi))
sinvary(&sp->z);
/* A little trick to prevent the bouboule from being
* bigger than the screen */
sp->sizex.maximum =
MIN(((double) sp->width) - sp->x.value,
sp->x.value);
sp->sizex.minimum = sp->sizex.maximum / 3.0;
/* Another trick to make the ball not too flat */
sp->sizey.minimum =
MAX(sp->sizex.value / MAX_SIZEX_SIZEY,
sp->sizey.maximum / 3.0);
sp->sizey.maximum =
MIN(sp->sizex.value * MAX_SIZEX_SIZEY,
MIN(((double) sp->height) - sp->y.value,
sp->y.value));
sinvary(&sp->sizex);
sinvary(&sp->sizey);
/*
* We calculate the rotation matrix values. We just make the
* rotation on the fly, without using a matrix.
* Star positions are recorded as unit vectors pointing in various
* directions. We just make them all rotate.
*/
CX = cos(sp->thetax.value);
SX = sin(sp->thetax.value);
CY = cos(sp->thetay.value);
SY = sin(sp->thetay.value);
CZ = cos(sp->thetaz.value);
SZ = sin(sp->thetaz.value);
for (i = 0; i < sp->NbStars; i++) {
star = &(sp->star[i]);
arc = &(sp->xarc[i]);
if (MI_WIN_IS_USE3D(mi)) {
arcleft = &(sp->xarcleft[i]);
/* to help the eyes, the starfield is always as wide as */
/* deep, so .sizex.value can be used. */
diff = (int) GETZDIFF(sp->sizex.value *
((SY * CX) * star->x + (SX) * star->y +
(CX * CY) * star->z) + sp->z.value);
}
arc->x = (short) ((sp->sizex.value *
((CY * CZ - SX * SY * SZ) * star->x +
(-CX * SZ) * star->y +
(SY * CZ + SZ * SX * CY) * star->z) +
sp->x.value));
arc->y = (short) ((sp->sizey.value *
((CY * SZ + SX * SY * CZ) * star->x +
(CX * CZ) * star->y +
(SY * SZ - SX * CY * CZ) * star->z) +
sp->y.value));
if (MI_WIN_IS_USE3D(mi)) {
arcleft->x = (short) ((sp->sizex.value *
((CY * CZ - SX * SY * SZ) * star->x +
(-CX * SZ) * star->y +
(SY * CZ + SZ * SX * CY) * star->z) +
sp->x.value));
arcleft->y = (short) ((sp->sizey.value *
((CY * SZ + SX * SY * CZ) * star->x +
(CX * CZ) * star->y +
(SY * SZ - SX * CY * CZ) * star->z) +
sp->y.value));
arc->x += diff;
arcleft->x -= diff;
}
if (star->size != 0) {
arc->x -= star->size;
arc->y -= star->size;
if (MI_WIN_IS_USE3D(mi)) {
arcleft->x -= star->size;
arcleft->y -= star->size;
}
}
}
/* First, we erase the previous starfield */
if (MI_WIN_IS_INSTALL(mi) && MI_WIN_IS_USE3D(mi))
XSetForeground(display, gc, MI_NONE_COLOR(mi));
else
XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
#if (ADAPT_ERASE == 1)
if (sp->hasbeenchecked == 0) {
/* We just calculate which method is the faster and eventually free
* the oldxarc list */
if (sp->xarc_time >
ADAPT_ARC_PREFERED * sp->rect_time) {
sp->hasbeenchecked = -2; /* XFillRectangle mode */
(void) free((void *) sp->oldxarc);
sp->oldxarc = NULL;
if (MI_WIN_IS_USE3D(mi)) {
(void) free((void *) sp->oldxarcleft);
sp->oldxarcleft = NULL;
}
} else {
sp->hasbeenchecked = -1; /* XFillArcs mode */
}
}
if (sp->hasbeenchecked == -2) {
/* Erasing is done with XFillRectangle */
XFillRectangle(display, window, gc,
x_1, y_1, x_2, y_2);
} else if (sp->hasbeenchecked == -1) {
/* Erasing is done with XFillArcs */
XFillArcs(display, window, gc,
sp->oldxarc, sp->NbStars);
if (MI_WIN_IS_USE3D(mi))
XFillArcs(display, window, gc,
sp->oldxarcleft, sp->NbStars);
} else {
long usec;
if (sp->hasbeenchecked > ADAPT_CHECKS) {
#ifdef GETTIMEOFDAY_TWO_ARGS
(void) gettimeofday(&tv1, NULL);
#else
(void) gettimeofday(&tv1);
#endif
XFillRectangle(display, window, gc,
x_1, y_1, x_2, y_2);
#ifdef GETTIMEOFDAY_TWO_ARGS
(void) gettimeofday(&tv2, NULL);
#else
(void) gettimeofday(&tv2);
#endif
usec = (tv2.tv_sec - tv1.tv_sec) * 1000000;
if (usec + tv2.tv_usec - tv1.tv_usec > 0) {
sp->rect_time += usec + tv2.tv_usec - tv1.tv_usec;
sp->hasbeenchecked--;
}
} else {
#ifdef GETTIMEOFDAY_TWO_ARGS
(void) gettimeofday(&tv1, NULL);
#else
(void) gettimeofday(&tv1);
#endif
XFillArcs(display, window, gc,
sp->oldxarc, sp->NbStars);
if (MI_WIN_IS_USE3D(mi))
XFillArcs(display, window, gc,
sp->oldxarcleft, sp->NbStars);
#ifdef GETTIMEOFDAY_TWO_ARGS
(void) gettimeofday(&tv2, NULL);
#else
(void) gettimeofday(&tv2);
#endif
usec = (tv2.tv_sec - tv1.tv_sec) * 1000000;
if (usec + tv2.tv_usec - tv1.tv_usec > 0) {
sp->xarc_time += usec + tv2.tv_usec - tv1.tv_usec;
sp->hasbeenchecked--;
}
}
}
#else
#if (USEOLDXARCS == 1)
XFillArcs(display, window, gc,
sp->oldxarc, sp->NbStars);
if (MI_WIN_IS_USE3D(mi))
XFillArcs(display, window, gc,
sp->oldxarcleft, sp->NbStars);
#else
XFillRectangle(display, window, gc,
x_1, y_1, x_2, y_2);
#endif
#endif
/* Then we draw the new one */
if (MI_WIN_IS_USE3D(mi)) {
if (MI_WIN_IS_INSTALL(mi))
XSetFunction(display, gc, GXor);
XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
XFillArcs(display, window, gc, sp->xarc, sp->NbStars);
XSetForeground(display, gc, MI_LEFT_COLOR(mi));
XFillArcs(display, window, gc, sp->xarcleft, sp->NbStars);
if (MI_WIN_IS_INSTALL(mi))
XSetFunction(display, gc, GXcopy);
} else {
XSetForeground(display, gc, sp->color);
XFillArcs(display, window, gc, sp->xarc, sp->NbStars);
}
#if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
#if (ADAPT_ERASE == 1)
if (sp->hasbeenchecked >= -1) {
arc = sp->xarc;
sp->xarc = sp->oldxarc;
sp->oldxarc = arc;
if (MI_WIN_IS_USE3D(mi)) {
arcleft = sp->xarcleft;
sp->xarcleft = sp->oldxarcleft;
sp->oldxarcleft = arcleft;
}
}
#else
arc = sp->xarc;
sp->xarc = sp->oldxarc;
sp->oldxarc = arc;
if (MI_WIN_IS_USE3D(mi)) {
arcleft = sp->xarcleft;
sp->xarcleft = sp->oldxarcleft;
sp->oldxarcleft = arcleft;
}
#endif
#endif
/* We set up the color for the next drawing */
if (!MI_WIN_IS_USE3D(mi) && MI_NPIXELS(mi) > 2 &&
(++sp->colorchange >= COLOR_CHANGES)) {
sp->colorchange = 0;
if (++sp->colorp >= MI_NPIXELS(mi))
sp->colorp = 0;
sp->color = MI_PIXEL(mi, sp->colorp);
}
}
ENTRYPOINT void
free_bouboule(ModeInfo * mi)
{
StarField *sp = &starfield[MI_SCREEN(mi)];
if (sp->star)
(void) free((void *) sp->star);
if (sp->xarc)
(void) free((void *) sp->xarc);
if (sp->xarcleft)
(void) free((void *) sp->xarcleft);
#if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
if (sp->oldxarc)
(void) free((void *) sp->oldxarc);
if (sp->oldxarcleft)
(void) free((void *) sp->oldxarcleft);
#endif
sinfree(&(sp->x));
sinfree(&(sp->y));
sinfree(&(sp->z));
sinfree(&(sp->sizex));
sinfree(&(sp->sizey));
sinfree(&(sp->thetax));
sinfree(&(sp->thetay));
sinfree(&(sp->thetaz));
}
ENTRYPOINT void
reshape_bouboule(ModeInfo * mi, int width, int height)
{
StarField *sp = &starfield[MI_SCREEN(mi)];
sp->width = width;
sp->height = height;
sininit(&sp->x,
sp->x.alpha, sp->x.step,
((double) sp->width) / 4.0,
3.0 * ((double) sp->width) / 4.0,
POSCANRAND);
sininit(&sp->y,
sp->y.alpha, sp->y.step,
((double) sp->height) / 4.0,
3.0 * ((double) sp->height) / 4.0,
POSCANRAND);
}
#ifndef STANDALONE
ENTRYPOINT void
refresh_bouboule(ModeInfo * mi)
{
/* Do nothing, it will refresh by itself */
}
#endif
XSCREENSAVER_MODULE ("Bouboule", bouboule)