1 /* xscreensaver, Copyright (c) 1992 Jamie Zawinski <jwz@mcom.com>
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
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>.
16 #include "screenhack.h"
20 extern FILE *popen (const char *, const char *);
21 extern int pclose (FILE *);
24 #define Pixel unsigned long
26 #define font_height(font) (font->ascent + font->descent)
27 #define FONT_NAME "-*-times-*-*-*-*-18-*-*-*-*-*-*-*"
31 static int Width, Height;
32 static GC fg_gc, bg_gc, text_fg_gc, text_bg_gc;
33 static char *words, *get_words();
35 static XFontStruct *font;
36 static char *def_words = "I'm out running around.";
37 static void init_images(), walk(), talk();
39 static unsigned long interval, look();
40 static Pixmap left0, left1, right0, right1;
41 static Pixmap left_front, right_front, front, down;
43 static char *program, *orig_program, *filename, *text;
46 #define FROM_PROGRAM 2
49 static int getwordsfrom;
53 static int state; /* indicates states: walking or getting passwd */
55 static void (*next_fn) ();
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"
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"
80 static Pixmap *images[] = {
81 &left0, &left1, &right0, &right1,
82 &left_front, &right_front, &front, &down
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
91 for (i = 0; i < sizeof (images) / sizeof (images[0]); i++)
93 XCreatePixmapFromBitmapData(dpy, window,
94 (char *) bits[i], 64, 64, 1, 0, 1)))
96 fprintf (stderr, "%s: Can't load nose images", progname);
117 register int tries = 0;
119 if ((random() & 1) && think())
121 talk(0); /* sets timeout to itself */
124 if (!(random() % 3) && (interval = look()))
129 interval = 20 + random() % 100;
133 length = Width / 100 + random() % 90, tries = 8;
136 switch (random() % 8)
139 if (x - X_INCR * length >= 5)
143 if (x + X_INCR * length <= Width - 70)
147 if (y - (Y_INCR * length) >= 5)
148 dir = UP, interval = 40;
151 if (y + Y_INCR * length <= Height - 70)
152 dir = DOWN, interval = 20;
155 if (x - X_INCR * length >= 5 && y - (Y_INCR * length) >= 5)
159 if (x + X_INCR * length <= Width - 70 &&
160 y - Y_INCR * length >= 5)
164 if (x - X_INCR * length >= 5 &&
165 y + Y_INCR * length <= Height - 70)
169 if (x + X_INCR * length <= Width - 70 &&
170 y + Y_INCR * length <= Height - 70)
171 dir = (RIGHT | DOWN);
188 register int incr = 0;
193 if (dir & (LEFT | RIGHT))
194 { /* left/right movement (mabye up/down too) */
195 up = -up; /* bouncing effect (even if hit a wall) */
199 frame = (up < 0) ? left0 : left1;
204 frame = (up < 0) ? right0 : right1;
206 if ((lastdir == FRONT || lastdir == DOWN) && dir & UP)
210 * workaround silly bug that leaves screen dust when guy is
211 * facing forward or down and moves up-left/right.
213 XCopyPlane(dpy, frame, window, fg_gc, 0, 0, 64, 64, x, y, 1L);
216 /* note that maybe neither UP nor DOWN is set! */
217 if (dir & UP && y > Y_INCR)
219 else if (dir & DOWN && y < Height - 64)
222 /* Explicit up/down movement only (no left/right) */
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)
235 else if (lastdir & RIGHT)
239 XCopyPlane(dpy, frame, window, fg_gc, 0, 0, 64, 64, x, y, 1L);
244 XCopyPlane(dpy, frame, window, fg_gc,
245 0, 0, 64, 64, --x, y + up, 1L);
248 else if (dir & RIGHT)
251 XCopyPlane(dpy, frame, window, fg_gc,
252 0, 0, 64, 64, ++x, y + up, 1L);
265 if (getwordsfrom == FROM_PROGRAM)
266 words = get_words(0, (char **) 0);
297 /* clear what we've written */
298 if (talking || force_erase)
302 XFillRectangle(dpy, window, bg_gc, s_rect.x - 5, s_rect.y - 5,
303 s_rect.width + 10, s_rect.height + 10);
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;
319 p = strcpy(buf, words);
321 if (!(p2 = index(p, '\n')) || !p2[1])
323 total = strlen (words);
324 strcpy (args[0], words);
325 width = XTextWidth(font, words, total);
329 /* p2 now points to the first '\n' */
330 for (height = 0; p; height++)
334 if ((w = XTextWidth(font, p, p2 - p)) > width)
336 total += p2 - p; /* total chars; count to determine reading
338 (void) strcpy(args[height], p);
339 if (height == MAXLINES - 1)
341 puts("Message too long!");
345 if (!(p2 = index(p, '\n')))
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.
354 s_rect.width = width + 30;
355 s_rect.height = height * font_height(font) + 30;
356 if (x - s_rect.width - 10 < 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;
364 s_rect.y = y - 5 - s_rect.height;
366 XFillRectangle(dpy, window, text_bg_gc,
367 s_rect.x, s_rect.y, s_rect.width, s_rect.height);
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);
378 Y = 15 + font_height(font);
380 /* now print each string in reverse order (start at bottom of box) */
381 for (Z = 0; Z < height; Z++)
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);
387 interval = (total / 15) * 1000;
388 if (interval < 2000) interval = 2000;
397 XCopyPlane(dpy, (random() & 1) ? down : front, window, fg_gc,
398 0, 0, 64, 64, x, y, 1L);
405 XCopyPlane(dpy, (random() & 1) ? left_front : right_front,
406 window, fg_gc, 0, 0, 64, 64, x, y, 1L);
411 XCopyPlane(dpy, (random() & 1) ? left0 : right0, window, fg_gc,
412 0, 0, 64, 64, x, y, 1L);
420 char *mode = get_string_resource ("mode", "Mode");
422 program = get_string_resource ("program", "Program");
423 filename = get_string_resource ("filename", "Filename");
424 text = get_string_resource ("text", "Text");
426 if (program) /* get stderr on stdout, so it shows up on the window */
428 orig_program = program;
429 program = (char *) malloc (strlen (program) + 10);
430 strcpy (program, "( ");
431 strcat (program, orig_program);
432 strcat (program, " ) 2>&1");
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;
444 "%s: mode must be program, file, or string, not %s\n",
449 if (getwordsfrom == FROM_PROGRAM && !program)
451 fprintf (stderr, "%s: no program specified.\n", progname);
454 if (getwordsfrom == FROM_FILE && !filename)
456 fprintf (stderr, "%s: no file specified.\n", progname);
463 static int first_time = 1;
469 static char buf[BUFSIZ];
470 register char *p = buf;
474 switch (getwordsfrom)
477 if ((pp = popen(program, "r")))
479 while (fgets(p, sizeof(buf) - strlen(buf), pp))
481 if (strlen(buf) + 1 < sizeof(buf))
482 p = buf + strlen(buf);
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)
493 case 1: strcat (buf, "( Get with the program, bub. )\n");
496 "( I blow my nose at you, you silly person! ) \n"); break;
498 "\nThe resource you want to\nset is `noseguy.program'\n");
501 strcat(buf,"\nHelp!! Help!!\nAAAAAAGGGGHHH!! \n\n"); break;
502 case 5: strcpy (buf, "You have new mail.\n"); break;
504 strcat(buf,"( Hello? Are you paying attention? )\n");break;
506 strcat (buf, "sh: what kind of fool do you take me for? \n");
519 if ((pp = fopen(filename, "r")))
521 while (fgets(p, sizeof(buf) - strlen(buf), pp))
523 if (strlen(buf) + 1 < sizeof(buf))
524 p = buf + strlen(buf);
530 sprintf (buf, "file \"%s\" is empty!", filename);
535 sprintf (buf, "couldn't read file \"%s\"!", filename);
547 if (!p || *p == '\0')
554 char *progclass = "Noseguy";
556 char *defaults [] = {
557 "Noseguy.background: black", /* to placate SGI */
558 "Noseguy.foreground: white",
560 "*program: fortune -s",
561 "noseguy.font: -*-new century schoolbook-*-r-*-*-*-180-*-*-*-*-*-*",
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 }
574 int options_size = (sizeof (options) / sizeof (options[0]));
582 Pixel fg, bg, text_fg, text_bg;
583 XWindowAttributes xgwa;
585 char *fontname = get_string_resource ("font", "Font");
591 XGetWindowAttributes (dpy, window, &xgwa);
592 Width = xgwa.width + 2;
593 Height = xgwa.height + 2;
594 cmap = xgwa.colormap;
599 if (!fontname || !(font = XLoadQueryFont(dpy, fontname)))
601 list = XListFonts(dpy, FONT_NAME, 32767, &foo);
602 for (i = 0; i < foo; i++)
603 if ((font = XLoadQueryFont(dpy, list[i])))
607 fprintf (stderr, "%s: Can't find a large font.", progname);
610 XFreeFontNames(list);
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"))
620 if (! get_string_resource ("textBackground", "Background"))
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,
630 gcvalues.foreground = bg;
631 gcvalues.background = fg;
632 bg_gc = XCreateGC (dpy, window,
633 GCForeground|GCBackground|GCGraphicsExposures|GCFont,
635 gcvalues.foreground = text_fg;
636 gcvalues.background = text_bg;
637 text_fg_gc = XCreateGC (dpy, window,
638 GCForeground|GCBackground|GCGraphicsExposures|GCFont,
640 gcvalues.foreground = text_bg;
641 gcvalues.background = text_fg;
642 text_bg_gc = XCreateGC (dpy, window,
643 GCForeground|GCBackground|GCGraphicsExposures|GCFont,
661 usleep (interval * 1000);