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