http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.01.tar.gz
[xscreensaver] / hacks / bubbles.c
index 99e64b10d1b3cbb2e34f4b9568242d1fc96ac26a..fe5ccef0585116a546358805185b045eb3529461 100644 (file)
@@ -1,6 +1,6 @@
 /* bubbles.c - frying pan / soft drink in a glass simulation */
 
-/*$Id: bubbles.c,v 1.13 1998/02/21 21:55:14 jwz Exp $*/
+/*$Id: bubbles.c,v 1.18 2002/01/17 02:16:04 jwz Exp $*/
 
 /*
  *  Copyright (C) 1995-1996 James Macnicol
  * 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"
 
 #include <limits.h>
 
 # include <unistd.h>
 #endif
 #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
 
 /* 
 extern void init_default_bubbles(void);
 extern int num_default_bubbles;
 extern char **default_bubbles[];
+static int drop_bubble( Bubble *bb );
 
 char *progclass = "Bubbles";
 
 char *defaults [] = {
-  "Bubbles.background: black",
-  "*foreground:                white",
+  ".background:                black",
+  ".foreground:                white",
   "*simple:            false",
   "*broken:            false",
   "*delay:             800",
   "*quiet:             false", 
   "*nodelay:           false",
+  "*drop:              false",
+  "*trails:            false",
   "*3D:                        false",
   0
 };
 
 XrmOptionDescRec 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" },
   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
+  { "-drop",            ".drop",        XrmoptionNoArg, "true" },
+  { "-rise",            ".rise",        XrmoptionNoArg, "true" },
+  { "-trails",          ".trails",      XrmoptionNoArg, "true" },
   { 0, 0, 0, 0 }
 };
 
@@ -128,15 +135,16 @@ static Visual *defvisual;
 static int bubble_min_radius;
 static int bubble_max_radius;
 static long *bubble_areas;
+static int *bubble_droppages;
 static GC draw_gc, erase_gc;
 
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
 static int num_bubble_pixmaps;
 static Bubble_Step **step_pixmaps;
-#endif /* HAVE_XPM */
+#endif
 
 /* Options stuff */
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
 static Bool simple = False;
 #else
 static Bool simple = True;
@@ -144,6 +152,9 @@ static Bool simple = True;
 static Bool broken = False;
 static Bool quiet = False;
 static Bool threed = False;
+static Bool drop = False;
+static Bool trails = False;
+static int drop_dir;
 static int delay;
 
 /* 
@@ -215,7 +226,7 @@ turned off if DEBUG isn't set. */
              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) ||
@@ -226,7 +237,7 @@ turned off if DEBUG isn't set. */
              bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
       die_bad_bubble(bb);  
     }
-#endif /* HAVE_XPM */
+#endif
   }
 #endif /* DEBUG */
   return 0;
@@ -428,20 +439,20 @@ adjust_areas (void)
   long factor;
   int i;
 
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
   if (simple)
     maxarea = bubble_areas[bubble_max_radius+1];
   else
     maxarea = step_pixmaps[num_bubble_pixmaps]->area;
-#else
+#else /* !FANCY_BUBBLES */
   maxarea = bubble_areas[bubble_max_radius+1];
-#endif /* HAVE_XPM */
+#endif /* !FANCY_BUBBLES */
   maxvalue = (double)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
+#ifdef FANCY_BUBBLES
     if (simple) {
       for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
        bubble_areas[i] /= factor;
@@ -461,13 +472,13 @@ adjust_areas (void)
 #endif /* DEBUG */
       }
     }
-#else
+#else /* !FANCY_BUBBLES */
     for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
       bubble_areas[i] /= factor;
       if (bubble_areas[i] == 0)
        bubble_areas[i] = 1;
     }
-#endif /* HAVE_XPM */
+#endif /* !FANCY_BUBBLES */
   }
 #ifdef DEBUG
   printf("maxarea = %ld\n", maxarea);
@@ -497,17 +508,17 @@ size. */
   if (simple) {
     rv->radius = bubble_min_radius;
     rv->area = bubble_areas[bubble_min_radius];
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
   } else {
     rv->step = 0;
     rv->radius = step_pixmaps[0]->radius;
     rv->area = step_pixmaps[0]->area;
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
   }
   rv->visible = 0;
   rv->magic = BUBBLE_MAGIC;
-  rv->x = ya_random() % screen_width;
-  rv->y = ya_random() % screen_height;
+  rv->x = random() % screen_width;
+  rv->y = random() % screen_height;
   rv->cell_index = pixel_to_mesh(rv->x, rv->y);
 
   return rv;
@@ -530,7 +541,7 @@ show_bubble(Bubble *bb)
               (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
               360*64);  
     } else {
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
       XSetClipOrigin(defdsp, step_pixmaps[bb->step]->draw_gc, 
                     (bb->x - bb->radius),
                     (bb->y - bb->radius));
@@ -541,7 +552,7 @@ show_bubble(Bubble *bb)
                (bb->radius * 2),  
                (bb->x - bb->radius),
                (bb->y - bb->radius));
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
     }
   }
 }
