1 /* xscreensaver, Copyright (c) 1992-2012 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"
19 #include "textclient.h"
25 #define font_height(font) (font->ascent + font->descent)
32 GC fg_gc, bg_gc, text_fg_gc, text_bg_gc;
36 unsigned long interval;
37 Pixmap left1, left2, right1, right2;
38 Pixmap left_front, right_front, front, down;
43 int state; /* indicates states: walking or getting passwd */
46 void (*next_fn) (struct state *);
48 int move_length, move_dir;
57 int x, y, width, height;
64 static void fill_words (struct state *);
65 static void walk (struct state *, int dir);
66 static void talk (struct state *, int erase);
67 static void talk_1 (struct state *);
68 static int think (struct state *);
69 static unsigned long look (struct state *);
73 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
74 # include "images/noseguy/nose-f1.xpm"
75 # include "images/noseguy/nose-f2.xpm"
76 # include "images/noseguy/nose-f3.xpm"
77 # include "images/noseguy/nose-f4.xpm"
78 # include "images/noseguy/nose-l1.xpm"
79 # include "images/noseguy/nose-l2.xpm"
80 # include "images/noseguy/nose-r1.xpm"
81 # include "images/noseguy/nose-r2.xpm"
83 # include "images/noseguy/nose-f1.xbm"
84 # include "images/noseguy/nose-f2.xbm"
85 # include "images/noseguy/nose-f3.xbm"
86 # include "images/noseguy/nose-f4.xbm"
87 # include "images/noseguy/nose-l1.xbm"
88 # include "images/noseguy/nose-l2.xbm"
89 # include "images/noseguy/nose-r1.xbm"
90 # include "images/noseguy/nose-r2.xbm"
94 init_images (struct state *st)
97 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
100 unsigned char *bits[8];
104 images[i++] = &st->left1;
105 images[i++] = &st->left2;
106 images[i++] = &st->right1;
107 images[i++] = &st->right2;
108 images[i++] = &st->left_front;
109 images[i++] = &st->right_front;
110 images[i++] = &st->front;
111 images[i] = &st->down;
113 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
116 bits[i++] = nose_l1_xpm;
117 bits[i++] = nose_l2_xpm;
118 bits[i++] = nose_r1_xpm;
119 bits[i++] = nose_r2_xpm;
120 bits[i++] = nose_f2_xpm;
121 bits[i++] = nose_f3_xpm;
122 bits[i++] = nose_f1_xpm;
123 bits[i] = nose_f4_xpm;
125 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
127 Pixmap pixmap = xpm_data_to_pixmap (st->dpy, st->window, bits[i],
131 fprintf (stderr, "%s: Can't load nose images\n", progname);
138 bits[i++] = nose_l1_bits;
139 bits[i++] = nose_l2_bits;
140 bits[i++] = nose_r1_bits;
141 bits[i++] = nose_r2_bits;
142 bits[i++] = nose_f2_bits;
143 bits[i++] = nose_f3_bits;
144 bits[i++] = nose_f1_bits;
145 bits[i++] = nose_f4_bits;
147 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
149 XCreatePixmapFromBitmapData(st->dpy, st->window,
150 (char *) bits[i], 64, 64, 1, 0, 1)))
152 fprintf (stderr, "%s: Can't load nose images\n", progname);
167 move (struct state *st)
169 if (!st->move_length)
171 register int tries = 0;
173 if ((random() & 1) && think(st))
175 talk(st, 0); /* sets timeout to itself */
178 if (!(random() % 3) && (st->interval = look(st)))
183 st->interval = 20 + random() % 100;
187 st->move_length = st->Width / 100 + random() % 90, tries = 8;
190 /* There maybe the case that we won't be able to exit from
191 this routine (especially when the geometry is too small)!!
193 Ensure that we can exit from this routine.
196 if (!tries && (st->move_length <= 1)) {
201 switch (random() % 8)
204 if (st->x - X_INCR * st->move_length >= 5)
208 if (st->x + X_INCR * st->move_length <= st->Width - 70)
209 st->move_dir = RIGHT;
212 if (st->y - (Y_INCR * st->move_length) >= 5)
213 st->move_dir = UP, st->interval = 40;
216 if (st->y + Y_INCR * st->move_length <= st->Height - 70)
217 st->move_dir = DOWN, st->interval = 20;
220 if (st->x - X_INCR * st->move_length >= 5 && st->y - (Y_INCR * st->move_length) >= 5)
221 st->move_dir = (LEFT | UP);
224 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
225 st->y - Y_INCR * st->move_length >= 5)
226 st->move_dir = (RIGHT | UP);
229 if (st->x - X_INCR * st->move_length >= 5 &&
230 st->y + Y_INCR * st->move_length <= st->Height - 70)
231 st->move_dir = (LEFT | DOWN);
234 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
235 st->y + Y_INCR * st->move_length <= st->Height - 70)
236 st->move_dir = (RIGHT | DOWN);
242 } while (!st->move_dir);
245 walk(st, st->move_dir);
251 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
252 XCopyArea (dpy,frame,window,gc,x,y,w,h,x2,y2)
254 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
255 XCopyPlane(dpy,frame,window,gc,x,y,w,h,x2,y2,1L)
259 walk (struct state *st, int dir)
261 register int incr = 0;
263 if (dir & (LEFT | RIGHT))
264 { /* left/right movement (mabye up/st->down too) */
265 st->walk_up = -st->walk_up; /* bouncing effect (even if hit a wall) */
269 st->walk_frame = (st->walk_up < 0) ? st->left1 : st->left2;
274 st->walk_frame = (st->walk_up < 0) ? st->right1 : st->right2;
276 if ((st->walk_lastdir == FRONT || st->walk_lastdir == DOWN) && dir & UP)
280 * workaround silly bug that leaves screen dust when guy is
281 * facing forward or st->down and moves up-left/right.
283 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
285 /* note that maybe neither UP nor DOWN is set! */
286 if (dir & UP && st->y > Y_INCR)
288 else if (dir & DOWN && st->y < st->Height - 64)
291 /* Explicit up/st->down movement only (no left/right) */
293 COPY(st->dpy, st->front, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y -= Y_INCR);
294 else if (dir == DOWN)
295 COPY(st->dpy, st->down, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y += Y_INCR);
296 else if (dir == FRONT && st->walk_frame != st->front)
299 st->walk_up = -st->walk_up;
300 if (st->walk_lastdir & LEFT)
301 st->walk_frame = st->left_front;
302 else if (st->walk_lastdir & RIGHT)
303 st->walk_frame = st->right_front;
305 st->walk_frame = st->front;
306 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
311 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, --st->x, st->y + st->walk_up);
313 else if (dir & RIGHT)
316 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, ++st->x, st->y + st->walk_up);
318 st->walk_lastdir = dir;
322 think (struct state *st)
334 talk (struct state *st, int force_erase)
342 char args[MAXLINES][256];
344 /* clear what we've written */
345 if (st->talking || force_erase)
349 XFillRectangle(st->dpy, st->window, st->bg_gc, st->s_rect.x - 5, st->s_rect.y - 5,
350 st->s_rect.width + 10, st->s_rect.height + 10);
356 /* might as well check the st->window for size changes now... */
357 XWindowAttributes xgwa;
358 XGetWindowAttributes (st->dpy, st->window, &xgwa);
359 st->Width = xgwa.width + 2;
360 st->Height = xgwa.height + 2;
368 for (p2 = p; *p2; p2++)
369 if (*p2 == '\t') *p2 = ' ';
371 if (!(p2 = strchr(p, '\n')) || !p2[1])
373 total = strlen (st->words);
374 strcpy (args[0], st->words);
375 width = XTextWidth(st->font, st->words, total);
379 /* p2 now points to the first '\n' */
380 for (height = 0; p; height++)
384 if ((w = XTextWidth(st->font, p, p2 - p)) > width)
386 total += p2 - p; /* total chars; count to determine reading
388 (void) strcpy(args[height], p);
389 if (height == MAXLINES - 1)
391 /* puts("Message too long!"); */
395 if (!(p2 = strchr(p, '\n')))
401 * Figure out the height and width in pixels (height, width) extend the
402 * new box by 15 pixels on the sides (30 total) top and bottom.
404 st->s_rect.width = width + 30;
405 st->s_rect.height = height * font_height(st->font) + 30;
406 if (st->x - st->s_rect.width - 10 < 5)
408 else if ((st->s_rect.x = st->x + 32 - (st->s_rect.width + 15) / 2)
409 + st->s_rect.width + 15 > st->Width - 5)
410 st->s_rect.x = st->Width - 15 - st->s_rect.width;
411 if (st->y - st->s_rect.height - 10 < 5)
412 st->s_rect.y = st->y + 64 + 5;
414 st->s_rect.y = st->y - 5 - st->s_rect.height;
416 XFillRectangle(st->dpy, st->window, st->text_bg_gc,
417 st->s_rect.x, st->s_rect.y, st->s_rect.width, st->s_rect.height);
419 /* make a box that's 5 pixels thick. Then add a thin box inside it */
420 XSetLineAttributes(st->dpy, st->text_fg_gc, 5, 0, 0, 0);
421 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
422 st->s_rect.x, st->s_rect.y, st->s_rect.width - 1, st->s_rect.height - 1);
423 XSetLineAttributes(st->dpy, st->text_fg_gc, 0, 0, 0, 0);
424 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
425 st->s_rect.x + 7, st->s_rect.y + 7, st->s_rect.width - 15, st->s_rect.height - 15);
428 st->Y = 15 + font_height(st->font);
430 /* now print each string in reverse order (start at bottom of box) */
431 for (Z = 0; Z < height; Z++)
433 int L = strlen(args[Z]);
434 if (args[Z][L-1] == '\r' || args[Z][L-1] == '\n')
436 XDrawString(st->dpy, st->window, st->text_fg_gc, st->s_rect.x + st->X, st->s_rect.y + st->Y,
438 st->Y += font_height(st->font);
440 st->interval = (total / 15) * 1000;
441 if (st->interval < 2000) st->interval = 2000;
442 st->next_fn = talk_1;
448 talk_1 (struct state *st)
455 look (struct state *st)
459 COPY(st->dpy, (random() & 1) ? st->down : st->front, st->window, st->fg_gc,
460 0, 0, 64, 64, st->x, st->y);
467 COPY(st->dpy, (random() & 1) ? st->left_front : st->right_front,
468 st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
473 COPY(st->dpy, (random() & 1) ? st->left1 : st->right1, st->window, st->fg_gc,
474 0, 0, 64, 64, st->x, st->y);
480 fill_words (struct state *st)
482 char *p = st->words + strlen(st->words);
483 while (p < st->words + sizeof(st->words) - 1 &&
484 st->lines < MAXLINES)
486 char c = textclient_getc (st->tc);
499 static const char *noseguy_defaults [] = {
500 ".background: black",
501 ".foreground: #CCCCCC",
502 "*textForeground: black",
503 "*textBackground: #CCCCCC",
505 "*program: xscreensaver-text --cols 40 | head -n15",
507 ".font: -*-new century schoolbook-*-r-*-*-*-180-*-*-*-*-*-*",
511 static XrmOptionDescRec noseguy_options [] = {
512 { "-program", ".program", XrmoptionSepArg, 0 },
513 { "-font", ".font", XrmoptionSepArg, 0 },
514 { "-text-foreground", ".textForeground", XrmoptionSepArg, 0 },
515 { "-text-background", ".textBackground", XrmoptionSepArg, 0 },
521 noseguy_init (Display *d, Window w)
523 struct state *st = (struct state *) calloc (1, sizeof(*st));
524 unsigned long fg, bg, text_fg, text_bg;
525 XWindowAttributes xgwa;
533 fontname = get_string_resource (st->dpy, "font", "Font");
534 XGetWindowAttributes (st->dpy, st->window, &xgwa);
535 st->Width = xgwa.width + 2;
536 st->Height = xgwa.height + 2;
537 cmap = xgwa.colormap;
539 st->program = get_string_resource (st->dpy, "program", "Program");
540 st->tc = textclient_open (st->dpy);
543 if (!fontname || !*fontname)
544 fprintf (stderr, "%s: no font specified.\n", progname);
545 st->font = XLoadQueryFont(st->dpy, fontname);
547 fprintf (stderr, "%s: could not load font %s.\n", progname, fontname);
551 fg = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
552 bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
553 text_fg = get_pixel_resource (st->dpy, cmap, "textForeground", "Foreground");
554 text_bg = get_pixel_resource (st->dpy, cmap, "textBackground", "Background");
555 /* notice when unspecified */
556 if (! get_string_resource (st->dpy, "textForeground", "Foreground"))
558 if (! get_string_resource (st->dpy, "textBackground", "Background"))
561 gcvalues.font = st->font->fid;
562 gcvalues.foreground = fg;
563 gcvalues.background = bg;
564 st->fg_gc = XCreateGC (st->dpy, st->window,
565 GCForeground|GCBackground|GCFont,
567 gcvalues.foreground = bg;
568 gcvalues.background = fg;
569 st->bg_gc = XCreateGC (st->dpy, st->window,
570 GCForeground|GCBackground|GCFont,
572 gcvalues.foreground = text_fg;
573 gcvalues.background = text_bg;
574 st->text_fg_gc = XCreateGC (st->dpy, st->window,
575 GCForeground|GCBackground|GCFont,
577 gcvalues.foreground = text_bg;
578 gcvalues.background = text_fg;
579 st->text_bg_gc = XCreateGC (st->dpy, st->window,
580 GCForeground|GCBackground|GCFont,
582 st->x = st->Width / 2;
583 st->y = st->Height / 2;
584 st->state = IS_MOVING;
591 noseguy_draw (Display *dpy, Window window, void *closure)
593 struct state *st = (struct state *) closure;
596 return (st->interval * 1000);
600 noseguy_reshape (Display *dpy, Window window, void *closure,
601 unsigned int w, unsigned int h)
603 struct state *st = (struct state *) closure;
609 noseguy_event (Display *dpy, Window window, void *closure, XEvent *event)
615 noseguy_free (Display *dpy, Window window, void *closure)
617 struct state *st = (struct state *) closure;
618 textclient_close (st->tc);
622 XSCREENSAVER_MODULE ("NoseGuy", noseguy)