ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-5.01.tar.gz
[xscreensaver] / hacks / noseguy.c
index 4707d422b029b2a9d326c0e0fd62cccc8944166c..0db99601d09eb119a9602226ceea4cee46fe4d39 100644 (file)
@@ -1,4 +1,5 @@
-/* xscreensaver, Copyright (c) 1992 Jamie Zawinski <jwz@lucid.com>
+/* xscreensaver, Copyright (c) 1992, 1996, 1997, 1998, 2005, 2006
+ *  Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  */
 
 /* Make a little guy with a big nose and a hat wanter around the screen,
-   spewing out messages.  Derived from xnlock by Dan Heller <argv@sun.com>.
+   spewing out messages.  Derived from xnlock by 
+   Dan Heller <argv@danheller.com>.
  */
 
 #include "screenhack.h"
+#include "xpm-pixmap.h"
 #include <stdio.h>
 
-#if __STDC__
-extern FILE *popen (const char *, const char *);
-extern int pclose (FILE *);
+#ifdef HAVE_COCOA
+# define HAVE_XPM
 #endif
 
-#define Pixel unsigned long
+extern FILE *popen (const char *, const char *);
+extern int pclose (FILE *);
 
 #define font_height(font)              (font->ascent + font->descent)
-#define FONT_NAME                      "-*-times-*-*-*-*-18-*-*-*-*-*-*-*"
-
-static Display *dpy;
-static Window window;
-static int Width, Height;
-static GC fg_gc, bg_gc, text_fg_gc, text_bg_gc;
-static char *words, *get_words();
-static int x, y;
-static XFontStruct *font;
-static char *def_words = "I'm out running around.";
-static void init_images(), walk(), talk();
-static int think();
-static unsigned long interval, look(); 
-static Pixmap left0, left1, right0, right1;
-static Pixmap left_front, right_front, front, down;
-
-static char *program, *orig_program, *filename, *text;
+
+
+struct state {
+  Display *dpy;
+  Window window;
+  int Width, Height;
+  GC fg_gc, bg_gc, text_fg_gc, text_bg_gc;
+  char *words;
+  int x, y;
+  XFontStruct *font;
+
+  unsigned long interval;
+  Pixmap left1, left2, right1, right2;
+  Pixmap left_front, right_front, front, down;
+
+  char *program, *orig_program;
+
+  int state;   /* indicates states: walking or getting passwd */
+  int first_time;
+
+  void (*next_fn) (struct state *);
+
+  int move_length, move_dir;
+
+  int      walk_lastdir;
+  int      walk_up;
+  Pixmap   walk_frame;
+
+  int X, Y, talking;
+
+  struct {
+    int x, y, width, height;
+  } s_rect;
+
+  char word_buf[BUFSIZ];
+};
+
+static char *get_words (struct state *);
+static void walk (struct state *, int dir);
+static void talk (struct state *, int erase);
+static void talk_1 (struct state *);
+static int think (struct state *);
+static unsigned long look (struct state *); 
 
 #define FROM_ARGV    1
 #define FROM_PROGRAM 2
 #define FROM_FILE    3
 #define FROM_RESRC   4
-static int getwordsfrom;
 
 #define IS_MOVING  1
 #define GET_PASSWD 2
-static int state;      /* indicates states: walking or getting passwd */
-
-static void (*next_fn) ();
 
-#include "noses/nose.0.left"
-#include "noses/nose.1.left"
-#include "noses/nose.0.right"
-#include "noses/nose.1.right"
-#include "noses/nose.left.front"
-#include "noses/nose.right.front"
-#include "noses/nose.front"
-#include "noses/nose.down"
+#if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
+# include "images/noseguy/nose-f1.xpm"
+# include "images/noseguy/nose-f2.xpm"
+# include "images/noseguy/nose-f3.xpm"
+# include "images/noseguy/nose-f4.xpm"
+# include "images/noseguy/nose-l1.xpm"
+# include "images/noseguy/nose-l2.xpm"
+# include "images/noseguy/nose-r1.xpm"
+# include "images/noseguy/nose-r2.xpm"
+#else
+# include "images/noseguy/nose-f1.xbm"
+# include "images/noseguy/nose-f2.xbm"
+# include "images/noseguy/nose-f3.xbm"
+# include "images/noseguy/nose-f4.xbm"
+# include "images/noseguy/nose-l1.xbm"
+# include "images/noseguy/nose-l2.xbm"
+# include "images/noseguy/nose-r1.xbm"
+# include "images/noseguy/nose-r2.xbm"
+#endif
 
 static void
