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