http://ftp.x.org/contrib/applications/xscreensaver-3.17.tar.gz
authorZygo Blaxell <zblaxell@hungrycats.org>
Mon, 2 Mar 2009 05:42:37 +0000 (00:42 -0500)
committerZygo Blaxell <zblaxell@hungrycats.org>
Mon, 2 Mar 2009 05:42:37 +0000 (00:42 -0500)
-rw-r--r-- 1 zblaxell zblaxell 1141331 Jul 14  1999 xscreensaver-3.17.tar.gz
649ce10f0d7409fedaf0da6fc472dee0cb6f2142  xscreensaver-3.17.tar.gz

24 files changed:
README
driver/XScreenSaver.ad.in
driver/lock.c
driver/prefs.c
driver/xscreensaver-command.man
driver/xscreensaver-demo.man
driver/xscreensaver.man
hacks/Makefile.in
hacks/compile_axp.com
hacks/compile_decc.com
hacks/flow.c
hacks/penetrate.c
hacks/penrose.c
hacks/shadebobs.c
hacks/slip.c
hacks/webcollage
hacks/webcollage.man
hacks/xlockmore.h
hacks/xlyap.c
hacks/xsublim.c [new file with mode: 0644]
setup.com
utils/version.h
xscreensaver.lsm
xscreensaver.spec

diff --git a/README b/README
index 561e0cc0728a11cd486f553c16db40159e4326d6..860debe27410203b8d4975fd9d20b9614ed20cfe 100644 (file)
--- a/README
+++ b/README
@@ -77,6 +77,12 @@ http://www.jwz.org/xscreensaver/.
 
                               ============
 
 
                               ============
 
+Changes since 3.16:   * New version of `webcollage' -- deals better with
+                        corrupted images, and can use an http proxy.
+                      * New hack, `xsublim' (run it in the background,
+                        rather than adding it to the programs list.)
+                      * The xscreensaver daemon was leaking a file descriptor
+                        each time you edited your .xscreensaver file.  Fixed.
 Changes since 3.15:   * New version of `shadebobs'.
                       * Improved image selection in `webcollage', and sped it
                         up slightly.
 Changes since 3.15:   * New version of `shadebobs'.
                       * Improved image selection in `webcollage', and sped it
                         up slightly.
index 160514d473399edd2c370aab097ca1c95d3facb7..ca7285b98a8e2cc65516acdcd4ebc2fa135a723c 100644 (file)
@@ -4,8 +4,8 @@
 !            a screen saver and locker for the X window system
 !                            by Jamie Zawinski
 !
 !            a screen saver and locker for the X window system
 !                            by Jamie Zawinski
 !
-!                              version 3.16
-!                                23-Jun-99
+!                              version 3.17
+!                                15-Jul-99
 !
 ! See "man xscreensaver" for more info.  The latest version is always
 ! available at http://www.jwz.org/xscreensaver/
 !
 ! See "man xscreensaver" for more info.  The latest version is always
 ! available at http://www.jwz.org/xscreensaver/
index ce12efb4abe3dda4bbe5a9f9993894e00563a97c..31d9138fc98aacd87a8173f42d1a5a5d3b6efe6c 100644 (file)
@@ -903,7 +903,7 @@ undo_vp_motion (saver_info *si)
      screen doesn't continue to scroll after we've reset the viewport.
    */
   XSync (si->dpy, False);
      screen doesn't continue to scroll after we've reset the viewport.
    */
   XSync (si->dpy, False);
-  usleep (250);  /* 1/4 second */
+  usleep (250000);  /* 1/4 second */
   XSync (si->dpy, False);
 
   status = XF86VidModeSetViewPort (si->dpy, screen,
   XSync (si->dpy, False);
 
   status = XF86VidModeSetViewPort (si->dpy, screen,
index fa8950f93bbcfefd70b696c14a73a00f796c9994..747db1e58e85c4107755ebdede87c3e3c37e985f 100644 (file)
@@ -363,6 +363,7 @@ parse_init_file (saver_preferences *p)
       if (!p->db) abort();
       handle_entry (&p->db, key, value, name, line);
     }
       if (!p->db) abort();
       handle_entry (&p->db, key, value, name, line);
     }
+  fclose (in);
   free(buf);
 
   p->init_file_date = write_date;
   free(buf);
 
   p->init_file_date = write_date;
index 352de640aef1fa925d851452c716983888e42aaa..9b5ed9a2dd7dc9a59bb27d89909768a405cb5ec8 100644 (file)
@@ -11,7 +11,7 @@
 .if n .sp 1
 .if t .sp .5
 ..
 .if n .sp 1
 .if t .sp .5
 ..
-.TH XScreenSaver 1 "23-Jun-99 (3.16)" "X Version 11"
+.TH XScreenSaver 1 "15-Jul-99 (3.17)" "X Version 11"
 .SH NAME
 xscreensaver-command - control a running xscreensaver process
 .SH SYNOPSIS
 .SH NAME
 xscreensaver-command - control a running xscreensaver process
 .SH SYNOPSIS
index d246c84b7d7cb2341ea611978f02d37503bf8eb1..c54c23e638623753d9fec9acec2439daca9d0746 100644 (file)
@@ -11,7 +11,7 @@
 .if n .sp 1
 .if t .sp .5
 ..
 .if n .sp 1
 .if t .sp .5
 ..
-.TH XScreenSaver 1 "23-Jun-99 (3.16)" "X Version 11"
+.TH XScreenSaver 1 "15-Jul-99 (3.17)" "X Version 11"
 .SH NAME
 xscreensaver-demo - interactively control the background xscreensaver daemon
 .SH SYNOPSIS
 .SH NAME
 xscreensaver-demo - interactively control the background xscreensaver daemon
 .SH SYNOPSIS
index 139467adcd7a350f3d957c0c27a543f893f42a24..e974833f7ef91afd8aa9e2508c368d00103130a5 100644 (file)
@@ -11,7 +11,7 @@
 .if n .sp 1
 .if t .sp .5
 ..
 .if n .sp 1
 .if t .sp .5
 ..
-.TH XScreenSaver 1 "23-Jun-99 (3.16)" "X Version 11"
+.TH XScreenSaver 1 "15-Jul-99 (3.17)" "X Version 11"
 .SH NAME
 xscreensaver - graphics hack and screen locker, launched when the user is idle
 .SH SYNOPSIS
 .SH NAME
 xscreensaver - graphics hack and screen locker, launched when the user is idle
 .SH SYNOPSIS
index b97bf27e13977ab1a2767b19c1a0023e0faf1f65..4f186cf38268714134c62b2af432dd9f6d5fbc4d 100644 (file)
@@ -84,7 +84,7 @@ SRCS          = attraction.c blitspin.c bouboule.c braid.c bubbles.c \
                  truchet.c bsod.c crystal.c discrete.c distort.c kumppa.c \
                  sonar.c demon.c loop.c t3d.c penetrate.c deluxe.c compass.c \
                  squiral.c xflame.c wander.c spotlight.c critical.c \
                  truchet.c bsod.c crystal.c discrete.c distort.c kumppa.c \
                  sonar.c demon.c loop.c t3d.c penetrate.c deluxe.c compass.c \
                  squiral.c xflame.c wander.c spotlight.c critical.c \
-                 phosphor.c xmatrix.c petri.c shadebobs.c
+                 phosphor.c xmatrix.c petri.c shadebobs.c xsublim.c
 SCRIPTS                = vidwhacker webcollage
 
 OBJS           = attraction.o blitspin.o bouboule.o braid.o bubbles.o \
 SCRIPTS                = vidwhacker webcollage
 
 OBJS           = attraction.o blitspin.o bouboule.o braid.o bubbles.o \
@@ -102,7 +102,7 @@ OBJS                = attraction.o blitspin.o bouboule.o braid.o bubbles.o \
                  truchet.o bsod.o crystal.o discrete.o distort.o kumppa.o \
                  sonar.o demon.o loop.o t3d.o penetrate.o deluxe.o compass.o \
                  squiral.o xflame.o wander.o spotlight.o critical.o \
                  truchet.o bsod.o crystal.o discrete.o distort.o kumppa.o \
                  sonar.o demon.o loop.o t3d.o penetrate.o deluxe.o compass.o \
                  squiral.o xflame.o wander.o spotlight.o critical.o \
-                 phosphor.o xmatrix.o petri.o shadebobs.o
+                 phosphor.o xmatrix.o petri.o shadebobs.o xsublim.o
 
 EXES           = attraction blitspin bouboule braid bubbles decayscreen deco \
                  drift flag flame forest vines galaxy grav greynetic halo \
 
 EXES           = attraction blitspin bouboule braid bubbles decayscreen deco \
                  drift flag flame forest vines galaxy grav greynetic halo \
@@ -115,7 +115,7 @@ EXES                = attraction blitspin bouboule braid bubbles decayscreen deco \
                  interference truchet bsod crystal discrete distort kumppa \
                  sonar demon loop t3d penetrate deluxe compass squiral \
                  xflame wander spotlight critical phosphor xmatrix petri \
                  interference truchet bsod crystal discrete distort kumppa \
                  sonar demon loop t3d penetrate deluxe compass squiral \
                  xflame wander spotlight critical phosphor xmatrix petri \
-                 shadebobs
+                 shadebobs xsublim
 
 HACK_OBJS_1    = $(UTILS_BIN)/resources.o $(UTILS_BIN)/visual.o \
                  $(UTILS_BIN)/usleep.o $(UTILS_BIN)/yarandom.o @XMU_OBJS@
 
 HACK_OBJS_1    = $(UTILS_BIN)/resources.o $(UTILS_BIN)/visual.o \
                  $(UTILS_BIN)/usleep.o $(UTILS_BIN)/yarandom.o @XMU_OBJS@
@@ -638,6 +638,13 @@ crystal:   crystal.o               $(XLOCK_OBJS)
        $(CC_HACK) -o $@ $@.o   $(XLOCK_OBJS) $(HACK_LIBS)
 
 
        $(CC_HACK) -o $@ $@.o   $(XLOCK_OBJS) $(HACK_LIBS)
 
 
+
+# This one is not like the others.
+#
+xsublim:       xsublim.o       $(HACK_OBJS_1)
+       $(CC_HACK) -o $@ $@.o   $(HACK_OBJS_1) $(HACK_LIBS)
+
+
 ##############################################################################
 #
 # DO NOT DELETE: updated by make distdepend
 ##############################################################################
 #
 # DO NOT DELETE: updated by make distdepend
@@ -1676,4 +1683,6 @@ shadebobs.o: $(UTILS_SRC)/hsv.h
 shadebobs.o: $(UTILS_SRC)/colors.h
 shadebobs.o: $(UTILS_SRC)/grabscreen.h
 shadebobs.o: $(UTILS_SRC)/visual.h
 shadebobs.o: $(UTILS_SRC)/colors.h
 shadebobs.o: $(UTILS_SRC)/grabscreen.h
 shadebobs.o: $(UTILS_SRC)/visual.h
+xsublim.o: $(UTILS_SRC)/usleep.h
+xsublim.o: $(UTILS_SRC)/resources.h
 
 
index 6bdfa03211c3b8914e83bf7ee908ff4ae770face..69a496d4f1025d5c22ff9c307a4f5d5bd9fed417 100644 (file)
@@ -88,4 +88,5 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XMATRIX.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XROGER-HACK.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XSCREENSAVER-SGIGL.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XMATRIX.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XROGER-HACK.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XSCREENSAVER-SGIGL.C
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XSUBLIM.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE,XLOCKMORE)/INCL=([],[-],[-.UTILS])/OBJ=SCREENHACK-XLOCK.OBJ SCREENHACK.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE,XLOCKMORE)/INCL=([],[-],[-.UTILS])/OBJ=SCREENHACK-XLOCK.OBJ SCREENHACK.C
index 6bdfa03211c3b8914e83bf7ee908ff4ae770face..69a496d4f1025d5c22ff9c307a4f5d5bd9fed417 100644 (file)
@@ -88,4 +88,5 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XMATRIX.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XROGER-HACK.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XSCREENSAVER-SGIGL.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XMATRIX.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XROGER-HACK.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XSCREENSAVER-SGIGL.C
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XSUBLIM.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE,XLOCKMORE)/INCL=([],[-],[-.UTILS])/OBJ=SCREENHACK-XLOCK.OBJ SCREENHACK.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE,XLOCKMORE)/INCL=([],[-],[-.UTILS])/OBJ=SCREENHACK-XLOCK.OBJ SCREENHACK.C
index f606e05d86cff324d9e440b65d8639eb10232865..d3cc904f8d3ee136e2672bdb83c7b7bbffd23046 100644 (file)
@@ -412,7 +412,7 @@ draw_flow(ModeInfo * mi)
 
                /* Fill the segment lists. */
 
 
                /* Fill the segment lists. */
 
-               if(sp->view.depth) /* perspective view has special points */
+               if(sp->view.depth) /* perspective view has special points */
                        if(b==0){ /* point of view */
                                sp->centre.x=X(0, b);
                                sp->centre.y=Y(0, b);
                        if(b==0){ /* point of view */
                                sp->centre.x=X(0, b);
                                sp->centre.y=Y(0, b);
@@ -476,6 +476,7 @@ draw_flow(ModeInfo * mi)
                                Z(1, b)=Z(0, 0);
 #endif
                        }
                                Z(1, b)=Z(0, 0);
 #endif
                        }
+               }
                
                for(i=0; i<2; i++){
                        double x=X(i,b)-sp->centre.x;
                
                for(i=0; i<2; i++){
                        double x=X(i,b)-sp->centre.x;
index 3e41af80e0461a6a257ca1e54b8697719e7de4b4..6ab47c2bfbb7aebdfabd8470edd4c6b80d22f6cd 100644 (file)
@@ -612,7 +612,7 @@ static void LoopBooms(Display *dpy, Window window, Colormap cmap, int xlim, int
         if (!m->alive)
                continue;
         
         if (!m->alive)
                continue;
         
-        if (loop & 1)
+        if (loop & 1) {
                if (m->outgoing) {
                  m->rad++;
                  if (m->rad >= m->max)
                if (m->outgoing) {
                  m->rad++;
                  if (m->rad >= m->max)
@@ -628,6 +628,7 @@ static void LoopBooms(Display *dpy, Window window, Colormap cmap, int xlim, int
                  if (m->rad <= 0)
                         m->alive = 0;
                }
                  if (m->rad <= 0)
                         m->alive = 0;
                }
+        }
   }
 }
 
   }
 }
 
index a8af2e51d13b43196718cdc6efc1f0b1f87b7a9a..07e351fc60d6e0589a8e7ee81bc29cb112f4a530 100644 (file)
@@ -1013,7 +1013,7 @@ add_tile(ModeInfo * mi,
        } while (node != tp->fringe.nodes);
 
        /* Rechain. */
        } while (node != tp->fringe.nodes);
 
        /* Rechain. */