-init_images ()
+init_images (struct state *st)
 {
-  static Pixmap *images[] = {
-    &left0, &left1, &right0, &right1,
-    &left_front, &right_front, &front, &down
-  };
-  static unsigned char *bits[] = {
-    nose_0_left_bits, nose_1_left_bits, nose_0_right_bits,
-    nose_1_right_bits, nose_left_front_bits, nose_right_front_bits,
-    nose_front_bits, nose_down_bits
-  };
-  int i;
-
-  for (i = 0; i < sizeof (images) / sizeof (images[0]); i++)
+  Pixmap *images[8];
+#if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
+  char **bits[8];
+#else
+  unsigned char *bits[8];
+#endif
+
+  int i = 0;
+  images[i++] = &st->left1;
+  images[i++] = &st->left2;
+  images[i++] = &st->right1;
+  images[i++] = &st->right2;
+  images[i++] = &st->left_front;
+  images[i++] = &st->right_front;
+  images[i++] = &st->front;
+  images[i++] = &st->down;
+
+#if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
+
+  i = 0;
+  bits[i++] = nose_l1_xpm;
+  bits[i++] = nose_l2_xpm;
+  bits[i++] = nose_r1_xpm;
+  bits[i++] = nose_r2_xpm;
+  bits[i++] = nose_f2_xpm;
+  bits[i++] = nose_f3_xpm;
+  bits[i++] = nose_f1_xpm;
+  bits[i++] = nose_f4_xpm;
+
+  for (i = 0; i < sizeof (images) / sizeof(*images); i++)
+    {
+      Pixmap pixmap = xpm_data_to_pixmap (st->dpy, st->window, bits[i],
+                                          0, 0, 0);
+      if (!pixmap)
+       {
+         fprintf (stderr, "%s: Can't load nose images\n", progname);
+         exit (1);
+       }
+      *images[i] = pixmap;
+    }
+#else
+  i = 0;
+  bits[i++] = nose_l1_bits;
+  bits[i++] = nose_l2_bits;
+  bits[i++] = nose_r1_bits;
+  bits[i++] = nose_r2_bits;
+  bits[i++] = nose_f2_bits;
+  bits[i++] = nose_f3_bits;
+  bits[i++] = nose_f1_bits;
+  bits[i++] = nose_f4_bits;
+
+  for (i = 0; i < sizeof (images) / sizeof(*images); i++)
     if (!(*images[i] =
-         XCreatePixmapFromBitmapData(dpy, window,
+         XCreatePixmapFromBitmapData(st->dpy, st->window,
                                      (char *) bits[i], 64, 64, 1, 0, 1)))
       {
-       fprintf (stderr, "%s: Can't load nose images", progname);
+       fprintf (stderr, "%s: Can't load nose images\n", progname);
        exit (1);
       }
+#endif
 }
 
 #define LEFT   001
@@ -96,222 +173,223 @@ init_images ()
 #define Y_INCR 2
 
 static void
-move()
+move (struct state *st)
 {
-    static int      length,
-                    dir;
-
-    if (!length)
+    if (!st->move_length)
     {
        register int    tries = 0;
-       dir = 0;
-       if ((random() & 1) && think())
+       st->move_dir = 0;
+       if ((random() & 1) && think(st))
        {
-           talk(0);            /* sets timeout to itself */
+           talk(st, 0);                /* sets timeout to itself */
            return;
        }
-       if (!(random() % 3) && (interval = look()))
+       if (!(random() % 3) && (st->interval = look(st)))
        {
-           next_fn = move;
+           st->next_fn = move;
            return;
        }
-       interval = 20 + random() % 100;
+       st->interval = 20 + random() % 100;
        do
        {
            if (!tries)
-               length = Width / 100 + random() % 90, tries = 8;
+               st->move_length = st->Width / 100 + random() % 90, tries = 8;
            else
                tries--;
+           /* There maybe the case that we won't be able to exit from
+              this routine (especially when the geometry is too small)!!
+
+              Ensure that we can exit from this routine.
+            */
+#if 1
+           if (!tries && (st->move_length <= 1)) {
+             st->move_length = 1;
+             break;
+           }
+#endif
            switch (random() % 8)
            {
            case 0:
-               if (x - X_INCR * length >= 5)
-                   dir = LEFT;
+               if (st->x - X_INCR * st->move_length >= 5)
+                   st->move_dir = LEFT;
                break;
            case 1:
-               if (x + X_INCR * length <= Width - 70)
-                   dir = RIGHT;
+               if (st->x + X_INCR * st->move_length <= st->Width - 70)
+                   st->move_dir = RIGHT;
                break;
            case 2:
-               if (y - (Y_INCR * length) >= 5)
-                   dir = UP, interval = 40;
+               if (st->y - (Y_INCR * st->move_length) >= 5)
+                   st->move_dir = UP, st->interval = 40;
                break;
            case 3:
-               if (y + Y_INCR * length <= Height - 70)
-                   dir = DOWN, interval = 20;
+               if (st->y + Y_INCR * st->move_length <= st->Height - 70)
+                   st->move_dir = DOWN, st->interval = 20;
                break;
            case 4:
-               if (x - X_INCR * length >= 5 && y - (Y_INCR * length) >= 5)
-                   dir = (LEFT | UP);
+               if (st->x - X_INCR * st->move_length >= 5 && st->y - (Y_INCR * st->move_length) >= 5)
+                   st->move_dir = (LEFT | UP);
                break;
            case 5:
-               if (x + X_INCR * length <= Width - 70 &&
-                   y - Y_INCR * length >= 5)
-                   dir = (RIGHT | UP);
+               if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
+                   st->y - Y_INCR * st->move_length >= 5)
+                   st->move_dir = (RIGHT | UP);
                break;
            case 6:
-               if (x - X_INCR * length >= 5 &&
-                   y + Y_INCR * length <= Height - 70)
-                   dir = (LEFT | DOWN);
+               if (st->x - X_INCR * st->move_length >= 5 &&
+                   st->y + Y_INCR * st->move_length <= st->Height - 70)
+                   st->move_dir = (LEFT | DOWN);
                break;
            case 7:
-               if (x + X_INCR * length <= Width - 70 &&
-                   y + Y_INCR * length <= Height - 70)
-                   dir = (RIGHT | DOWN);
+               if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
+                   st->y + Y_INCR * st->move_length <= st->Height - 70)
+                   st->move_dir = (RIGHT | DOWN);
                break;
            default:
                /* No Defaults */
                break;
            }
-       } while (!dir);
+       } while (!st->move_dir);
     }
