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 /* There maybe the case that we won't be able to exit from
200 this routine (especially when the geometry is too small)!!
202 Ensure that we can exit from this routine.
205 if (!tries && (st->move_length <= 1)) {
210 switch (random() % 8)
213 if (st->x - X_INCR * st->move_length >= 5)
217 if (st->x + X_INCR * st->move_length <= st->Width - 70)
218 st->move_dir = RIGHT;
221 if (st->y - (Y_INCR * st->move_length) >= 5)
222 st->move_dir = UP, st->interval = 40;
225 if (st->y + Y_INCR * st->move_length <= st->Height - 70)
226 st->move_dir = DOWN, st->interval = 20;
229 if (st->x - X_INCR * st->move_length >= 5 && st->y - (Y_INCR * st->move_length) >= 5)
230 st->move_dir = (LEFT | UP);
233 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
234 st->y - Y_INCR * st->move_length >= 5)
235 st->move_dir = (RIGHT | UP);
238 if (st->x - X_INCR * st->move_length >= 5 &&
239 st->y + Y_INCR * st->move_length <= st->Height - 70)
240 st->move_dir = (LEFT | DOWN);
243 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
244 st->y + Y_INCR * st->move_length <= st->Height - 70)
245 st->move_dir = (RIGHT | DOWN);
251 } while (!st->move_dir);
254 walk(st, st->move_dir);
260 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
261 XCopyArea (dpy,frame,window,gc,x,y,w,h,x2,y2)
263 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
264 XCopyPlane(dpy,frame,window,gc,x,y,w,h,x2,y2,1L)
268 walk (struct state *st, int dir)
270 register int incr = 0;
272 if (dir & (LEFT | RIGHT))
273 { /* left/right movement (mabye up/st->down too) */
274 st->walk_up = -st->walk_up; /* bouncing effect (even if hit a wall) */
278 st->walk_frame = (st->walk_up < 0) ? st->left1 : st->left2;
283 st->walk_frame = (st->walk_up < 0) ? st->right1 : st->right2;
285 if ((st->walk_lastdir == FRONT || st->walk_lastdir == DOWN) && dir & UP)
289 * workaround silly bug that leaves screen dust when guy is
290 * facing forward or st->down and moves up-left/right.
292 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
294 /* note that maybe neither UP nor DOWN is set! */
295 if (dir & UP && st->y > Y_INCR)
297 else if (dir & DOWN && st->y < st->Height - 64)
300 /* Explicit up/st->down movement only (no left/right) */
302 COPY(st->dpy, st->front, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y -= Y_INCR);
303 else if (dir == DOWN)
304 COPY(st->dpy, st->down, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y += Y_INCR);
305 else if (dir == FRONT && st->walk_frame != st->front)
308 st->walk_up = -st->walk_up;
309 if (st->walk_lastdir & LEFT)
310 st->walk_frame = st->left_front;
311 else if (st->walk_lastdir & RIGHT)
312 st->walk_frame = st->right_front;
314 st->walk_frame = st->front;
315 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
320 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, --st->x, st->y + st->walk_up);
322 else if (dir & RIGHT)
325 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, ++st->x, st->y + st->walk_up);
327 st->walk_lastdir = dir;
331 think (struct state *st)
337 st->words = get_words(st);
346 #define BUFSIZ ((MAXLINES + 1) * 100)
350 talk (struct state *st, int force_erase)
361 /* clear what we've written */
362 if (st->talking || force_erase)
366 XFillRectangle(st->dpy, st->window, st->bg_gc, st->s_rect.x - 5, st->s_rect.y - 5,
367 st->s_rect.width + 10, st->s_rect.height + 10);
373 /* might as well check the st->window for size changes now... */
374 XWindowAttributes xgwa;
375 XGetWindowAttributes (st->dpy, st->window, &xgwa);
376 st->Width = xgwa.width + 2;
377 st->Height = xgwa.height + 2;
383 p = strcpy(buf, st->words);
385 for (p2 = p; *p2; p2++)
386 if (*p2 == '\t') *p2 = ' ';
388 if (!(p2 = strchr(p, '\n')) || !p2[1])
390 total = strlen (st->words);
391 strcpy (args[0], st->words);
392 width = XTextWidth(st->font, st->words, total);
396 /* p2 now points to the first '\n' */
397 for (height = 0; p; height++)
401 if ((w = XTextWidth(st->font, p, p2 - p)) > width)
403 total += p2 - p; /* total chars; count to determine reading
405 (void) strcpy(args[height], p);
406 if (height == MAXLINES - 1)
408 /* puts("Message too long!"); */
412 if (!(p2 = strchr(p, '\n')))
418 * Figure out the height and width in pixels (height, width) extend the
419 * new box by 15 pixels on the sides (30 total) top and bottom.
421 st->s_rect.width = width + 30;
422 st->s_rect.height = height * font_height(st->font) + 30;
423 if (st->x - st->s_rect.width - 10 < 5)
425 else if ((st->s_rect.x = st->x + 32 - (st->s_rect.width + 15) / 2)
426 + st->s_rect.width + 15 > st->Width - 5)
427 st->s_rect.x = st->Width - 15 - st->s_rect.width;
428 if (st->y - st->s_rect.height - 10 < 5)
429 st->s_rect.y = st->y + 64 + 5;
431 st->s_rect.y = st->y - 5 - st->s_rect.height;
433 XFillRectangle(st->dpy, st->window, st->text_bg_gc,
434 st->s_rect.x, st->s_rect.y, st->s_rect.width, st->s_rect.height);
436 /* make a box that's 5 pixels thick. Then add a thin box inside it */
437 XSetLineAttributes(st->dpy, st->text_fg_gc, 5, 0, 0, 0);
438 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
439 st->s_rect.x, st->s_rect.y, st->s_rect.width - 1, st->s_rect.height - 1);
440 XSetLineAttributes(st->dpy, st->text_fg_gc, 0, 0, 0, 0);
441 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
442 st->s_rect.x + 7, st->s_rect.y + 7, st->s_rect.width - 15, st->s_rect.height - 15);
445 st->Y = 15 + font_height(st->font);
447 /* now print each string in reverse order (start at bottom of box) */
448 for (Z = 0; Z < height; Z++)
450 int L = strlen(args[Z]);
451 if (args[Z][L-1] == '\r' || args[Z][L-1] == '\n')
453 XDrawString(st->dpy, st->window, st->text_fg_gc, st->s_rect.x + st->X, st->s_rect.y + st->Y,
455 st->Y += font_height(st->font);
457 st->interval = (total / 15) * 1000;
458 if (st->interval < 2000) st->interval = 2000;
459 st->next_fn = talk_1;
463 talk_1 (struct state *st)
470 look (struct state *st)
474 COPY(st->dpy, (random() & 1) ? st->down : st->front, st->window, st->fg_gc,
475 0, 0, 64, 64, st->x, st->y);
482 COPY(st->dpy, (random() & 1) ? st->left_front : st->right_front,
483 st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
488 COPY(st->dpy, (random() & 1) ? st->left1 : st->right1, st->window, st->fg_gc,
489 0, 0, 64, 64, st->x, st->y);
495 init_words (struct state *st)
497 st->program = get_string_resource (st->dpy, "program", "Program");
499 if (st->program) /* get stderr on stdout, so it shows up on the window */
501 st->orig_program = st->program;
502 st->program = (char *) malloc (strlen (st->program) + 10);
503 strcpy (st->program, "( ");
504 strcat (st->program, st->orig_program);
505 strcat (st->program, " ) 2>&1");
508 st->words = get_words(st);
512 get_words (struct state *st)
515 register char *p = st->word_buf;
517 st->word_buf[0] = '\0';
519 if ((pp = popen(st->program, "r")))
521 while (fgets(p, sizeof(st->word_buf) - strlen(st->word_buf), pp))
523 if (strlen(st->word_buf) + 1 < sizeof(st->word_buf))
524 p = st->word_buf + strlen(st->word_buf);
529 if (! st->word_buf[0])
530 sprintf (st->word_buf, "\"%s\" produced no output!", st->orig_program);
531 else if (!st->first_time &&
532 (strstr (st->word_buf, ": not found") ||
533 strstr (st->word_buf, ": Not found") ||
534 strstr (st->word_buf, ": command not found") ||
535 strstr (st->word_buf, ": Command not found")))
536 switch (random () % 20)
538 case 1: strcat (st->word_buf, "( Get with the st->program, bub. )\n");
540 case 2: strcat (st->word_buf,
541 "( I blow my nose at you, you silly person! ) \n"); break;
542 case 3: strcat (st->word_buf,
543 "\nThe resource you want to\nset is `noseguy.program'\n");
546 strcat(st->word_buf,"\nHelp!! Help!!\nAAAAAAGGGGHHH!! \n\n"); break;
547 case 5: strcpy (st->word_buf, "You have new mail.\n"); break;
549 strcat(st->word_buf,"( Hello? Are you paying attention? )\n");break;
551 strcat (st->word_buf, "sh: what kind of fool do you take me for? \n");
567 static const char *noseguy_defaults [] = {
568 ".background: black",
569 ".foreground: #CCCCCC",
570 "*textForeground: black",
571 "*textBackground: #CCCCCC",
572 "*program: xscreensaver-text --cols 40 | head -n15",
573 ".font: -*-new century schoolbook-*-r-*-*-*-180-*-*-*-*-*-*",
577 static XrmOptionDescRec noseguy_options [] = {
578 { "-program", ".program", XrmoptionSepArg, 0 },
579 { "-font", ".font", XrmoptionSepArg, 0 },
580 { "-text-foreground", ".textForeground", XrmoptionSepArg, 0 },
581 { "-text-background", ".textBackground", XrmoptionSepArg, 0 },
587 noseguy_init (Display *d, Window w)
589 struct state *st = (struct state *) calloc (1, sizeof(*st));
590 unsigned long fg, bg, text_fg, text_bg;
591 XWindowAttributes xgwa;
599 fontname = get_string_resource (st->dpy, "font", "Font");
600 XGetWindowAttributes (st->dpy, st->window, &xgwa);
601 st->Width = xgwa.width + 2;
602 st->Height = xgwa.height + 2;
603 cmap = xgwa.colormap;
608 if (!fontname || !*fontname)
609 fprintf (stderr, "%s: no font specified.\n", progname);
610 st->font = XLoadQueryFont(st->dpy, fontname);
612 fprintf (stderr, "%s: could not load font %s.\n", progname, fontname);
614 fg = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
615 bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
616 text_fg = get_pixel_resource (st->dpy, cmap, "textForeground", "Foreground");
617 text_bg = get_pixel_resource (st->dpy, cmap, "textBackground", "Background");
618 /* notice when unspecified */
619 if (! get_string_resource (st->dpy, "textForeground", "Foreground"))
621 if (! get_string_resource (st->dpy, "textBackground", "Background"))
624 gcvalues.font = st->font->fid;
625 gcvalues.foreground = fg;
626 gcvalues.background = bg;
627 st->fg_gc = XCreateGC (st->dpy, st->window,
628 GCForeground|GCBackground|GCFont,
630 gcvalues.foreground = bg;
631 gcvalues.background = fg;
632 st->bg_gc = XCreateGC (st->dpy, st->window,
633 GCForeground|GCBackground|GCFont,
635 gcvalues.foreground = text_fg;
636 gcvalues.background = text_bg;
637 st->text_fg_gc = XCreateGC (st->dpy, st->window,
638 GCForeground|GCBackground|GCFont,
640 gcvalues.foreground = text_bg;
641 gcvalues.background = text_fg;
642 st->text_bg_gc = XCreateGC (st->dpy, st->window,
643 GCForeground|GCBackground|GCFont,
645 st->x = st->Width / 2;
646 st->y = st->Height / 2;
647 st->state = IS_MOVING;
654 noseguy_draw (Display *dpy, Window window, void *closure)
656 struct state *st = (struct state *) closure;
658 return (st->interval * 1000);
662 noseguy_reshape (Display *dpy, Window window, void *closure,
663 unsigned int w, unsigned int h)
668 noseguy_event (Display *dpy, Window window, void *closure, XEvent *event)
674 noseguy_free (Display *dpy, Window window, void *closure)
678 XSCREENSAVER_MODULE ("NoseGuy", noseguy)