http://www.jwz.org/xscreensaver/xscreensaver-5.07.tar.gz
[xscreensaver] / hacks / bubbles.c
index 2033130c38b0f34a946e227cef42323c23fd6809..54a0457432c189cbd44392c447708c26ee263e4c 100644 (file)
@@ -1,19 +1,17 @@
 /* bubbles.c - frying pan / soft drink in a glass simulation */
 
-/*$Id: bubbles.c,v 1.1 1996/09/08 01:35:40 jwz Exp $*/
+/*$Id: bubbles.c,v 1.30 2008/07/31 19:27:48 jwz Exp $*/
 
 /*
  *  Copyright (C) 1995-1996 James Macnicol
  *
- *   This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any later
- * version.
- *
- *   This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details. 
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
  */
 
 /*
  * and things are _much_ nicer.)
  *
  * Author:           James Macnicol 
- * Internet E-mail : J.Macnicol@student.anu.edu.au
+ * Internet E-mail : j-macnicol@adfa.edu.au
  */
 
-#include "bubbles.h"
-
-#ifdef BUBBLES_IO
-#include <dirent.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#endif /* BUBBLES_IO */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
 
-#include <limits.h>
 #include <math.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <unistd.h>
+#include <limits.h>
+
+#ifndef VMS
+# include <sys/wait.h>
+#else /* VMS */
+# if __DECC_VER >= 50200000
+#  include <sys/wait.h>
+# endif
+#endif /* VMS */
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
 #include "screenhack.h"
-#include "../utils/yarandom.h"
+#include "yarandom.h"
+#include "bubbles.h"
+#include "xpm-pixmap.h"
 
-#ifdef HAVE_XPM
-#include <X11/xpm.h>
+#if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
+# define FANCY_BUBBLES
 #endif
 
 /* 
  * Public variables 
  */
 