-    walk(dir);
-    --length;
-    next_fn = move;
+    if (st->move_dir)
+      walk(st, st->move_dir);
+    --st->move_length;
+    st->next_fn = move;
 }
 
+#ifdef HAVE_XPM
+# define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
+  XCopyArea (dpy,frame,window,gc,x,y,w,h,x2,y2)
+#else
+# define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
+  XCopyPlane(dpy,frame,window,gc,x,y,w,h,x2,y2,1L)
+#endif
+
 static void
-walk(dir)
-    register int    dir;
+walk (struct state *st, int dir)
 {
     register int    incr = 0;
-    static int      lastdir;
-    static int      up = 1;
-    static Pixmap   frame;
 
     if (dir & (LEFT | RIGHT))
-    {                          /* left/right movement (mabye up/down too) */
-       up = -up;               /* bouncing effect (even if hit a wall) */
+    {                          /* left/right movement (mabye up/st->down too) */
+       st->walk_up = -st->walk_up;             /* bouncing effect (even if hit a wall) */
        if (dir & LEFT)
        {
            incr = X_INCR;
-           frame = (up < 0) ? left0 : left1;
+           st->walk_frame = (st->walk_up < 0) ? st->left1 : st->left2;
        }
        else
        {
            incr = -X_INCR;
-           frame = (up < 0) ? right0 : right1;
+           st->walk_frame = (st->walk_up < 0) ? st->right1 : st->right2;
        }
-       if ((lastdir == FRONT || lastdir == DOWN) && dir & UP)
+       if ((st->walk_lastdir == FRONT || st->walk_lastdir == DOWN) && dir & UP)
        {
 
            /*
             * workaround silly bug that leaves screen dust when guy is
-            * facing forward or down and moves up-left/right.
+            * facing forward or st->down and moves up-left/right.
             */
-           XCopyPlane(dpy, frame, window, fg_gc, 0, 0, 64, 64, x, y, 1L);
-           XFlush(dpy);
+           COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
        }
        /* note that maybe neither UP nor DOWN is set! */
-       if (dir & UP && y > Y_INCR)
-           y -= Y_INCR;
-       else if (dir & DOWN && y < Height - 64)
-           y += Y_INCR;
+       if (dir & UP && st->y > Y_INCR)
+           st->y -= Y_INCR;
+       else if (dir & DOWN && st->y < st->Height - 64)
+           st->y += Y_INCR;
     }
-    /* Explicit up/down movement only (no left/right) */
+    /* Explicit up/st->down movement only (no left/right) */
     else if (dir == UP)
-       XCopyPlane(dpy, front, window, fg_gc,
-                  0, 0, 64, 64, x, y -= Y_INCR, 1L);
+       COPY(st->dpy, st->front, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y -= Y_INCR);
     else if (dir == DOWN)
-       XCopyPlane(dpy, down, window, fg_gc,
-                  0, 0, 64, 64, x, y += Y_INCR, 1L);
-    else if (dir == FRONT && frame != front)
+       COPY(st->dpy, st->down, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y += Y_INCR);
+    else if (dir == FRONT && st->walk_frame != st->front)
     {
-       if (up > 0)
-           up = -up;
-       if (lastdir & LEFT)
-           frame = left_front;
-       else if (lastdir & RIGHT)
-           frame = right_front;
+       if (st->walk_up > 0)
+           st->walk_up = -st->walk_up;
+       if (st->walk_lastdir & LEFT)
+           st->walk_frame = st->left_front;
+       else if (st->walk_lastdir & RIGHT)
+           st->walk_frame = st->right_front;
        else
-           frame = front;
-       XCopyPlane(dpy, frame, window, fg_gc, 0, 0, 64, 64, x, y, 1L);
+           st->walk_frame = st->front;
+       COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
     }
     if (dir & LEFT)
        while (--incr >= 0)
        {
-           XCopyPlane(dpy, frame, window, fg_gc,
-                      0, 0, 64, 64, --x, y + up, 1L);
-           XFlush(dpy);
+           COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, --st->x, st->y + st->walk_up);
        }
     else if (dir & RIGHT)
        while (++incr <= 0)
        {
-           XCopyPlane(dpy, frame, window, fg_gc,
-                      0, 0, 64, 64, ++x, y + up, 1L);
-           XFlush(dpy);
+           COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, ++st->x, st->y + st->walk_up);
        }
