1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* swarm --- swarm of bees */
3 
4 #if 0
5 static const char sccsid[] = "@(#)swarm.c	5.00 2000/11/01 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Copyright (c) 1991 by Patrick J. Naughton.
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  * 13-Jul-2000: Bee trails implemented by Juan Heguiabehere <jheguia@usa.net>
27  * 10-May-1997: Compatible with xscreensaver
28  * 31-Aug-1990: Adapted from xswarm by Jeff Butterworth <butterwo@ncsc.org>
29  */
30 
31 #ifdef STANDALONE
32 #define MODE_swarm
33 #define DEFAULTS "*delay: 15000 \n" \
34 	"*count: -100 \n" \
35 	"*size: -100 \n" \
36 
37 # define free_swarm 0
38 # define reshape_swarm 0
39 # define swarm_handle_event 0
40 #define BRIGHT_COLORS
41 #define SMOOTH_COLORS
42 #include "xlockmore.h"		/* from the xscreensaver distribution */
43 #include <X11/Xutil.h>
44 #else /* !STANDALONE */
45 #include "xlock.h"		/* from the xlockmore distribution */
46 #endif /* !STANDALONE */
47 
48 #ifdef MODE_swarm
49 
50 #define DEF_TRACKMOUSE  "False"
51 
52 static Bool trackmouse;
53 
54 static XrmOptionDescRec opts[] =
55 {
56         {(char *) "-trackmouse", (char *) ".swarm.trackmouse", XrmoptionNoArg, (caddr_t) "on"},
57         {(char *) "+trackmouse", (char *) ".swarm.trackmouse", XrmoptionNoArg, (caddr_t) "off"}
58 };
59 
60 static argtype vars[] =
61 {
62         {(void *) & trackmouse, (char *) "trackmouse", (char *) "TrackMouse", (char *) DEF_TRACKMOUSE, t_Bool}
63 };
64 
65 static OptionStruct desc[] =
66 {
67         {(char *) "-/+trackmouse", (char *) "turn on/off the tracking of the mouse"}
68 };
69 
70 ENTRYPOINT ModeSpecOpt swarm_opts =
71 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
72 
73 
74 #ifdef USE_MODULES
75 ModStruct   swarm_description =
76 {"swarm", "init_swarm", "draw_swarm", "release_swarm",
77  "refresh_swarm", "init_swarm", (char *) NULL, &swarm_opts,
78  15000, -100, 1, -100, 64, 1.0, "",
79  "Shows a swarm of bees following a wasp", 0, NULL};
80 
81 #endif
82 
83 #define MINBEES	1		/* min number of bees */
84 #define MINTRAIL 3		/* min number of time positions recorded */
85 #define BEEACC	3		/* acceleration of bees */
86 #define WASPACC 5		/* maximum acceleration of wasp */
87 #define BEEVEL	17		/* maximum bee velocity */
88 #define WASPVEL 15		/* maximum wasp velocity */
89 
90 /* Macros */
91 #define X(t,b)	(sp->x[((t)*sp->beecount+(b))])
92 #define Y(t,b)	(sp->y[((t)*sp->beecount+(b))])
93 #define balance_rand(v)	((NRAND(v))-((v-1)/2))	/* random number around 0, input odd */
94 
95 typedef struct {
96 	int         pix;
97 	int         width;
98 	int         height;
99 	int         border;	/* wasp won't go closer than this to the edge */
100 	int         beecount;	/* number of bees */
101 	XSegment   *segs;	/* bee lines */
102 	XSegment   *old_segs;	/* old bee lines */
103 	float      *x, *y;	/* bee positions x[time][bee#] */
104 	float      *xv, *yv;	/* bee velocities xv[bee#] */
105 	short       wx[3];
106 	short       wy[3];
107 	short       wxv;
108 	short       wyv;
109 	short       tick;
110 	short       rolloverflag;
111 	short       taillen;
112 	Cursor      cursor;
113 } swarmstruct;
114 
115 static swarmstruct *swarms = (swarmstruct *) NULL;
116 
117 static void
free_segs(swarmstruct * sp)118 free_segs(swarmstruct *sp)
119 {
120 	if (sp->segs != NULL) {
121 		free(sp->segs);
122 		sp->segs = (XSegment *) NULL;
123 	}
124 	if (sp->old_segs != NULL) {
125 		free(sp->old_segs);
126 		sp->old_segs = (XSegment *) NULL;
127 	}
128 }
129 
130 static void
free_bees(swarmstruct * sp)131 free_bees(swarmstruct *sp)
132 {
133 	free_segs(sp);
134 	if (sp->x != NULL) {
135 		free(sp->x);
136 		sp->x = (float *) NULL;
137 	}
138 	if (sp->y != NULL) {
139 		free(sp->y);
140 		sp->y = (float *) NULL;
141 	}
142 	if (sp->xv != NULL) {
143 		free(sp->xv);
144 		sp->xv = (float *) NULL;
145 	}
146 	if (sp->yv != NULL) {
147 		free(sp->yv);
148 		sp->yv = (float *) NULL;
149 	}
150 }
151 
152 static void
free_swarm_screen(Display * display,swarmstruct * sp)153 free_swarm_screen(Display *display, swarmstruct *sp)
154 {
155 	if (sp == NULL) {
156 		return;
157 	}
158 	free_bees(sp);
159 	if (sp->cursor != None) {
160 		XFreeCursor(display, sp->cursor);
161 		sp->cursor = None;
162 	}
163 	sp = NULL;
164 }
165 
166 ENTRYPOINT void
init_swarm(ModeInfo * mi)167 init_swarm(ModeInfo * mi)
168 {
169 	Display    *display = MI_DISPLAY(mi);
170 	Window      window = MI_WINDOW(mi);
171 	int         b, t;
172 	swarmstruct *sp;
173 
174 	MI_INIT(mi, swarms);
175 	sp = &swarms[MI_SCREEN(mi)];
176 
177 	sp->beecount = MI_COUNT(mi);
178 	if (sp->beecount < -MINBEES) {
179 		sp->beecount = NRAND(-sp->beecount - MINBEES + 1) + MINBEES;
180 		/* if sp->beecount is random ... the size can change */
181 		free_bees(sp);
182 	} else if (sp->beecount < MINBEES)
183 		sp->beecount = MINBEES;
184 	sp->taillen = MI_SIZE(mi);
185 	if (sp->taillen < -MINTRAIL) {
186 		/* Change by sqr so its seems more variable */
187 		sp->taillen = NRAND((int) sqrt((double) (-sp->taillen - MINTRAIL + 1)));
188 		sp->taillen = sp->taillen * sp->taillen + MINTRAIL;
189 		free_segs(sp);
190 	} else if (sp->taillen < MINTRAIL)
191 		sp->taillen = MINTRAIL;
192 	sp->width = MI_WIDTH(mi);
193 	sp->height = MI_HEIGHT(mi);
194 	sp->border = (sp->width + sp->height) / 50;
195 	sp->tick = 0;
196 	sp->rolloverflag = 0;
197 
198 	if (trackmouse && !sp->cursor) {	/* Create an invisible cursor */
199 		Pixmap      bit;
200 		XColor      black;
201 
202 		black.red = 0;
203 		black.green = 0;
204 		black.blue = 0;
205 		black.flags = DoRed | DoGreen | DoBlue;
206 		if ((bit = XCreatePixmapFromBitmapData(display, window,
207 				(char *) "\000", 1, 1, MI_BLACK_PIXEL(mi),
208 				MI_BLACK_PIXEL(mi), 1)) == None) {
209 			free_bees(sp); /* Do not need to free cursor */
210 			return;
211 		}
212 
213 		if ((sp->cursor = XCreatePixmapCursor(display, bit, bit,
214 				 &black, &black, 0, 0)) == None) {
215 			XFreePixmap(display, bit);
216 			free_bees(sp);
217 			return;
218 		}
219 		XFreePixmap(display, bit);
220 	}
221 	XDefineCursor(display, window, sp->cursor);
222 
223 	MI_CLEARWINDOW(mi);
224 
225 	/* Allocate memory. */
226 
227 	if (sp->segs == NULL) {
228 		if (((sp->segs = (XSegment *) malloc(sizeof (XSegment) *
229 			sp->beecount)) == NULL) ||
230 		    ((sp->old_segs = (XSegment *) malloc(sizeof (XSegment) *
231 			sp->beecount)) == NULL) ||
232 		    ((sp->x = (float *) malloc(sizeof (float) * sp->beecount *
233 			sp->taillen)) == NULL) ||
234 		    ((sp->y = (float *) malloc(sizeof (float) * sp->beecount *
235 			sp->taillen)) == NULL) ||
236 		    ((sp->xv = (float *) malloc(sizeof (float) *
237 			sp->beecount)) == NULL) ||
238 		    ((sp->yv = (float *) malloc(sizeof (float) *
239 			sp->beecount)) == NULL)) {
240 			free_swarm_screen(display, sp);
241 			return;
242 		}
243 	}
244 	/* Initialize point positions, velocities, etc. */
245 	if (MI_NPIXELS(mi) > 2)
246 		sp->pix = NRAND(MI_NPIXELS(mi));
247 
248 	/* wasp */
249 	sp->wx[0] = sp->border + NRAND(sp->width - 2 * sp->border);
250 	sp->wy[0] = sp->border + NRAND(sp->height - 2 * sp->border);
251 	sp->wx[1] = sp->wx[0];
252 	sp->wy[1] = sp->wy[0];
253 	sp->wxv = 0;
254 	sp->wyv = 0;
255 
256 	/* bees */
257 	for (b = 0; b < sp->beecount; b++) {
258 		X(0, b) = NRAND(sp->width);
259 		Y(0, b) = NRAND(sp->height);
260 		for (t = 1; t < sp->taillen; t++) {
261 			X(t, b) = X(0, b);
262 			Y(t, b) = Y(0, b);
263 		}
264 		sp->xv[b] = balance_rand(7);
265 		sp->yv[b] = balance_rand(7);
266 	}
267 }
268 
269 ENTRYPOINT void
draw_swarm(ModeInfo * mi)270 draw_swarm(ModeInfo * mi)
271 {
272 	Display    *display = MI_DISPLAY(mi);
273 	Window      window = MI_WINDOW(mi);
274 	GC          gc = MI_GC(mi);
275 	int         b, newlimit;
276 	Bool        track_p = trackmouse;
277 	int         cx, cy;
278 	short       prev;
279 	float       speed;
280 	swarmstruct *sp;
281 
282 	if (swarms == NULL)
283 		return;
284 	sp = &swarms[MI_SCREEN(mi)];
285 	if (sp->segs == NULL)
286 		return;
287 
288 	MI_IS_DRAWN(mi) = True;
289 	if (track_p) {
290 		Window      r, c;
291 		int         rx, ry;
292 		unsigned int m;
293 
294 		(void) XQueryPointer(display, window,
295 				     &r, &c, &rx, &ry, &cx, &cy, &m);
296 		if (cx <= sp->border || cy <= sp->border ||
297 		    cx >= MI_WIDTH(mi) - 1 - sp->border ||
298 		    cy >= MI_HEIGHT(mi) - 1 - sp->border)
299 			track_p = False;
300 	}
301 	/* <=- Wasp -=> */
302 	/* Age the arrays. */
303 	sp->wx[2] = sp->wx[1];
304 	sp->wx[1] = sp->wx[0];
305 	sp->wy[2] = sp->wy[1];
306 	sp->wy[1] = sp->wy[0];
307 	if (track_p) {
308 		sp->wx[0] = cx;
309 		sp->wy[0] = cy;
310 	} else {
311 
312 		/* Accelerate */
313 		sp->wxv += balance_rand(WASPACC);
314 		sp->wyv += balance_rand(WASPACC);
315 
316 		/* Speed Limit Checks */
317 		speed = sqrt((double) sp->wxv * sp->wxv + sp->wyv * sp->wyv);
318 		if (speed > WASPVEL) {
319 			newlimit = (int) ((NRAND(WASPVEL) + WASPVEL / 2) / speed);
320 			sp->wxv *= newlimit;
321 			sp->wyv *= newlimit;
322 		}
323 		/* Move */
324 		sp->wx[0] = sp->wx[1] + sp->wxv;
325 		sp->wy[0] = sp->wy[1] + sp->wyv;
326 
327 		/* Bounce Checks */
328 		if ((sp->wx[0] < sp->border) || (sp->wx[0] > sp->width - sp->border - 1)) {
329 			sp->wxv = -sp->wxv;
330 			sp->wx[0] += sp->wxv;
331 		}
332 		if ((sp->wy[0] < sp->border) || (sp->wy[0] > sp->height - sp->border - 1)) {
333 			sp->wyv = -sp->wyv;
334 			sp->wy[0] += sp->wyv;
335 		}
336 		/* Don't let things settle down. */
337 		sp->xv[NRAND(sp->beecount)] += balance_rand(3);
338 		sp->yv[NRAND(sp->beecount)] += balance_rand(3);
339 	}
340 	/* <=- Bees -=> */
341 	sp->tick = (sp->tick + 1) % (sp->taillen);
342 	prev = (sp->tick) ? sp->tick - 1 : sp->taillen - 1;
343 	if (sp->tick == sp->taillen - 1)
344 		sp->rolloverflag = 1;
345 	for (b = 0; b < sp->beecount; b++) {
346 		int         distance, dx, dy;
347 
348 		/* Accelerate */
349 		dx = (int) (sp->wx[1] - X(prev, b));
350 		dy = (int) (sp->wy[1] - Y(prev, b));
351 		distance = (int) sqrt((double) dx * dx + dy * dy);
352 		if (distance == 0)
353 			distance = 1;
354 		sp->xv[b] += dx * BEEACC / (2 * distance);
355 		sp->yv[b] += dy * BEEACC / (2 * distance);
356 
357 		/* Speed Limit Checks */
358 		speed = sqrt(sp->xv[b] * sp->xv[b] + sp->yv[b] * sp->yv[b]);
359 		if (speed > BEEVEL) {
360 			newlimit = (int) ((NRAND(BEEVEL) + BEEVEL / 2) / speed);
361 			sp->xv[b] *= newlimit;
362 			sp->yv[b] *= newlimit;
363 		}
364 		/* Move */
365 		X(sp->tick, b) = X(prev, b) + sp->xv[b];
366 		Y(sp->tick, b) = Y(prev, b) + sp->yv[b];
367 
368 		/* Fill the segment lists. */
369 		sp->segs[b].x1 = (short) X(sp->tick, b);
370 		sp->segs[b].y1 = (short) Y(sp->tick, b);
371 		sp->segs[b].x2 = (short) X(prev, b);
372 		sp->segs[b].y2 = (short) Y(prev, b);
373 		sp->old_segs[b].x1 = (short) X(((sp->tick+2)%sp->taillen), b);
374 		sp->old_segs[b].y1 = (short) Y(((sp->tick+2)%sp->taillen), b);
375 		sp->old_segs[b].x2 = (short) X(((sp->tick+1)%sp->taillen), b);
376 		sp->old_segs[b].y2 = (short) Y(((sp->tick+1)%sp->taillen), b);
377 	}
378 
379 	XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
380 	XDrawLine(display, window, gc,
381 		  sp->wx[1], sp->wy[1], sp->wx[2], sp->wy[2]);
382 	if (sp->rolloverflag) {
383 		XDrawSegments(display, window, gc, sp->old_segs, sp->beecount);
384 	}
385 	XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
386 	XDrawLine(display, window, gc,
387 		  sp->wx[0], sp->wy[0], sp->wx[1], sp->wy[1]);
388 	if (MI_NPIXELS(mi) > 2) {
389 		XSetForeground(display, gc, MI_PIXEL(mi, sp->pix));
390 		if (++sp->pix >= MI_NPIXELS(mi))
391 			sp->pix = 0;
392 	}
393 	XDrawSegments(display, window, gc, sp->segs, sp->beecount);
394 }
395 
396 ENTRYPOINT void
release_swarm(ModeInfo * mi)397 release_swarm(ModeInfo * mi)
398 {
399 	if (swarms != NULL) {
400 		int         screen;
401 
402 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
403 			free_swarm_screen(MI_DISPLAY(mi), &swarms[screen]);
404 		free(swarms);
405 		swarms = (swarmstruct *) NULL;
406 	}
407 }
408 
409 #ifndef STANDALONE
410 ENTRYPOINT void
refresh_swarm(ModeInfo * mi)411 refresh_swarm(ModeInfo * mi)
412 {
413 	MI_CLEARWINDOW(mi);
414 }
415 #endif
416 
417 XSCREENSAVER_MODULE ("Swarm", swarm)
418 
419 #endif /* MODE_swarm */
420