-       if (!(fc & FC_CUT_THIS))
+       if (!(fc & FC_CUT_THIS)) {
                if (side == S_LEFT) {
                        vertex->next = right;
                        right->prev = vertex;
                if (side == S_LEFT) {
                        vertex->next = right;
                        right->prev = vertex;
@@ -1021,6 +1021,7 @@ add_tile(ModeInfo * mi,
                        vertex->prev = left;
                        left->next = vertex;
                }
                        vertex->prev = left;
                        left->next = vertex;
                }
+       }
        if (!(fc & FC_CUT_FAR)) {
                if (!(fc & FC_CUT_LEFT)) {
                        far->next = left;
        if (!(fc & FC_CUT_FAR)) {
                if (!(fc & FC_CUT_LEFT)) {
                        far->next = left;
index fc89ceeecbaaacb86175bbd47d6573e4ac6ca9d0..34965dfc465303ffdec3764ef6c43d7d3d6b332e 100644 (file)
@@ -246,7 +246,7 @@ static void SetPalette(Display *pDisplay, Window Win, char *sColor,
 
 
 static void Initialize( Display *pDisplay, Window Win,
 
 
 static void Initialize( Display *pDisplay, Window Win,
-                        GC *pGC, XImage *pXImage,
+                        GC *pGC, XImage **pXImage,
                         int *ncolorsP, XColor *aXColors )
 {
   XGCValues gcValues;
                         int *ncolorsP, XColor *aXColors )
 {
   XGCValues gcValues;
@@ -273,21 +273,13 @@ static void Initialize( Display *pDisplay, Window Win,
   /*  Create the GC. */
   *pGC = XCreateGC( pDisplay, Win, 0, &gcValues );
 
   /*  Create the GC. */
   *pGC = XCreateGC( pDisplay, Win, 0, &gcValues );
 
-  memset (pXImage, 0, sizeof(*pXImage));
-  pXImage->width = XWinAttribs.width;                   /* Width of image */
-  pXImage->height = XWinAttribs.height;                 /* Height of image */
-  pXImage->format = ZPixmap;                /* XYBitmap, XYPixmap, ZPixmap */
-
-  /* Pointer to image data */
-  pXImage->byte_order = ImageByteOrder(pDisplay);
-  pXImage->bitmap_unit = BitmapUnit(pDisplay);
-  pXImage->bitmap_bit_order = BitmapBitOrder(pDisplay);
-  pXImage->bitmap_pad = BitmapPad(pDisplay);
-  pXImage->depth = XWinAttribs.depth;
-  pXImage->bytes_per_line = 0;                /* Accelerator to next line */
-  pXImage->bits_per_pixel = bpp;
-  XInitImage( pXImage );
-  pXImage->data = calloc(pXImage->bytes_per_line, pXImage->height);
+  *pXImage = XCreateImage(pDisplay, XWinAttribs.visual,
+                         XWinAttribs.depth,
+                         ZPixmap, 0, NULL,
+                         XWinAttribs.width, XWinAttribs.height,
+                         BitmapPad(pDisplay), 0);
+  (*pXImage)->data = calloc((*pXImage)->bytes_per_line,
+                           (*pXImage)->height);
 
   /*  These are precalculations used in Execute(). */
   nMaxExtentX = ( XWinAttribs.width / 2 ) - 20;
 
   /*  These are precalculations used in Execute(). */
   nMaxExtentX = ( XWinAttribs.width / 2 ) - 20;
@@ -317,7 +309,7 @@ void screenhack(Display *pDisplay, Window Win )
   GC gc;
   int ncolors = 0;
   XColor aXColors[ 256 ];
   GC gc;
   int ncolors = 0;
   XColor aXColors[ 256 ];
-  XImage Image;
+  XImage *pImage;
   unsigned char nShadeBobCount, iShadeBob;
   SShadeBob *aShadeBobs;
 #ifdef VERBOSE
   unsigned char nShadeBobCount, iShadeBob;
   SShadeBob *aShadeBobs;
 #ifdef VERBOSE
@@ -339,7 +331,7 @@ void screenhack(Display *pDisplay, Window Win )
   printf( "Allocated %d ShadeBobs\n", nShadeBobCount );
 #endif  /*  VERBOSE */
 
   printf( "Allocated %d ShadeBobs\n", nShadeBobCount );
 #endif  /*  VERBOSE */
 
-  Initialize( pDisplay, Win, &gc, &Image, &ncolors, aXColors );
+  Initialize( pDisplay, Win, &gc, &pImage, &ncolors, aXColors );
 
   for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
     InitShadeBob( &aShadeBobs[ iShadeBob ], iShadeBob % 2 );
 
   for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
     InitShadeBob( &aShadeBobs[ iShadeBob ], iShadeBob % 2 );
@@ -356,7 +348,7 @@ void screenhack(Display *pDisplay, Window Win )
     {
       i = 0;
       XClearWindow (pDisplay, Win);
     {
       i = 0;
       XClearWindow (pDisplay, Win);
-      memset (Image.data, 0, Image.bytes_per_line * Image.height);
+      memset (pImage->data, 0, pImage->bytes_per_line * pImage->height);
       for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
         ResetShadeBob( &aShadeBobs[ iShadeBob ] );
       SetPalette( pDisplay, Win, sColor, &ncolors, aXColors );
       for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
         ResetShadeBob( &aShadeBobs[ iShadeBob ] );
       SetPalette( pDisplay, Win, sColor, &ncolors, aXColors );
@@ -364,7 +356,7 @@ void screenhack(Display *pDisplay, Window Win )
 
     for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
       Execute( &aShadeBobs[ iShadeBob ], pDisplay, Win, &gc,
 
     for( iShadeBob=0; iShadeBob<nShadeBobCount; iShadeBob++ )
       Execute( &aShadeBobs[ iShadeBob ], pDisplay, Win, &gc,
-               &Image, ncolors, aXColors );
+               pImage, ncolors, aXColors );
 
     if( delay && !(i % 4) )
                usleep(delay);
 
     if( delay && !(i % 4) )
                usleep(delay);
@@ -381,8 +373,8 @@ void screenhack(Display *pDisplay, Window Win )
   }
 
   free( anSinTable );
   }
 
   free( anSinTable );
-  free( Image.data );
-  XDestroyImage( &Image );
+  free( pImage->data );
+  XDestroyImage( pImage );
   free( aShadeBobs );
 }
 
   free( aShadeBobs );
 }
 
index 395fea1cd37c0d5804d5292ce047cdfb51f63b31..5c51243688fe8cf8740be80e67fd5ff3ddd45db4 100644 (file)
@@ -128,13 +128,14 @@ prepare_screen(ModeInfo * mi, slipstruct * s)
 
        for (i = 0; i < n; i++) {
          int ww = ((w/2) + halfrandom(w));
 
        for (i = 0; i < n; i++) {
          int ww = ((w/2) + halfrandom(w));
-               if (not_solid)
+               if (not_solid) {
                        if (MI_NPIXELS(mi) > 2)
                                XSetForeground(display, gc, MI_PIXEL(mi, halfrandom(MI_NPIXELS(mi))));
                        else if (halfrandom(2))
                                XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
                        else
                                XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
                        if (MI_NPIXELS(mi) > 2)
                                XSetForeground(display, gc, MI_PIXEL(mi, halfrandom(MI_NPIXELS(mi))));
                        else if (halfrandom(2))
                                XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
                        else
                                XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
+               }
                XFillRectangle(display, MI_WINDOW(mi), gc,
                               halfrandom(s->width - ww),
                               halfrandom(s->height - ww),
                XFillRectangle(display, MI_WINDOW(mi), gc,
                               halfrandom(s->width - ww),
                               halfrandom(s->height - ww),
index 49768c978262200eeb7bbc1883029a47814c4db6..9f767edaabb6abb61ea93d1e6b27db06312d6740 100755 (executable)
 #
 #   default-n:  webcollage -root                                       \n\
 #   default-n: webcollage -root -filter 'vidwhacker -stdin -stdout'    \n\
 #
 #   default-n:  webcollage -root                                       \n\
 #   default-n: webcollage -root -filter 'vidwhacker -stdin -stdout'    \n\
-#
-# To run this as a CGI program on a web site, do this (these instructions
-# work with Apache 1.3 or newer):
-#
-#   1:  Place this program in your document directory, named "webcollage".
-#       The name shouldn't end in .cgi or .html, since this CGI behaves like
-#       a directory.
-#   2:  Make it world-readable and world-executable.
-#   3:  Create a ".htaccess" file in the same directory containing these lines:
-#         <Files ~ "^webcollage$">
-#         SetHandler cgi-script
-#         </Files>
-#   4:  Create these files in the same directory, world-writable, zero-length:
-#        collage.ppm
-#        collage.tmp
-#        collage.jpg
-#        collage.pending
-#        collage.map
-#
-# Now the CGI is ready to go.
-
-my $copyright = "WebCollage, Copyright (c) 1999" .
-    " Jamie Zawinski <jwz\@jwz.org>\n" .
-    "            http://www.jwz.org/xscreensaver/\n";
 
 
-my $argv0 = $0;
-my $progname = $argv0; $progname =~ s@.*/@@g;
-my $version = q{ $Revision: 1.7 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/;
+require 5;
+#use diagnostics;
+use strict;
 
 use Socket;
 require Time::Local;
 
 use Socket;
 require Time::Local;
@@ -51,41 +27,46 @@ require POSIX;
 use Fcntl ':flock'; # import LOCK_* constants
 
 
 use Fcntl ':flock'; # import LOCK_* constants
 
 
-#  CGI Parameters
-
-my $data_dir     = "";           # if you want the following files to go in
-                                  # some directory below ".", name it here.
-
-my $image_ppm    = "${data_dir}collage.ppm"; # names of the various data files.
-my $image_tmp    = "${data_dir}collage.tmp";
-my $image_jpg    = "${data_dir}collage.jpg";
-my $pending_file = "${data_dir}collage.pending";
-my $map_file     = "${data_dir}collage.map";
-
-my $url_generation_time  = 60;   # total time to spend getting URLs.
-my $image_retrieval_time = 60;    # maximum time to spend loading all images.
-my $max_map_entries = 100;       # how many lines to save in $map_file.
-my $pastes_per_load = 3;          # how many images to try and paste each time.
-
-my $max_age = 5 * 60;             # minutes before it is considered stale.
-my $scale = 1.0;                  # client-side image expansion.
-
-my $img_width = 800;             # size of the image being generated.
-my $img_height = 600;
-
-my @all_files = ($image_ppm, $image_tmp, $image_jpg, $pending_file, $map_file);
-my $script_date;
+my $version = q{ $Revision: 1.32 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/;
+my $copyright = "WebCollage $version, Copyright (c) 1999" .
+    " Jamie Zawinski <jwz\@jwz.org>\n" .
+    "            http://www.jwz.org/xscreensaver/\n";
 
 
-# Other Parameters
+my $argv0 = $0;
+my $progname = $argv0; $progname =~ s@.*/@@g;
 
 my $random_redirector = "http://random.yahoo.com/bin/ryl";
 
 my $random_redirector = "http://random.yahoo.com/bin/ryl";
-my $image_randomizer_a = "http://image.altavista.com/";
-my $image_randomizer = $image_randomizer_a . "cgi-bin/avncgi" .
-                       "?do=3&verb=no&oshape=n&oorder=" .
-                       "&ophoto=1&oart=1&ocolor=1&obw=1" .
-                       "&stype=simage&oprem=0&query=";
-
+my $image_randomizer_1 = "http://image.altavista.com/cgi-bin/avncgi" .
+                         "?do=3" .
+                         "&verb=n" .
+                         "&oshape=n" .
+                         "&oorder=" .
+                         "&ophoto=1&oart=1&ocolor=1&obw=1" .
+                         "&stype=simage" .
+                         "&oprem=0" .
+                         "&query=";
+my $image_randomizer_2 = "http://www.hotbot.com/?clickSrc=search" .
+                         "&submit=SEARCH&SM=SC&LG=any" .
+                         "&AM0=MC&AT0=words&AW0=" .
+                         "&AM1=MN&AT1=words&AW1=" .
+                         "&savenummod=2&date=within" .
+                         "&DV=0&DR=newer&DM=1&DD=1&DY=99&FVI=1&FS=&RD=RG" .
+                         "&RG=all&Domain=&PS=A&PD=&STEM=1&DC=50&DE=0&_v=2" .
+                         "&OPs=MDRTP&NUMMOD=2" .
+                         "&MT=";
+my $image_randomizer_3 = "http://www.altavista.com/cgi-bin/query?pg=q" .
+                         "&text=yes&kl=XX&stype=stext&q=";
+
+my $image_ppm   = ($ENV{TMPDIR} ? $ENV{TMPDIR} : "/tmp") . "/webcollage." . $$;
+my $image_tmp1  = $image_ppm . "-1";
+my $image_tmp2  = $image_ppm . "-2";
+
+my $img_width;           # size of the image being generated.
+my $img_height;
+
+my $http_proxy = undef;
 my $http_timeout = 30;
 my $http_timeout = 30;
+my $cvt_timeout = 10;
 my $ppm_to_root_window_cmd = "xv -root -rmode 5 -viewonly" .
                              " +noresetroot %%PPM%% -quit";
 my $filter_cmd = undef;
 my $ppm_to_root_window_cmd = "xv -root -rmode 5 -viewonly" .
                              " +noresetroot %%PPM%% -quit";
 my $filter_cmd = undef;
@@ -100,13 +81,22 @@ my $wordlist = "/usr/dict/words";
 if (!-r $wordlist) {
     $wordlist = "/usr/share/lib/dict/words";    # irix
 }
 if (!-r $wordlist) {
     $wordlist = "/usr/share/lib/dict/words";    # irix
 }
+die "$wordlist doesn't exist!\n" unless (-r $wordlist);
 
 
 my $min_width = 50;
 my $min_height = 50;
 my $min_ratio = 1/5;
 
 
 
 my $min_width = 50;
 my $min_height = 50;
 my $min_ratio = 1/5;
 
-my $DEBUG = 0;
+my $verbose = 0;
+
+my %rejected_urls;
+my @tripwire_words = ("aberrate", "abode", "amorphous", "antioch",
+                      "arrhenius", "arteriole", "blanket", "brainchild",
+                      "burdensome", "carnival", "cherub", "chord", "clever",
+                      "dedicate", "dilogarithm", "dolan", "dryden",
+                      "eggplant");
+
 
 
 
 
 
 
@@ -123,51 +113,76 @@ sub get_document_1 {
     my ( $url, $referer, $timeout ) = @_;
 
     if (!defined($timeout)) { $timeout = $http_timeout; }
     my ( $url, $referer, $timeout ) = @_;
 
     if (!defined($timeout)) { $timeout = $http_timeout; }
-    if ($timeout <= 0) { return undef; }
+    if ($timeout <= 0) { return (); }
     if ($timeout > $http_timeout) { $timeout = $http_timeout; }
 
     if ($timeout > $http_timeout) { $timeout = $http_timeout; }
 
-    if ( $DEBUG > 3 ) {
-       print STDERR "get_document_1 $url " .
+    if ( $verbose > 3 ) {
+       print STDERR "$progname: get_document_1 $url " .
             ($referer ? $referer : "") . "\n";
     }
 
     my($url_proto, $dummy, $serverstring, $path) = split(/\//, $url, 4);
     if (! ($url_proto && $url_proto =~ m/^http:$/i)) {
             ($referer ? $referer : "") . "\n";
     }
 
     my($url_proto, $dummy, $serverstring, $path) = split(/\//, $url, 4);
     if (! ($url_proto && $url_proto =~ m/^http:$/i)) {
-        if ($DEBUG) { print STDERR "not an HTTP URL: $url\n"; }
-        return undef;
+        if ($verbose) { print STDERR "$progname: not an HTTP URL: $url\n"; }
+        return ();
     }
     }
+
+    $path = "" unless $path;
+
     my($them,$port) = split(/:/, $serverstring);
     $port = 80 unless $port;
     my($them,$port) = split(/:/, $serverstring);
     $port = 80 unless $port;
-    my $size="";
+
+    my $them2 = $them;
+    my $port2 = $port;
+    if ($http_proxy) {
+        $serverstring = $http_proxy if $http_proxy;
+        ($them2,$port2) = split(/:/, $serverstring);
+        $port2 = 80 unless $port2;
+    }
 
     my ($remote, $iaddr, $paddr, $proto, $line);
 
     my ($remote, $iaddr, $paddr, $proto, $line);
-    $remote = $them;
-    if ($port =~ /\D/) { $port = getservbyname($port, 'tcp') }
-    return unless $port;
-    $iaddr   = inet_aton($remote)               || return;
-    $paddr   = sockaddr_in($port, $iaddr);
+    $remote = $them2;
+    if ($port2 =~ /\D/) { $port2 = getservbyname($port2, 'tcp') }
+    return unless $port2;
+    $iaddr   = inet_aton($remote) || return;
+    $paddr   = sockaddr_in($port2, $iaddr);
+
 
     @_ =
     eval {
         local $SIG{ALRM}  = sub {
 
     @_ =
     eval {
         local $SIG{ALRM}  = sub {
-            if ($DEBUG > 0) {
-                print STDERR "timed out ($timeout) for $url\n";
+            if ($verbose > 0) {
+                print STDERR "$progname: timed out ($timeout) for $url\n";
             }
             }
-            die "alarm\n" };
+            die "alarm\n"
+            };
         alarm $timeout;
 
         $proto   = getprotobyname('tcp');
         alarm $timeout;
 
         $proto   = getprotobyname('tcp');
-        socket(S, PF_INET, SOCK_STREAM, $proto)  || return;
-        connect(S, $paddr)    || return;
+        if (!socket(S, PF_INET, SOCK_STREAM, $proto)) {
+            print STDERR "$progname: socket: $!\n" if ($verbose);
+            return;
+        }
+        if (!connect(S, $paddr)) {
+            print STDERR "$progname: connect($serverstring): $!\n"
+                if ($verbose);
+            return;
+        }
 
         select(S); $| = 1; select(STDOUT);
 
 
         select(S); $| = 1; select(STDOUT);
 
-        print S ("GET /$path HTTP/1.0\n" .
+        my $cookie;
+        if ($remote =~ m/\baltavista\.com$/i) {
+            # kludge to tell the various altavista sites to be uncensored.
+            $cookie = "AV_ALL=1";
+        }
+
+        print S ("GET " . ($http_proxy ? $url : "/$path") . " HTTP/1.0\n" .
                  "Host: $them\n" .
                  "User-Agent: $progname/$version\n" .
                  ($referer ? "Referer: $referer\n" : "") .
                  "Host: $them\n" .
                  "User-Agent: $progname/$version\n" .
                  ($referer ? "Referer: $referer\n" : "") .
+                 ($cookie ? "Cookie: $cookie\n" : "") .
                  "\n");
                  "\n");
-
         my $http = <S>;
 
         my $head = "";
         my $http = <S>;
 
         my $head = "";
@@ -182,12 +197,16 @@ sub get_document_1 {
 
         close S;
 
 
         close S;
 
+        if ( $verbose > 3 ) {
+            print STDERR "$progname:    ==> $http\n";
+        }
+
         return ( $http, $head, $body );
     };
     die if ($@ && $@ ne "alarm\n");       # propagate errors
     if ($@) {
         # timed out
         return ( $http, $head, $body );
     };
     die if ($@ && $@ ne "alarm\n");       # propagate errors
     if ($@) {
         # timed out
-        return undef;
+        return ();
     } else {
         # didn't
         alarm 0;
     } else {
         # didn't
         alarm 0;
@@ -203,8 +222,12 @@ sub get_document {
     my ( $url, $referer, $timeout ) = @_;
     my $start = time;
 
     my ( $url, $referer, $timeout ) = @_;
     my $start = time;
 
+    my $orig_url = $url;
+    my $loop_count = 0;
+    my $max_loop_count = 4;
+
     do {
     do {
-        if (defined($timeout) && $timeout <= 0) { return undef; }
+        if (defined($timeout) && $timeout <= 0) { return (); }
 
        my ( $http, $head, $body ) = get_document_1 ($url, $referer, $timeout);
 
 
        my ( $http, $head, $body ) = get_document_1 ($url, $referer, $timeout);
 
@@ -215,25 +238,46 @@ sub get_document {
             $start = $now;
         }
 
             $start = $now;
         }
 
-       return undef if ( ! $body );
+       return () if ( ! $body );
 
        if ( $http =~ m@HTTP/[0-9.]+ 30[23]@ ) {
            $_ = $head;
            my ( $location ) = m@^location:[ \t]*(.*)$@im;
            if ( $location ) {
 
        if ( $http =~ m@HTTP/[0-9.]+ 30[23]@ ) {
            $_ = $head;
            my ( $location ) = m@^location:[ \t]*(.*)$@im;
            if ( $location ) {
+                $location =~ s/[\r\n]$//;
 
 
-               if ( $DEBUG > 3 ) {
-                   print STDERR "redirect from $url to $location\n";
+               if ( $verbose > 3 ) {
+                   print STDERR "$progname: redirect from " .
+                        "$url to $location\n";
                }
                 $referer = $url;
                $url = $location;
                }
                 $referer = $url;
                $url = $location;
+
+                if ($url =~ m@^/@) {
+                    $referer =~ m@^(http://[^/]+)@i;
+                    $url = $1 . $url;
+                } elsif (! ($url =~ m@^[a-z]+:@i)) {
+                    $_ = $referer;
+                    s@[^/]+$@@g if m@^http://[^/]+/@i;
+                    $_ .= "/" if m@^http://[^/]+$@i;
+                    $url = $_ . $url;
+                }
+
            } else {
                return ( $url, $body );
            }
 
            } else {
                return ( $url, $body );
            }
 
+            if ($loop_count++ > $max_loop_count) {
+               if ( $verbose > 1 ) {
+                   print STDERR "$progname: too many redirects " .
+                        "($max_loop_count) from $orig_url\n";
+               }
+                return ();
+            }
+
         } elsif ( $http =~ m@HTTP/[0-9.]+ [4-9][0-9][0-9]@ ) {
             # http errors -- return nothing.
         } elsif ( $http =~ m@HTTP/[0-9.]+ [4-9][0-9][0-9]@ ) {
             # http errors -- return nothing.
-            return undef;
+            return ();
 
        } else {
 
 
        } else {
 
@@ -245,17 +289,18 @@ sub get_document {
 
 
 # given a URL and the body text at that URL, selects and returns a random
 
 
 # given a URL and the body text at that URL, selects and returns a random
-# image from it.  returns undef if no suitable images found.
+# image from it.  returns () if no suitable images found.
 #
 sub pick_image_from_body {
 #
 sub pick_image_from_body {
-    my ( $base, $body ) = @_;
+    my ( $url, $body ) = @_;
 
 
-    $_ = $base;
+    my $base = $url;
+    $_ = $url;
 
     # if there's at least one slash after the host, take off the last
     # pathname component
     if ( m@^http://[^/]+/@io ) {
 
     # if there's at least one slash after the host, take off the last
     # pathname component
     if ( m@^http://[^/]+/@io ) {
-       ( $base = $base ) =~ s@[^/]+$@@go;
+       $base =~ s@[^/]+$@@go;
     }
 
     # if there are no slashes after the host at all, put one on the end.
     }
 
     # if there are no slashes after the host at all, put one on the end.
@@ -263,8 +308,8 @@ sub pick_image_from_body {
        $base .= "/";
     }
 
        $base .= "/";
     }
 
-    if ( $DEBUG > 3 ) {
-       print STDERR "base is $base\n";
+    if ( $verbose > 3 ) {
+       print STDERR "$progname: base is $base\n";
     }
 
 
     }
 
 
@@ -276,16 +321,79 @@ sub pick_image_from_body {
     # nuke comments
     s/<!--.*?-->//go;
 
     # nuke comments
     s/<!--.*?-->//go;
 
+
+    # There are certain web sites that list huge numbers of dictionary
+    # words in their bodies or in their <META NAME=KEYWORDS> tags (surprise!
+    # Porn sites tend not to be reputable!)
+    #
+    # I do not want webcollage to filter on content: I want it to select
+    # randomly from the set of images on the web.  All the logic here for
+    # rejecting some images is really a set of heuristics for rejecting
+    # images that are not really images: for rejecting *text* that is in
+    # GIF/JPEG form.  I don't want text, I want pictures, and I want the
+    # content of the pictures to be randomly selected from among all the
+    # available content.
+    #
+    # So, filtering out "dirty" pictures by looking for "dirty" keywords
+    # would be wrong: dirty pictures exist, like it or not, so webcollage
+    # should be able to select them.
+    #
+    # However, picking a random URL is a hard thing to do.  The mechanism I'm
+    # using is to search for a selection of random words.  This is not
+    # perfect, but works ok most of the time.  The way it breaks down is when
+    # some URLs get precedence because their pages list *every word* as
+    # related -- those URLs come up more often than others.
+    #
+    # So, after we've retrieved a URL, if it has too many keywords, reject
+    # it.  We reject it not on the basis of what those keywords are, but on
+    # the basis that by having so many, the page has gotten an unfair
+    # advantage against our randomizer.
+    #
+    my $trip_count = 0;
+    foreach my $trip (@tripwire_words) {
+        $trip_count++ if m/$trip/i;
+    }
+    if ($trip_count >= $#tripwire_words - 2) {
+        if ($verbose > 1) {
+            print STDERR "$progname: there is probably a dictionary in" .
+                " \"$url\": rejecting.\n";
+        }
+        $rejected_urls{$url} = -1;
+        return ();
+    }
+
+
     my @urls;
     my %unique_urls;
 
     foreach (split(/ *</)) {
     my @urls;
     my %unique_urls;
 
     foreach (split(/ *</)) {
-       if ( m/^(img|a) .*(src|href) ?= ?\"? ?(.*?)[ >\"]/io ) {
+        if ( m/^meta /i ) {
+
+            # Likewise, reject any web pages that have a KEYWORDS meta tag
+            # that is too long.
+            #
+            if (m/name ?= ?\"?keywords\"?/i &&
+                m/content ?= ?\"([^\"]+)\"/) {
+                my $L = length($1);
+                if ($L > 1000) {
+                    if ($verbose > 1) {
+                        print STDERR "$progname: keywords of" .
+                            " length $L in $url: rejecting.\n";
+                    }
+                    $rejected_urls{$url} = $L;
+                    return ();
+                } elsif ( $verbose > 2 ) {
+                    print STDERR "$progname: keywords of length $L" .
+                        " in $url (ok.)\n";
+                }
+            }
+
+        } elsif ( m/^(img|a) .*(src|href) ?= ?\"? ?(.*?)[ >\"]/io ) {
 
            my $was_inline = ( "$1" eq "a" || "$1" eq "A" );
            my $link = $3;
 
            my $was_inline = ( "$1" eq "a" || "$1" eq "A" );
            my $link = $3;
-           my ( $width )  = m/width ?=[ \"]*([0-9]+)/oi;
-           my ( $height ) = m/height ?=[ \"]*([0-9]+)/oi;
+           my ( $width )  = m/width ?=[ \"]*(\d+)/oi;
+           my ( $height ) = m/height ?=[ \"]*(\d+)/oi;
            $_ = $link;
 
            if ( m@^/@o ) {
            $_ = $link;
 
            if ( m@^/@o ) {
@@ -309,25 +417,21 @@ sub pick_image_from_body {
                next;
            }
 
                next;
            }
 
-#          # skip GIF?
-#          if ( m@[.](gif)@io ) {
-##             if ( $DEBUG > 2 ) { print STDERR "skip GIF $_\n"; }
-#              next;
-#          }
-
            # skip really short or really narrow images
            if ( $width && $width < $min_width) {
            # skip really short or really narrow images
            if ( $width && $width < $min_width) {
-               if ( $DEBUG > 2 ) {
+               if ( $verbose > 2 ) {
                     if (!$height) { $height = "?"; }
                     if (!$height) { $height = "?"; }
-                   print STDERR "skip narrow image $_ ($width x $height)\n";
+                   print STDERR "$progname: skip narrow image " .
+                        "$_ (${width}x$height)\n";
                }
                next;
            }
 
            if ( $height && $height < $min_height) {
                }
                next;
            }
 
            if ( $height && $height < $min_height) {
-               if ( $DEBUG > 2 ) {
+               if ( $verbose > 2 ) {
                     if (!$width) { $width = "?"; }
                     if (!$width) { $width = "?"; }
-                   print STDERR "skip short image $_ ($width x $height)\n";
+                   print STDERR "$progname: skip short image " .
+                        "$_ (${width}x$height)\n";
                }
                next;
            }
                }
                next;
            }
@@ -335,9 +439,10 @@ sub pick_image_from_body {
             # skip images with ratios that make them look like banners.
            if ( $min_ratio && $width && $height &&
                 ($width * $min_ratio ) > $height ) {
             # skip images with ratios that make them look like banners.
            if ( $min_ratio && $width && $height &&
                 ($width * $min_ratio ) > $height ) {
-               if ( $DEBUG > 2 ) {
+               if ( $verbose > 2 ) {
                     if (!$height) { $height = "?"; }
                     if (!$height) { $height = "?"; }
-                   print STDERR "skip bad ratio $_ ($width x $height)\n";
+                   print STDERR "$progname: skip bad ratio " .
+                        "$_ (${width}x$height)\n";
                }
                next;
            }
                }
                next;
            }
@@ -345,12 +450,14 @@ sub pick_image_from_body {
            my $url = $_;
 
            if ( $unique_urls{$url} ) {
            my $url = $_;
 
            if ( $unique_urls{$url} ) {
-               if ( $DEBUG > 2 ) { print STDERR "skip duplicate image $_\n"; }
+               if ( $verbose > 2 ) {
+                    print STDERR "$progname: skip duplicate image $_\n";
+                }
                next;
            }
 
                next;
            }
 
-           if ( $DEBUG > 2 ) {
-               print STDERR "got $url" . 
+           if ( $verbose > 2 ) {
+               print STDERR "$progname: got $url" . 
                    ($width && $height ? " (${width}x${height})" : "") .
                    ($was_inline ? " (inline)" : "") . "\n";
            }
                    ($width && $height ? " (${width}x${height})" : "") .
                    ($was_inline ? " (inline)" : "") . "\n";
            }
@@ -373,20 +480,20 @@ sub pick_image_from_body {
     }
 
     if ( $#urls == 0 ) {
     }
 
     if ( $#urls == 0 ) {
-       if ( $DEBUG > 2 ) {
-           print STDERR "no images on $base\n";
+       if ( $verbose > 2 ) {
+           print STDERR "$progname: no images on $base\n";
        }
        }
-       return undef;
+       return ();
     }
 
     }
 
-    return undef if ( $#urls < 1 );
+    return () if ( $#urls < 1 );
 
     # pick a random element of the table
     my $i = ((rand() * 99999) % $#urls);
 
     # pick a random element of the table
     my $i = ((rand() * 99999) % $#urls);
-    my $url = $urls[$i];
+    $url = $urls[$i];
 
 
-    if ( $DEBUG > 2 ) {
-       print STDERR "picked $url\n";
+    if ( $verbose > 2 ) {
+       print STDERR "$progname: picked $url\n";
     }
 
     return $url;
     }
 
     return $url;
@@ -395,13 +502,13 @@ sub pick_image_from_body {
 
 # Using the URL-randomizer, picks a random image on a random page, and
 # returns two URLs: the page containing the image, and the image.
 
 # Using the URL-randomizer, picks a random image on a random page, and
 # returns two URLs: the page containing the image, and the image.
-# Returns undef if nothing found this time.
+# Returns () if nothing found this time.
 #
 sub pick_from_url_randomizer {
     my ( $timeout ) = @_;
 
 #
 sub pick_from_url_randomizer {
     my ( $timeout ) = @_;
 
-    if ( $DEBUG > 3 ) {
-       print STDERR "\n\npicking from $random_redirector...\n\n";
+    if ( $verbose > 3 ) {
+       print STDERR "\n\n$progname: picking from $random_redirector...\n\n";
     }
 
     my ( $base, $body ) = get_document ($random_redirector, undef, $timeout);
     }
 
     my ( $base, $body ) = get_document ($random_redirector, undef, $timeout);
@@ -410,9 +517,9 @@ sub pick_from_url_randomizer {
     my $img = pick_image_from_body ($base, $body);
 
     if ($img) {
     my $img = pick_image_from_body ($base, $body);
 
     if ($img) {
-        return ($base, $img);
+        return ($base, $img, "yahoo");
     } else {
     } else {
-        return undef;
+        return ();
     }
 }
 
     }
 }
 
@@ -449,10 +556,10 @@ sub random_word {
 
 # Using the image-randomizer, picks a random image on a random page, and
 # returns two URLs: the page containing the image, and the image.
 
 # Using the image-randomizer, picks a random image on a random page, and
 # returns two URLs: the page containing the image, and the image.
-# Returns undef if nothing found this time.
+# Returns () if nothing found this time.
 #
 sub pick_from_image_randomizer {
 #
 sub pick_from_image_randomizer {
-    my ( $timeout ) = @_;
+    my ( $timeout, $which ) = @_;
 
     my $words = random_word;
     $words .= "%20" . random_word;
 
     my $words = random_word;
     $words .= "%20" . random_word;
@@ -460,96 +567,195 @@ sub pick_from_image_randomizer {
     $words .= "%20" . random_word;
     $words .= "%20" . random_word;
 
     $words .= "%20" . random_word;
     $words .= "%20" . random_word;
 
-    my $search_url = $image_randomizer . $words;
+    my $search_url = ($which == 0 ? $image_randomizer_1 :
+                      $which == 1 ? $image_randomizer_2 :
+                      $image_randomizer_3) .
+        $words;
+
+    # Pick a random search-result page instead of always taking the first.
+    # This assumes there are at least 10 pages...
+    if ($which == 0) {
+        $search_url .= "&pgno=" . (int(rand(9)) + 1);
+    } elsif ($which == 2) {
+        $search_url .= "&stq=" . (10 * (int(rand(9)) + 1));
+    }
 
 
-    if ( $DEBUG > 3 ) {
-        $_ = $words; s/%20/ /g; print STDERR "search words: $_\n";
+    if ( $verbose > 3 ) {
+        $_ = $words; s/%20/ /g; print STDERR "$progname: search words: $_\n";
     }
 
     }
 
-    if ( $DEBUG > 3 ) {
-       print STDERR "\n\npicking from $search_url\n";
+    if ( $verbose > 3 ) {
+       print STDERR "\n\n$progname: picking from $search_url\n";
     }
 
     my $start = time;
     my ( $base, $body ) = get_document ($search_url, undef, $timeout);
     if (defined ($timeout)) {
         $timeout -= (time - $start);
     }
 
     my $start = time;
     my ( $base, $body ) = get_document ($search_url, undef, $timeout);
     if (defined ($timeout)) {
         $timeout -= (time - $start);
-        return undef if ($timeout <= 0);
+        return () if ($timeout <= 0);
     }
 
     }
 
-    return undef if (! $body);
+    return () if (! $body);
 
 
     my @subpages;
     my $skipped = 0;
 
 
 
     my @subpages;
     my $skipped = 0;
 
+    my $search_count = "?";
+    if ($which == 0 &&
+        $body =~ m@found (approximately |about )?(<B>)?(\d+)(</B>)? image@) {
+        $search_count = $3;
+    } elsif ($which == 1 && $body =~ m@<NOBR>((\d{1,3})(,\d{3})*)&nbsp;@i) {
+        $search_count = $1;
+    } elsif ($which == 2 && $body =~ m@found ((\d{1,3})(,\d{3})*|\d+) Web p@) {
+        $search_count = $1;
+    }
+    1 while ($search_count =~ s/^(\d+)(\d{3})/$1,$2/);
+
+    my $length = length($body);
+    my $href_count = 0;
+
     $_ = $body;
     $_ = $body;
+    s/[\r\n\t ]+/ /g;
+
+    s/Result Pages:.*$//;               # trim off page footer
+
     s/(<A )/\n$1/gi;
     foreach (split(/\n/)) {
     s/(<A )/\n$1/gi;
     foreach (split(/\n/)) {
+        $href_count++;
+        my ($u) = m@<A\s.*\bHREF\s*=\s*([^>]+)>@i;
+        next unless $u;
+        if ($u =~ m/^\"([^\"]*)\"/) { $u = $1; }   # quoted string
+        elsif ($u =~ m/^([^\s]*)\s/) { $u = $1; }  # or token
+
+        if ($which == 1) {
+            # Kludge to decode HotBot pages
+            next unless ($u =~ m@/director\.asp\?target=(http%3A[^&>]+)@);
+            $u = url_decode($1);
+        }
 
 
-        if ( m@<A HREF=([^>]+)><IMG SRC=http://image\.altavista\.com@i ) {
+        next unless ($u =~ m@^http://@i);  # skip non-http and relative urls.
 
 
-            my $u = $1;
-            if (m/^"(.*)"$/) { $u = $1; }
+        next if ($u =~ m@[/.]altavista\.com@i);  # skip altavista builtins
+        next if ($u =~ m@[/.]digital\.com@i);
+        next if ($u =~ m@[/.]doubleclick\.net@i);
 
 
-            if (m@\.corbis\.com/@) {
-                $skipped = 1;
-                if ( $DEBUG > 3 ) {
-                    print STDERR "skipping corbis URL: $_\n";
-                }
-                next;
-            } elsif ( $DEBUG > 3 ) {
-                print STDERR "sub-page: $1\n";
+        if ($which == 0 && $u =~ m@[/.]corbis\.com/@) {
+            $skipped = 1;
+            if ( $verbose > 3 ) {
+                print STDERR "$progname: skipping corbis URL: $u\n";
             }
             }
+            next;
 
 
-            $subpages[++$#subpages] = $u;
+        } elsif ( $rejected_urls{$u} ) {
+            if ( $verbose > 3 ) {
+                my $L = $rejected_urls{$u};
+                print STDERR "$progname: pre-rejecting sub-page: $u\n";
+            }
+            next;
+
+        } elsif ( $verbose > 3 ) {
+            print STDERR "$progname: sub-page: $u\n";
         }
         }
+
+        $subpages[++$#subpages] = $u;
     }
 
     }
 
-    if ( $#subpages <= 0 ) {
-        if (!$skipped) {
-            print STDERR "Found nothing on $base\n";
+    if ( $#subpages < 0 ) {
+        if (!$skipped && $verbose > 1) {
+            print STDERR "$progname: found nothing on $base " .
+                "($length bytes, $href_count links).\n";
         }
         }
-       return undef;
+       return ();
     }
 
     # pick a random element of the table
     }
 
     # pick a random element of the table
-    my $i = ((rand() * 99999) % $#subpages);
+    my $i = ((rand() * 99999) % ($#subpages + 1));
     my $subpage = $subpages[$i];
 
     my $subpage = $subpages[$i];
 
-    if ( $DEBUG > 3 ) {
-       print STDERR "picked page $subpage\n";
+    if ( $verbose > 3 ) {
+       print STDERR "$progname: picked page $subpage\n";
     }
 
 
 
     my ( $base2, $body2 ) = get_document ($subpage, $base, $timeout);
 
     }
 
 
 
     my ( $base2, $body2 ) = get_document ($subpage, $base, $timeout);
 
-    return undef if (!$base2 || !body2);
+    return () if (!$base2 || !$body2);
 
     my $img = pick_image_from_body ($base2, $body2);
 
     if ($img) {
 
     my $img = pick_image_from_body ($base2, $body2);
 
     if ($img) {
-        return ($base2, $img);
+        return ($base2, $img,
+                ($which == 0 ? "imagevista" :
+                 $which == 1 ? "hotbot" : "altavista") .
+                "/$search_count");
     } else {
     } else {
-        return undef;
+        return ();
     }
 }
 
 
 # Picks a random image on a random page, and returns two URLs:
 # the page containing the image, and the image. 
     }
 }
 
 
 # Picks a random image on a random page, and returns two URLs:
 # the page containing the image, and the image. 
-# Returns undef if nothing found this time.
+# Returns () if nothing found this time.
 # Uses the url-randomizer 1 time in 5, else the image randomizer.
 #
 # Uses the url-randomizer 1 time in 5, else the image randomizer.
 #
+my $total_0 = 0;
+my $total_1 = 0;
+my $total_2 = 0;
+my $total_3 = 0;
+my $count_0 = 0;
+my $count_1 = 0;
+my $count_2 = 0;
+my $count_3 = 0;
+
 sub pick_image {
     my ( $timeout ) = @_;
 
 sub pick_image {
     my ( $timeout ) = @_;
 
-    if (int(rand 5) == 0) {
-        return pick_from_url_randomizer ($timeout);
+    my $r = int(rand(100));
+    my ($base, $img, $source, $total, $count);
+
+    if ($r < 20) {
+        ($base, $img, $source) = pick_from_url_randomizer ($timeout);
+        $total = ++$total_0;
+        $count = ++$count_0 if $img;
+
+    } elsif ($r < 60) {
+        ($base, $img, $source) = pick_from_image_randomizer ($timeout, 0);
+        $total = ++$total_1;
+        $count = ++$count_1 if $img;
+
+#    } elsif ($r < 80) {
+#        # HotBot sucks: 98% of the time, it says "no pages match your
+#        # search", and then if I load the URL again by hand, it works.
+#        # I don't understand what's going wrong here, but we're not getting
+#        # any good data back from them, so forget it for now.
+#
+#        ($base, $img, $source) = pick_from_image_randomizer ($timeout, 1);
+#        $total = ++$total_2;
+#        $count = ++$count_2 if $img;
+
     } else {
     } else {
-        return pick_from_image_randomizer ($timeout);
+        ($base, $img, $source) = pick_from_image_randomizer ($timeout, 2);
+        $total = ++$total_3;
+        $count = ++$count_3 if $img;
+    }
+
+    if ($source && $total > 0) {
+        $source .= " " . int(($count / $total) * 100) . "%";
     }
     }
+    return ($base, $img, $source);
+}
+
+
+# Does %-decoding.
+#
+sub url_decode {
+    ($_) = @_;
+    tr/+/ /;
+    s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
+    return $_;
 }
 
 
 }
 
 
@@ -559,7 +765,7 @@ sub gif_size {
     my ($body) = @_;
     my $type = substr($body, 0, 6);
     my $s;
     my ($body) = @_;
     my $type = substr($body, 0, 6);
     my $s;
-    return undef unless ($type =~ /GIF8[7,9]a/);
+    return () unless ($type =~ /GIF8[7,9]a/);
     $s = substr ($body, 6, 10);
     my ($a,$b,$c,$d) = unpack ("C"x4, $s);
     return (($b<<8|$a), ($d<<8|$c));
     $s = substr ($body, 6, 10);
     my ($a,$b,$c,$d) = unpack ("C"x4, $s);
     return (($b<<8|$a), ($d<<8|$c));
@@ -572,9 +778,9 @@ sub jpeg_size {
     my $i = 0;
     my $L = length($body);
     
     my $i = 0;
     my $L = length($body);
     
-    $c1 = substr($body, $i, 1); $i++;
-    $c2 = substr($body, $i, 1); $i++;
-    return undef unless (ord($c1) == 0xFF && ord($c2) == 0xD8);
+    my $c1 = substr($body, $i, 1); $i++;
+    my $c2 = substr($body, $i, 1); $i++;
+    return () unless (ord($c1) == 0xFF && ord($c2) == 0xD8);
 
     my $ch = "0";
     while (ord($ch) != 0xDA && $i < $L) {
 
     my $ch = "0";
     while (ord($ch) != 0xDA && $i < $L) {
@@ -605,11 +811,11 @@ sub jpeg_size {
             my $s = substr($body, $i, 2); $i += 2;
             my ($c1, $c2) = unpack ("C"x2, $s); 
             my $length = ($c1 << 8) | $c2;
             my $s = substr($body, $i, 2); $i += 2;
             my ($c1, $c2) = unpack ("C"x2, $s); 
             my $length = ($c1 << 8) | $c2;
-            return undef if ($length < 2);
+            return () if ($length < 2);
             $i += $length-2;
         }
     }
             $i += $length-2;
         }
     }
-    return undef;
+    return ();
 }
 
 # Given the raw body of a GIF or JPEG document, returns the dimensions of
 }
 
 # Given the raw body of a GIF or JPEG document, returns the dimensions of
@@ -635,781 +841,169 @@ sub which {
     return undef;
 }
 
     return undef;
 }
 
-##############################################################################
-#
-# Running as a CGI
-#
-##############################################################################
-
-my $body_tag = "<BODY BGCOLOR=\"#000000\" TEXT=\"#DDFFDD\"\n" .
-    "      LINK=\"#00EEEE\" VLINK=\"#EEEE00\" ALINK=\"#FF0000\">\n";
-
-my $html_document =
-    ("" .
-     "<HTML>\n" .
-     "<HEAD>\n" .
-     " <TITLE>WebCollage</TITLE>\n" .
-     "\n" .
-     "</HEAD>\n" .
-     $body_tag .
-     "\n" .
-     "<CENTER><FONT SIZE=1><BR></FONT>" .
-     "<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=720>\n" .
-     " <TR>\n" .
-     "  <TD ALIGN=CENTER VALIGN=TOP NOWRAP>\n" .
-     "   <FONT SIZE=\"+3\"><B>WebCollage: </B></FONT>\n" .
-     "   <FONT SIZE=\"-1\"><BR><B>by\n" .
-     "     <A HREF=\"http://www.jwz.org/\">Jamie Zawinski</A></B>\n" .
-     "  </TD>\n" .
-     "  <TD ALIGN=LEFT VALIGN=TOP>\n" .
-     "\n" .
-     "   <P><FONT SIZE=\"+3\"><B>Exterminate All Rational Thought.\n" .
-     "   </B></FONT>\n" .
-     "   <BR>This program creates collages out of random images\n" .
-     "   found on the Web.\n" .
-     "   <P>More images are being added to the\n" .
-     "   collage now: please wait for the image below to load.\n" .
-     "   This will take a minute or two, since it has to contact\n" .
-     "   other web sites to retrieve the images before it can construct\n" .
-     "   the collage.  Once the image below is loaded, you can reload\n" .
-     "   this page to do it again.\n" .
-     "   <P>If you enjoy this, you might also enjoy\n" .
-     "   <A HREF=\"http://www.jwz.org/dadadodo/\">DadaDodo</A>.\n" .
-     "   WebCollage also works as a screen saver, for those of you\n" .
-     "   using Unix: it is included with the\n" .
-     "   <A HREF=\"http://www.jwz.org/xscreensaver/\">XScreenSaver</A>\n" .
-     "   package.<P>\n" .
-     "  </TD>\n" .
-     " </TR>\n" .
-     " <TR>\n" .
-     "  <TD COLSPAN=2 VALIGN=TOP ALIGN=CENTER><TABLE \n" .
-     "      BORDER=2 WIDTH=%%WIDTH%% HEIGHT=%%HEIGHT%% \n" .
-     "      CELLPADDING=0 CELLSPACING=0>\n" .
-     "    <TR><TD BGCOLOR=\"#C0C0C0\">\n" .
-     "      %%MAP%%\n" .
-     "     <A NAME=\"#image\">\n" .
-     "     <IMG SRC=\"%%IMAGE%%\" BORDER=0 \n" .
-     "      WIDTH=%%WIDTH%% HEIGHT=%%HEIGHT%% \n" .
-     "      USEMAP=\"#collage\"></A></TD></TR>\n" .
-     "  </TABLE></TD>\n" .
-     " </TR>\n" .
-     "</TABLE>\n" .
-     "<P>\n" .
-     "</CENTER>\n");
-
-
-my @time_fmt_days = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
-my @time_fmt_months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun",
-                       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
-
-# Converts a time_t to a string acceptable to HTTP.
-#
-sub format_http_time {
-    my ($time) = @_;
-    my @t = gmtime($time);
-    my ($sec, $min, $hour, $mday, $mon, $year, $wday) = @t;
-    $year += 1900;
-    $wday = $time_fmt_days[$wday];
-    $mon = $time_fmt_months[$mon];
-    return sprintf("%s, %02d %s %d %02d:%02d:%02d GMT",
-                   $wday, $mday, $mon, $year, $hour, $min, $sec);
-}
-
 
 
-
-# Parses exactly the time format that HTTP requires, no more, no less.
-#
-sub parse_http_time {
+# Like rand(), but chooses numbers with a bell curve distribution.
+sub bellrand {
     ($_) = @_;
     ($_) = @_;
-
-    if (!m/^[SMTWF][a-z][a-z]+, (\d\d)[- ]([JFMAJSOND][a-z][a-z]+)[- ](\d\d\d?\d?)[- ](\d\d):(\d\d):(\d\d)( GMT)?$/o) {
-        return undef;
-    }
-
-    my @moy = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
-    @moy{@moy} = (1..12);
-
-    my $t = Time::Local::timegm($6, $5, $4, $1, $moy{$2}-1,
-                                ($3 < 100 ? $3 : $3-1900));
-    return ($t < 0 ? undef : $t);
-}
-
-
-# Given a modification time, returns a time_t to use as the expiration time
-# of both the HTML and the JPEG.
-#
-sub compute_expires_time {
-    my ($mod_time) = (@_);
-    my $now = time;
-    if ($mod_time < $now) { $mod_time = $now; }
-    return $mod_time + $max_age;
+    $_ = 1.0 unless defined($_);
+    $_ /= 3.0;
+    return (rand($_) + rand($_) + rand($_));
 }
 
 
 }
 
 
-# Parse the If-Modified-Since header, and write a response if appropriate.
-# If this returns 1, we're done.
+##############################################################################
 #
 #
-sub do_ifmod {
-    # see http://vancouver-webpages.com/proxy/log-tail.pl and
-    # http://mnot.cbd.net.au/cache_docs/ for clues about how to
-    # do cacheing properly with CGI-generated documents.
-    my ($mod_time) = (@_);
-    if ($ENV{HTTP_IF_MODIFIED_SINCE}) {
-        my $ims = $ENV{HTTP_IF_MODIFIED_SINCE};
-        $ims =~ s/;.*// ; # lose trailing "; length=3082"
-        $ims = parse_http_time($ims);
-        if ($ims && $mod_time <= $ims) {
-            print "Status: 304 Not Modified\n\n" ;
-            return 1;
-        }
-    }
-    return 0;
-}
-
-
-# Returns N urls of images (and the pages on which they were found.)
-# Unless there is a significant surplus of URLs in the $pending_file,
-# this will spend $url_generation_time seconds generating as many URLs
-# as it can.  The first N will be returned, and the rest will be left
-# in the file.
+# Generating a list of urls only
 #
 #
-sub get_image_urls {
-    my ($count) = @_;
-
-    my @urls;
-    my $body = "";
-    my $file_count = 0;
-
-    local *PEND;
-
-    # Open and lock the file (read/write.)
-    # rewind after locking, in case we had to wait for the lock.
-    #
-    open (PEND, "+<$pending_file") || die "couldn't open $pending_file: $!";
-
-    if ($DEBUG > 2) { print STDERR "jpeg: opened $pending_file\n"; }
-
-    my $flock_wait = time;
-    flock (PEND, LOCK_EX) || die "couldn't lock $pending_file: $!";
-    $flock_wait = (time - $flock_wait);
-
-    seek (PEND, 0, 0)     || die "couldn't rewind $pending_file: $!";
-
-    if ($DEBUG > 2) { print STDERR "jpeg: locked $pending_file\n"; }
-
-
-    # Take N URLs off the top, and leave the rest.
-    #
-    while (<PEND>) {
-        if (--$count >= 0) {
-            if ($DEBUG > 3) { print STDERR "  <   $_"; }
-            s/[\r\n]+$//;
-            $urls[++$#urls] = $_;
-        } else {
-            $body .= $_;
-            if ($DEBUG > 3) { print STDERR "  -   $_"; }
-            $file_count++;
-        }
-    }
-
-    # rewind and overwrite the file
-    seek (PEND, 0, 0)  || die "couldn't rewind $pending_file: $!";
-    truncate (PEND, 0) || die "couldn't truncate $pending_file: $!";
-    print PEND $body;
-
-
-    # If there are fewer than 3x as many URLs as we took left in the file,
-    # then generate as many URLs as we can in N seconds.  Take what we
-    # need from that, and append the rest to the file.  Note that we are
-    # still holding a lock on the file.
-    #
-    # Count the time spent waiting for flock as time spent gathering URLs.
-    # Because that means someone else was doing it.
-    #
-    $body = "";
-    if ($file_count < $count * 3) {
-        my $timeout = $url_generation_time - $flock_wait;
-        my $start = time;
-
-        while (1) {
-            last if ($timeout <= 0);
-
-            if ($DEBUG > 2) { print STDERR "time remaining: $timeout\n"; }
-            my ($base, $img) = pick_image ($timeout);
-
-            if ($img) {
-                $img  =~ s/ /%20/g;
-                $base =~ s/ /%20/g;
-                $_ = "$img $base";
-                if ($count-- >= 0) {
-                    if ($DEBUG > 3) { print STDERR "  <<   $img\n"; }
-                    $urls[++$#urls] = $_;
-                } else {
-                    if ($DEBUG > 3) { print STDERR "  >>   $img\n"; }
-                    print PEND "$_\n"; # append to file
-                    $file_count++;
-                }
-            }
+##############################################################################
 
 
-            my $now = time;
-            my $elapsed = $now - $start;
-            $timeout -= $elapsed;
-            $start = $now;
+sub url_only_output {
+    do {
+        my ($base, $img) = pick_image;
+        if ($img) {
+            $base =~ s/ /%20/g;
+            $img  =~ s/ /%20/g;
+            print "$img $base\n";
         }
         }
-    }
-
-    my $of = select(PEND); $| = 1; select($of);                # flush output
-    print PEND "";
-
-    flock (PEND, LOCK_UN) || die "couldn't unlock $pending_file: $!";
-    close (PEND)          || die "couldn't close $pending_file: $!";
-
-    if ($DEBUG > 2) {
-        print STDERR "jpeg: closed $pending_file; $file_count urls in file;" .
-            " returning $#urls.\n";
-    }
-
-    return @urls;
+    } while (1);
 }
 
 }
 
+##############################################################################
+#
+# Running as an xscreensaver module
+#
+##############################################################################
 
 
-sub cgi_reset_all_files {
-    foreach (@all_files) {
-        my $file = $_;
-        local *OUT;
-        open (OUT, "+<$file") || die "couldn't open $file: $!";
-        flock (OUT, LOCK_EX)  || die "couldn't lock $file: $!";
-        truncate (OUT, 0)     || die "couldn't truncate $file: $!";
-        flock (OUT, LOCK_UN)  || die "couldn't unlock $file: $!";
-        close (OUT)           || die "couldn't close $file: $!";
-    }
-
-    system "ppmmake '#000000' $img_width $img_height > $image_ppm" ||
-        die "failed to create blank $image_ppm file: $!";
-    system "cjpeg -progressive $image_ppm > $image_jpg" ||
-        die "failed to create blank $image_jpg file: $!";
+sub x_cleanup {
+    my ($sig) = @_;
+    if ($verbose > 0) { print STDERR "$progname: caught signal $sig.\n"; }
+    unlink $image_ppm, $image_tmp1, $image_tmp2;
+    exit 1;
 }
 
 
 }
 
 
-# Given the URL of an image and the page on which it was found, this will
-# load the image, and paste it at a random spot in $image_ppm and $img_jpg.
-# It will also update $map_file to contain the appropriate referer, and
-# will limit it to $max_map_entries lines.
+# Like system, but prints status about exit codes, and kills this process
+# with whatever signal killed the sub-process, if any.
 #
 #
-sub cgi_paste_image {
-    my ($img, $referer) = @_;
-
-    my ( $base, $body ) = get_document ($img, $referer);
-    return if (!$base || !$body);
-
-    my ($iw, $ih) = image_size ($body);
-    return if (!$iw || !$ih);
-
-    if ($DEBUG > 2) { print STDERR "got $base ($iw x $ih)\n"; }
-
-    my $cmd;
-
-    if ($base =~ m/\.gif$/i) {
-        $cmd = "giftopnm";
-    } else {
-        $cmd = "djpeg";
-    }
-
-    if ($iw > $img_width || $ih > $img_height) {
-        while ($iw > $img_width || $ih > $img_height) {
-            $iw = int($iw / 2);
-            $ih = int($ih / 2);
-        }
-        $cmd .= " | pnmscale -xysize $iw $ih";
-    }
-
-    my $x = int (rand() * ($img_width - $iw));
-    my $y = int (rand() * ($img_height - $ih));
-
-    $cmd .= " | pnmpaste - $x $y $image_ppm";
-
-
-    local *MAP;
-    local *PIPE_OUT;
-
-    # Open and lock the map (read/write.)
-    # rewind after locking, in case we had to wait for the lock.
-    # This lock doubles as our lock on the image file itself.
-    #
-    open (MAP, "+<$map_file") || die "couldn't open $map_file: $!";
-
-    if ($DEBUG > 2) { print STDERR "jpeg: opened $map_file\n"; }
-
-    flock (MAP, LOCK_EX) || die "couldn't lock $map_file: $!";
-    seek (MAP, 0, 0)     || die "couldn't rewind $map_file: $!";
-
-    if ($DEBUG > 2) { print STDERR "jpeg: locked $map_file\n"; }
-
-    # Read in the first hundred lines of the map file.
-    #
-    my $map = "";
-    my $count = 0;
-    while (<MAP>) {
-        last if ($count++ > $max_map_entries);
-        $map .= $_;
+sub nontrapping_system {
+    $! = 0;
+    
+    if ($verbose > 1) {
+        $_ = join(" ", @_);
+        s/\"[^\"]+\"/\"...\"/g;
+        print STDERR "$progname: executing \"$_\"\n";
     }
 
     }
 
-    # Add this entry to the front of the map data.
-    #
-    $map = "$x $y $iw $ih $referer\n" . $map;
+    my $rc = system @_;
 
 
-
-    # Ensure that the $image_ppm file exists and has a ppm in it.
-    #
-    my $ppm_size = $img_width * $img_height * 3 * 2;
-    my $s = (stat($image_ppm))[7];
-    if ($s < $ppm_size) {
-
-        if ( $DEBUG ) {
-            print STDERR "$image_ppm is $s bytes;" .
-                " should be at least $ppm_size\n";
-            print STDERR "resetting everything.";
-            cgi_reset_all_files();
+    if ($rc == 0) {
+        if ($verbose > 1) {
+            print STDERR "$progname: subproc exited normally.\n";
         }
         }
-    }
-
-    # Paste the bits into the image.  Note that the map file is still locked.
-    #
-    local *TMP;
-    open (TMP, ">$image_tmp") || die "couldn't open $image_tmp: $!";
-    close (TMP);
-
-    if (! $DEBUG ) {
-        $cmd = "( $cmd ) 2>/dev/null";
-    }
-
-    $cmd .= " > $image_tmp";
-    if ($DEBUG > 2) { print STDERR "executing $cmd\n"; }
-
-    if (open(PIPE_OUT, "| $cmd")) {
-        print PIPE_OUT $body;
-        close(PIPE_OUT);
-
-        if ($DEBUG > 2) { system "ls -ldF $image_tmp >&2"; }
-
-        my @tmp_stat = stat($image_tmp);
-        if (@tmp_stat && $tmp_stat[7] < 200) {
-#            unlink ($image_tmp) || die "couldn't unlink $image_tmp: $!";
-            open (OUT, ">$image_tmp") || die "$image_tmp unwritable: $!";
-            close (OUT);
-            if ($DEBUG > 2) { print STDERR "FAILED writing $image_ppm\n"; }
-        } else {
-#            rename ($image_tmp, $image_ppm) ||
-#                die "couldn't rename $image_tmp to $image_ppm: $!";
-            local *IN;
-            local *OUT;
-            open (IN, "+<$image_tmp") || die "$image_tmp unreadable: $!";
-            open (OUT, ">$image_ppm") || die "$image_ppm unwritable: $!";
-            while (<IN>) { print OUT $_; }
-            truncate (IN, 0) || die "couldn't truncate $image_tmp: $!";
-            close (IN);
-            close (OUT) || die "couldn't write $image_ppm: $!";
-            if ($DEBUG > 2) { print STDERR "wrote $image_ppm\n"; }
-
-
-            # Now convert the PPM to a JPEG.
-            #
-            system "cjpeg -progressive $image_ppm > $image_tmp 2>/dev/null";
-
-            @tmp_stat = stat($image_tmp);
-            if (@tmp_stat && $tmp_stat[7] < 200) {
-#                unlink ($image_tmp) || die "couldn't unlink $image_tmp: $!";
-                open (OUT, ">$image_tmp") || die "$image_tmp unwritable: $!";
-                close (OUT);
-                if ($DEBUG > 2) { print STDERR "FAILED writing $image_jpg\n"; }
-            } else {
-#                rename ($image_tmp, $image_ppm) ||
-#                    die "couldn't rename $image_tmp to $image_ppm: $!";
-                open (IN, "+<$image_tmp") || die "$image_tmp unreadable: $!";
-                open (OUT, ">$image_jpg") || die "$image_jpg unwritable: $!";
-                while (<IN>) { print OUT $_; }
-                truncate (IN, 0) || die "couldn't truncate $image_tmp: $!";
-                close (IN);
-                close (OUT) || die "couldn't write $image_jpg: $!";
-                if ($DEBUG > 2) { print STDERR "wrote $image_jpg\n"; }
+    } elsif (($rc & 0xff) == 0) {
+        $rc >>= 8;
+        if ($verbose) {
+            print "$progname: subproc exited with status $rc.\n";
+        }
+    } else {
+        if ($rc & 0x80) {
+            if ($verbose) {
+                print "$progname: subproc dumped core.\n";
             }
             }
+            $rc &= ~0x80;
         }
         }
-
-        # Overwrite the map data.
-        #
-        seek (MAP, 0, 0)  || die "couldn't rewind $map_file: $!";
-        truncate (MAP, 0) || die "couldn't truncate $map_file: $!";
-        print MAP $map;
-    }
-
-    my $of = select(MAP); $| = 1; select($of);         # flush output
-    print MAP "";
-
-    flock (MAP, LOCK_UN) || die "couldn't unlock $map_file: $!";
-    close (MAP)          || die "couldn't close $map_file: $!";
-
-    if ($DEBUG > 2) { print STDERR "jpeg: closed $map_file\n"; }
-}
-
-
-sub cgi_generate_image {
-
-    $SIG{PIPE} = 'IGNORE';
-
-    my @urls = get_image_urls ($pastes_per_load);
-    my $end_time = time + $image_retrieval_time;
-
-    if ($DEBUG > 2) {
-        print STDERR "loading $#urls images\n";
-    }
-
-    foreach (@urls) {
-        my ($img, $referer) = m/^([^ ]+) ([^ ]+)/;
-        if ($img) {
-            cgi_paste_image ($img, $referer);
+        if ($verbose) {
+            print "$progname: subproc died with signal $rc.\n";
         }
         }
-        last if (time > $end_time);
+        # die that way ourselves.
+        kill $rc, $$;
     }
     }
-}
-
-
-sub cgi_sanity_check {
-    my $error = undef;
-    foreach (@all_files) {
-        if (! -e $_) { $error = "$_ does not exist.\n"; }
-        elsif (! -r $_) { $error = "$_ is unreadable.\n"; }
-        elsif (! -w $_) { $error = "$_ is unwritable.\n"; }
-        last if ($error);
-    }
-
-    return unless $error;
-
-    print "Content-Type: text/html\n";
-    print "\n\n<TITLE>Error</TITLE>$body_tag<H1>Error</H1>";
-    print POSIX::getcwd() . "/" . $error . "<P>\n";
-
-    $_ = join("</TT>, <TT>", @all_files);
-    s/,([^,]*)$/, and$1/;
 
 
-    print "Each of the files: <TT>$_</TT>\n";
-    print " must exist and be readable and writable by the httpd process\n";
-    print "(which probably means they must be globally readable and\n";
-    print "writable, since on most systems, CGI scripts run as the\n";
-    print "user <I>nobody</I>.)\n<P>\n";
-
-    exit (0);
+    return $rc;
 }
 
 
 }
 
 
-# Write the encapsulating HTML document and associated HTTP headers.
-# This is fast -- it just writes out the wrapper document corresponding
-# to the data currently on disk.  It is the loading of the sub-image
-# that does the real work.
+# Given the URL of a GIF or JPEG image, and the body of that image, writes a
+# PPM to the given output file.  Returns the width/height of the image if 
+# successful.
 #
 #
-sub cgi_emit_html_document {
-
-    cgi_sanity_check;
-
-    my $map_file_date;
-    my $doc = $html_document;
-
-    my $w2 = int ($img_width  * $scale);
-    my $h2 = int ($img_height * $scale);
-    $doc =~ s/%%WIDTH%%/$w2/g;
-    $doc =~ s/%%HEIGHT%%/$h2/g;
-
-    local *MAP;
-    open (MAP, "<$map_file") || die "couldn't open $map_file: $!";
-    if ($DEBUG > 2) { print STDERR "html: opened $map_file\n"; }
-
-    flock (MAP, LOCK_SH) || die "couldn't lock $map_file: $!";
-    seek (MAP, 0, 0)     || die "couldn't rewind $map_file: $!";
-    if ($DEBUG > 2) { print STDERR "html: locked $map_file\n"; }
-
-    $map_file_date = (stat(MAP))[9];
-
-    my $map = "<MAP NAME=\"collage\">\n";
-    while (<MAP>) {
-        my ($x, $y, $w, $h, $url) =
-            m/^([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) (.*)$/;
-        if ($w && $h) {
-            $x = int($x * $scale);
-            $y = int($y * $scale);
-            $w = int($w * $scale);
-            $h = int($h * $scale);
-
-            # protect against URLs that contain <, >, or ".
-            $url =~ s/([<>\"])/uc sprintf("%%%02X",ord($1))/eg;
-
-            my $x2 = $x + $w;
-            my $y2 = $y + $h;
-            $map .=
-                "<AREA SHAPE=RECT COORDS=\"$x,$y,$x2,$y2\" HREF=\"$url\">\n";
-        }
-    }
-    $map .= "</MAP>";
-    flock (MAP, LOCK_UN) || die "couldn't unlock $map_file: $!";
-    close (MAP)          || die "couldn't close $map_file: $!";
-
-    if ($DEBUG > 2) { print STDERR "html: closed $map_file\n"; }
-
-    $doc =~ s/%%MAP%%/$map/g;
-
-    my $img_name = "current";
+sub image_to_pnm {
+    my ($url, $body, $output) = @_;
+    my ($cmd, $cmd2, $w, $h);
 
 
-    $doc =~ s@%%IMAGE%%@images/$img_name.jpg@g;
-
-
-    my $mod_time = $map_file_date;
-    if ($script_date > $mod_time) { $mod_time = $script_date; }
-
-    if (do_ifmod($mod_time)) {
-        return;
-    }
-
-    my $exp = compute_expires_time($mod_time);
-
-    print "Content-Type: text/html\n";
-    print "Content-Length: " . length($doc) . "\n";
-    print "Last-Modified: " . format_http_time($mod_time) . "\n";
-
-    # This is a suggestion to consider the object invalid after the given
-    # date.  This is sometimes ignored.
-    #
-    print "Expires: " . format_http_time($exp) . "\n";
-
-    # This may or may not cause a cacheing proxy to pass this stuff along.
-    # It's not standardized, but was historically used for... something.
-    print "Pragma: no-cache\n";
-
-    # This says the same thing as the Expires header, but it is a stronger
-    # assertion that we're serious and should be listened to.
-    #
-    my $age = $exp - time;
-    print "Cache-Control: max-age=$age, must-revalidate\n";
-
-    print "\n";
-    print $doc;
-}
-
-
-# Write the interior JPEG document and associated HTTP headers.
-#
-sub cgi_emit_jpeg_document {
-
-    my $image_data = "";
-    my $jpg_file_date;
-    my $do_ims = 0;
-
-    # The map file is the means by which we hold write-locks on the image
-    # file.  So first obtain the lock on that file.
-    #
-    local *MAP;
-    open (MAP, "+<$map_file") || die "couldn't open $map_file: $!";
-
-    if ($DEBUG > 2) { print STDERR "jpeg: opened $map_file\n"; }
-    flock (MAP, LOCK_SH) || die "couldn't lock $map_file: $!";
-    if ($DEBUG > 2) { print STDERR "jpeg: locked $map_file\n"; }
-
-    # Now we have exclusive access to the image file.  Read it.
-    #
-    local *IMG;
-    open (IMG, "<$image_jpg") || die "couldn't open $image_jpg: $!";
-
-    $jpg_file_date = (stat(IMG))[9];
-
-    if (do_ifmod($jpg_file_date)) {
-        $do_ims = 1;
-        if ($DEBUG > 2) {
-            my $ims = $ENV{HTTP_IF_MODIFIED_SINCE};
-            $ims =~ s/;.*//;
-            print STDERR "not-modified-since " .
-                localtime(parse_http_time($ims)) . "\n";
-            print STDERR "jpg date: " . localtime($jpg_file_date) . "\n";
-        }
+    if ((@_ = gif_size ($body))) {
+        ($w, $h) = @_;
+        $cmd = "giftopnm";
+    } elsif ((@_ = jpeg_size ($body))) {
+        ($w, $h) = @_;
+        $cmd = "djpeg";
+    } else {
+        return ();
     }
 
     }
 
-    if (!$do_ims) {
-        while (<IMG>) { $image_data .= $_; }
+    $cmd2 = "exec $cmd";        # yes, this really is necessary.  if we don't
+                                # do this, the process doesn't die properly.
+    if ($verbose == 0) {
+        $cmd2 .= " 2>/dev/null";
     }
     }
-    close (IMG) || die "couldn't close $image_jpg: $!";
-
-    # Now free the lock so that others can write to the file.
-    #
-    flock (MAP, LOCK_UN) || die "couldn't unlock $map_file: $!";
-    close (MAP)          || die "couldn't close $map_file: $!";
-    if ($DEBUG > 2) { print STDERR "jpeg: closed $map_file\n"; }
-
-    return if ($do_ims);
 
 
-
-    # At this point, we have the image data we will be returning.
-    # However, don't return it yet -- first go off and generate the
-    # *next* image, then we can return *this* one.  If we don't do it
-    # in this order, people will jump the gun hitting reload, and no
-    # image updates will happen.
-    #
-    my $type = "image/jpeg";
-    my $mod_time = $jpg_file_date;
-    if ($script_date > $mod_time) { $mod_time = $script_date; }
-
-    print "Last-Modified: " . format_http_time($mod_time) . "\n";
-    print "Expires: " . format_http_time(compute_expires_time($mod_time))
-        . "\n";
-    print "Content-Type: $type\n";
-    print "Content-Length: " . length($image_data) . "\n";
-    print "\n";
-
-    # Now, before returning the image data, go catatonic for a minute
-    # while we load some URLs and make the next image.
+    # There exist corrupted GIF and JPEG files that can make giftopnm and
+    # djpeg lose their minds and go into a loop.  So this gives those programs
+    # a small timeout -- if they don't complete in time, kill them.
     #
     #
-    cgi_generate_image;
-
-    # Done setting up for next time -- now finish loading.
-    #
-    print $image_data;
-    $image_data = undef;
-}
-
-
-# Write the source code of this script as a text/plain document.
-#
-sub cgi_emit_source_document {
-    my $mod_time = $script_date;
-
-    if (do_ifmod($mod_time)) {
-        return;
-    }
-
-    print "Content-Type: text/plain\n";
-    print "Last-Modified: " . format_http_time($mod_time) . "\n";
-    print "\n";
-    open (IN, "<$argv0") || die "couldn't open $argv0: $!";
-    while (<IN>) {
-        print;
-    }
-    close (IN);
-}
-
-
-# Parse the various environment variables to decide how we were invoked,
-# and then do something about it.
-#
-sub cgi_main {
-
-    $DEBUG=4;
-
-    $ENV{PATH} .= ":/usr/local/bin";
-
-    # make sure the various programs we execute exist, right up front.
-    foreach ("ppmmake", "cjpeg", "djpeg", "giftopnm", "pnmpaste", "pnmscale") {
-        if (!which ($_)) {
-            print "Content-Type: text/html\n";
-            print "\n\n<TITLE>Error</TITLE>$body_tag<H1>Error</H1>";
-            print "The <TT>$_</TT> program was not found on \$PATH.<BR>\n";
-
-            my $p = $ENV{PATH};
-            $p =~ s/%/%25/g; $p =~ s/\&/%26/g;
-            $p =~ s/</%3C/g; $p =~ s/>/%3E/g;
-            $p =~ s/:/:<WBR>/g;
-            print "\$PATH is: <TT>$p</TT><P>\n";
-            exit (0);
-        }
-    }
-
-    $script_date = (stat($argv0))[9];
-
-    print "Blat: Foop\n";
-
-    if ($ENV{REQUEST_METHOD} &&
-        $ENV{REQUEST_METHOD} ne "GET" &&
-        $ENV{REQUEST_METHOD} ne "HEAD" ) {
-        print "Content-Type: text/html\n";
-        print "\n\n<TITLE>Error</TITLE>$body_tag<H1>Error</H1>";
-        $_ = $ENV{REQUEST_METHOD};
-        print "bad request method: <TT>$_</TT>\n";
-        exit (0);
-
-    } elsif ( $ENV{QUERY_STRING} ) {
-        if ( $ENV{QUERY_STRING} eq "reset" ) {
-            cgi_reset_all_files;
+    my $pid;
+    @_ = eval {
+        my $timed_out;
 
 
-            print "Content-Type: text/html\n";
-            print "\n\n<TITLE>Collage Reset</TITLE>";
-            print "$body_tag<H1>Collage Reset</H1><P>\n";
-            exit (0);
+        local $SIG{ALRM}  = sub {
+            if ($verbose > 0) {
+                print STDERR "$progname: timed out ($cvt_timeout) for " .
+                    "$cmd on \"$url\" in pid $pid\n";
+            }
+            kill ('TERM', $pid) if ($pid);
+            $timed_out = 1;
+        };
+
+        if (($pid = open(PIPE, "| $cmd2 > $output"))) {
+            $timed_out = 0;
+            alarm $cvt_timeout;
+            print PIPE $body;
+            close PIPE;
+
+            if ($verbose > 3) { print STDERR "$progname: awaiting $pid\n"; }
+            waitpid ($pid, 0);
+            if ($verbose > 3) { print STDERR "$progname: $pid completed\n"; }
+
+
+            my $size = (stat($output))[7];
+            if ($size < 5) {
+                if ($verbose) {
+                    print STDERR "$progname: $cmd on ${w}x$h \"$url\" failed" .
+                        " ($size bytes)\n";
+                }
+                return ();
+            }
 
 
+            if ($verbose > 1) {
+                print STDERR "$progname: created ${w}x$h $output ($cmd)\n";
+            }
+            return ($w, $h);
         } else {
         } else {
-            print "Content-Type: text/html\n";
-            print "\n\n<TITLE>Error</TITLE>$body_tag<H1>Error</H1>";
-            $_ = $ENV{QUERY_STRING};
-            print "malformed URL: <TT>$_</TT>\n";
-            exit (0);
+            print STDERR "$progname: $cmd failed: $!\n";
+            return ();
         }
         }
-
-    } elsif ( !$ENV{PATH_INFO} || $ENV{PATH_INFO} eq "" ) {
-        # don't allow /webcollage as a URL -- force it to be /webcollage/
-        print "Status: 301 Moved Permanently\n";
-        print "Location: http://" .
-            ($ENV{HTTP_HOST} ? $ENV{HTTP_HOST} : 
-             $ENV{SERVER_NAME} ? $ENV{SERVER_NAME} : "???") .
-            ($ENV{REQUEST_URI} ? $ENV{REQUEST_URI} : "") .
-                "/\n\n";
-        exit (0);
-
-    } elsif ( $ENV{PATH_INFO} eq "/" ) {
-       cgi_emit_html_document;
-
-    } elsif ( $ENV{PATH_INFO} =~ m@^/images/[^/]+\.jpg$@ ) {
-        cgi_emit_jpeg_document;
-
-    } elsif ( $ENV{PATH_INFO} eq "/webcollage.pl" ) {
-        cgi_emit_source_document;
-
+    };
+    die if ($@ && $@ ne "alarm\n");       # propagate errors
+    if ($@) {
+        # timed out
+        return ();
     } else {
     } else {
-        print "Content-Type: text/html\n";
-        print "\n\n<TITLE>Error</TITLE>$body_tag<H1>Error</H1>";
-        $_ = $ENV{PATH_INFO};
-        print "malformed URL: <TT>$_</TT>\n";
-        exit (0);
+        # didn't
+        alarm 0;
+        return @_;
     }
 }
 
     }
 }
 
-
-##############################################################################
-#
-# Generating a list of urls only
-#
-##############################################################################
-
-sub url_only_output {
-    $| = 1;
-    do {
-        my ($base, $img) = pick_image;
-        if ($img) {
-            $base =~ s/ /%20/g;
-            $img  =~ s/ /%20/g;
-            print "$img $base\n";
-        }
-    } while (1);
-}
-
-##############################################################################
-#
-# Running as an xscreensaver module
-#
-##############################################################################
-
-my $image_tmp2;
-my $image_tmp3;
-
-sub x_cleanup {
-    if ($DEBUG > 0) { print STDERR "caught signal\n"; }
-    unlink $image_ppm, $image_tmp, $image_tmp2, $image_tmp3;
-    exit 1;
-}
-
-
 sub x_output {
 
     my $win_cmd = $ppm_to_root_window_cmd;
 sub x_output {
 
     my $win_cmd = $ppm_to_root_window_cmd;
@@ -1417,7 +1011,7 @@ sub x_output {
 
     # make sure the various programs we execute exist, right up front.
     foreach ("ppmmake", "giftopnm", "djpeg", "pnmpaste", "pnmscale",
 
     # make sure the various programs we execute exist, right up front.
     foreach ("ppmmake", "giftopnm", "djpeg", "pnmpaste", "pnmscale",
-             $win_cmd) {
+             "pnmcut", $win_cmd) {
         which ($_) || die "$progname: $_ not found on \$PATH.\n";
     }
 
         which ($_) || die "$progname: $_ not found on \$PATH.\n";
     }
 
@@ -1435,7 +1029,7 @@ sub x_output {
         $_ = "xdpyinfo";
         which ($_) || die "$progname: $_ not found on \$PATH.\n";
         $_ = `$_`;
         $_ = "xdpyinfo";
         which ($_) || die "$progname: $_ not found on \$PATH.\n";
         $_ = `$_`;
-        ($img_width, $img_height) = m/dimensions: *([0-9]+)x([0-9]+) /;
+        ($img_width, $img_height) = m/dimensions: *(\d+)x(\d+) /;
     }
 
     my $bgcolor = "#000000";
     }
 
     my $bgcolor = "#000000";
@@ -1448,7 +1042,8 @@ sub x_output {
             $bgimage = $background;
             
         } elsif (! $background =~ m@^[-a-z0-9 ]+$@i) {
             $bgimage = $background;
             
         } elsif (! $background =~ m@^[-a-z0-9 ]+$@i) {
-            print STDERR "not a color or readable file: $background\n";
+            print STDERR "$progname: not a color or readable file: " .
+                "$background\n";
             exit 1;
         } else {
             # default to assuming it's a color
             exit 1;
         } else {
             # default to assuming it's a color
@@ -1459,182 +1054,324 @@ sub x_output {
     # Create the sold-colored base image.
     #
     $_ = "ppmmake '$bgcolor' $img_width $img_height";
     # Create the sold-colored base image.
     #
     $_ = "ppmmake '$bgcolor' $img_width $img_height";
-    if ($DEBUG > 1) {
-        print STDERR "creating base image: $_\n";
+    if ($verbose > 1) {
+        print STDERR "$progname: creating base image: $_\n";
     }
     }
-    system "$_ > $image_ppm";
+    nontrapping_system "$_ > $image_ppm";
 
     # Paste the default background image in the middle of it.
     #
     if ($bgimage) {
         my ($iw, $ih);
 
     # Paste the default background image in the middle of it.
     #
     if ($bgimage) {
         my ($iw, $ih);
-        if (open(IMG, "<$bgimage")) {
-            $_ = <IMG>;
-            $_ = <IMG>;
-            ($iw, $ih) = m/^([0-9]+) ([0-9]+)$/;
-            close (IMG);
+
+        my $body = "";
+        local *IMG;
+        open(IMG, "<$bgimage") || die ("couldn't open $bgimage: $!\n");
+        my $cmd;
+        while (<IMG>) { $body .= $_; }
+        close (IMG);
+        if ((@_ = gif_size ($body))) {
+            ($iw, $ih) = @_;
+            $cmd = "giftopnm |";
+        } elsif ((@_ = jpeg_size ($body))) {
+            ($iw, $ih) = @_;
+            $cmd = "djpeg |";
+        } elsif ($body =~ "^P\d\n(\d+) (\d+)\n") {
+            $iw = $1;
+            $ih = $2;
+            $cmd = "";
+        } else {
+            die "$progname: $bgimage is not a GIF, JPEG, or PPM.\n";
         }
         }
-        my $x = int (($img_width - $iw) / 2);
+
+        my $x = int (($img_width  - $iw) / 2);
         my $y = int (($img_height - $ih) / 2);
         my $y = int (($img_height - $ih) / 2);
-        if ($DEBUG > 1) {
-            print STDERR "pasting $bgimage into base image at $x, $y\n";
+        if ($verbose > 1) {
+            print STDERR "$progname: pasting $bgimage (${iw}x$ih) into base ".
+                "image at $x,$y\n";
         }
         }
-        system "pnmpaste $bgimage $x $y $image_ppm > $image_tmp2" .
-            " && mv $image_tmp2 $image_ppm";
-    }
-
 
 
-    do {
-        my ($base, $img) = pick_image;
+        $cmd .= "pnmpaste - $x $y $image_ppm > $image_tmp1";
+        open (IMG, "| $cmd") || die ("running $cmd: $!\n");
+        print IMG $body;
+        close (IMG);
+        if ($verbose > 1) {
+            print STDERR "$progname: subproc exited normally.\n";
+        }
+        rename ($image_tmp1, $image_ppm) ||
+            die ("renaming $image_tmp1 to $image_ppm: $!\n");
+    }
 
 
-        my ($headers, $body);
+    while (1) {
+        my ($base, $img, $source) = pick_image();
         if ($img) {
         if ($img) {
-            ($headers, $body) = get_document ($img, $base);
+            my ($headers, $body) = get_document ($img, $base);
+            if ($body) {
+                handle_image ($base, $img, $body, $source);
+            }
         }
         }
+        unlink $image_tmp1, $image_tmp2;
+        sleep $delay;
+    }
+}
 
 
-        if ($body) {
+sub handle_image {
+    my ($base, $img, $body, $source) = @_;
 
 
-            if ($DEBUG > 0) {
-                print STDERR "got $img (" . length($body) . ")\n";
-            }
+    if ($verbose > 1) {
+        print STDERR "$progname: got $img (" . length($body) . ")\n";
+    }
 
 
-            my $cmd;
-            if ($img =~ m/\.gif/i) {
-                $cmd = "giftopnm";
-            } else {
-                $cmd = "djpeg";
-            }
+    my ($iw, $ih) = image_to_pnm ($img, $body, $image_tmp1);
+    return 0 unless ($iw && $ih);
 
 
-            if ($DEBUG == 0) {
-                $cmd .= " 2>/dev/null";
-            }
+    my $ow = $iw;  # used only for error messages
+    my $oh = $ih;
 
 
-            if (open(PIPE, "| $cmd > $image_tmp")) {
-                print PIPE $body;
-                close PIPE;
+    # don't just tack this onto the front of the pipeline -- we want it to
+    # be able to change the size of the input image.
+    #
+    if ($filter_cmd) {
+        if ($verbose > 1) {
+            print STDERR "$progname: running $filter_cmd\n";
+        }
 
 
-                if ($DEBUG > 1) {
-                    print STDERR "created $image_tmp ($cmd)\n";
-                }
+        my $rc = nontrapping_system "($filter_cmd) < $image_tmp1 >$image_tmp2";
+        if ($rc != 0) {
+            if ($verbose) {
+                print STDERR "$progname: failed command: \"$filter_cmd\"\n";
+                print STDERR "$progname: failed url: \"$img\" (${ow}x$oh)\n";
             }
             }
+            return;
+        }
+        rename ($image_tmp2, $image_tmp1);
+
+        # re-get the width/height in case the filter resized it.
+        local *IMG;
+        open(IMG, "<$image_tmp1") || return 0;
+        $_ = <IMG>;
+        $_ = <IMG>;
+        ($iw, $ih) = m/^(\d+) (\d+)$/;
+        close (IMG);
+        return 0 unless ($iw && $ih);
+    }
 
 
-            if (-s $image_tmp) {
+    my $target_w = $img_width;
+    my $target_h = $img_height;
 
 
-                if ($filter_cmd) {
-                    if ($DEBUG > 1) {
-                        print STDERR "running $filter_cmd\n";
-                    }
-                    system "($filter_cmd) < $image_tmp > $image_tmp3" .
-                        " && mv $image_tmp3 $image_tmp";
-                }
+    my $cmd = "";
 
 
-                my ($iw, $ih);
-                if (open(IMG, "<$image_tmp")) {
-                    $_ = <IMG>;
-                    $_ = <IMG>;
-                    ($iw, $ih) = m/^([0-9]+) ([0-9]+)$/;
-                    close (IMG);
-                }
 
 
-                if ($iw && $ih) {
+    # Usually scale the image to fit on the screen -- but sometimes scale it
+    # to fit on half or a quarter of the screen.  Note that we don't merely
+    # scale it to fit, we instead cut it in half until it fits -- that should
+    # give a wider distribution of sizes.
+    #
+    if (rand() < 0.3) { $target_w /= 2; $target_h /= 2; }
+    if (rand() < 0.3) { $target_w /= 2; $target_h /= 2; }
 
 
-                    if ($DEBUG > 1) {
-                        print STDERR "image size is $iw x $ih\n";
-                    }
+    if ($iw > $target_w || $ih > $target_h) {
+        while ($iw > $target_w ||
+               $ih > $target_h) {
+            $iw = int($iw / 2);
+            $ih = int($ih / 2);
+        }
+        if ($iw <= 10 || $ih <= 10) {
+            if ($verbose > 1) {
+                print STDERR "$progname: scaling to ${iw}x$ih would " .
+                    "have been bogus.\n";
+            }
+            return 0;
+        }
 
 
-                    if ($iw > $img_width || $ih > $img_height) {
-                        while ($iw > $img_width ||
-                               $ih > $img_height) {
-                            $iw = int($iw / 2);
-                            $ih = int($ih / 2);
-                        }
-                        if ($DEBUG > 1) {
-                            print STDERR "scaling to $iw x $ih\n";
-                        }
-                        system "pnmscale -xysize $iw $ih $image_tmp" .
-                            " > $image_tmp2" .
-                            " 2>/dev/null && mv $image_tmp2 $image_tmp";
-                    }
+        if ($verbose > 1) {
+            print STDERR "$progname: scaling to ${iw}x$ih\n";
+        }
 
 
-                    my $x = int (rand() * ($img_width - $iw));
-                    my $y = int (rand() * ($img_height - $ih));
+        $cmd .= " | pnmscale -xsize $iw -ysize $ih";
+    }
 
 
-                    if ($DEBUG > 1) {
-                        print STDERR "pasting at $x, $y in $image_ppm\n";
-                    }
 
 
-                    system "pnmpaste $image_tmp $x $y $image_ppm" .
-                        " > $image_tmp2" .
-                        " && mv $image_tmp2 $image_ppm";
+    my $src = $image_tmp1;
 
 
+    my $crop_x = 0;     # the sub-rectangle of the image
+    my $crop_y = 0;     # that we will actually paste.
+    my $crop_w = $iw;
+    my $crop_h = $ih;
 
 
-                    my $target = $image_ppm;
-                    if ($post_filter_cmd) {
-                        if ($DEBUG > 1) {
-                            print STDERR "running $post_filter_cmd\n";
-                        }
-                        system "($post_filter_cmd) < $image_ppm > $image_tmp3";
-                        $target = $image_tmp3;
-                    }
+    # The chance that we will randomly crop out a section of an image starts
+    # out fairly low, but goes up for images that are very large, or images
+    # that have ratios that make them look like banners (we try to avoid
+    # banner images entirely, but they slip through when the IMG tags didn't
+    # have WIDTH and HEIGHT specified.)
+    #
+    my $crop_chance = 0.2;
+    if ($iw > $img_width * 0.4 || $ih > $img_height * 0.4) {
+        $crop_chance += 0.2;
+    }
+    if ($iw > $img_width * 0.7 || $ih > $img_height * 0.7) {
+        $crop_chance += 0.2;
+    }
+    if ($min_ratio && ($iw * $min_ratio) > $ih) {
+        $crop_chance += 0.7;
+    }
 
 
-                    if (!$no_output_p) {
+    if ($verbose > 2 && $crop_chance > 0.1) {
+        print STDERR "$progname: crop chance: $crop_chance\n";
+    }
 
 
-                        my $tsize = (stat($target))[7];
-                        if ($tsize > 200) {
-                            $_ = $ppm_to_root_window_cmd;
-                            s/%%PPM%%/$target/;
+    if (rand() < $crop_chance) {
 
 
-                            if ($DEBUG > 1) {
-                                print STDERR "running $_\n";
-                            }
-                            system $_;
+        my $ow = $crop_w;
+        my $oh = $crop_h;
 
 
-                        } elsif ($DEBUG > 1) {
-                            print STDERR "$target size is $tsize\n";
-                        }
-                    }
-                }
-            }
-            unlink $image_tmp, $image_tmp2, $image_tmp3;
+        if ($crop_w > $min_width) {
+            # if it's a banner, select the width linearly.
+            # otherwise, select a bell.
+            my $r = (($min_ratio && ($iw * $min_ratio) > $ih)
+                     ? rand()
+                     : bellrand());
+            $crop_w = $min_width + int ($r * ($crop_w - $min_width));
+            $crop_x = int (rand() * ($ow - $crop_w));
+        }
+        if ($crop_h > $min_height) {
+            # height always selects as a bell.
+            $crop_h = $min_height + int (bellrand() * ($crop_h - $min_height));
+            $crop_y = int (rand() * ($oh - $crop_h));
         }
 
         }
 
-        sleep $delay;
+        if ($verbose > 1 &&
+            ($crop_x != 0   || $crop_y != 0 ||
+             $crop_w != $iw || $crop_h != $ih)) {
+            print STDERR "$progname: randomly cropping to " .
+                "${crop_w}x$crop_h \@ $crop_x,$crop_y\n";
+        }
+    }
 
 
-    } while (1);
-}
+    # Where the image should logically land -- this might be negative.
+    #
+    my $x = int((rand() * ($img_width  + $crop_w/2)) - $crop_w*3/4);
+    my $y = int((rand() * ($img_height + $crop_h/2)) - $crop_h*3/4);
 
 
+    # if we have chosen to paste the image outside of the rectangle of the
+    # screen, then we need to crop it.
+    #
+    if ($x < 0 ||
+        $y < 0 ||
+        $x + $crop_w > $img_width ||
+        $y + $crop_h > $img_height) {
+
+        if ($verbose > 1) {
+            print STDERR "$progname: cropping for effective paste of " .
+                "${crop_w}x$crop_h \@ $x,$y\n";
+        }
+
+        if ($x < 0) { $crop_x -= $x; $crop_w += $x; $x = 0; }
+        if ($y < 0) { $crop_y -= $y; $crop_h += $y; $y = 0; }
 
 
-sub x_main {
+        if ($x + $crop_w >= $img_width)  { $crop_w = $img_width  - $x - 1; }
+        if ($y + $crop_h >= $img_height) { $crop_h = $img_height - $y - 1; }
+    }
 
 
-    # Unlike CGI, when running in X mode, the various tmp files should be
-    # in the /tmp directory and should have gensymed names.
+    # If any cropping needs to happen, add pnmcut.
     #
     #
-    $image_ppm = ($ENV{TMPDIR} ? $ENV{TMPDIR} : "/tmp") . "/webcollage." . $$;
-    $image_tmp   = $image_ppm . "-1";
-    $image_tmp2  = $image_ppm . "-2";
-    $image_tmp3  = $image_ppm . "-3";
+    if ($crop_x != 0   || $crop_y != 0 ||
+        $crop_w != $iw || $crop_h != $ih) {
+        $iw = $crop_w;
+        $ih = $crop_h;
+        $cmd .= " | pnmcut $crop_x $crop_y $iw $ih";
+        if ($verbose > 1) {
+            print STDERR "$progname: cropping to ${crop_w}x$crop_h \@ " .
+                "$crop_x,$crop_y\n";
+        }
+    }
+
+    if ($verbose > 1) {
+        print STDERR "$progname: pasting ${iw}x$ih \@ $x,$y in $image_ppm\n";
+    }
+
+    $cmd .= " | pnmpaste - $x $y $image_ppm";
+
+    $cmd =~ s@^ *\| *@@;
+    my $rc = nontrapping_system "($cmd) < $image_tmp1 > $image_tmp2";
 
 
-    # In X mode, these aren't used.  Set them to undef to error if we try.
+    if ($rc != 0) {
+        if ($verbose) {
+            print STDERR "$progname: failed command: \"$cmd\"\n";
+            print STDERR "$progname: failed url: \"$img\" (${ow}x$oh)\n";
+        }
+        return;
+    }
+
+    rename ($image_tmp2, $image_ppm) || return;
+
+    my $target = "$image_ppm";
+
+    # don't just tack this onto the end of the pipeline -- we don't want it
+    # to end up in $image_ppm, because we don't want the results to be
+    # cumulative.
     #
     #
-    $data_dir = undef;
-    $image_jpg = undef;
-    $pending_file = undef;
-    $map_file = undef;
-    $url_generation_time = undef;
-    $image_retrieval_time = undef;
-    $max_map_entries = undef;
-    $pastes_per_load = undef;
-    $max_age = undef;
-    $script_date = undef;
-    @all_files = undef;
-
-    # In X mode, these come either from the command line, or from the X server.
-    $img_width = undef;
-    $img_height = undef;
+    if ($post_filter_cmd) {
+        $target = $image_tmp1;
+        $rc = nontrapping_system "($post_filter_cmd) < $image_ppm > $target";
+        if ($rc != 0) {
+            if ($verbose) {
+                print STDERR "$progname: filter failed: " .
+                    "\"$post_filter_cmd\"\n";
+            }
+            return;
+        }
+    }
+
+    if (!$no_output_p) {
+        my $tsize = (stat($target))[7];
+        if ($tsize > 200) {
+            $cmd = $ppm_to_root_window_cmd;
+            $cmd =~ s/%%PPM%%/$target/;
+
+            # xv seems to hate being killed.  it tends to forget to clean
+            # up after itself, and leaves windows around and colors allocated.
+            # I had this same problem with vidwhacker, and I'm not entirely
+            # sure what I did to fix it.  But, let's try this: launch xv
+            # in the background, so that killing this process doesn't kill it.
+            # it will die of its own accord soon enough.  So this means we
+            # start pumping bits to the root window in parallel with starting
+            # the next network retrieval, which is probably a better thing
+            # to do anyway.
+            #
+            $cmd .= "&";
+
+            $rc = nontrapping_system ($cmd);
+
+            if ($rc != 0) {
+                if ($verbose) {
+                    print STDERR "$progname: display failed: \"$cmd\"\n";
+                }
+                return;
+            }
+
+        } elsif ($verbose > 1) {
+            print STDERR "$progname: $target size is $tsize\n";
+        }
+    }
+
+    if ($verbose > 0) {
+        print STDOUT "image: ${iw}x${ih} @ $x,$y $base $source\n";
+    }
+
+    return 1;
+}
 
 
 
 
+sub main {
+    $| = 1;
+    srand(time ^ $$);
+
     my $root_p = 0;
 
     my $root_p = 0;
 
+    # historical suckage: the environment variable name is lower case.
+    $http_proxy = $ENV{http_proxy} || $ENV{HTTP_PROXY};
+
     while ($_ = $ARGV[0]) {
         shift @ARGV;
         if ($_ eq "-display" ||
     while ($_ = $ARGV[0]) {
         shift @ARGV;
         if ($_ eq "-display" ||
@@ -1652,9 +1389,9 @@ sub x_main {
             $urls_only_p = 1;
             $no_output_p = 1;
         } elsif ($_ eq "-verbose") {
             $urls_only_p = 1;
             $no_output_p = 1;
         } elsif ($_ eq "-verbose") {
-            $DEBUG++;
+            $verbose++;
         } elsif (m/^-v+$/) {
         } elsif (m/^-v+$/) {
-            $DEBUG += length($_)-1;
+            $verbose += length($_)-1;
         } elsif ($_ eq "-delay") {
             $delay = shift @ARGV;
         } elsif ($_ eq "-timeout") {
         } elsif ($_ eq "-delay") {
             $delay = shift @ARGV;
         } elsif ($_ eq "-timeout") {
@@ -1667,20 +1404,31 @@ sub x_main {
             $background = shift @ARGV;
         } elsif ($_ eq "-size") {
             $_ = shift @ARGV;
             $background = shift @ARGV;
         } elsif ($_ eq "-size") {
             $_ = shift @ARGV;
-            if (m@^([0-9]+)x([0-9]+)$@) {
+            if (m@^(\d+)x(\d+)$@) {
                 $img_width = $1;
                 $img_height = $2;
             } else {
                 die "$progname: argument to \"-size\" must be" .
                     " of the form \"640x400\"\n";
             }
                 $img_width = $1;
                 $img_height = $2;
             } else {
                 die "$progname: argument to \"-size\" must be" .
                     " of the form \"640x400\"\n";
             }
+        } elsif ($_ eq "-proxy" || $_ eq "-http-proxy") {
+            $http_proxy = shift @ARGV;
         } else {
             die "$copyright\nusage: $progname [-root]" .
                 " [-display dpy] [-root] [-verbose] [-timeout secs]\n" .
         } else {
             die "$copyright\nusage: $progname [-root]" .
                 " [-display dpy] [-root] [-verbose] [-timeout secs]\n" .
-                "\t\t  [-delay secs] [-filter cmd] [-filter2 cmd]\n";
+                "\t\t  [-delay secs] [-filter cmd] [-filter2 cmd]\n" .
+                "\t\t  [-http-proxy host[:port]]\n";
         }
     }
 
         }
     }
 
+    if ($http_proxy && $http_proxy eq "") {
+        $http_proxy = undef;
+    }
+    if ($http_proxy && $http_proxy =~ m@^http://([^/]*)/?$@ ) {
+        # historical suckage: allow "http://host:port" as well as "host:port".
+        $http_proxy = $1;
+    }
+
     if (!$root_p && !$no_output_p) {
         die "$copyright" .
             "$progname: the -root argument is manditory (for now.)\n";
     if (!$root_p && !$no_output_p) {
         die "$copyright" .
             "$progname: the -root argument is manditory (for now.)\n";
@@ -1697,21 +1445,5 @@ sub x_main {
     }
 }
 
     }
 }
 
-
-##############################################################################
-#
-# Decide if we're in X or CGI mode, and dispatch.
-#
-##############################################################################
-
-sub main {
-    srand(time ^ $$);
-    if ( $progname =~ m/\.cgi$/i || $ENV{REQUEST_METHOD} ) {
-        cgi_main;
-    } else {
-        x_main;
-    }
-}
-
 main;
 exit (0);
 main;
 exit (0);
index 6004c45531fb9c1869270f33cf8cf723a115c51b..10eb69d1bf1f112dfe00ea2abf94784a3eda3311 100644 (file)
@@ -19,6 +19,7 @@ webcollage - decorate the screen with random images from the web
 [\-display \fIhost:display.screen\fP] [\-root] [\-verbose]
 [\-delay \fIsecs\fP] [\-timeout \fIsecs\fP] [\-background \fIbg\fP]
 [\-filter \fIcommand\fP] [\-filter2 \fIcommand\fP]
 [\-display \fIhost:display.screen\fP] [\-root] [\-verbose]
 [\-delay \fIsecs\fP] [\-timeout \fIsecs\fP] [\-background \fIbg\fP]
 [\-filter \fIcommand\fP] [\-filter2 \fIcommand\fP]
+[\-http\-proxy host[:port]]
 .SH DESCRIPTION
 The \fIwebcollage\fP program pulls random image off of the World Wide Web
 and scatters them on the root window.  One satisfied customer described it
 .SH DESCRIPTION
 The \fIwebcollage\fP program pulls random image off of the World Wide Web
 and scatters them on the root window.  One satisfied customer described it
@@ -31,10 +32,6 @@ and
 .BR djpeg (1)
 tools.
 
 .BR djpeg (1)
 tools.
 
-\fIwebcollage\fP also works as a CGI program, that will add images to the
-collage on a web page each time the page is loaded.  See the comments at 
-the top of the source code for installation instructions.
-
 \fIwebcollage\fP is written in
 .BR perl (1)
 and requires Perl 5.
 \fIwebcollage\fP is written in
 .BR perl (1)
 and requires Perl 5.
@@ -78,6 +75,11 @@ webcollage -root -filter 'vidwhacker -stdin -stdout'
 Filter the \fIcomposite\fP image through this command.  The \fI-filter\fP
 option applies to the sub-images; the \fI-filter2\fP applies to the
 final, full-screen image.
 Filter the \fIcomposite\fP image through this command.  The \fI-filter\fP
 option applies to the sub-images; the \fI-filter2\fP applies to the
 final, full-screen image.
+.TP 8
+.B \-http\-proxy \fIhost:port\fP
+If you must go through a proxy to connect to the web, you can specify it 
+with this option, or with the \fB$http_proxy\fP or \fB$HTTP_PROXY\fP 
+environment variables.
 .SH ENVIRONMENT
 .PP
 .TP 8
 .SH ENVIRONMENT
 .PP
 .TP 8
@@ -87,6 +89,9 @@ to get the default host and display number.
 .B XENVIRONMENT
 to get the name of a resource file that overrides the global resources
 stored in the RESOURCE_MANAGER property.
 .B XENVIRONMENT
 to get the name of a resource file that overrides the global resources
 stored in the RESOURCE_MANAGER property.
+.TP 8
+.B http_proxy\fR or \fPHTTP_PROXY
+to get the default HTTP proxy host and port.
 .SH FILES AND URLS
 .TP
 .I /usr/dict/words \fRor\fP /usr/share/lib/dict/words
 .SH FILES AND URLS
 .TP
 .I /usr/dict/words \fRor\fP /usr/share/lib/dict/words
index d39119abd38e40968a13ed1d85d8ce8161e3ea0d..0c2b613e07a7711507bbb361ad5f5e2649e6e467 100644 (file)
@@ -90,6 +90,7 @@ ERROR!  Sorry, xlockmore.h requires ANSI C (gcc, for example.)
 #define MI_IS_VERBOSE(MI)      (MI_WIN_IS_VERBOSE(MI))
 #define MI_IS_INSTALL(MI)      (MI_WIN_IS_INSTALL(MI))
 #define MI_IS_DEBUG(MI)                (False)
 #define MI_IS_VERBOSE(MI)      (MI_WIN_IS_VERBOSE(MI))
 #define MI_IS_INSTALL(MI)      (MI_WIN_IS_INSTALL(MI))
 #define MI_IS_DEBUG(MI)                (False)
+#define MI_IS_MOUSE(MI)                (False)
 
 #define MI_CLEARWINDOW(mi) XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi))
 
 
 #define MI_CLEARWINDOW(mi) XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi))
 
index 3742df68858b61e40a6d5236a70c3f87ae738a21..ab7fde5db42b3fd1120da61a1732129284b762bd 100644 (file)
@@ -605,7 +605,7 @@ complyap(void)
   if (!run)
     return TRUE;
   a += a_inc;
   if (!run)
     return TRUE;
   a += a_inc;
-  if (a >= max_a)
+  if (a >= max_a) {
     if (sendpoint(lyapunov) == TRUE)
       return FALSE;
     else {
     if (sendpoint(lyapunov) == TRUE)
       return FALSE;
     else {
@@ -614,6 +614,7 @@ complyap(void)
        save_to_file();
       return TRUE;
     }
        save_to_file();
       return TRUE;
     }
+  }
   if (b >= max_b) {
     FlushBuffer();
     if (savefile)
   if (b >= max_b) {
     FlushBuffer();
     if (savefile)
diff --git a/hacks/xsublim.c b/hacks/xsublim.c
new file mode 100644 (file)
index 0000000..231bddc
--- /dev/null
@@ -0,0 +1,773 @@
+/*****************************************************************************
+ *                                                                           *
+ * xsublim -- Submit.  Conform.  Obey.                                       *
+ *                                                                           *
+ * Copyright (c) 1999 Greg Knauss (greg@eod.com)                             *
+ *                                                                           *
+ * Thanks to Jamie Zawinski, whose suggestions and advice made what was a    *
+ * boring little program into a less boring (and a _lot_ less little)        *
+ * program.                                                                  *
+ *                                                                           *
+ * 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.                                                         *
+ *                                                                           *
+ * Stare into the subliminal for as long as you can...                       *
+ *                                                                           *
+ *****************************************************************************/
+
+
+/* Warnings *******************************************************************
+
+       Please don't end this process with a SIGKILL.  If it's got the X server
+       grabbed when you do, you'll never get it back and it won't be my fault.
+*/
+
+
+/* Arguments ******************************************************************
+
+       -font font           Font to use
+       -delayShow ms        Microsecs for display of each word
+       -delayWord ms        Microsecs for blank between words
+       -delayPhraseMin ms   Microsecs for min blank between phrases
+       -delayPhraseMax ms   Microsecs for max blank between phrases
+       -random              Show phrases in random order (Default)
+       -no-random           Show phrases in listed order
+       -screensaver         Wait for an active screensaver (Default)
+       -no-screensaver      Draw over active windows       
+       -outline             Draw a contrasting outline around words (Default)
+       -no-outline          Draw words without an outline
+*/
+
+
+/* Defines *******************************************************************/
+#define XSUBLIM_NAME               "XSublim"
+#define XSUBLIM_TEXT_COUNT         1000
+#define XSUBLIM_TEXT_LENGTH        128
+#define XSUBLIM_TEXT_OUTLINE       1
+#define XSUBLIM_ARG_DELAYSHOW      "delayShow"
+#define XSUBLIM_ARG_DELAYWORD      "delayWord"
+#define XSUBLIM_ARG_DELAYPHRASEMIN "delayPhraseMin"
+#define XSUBLIM_ARG_DELAYPHRASEMAX "delayPhraseMax"
+#define XSUBLIM_ARG_RANDOM         "random"
+#define XSUBLIM_ARG_FILE           "file"
+#define XSUBLIM_ARG_SCREENSAVER    "screensaver"
+#define XSUBLIM_ARG_OUTLINE        "outline"
+#define XSUBLIM_ARG_CENTER         "center"
+#define XSUBLIM_ARG_FONT           "font"
+#define XSUBLIM_ARG_PHRASES        "phrases"
+#ifndef TRUE
+#define FALSE 0
+#define TRUE  1
+#endif
+
+
+/* Includes ******************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <X11/Intrinsic.h>
+#include <X11/IntrinsicP.h>
+#include <X11/CoreP.h>
+#include <X11/Shell.h>
+#include <X11/StringDefs.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xos.h>
+#include <X11/Xproto.h>
+#if defined(__sgi)
+#include <X11/SGIScheme.h>
+#endif
+
+#include "usleep.h"
+#include "resources.h"
+
+
+/* Globals *******************************************************************/
+char*        progname;
+XtAppContext app;
+XrmDatabase  db;
+char*        progclass = XSUBLIM_NAME;
+char*        defaults[] =
+{
+       ".background:                     #000000",
+       ".foreground:                     #FFFFFF",
+       "*" XSUBLIM_ARG_PHRASES ":"
+        "Submit.\\n"
+        "Conform.\\n"
+        "Obey.\\n"
+        "Consume.\\n"
+        "Be silent.\\n"
+        "Fear.\\n"
+        "Waste.\\n"
+        "Watch TV.\\n"
+        "Hate yourself.\\n"
+        "Buy needlessly.\\n"
+        "Despair quietly.\\n"
+        "God hates you.\\n"
+        "You are being watched.\\n"
+        "You will be punished.\\n"
+        "You serve no purpose.\\n"
+        "Your contributions are ignored.\\n"
+        "They are laughing at you.\\n"
+        "Surrender.\\n"
+        "You will fail.\\n"
+        "Never question.\\n"
+        "You are a prisoner.\\n"
+        "You are helpless.\\n"
+        "You are diseased.\\n"
+        "Fear the unknown.\\n"
+        "Happiness follows obedience.\\n"
+        "Ignorance is strength.\\n"
+        "War is peace.\\n"
+        "Freedom is slavery.\\n"
+        "Abandon all hope.\\n"
+        "You will be assimilated.\\n"
+        "Resistance is futile.\\n"
+        "Resistance is useless.\\n"
+        "Life is pain.\\n"
+        "No escape.\\n"
+        "What's that smell?\\n"
+        "All praise the company.\\n"
+        "Fnord.\\n",
+       "*" XSUBLIM_ARG_FONT ":           -*-utopia-*-r-*-*-*-600-*-*-p-*-*-*",
+       "*" XSUBLIM_ARG_DELAYSHOW ":      40000",
+       "*" XSUBLIM_ARG_DELAYWORD ":      100000",
+       "*" XSUBLIM_ARG_DELAYPHRASEMIN ": 5000000",
+       "*" XSUBLIM_ARG_DELAYPHRASEMAX ": 20000000",
+       "*" XSUBLIM_ARG_RANDOM ":         true",
+       "*" XSUBLIM_ARG_SCREENSAVER ":    true",
+       "*" XSUBLIM_ARG_OUTLINE":         true",
+       "*" XSUBLIM_ARG_CENTER":          true",
+       NULL
+};
+XrmOptionDescRec options[] =
+{
+       {"-" XSUBLIM_ARG_FONT,          "." XSUBLIM_ARG_FONT,
+        XrmoptionSepArg,0},
+       {"-" XSUBLIM_ARG_DELAYSHOW,     "." XSUBLIM_ARG_DELAYSHOW,
+        XrmoptionSepArg,0},
+       {"-" XSUBLIM_ARG_DELAYWORD,     "." XSUBLIM_ARG_DELAYWORD,
+        XrmoptionSepArg,0},
+       {"-" XSUBLIM_ARG_DELAYPHRASEMIN,"." XSUBLIM_ARG_DELAYPHRASEMIN,
+        XrmoptionSepArg,0},
+       {"-" XSUBLIM_ARG_DELAYPHRASEMAX,"." XSUBLIM_ARG_DELAYPHRASEMAX,
+        XrmoptionSepArg,0},
+       {"-" XSUBLIM_ARG_RANDOM,        "." XSUBLIM_ARG_RANDOM,
+        XrmoptionNoArg,"true"},
+       {"-no-" XSUBLIM_ARG_RANDOM,     "." XSUBLIM_ARG_RANDOM,
+        XrmoptionNoArg,"false"},
+       {"-" XSUBLIM_ARG_FILE,          "." XSUBLIM_ARG_FILE,
+        XrmoptionSepArg,0 },
+       {"-" XSUBLIM_ARG_SCREENSAVER,   "." XSUBLIM_ARG_SCREENSAVER,
+        XrmoptionNoArg,"true"},
+       {"-no-" XSUBLIM_ARG_SCREENSAVER,"." XSUBLIM_ARG_SCREENSAVER,
+        XrmoptionNoArg,"false"},
+       {"-" XSUBLIM_ARG_OUTLINE,       "." XSUBLIM_ARG_OUTLINE,
+        XrmoptionNoArg,"true"},
+       {"-no-" XSUBLIM_ARG_OUTLINE,    "." XSUBLIM_ARG_OUTLINE,
+        XrmoptionNoArg,"false"},
+       {"-" XSUBLIM_ARG_CENTER,        "." XSUBLIM_ARG_CENTER,
+        XrmoptionNoArg,"true"},
+       {"-no-" XSUBLIM_ARG_CENTER,     "." XSUBLIM_ARG_CENTER,
+        XrmoptionNoArg,"false"},
+       {NULL,                          NULL,
+        0,              0 }
+};
+static int Xsublim_Sig_Last;
+
+
+/* Functions *****************************************************************/
+
+/* Defer signals to protect the server grab ================================ */
+void xsublim_Sig_Catch(int sig_Number)
+{
+       /* BSD needs this reset each time, and it shouldn't hurt anything
+          else */
+       signal(sig_Number,xsublim_Sig_Catch);
+       Xsublim_Sig_Last = sig_Number;
+}
+
+/* Get the screensaver's window ============================================ */
+static XErrorHandler Xsublim_Ss_Handler = NULL;
+static int           Xsublim_Ss_Status;
+
+/* This was all basically swiped from driver/remote.c and util/vroot.h */
+static int xsublim_Ss_Handler(Display* handle_Display,
+            XErrorEvent* handle_Error)
+{
+       if (handle_Error->error_code == BadWindow)
+       {
+               Xsublim_Ss_Status = BadWindow;
+               return 0;
+       }
+       if (Xsublim_Ss_Handler == NULL)
+       {
+               fprintf(stderr,"%x: ",progname);
+               abort();
+       }
+       return (*Xsublim_Ss_Handler)(handle_Display,handle_Error);
+}
+static Window xsublim_Ss_GetWindow(Display* ss_Display)
+{
+       Window        win_Root;
+       Window        win_RootReturn;
+       Window        win_Parent;
+       Window*       win_Child;
+       Window        win_Win;
+       int           child_Count;
+       int           child_Index;
+       Atom          prop_Type;
+       int           prop_Format;
+       unsigned long prop_Count;
+       unsigned long prop_Bytes;
+       char*         prop_Value;
+       int           prop_Status;
+       static Atom   XA_SCREENSAVER_VERSION = -1;
+       static Atom   __SWM_VROOT;
+
+       /* Assume bad things */
+       win_Win = 0;
+       win_Child = NULL;
+
+       /* Find the atoms */
+       if (XA_SCREENSAVER_VERSION == -1)
+       {
+               XA_SCREENSAVER_VERSION = XInternAtom(ss_Display,
+                "_SCREENSAVER_VERSION",FALSE);
+               __SWM_VROOT = XInternAtom(ss_Display,"__SWM_VROOT",FALSE);
+       }
+
+       /* Find a screensaver window */
+       win_Root = RootWindowOfScreen(DefaultScreenOfDisplay(ss_Display));
+       if (XQueryTree(ss_Display,win_Root,&win_RootReturn,&win_Parent,
+        &win_Child,&child_Count) != FALSE)
+       {
+               if (
+                (win_Root == win_RootReturn) &&
+                (win_Parent == 0) &&
+                (win_Child != NULL) &&
+                (child_Count > 0))
+               {
+                       for (child_Index = 0;child_Index < child_Count;
+                        child_Index++)
+                       {
+                               XSync(ss_Display,FALSE);
+                               Xsublim_Ss_Status = 0;
+                               Xsublim_Ss_Handler =
+                                XSetErrorHandler(xsublim_Ss_Handler);
+                               prop_Value = NULL;
+                               prop_Status = XGetWindowProperty(ss_Display,
+                                win_Child[child_Index],XA_SCREENSAVER_VERSION,
+                                0,200,FALSE,XA_STRING,&prop_Type,&prop_Format,
+                                &prop_Count,&prop_Bytes,
+                                (unsigned char**)&prop_Value);
+                               XSync(ss_Display,FALSE);
+                               XSetErrorHandler(Xsublim_Ss_Handler);
+                               if (prop_Value != NULL)
+                               {
+                                       XFree(prop_Value);
+                               }
+                               if (Xsublim_Ss_Status == BadWindow)
+                               {
+                                       prop_Status = BadWindow;
+                               }
+                               if ((prop_Status == Success) &&
+                                (prop_Type != None))
+                               {
+                                       /* See if it's a virtual root */
+                                       prop_Value = NULL;
+                                       prop_Status =
+                                        XGetWindowProperty(ss_Display,
+                                        win_Child[child_Index],__SWM_VROOT,0,
+                                        1,FALSE,XA_WINDOW,&prop_Type,
+                                        &prop_Format,&prop_Count,&prop_Bytes,
+                                        (unsigned char**)&prop_Value);
+                                       if (prop_Value != NULL)
+                                       {
+                                               XFree(prop_Value);
+                                       }
+                                       if ((prop_Status == Success) &&
+                                        (prop_Type != None))
+                                       {
+                                               win_Win =
+                                                win_Child[child_Index];
+                                       }
+                               }
+                       }
+               }
+       }
+       if (win_Child != NULL)
+       {
+               XFree(win_Child);
+       }
+       return win_Win;
+}
+
+/* Main ==================================================================== */
+static XErrorHandler Xsublim_Sh_Handler = NULL;
+static int           Xsublim_Sh_Status = 0;
+
+static int xsublim_Sh_Handler(Display* handle_Display,
+            XErrorEvent* handle_Error)
+{
+       if (handle_Error->error_code == BadMatch)
+       {
+               Xsublim_Sh_Status = BadMatch;
+               return 0;
+       }
+       if (Xsublim_Sh_Handler == NULL)
+       {
+               fprintf(stderr,"%s: ",progname);
+               abort();
+       }
+       return (*Xsublim_Sh_Handler)(handle_Display,handle_Error);
+}
+int main(int argc,char* argv[])
+{
+       int               sig_Number;
+       int               sig_Signal[] =
+       {
+               SIGHUP,
+               SIGINT,
+               SIGQUIT,
+               SIGILL,
+               SIGTRAP,
+               SIGIOT,
+               SIGABRT,
+#if defined(SIGEMT)
+               SIGEMT,
+#endif
+               SIGFPE,
+               SIGBUS,
+               SIGSEGV,
+#if defined(SIGSYS)
+               SIGSYS,
+#endif
+               SIGTERM,
+#if defined(SIGXCPU)
+               SIGXCPU,
+#endif
+#if defined(SIGXFSZ)
+               SIGXFSZ,
+#endif
+#if defined(SIGDANGER)
+               SIGDANGER,
+#endif
+               -1
+       };
+       Widget            app_App;
+       Display*          disp_Display;
+       Window            win_Root;
+       XWindowAttributes attr_Win;
+       XGCValues         gc_ValFore;
+       XGCValues         gc_ValBack;
+       GC                gc_GcFore;
+       GC                gc_GcBack;
+       XFontStruct*      font_Font;
+       char*             font_List[] =
+       {
+               "-*-character-*-r-*-*-*-600-*-*-p-*-*-*",
+               "-*-helvetica-*-r-*-*-*-600-*-*-p-*-*-*",
+               "-*-lucida-*-r-*-*-*-600-*-*-p-*-*-*",
+               "-*-times-*-r-*-*-*-600-*-*-p-*-*-*",
+               "-*-*-*-r-*-sans-*-600-*-*-p-*-*-*",
+               "-*-*-*-r-*-*-*-600-*-*-m-*-*-*",
+
+               "-*-helvetica-*-r-*-*-*-240-*-*-p-*-*-*",
+               "-*-lucida-*-r-*-*-*-240-*-*-p-*-*-*",
+               "-*-times-*-r-*-*-*-240-*-*-p-*-*-*",
+               "-*-*-*-r-*-sans-*-240-*-*-p-*-*-*",
+               "-*-*-*-r-*-*-*-240-*-*-m-*-*-*",
+               "fixed",
+               NULL
+       };
+       int               font_Index;
+       int               text_Length;
+       int               text_X;
+       int               text_Y;
+       int               text_Width;
+       int               text_Height;
+       char*             text_List[XSUBLIM_TEXT_COUNT];
+       int               text_Used[XSUBLIM_TEXT_COUNT];
+       char              text_Text[XSUBLIM_TEXT_LENGTH+1];
+       char*             text_Phrase;
+       char*             text_Word;
+       int               text_Index;
+       int               text_Item;
+       int               text_Count;
+       struct
+       {
+               int outline_X;
+               int outline_Y;
+       }                 text_Outline[] =
+       {
+               { -1,-1 },
+               {  1,-1 },
+               { -1, 1 },
+               {  1, 1 },
+               {  0, 0 }
+       };
+       int               text_OutlineIndex;
+       XImage*           image_Image = NULL;
+       int               image_X = 0;
+       int               image_Y = 0;
+       int               image_Width = 0;
+       int               image_Height = 0;
+       int               arg_Count;
+       int               arg_FlagCenter;
+       int               arg_FlagOutline;
+       int               arg_FlagScreensaver;
+       int               arg_FlagRandom;
+       int               arg_DelayShow;
+       int               arg_DelayWord;
+       int               arg_DelayPhraseMin;
+       int               arg_DelayPhraseMax;
+       char*             arg_Text;
+
+       /* Set-up ---------------------------------------------------------- */
+
+       /* Catch signals */
+       Xsublim_Sig_Last = -1;
+       for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
+       {
+               signal(sig_Number,xsublim_Sig_Catch);
+       }
+
+       /* Randomize */
+       srandom((int)time((time_t*)0));
+
+       /* Handle all the X nonsense */
+#if defined(__sgi)
+       SgiUseSchemes("none");
+#endif
+       for (arg_Count = 0;options[arg_Count].option != NULL;arg_Count++)
+       {
+               ;
+       }
+       app_App = XtAppInitialize(&app,progclass,options,arg_Count,&argc,argv,
+        defaults,0,0);
+
+        /* jwz */
+        if (argc > 1)
+          {
+            int x = 18;
+            int end = 78;
+            int i;
+            int count = (sizeof(options)/sizeof(*options))-1;
+            fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
+            fprintf (stderr, "Options include: ");
+            for (i = 0; i < count; i++)
+              {
+                char *sw = options [i].option;
+                Bool argp = (options [i].argKind == XrmoptionSepArg);
+                int size = strlen (sw) + (argp ? 6 : 0) + 2;
+                if (x + size >= end)
+                  {
+                    fprintf (stderr, "\n\t\t ");
+                    x = 18;
+                  }
+                x += size;
+                fprintf (stderr, "%s", sw);
+                if (argp) fprintf (stderr, " <arg>");
+                if (i != count-1) fprintf (stderr, ", ");
+              }
+            fprintf (stderr, ".\n");
+            exit (-1);
+          }
+
+       disp_Display = XtDisplay(app_App);
+       db = XtDatabase(disp_Display);
+       XtGetApplicationNameAndClass(disp_Display,&progname,&progclass);
+       win_Root = RootWindowOfScreen(XtScreen(app_App));
+       XtDestroyWidget(app_App);
+
+       /* Get the arguments */
+       arg_FlagCenter = get_boolean_resource(XSUBLIM_ARG_CENTER,"Boolean");
+       arg_FlagOutline = get_boolean_resource(XSUBLIM_ARG_OUTLINE,"Boolean");
+       arg_FlagScreensaver = get_boolean_resource(XSUBLIM_ARG_SCREENSAVER,
+        "Boolean");
+       arg_FlagRandom = get_boolean_resource(XSUBLIM_ARG_RANDOM,"Boolean");
+       arg_DelayShow = get_integer_resource(XSUBLIM_ARG_DELAYSHOW,"Integer");
+       arg_DelayWord = get_integer_resource(XSUBLIM_ARG_DELAYWORD,"Integer");
+       arg_DelayPhraseMin = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMIN,
+        "Integer");
+       arg_DelayPhraseMax = get_integer_resource(XSUBLIM_ARG_DELAYPHRASEMAX,
+        "Integer");
+       if (arg_DelayPhraseMax < arg_DelayPhraseMin)
+       {
+               arg_DelayPhraseMax = arg_DelayPhraseMin;
+       }
+
+       /* Get the phrases */
+       text_Index = 0;
+       text_Item = 0;
+       text_Count = 0;
+       memset(text_Used,0,sizeof(text_Used));
+       arg_Text = get_string_resource(XSUBLIM_ARG_PHRASES,"Phrases");
+       if (arg_Text != NULL)
+       {
+               arg_Text = strdup(arg_Text);
+               while (((text_Phrase = strtok(arg_Text,"\n")) != NULL) &&
+                (text_Count < XSUBLIM_TEXT_COUNT))
+               {
+                       arg_Text = NULL;
+                       text_List[text_Count] = text_Phrase;
+                       text_Count++;
+               }
+       }
+       text_List[text_Count] = NULL;
+       if (text_Count == 0)
+       {
+               fprintf(stderr,"%s: No text to display\n",progname);
+               exit(-1);
+       }
+
+       /* Load the font */
+       font_Font = XLoadQueryFont(disp_Display,
+        get_string_resource(XSUBLIM_ARG_FONT,"Font"));
+       font_Index = 0;
+       while ((font_Font == NULL) && (font_List[font_Index] != NULL))
+       {
+               font_Font = XLoadQueryFont(disp_Display,font_List[font_Index]);
+               font_Index++;
+       }
+       if (font_Font == NULL)
+       {
+               fprintf(stderr,"%s: Couldn't load a font\n",progname);
+               exit(-1);
+       }
+
+       /* Create the GCs */
+       XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
+       gc_ValFore.font = font_Font->fid;
+       gc_ValFore.foreground = get_pixel_resource("foreground","Foreground",
+        disp_Display,attr_Win.colormap);
+       gc_ValFore.background = get_pixel_resource("background","Background",
+        disp_Display,attr_Win.colormap);
+       gc_ValFore.subwindow_mode = IncludeInferiors;
+       gc_GcFore = XCreateGC(disp_Display,win_Root,
+        (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValFore);
+       gc_ValBack.font = font_Font->fid;
+       gc_ValBack.foreground = get_pixel_resource("background","Background",
+        disp_Display,attr_Win.colormap);
+       gc_ValBack.background = get_pixel_resource("foreground","Foreground",
+        disp_Display,attr_Win.colormap);
+       gc_ValBack.subwindow_mode = IncludeInferiors;
+       gc_GcBack = XCreateGC(disp_Display,win_Root,
+        (GCFont|GCForeground|GCBackground|GCSubwindowMode),&gc_ValBack);
+
+       /* Loop ------------------------------------------------------------ */
+       while (Xsublim_Sig_Last == -1)
+       {
+               /* Once-per-phrase stuff ----------------------------------- */
+
+               /* If we're waiting for a screensaver... */
+               if (arg_FlagScreensaver != FALSE)
+               {
+                       /* Find the screensaver's window */
+                       win_Root = xsublim_Ss_GetWindow(disp_Display);
+                       if (win_Root == 0)
+                       {
+                               usleep(30000000);
+                               continue;
+                       }
+               }
+
+               /* Pick the next phrase */
+               if (arg_FlagRandom != FALSE)
+               {
+                       text_Item = random()%text_Count;
+                       text_Index = 0;
+               }
+               while (text_Used[text_Item] != FALSE)
+               {
+                       text_Index++;
+                       text_Item++;
+                       if (text_Index == text_Count)
+                       {
+                               text_Index = 0;
+                               memset(text_Used,0,sizeof(text_Used));
+                       }
+                       if (text_List[text_Item] == NULL)
+                       {
+                               text_Item = 0;
+                       }
+               }
+               text_Used[text_Item] = TRUE;
+               strncpy(text_Text,text_List[text_Item],
+                XSUBLIM_TEXT_LENGTH);
+               text_Phrase = text_Text;
+
+               /* Run through the phrase */
+               while (((text_Word = strtok(text_Phrase," \t")) != NULL) &&
+                (Xsublim_Sig_Last == -1))
+               {
+                       text_Phrase = NULL;
+
+                       /* Once-per-word stuff ----------------------------- */
+
+                       /* Find the text's position */
+                       XGetWindowAttributes(disp_Display,win_Root,&attr_Win);
+                       text_Length = strlen(text_Word);
+                       text_Width = XTextWidth(font_Font,text_Word,
+                        text_Length)+XSUBLIM_TEXT_OUTLINE*2;
+                       text_Height = font_Font->ascent+font_Font->descent+1+
+                        XSUBLIM_TEXT_OUTLINE*2;
+                       if (arg_FlagCenter == FALSE)
+                       {
+                               text_X = random()%(attr_Win.width-text_Width);
+                               text_Y = random()%(attr_Win.height-
+                                text_Height);
+                       }
+                       else
+                       {
+                               text_X = (attr_Win.width/2)-(text_Width/2);
+                               text_Y = (attr_Win.height/2)-(text_Height/2);
+                       }
+
+                       /* Find the image's position (and pad it out slightly,
+                          otherwise bits of letter get left behind -- are
+                          there boundry issues I don't know about?) */
+                       image_X = text_X-16;
+                       image_Y = text_Y;
+                       image_Width = text_Width+32;
+                       image_Height = text_Height;
+                       if (image_X < 0)
+                       {
+                               image_X = 0;
+                       }
+                       if (image_Y < 0)
+                       {
+                               image_Y = 0;
+                       }
+                       if (image_X+image_Width > attr_Win.width)
+                       {
+                               image_Width = attr_Win.width-image_X;
+                       }
+                       if (image_Y+image_Height > attr_Win.height)
+                       {
+                               image_Height = attr_Win.height-image_Y;
+                       }
+
+                       /* Influence people for our own ends --------------- */
+
+                       /* Grab the server -- we can't let anybody draw over
+                          us */
+                       XSync(disp_Display,FALSE);
+                       XGrabServer(disp_Display);
+                       XSync(disp_Display,FALSE);
+
+                       /* Set up an error handler that ignores BadMatches --
+                          since the screensaver can take its window away at
+                          any time, any call that uses it might choke */
+                       Xsublim_Sh_Status = 0;
+                       Xsublim_Sh_Handler =
+                        XSetErrorHandler(xsublim_Sh_Handler);
+
+                       /* Save the current background */
+                       image_Image = XGetImage(disp_Display,win_Root,image_X,
+                        image_Y,image_Width,image_Height,~0L,ZPixmap);
+
+                       /* If we've successfully saved the background... */
+                       if (image_Image != NULL)
+                       {
+                               if (Xsublim_Sh_Status == 0)
+                               {
+                                       /* Draw the outline */
+                                       if (arg_FlagOutline != FALSE)
+                                       {
+                                               for (text_OutlineIndex = 0;
+                                                text_Outline[
+                                                text_OutlineIndex].outline_X
+                                                != 0;text_OutlineIndex++)
+                                               {
+                                                       /* Y'know, eight
+                                                          character tabs and
+                                                          descriptive variable
+                                                          names become
+                                                          annoying at some
+                                                          point... */
+                                                       XDrawString(
+                                                        disp_Display,
+                                                        win_Root,gc_GcBack,
+                                                        text_X+text_Outline[
+                                                        text_OutlineIndex].
+                                                        outline_X*
+                                                        XSUBLIM_TEXT_OUTLINE,
+                                                        text_Y+
+                                                        (font_Font->ascent)+
+                                                        text_Outline[
+                                                        text_OutlineIndex].
+                                                        outline_Y*
+                                                        XSUBLIM_TEXT_OUTLINE,
+                                                        text_Word,
+                                                        text_Length);
+                                               }
+                                       }
+
+                                       /* Draw the word */
+                                       XDrawString(disp_Display,win_Root,
+                                        gc_GcFore,text_X,
+                                        text_Y+(font_Font->ascent),text_Word,
+                                        text_Length);
+                               }
+                               if (Xsublim_Sh_Status == 0)
+                               {
+                                       /* Wait a bit */
+                                       XSync(disp_Display,FALSE);
+                                       if (Xsublim_Sig_Last == -1)
+                                       {
+                                               usleep(arg_DelayShow);
+                                       }
+       
+                                       /* Restore the background */
+                                       XPutImage(disp_Display,win_Root,
+                                        gc_GcFore,image_Image,0,0,image_X,
+                                        image_Y,image_Width,image_Height);
+                               }
+
+                               /* Free the image (and it's goddamned structure
+                                  -- the man page for XCreateImage() lies,
+                                  lies, lies!) */
+                               XDestroyImage(image_Image);
+                               XFree(image_Image);
+                       }
+
+                       /* Restore the error handler, ungrab the server */
+                        XSync(disp_Display, FALSE);
+                       XSetErrorHandler(Xsublim_Sh_Handler);
+                       XUngrabServer(disp_Display);
+                        XSync(disp_Display, FALSE);
+
+                       /* Pause between words */
+                       if (Xsublim_Sig_Last == -1)
+                       {
+                               usleep(arg_DelayWord);
+                       }
+               }
+
+               /* Pause between phrases */
+               if (Xsublim_Sig_Last == -1)
+               {
+                       usleep(random()%(arg_DelayPhraseMax-
+                        arg_DelayPhraseMin+1)+arg_DelayPhraseMin);
+               }
+       }
+
+       /* Exit ------------------------------------------------------------ */
+       for (sig_Number = 0;sig_Signal[sig_Number] != -1;sig_Number++)
+       {
+               signal(sig_Number,SIG_DFL);
+       }
+       kill(getpid(),Xsublim_Sig_Last);
+
+       return 0;
+}
index b6c545ecd99e6977af4c3d30870ba5307e8ae799..1e6252d1c18a864e7cd243af46a07d189cb31fee 100644 (file)
--- a/setup.com
+++ b/setup.com
@@ -89,6 +89,7 @@ $ xjack               :== $'mydir'xjack
 $ xlyap                :== $'mydir'xlyap
 $ xmatrix      :== $'mydir'xmatrix
 $ xroger       :== $'mydir'xroger
 $ xlyap                :== $'mydir'xlyap
 $ xmatrix      :== $'mydir'xmatrix
 $ xroger       :== $'mydir'xroger
+$ xsublim      :== $'mydir'xsublim
 $ set def [-.DRIVER]
 $ mydir  = mydisk+f$directory()
 $ xscreensaver :== $'mydir'xscreensaver
 $ set def [-.DRIVER]
 $ mydir  = mydisk+f$directory()
 $ xscreensaver :== $'mydir'xscreensaver
index 51cf9ad88ed123fb650247bea569abf2387d71fa..f71c52127fe549ea8cb53b6fa3d99b44e33fc8f8 100644 (file)
@@ -1,2 +1,2 @@
 static const char screensaver_id[] =
 static const char screensaver_id[] =
-       "@(#)xscreensaver 3.16 (23-Jun-99), by Jamie Zawinski (jwz@jwz.org)";
+       "@(#)xscreensaver 3.17 (15-Jul-99), by Jamie Zawinski (jwz@jwz.org)";
index 640030084d466b7e3f73f889e68486f60fe3a707..088d85c0395d10afbcaaf2f3b972aff2d8c7f1f6 100644 (file)
@@ -1,7 +1,7 @@
 Begin3
 Title:          xscreensaver
 Begin3
 Title:          xscreensaver
-Version:        3.16
-Entered-date:   23JUN99
+Version:        3.17
+Entered-date:   15JUL99
 Description:    A modular screen saver and locker for the X Window System.
                 Highly customizable: allows the use of any program that
                 can draw on the root window as a display mode.
 Description:    A modular screen saver and locker for the X Window System.
                 Highly customizable: allows the use of any program that
                 can draw on the root window as a display mode.
@@ -10,15 +10,15 @@ Keywords:       screen saver, screen lock, lock, xlock, X11
 Author:         jwz@jwz.org (Jamie Zawinski)
 Maintained-by:  jwz@jwz.org (Jamie Zawinski)
 Primary-site:   http://www.jwz.org/xscreensaver/
 Author:         jwz@jwz.org (Jamie Zawinski)
 Maintained-by:  jwz@jwz.org (Jamie Zawinski)
 Primary-site:   http://www.jwz.org/xscreensaver/
-                1135K xscreensaver-3.16.tar.gz
+                1141K xscreensaver-3.17.tar.gz
                 35K  xscreensaver.README
                 1K   xscreensaver.lsm
 Alternate-site: sunsite.unc.edu /pub/Linux/X11/screensavers/
                 35K  xscreensaver.README
                 1K   xscreensaver.lsm
 Alternate-site: sunsite.unc.edu /pub/Linux/X11/screensavers/
-                1135K xscreensaver-3.16.tar.gz
+                1141K xscreensaver-3.17.tar.gz
                 35K  xscreensaver.README
                 1K   xscreensaver.lsm
 Alternate-site: ftp.x.org /contrib/applications/
                 35K  xscreensaver.README
                 1K   xscreensaver.lsm
 Alternate-site: ftp.x.org /contrib/applications/
-                1135K xscreensaver-3.16.tar.gz
+                1141K xscreensaver-3.17.tar.gz
                 35K  xscreensaver.README
                 1K   xscreensaver.lsm
 Platforms:      Linux, Irix, SunOS, Solaris, HPUX, AIX, FreeBSD, NetBSD,
                 35K  xscreensaver.README
                 1K   xscreensaver.lsm
 Platforms:      Linux, Irix, SunOS, Solaris, HPUX, AIX, FreeBSD, NetBSD,
index e3085f42377f3981208794350d2eb862105f2182..0197936eb3bc13a9661dcf89205d4355c45f10b2 100644 (file)
@@ -1,7 +1,7 @@
 Name: xscreensaver
 Summary: X screen saver and locker
 Vendor: Jamie Zawinski <jwz@jwz.org>
 Name: xscreensaver
 Summary: X screen saver and locker
 Vendor: Jamie Zawinski <jwz@jwz.org>
-Version: 3.16
+Version: 3.17
 Release: 1
 URL: http://www.jwz.org/xscreensaver/
 Source: xscreensaver-%{version}.tar.gz
 Release: 1
 URL: http://www.jwz.org/xscreensaver/
 Source: xscreensaver-%{version}.tar.gz