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