@@ -563,7 +574,7 @@ hide_bubble(Bubble *bb)
               (bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
               360*64);
     } else {
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
       if (! broken) {
        XSetClipOrigin(defdsp, step_pixmaps[bb->step]->erase_gc, 
                       (bb->x - bb->radius), (bb->y - bb->radius));
@@ -574,7 +585,7 @@ hide_bubble(Bubble *bb)
                       (bb->radius * 2),
                       (bb->radius * 2));
       }
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
     }
   }
 }
@@ -747,38 +758,50 @@ bubble_eat(Bubble *diner, Bubble *food)
   diner->area += food->area;
   delete_bubble_in_mesh(food, DELETE_BUBBLE);
 
-  if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
-    delete_bubble_in_mesh(diner, DELETE_BUBBLE);
-    return 0;
+  if (drop) {
+       if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
+         diner->area = bubble_areas[bubble_max_radius];
+       }
+#ifdef FANCY_BUBBLES
+       if ((! simple) && (diner->area > step_pixmaps[num_bubble_pixmaps]->area)) {
+         diner->area = step_pixmaps[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 ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
+         delete_bubble_in_mesh(diner, DELETE_BUBBLE);
+         return 0;
+       }
+#ifdef FANCY_BUBBLES
+       if ((! simple) && (diner->area > 
+                                          step_pixmaps[num_bubble_pixmaps]->area)) {
+         delete_bubble_in_mesh(diner, DELETE_BUBBLE);
+         return 0;
+       }
+#endif /* FANCY_BUBBLES */
   }
-#endif /* HAVE_XPM */
 
   if (simple) {
     if (diner->area > 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 < bubble_max_radius - 1) && (diner->area > bubble_areas[i+1]))
+               ++i;
       diner->radius = i;
     }
     show_bubble(diner);
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
   } else {
     if (diner->area > step_pixmaps[diner->step+1]->area) {
       i = diner->step;
-      while (diner->area > step_pixmaps[i+1]->area)
-       ++i;
+      while ((i < num_bubble_pixmaps - 1) && (diner->area > step_pixmaps[i+1]->area))
+               ++i;
       diner->step = i;
       diner->radius = step_pixmaps[diner->step]->radius;
     }
     show_bubble(diner);
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
   }
 
   /* Now adjust locations and cells if need be */
