1 /* xscreensaver, Copyright (c) 1992, 1996, 1997, 1998
2 * Jamie Zawinski <jwz@jwz.org>
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
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>.
18 #include "screenhack.h"
21 extern FILE *popen (const char *, const char *);
22 extern int pclose (FILE *);
24 #define font_height(font) (font->ascent + font->descent)
25 #define FONT_NAME "-*-times-*-*-*-*-18-*-*-*-*-*-*-*"
29 static int Width, Height;
30 static GC fg_gc, bg_gc, text_fg_gc, text_bg_gc;
32 static char *get_words (void);
34 static XFontStruct *font;
35 static char *def_words = "I'm out running around.";
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;
45 static char *program, *orig_program, *filename, *text;
48 #define FROM_PROGRAM 2
51 static int getwordsfrom;
55 static int state; /* indicates states: walking or getting passwd */
57 static void (*next_fn) (void);
62 # include "images/noseguy/nose-f1.xpm"
63 # include "images/noseguy/nose-f2.xpm"
64 # include "images/noseguy/nose-f3.xpm"
65 # include "images/noseguy/nose-f4.xpm"
66 # include "images/noseguy/nose-l1.xpm"
67 # include "images/noseguy/nose-l2.xpm"
68 # include "images/noseguy/nose-r1.xpm"
69 # include "images/noseguy/nose-r2.xpm"
71 # include "images/noseguy/nose-f1.xbm"
72 # include "images/noseguy/nose-f2.xbm"
73 # include "images/noseguy/nose-f3.xbm"
74 # include "images/noseguy/nose-f4.xbm"
75 # include "images/noseguy/nose-l1.xbm"
76 # include "images/noseguy/nose-l2.xbm"
77 # include "images/noseguy/nose-r1.xbm"
78 # include "images/noseguy/nose-r2.xbm"
84 static Pixmap *images[] = {
85 &left1, &left2, &right1, &right2,
86 &left_front, &right_front, &front, &down
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
94 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
96 XWindowAttributes xgwa;
97 XpmAttributes xpmattrs;
100 xpmattrs.valuemask = 0;
102 XGetWindowAttributes (dpy, window, &xgwa);
105 xpmattrs.valuemask |= XpmCloseness;
106 xpmattrs.closeness = 40000;
109 xpmattrs.valuemask |= XpmVisual;
110 xpmattrs.visual = xgwa.visual;
113 xpmattrs.valuemask |= XpmDepth;
114 xpmattrs.depth = xgwa.depth;
117 xpmattrs.valuemask |= XpmColormap;
118 xpmattrs.colormap = xgwa.colormap;
121 result = XpmCreatePixmapFromData(dpy, window, bits[i],
122 &pixmap, 0 /* mask */, &xpmattrs);
123 if (!pixmap || (result != XpmSuccess && result != XpmColorError))
125 fprintf (stderr, "%s: Can't load nose images\n", progname);
131 static unsigned char *bits[] = {
132 nose_l1_bits, nose_l2_bits, nose_r1_bits, nose_r2_bits,
133 nose_f2_bits, nose_f3_bits, nose_f1_bits, nose_f4_bits
136 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
138 XCreatePixmapFromBitmapData(dpy, window,
139 (char *) bits[i], 64, 64, 1, 0, 1)))
141 fprintf (stderr, "%s: Can't load nose images\n", progname);
163 register int tries = 0;
165 if ((random() & 1) && think())
167 talk(0); /* sets timeout to itself */
170 if (!(random() % 3) && (interval = look()))
175 interval = 20 + random() % 100;
179 length = Width / 100 + random() % 90, tries = 8;
182 switch (random() % 8)
185 if (x - X_INCR * length >= 5)
189 if (x + X_INCR * length <= Width - 70)
193 if (y - (Y_INCR * length) >= 5)
194 dir = UP, interval = 40;
197 if (y + Y_INCR * length <= Height - 70)
198 dir = DOWN, interval = 20;
201 if (x - X_INCR * length >= 5 && y - (Y_INCR * length) >= 5)
205 if (x + X_INCR * length <= Width - 70 &&
206 y - Y_INCR * length >= 5)
210 if (x - X_INCR * length >= 5 &&
211 y + Y_INCR * length <= Height - 70)
215 if (x + X_INCR * length <= Width - 70 &&
216 y + Y_INCR * length <= Height - 70)
217 dir = (RIGHT | DOWN);
231 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
232 XCopyArea (dpy,frame,window,gc,x,y,w,h,x2,y2)
234 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
235 XCopyPlane(dpy,frame,window,gc,x,y,w,h,x2,y2,1L)
241 register int incr = 0;
246 if (dir & (LEFT | RIGHT))
247 { /* left/right movement (mabye up/down too) */
248 up = -up; /* bouncing effect (even if hit a wall) */
252 frame = (up < 0) ? left1 : left2;
257 frame = (up < 0) ? right1 : right2;
259 if ((lastdir == FRONT || lastdir == DOWN) && dir & UP)
263 * workaround silly bug that leaves screen dust when guy is
264 * facing forward or down and moves up-left/right.
266 COPY(dpy, frame, window, fg_gc, 0, 0, 64, 64, x, y);
269 /* note that maybe neither UP nor DOWN is set! */
270 if (dir & UP && y > Y_INCR)
272 else if (dir & DOWN && y < Height - 64)
275 /* Explicit up/down movement only (no left/right) */
277 COPY(dpy, front, window, fg_gc, 0, 0, 64, 64, x, y -= Y_INCR);
278 else if (dir == DOWN)
279 COPY(dpy, down, window, fg_gc, 0, 0, 64, 64, x, y += Y_INCR);
280 else if (dir == FRONT && frame != front)
286 else if (lastdir & RIGHT)
290 COPY(dpy, frame, window, fg_gc, 0, 0, 64, 64, x, y);
295 COPY(dpy, frame, window, fg_gc, 0, 0, 64, 64, --x, y + up);
298 else if (dir & RIGHT)
301 COPY(dpy, frame, window, fg_gc, 0, 0, 64, 64, ++x, y + up);
314 if (getwordsfrom == FROM_PROGRAM)
324 talk(int force_erase)
345 /* clear what we've written */
346 if (talking || force_erase)
350 XFillRectangle(dpy, window, bg_gc, s_rect.x - 5, s_rect.y - 5,
351 s_rect.width + 10, s_rect.height + 10);
357 /* might as well check the window for size changes now... */
358 XWindowAttributes xgwa;
359 XGetWindowAttributes (dpy, window, &xgwa);
360 Width = xgwa.width + 2;
361 Height = xgwa.height + 2;
367 p = strcpy(buf, words);
369 if (!(p2 = strchr(p, '\n')) || !p2[1])
371 total = strlen (words);
372 strcpy (args[0], words);
373 width = XTextWidth(font, words, total);
377 /* p2 now points to the first '\n' */
378 for (height = 0; p; height++)
382 if ((w = XTextWidth(font, p, p2 - p)) > width)
384 total += p2 - p; /* total chars; count to determine reading
386 (void) strcpy(args[height], p);
387 if (height == MAXLINES - 1)
389 puts("Message too long!");
393 if (!(p2 = strchr(p, '\n')))
399 * Figure out the height and width in pixels (height, width) extend the
400 * new box by 15 pixels on the sides (30 total) top and bottom.
402 s_rect.width = width + 30;
403 s_rect.height = height * font_height(font) + 30;
404 if (x - s_rect.width - 10 < 5)
406 else if ((s_rect.x = x + 32 - (s_rect.width + 15) / 2)
407 + s_rect.width + 15 > Width - 5)
408 s_rect.x = Width - 15 - s_rect.width;
409 if (y - s_rect.height - 10 < 5)
410 s_rect.y = y + 64 + 5;
412 s_rect.y = y - 5 - s_rect.height;
414 XFillRectangle(dpy, window, text_bg_gc,
415 s_rect.x, s_rect.y, s_rect.width, s_rect.height);
417 /* make a box that's 5 pixels thick. Then add a thin box inside it */
418 XSetLineAttributes(dpy, text_fg_gc, 5, 0, 0, 0);
419 XDrawRectangle(dpy, window, text_fg_gc,
420 s_rect.x, s_rect.y, s_rect.width - 1, s_rect.height - 1);
421 XSetLineAttributes(dpy, text_fg_gc, 0, 0, 0, 0);
422 XDrawRectangle(dpy, window, text_fg_gc,
423 s_rect.x + 7, s_rect.y + 7, s_rect.width - 15, s_rect.height - 15);
426 Y = 15 + font_height(font);
428 /* now print each string in reverse order (start at bottom of box) */
429 for (Z = 0; Z < height; Z++)
431 XDrawString(dpy, window, text_fg_gc, s_rect.x + X, s_rect.y + Y,
432 args[Z], strlen(args[Z]));
433 Y += font_height(font);
435 interval = (total / 15) * 1000;
436 if (interval < 2000) interval = 2000;
440 static void talk_1 (void)
451 COPY(dpy, (random() & 1) ? down : front, window, fg_gc,
459 COPY(dpy, (random() & 1) ? left_front : right_front,
460 window, fg_gc, 0, 0, 64, 64, x, y);
465 COPY(dpy, (random() & 1) ? left1 : right1, window, fg_gc,
474 char *mode = get_string_resource ("mode", "Mode");
476 program = get_string_resource ("program", "Program");
477 filename = get_string_resource ("filename", "Filename");
478 text = get_string_resource ("text", "Text");
480 if (program) /* get stderr on stdout, so it shows up on the window */
482 orig_program = program;
483 program = (char *) malloc (strlen (program) + 10);
484 strcpy (program, "( ");
485 strcat (program, orig_program);
486 strcat (program, " ) 2>&1");
489 if (!mode || !strcmp (mode, "program"))
490 getwordsfrom = FROM_PROGRAM;
491 else if (!strcmp (mode, "file"))
492 getwordsfrom = FROM_FILE;
493 else if (!strcmp (mode, "string"))
494 getwordsfrom = FROM_RESRC;
498 "%s: mode must be program, file, or string, not %s\n",
503 if (getwordsfrom == FROM_PROGRAM && !program)
505 fprintf (stderr, "%s: no program specified.\n", progname);
508 if (getwordsfrom == FROM_FILE && !filename)
510 fprintf (stderr, "%s: no file specified.\n", progname);
517 static int first_time = 1;
523 static char buf[BUFSIZ];
524 register char *p = buf;
528 switch (getwordsfrom)
532 if ((pp = popen(program, "r")))
534 while (fgets(p, sizeof(buf) - strlen(buf), pp))
536 if (strlen(buf) + 1 < sizeof(buf))
537 p = buf + strlen(buf);
543 sprintf (buf, "\"%s\" produced no output!", orig_program);
544 else if (!first_time &&
545 (strstr (buf, ": not found") ||
546 strstr (buf, ": Not found") ||
547 strstr (buf, ": command not found") ||
548 strstr (buf, ": Command not found")))
549 switch (random () % 20)
551 case 1: strcat (buf, "( Get with the program, bub. )\n");
554 "( I blow my nose at you, you silly person! ) \n"); break;
556 "\nThe resource you want to\nset is `noseguy.program'\n");
559 strcat(buf,"\nHelp!! Help!!\nAAAAAAGGGGHHH!! \n\n"); break;
560 case 5: strcpy (buf, "You have new mail.\n"); break;
562 strcat(buf,"( Hello? Are you paying attention? )\n");break;
564 strcat (buf, "sh: what kind of fool do you take me for? \n");
578 if ((pp = fopen(filename, "r")))
580 while (fgets(p, sizeof(buf) - strlen(buf), pp))
582 if (strlen(buf) + 1 < sizeof(buf))
583 p = buf + strlen(buf);
589 sprintf (buf, "file \"%s\" is empty!", filename);
594 sprintf (buf, "couldn't read file \"%s\"!", filename);
606 if (!p || *p == '\0')
613 char *progclass = "Noseguy";
615 char *defaults [] = {
616 ".background: black",
617 ".foreground: gray80",
623 "*program: " FORTUNE_PROGRAM,
624 "noseguy.font: -*-new century schoolbook-*-r-*-*-*-180-*-*-*-*-*-*",
628 XrmOptionDescRec options [] = {
629 { "-mode", ".mode", XrmoptionSepArg, 0 },
630 { "-program", ".program", XrmoptionSepArg, 0 },
631 { "-text", ".text", XrmoptionSepArg, 0 },
632 { "-filename", ".filename", XrmoptionSepArg, 0 },
633 { "-font", ".font", XrmoptionSepArg, 0 },
634 { "-text-foreground", ".textForeground", XrmoptionSepArg, 0 },
635 { "-text-background", ".textBackground", XrmoptionSepArg, 0 },
641 noseguy_init (Display *d, Window w)
643 unsigned long fg, bg, text_fg, text_bg;
644 XWindowAttributes xgwa;
646 char *fontname = get_string_resource ("font", "Font");
652 XGetWindowAttributes (dpy, window, &xgwa);
653 Width = xgwa.width + 2;
654 Height = xgwa.height + 2;
655 cmap = xgwa.colormap;
660 if (!fontname || !(font = XLoadQueryFont(dpy, fontname)))
662 list = XListFonts(dpy, FONT_NAME, 32767, &foo);
663 for (i = 0; i < foo; i++)
664 if ((font = XLoadQueryFont(dpy, list[i])))
668 fprintf (stderr, "%s: Can't find a large font.", progname);
671 XFreeFontNames(list);
674 fg = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
675 bg = get_pixel_resource ("background", "Background", dpy, cmap);
676 text_fg = get_pixel_resource ("textForeground", "Foreground", dpy, cmap);
677 text_bg = get_pixel_resource ("textBackground", "Background", dpy, cmap);
678 /* notice when unspecified */
679 if (! get_string_resource ("textForeground", "Foreground"))
681 if (! get_string_resource ("textBackground", "Background"))
684 gcvalues.font = font->fid;
685 gcvalues.graphics_exposures = False;
686 gcvalues.foreground = fg;
687 gcvalues.background = bg;
688 fg_gc = XCreateGC (dpy, window,
689 GCForeground|GCBackground|GCGraphicsExposures|GCFont,
691 gcvalues.foreground = bg;
692 gcvalues.background = fg;
693 bg_gc = XCreateGC (dpy, window,
694 GCForeground|GCBackground|GCGraphicsExposures|GCFont,
696 gcvalues.foreground = text_fg;
697 gcvalues.background = text_bg;
698 text_fg_gc = XCreateGC (dpy, window,
699 GCForeground|GCBackground|GCGraphicsExposures|GCFont,
701 gcvalues.foreground = text_bg;
702 gcvalues.background = text_fg;
703 text_bg_gc = XCreateGC (dpy, window,
704 GCForeground|GCBackground|GCGraphicsExposures|GCFont,
712 screenhack (Display *d, Window w)
720 screenhack_handle_events (dpy);
721 usleep (interval * 1000);