http://www.jwz.org/xscreensaver/xscreensaver-5.07.tar.gz
[xscreensaver] / hacks / bubbles.c
index db87ffe492d04444ba07447a72d802d253538bcc..54a0457432c189cbd44392c447708c26ee263e4c 100644 (file)
@@ -1,19 +1,17 @@
 /* bubbles.c - frying pan / soft drink in a glass simulation */
 
-/*$Id: bubbles.c,v 1.10 1997/12/03 10:56:13 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 <math.h>
-#include "screenhack.h"
-#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 <math.h>
 #include <limits.h>
 
-#ifdef SIGNAL_NONSENSE         /* what's this crap doing in here? */
-#include <signal.h>
-#endif /* SIGNAL_NONSENSE */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
 #ifndef VMS
 # include <sys/wait.h>
 #else /* VMS */
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
+
+#include "screenhack.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:      800",
-#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 },
+  { "-mode",            ".mode",        XrmoptionSepArg, 0 },
+  { "-drop",            ".mode",        XrmoptionNoArg, "drop" },
+  { "-rise",            ".mode",        XrmoptionNoArg, "rise" },
+  { "-trails",          ".trails",      XrmoptionNoArg, "true" },
   { 0, 0, 0, 0 }
 };
 
@@ -126,66 +104,59 @@ XrmOptionDescRec options [] = {
  * 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;
-static Visual *defvisual;
-
-/* 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(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);
@@ -224,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);
   }
@@ -232,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);
       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);
       die_bad_bubble(bb);  
     }
-#endif /* HAVE_XPM */
+#endif
   }
 #endif /* DEBUG */
   return 0;
@@ -296,18 +267,18 @@ add_bubble_to_list(Bubble **list, Bubble *bb)
 
 
 static void 
-init_mesh (void)
+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(int x, int y)
+cell_to_mesh(struct state *st, int x, int y)
 /* convert cell coordinates to mesh index */
 {
 #ifdef DEBUG
@@ -316,31 +287,31 @@ cell_to_mesh(int x, int y)
     exit(1);
   }
 #endif
-  return ((mesh_width * y) + x);
+  return ((st->mesh_width * y) + x);
 }
 
 static void 
