1 /* xscreensaver, Copyright (c) 1992-2014 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;
42 int state; /* indicates states: walking or getting passwd */
45 void (*next_fn) (struct state *);
47 int move_length, move_dir;
56 int x, y, width, height;
63 static void fill_words (struct state *);
64 static void walk (struct state *, int dir);
65 static void talk (struct state *, int erase);
66 static void talk_1 (struct state *);
67 static int think (struct state *);
68 static unsigned long look (struct state *);
72 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
73 # include "images/noseguy/nose-f1.xpm"
74 # include "images/noseguy/nose-f2.xpm"
75 # include "images/noseguy/nose-f3.xpm"
76 # include "images/noseguy/nose-f4.xpm"
77 # include "images/noseguy/nose-l1.xpm"
78 # include "images/noseguy/nose-l2.xpm"
79 # include "images/noseguy/nose-r1.xpm"
80 # include "images/noseguy/nose-r2.xpm"
82 # include "images/noseguy/nose-f1.xbm"
83 # include "images/noseguy/nose-f2.xbm"
84 # include "images/noseguy/nose-f3.xbm"
85 # include "images/noseguy/nose-f4.xbm"
86 # include "images/noseguy/nose-l1.xbm"
87 # include "images/noseguy/nose-l2.xbm"
88 # include "images/noseguy/nose-r1.xbm"
89 # include "images/noseguy/nose-r2.xbm"
93 init_images (struct state *st)
96 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
99 unsigned char *bits[8];
103 images[i++] = &st->left1;
104 images[i++] = &st->left2;
105 images[i++] = &st->right1;
106 images[i++] = &st->right2;
107 images[i++] = &st->left_front;
108 images[i++] = &st->right_front;
109 images[i++] = &st->front;
110 images[i] = &st->down;
112 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
115 bits[i++] = nose_l1_xpm;
116 bits[i++] = nose_l2_xpm;
117 bits[i++] = nose_r1_xpm;
118 bits[i++] = nose_r2_xpm;
119 bits[i++] = nose_f2_xpm;
120 bits[i++] = nose_f3_xpm;
121 bits[i++] = nose_f1_xpm;
122 bits[i] = nose_f4_xpm;
124 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
126 Pixmap pixmap = xpm_data_to_pixmap (st->dpy, st->window, bits[i],
130 fprintf (stderr, "%s: Can't load nose images\n", progname);
137 bits[i++] = nose_l1_bits;
138 bits[i++] = nose_l2_bits;
139 bits[i++] = nose_r1_bits;
140 bits[i++] = nose_r2_bits;
141 bits[i++] = nose_f2_bits;
142 bits[i++] = nose_f3_bits;
143 bits[i++] = nose_f1_bits;
144 bits[i++] = nose_f4_bits;
146 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
148 XCreatePixmapFromBitmapData(st->dpy, st->window,
149 (char *) bits[i], 64, 64, 1, 0, 1)))
151 fprintf (stderr, "%s: Can't load nose images\n", progname);
166 move (struct state *st)
168 if (!st->move_length)
170 register int tries = 0;
172 if ((random() & 1) && think(st))
174 talk(st, 0); /* sets timeout to itself */
177 if (!(random() % 3) && (st->interval = look(st)))
182 st->interval = 20 + random() % 100;
186 st->move_length = st->Width / 100 + random() % 90, tries = 8;
189 /* There maybe the case that we won't be able to exit from
190 this routine (especially when the geometry is too small)!!
192 Ensure that we can exit from this routine.
195 if (!tries && (st->move_length <= 1)) {
200 switch (random() % 8)
203 if (st->x - X_INCR * st->move_length >= 5)
207 if (st->x + X_INCR * st->move_length <= st->Width - 70)
208 st->move_dir = RIGHT;
211 if (st->y - (Y_INCR * st->move_length) >= 5)
212 st->move_dir = UP, st->interval = 40;
215 if (st->y + Y_INCR * st->move_length <= st->Height - 70)
216 st->move_dir = DOWN, st->interval = 20;
219 if (st->x - X_INCR * st->move_length >= 5 && st->y - (Y_INCR * st->move_length) >= 5)
220 st->move_dir = (LEFT | UP);
223 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
224 st->y - Y_INCR * st->move_length >= 5)
225 st->move_dir = (RIGHT | UP);
228 if (st->x - X_INCR * st->move_length >= 5 &&
229 st->y + Y_INCR * st->move_length <= st->Height - 70)
230 st->move_dir = (LEFT | DOWN);
233 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
234 st->y + Y_INCR * st->move_length <= st->Height - 70)
235 st->move_dir = (RIGHT | DOWN);
241 } while (!st->move_dir);
244 walk(st, st->move_dir);
250 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
251 XCopyArea (dpy,frame,window,gc,x,y,w,h,x2,y2)
253 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
254 XCopyPlane(dpy,frame,window,gc,x,y,w,h,x2,y2,1L)
258 walk (struct state *st, int dir)
260 register int incr = 0;
262 if (dir & (LEFT | RIGHT))
263 { /* left/right movement (mabye up/st->down too) */
264 st->walk_up = -st->walk_up; /* bouncing effect (even if hit a wall) */
268 st->walk_frame = (st->walk_up < 0) ? st->left1 : st->left2;
273 st->walk_frame = (st->walk_up < 0) ? st->right1 : st->right2;
275 if ((st->walk_lastdir == FRONT || st->walk_lastdir == DOWN) && dir & UP)
279 * workaround silly bug that leaves screen dust when guy is
280 * facing forward or st->down and moves up-left/right.
282 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
284 /* note that maybe neither UP nor DOWN is set! */
285 if (dir & UP && st->y > Y_INCR)
287 else if (dir & DOWN && st->y < st->Height - 64)
290 /* Explicit up/st->down movement only (no left/right) */
292 COPY(st->dpy, st->front, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y -= Y_INCR);
293 else if (dir == DOWN)
294 COPY(st->dpy, st->down, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y += Y_INCR);
295 else if (dir == FRONT && st->walk_frame != st->front)
298 st->walk_up = -st->walk_up;
299 if (st->walk_lastdir & LEFT)
300 st->walk_frame = st->left_front;
301 else if (st->walk_lastdir & RIGHT)
302 st->walk_frame = st->right_front;
304 st->walk_frame = st->front;
305 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
310 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, --st->x, st->y + st->walk_up);
312 else if (dir & RIGHT)
315 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, ++st->x, st->y + st->walk_up);
317 st->walk_lastdir = dir;
321 think (struct state *st)
334 talk (struct state *st, int force_erase)
342 char args[MAXLINES][LINELEN];
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;
365 /* If there is actually no words, just return */
374 for (p2 = p; *p2; p2++)
375 if (*p2 == '\t') *p2 = ' ';
377 if (!(p2 = strchr(p, '\n')) || !p2[1])
379 total = strlen (st->words);
380 strncpy (args[0], st->words, LINELEN);
381 args[0][LINELEN - 1] = 0;
382 width = XTextWidth(st->font, st->words, total);
386 /* p2 now points to the first '\n' */
387 for (height = 0; p; height++)
391 if ((w = XTextWidth(st->font, p, p2 - p)) > width)
393 total += p2 - p; /* total chars; count to determine reading
395 (void) strncpy(args[height], p, LINELEN);
396 args[height][LINELEN - 1] = 0;
397 if (height == MAXLINES - 1)
399 /* puts("Message too long!"); */
403 if (!(p2 = strchr(p, '\n')))
409 * Figure out the height and width in pixels (height, width) extend the
410 * new box by 15 pixels on the sides (30 total) top and bottom.
412 st->s_rect.width = width + 30;
413 st->s_rect.height = height * font_height(st->font) + 30;
414 if (st->x - st->s_rect.width - 10 < 5)
416 else if ((st->s_rect.x = st->x + 32 - (st->s_rect.width + 15) / 2)
417 + st->s_rect.width + 15 > st->Width - 5)
418 st->s_rect.x = st->Width - 15 - st->s_rect.width;
419 if (st->y - st->s_rect.height - 10 < 5)
420 st->s_rect.y = st->y + 64 + 5;
422 st->s_rect.y = st->y - 5 - st->s_rect.height;
424 XFillRectangle(st->dpy, st->window, st->text_bg_gc,
425 st->s_rect.x, st->s_rect.y, st->s_rect.width, st->s_rect.height);
427 /* make a box that's 5 pixels thick. Then add a thin box inside it */
428 XSetLineAttributes(st->dpy, st->text_fg_gc, 5, 0, 0, 0);
429 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
430 st->s_rect.x, st->s_rect.y, st->s_rect.width - 1, st->s_rect.height - 1);
431 XSetLineAttributes(st->dpy, st->text_fg_gc, 0, 0, 0, 0);
432 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
433 st->s_rect.x + 7, st->s_rect.y + 7, st->s_rect.width - 15, st->s_rect.height - 15);
436 st->Y = 15 + font_height(st->font);
438 /* now print each string in reverse order (start at bottom of box) */
439 for (Z = 0; Z < height; Z++)
441 int L = strlen(args[Z]);
442 /* If there are continuous new lines, L can be 0 */
443 if (L && (args[Z][L-1] == '\r' || args[Z][L-1] == '\n'))
445 XDrawString(st->dpy, st->window, st->text_fg_gc, st->s_rect.x + st->X, st->s_rect.y + st->Y,
447 st->Y += font_height(st->font);
449 st->interval = (total / 15) * 1000;
450 if (st->interval < 2000) st->interval = 2000;
451 st->next_fn = talk_1;
457 talk_1 (struct state *st)
464 look (struct state *st)
468 COPY(st->dpy, (random() & 1) ? st->down : st->front, st->window, st->fg_gc,
469 0, 0, 64, 64, st->x, st->y);
476 COPY(st->dpy, (random() & 1) ? st->left_front : st->right_front,
477 st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
482 COPY(st->dpy, (random() & 1) ? st->left1 : st->right1, st->window, st->fg_gc,
483 0, 0, 64, 64, st->x, st->y);
489 fill_words (struct state *st)
491 char *p = st->words + strlen(st->words);
496 for (c = st->words; c < p; c++)
500 while (p < st->words + sizeof(st->words) - 1 &&
503 int c = textclient_getc (st->tc);
518 static const char *noseguy_defaults [] = {
519 ".background: black",
520 ".foreground: #CCCCCC",
521 "*textForeground: black",
522 "*textBackground: #CCCCCC",
524 "*program: xscreensaver-text",
526 ".font: -*-new century schoolbook-*-r-*-*-*-180-*-*-*-*-*-*",
530 static XrmOptionDescRec noseguy_options [] = {
531 { "-program", ".program", XrmoptionSepArg, 0 },
532 { "-font", ".font", XrmoptionSepArg, 0 },
533 { "-text-foreground", ".textForeground", XrmoptionSepArg, 0 },
534 { "-text-background", ".textBackground", XrmoptionSepArg, 0 },
540 noseguy_init (Display *d, Window w)
542 struct state *st = (struct state *) calloc (1, sizeof(*st));
543 unsigned long fg, bg, text_fg, text_bg;
544 XWindowAttributes xgwa;
552 fontname = get_string_resource (st->dpy, "font", "Font");
553 XGetWindowAttributes (st->dpy, st->window, &xgwa);
554 st->Width = xgwa.width + 2;
555 st->Height = xgwa.height + 2;
556 cmap = xgwa.colormap;
558 st->tc = textclient_open (st->dpy);
562 textclient_reshape (st->tc, w, h, w, h,
563 /* Passing MAXLINES isn't actually necessary */
569 if (!fontname || !*fontname)
570 fprintf (stderr, "%s: no font specified.\n", progname);
571 st->font = XLoadQueryFont(st->dpy, fontname);
573 fprintf (stderr, "%s: could not load font %s.\n", progname, fontname);
577 fg = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
578 bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
579 text_fg = get_pixel_resource (st->dpy, cmap, "textForeground", "Foreground");
580 text_bg = get_pixel_resource (st->dpy, cmap, "textBackground", "Background");
581 /* notice when unspecified */
582 if (! get_string_resource (st->dpy, "textForeground", "Foreground"))
584 if (! get_string_resource (st->dpy, "textBackground", "Background"))
587 gcvalues.font = st->font->fid;
588 gcvalues.foreground = fg;
589 gcvalues.background = bg;
590 st->fg_gc = XCreateGC (st->dpy, st->window,
591 GCForeground|GCBackground|GCFont,
593 gcvalues.foreground = bg;
594 gcvalues.background = fg;
595 st->bg_gc = XCreateGC (st->dpy, st->window,
596 GCForeground|GCBackground|GCFont,
598 gcvalues.foreground = text_fg;
599 gcvalues.background = text_bg;
600 st->text_fg_gc = XCreateGC (st->dpy, st->window,
601 GCForeground|GCBackground|GCFont,
603 gcvalues.foreground = text_bg;
604 gcvalues.background = text_fg;
605 st->text_bg_gc = XCreateGC (st->dpy, st->window,
606 GCForeground|GCBackground|GCFont,
608 st->x = st->Width / 2;
609 st->y = st->Height / 2;
610 st->state = IS_MOVING;
617 noseguy_draw (Display *dpy, Window window, void *closure)
619 struct state *st = (struct state *) closure;
622 return (st->interval * 1000);
626 noseguy_reshape (Display *dpy, Window window, void *closure,
627 unsigned int w, unsigned int h)
629 struct state *st = (struct state *) closure;
635 noseguy_event (Display *dpy, Window window, void *closure, XEvent *event)
641 noseguy_free (Display *dpy, Window window, void *closure)
643 struct state *st = (struct state *) closure;
644 textclient_close (st->tc);
648 XSCREENSAVER_MODULE ("NoseGuy", noseguy)