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