-mesh_to_cell(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(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(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
@@ -358,7 +329,7 @@ print_adjacents(int *adj)
 #endif /* DEBUG */
 
 static void 
-add_to_mesh(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. */
@@ -370,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 (void)
+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 (void)
+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 (void)
+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;
@@ -423,31 +394,31 @@ total_bubbles (void)
 #endif /* DEBUG */
 
 static void 
-calculate_adjacent_list (void)
+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 (void)
+adjust_areas (struct state *st)
 /* Adjust areas of bubbles so we don't get overflow in weighted_mean() */
 {
   double maxvalue;
@@ -455,46 +426,46 @@ adjust_areas (void)
   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);
@@ -509,7 +480,7 @@ adjust_areas (void)
  */
 
 static Bubble *
-new_bubble (void)
+new_bubble (struct state *st)
 /* Add a new bubble at some random position on the screen of the smallest
 size. */
 {
@@ -521,27 +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(Bubble *bb)
+show_bubble(struct state *st, Bubble *bb)
 /* paint the bubble on the screen */
 {
   if (null_bubble(bb)) {
@@ -552,29 +523,29 @@ show_bubble(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(Bubble *bb)
+hide_bubble(struct state *st, Bubble *bb)
 /* erase the bubble */
 {
   if (null_bubble(bb)) {
@@ -585,29 +556,29 @@ hide_bubble(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(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,
@@ -620,15 +591,15 @@ delete_bubble_in_mesh(Bubble *bb, int 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);
@@ -642,7 +613,7 @@ ulongsqrint(int x)
 }
 
 static Bubble *
-get_closest_bubble(Bubble *bb)
+get_closest_bubble(struct state *st, Bubble *bb)
 /* Find the closest bubble touching the this bubble, NULL if none are
    touching. */
 {
@@ -664,14 +635,14 @@ get_closest_bubble(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;
@@ -695,7 +666,7 @@ get_closest_bubble(Bubble *bb)
 
 #ifdef DEBUG
 static void
-ldr_barf (void)
+ldr_barf (struct state *st)
 {
 }
 #endif /* DEBUG */
@@ -746,7 +717,7 @@ weighted_mean(int n1, int n2, long w1, long w2)
 }
 
 static int
-bubble_eat(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;
@@ -766,60 +737,72 @@ bubble_eat(Bubble *diner, Bubble *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(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 */
 {
@@ -836,13 +819,13 @@ merge_bubbles(Bubble *b1, Bubble *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;
@@ -853,7 +836,7 @@ merge_bubbles(Bubble *b1, Bubble *b2)
       break;
     }
   } else if (b1size < b2size) {
-    switch (bubble_eat(b2, b1)) {
+    switch (bubble_eat(st, b2, b1)) {
     case 0:
       return 0;
       break;
@@ -864,8 +847,8 @@ merge_bubbles(Bubble *b1, Bubble *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;
@@ -876,7 +859,7 @@ merge_bubbles(Bubble *b1, Bubble *b2)
        break;
       }
     } else {
-      switch (bubble_eat(b2, b1)) {
+      switch (bubble_eat(st, b2, b1)) {
       case 0:
        return 0;
        break;
@@ -893,7 +876,7 @@ merge_bubbles(Bubble *b1, Bubble *b2)
 }
 
 static void 
-insert_new_bubble(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. */
@@ -909,34 +892,134 @@ insert_new_bubble(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(Bubble *bb)
@@ -958,54 +1041,7 @@ get_length_of_bubble_list(Bubble *bb)
  * still check for XPM, though!
  */
 
-#ifdef HAVE_XPM
-
-static void 
-free_pixmaps (void)
-/* 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);
-  }
-}
-
-#ifdef SIGNAL_NONSENSE
-static void 
-onintr(int a)
-/* This gets called when SIGINT or SIGTERM is received */
-{
-  free_pixmaps();
-  exit(0);
-}
-
-#ifdef DEBUG
-static void
-onsegv(int a)
-/* Called when SEGV detected.   Hmmmmm.... */
-{
-  fflush(stdout);
-  fprintf(stderr, "SEGV detected! : %d\n", a);
-  exit(1);
-}
-#endif /* DEBUG */
-#endif /* SIGNAL_NONSENSE */
-
+#ifdef FANCY_BUBBLES
 
 /*
  * Pixmaps without file I/O (but do have XPM)
@@ -1045,7 +1081,7 @@ extrapolate(int i1, int i2)
 }
 
 static void 
-make_pixmap_array(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;
@@ -1059,25 +1095,25 @@ make_pixmap_array(Bubble_Step *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);
 
@@ -1085,49 +1121,51 @@ make_pixmap_array(Bubble_Step *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(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
@@ -1142,63 +1180,29 @@ changes made to either should be propagated onwards! */
     exit(1);
   }
 
-  bl->xpmattrs.valuemask = 0;
-
-#ifdef XpmCloseness
-  bl->xpmattrs.valuemask |= XpmCloseness;
-  bl->xpmattrs.closeness = 40000;
-#endif
-#ifdef XpmVisual
-  bl->xpmattrs.valuemask |= XpmVisual;
-  bl->xpmattrs.visual = defvisual;
-#endif
-#ifdef XpmDepth
-  bl->xpmattrs.valuemask |= XpmDepth;
-  bl->xpmattrs.depth = screen_depth;
-#endif
-#ifdef XpmColormap
-  bl->xpmattrs.valuemask |= XpmColormap;
-  bl->xpmattrs.colormap = defcmap;
-#endif
-
-
-  /* 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;
@@ -1206,28 +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 */
-#ifdef SIGNAL_NONSENSE
-  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 */
-#endif /* SIGNAL_NONSENSE */
+  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;
@@ -1241,411 +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(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(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(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(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(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(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(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);
-  }
-
-#ifdef SIGNAL_NONSENSE
-  /* 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 */
-#endif /* SIGNAL_NONSENSE */
-  
-  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);
+  make_pixmap_array(st, pixmap_list);
 }
 
-static void 
-shell_exec(char *command)
-/* Forks a shell to execute "command" then waits for command to finish */
-{
-  int pid, status, wval;
+#endif /* FANCY_BUBBLES */
 
-  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);
-      }    
-  }
-}
-
-static void 
-uncompress_file(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 /* BUBBLES_IO */
 
 /* 
  * Main stuff 
@@ -1653,227 +1241,193 @@ namebuf */
 
 
 static void 
-get_resources(Display *dpy, Window window)
+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 */
-
+  Bool rise;
   XWindowAttributes xgwa;
   Colormap cmap;
-  XGetWindowAttributes (dpy, window, &xgwa);
+  char *s;
+  XGetWindowAttributes (st->dpy, st->window, &xgwa);
   cmap = xgwa.colormap;
 
-  threed = get_boolean_resource("3D", "Boolean");
-  quiet = get_boolean_resource("quiet", "Boolean");
-  simple = get_boolean_resource("simple", "Boolean");
+  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)
+  if ((mono_p) && (! st->simple)) {
+    if (! st->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,
-                                        cmap);
-  default_bg_pixel = get_pixel_resource ("background", "Background", dpy,
-                                        cmap);
-
-  if (simple) {
+    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 (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, window);
+  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;
-  defvisual = xgwa.visual;
+  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
+#ifndef FANCY_BUBBLES
     fprintf(stderr,
-           "Bug: simple mode code not set but HAVE_XPM not defined\n");
+           "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 (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 (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)