@@ -837,7 +860,7 @@ merge_bubbles(Bubble *b1, Bubble *b2)
       break;
     }
   } else {
-    if ((ya_random() % 2) == 0) {
+    if ((random() % 2) == 0) {
       switch (bubble_eat(b1, b2)) {
       case 0:
        return 0;
@@ -883,33 +906,133 @@ insert_new_bubble(Bubble *tmp)
   
   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;
+  if (null_bubble(touch))
+       return;
+
+  while (1) {
+
+       /* Merge all touching bubbles */
+       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 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(nextbub);
+       }
+       
+       if (null_bubble(nextbub))
+         break;
+
+       /* Shift bubble down.  Break if we run off the screen. */
+       if (drop) {
+         if (drop_bubble( 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(nextbub);
+       if (null_bubble(touch)) {
+         /* We also continue every so often if we're dropping and the bubble is at max size */
+         if (drop) {
+               if (simple) {
+                 if ((nextbub->area >= bubble_areas[bubble_max_radius - 1]) && (random() % 2 == 0))
+                       continue;
+               }
+#ifdef FANCY_BUBBLES
+               else {
+                 if ((nextbub->step >= num_bubble_pixmaps - 1) && (random() % 2 == 0))
+                       continue;
+               }
+#endif /* FANCY_BUBBLES */
+      }
+         break;
+       }
+
+  }
+}
+
+
+static void
+leave_trail( Bubble *bb ) 
+{
+  Bubble *tmp;
+
+  tmp = new_bubble();
+  tmp->x = bb->x;
+  tmp->y = bb->y - ((bb->radius + 10) * drop_dir);
+  tmp->cell_index = pixel_to_mesh(tmp->x, tmp->y);
+  add_to_mesh(tmp);
+  insert_new_bubble(tmp);
+  show_bubble( tmp );  
+}
+
+
+static int
+drop_bubble( Bubble *bb )
+{
+  int newmi;
+
+  hide_bubble( bb );
+
+  if (simple)
+       (bb->y) += (bubble_droppages[bb->radius] * drop_dir);
+#ifdef FANCY_BUBBLES
+  else
+       (bb->y) += (step_pixmaps[bb->step]->droppage * drop_dir);
+#endif /* FANCY_BUBBLES */
+  if ((bb->y < 0) || (bb->y > screen_height)) {
+       delete_bubble_in_mesh( bb, DELETE_BUBBLE );
+       return -1;
   }
+
+  show_bubble( bb );
+
+  /* Now adjust locations and cells if need be */
+  newmi = pixel_to_mesh(bb->x, bb->y);
+  if (newmi != bb->cell_index) {
+    delete_bubble_in_mesh(bb, KEEP_BUBBLE);
+    bb->cell_index = newmi;
+    add_to_mesh(bb);
+  }
+
+  if (trails) {
+       if (simple) {
+         if ((bb->area >= bubble_areas[bubble_max_radius - 1]) && (random() % 2 == 0)) 
+               leave_trail( bb );
+       }
+#ifdef FANCY_BUBBLES
+       else { 
+         if ((bb->step >= num_bubble_pixmaps - 1) && (random() % 2 == 0))
+               leave_trail( bb );
+       }
+#endif /* FANCY_BUBBLES */
+  }
+
+  return 0;
 }
 
+
 #ifdef DEBUG
 static int
 get_length_of_bubble_list(Bubble *bb)
@@ -931,7 +1054,7 @@ get_length_of_bubble_list(Bubble *bb)
  * still check for XPM, though!
  */
 
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
 
 /*
  * Pixmaps without file I/O (but do have XPM)
@@ -1042,6 +1165,10 @@ make_pixmap_array(Bubble_Step *list)
     prevrad = step_pixmaps[ind]->radius;
   }
 #endif /* DEBUG */
+
+  /* Now populate the droppage values */
+  for (ind = 0; ind < num_bubble_pixmaps; ind++)
+         step_pixmaps[ind]->droppage = MAX_DROPPAGE * ind / num_bubble_pixmaps;
 }
 
 static void
@@ -1052,7 +1179,6 @@ make_pixmap_from_default(char **pixmap_data, Bubble_Step *bl)
  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
@@ -1067,49 +1193,16 @@ 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;
+#ifdef FANCY_BUBBLES
+  {
+    int w, h;
+    bl->ball = xpm_data_to_pixmap (defdsp, defwin, (char **) pixmap_data,
+                                   &w, &h, &bl->shape_mask);
+    bl->radius = MAX(w, h) / 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);
   }
-  
+#endif /* FANCY_BUBBLES */
+
   gcv.plane_mask = AllPlanes;
   gcv.foreground = default_fg_pixel;
   gcv.function = GXcopy;
@@ -1153,7 +1246,7 @@ default_to_pixmaps (void)
   make_pixmap_array(pixmap_list);
 }
 
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
 
 
 /* 
@@ -1165,7 +1258,7 @@ static void
 get_resources(Display *dpy, Window window)
 /* Get the appropriate X resources and warn about any inconsistencies. */
 {
-  Bool nodelay;
+  Bool nodelay, rise;
   XWindowAttributes xgwa;
   Colormap cmap;
   XGetWindowAttributes (dpy, window, &xgwa);
@@ -1188,6 +1281,17 @@ get_resources(Display *dpy, Window window)
   if (delay < 0)
     delay = 0;
 
+  drop = get_boolean_resource("drop", "Boolean");
+  rise = get_boolean_resource("rise", "Boolean");
+  trails = get_boolean_resource("trails", "Boolean");
+  if (drop && rise) {
+       fprintf( stderr, "Sorry, bubbles can't both drop and rise\n" );
+       exit(1);
+  }
+  drop_dir = (drop ? 1 : -1);
+  if (drop || rise)
+       drop = 1;
+
   default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy,
                                         cmap);
   default_bg_pixel = get_pixel_resource ("background", "Background", dpy,
@@ -1200,11 +1304,11 @@ get_resources(Display *dpy, Window window)
       if (! quiet)
        fprintf(stderr, "-broken not available in simple mode\n");
   } else {
-#ifndef HAVE_XPM
+#ifndef FANCY_BUBBLES
     simple = 1;
-#else
+#else  /* FANCY_BUBBLES */
     broken = get_boolean_resource("broken", "Boolean");
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
   }
 }
 
@@ -1218,8 +1322,6 @@ init_bubbles (Display *dpy, Window window)
   defdsp = dpy;
   defwin = window;
 
-  ya_rand_init(0);
-
   get_resources(dpy, window);
 
   XGetWindowAttributes (dpy, window, &xgwa);
@@ -1257,20 +1359,27 @@ init_bubbles (Display *dpy, Window window)
     for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
       bubble_areas[i] = calc_bubble_area(i);
 
+       /* Now populate the droppage values */
+    bubble_droppages = (int *)xmalloc((bubble_max_radius + 2) * sizeof(int));
+    for (i = 0; i < bubble_min_radius; i++)
+      bubble_droppages[i] = 0;
+    for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
+      bubble_droppages[i] = MAX_DROPPAGE * (i - bubble_min_radius) / (bubble_max_radius - bubble_min_radius);
+
     mesh_length = (2 * 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(). */
     default_to_pixmaps();
 
     /* Set mesh length */
     mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
 
     /* Am I missing something in here??? */
   }
@@ -1304,7 +1413,7 @@ bubbles (Display *dpy, Window window)
   add_to_mesh(tmp);
   insert_new_bubble(tmp);
 
-  XSync (dpy, True);
+  XSync (dpy, False);
 }
 
 
@@ -1314,6 +1423,7 @@ screenhack (Display *dpy, Window window)
   init_bubbles (dpy, window);
   while (1) {
     bubbles (dpy, window);
+    screenhack_handle_events (dpy);
     if (delay)
       usleep(delay);
   }