-    lastdir = dir;
+    st->walk_lastdir = dir;
 }
 
 static int
-think()
+think (struct state *st)
 {
     if (random() & 1)
-       walk(FRONT);
+       walk(st, FRONT);
     if (random() & 1)
     {
-       if (getwordsfrom == FROM_PROGRAM)
-           words = get_words(0, (char **) 0);
-       return 1;
+      st->words = get_words(st);
+      return 1;
     }
     return 0;
 }
 
-#define MAXLINES 40
+#define MAXLINES 25
+
+#undef BUFSIZ
+#define BUFSIZ ((MAXLINES + 1) * 100)
+
 
 static void
-talk(force_erase)
-    int             force_erase;
+talk (struct state *st, int force_erase)
 {
     int             width = 0,
                     height,
                     Z,
                     total = 0;
-    static int      X,
-                    Y,
-                    talking;
-    static struct
-    {
-       int             x,
-                       y,
-                       width,
-                       height;
-    }               s_rect;
     register char  *p,
                    *p2;
     char            buf[BUFSIZ],
                     args[MAXLINES][256];
 
     /* clear what we've written */
-    if (talking || force_erase)
+    if (st->talking || force_erase)
     {
-       if (!talking)
+       if (!st->talking)
            return;
-       XFillRectangle(dpy, window, bg_gc, s_rect.x - 5, s_rect.y - 5,
-                      s_rect.width + 10, s_rect.height + 10);
-       talking = 0;
+       XFillRectangle(st->dpy, st->window, st->bg_gc, st->s_rect.x - 5, st->s_rect.y - 5,
+                      st->s_rect.width + 10, st->s_rect.height + 10);
+       st->talking = 0;
        if (!force_erase)
-         next_fn = move;
-       interval = 0;
+         st->next_fn = move;
+       st->interval = 0;
        {
-         /* might as well check the window for size changes now... */
+         /* might as well check the st->window for size changes now... */
          XWindowAttributes xgwa;
-         XGetWindowAttributes (dpy, window, &xgwa);
-         Width = xgwa.width + 2;
-         Height = xgwa.height + 2;
+         XGetWindowAttributes (st->dpy, st->window, &xgwa);
+         st->Width = xgwa.width + 2;
+         st->Height = xgwa.height + 2;
        }
        return;
     }
-    talking = 1;
-    walk(FRONT);
-    p = strcpy(buf, words);
+    st->talking = 1;
+    walk(st, FRONT);
+    p = strcpy(buf, st->words);
+
+    for (p2 = p; *p2; p2++)
+      if (*p2 == '\t') *p2 = ' ';
 
-    if (!(p2 = index(p, '\n')) || !p2[1])
+    if (!(p2 = strchr(p, '\n')) || !p2[1])
       {
-       total = strlen (words);
-       strcpy (args[0], words);
-       width = XTextWidth(font, words, total);
+       total = strlen (st->words);
+       strcpy (args[0], st->words);
+       width = XTextWidth(st->font, st->words, total);
        height = 0;
       }
     else
@@ -320,18 +398,18 @@ talk(force_erase)
        {
          int             w;
          *p2 = 0;
-         if ((w = XTextWidth(font, p, p2 - p)) > width)
+         if ((w = XTextWidth(st->font, p, p2 - p)) > width)
            width = w;
          total += p2 - p;      /* total chars; count to determine reading
                                 * time */
          (void) strcpy(args[height], p);
          if (height == MAXLINES - 1)
            {
-             puts("Message too long!");
+             /* puts("Message too long!"); */
              break;
            }
          p = p2 + 1;
-         if (!(p2 = index(p, '\n')))
+         if (!(p2 = strchr(p, '\n')))
            break;
        }
     height++;
@@ -340,291 +418,261 @@ talk(force_erase)
      * Figure out the height and width in pixels (height, width) extend the
      * new box by 15 pixels on the sides (30 total) top and bottom.
      */
-    s_rect.width = width + 30;
-    s_rect.height = height * font_height(font) + 30;
-    if (x - s_rect.width - 10 < 5)
-       s_rect.x = 5;
-    else if ((s_rect.x = x + 32 - (s_rect.width + 15) / 2)
-            + s_rect.width + 15 > Width - 5)
-       s_rect.x = Width - 15 - s_rect.width;
-    if (y - s_rect.height - 10 < 5)
-       s_rect.y = y + 64 + 5;
+    st->s_rect.width = width + 30;
+    st->s_rect.height = height * font_height(st->font) + 30;
+    if (st->x - st->s_rect.width - 10 < 5)
+       st->s_rect.x = 5;
+    else if ((st->s_rect.x = st->x + 32 - (st->s_rect.width + 15) / 2)
+            + st->s_rect.width + 15 > st->Width - 5)
+       st->s_rect.x = st->Width - 15 - st->s_rect.width;
+    if (st->y - st->s_rect.height - 10 < 5)
+       st->s_rect.y = st->y + 64 + 5;
     else
-       s_rect.y = y - 5 - s_rect.height;
+       st->s_rect.y = st->y - 5 - st->s_rect.height;
 
-    XFillRectangle(dpy, window, text_bg_gc,
-        s_rect.x, s_rect.y, s_rect.width, s_rect.height);
+    XFillRectangle(st->dpy, st->window, st->text_bg_gc,
+        st->s_rect.x, st->s_rect.y, st->s_rect.width, st->s_rect.height);
 
     /* make a box that's 5 pixels thick. Then add a thin box inside it */
-    XSetLineAttributes(dpy, text_fg_gc, 5, 0, 0, 0);
-    XDrawRectangle(dpy, window, text_fg_gc,
-                  s_rect.x, s_rect.y, s_rect.width - 1, s_rect.height - 1);
-    XSetLineAttributes(dpy, text_fg_gc, 0, 0, 0, 0);
-    XDrawRectangle(dpy, window, text_fg_gc,
-        s_rect.x + 7, s_rect.y + 7, s_rect.width - 15, s_rect.height - 15);
+    XSetLineAttributes(st->dpy, st->text_fg_gc, 5, 0, 0, 0);
+    XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
+                  st->s_rect.x, st->s_rect.y, st->s_rect.width - 1, st->s_rect.height - 1);
+    XSetLineAttributes(st->dpy, st->text_fg_gc, 0, 0, 0, 0);
+    XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
+        st->s_rect.x + 7, st->s_rect.y + 7, st->s_rect.width - 15, st->s_rect.height - 15);
 