-#ifndef NO_DEFAULT_BUBBLE
-extern int num_default_bubbles;
-extern char **default_bubbles[];
-#endif /* NO_DEFAULT_BUBBLE */
-
-char *progclass = "Bubbles";
-
-char *defaults [] = {
-  "*background: black",
-  "*foreground: white",
-  "*simple:     false",
-  "*broken:     false",
-  "*delay:      2000",
-#ifdef BUBBLES_IO
-  "*file:       (default)",
-  "*directory:  (default)",
-#endif /* BUBBLES_IO */
-  "*quiet:      false", 
-  "*nodelay:    false",
-  "*3D:     false",
-  "*geometry:   400x300",
+static const char *bubbles_defaults [] = {
+  ".background:                black",
+  ".foreground:                white",
+  "*fpsSolid:          true",
+  "*simple:            false",
+  "*broken:            false",
+  "*delay:             10000",
+  "*quiet:             false", 
+  "*mode:              float",
+  "*trails:            false",
+  "*3D:                        false",
   0
 };
 
-XrmOptionDescRec options [] = {
+static XrmOptionDescRec bubbles_options [] = {
   { "-simple",          ".simple",      XrmoptionNoArg, "true" },
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
   { "-broken",          ".broken",      XrmoptionNoArg, "true" },
-#endif /* HAVE_XPM */
+#endif
   { "-quiet",           ".quiet",       XrmoptionNoArg, "true" },
-  { "-nodelay",         ".nodelay",     XrmoptionNoArg, "true" },
   { "-3D",          ".3D",      XrmoptionNoArg, "true" },
-#ifdef BUBBLES_IO
-  { "-file",            ".file",        XrmoptionSepArg, 0 },
-  { "-directory",       ".directory",   XrmoptionSepArg, 0 },
-#endif /* BUBBLES_IO */
-  { "-delay",           ".delay",       XrmoptionSepArg, 0 }
+  { "-delay",           ".delay",       XrmoptionSepArg, 0 },
+  { "-mode",            ".mode",        XrmoptionSepArg, 0 },
+  { "-drop",            ".mode",        XrmoptionNoArg, "drop" },
+  { "-rise",            ".mode",        XrmoptionNoArg, "rise" },
+  { "-trails",          ".trails",      XrmoptionNoArg, "true" },
+  { 0, 0, 0, 0 }
 };
-int options_size = (sizeof (options) / sizeof (options[0]));
 
 /* 
  * Private variables 
  */
 
-static Bubble **mesh;
-static int mesh_length;
-static int mesh_width;
-static int mesh_height;
-static int mesh_cells;
+struct state {
+  Display *dpy;
+  Window window;
 
-static int **adjacent_list;
+  Bubble **mesh;
+  int mesh_length;
+  int mesh_width;
+  int mesh_height;
+  int mesh_cells;
 
-static int screen_width;
-static int screen_height;
-static int screen_depth;
-static unsigned int default_fg_pixel, default_bg_pixel;
-/* 
- * I know it's not elegant to save this stuff in global variables
- * but we need it for the signal handler.
- */
-static Display *defdsp;
-static Window defwin;
-static Colormap defcmap;
-
-/* For simple mode only */
-static int bubble_min_radius;
-static int bubble_max_radius;
-static long *bubble_areas;
-static GC draw_gc, erase_gc;
-
-#ifdef HAVE_XPM
-static int num_bubble_pixmaps;
-static Bubble_Step **step_pixmaps;
-#ifdef BUBBLES_IO
-static char *pixmap_file;
-#endif /* BUBBLES_IO */
-static int use_default_bubble;
-#endif /* HAVE_XPM */
-
-/* Options stuff */
-#ifdef HAVE_XPM
-static Bool simple = False;
-#else
-static Bool simple = True;
+  int **adjacent_list;
+
+  int screen_width;
+  int screen_height;
+  int screen_depth;
+  unsigned int default_fg_pixel, default_bg_pixel;
+
+  int bubble_min_radius;       /* For simple mode only */
+  int bubble_max_radius;
+  long *bubble_areas;
+  int *bubble_droppages;
+  GC draw_gc, erase_gc;
+
+#ifdef FANCY_BUBBLES
+  int num_bubble_pixmaps;
+  Bubble_Step **step_pixmaps;
 #endif
-static Bool broken = False;
-static Bool quiet = False;
-static Bool threed = False;
-static int delay;
+
+  Bool simple;
+  Bool broken;
+  Bool quiet;
+  Bool threed;
+  Bool drop;
+  Bool trails;
+  int drop_dir;
+  int delay;
+};
+
+static int drop_bubble( struct state *st, Bubble *bb );
 
 /* 
  * To prevent forward references, some stuff is up here 
  */
 
 static long
-calc_bubble_area(r)
-     int r;
+calc_bubble_area(struct state *st, int r)
 /* Calculate the area of a bubble of radius r */
 {
 #ifdef DEBUG
   printf("%d %g\n", r,
         10.0 * PI * (double)r * (double)r * (double)r);
 #endif /* DEBUG */
-  if (threed)
+  if (st->threed)
     return (long)(10.0 * PI * (double)r * (double)r * (double)r);
   else
     return (long)(10.0 * PI * (double)r * (double)r);
 }
 
 static void *
-xmalloc(size)
-     size_t size;
+xmalloc(size_t size)
 /* Safe malloc */
 {
   void *ret;
@@ -193,8 +177,7 @@ xmalloc(size)
 
 #ifdef DEBUG
 static void 
-die_bad_bubble(bb)
-     Bubble *bb;
+die_bad_bubble(Bubble *bb)
 /* This is for use with GDB */
 {
   fprintf(stderr, "Bad bubble detected at 0x%x!\n", (int)bb);
@@ -203,8 +186,7 @@ die_bad_bubble(bb)
 #endif
 
 static int
-null_bubble(bb)
-     Bubble *bb;
+null_bubble(Bubble *bb)
 /* Returns true if the pointer passed is NULL.  If not then this checks to
 see if the bubble is valid (i.e. the (x,y) position is valid and the magic
 number is set correctly.  This only a sanity check for debugging and is
@@ -213,7 +195,7 @@ turned off if DEBUG isn't set. */
   if (bb == (Bubble *)NULL)
     return 1;
 #ifdef DEBUG
-  if ((bb->cell_index < 0) || (bb->cell_index > mesh_cells)) {
+  if ((bb->cell_index < 0) || (bb->cell_index > st->mesh_cells)) {
     fprintf(stderr, "cell_index = %d\n", bb->cell_index);
     die_bad_bubble(bb);
   }
@@ -221,28 +203,28 @@ turned off if DEBUG isn't set. */
     fprintf(stderr, "Magic = %d\n", bb->magic);
     die_bad_bubble(bb);
   }
-  if (simple) {
-    if ((bb->x < 0) || (bb->x > screen_width) ||
-       (bb->y < 0) || (bb->y > screen_height) ||
-       (bb->radius < bubble_min_radius) || (bb->radius >
-                                            bubble_max_radius)) {
+  if (st->simple) {
+    if ((bb->x < 0) || (bb->x > st->screen_width) ||
+       (bb->y < 0) || (bb->y > st->screen_height) ||
+       (bb->radius < st->bubble_min_radius) || (bb->radius >
+                                            st->bubble_max_radius)) {
       fprintf(stderr,
-             "radius = %d, x = %d, y = %d, magic = %d, \
-cell index = %d\n", bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
+             "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
+             bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
       die_bad_bubble(bb);  
     }
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
   } else {
-    if ((bb->x < 0) || (bb->x > screen_width) ||
-       (bb->y < 0) || (bb->y > screen_height) ||
-       (bb->radius < step_pixmaps[0]->radius) || 
-       (bb->radius > step_pixmaps[num_bubble_pixmaps-1]->radius)) {
+    if ((bb->x < 0) || (bb->x > st->screen_width) ||
+       (bb->y < 0) || (bb->y > st->screen_height) ||
+       (bb->radius < st->step_pixmaps[0]->radius) || 
+       (bb->radius > st->step_pixmaps[st->num_bubble_pixmaps-1]->radius)) {
       fprintf(stderr,
-             "radius = %d, x = %d, y = %d, magic = %d, \
-cell index = %d\n", bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
+             "radius = %d, x = %d, y = %d, magic = %d, cell index = %d\n",
+             bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
       die_bad_bubble(bb);  
     }
-#endif /* HAVE_XPM */
+#endif
   }
 #endif /* DEBUG */
   return 0;
@@ -250,8 +232,7 @@ cell index = %d\n", bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
 
 #ifdef DEBUG
 static void 
-print_bubble_list(bb)
-     Bubble *bb;
+print_bubble_list(Bubble *bb)
 /* Print list of where all the bubbles are.  For debugging purposes only. */
 {
   if (! null_bubble(bb)) {
@@ -262,9 +243,7 @@ print_bubble_list(bb)
 #endif /* DEBUG */
 
 static void 
-add_bubble_to_list(list, bb)
-     Bubble **list;
-     Bubble *bb;
+add_bubble_to_list(Bubble **list, Bubble *bb)
 /* Take a pointer to a list of bubbles and stick bb at the head of the
  list. */
 {
@@ -288,20 +267,18 @@ add_bubble_to_list(list, bb)
 
 
 static void 
-init_mesh()
+init_mesh (struct state *st)
 /* Setup the mesh of bubbles */
 {
   int i;
 
-  mesh = (Bubble **)xmalloc(mesh_cells * sizeof(Bubble *));
-  for (i = 0; i < mesh_cells; i++)
-    mesh[i] = (Bubble *)NULL;
+  st->mesh = (Bubble **)xmalloc(st->mesh_cells * sizeof(Bubble *));
+  for (i = 0; i < st->mesh_cells; i++)
+    st->mesh[i] = (Bubble *)NULL;
 }
 
 static int
-cell_to_mesh(x, y)
-     int x;
-     int y;
+cell_to_mesh(struct state *st, int x, int y)
 /* convert cell coordinates to mesh index */
 {
 #ifdef DEBUG
@@ -310,44 +287,36 @@ cell_to_mesh(x, y)
     exit(1);
   }
 #endif
-  return ((mesh_width * y) + x);
+  return ((st->mesh_width * y) + x);
 }
 
 static void 
-mesh_to_cell(mi, cx, cy)
-     int mi;
-     int *cx;
-     int *cy;
+mesh_to_cell(struct state *st, int mi, int *cx, int *cy)
 /* convert mesh index into cell coordinates */
 {
-  *cx = mi % mesh_width;
-  *cy = mi / mesh_width;
+  *cx = mi % st->mesh_width;
+  *cy = mi / st->mesh_width;
 }
 
 static int
-pixel_to_mesh(x, y)
-     int x;
-     int y;
+pixel_to_mesh(struct state *st, int x, int y)
 /* convert screen coordinates into mesh index */
 {
-  return cell_to_mesh((x / mesh_length), (y / mesh_length));
+  return cell_to_mesh(st, (x / st->mesh_length), (y / st->mesh_length));
 }
 
 static int
-verify_mesh_index(x, y)
-     int x;
-     int y;
+verify_mesh_index(struct state *st, int x, int y)
 /* check to see if (x,y) is in the mesh */
 {
-  if ((x < 0) || (y < 0) || (x >= mesh_width) || (y >= mesh_height))
+  if ((x < 0) || (y < 0) || (x >= st->mesh_width) || (y >= st->mesh_height))
     return (-1);
-  return (cell_to_mesh(x, y));
+  return (cell_to_mesh(st, x, y));
 }
 
 #ifdef DEBUG
 static void 
-print_adjacents(adj)
-    int *adj;
+print_adjacents(int *adj)
 /* Print a list of the cells calculated above.  For debugging only. */
 {
   int i;
@@ -360,8 +329,7 @@ print_adjacents(adj)
 #endif /* DEBUG */
 
 static void 
-add_to_mesh(bb)
-     Bubble *bb;
+add_to_mesh(struct state *st, Bubble *bb)
 /* Add the given bubble to the mesh by sticking it on the front of the
 list.  bb is already allocated so no need to malloc() anything, just
 adjust pointers. */
@@ -373,48 +341,48 @@ adjust pointers. */
   }
 #endif /* DEBUG */
 
-  add_bubble_to_list(&mesh[bb->cell_index], bb);
+  add_bubble_to_list(&st->mesh[bb->cell_index], bb);
 }
 
 #ifdef DEBUG
 static void 
-print_mesh()
+print_mesh (struct state *st)
 /* Print the contents of the mesh */
 {
   int i;
 
-  for (i = 0; i < mesh_cells; i++) {
-    if (! null_bubble(mesh[i])) {
+  for (i = 0; i < st->mesh_cells; i++) {
+    if (! null_bubble(st->mesh[i])) {
       printf("Mesh cell %d\n", i);
-      print_bubble_list(mesh[i]);
+      print_bubble_list(st->mesh[i]);
     }
   }
 }
 
 static void 
-valid_mesh()
+valid_mesh (struct state *st)
 /* Check to see if the mesh is Okay.  For debugging only. */
 {
   int i;
   Bubble *b;
 
-  for (i = 0; i < mesh_cells; i++) {
-    b = mesh[i];
+  for (i = 0; i < st->mesh_cells; i++) {
+    b = st->mesh[i];
     while (! null_bubble(b))
       b = b->next;
   }
 }
 
 static int
-total_bubbles()
+total_bubbles (struct state *st)
 /* Count how many bubbles there are in total.  For debugging only. */
 {
   int rv = 0;
   int i;
   Bubble *b;
 
-  for (i = 0; i < mesh_cells; i++) {
-    b = mesh[i];
+  for (i = 0; i < st->mesh_cells; i++) {
+    b = st->mesh[i];
     while (! null_bubble(b)) {
       rv++;
       b = b->next;
@@ -426,31 +394,31 @@ total_bubbles()
 #endif /* DEBUG */
 
 static void 
-calculate_adjacent_list()
+calculate_adjacent_list (struct state *st)
 /* Calculate the list of cells adjacent to a particular cell for use
    later. */
 {
   int i; 
   int ix, iy;
 
-  adjacent_list = (int **)xmalloc(mesh_cells * sizeof(int *));
-  for (i = 0; i < mesh_cells; i++) {
-    adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
-    mesh_to_cell(i, &ix, &iy);
-    adjacent_list[i][0] = verify_mesh_index(--ix, --iy);
-    adjacent_list[i][1] = verify_mesh_index(++ix, iy);
-    adjacent_list[i][2] = verify_mesh_index(++ix, iy);
-    adjacent_list[i][3] = verify_mesh_index(ix, ++iy);
-    adjacent_list[i][4] = verify_mesh_index(ix, ++iy);
-    adjacent_list[i][5] = verify_mesh_index(--ix, iy);
-    adjacent_list[i][6] = verify_mesh_index(--ix, iy);
-    adjacent_list[i][7] = verify_mesh_index(ix, --iy);
-    adjacent_list[i][8] = i;
+  st->adjacent_list = (int **)xmalloc(st->mesh_cells * sizeof(int *));
+  for (i = 0; i < st->mesh_cells; i++) {
+    st->adjacent_list[i] = (int *)xmalloc(9 * sizeof(int));
+    mesh_to_cell(st, i, &ix, &iy);
+    st->adjacent_list[i][0] = verify_mesh_index(st, --ix, --iy);
+    st->adjacent_list[i][1] = verify_mesh_index(st, ++ix, iy);
+    st->adjacent_list[i][2] = verify_mesh_index(st, ++ix, iy);
+    st->adjacent_list[i][3] = verify_mesh_index(st, ix, ++iy);
+    st->adjacent_list[i][4] = verify_mesh_index(st, ix, ++iy);
+    st->adjacent_list[i][5] = verify_mesh_index(st, --ix, iy);
+    st->adjacent_list[i][6] = verify_mesh_index(st, --ix, iy);
+    st->adjacent_list[i][7] = verify_mesh_index(st, ix, --iy);
+    st->adjacent_list[i][8] = i;
   }
 }
 
 static void
-adjust_areas()
+adjust_areas (struct state *st)
 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
 {
   double maxvalue;
@@ -458,46 +426,46 @@ adjust_areas()
   long factor;
   int i;
 
-#ifdef HAVE_XPM
-  if (simple)
-    maxarea = bubble_areas[bubble_max_radius+1];
+#ifdef FANCY_BUBBLES
+  if (st->simple)
+    maxarea = st->bubble_areas[st->bubble_max_radius+1];
   else
-    maxarea = step_pixmaps[num_bubble_pixmaps]->area;
-#else
-  maxarea = bubble_areas[bubble_max_radius+1];
-#endif /* HAVE_XPM */
-  maxvalue = (double)screen_width * 2.0 * (double)maxarea;
+    maxarea = st->step_pixmaps[st->num_bubble_pixmaps]->area;
+#else /* !FANCY_BUBBLES */
+  maxarea = st->bubble_areas[st->bubble_max_radius+1];
+#endif /* !FANCY_BUBBLES */
+  maxvalue = (double)st->screen_width * 2.0 * (double)maxarea;
   factor = (long)ceil(maxvalue / (double)LONG_MAX);
   if (factor > 1) {
     /* Overflow will occur in weighted_mean().  We must divide areas
        each by factor so it will never do so. */
-#ifdef HAVE_XPM
-    if (simple) {
-      for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
-       bubble_areas[i] /= factor;
-       if (bubble_areas[i] == 0)
-         bubble_areas[i] = 1;
+#ifdef FANCY_BUBBLES
+    if (st->simple) {
+      for (i = st->bubble_min_radius; i <= st->bubble_max_radius+1; i++) {
+       st->bubble_areas[i] /= factor;
+       if (st->bubble_areas[i] == 0)
+         st->bubble_areas[i] = 1;
       }
     } else {
-      for (i = 0; i <= num_bubble_pixmaps; i++) {
+      for (i = 0; i <= st->num_bubble_pixmaps; i++) {
 #ifdef DEBUG
-       printf("area = %ld", step_pixmaps[i]->area);
+       printf("area = %ld", st->step_pixmaps[i]->area);
 #endif /* DEBUG */
-       step_pixmaps[i]->area /= factor;
-       if (step_pixmaps[i]->area == 0)
-         step_pixmaps[i]->area = 1;
+       st->step_pixmaps[i]->area /= factor;
+       if (st->step_pixmaps[i]->area == 0)
+         st->step_pixmaps[i]->area = 1;
 #ifdef DEBUG
-       printf("-> %ld\n", step_pixmaps[i]->area);
+       printf("-> %ld\n", st->step_pixmaps[i]->area);
 #endif /* DEBUG */
       }
     }
-#else
-    for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
-      bubble_areas[i] /= factor;
-      if (bubble_areas[i] == 0)
-       bubble_areas[i] = 1;
+#else /* !FANCY_BUBBLES */
+    for (i = st->bubble_min_radius; i <= st->bubble_max_radius+1; i++) {
+      st->bubble_areas[i] /= factor;
+      if (st->bubble_areas[i] == 0)
+       st->bubble_areas[i] = 1;
     }
-#endif /* HAVE_XPM */
+#endif /* !FANCY_BUBBLES */
   }
 #ifdef DEBUG
   printf("maxarea = %ld\n", maxarea);
@@ -512,7 +480,7 @@ adjust_areas()
  */
 
 static Bubble *
-new_bubble()
+new_bubble (struct state *st)
 /* Add a new bubble at some random position on the screen of the smallest
 size. */
 {
@@ -524,28 +492,27 @@ size. */
     exit(1);
   }
 
-  if (simple) {
-    rv->radius = bubble_min_radius;
-    rv->area = bubble_areas[bubble_min_radius];
-#ifdef HAVE_XPM
+  if (st->simple) {
+    rv->radius = st->bubble_min_radius;
+    rv->area = st->bubble_areas[st->bubble_min_radius];
+#ifdef FANCY_BUBBLES
   } else {
     rv->step = 0;
-    rv->radius = step_pixmaps[0]->radius;
-    rv->area = step_pixmaps[0]->area;
-#endif /* HAVE_XPM */
+    rv->radius = st->step_pixmaps[0]->radius;
+    rv->area = st->step_pixmaps[0]->area;
+#endif /* FANCY_BUBBLES */
   }
   rv->visible = 0;
   rv->magic = BUBBLE_MAGIC;
-  rv->x = ya_random() % screen_width;
-  rv->y = ya_random() % screen_height;
-  rv->cell_index = pixel_to_mesh(rv->x, rv->y);
+  rv->x = random() % st->screen_width;
+  rv->y = random() % st->screen_height;
+  rv->cell_index = pixel_to_mesh(st, rv->x, rv->y);
 
   return rv;
 }
 
 static void 
-show_bubble(bb)
-     Bubble *bb;
+show_bubble(struct state *st, Bubble *bb)
 /* paint the bubble on the screen */
 {
   if (null_bubble(bb)) {
@@ -556,30 +523,29 @@ show_bubble(bb)
   if (! bb->visible) {
     bb->visible = 1;
 
-    if (simple) {
-      XDrawArc(defdsp, defwin, draw_gc, (bb->x - bb->radius),
+    if (st->simple) {
+      XDrawArc(st->dpy, st->window, st->draw_gc, (bb->x - bb->radius),
               (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
               360*64);  
     } else {
-#ifdef HAVE_XPM
-      XSetClipOrigin(defdsp, step_pixmaps[bb->step]->draw_gc, 
+#ifdef FANCY_BUBBLES
+      XSetClipOrigin(st->dpy, st->step_pixmaps[bb->step]->draw_gc, 
                     (bb->x - bb->radius),
                     (bb->y - bb->radius));
       
-      XCopyArea(defdsp, step_pixmaps[bb->step]->ball, defwin
-               step_pixmaps[bb->step]->draw_gc,
+      XCopyArea(st->dpy, st->step_pixmaps[bb->step]->ball, st->window
+               st->step_pixmaps[bb->step]->draw_gc,
                0, 0, (bb->radius * 2), 
                (bb->radius * 2),  
                (bb->x - bb->radius),
                (bb->y - bb->radius));
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
     }
   }
 }
 
 static void 
-hide_bubble(bb)
-     Bubble *bb;
+hide_bubble(struct state *st, Bubble *bb)
 /* erase the bubble */
 {
   if (null_bubble(bb)) {
@@ -590,31 +556,29 @@ hide_bubble(bb)
   if (bb->visible) {
     bb->visible = 0;
 
-    if (simple) {
-      XDrawArc(defdsp, defwin, erase_gc, (bb->x - bb->radius),
+    if (st->simple) {
+      XDrawArc(st->dpy, st->window, st->erase_gc, (bb->x - bb->radius),
               (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
               360*64);
     } else {
-#ifdef HAVE_XPM
-      if (! broken) {
-       XSetClipOrigin(defdsp, step_pixmaps[bb->step]->erase_gc, 
+#ifdef FANCY_BUBBLES
+      if (! st->broken) {
+       XSetClipOrigin(st->dpy, st->step_pixmaps[bb->step]->erase_gc, 
                       (bb->x - bb->radius), (bb->y - bb->radius));
        
-       XFillRectangle(defdsp, defwin, step_pixmaps[bb->step]->erase_gc,
+       XFillRectangle(st->dpy, st->window, st->step_pixmaps[bb->step]->erase_gc,
                       (bb->x - bb->radius),
                       (bb->y - bb->radius),
                       (bb->radius * 2),
                       (bb->radius * 2));
       }
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
     }
   }
 }
 
 static void 
-delete_bubble_in_mesh(bb, keep_bubble)
-     Bubble *bb;
-     int keep_bubble;
+delete_bubble_in_mesh(struct state *st, Bubble *bb, int keep_bubble)
 /* Delete an individual bubble, adjusting list of bubbles around it.
    If keep_bubble is true then the bubble isn't actually deleted.  We
    use this to allow bubbles to change mesh cells without reallocating,
@@ -627,31 +591,29 @@ delete_bubble_in_mesh(bb, keep_bubble)
   } else if ((!null_bubble(bb->prev)) &&
             (null_bubble(bb->next))) {
     bb->prev->next = (Bubble *)NULL;
-    bb->next = mesh[bb->cell_index];
+    bb->next = st->mesh[bb->cell_index];
   } else if ((null_bubble(bb->prev)) &&
             (!null_bubble(bb->next))) {
     bb->next->prev = (Bubble *)NULL;
-    mesh[bb->cell_index] = bb->next;
-    bb->next = mesh[bb->cell_index];
+    st->mesh[bb->cell_index] = bb->next;
+    bb->next = st->mesh[bb->cell_index];
   } else {
     /* Only item on list */
-    mesh[bb->cell_index] = (Bubble *)NULL;
+    st->mesh[bb->cell_index] = (Bubble *)NULL;
   }             
   if (! keep_bubble)
     free(bb);
 }
 
 static unsigned long 
-ulongsqrint(x)
-     int x;
+ulongsqrint(int x)
 /* Saves ugly inline code */
 {
   return ((unsigned long)x * (unsigned long)x);
 }
 
 static Bubble *
-get_closest_bubble(bb)
-     Bubble *bb;
+get_closest_bubble(struct state *st, Bubble *bb)
 /* Find the closest bubble touching the this bubble, NULL if none are
    touching. */
 {
@@ -673,14 +635,14 @@ get_closest_bubble(bb)
   for (i = 0; i < 9; i++) {
     /* There is a bug here where bb->cell_index is negaitve.. */
 #ifdef DEBUG
-    if ((bb->cell_index < 0) || (bb->cell_index >= mesh_cells)) {
+    if ((bb->cell_index < 0) || (bb->cell_index >= st->mesh_cells)) {
       fprintf(stderr, "bb->cell_index = %d\n", bb->cell_index);
       exit(1);
     }
 #endif /* DEBUG */
 /*    printf("%d,", bb->cell_index); */
-    if (adjacent_list[bb->cell_index][i] != -1) {
-      tmp = mesh[adjacent_list[bb->cell_index][i]];
+    if (st->adjacent_list[bb->cell_index][i] != -1) {
+      tmp = st->mesh[st->adjacent_list[bb->cell_index][i]];
       while (! null_bubble(tmp)) {
        if (tmp != bb) {
          dx = tmp->x - bb->x;
@@ -704,15 +666,13 @@ get_closest_bubble(bb)
 
 #ifdef DEBUG
 static void
-ldr_barf()
+ldr_barf (struct state *st)
 {
 }
 #endif /* DEBUG */
 
 static long
-long_div_round(num, dem)
-     long num;
-     long dem;
+long_div_round(long num, long dem)
 {
   long divvie, moddo;
 
@@ -741,17 +701,14 @@ long_div_round(num, dem)
 }
 
 static int
-weighted_mean(n1, n2, w1, w2)
-     int n1;
-     int n2;
-     long w1;
-     long w2;
+weighted_mean(int n1, int n2, long w1, long w2)
 /* Mean of n1 and n2 respectively weighted by weights w1 and w2. */
 {
 #ifdef DEBUG
   if ((w1 <= 0) || (w2 <= 0)) {
-    fprintf(stderr, "Bad weights passed to weighted_mean() - \
-(%d, %d, %ld, %ld)!\n", n1, n2, w1, w2);
+    fprintf(stderr,
+           "Bad weights passed to weighted_mean() - (%d, %d, %ld, %ld)!\n",
+           n1, n2, w1, w2);
     exit(1);
   }
 #endif /* DEBUG */
@@ -760,9 +717,7 @@ weighted_mean(n1, n2, w1, w2)
 }
 
 static int
-bubble_eat(diner, food)
-     Bubble *diner;
-     Bubble *food;
+bubble_eat(struct state *st, Bubble *diner, Bubble *food)
 /* The diner eats the food.  Returns true (1) if the diner still exists */
 { 
   int i;
@@ -782,62 +737,72 @@ bubble_eat(diner, food)
      would then not have to redraw bubbles which don't change in
      size. */
 
-  hide_bubble(diner);
-  hide_bubble(food);
+  hide_bubble(st, diner);
+  hide_bubble(st, food);
   diner->x = weighted_mean(diner->x, food->x, diner->area, food->area);
   diner->y = weighted_mean(diner->y, food->y, diner->area, food->area);
-  newmi = pixel_to_mesh(diner->x, diner->y);
+  newmi = pixel_to_mesh(st, diner->x, diner->y);
   diner->area += food->area;
-  delete_bubble_in_mesh(food, DELETE_BUBBLE);
+  delete_bubble_in_mesh(st, food, DELETE_BUBBLE);
 
-  if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
-    delete_bubble_in_mesh(diner, DELETE_BUBBLE);
-    return 0;
+  if (st->drop) {
+       if ((st->simple) && (diner->area > st->bubble_areas[st->bubble_max_radius])) {
+         diner->area = st->bubble_areas[st->bubble_max_radius];
+       }
+#ifdef FANCY_BUBBLES
+       if ((! st->simple) && (diner->area > st->step_pixmaps[st->num_bubble_pixmaps]->area)) {
+         diner->area = st->step_pixmaps[st->num_bubble_pixmaps]->area;
+       }
+#endif /* FANCY_BUBBLES */
   }
-#ifdef HAVE_XPM
-  if ((! simple) && (diner->area > 
-                    step_pixmaps[num_bubble_pixmaps]->area)) {
-    delete_bubble_in_mesh(diner, DELETE_BUBBLE);
-    return 0;
+  else {
+       if ((st->simple) && (diner->area > st->bubble_areas[st->bubble_max_radius])) {
+         delete_bubble_in_mesh(st, diner, DELETE_BUBBLE);
+         return 0;
+       }
+#ifdef FANCY_BUBBLES
+       if ((! st->simple) && (diner->area > 
+                                          st->step_pixmaps[st->num_bubble_pixmaps]->area)) {
+         delete_bubble_in_mesh(st, diner, DELETE_BUBBLE);
+         return 0;
+       }
+#endif /* FANCY_BUBBLES */
   }
-#endif /* HAVE_XPM */
 
-  if (simple) {
-    if (diner->area > bubble_areas[diner->radius + 1]) {
+  if (st->simple) {
+    if (diner->area > st->bubble_areas[diner->radius + 1]) {
       /* Move the bubble to a new radius */
       i = diner->radius;
-      while (diner->area > bubble_areas[i+1])
-       ++i;
+      while ((i < st->bubble_max_radius - 1) && (diner->area > st->bubble_areas[i+1]))
+               ++i;
       diner->radius = i;
     }
-    show_bubble(diner);
-#ifdef HAVE_XPM
+    show_bubble(st, diner);
+#ifdef FANCY_BUBBLES
   } else {
-    if (diner->area > step_pixmaps[diner->step+1]->area) {
+    if (diner->area > st->step_pixmaps[diner->step+1]->area) {
       i = diner->step;
-      while (diner->area > step_pixmaps[i+1]->area)
-       ++i;
+      while ((i < st->num_bubble_pixmaps - 1) && (diner->area > st->step_pixmaps[i+1]->area))
+               ++i;
       diner->step = i;
-      diner->radius = step_pixmaps[diner->step]->radius;
+      diner->radius = st->step_pixmaps[diner->step]->radius;
     }
-    show_bubble(diner);
-#endif /* HAVE_XPM */
+    show_bubble(st, diner);
+#endif /* FANCY_BUBBLES */
   }
 
   /* Now adjust locations and cells if need be */
   if (newmi != diner->cell_index) {
-    delete_bubble_in_mesh(diner, KEEP_BUBBLE);
+    delete_bubble_in_mesh(st, diner, KEEP_BUBBLE);
     diner->cell_index = newmi;
-    add_to_mesh(diner);
+    add_to_mesh(st, diner);
   }
 
   return 1;
 }
 
 static int
-merge_bubbles(b1, b2)
-     Bubble *b1;
-     Bubble *b2;
+merge_bubbles(struct state *st, Bubble *b1, Bubble *b2)
 /* These two bubbles merge into one.  If the first one wins out return
 1 else return 2.  If there is no winner (it explodes) then return 0 */
 {
@@ -854,13 +819,13 @@ merge_bubbles(b1, b2)
 #endif /* DEBUG */
 
   if (b1 == b2) {
-    hide_bubble(b1);
-    delete_bubble_in_mesh(b1, DELETE_BUBBLE);
+    hide_bubble(st, b1);
+    delete_bubble_in_mesh(st, b1, DELETE_BUBBLE);
     return 0;
   }
 
   if (b1size > b2size) {
-    switch (bubble_eat(b1, b2)) {
+    switch (bubble_eat(st, b1, b2)) {
     case 0:
       return 0;
       break;
@@ -871,7 +836,7 @@ merge_bubbles(b1, b2)
       break;
     }
   } else if (b1size < b2size) {
-    switch (bubble_eat(b2, b1)) {
+    switch (bubble_eat(st, b2, b1)) {
     case 0:
       return 0;
       break;
@@ -882,8 +847,8 @@ merge_bubbles(b1, b2)
       break;
     }
   } else {
-    if ((ya_random() % 2) == 0) {
-      switch (bubble_eat(b1, b2)) {
+    if ((random() % 2) == 0) {
+      switch (bubble_eat(st, b1, b2)) {
       case 0:
        return 0;
        break;
@@ -894,7 +859,7 @@ merge_bubbles(b1, b2)
        break;
       }
     } else {
-      switch (bubble_eat(b2, b1)) {
+      switch (bubble_eat(st, b2, b1)) {
       case 0:
        return 0;
        break;
@@ -911,8 +876,7 @@ merge_bubbles(b1, b2)
 }
 
 static void 
-insert_new_bubble(tmp)
-     Bubble *tmp;
+insert_new_bubble(struct state *st, Bubble *tmp)
 /* Calculates which bubbles are eaten when a new bubble tmp is
    inserted.  This is called recursively in case when a bubble grows
    it eats others.  Careful to pick out disappearing bubbles. */
@@ -928,38 +892,137 @@ insert_new_bubble(tmp)
 #endif /* DEBUG */
   
   nextbub = tmp;
-  touch = get_closest_bubble(nextbub);
-  while (! null_bubble(touch)) {
-    switch (merge_bubbles(nextbub, touch)) {
-    case 2:
-      /* touch ate nextbub and survived */
-      nextbub = touch;
-      break;
-    case 1:
-      /* nextbub ate touch and survived */
-      break;
-    case 0:
-      /* somebody ate someone else but they exploded */
-      nextbub = (Bubble *)NULL;
-      break;
-    default:
-      /* something went wrong */
-      fprintf(stderr, "Error occurred in insert_new_bubble()\n");
-      exit(1);
-    }
-    /* Check to see if there are any other bubbles still in the area
-       and if we need to do this all over again for them. */
-    if (! null_bubble(nextbub))
-      touch = get_closest_bubble(nextbub);
-    else
-      touch = (Bubble *)NULL;
+  touch = get_closest_bubble(st, nextbub);
+  if (null_bubble(touch))
+       return;
+
+  while (1) {
+
+       /* Merge all touching bubbles */
+       while (! null_bubble(touch)) {
+         switch (merge_bubbles(st, nextbub, touch)) {
+         case 2:
+               /* touch ate nextbub and survived */
+               nextbub = touch;
+               break;
+         case 1:
+               /* nextbub ate touch and survived */
+               break;
+         case 0:
+               /* somebody ate someone else but they exploded */
+               nextbub = (Bubble *)NULL;
+               break;
+         default:
+               /* something went wrong */
+               fprintf(stderr, "Error occurred in insert_new_bubble()\n");
+               exit(1);
+         }
+       
+         /* Check to see if any bubble survived. */
+         if (null_bubble(nextbub))
+               break;
+
+         /* Check to see if there are any other bubbles still in the area
+                and if we need to do this all over again for them. */
+         touch = get_closest_bubble(st, nextbub);
+       }
+       
+       if (null_bubble(nextbub))
+         break;
+
+       /* Shift bubble down.  Break if we run off the screen. */
+       if (st->drop) {
+         if (drop_bubble( st, nextbub ) == -1)
+               break;
+       }
+
+       /* Check to see if there are any other bubbles still in the area
+          and if we need to do this all over again for them. */
+       touch = get_closest_bubble(st, nextbub);
+       if (null_bubble(touch)) {
+         /* We also continue every so often if we're dropping and the bubble is at max size */
+         if (st->drop) {
+               if (st->simple) {
+                 if ((nextbub->area >= st->bubble_areas[st->bubble_max_radius - 1]) && (random() % 2 == 0))
+                       continue;
+               }
+#ifdef FANCY_BUBBLES
+               else {
+                 if ((nextbub->step >= st->num_bubble_pixmaps - 1) && (random() % 2 == 0))
+                       continue;
+               }
+#endif /* FANCY_BUBBLES */
+      }
+         break;
+       }
+
   }
 }
 
+
+static void
+leave_trail(struct state *st,  Bubble *bb ) 
+{
+  Bubble *tmp;
+
+  tmp = new_bubble(st);
+  tmp->x = bb->x;
+  tmp->y = bb->y - ((bb->radius + 10) * st->drop_dir);
+  tmp->cell_index = pixel_to_mesh(st, tmp->x, tmp->y);
+  add_to_mesh(st, tmp);
+  insert_new_bubble(st, tmp);
+  show_bubble( st, tmp );      
+}
+
+
+static int
+drop_bubble( struct state *st, Bubble *bb )
+{
+  int newmi;
+
+  hide_bubble( st, bb );
+
+  if (st->simple)
+       (bb->y) += (st->bubble_droppages[bb->radius] * st->drop_dir);
+#ifdef FANCY_BUBBLES
+  else
+       (bb->y) += (st->step_pixmaps[bb->step]->droppage * st->drop_dir);
+#endif /* FANCY_BUBBLES */
+  if ((bb->y < 0) || (bb->y > st->screen_height)) {
+       delete_bubble_in_mesh( st, bb, DELETE_BUBBLE );
+       return -1;
+  }
+
+  show_bubble( st, bb );
+
+  /* Now adjust locations and cells if need be */
+  newmi = pixel_to_mesh(st, bb->x, bb->y);
+  if (newmi != bb->cell_index) {
+    delete_bubble_in_mesh(st, bb, KEEP_BUBBLE);
+    bb->cell_index = newmi;
+    add_to_mesh(st, bb);
+  }
+
+  if (st->trails) {
+       if (st->simple) {
+         if ((bb->area >= st->bubble_areas[st->bubble_max_radius - 1]) && (random() % 2 == 0)) 
+               leave_trail( st, bb );
+       }
+#ifdef FANCY_BUBBLES
+       else { 
+         if ((bb->step >= st->num_bubble_pixmaps - 1) && (random() % 2 == 0))
+               leave_trail( st, bb );
+       }
+#endif /* FANCY_BUBBLES */
+  }
+
+  return 0;
+}
+
+
 #ifdef DEBUG
 static int
-get_length_of_bubble_list(bb)
-     Bubble *bb;
+get_length_of_bubble_list(Bubble *bb)
 {
   Bubble *tmp = bb;
   int rv = 0;
@@ -978,63 +1041,14 @@ get_length_of_bubble_list(bb)
  * still check for XPM, though!
  */
 
-#ifdef HAVE_XPM
-
-static void 
-free_pixmaps()
-/* Free resources associated with XPM */
-{
-  int i;
-
-#ifdef DEBUG
-  if (simple) {
-    fprintf(stderr, "free_pixmaps() called in simple mode\n");
-    exit(1);
-  }
-  printf("free_pixmaps()\n");
-#endif /* DEBUG */
-
-  for(i = 0; i < (num_bubble_pixmaps - 1); i++) {
-    XFreePixmap(defdsp, step_pixmaps[i]->ball);
-    XFreePixmap(defdsp, step_pixmaps[i]->shape_mask);
-    XFreeGC(defdsp, step_pixmaps[i]->draw_gc);
-    XFreeGC(defdsp, step_pixmaps[i]->erase_gc);
-    XFreeColors(defdsp, defcmap, step_pixmaps[i]->xpmattrs.pixels, 
-               step_pixmaps[i]->xpmattrs.npixels, 0);
-    XpmFreeAttributes(&step_pixmaps[i]->xpmattrs);
-  }
-}
-
-static void 
-onintr(a)
-     int a;
-/* This gets called when SIGINT or SIGTERM is received */
-{
-  free_pixmaps();
-  exit(0);
-}
-
-#ifdef DEBUG
-static void
-onsegv(a)
-     int a;
-/* Called when SEGV detected.   Hmmmmm.... */
-{
-  fflush(stdout);
-  fprintf(stderr, "SEGV detected! : %d\n", a);
-  exit(1);
-}
-#endif /* DEBUG */
-
+#ifdef FANCY_BUBBLES
 
 /*
  * Pixmaps without file I/O (but do have XPM)
  */
 
 static void 
-pixmap_sort(head, numelems)
-     Bubble_Step **head;
-     int numelems;
+pixmap_sort(Bubble_Step **head, int numelems)
 /* Couldn't get qsort to work right with this so I wrote my own.  This puts
 the numelems length array with first element at head into order of radius.
 */
@@ -1061,16 +1075,13 @@ the numelems length array with first element at head into order of radius.
 }
 
 static int
-extrapolate(i1, i2)
-     int i1;
-     int i2;
+extrapolate(int i1, int i2)
 {
   return (i2 + (i2 - i1));
 }
 
 static void 
-make_pixmap_array(list)
-     Bubble_Step *list;
+make_pixmap_array(struct state *st, Bubble_Step *list)
 /* From a linked list of bubbles construct the array step_pixmaps */
 {
   Bubble_Step *tmp = list;
@@ -1084,25 +1095,25 @@ make_pixmap_array(list)
     exit(1);
   }
 
-  num_bubble_pixmaps = 1;
+  st->num_bubble_pixmaps = 1;
   while(tmp->next != (Bubble_Step *)NULL) {
     tmp = tmp->next;
-    ++num_bubble_pixmaps;
+    ++st->num_bubble_pixmaps;
   }
 
-  if (num_bubble_pixmaps < 2) {
+  if (st->num_bubble_pixmaps < 2) {
     fprintf(stderr, "Must be at least two bubbles in file\n");
     exit(1);
   }
 
-  step_pixmaps = (Bubble_Step **)xmalloc((num_bubble_pixmaps + 1) * 
+  st->step_pixmaps = (Bubble_Step **)xmalloc((st->num_bubble_pixmaps + 1) * 
                                         sizeof(Bubble_Step *));
 
   /* Copy them blindly into the array for sorting. */
   ind = 0;
   tmp = list;
   do {
-    step_pixmaps[ind++] = tmp;
+    st->step_pixmaps[ind++] = tmp;
     tmp = tmp->next;
   } while(tmp != (Bubble_Step *)NULL);
 
@@ -1110,51 +1121,51 @@ make_pixmap_array(list)
      bubble hangs around and doesn't pop immediately.  It's radius and area
      are found by extrapolating from the largest two bubbles with pixmaps. */
 
-  step_pixmaps[num_bubble_pixmaps] = 
+  st->step_pixmaps[st->num_bubble_pixmaps] = 
     (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
-  step_pixmaps[num_bubble_pixmaps]->radius = INT_MAX;
+  st->step_pixmaps[st->num_bubble_pixmaps]->radius = INT_MAX;
 
-  pixmap_sort(step_pixmaps, (num_bubble_pixmaps + 1));
+  pixmap_sort(st->step_pixmaps, (st->num_bubble_pixmaps + 1));
 
 #ifdef DEBUG
-  if (step_pixmaps[num_bubble_pixmaps]->radius != INT_MAX) {
+  if (st->step_pixmaps[st->num_bubble_pixmaps]->radius != INT_MAX) {
     fprintf(stderr, "pixmap_sort() screwed up make_pixmap_array\n");
   }
 #endif /* DEBUG */
 
-  step_pixmaps[num_bubble_pixmaps]->radius = 
-    extrapolate(step_pixmaps[num_bubble_pixmaps-2]->radius,
-               step_pixmaps[num_bubble_pixmaps-1]->radius);
-  step_pixmaps[num_bubble_pixmaps]->area = 
-    calc_bubble_area(step_pixmaps[num_bubble_pixmaps]->radius);
+  st->step_pixmaps[st->num_bubble_pixmaps]->radius = 
+    extrapolate(st->step_pixmaps[st->num_bubble_pixmaps-2]->radius,
+               st->step_pixmaps[st->num_bubble_pixmaps-1]->radius);
+  st->step_pixmaps[st->num_bubble_pixmaps]->area = 
+    calc_bubble_area(st, st->step_pixmaps[st->num_bubble_pixmaps]->radius);
   
 
 #ifdef DEBUG
   /* Now check for correct order */
-  for (ind = 0; ind < num_bubble_pixmaps; ind++) {
+  for (ind = 0; ind < st->num_bubble_pixmaps; ind++) {
     if (prevrad > 0) {
-      if (step_pixmaps[ind]->radius < prevrad) {
+      if (st->step_pixmaps[ind]->radius < prevrad) {
        fprintf(stderr, "Pixmaps not in ascending order of radius\n");
        exit(1);
       }
     }
-    prevrad = step_pixmaps[ind]->radius;
+    prevrad = st->step_pixmaps[ind]->radius;
   }
 #endif /* DEBUG */
+
+  /* Now populate the droppage values */
+  for (ind = 0; ind < st->num_bubble_pixmaps; ind++)
+         st->step_pixmaps[ind]->droppage = MAX_DROPPAGE * ind / st->num_bubble_pixmaps;
 }
 
-#ifndef NO_DEFAULT_BUBBLE
 static void
-make_pixmap_from_default(pixmap_data, bl)
-     char **pixmap_data;
-     Bubble_Step *bl;
+make_pixmap_from_default(struct state *st, char **pixmap_data, Bubble_Step *bl)
 /* Read pixmap data which has been compiled into the program and a pointer
  to which has been passed. 
 
  This is virtually copied verbatim from make_pixmap_from_file() above and
 changes made to either should be propagated onwards! */
 {
-  int result;
   XGCValues gcv;
 
 #ifdef DEBUG
@@ -1169,47 +1180,29 @@ changes made to either should be propagated onwards! */
     exit(1);
   }
 
-  bl->xpmattrs.closeness = 40000;
-  bl->xpmattrs.valuemask = XpmColormap | XpmCloseness;
-  bl->xpmattrs.colormap = defcmap;
-
-  /* This is the only line which is different from make_pixmap_from_file() */
-  result = XpmCreatePixmapFromData(defdsp, defwin, pixmap_data, &bl->ball, 
-                                  &bl->shape_mask, &bl->xpmattrs);
-
-  switch(result) {
-  case XpmColorError:
-    fprintf(stderr, "xpm: color substitution performed\n");
-    /* fall through */
-  case XpmSuccess:
-    bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
-    bl->area = calc_bubble_area(bl->radius);
-    break;
-  case XpmColorFailed:
-    fprintf(stderr, "xpm: color allocation failed\n");
-    exit(1);
-  case XpmNoMemory:
-    fprintf(stderr, "xpm: out of memory\n");
-    exit(1);
-  default:
-    fprintf(stderr, "xpm: unknown error code %d\n", result);
-    exit(1);
+#ifdef FANCY_BUBBLES
+  {
+    int w, h;
+    bl->ball = xpm_data_to_pixmap (st->dpy, st->window, (char **) pixmap_data,
+                                   &w, &h, &bl->shape_mask);
+    bl->radius = MAX(w, h) / 2;
+    bl->area = calc_bubble_area(st, bl->radius);
   }
-  
-  gcv.plane_mask = AllPlanes;
-  gcv.foreground = default_fg_pixel;
+#endif /* FANCY_BUBBLES */
+
+  gcv.foreground = st->default_fg_pixel;
   gcv.function = GXcopy;
-  bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
-  XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
+  bl->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
+  XSetClipMask(st->dpy, bl->draw_gc, bl->shape_mask);
   
-  gcv.foreground = default_bg_pixel;
+  gcv.foreground = st->default_bg_pixel;
   gcv.function = GXcopy;
-  bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
-  XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);  
+  bl->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
+  XSetClipMask(st->dpy, bl->erase_gc, bl->shape_mask);
 }
 
 static void 
-default_to_pixmaps(void)
+default_to_pixmaps (struct state *st)
 /* Make pixmaps out of default ball data stored in bubbles_default.c */
 {
   int i;
@@ -1217,26 +1210,12 @@ default_to_pixmaps(void)
   Bubble_Step *newpix, *tmppix;
   char **pixpt;
 
-  /* Make sure pixmaps are freed when program is terminated */
-  /* This is when I hit ^C */
-  if (signal(SIGINT, SIG_IGN) != SIG_IGN)
-    signal(SIGINT, onintr);
-  /* xscreensaver sends SIGTERM */
-  if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
-    signal(SIGTERM, onintr);
-#ifdef DEBUG
-  if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) {
-    printf("Setting signal handler for SIGSEGV\n");
-    signal(SIGSEGV, onsegv);
-  } else {
-    printf("Didn't set signal hanlder for SIGSEGV\n");
-  }
-#endif /* DEBUG */
+  init_default_bubbles();
 
   for (i = 0; i < num_default_bubbles; i++) {
     pixpt = default_bubbles[i];
     newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
-    make_pixmap_from_default(pixpt, newpix);
+    make_pixmap_from_default(st, pixpt, newpix);
     /* Now add to list */
     if (pixmap_list == (Bubble_Step *)NULL) {
       pixmap_list = newpix;
@@ -1250,422 +1229,11 @@ default_to_pixmaps(void)
   }
   
   /* Finally construct step_pixmaps[] */
-  make_pixmap_array(pixmap_list);
-}
-
-#endif /* NO_DEFAULT_BUBBLE */
-
-#endif /* HAVE_XPM */
-
-/* 
- * File I/O stuff
- */
-
-#ifdef BUBBLES_IO
-
-static DIR *
-my_opendir(name)
-     char *name;
-/* Like opendir() but checks for things so we don't have to do it multiple
-times in the code. */
-{
-  DIR *rv;
-
-  if (name == (char *)NULL) {
-    fprintf(stderr, "NULL directory name\n");
-    return (DIR *)NULL;
-  }
-  
-  if ((rv = opendir(name)) == NULL) {
-    perror(name);
-    return (DIR *)NULL;
-  }
-
-  return rv;
-}
-
-static int
-regular_file(name)
-     char *name;
-/* Check to see if we can use the named file.  This was broken under Linux
-1.3.45 but seems to be okay under 1.3.54.  The parameter "name" was being
-trashed if the file didn't exist.  Yeah, I know 1.3.x are development
-kernels....
-*/
-{
-  int fd;
-
-  if ((fd = open(name, O_RDONLY)) == -1) {
-    perror(name);
-    return 0;
-  } else {
-    close(fd);
-    return 1;
-  }
-}
-
-static char *
-get_random_name(dir)
-     char *dir;
-/* Pick an appropriate file at random out of the files in the directory dir */
-{
-  STRUCT_DIRENT *dp;
-  DIR *dfd;
-  int numentries = 0;
-  int entnum;
-  int x;
-  char buf[PATH_BUF_SIZE];
-  char *rv;
-
-  if ((dfd = my_opendir(dir)) == (DIR *)NULL)
-    return (char *)NULL;
-
-  while ((dp = readdir(dfd)) != NULL) {
-    if ((strcmp(DIRENT_NAME, ".") == 0) || (strcmp(DIRENT_NAME, "..") == 0))
-      continue;
-    if ((strlen(dir)+strlen(DIRENT_NAME)+2) > 1024) {
-      fprintf(stderr, "name %s/%s too long\n", dir, DIRENT_NAME);
-      continue;
-    }
-    if (sprintf(buf, "%s/%s", dir, DIRENT_NAME) > (PATH_BUF_SIZE-1)) {
-      fprintf(stderr, "path buffer overflowed in get_random_name()\n");
-      continue;
-    }
-    if (regular_file(buf))
-      ++numentries;
-  }
-  closedir(dfd);
-  if (numentries == 0) {
-    fprintf(stderr, "No suitable files found in %s\n", dir);
-    return (char *)NULL;
-  }
-  entnum = ya_random() % numentries;
-  x = 0;
-
-  if ((dfd = my_opendir(dir)) == (DIR *)NULL)
-    return (char *)NULL;
-  while ((dp = readdir(dfd)) != NULL) {
-    if ((strcmp(DIRENT_NAME, ".") == 0) || (strcmp(DIRENT_NAME, "..") == 0))
-      continue;
-    if ((strlen(dir)+strlen(DIRENT_NAME)+2) > 1024) {
-      /* We warned about this previously */
-      continue;
-    }
-    if (sprintf(buf, "%s/%s", dir, DIRENT_NAME) > (PATH_BUF_SIZE-1)) {
-      fprintf(stderr, "path buffer overflowed in get_random_name()\n");
-      continue;
-    }
-    if (regular_file(buf)) {
-      if (x == entnum) {
-       rv = (char *)xmalloc(1024 * sizeof(char));
-       strcpy(rv, buf);
-       closedir(dfd);
-       return rv;
-      }
-      ++x;
-    }
-  }
-  /* We've screwed up if we reach here - someone must have deleted all the
-     files while we were counting them... */
-  fprintf(stderr, "get_random_name(): Oops!\n");
-  exit(1);
-}
-
-static int
-read_line(fd, buf, bufsize)
-     int fd;
-     char **buf;
-     int bufsize;
-/* A line is read from fd until a '\n' is found or EOF is reached.  (*buf)
-is initially of length bufsize and is extended by bufsize chars if need
-be (for as many times as it takes). */
-{
-  char x;
-  int pos = 0;
-  int size = bufsize;
-  int rv;
-  char *newbuf;
-
-  while (1) {
-    rv = read(fd, &x, 1);
-    if (rv == -1) {
-      perror("read_line(): ");
-      return IO_ERROR;
-    } else if (rv == 0) {
-      (*buf)[pos] = '\0';
-      return EOF_REACHED;
-    } else if (x == '\n') {
-      (*buf)[pos] = '\0';
-      return LINE_READ;
-    } else {
-      (*buf)[pos++] = x;
-      if (pos == (size - 1)) {
-       /* We've come to the end of the space */
-       newbuf = (char *)xmalloc((size+bufsize) * sizeof(char));
-       strncpy(newbuf, *buf, (size - 1));
-       free(*buf);
-       *buf = newbuf;
-       size += bufsize;
-      }
-    }
-  }
-}
-
-static int
-create_temp_file(name)
-     char **name;
-/* Create a temporary file in /tmp and return a filedescriptor to it */
-{
-  int rv;
-  if (*name != (char *)NULL)
-    free(*name);
-
-  if ((*name = tempnam("/tmp", "abxdfes")) == (char *)NULL) {
-    fprintf(stderr, "Couldn't make new temporary file\n");
-    exit(1);
-  }
-/*   printf("Temp file created : %s\n", *name); */
-  if ((rv = creat(*name, 0644)) == -1) {
-    fprintf(stderr, "Couldn't open temporary file\n");
-    exit(1);
-  }
-  
-  return rv;
-}
-
-
-#ifdef BUBBLES_IO
-static void 
-make_pixmap_from_file(fname, bl)
-     char *fname;
-     Bubble_Step *bl;
-/* Read the pixmap in file fname into structure bl which must already
- be allocated. */
-{
-  int result;
-  XGCValues gcv;
-
-  if (bl == (Bubble_Step *)NULL) {
-    fprintf(stderr, "NULL pointer passed to make_pixmap()\n");
-    exit(1);
-  }
-
-  bl->xpmattrs.closeness = 40000;
-  bl->xpmattrs.valuemask = XpmColormap | XpmCloseness;
-  bl->xpmattrs.colormap = defcmap;
-
-  result = XpmReadFileToPixmap(defdsp, defwin, fname, &bl->ball, 
-                              &bl->shape_mask, &bl->xpmattrs);
-
-  switch(result) {
-  case XpmColorError:
-    fprintf(stderr, "xpm: color substitution performed\n");
-    /* fall through */
-  case XpmSuccess:
-    bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
-    bl->area = calc_bubble_area(bl->radius);
-    break;
-  case XpmColorFailed:
-    fprintf(stderr, "xpm: color allocation failed\n");
-    exit(1);
-  case XpmNoMemory:
-    fprintf(stderr, "xpm: out of memory\n");
-    exit(1);
-  default:
-    fprintf(stderr, "xpm: unknown error code %d\n", result);
-    exit(1);
-  }
-  
-  gcv.plane_mask = AllPlanes;
-  gcv.foreground = default_fg_pixel;
-  gcv.function = GXcopy;
-  bl->draw_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
-  XSetClipMask(defdsp, bl->draw_gc, bl->shape_mask);
-  
-  gcv.foreground = default_bg_pixel;
-  gcv.function = GXcopy;
-  bl->erase_gc = XCreateGC (defdsp, defwin, GCForeground, &gcv);
-  XSetClipMask(defdsp, bl->erase_gc, bl->shape_mask);  
-}
-#endif /* BUBBLES_IO */
-
-static void 
-read_file_to_pixmaps(fname)
-     char *fname;
-/* Read the pixmaps contained in the file fname into memory.  THESE SHOULD
-BE UNCOMPRESSED AND READY TO GO! */
-{
-  int fd, tmpfd=0, rv;
-  int inxpm = 0;
-  int xpmseen = 0;
-  char *buf = (char *)NULL;
-  char *tmpname = (char *)NULL;
-  Bubble_Step *pixmap_list = (Bubble_Step *)NULL;
-  Bubble_Step *newpix, *tmppix;
-
-  /* We first create a linked list of pixmaps before allocating
-     memory for the array */
-
-  if ((fd = open(fname, O_RDONLY)) == -1) {
-    fprintf(stderr, "Couldn't open %s\n", fname);
-    exit(1);
-  }
-
-  /* Make sure pixmaps are freed when program is terminated */
-  /* This is when I hit ^C */
-  if (signal(SIGINT, SIG_IGN) != SIG_IGN)
-    signal(SIGINT, onintr);
-  /* xscreensaver sends SIGTERM */
-  if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
-    signal(SIGTERM, onintr);
-#ifdef DEBUG
-  if (signal(SIGSEGV, SIGN_IGN) != SIG_IGN)
-    signal(SIGSEGV, onsegv);
-#endif /* DEBUG */
-  
-  while (1) {
-    if (inxpm == 2)
-      break;
-
-    buf = (char *)malloc(READ_LINE_BUF_SIZE * sizeof(char));
-
-    switch ((rv = read_line(fd, &buf, READ_LINE_BUF_SIZE))) {
-    case IO_ERROR:
-      fprintf(stderr, "An I/O error occurred\n");
-      exit(1);
-    case EOF_REACHED:
-      if (inxpm) {
-       fprintf(stderr, "EOF occurred inside an XPM block\n");
-       exit(1);
-      } else
-       inxpm = 2;
-      break;
-    case LINE_READ:
-      if (inxpm) {
-       if (strncmp("};", buf, 2) == 0) {
-         inxpm = 0;
-         write(tmpfd, buf, strlen(buf));
-         write(tmpfd, "\n", 1);
-         close(tmpfd);
-         /* Now process the tmpfile */
-         newpix = (Bubble_Step *)xmalloc(sizeof(Bubble_Step));
-         make_pixmap_from_file(tmpname, newpix);
-         /* Now add to list */
-         if (pixmap_list == (Bubble_Step *)NULL) {
-           pixmap_list = newpix;
-         } else {
-           tmppix = pixmap_list;
-           while (tmppix->next != (Bubble_Step *)NULL)
-             tmppix = tmppix->next;
-           tmppix->next = newpix;
-         }
-         newpix->next = (Bubble_Step *)NULL;
-         unlink(tmpname);
-       } else {
-         write(tmpfd, buf, strlen(buf));
-         write(tmpfd, "\n", 1);
-       }
-      } else {
-       if (strncmp("/* XPM */", buf, 9) == 0) {
-         tmpfd = create_temp_file(&tmpname);
-/* This proves XPM's performance is kinda pathetic */
-#ifdef DEBUG
-         printf("New XPM detected : %s, fd=%d\n", tmpname, tmpfd); 
-#endif /* DEBUG */
-         inxpm = 1;
-         xpmseen = 1;
-       }
-       write(tmpfd, buf, strlen(buf));
-       write(tmpfd, "\n", 1);
-      }
-      break;
-    default:
-      fprintf(stderr, "read_line returned unknown code %d\n", rv);
-      exit(1);
-    }
-
-    free(buf);
-  }
-
-  close(fd);
-  if (buf != (char *)NULL)
-    free(buf);
-  if (tmpname != (char *)NULL)
-    free(tmpname);
-
-  if (! xpmseen) {
-    fprintf(stderr, "There was no XPM data in the file %s\n", fname);
-    exit(1);
-  }
-
-  /* Finally construct step_pixmaps[] */
-  make_pixmap_array(pixmap_list);
-}
-
-static void 
-shell_exec(command)
-     char *command;
-/* Forks a shell to execute "command" then waits for command to finish */
-{
-  int pid, status, wval;
-
-  switch(pid=fork()) {
-  case 0:
-    if (execlp(BOURNESH, BOURNESH, "-c", command, (char *)NULL) == -1) {
-      fprintf(stderr, "Couldn't exec shell %s\n", BOURNESH);
-      exit(1);
-    }
-    /* fall through if execlp() fails */
-  case -1:
-    /* Couldn't fork */
-    perror(progname);
-    exit(1);
-  default:
-    while ((wval = wait(&status)) != pid)
-      if (wval == -1) {
-       perror(progname);
-       exit(1);
-      }    
-  }
+  make_pixmap_array(st, pixmap_list);
 }
 
-static void 
-uncompress_file(current, namebuf)
-     char *current;
-     char *namebuf;
-/* If the file current is compressed (i.e. its name ends in .gz or .Z,
-no check is made to see if it is actually a compressed file...) then a
-new temporary file is created for it and it is decompressed into there,
-returning the name of the file to namebuf, else current is returned in
-namebuf */
-{
-  int fd;
-  char *tname = (char *)NULL;
-  char argbuf[COMMAND_BUF_SIZE];
-
-  if (((strlen(current) >=4) && 
-       (strncmp(&current[strlen(current)-3], ".gz", 3) == 0)) || 
-      ((strlen(current) >=3) && 
-       (strncmp(&current[strlen(current)-2], ".Z", 2) == 0))) {
-    fd = create_temp_file(&tname);
-    /* close immediately but don't unlink so we should have a zero length
-       file in /tmp which we can append to */
-    close(fd);
-    if (sprintf(argbuf, "%s -dc %s > %s", GZIP, current, tname) > 
-       (COMMAND_BUF_SIZE-1)) {
-      fprintf(stderr, "command buffer overflowed in uncompress_file()\n");
-      exit(1);
-    }
-    shell_exec(argbuf);
-    strcpy(namebuf, tname);
-  } else {
-    strcpy(namebuf, current);
-  }
-  return;
-}
+#endif /* FANCY_BUBBLES */
 
-#endif /* BUBBLES_IO */
 
 /* 
  * Main stuff 
@@ -1673,230 +1241,193 @@ namebuf */
 
 
 static void 
-get_resources(dpy)
-     Display *dpy;
+get_resources(struct state *st)
 /* Get the appropriate X resources and warn about any inconsistencies. */
 {
-  Bool nodelay;
-#ifdef BUBBLES_IO
-#ifdef HAVE_XPM
-  char *dirname;
-#else
-  char *foo, *bar;
-#endif /* HAVE_XPM */
-#endif /* BUBBLES_IO */
-
-  threed = get_boolean_resource("3D", "Boolean");
-  quiet = get_boolean_resource("quiet", "Boolean");
-  simple = get_boolean_resource("simple", "Boolean");
+  Bool rise;
+  XWindowAttributes xgwa;
+  Colormap cmap;
+  char *s;
+  XGetWindowAttributes (st->dpy, st->window, &xgwa);
+  cmap = xgwa.colormap;
+
+  st->threed = get_boolean_resource(st->dpy, "3D", "Boolean");
+  st->quiet = get_boolean_resource(st->dpy, "quiet", "Boolean");
+  st->simple = get_boolean_resource(st->dpy, "simple", "Boolean");
   /* Forbid rendered bubbles on monochrome displays */
-  if ((mono_p) && (! simple)) {
-    if (! quiet)
-      fprintf(stderr, "Rendered bubbles not supported on monochrome \
-displays\n");
-    simple = True;
-  }
-  delay = get_integer_resource("delay", "Integer");
-  nodelay = get_boolean_resource("nodelay", "Boolean");
-  if (nodelay)
-    delay = 0;
-  if (delay < 0)
-    delay = 0;
-
-  default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy,
-                                        DefaultColormap(dpy, 
-                                                        DefaultScreen(dpy)));
-  default_bg_pixel = get_pixel_resource ("background", "Background", dpy,
-                                        DefaultColormap(dpy, 
-                                                        DefaultScreen(dpy)));
-
-  if (simple) {
+  if ((mono_p) && (! st->simple)) {
+    if (! st->quiet)
+      fprintf(stderr,
+             "Rendered bubbles not supported on monochrome displays\n");
+    st->simple = True;
+  }
+  st->delay = get_integer_resource(st->dpy, "delay", "Integer");
+
+  s = get_string_resource (st->dpy, "mode", "Mode");
+  rise = False;
+  if (!s || !*s || !strcasecmp (s, "float"))
+    ;
+  else if (!strcasecmp (s, "rise"))
+    rise = True;
+  else if (!strcasecmp (s, "drop"))
+    st->drop = True;
+  else
+    fprintf (stderr, "%s: bogus mode: \"%s\"\n", progname, s);
+
+  st->trails = get_boolean_resource(st->dpy, "trails", "Boolean");
+  st->drop_dir = (st->drop ? 1 : -1);
+  if (st->drop || rise)
+       st->drop = 1;
+
+  st->default_fg_pixel = get_pixel_resource (st->dpy,
+                                        cmap, "foreground", "Foreground");
+  st->default_bg_pixel = get_pixel_resource (st->dpy,
+                                        cmap, "background", "Background");
+
+  if (st->simple) {
     /* This is easy */
-    broken = get_boolean_resource("broken", "Boolean");
-    if (broken)
-      if (! quiet)
+    st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
+    if (st->broken)
+      if (! st->quiet)
        fprintf(stderr, "-broken not available in simple mode\n");
   } else {
-#ifndef HAVE_XPM
-    simple = 1;
-#else
-    broken = get_boolean_resource("broken", "Boolean");
-#ifdef BUBBLES_IO
-    pixmap_file = get_string_resource("file", "File");
-    dirname = get_string_resource("directory", "Directory");    
-#ifdef NO_DEFAULT_BUBBLE
-    /* Must specify -file or -directory if no default bubble compiled in */
-    if (strcmp(pixmap_file, "(default)") != 0) {
-    } else if (strcmp(dirname, "(default)") != 0) {
-      if ((pixmap_file = get_random_name(dirname)) == (char *)NULL) {
-       /* Die if we can't open directory - make it consistent with -file
-          when it fails, rather than falling back to default. */
-       exit(1);
-      }
-    } else {
-      fprintf(stderr, "No default bubble compiled in - use -file or \
--directory\n");
-      exit(1);
-    }
-#else
-    if (strcmp(pixmap_file, "(default)") != 0) {
-    } else if (strcmp(dirname, "(default)") != 0) {
-      if ((pixmap_file = get_random_name(dirname)) == (char *)NULL) {
-       exit(1);
-      }
-    } else {
-      /* Use default bubble */
-      use_default_bubble = 1;
-    }
-#endif /* NO_DEFAULT_BUBBLE */
-#else 
-    use_default_bubble = 1;
-#endif /* BUBBLES_IO */
-#endif /* HAVE_XPM */
+#ifndef FANCY_BUBBLES
+    st->simple = 1;
+#else  /* FANCY_BUBBLES */
+    st->broken = get_boolean_resource(st->dpy, "broken", "Boolean");
+#endif /* FANCY_BUBBLES */
   }
 }
 
-static void
-init_bubbles (dpy, window)
-     Display *dpy;
-     Window window;
+static void *
+bubbles_init (Display *dpy, Window window)
 {
+  struct state *st = (struct state *) calloc (1, sizeof(*st));
   XGCValues gcv;
   XWindowAttributes xgwa;
   int i;
-#ifdef BUBBLES_IO
-  char uncompressed[1024];
-#endif /* BUBBLES_IO */
-
-  defdsp = dpy;
-  defwin = window;
 
-  ya_rand_init(0);
+  st->dpy = dpy;
+  st->window = window;
 
-  get_resources(dpy);
+  get_resources(st);
 
-  XGetWindowAttributes (dpy, window, &xgwa);
+  XGetWindowAttributes (st->dpy, st->window, &xgwa);
 
 #ifdef DEBUG
   printf("sizof(int) on this platform is %d\n", sizeof(int));
   printf("sizof(long) on this platform is %d\n", sizeof(long));
 #endif /* DEBUG */
 
-  screen_width = xgwa.width;
-  screen_height = xgwa.height;
-  screen_depth = xgwa.depth;
-  defcmap = xgwa.colormap;
+  st->screen_width = xgwa.width;
+  st->screen_height = xgwa.height;
+  st->screen_depth = xgwa.depth;
 
-  if (simple) {
+  if (st->simple) {
     /* These are pretty much plucked out of the air */
-    bubble_min_radius = (int)(0.006*(double)(MIN(screen_width, 
-                                                screen_height)));
-    bubble_max_radius = (int)(0.045*(double)(MIN(screen_width,
-                                                screen_height)));
+    st->bubble_min_radius = (int)(0.006*(double)(MIN(st->screen_width, 
+                                                st->screen_height)));
+    st->bubble_max_radius = (int)(0.045*(double)(MIN(st->screen_width,
+                                                st->screen_height)));
     /* Some trivial values */
-    if (bubble_min_radius < 1)
-      bubble_min_radius = 1;
-    if (bubble_max_radius <= bubble_min_radius)
-      bubble_max_radius = bubble_min_radius + 1;
+    if (st->bubble_min_radius < 1)
+      st->bubble_min_radius = 1;
+    if (st->bubble_max_radius <= st->bubble_min_radius)
+      st->bubble_max_radius = st->bubble_min_radius + 1;
 
-    mesh_length = (2 * bubble_max_radius) + 3;
+    st->mesh_length = (2 * st->bubble_max_radius) + 3;
 
     /* store area of each bubble of certain radius as number of 1/10s of
        a pixel area.  PI is defined in <math.h> */
-    bubble_areas = (long *)xmalloc((bubble_max_radius + 2) * sizeof(int));
-    for (i = 0; i < bubble_min_radius; i++)
-      bubble_areas[i] = 0;
-    for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
-      bubble_areas[i] = calc_bubble_area(i);
-
-    mesh_length = (2 * bubble_max_radius) + 3;
+    st->bubble_areas = (long *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
+    for (i = 0; i < st->bubble_min_radius; i++)
+      st->bubble_areas[i] = 0;
+    for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
+      st->bubble_areas[i] = calc_bubble_area(st, i);
+
+       /* Now populate the droppage values */
+    st->bubble_droppages = (int *)xmalloc((st->bubble_max_radius + 2) * sizeof(int));
+    for (i = 0; i < st->bubble_min_radius; i++)
+      st->bubble_droppages[i] = 0;
+    for (i = st->bubble_min_radius; i <= (st->bubble_max_radius+1); i++)
+      st->bubble_droppages[i] = MAX_DROPPAGE * (i - st->bubble_min_radius) / (st->bubble_max_radius - st->bubble_min_radius);
+
+    st->mesh_length = (2 * st->bubble_max_radius) + 3;
   } else {
-#ifndef HAVE_XPM
-    fprintf(stderr, "Bug: simple mode code not set but HAVE_XPM not \
-defined\n");
+#ifndef FANCY_BUBBLES
+    fprintf(stderr,
+           "Bug: simple mode code not set but FANCY_BUBBLES not defined\n");
     exit(1);
-#else
+#else  /* FANCY_BUBBLES */
     /* Make sure all #ifdef sort of things have been taken care of in
        get_resources(). */
-    if (use_default_bubble) {
-#ifdef NO_DEFAULT_BUBBLE
-      fprintf(stderr, "Bug: use_default_bubble and NO_DEFAULT_BUBBLE both \
-defined\n");
-      exit(1);
-#else
-      default_to_pixmaps();
-#endif /* NO_DEFAULT_BUBBLE */
+    default_to_pixmaps(st);
 
-      /* Set mesh length */
-      mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
-    } else {
-#ifdef BUBBLES_IO
-      if (! regular_file(pixmap_file)) {
-       /* perror() in regular_file printed error message */
-       exit(1);
-      }
-      uncompress_file(pixmap_file, uncompressed);
-      read_file_to_pixmaps(uncompressed);
-      if (strcmp(pixmap_file, uncompressed))
-       unlink(uncompressed);
-
-      mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
-#else
-      fprintf(stderr, "Bug: use_default_bubble is not defined yet I/O is not \
-compiled in\n");
-      exit(1);
-#endif /* BUBBLES_IO */
-    }
-#endif /* HAVE_XPM */
+    /* Set mesh length */
+    st->mesh_length = (2 * st->step_pixmaps[st->num_bubble_pixmaps-1]->radius) + 3;
+#endif /* FANCY_BUBBLES */
 
     /* Am I missing something in here??? */
   }
 
-  mesh_width = (screen_width / mesh_length) + 1;
-  mesh_height = (screen_height / mesh_length) + 1;
-  mesh_cells = mesh_width * mesh_height;
-  init_mesh();
+  st->mesh_width = (st->screen_width / st->mesh_length) + 1;
+  st->mesh_height = (st->screen_height / st->mesh_length) + 1;
+  st->mesh_cells = st->mesh_width * st->mesh_height;
+  init_mesh(st);
 
-  calculate_adjacent_list();
+  calculate_adjacent_list(st);
 
-  adjust_areas();
+  adjust_areas(st);
 
   /* Graphics contexts for simple mode */
-  if (simple) {
-    gcv.foreground = default_fg_pixel;
-    draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
-    gcv.foreground = default_bg_pixel;
-    erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
+  if (st->simple) {
+    gcv.foreground = st->default_fg_pixel;
+    st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
+    gcv.foreground = st->default_bg_pixel;
+    st->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
   }
 
-  XClearWindow (dpy, window);
+  XClearWindow (st->dpy, st->window);
+
+# ifndef FANCY_BUBBLES
+  st->simple = True;
+# endif
+
+  return st;
 }
 
-static void 
-bubbles (dpy, window)
-     Display *dpy;
-     Window window;
+static unsigned long
+bubbles_draw (Display *dpy, Window window, void *closure)
 {
-  Bubble *tmp;
+  struct state *st = (struct state *) closure;
+  int i;
+  for (i = 0; i < 5; i++)
+    {
+      Bubble *tmp = new_bubble(st);
+      add_to_mesh(st, tmp);
+      insert_new_bubble(st, tmp);
+    }
+  return st->delay;
+}
 
-  tmp = new_bubble();
-  add_to_mesh(tmp);
-  insert_new_bubble(tmp);
 
-  XSync (dpy, True);
+static void
+bubbles_reshape (Display *dpy, Window window, void *closure, 
+                 unsigned int w, unsigned int h)
+{
 }
 
+static Bool
+bubbles_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+  return False;
+}
 
-void 
-screenhack (dpy, window)
-     Display *dpy;
-     Window window;
+static void
+bubbles_free (Display *dpy, Window window, void *closure)
 {
-  init_bubbles (dpy, window);
-  while (1) {
-    bubbles (dpy, window);
-    if (delay)
-      usleep(delay);
-  }
+  struct state *st = (struct state *) closure;
+  free (st);
 }
 
+XSCREENSAVER_MODULE ("Bubbles", bubbles)