1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* bat --- bouncing bats */
3 
4 #if 0
5 static const char sccsid[] = "@(#)bat.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  * 18-Sep-1995: 5 bats now in color <patol@info.isbiel.ch>
28  * 20-Sep-1994: 5 bats instead of bouncing balls, based on bounce.c
29  *              <patol@info.isbiel.ch>
30  * 02-Sep-1993: bounce version David Bagley <bagleyd AT verizon.net>
31  * 1986: Sun Microsystems
32  */
33 
34 /*-
35  * original copyright
36  * **************************************************************************
37  * Copyright 1988 by Sun Microsystems, Inc. Mountain View, CA.
38  *
39  * All Rights Reserved
40  *
41  * Permission to use, copy, modify, and distribute this software and its
42  * documentation for any purpose and without fee is hereby granted, provided
43  * that the above copyright notice appear in all copies and that both that
44  * copyright notice and this permission notice appear in supporting
45  * documentation, and that the names of Sun or MIT not be used in advertising
46  * or publicity pertaining to distribution of the software without specific
47  * prior written permission. Sun and M.I.T. make no representations about the
48  * suitability of this software for any purpose. It is provided "as is"
49  * without any express or implied warranty.
50  *
51  * SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
53  * IN NO EVENT SHALL SUN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
54  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
55  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
56  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
57  * SOFTWARE.
58  * ***************************************************************************
59  */
60 
61 #ifdef STANDALONE
62 # define MODE_bat
63 # define DEFAULTS	"*delay: 100000 \n" \
64 			"*count: -8 \n" \
65 			"*size: 0 \n" \
66 			"*ncolors: 200 \n" \
67 			"*verbose: False \n" \
68 
69 # define reshape_bat 0
70 # define bat_handle_event 0
71 #include "xlockmore.h"		/* in xscreensaver distribution */
72 #else /* STANDALONE */
73 # include "xlock.h"		/* in xlockmore distribution */
74 # include "vis.h"
75 # include "color.h"
76 # include "iostuff.h"
77 #endif /* STANDALONE */
78 
79 #ifdef MODE_bat
80 
81 ENTRYPOINT ModeSpecOpt bat_opts =
82 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
83 
84 #ifdef USE_MODULES
85 ENTRYPOINT ModStruct bat_description =
86 {"bat", "init_bat", "draw_bat", "release_bat",
87  "refresh_bat", "init_bat", "free_bat", &bat_opts,
88  100000, -8, 1, 0, 64, 1.0, "",
89  "Shows bouncing flying bats", 0, NULL};
90 
91 #endif
92 
93 #ifdef HAVE_XPM
94 #include <X11/xpm.h>
95 #include "pixmaps/bat-0.xpm"
96 #include "pixmaps/bat-1.xpm"
97 #include "pixmaps/bat-2.xpm"
98 #include "pixmaps/bat-3.xpm"
99 #include "pixmaps/bat-4.xpm"
100 #endif
101 
102 #include "bitmaps/bat-0.xbm"
103 #include "bitmaps/bat-1.xbm"
104 #include "bitmaps/bat-2.xbm"
105 #include "bitmaps/bat-3.xbm"
106 #include "bitmaps/bat-4.xbm"
107 
108 #ifndef STANDALONE
109 /* aliases for vars defined in the bitmap file */
110 #define BAT_WIDTH     image_width
111 #define BAT_HEIGHT    image_height
112 #define BAT_BITS      image_bits
113 
114 #include "bat.xbm"
115 
116 #ifdef HAVE_XPM
117 #define BAT_NAME      image_name
118 #include "bat.xpm"
119 #define DEFAULT_XPM 1
120 #endif
121 #endif
122 
123 #define MAX_STRENGTH 24
124 #define FRICTION 15
125 #define PENETRATION 0.4
126 #define SLIPAGE 4
127 #define TIME 32
128 #define MINBATS 1
129 #define MINSIZE 1
130 #define MINGRIDSIZE 3
131 
132 #define ORIENTS 8
133 #define ORIENTCYCLE 32
134 #define CCW 1
135 #define CW (ORIENTS-1)
136 #define DIR(x)	(((x)>=0)?CCW:CW)
137 #define SIGN(x)	(((x)>=0)?1:-1)
138 
139 static XImage bimages[] =
140 {
141 	{0, 0, 0, XYBitmap, 0, LSBFirst, 8, LSBFirst, 8, 1},
142 	{0, 0, 0, XYBitmap, 0, LSBFirst, 8, LSBFirst, 8, 1},
143 	{0, 0, 0, XYBitmap, 0, LSBFirst, 8, LSBFirst, 8, 1},
144 	{0, 0, 0, XYBitmap, 0, LSBFirst, 8, LSBFirst, 8, 1},
145 	{0, 0, 0, XYBitmap, 0, LSBFirst, 8, LSBFirst, 8, 1}
146 };
147 
148 typedef struct {
149 	int         x, y, xlast, ylast;
150 	int         spincount, spindelay, spindir, orient;
151 	int         vx, vy, vang;
152 	int         graphics_format;
153 	unsigned long color;
154 } batstruct;
155 
156 typedef struct {
157 	int         width, height;
158 	int         nbats;
159 	int         xs, ys;
160 	int         floor;
161 	int         avgsize;
162 	int         restartnum;
163 	int         graphics_format;
164 	int         pixelmode;
165 	GC          backGC;
166 	XImage     *logo;
167 	Pixmap      pixmap;
168 	Colormap    cmap;
169 	unsigned long black;
170 	batstruct  *bats;
171 	XImage     *images[ORIENTS / 2 + 1];
172 } bouncestruct;
173 
174 static bouncestruct *bounces = (bouncestruct *) NULL;
175 
176 static unsigned char *bits[] =
177 {
178 	bat0_bits, bat1_bits, bat2_bits, bat3_bits, bat4_bits
179 };
180 
181 #ifdef HAVE_XPM
182 static char **pixs[] =
183 {
184 	bat0, bat1, bat2, bat3, bat4
185 };
186 
187 #endif
188 
189 static void
checkCollision(bouncestruct * bp,int a_bat)190 checkCollision(bouncestruct * bp, int a_bat)
191 {
192 	int         i, amount, spin, d, size;
193 	double      x, y;
194 
195 	for (i = 0; i < bp->nbats; i++) {
196 		if (i != a_bat) {
197 			x = (double) (bp->bats[i].x - bp->bats[a_bat].x);
198 			y = (double) (bp->bats[i].y - bp->bats[a_bat].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->bats[i].vx += (int) ((double) amount * x / d);
206 				bp->bats[i].vy += (int) ((double) amount * y / d);
207 				bp->bats[i].vx -= bp->bats[i].vx / FRICTION;
208 				bp->bats[i].vy -= bp->bats[i].vy / FRICTION;
209 				bp->bats[a_bat].vx -= (int) ((double) amount * x / d);
210 				bp->bats[a_bat].vy -= (int) ((double) amount * y / d);
211 				bp->bats[a_bat].vx -= bp->bats[a_bat].vx / FRICTION;
212 				bp->bats[a_bat].vy -= bp->bats[a_bat].vy / FRICTION;
213 				spin = (bp->bats[i].vang - bp->bats[a_bat].vang) /
214 					(2 * size * SLIPAGE);
215 				bp->bats[i].vang -= spin;
216 				bp->bats[a_bat].vang += spin;
217 				bp->bats[i].spindir = DIR(bp->bats[i].vang);
218 				bp->bats[a_bat].spindir = DIR(bp->bats[a_bat].vang);
219 				if (!bp->bats[i].vang) {
220 					bp->bats[i].spindelay = 1;
221 					bp->bats[i].spindir = 0;
222 				} else
223 					bp->bats[i].spindelay = (int) ((double) M_PI *
224 							bp->avgsize / (ABS(bp->bats[i].vang))) + 1;
225 				if (!bp->bats[a_bat].vang) {
226 					bp->bats[a_bat].spindelay = 1;
227 					bp->bats[a_bat].spindir = 0;
228 				} else
229 					bp->bats[a_bat].spindelay = (int) ((double) M_PI *
230 							bp->avgsize / (ABS(bp->bats[a_bat].vang))) + 1;
231 				return;
232 			}
233 		}
234 	}
235 }
236 
237 static void
drawbat(ModeInfo * mi,batstruct * bat)238 drawbat(ModeInfo * mi, batstruct * bat)
239 {
240 	Display    *display = MI_DISPLAY(mi);
241 	Window      window = MI_WINDOW(mi);
242 	bouncestruct *bp = &bounces[MI_SCREEN(mi)];
243 
244 	if (bp->pixelmode) {
245 		if (bat->xlast != -1) {
246 			XSetForeground(display, bp->backGC, bp->black);
247 			XFillRectangle(display, window, bp->backGC,
248 				bat->xlast, bat->ylast, bp->xs, bp->ys);
249 		}
250 		XSetForeground(display, bp->backGC, bat->color);
251 		XCopyPlane(display, bp->pixmap, window, bp->backGC,
252 			0, 0, bp->xs, bp->ys, bat->x, bat->y, 1L);
253 	} else {
254 		XSetForeground(display, bp->backGC, bat->color);
255 		if (bp->logo)
256 			(void) XPutImage(display, window, bp->backGC, bp->logo,
257 				0, 0, bat->x, bat->y, bp->xs, bp->ys);
258 		else
259 /* PURIFY 4.0.1 on SunOS4 and on Solaris 2 reports a 15985 byte memory leak on
260    * the next line. */
261 			(void) XPutImage(display, window, bp->backGC,
262 				bp->images[(bat->orient > ORIENTS / 2) ?
263 					ORIENTS - bat->orient : bat->orient],
264 					0, 0, bat->x, bat->y, bp->xs, bp->ys);
265 		if (bat->xlast != -1) {
266 			XSetForeground(display, bp->backGC, bp->black);
267 			ERASE_IMAGE(display, window, bp->backGC,
268 				bat->x, bat->y, bat->xlast, bat->ylast, bp->xs, bp->ys);
269 		}
270 	}
271 }
272 
273 static void
flapbat(batstruct * bat,int dir,int * vel,int avgsize)274 flapbat(batstruct * bat, int dir, int *vel, int avgsize)
275 {
276 	*vel -= (int) ((*vel + SIGN(*vel * dir) *
277 			bat->spindelay * ORIENTCYCLE / (M_PI * avgsize)) / SLIPAGE);
278 	if (*vel) {
279 		bat->spindir = DIR(*vel * dir);
280 		bat->vang = *vel * ORIENTCYCLE;
281 		bat->spindelay = (int) ((double) M_PI * avgsize / (ABS(bat->vang))) + 1;
282 	} else
283 		bat->spindir = 0;
284 }
285 
286 static void
movebat(bouncestruct * bp,batstruct * bat)287 movebat(bouncestruct * bp, batstruct * bat)
288 {
289 	bat->xlast = bat->x;
290 	bat->ylast = bat->y;
291 	bat->x += bat->vx;
292 	if (bat->x > (bp->width - bp->xs)) {
293 		/* Bounce off the right edge */
294 		bat->x = 2 * (bp->width - bp->xs) - bat->x;
295 		bat->vx = -bat->vx + bat->vx / FRICTION;
296 		flapbat(bat, 1, &bat->vy, bp->avgsize);
297 	} else if (bat->x < 0) {
298 		/* Bounce off the left edge */
299 		bat->x = -bat->x;
300 		bat->vx = -bat->vx + bat->vx / FRICTION;
301 		flapbat(bat, -1, &bat->vy, bp->avgsize);
302 	}
303 	bat->vy++;
304 	bat->y += bat->vy;
305 	if (bat->y >= (bp->height + bp->floor * bp->ys)) {
306 		/* Do not want to see bat bounce */
307 		/* Bounce off the bottom edge */
308 		bat->y = (bp->height - bp->ys);
309 		bat->vy = -bat->vy + bat->vy / FRICTION;
310 		flapbat(bat, -1, &bat->vx, bp->avgsize);
311 	}
312 #if 0
313 	else if (bat->y < 0) {
314 		/* Bounce off the top edge */
315 		bat->y = -bat->y;
316 		bat->vy = -bat->vy + bat->vy / FRICTION;
317 		flapbat(bat, 1, &bat->vx, bp->avgsize);
318 	}
319 #endif
320 	if (bat->spindir) {
321 		bat->spincount--;
322 		if (!bat->spincount) {
323 			bat->orient = (bat->spindir + bat->orient) % ORIENTS;
324 			bat->spincount = bat->spindelay;
325 		}
326 	}
327 }
328 
329 static int
collide(bouncestruct * bp,int a_bat)330 collide(bouncestruct * bp, int a_bat)
331 {
332 	int         i, d, x, y;
333 
334 	for (i = 0; i < a_bat; i++) {
335 		x = (bp->bats[i].x - bp->bats[a_bat].x);
336 		y = (bp->bats[i].y - bp->bats[a_bat].y);
337 		d = (int) sqrt((double) (x * x + y * y));
338 		if (d < bp->avgsize)
339 			return i;
340 	}
341 	return i;
342 }
343 
344 static void
free_stuff(Display * display,bouncestruct * bp)345 free_stuff(Display * display, bouncestruct * bp)
346 {
347 #ifdef HAVE_XPM
348 	if (bp->graphics_format == IS_XPM) {
349 		int         i;
350 
351 		for (i = 0; i <= ORIENTS / 2; i++) {
352 			if (bp->images[i]) {
353 				(void) XDestroyImage(bp->images[i]);
354 				bp->images[i] = None;
355 			}
356 		}
357 		bp->graphics_format = IS_NONE;
358 	}
359 	if (bp->cmap != None) {
360 		XFreeColormap(display, bp->cmap);
361 		if (bp->backGC != None) {
362 			XFreeGC(display, bp->backGC);
363 			bp->backGC = None;
364 		}
365 		bp->cmap = None;
366 	} else
367 		bp->backGC = None;
368 #endif
369 #ifndef STANDALONE
370 /* this used to work there I think */
371 	if (bp->logo != None) {
372 		destroyImage(&bp->logo, &bp->graphics_format);
373 		bp->logo = None;
374 	}
375 #endif
376 }
377 
378 static void
free_bat_screen(Display * display,bouncestruct * bp)379 free_bat_screen(Display *display, bouncestruct *bp)
380 {
381 	if (bp == NULL) {
382 		return;
383 	}
384 	free_stuff(display, bp);
385 	if (bp->bats != NULL) {
386 		free(bp->bats);
387 		bp->bats = (batstruct *) NULL;
388 	}
389 	if (bp->pixmap != None) {
390 		XFreePixmap(display, bp->pixmap);
391 		bp->pixmap = None;
392 	}
393 	bp = NULL;
394 }
395 
396 static Bool
init_stuff(ModeInfo * mi)397 init_stuff(ModeInfo * mi)
398 {
399 	Display    *display = MI_DISPLAY(mi);
400 	Window      window = MI_WINDOW(mi);
401 	bouncestruct *bp = &bounces[MI_SCREEN(mi)];
402 	int i;
403 
404 #ifndef STANDALONE
405 /* this used to work there I think */
406 	if (MI_BITMAP(mi) && strlen(MI_BITMAP(mi))) {
407 		if (bp->logo == None) {
408 			getImage(mi, &bp->logo, BAT_WIDTH, BAT_HEIGHT, BAT_BITS,
409 #ifdef HAVE_XPM
410 				DEFAULT_XPM, BAT_NAME,
411 #endif
412 				&bp->graphics_format, &bp->cmap, &bp->black);
413 			if (bp->logo == None) {
414 				free_bat_screen(display, bp);
415 				return False;
416 			}
417 		}
418 	} else
419 #endif
420 	{
421 		if (bp->cmap == None) {
422 /* problems here with XpmCreateImageFromData */
423 #ifdef HAVE_XPM
424 		int         total = 0;
425 
426 		if (!MI_IS_FULLRANDOM(mi) || (LRAND() & 1)) {
427 			XpmAttributes attrib;
428 
429 #ifndef STANDALONE
430 			if (!fixedColors(mi)) {
431 				if ((bp->cmap = XCreateColormap(display, window,
432 						MI_VISUAL(mi), AllocNone)) == None) {
433 					free_bat_screen(display, bp);
434 					return False;
435 				}
436 				attrib.colormap = bp->cmap;
437 				reserveColors(mi, bp->cmap, &bp->black);
438 			} else
439 #endif /* STANDALONE */
440 				attrib.colormap = MI_COLORMAP(mi);
441 
442 			attrib.visual = MI_VISUAL(mi);
443 			attrib.depth = MI_DEPTH(mi);
444 			attrib.valuemask = XpmVisual | XpmColormap | XpmDepth;
445 
446 			if (bp->graphics_format == IS_NONE
447 #ifndef USE_MONOXPM
448 					&& MI_NPIXELS(mi) > 2
449 #endif
450 				) {
451 				for (i = 0; i <= ORIENTS / 2; i++)
452 					if (XpmSuccess != XpmCreateImageFromData(display, pixs[i],
453 					&(bp->images[i]), (XImage **) NULL, &attrib))
454 						break;
455 				bp->graphics_format = IS_XPM;
456 				total = i;
457 				if (total <= ORIENTS / 2) {	/* All or nothing */
458 					bp->graphics_format = IS_XBM;
459 					if (MI_IS_VERBOSE(mi))
460 						(void) fprintf(stderr, "Full color images could not be loaded.\n");
461 					for (i = 0; i < total; i++) {
462 						(void) XDestroyImage(bp->images[i]);
463 						bp->images[i] = None;
464 					}
465 					bp->images[total] = None;
466 					if (bp->cmap != None) {
467 						XFreeColormap(display, bp->cmap);
468 						bp->cmap = None;
469 					}
470 				}
471 			}
472 		}
473 		if (total <= ORIENTS / 2)
474 #endif
475 			{
476 				if (!bimages[0].data)	/* Only need to do this once */
477 					for (i = 0; i <= ORIENTS / 2; i++) {
478 						bimages[i].data = (char *) bits[i];
479 						bimages[i].width = bat0_width;
480 						bimages[i].height = bat0_height;
481 						bimages[i].bytes_per_line = (bat0_width + 7) / 8;
482 					}
483 				for (i = 0; i <= ORIENTS / 2; i++)
484 					bp->images[i] = &(bimages[i]);
485 			}
486 		}
487 	}
488 	if (bp->cmap != None) {
489 #ifndef STANDALONE
490 		setColormap(display, window, bp->cmap, MI_IS_INWINDOW(mi));
491 #endif
492 		if (bp->backGC == None) {
493 			XGCValues   xgcv;
494 
495 			xgcv.background = bp->black;
496 			if ((bp->backGC = XCreateGC(display, window,
497 					GCBackground, &xgcv)) == None) {
498 				free_bat_screen(display, bp);
499 				return False;
500 			}
501 		}
502 	} else {
503 		bp->black = MI_BLACK_PIXEL(mi);
504 		bp->backGC = MI_GC(mi);
505 	}
506 	return True;
507 }
508 
509 ENTRYPOINT void
free_bat(ModeInfo * mi)510 free_bat(ModeInfo * mi)
511 {
512 	free_bat_screen(MI_DISPLAY(mi), &bounces[MI_SCREEN(mi)]);
513 }
514 
515 ENTRYPOINT void
init_bat(ModeInfo * mi)516 init_bat(ModeInfo * mi)
517 {
518 	Display    *display = MI_DISPLAY(mi);
519 	Window      window = MI_WINDOW(mi);
520 	int         size = MI_SIZE(mi);
521 	int         i, tryagain = 0;
522 	bouncestruct *bp;
523 
524 	MI_INIT(mi, bounces);
525 	bp = &bounces[MI_SCREEN(mi)];
526 
527 	free_stuff(display, bp);
528 	bp->width = MI_WIDTH(mi);
529 	bp->height = MI_HEIGHT(mi);
530 	if (bp->width < 2)
531 		bp->width = 2;
532 	if (bp->height < 2)
533 		bp->height = 2;
534 	bp->restartnum = TIME;
535 	bp->floor = NRAND(3) + 2;
536 
537 	bp->nbats = MI_COUNT(mi);
538 	if (bp->nbats < -MINBATS) {
539 		/* if bp->nbats is random ... the size can change */
540 		if (bp->bats != NULL) {
541 			free(bp->bats);
542 			bp->bats = (batstruct *)NULL;
543 		}
544 		bp->nbats = NRAND(-bp->nbats - MINBATS + 1) + MINBATS;
545 	} else if (bp->nbats < MINBATS)
546 		bp->nbats = MINBATS;
547 	if (!bp->bats) {
548 		if ((bp->bats = (batstruct *) malloc(bp->nbats *
549 					sizeof (batstruct))) == NULL) {
550 			free_bat_screen(display, bp);
551 			return;
552 		}
553 	}
554 	if (!init_stuff(mi))
555 		return;
556 	if (size == 0 ||
557 			MINGRIDSIZE * size > bp->width / 2 || MINGRIDSIZE * size > bp->height) {
558 		if (bp->logo) {
559 			bp->xs = bp->logo->width;
560 			bp->ys = bp->logo->height;
561 		} else {
562 			bp->xs = bat0_width;
563 			bp->ys = bat0_height;
564 		}
565 		if (bp->width > MINGRIDSIZE * bp->xs &&
566 				bp->height > MINGRIDSIZE * bp->ys) {
567 			bp->pixelmode = False;
568 		} else {
569 			bp->pixelmode = True;
570 			bp->ys = MAX(MINSIZE, MIN(bp->width / 2, bp->height) / MINGRIDSIZE);
571 			bp->xs = 2 * bp->ys;
572 			free_stuff(display, bp); /* too big */
573 		}
574 	} else {
575 		bp->pixelmode = True;
576 		if (size < -MINSIZE)
577 			bp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(bp->width / 2, bp->height) /
578 				MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE;
579 		else if (size < MINSIZE)
580 			bp->ys = MINSIZE;
581 		else
582 			bp->ys = MIN(size, MAX(MINSIZE, MIN(bp->width / 2, bp->height) /
583 				MINGRIDSIZE));
584 		bp->xs = 2 * bp->ys;
585 	}
586 	bp->avgsize = (bp->xs + bp->ys) / 2;
587 
588 	if (bp->pixelmode) {
589 		GC fg_gc, bg_gc;
590 		XGCValues gcv;
591 
592 		if ((bp->pixmap = XCreatePixmap(display, window, bp->xs, bp->ys, 1)) ==
593 				None) {
594 			free_bat_screen(display, bp);
595 			return;
596 		}
597 		gcv.foreground = 0;
598 		gcv.background = 1;
599 		if ((bg_gc = XCreateGC(display, bp->pixmap,
600 				GCForeground | GCBackground, &gcv)) == None) {
601 			free_bat_screen(display, bp);
602 			return;
603 		}
604 		gcv.background = 0;
605 		gcv.foreground = 1;
606 		if ((fg_gc = XCreateGC(display, bp->pixmap,
607 				GCForeground | GCBackground, &gcv)) == None) {
608 			XFreeGC(display, bg_gc);
609 			free_bat_screen(display, bp);
610 			return;
611 		}
612 		XFillRectangle(display, bp->pixmap, bg_gc,
613 			0, 0, bp->xs, bp->ys);
614 		XFillArc(display, bp->pixmap, fg_gc,
615 			0, 0, bp->xs / 2, 2 * bp->ys,
616 			0, 11520);
617 		XFillArc(display, bp->pixmap, fg_gc,
618 			bp->xs / 2, 0, bp->xs / 2, 2 * bp->ys,
619 			0, 11520);
620 		XFillRectangle(display, bp->pixmap, fg_gc,
621 			bp->xs / 4, bp->ys / 2,
622 			bp->xs / 2, bp->ys / 2);
623 		XFillArc(display, bp->pixmap, bg_gc,
624 			0, bp->ys / 2, bp->xs / 2, 2 * bp->ys,
625 			0, 11520);
626 		XFillArc(display, bp->pixmap, bg_gc,
627 			bp->xs / 2, bp->ys / 2, bp->xs / 2, 2 * bp->ys,
628 			0, 11520);
629 		XFreeGC(display, bg_gc);
630 		XFreeGC(display, fg_gc);
631 
632 		bp->black = MI_BLACK_PIXEL(mi);
633 		bp->backGC = MI_GC(mi);
634 	}
635 
636 	i = 0;
637 	while (i < bp->nbats) {
638 		bp->bats[i].vx = ((LRAND() & 1) ? -1 : 1) * (NRAND(MAX_STRENGTH) + 1);
639 		bp->bats[i].x = (bp->bats[i].vx >= 0) ? 0 : bp->width - bp->xs;
640 		bp->bats[i].y = NRAND(bp->height / 2);
641 		if (i == collide(bp, i) || tryagain >= 8) {
642 			if (MI_NPIXELS(mi) > 2)
643 				bp->bats[i].color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
644 			else
645 				bp->bats[i].color = MI_WHITE_PIXEL(mi);
646 			bp->bats[i].xlast = -1;
647 			bp->bats[i].ylast = 0;
648 			bp->bats[i].spincount = 1;
649 			bp->bats[i].spindelay = 1;
650 			bp->bats[i].vy = ((LRAND() & 1) ? -1 : 1) * NRAND(MAX_STRENGTH);
651 			bp->bats[i].spindir = 0;
652 			bp->bats[i].vang = 0;
653 			bp->bats[i].orient = NRAND(ORIENTS);
654 			i++;
655 		} else
656 			tryagain++;
657 	}
658 	/* don't want any exposure events from XCopyPlane */
659 	XSetGraphicsExposures(display, MI_GC(mi), False);
660 	MI_CLEARWINDOWCOLORMAP(mi, bp->backGC, bp->black);
661 }
662 
663 ENTRYPOINT void
draw_bat(ModeInfo * mi)664 draw_bat(ModeInfo * mi)
665 {
666 	int         i;
667 	bouncestruct *bp;
668 
669 	if (bounces == NULL)
670 		return;
671 	bp = &bounces[MI_SCREEN(mi)];
672 	if (bp->bats == NULL)
673 		return;
674 
675 	MI_IS_DRAWN(mi) = True;
676 	for (i = 0; i < bp->nbats; i++) {
677 		drawbat(mi, &bp->bats[i]);
678 		movebat(bp, &bp->bats[i]);
679 	}
680 	for (i = 0; i < bp->nbats; i++)
681 		checkCollision(bp, i);
682 	if (!NRAND(TIME))	/* Put some randomness into the time */
683 		bp->restartnum--;
684 	if (!bp->restartnum)
685 		init_bat(mi);
686 }
687 
688 ENTRYPOINT void
release_bat(ModeInfo * mi)689 release_bat(ModeInfo * mi)
690 {
691 	if (bounces != NULL) {
692 		int        screen;
693 
694 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
695 			free_bat_screen(MI_DISPLAY(mi), &bounces[screen]);
696 		free(bounces);
697 		bounces = (bouncestruct *) NULL;
698 	}
699 }
700 
701 #ifndef STANDALONE
702 ENTRYPOINT void
refresh_bat(ModeInfo * mi)703 refresh_bat(ModeInfo * mi)
704 {
705 	bouncestruct *bp;
706 
707 	if (bounces == NULL)
708 		return;
709 	bp = &bounces[MI_SCREEN(mi)];
710 	if (bp->bats == NULL)
711 		return;
712 
713 #ifdef HAVE_XPM
714 	/* This is only needed when another program changes the colormap. */
715 	if (MI_BITMAP(mi) && strlen(MI_BITMAP(mi))) {
716 		init_bat(mi);
717 	} else {
718 		MI_CLEARWINDOWCOLORMAP(mi, bp->backGC, bp->black);
719 		free_stuff(MI_DISPLAY(mi), bp);
720 		(void) init_stuff(mi);
721 	}
722 #else
723 	MI_CLEARWINDOWCOLORMAP(mi, bp->backGC, bp->black);
724 #endif
725 }
726 #endif
727 
728 XSCREENSAVER_MODULE ("Bat", bat)
729 
730 #endif /* MODE_bat */
731