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"
26 #define font_height(font) (font->ascent + font->descent)
33 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;
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;
67 static void fill_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 *);
76 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
77 # include "images/noseguy/nose-f1.xpm"
78 # include "images/noseguy/nose-f2.xpm"
79 # include "images/noseguy/nose-f3.xpm"
80 # include "images/noseguy/nose-f4.xpm"
81 # include "images/noseguy/nose-l1.xpm"
82 # include "images/noseguy/nose-l2.xpm"
83 # include "images/noseguy/nose-r1.xpm"
84 # include "images/noseguy/nose-r2.xpm"
86 # include "images/noseguy/nose-f1.xbm"
87 # include "images/noseguy/nose-f2.xbm"
88 # include "images/noseguy/nose-f3.xbm"
89 # include "images/noseguy/nose-f4.xbm"
90 # include "images/noseguy/nose-l1.xbm"
91 # include "images/noseguy/nose-l2.xbm"
92 # include "images/noseguy/nose-r1.xbm"
93 # include "images/noseguy/nose-r2.xbm"
97 init_images (struct state *st)
100 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
103 unsigned char *bits[8];
107 images[i++] = &st->left1;
108 images[i++] = &st->left2;
109 images[i++] = &st->right1;
110 images[i++] = &st->right2;
111 images[i++] = &st->left_front;
112 images[i++] = &st->right_front;
113 images[i++] = &st->front;
114 images[i] = &st->down;
116 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
119 bits[i++] = nose_l1_xpm;
120 bits[i++] = nose_l2_xpm;
121 bits[i++] = nose_r1_xpm;
122 bits[i++] = nose_r2_xpm;
123 bits[i++] = nose_f2_xpm;
124 bits[i++] = nose_f3_xpm;
125 bits[i++] = nose_f1_xpm;
126 bits[i] = nose_f4_xpm;
128 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
130 Pixmap pixmap = xpm_data_to_pixmap (st->dpy, st->window, bits[i],
134 fprintf (stderr, "%s: Can't load nose images\n", progname);
141 bits[i++] = nose_l1_bits;
142 bits[i++] = nose_l2_bits;
143 bits[i++] = nose_r1_bits;
144 bits[i++] = nose_r2_bits;
145 bits[i++] = nose_f2_bits;
146 bits[i++] = nose_f3_bits;
147 bits[i++] = nose_f1_bits;
148 bits[i++] = nose_f4_bits;
150 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
152 XCreatePixmapFromBitmapData(st->dpy, st->window,
153 (char *) bits[i], 64, 64, 1, 0, 1)))
155 fprintf (stderr, "%s: Can't load nose images\n", progname);
170 move (struct state *st)
172 if (!st->move_length)
174 register int tries = 0;
176 if ((random() & 1) && think(st))
178 talk(st, 0); /* sets timeout to itself */
181 if (!(random() % 3) && (st->interval = look(st)))
186 st->interval = 20 + random() % 100;
190 st->move_length = st->Width / 100 + random() % 90, tries = 8;
193 /* There maybe the case that we won't be able to exit from
194 this routine (especially when the geometry is too small)!!
196 Ensure that we can exit from this routine.
199 if (!tries && (st->move_length <= 1)) {
204 switch (random() % 8)
207 if (st->x - X_INCR * st->move_length >= 5)
211 if (st->x + X_INCR * st->move_length <= st->Width - 70)
212 st->move_dir = RIGHT;
215 if (st->y - (Y_INCR * st->move_length) >= 5)
216 st->move_dir = UP, st->interval = 40;
219 if (st->y + Y_INCR * st->move_length <= st->Height - 70)
220 st->move_dir = DOWN, st->interval = 20;
223 if (st->x - X_INCR * st->move_length >= 5 && st->y - (Y_INCR * st->move_length) >= 5)
224 st->move_dir = (LEFT | UP);
227 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
228 st->y - Y_INCR * st->move_length >= 5)
229 st->move_dir = (RIGHT | UP);
232 if (st->x - X_INCR * st->move_length >= 5 &&
233 st->y + Y_INCR * st->move_length <= st->Height - 70)
234 st->move_dir = (LEFT | DOWN);
237 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
238 st->y + Y_INCR * st->move_length <= st->Height - 70)
239 st->move_dir = (RIGHT | DOWN);
245 } while (!st->move_dir);
248 walk(st, st->move_dir);
254 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
255 XCopyArea (dpy,frame,window,gc,x,y,w,h,x2,y2)
257 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) \
258 XCopyPlane(dpy,frame,window,gc,x,y,w,h,x2,y2,1L)
262 walk (struct state *st, int dir)
264 register int incr = 0;
266 if (dir & (LEFT | RIGHT))
267 { /* left/right movement (mabye up/st->down too) */
268 st->walk_up = -st->walk_up; /* bouncing effect (even if hit a wall) */
272 st->walk_frame = (st->walk_up < 0) ? st->left1 : st->left2;
277 st->walk_frame = (st->walk_up < 0) ? st->right1 : st->right2;
279 if ((st->walk_lastdir == FRONT || st->walk_lastdir == DOWN) && dir & UP)
283 * workaround silly bug that leaves screen dust when guy is
284 * facing forward or st->down and moves up-left/right.
286 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
288 /* note that maybe neither UP nor DOWN is set! */
289 if (dir & UP && st->y > Y_INCR)
291 else if (dir & DOWN && st->y < st->Height - 64)
294 /* Explicit up/st->down movement only (no left/right) */
296 COPY(st->dpy, st->front, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y -= Y_INCR);
297 else if (dir == DOWN)
298 COPY(st->dpy, st->down, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y += Y_INCR);
299 else if (dir == FRONT && st->walk_frame != st->front)
302 st->walk_up = -st->walk_up;
303 if (st->walk_lastdir & LEFT)
304 st->walk_frame = st->left_front;
305 else if (st->walk_lastdir & RIGHT)
306 st->walk_frame = st->right_front;
308 st->walk_frame = st->front;
309 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
314 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, --st->x, st->y + st->walk_up);
316 else if (dir & RIGHT)
319 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, 0, 0, 64, 64, ++st->x, st->y + st->walk_up);
321 st->walk_lastdir = dir;
325 think (struct state *st)
338 talk (struct state *st, int force_erase)
346 char args[MAXLINES][LINELEN];
348 /* clear what we've written */
349 if (st->talking || force_erase)
353 XFillRectangle(st->dpy, st->window, st->bg_gc, st->s_rect.x - 5, st->s_rect.y - 5,
354 st->s_rect.width + 10, st->s_rect.height + 10);
360 /* might as well check the st->window for size changes now... */
361 XWindowAttributes xgwa;
362 XGetWindowAttributes (st->dpy, st->window, &xgwa);
363 st->Width = xgwa.width + 2;
364 st->Height = xgwa.height + 2;
369 /* If there is actually no words, just return */
378 for (p2 = p; *p2; p2++)
379 if (*p2 == '\t') *p2 = ' ';
381 if (!(p2 = strchr(p, '\n')) || !p2[1])
385 total = strlen (st->words);
386 strncpy (args[0], st->words, LINELEN);
387 args[0][LINELEN - 1] = 0;
388 XftTextExtentsUtf8 (st->dpy, st->xftfont,
389 (FcChar8 *) st->words, total,
391 width = extents.xOff;
395 /* p2 now points to the first '\n' */
396 for (height = 0; p; height++)
402 XftTextExtentsUtf8 (st->dpy, st->xftfont,
403 (FcChar8 *) p, p2 - p,
409 total += p2 - p; /* total chars; count to determine reading
411 (void) strncpy(args[height], p, LINELEN);
412 args[height][LINELEN - 1] = 0;
413 if (height == MAXLINES - 1)
415 /* puts("Message too long!"); */
419 if (!(p2 = strchr(p, '\n')))
425 * Figure out the height and width in pixels (height, width) extend the
426 * new box by 15 pixels on the sides (30 total) top and bottom.
428 st->s_rect.width = width + 30;
429 st->s_rect.height = height * font_height(st->xftfont) + 30;
430 if (st->x - st->s_rect.width - 10 < 5)
432 else if ((st->s_rect.x = st->x + 32 - (st->s_rect.width + 15) / 2)
433 + st->s_rect.width + 15 > st->Width - 5)
434 st->s_rect.x = st->Width - 15 - st->s_rect.width;
435 if (st->y - st->s_rect.height - 10 < 5)
436 st->s_rect.y = st->y + 64 + 5;
438 st->s_rect.y = st->y - 5 - st->s_rect.height;
440 XFillRectangle(st->dpy, st->window, st->text_bg_gc,
441 st->s_rect.x, st->s_rect.y, st->s_rect.width, st->s_rect.height);
443 /* make a box that's 5 pixels thick. Then add a thin box inside it */
444 XSetLineAttributes(st->dpy, st->text_fg_gc, 5, 0, 0, 0);
445 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
446 st->s_rect.x, st->s_rect.y, st->s_rect.width - 1, st->s_rect.height - 1);
447 XSetLineAttributes(st->dpy, st->text_fg_gc, 0, 0, 0, 0);
448 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
449 st->s_rect.x + 7, st->s_rect.y + 7, st->s_rect.width - 15, st->s_rect.height - 15);
452 st->Y = 15 + font_height(st->xftfont);
454 /* now print each string in reverse order (start at bottom of box) */
455 for (Z = 0; Z < height; Z++)
457 int L = strlen(args[Z]);
458 /* If there are continuous new lines, L can be 0 */
459 if (L && (args[Z][L-1] == '\r' || args[Z][L-1] == '\n'))
461 XftDrawStringUtf8 (st->xftdraw, &st->xftcolor, st->xftfont,
462 st->s_rect.x + st->X, st->s_rect.y + st->Y,
463 (FcChar8 *) args[Z], L);
465 st->Y += font_height(st->xftfont);
467 st->interval = (total / 15) * 1000;
468 if (st->interval < 2000) st->interval = 2000;
469 st->next_fn = talk_1;
475 talk_1 (struct state *st)
482 look (struct state *st)
486 COPY(st->dpy, (random() & 1) ? st->down : st->front, st->window, st->fg_gc,
487 0, 0, 64, 64, st->x, st->y);
494 COPY(st->dpy, (random() & 1) ? st->left_front : st->right_front,
495 st->window, st->fg_gc, 0, 0, 64, 64, st->x, st->y);
500 COPY(st->dpy, (random() & 1) ? st->left1 : st->right1, st->window, st->fg_gc,
501 0, 0, 64, 64, st->x, st->y);
507 fill_words (struct state *st)
509 char *p = st->words + strlen(st->words);
514 for (c = st->words; c < p; c++)
518 while (p < st->words + sizeof(st->words) - 1 &&
521 int c = textclient_getc (st->tc);
536 static const char *noseguy_defaults [] = {
537 ".background: black",
538 ".foreground: #CCCCCC",
539 "*textForeground: black",
540 "*textBackground: #CCCCCC",
542 "*program: xscreensaver-text",
544 ".font: -*-helvetica-medium-r-*-*-*-140-*-*-*-*-*-*",
548 static XrmOptionDescRec noseguy_options [] = {
549 { "-program", ".program", XrmoptionSepArg, 0 },
550 { "-font", ".font", XrmoptionSepArg, 0 },
551 { "-text-foreground", ".textForeground", XrmoptionSepArg, 0 },
552 { "-text-background", ".textBackground", XrmoptionSepArg, 0 },
558 noseguy_init (Display *d, Window w)
560 struct state *st = (struct state *) calloc (1, sizeof(*st));
561 unsigned long fg, bg, text_fg, text_bg;
562 XWindowAttributes xgwa;
570 fontname = get_string_resource (st->dpy, "font", "Font");
571 XGetWindowAttributes (st->dpy, st->window, &xgwa);
572 st->Width = xgwa.width + 2;
573 st->Height = xgwa.height + 2;
574 cmap = xgwa.colormap;
576 st->tc = textclient_open (st->dpy);
580 textclient_reshape (st->tc, w, h, w, h,
581 /* Passing MAXLINES isn't actually necessary */
587 st->xftfont = XftFontOpenXlfd (st->dpy, screen_number (xgwa.screen),
589 XftColorAllocName (st->dpy, xgwa.visual, xgwa.colormap,
590 get_string_resource (st->dpy,
591 "textForeground", "Foreground"),
593 st->xftdraw = XftDrawCreate (st->dpy, st->window, xgwa.visual,
597 fg = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
598 bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
599 text_fg = get_pixel_resource (st->dpy, cmap, "textForeground", "Foreground");
600 text_bg = get_pixel_resource (st->dpy, cmap, "textBackground", "Background");
601 /* notice when unspecified */
602 if (! get_string_resource (st->dpy, "textForeground", "Foreground"))
604 if (! get_string_resource (st->dpy, "textBackground", "Background"))
607 gcvalues.foreground = fg;
608 gcvalues.background = bg;
609 st->fg_gc = XCreateGC (st->dpy, st->window,
610 GCForeground|GCBackground,
612 gcvalues.foreground = bg;
613 gcvalues.background = fg;
614 st->bg_gc = XCreateGC (st->dpy, st->window,
615 GCForeground|GCBackground,
617 gcvalues.foreground = text_fg;
618 gcvalues.background = text_bg;
619 st->text_fg_gc = XCreateGC (st->dpy, st->window,
620 GCForeground|GCBackground,
622 gcvalues.foreground = text_bg;
623 gcvalues.background = text_fg;
624 st->text_bg_gc = XCreateGC (st->dpy, st->window,
625 GCForeground|GCBackground,
627 st->x = st->Width / 2;
628 st->y = st->Height / 2;
629 st->state = IS_MOVING;
636 noseguy_draw (Display *dpy, Window window, void *closure)
638 struct state *st = (struct state *) closure;
641 return (st->interval * 1000);
645 noseguy_reshape (Display *dpy, Window window, void *closure,
646 unsigned int w, unsigned int h)
648 struct state *st = (struct state *) closure;
654 noseguy_event (Display *dpy, Window window, void *closure, XEvent *event)
660 noseguy_free (Display *dpy, Window window, void *closure)
662 struct state *st = (struct state *) closure;
663 textclient_close (st->tc);
667 XSCREENSAVER_MODULE ("NoseGuy", noseguy)