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