-    X = 15;
-    Y = 15 + font_height(font);
+    st->X = 15;
+    st->Y = 15 + font_height(st->font);
 
     /* now print each string in reverse order (start at bottom of box) */
     for (Z = 0; Z < height; Z++)
     {
-       XDrawString(dpy, window, text_fg_gc, s_rect.x + X, s_rect.y + Y,
-                   args[Z], strlen(args[Z]));
-       Y += font_height(font);
+        int L = strlen(args[Z]);
+        if (args[Z][L-1] == '\r' || args[Z][L-1] == '\n')
+          args[Z][--L] = 0;
+       XDrawString(st->dpy, st->window, st->text_fg_gc, st->s_rect.x + st->X, st->s_rect.y + st->Y,
+                   args[Z], L);
+       st->Y += font_height(st->font);
     }
-    interval = (total / 15) * 1000;
-    if (interval < 2000) interval = 2000;
-    next_fn = talk;
+    st->interval = (total / 15) * 1000;
+    if (st->interval < 2000) st->interval = 2000;
+    st->next_fn = talk_1;
 }
 
+static void
+talk_1 (struct state *st) 
+{
+  talk(st, 0);
+}
+
+
 static unsigned long
-look()
+look (struct state *st)
 {
     if (random() % 3)
     {
-       XCopyPlane(dpy, (random() & 1) ? down : front, window, fg_gc,
-                  0, 0, 64, 64, x, y, 1L);
+       COPY(st->dpy, (random() & 1) ? st->down : st->front, st->window, st->fg_gc,
+            0, 0, 64, 64, st->x, st->y);
        return 1000L;
     }
     if (!(random() % 5))
        return 0;
     if (random() % 3)
     {
-       XCopyPlane(dpy, (random() & 1) ? left_front : right_front,
-                  window, fg_gc, 0, 0, 64, 64, x, y, 1L);
+       COPY(st->dpy, (random() & 1) ? st->left_front : st->right_front,
+            st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
        return 1000L;
     }
     if (!(random() % 5))
        return 0;
-    XCopyPlane(dpy, (random() & 1) ? left0 : right0, window, fg_gc,
-              0, 0, 64, 64, x, y, 1L);
+    COPY(st->dpy, (random() & 1) ? st->left1 : st->right1, st->window, st->fg_gc,
+        0, 0, 64, 64, st->x, st->y);
     return 1000L;
 }
 
 
 static void
-init_words()
+init_words (struct state *st)
 {
-  char *mode = get_string_resource ("mode", "Mode");
-
-  program = get_string_resource ("program", "Program");
-  filename = get_string_resource ("filename", "Filename");
-  text = get_string_resource ("text", "Text");
-
-  if (program) /* get stderr on stdout, so it shows up on the window */
-    {
-      orig_program = program;
-      program = (char *) malloc (strlen (program) + 10);
-      strcpy (program, "( ");
-      strcat (program, orig_program);
-      strcat (program, " ) 2>&1");
-    }
+  st->program = get_string_resource (st->dpy, "program", "Program");
 
-  if (!mode || !strcmp (mode, "program"))
-    getwordsfrom = FROM_PROGRAM;
-  else if (!strcmp (mode, "file"))
-    getwordsfrom = FROM_FILE;
-  else if (!strcmp (mode, "string"))
-    getwordsfrom = FROM_RESRC;
-  else
-    {
-      fprintf (stderr,
-              "%s: mode must be program, file, or string, not %s\n",
-              progname, mode);
-      exit (1);
-    }
-
-  if (getwordsfrom == FROM_PROGRAM && !program)
-    {
-      fprintf (stderr, "%s: no program specified.\n", progname);
-      exit (1);
-    }
-  if (getwordsfrom == FROM_FILE && !filename)
+  if (st->program)     /* get stderr on stdout, so it shows up on the window */
     {
-      fprintf (stderr, "%s: no file specified.\n", progname);
-      exit (1);
+      st->orig_program = st->program;
+      st->program = (char *) malloc (strlen (st->program) + 10);
+      strcpy (st->program, "( ");
+      strcat (st->program, st->orig_program);
+      strcat (st->program, " ) 2>&1");
     }
 
-  words = get_words(); 
+  st->words = get_words(st);   
 }
 
 static char *
-get_words()
+get_words (struct state *st)
 {
     FILE           *pp;
-    static char     buf[BUFSIZ];
-    register char  *p = buf;
+    register char  *p = st->word_buf;
 
-    buf[0] = '\0';
+    st->word_buf[0] = '\0';
 
-    switch (getwordsfrom)
-    {
-    case FROM_PROGRAM:
-       if (pp = popen(program, "r"))
+       if ((pp = popen(st->program, "r")))
        {
-           while (fgets(p, sizeof(buf) - strlen(buf), pp))
+           while (fgets(p, sizeof(st->word_buf) - strlen(st->word_buf), pp))
            {
-               if (strlen(buf) + 1 < sizeof(buf))
-                   p = buf + strlen(buf);
+               if (strlen(st->word_buf) + 1 < sizeof(st->word_buf))
+                   p = st->word_buf + strlen(st->word_buf);
                else
                    break;
            }
            (void) pclose(pp);
-           if (! buf[0])
-             sprintf (buf, "\"%s\" produced no output!", orig_program);
-           p = buf;
-       }
-       else
-       {
-           perror(program);
-           p = def_words;
-       }
-       break;
-    case FROM_FILE:
-       if (pp = fopen(filename, "r"))
-       {
-           while (fgets(p, sizeof(buf) - strlen(buf), pp))
-           {
-               if (strlen(buf) + 1 < sizeof(buf))
-                   p = buf + strlen(buf);
-               else
-                   break;
-           }
-           (void) fclose(pp);
-           if (! buf[0])
-             sprintf (buf, "file \"%s\" is empty!", filename);
-           p = buf;
+           if (! st->word_buf[0])
+             sprintf (st->word_buf, "\"%s\" produced no output!", st->orig_program);
+           else if (!st->first_time &&
+                    (strstr (st->word_buf, ": not found") ||
+                     strstr (st->word_buf, ": Not found") ||
+                      strstr (st->word_buf, ": command not found") ||
+                      strstr (st->word_buf, ": Command not found")))
+             switch (random () % 20)
+               {
+               case 1: strcat (st->word_buf, "( Get with the st->program, bub. )\n");
+                 break;
+               case 2: strcat (st->word_buf,
+                 "( I blow my nose at you, you silly person! ) \n"); break;
+               case 3: strcat (st->word_buf,
+                 "\nThe resource you want to\nset is `noseguy.program'\n");
+                 break;
+               case 4:
+                 strcat(st->word_buf,"\nHelp!!  Help!!\nAAAAAAGGGGHHH!!  \n\n"); break;
+               case 5: strcpy (st->word_buf, "You have new mail.\n"); break;
+               case 6:
+                 strcat(st->word_buf,"( Hello?  Are you paying attention? )\n");break;
+               case 7:
+                 strcat (st->word_buf, "sh: what kind of fool do you take me for? \n");
+                 break;
+               }
+           st->first_time = 0;
+           p = st->word_buf;
        }
        else
        {
-         sprintf (buf, "couldn't read file \"%s\"!", filename);
-         p = buf;
+           perror(st->program);
        }
-       break;
-    case FROM_RESRC:
-       p = text;
-       break;
-    default:
-       p = def_words;
-       break;
-    }
 
-    if (!p || *p == '\0')
-       p = def_words;
     return p;
 }
 
 
 \f
-char *progclass = "Noseguy";
-
-char *defaults [] = {
-  "*background:                black",
-  "*foreground:                white",
-  "*mode:              program",
-  "*program:           fortune -s",
-  "noseguy.font:       -*-new century schoolbook-*-r-*-*-*-180-*-*-*-*-*-*",
+static const char *noseguy_defaults [] = {
+  ".background:            black",
+  ".foreground:            #CCCCCC",
+  "*textForeground: black",
+  "*textBackground: #CCCCCC",
+  "*program:    xscreensaver-text --cols 40 | head -n15",
+  ".font:       -*-new century schoolbook-*-r-*-*-*-180-*-*-*-*-*-*",
   0
 };
 
-XrmOptionDescRec options [] = {
-  { "-mode",           ".mode",                XrmoptionSepArg, 0 },
+static XrmOptionDescRec noseguy_options [] = {
   { "-program",                ".program",             XrmoptionSepArg, 0 },
-  { "-text",           ".text",                XrmoptionSepArg, 0 },
-  { "-filename",       ".filename",            XrmoptionSepArg, 0 },
   { "-font",           ".font",                XrmoptionSepArg, 0 },
   { "-text-foreground",        ".textForeground",      XrmoptionSepArg, 0 },
-  { "-text-background",        ".textBackground",      XrmoptionSepArg, 0 }
+  { "-text-background",        ".textBackground",      XrmoptionSepArg, 0 },
+  { 0, 0, 0, 0 }
 };
-int options_size = (sizeof (options) / sizeof (options[0]));
 
 
-static void
-noseguy_init (d, w)
-     Display *d;
-     Window w;
+static void *
+noseguy_init (Display *d, Window w)
 {
-  Pixel fg, bg, text_fg, text_bg;
+  struct state *st = (struct state *) calloc (1, sizeof(*st));
+  unsigned long fg, bg, text_fg, text_bg;
   XWindowAttributes xgwa;
   Colormap cmap;
-  char *fontname = get_string_resource ("font", "Font");
-  char **list;
-  int foo, i;
+  char *fontname;
   XGCValues gcvalues;
-  dpy = d;
-  window = w;
-  XGetWindowAttributes (dpy, window, &xgwa);
-  Width = xgwa.width + 2;
-  Height = xgwa.height + 2;
+  st->dpy = d;
+  st->window = w;
+  st->first_time = 1;
+
+  fontname = get_string_resource (st->dpy, "font", "Font");
+  XGetWindowAttributes (st->dpy, st->window, &xgwa);
+  st->Width = xgwa.width + 2;
+  st->Height = xgwa.height + 2;
   cmap = xgwa.colormap;
 
-  init_words();
-  init_images();
+  init_words(st);
+  init_images(st);
 
-  if (!fontname || !(font = XLoadQueryFont(dpy, fontname)))
-    {
-       list = XListFonts(dpy, FONT_NAME, 32767, &foo);
-       for (i = 0; i < foo; i++)
-           if (font = XLoadQueryFont(dpy, list[i]))
-               break;
-       if (!font)
-         {
-           fprintf (stderr, "%s: Can't find a large font.", progname);
-           exit (1);
-         }
-       XFreeFontNames(list);
-    }
+  if (!fontname || !*fontname)
+    fprintf (stderr, "%s: no font specified.\n", progname);
+  st->font = XLoadQueryFont(st->dpy, fontname);
+  if (!st->font)
+    fprintf (stderr, "%s: could not load font %s.\n", progname, fontname);
 
-  fg = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
-  bg = get_pixel_resource ("background", "Background", dpy, cmap);
-  text_fg = get_pixel_resource ("textForeground", "Foreground", dpy, cmap);
-  text_bg = get_pixel_resource ("textBackground", "Background", dpy, cmap);
+  fg = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
+  bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
+  text_fg = get_pixel_resource (st->dpy, cmap, "textForeground", "Foreground");
+  text_bg = get_pixel_resource (st->dpy, cmap, "textBackground", "Background");
   /* notice when unspecified */
-  if (! get_string_resource ("textForeground", "Foreground"))
+  if (! get_string_resource (st->dpy, "textForeground", "Foreground"))
     text_fg = bg;
-  if (! get_string_resource ("textBackground", "Background"))
+  if (! get_string_resource (st->dpy, "textBackground", "Background"))
     text_bg = fg;
 
-  gcvalues.font = font->fid;
-  gcvalues.graphics_exposures = False;
+  gcvalues.font = st->font->fid;
   gcvalues.foreground = fg;
   gcvalues.background = bg;
-  fg_gc = XCreateGC (dpy, window,
-                    GCForeground|GCBackground|GCGraphicsExposures|GCFont,
+  st->fg_gc = XCreateGC (st->dpy, st->window,
+                         GCForeground|GCBackground|GCFont,
                     &gcvalues);
   gcvalues.foreground = bg;
   gcvalues.background = fg;
-  bg_gc = XCreateGC (dpy, window,
-                    GCForeground|GCBackground|GCGraphicsExposures|GCFont,
+  st->bg_gc = XCreateGC (st->dpy, st->window,
+                         GCForeground|GCBackground|GCFont,
                     &gcvalues);
   gcvalues.foreground = text_fg;
   gcvalues.background = text_bg;
-  text_fg_gc = XCreateGC (dpy, window,
-                         GCForeground|GCBackground|GCGraphicsExposures|GCFont,
+  st->text_fg_gc = XCreateGC (st->dpy, st->window,
+                              GCForeground|GCBackground|GCFont,
                          &gcvalues);
   gcvalues.foreground = text_bg;
   gcvalues.background = text_fg;
-  text_bg_gc = XCreateGC (dpy, window,
-                         GCForeground|GCBackground|GCGraphicsExposures|GCFont,
+  st->text_bg_gc = XCreateGC (st->dpy, st->window, 
+                              GCForeground|GCBackground|GCFont,
                          &gcvalues);
-  x = Width / 2;
-  y = Height / 2;
-  state = IS_MOVING;
+  st->x = st->Width / 2;
+  st->y = st->Height / 2;
+  st->state = IS_MOVING;
+  st->next_fn = move;
+  st->walk_up = 1;
+  return st;
 }
      
-void
-screenhack (d, w)
-     Display *d;
-     Window w;
+static unsigned long
+noseguy_draw (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
+  st->next_fn(st);
+  return (st->interval * 1000);
+}
+
+static void
+noseguy_reshape (Display *dpy, Window window, void *closure, 
+                 unsigned int w, unsigned int h)
+{
+}
+
+static Bool
+noseguy_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+  return False;
+}
+
+static void
+noseguy_free (Display *dpy, Window window, void *closure)
 {
-  noseguy_init (d, w);
-  next_fn = move;
-  while (1)
-    {
-      next_fn (0);
-      XSync (dpy, True);
-      usleep (interval * 1000);
-    }
 }
 
+XSCREENSAVER_MODULE ("NoseGuy", noseguy)