1 /* xscreensaver, Copyright (c) 1992, 1996, 1997, 1998, 2005, 2006
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"
19 #include "xpm-pixmap.h"
26 extern FILE *popen (const char *, const char *);
27 extern int pclose (FILE *);
29 #define font_height(font) (font->ascent + font->descent)
36 GC fg_gc, bg_gc, text_fg_gc, text_bg_gc;
41 unsigned long interval;
42 Pixmap left1, left2, right1, right2;
43 Pixmap left_front, right_front, front, down;
45 char *program, *orig_program;
47 int state; /* indicates states: walking or getting passwd */
50 void (*next_fn) (struct state *);
52 int move_length, move_dir;
61 int x, y, width, height;
64 char word_buf[BUFSIZ];
67 static char *get_words (struct state *);
68 static void walk (struct state *, int dir);
69 static void talk (struct state *, int erase);
70 static void talk_1 (struct state *);
71 static int think (struct state *);
72 static unsigned long look (struct state *);
75 #define FROM_PROGRAM 2
82 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
83 # include "images/noseguy/nose-f1.xpm"
84 # include "images/noseguy/nose-f2.xpm"
85 # include "images/noseguy/nose-f3.xpm"
86 # include "images/noseguy/nose-f4.xpm"
87 # include "images/noseguy/nose-l1.xpm"
88 # include "images/noseguy/nose-l2.xpm"
89 # include "images/noseguy/nose-r1.xpm"
90 # include "images/noseguy/nose-r2.xpm"
92 # include "images/noseguy/nose-f1.xbm"
93 # include "images/noseguy/nose-f2.xbm"
94 # include "images/noseguy/nose-f3.xbm"
95 # include "images/noseguy/nose-f4.xbm"
96 # include "images/noseguy/nose-l1.xbm"
97 # include "images/noseguy/nose-l2.xbm"
98 # include "images/noseguy/nose-r1.xbm"
99 # include "images/noseguy/nose-r2.xbm"
103 init_images (struct state *st)
106 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
109 unsigned char *bits[8];
113 images[i++] = &st->left1;
114 images[i++] = &st->left2;
115 images[i++] = &st->right1;
116 images[i++] = &st->right2;
117 images[i++] = &st->left_front;
118 images[i++] = &st->right_front;
119 images[i++] = &st->front;
120 images[i++] = &st->down;
122 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
125 bits[i++] = nose_l1_xpm;
126 bits[i++] = nose_l2_xpm;
127 bits[i++] = nose_r1_xpm;
128 bits[i++] = nose_r2_xpm;
129 bits[i++] = nose_f2_xpm;
130 bits[i++] = nose_f3_xpm;
131 bits[i++] = nose_f1_xpm;
132 bits[i++] = nose_f4_xpm;
134 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
136 Pixmap pixmap = xpm_data_to_pixmap (st->dpy, st->window, bits[i],
140 fprintf (stderr, "%s: Can't load nose images\n", progname);
147 bits[i++] = nose_l1_bits;
148 bits[i++] = nose_l2_bits;
149 bits[i++] = nose_r1_bits;
150 bits[i++] = nose_r2_bits;
151 bits[i++] = nose_f2_bits;
152 bits[i++] = nose_f3_bits;
153 bits[i++] = nose_f1_bits;
154 bits[i++] = nose_f4_bits;
156 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
158 XCreatePixmapFromBitmapData(st->dpy, st->window,
159 (char *) bits[i], 64, 64, 1, 0, 1)))
161 fprintf (stderr, "%s: Can't load nose images\n", progname);
176 move (struct state *st)
178 if (!st->move_length)
180 register int tries = 0;
182 if ((random() & 1) && think(st))
184 talk(st, 0); /* sets timeout to itself */
187 if (!(random() % 3) && (st->interval = look(st)))
192 st->interval = 20 + random() % 100;
196 st->move_length = st->Width / 100 + random() % 90, tries = 8;
199 switch (random() % 8)
202 if (st->x - X_INCR * st->move_length >= 5)
206 if (st->x + X_INCR * st->move_length <= st->Width - 70)
207 st->move_dir = RIGHT;
210 if (st->y - (Y_INCR * st->move_length) >= 5)
211 st->move_dir = UP, st->interval = 40;
214 if (st->y + Y_INCR * st->move_length <= st->Height - 70)
215 st->move_dir = DOWN, st->interval = 20;
218 if (st->x - X_INCR * st->move_length >= 5 && st->y - (Y_INCR * st->move_length) >= 5)
219 st->move_dir = (LEFT | UP);
222 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
223 st->y - Y_INCR * st->move_length >= 5)
224 st->move_dir = (RIGHT | UP);
227 if (st->x - X_INCR * st->move_length >= 5 &&
228 st->y + Y_INCR * st->move_length <= st->Height - 70)
229 st->move_dir = (LEFT | DOWN);
232 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
233 st->y + Y_INCR * st->move_length <= st->Height - 70)
234 st->move_dir = (RIGHT | DOWN);
240 } while (!st->move_dir);
242 walk(st, st->move_dir);
248 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
249 XCopyArea (dpy,frame,window,gc,x,y,w,h,x2,y2)
251 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
252 XCopyPlane(dpy,frame,window,gc,x,y,w,h,x2,y2,1L)
256 walk (struct state *st, int dir)
258 register int incr = 0;
260 if (dir & (LEFT | RIGHT))
261 { /* left/right movement (mabye up/st->down too) */
262 st->walk_up = -st->walk_up; /* bouncing effect (even if hit a wall) */
266 st->walk_frame = (st->walk_up < 0) ? st->left1 : st->left2;
271 st->walk_frame = (st->walk_up < 0) ? st->right1 : st->right2;
273 if ((st->walk_lastdir == FRONT || st->walk_lastdir == DOWN) && dir & UP)
277 * workaround silly bug that leaves screen dust when guy is
278 * facing forward or st->down and moves up-left/right.
280 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
282 /* note that maybe neither UP nor DOWN is set! */
283 if (dir & UP && st->y > Y_INCR)
285 else if (dir & DOWN && st->y < st->Height - 64)
288 /* Explicit up/st->down movement only (no left/right) */
290 COPY(st->dpy, st->front, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y -= Y_INCR);
291 else if (dir == DOWN)
292 COPY(st->dpy, st->down, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y += Y_INCR);
293 else if (dir == FRONT && st->walk_frame != st->front)
296 st->walk_up = -st->walk_up;
297 if (st->walk_lastdir & LEFT)
298 st->walk_frame = st->left_front;
299 else if (st->walk_lastdir & RIGHT)
300 st->walk_frame = st->right_front;
302 st->walk_frame = st->front;
303 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
308 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, --st->x, st->y + st->walk_up);
310 else if (dir & RIGHT)
313 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, ++st->x, st->y + st->walk_up);
315 st->walk_lastdir = dir;
319 think (struct state *st)
325 st->words = get_words(st);
334 #define BUFSIZ ((MAXLINES + 1) * 100)
338 talk (struct state *st, int force_erase)
349 /* clear what we've written */
350 if (st->talking || force_erase)
354 XFillRectangle(st->dpy, st->window, st->bg_gc, st->s_rect.x - 5, st->s_rect.y - 5,
355 st->s_rect.width + 10, st->s_rect.height + 10);
361 /* might as well check the st->window for size changes now... */
362 XWindowAttributes xgwa;
363 XGetWindowAttributes (st->dpy, st->window, &xgwa);
364 st->Width = xgwa.width + 2;
365 st->Height = xgwa.height + 2;
371 p = strcpy(buf, st->words);
373 for (p2 = p; *p2; p2++)
374 if (*p2 == '\t') *p2 = ' ';
376 if (!(p2 = strchr(p, '\n')) || !p2[1])
378 total = strlen (st->words);
379 strcpy (args[0], st->words);
380 width = XTextWidth(st->font, st->words, total);
384 /* p2 now points to the first '\n' */
385 for (height = 0; p; height++)
389 if ((w = XTextWidth(st->font, p, p2 - p)) > width)
391 total += p2 - p; /* total chars; count to determine reading
393 (void) strcpy(args[height], p);
394 if (height == MAXLINES - 1)
396 /* puts("Message too long!"); */
400 if (!(p2 = strchr(p, '\n')))
406 * Figure out the height and width in pixels (height, width) extend the
407 * new box by 15 pixels on the sides (30 total) top and bottom.
409 st->s_rect.width = width + 30;
410 st->s_rect.height = height * font_height(st->font) + 30;
411 if (st->x - st->s_rect.width - 10 < 5)
413 else if ((st->s_rect.x = st->x + 32 - (st->s_rect.width + 15) / 2)
414 + st->s_rect.width + 15 > st->Width - 5)
415 st->s_rect.x = st->Width - 15 - st->s_rect.width;
416 if (st->y - st->s_rect.height - 10 < 5)
417 st->s_rect.y = st->y + 64 + 5;
419 st->s_rect.y = st->y - 5 - st->s_rect.height;
421 XFillRectangle(st->dpy, st->window, st->text_bg_gc,
422 st->s_rect.x, st->s_rect.y, st->s_rect.width, st->s_rect.height);
424 /* make a box that's 5 pixels thick. Then add a thin box inside it */
425 XSetLineAttributes(st->dpy, st->text_fg_gc, 5, 0, 0, 0);
426 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
427 st->s_rect.x, st->s_rect.y, st->s_rect.width - 1, st->s_rect.height - 1);
428 XSetLineAttributes(st->dpy, st->text_fg_gc, 0, 0, 0, 0);
429 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
430 st->s_rect.x + 7, st->s_rect.y + 7, st->s_rect.width - 15, st->s_rect.height - 15);
433 st->Y = 15 + font_height(st->font);
435 /* now print each string in reverse order (start at bottom of box) */
436 for (Z = 0; Z < height; Z++)
438 int L = strlen(args[Z]);
439 if (args[Z][L-1] == '\r' || args[Z][L-1] == '\n')
441 XDrawString(st->dpy, st->window, st->text_fg_gc, st->s_rect.x + st->X, st->s_rect.y + st->Y,
443 st->Y += font_height(st->font);
445 st->interval = (total / 15) * 1000;
446 if (st->interval < 2000) st->interval = 2000;
447 st->next_fn = talk_1;
451 talk_1 (struct state *st)
458 look (struct state *st)
462 COPY(st->dpy, (random() & 1) ? st->down : st->front, st->window, st->fg_gc,
463 0, 0, 64, 64, st->x, st->y);
470 COPY(st->dpy, (random() & 1) ? st->left_front : st->right_front,
471 st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
476 COPY(st->dpy, (random() & 1) ? st->left1 : st->right1, st->window, st->fg_gc,
477 0, 0, 64, 64, st->x, st->y);
483 init_words (struct state *st)
485 st->program = get_string_resource (st->dpy, "program", "Program");
487 if (st->program) /* get stderr on stdout, so it shows up on the window */
489 st->orig_program = st->program;
490 st->program = (char *) malloc (strlen (st->program) + 10);
491 strcpy (st->program, "( ");
492 strcat (st->program, st->orig_program);
493 strcat (st->program, " ) 2>&1");
496 st->words = get_words(st);
500 get_words (struct state *st)
503 register char *p = st->word_buf;
505 st->word_buf[0] = '\0';
507 if ((pp = popen(st->program, "r")))
509 while (fgets(p, sizeof(st->word_buf) - strlen(st->word_buf), pp))
511 if (strlen(st->word_buf) + 1 < sizeof(st->word_buf))
512 p = st->word_buf + strlen(st->word_buf);
517 if (! st->word_buf[0])
518 sprintf (st->word_buf, "\"%s\" produced no output!", st->orig_program);
519 else if (!st->first_time &&
520 (strstr (st->word_buf, ": not found") ||
521 strstr (st->word_buf, ": Not found") ||
522 strstr (st->word_buf, ": command not found") ||
523 strstr (st->word_buf, ": Command not found")))
524 switch (random () % 20)
526 case 1: strcat (st->word_buf, "( Get with the st->program, bub. )\n");
528 case 2: strcat (st->word_buf,
529 "( I blow my nose at you, you silly person! ) \n"); break;
530 case 3: strcat (st->word_buf,
531 "\nThe resource you want to\nset is `noseguy.program'\n");
534 strcat(st->word_buf,"\nHelp!! Help!!\nAAAAAAGGGGHHH!! \n\n"); break;
535 case 5: strcpy (st->word_buf, "You have new mail.\n"); break;
537 strcat(st->word_buf,"( Hello? Are you paying attention? )\n");break;
539 strcat (st->word_buf, "sh: what kind of fool do you take me for? \n");
555 static const char *noseguy_defaults [] = {
556 ".background: black",
557 ".foreground: #CCCCCC",
558 "*textForeground: black",
559 "*textBackground: #CCCCCC",
560 "*program: xscreensaver-text --cols 40 | head -n15",
561 ".font: -*-new century schoolbook-*-r-*-*-*-180-*-*-*-*-*-*",
565 static XrmOptionDescRec noseguy_options [] = {
566 { "-program", ".program", XrmoptionSepArg, 0 },
567 { "-font", ".font", XrmoptionSepArg, 0 },
568 { "-text-foreground", ".textForeground", XrmoptionSepArg, 0 },
569 { "-text-background", ".textBackground", XrmoptionSepArg, 0 },
575 noseguy_init (Display *d, Window w)
577 struct state *st = (struct state *) calloc (1, sizeof(*st));
578 unsigned long fg, bg, text_fg, text_bg;
579 XWindowAttributes xgwa;
587 fontname = get_string_resource (st->dpy, "font", "Font");
588 XGetWindowAttributes (st->dpy, st->window, &xgwa);
589 st->Width = xgwa.width + 2;
590 st->Height = xgwa.height + 2;
591 cmap = xgwa.colormap;
596 if (!fontname || !*fontname)
597 fprintf (stderr, "%s: no font specified.\n", progname);
598 st->font = XLoadQueryFont(st->dpy, fontname);
600 fprintf (stderr, "%s: could not load font %s.\n", progname, fontname);
602 fg = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
603 bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
604 text_fg = get_pixel_resource (st->dpy, cmap, "textForeground", "Foreground");
605 text_bg = get_pixel_resource (st->dpy, cmap, "textBackground", "Background");
606 /* notice when unspecified */
607 if (! get_string_resource (st->dpy, "textForeground", "Foreground"))
609 if (! get_string_resource (st->dpy, "textBackground", "Background"))
612 gcvalues.font = st->font->fid;
613 gcvalues.foreground = fg;
614 gcvalues.background = bg;
615 st->fg_gc = XCreateGC (st->dpy, st->window,
616 GCForeground|GCBackground|GCFont,
618 gcvalues.foreground = bg;
619 gcvalues.background = fg;
620 st->bg_gc = XCreateGC (st->dpy, st->window,
621 GCForeground|GCBackground|GCFont,
623 gcvalues.foreground = text_fg;
624 gcvalues.background = text_bg;
625 st->text_fg_gc = XCreateGC (st->dpy, st->window,
626 GCForeground|GCBackground|GCFont,
628 gcvalues.foreground = text_bg;
629 gcvalues.background = text_fg;
630 st->text_bg_gc = XCreateGC (st->dpy, st->window,
631 GCForeground|GCBackground|GCFont,
633 st->x = st->Width / 2;
634 st->y = st->Height / 2;
635 st->state = IS_MOVING;
642 noseguy_draw (Display *dpy, Window window, void *closure)
644 struct state *st = (struct state *) closure;
646 return (st->interval * 1000);
650 noseguy_reshape (Display *dpy, Window window, void *closure,
651 unsigned int w, unsigned int h)
656 noseguy_event (Display *dpy, Window window, void *closure, XEvent *event)
662 noseguy_free (Display *dpy, Window window, void *closure)
666 XSCREENSAVER_MODULE ("NoseGuy", noseguy)