source: trunk/third/xscreensaver/hacks/noseguy.c @ 20148

Revision 20148, 16.8 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
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
22extern FILE *popen (const char *, const char *);
23extern int pclose (FILE *);
24
25#define font_height(font)               (font->ascent + font->descent)
26#define FONT_NAME                       "-*-times-*-*-*-*-18-*-*-*-*-*-*-*"
27
28static Display *dpy;
29static Window window;
30static int Width, Height;
31static GC fg_gc, bg_gc, text_fg_gc, text_bg_gc;
32static char *words;
33static char *get_words (void);
34static int x, y;
35static XFontStruct *font;
36static char *def_words = "I'm out running around.";
37static void walk (int dir);
38static void talk (int erase);
39static void talk_1 (void);
40static int think (void);
41static unsigned long interval;
42static unsigned long look (void);
43static Pixmap left1, left2, right1, right2;
44static Pixmap left_front, right_front, front, down;
45
46static 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
52static int getwordsfrom;
53
54#define IS_MOVING  1
55#define GET_PASSWD 2
56static int state;       /* indicates states: walking or getting passwd */
57
58static 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
80static void
81init_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
132static void
133move (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
215static void
216walk(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
284static int
285think (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 25
299
300#undef BUFSIZ
301#define BUFSIZ ((MAXLINES + 1) * 100)
302
303
304static void
305talk(int force_erase)
306{
307    int             width = 0,
308                    height,
309                    Z,
310                    total = 0;
311    static int      X,
312                    Y,
313                    talking;
314    static struct
315    {
316        int             x,
317                        y,
318                        width,
319                        height;
320    }               s_rect;
321    register char  *p,
322                   *p2;
323    char            buf[BUFSIZ],
324                    args[MAXLINES][256];
325
326    /* clear what we've written */
327    if (talking || force_erase)
328    {
329        if (!talking)
330            return;
331        XFillRectangle(dpy, window, bg_gc, s_rect.x - 5, s_rect.y - 5,
332                       s_rect.width + 10, s_rect.height + 10);
333        talking = 0;
334        if (!force_erase)
335          next_fn = move;
336        interval = 0;
337        {
338          /* might as well check the window for size changes now... */
339          XWindowAttributes xgwa;
340          XGetWindowAttributes (dpy, window, &xgwa);
341          Width = xgwa.width + 2;
342          Height = xgwa.height + 2;
343        }
344        return;
345    }
346    talking = 1;
347    walk(FRONT);
348    p = strcpy(buf, words);
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
421static void talk_1 (void)
422{
423  talk(0);
424}
425
426
427static unsigned long
428look (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
452static void
453init_words (void)
454{
455  char *mode = get_string_resource ("mode", "Mode");
456
457  program = get_string_resource ("program", "Program");
458  filename = get_string_resource ("filename", "Filename");
459  text = get_string_resource ("text", "Text");
460
461  if (program)  /* get stderr on stdout, so it shows up on the window */
462    {
463      orig_program = program;
464      program = (char *) malloc (strlen (program) + 10);
465      strcpy (program, "( ");
466      strcat (program, orig_program);
467      strcat (program, " ) 2>&1");
468    }
469
470  if (!mode || !strcmp (mode, "program"))
471    getwordsfrom = FROM_PROGRAM;
472  else if (!strcmp (mode, "file"))
473    getwordsfrom = FROM_FILE;
474  else if (!strcmp (mode, "string"))
475    getwordsfrom = FROM_RESRC;
476  else
477    {
478      fprintf (stderr,
479               "%s: mode must be program, file, or string, not %s\n",
480               progname, mode);
481      exit (1);
482    }
483
484  if (getwordsfrom == FROM_PROGRAM && !program)
485    {
486      fprintf (stderr, "%s: no program specified.\n", progname);
487      exit (1);
488    }
489  if (getwordsfrom == FROM_FILE && !filename)
490    {
491      fprintf (stderr, "%s: no file specified.\n", progname);
492      exit (1);
493    }
494
495  words = get_words(); 
496}
497
498static int first_time = 1;
499
500static char *
501get_words (void)
502{
503    FILE           *pp;
504    static char     buf[BUFSIZ];
505    register char  *p = buf;
506
507    buf[0] = '\0';
508
509    switch (getwordsfrom)
510    {
511    case FROM_PROGRAM:
512#ifndef VMS
513        if ((pp = popen(program, "r")))
514        {
515            while (fgets(p, sizeof(buf) - strlen(buf), pp))
516            {
517                if (strlen(buf) + 1 < sizeof(buf))
518                    p = buf + strlen(buf);
519                else
520                    break;
521            }
522            (void) pclose(pp);
523            if (! buf[0])
524              sprintf (buf, "\"%s\" produced no output!", orig_program);
525            else if (!first_time &&
526                     (strstr (buf, ": not found") ||
527                      strstr (buf, ": Not found") ||
528                      strstr (buf, ": command not found") ||
529                      strstr (buf, ": Command not found")))
530              switch (random () % 20)
531                {
532                case 1: strcat (buf, "( Get with the program, bub. )\n");
533                  break;
534                case 2: strcat (buf,
535                  "( I blow my nose at you, you silly person! ) \n"); break;
536                case 3: strcat (buf,
537                  "\nThe resource you want to\nset is `noseguy.program'\n");
538                  break;
539                case 4:
540                  strcat(buf,"\nHelp!!  Help!!\nAAAAAAGGGGHHH!!  \n\n"); break;
541                case 5: strcpy (buf, "You have new mail.\n"); break;
542                case 6:
543                  strcat(buf,"( Hello?  Are you paying attention? )\n");break;
544                case 7:
545                  strcat (buf, "sh: what kind of fool do you take me for? \n");
546                  break;
547                }
548            first_time = 0;
549            p = buf;
550        }
551        else
552        {
553            perror(program);
554            p = def_words;
555        }
556        break;
557#endif /* VMS */
558    case FROM_FILE:
559        if ((pp = fopen(filename, "r")))
560        {
561            while (fgets(p, sizeof(buf) - strlen(buf), pp))
562            {
563                if (strlen(buf) + 1 < sizeof(buf))
564                    p = buf + strlen(buf);
565                else
566                    break;
567            }
568            (void) fclose(pp);
569            if (! buf[0])
570              sprintf (buf, "file \"%s\" is empty!", filename);
571            p = buf;
572        }
573        else
574        {
575          sprintf (buf, "couldn't read file \"%s\"!", filename);
576          p = buf;
577        }
578        break;
579    case FROM_RESRC:
580        p = text;
581        break;
582    default:
583        p = def_words;
584        break;
585    }
586
587    if (!p || *p == '\0')
588        p = def_words;
589    return p;
590}
591
592
593
594char *progclass = "Noseguy";
595
596char *defaults [] = {
597  ".background:         black",
598  ".foreground:         gray80",
599#ifndef VMS
600  "*mode:               program",
601#else
602  "*mode:               string",
603#endif
604  "*program:            " FORTUNE_PROGRAM,
605  "noseguy.font:        -*-new century schoolbook-*-r-*-*-*-180-*-*-*-*-*-*",
606  0
607};
608
609XrmOptionDescRec options [] = {
610  { "-mode",            ".mode",                XrmoptionSepArg, 0 },
611  { "-program",         ".program",             XrmoptionSepArg, 0 },
612  { "-text",            ".text",                XrmoptionSepArg, 0 },
613  { "-filename",        ".filename",            XrmoptionSepArg, 0 },
614  { "-font",            ".font",                XrmoptionSepArg, 0 },
615  { "-text-foreground", ".textForeground",      XrmoptionSepArg, 0 },
616  { "-text-background", ".textBackground",      XrmoptionSepArg, 0 },
617  { 0, 0, 0, 0 }
618};
619
620
621static void
622noseguy_init (Display *d, Window w)
623{
624  unsigned long fg, bg, text_fg, text_bg;
625  XWindowAttributes xgwa;
626  Colormap cmap;
627  char *fontname = get_string_resource ("font", "Font");
628  char **list;
629  int foo, i;
630  XGCValues gcvalues;
631  dpy = d;
632  window = w;
633  XGetWindowAttributes (dpy, window, &xgwa);
634  Width = xgwa.width + 2;
635  Height = xgwa.height + 2;
636  cmap = xgwa.colormap;
637
638  init_words();
639  init_images();
640
641  if (!fontname || !(font = XLoadQueryFont(dpy, fontname)))
642    {
643        list = XListFonts(dpy, FONT_NAME, 32767, &foo);
644        for (i = 0; i < foo; i++)
645            if ((font = XLoadQueryFont(dpy, list[i])))
646                break;
647        if (!font)
648          {
649            fprintf (stderr, "%s: Can't find a large font.", progname);
650            exit (1);
651          }
652        XFreeFontNames(list);
653    }
654
655  fg = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
656  bg = get_pixel_resource ("background", "Background", dpy, cmap);
657  text_fg = get_pixel_resource ("textForeground", "Foreground", dpy, cmap);
658  text_bg = get_pixel_resource ("textBackground", "Background", dpy, cmap);
659  /* notice when unspecified */
660  if (! get_string_resource ("textForeground", "Foreground"))
661    text_fg = bg;
662  if (! get_string_resource ("textBackground", "Background"))
663    text_bg = fg;
664
665  gcvalues.font = font->fid;
666  gcvalues.graphics_exposures = False;
667  gcvalues.foreground = fg;
668  gcvalues.background = bg;
669  fg_gc = XCreateGC (dpy, window,
670                     GCForeground|GCBackground|GCGraphicsExposures|GCFont,
671                     &gcvalues);
672  gcvalues.foreground = bg;
673  gcvalues.background = fg;
674  bg_gc = XCreateGC (dpy, window,
675                     GCForeground|GCBackground|GCGraphicsExposures|GCFont,
676                     &gcvalues);
677  gcvalues.foreground = text_fg;
678  gcvalues.background = text_bg;
679  text_fg_gc = XCreateGC (dpy, window,
680                          GCForeground|GCBackground|GCGraphicsExposures|GCFont,
681                          &gcvalues);
682  gcvalues.foreground = text_bg;
683  gcvalues.background = text_fg;
684  text_bg_gc = XCreateGC (dpy, window,
685                          GCForeground|GCBackground|GCGraphicsExposures|GCFont,
686                          &gcvalues);
687  x = Width / 2;
688  y = Height / 2;
689  state = IS_MOVING;
690}
691     
692void
693screenhack (Display *d, Window w)
694{
695  noseguy_init (d, w);
696  next_fn = move;
697  while (1)
698    {
699      next_fn();
700      XSync (dpy, False);
701      screenhack_handle_events (dpy);
702      usleep (interval * 1000);
703    }
704}
705
Note: See TracBrowser for help on using the repository browser.