1 /* bubbles.c - frying pan / soft drink in a glass simulation */
2 
3 /*$Id: bubbles.c,v 1.30 2008/07/31 19:27:48 jwz Exp $*/
4 
5 /*
6  *  Copyright (C) 1995-1996 James Macnicol
7  *
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notice appear in all copies and that both that
11  * copyright notice and this permission notice appear in supporting
12  * documentation.  No representations are made about the suitability of this
13  * software for any purpose.  It is provided "as is" without express or
14  * implied warranty.
15  */
16 
17 /*
18  * I got my original inspiration for this by looking at the bottom of a
19  * frying pan while something was cooking and watching the little bubbles
20  * coming off the bottom of the pan as the oil was boiling joining together
21  * to form bigger bubbles and finally to *pop* and disappear.  I had some
22  * time on my hands so I wrote this little xscreensaver module to imitate
23  * it.  Now that it's done it reminds me more of the bubbles you get in
24  * a glass of fizzy soft drink.....
25  *
26  * The problem seemed to be that the position/size etc. of all the bubbles
27  * on the screen had to be remembered and searched through to find when
28  * bubbles hit each other and combined.  To do this more efficiently, the
29  * window/screen is divided up into a square mesh of side length mesh_length
30  * and separate lists of bubbles contained in each cell of the mesh are
31  * kept.  Only the cells in the immediate vicinity of the bubble in question
32  * are searched.  This should make things more efficient although the whole
33  * thing seems to use up too much CPU, but then I'm using an ancient PC so
34  * perhaps it's not surprising .
35  * (Six months after I wrote the above I now have a Pentium with PCI graphics
36  * and things are _much_ nicer.)
37  *
38  * Author:           James Macnicol
39  * Internet E-mail : j-macnicol@adfa.edu.au
40  */
41 
42 #ifdef HAVE_CONFIG_H
43 # include "config.h"
44 #endif
45 
46 #undef DEBUG /* doesn't compile */
47 
48 #include <math.h>
49 #include <limits.h>
50 
51 #ifndef VMS
52 # include <sys/wait.h>
53 #else /* VMS */
54 # if __DECC_VER >= 50200000
55 #  include <sys/wait.h>
56 # endif
57 #endif /* VMS */
58 
59 #ifdef HAVE_UNISTD_H
60 # include <unistd.h>
61 #endif
62 
63 #include "screenhack.h"
64 #include "yarandom.h"
65 #include "bubbles.h"
66 #include "ximage-loader.h"
67 
68 #define FANCY_BUBBLES
69 
70 /*
71  * Public variables
72  */
73 
74 static const char *bubbles_defaults [] = {
75   ".background:		black",
76   ".foreground:		white",
77   "*fpsSolid:		true",
78   "*simple:		false",
79   "*broken:		false",
80   "*delay:		10000",
81   "*quiet:		false",
82   "*mode:		float",
83   "*trails:		false",
84   "*3D:			false",
85   0
86 };
87 
88 static XrmOptionDescRec bubbles_options [] = {
89   { "-simple",          ".simple",      XrmoptionNoArg, "true" },
90 #ifdef FANCY_BUBBLES
91   { "-broken",          ".broken",      XrmoptionNoArg, "true" },
92 #endif
93   { "-quiet",           ".quiet",       XrmoptionNoArg, "true" },
94   { "-3D",          ".3D",      XrmoptionNoArg, "true" },
95   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
96   { "-mode",            ".mode",        XrmoptionSepArg, 0 },
97   { "-drop",            ".mode",        XrmoptionNoArg, "drop" },
98   { "-rise",            ".mode",        XrmoptionNoArg, "rise" },
99   { "-trails",          ".trails",      XrmoptionNoArg, "true" },
100   { 0, 0, 0, 0 }
101 };
102 
103 /*
104  * Private variables
105  */
106 
107 struct state {
108   Display *dpy;
109   Window window;
110 
111   Bubble **mesh;
112   int mesh_length;
113   int mesh_width;
114   int mesh_height;
115   int mesh_cells;
116 
117   int **adjacent_list;
118 
119   int screen_width;
120   int screen_height;
121   int screen_depth;
122   unsigned int default_fg_pixel, default_bg_pixel;
123 
124   int bubble_min_radius;	/* For simple mode only */
125   int bubble_max_radius;
126   long *bubble_areas;
127   int *bubble_droppages;
128   GC draw_gc, erase_gc;
129 
130 #ifdef FANCY_BUBBLES
131   int num_bubble_pixmaps;
132   Bubble_Step **step_pixmaps, **def_list;
133 #endif
134 
135   Bool simple;
136   Bool broken;
137   Bool quiet;
138   Bool threed;
139   Bool drop;
140   Bool trails;
141   int drop_dir;
142   int delay;
143 };
144 
145 static int drop_bubble( struct state *st, Bubble *bb );
146 
147 /*
148  * To prevent forward references, some stuff is up here
149  */
150 
151 static long
calc_bubble_area(struct state * st,int r)152 calc_bubble_area(struct state *st, int r)
153 /* Calculate the area of a bubble of radius r */
154 {
155 #ifdef DEBUG
156   printf("%d %g\n", r,
157 	 10.0 * PI * (double)r * (double)r * (double)r);
158 #endif /* DEBUG */
159   if (st->threed)
160     return (long)(10.0 * PI * (double)r * (double)r * (double)r);
161   else
162     return (long)(10.0 * PI * (double)r * (double)r);
163 }
164 
165 static void *
xmalloc(size_t size)166 xmalloc(size_t size)
167 /* Safe malloc */
168 {
169   void *ret;
170 
171   if ((ret = malloc(size)) == NULL) {
172     fprintf(stderr, "%s: out of memory\n", progname);
173     exit(1);
174   }
175   return ret;
176 }
177 
178 #ifdef DEBUG
179 static void
die_bad_bubble(Bubble * bb)180 die_bad_bubble(Bubble *bb)
181 /* This is for use with GDB */
182 {
183   fprintf(stderr, "Bad bubble detected at 0x%x!\n", (int)bb);
184   exit(1);
185 }
186 #endif
187 
188 static int
null_bubble(Bubble * bb)189 null_bubble(Bubble *bb)
190 /* Returns true if the pointer passed is NULL.  If not then this checks to
191 see if the bubble is valid (i.e. the (x,y) position is valid and the magic
192 number is set correctly.  This only a sanity check for debugging and is
193 turned off if DEBUG isn't set. */
194 {
195   if (bb == (Bubble *)NULL)
196     return 1;
197 #ifdef DEBUG
198   if ((bb->cell_index < 0) || (bb->cell_index > st->mesh_cells)) {
199     fprintf(stderr, "cell_index = %d\n", bb->cell_index);
200     die_bad_bubble(bb);
201   }
202   if (bb->magic != BUBBLE_MAGIC)  {
203     fprintf(stderr, "Magic = %d\n", bb->magic);
204     die_bad_bubble(bb);
205   }
206   if (st->simple) {
207     if ((bb->x < 0) || (bb->x > st->screen_width) ||
208 	(bb->y < 0) || (bb->y > st->screen_height) ||
209 	(bb->radius < st->bubble_min_radius) || (bb->radius >
210 					     st->bubble_max_radius)) {
211       fprintf(stderr,
212 	      "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
213 	      bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
214       die_bad_bubble(bb);
215     }
216 #ifdef FANCY_BUBBLES
217   } else {
218     if ((bb->x < 0) || (bb->x > st->screen_width) ||
219 	(bb->y < 0) || (bb->y > st->screen_height) ||
220 	(bb->radius < st->step_pixmaps[0]->radius) ||
221 	(bb->radius > st->step_pixmaps[st->num_bubble_pixmaps-1]->radius)) {
222       fprintf(stderr,
223 	      "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
224 	      bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
225       die_bad_bubble(bb);
226     }
227 #endif
228   }
229 #endif /* DEBUG */
230   return 0;
231 }
232 
233 #ifdef DEBUG
234 static void
print_bubble_list(Bubble * bb)235 print_bubble_list(Bubble *bb)
236 /* Print list of where all the bubbles are.  For debugging purposes only. */
237 {
238   if (! null_bubble(bb)) {
239     printf("  (%d, %d) %d\n", bb->x, bb->y, bb->radius);
240     print_bubble_list(bb->next);
241   }
242 }
243 #endif /* DEBUG */
244 
245 static void
add_bubble_to_list(Bubble ** list,Bubble * bb)246 add_bubble_to_list(Bubble **list, Bubble *bb)
247 /* Take a pointer to a list of bubbles and stick bb at the head of the
248  list. */
249 {
250   Bubble *head = *list;
251 
252   if (null_bubble(head)) {
253     bb->prev = (Bubble *)NULL;
254     bb->next = (Bubble *)NULL;
255   } else {
256     bb->next = head;
257     bb->prev = (Bubble *)NULL;
258     head->prev = bb;
259   }
260   *list = bb;
261 }
262 
263 
264 /*
265  * Mesh stuff
266  */
267 
268 
269 static void
init_mesh(struct state * st)270 init_mesh (struct state *st)
271 /* Setup the mesh of bubbles */
272 {
273   int i;
274 
275   st->mesh = (Bubble **)xmalloc(st->mesh_cells * sizeof(Bubble *));
276   for (i = 0; i < st->mesh_cells; i++)
277     st->mesh[i] = (Bubble *)NULL;
278 }
279 
280 static int
cell_to_mesh(struct state * st,int x,int y)281 cell_to_mesh(struct state *st, int x, int y)
282 /* convert cell coordinates to mesh index */
283 {
284 #ifdef DEBUG
285   if ((x < 0) || (y < 0)) {
286     fprintf(stderr, "cell_to_mesh: x = %d, y = %d\n", x, y);
287     exit(1);
288   }
289 #endif
290   return ((st->mesh_width * y) + x);
291 }
292 
293 static void
mesh_to_cell(struct state * st,int mi,int * cx,int * cy)294 mesh_to_cell(struct state *st, int mi, int *cx, int *cy)
295 /* convert mesh index into cell coordinates */
296 {
297   *cx = mi % st->mesh_width;
298   *cy = mi / st->mesh_width;
299 }
300 
301 static int
pixel_to_mesh(struct state * st,int x,int y)302 pixel_to_mesh(struct state *st, int x, int y)
303 /* convert screen coordinates into mesh index */
304 {
305   return cell_to_mesh(st, (x / st->mesh_length), (y / st->mesh_length));
306 }
307 
308 static int
verify_mesh_index(struct state * st,int x,int y)309 verify_mesh_index(struct state *st, int x, int y)
310 /* check to see if (x,y) is in the mesh */
311 {
312   if ((x < 0) || (y < 0) || (x >= st->mesh_width) || (y >= st->mesh_height))
313     return (-1);
314   return (cell_to_mesh(st, x, y));
315 }
316 
317 #ifdef DEBUG
318 static void
print_adjacents(int * adj)319 print_adjacents(int *adj)
320 /* Print a list of the cells calculated above.  For debugging only. */
321 {
322   int i;
323 
324   printf("(");
325   for (i = 0; i < 8; i++)
326     printf("%d ", adj[i]);
327   printf(")\n");
328 }
329 #endif /* DEBUG */
330 
331 static void
add_to_mesh(struct state * st,Bubble * bb)332 add_to_mesh(struct state *st, Bubble *bb)
333 /* Add the given bubble to the mesh by sticking it on the front of the
334 list.  bb is already allocated so no need to malloc() anything, just
335 adjust pointers. */
336 {
337 #ifdef DEBUG
338   if (null_bubble(bb)) {
339     fprintf(stderr, "Bad bubble passed to add_to_mesh()!\n");
340     exit(1);
341   }
342 #endif /* DEBUG */
343 
344   add_bubble_to_list(&st->mesh[bb->cell_index], bb);
345 }
346 
347 #ifdef DEBUG
348 static void
print_mesh(struct state * st)349 print_mesh (struct state *st)
350 /* Print the contents of the mesh */
351 {
352   int i;
353 
354   for (i = 0; i < st->mesh_cells; i++) {
355     if (! null_bubble(st->mesh[i])) {
356       printf("Mesh cell %d\n", i);
357       print_bubble_list(st->mesh[i]);
358     }
359   }
360 }
361 
362 static void
valid_mesh(struct state * st)363 valid_mesh (struct state *st)
364 /* Check to see if the mesh is Okay.  For debugging only. */
365 {
366   int i;
367   Bubble *b;
368 
369   for (i = 0; i < st->mesh_cells; i++) {
370     b = st->mesh[i];
371     while (! null_bubble(b))
372       b = b->next;
373   }
374 }
375 
376 static int
total_bubbles(struct state * st)377 total_bubbles (struct state *st)
378 /* Count how many bubbles there are in total.  For debugging only. */
379 {
380   int rv = 0;
381   int i;
382   Bubble *b;
383 
384   for (i = 0; i < st->mesh_cells; i++) {
385     b = st->mesh[i];
386     while (! null_bubble(b)) {
387       rv++;
388       b = b->next;
389     }
390   }
391 
392   return rv;
393 }
394 #endif /* DEBUG */
395 
396 static void
calculate_adjacent_list(struct state * st)397 calculate_adjacent_list (struct state *st)
398 /* Calculate the list of cells adjacent to a particular cell for use
399    later. */
400 {
401   int i;
402   int ix, iy;
403 
404   st->adjacent_list = (int **)xmalloc(st->mesh_cells * sizeof(int *));
405   for (i = 0; i < st->mesh_cells; i++) {
406     st->adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
407     mesh_to_cell(st, i, &ix, &iy);
408     st->adjacent_list[i][0] = verify_mesh_index(st, --ix, --iy);
409     st->adjacent_list[i][1] = verify_mesh_index(st, ++ix, iy);
410     st->adjacent_list[i][2] = verify_mesh_index(st, ++ix, iy);
411     st->adjacent_list[i][3] = verify_mesh_index(st, ix, ++iy);
412     st->adjacent_list[i][4] = verify_mesh_index(st, ix, ++iy);
413     st->adjacent_list[i][5] = verify_mesh_index(st, --ix, iy);
414     st->adjacent_list[i][6] = verify_mesh_index(st, --ix, iy);
415     st->adjacent_list[i][7] = verify_mesh_index(st, ix, --iy);
416     st->adjacent_list[i][8] = i;
417   }
418 }
419 
420 static void
adjust_areas(struct state * st)421 adjust_areas (struct state *st)
422 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
423 {
424   double maxvalue;
425   long maxarea;
426   long factor;
427   int i;
428 
429 #ifdef FANCY_BUBBLES
430   if (st->simple)
431     maxarea = st->bubble_areas[st->bubble_max_radius+1];
432   else
433     maxarea = st->step_pixmaps[st->num_bubble_pixmaps]->area;
434 #else /* !FANCY_BUBBLES */
435   maxarea = st->bubble_areas[st->bubble_max_radius+1];
436 #endif /* !FANCY_BUBBLES */
437   maxvalue = (double)st->screen_width * 2.0 * (double)maxarea;
438   factor = (long)ceil(maxvalue / (double)LONG_MAX);
439   if (factor > 1) {
440     /* Overflow will occur in weighted_mean().  We must divide areas
441        each by factor so it will never do so. */
442 #ifdef FANCY_BUBBLES
443     if (st->simple) {
444       for (i = st->bubble_min_radius; i <= st->bubble_max_radius+1; i++) {
445 	st->bubble_areas[i] /= factor;
446 	if (st->bubble_areas[i] == 0)
447 	  st->bubble_areas[i] = 1;
448       }
449     } else {
450       for (i = 0; i <= st->num_bubble_pixmaps; i++) {
451 #ifdef DEBUG
452 	printf("area = %ld", st->step_pixmaps[i]->area);
453 #endif /* DEBUG */
454 	st->step_pixmaps[i]->area /= factor;
455 	if (st->step_pixmaps[i]->area == 0)
456 	  st->step_pixmaps[i]->area = 1;
457 #ifdef DEBUG
458 	printf("-> %ld\n", st->step_pixmaps[i]->area);
459 #endif /* DEBUG */
460       }
461     }
462 #else /* !FANCY_BUBBLES */
463     for (i = st->bubble_min_radius; i <= st->bubble_max_radius+1; i++) {
464       st->bubble_areas[i] /= factor;
465       if (st->bubble_areas[i] == 0)
466 	st->bubble_areas[i] = 1;
467     }
468 #endif /* !FANCY_BUBBLES */
469   }
470 #ifdef DEBUG
471   printf("maxarea = %ld\n", maxarea);
472   printf("maxvalue = %g\n", maxvalue);
473   printf("LONG_MAX = %ld\n", LONG_MAX);
474   printf("factor = %ld\n", factor);
475 #endif /* DEBUG */
476 }
477 
478 /*
479  * Bubbles stuff
480  */
481 
482 static Bubble *
new_bubble(struct state * st)483 new_bubble (struct state *st)
484 /* Add a new bubble at some random position on the screen of the smallest
485 size. */
486 {
487   Bubble *rv = (Bubble *)xmalloc(sizeof(Bubble));
488 
489   /* Can't use null_bubble() here since magic number hasn't been set */
490   if (rv == (Bubble *)NULL) {
491     fprintf(stderr, "Ran out of memory!\n");
492     exit(1);
493   }
494 
495   if (st->simple) {
496     rv->radius = st->bubble_min_radius;
497     rv->area = st->bubble_areas[st->bubble_min_radius];
498 #ifdef FANCY_BUBBLES
499   } else {
500     rv->step = 0;
501     rv->radius = st->step_pixmaps[0]->radius;
502     rv->area = st->step_pixmaps[0]->area;
503 #endif /* FANCY_BUBBLES */
504   }
505   rv->visible = 0;
506   rv->magic = BUBBLE_MAGIC;
507   rv->x = random() % st->screen_width;
508   rv->y = random() % st->screen_height;
509   rv->cell_index = pixel_to_mesh(st, rv->x, rv->y);
510 
511   return rv;
512 }
513 
514 static void
show_bubble(struct state * st,Bubble * bb)515 show_bubble(struct state *st, Bubble *bb)
516 /* paint the bubble on the screen */
517 {
518   if (null_bubble(bb)) {
519     fprintf(stderr, "NULL bubble passed to show_bubble\n");
520     exit(1);
521   }
522 
523   if (! bb->visible) {
524     bb->visible = 1;
525 
526     if (st->simple) {
527       XDrawArc(st->dpy, st->window, st->draw_gc, (bb->x - bb->radius),
528 	       (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
529 	       360*64);
530     } else {
531 #ifdef FANCY_BUBBLES
532       XSetClipOrigin(st->dpy, st->step_pixmaps[bb->step]->draw_gc,
533 		     (bb->x - bb->radius),
534 		     (bb->y - bb->radius));
535 
536       XCopyArea(st->dpy, st->step_pixmaps[bb->step]->ball, st->window,
537 		st->step_pixmaps[bb->step]->draw_gc,
538 		0, 0, (bb->radius * 2),
539 		(bb->radius * 2),
540 		(bb->x - bb->radius),
541 		(bb->y - bb->radius));
542 #endif /* FANCY_BUBBLES */
543     }
544   }
545 }
546 
547 static void
hide_bubble(struct state * st,Bubble * bb)548 hide_bubble(struct state *st, Bubble *bb)
549 /* erase the bubble */
550 {
551   if (null_bubble(bb)) {
552     fprintf(stderr, "NULL bubble passed to hide_bubble\n");
553     exit(1);
554   }
555 
556   if (bb->visible) {
557     bb->visible = 0;
558 
559     if (st->simple) {
560       XDrawArc(st->dpy, st->window, st->erase_gc, (bb->x - bb->radius),
561 	       (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
562 	       360*64);
563     } else {
564 #ifdef FANCY_BUBBLES
565       if (! st->broken) {
566 	XSetClipOrigin(st->dpy, st->step_pixmaps[bb->step]->erase_gc,
567 		       (bb->x - bb->radius), (bb->y - bb->radius));
568 
569 	XFillRectangle(st->dpy, st->window, st->step_pixmaps[bb->step]->erase_gc,
570 		       (bb->x - bb->radius),
571 		       (bb->y - bb->radius),
572 		       (bb->radius * 2),
573 		       (bb->radius * 2));
574       }
575 #endif /* FANCY_BUBBLES */
576     }
577   }
578 }
579 
580 static void
delete_bubble_in_mesh(struct state * st,Bubble * bb,int keep_bubble)581 delete_bubble_in_mesh(struct state *st, Bubble *bb, int keep_bubble)
582 /* Delete an individual bubble, adjusting list of bubbles around it.
583    If keep_bubble is true then the bubble isn't actually deleted.  We
584    use this to allow bubbles to change mesh cells without reallocating,
585    (it needs this when two bubbles collide and the centre position is
586    recalculated, and this may stray over a mesh boundary). */
587 {
588   if ((!null_bubble(bb->prev)) && (!null_bubble(bb->next))) {
589     bb->prev->next = bb->next;
590     bb->next->prev = bb->prev;
591   } else if ((!null_bubble(bb->prev)) &&
592 	     (null_bubble(bb->next))) {
593     bb->prev->next = (Bubble *)NULL;
594     bb->next = st->mesh[bb->cell_index];
595   } else if ((null_bubble(bb->prev)) &&
596 	     (!null_bubble(bb->next))) {
597     bb->next->prev = (Bubble *)NULL;
598     st->mesh[bb->cell_index] = bb->next;
599     bb->next = st->mesh[bb->cell_index];
600   } else {
601     /* Only item on list */
602     st->mesh[bb->cell_index] = (Bubble *)NULL;
603   }
604   if (! keep_bubble)
605     free(bb);
606 }
607 
608 static unsigned long
ulongsqrint(int x)609 ulongsqrint(int x)
610 /* Saves ugly inline code */
611 {
612   return ((unsigned long)x * (unsigned long)x);
613 }
614 
615 static Bubble *
get_closest_bubble(struct state * st,Bubble * bb)616 get_closest_bubble(struct state *st, Bubble *bb)
617 /* Find the closest bubble touching the this bubble, NULL if none are
618    touching. */
619 {
620   Bubble *rv = (Bubble *)NULL;
621   Bubble *tmp;
622   unsigned long separation2, touchdist2;
623   int dx, dy;
624   unsigned long closest2 = ULONG_MAX;
625   int i;
626 
627 #ifdef DEBUG
628   if (null_bubble(bb)) {
629     fprintf(stderr, "NULL pointer 0x%x passed to get_closest_bubble()!",
630 	    (int)bb);
631     exit(1);
632   }
633 #endif /* DEBUG */
634 
635   for (i = 0; i < 9; i++) {
636     /* There is a bug here where bb->cell_index is negaitve.. */
637 #ifdef DEBUG
638     if ((bb->cell_index < 0) || (bb->cell_index >= st->mesh_cells)) {
639       fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
640       exit(1);
641     }
642 #endif /* DEBUG */
643 /*    printf("%d,", bb->cell_index); */
644     if (st->adjacent_list[bb->cell_index][i] != -1) {
645       tmp = st->mesh[st->adjacent_list[bb->cell_index][i]];
646       while (! null_bubble(tmp)) {
647 	if (tmp != bb) {
648 	  dx = tmp->x - bb->x;
649 	  dy = tmp->y - bb->y;
650 	  separation2 = ulongsqrint(dx) + ulongsqrint(dy);
651 	  /* Add extra leeway so circles _never_ overlap */
652 	  touchdist2 = ulongsqrint(tmp->radius + bb->radius + 2);
653 	  if ((separation2 <= touchdist2) && (separation2 <
654 					      closest2)) {
655 	    rv = tmp;
656 	    closest2 = separation2;
657 	  }
658 	}
659 	tmp = tmp->next;
660       }
661     }
662   }
663 
664   return rv;
665 }
666 
667 #ifdef DEBUG
668 static void
ldr_barf(struct state * st)669 ldr_barf (struct state *st)
670 {
671 }
672 #endif /* DEBUG */
673 
674 static long
long_div_round(long num,long dem)675 long_div_round(long num, long dem)
676 {
677   long divvie, moddo;
678 
679 #ifdef DEBUG
680   if ((num < 0) || (dem < 0)) {
681     fprintf(stderr, "long_div_round: %ld, %ld\n", num, dem);
682     ldr_barf();
683     exit(1);
684   }
685 #endif /* DEBUG */
686 
687   divvie = num / dem;
688   moddo = num % dem;
689   if (moddo > (dem / 2))
690     ++divvie;
691 
692 #ifdef DEBUG
693   if ((divvie < 0) || (moddo < 0)) {
694     fprintf(stderr, "long_div_round: %ld, %ld\n", divvie, moddo);
695     ldr_barf();
696     exit(1);
697   }
698 #endif /* DEBUG */
699 
700   return divvie;
701 }
702 
703 static int
weighted_mean(int n1,int n2,long w1,long w2)704 weighted_mean(int n1, int n2, long w1, long w2)
705 /* Mean of n1 and n2 respectively weighted by weights w1 and w2. */
706 {
707 #ifdef DEBUG
708   if ((w1 <= 0) || (w2 <= 0)) {
709     fprintf(stderr,
710 	    "Bad weights passed to weighted_mean() - (%d, %d, %ld, %ld)!\n",
711 	    n1, n2, w1, w2);
712     exit(1);
713   }
714 #endif /* DEBUG */
715   return ((int)long_div_round((long)n1 * w1 + (long)n2 * w2,
716                            w1 + w2));
717 }
718 
719 static int
bubble_eat(struct state * st,Bubble * diner,Bubble * food)720 bubble_eat(struct state *st, Bubble *diner, Bubble *food)
721 /* The diner eats the food.  Returns true (1) if the diner still exists */
722 {
723   int i;
724   int newmi;
725 
726 #ifdef DEBUG
727   if ((null_bubble(diner)) || (null_bubble(food))) {
728     fprintf(stderr, "Bad bubbles passed to bubble_eat()!\n");
729     exit(1);
730   }
731 #endif /* DEBUG */
732 
733   /* We hide the diner even in the case that it doesn't grow so that
734      if the food overlaps its boundary it is replaced. This could
735      probably be solved by letting bubbles eat others which are close
736      but not quite touching.  It's probably worth it, too, since we
737      would then not have to redraw bubbles which don't change in
738      size. */
739 
740   hide_bubble(st, diner);
741   hide_bubble(st, food);
742   diner->x = weighted_mean(diner->x, food->x, diner->area, food->area);
743   diner->y = weighted_mean(diner->y, food->y, diner->area, food->area);
744   newmi = pixel_to_mesh(st, diner->x, diner->y);
745   diner->area += food->area;
746   delete_bubble_in_mesh(st, food, DELETE_BUBBLE);
747 
748   if (st->drop) {
749 	if ((st->simple) && (diner->area > st->bubble_areas[st->bubble_max_radius])) {
750 	  diner->area = st->bubble_areas[st->bubble_max_radius];
751 	}
752 #ifdef FANCY_BUBBLES
753 	if ((! st->simple) && (diner->area > st->step_pixmaps[st->num_bubble_pixmaps]->area)) {
754 	  diner->area = st->step_pixmaps[st->num_bubble_pixmaps]->area;
755 	}
756 #endif /* FANCY_BUBBLES */
757   }
758   else {
759 	if ((st->simple) && (diner->area > st->bubble_areas[st->bubble_max_radius])) {
760 	  delete_bubble_in_mesh(st, diner, DELETE_BUBBLE);
761 	  return 0;
762 	}
763 #ifdef FANCY_BUBBLES
764 	if ((! st->simple) && (diner->area >
765 					   st->step_pixmaps[st->num_bubble_pixmaps]->area)) {
766 	  delete_bubble_in_mesh(st, diner, DELETE_BUBBLE);
767 	  return 0;
768 	}
769 #endif /* FANCY_BUBBLES */
770   }
771 
772   if (st->simple) {
773     if (diner->area > st->bubble_areas[diner->radius + 1]) {
774       /* Move the bubble to a new radius */
775       i = diner->radius;
776       while ((i < st->bubble_max_radius - 1) && (diner->area > st->bubble_areas[i+1]))
777 		++i;
778       diner->radius = i;
779     }
780     show_bubble(st, diner);
781 #ifdef FANCY_BUBBLES
782   } else {
783     if (diner->area > st->step_pixmaps[diner->step+1]->area) {
784       i = diner->step;
785       while ((i < st->num_bubble_pixmaps - 1) && (diner->area > st->step_pixmaps[i+1]->area))
786 		++i;
787       diner->step = i;
788       diner->radius = st->step_pixmaps[diner->step]->radius;
789     }
790     show_bubble(st, diner);
791 #endif /* FANCY_BUBBLES */
792   }
793 
794   /* Now adjust locations and cells if need be */
795   if (newmi != diner->cell_index) {
796     delete_bubble_in_mesh(st, diner, KEEP_BUBBLE);
797     diner->cell_index = newmi;
798     add_to_mesh(st, diner);
799   }
800 
801   return 1;
802 }
803 
804 static int
merge_bubbles(struct state * st,Bubble * b1,Bubble * b2)805 merge_bubbles(struct state *st, Bubble *b1, Bubble *b2)
806 /* These two bubbles merge into one.  If the first one wins out return
807 1 else return 2.  If there is no winner (it explodes) then return 0 */
808 {
809   int b1size, b2size;
810 
811   b1size = b1->area;
812   b2size = b2->area;
813 
814 #ifdef DEBUG
815   if ((null_bubble(b1) || null_bubble(b2))) {
816     fprintf(stderr, "NULL bubble passed to merge_bubbles()!\n");
817     exit(1);
818   }
819 #endif /* DEBUG */
820 
821   if (b1 == b2) {
822     hide_bubble(st, b1);
823     delete_bubble_in_mesh(st, b1, DELETE_BUBBLE);
824     return 0;
825   }
826 
827   if (b1size > b2size) {
828     switch (bubble_eat(st, b1, b2)) {
829     case 0:
830       return 0;
831       break;
832     case 1:
833       return 1;
834       break;
835     default:
836       break;
837     }
838   } else if (b1size < b2size) {
839     switch (bubble_eat(st, b2, b1)) {
840     case 0:
841       return 0;
842       break;
843     case 1:
844       return 2;
845       break;
846     default:
847       break;
848     }
849   } else {
850     if ((random() % 2) == 0) {
851       switch (bubble_eat(st, b1, b2)) {
852       case 0:
853 	return 0;
854 	break;
855       case 1:
856 	return 1;
857 	break;
858       default:
859 	break;
860       }
861     } else {
862       switch (bubble_eat(st, b2, b1)) {
863       case 0:
864 	return 0;
865 	break;
866       case 1:
867 	return 2;
868 	break;
869       default:
870 	break;
871       }
872     }
873   }
874   fprintf(stderr, "An error occurred in merge_bubbles()\n");
875   exit(1);
876 }
877 
878 static void
insert_new_bubble(struct state * st,Bubble * tmp)879 insert_new_bubble(struct state *st, Bubble *tmp)
880 /* Calculates which bubbles are eaten when a new bubble tmp is
881    inserted.  This is called recursively in case when a bubble grows
882    it eats others.  Careful to pick out disappearing bubbles. */
883 {
884   Bubble *nextbub;
885   Bubble *touch;
886 
887 #ifdef DEBUG
888   if (null_bubble(tmp)) {
889     fprintf(stderr, "Bad bubble passed to insert_new_bubble()!\n");
890     exit(1);
891   }
892 #endif /* DEBUG */
893 
894   nextbub = tmp;
895   touch = get_closest_bubble(st, nextbub);
896   if (null_bubble(touch))
897 	return;
898 
899   while (1) {
900 
901 	/* Merge all touching bubbles */
902 	while (! null_bubble(touch)) {
903 	  switch (merge_bubbles(st, nextbub, touch)) {
904 	  case 2:
905 		/* touch ate nextbub and survived */
906 		nextbub = touch;
907 		break;
908 	  case 1:
909 		/* nextbub ate touch and survived */
910 		break;
911 	  case 0:
912 		/* somebody ate someone else but they exploded */
913 		nextbub = (Bubble *)NULL;
914 		break;
915 	  default:
916 		/* something went wrong */
917 		fprintf(stderr, "Error occurred in insert_new_bubble()\n");
918 		exit(1);
919 	  }
920 
921 	  /* Check to see if any bubble survived. */
922 	  if (null_bubble(nextbub))
923 		break;
924 
925 	  /* Check to see if there are any other bubbles still in the area
926 		 and if we need to do this all over again for them. */
927 	  touch = get_closest_bubble(st, nextbub);
928 	}
929 
930 	if (null_bubble(nextbub))
931 	  break;
932 
933 	/* Shift bubble down.  Break if we run off the screen. */
934 	if (st->drop) {
935 	  if (drop_bubble( st, nextbub ) == -1)
936 		break;
937 	}
938 
939 	/* Check to see if there are any other bubbles still in the area
940 	   and if we need to do this all over again for them. */
941 	touch = get_closest_bubble(st, nextbub);
942 	if (null_bubble(touch)) {
943 	  /* We also continue every so often if we're dropping and the bubble is at max size */
944 	  if (st->drop) {
945 		if (st->simple) {
946 		  if ((nextbub->area >= st->bubble_areas[st->bubble_max_radius - 1]) && (random() % 2 == 0))
947 			continue;
948 		}
949 #ifdef FANCY_BUBBLES
950 		else {
951 		  if ((nextbub->step >= st->num_bubble_pixmaps - 1) && (random() % 2 == 0))
952 			continue;
953 		}
954 #endif /* FANCY_BUBBLES */
955       }
956 	  break;
957 	}
958 
959   }
960 }
961 
962 
963 static void
leave_trail(struct state * st,Bubble * bb)964 leave_trail(struct state *st,  Bubble *bb )
965 {
966   Bubble *tmp;
967 
968   tmp = new_bubble(st);
969   tmp->x = bb->x;
970   tmp->y = bb->y - ((bb->radius + 10) * st->drop_dir);
971   tmp->cell_index = pixel_to_mesh(st, tmp->x, tmp->y);
972   add_to_mesh(st, tmp);
973   insert_new_bubble(st, tmp);
974   show_bubble( st, tmp );
975 }
976 
977 
978 static int
drop_bubble(struct state * st,Bubble * bb)979 drop_bubble( struct state *st, Bubble *bb )
980 {
981   int newmi;
982 
983   hide_bubble( st, bb );
984 
985   if (st->simple)
986 	(bb->y) += (st->bubble_droppages[bb->radius] * st->drop_dir);
987 #ifdef FANCY_BUBBLES
988   else
989 	(bb->y) += (st->step_pixmaps[bb->step]->droppage * st->drop_dir);
990 #endif /* FANCY_BUBBLES */
991   if ((bb->y < 0) || (bb->y > st->screen_height)) {
992 	delete_bubble_in_mesh( st, bb, DELETE_BUBBLE );
993 	return -1;
994   }
995 
996   show_bubble( st, bb );
997 
998   /* Now adjust locations and cells if need be */
999   newmi = pixel_to_mesh(st, bb->x, bb->y);
1000   if (newmi != bb->cell_index) {
1001     delete_bubble_in_mesh(st, bb, KEEP_BUBBLE);
1002     bb->cell_index = newmi;
1003     add_to_mesh(st, bb);
1004   }
1005 
1006   if (st->trails) {
1007 	if (st->simple) {
1008 	  if ((bb->area >= st->bubble_areas[st->bubble_max_radius - 1]) && (random() % 2 == 0))
1009 		leave_trail( st, bb );
1010 	}
1011 #ifdef FANCY_BUBBLES
1012 	else {
1013 	  if ((bb->step >= st->num_bubble_pixmaps - 1) && (random() % 2 == 0))
1014 		leave_trail( st, bb );
1015 	}
1016 #endif /* FANCY_BUBBLES */
1017   }
1018 
1019   return 0;
1020 }
1021 
1022 
1023 #ifdef DEBUG
1024 static int
get_length_of_bubble_list(Bubble * bb)1025 get_length_of_bubble_list(Bubble *bb)
1026 {
1027   Bubble *tmp = bb;
1028   int rv = 0;
1029 
1030   while (! null_bubble(tmp)) {
1031     rv++;
1032     tmp = tmp->next;
1033   }
1034 
1035   return rv;
1036 }
1037 #endif /* DEBUG */
1038 
1039 /*
1040  * Pixmap stuff used regardless of whether file I/O is available.  Must
1041  * still check for XPM, though!
1042  */
1043 
1044 #ifdef FANCY_BUBBLES
1045 
1046 /*
1047  * Pixmaps without file I/O (but do have XPM)
1048  */
1049 
1050 static void
pixmap_sort(Bubble_Step ** head,int numelems)1051 pixmap_sort(Bubble_Step **head, int numelems)
1052 /* Couldn't get qsort to work right with this so I wrote my own.  This puts
1053 the numelems length array with first element at head into order of radius.
1054 */
1055 {
1056   Bubble_Step tmp;
1057   Bubble_Step *least = 0;
1058   int minradius = INT_MAX;
1059   int i;
1060 
1061   for (i = 0; i < numelems; i++) {
1062     if (head[i]->radius < minradius) {
1063       least = head[i];
1064       minradius = head[i]->radius;
1065     }
1066   }
1067   if (*head != least) {
1068     memcpy(&tmp, least, sizeof(Bubble_Step));
1069     memcpy(least, *head, sizeof(Bubble_Step));
1070     memcpy(*head, &tmp, sizeof(Bubble_Step));
1071   }
1072 
1073   if (numelems > 2)
1074     pixmap_sort(&head[1], numelems-1);
1075 }
1076 
1077 static int
extrapolate(int i1,int i2)1078 extrapolate(int i1, int i2)
1079 {
1080   return (i2 + (i2 - i1));
1081 }
1082 
1083 static void
make_pixmap_array(struct state * st,Bubble_Step * list)1084 make_pixmap_array(struct state *st, Bubble_Step *list)
1085 /* From a linked list of bubbles construct the array step_pixmaps */
1086 {
1087   Bubble_Step *tmp = list;
1088   int ind;
1089 #ifdef DEBUG
1090   int prevrad = -1;
1091 #endif
1092 
1093   if (list == (Bubble_Step *)NULL) {
1094     fprintf(stderr, "NULL list passed to make_pixmap_array\n");
1095     exit(1);
1096   }
1097 
1098   st->num_bubble_pixmaps = 1;
1099   while(tmp->next != (Bubble_Step *)NULL) {
1100     tmp = tmp->next;
1101     ++st->num_bubble_pixmaps;
1102   }
1103 
1104   if (st->num_bubble_pixmaps < 2) {
1105     fprintf(stderr, "Must be at least two bubbles in file\n");
1106     exit(1);
1107   }
1108 
1109   st->step_pixmaps = (Bubble_Step **)xmalloc((st->num_bubble_pixmaps + 1) *
1110 					 sizeof(Bubble_Step *));
1111 
1112   /* Copy them blindly into the array for sorting. */
1113   ind = 0;
1114   tmp = list;
1115   do {
1116     st->step_pixmaps[ind++] = tmp;
1117     tmp = tmp->next;
1118   } while(tmp != (Bubble_Step *)NULL);
1119 
1120   /* We make another bubble beyond the ones with pixmaps so that the final
1121      bubble hangs around and doesn't pop immediately.  It's radius and area
1122      are found by extrapolating from the largest two bubbles with pixmaps. */
1123 
1124   st->step_pixmaps[st->num_bubble_pixmaps] =
1125     (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1126   st->step_pixmaps[st->num_bubble_pixmaps]->radius = INT_MAX;
1127 
1128   pixmap_sort(st->step_pixmaps, (st->num_bubble_pixmaps + 1));
1129 
1130 #ifdef DEBUG
1131   if (st->step_pixmaps[st->num_bubble_pixmaps]->radius != INT_MAX) {
1132     fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
1133   }
1134 #endif /* DEBUG */
1135 
1136   st->step_pixmaps[st->num_bubble_pixmaps]->radius =
1137     extrapolate(st->step_pixmaps[st->num_bubble_pixmaps-2]->radius,
1138 		st->step_pixmaps[st->num_bubble_pixmaps-1]->radius);
1139   st->step_pixmaps[st->num_bubble_pixmaps]->area =
1140     calc_bubble_area(st, st->step_pixmaps[st->num_bubble_pixmaps]->radius);
1141 
1142 
1143 #ifdef DEBUG
1144   /* Now check for correct order */
1145   for (ind = 0; ind < st->num_bubble_pixmaps; ind++) {
1146     if (prevrad > 0) {
1147       if (st->step_pixmaps[ind]->radius < prevrad) {
1148 	fprintf(stderr, "Pixmaps not in ascending order of radius\n");
1149 	exit(1);
1150       }
1151     }
1152     prevrad = st->step_pixmaps[ind]->radius;
1153   }
1154 #endif /* DEBUG */
1155 
1156   /* Now populate the droppage values */
1157   for (ind = 0; ind < st->num_bubble_pixmaps; ind++)
1158 	  st->step_pixmaps[ind]->droppage = MAX_DROPPAGE * ind / st->num_bubble_pixmaps;
1159 }
1160 
1161 static void
make_pixmap_from_default(struct state * st,const unsigned char * png_data,unsigned long data_size,Bubble_Step * bl)1162 make_pixmap_from_default(struct state *st,
1163                          const unsigned char *png_data,
1164                          unsigned long data_size,
1165                          Bubble_Step *bl)
1166 /* Read pixmap data which has been compiled into the program and a pointer
1167  to which has been passed.
1168 
1169  This is virtually copied verbatim from make_pixmap_from_file() above and
1170 changes made to either should be propagated onwards! */
1171 {
1172   XGCValues gcv;
1173 
1174 #ifdef DEBUG
1175   if (pixmap_data == (char **)0) {
1176     fprintf(stderr, "make_pixmap_from_default(): NULL passed\n");
1177     exit(1);
1178   }
1179 #endif
1180 
1181   if (bl == (Bubble_Step *)NULL) {
1182     fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1183     exit(1);
1184   }
1185 
1186 #ifdef FANCY_BUBBLES
1187   {
1188     int w, h;
1189     bl->ball = image_data_to_pixmap (st->dpy, st->window, png_data, data_size,
1190                                      &w, &h, &bl->shape_mask);
1191     bl->radius = MAX(w, h) / 2;
1192     bl->area = calc_bubble_area(st, bl->radius);
1193   }
1194 #endif /* FANCY_BUBBLES */
1195 
1196   gcv.foreground = st->default_fg_pixel;
1197   gcv.function = GXcopy;
1198   bl->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1199   XSetClipMask(st->dpy, bl->draw_gc, bl->shape_mask);
1200 
1201   gcv.foreground = st->default_bg_pixel;
1202   gcv.function = GXcopy;
1203   bl->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1204   XSetClipMask(st->dpy, bl->erase_gc, bl->shape_mask);
1205 }
1206 
1207 static void
default_to_pixmaps(struct state * st)1208 default_to_pixmaps (struct state *st)
1209 /* Make pixmaps out of default ball data stored in bubbles_default.c */
1210 {
1211   int i;
1212   Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1213   Bubble_Step *newpix, *tmppix;
1214 
1215   init_default_bubbles();
1216 
1217   st->def_list = (Bubble_Step **)xmalloc((num_default_bubbles + 1) *
1218 					 sizeof(Bubble_Step *));
1219   for (i = 0; i < num_default_bubbles; i++) {
1220     newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1221     make_pixmap_from_default(st,
1222                              default_bubbles[i].png,
1223                              default_bubbles[i].size,
1224                              newpix);
1225     /* Now add to list */
1226     st->def_list[i] = newpix;
1227     if (pixmap_list == (Bubble_Step *)NULL) {
1228       pixmap_list = newpix;
1229     } else {
1230       tmppix = pixmap_list;
1231       while (tmppix->next != (Bubble_Step *)NULL)
1232 	tmppix = tmppix->next;
1233       tmppix->next = newpix;
1234     }
1235     newpix->next = (Bubble_Step *)NULL;
1236   }
1237 
1238   /* Finally construct step_pixmaps[] */
1239   make_pixmap_array(st, pixmap_list);
1240 }
1241 
1242 #endif /* FANCY_BUBBLES */
1243 
1244 
1245 /*
1246  * Main stuff
1247  */
1248 
1249 
1250 static void
get_resources(struct state * st)1251 get_resources(struct state *st)
1252 /* Get the appropriate X resources and warn about any inconsistencies. */
1253 {
1254   Bool rise;
1255   XWindowAttributes xgwa;
1256   Colormap cmap;
1257   char *s;
1258   XGetWindowAttributes (st->dpy, st->window, &xgwa);
1259   cmap = xgwa.colormap;
1260 
1261   st->threed = get_boolean_resource(st->dpy, "3D", "Boolean");
1262   st->quiet = get_boolean_resource(st->dpy, "quiet", "Boolean");
1263   st->simple = get_boolean_resource(st->dpy, "simple", "Boolean");
1264   /* Forbid rendered bubbles on monochrome displays */
1265   if ((mono_p) && (! st->simple)) {
1266     if (! st->quiet)
1267       fprintf(stderr,
1268 	      "Rendered bubbles not supported on monochrome displays\n");
1269     st->simple = True;
1270   }
1271   st->delay = get_integer_resource(st->dpy, "delay", "Integer");
1272 
1273   s = get_string_resource (st->dpy, "mode", "Mode");
1274   rise = False;
1275   if (!s || !*s || !strcasecmp (s, "float"))
1276     ;
1277   else if (!strcasecmp (s, "rise"))
1278     rise = True;
1279   else if (!strcasecmp (s, "drop"))
1280     st->drop = True;
1281   else
1282     fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
1283 
1284   if (s) free (s);
1285 
1286   st->trails = get_boolean_resource(st->dpy, "trails", "Boolean");
1287   st->drop_dir = (st->drop ? 1 : -1);
1288   if (st->drop || rise)
1289 	st->drop = 1;
1290 
1291   st->default_fg_pixel = get_pixel_resource (st->dpy,
1292 					 cmap, "foreground", "Foreground");
1293   st->default_bg_pixel = get_pixel_resource (st->dpy,
1294 					 cmap, "background", "Background");
1295 
1296   if (st->simple) {
1297     /* This is easy */
1298     st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
1299     if (st->broken)
1300       if (! st->quiet)
1301 	fprintf(stderr, "-broken not available in simple mode\n");
1302   } else {
1303 #ifndef FANCY_BUBBLES
1304     st->simple = 1;
1305 #else  /* FANCY_BUBBLES */
1306     st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
1307 #endif /* FANCY_BUBBLES */
1308   }
1309 }
1310 
1311 static void *
bubbles_init(Display * dpy,Window window)1312 bubbles_init (Display *dpy, Window window)
1313 {
1314   struct state *st = (struct state *) calloc (1, sizeof(*st));
1315   XGCValues gcv;
1316   XWindowAttributes xgwa;
1317   int i;
1318 
1319   st->dpy = dpy;
1320   st->window = window;
1321 
1322   get_resources(st);
1323 
1324   XGetWindowAttributes (st->dpy, st->window, &xgwa);
1325 
1326 #ifdef DEBUG
1327   printf("sizof(int) on this platform is %d\n", sizeof(int));
1328   printf("sizof(long) on this platform is %d\n", sizeof(long));
1329 #endif /* DEBUG */
1330 
1331   st->screen_width = xgwa.width;
1332   st->screen_height = xgwa.height;
1333   st->screen_depth = xgwa.depth;
1334 
1335   if (st->simple) {
1336     /* These are pretty much plucked out of the air */
1337     st->bubble_min_radius = (int)(0.006*(double)(MIN(st->screen_width,
1338 						 st->screen_height)));
1339     st->bubble_max_radius = (int)(0.045*(double)(MIN(st->screen_width,
1340 						 st->screen_height)));
1341     /* Some trivial values */
1342     if (st->bubble_min_radius < 1)
1343       st->bubble_min_radius = 1;
1344     if (st->bubble_max_radius <= st->bubble_min_radius)
1345       st->bubble_max_radius = st->bubble_min_radius + 1;
1346 
1347     st->mesh_length = (2 * st->bubble_max_radius) + 3;
1348 
1349     /* store area of each bubble of certain radius as number of 1/10s of
1350        a pixel area.  PI is defined in <math.h> */
1351     st->bubble_areas = (long *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
1352     for (i = 0; i < st->bubble_min_radius; i++)
1353       st->bubble_areas[i] = 0;
1354     for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
1355       st->bubble_areas[i] = calc_bubble_area(st, i);
1356 
1357 	/* Now populate the droppage values */
1358     st->bubble_droppages = (int *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
1359     for (i = 0; i < st->bubble_min_radius; i++)
1360       st->bubble_droppages[i] = 0;
1361     for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
1362       st->bubble_droppages[i] = MAX_DROPPAGE * (i - st->bubble_min_radius) / (st->bubble_max_radius - st->bubble_min_radius);
1363 
1364     st->mesh_length = (2 * st->bubble_max_radius) + 3;
1365   } else {
1366 #ifndef FANCY_BUBBLES
1367     fprintf(stderr,
1368 	    "Bug: simple mode code not set but FANCY_BUBBLES not defined\n");
1369     exit(1);
1370 #else  /* FANCY_BUBBLES */
1371     /* Make sure all #ifdef sort of things have been taken care of in
1372        get_resources(). */
1373     default_to_pixmaps(st);
1374 
1375     /* Set mesh length */
1376     st->mesh_length = (2 * st->step_pixmaps[st->num_bubble_pixmaps-1]->radius) + 3;
1377 #endif /* FANCY_BUBBLES */
1378 
1379     /* Am I missing something in here??? */
1380   }
1381 
1382   st->mesh_width = (st->screen_width / st->mesh_length) + 1;
1383   st->mesh_height = (st->screen_height / st->mesh_length) + 1;
1384   st->mesh_cells = st->mesh_width * st->mesh_height;
1385   init_mesh(st);
1386 
1387   calculate_adjacent_list(st);
1388 
1389   adjust_areas(st);
1390 
1391   /* Graphics contexts for simple mode */
1392   if (st->simple) {
1393     gcv.foreground = st->default_fg_pixel;
1394     st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1395     gcv.foreground = st->default_bg_pixel;
1396     st->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1397   }
1398 
1399   XClearWindow (st->dpy, st->window);
1400 
1401 # ifndef FANCY_BUBBLES
1402   st->simple = True;
1403 # endif
1404 
1405   return st;
1406 }
1407 
1408 static unsigned long
bubbles_draw(Display * dpy,Window window,void * closure)1409 bubbles_draw (Display *dpy, Window window, void *closure)
1410 {
1411   struct state *st = (struct state *) closure;
1412   int i;
1413   for (i = 0; i < 5; i++)
1414     {
1415       Bubble *tmp = new_bubble(st);
1416       add_to_mesh(st, tmp);
1417       insert_new_bubble(st, tmp);
1418     }
1419   return st->delay;
1420 }
1421 
1422 
1423 static void
bubbles_reshape(Display * dpy,Window window,void * closure,unsigned int w,unsigned int h)1424 bubbles_reshape (Display *dpy, Window window, void *closure,
1425                  unsigned int w, unsigned int h)
1426 {
1427 }
1428 
1429 static Bool
bubbles_event(Display * dpy,Window window,void * closure,XEvent * event)1430 bubbles_event (Display *dpy, Window window, void *closure, XEvent *event)
1431 {
1432   return False;
1433 }
1434 
1435 static void
free_bubble(Display * dpy,Bubble_Step * b)1436 free_bubble (Display *dpy, Bubble_Step *b)
1437 {
1438   if (b->ball) XFreePixmap (dpy, b->ball);
1439   if (b->shape_mask) XFreePixmap (dpy, b->shape_mask);
1440   if (b->draw_gc) XFreeGC (dpy, b->draw_gc);
1441   if (b->erase_gc) XFreeGC (dpy, b->erase_gc);
1442   free (b);
1443 }
1444 
1445 static void
bubbles_free(Display * dpy,Window window,void * closure)1446 bubbles_free (Display *dpy, Window window, void *closure)
1447 {
1448   struct state *st = (struct state *) closure;
1449   int i;
1450 
1451   /* #### The lifetime of objects in this program is fucking incomprehensible.
1452      I give up. */
1453 
1454   /* for (i = 0; i < num_default_bubbles; i++)
1455     free_bubble (dpy, st->def_list[i]); */
1456   for (i = 0; i < st->num_bubble_pixmaps; i++)
1457     free_bubble (dpy, st->step_pixmaps[i]);
1458   for (i = 0; i < st->mesh_cells; i++)
1459     free (st->adjacent_list[i]);
1460   /* for (i = 0; i < st->mesh_cells; i++)
1461     free_bubble (dpy, st->mesh[i]); */
1462   if (st->draw_gc) XFreeGC (dpy, st->draw_gc);
1463   if (st->erase_gc) XFreeGC (dpy, st->erase_gc);
1464   free (st->adjacent_list);
1465   free (st->def_list);
1466   free (st->step_pixmaps);
1467   free (st->mesh);
1468   free (st);
1469 }
1470 
1471 XSCREENSAVER_MODULE ("Bubbles", bubbles)
1472