1 /* xscreensaver, Copyright (c) 1992-2018 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 "ximage-loader.h"
19 #include "textclient.h"
22 #define font_height(font) (font->ascent + font->descent)
25 typedef struct { Pixmap p, m; } PM;
31 GC fg_gc, bg_gc, text_fg_gc, text_bg_gc;
38 unsigned long interval;
39 PM left1, left2, right1, right2, left_front, right_front, front, down;
44 int state; /* indicates states: walking or getting passwd */
47 void (*next_fn) (struct state *);
49 int move_length, move_dir;
58 int x, y, width, height;
65 static void fill_words (struct state *);
66 static void walk (struct state *, int dir);
67 static void talk (struct state *, int erase);
68 static void talk_1 (struct state *);
69 static int think (struct state *);
70 static unsigned long look (struct state *);
74 #include "images/gen/nose-f1_png.h"
75 #include "images/gen/nose-f2_png.h"
76 #include "images/gen/nose-f3_png.h"
77 #include "images/gen/nose-f4_png.h"
78 #include "images/gen/nose-l1_png.h"
79 #include "images/gen/nose-l2_png.h"
80 #include "images/gen/nose-r1_png.h"
81 #include "images/gen/nose-r2_png.h"
84 double_pixmap (Display *dpy, Visual *visual, int depth, Pixmap pixmap,
88 Pixmap p2 = XCreatePixmap(dpy, pixmap, pix_w*2, pix_h*2, depth);
89 XImage *i1 = XGetImage (dpy, pixmap, 0, 0, pix_w, pix_h, ~0L,
90 (depth == 1 ? XYPixmap : ZPixmap));
91 XImage *i2 = XCreateImage (dpy, visual, depth,
92 (depth == 1 ? XYPixmap : ZPixmap), 0, 0,
93 pix_w*2, pix_h*2, 8, 0);
95 GC gc = XCreateGC (dpy, p2, 0, &gcv);
96 i2->data = (char *) calloc(i2->height, i2->bytes_per_line);
97 for (y = 0; y < pix_h; y++)
98 for (x = 0; x < pix_w; x++)
100 unsigned long p = XGetPixel(i1, x, y);
101 XPutPixel(i2, x*2, y*2, p);
102 XPutPixel(i2, x*2+1, y*2, p);
103 XPutPixel(i2, x*2, y*2+1, p);
104 XPutPixel(i2, x*2+1, y*2+1, p);
106 free(i1->data); i1->data = 0;
108 XPutImage(dpy, p2, gc, i2, 0, 0, 0, 0, i2->width, i2->height);
110 free(i2->data); i2->data = 0;
112 XFreePixmap(dpy, pixmap);
118 init_images (struct state *st)
121 struct { const unsigned char *png; unsigned long size; } bits[8];
122 XWindowAttributes xgwa;
123 XGetWindowAttributes (st->dpy, st->window, &xgwa);
126 images[i++] = &st->left1;
127 images[i++] = &st->left2;
128 images[i++] = &st->right1;
129 images[i++] = &st->right2;
130 images[i++] = &st->left_front;
131 images[i++] = &st->right_front;
132 images[i++] = &st->front;
133 images[i] = &st->down;
135 #define DEF(N,S) bits[i].png = N; bits[i].size = S; i++
137 DEF(nose_l1_png, sizeof(nose_l1_png));
138 DEF(nose_l2_png, sizeof(nose_l2_png));
139 DEF(nose_r1_png, sizeof(nose_r1_png));
140 DEF(nose_r2_png, sizeof(nose_r2_png));
141 DEF(nose_f2_png, sizeof(nose_f2_png));
142 DEF(nose_f3_png, sizeof(nose_f3_png));
143 DEF(nose_f1_png, sizeof(nose_f1_png));
144 DEF(nose_f4_png, sizeof(nose_f4_png));
146 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
149 Pixmap pixmap = image_data_to_pixmap (st->dpy, st->window,
150 bits[i].png, bits[i].size,
151 &st->pix_w, &st->pix_h, &mask);
154 fprintf (stderr, "%s: Can't load nose images\n", progname);
157 images[i]->p = pixmap;
161 if (xgwa.width > 2560) /* Retina display */
163 for (i = 0; i < sizeof (images) / sizeof(*images); i++)
165 images[i]->p = double_pixmap (st->dpy, xgwa.visual, xgwa.depth,
166 images[i]->p, st->pix_w, st->pix_h);
167 images[i]->m = double_pixmap (st->dpy, xgwa.visual, 1,
168 images[i]->m, st->pix_w, st->pix_h);
184 move (struct state *st)
186 if (!st->move_length)
188 register int tries = 0;
190 if ((random() & 1) && think(st))
192 talk(st, 0); /* sets timeout to itself */
195 if (!(random() % 3) && (st->interval = look(st)))
200 st->interval = 20 + random() % 100;
204 st->move_length = st->Width / 100 + random() % 90, tries = 8;
207 /* There maybe the case that we won't be able to exit from
208 this routine (especially when the geometry is too small)!!
210 Ensure that we can exit from this routine.
213 if (!tries && (st->move_length <= 1)) {
218 switch (random() % 8)
221 if (st->x - X_INCR * st->move_length >= 5)
225 if (st->x + X_INCR * st->move_length <= st->Width - 70)
226 st->move_dir = RIGHT;
229 if (st->y - (Y_INCR * st->move_length) >= 5)
230 st->move_dir = UP, st->interval = 40;
233 if (st->y + Y_INCR * st->move_length <= st->Height - 70)
234 st->move_dir = DOWN, st->interval = 20;
237 if (st->x - X_INCR * st->move_length >= 5 && st->y - (Y_INCR * st->move_length) >= 5)
238 st->move_dir = (LEFT | UP);
241 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
242 st->y - Y_INCR * st->move_length >= 5)
243 st->move_dir = (RIGHT | UP);
246 if (st->x - X_INCR * st->move_length >= 5 &&
247 st->y + Y_INCR * st->move_length <= st->Height - 70)
248 st->move_dir = (LEFT | DOWN);
251 if (st->x + X_INCR * st->move_length <= st->Width - 70 &&
252 st->y + Y_INCR * st->move_length <= st->Height - 70)
253 st->move_dir = (RIGHT | DOWN);
259 } while (!st->move_dir);
262 walk(st, st->move_dir);
267 # define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) do {\
268 int X2 = (x2), Y2 = (y2); \
269 PM *FRAME = (frame); \
270 XFillRectangle(dpy,window,st->bg_gc,X2,Y2,w,h); \
271 XSetClipMask (dpy,gc,FRAME->m); \
272 XSetClipOrigin (dpy,gc,X2,Y2); \
273 XCopyArea (dpy,FRAME->p,window,gc,x,y,w,h,X2,Y2); \
274 XSetClipMask (dpy,gc,None); \
278 walk (struct state *st, int dir)
280 register int incr = 0;
282 if (dir & (LEFT | RIGHT))
283 { /* left/right movement (mabye up/st->down too) */
284 st->walk_up = -st->walk_up; /* bouncing effect (even if hit a wall) */
288 st->walk_frame = (st->walk_up < 0) ? &st->left1 : &st->left2;
293 st->walk_frame = (st->walk_up < 0) ? &st->right1 : &st->right2;
295 if ((st->walk_lastdir == FRONT || st->walk_lastdir == DOWN) && dir & UP)
299 * workaround silly bug that leaves screen dust when guy is
300 * facing forward or st->down and moves up-left/right.
302 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc,
303 0, 0, st->pix_w, st->pix_h, st->x, st->y);
305 /* note that maybe neither UP nor DOWN is set! */
306 if (dir & UP && st->y > Y_INCR)
308 else if (dir & DOWN && st->y < st->Height - st->pix_h)
311 /* Explicit up/st->down movement only (no left/right) */
313 COPY(st->dpy, &st->front, st->window, st->fg_gc,
314 0, 0, st->pix_w, st->pix_h, st->x, st->y -= Y_INCR);
315 else if (dir == DOWN)
316 COPY(st->dpy, &st->down, st->window, st->fg_gc,
317 0, 0, st->pix_w, st->pix_h, st->x, st->y += Y_INCR);
318 else if (dir == FRONT && st->walk_frame != &st->front)
321 st->walk_up = -st->walk_up;
322 if (st->walk_lastdir & LEFT)
323 st->walk_frame = &st->left_front;
324 else if (st->walk_lastdir & RIGHT)
325 st->walk_frame = &st->right_front;
327 st->walk_frame = &st->front;
328 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc,
329 0, 0, st->pix_w, st->pix_h, st->x, st->y);
334 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc,
335 0, 0, st->pix_w, st->pix_h, --st->x, st->y + st->walk_up);
337 else if (dir & RIGHT)
340 COPY(st->dpy, st->walk_frame, st->window, st->fg_gc,
341 0, 0, st->pix_w, st->pix_h, ++st->x, st->y + st->walk_up);
343 st->walk_lastdir = dir;
347 think (struct state *st)
360 talk (struct state *st, int force_erase)
368 char args[MAXLINES][LINELEN];
370 /* clear what we've written */
371 if (st->talking || force_erase)
375 XFillRectangle(st->dpy, st->window, st->bg_gc, st->s_rect.x - 5, st->s_rect.y - 5,
376 st->s_rect.width + 10, st->s_rect.height + 10);
382 /* might as well check the st->window for size changes now... */
383 XWindowAttributes xgwa;
384 XGetWindowAttributes (st->dpy, st->window, &xgwa);
385 st->Width = xgwa.width + 2;
386 st->Height = xgwa.height + 2;
391 /* If there is actually no words, just return */
400 for (p2 = p; *p2; p2++)
401 if (*p2 == '\t') *p2 = ' ';
403 if (!(p2 = strchr(p, '\n')) || !p2[1])
407 total = strlen (st->words);
408 strncpy (args[0], st->words, LINELEN);
409 args[0][LINELEN - 1] = 0;
410 XftTextExtentsUtf8 (st->dpy, st->xftfont,
411 (FcChar8 *) st->words, total,
413 width = extents.xOff;
417 /* p2 now points to the first '\n' */
418 for (height = 0; p; height++)
424 XftTextExtentsUtf8 (st->dpy, st->xftfont,
425 (FcChar8 *) p, p2 - p,
431 total += p2 - p; /* total chars; count to determine reading
433 (void) strncpy(args[height], p, LINELEN);
434 args[height][LINELEN - 1] = 0;
435 if (height == MAXLINES - 1)
437 /* puts("Message too long!"); */
441 if (!(p2 = strchr(p, '\n')))
447 * Figure out the height and width in pixels (height, width) extend the
448 * new box by 15 pixels on the sides (30 total) top and bottom.
450 st->s_rect.width = width + 30;
451 st->s_rect.height = height * font_height(st->xftfont) + 30;
452 if (st->x - st->s_rect.width - 10 < 5)
454 else if ((st->s_rect.x = st->x + 32 - (st->s_rect.width + 15) / 2)
455 + st->s_rect.width + 15 > st->Width - 5)
456 st->s_rect.x = st->Width - 15 - st->s_rect.width;
457 if (st->y - st->s_rect.height - 10 < 5)
458 st->s_rect.y = st->y + st->pix_h + 5;
460 st->s_rect.y = st->y - 5 - st->s_rect.height;
462 XFillRectangle(st->dpy, st->window, st->text_bg_gc,
463 st->s_rect.x, st->s_rect.y, st->s_rect.width, st->s_rect.height);
465 /* make a box that's 5 pixels thick. Then add a thin box inside it */
466 XSetLineAttributes(st->dpy, st->text_fg_gc, 5, 0, 0, 0);
467 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
468 st->s_rect.x, st->s_rect.y, st->s_rect.width - 1, st->s_rect.height - 1);
469 XSetLineAttributes(st->dpy, st->text_fg_gc, 0, 0, 0, 0);
470 XDrawRectangle(st->dpy, st->window, st->text_fg_gc,
471 st->s_rect.x + 7, st->s_rect.y + 7, st->s_rect.width - 15, st->s_rect.height - 15);
474 st->Y = 15 + font_height(st->xftfont);
476 /* now print each string in reverse order (start at bottom of box) */
477 for (Z = 0; Z < height; Z++)
479 int L = strlen(args[Z]);
480 /* If there are continuous new lines, L can be 0 */
481 if (L && (args[Z][L-1] == '\r' || args[Z][L-1] == '\n'))
483 XftDrawStringUtf8 (st->xftdraw, &st->xftcolor, st->xftfont,
484 st->s_rect.x + st->X, st->s_rect.y + st->Y,
485 (FcChar8 *) args[Z], L);
487 st->Y += font_height(st->xftfont);
489 st->interval = (total / 15) * 1000;
490 if (st->interval < 2000) st->interval = 2000;
491 st->next_fn = talk_1;
497 talk_1 (struct state *st)
504 look (struct state *st)
508 COPY(st->dpy, (random() & 1) ? &st->down : &st->front, st->window, st->fg_gc,
509 0, 0, st->pix_w, st->pix_h, st->x, st->y);
516 COPY(st->dpy, (random() & 1) ? &st->left_front : &st->right_front,
517 st->window, st->fg_gc, 0, 0, st->pix_w, st->pix_h, st->x, st->y);
522 COPY(st->dpy, (random() & 1) ? &st->left1 : &st->right1, st->window, st->fg_gc,
523 0, 0, st->pix_w, st->pix_h, st->x, st->y);
529 fill_words (struct state *st)
531 char *p = st->words + strlen(st->words);
536 for (c = st->words; c < p; c++)
540 while (p < st->words + sizeof(st->words) - 1 &&
543 int c = textclient_getc (st->tc);
558 static const char *noseguy_defaults [] = {
559 ".background: black",
560 ".foreground: #CCCCCC",
561 "*textForeground: black",
562 "*textBackground: #CCCCCC",
564 "*program: xscreensaver-text",
566 ".font: -*-helvetica-medium-r-*-*-*-140-*-*-*-*-*-*",
570 static XrmOptionDescRec noseguy_options [] = {
571 { "-program", ".program", XrmoptionSepArg, 0 },
572 { "-font", ".font", XrmoptionSepArg, 0 },
573 { "-text-foreground", ".textForeground", XrmoptionSepArg, 0 },
574 { "-text-background", ".textBackground", XrmoptionSepArg, 0 },
580 noseguy_init (Display *d, Window w)
582 struct state *st = (struct state *) calloc (1, sizeof(*st));
583 unsigned long fg, bg, text_fg, text_bg;
584 XWindowAttributes xgwa;
592 fontname = get_string_resource (st->dpy, "font", "Font");
593 XGetWindowAttributes (st->dpy, st->window, &xgwa);
594 st->Width = xgwa.width + 2;
595 st->Height = xgwa.height + 2;
596 cmap = xgwa.colormap;
598 st->tc = textclient_open (st->dpy);
602 textclient_reshape (st->tc, w, h, w, h,
603 /* Passing MAXLINES isn't actually necessary */
609 st->xftfont = XftFontOpenXlfd (st->dpy, screen_number (xgwa.screen),
611 XftColorAllocName (st->dpy, xgwa.visual, xgwa.colormap,
612 get_string_resource (st->dpy,
613 "textForeground", "Foreground"),
615 st->xftdraw = XftDrawCreate (st->dpy, st->window, xgwa.visual,
619 fg = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
620 bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
621 text_fg = get_pixel_resource (st->dpy, cmap, "textForeground", "Foreground");
622 text_bg = get_pixel_resource (st->dpy, cmap, "textBackground", "Background");
623 /* notice when unspecified */
624 if (! get_string_resource (st->dpy, "textForeground", "Foreground"))
626 if (! get_string_resource (st->dpy, "textBackground", "Background"))
629 gcvalues.foreground = fg;
630 gcvalues.background = bg;
631 st->fg_gc = XCreateGC (st->dpy, st->window,
632 GCForeground|GCBackground,
634 gcvalues.foreground = bg;
635 gcvalues.background = fg;
636 st->bg_gc = XCreateGC (st->dpy, st->window,
637 GCForeground|GCBackground,
639 gcvalues.foreground = text_fg;
640 gcvalues.background = text_bg;
641 st->text_fg_gc = XCreateGC (st->dpy, st->window,
642 GCForeground|GCBackground,
644 gcvalues.foreground = text_bg;
645 gcvalues.background = text_fg;
646 st->text_bg_gc = XCreateGC (st->dpy, st->window,
647 GCForeground|GCBackground,
649 st->x = st->Width / 2;
650 st->y = st->Height / 2;
651 st->state = IS_MOVING;
658 noseguy_draw (Display *dpy, Window window, void *closure)
660 struct state *st = (struct state *) closure;
663 return (st->interval * 1000);
667 noseguy_reshape (Display *dpy, Window window, void *closure,
668 unsigned int w, unsigned int h)
670 struct state *st = (struct state *) closure;
676 noseguy_event (Display *dpy, Window window, void *closure, XEvent *event)
682 noseguy_free (Display *dpy, Window window, void *closure)
684 struct state *st = (struct state *) closure;
685 textclient_close (st->tc);
689 XSCREENSAVER_MODULE ("NoseGuy", noseguy)