From http://www.jwz.org/xscreensaver/xscreensaver-5.39.tar.gz
[xscreensaver] / hacks / bubbles.c
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;
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
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 *
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 
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
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 
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 
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 
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
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 
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
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
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 
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 
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 
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 
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
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 
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
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 *
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 
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 
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 
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 
609 ulongsqrint(int x)
610 /* Saves ugly inline code */
611 {
612   return ((unsigned long)x * (unsigned long)x);
613 }
614
615 static Bubble *
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
669 ldr_barf (struct state *st)
670 {
671 }
672 #endif /* DEBUG */
673
674 static long
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
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
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
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 
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
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
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
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 
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
1078 extrapolate(int i1, int i2)
1079 {
1080   return (i2 + (i2 - i1));
1081 }
1082
1083 static void 
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
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 
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   for (i = 0; i < num_default_bubbles; i++) {
1218     newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1219     make_pixmap_from_default(st,
1220                              default_bubbles[i].png, 
1221                              default_bubbles[i].size,
1222                              newpix);
1223     /* Now add to list */
1224     if (pixmap_list == (Bubble_Step *)NULL) {
1225       pixmap_list = newpix;
1226     } else {
1227       tmppix = pixmap_list;
1228       while (tmppix->next != (Bubble_Step *)NULL)
1229         tmppix = tmppix->next;
1230       tmppix->next = newpix;
1231     }
1232     newpix->next = (Bubble_Step *)NULL;
1233   }
1234   
1235   /* Finally construct step_pixmaps[] */
1236   make_pixmap_array(st, pixmap_list);
1237 }
1238
1239 #endif /* FANCY_BUBBLES */
1240
1241
1242 /* 
1243  * Main stuff 
1244  */
1245
1246
1247 static void 
1248 get_resources(struct state *st)
1249 /* Get the appropriate X resources and warn about any inconsistencies. */
1250 {
1251   Bool rise;
1252   XWindowAttributes xgwa;
1253   Colormap cmap;
1254   char *s;
1255   XGetWindowAttributes (st->dpy, st->window, &xgwa);
1256   cmap = xgwa.colormap;
1257
1258   st->threed = get_boolean_resource(st->dpy, "3D", "Boolean");
1259   st->quiet = get_boolean_resource(st->dpy, "quiet", "Boolean");
1260   st->simple = get_boolean_resource(st->dpy, "simple", "Boolean");
1261   /* Forbid rendered bubbles on monochrome displays */
1262   if ((mono_p) && (! st->simple)) {
1263     if (! st->quiet)
1264       fprintf(stderr,
1265               "Rendered bubbles not supported on monochrome displays\n");
1266     st->simple = True;
1267   }
1268   st->delay = get_integer_resource(st->dpy, "delay", "Integer");
1269
1270   s = get_string_resource (st->dpy, "mode", "Mode");
1271   rise = False;
1272   if (!s || !*s || !strcasecmp (s, "float"))
1273     ;
1274   else if (!strcasecmp (s, "rise"))
1275     rise = True;
1276   else if (!strcasecmp (s, "drop"))
1277     st->drop = True;
1278   else
1279     fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
1280
1281   st->trails = get_boolean_resource(st->dpy, "trails", "Boolean");
1282   st->drop_dir = (st->drop ? 1 : -1);
1283   if (st->drop || rise)
1284         st->drop = 1;
1285
1286   st->default_fg_pixel = get_pixel_resource (st->dpy,
1287                                          cmap, "foreground", "Foreground");
1288   st->default_bg_pixel = get_pixel_resource (st->dpy,
1289                                          cmap, "background", "Background");
1290
1291   if (st->simple) {
1292     /* This is easy */
1293     st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
1294     if (st->broken)
1295       if (! st->quiet)
1296         fprintf(stderr, "-broken not available in simple mode\n");
1297   } else {
1298 #ifndef FANCY_BUBBLES
1299     st->simple = 1;
1300 #else  /* FANCY_BUBBLES */
1301     st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
1302 #endif /* FANCY_BUBBLES */
1303   }
1304 }
1305
1306 static void *
1307 bubbles_init (Display *dpy, Window window)
1308 {
1309   struct state *st = (struct state *) calloc (1, sizeof(*st));
1310   XGCValues gcv;
1311   XWindowAttributes xgwa;
1312   int i;
1313
1314   st->dpy = dpy;
1315   st->window = window;
1316
1317   get_resources(st);
1318
1319   XGetWindowAttributes (st->dpy, st->window, &xgwa);
1320
1321 #ifdef DEBUG
1322   printf("sizof(int) on this platform is %d\n", sizeof(int));
1323   printf("sizof(long) on this platform is %d\n", sizeof(long));
1324 #endif /* DEBUG */
1325
1326   st->screen_width = xgwa.width;
1327   st->screen_height = xgwa.height;
1328   st->screen_depth = xgwa.depth;
1329
1330   if (st->simple) {
1331     /* These are pretty much plucked out of the air */
1332     st->bubble_min_radius = (int)(0.006*(double)(MIN(st->screen_width, 
1333                                                  st->screen_height)));
1334     st->bubble_max_radius = (int)(0.045*(double)(MIN(st->screen_width,
1335                                                  st->screen_height)));
1336     /* Some trivial values */
1337     if (st->bubble_min_radius < 1)
1338       st->bubble_min_radius = 1;
1339     if (st->bubble_max_radius <= st->bubble_min_radius)
1340       st->bubble_max_radius = st->bubble_min_radius + 1;
1341
1342     st->mesh_length = (2 * st->bubble_max_radius) + 3;
1343
1344     /* store area of each bubble of certain radius as number of 1/10s of
1345        a pixel area.  PI is defined in <math.h> */
1346     st->bubble_areas = (long *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
1347     for (i = 0; i < st->bubble_min_radius; i++)
1348       st->bubble_areas[i] = 0;
1349     for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
1350       st->bubble_areas[i] = calc_bubble_area(st, i);
1351
1352         /* Now populate the droppage values */
1353     st->bubble_droppages = (int *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
1354     for (i = 0; i < st->bubble_min_radius; i++)
1355       st->bubble_droppages[i] = 0;
1356     for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
1357       st->bubble_droppages[i] = MAX_DROPPAGE * (i - st->bubble_min_radius) / (st->bubble_max_radius - st->bubble_min_radius);
1358
1359     st->mesh_length = (2 * st->bubble_max_radius) + 3;
1360   } else {
1361 #ifndef FANCY_BUBBLES
1362     fprintf(stderr,
1363             "Bug: simple mode code not set but FANCY_BUBBLES not defined\n");
1364     exit(1);
1365 #else  /* FANCY_BUBBLES */
1366     /* Make sure all #ifdef sort of things have been taken care of in
1367        get_resources(). */
1368     default_to_pixmaps(st);
1369
1370     /* Set mesh length */
1371     st->mesh_length = (2 * st->step_pixmaps[st->num_bubble_pixmaps-1]->radius) + 3;
1372 #endif /* FANCY_BUBBLES */
1373
1374     /* Am I missing something in here??? */
1375   }
1376
1377   st->mesh_width = (st->screen_width / st->mesh_length) + 1;
1378   st->mesh_height = (st->screen_height / st->mesh_length) + 1;
1379   st->mesh_cells = st->mesh_width * st->mesh_height;
1380   init_mesh(st);
1381
1382   calculate_adjacent_list(st);
1383
1384   adjust_areas(st);
1385
1386   /* Graphics contexts for simple mode */
1387   if (st->simple) {
1388     gcv.foreground = st->default_fg_pixel;
1389     st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1390     gcv.foreground = st->default_bg_pixel;
1391     st->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1392   }
1393
1394   XClearWindow (st->dpy, st->window);
1395
1396 # ifndef FANCY_BUBBLES
1397   st->simple = True;
1398 # endif
1399
1400   return st;
1401 }
1402
1403 static unsigned long
1404 bubbles_draw (Display *dpy, Window window, void *closure)
1405 {
1406   struct state *st = (struct state *) closure;
1407   int i;
1408   for (i = 0; i < 5; i++)
1409     {
1410       Bubble *tmp = new_bubble(st);
1411       add_to_mesh(st, tmp);
1412       insert_new_bubble(st, tmp);
1413     }
1414   return st->delay;
1415 }
1416
1417
1418 static void
1419 bubbles_reshape (Display *dpy, Window window, void *closure, 
1420                  unsigned int w, unsigned int h)
1421 {
1422 }
1423
1424 static Bool
1425 bubbles_event (Display *dpy, Window window, void *closure, XEvent *event)
1426 {
1427   return False;
1428 }
1429
1430 static void
1431 bubbles_free (Display *dpy, Window window, void *closure)
1432 {
1433   struct state *st = (struct state *) closure;
1434   free (st);
1435 }
1436
1437 XSCREENSAVER_MODULE ("Bubbles", bubbles)