http://www.ibiblio.org/pub/historic-linux/ftp-archives/sunsite.unc.edu/Sep-29-1996...
[xscreensaver] / hacks / noseguy.c
1 /* xscreensaver, Copyright (c) 1992 Jamie Zawinski <jwz@netscape.com>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* Make a little guy with a big nose and a hat wanter around the screen,
13    spewing out messages.  Derived from xnlock by Dan Heller <argv@sun.com>.
14  */
15
16 #include "screenhack.h"
17 #include <stdio.h>
18
19 #if __STDC__
20 extern FILE *popen (const char *, const char *);
21 extern int pclose (FILE *);
22 #endif
23
24 #define Pixel unsigned long
25
26 #define font_height(font)               (font->ascent + font->descent)
27 #define FONT_NAME                       "-*-times-*-*-*-*-18-*-*-*-*-*-*-*"
28
29 static Display *dpy;
30 static Window window;
31 static int Width, Height;
32 static GC fg_gc, bg_gc, text_fg_gc, text_bg_gc;
33 static char *words, *get_words();
34 static int x, y;
35 static XFontStruct *font;
36 static char *def_words = "I'm out running around.";
37 static void init_images(), walk(), talk();
38 static int think();
39 static unsigned long interval, look(); 
40 static Pixmap left0, left1, right0, right1;
41 static Pixmap left_front, right_front, front, down;
42
43 static char *program, *orig_program, *filename, *text;
44
45 #define FROM_ARGV    1
46 #define FROM_PROGRAM 2
47 #define FROM_FILE    3
48 #define FROM_RESRC   4
49 static int getwordsfrom;
50
51 #define IS_MOVING  1
52 #define GET_PASSWD 2
53 static int state;       /* indicates states: walking or getting passwd */
54
55 static void (*next_fn) ();
56
57 #include "noses/nose.0.left"
58 #include "noses/nose.1.left"
59 #include "noses/nose.0.right"
60 #include "noses/nose.1.right"
61 #include "noses/nose.left.front"
62 #include "noses/nose.right.front"
63 #include "noses/nose.front"
64 #include "noses/nose.down"
65
66 static void
67 init_images ()
68 {
69   static Pixmap *images[] = {
70     &left0, &left1, &right0, &right1,
71     &left_front, &right_front, &front, &down
72   };
73   static unsigned char *bits[] = {
74     nose_0_left_bits, nose_1_left_bits, nose_0_right_bits,
75     nose_1_right_bits, nose_left_front_bits, nose_right_front_bits,
76     nose_front_bits, nose_down_bits
77   };
78   int i;
79
80   for (i = 0; i < sizeof (images) / sizeof (images[0]); i++)
81     if (!(*images[i] =
82           XCreatePixmapFromBitmapData(dpy, window,
83                                       (char *) bits[i], 64, 64, 1, 0, 1)))
84       {
85         fprintf (stderr, "%s: Can't load nose images", progname);
86         exit (1);
87       }
88 }
89
90 #define LEFT    001
91 #define RIGHT   002
92 #define DOWN    004
93 #define UP      010
94 #define FRONT   020
95 #define X_INCR 3
96 #define Y_INCR 2
97
98 static void
99 move()
100 {
101     static int      length,
102                     dir;
103
104     if (!length)
105     {
106         register int    tries = 0;
107         dir = 0;
108         if ((random() & 1) && think())
109         {
110             talk(0);            /* sets timeout to itself */
111             return;
112         }
113         if (!(random() % 3) && (interval = look()))
114         {
115             next_fn = move;
116             return;
117         }
118         interval = 20 + random() % 100;
119         do
120         {
121             if (!tries)
122                 length = Width / 100 + random() % 90, tries = 8;
123             else
124                 tries--;
125             switch (random() % 8)
126             {
127             case 0:
128                 if (x - X_INCR * length >= 5)
129                     dir = LEFT;
130                 break;
131             case 1:
132                 if (x + X_INCR * length <= Width - 70)
133                     dir = RIGHT;
134                 break;
135             case 2:
136                 if (y - (Y_INCR * length) >= 5)
137                     dir = UP, interval = 40;
138                 break;
139             case 3:
140                 if (y + Y_INCR * length <= Height - 70)
141                     dir = DOWN, interval = 20;
142                 break;
143             case 4:
144                 if (x - X_INCR * length >= 5 && y - (Y_INCR * length) >= 5)
145                     dir = (LEFT | UP);
146                 break;
147             case 5:
148                 if (x + X_INCR * length <= Width - 70 &&
149                     y - Y_INCR * length >= 5)
150                     dir = (RIGHT | UP);
151                 break;
152             case 6:
153                 if (x - X_INCR * length >= 5 &&
154                     y + Y_INCR * length <= Height - 70)
155                     dir = (LEFT | DOWN);
156                 break;
157             case 7:
158                 if (x + X_INCR * length <= Width - 70 &&
159                     y + Y_INCR * length <= Height - 70)
160                     dir = (RIGHT | DOWN);
161                 break;
162             default:
163                 /* No Defaults */
164                 break;
165             }
166         } while (!dir);
167     }
168     walk(dir);
169     --length;
170     next_fn = move;
171 }
172
173 static void
174 walk(dir)
175     register int    dir;
176 {
177     register int    incr = 0;
178     static int      lastdir;
179     static int      up = 1;
180     static Pixmap   frame;
181
182     if (dir & (LEFT | RIGHT))
183     {                           /* left/right movement (mabye up/down too) */
184         up = -up;               /* bouncing effect (even if hit a wall) */
185         if (dir & LEFT)
186         {
187             incr = X_INCR;
188             frame = (up < 0) ? left0 : left1;
189         }
190         else
191         {
192             incr = -X_INCR;
193             frame = (up < 0) ? right0 : right1;
194         }
195         if ((lastdir == FRONT || lastdir == DOWN) && dir & UP)
196         {
197
198             /*
199              * workaround silly bug that leaves screen dust when guy is
200              * facing forward or down and moves up-left/right.
201              */
202             XCopyPlane(dpy, frame, window, fg_gc, 0, 0, 64, 64, x, y, 1L);
203             XFlush(dpy);
204         }
205         /* note that maybe neither UP nor DOWN is set! */
206         if (dir & UP && y > Y_INCR)
207             y -= Y_INCR;
208         else if (dir & DOWN && y < Height - 64)
209             y += Y_INCR;
210     }
211     /* Explicit up/down movement only (no left/right) */
212     else if (dir == UP)
213         XCopyPlane(dpy, front, window, fg_gc,
214                    0, 0, 64, 64, x, y -= Y_INCR, 1L);
215     else if (dir == DOWN)
216         XCopyPlane(dpy, down, window, fg_gc,
217                    0, 0, 64, 64, x, y += Y_INCR, 1L);
218     else if (dir == FRONT && frame != front)
219     {
220         if (up > 0)
221             up = -up;
222         if (lastdir & LEFT)
223             frame = left_front;
224         else if (lastdir & RIGHT)
225             frame = right_front;
226         else
227             frame = front;
228         XCopyPlane(dpy, frame, window, fg_gc, 0, 0, 64, 64, x, y, 1L);
229     }
230     if (dir & LEFT)
231         while (--incr >= 0)
232         {
233             XCopyPlane(dpy, frame, window, fg_gc,
234                        0, 0, 64, 64, --x, y + up, 1L);
235             XFlush(dpy);
236         }
237     else if (dir & RIGHT)
238         while (++incr <= 0)
239         {
240             XCopyPlane(dpy, frame, window, fg_gc,
241                        0, 0, 64, 64, ++x, y + up, 1L);
242             XFlush(dpy);
243         }
244     lastdir = dir;
245 }
246
247 static int
248 think()
249 {
250     if (random() & 1)
251         walk(FRONT);
252     if (random() & 1)
253     {
254         if (getwordsfrom == FROM_PROGRAM)
255             words = get_words(0, (char **) 0);
256         return 1;
257     }
258     return 0;
259 }
260
261 #define MAXLINES 40
262
263 static void
264 talk(force_erase)
265     int             force_erase;
266 {
267     int             width = 0,
268                     height,
269                     Z,
270                     total = 0;
271     static int      X,
272                     Y,
273                     talking;
274     static struct
275     {
276         int             x,
277                         y,
278                         width,
279                         height;
280     }               s_rect;
281     register char  *p,
282                    *p2;
283     char            buf[BUFSIZ],
284                     args[MAXLINES][256];
285
286     /* clear what we've written */
287     if (talking || force_erase)
288     {
289         if (!talking)
290             return;
291         XFillRectangle(dpy, window, bg_gc, s_rect.x - 5, s_rect.y - 5,
292                        s_rect.width + 10, s_rect.height + 10);
293         talking = 0;
294         if (!force_erase)
295           next_fn = move;
296         interval = 0;
297         {
298           /* might as well check the window for size changes now... */
299           XWindowAttributes xgwa;
300           XGetWindowAttributes (dpy, window, &xgwa);
301           Width = xgwa.width + 2;
302           Height = xgwa.height + 2;
303         }
304         return;
305     }
306     talking = 1;
307     walk(FRONT);
308     p = strcpy(buf, words);
309
310     if (!(p2 = index(p, '\n')) || !p2[1])
311       {
312         total = strlen (words);
313         strcpy (args[0], words);
314         width = XTextWidth(font, words, total);
315         height = 0;
316       }
317     else
318       /* p2 now points to the first '\n' */
319       for (height = 0; p; height++)
320         {
321           int             w;
322           *p2 = 0;
323           if ((w = XTextWidth(font, p, p2 - p)) > width)
324             width = w;
325           total += p2 - p;      /* total chars; count to determine reading
326                                  * time */
327           (void) strcpy(args[height], p);
328           if (height == MAXLINES - 1)
329             {
330               puts("Message too long!");
331               break;
332             }
333           p = p2 + 1;
334           if (!(p2 = index(p, '\n')))
335             break;
336         }
337     height++;
338
339     /*
340      * Figure out the height and width in pixels (height, width) extend the
341      * new box by 15 pixels on the sides (30 total) top and bottom.
342      */
343     s_rect.width = width + 30;
344     s_rect.height = height * font_height(font) + 30;
345     if (x - s_rect.width - 10 < 5)
346         s_rect.x = 5;
347     else if ((s_rect.x = x + 32 - (s_rect.width + 15) / 2)
348              + s_rect.width + 15 > Width - 5)
349         s_rect.x = Width - 15 - s_rect.width;
350     if (y - s_rect.height - 10 < 5)
351         s_rect.y = y + 64 + 5;
352     else
353         s_rect.y = y - 5 - s_rect.height;
354
355     XFillRectangle(dpy, window, text_bg_gc,
356          s_rect.x, s_rect.y, s_rect.width, s_rect.height);
357
358     /* make a box that's 5 pixels thick. Then add a thin box inside it */
359     XSetLineAttributes(dpy, text_fg_gc, 5, 0, 0, 0);
360     XDrawRectangle(dpy, window, text_fg_gc,
361                    s_rect.x, s_rect.y, s_rect.width - 1, s_rect.height - 1);
362     XSetLineAttributes(dpy, text_fg_gc, 0, 0, 0, 0);
363     XDrawRectangle(dpy, window, text_fg_gc,
364          s_rect.x + 7, s_rect.y + 7, s_rect.width - 15, s_rect.height - 15);
365
366     X = 15;
367     Y = 15 + font_height(font);
368
369     /* now print each string in reverse order (start at bottom of box) */
370     for (Z = 0; Z < height; Z++)
371     {
372         XDrawString(dpy, window, text_fg_gc, s_rect.x + X, s_rect.y + Y,
373                     args[Z], strlen(args[Z]));
374         Y += font_height(font);
375     }
376     interval = (total / 15) * 1000;
377     if (interval < 2000) interval = 2000;
378     next_fn = talk;
379 }
380
381 static unsigned long
382 look()
383 {
384     if (random() % 3)
385     {
386         XCopyPlane(dpy, (random() & 1) ? down : front, window, fg_gc,
387                    0, 0, 64, 64, x, y, 1L);
388         return 1000L;
389     }
390     if (!(random() % 5))
391         return 0;
392     if (random() % 3)
393     {
394         XCopyPlane(dpy, (random() & 1) ? left_front : right_front,
395                    window, fg_gc, 0, 0, 64, 64, x, y, 1L);
396         return 1000L;
397     }
398     if (!(random() % 5))
399         return 0;
400     XCopyPlane(dpy, (random() & 1) ? left0 : right0, window, fg_gc,
401                0, 0, 64, 64, x, y, 1L);
402     return 1000L;
403 }
404
405
406 static void
407 init_words()
408 {
409   char *mode = get_string_resource ("mode", "Mode");
410
411   program = get_string_resource ("program", "Program");
412   filename = get_string_resource ("filename", "Filename");
413   text = get_string_resource ("text", "Text");
414
415   if (program)  /* get stderr on stdout, so it shows up on the window */
416     {
417       orig_program = program;
418       program = (char *) malloc (strlen (program) + 10);
419       strcpy (program, "( ");
420       strcat (program, orig_program);
421       strcat (program, " ) 2>&1");
422     }
423
424   if (!mode || !strcmp (mode, "program"))
425     getwordsfrom = FROM_PROGRAM;
426   else if (!strcmp (mode, "file"))
427     getwordsfrom = FROM_FILE;
428   else if (!strcmp (mode, "string"))
429     getwordsfrom = FROM_RESRC;
430   else
431     {
432       fprintf (stderr,
433                "%s: mode must be program, file, or string, not %s\n",
434                progname, mode);
435       exit (1);
436     }
437
438   if (getwordsfrom == FROM_PROGRAM && !program)
439     {
440       fprintf (stderr, "%s: no program specified.\n", progname);
441       exit (1);
442     }
443   if (getwordsfrom == FROM_FILE && !filename)
444     {
445       fprintf (stderr, "%s: no file specified.\n", progname);
446       exit (1);
447     }
448
449   words = get_words();  
450 }
451
452 static int first_time = 1;
453
454 static char *
455 get_words()
456 {
457     FILE           *pp;
458     static char     buf[BUFSIZ];
459     register char  *p = buf;
460
461     buf[0] = '\0';
462
463     switch (getwordsfrom)
464     {
465     case FROM_PROGRAM:
466         if ((pp = popen(program, "r")))
467         {
468             while (fgets(p, sizeof(buf) - strlen(buf), pp))
469             {
470                 if (strlen(buf) + 1 < sizeof(buf))
471                     p = buf + strlen(buf);
472                 else
473                     break;
474             }
475             (void) pclose(pp);
476             if (! buf[0])
477               sprintf (buf, "\"%s\" produced no output!", orig_program);
478             else if (!first_time &&
479                      !strcmp (buf, "sh: fortune: not found\n"))
480               switch (random () % 20)
481                 {
482                 case 1: strcat (buf, "( Get with the program, bub. )\n");
483                   break;
484                 case 2: strcat (buf,
485                   "( I blow my nose at you, you silly person! ) \n"); break;
486                 case 3: strcat (buf,
487                   "\nThe resource you want to\nset is `noseguy.program'\n");
488                   break;
489                 case 4:
490                   strcat(buf,"\nHelp!!  Help!!\nAAAAAAGGGGHHH!!  \n\n"); break;
491                 case 5: strcpy (buf, "You have new mail.\n"); break;
492                 case 6:
493                   strcat(buf,"( Hello?  Are you paying attention? )\n");break;
494                 case 7:
495                   strcat (buf, "sh: what kind of fool do you take me for? \n");
496                   break;
497                 }
498             first_time = 0;
499             p = buf;
500         }
501         else
502         {
503             perror(program);
504             p = def_words;
505         }
506         break;
507     case FROM_FILE:
508         if ((pp = fopen(filename, "r")))
509         {
510             while (fgets(p, sizeof(buf) - strlen(buf), pp))
511             {
512                 if (strlen(buf) + 1 < sizeof(buf))
513                     p = buf + strlen(buf);
514                 else
515                     break;
516             }
517             (void) fclose(pp);
518             if (! buf[0])
519               sprintf (buf, "file \"%s\" is empty!", filename);
520             p = buf;
521         }
522         else
523         {
524           sprintf (buf, "couldn't read file \"%s\"!", filename);
525           p = buf;
526         }
527         break;
528     case FROM_RESRC:
529         p = text;
530         break;
531     default:
532         p = def_words;
533         break;
534     }
535
536     if (!p || *p == '\0')
537         p = def_words;
538     return p;
539 }
540
541
542 \f
543 char *progclass = "Noseguy";
544
545 char *defaults [] = {
546   "Noseguy.background:  black",         /* to placate SGI */
547   "Noseguy.foreground:  white",
548   "*mode:               program",
549   "*program:            fortune -s",
550   "noseguy.font:        -*-new century schoolbook-*-r-*-*-*-180-*-*-*-*-*-*",
551   0
552 };
553
554 XrmOptionDescRec options [] = {
555   { "-mode",            ".mode",                XrmoptionSepArg, 0 },
556   { "-program",         ".program",             XrmoptionSepArg, 0 },
557   { "-text",            ".text",                XrmoptionSepArg, 0 },
558   { "-filename",        ".filename",            XrmoptionSepArg, 0 },
559   { "-font",            ".font",                XrmoptionSepArg, 0 },
560   { "-text-foreground", ".textForeground",      XrmoptionSepArg, 0 },
561   { "-text-background", ".textBackground",      XrmoptionSepArg, 0 }
562 };
563 int options_size = (sizeof (options) / sizeof (options[0]));
564
565
566 static void
567 noseguy_init (d, w)
568      Display *d;
569      Window w;
570 {
571   Pixel fg, bg, text_fg, text_bg;
572   XWindowAttributes xgwa;
573   Colormap cmap;
574   char *fontname = get_string_resource ("font", "Font");
575   char **list;
576   int foo, i;
577   XGCValues gcvalues;
578   dpy = d;
579   window = w;
580   XGetWindowAttributes (dpy, window, &xgwa);
581   Width = xgwa.width + 2;
582   Height = xgwa.height + 2;
583   cmap = xgwa.colormap;
584
585   init_words();
586   init_images();
587
588   if (!fontname || !(font = XLoadQueryFont(dpy, fontname)))
589     {
590         list = XListFonts(dpy, FONT_NAME, 32767, &foo);
591         for (i = 0; i < foo; i++)
592             if ((font = XLoadQueryFont(dpy, list[i])))
593                 break;
594         if (!font)
595           {
596             fprintf (stderr, "%s: Can't find a large font.", progname);
597             exit (1);
598           }
599         XFreeFontNames(list);
600     }
601
602   fg = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
603   bg = get_pixel_resource ("background", "Background", dpy, cmap);
604   text_fg = get_pixel_resource ("textForeground", "Foreground", dpy, cmap);
605   text_bg = get_pixel_resource ("textBackground", "Background", dpy, cmap);
606   /* notice when unspecified */
607   if (! get_string_resource ("textForeground", "Foreground"))
608     text_fg = bg;
609   if (! get_string_resource ("textBackground", "Background"))
610     text_bg = fg;
611
612   gcvalues.font = font->fid;
613   gcvalues.graphics_exposures = False;
614   gcvalues.foreground = fg;
615   gcvalues.background = bg;
616   fg_gc = XCreateGC (dpy, window,
617                      GCForeground|GCBackground|GCGraphicsExposures|GCFont,
618                      &gcvalues);
619   gcvalues.foreground = bg;
620   gcvalues.background = fg;
621   bg_gc = XCreateGC (dpy, window,
622                      GCForeground|GCBackground|GCGraphicsExposures|GCFont,
623                      &gcvalues);
624   gcvalues.foreground = text_fg;
625   gcvalues.background = text_bg;
626   text_fg_gc = XCreateGC (dpy, window,
627                           GCForeground|GCBackground|GCGraphicsExposures|GCFont,
628                           &gcvalues);
629   gcvalues.foreground = text_bg;
630   gcvalues.background = text_fg;
631   text_bg_gc = XCreateGC (dpy, window,
632                           GCForeground|GCBackground|GCGraphicsExposures|GCFont,
633                           &gcvalues);
634   x = Width / 2;
635   y = Height / 2;
636   state = IS_MOVING;
637 }
638      
639 void
640 screenhack (d, w)
641      Display *d;
642      Window w;
643 {
644   noseguy_init (d, w);
645   next_fn = move;
646   while (1)
647     {
648       next_fn (0);
649       XSync (dpy, True);
650       usleep (interval * 1000);
651     }
652 }
653