ftp://ftp.demon.nl/disk1/redhat-contrib/libc5/SRPMS/xscreensaver-2.14-1.src.rpm
[xscreensaver] / hacks / bubbles.c
1 /* bubbles.c - frying pan / soft drink in a glass simulation */
2
3 /*$Id: bubbles.c,v 1.10 1997/12/03 10:56:13 jwz Exp $*/
4
5 /*
6  *  Copyright (C) 1995-1996 James Macnicol
7  *
8  *   This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; either version 2, or (at your option) any later
11  * version.
12  *
13  *   This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
15  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16  * for more details. 
17  */
18
19 /*
20  * I got my original inspiration for this by looking at the bottom of a 
21  * frying pan while something was cooking and watching the little bubbles
22  * coming off the bottom of the pan as the oil was boiling joining together
23  * to form bigger bubbles and finally to *pop* and disappear.  I had some
24  * time on my hands so I wrote this little xscreensaver module to imitate
25  * it.  Now that it's done it reminds me more of the bubbles you get in
26  * a glass of fizzy soft drink.....
27  *
28  * The problem seemed to be that the position/size etc. of all the bubbles
29  * on the screen had to be remembered and searched through to find when
30  * bubbles hit each other and combined.  To do this more efficiently, the
31  * window/screen is divided up into a square mesh of side length mesh_length
32  * and separate lists of bubbles contained in each cell of the mesh are
33  * kept.  Only the cells in the immediate vicinity of the bubble in question
34  * are searched.  This should make things more efficient although the whole
35  * thing seems to use up too much CPU, but then I'm using an ancient PC so
36  * perhaps it's not surprising .
37  * (Six months after I wrote the above I now have a Pentium with PCI graphics 
38  * and things are _much_ nicer.)
39  *
40  * Author:           James Macnicol 
41  * Internet E-mail : J.Macnicol@student.anu.edu.au
42  */
43
44 #include <math.h>
45 #include "screenhack.h"
46 #include "bubbles.h"
47
48 #ifdef BUBBLES_IO
49 # include <dirent.h>
50 # include <fcntl.h>
51 # include <sys/types.h>
52 #endif /* BUBBLES_IO */
53
54 #include <limits.h>
55
56 #ifdef SIGNAL_NONSENSE          /* what's this crap doing in here? */
57 #include <signal.h>
58 #endif /* SIGNAL_NONSENSE */
59
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63
64 #ifndef VMS
65 # include <sys/wait.h>
66 #else /* VMS */
67 # if __DECC_VER >= 50200000
68 #  include <sys/wait.h>
69 # endif
70 #endif /* VMS */
71
72 #ifdef HAVE_UNISTD_H
73 # include <unistd.h>
74 #endif
75 #include "yarandom.h"
76
77 #ifdef HAVE_XPM
78 #include <X11/xpm.h>
79 #endif
80
81 /* 
82  * Public variables 
83  */
84
85 #ifndef NO_DEFAULT_BUBBLE
86 extern int num_default_bubbles;
87 extern char **default_bubbles[];
88 #endif /* NO_DEFAULT_BUBBLE */
89
90 char *progclass = "Bubbles";
91
92 char *defaults [] = {
93   "*background: black",
94   "*foreground: white",
95   "*simple:     false",
96   "*broken:     false",
97   "*delay:      800",
98 #ifdef BUBBLES_IO
99   "*file:       (default)",
100   "*directory:  (default)",
101 #endif /* BUBBLES_IO */
102   "*quiet:      false", 
103   "*nodelay:    false",
104   "*3D:     false",
105   "*geometry:   400x300",
106   0
107 };
108
109 XrmOptionDescRec options [] = {
110   { "-simple",          ".simple",      XrmoptionNoArg, "true" },
111 #ifdef HAVE_XPM
112   { "-broken",          ".broken",      XrmoptionNoArg, "true" },
113 #endif /* HAVE_XPM */
114   { "-quiet",           ".quiet",       XrmoptionNoArg, "true" },
115   { "-nodelay",         ".nodelay",     XrmoptionNoArg, "true" },
116   { "-3D",          ".3D",      XrmoptionNoArg, "true" },
117 #ifdef BUBBLES_IO
118   { "-file",            ".file",        XrmoptionSepArg, 0 },
119   { "-directory",       ".directory",   XrmoptionSepArg, 0 },
120 #endif /* BUBBLES_IO */
121   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
122   { 0, 0, 0, 0 }
123 };
124
125 /* 
126  * Private variables 
127  */
128
129 static Bubble **mesh;
130 static int mesh_length;
131 static int mesh_width;
132 static int mesh_height;
133 static int mesh_cells;
134
135 static int **adjacent_list;
136
137 static int screen_width;
138 static int screen_height;
139 static int screen_depth;
140 static unsigned int default_fg_pixel, default_bg_pixel;
141 /* 
142  * I know it's not elegant to save this stuff in global variables
143  * but we need it for the signal handler.
144  */
145 static Display *defdsp;
146 static Window defwin;
147 static Colormap defcmap;
148 static Visual *defvisual;
149
150 /* For simple mode only */
151 static int bubble_min_radius;
152 static int bubble_max_radius;
153 static long *bubble_areas;
154 static GC draw_gc, erase_gc;
155
156 #ifdef HAVE_XPM
157 static int num_bubble_pixmaps;
158 static Bubble_Step **step_pixmaps;
159 #ifdef BUBBLES_IO
160 static char *pixmap_file;
161 #endif /* BUBBLES_IO */
162 static int use_default_bubble;
163 #endif /* HAVE_XPM */
164
165 /* Options stuff */
166 #ifdef HAVE_XPM
167 static Bool simple = False;
168 #else
169 static Bool simple = True;
170 #endif
171 static Bool broken = False;
172 static Bool quiet = False;
173 static Bool threed = False;
174 static int delay;
175
176 /* 
177  * To prevent forward references, some stuff is up here 
178  */
179
180 static long
181 calc_bubble_area(int r)
182 /* Calculate the area of a bubble of radius r */
183 {
184 #ifdef DEBUG
185   printf("%d %g\n", r,
186          10.0 * PI * (double)r * (double)r * (double)r);
187 #endif /* DEBUG */
188   if (threed)
189     return (long)(10.0 * PI * (double)r * (double)r * (double)r);
190   else
191     return (long)(10.0 * PI * (double)r * (double)r);
192 }
193
194 static void *
195 xmalloc(size_t size)
196 /* Safe malloc */
197 {
198   void *ret;
199
200   if ((ret = malloc(size)) == NULL) {
201     fprintf(stderr, "%s: out of memory\n", progname);
202     exit(1);
203   }
204   return ret;
205 }
206
207 #ifdef DEBUG
208 static void 
209 die_bad_bubble(Bubble *bb)
210 /* This is for use with GDB */
211 {
212   fprintf(stderr, "Bad bubble detected at 0x%x!\n", (int)bb);
213   exit(1);
214 }
215 #endif
216
217 static int
218 null_bubble(Bubble *bb)
219 /* Returns true if the pointer passed is NULL.  If not then this checks to
220 see if the bubble is valid (i.e. the (x,y) position is valid and the magic
221 number is set correctly.  This only a sanity check for debugging and is
222 turned off if DEBUG isn't set. */
223 {
224   if (bb == (Bubble *)NULL)
225     return 1;
226 #ifdef DEBUG
227   if ((bb->cell_index < 0) || (bb->cell_index > mesh_cells)) {
228     fprintf(stderr, "cell_index = %d\n", bb->cell_index);
229     die_bad_bubble(bb);
230   }
231   if (bb->magic != BUBBLE_MAGIC)  {
232     fprintf(stderr, "Magic = %d\n", bb->magic);
233     die_bad_bubble(bb);
234   }
235   if (simple) {
236     if ((bb->x < 0) || (bb->x > screen_width) ||
237         (bb->y < 0) || (bb->y > screen_height) ||
238         (bb->radius < bubble_min_radius) || (bb->radius >
239                                              bubble_max_radius)) {
240       fprintf(stderr,
241               "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
242               bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
243       die_bad_bubble(bb);  
244     }
245 #ifdef HAVE_XPM
246   } else {
247     if ((bb->x < 0) || (bb->x > screen_width) ||
248         (bb->y < 0) || (bb->y > screen_height) ||
249         (bb->radius < step_pixmaps[0]->radius) || 
250         (bb->radius > step_pixmaps[num_bubble_pixmaps-1]->radius)) {
251       fprintf(stderr,
252               "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
253               bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
254       die_bad_bubble(bb);  
255     }
256 #endif /* HAVE_XPM */
257   }
258 #endif /* DEBUG */
259   return 0;
260 }
261
262 #ifdef DEBUG
263 static void 
264 print_bubble_list(Bubble *bb)
265 /* Print list of where all the bubbles are.  For debugging purposes only. */
266 {
267   if (! null_bubble(bb)) {
268     printf("  (%d, %d) %d\n", bb->x, bb->y, bb->radius);
269     print_bubble_list(bb->next);
270   }
271 }
272 #endif /* DEBUG */
273
274 static void 
275 add_bubble_to_list(Bubble **list, Bubble *bb)
276 /* Take a pointer to a list of bubbles and stick bb at the head of the
277  list. */
278 {
279   Bubble *head = *list;
280
281   if (null_bubble(head)) {
282     bb->prev = (Bubble *)NULL;
283     bb->next = (Bubble *)NULL;
284   } else {
285     bb->next = head;
286     bb->prev = (Bubble *)NULL;
287     head->prev = bb;
288   }
289   *list = bb;
290 }
291
292
293 /* 
294  * Mesh stuff 
295  */
296
297
298 static void 
299 init_mesh (void)
300 /* Setup the mesh of bubbles */
301 {
302   int i;
303
304   mesh = (Bubble **)xmalloc(mesh_cells * sizeof(Bubble *));
305   for (i = 0; i < mesh_cells; i++)
306     mesh[i] = (Bubble *)NULL;
307 }
308
309 static int
310 cell_to_mesh(int x, int y)
311 /* convert cell coordinates to mesh index */
312 {
313 #ifdef DEBUG
314   if ((x < 0) || (y < 0)) {
315     fprintf(stderr, "cell_to_mesh: x = %d, y = %d\n", x, y);
316     exit(1);
317   }
318 #endif
319   return ((mesh_width * y) + x);
320 }
321
322 static void 
323 mesh_to_cell(int mi, int *cx, int *cy)
324 /* convert mesh index into cell coordinates */
325 {
326   *cx = mi % mesh_width;
327   *cy = mi / mesh_width;
328 }
329
330 static int
331 pixel_to_mesh(int x, int y)
332 /* convert screen coordinates into mesh index */
333 {
334   return cell_to_mesh((x / mesh_length), (y / mesh_length));
335 }
336
337 static int
338 verify_mesh_index(int x, int y)
339 /* check to see if (x,y) is in the mesh */
340 {
341   if ((x < 0) || (y < 0) || (x >= mesh_width) || (y >= mesh_height))
342     return (-1);
343   return (cell_to_mesh(x, y));
344 }
345
346 #ifdef DEBUG
347 static void 
348 print_adjacents(int *adj)
349 /* Print a list of the cells calculated above.  For debugging only. */
350 {
351   int i;
352
353   printf("(");
354   for (i = 0; i < 8; i++)
355     printf("%d ", adj[i]);
356   printf(")\n");
357 }
358 #endif /* DEBUG */
359
360 static void 
361 add_to_mesh(Bubble *bb)
362 /* Add the given bubble to the mesh by sticking it on the front of the
363 list.  bb is already allocated so no need to malloc() anything, just
364 adjust pointers. */
365 {
366 #ifdef DEBUG
367   if (null_bubble(bb)) {
368     fprintf(stderr, "Bad bubble passed to add_to_mesh()!\n");
369     exit(1);
370   }
371 #endif /* DEBUG */
372
373   add_bubble_to_list(&mesh[bb->cell_index], bb);
374 }
375
376 #ifdef DEBUG
377 static void 
378 print_mesh (void)
379 /* Print the contents of the mesh */
380 {
381   int i;
382
383   for (i = 0; i < mesh_cells; i++) {
384     if (! null_bubble(mesh[i])) {
385       printf("Mesh cell %d\n", i);
386       print_bubble_list(mesh[i]);
387     }
388   }
389 }
390
391 static void 
392 valid_mesh (void)
393 /* Check to see if the mesh is Okay.  For debugging only. */
394 {
395   int i;
396   Bubble *b;
397
398   for (i = 0; i < mesh_cells; i++) {
399     b = mesh[i];
400     while (! null_bubble(b))
401       b = b->next;
402   }
403 }
404
405 static int
406 total_bubbles (void)
407 /* Count how many bubbles there are in total.  For debugging only. */
408 {
409   int rv = 0;
410   int i;
411   Bubble *b;
412
413   for (i = 0; i < mesh_cells; i++) {
414     b = mesh[i];
415     while (! null_bubble(b)) {
416       rv++;
417       b = b->next;
418     } 
419   }
420
421   return rv;
422 }
423 #endif /* DEBUG */
424
425 static void 
426 calculate_adjacent_list (void)
427 /* Calculate the list of cells adjacent to a particular cell for use
428    later. */
429 {
430   int i; 
431   int ix, iy;
432
433   adjacent_list = (int **)xmalloc(mesh_cells * sizeof(int *));
434   for (i = 0; i < mesh_cells; i++) {
435     adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
436     mesh_to_cell(i, &ix, &iy);
437     adjacent_list[i][0] = verify_mesh_index(--ix, --iy);
438     adjacent_list[i][1] = verify_mesh_index(++ix, iy);
439     adjacent_list[i][2] = verify_mesh_index(++ix, iy);
440     adjacent_list[i][3] = verify_mesh_index(ix, ++iy);
441     adjacent_list[i][4] = verify_mesh_index(ix, ++iy);
442     adjacent_list[i][5] = verify_mesh_index(--ix, iy);
443     adjacent_list[i][6] = verify_mesh_index(--ix, iy);
444     adjacent_list[i][7] = verify_mesh_index(ix, --iy);
445     adjacent_list[i][8] = i;
446   }
447 }
448
449 static void
450 adjust_areas (void)
451 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
452 {
453   double maxvalue;
454   long maxarea;
455   long factor;
456   int i;
457
458 #ifdef HAVE_XPM
459   if (simple)
460     maxarea = bubble_areas[bubble_max_radius+1];
461   else
462     maxarea = step_pixmaps[num_bubble_pixmaps]->area;
463 #else
464   maxarea = bubble_areas[bubble_max_radius+1];
465 #endif /* HAVE_XPM */
466   maxvalue = (double)screen_width * 2.0 * (double)maxarea;
467   factor = (long)ceil(maxvalue / (double)LONG_MAX);
468   if (factor > 1) {
469     /* Overflow will occur in weighted_mean().  We must divide areas
470        each by factor so it will never do so. */
471 #ifdef HAVE_XPM
472     if (simple) {
473       for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
474         bubble_areas[i] /= factor;
475         if (bubble_areas[i] == 0)
476           bubble_areas[i] = 1;
477       }
478     } else {
479       for (i = 0; i <= num_bubble_pixmaps; i++) {
480 #ifdef DEBUG
481         printf("area = %ld", step_pixmaps[i]->area);
482 #endif /* DEBUG */
483         step_pixmaps[i]->area /= factor;
484         if (step_pixmaps[i]->area == 0)
485           step_pixmaps[i]->area = 1;
486 #ifdef DEBUG
487         printf("-> %ld\n", step_pixmaps[i]->area);
488 #endif /* DEBUG */
489       }
490     }
491 #else
492     for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
493       bubble_areas[i] /= factor;
494       if (bubble_areas[i] == 0)
495         bubble_areas[i] = 1;
496     }
497 #endif /* HAVE_XPM */
498   }
499 #ifdef DEBUG
500   printf("maxarea = %ld\n", maxarea);
501   printf("maxvalue = %g\n", maxvalue);
502   printf("LONG_MAX = %ld\n", LONG_MAX);
503   printf("factor = %ld\n", factor);
504 #endif /* DEBUG */
505 }
506
507 /* 
508  * Bubbles stuff 
509  */
510
511 static Bubble *
512 new_bubble (void)
513 /* Add a new bubble at some random position on the screen of the smallest
514 size. */
515 {
516   Bubble *rv = (Bubble *)xmalloc(sizeof(Bubble));
517
518   /* Can't use null_bubble() here since magic number hasn't been set */
519   if (rv == (Bubble *)NULL) {
520     fprintf(stderr, "Ran out of memory!\n");
521     exit(1);
522   }
523
524   if (simple) {
525     rv->radius = bubble_min_radius;
526     rv->area = bubble_areas[bubble_min_radius];
527 #ifdef HAVE_XPM
528   } else {
529     rv->step = 0;
530     rv->radius = step_pixmaps[0]->radius;
531     rv->area = step_pixmaps[0]->area;
532 #endif /* HAVE_XPM */
533   }
534   rv->visible = 0;
535   rv->magic = BUBBLE_MAGIC;
536   rv->x = ya_random() % screen_width;
537   rv->y = ya_random() % screen_height;
538   rv->cell_index = pixel_to_mesh(rv->x, rv->y);
539
540   return rv;
541 }
542
543 static void 
544 show_bubble(Bubble *bb)
545 /* paint the bubble on the screen */
546 {
547   if (null_bubble(bb)) {
548     fprintf(stderr, "NULL bubble passed to show_bubble\n");
549     exit(1);
550   }
551
552   if (! bb->visible) {
553     bb->visible = 1;
554
555     if (simple) {
556       XDrawArc(defdsp, defwin, draw_gc, (bb->x - bb->radius),
557                (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
558                360*64);  
559     } else {
560 #ifdef HAVE_XPM
561       XSetClipOrigin(defdsp, step_pixmaps[bb->step]->draw_gc, 
562                      (bb->x - bb->radius),
563                      (bb->y - bb->radius));
564       
565       XCopyArea(defdsp, step_pixmaps[bb->step]->ball, defwin, 
566                 step_pixmaps[bb->step]->draw_gc,
567                 0, 0, (bb->radius * 2), 
568                 (bb->radius * 2),  
569                 (bb->x - bb->radius),
570                 (bb->y - bb->radius));
571 #endif /* HAVE_XPM */
572     }
573   }
574 }
575
576 static void 
577 hide_bubble(Bubble *bb)
578 /* erase the bubble */
579 {
580   if (null_bubble(bb)) {
581     fprintf(stderr, "NULL bubble passed to hide_bubble\n");
582     exit(1);
583   }
584
585   if (bb->visible) {
586     bb->visible = 0;
587
588     if (simple) {
589       XDrawArc(defdsp, defwin, erase_gc, (bb->x - bb->radius),
590                (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
591                360*64);
592     } else {
593 #ifdef HAVE_XPM
594       if (! broken) {
595         XSetClipOrigin(defdsp, step_pixmaps[bb->step]->erase_gc, 
596                        (bb->x - bb->radius), (bb->y - bb->radius));
597         
598         XFillRectangle(defdsp, defwin, step_pixmaps[bb->step]->erase_gc,
599                        (bb->x - bb->radius),
600                        (bb->y - bb->radius),
601                        (bb->radius * 2),
602                        (bb->radius * 2));
603       }
604 #endif /* HAVE_XPM */
605     }
606   }
607 }
608
609 static void 
610 delete_bubble_in_mesh(Bubble *bb, int keep_bubble)
611 /* Delete an individual bubble, adjusting list of bubbles around it.
612    If keep_bubble is true then the bubble isn't actually deleted.  We
613    use this to allow bubbles to change mesh cells without reallocating,
614    (it needs this when two bubbles collide and the centre position is
615    recalculated, and this may stray over a mesh boundary). */
616 {
617   if ((!null_bubble(bb->prev)) && (!null_bubble(bb->next))) {
618     bb->prev->next = bb->next;
619     bb->next->prev = bb->prev;
620   } else if ((!null_bubble(bb->prev)) &&
621              (null_bubble(bb->next))) {
622     bb->prev->next = (Bubble *)NULL;
623     bb->next = mesh[bb->cell_index];
624   } else if ((null_bubble(bb->prev)) &&
625              (!null_bubble(bb->next))) {
626     bb->next->prev = (Bubble *)NULL;
627     mesh[bb->cell_index] = bb->next;
628     bb->next = mesh[bb->cell_index];
629   } else {
630     /* Only item on list */
631     mesh[bb->cell_index] = (Bubble *)NULL;
632   }              
633   if (! keep_bubble)
634     free(bb);
635 }
636
637 static unsigned long 
638 ulongsqrint(int x)
639 /* Saves ugly inline code */
640 {
641   return ((unsigned long)x * (unsigned long)x);
642 }
643
644 static Bubble *
645 get_closest_bubble(Bubble *bb)
646 /* Find the closest bubble touching the this bubble, NULL if none are
647    touching. */
648 {
649   Bubble *rv = (Bubble *)NULL;
650   Bubble *tmp;
651   unsigned long separation2, touchdist2;
652   int dx, dy;
653   unsigned long closest2 = ULONG_MAX;
654   int i;
655
656 #ifdef DEBUG 
657   if (null_bubble(bb)) {
658     fprintf(stderr, "NULL pointer 0x%x passed to get_closest_bubble()!", 
659             (int)bb);
660     exit(1);
661   }
662 #endif /* DEBUG */
663
664   for (i = 0; i < 9; i++) {
665     /* There is a bug here where bb->cell_index is negaitve.. */
666 #ifdef DEBUG
667     if ((bb->cell_index < 0) || (bb->cell_index >= mesh_cells)) {
668       fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
669       exit(1);
670     }
671 #endif /* DEBUG */
672 /*    printf("%d,", bb->cell_index); */
673     if (adjacent_list[bb->cell_index][i] != -1) {
674       tmp = mesh[adjacent_list[bb->cell_index][i]];
675       while (! null_bubble(tmp)) {
676         if (tmp != bb) {
677           dx = tmp->x - bb->x;
678           dy = tmp->y - bb->y;
679           separation2 = ulongsqrint(dx) + ulongsqrint(dy);
680           /* Add extra leeway so circles _never_ overlap */
681           touchdist2 = ulongsqrint(tmp->radius + bb->radius + 2);
682           if ((separation2 <= touchdist2) && (separation2 <
683                                               closest2)) {
684             rv = tmp;
685             closest2 = separation2;
686           }
687         }
688         tmp = tmp->next;
689       }
690     }
691   }
692
693   return rv;
694 }
695
696 #ifdef DEBUG
697 static void
698 ldr_barf (void)
699 {
700 }
701 #endif /* DEBUG */
702
703 static long
704 long_div_round(long num, long dem)
705 {
706   long divvie, moddo;
707
708 #ifdef DEBUG
709   if ((num < 0) || (dem < 0)) {
710     fprintf(stderr, "long_div_round: %ld, %ld\n", num, dem);
711     ldr_barf();
712     exit(1);
713   }
714 #endif /* DEBUG */
715
716   divvie = num / dem;
717   moddo = num % dem;
718   if (moddo > (dem / 2))
719     ++divvie;
720
721 #ifdef DEBUG
722   if ((divvie < 0) || (moddo < 0)) {
723     fprintf(stderr, "long_div_round: %ld, %ld\n", divvie, moddo);
724     ldr_barf();
725     exit(1);
726   }
727 #endif /* DEBUG */
728
729   return divvie;
730 }
731
732 static int
733 weighted_mean(int n1, int n2, long w1, long w2)
734 /* Mean of n1 and n2 respectively weighted by weights w1 and w2. */
735 {
736 #ifdef DEBUG
737   if ((w1 <= 0) || (w2 <= 0)) {
738     fprintf(stderr,
739             "Bad weights passed to weighted_mean() - (%d, %d, %ld, %ld)!\n",
740             n1, n2, w1, w2);
741     exit(1);
742   }
743 #endif /* DEBUG */
744   return ((int)long_div_round((long)n1 * w1 + (long)n2 * w2,
745                            w1 + w2));
746 }
747
748 static int
749 bubble_eat(Bubble *diner, Bubble *food)
750 /* The diner eats the food.  Returns true (1) if the diner still exists */
751
752   int i;
753   int newmi;
754
755 #ifdef DEBUG
756   if ((null_bubble(diner)) || (null_bubble(food))) {
757     fprintf(stderr, "Bad bubbles passed to bubble_eat()!\n");
758     exit(1);
759   }
760 #endif /* DEBUG */
761
762   /* We hide the diner even in the case that it doesn't grow so that
763      if the food overlaps its boundary it is replaced. This could
764      probably be solved by letting bubbles eat others which are close
765      but not quite touching.  It's probably worth it, too, since we
766      would then not have to redraw bubbles which don't change in
767      size. */
768
769   hide_bubble(diner);
770   hide_bubble(food);
771   diner->x = weighted_mean(diner->x, food->x, diner->area, food->area);
772   diner->y = weighted_mean(diner->y, food->y, diner->area, food->area);
773   newmi = pixel_to_mesh(diner->x, diner->y);
774   diner->area += food->area;
775   delete_bubble_in_mesh(food, DELETE_BUBBLE);
776
777   if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
778     delete_bubble_in_mesh(diner, DELETE_BUBBLE);
779     return 0;
780   }
781 #ifdef HAVE_XPM
782   if ((! simple) && (diner->area > 
783                      step_pixmaps[num_bubble_pixmaps]->area)) {
784     delete_bubble_in_mesh(diner, DELETE_BUBBLE);
785     return 0;
786   }
787 #endif /* HAVE_XPM */
788
789   if (simple) {
790     if (diner->area > bubble_areas[diner->radius + 1]) {
791       /* Move the bubble to a new radius */
792       i = diner->radius;
793       while (diner->area > bubble_areas[i+1])
794         ++i;
795       diner->radius = i;
796     }
797     show_bubble(diner);
798 #ifdef HAVE_XPM
799   } else {
800     if (diner->area > step_pixmaps[diner->step+1]->area) {
801       i = diner->step;
802       while (diner->area > step_pixmaps[i+1]->area)
803         ++i;
804       diner->step = i;
805       diner->radius = step_pixmaps[diner->step]->radius;
806     }
807     show_bubble(diner);
808 #endif /* HAVE_XPM */
809   }
810
811   /* Now adjust locations and cells if need be */
812   if (newmi != diner->cell_index) {
813     delete_bubble_in_mesh(diner, KEEP_BUBBLE);
814     diner->cell_index = newmi;
815     add_to_mesh(diner);
816   }
817
818   return 1;
819 }
820
821 static int
822 merge_bubbles(Bubble *b1, Bubble *b2)
823 /* These two bubbles merge into one.  If the first one wins out return
824 1 else return 2.  If there is no winner (it explodes) then return 0 */
825 {
826   int b1size, b2size;
827
828   b1size = b1->area;
829   b2size = b2->area;
830
831 #ifdef DEBUG
832   if ((null_bubble(b1) || null_bubble(b2))) {
833     fprintf(stderr, "NULL bubble passed to merge_bubbles()!\n");
834     exit(1);
835   }
836 #endif /* DEBUG */
837
838   if (b1 == b2) {
839     hide_bubble(b1);
840     delete_bubble_in_mesh(b1, DELETE_BUBBLE);
841     return 0;
842   }
843
844   if (b1size > b2size) {
845     switch (bubble_eat(b1, b2)) {
846     case 0:
847       return 0;
848       break;
849     case 1:
850       return 1;
851       break;
852     default:
853       break;
854     }
855   } else if (b1size < b2size) {
856     switch (bubble_eat(b2, b1)) {
857     case 0:
858       return 0;
859       break;
860     case 1:
861       return 2;
862       break;
863     default:
864       break;
865     }
866   } else {
867     if ((ya_random() % 2) == 0) {
868       switch (bubble_eat(b1, b2)) {
869       case 0:
870         return 0;
871         break;
872       case 1:
873         return 1;
874         break;
875       default:
876         break;
877       }
878     } else {
879       switch (bubble_eat(b2, b1)) {
880       case 0:
881         return 0;
882         break;
883       case 1:
884         return 2;
885         break;
886       default:
887         break;
888       }
889     }
890   }
891   fprintf(stderr, "An error occurred in merge_bubbles()\n");
892   exit(1);
893 }
894
895 static void 
896 insert_new_bubble(Bubble *tmp)
897 /* Calculates which bubbles are eaten when a new bubble tmp is
898    inserted.  This is called recursively in case when a bubble grows
899    it eats others.  Careful to pick out disappearing bubbles. */
900 {
901   Bubble *nextbub;
902   Bubble *touch;
903
904 #ifdef DEBUG
905   if (null_bubble(tmp)) {
906     fprintf(stderr, "Bad bubble passed to insert_new_bubble()!\n");
907     exit(1);
908   }
909 #endif /* DEBUG */
910   
911   nextbub = tmp;
912   touch = get_closest_bubble(nextbub);
913   while (! null_bubble(touch)) {
914     switch (merge_bubbles(nextbub, touch)) {
915     case 2:
916       /* touch ate nextbub and survived */
917       nextbub = touch;
918       break;
919     case 1:
920       /* nextbub ate touch and survived */
921       break;
922     case 0:
923       /* somebody ate someone else but they exploded */
924       nextbub = (Bubble *)NULL;
925       break;
926     default:
927       /* something went wrong */
928       fprintf(stderr, "Error occurred in insert_new_bubble()\n");
929       exit(1);
930     }
931     /* Check to see if there are any other bubbles still in the area
932        and if we need to do this all over again for them. */
933     if (! null_bubble(nextbub))
934       touch = get_closest_bubble(nextbub);
935     else
936       touch = (Bubble *)NULL;
937   }
938 }
939
940 #ifdef DEBUG
941 static int
942 get_length_of_bubble_list(Bubble *bb)
943 {
944   Bubble *tmp = bb;
945   int rv = 0;
946
947   while (! null_bubble(tmp)) {
948     rv++;
949     tmp = tmp->next;
950   }
951
952   return rv;
953 }
954 #endif /* DEBUG */
955
956 /*
957  * Pixmap stuff used regardless of whether file I/O is available.  Must
958  * still check for XPM, though!
959  */
960
961 #ifdef HAVE_XPM
962
963 static void 
964 free_pixmaps (void)
965 /* Free resources associated with XPM */
966 {
967   int i;
968
969 #ifdef DEBUG
970   if (simple) {
971     fprintf(stderr, "free_pixmaps() called in simple mode\n");
972     exit(1);
973   }
974   printf("free_pixmaps()\n");
975 #endif /* DEBUG */
976
977   for(i = 0; i < (num_bubble_pixmaps - 1); i++) {
978     XFreePixmap(defdsp, step_pixmaps[i]->ball);
979     XFreePixmap(defdsp, step_pixmaps[i]->shape_mask);
980     XFreeGC(defdsp, step_pixmaps[i]->draw_gc);
981     XFreeGC(defdsp, step_pixmaps[i]->erase_gc);
982     XFreeColors(defdsp, defcmap, step_pixmaps[i]->xpmattrs.pixels, 
983                 step_pixmaps[i]->xpmattrs.npixels, 0);
984     XpmFreeAttributes(&step_pixmaps[i]->xpmattrs);
985   }
986 }
987
988 #ifdef SIGNAL_NONSENSE
989 static void 
990 onintr(int a)
991 /* This gets called when SIGINT or SIGTERM is received */
992 {
993   free_pixmaps();
994   exit(0);
995 }
996
997 #ifdef DEBUG
998 static void
999 onsegv(int a)
1000 /* Called when SEGV detected.   Hmmmmm.... */
1001 {
1002   fflush(stdout);
1003   fprintf(stderr, "SEGV detected! : %d\n", a);
1004   exit(1);
1005 }
1006 #endif /* DEBUG */
1007 #endif /* SIGNAL_NONSENSE */
1008
1009
1010 /*
1011  * Pixmaps without file I/O (but do have XPM)
1012  */
1013
1014 static void 
1015 pixmap_sort(Bubble_Step **head, int numelems)
1016 /* Couldn't get qsort to work right with this so I wrote my own.  This puts
1017 the numelems length array with first element at head into order of radius.
1018 */
1019 {
1020   Bubble_Step tmp;
1021   Bubble_Step *least = 0;
1022   int minradius = INT_MAX;
1023   int i;
1024
1025   for (i = 0; i < numelems; i++) {
1026     if (head[i]->radius < minradius) {
1027       least = head[i];
1028       minradius = head[i]->radius;
1029     }
1030   }
1031   if (*head != least) {
1032     memcpy(&tmp, least, sizeof(Bubble_Step));
1033     memcpy(least, *head, sizeof(Bubble_Step));
1034     memcpy(*head, &tmp, sizeof(Bubble_Step));
1035   }
1036
1037   if (numelems > 2)
1038     pixmap_sort(&head[1], numelems-1);
1039 }
1040
1041 static int
1042 extrapolate(int i1, int i2)
1043 {
1044   return (i2 + (i2 - i1));
1045 }
1046
1047 static void 
1048 make_pixmap_array(Bubble_Step *list)
1049 /* From a linked list of bubbles construct the array step_pixmaps */
1050 {
1051   Bubble_Step *tmp = list;
1052   int ind;
1053 #ifdef DEBUG
1054   int prevrad = -1;
1055 #endif
1056   
1057   if (list == (Bubble_Step *)NULL) {
1058     fprintf(stderr, "NULL list passed to make_pixmap_array\n");
1059     exit(1);
1060   }
1061
1062   num_bubble_pixmaps = 1;
1063   while(tmp->next != (Bubble_Step *)NULL) {
1064     tmp = tmp->next;
1065     ++num_bubble_pixmaps;
1066   }
1067
1068   if (num_bubble_pixmaps < 2) {
1069     fprintf(stderr, "Must be at least two bubbles in file\n");
1070     exit(1);
1071   }
1072
1073   step_pixmaps = (Bubble_Step **)xmalloc((num_bubble_pixmaps + 1) * 
1074                                          sizeof(Bubble_Step *));
1075
1076   /* Copy them blindly into the array for sorting. */
1077   ind = 0;
1078   tmp = list;
1079   do {
1080     step_pixmaps[ind++] = tmp;
1081     tmp = tmp->next;
1082   } while(tmp != (Bubble_Step *)NULL);
1083
1084   /* We make another bubble beyond the ones with pixmaps so that the final
1085      bubble hangs around and doesn't pop immediately.  It's radius and area
1086      are found by extrapolating from the largest two bubbles with pixmaps. */
1087
1088   step_pixmaps[num_bubble_pixmaps] = 
1089     (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1090   step_pixmaps[num_bubble_pixmaps]->radius = INT_MAX;
1091
1092   pixmap_sort(step_pixmaps, (num_bubble_pixmaps + 1));
1093
1094 #ifdef DEBUG
1095   if (step_pixmaps[num_bubble_pixmaps]->radius != INT_MAX) {
1096     fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
1097   }
1098 #endif /* DEBUG */
1099
1100   step_pixmaps[num_bubble_pixmaps]->radius = 
1101     extrapolate(step_pixmaps[num_bubble_pixmaps-2]->radius,
1102                 step_pixmaps[num_bubble_pixmaps-1]->radius);
1103   step_pixmaps[num_bubble_pixmaps]->area = 
1104     calc_bubble_area(step_pixmaps[num_bubble_pixmaps]->radius);
1105   
1106
1107 #ifdef DEBUG
1108   /* Now check for correct order */
1109   for (ind = 0; ind < num_bubble_pixmaps; ind++) {
1110     if (prevrad > 0) {
1111       if (step_pixmaps[ind]->radius < prevrad) {
1112         fprintf(stderr, "Pixmaps not in ascending order of radius\n");
1113         exit(1);
1114       }
1115     }
1116     prevrad = step_pixmaps[ind]->radius;
1117   }
1118 #endif /* DEBUG */
1119 }
1120
1121 #ifndef NO_DEFAULT_BUBBLE
1122 static void
1123 make_pixmap_from_default(char **pixmap_data, Bubble_Step *bl)
1124 /* Read pixmap data which has been compiled into the program and a pointer
1125  to which has been passed. 
1126
1127  This is virtually copied verbatim from make_pixmap_from_file() above and
1128 changes made to either should be propagated onwards! */
1129 {
1130   int result;
1131   XGCValues gcv;
1132
1133 #ifdef DEBUG
1134   if (pixmap_data == (char **)0) {
1135     fprintf(stderr, "make_pixmap_from_default(): NULL passed\n");
1136     exit(1);
1137   }
1138 #endif
1139
1140   if (bl == (Bubble_Step *)NULL) {
1141     fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1142     exit(1);
1143   }
1144
1145   bl->xpmattrs.valuemask = 0;
1146
1147 #ifdef XpmCloseness
1148   bl->xpmattrs.valuemask |= XpmCloseness;
1149   bl->xpmattrs.closeness = 40000;
1150 #endif
1151 #ifdef XpmVisual
1152   bl->xpmattrs.valuemask |= XpmVisual;
1153   bl->xpmattrs.visual = defvisual;
1154 #endif
1155 #ifdef XpmDepth
1156   bl->xpmattrs.valuemask |= XpmDepth;
1157   bl->xpmattrs.depth = screen_depth;
1158 #endif
1159 #ifdef XpmColormap
1160   bl->xpmattrs.valuemask |= XpmColormap;
1161   bl->xpmattrs.colormap = defcmap;
1162 #endif
1163
1164
1165   /* This is the only line which is different from make_pixmap_from_file() */
1166   result = XpmCreatePixmapFromData(defdsp, defwin, pixmap_data, &bl->ball, 
1167                                    &bl->shape_mask, &bl->xpmattrs);
1168
1169   switch(result) {
1170   case XpmColorError:
1171     fprintf(stderr, "xpm: color substitution performed\n");
1172     /* fall through */
1173   case XpmSuccess:
1174     bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
1175     bl->area = calc_bubble_area(bl->radius);
1176     break;
1177   case XpmColorFailed:
1178     fprintf(stderr, "xpm: color allocation failed\n");
1179     exit(1);
1180   case XpmNoMemory:
1181     fprintf(stderr, "xpm: out of memory\n");
1182     exit(1);
1183   default:
1184     fprintf(stderr, "xpm: unknown error code %d\n", result);
1185     exit(1);
1186   }
1187   
1188   gcv.plane_mask = AllPlanes;
1189   gcv.foreground = default_fg_pixel;
1190   gcv.function = GXcopy;
1191   bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1192   XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
1193   
1194   gcv.foreground = default_bg_pixel;
1195   gcv.function = GXcopy;
1196   bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1197   XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);  
1198 }
1199
1200 static void 
1201 default_to_pixmaps (void)
1202 /* Make pixmaps out of default ball data stored in bubbles_default.c */
1203 {
1204   int i;
1205   Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1206   Bubble_Step *newpix, *tmppix;
1207   char **pixpt;
1208
1209   /* Make sure pixmaps are freed when program is terminated */
1210   /* This is when I hit ^C */
1211 #ifdef SIGNAL_NONSENSE
1212   if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1213     signal(SIGINT, onintr);
1214   /* xscreensaver sends SIGTERM */
1215   if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1216     signal(SIGTERM, onintr);
1217 #ifdef DEBUG
1218   if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) {
1219     printf("Setting signal handler for SIGSEGV\n");
1220     signal(SIGSEGV, onsegv);
1221   } else {
1222     printf("Didn't set signal hanlder for SIGSEGV\n");
1223   }
1224 #endif /* DEBUG */
1225 #endif /* SIGNAL_NONSENSE */
1226
1227   for (i = 0; i < num_default_bubbles; i++) {
1228     pixpt = default_bubbles[i];
1229     newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1230     make_pixmap_from_default(pixpt, newpix);
1231     /* Now add to list */
1232     if (pixmap_list == (Bubble_Step *)NULL) {
1233       pixmap_list = newpix;
1234     } else {
1235       tmppix = pixmap_list;
1236       while (tmppix->next != (Bubble_Step *)NULL)
1237         tmppix = tmppix->next;
1238       tmppix->next = newpix;
1239     }
1240     newpix->next = (Bubble_Step *)NULL;
1241   }
1242   
1243   /* Finally construct step_pixmaps[] */
1244   make_pixmap_array(pixmap_list);
1245 }
1246
1247 #endif /* NO_DEFAULT_BUBBLE */
1248
1249 #endif /* HAVE_XPM */
1250
1251 /* 
1252  * File I/O stuff
1253  */
1254
1255 #ifdef BUBBLES_IO
1256
1257 static DIR *
1258 my_opendir(char *name)
1259 /* Like opendir() but checks for things so we don't have to do it multiple
1260 times in the code. */
1261 {
1262   DIR *rv;
1263
1264   if (name == (char *)NULL) {
1265     fprintf(stderr, "NULL directory name\n");
1266     return (DIR *)NULL;
1267   }
1268   
1269   if ((rv = opendir(name)) == NULL) {
1270     perror(name);
1271     return (DIR *)NULL;
1272   }
1273
1274   return rv;
1275 }
1276
1277 static int
1278 regular_file(char *name)
1279 /* Check to see if we can use the named file.  This was broken under Linux
1280 1.3.45 but seems to be okay under 1.3.54.  The parameter "name" was being
1281 trashed if the file didn't exist.  Yeah, I know 1.3.x are development
1282 kernels....
1283 */
1284 {
1285   int fd;
1286
1287   if ((fd = open(name, O_RDONLY)) == -1) {
1288     perror(name);
1289     return 0;
1290   } else {
1291     close(fd);
1292     return 1;
1293   }
1294 }
1295
1296 static char *
1297 get_random_name(char *dir)
1298 /* Pick an appropriate file at random out of the files in the directory dir */
1299 {
1300   STRUCT_DIRENT *dp;
1301   DIR *dfd;
1302   int numentries = 0;
1303   int entnum;
1304   int x;
1305   char buf[PATH_BUF_SIZE];
1306   char *rv;
1307
1308   if ((dfd = my_opendir(dir)) == (DIR *)NULL)
1309     return (char *)NULL;
1310
1311   while ((dp = readdir(dfd)) != NULL) {
1312     if ((strcmp(DIRENT_NAME, ".") == 0) || (strcmp(DIRENT_NAME, "..") == 0))
1313       continue;
1314     if ((strlen(dir)+strlen(DIRENT_NAME)+2) > 1024) {
1315       fprintf(stderr, "name %s/%s too long\n", dir, DIRENT_NAME);
1316       continue;
1317     }
1318     if (sprintf(buf, "%s/%s", dir, DIRENT_NAME) > (PATH_BUF_SIZE-1)) {
1319       fprintf(stderr, "path buffer overflowed in get_random_name()\n");
1320       continue;
1321     }
1322     if (regular_file(buf))
1323       ++numentries;
1324   }
1325   closedir(dfd);
1326   if (numentries == 0) {
1327     fprintf(stderr, "No suitable files found in %s\n", dir);
1328     return (char *)NULL;
1329   }
1330   entnum = ya_random() % numentries;
1331   x = 0;
1332
1333   if ((dfd = my_opendir(dir)) == (DIR *)NULL)
1334     return (char *)NULL;
1335   while ((dp = readdir(dfd)) != NULL) {
1336     if ((strcmp(DIRENT_NAME, ".") == 0) || (strcmp(DIRENT_NAME, "..") == 0))
1337       continue;
1338     if ((strlen(dir)+strlen(DIRENT_NAME)+2) > 1024) {
1339       /* We warned about this previously */
1340       continue;
1341     }
1342     if (sprintf(buf, "%s/%s", dir, DIRENT_NAME) > (PATH_BUF_SIZE-1)) {
1343       fprintf(stderr, "path buffer overflowed in get_random_name()\n");
1344       continue;
1345     }
1346     if (regular_file(buf)) {
1347       if (x == entnum) {
1348         rv = (char *)xmalloc(1024 * sizeof(char));
1349         strcpy(rv, buf);
1350         closedir(dfd);
1351         return rv;
1352       }
1353       ++x;
1354     }
1355   }
1356   /* We've screwed up if we reach here - someone must have deleted all the
1357      files while we were counting them... */
1358   fprintf(stderr, "get_random_name(): Oops!\n");
1359   exit(1);
1360 }
1361
1362 static int
1363 read_line(int fd, char **buf, int bufsize)
1364 /* A line is read from fd until a '\n' is found or EOF is reached.  (*buf)
1365 is initially of length bufsize and is extended by bufsize chars if need
1366 be (for as many times as it takes). */
1367 {
1368   char x;
1369   int pos = 0;
1370   int size = bufsize;
1371   int rv;
1372   char *newbuf;
1373
1374   while (1) {
1375     rv = read(fd, &x, 1);
1376     if (rv == -1) {
1377       perror("read_line(): ");
1378       return IO_ERROR;
1379     } else if (rv == 0) {
1380       (*buf)[pos] = '\0';
1381       return EOF_REACHED;
1382     } else if (x == '\n') {
1383       (*buf)[pos] = '\0';
1384       return LINE_READ;
1385     } else {
1386       (*buf)[pos++] = x;
1387       if (pos == (size - 1)) {
1388         /* We've come to the end of the space */
1389         newbuf = (char *)xmalloc((size+bufsize) * sizeof(char));
1390         strncpy(newbuf, *buf, (size - 1));
1391         free(*buf);
1392         *buf = newbuf;
1393         size += bufsize;
1394       }
1395     }
1396   }
1397 }
1398
1399 static int
1400 create_temp_file(char **name)
1401 /* Create a temporary file in /tmp and return a filedescriptor to it */
1402 {
1403   int rv;
1404  
1405   if (*name != (char *)NULL)
1406     free(*name);
1407
1408   if ((*name = tempnam("/tmp", "abxdfes")) == (char *)NULL) {
1409     fprintf(stderr, "Couldn't make new temporary file\n");
1410     exit(1);
1411   }
1412 /*   printf("Temp file created : %s\n", *name); */
1413   if ((rv = creat(*name, 0644)) == -1) {
1414     fprintf(stderr, "Couldn't open temporary file\n");
1415     exit(1);
1416   }
1417   
1418   return rv;
1419 }
1420
1421
1422 #ifdef BUBBLES_IO
1423 static void 
1424 make_pixmap_from_file(char *fname, Bubble_Step *bl)
1425 /* Read the pixmap in file fname into structure bl which must already
1426  be allocated. */
1427 {
1428   int result;
1429   XGCValues gcv;
1430
1431   if (bl == (Bubble_Step *)NULL) {
1432     fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
1433     exit(1);
1434   }
1435
1436   bl->xpmattrs.closeness = 40000;
1437   bl->xpmattrs.valuemask = XpmColormap | XpmCloseness;
1438   bl->xpmattrs.colormap = defcmap;
1439
1440   result = XpmReadFileToPixmap(defdsp, defwin, fname, &bl->ball, 
1441                                &bl->shape_mask, &bl->xpmattrs);
1442
1443   switch(result) {
1444   case XpmColorError:
1445     fprintf(stderr, "xpm: color substitution performed\n");
1446     /* fall through */
1447   case XpmSuccess:
1448     bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
1449     bl->area = calc_bubble_area(bl->radius);
1450     break;
1451   case XpmColorFailed:
1452     fprintf(stderr, "xpm: color allocation failed\n");
1453     exit(1);
1454   case XpmNoMemory:
1455     fprintf(stderr, "xpm: out of memory\n");
1456     exit(1);
1457   default:
1458     fprintf(stderr, "xpm: unknown error code %d\n", result);
1459     exit(1);
1460   }
1461   
1462   gcv.plane_mask = AllPlanes;
1463   gcv.foreground = default_fg_pixel;
1464   gcv.function = GXcopy;
1465   bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1466   XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
1467   
1468   gcv.foreground = default_bg_pixel;
1469   gcv.function = GXcopy;
1470   bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
1471   XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);  
1472 }
1473 #endif /* BUBBLES_IO */
1474
1475 static void 
1476 read_file_to_pixmaps(char *fname)
1477 /* Read the pixmaps contained in the file fname into memory.  THESE SHOULD
1478 BE UNCOMPRESSED AND READY TO GO! */
1479 {
1480   int fd, tmpfd=0, rv;
1481   int inxpm = 0;
1482   int xpmseen = 0;
1483   char *buf = (char *)NULL;
1484   char *tmpname = (char *)NULL;
1485   Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
1486   Bubble_Step *newpix, *tmppix;
1487
1488   /* We first create a linked list of pixmaps before allocating
1489      memory for the array */
1490
1491   if ((fd = open(fname, O_RDONLY)) == -1) {
1492     fprintf(stderr, "Couldn't open %s\n", fname);
1493     exit(1);
1494   }
1495
1496 #ifdef SIGNAL_NONSENSE
1497   /* Make sure pixmaps are freed when program is terminated */
1498   /* This is when I hit ^C */
1499   if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1500     signal(SIGINT, onintr);
1501   /* xscreensaver sends SIGTERM */
1502   if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1503     signal(SIGTERM, onintr);
1504 #ifdef DEBUG
1505   if (signal(SIGSEGV, SIGN_IGN) != SIG_IGN)
1506     signal(SIGSEGV, onsegv);
1507 #endif /* DEBUG */
1508 #endif /* SIGNAL_NONSENSE */
1509   
1510   while (1) {
1511     if (inxpm == 2)
1512       break;
1513
1514     buf = (char *)malloc(READ_LINE_BUF_SIZE * sizeof(char));
1515
1516     switch ((rv = read_line(fd, &buf, READ_LINE_BUF_SIZE))) {
1517     case IO_ERROR:
1518       fprintf(stderr, "An I/O error occurred\n");
1519       exit(1);
1520     case EOF_REACHED:
1521       if (inxpm) {
1522         fprintf(stderr, "EOF occurred inside an XPM block\n");
1523         exit(1);
1524       } else
1525         inxpm = 2;
1526       break;
1527     case LINE_READ:
1528       if (inxpm) {
1529         if (strncmp("};", buf, 2) == 0) {
1530           inxpm = 0;
1531           write(tmpfd, buf, strlen(buf));
1532           write(tmpfd, "\n", 1);
1533           close(tmpfd);
1534           /* Now process the tmpfile */
1535           newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
1536           make_pixmap_from_file(tmpname, newpix);
1537           /* Now add to list */
1538           if (pixmap_list == (Bubble_Step *)NULL) {
1539             pixmap_list = newpix;
1540           } else {
1541             tmppix = pixmap_list;
1542             while (tmppix->next != (Bubble_Step *)NULL)
1543               tmppix = tmppix->next;
1544             tmppix->next = newpix;
1545           }
1546           newpix->next = (Bubble_Step *)NULL;
1547           unlink(tmpname);
1548         } else {
1549           write(tmpfd, buf, strlen(buf));
1550           write(tmpfd, "\n", 1);
1551         }
1552       } else {
1553         if (strncmp("/* XPM */", buf, 9) == 0) {
1554           tmpfd = create_temp_file(&tmpname);
1555 /* This proves XPM's performance is kinda pathetic */
1556 #ifdef DEBUG
1557           printf("New XPM detected : %s, fd=%d\n", tmpname, tmpfd); 
1558 #endif /* DEBUG */
1559           inxpm = 1;
1560           xpmseen = 1;
1561         }
1562         write(tmpfd, buf, strlen(buf));
1563         write(tmpfd, "\n", 1);
1564       }
1565       break;
1566     default:
1567       fprintf(stderr, "read_line returned unknown code %d\n", rv);
1568       exit(1);
1569     }
1570
1571     free(buf);
1572   }
1573
1574   close(fd);
1575   if (buf != (char *)NULL)
1576     free(buf);
1577   if (tmpname != (char *)NULL)
1578     free(tmpname);
1579
1580   if (! xpmseen) {
1581     fprintf(stderr, "There was no XPM data in the file %s\n", fname);
1582     exit(1);
1583   }
1584
1585   /* Finally construct step_pixmaps[] */
1586   make_pixmap_array(pixmap_list);
1587 }
1588
1589 static void 
1590 shell_exec(char *command)
1591 /* Forks a shell to execute "command" then waits for command to finish */
1592 {
1593   int pid, status, wval;
1594
1595   switch(pid=fork()) {
1596   case 0:
1597     if (execlp(BOURNESH, BOURNESH, "-c", command, (char *)NULL) == -1) {
1598       fprintf(stderr, "Couldn't exec shell %s\n", BOURNESH);
1599       exit(1);
1600     }
1601     /* fall through if execlp() fails */
1602   case -1:
1603     /* Couldn't fork */
1604     perror(progname);
1605     exit(1);
1606   default:
1607     while ((wval = wait(&status)) != pid)
1608       if (wval == -1) {
1609         perror(progname);
1610         exit(1);
1611       }    
1612   }
1613 }
1614
1615 static void 
1616 uncompress_file(char *current, char *namebuf)
1617 /* If the file current is compressed (i.e. its name ends in .gz or .Z,
1618 no check is made to see if it is actually a compressed file...) then a
1619 new temporary file is created for it and it is decompressed into there,
1620 returning the name of the file to namebuf, else current is returned in
1621 namebuf */
1622 {
1623   int fd;
1624   char *tname = (char *)NULL;
1625   char argbuf[COMMAND_BUF_SIZE];
1626
1627   if (((strlen(current) >=4) && 
1628        (strncmp(&current[strlen(current)-3], ".gz", 3) == 0)) || 
1629       ((strlen(current) >=3) && 
1630        (strncmp(&current[strlen(current)-2], ".Z", 2) == 0))) {
1631     fd = create_temp_file(&tname);
1632     /* close immediately but don't unlink so we should have a zero length
1633        file in /tmp which we can append to */
1634     close(fd);
1635     if (sprintf(argbuf, "%s -dc %s > %s", GZIP, current, tname) > 
1636         (COMMAND_BUF_SIZE-1)) {
1637       fprintf(stderr, "command buffer overflowed in uncompress_file()\n");
1638       exit(1);
1639     }
1640     shell_exec(argbuf);
1641     strcpy(namebuf, tname);
1642   } else {
1643     strcpy(namebuf, current);
1644   }
1645   return;
1646 }
1647
1648 #endif /* BUBBLES_IO */
1649
1650 /* 
1651  * Main stuff 
1652  */
1653
1654
1655 static void 
1656 get_resources(Display *dpy, Window window)
1657 /* Get the appropriate X resources and warn about any inconsistencies. */
1658 {
1659   Bool nodelay;
1660 #ifdef BUBBLES_IO
1661 #ifdef HAVE_XPM
1662   char *dirname;
1663 #else
1664   char *foo, *bar;
1665 #endif /* HAVE_XPM */
1666 #endif /* BUBBLES_IO */
1667
1668   XWindowAttributes xgwa;
1669   Colormap cmap;
1670   XGetWindowAttributes (dpy, window, &xgwa);
1671   cmap = xgwa.colormap;
1672
1673   threed = get_boolean_resource("3D", "Boolean");
1674   quiet = get_boolean_resource("quiet", "Boolean");
1675   simple = get_boolean_resource("simple", "Boolean");
1676   /* Forbid rendered bubbles on monochrome displays */
1677   if ((mono_p) && (! simple)) {
1678     if (! quiet)
1679       fprintf(stderr,
1680               "Rendered bubbles not supported on monochrome displays\n");
1681     simple = True;
1682   }
1683   delay = get_integer_resource("delay", "Integer");
1684   nodelay = get_boolean_resource("nodelay", "Boolean");
1685   if (nodelay)
1686     delay = 0;
1687   if (delay < 0)
1688     delay = 0;
1689
1690   default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy,
1691                                          cmap);
1692   default_bg_pixel = get_pixel_resource ("background", "Background", dpy,
1693                                          cmap);
1694
1695   if (simple) {
1696     /* This is easy */
1697     broken = get_boolean_resource("broken", "Boolean");
1698     if (broken)
1699       if (! quiet)
1700         fprintf(stderr, "-broken not available in simple mode\n");
1701   } else {
1702 #ifndef HAVE_XPM
1703     simple = 1;
1704 #else
1705     broken = get_boolean_resource("broken", "Boolean");
1706 #ifdef BUBBLES_IO
1707     pixmap_file = get_string_resource("file", "File");
1708     dirname = get_string_resource("directory", "Directory");    
1709 #ifdef NO_DEFAULT_BUBBLE
1710     /* Must specify -file or -directory if no default bubble compiled in */
1711     if (strcmp(pixmap_file, "(default)") != 0) {
1712     } else if (strcmp(dirname, "(default)") != 0) {
1713       if ((pixmap_file = get_random_name(dirname)) == (char *)NULL) {
1714         /* Die if we can't open directory - make it consistent with -file
1715            when it fails, rather than falling back to default. */
1716         exit(1);
1717       }
1718     } else {
1719       fprintf(stderr,
1720               "No default bubble compiled in - use -file or -directory\n");
1721       exit(1);
1722     }
1723 #else
1724     if (strcmp(pixmap_file, "(default)") != 0) {
1725     } else if (strcmp(dirname, "(default)") != 0) {
1726       if ((pixmap_file = get_random_name(dirname)) == (char *)NULL) {
1727         exit(1);
1728       }
1729     } else {
1730       /* Use default bubble */
1731       use_default_bubble = 1;
1732     }
1733 #endif /* NO_DEFAULT_BUBBLE */
1734 #else 
1735     use_default_bubble = 1;
1736 #endif /* BUBBLES_IO */
1737 #endif /* HAVE_XPM */
1738   }
1739 }
1740
1741 static void
1742 init_bubbles (Display *dpy, Window window)
1743 {
1744   XGCValues gcv;
1745   XWindowAttributes xgwa;
1746   int i;
1747 #ifdef BUBBLES_IO
1748   char uncompressed[1024];
1749 #endif /* BUBBLES_IO */
1750
1751   defdsp = dpy;
1752   defwin = window;
1753
1754   ya_rand_init(0);
1755
1756   get_resources(dpy, window);
1757
1758   XGetWindowAttributes (dpy, window, &xgwa);
1759
1760 #ifdef DEBUG
1761   printf("sizof(int) on this platform is %d\n", sizeof(int));
1762   printf("sizof(long) on this platform is %d\n", sizeof(long));
1763 #endif /* DEBUG */
1764
1765   screen_width = xgwa.width;
1766   screen_height = xgwa.height;
1767   screen_depth = xgwa.depth;
1768   defcmap = xgwa.colormap;
1769   defvisual = xgwa.visual;
1770
1771   if (simple) {
1772     /* These are pretty much plucked out of the air */
1773     bubble_min_radius = (int)(0.006*(double)(MIN(screen_width, 
1774                                                  screen_height)));
1775     bubble_max_radius = (int)(0.045*(double)(MIN(screen_width,
1776                                                  screen_height)));
1777     /* Some trivial values */
1778     if (bubble_min_radius < 1)
1779       bubble_min_radius = 1;
1780     if (bubble_max_radius <= bubble_min_radius)
1781       bubble_max_radius = bubble_min_radius + 1;
1782
1783     mesh_length = (2 * bubble_max_radius) + 3;
1784
1785     /* store area of each bubble of certain radius as number of 1/10s of
1786        a pixel area.  PI is defined in <math.h> */
1787     bubble_areas = (long *)xmalloc((bubble_max_radius + 2) * sizeof(int));
1788     for (i = 0; i < bubble_min_radius; i++)
1789       bubble_areas[i] = 0;
1790     for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
1791       bubble_areas[i] = calc_bubble_area(i);
1792
1793     mesh_length = (2 * bubble_max_radius) + 3;
1794   } else {
1795 #ifndef HAVE_XPM
1796     fprintf(stderr,
1797             "Bug: simple mode code not set but HAVE_XPM not defined\n");
1798     exit(1);
1799 #else
1800     /* Make sure all #ifdef sort of things have been taken care of in
1801        get_resources(). */
1802     if (use_default_bubble) {
1803 #ifdef NO_DEFAULT_BUBBLE
1804       fprintf(stderr,
1805               "Bug: use_default_bubble and NO_DEFAULT_BUBBLE both defined\n");
1806       exit(1);
1807 #else
1808       default_to_pixmaps();
1809 #endif /* NO_DEFAULT_BUBBLE */
1810
1811       /* Set mesh length */
1812       mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
1813     } else {
1814 #ifdef BUBBLES_IO
1815       if (! regular_file(pixmap_file)) {
1816         /* perror() in regular_file printed error message */
1817         exit(1);
1818       }
1819       uncompress_file(pixmap_file, uncompressed);
1820       read_file_to_pixmaps(uncompressed);
1821       if (strcmp(pixmap_file, uncompressed))
1822         unlink(uncompressed);
1823
1824       mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
1825 #else
1826       fprintf(stderr,
1827         "Bug: use_default_bubble is not defined yet I/O is not compiled in\n");
1828       exit(1);
1829 #endif /* BUBBLES_IO */
1830     }
1831 #endif /* HAVE_XPM */
1832
1833     /* Am I missing something in here??? */
1834   }
1835
1836   mesh_width = (screen_width / mesh_length) + 1;
1837   mesh_height = (screen_height / mesh_length) + 1;
1838   mesh_cells = mesh_width * mesh_height;
1839   init_mesh();
1840
1841   calculate_adjacent_list();
1842
1843   adjust_areas();
1844
1845   /* Graphics contexts for simple mode */
1846   if (simple) {
1847     gcv.foreground = default_fg_pixel;
1848     draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1849     gcv.foreground = default_bg_pixel;
1850     erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1851   }
1852
1853   XClearWindow (dpy, window);
1854 }
1855
1856 static void 
1857 bubbles (Display *dpy, Window window)
1858 {
1859   Bubble *tmp;
1860
1861   tmp = new_bubble();
1862   add_to_mesh(tmp);
1863   insert_new_bubble(tmp);
1864
1865   XSync (dpy, True);
1866 }
1867
1868
1869 void 
1870 screenhack (Display *dpy, Window window)
1871 {
1872   init_bubbles (dpy, window);
1873   while (1) {
1874     bubbles (dpy, window);
1875     if (delay)
1876       usleep(delay);
1877   }
1878 }
1879