1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* bounce --- bouncing footballs */
3 
4 #if 0
5 static const char sccsid[] = "@(#)bounce.c	5.00 2000/11/01 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Copyright (c) 1988 by Sun Microsystems
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * Revision History:
25  * 01-Nov-2000: Allocation checks
26  * 10-May-1997: Compatible with xscreensaver
27  * 01-Apr-1997: Curtis Larsen <larsen@rtp3.med.utah.edu>
28  *              The modification is only for the inroot option.  It causes
29  *              the balls to see  children of the root window and bounce
30  *              off of the sides of them.  New windows are only recognized
31  *              after every init_bounce, because fvwm did not like xlock
32  *              setting SubstructureNotifyMask on root.  I did not fix the
33  *              initial placement of balls yet, so they can start out
34  *              underneath windows.
35  * 18-Sep-1995: tinkered with it to look like bat.c .
36  * 15-Jul-1994: cleaned up in time for the final match.
37  * 04-Apr-1994: spinning multiple ball version
38  *              (I got a lot of help from with the physics of ball to ball
39  *              collision looking at the source of xpool from Ismail ARIT
40  *              <iarit@tara.mines.colorado.edu>
41  * 22-Mar-1994: got rid of flashing problem by only erasing parts of the
42  *              image that will not be covered up by the next image.
43  * 02-Sep-1993: xlock version David Bagley <bagleyd AT verizon.net>
44  * 1986: Sun Microsystems
45  */
46 
47 /*-
48  * original copyright
49  * **************************************************************************
50  * Copyright 1988 by Sun Microsystems, Inc. Mountain View, CA.
51  *
52  * All Rights Reserved
53  *
54  * Permission to use, copy, modify, and distribute this software and its
55  * documentation for any purpose and without fee is hereby granted, provided
56  * that the above copyright notice appear in all copies and that both that
57  * copyright notice and this permission notice appear in supporting
58  * documentation, and that the names of Sun or MIT not be used in advertising
59  * or publicity pertaining to distribution of the software without specific
60  * prior written permission. Sun and M.I.T. make no representations about the
61  * suitability of this software for any purpose. It is provided "as is"
62  * without any express or implied warranty.
63  *
64  * SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
65  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
66  * IN NO EVENT SHALL SUN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
67  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
68  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
69  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
70  * SOFTWARE.
71  * ***************************************************************************
72  */
73 
74 /*-
75  * Open for improvement:
76  *  include different balls (size and mass)
77  *    how about be real crazy and put in an American/Australian football?
78  *  should only have 1 bitmap for ball, the others should be generated
79  *    as 90 degree rotations.
80  *  multiscreen interaction
81  */
82 
83 #ifdef STANDALONE
84 # define MODE_bounce
85 # define DEFAULTS	"*delay: 5000 \n" \
86 			"*count: -10 \n" \
87 			"*size: 0 \n" \
88 			"*ncolors: 200 \n" \
89 
90 # define free_bounce 0
91 # define reshape_bounce 0
92 # define bounce_handle_event 0
93 #include "xlockmore.h"		/* in xscreensaver distribution */
94 #else /* STANDALONE */
95 # include "xlock.h"		/* in xlockmore distribution */
96 # include "color.h"
97 # include "iostuff.h"
98 #endif /* STANDALONE */
99 
100 #ifdef MODE_bounce
101 
102 ENTRYPOINT ModeSpecOpt bounce_opts =
103 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
104 
105 #ifdef USE_MODULES
106 const ModStruct bounce_description =
107 {"bounce", "init_bounce", "draw_bounce", "release_bounce",
108  "refresh_bounce", "init_bounce", (char *) NULL, &bounce_opts,
109  5000, -10, 1, 0, 64, 1.0, "",
110  "Shows bouncing footballs", 0, NULL};
111 
112 #endif
113 
114 #include "bitmaps/bounce-0.xbm"
115 #include "bitmaps/bounce-1.xbm"
116 #include "bitmaps/bounce-2.xbm"
117 #include "bitmaps/bounce-3.xbm"
118 #include "bitmaps/bounce-mask.xbm"
119 
120 #define BALLBITS(n,w,h)\
121   if ((bp->pixmaps[bp->init_orients]=\
122   XCreateBitmapFromData(display,window,(char *)n,w,h))==None){\
123   free_bounce_screen(display,bp); return False;} else {bp->init_orients++;}
124 
125 #ifndef STANDALONE
126 /* aliases for vars defined in the bitmap file */
127 #define BOUNCE_WIDTH     image_width
128 #define BOUNCE_HEIGHT    image_height
129 #define BOUNCE_BITS      image_bits
130 
131 #include "bounce.xbm"
132 
133 #ifdef HAVE_XPM
134 #define BOUNCE_NAME      image_name
135 #include "bounce.xpm"
136 #define DEFAULT_XPM 1
137 #endif
138 #endif
139 
140 #define MAX_STRENGTH 24
141 #define FRICTION 24
142 #define PENETRATION 0.3
143 #define SLIPAGE 4
144 #define TIME 32
145 #define MINBALLS 1
146 #define MINSIZE 1
147 #define MINGRIDSIZE 5
148 
149 #define ORIENTS 4
150 #define ORIENTCYCLE 16
151 #define CCW 1
152 #define CW (ORIENTS-1)
153 #define DIR(x)	(((x)>=0)?CCW:CW)
154 #define SIGN(x)	(((x)>=0)?1:-1)
155 
156 typedef struct {
157 	int         x, y;
158 	int         width, height;
159 } ballwindow;
160 
161 typedef struct {
162 	int         x, y, xlast, ylast, orientlast;
163 	int         spincount, spindelay, spindir, orient;
164 	int         vx, vy, vang;
165 	unsigned long color;
166 } ballstruct;
167 
168 typedef struct {
169 	int         width, height;
170 	int         nballs;
171 	int         xs, ys, avgsize;
172 	int         restartnum;
173 	int         pixelmode;
174 	ballstruct *balls;
175 	int         graphics_format;
176 	GC          backGC;
177 	XImage     *logo;
178 	unsigned long black;
179 	Colormap    cmap;
180 	GC          stippledGC;
181 	Pixmap      pixmaps[ORIENTS + 1];
182 	int         init_orients;
183 	int         nwindow;
184 	ballwindow *windows;
185 } bouncestruct;
186 
187 static bouncestruct *bounces = (bouncestruct *) NULL;
188 
189 static void
checkCollision(bouncestruct * bp,int aball)190 checkCollision(bouncestruct * bp, int aball)
191 {
192 	int         i, amount, spin, d, size;
193 	double      x, y;
194 
195 	for (i = 0; i < bp->nballs; i++) {
196 		if (i != aball) {
197 			x = (double) (bp->balls[i].x - bp->balls[aball].x);
198 			y = (double) (bp->balls[i].y - bp->balls[aball].y);
199 			d = (int) sqrt(x * x + y * y);
200 			size = bp->avgsize;
201 			if (d > 0 && d < size) {
202 				amount = size - d;
203 				if (amount > PENETRATION * size)
204 					amount = (int) (PENETRATION * size);
205 				bp->balls[i].vx += (int) ((double) amount * x / d);
206 				bp->balls[i].vy += (int) ((double) amount * y / d);
207 				bp->balls[i].vx -= bp->balls[i].vx / FRICTION;
208 				bp->balls[i].vy -= bp->balls[i].vy / FRICTION;
209 				bp->balls[aball].vx -= (int) ((double) amount * x / d);
210 				bp->balls[aball].vy -= (int) ((double) amount * y / d);
211 				bp->balls[aball].vx -= bp->balls[aball].vx / FRICTION;
212 				bp->balls[aball].vy -= bp->balls[aball].vy / FRICTION;
213 				spin = (bp->balls[i].vang - bp->balls[aball].vang) /
214 					(2 * size * SLIPAGE);
215 				bp->balls[i].vang -= spin;
216 				bp->balls[aball].vang += spin;
217 				bp->balls[i].spindir = DIR(bp->balls[i].vang);
218 				bp->balls[aball].spindir = DIR(bp->balls[aball].vang);
219 				if (!bp->balls[i].vang) {
220 					bp->balls[i].spindelay = 1;
221 					bp->balls[i].spindir = 0;
222 				} else
223 					bp->balls[i].spindelay = (int) ((double) M_PI *
224 							bp->avgsize / (ABS(bp->balls[i].vang))) + 1;
225 				if (!bp->balls[aball].vang) {
226 					bp->balls[aball].spindelay = 1;
227 					bp->balls[aball].spindir = 0;
228 				} else
229 					bp->balls[aball].spindelay = (int) ((double) M_PI *
230 							bp->avgsize / (ABS(bp->balls[aball].vang))) + 1;
231 				return;
232 			}
233 		}
234 	}
235 }
236 
237 static void
drawball(ModeInfo * mi,ballstruct * ball)238 drawball(ModeInfo * mi, ballstruct * ball)
239 {
240 	Display    *display = MI_DISPLAY(mi);
241 	Window      window = MI_WINDOW(mi);
242 	bouncestruct *bp = &bounces[MI_SCREEN(mi)];
243 
244 	if (ball->xlast != -1) {
245 		if (bp->logo && !bp->pixelmode) {
246 			XSetForeground(display, bp->backGC, bp->black);
247 #ifdef FLASH
248 			XFillRectangle(display, window, bp->backGC,
249 				ball->xlast, ball->ylast, bp->xs, bp->ys);
250 #else
251 			ERASE_IMAGE(display, window, bp->backGC,
252 				ball->x, ball->y, ball->xlast, ball->ylast,
253 				bp->xs, bp->ys);
254 #endif
255 		} else {
256 			XSetForeground(display, bp->stippledGC, MI_BLACK_PIXEL(mi));
257 			XSetStipple(display, bp->stippledGC,
258 				bp->pixmaps[(bp->pixelmode) ? 0 : ORIENTS]);
259 			XSetFillStyle(display, bp->stippledGC, FillStippled);
260 			XSetTSOrigin(display, bp->stippledGC,
261 				ball->xlast, ball->ylast);
262 			XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi));
263 #ifdef FLASH
264 			XFillRectangle(display, window, MI_GC(mi),
265 				ball->xlast, ball->ylast, bp->xs, bp->ys);
266 #else
267 			ERASE_IMAGE(display, window, MI_GC(mi),
268 				ball->x, ball->y, ball->xlast, ball->ylast,
269 				bp->xs, bp->ys);
270 #endif
271 		}
272 	}
273 	if (bp->logo && !bp->pixelmode) {
274 		XSetForeground(display, bp->backGC, ball->color);
275 		if (bp->logo)
276 			(void) XPutImage(display, window, bp->backGC, bp->logo,
277 				0, 0, ball->x, ball->y, bp->xs, bp->ys);
278 	} else {
279 		XSetTSOrigin(display, bp->stippledGC, ball->x, ball->y);
280 		XSetForeground(display, bp->stippledGC, ball->color);
281 		XSetStipple(display, bp->stippledGC,
282 			bp->pixmaps[(bp->pixelmode) ? 0 : ball->orient]);
283 #ifdef FLASH
284 		XSetFillStyle(display, bp->stippledGC, FillStippled);
285 #else
286 		XSetFillStyle(display, bp->stippledGC, FillOpaqueStippled);
287 #endif
288 		XFillRectangle(display, window, bp->stippledGC,
289 			ball->x, ball->y, bp->xs, bp->ys);
290 	}
291 	XFlush(display);
292 }
293 
294 static void
spinball(ballstruct * ball,int dir,int * vel,int avgsize)295 spinball(ballstruct * ball, int dir, int *vel, int avgsize)
296 {
297 	*vel -= (int) ((*vel + SIGN(*vel * dir) *
298 		ball->spindelay * ORIENTCYCLE / (M_PI * avgsize)) / SLIPAGE);
299 	if (*vel) {
300 		ball->spindir = DIR(*vel * dir);
301 		ball->vang = *vel * ORIENTCYCLE;
302 		ball->spindelay = (int) ((double) M_PI * avgsize / (ABS(ball->vang))) + 1;
303 	} else
304 		ball->spindir = 0;
305 }
306 
307 #define BETWEEN(x, xmin, xmax) (((x) >= (xmin)) && ((x) <= (xmax)))
308 static void
hit_left_wall(ModeInfo * mi,ballstruct * ball,int ytop,int height,int x,int side)309 hit_left_wall(ModeInfo * mi, ballstruct * ball,
310 		int ytop, int height, int x, int side)
311 {
312 	bouncestruct *bp = &bounces[MI_SCREEN(mi)];
313 
314 	if ((ball->x <= x) && ((ball->xlast >= x) || side) &&
315 			BETWEEN(ball->y, ytop - bp->ys, ytop + height)) {
316 		/* Bounce off the wall to the left of the ball */
317 
318 		ball->x = 2 * x - ball->x;
319 		ball->vx = (ball->vx - (ball->vx * FRICTION)) / FRICTION;
320 		spinball(ball, -1, &ball->vy, bp->avgsize);
321 	}
322 }
323 
324 static void
hit_right_wall(ModeInfo * mi,ballstruct * ball,int ytop,int height,int x,int side)325 hit_right_wall(ModeInfo * mi, ballstruct * ball,
326 		int ytop, int height, int x, int side)
327 {
328 	bouncestruct *bp = &bounces[MI_SCREEN(mi)];
329 
330 	x -= bp->xs;		/* account for ball width */
331 
332 	if ((ball->x >= x) && ((ball->xlast <= x) || side) &&
333 			BETWEEN(ball->y, ytop - bp->ys, ytop + height)) {
334 		/* Bounce off the wall to the right of the ball */
335 
336 		ball->x = 2 * x - ball->x;
337 		ball->vx = (ball->vx - (ball->vx * FRICTION)) / FRICTION;
338 		spinball(ball, 1, &ball->vy, bp->avgsize);
339 	}
340 }
341 
342 static void
hit_top_wall(bouncestruct * bp,ballstruct * ball,int xleft,int width,int y,int side)343 hit_top_wall(bouncestruct * bp, ballstruct * ball, int xleft, int width, int y, int side)
344 {
345 	if ((ball->y <= y) && ((ball->ylast >= y) || side) &&
346 			BETWEEN(ball->x, xleft - bp->xs, xleft + width)) {
347 		/* Bounce off the wall to the top of the ball */
348 		ball->y = 2 * y - ball->y;
349 		/* HACK to make it look better for iconified mode */
350 		if (y == 0) {
351 			ball->vy = 0;
352 		} else {
353 			ball->vy = (ball->vy - (FRICTION * ball->vy)) / FRICTION;
354 		}
355 		spinball(ball, 1, &ball->vx, bp->avgsize);
356 	}
357 }
358 
359 
360 static void
hit_bottom_wall(bouncestruct * bp,ballstruct * ball,int xleft,int width,int y,int side)361 hit_bottom_wall(bouncestruct * bp, ballstruct * ball, int xleft, int width, int y, int side)
362 {
363 	y -= bp->ys;		/* account for ball height */
364 
365 	if ((ball->y >= y) && ((ball->ylast <= y) || side) &&
366 			BETWEEN(ball->x, xleft - bp->xs, xleft + width)) {
367 		/* Bounce off the wall to the bottom of the ball */
368 		ball->y = y;
369 		ball->vy = (ball->vy - (FRICTION * ball->vy)) / FRICTION;
370 		spinball(ball, -1, &ball->vx, bp->avgsize);
371 	}
372 }
373 
374 static void
moveball(ModeInfo * mi,ballstruct * ball)375 moveball(ModeInfo * mi, ballstruct * ball)
376 {
377 	bouncestruct *bp = &bounces[MI_SCREEN(mi)];
378 	int         i;
379 	ballwindow *win;
380 
381 	ball->xlast = ball->x;
382 	ball->ylast = ball->y;
383 	ball->orientlast = ball->orient;
384 	ball->x += ball->vx;
385 
386 	for (i = 0; i < bp->nwindow; i++) {
387 		win = &bp->windows[i];
388 
389 		hit_left_wall(mi, ball, win->y, win->height, win->x + win->width, 0);
390 		hit_right_wall(mi, ball, win->y, win->height, win->x, 0);
391 	}
392 	hit_right_wall(mi, ball, 0, bp->height, bp->width, 1);
393 	hit_left_wall(mi, ball, 0, bp->height, 0, 1);
394 
395 	ball->vy++;
396 	ball->y += ball->vy;
397 
398 	for (i = 0; i < bp->nwindow; i++) {
399 		win = &bp->windows[i];
400 
401 		hit_top_wall(bp, ball, win->x, win->width, win->y + win->height, 0);
402 		hit_bottom_wall(bp, ball, win->x, win->width, win->y, 0);
403 	}
404 	hit_top_wall(bp, ball, 0, bp->width, 0, 1);
405 	hit_bottom_wall(bp, ball, 0, bp->width, bp->height, 1);
406 
407 	if (ball->spindir) {
408 		ball->spincount--;
409 		if (!ball->spincount) {
410 			ball->orient = (ball->spindir + ball->orient) % ORIENTS;
411 			ball->spincount = ball->spindelay;
412 		}
413 	}
414 }
415 
416 static int
collide(bouncestruct * bp,int aball)417 collide(bouncestruct * bp, int aball)
418 {
419 	int         i, d, x, y;
420 
421 	for (i = 0; i < aball; i++) {
422 		x = (bp->balls[i].x - bp->balls[aball].x);
423 		y = (bp->balls[i].y - bp->balls[aball].y);
424 		d = (int) sqrt((double) (x * x + y * y));
425 		if (d < bp->avgsize)
426 			return i;
427 	}
428 	return i;
429 }
430 
431 static void
bounce_windows(ModeInfo * mi,bouncestruct * bp)432 bounce_windows(ModeInfo * mi, bouncestruct * bp)
433 {
434 	Window      root, parent, *children;
435 	unsigned int nchildren;
436 	int         i;
437 	int         n;
438 
439 	if (!MI_IS_INROOT(mi)) {
440 		bp->nwindow = 0;
441 		return;
442 	}
443 	if (XQueryTree(MI_DISPLAY(mi), MI_WINDOW(mi),
444 			&root, &parent, &children, &nchildren) == 0) {	/* failure */
445 		bp->nwindow = 0;
446 		return;
447 	}
448 	bp->nwindow = nchildren;
449 	if (bp->windows != NULL)
450 		free(bp->windows);
451 	if ((bp->windows = (ballwindow *) malloc(bp->nwindow *
452 			sizeof (ballwindow))) == NULL) {
453 		XFree((caddr_t) children);
454 		bp->nwindow = 0;
455 		return;
456 	}
457 	for (n = 0, i = 0; i < bp->nwindow; i++) {
458 		XWindowAttributes att;
459 
460 /*-
461    May give
462    X Error of failed request: BadWindow (invalid Window parameter)
463    Major opcode of failed request:  3 (X_GetWindowAttributes)
464  */
465 		if (XGetWindowAttributes(MI_DISPLAY(mi), children[i], &att) == 0) {	/* failure */
466 			XFree((caddr_t) children);
467 			bp->nwindow = 0;
468 			free(bp->windows);
469 			bp->windows = (ballwindow *) NULL;
470 			return;
471 		}
472 		if ((att.x < 0) || (att.x > bp->width) ||
473 				(att.y < 0) || (att.y > bp->height) ||
474 #if defined(__cplusplus) || defined(c_plusplus)
475 				(att.c_class != InputOutput) ||
476 #else
477 				(att.class != InputOutput) ||
478 #endif
479 				(att.map_state != IsViewable)) {
480 			continue;
481 		}
482 		bp->windows[n].x = att.x;
483 		bp->windows[n].y = att.y;
484 		bp->windows[n].width = att.width;
485 		bp->windows[n].height = att.height;
486 		n++;
487 	}
488 	bp->nwindow = n;
489 	XFree((caddr_t) children);
490 	return;
491 }
492 
493 static void
free_stuff(Display * display,bouncestruct * bp)494 free_stuff(Display * display, bouncestruct * bp)
495 {
496 	int bits;
497 
498 	for (bits = 0; bits < bp->init_orients; bits++) {
499 		if (bp->pixmaps[bits] != None) {
500 			XFreePixmap(display, bp->pixmaps[bits]);
501 			bp->pixmaps[bits] = None;
502 		}
503 	}
504 	bp->init_orients = 0;
505 	if (bp->cmap != None) {
506 		XFreeColormap(display, bp->cmap);
507 		if (bp->backGC != None) {
508 			XFreeGC(display, bp->backGC);
509 			bp->backGC = None;
510 		}
511 		bp->cmap = None;
512 	} else
513 		bp->backGC = None;
514 #ifndef STANDALONE
515 /* this used to work there I think */
516 	if (bp->logo != None) {
517 		destroyImage(&bp->logo, &bp->graphics_format);
518 		bp->logo = None;
519 	}
520 #endif
521 }
522 
523 static void
free_bounce_screen(Display * display,bouncestruct * bp)524 free_bounce_screen(Display *display, bouncestruct *bp)
525 {
526 	if (bp == NULL) {
527 		return;
528 	}
529 	if (bp->balls != NULL) {
530 		free(bp->balls);
531 		bp->balls = (ballstruct *) NULL;
532 	}
533 	free_stuff(display, bp);
534 	if (bp->stippledGC != None) {
535 		XFreeGC(display, bp->stippledGC);
536 		bp->stippledGC = None;
537 	}
538 	if (bp->windows != NULL) {
539 		free(bp->windows);
540 		bp->windows = (ballwindow *) NULL;
541 	}
542 	bp = NULL;
543 }
544 
545 static Bool
init_stuff(ModeInfo * mi)546 init_stuff(ModeInfo * mi)
547 {
548 	Display    *display = MI_DISPLAY(mi);
549 	Window      window = MI_WINDOW(mi);
550 	bouncestruct *bp = &bounces[MI_SCREEN(mi)];
551 	XGCValues   gcv;
552 
553 #ifndef STANDALONE
554 /* this used to work there I think */
555 	if (MI_BITMAP(mi) && strlen(MI_BITMAP(mi))) {
556 		if (bp->logo == None) {
557 			getImage(mi, &bp->logo, BOUNCE_WIDTH, BOUNCE_HEIGHT, BOUNCE_BITS,
558 #ifdef HAVE_XPM
559 				DEFAULT_XPM, BOUNCE_NAME,
560 #endif
561 				&bp->graphics_format, &bp->cmap, &bp->black);
562 			if (bp->logo == None) {
563 				free_bounce_screen(display, bp);
564 				return False;
565 			}
566 		}
567 	} else
568 #endif
569 	{
570 		BALLBITS(bounce0_bits, bounce0_width, bounce0_height);
571 		BALLBITS(bounce1_bits, bounce1_width, bounce1_height);
572 		BALLBITS(bounce2_bits, bounce2_width, bounce2_height);
573 		BALLBITS(bounce3_bits, bounce3_width, bounce3_height);
574 		BALLBITS(bouncemask_bits, bouncemask_width, bouncemask_height);
575 	}
576 	if (bp->cmap != None) {
577 #ifndef STANDALONE
578 		setColormap(display, window, bp->cmap, MI_IS_INWINDOW(mi));
579 #endif
580 		if (bp->backGC == None) {
581 			gcv.background = bp->black;
582 			if ((bp->backGC = XCreateGC(display, window,
583 					GCBackground, &gcv)) == None) {
584 				free_bounce_screen(display, bp);
585 				return False;
586 			}
587 		}
588 	} else {
589 		bp->black = MI_BLACK_PIXEL(mi);
590 		bp->backGC = MI_GC(mi);
591 	}
592 	return True;
593 }
594 
595 ENTRYPOINT void
init_bounce(ModeInfo * mi)596 init_bounce(ModeInfo * mi)
597 {
598 	Display    *display = MI_DISPLAY(mi);
599 	Window      window = MI_WINDOW(mi);
600 	int         size = MI_SIZE(mi);
601 	bouncestruct *bp;
602 	int         i, tryagain = 0;
603 	XGCValues   gcv;
604 
605 	MI_INIT(mi, bounces);
606 	bp = &bounces[MI_SCREEN(mi)];
607 
608 	free_stuff(display, bp);
609 	bp->width = MI_WIDTH(mi);
610 	bp->height = MI_HEIGHT(mi);
611 	if (bp->width < 2)
612 		bp->width = 2;
613 	if (bp->height < 2)
614 		bp->height = 2;
615 	bp->restartnum = TIME;
616 	bounce_windows(mi, bp);
617 	bp->nballs = MI_COUNT(mi);
618 	if (bp->nballs < -MINBALLS) {
619 		/* if bp->nballs is random ... the size can change */
620 		if (bp->balls != NULL) {
621 			free(bp->balls);
622 			bp->balls = (ballstruct *) NULL;
623 		}
624 		bp->nballs = NRAND(-bp->nballs - MINBALLS + 1) + MINBALLS;
625 	} else if (bp->nballs < MINBALLS)
626 		bp->nballs = MINBALLS;
627 	if (bp->balls == NULL) {
628 		if ((bp->balls = (ballstruct *) malloc(bp->nballs *
629 				sizeof (ballstruct))) == NULL) {
630 			free_bounce_screen(display, bp);
631 			return;
632 		}
633 	}
634 	if (!init_stuff(mi))
635 		return;
636 	if (bp->stippledGC == None) {
637 		gcv.foreground = MI_BLACK_PIXEL(mi);
638 		gcv.background = MI_BLACK_PIXEL(mi);
639 		if ((bp->stippledGC = XCreateGC(display, window,
640 				GCForeground | GCBackground, &gcv)) == None) {
641 			free_bounce_screen(display, bp);
642 			return;
643 		}
644 	}
645 	if (size == 0 ||
646 			MINGRIDSIZE * size > bp->width || MINGRIDSIZE * size > bp->height) {
647 		if (bp->logo) {
648 			bp->xs = bp->logo->width;
649 			bp->ys = bp->logo->height;
650 		} else {
651 			bp->xs = bounce0_width;
652 			bp->ys = bounce0_height;
653 		}
654 		if (bp->width > MINGRIDSIZE * bp->xs &&
655 				bp->height > MINGRIDSIZE * bp->ys) {
656 			bp->pixelmode = False;
657 		} else {
658 			bp->pixelmode = True;
659 			bp->ys = MAX(MINSIZE, MIN(bp->width, bp->height) / MINGRIDSIZE);
660 			bp->xs = bp->ys;
661 			free_stuff(display, bp);
662 		}
663 	} else {
664 		bp->pixelmode = True;
665 		if (size < -MINSIZE)
666 			bp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(bp->width, bp->height) /
667 					MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
668 		else if (size < MINSIZE)
669 			bp->ys = MINSIZE;
670 		else
671 			bp->ys = MIN(size, MAX(MINSIZE, MIN(bp->width, bp->height) /
672 					MINGRIDSIZE));
673 		bp->xs = bp->ys;
674 	}
675 	if (bp->pixelmode) {
676 		GC fg_gc, bg_gc;
677 
678 		if ((bp->pixmaps[0] = XCreatePixmap(display, window,
679 				bp->xs, bp->ys, 1)) == None) {
680 			free_bounce_screen(display, bp);
681 			return;
682 		}
683 		bp->init_orients = 1;
684 		gcv.foreground = 0;
685 		gcv.background = 1;
686 		if ((bg_gc = XCreateGC(display, bp->pixmaps[0],
687 				GCForeground | GCBackground, &gcv)) == None) {
688 			free_bounce_screen(display, bp);
689 			return;
690 		}
691 		gcv.foreground = 1;
692 		gcv.background = 0;
693 		if ((fg_gc = XCreateGC(display, bp->pixmaps[0],
694 				GCForeground | GCBackground, &gcv)) == None) {
695 			XFreeGC(display, bg_gc);
696 			free_bounce_screen(display, bp);
697 			return;
698 		}
699 		XFillRectangle(display, bp->pixmaps[0], bg_gc,
700 			0, 0, bp->xs, bp->ys);
701 		XFillArc(display, bp->pixmaps[0], fg_gc,
702 			0, 0, bp->xs, bp->ys, 0, 23040);
703 		XFreeGC(display, bg_gc);
704 		XFreeGC(display, fg_gc);
705 	}
706 	bp->avgsize = (bp->xs + bp->ys) / 2;
707 	i = 0;
708 	while (i < bp->nballs) {
709 		bp->balls[i].vx = ((LRAND() & 1) ? -1 : 1) * (NRAND(MAX_STRENGTH) + 1);
710 		bp->balls[i].x = (bp->balls[i].vx >= 0) ? 0 : bp->width - bp->xs;
711 		bp->balls[i].y = NRAND(bp->height / 2);
712 		if (i == collide(bp, i) || tryagain >= 8) {
713 			if (MI_NPIXELS(mi) > 2)
714 				bp->balls[i].color =
715 					MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
716 			else
717 				bp->balls[i].color = MI_WHITE_PIXEL(mi);
718 			bp->balls[i].xlast = -1;
719 			bp->balls[i].ylast = 0;
720 			bp->balls[i].orientlast = 0;
721 			bp->balls[i].spincount = 1;
722 			bp->balls[i].spindelay = 1;
723 			bp->balls[i].vy = ((LRAND() & 1) ? -1 : 1) * NRAND(MAX_STRENGTH);
724 			bp->balls[i].spindir = 0;
725 			bp->balls[i].vang = 0;
726 			bp->balls[i].orient = NRAND(ORIENTS);
727 			i++;
728 		} else
729 			tryagain++;
730 	}
731 	MI_CLEARWINDOW(mi);
732 }
733 
734 ENTRYPOINT void
draw_bounce(ModeInfo * mi)735 draw_bounce(ModeInfo * mi)
736 {
737 	int         i;
738 	bouncestruct *bp;
739 
740 	if (bounces == NULL)
741 		return;
742 	bp = &bounces[MI_SCREEN(mi)];
743 	if (bp->balls == NULL)
744 		return;
745 
746 	MI_IS_DRAWN(mi) = True;
747 	for (i = 0; i < bp->nballs; i++) {
748 		drawball(mi, &bp->balls[i]);
749 		moveball(mi, &bp->balls[i]);
750 	}
751 	for (i = 0; i < bp->nballs; i++)
752 		checkCollision(bp, i);
753 	if (!NRAND(TIME))	/* Put some randomness into the time */
754 		bp->restartnum--;
755 	if (!bp->restartnum)
756 		init_bounce(mi);
757 }
758 
759 ENTRYPOINT void
release_bounce(ModeInfo * mi)760 release_bounce(ModeInfo * mi)
761 {
762 	if (bounces != NULL) {
763 		int        screen;
764 
765 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
766 			free_bounce_screen(MI_DISPLAY(mi), &bounces[screen]);
767 		free(bounces);
768 		bounces = (bouncestruct *) NULL;
769 	}
770 }
771 
772 #ifndef STANDALONE
773 ENTRYPOINT void
refresh_bounce(ModeInfo * mi)774 refresh_bounce(ModeInfo * mi)
775 {
776 	bouncestruct *bp = &bounces[MI_SCREEN(mi)];
777 
778 	MI_CLEARWINDOW(mi);
779 	bounce_windows(mi, bp);
780 }
781 #endif
782 
783 XSCREENSAVER_MODULE ("Bounce", bounce)
784 
785 #endif /* MODE_bounce */
786