1 /* xscreensaver, Copyright (c) 1992-2008 Jamie Zawinski <jwz@jwz.org>
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
14 Dan Heller <argv@danheller.com>.
17 #include "screenhack.h"
18 #include "xpm-pixmap.h"
25 extern FILE *popen (const char *, const char *);
26 extern int pclose (FILE *);
28 #define font_height(font) (font->ascent + font->descent)
35 GC fg_gc, bg_gc, text_fg_gc, text_bg_gc;
40 unsigned long interval;
41 Pixmap left1, left2, right1, right2;
42 Pixmap left_front, right_front, front, down;
44 char *program, *orig_program;
46 int state; /* indicates states: walking or getting passwd */
49 void (*next_fn) (struct state *);
51 int move_length, move_dir;
60 int x, y, width, height;
63 char word_buf[BUFSIZ];
66 static char *get_words (struct state *);
67 static void walk (struct state *, int dir);
68 static void talk (struct state *, int erase);
69 static void talk_1 (struct state *);
70 static int think (struct state *);
71 static unsigned long look (struct state *);
74 #define FROM_PROGRAM 2
81 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
82 # include "images/noseguy/nose-f1.xpm"
83 # include "images/noseguy/nose-f2.xpm"
84 # include "images/noseguy/nose-f3.xpm"
85 # include "images/noseguy/nose-f4.xpm"
86 # include "images/noseguy/nose-l1.xpm"
87 # include "images/noseguy/nose-l2.xpm"
88 # include "images/noseguy/nose-r1.xpm"
89 # include "images/noseguy/nose-r2.xpm"
91 # include "images/noseguy/nose-f1.xbm"
92 # include "images/noseguy/nose-f2.xbm"
93 # include "images/noseguy/nose-f3.xbm"
94 # include "images/noseguy/nose-f4.xbm"
95 # include "images/noseguy/nose-l1.xbm"
96 # include "images/noseguy/nose-l2.xbm"
97 # include "images/noseguy/nose-r1.xbm"
98 # include "images/noseguy/nose-r2.xbm"
102 init_images (struct state *st)
105 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
108 unsigned char *bits[8];
112 images[i++] = &st->left1;
113 images[i++] = &st->left2;
114 images[i++] = &st->right1;
115 images[i++] = &st->right2;
116 images[i++] = &st->left_front;
117 images[i++] = &st->right_front;
118 images[i++] = &st->front;
119 images[i++] = &st->down;
121 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
124 bits[i++] = nose_l1_xpm;
125 bits[i++] = nose_l2_xpm;
126 bits[i++] = nose_r1_xpm;
127 bits[i++] = nose_r2_xpm;
128 bits[i++] = nose_f2_xpm;
129 bits[i++] = nose_f3_xpm;
130 bits[i++] = nose_f1_xpm;
131 bits[i++] = nose_f4_xpm;
133 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
135 Pixmap pixmap = xpm_data_to_pixmap (st->dpy, st->window, bits[i],
139 fprintf (stderr, "%s: Can't load nose images\n", progname);
146 bits[i++] = nose_l1_bits;
147 bits[i++] = nose_l2_bits;
148 bits[i++] = nose_r1_bits;
149 bits[i++] = nose_r2_bits;
150 bits[i++] = nose_f2_bits;
151 bits[i++] = nose_f3_bits;
152 bits[i++] = nose_f1_bits;
153 bits[i++] = nose_f4_bits;
155 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
157 XCreatePixmapFromBitmapData(st->dpy, st->window,
158 (char *) bits[i], 64, 64, 1, 0, 1)))
160 fprintf (stderr, "%s: Can't load nose images\n", progname);
175 move (struct state *st)
177 if (!st->move_length)
179 register int tries = 0;
181 if ((random() & 1) && think(st))
183 talk(st, 0); /* sets timeout to itself */
186 if (!(random() % 3) && (st->interval = look(st)))
191 st->interval = 20 + random() % 100;
195 st->move_length = st->Width / 100 + random() % 90, tries = 8;
198 /* There maybe the case that we won't be able to exit from
199 this routine (especially when the geometry is too small)!!
201 Ensure that we can exit from this routine.
204 if (!tries && (st->move_length <= 1)) {
209 switch (random() % 8)
212 if (st->x - X_INCR * st->move_length >= 5)
216 if (st->x + X_INCR * st->move_length <= st->Width - 70)
217 st->move_dir = RIGHT;
220 if (st->y - (Y_INCR * st->move_length) >= 5)
221 st->move_dir = UP, st->interval = 40;
224 if (st->y + Y_INCR * st->move_length <= st->Height - 70)
225 st->move_dir = DOWN, st->interval = 20;
228 if (st->x - X_INCR * st->move_length >= 5 && st->y - (Y_INCR * st->move_length) >= 5)
229 st->move_dir = (LEFT | UP);
232 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
233 st->y - Y_INCR * st->move_length >= 5)
234 st->move_dir = (RIGHT | UP);
237 if (st->x - X_INCR * st->move_length >= 5 &&
238 st->y + Y_INCR * st->move_length <= st->Height - 70)
239 st->move_dir = (LEFT | DOWN);
242 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
243 st->y + Y_INCR * st->move_length <= st->Height - 70)
244 st->move_dir = (RIGHT | DOWN);
250 } while (!st->move_dir);
253 walk(st, st->move_dir);
259 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
260 XCopyArea (dpy,frame,window,gc,x,y,w,h,x2,y2)
262 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
263 XCopyPlane(dpy,frame,window,gc,x,y,w,h,x2,y2,1L)
267 walk (struct state *st, int dir)
269 register int incr = 0;
271 if (dir & (LEFT | RIGHT))
272 { /* left/right movement (mabye up/st->down too) */
273 st->walk_up = -st->walk_up; /* bouncing effect (even if hit a wall) */
277 st->walk_frame = (st->walk_up < 0) ? st->left1 : st->left2;
282 st->walk_frame = (st->walk_up < 0) ? st->right1 : st->right2;
284 if ((st->walk_lastdir == FRONT || st->walk_lastdir == DOWN) && dir & UP)
288 * workaround silly bug that leaves screen dust when guy is
289 * facing forward or st->down and moves up-left/right.
291 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
293 /* note that maybe neither UP nor DOWN is set! */
294 if (dir & UP && st->y > Y_INCR)
296 else if (dir & DOWN && st->y < st->Height - 64)
299 /* Explicit up/st->down movement only (no left/right) */
301 COPY(st->dpy, st->front, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y -= Y_INCR);
302 else if (dir == DOWN)
303 COPY(st->dpy, st->down, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y += Y_INCR);
304 else if (dir == FRONT && st->walk_frame != st->front)
307 st->walk_up = -st->walk_up;
308 if (st->walk_lastdir & LEFT)
309 st->walk_frame = st->left_front;
310 else if (st->walk_lastdir & RIGHT)
311 st->walk_frame = st->right_front;
313 st->walk_frame = st->front;
314 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
319 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, --st->x, st->y + st->walk_up);
321 else if (dir & RIGHT)
324 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, ++st->x, st->y + st->walk_up);
326 st->walk_lastdir = dir;
330 think (struct state *st)
336 st->words = get_words(st);
345 #define BUFSIZ ((MAXLINES + 1) * 100)
349 talk (struct state *st, int force_erase)
360 /* clear what we've written */
361 if (st->talking || force_erase)
365 XFillRectangle(st->dpy, st->window, st->bg_gc, st->s_rect.x - 5, st->s_rect.y - 5,
366 st->s_rect.width + 10, st->s_rect.height + 10);
372 /* might as well check the st->window for size changes now... */
373 XWindowAttributes xgwa;
374 XGetWindowAttributes (st->dpy, st->window, &xgwa);
375 st->Width = xgwa.width + 2;
376 st->Height = xgwa.height + 2;
382 p = strcpy(buf, st->words);
384 for (p2 = p; *p2; p2++)
385 if (*p2 == '\t') *p2 = ' ';
387 if (!(p2 = strchr(p, '\n')) || !p2[1])
389 total = strlen (st->words);
390 strcpy (args[0], st->words);
391 width = XTextWidth(st->font, st->words, total);
395 /* p2 now points to the first '\n' */
396 for (height = 0; p; height++)
400 if ((w = XTextWidth(st->font, p, p2 - p)) > width)
402 total += p2 - p; /* total chars; count to determine reading
404 (void) strcpy(args[height], p);
405 if (height == MAXLINES - 1)
407 /* puts("Message too long!"); */
411 if (!(p2 = strchr(p, '\n')))
417 * Figure out the height and width in pixels (height, width) extend the
418 * new box by 15 pixels on the sides (30 total) top and bottom.
420 st->s_rect.width = width + 30;
421 st->s_rect.height = height * font_height(st->font) + 30;
422 if (st->x - st->s_rect.width - 10 < 5)
424 else if ((st->s_rect.x = st->x + 32 - (st->s_rect.width + 15) / 2)
425 + st->s_rect.width + 15 > st->Width - 5)
426 st->s_rect.x = st->Width - 15 - st->s_rect.width;
427 if (st->y - st->s_rect.height - 10 < 5)
428 st->s_rect.y = st->y + 64 + 5;
430 st->s_rect.y = st->y - 5 - st->s_rect.height;
432 XFillRectangle(st->dpy, st->window, st->text_bg_gc,
433 st->s_rect.x, st->s_rect.y, st->s_rect.width, st->s_rect.height);
435 /* make a box that's 5 pixels thick. Then add a thin box inside it */
436 XSetLineAttributes(st->dpy, st->text_fg_gc, 5, 0, 0, 0);
437 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
438 st->s_rect.x, st->s_rect.y, st->s_rect.width - 1, st->s_rect.height - 1);
439 XSetLineAttributes(st->dpy, st->text_fg_gc, 0, 0, 0, 0);
440 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
441 st->s_rect.x + 7, st->s_rect.y + 7, st->s_rect.width - 15, st->s_rect.height - 15);
444 st->Y = 15 + font_height(st->font);
446 /* now print each string in reverse order (start at bottom of box) */
447 for (Z = 0; Z < height; Z++)
449 int L = strlen(args[Z]);
450 if (args[Z][L-1] == '\r' || args[Z][L-1] == '\n')
452 XDrawString(st->dpy, st->window, st->text_fg_gc, st->s_rect.x + st->X, st->s_rect.y + st->Y,
454 st->Y += font_height(st->font);
456 st->interval = (total / 15) * 1000;
457 if (st->interval < 2000) st->interval = 2000;
458 st->next_fn = talk_1;
462 talk_1 (struct state *st)
469 look (struct state *st)
473 COPY(st->dpy, (random() & 1) ? st->down : st->front, st->window, st->fg_gc,
474 0, 0, 64, 64, st->x, st->y);
481 COPY(st->dpy, (random() & 1) ? st->left_front : st->right_front,
482 st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
487 COPY(st->dpy, (random() & 1) ? st->left1 : st->right1, st->window, st->fg_gc,
488 0, 0, 64, 64, st->x, st->y);
494 init_words (struct state *st)
496 st->program = get_string_resource (st->dpy, "program", "Program");
498 if (st->program) /* get stderr on stdout, so it shows up on the window */
500 st->orig_program = st->program;
501 st->program = (char *) malloc (strlen (st->program) + 10);
502 strcpy (st->program, "( ");
503 strcat (st->program, st->orig_program);
504 strcat (st->program, " ) 2>&1");
507 st->words = get_words(st);
511 get_words (struct state *st)
514 register char *p = st->word_buf;
516 st->word_buf[0] = '\0';
518 if ((pp = popen(st->program, "r")))
520 while (fgets(p, sizeof(st->word_buf) - strlen(st->word_buf), pp))
522 if (strlen(st->word_buf) + 1 < sizeof(st->word_buf))
523 p = st->word_buf + strlen(st->word_buf);
528 if (! st->word_buf[0])
529 sprintf (st->word_buf, "\"%s\" produced no output!", st->orig_program);
530 else if (!st->first_time &&
531 (strstr (st->word_buf, ": not found") ||
532 strstr (st->word_buf, ": Not found") ||
533 strstr (st->word_buf, ": command not found") ||
534 strstr (st->word_buf, ": Command not found")))
535 switch (random () % 20)
537 case 1: strcat (st->word_buf, "( Get with the st->program, bub. )\n");
539 case 2: strcat (st->word_buf,
540 "( I blow my nose at you, you silly person! ) \n"); break;
541 case 3: strcat (st->word_buf,
542 "\nThe resource you want to\nset is `noseguy.program'\n");
545 strcat(st->word_buf,"\nHelp!! Help!!\nAAAAAAGGGGHHH!! \n\n"); break;
546 case 5: strcpy (st->word_buf, "You have new mail.\n"); break;
548 strcat(st->word_buf,"( Hello? Are you paying attention? )\n");break;
550 strcat (st->word_buf, "sh: what kind of fool do you take me for? \n");
566 static const char *noseguy_defaults [] = {
567 ".background: black",
568 ".foreground: #CCCCCC",
569 "*textForeground: black",
570 "*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)