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 *);
75 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
76 # include "images/noseguy/nose-f1.xpm"
77 # include "images/noseguy/nose-f2.xpm"
78 # include "images/noseguy/nose-f3.xpm"
79 # include "images/noseguy/nose-f4.xpm"
80 # include "images/noseguy/nose-l1.xpm"
81 # include "images/noseguy/nose-l2.xpm"
82 # include "images/noseguy/nose-r1.xpm"
83 # include "images/noseguy/nose-r2.xpm"
85 # include "images/noseguy/nose-f1.xbm"
86 # include "images/noseguy/nose-f2.xbm"
87 # include "images/noseguy/nose-f3.xbm"
88 # include "images/noseguy/nose-f4.xbm"
89 # include "images/noseguy/nose-l1.xbm"
90 # include "images/noseguy/nose-l2.xbm"
91 # include "images/noseguy/nose-r1.xbm"
92 # include "images/noseguy/nose-r2.xbm"
96 init_images (struct state *st)
99 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
102 unsigned char *bits[8];
106 images[i++] = &st->left1;
107 images[i++] = &st->left2;
108 images[i++] = &st->right1;
109 images[i++] = &st->right2;
110 images[i++] = &st->left_front;
111 images[i++] = &st->right_front;
112 images[i++] = &st->front;
113 images[i] = &st->down;
115 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
118 bits[i++] = nose_l1_xpm;
119 bits[i++] = nose_l2_xpm;
120 bits[i++] = nose_r1_xpm;
121 bits[i++] = nose_r2_xpm;
122 bits[i++] = nose_f2_xpm;
123 bits[i++] = nose_f3_xpm;
124 bits[i++] = nose_f1_xpm;
125 bits[i] = nose_f4_xpm;
127 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
129 Pixmap pixmap = xpm_data_to_pixmap (st->dpy, st->window, bits[i],
133 fprintf (stderr, "%s: Can't load nose images\n", progname);
140 bits[i++] = nose_l1_bits;
141 bits[i++] = nose_l2_bits;
142 bits[i++] = nose_r1_bits;
143 bits[i++] = nose_r2_bits;
144 bits[i++] = nose_f2_bits;
145 bits[i++] = nose_f3_bits;
146 bits[i++] = nose_f1_bits;
147 bits[i++] = nose_f4_bits;
149 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
151 XCreatePixmapFromBitmapData(st->dpy, st->window,
152 (char *) bits[i], 64, 64, 1, 0, 1)))
154 fprintf (stderr, "%s: Can't load nose images\n", progname);
169 move (struct state *st)
171 if (!st->move_length)
173 register int tries = 0;
175 if ((random() & 1) && think(st))
177 talk(st, 0); /* sets timeout to itself */
180 if (!(random() % 3) && (st->interval = look(st)))
185 st->interval = 20 + random() % 100;
189 st->move_length = st->Width / 100 + random() % 90, tries = 8;
192 /* There maybe the case that we won't be able to exit from
193 this routine (especially when the geometry is too small)!!
195 Ensure that we can exit from this routine.
198 if (!tries && (st->move_length <= 1)) {
203 switch (random() % 8)
206 if (st->x - X_INCR * st->move_length >= 5)
210 if (st->x + X_INCR * st->move_length <= st->Width - 70)
211 st->move_dir = RIGHT;
214 if (st->y - (Y_INCR * st->move_length) >= 5)
215 st->move_dir = UP, st->interval = 40;
218 if (st->y + Y_INCR * st->move_length <= st->Height - 70)
219 st->move_dir = DOWN, st->interval = 20;
222 if (st->x - X_INCR * st->move_length >= 5 && st->y - (Y_INCR * st->move_length) >= 5)
223 st->move_dir = (LEFT | UP);
226 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
227 st->y - Y_INCR * st->move_length >= 5)
228 st->move_dir = (RIGHT | UP);
231 if (st->x - X_INCR * st->move_length >= 5 &&
232 st->y + Y_INCR * st->move_length <= st->Height - 70)
233 st->move_dir = (LEFT | DOWN);
236 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
237 st->y + Y_INCR * st->move_length <= st->Height - 70)
238 st->move_dir = (RIGHT | DOWN);
244 } while (!st->move_dir);
247 walk(st, st->move_dir);
253 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
254 XCopyArea (dpy,frame,window,gc,x,y,w,h,x2,y2)
256 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
257 XCopyPlane(dpy,frame,window,gc,x,y,w,h,x2,y2,1L)
261 walk (struct state *st, int dir)
263 register int incr = 0;
265 if (dir & (LEFT | RIGHT))
266 { /* left/right movement (mabye up/st->down too) */
267 st->walk_up = -st->walk_up; /* bouncing effect (even if hit a wall) */
271 st->walk_frame = (st->walk_up < 0) ? st->left1 : st->left2;
276 st->walk_frame = (st->walk_up < 0) ? st->right1 : st->right2;
278 if ((st->walk_lastdir == FRONT || st->walk_lastdir == DOWN) && dir & UP)
282 * workaround silly bug that leaves screen dust when guy is
283 * facing forward or st->down and moves up-left/right.
285 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
287 /* note that maybe neither UP nor DOWN is set! */
288 if (dir & UP && st->y > Y_INCR)
290 else if (dir & DOWN && st->y < st->Height - 64)
293 /* Explicit up/st->down movement only (no left/right) */
295 COPY(st->dpy, st->front, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y -= Y_INCR);
296 else if (dir == DOWN)
297 COPY(st->dpy, st->down, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y += Y_INCR);
298 else if (dir == FRONT && st->walk_frame != st->front)
301 st->walk_up = -st->walk_up;
302 if (st->walk_lastdir & LEFT)
303 st->walk_frame = st->left_front;
304 else if (st->walk_lastdir & RIGHT)
305 st->walk_frame = st->right_front;
307 st->walk_frame = st->front;
308 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
313 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, --st->x, st->y + st->walk_up);
315 else if (dir & RIGHT)
318 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, ++st->x, st->y + st->walk_up);
320 st->walk_lastdir = dir;
324 think (struct state *st)
330 st->words = get_words(st);
339 #define BUFSIZ ((MAXLINES + 1) * 100)
343 talk (struct state *st, int force_erase)
354 /* clear what we've written */
355 if (st->talking || force_erase)
359 XFillRectangle(st->dpy, st->window, st->bg_gc, st->s_rect.x - 5, st->s_rect.y - 5,
360 st->s_rect.width + 10, st->s_rect.height + 10);
366 /* might as well check the st->window for size changes now... */
367 XWindowAttributes xgwa;
368 XGetWindowAttributes (st->dpy, st->window, &xgwa);
369 st->Width = xgwa.width + 2;
370 st->Height = xgwa.height + 2;
376 p = strcpy(buf, st->words);
378 for (p2 = p; *p2; p2++)
379 if (*p2 == '\t') *p2 = ' ';
381 if (!(p2 = strchr(p, '\n')) || !p2[1])
383 total = strlen (st->words);
384 strcpy (args[0], st->words);
385 width = XTextWidth(st->font, st->words, total);
389 /* p2 now points to the first '\n' */
390 for (height = 0; p; height++)
394 if ((w = XTextWidth(st->font, p, p2 - p)) > width)
396 total += p2 - p; /* total chars; count to determine reading
398 (void) strcpy(args[height], p);
399 if (height == MAXLINES - 1)
401 /* puts("Message too long!"); */
405 if (!(p2 = strchr(p, '\n')))
411 * Figure out the height and width in pixels (height, width) extend the
412 * new box by 15 pixels on the sides (30 total) top and bottom.
414 st->s_rect.width = width + 30;
415 st->s_rect.height = height * font_height(st->font) + 30;
416 if (st->x - st->s_rect.width - 10 < 5)
418 else if ((st->s_rect.x = st->x + 32 - (st->s_rect.width + 15) / 2)
419 + st->s_rect.width + 15 > st->Width - 5)
420 st->s_rect.x = st->Width - 15 - st->s_rect.width;
421 if (st->y - st->s_rect.height - 10 < 5)
422 st->s_rect.y = st->y + 64 + 5;
424 st->s_rect.y = st->y - 5 - st->s_rect.height;
426 XFillRectangle(st->dpy, st->window, st->text_bg_gc,
427 st->s_rect.x, st->s_rect.y, st->s_rect.width, st->s_rect.height);
429 /* make a box that's 5 pixels thick. Then add a thin box inside it */
430 XSetLineAttributes(st->dpy, st->text_fg_gc, 5, 0, 0, 0);
431 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
432 st->s_rect.x, st->s_rect.y, st->s_rect.width - 1, st->s_rect.height - 1);
433 XSetLineAttributes(st->dpy, st->text_fg_gc, 0, 0, 0, 0);
434 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
435 st->s_rect.x + 7, st->s_rect.y + 7, st->s_rect.width - 15, st->s_rect.height - 15);
438 st->Y = 15 + font_height(st->font);
440 /* now print each string in reverse order (start at bottom of box) */
441 for (Z = 0; Z < height; Z++)
443 int L = strlen(args[Z]);
444 if (args[Z][L-1] == '\r' || args[Z][L-1] == '\n')
446 XDrawString(st->dpy, st->window, st->text_fg_gc, st->s_rect.x + st->X, st->s_rect.y + st->Y,
448 st->Y += font_height(st->font);
450 st->interval = (total / 15) * 1000;
451 if (st->interval < 2000) st->interval = 2000;
452 st->next_fn = talk_1;
456 talk_1 (struct state *st)
463 look (struct state *st)
467 COPY(st->dpy, (random() & 1) ? st->down : st->front, st->window, st->fg_gc,
468 0, 0, 64, 64, st->x, st->y);
475 COPY(st->dpy, (random() & 1) ? st->left_front : st->right_front,
476 st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
481 COPY(st->dpy, (random() & 1) ? st->left1 : st->right1, st->window, st->fg_gc,
482 0, 0, 64, 64, st->x, st->y);
488 init_words (struct state *st)
490 st->program = get_string_resource (st->dpy, "program", "Program");
492 if (st->program) /* get stderr on stdout, so it shows up on the window */
494 st->orig_program = st->program;
495 st->program = (char *) malloc (strlen (st->program) + 10);
496 strcpy (st->program, "( ");
497 strcat (st->program, st->orig_program);
498 strcat (st->program, " ) 2>&1");
501 st->words = get_words(st);
505 get_words (struct state *st)
508 register char *p = st->word_buf;
510 st->word_buf[0] = '\0';
512 if ((pp = popen(st->program, "r")))
514 while (fgets(p, sizeof(st->word_buf) - strlen(st->word_buf), pp))
516 if (strlen(st->word_buf) + 1 < sizeof(st->word_buf))
517 p = st->word_buf + strlen(st->word_buf);
522 if (! st->word_buf[0])
523 sprintf (st->word_buf, "\"%s\" produced no output!", st->orig_program);
524 else if (!st->first_time &&
525 (strstr (st->word_buf, ": not found") ||
526 strstr (st->word_buf, ": Not found") ||
527 strstr (st->word_buf, ": command not found") ||
528 strstr (st->word_buf, ": Command not found")))
529 switch (random () % 20)
531 case 1: strcat (st->word_buf, "( Get with the st->program, bub. )\n");
533 case 2: strcat (st->word_buf,
534 "( I blow my nose at you, you silly person! ) \n"); break;
535 case 3: strcat (st->word_buf,
536 "\nThe resource you want to\nset is `noseguy.program'\n");
539 strcat(st->word_buf,"\nHelp!! Help!!\nAAAAAAGGGGHHH!! \n\n"); break;
540 case 5: strcpy (st->word_buf, "You have new mail.\n"); break;
542 strcat(st->word_buf,"( Hello? Are you paying attention? )\n");break;
544 strcat (st->word_buf, "sh: what kind of fool do you take me for? \n");
560 static const char *noseguy_defaults [] = {
561 ".background: black",
562 ".foreground: #CCCCCC",
563 "*textForeground: black",
564 "*textBackground: #CCCCCC",
566 "*program: xscreensaver-text --cols 40 | head -n15",
567 ".font: -*-new century schoolbook-*-r-*-*-*-180-*-*-*-*-*-*",
571 static XrmOptionDescRec noseguy_options [] = {
572 { "-program", ".program", XrmoptionSepArg, 0 },
573 { "-font", ".font", XrmoptionSepArg, 0 },
574 { "-text-foreground", ".textForeground", XrmoptionSepArg, 0 },
575 { "-text-background", ".textBackground", XrmoptionSepArg, 0 },
581 noseguy_init (Display *d, Window w)
583 struct state *st = (struct state *) calloc (1, sizeof(*st));
584 unsigned long fg, bg, text_fg, text_bg;
585 XWindowAttributes xgwa;
593 fontname = get_string_resource (st->dpy, "font", "Font");
594 XGetWindowAttributes (st->dpy, st->window, &xgwa);
595 st->Width = xgwa.width + 2;
596 st->Height = xgwa.height + 2;
597 cmap = xgwa.colormap;
602 if (!fontname || !*fontname)
603 fprintf (stderr, "%s: no font specified.\n", progname);
604 st->font = XLoadQueryFont(st->dpy, fontname);
606 fprintf (stderr, "%s: could not load font %s.\n", progname, fontname);
610 fg = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
611 bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
612 text_fg = get_pixel_resource (st->dpy, cmap, "textForeground", "Foreground");
613 text_bg = get_pixel_resource (st->dpy, cmap, "textBackground", "Background");
614 /* notice when unspecified */
615 if (! get_string_resource (st->dpy, "textForeground", "Foreground"))
617 if (! get_string_resource (st->dpy, "textBackground", "Background"))
620 gcvalues.font = st->font->fid;
621 gcvalues.foreground = fg;
622 gcvalues.background = bg;
623 st->fg_gc = XCreateGC (st->dpy, st->window,
624 GCForeground|GCBackground|GCFont,
626 gcvalues.foreground = bg;
627 gcvalues.background = fg;
628 st->bg_gc = XCreateGC (st->dpy, st->window,
629 GCForeground|GCBackground|GCFont,
631 gcvalues.foreground = text_fg;
632 gcvalues.background = text_bg;
633 st->text_fg_gc = XCreateGC (st->dpy, st->window,
634 GCForeground|GCBackground|GCFont,
636 gcvalues.foreground = text_bg;
637 gcvalues.background = text_fg;
638 st->text_bg_gc = XCreateGC (st->dpy, st->window,
639 GCForeground|GCBackground|GCFont,
641 st->x = st->Width / 2;
642 st->y = st->Height / 2;
643 st->state = IS_MOVING;
650 noseguy_draw (Display *dpy, Window window, void *closure)
652 struct state *st = (struct state *) closure;
654 return (st->interval * 1000);
658 noseguy_reshape (Display *dpy, Window window, void *closure,
659 unsigned int w, unsigned int h)
664 noseguy_event (Display *dpy, Window window, void *closure, XEvent *event)
670 noseguy_free (Display *dpy, Window window, void *closure)
674 XSCREENSAVER_MODULE ("NoseGuy", noseguy)