From d3a98cf6cbc3bd0b9efc570f58e8812c03931c18 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 16 Oct 2018 10:08:48 +0200 Subject: Original 5.40 --- hacks/noseguy.c | 689 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 689 insertions(+) create mode 100644 hacks/noseguy.c (limited to 'hacks/noseguy.c') diff --git a/hacks/noseguy.c b/hacks/noseguy.c new file mode 100644 index 0000000..726c2a7 --- /dev/null +++ b/hacks/noseguy.c @@ -0,0 +1,689 @@ +/* xscreensaver, Copyright (c) 1992-2018 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +/* Make a little guy with a big nose and a hat wanter around the screen, + spewing out messages. Derived from xnlock by + Dan Heller . + */ + +#include "screenhack.h" +#include "ximage-loader.h" +#include "textclient.h" +#include "xft.h" + +#define font_height(font) (font->ascent + font->descent) + + +typedef struct { Pixmap p, m; } PM; + +struct state { + Display *dpy; + Window window; + int Width, Height; + GC fg_gc, bg_gc, text_fg_gc, text_bg_gc; + int x, y; + + XftFont *xftfont; + XftColor xftcolor; + XftDraw *xftdraw; + + unsigned long interval; + PM left1, left2, right1, right2, left_front, right_front, front, down; + int pix_w, pix_h; + + text_data *tc; + + int state; /* indicates states: walking or getting passwd */ + int first_time; + + void (*next_fn) (struct state *); + + int move_length, move_dir; + + int walk_lastdir; + int walk_up; + PM *walk_frame; + + int X, Y, talking; + + struct { + int x, y, width, height; + } s_rect; + + char words[10240]; + int lines; +}; + +static void fill_words (struct state *); +static void walk (struct state *, int dir); +static void talk (struct state *, int erase); +static void talk_1 (struct state *); +static int think (struct state *); +static unsigned long look (struct state *); + +#define IS_MOVING 1 + +#include "images/gen/nose-f1_png.h" +#include "images/gen/nose-f2_png.h" +#include "images/gen/nose-f3_png.h" +#include "images/gen/nose-f4_png.h" +#include "images/gen/nose-l1_png.h" +#include "images/gen/nose-l2_png.h" +#include "images/gen/nose-r1_png.h" +#include "images/gen/nose-r2_png.h" + +static Pixmap +double_pixmap (Display *dpy, Visual *visual, int depth, Pixmap pixmap, + int pix_w, int pix_h) +{ + int x, y; + Pixmap p2 = XCreatePixmap(dpy, pixmap, pix_w*2, pix_h*2, depth); + XImage *i1 = XGetImage (dpy, pixmap, 0, 0, pix_w, pix_h, ~0L, + (depth == 1 ? XYPixmap : ZPixmap)); + XImage *i2 = XCreateImage (dpy, visual, depth, + (depth == 1 ? XYPixmap : ZPixmap), 0, 0, + pix_w*2, pix_h*2, 8, 0); + XGCValues gcv; + GC gc = XCreateGC (dpy, p2, 0, &gcv); + i2->data = (char *) calloc(i2->height, i2->bytes_per_line); + for (y = 0; y < pix_h; y++) + for (x = 0; x < pix_w; x++) + { + unsigned long p = XGetPixel(i1, x, y); + XPutPixel(i2, x*2, y*2, p); + XPutPixel(i2, x*2+1, y*2, p); + XPutPixel(i2, x*2, y*2+1, p); + XPutPixel(i2, x*2+1, y*2+1, p); + } + free(i1->data); i1->data = 0; + XDestroyImage(i1); + XPutImage(dpy, p2, gc, i2, 0, 0, 0, 0, i2->width, i2->height); + XFreeGC (dpy, gc); + free(i2->data); i2->data = 0; + XDestroyImage(i2); + XFreePixmap(dpy, pixmap); + return p2; +} + + +static void +init_images (struct state *st) +{ + PM *images[8]; + struct { const unsigned char *png; unsigned long size; } bits[8]; + XWindowAttributes xgwa; + XGetWindowAttributes (st->dpy, st->window, &xgwa); + + int i = 0; + images[i++] = &st->left1; + images[i++] = &st->left2; + images[i++] = &st->right1; + images[i++] = &st->right2; + images[i++] = &st->left_front; + images[i++] = &st->right_front; + images[i++] = &st->front; + images[i] = &st->down; + +#define DEF(N,S) bits[i].png = N; bits[i].size = S; i++ + i = 0; + DEF(nose_l1_png, sizeof(nose_l1_png)); + DEF(nose_l2_png, sizeof(nose_l2_png)); + DEF(nose_r1_png, sizeof(nose_r1_png)); + DEF(nose_r2_png, sizeof(nose_r2_png)); + DEF(nose_f2_png, sizeof(nose_f2_png)); + DEF(nose_f3_png, sizeof(nose_f3_png)); + DEF(nose_f1_png, sizeof(nose_f1_png)); + DEF(nose_f4_png, sizeof(nose_f4_png)); + + for (i = 0; i < sizeof (images) / sizeof(*images); i++) + { + Pixmap mask = 0; + Pixmap pixmap = image_data_to_pixmap (st->dpy, st->window, + bits[i].png, bits[i].size, + &st->pix_w, &st->pix_h, &mask); + if (!pixmap) + { + fprintf (stderr, "%s: Can't load nose images\n", progname); + exit (1); + } + images[i]->p = pixmap; + images[i]->m = mask; + } + + if (xgwa.width > 2560) /* Retina display */ + { + for (i = 0; i < sizeof (images) / sizeof(*images); i++) + { + images[i]->p = double_pixmap (st->dpy, xgwa.visual, xgwa.depth, + images[i]->p, st->pix_w, st->pix_h); + images[i]->m = double_pixmap (st->dpy, xgwa.visual, 1, + images[i]->m, st->pix_w, st->pix_h); + } + st->pix_w *= 2; + st->pix_h *= 2; + } +} + +#define LEFT 001 +#define RIGHT 002 +#define DOWN 004 +#define UP 010 +#define FRONT 020 +#define X_INCR 3 +#define Y_INCR 2 + +static void +move (struct state *st) +{ + if (!st->move_length) + { + register int tries = 0; + st->move_dir = 0; + if ((random() & 1) && think(st)) + { + talk(st, 0); /* sets timeout to itself */ + return; + } + if (!(random() % 3) && (st->interval = look(st))) + { + st->next_fn = move; + return; + } + st->interval = 20 + random() % 100; + do + { + if (!tries) + st->move_length = st->Width / 100 + random() % 90, tries = 8; + else + tries--; + /* There maybe the case that we won't be able to exit from + this routine (especially when the geometry is too small)!! + + Ensure that we can exit from this routine. + */ +#if 1 + if (!tries && (st->move_length <= 1)) { + st->move_length = 1; + break; + } +#endif + switch (random() % 8) + { + case 0: + if (st->x - X_INCR * st->move_length >= 5) + st->move_dir = LEFT; + break; + case 1: + if (st->x + X_INCR * st->move_length <= st->Width - 70) + st->move_dir = RIGHT; + break; + case 2: + if (st->y - (Y_INCR * st->move_length) >= 5) + st->move_dir = UP, st->interval = 40; + break; + case 3: + if (st->y + Y_INCR * st->move_length <= st->Height - 70) + st->move_dir = DOWN, st->interval = 20; + break; + case 4: + if (st->x - X_INCR * st->move_length >= 5 && st->y - (Y_INCR * st->move_length) >= 5) + st->move_dir = (LEFT | UP); + break; + case 5: + if (st->x + X_INCR * st->move_length <= st->Width - 70 && + st->y - Y_INCR * st->move_length >= 5) + st->move_dir = (RIGHT | UP); + break; + case 6: + if (st->x - X_INCR * st->move_length >= 5 && + st->y + Y_INCR * st->move_length <= st->Height - 70) + st->move_dir = (LEFT | DOWN); + break; + case 7: + if (st->x + X_INCR * st->move_length <= st->Width - 70 && + st->y + Y_INCR * st->move_length <= st->Height - 70) + st->move_dir = (RIGHT | DOWN); + break; + default: + /* No Defaults */ + break; + } + } while (!st->move_dir); + } + if (st->move_dir) + walk(st, st->move_dir); + --st->move_length; + st->next_fn = move; +} + +# define COPY(dpy,frame,window,gc,x,y,w,h,x2,y2) do {\ + int X2 = (x2), Y2 = (y2); \ + PM *FRAME = (frame); \ + XFillRectangle(dpy,window,st->bg_gc,X2,Y2,w,h); \ + XSetClipMask (dpy,gc,FRAME->m); \ + XSetClipOrigin (dpy,gc,X2,Y2); \ + XCopyArea (dpy,FRAME->p,window,gc,x,y,w,h,X2,Y2); \ + XSetClipMask (dpy,gc,None); \ + } while(0) + +static void +walk (struct state *st, int dir) +{ + register int incr = 0; + + if (dir & (LEFT | RIGHT)) + { /* left/right movement (mabye up/st->down too) */ + st->walk_up = -st->walk_up; /* bouncing effect (even if hit a wall) */ + if (dir & LEFT) + { + incr = X_INCR; + st->walk_frame = (st->walk_up < 0) ? &st->left1 : &st->left2; + } + else + { + incr = -X_INCR; + st->walk_frame = (st->walk_up < 0) ? &st->right1 : &st->right2; + } + if ((st->walk_lastdir == FRONT || st->walk_lastdir == DOWN) && dir & UP) + { + + /* + * workaround silly bug that leaves screen dust when guy is + * facing forward or st->down and moves up-left/right. + */ + COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, + 0, 0, st->pix_w, st->pix_h, st->x, st->y); + } + /* note that maybe neither UP nor DOWN is set! */ + if (dir & UP && st->y > Y_INCR) + st->y -= Y_INCR; + else if (dir & DOWN && st->y < st->Height - st->pix_h) + st->y += Y_INCR; + } + /* Explicit up/st->down movement only (no left/right) */ + else if (dir == UP) + COPY(st->dpy, &st->front, st->window, st->fg_gc, + 0, 0, st->pix_w, st->pix_h, st->x, st->y -= Y_INCR); + else if (dir == DOWN) + COPY(st->dpy, &st->down, st->window, st->fg_gc, + 0, 0, st->pix_w, st->pix_h, st->x, st->y += Y_INCR); + else if (dir == FRONT && st->walk_frame != &st->front) + { + if (st->walk_up > 0) + st->walk_up = -st->walk_up; + if (st->walk_lastdir & LEFT) + st->walk_frame = &st->left_front; + else if (st->walk_lastdir & RIGHT) + st->walk_frame = &st->right_front; + else + st->walk_frame = &st->front; + COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, + 0, 0, st->pix_w, st->pix_h, st->x, st->y); + } + if (dir & LEFT) + while (--incr >= 0) + { + COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, + 0, 0, st->pix_w, st->pix_h, --st->x, st->y + st->walk_up); + } + else if (dir & RIGHT) + while (++incr <= 0) + { + COPY(st->dpy, st->walk_frame, st->window, st->fg_gc, + 0, 0, st->pix_w, st->pix_h, ++st->x, st->y + st->walk_up); + } + st->walk_lastdir = dir; +} + +static int +think (struct state *st) +{ + if (random() & 1) + walk(st, FRONT); + if (random() & 1) + return 1; + return 0; +} + +#define MAXLINES 10 +#define LINELEN 256 + +static void +talk (struct state *st, int force_erase) +{ + int width = 0, + height, + Z, + total = 0; + register char *p, + *p2; + char args[MAXLINES][LINELEN]; + + /* clear what we've written */ + if (st->talking || force_erase) + { + if (!st->talking) + return; + XFillRectangle(st->dpy, st->window, st->bg_gc, st->s_rect.x - 5, st->s_rect.y - 5, + st->s_rect.width + 10, st->s_rect.height + 10); + st->talking = 0; + if (!force_erase) + st->next_fn = move; + st->interval = 0; + { + /* might as well check the st->window for size changes now... */ + XWindowAttributes xgwa; + XGetWindowAttributes (st->dpy, st->window, &xgwa); + st->Width = xgwa.width + 2; + st->Height = xgwa.height + 2; + } + return; + } + p = st->words; + /* If there is actually no words, just return */ + if (!*p) + { + st->talking = 0; + return; + } + st->talking = 1; + walk(st, FRONT); + + for (p2 = p; *p2; p2++) + if (*p2 == '\t') *p2 = ' '; + + if (!(p2 = strchr(p, '\n')) || !p2[1]) + { + XGlyphInfo extents; + + total = strlen (st->words); + strncpy (args[0], st->words, LINELEN); + args[0][LINELEN - 1] = 0; + XftTextExtentsUtf8 (st->dpy, st->xftfont, + (FcChar8 *) st->words, total, + &extents); + width = extents.xOff; + height = 0; + } + else + /* p2 now points to the first '\n' */ + for (height = 0; p; height++) + { + int w; + XGlyphInfo extents; + *p2 = 0; + + XftTextExtentsUtf8 (st->dpy, st->xftfont, + (FcChar8 *) p, p2 - p, + &extents); + w = extents.xOff; + if (w > width) + width = w; + + total += p2 - p; /* total chars; count to determine reading + * time */ + (void) strncpy(args[height], p, LINELEN); + args[height][LINELEN - 1] = 0; + if (height == MAXLINES - 1) + { + /* puts("Message too long!"); */ + break; + } + p = p2 + 1; + if (!(p2 = strchr(p, '\n'))) + break; + } + height++; + + /* + * Figure out the height and width in pixels (height, width) extend the + * new box by 15 pixels on the sides (30 total) top and bottom. + */ + st->s_rect.width = width + 30; + st->s_rect.height = height * font_height(st->xftfont) + 30; + if (st->x - st->s_rect.width - 10 < 5) + st->s_rect.x = 5; + else if ((st->s_rect.x = st->x + 32 - (st->s_rect.width + 15) / 2) + + st->s_rect.width + 15 > st->Width - 5) + st->s_rect.x = st->Width - 15 - st->s_rect.width; + if (st->y - st->s_rect.height - 10 < 5) + st->s_rect.y = st->y + st->pix_h + 5; + else + st->s_rect.y = st->y - 5 - st->s_rect.height; + + XFillRectangle(st->dpy, st->window, st->text_bg_gc, + st->s_rect.x, st->s_rect.y, st->s_rect.width, st->s_rect.height); + + /* make a box that's 5 pixels thick. Then add a thin box inside it */ + XSetLineAttributes(st->dpy, st->text_fg_gc, 5, 0, 0, 0); + XDrawRectangle(st->dpy, st->window, st->text_fg_gc, + st->s_rect.x, st->s_rect.y, st->s_rect.width - 1, st->s_rect.height - 1); + XSetLineAttributes(st->dpy, st->text_fg_gc, 0, 0, 0, 0); + XDrawRectangle(st->dpy, st->window, st->text_fg_gc, + st->s_rect.x + 7, st->s_rect.y + 7, st->s_rect.width - 15, st->s_rect.height - 15); + + st->X = 15; + st->Y = 15 + font_height(st->xftfont); + + /* now print each string in reverse order (start at bottom of box) */ + for (Z = 0; Z < height; Z++) + { + int L = strlen(args[Z]); + /* If there are continuous new lines, L can be 0 */ + if (L && (args[Z][L-1] == '\r' || args[Z][L-1] == '\n')) + args[Z][--L] = 0; + XftDrawStringUtf8 (st->xftdraw, &st->xftcolor, st->xftfont, + st->s_rect.x + st->X, st->s_rect.y + st->Y, + (FcChar8 *) args[Z], L); + + st->Y += font_height(st->xftfont); + } + st->interval = (total / 15) * 1000; + if (st->interval < 2000) st->interval = 2000; + st->next_fn = talk_1; + *st->words = 0; + st->lines = 0; +} + +static void +talk_1 (struct state *st) +{ + talk(st, 0); +} + + +static unsigned long +look (struct state *st) +{ + if (random() % 3) + { + COPY(st->dpy, (random() & 1) ? &st->down : &st->front, st->window, st->fg_gc, + 0, 0, st->pix_w, st->pix_h, st->x, st->y); + return 1000L; + } + if (!(random() % 5)) + return 0; + if (random() % 3) + { + COPY(st->dpy, (random() & 1) ? &st->left_front : &st->right_front, + st->window, st->fg_gc, 0, 0, st->pix_w, st->pix_h, st->x, st->y); + return 1000L; + } + if (!(random() % 5)) + return 0; + COPY(st->dpy, (random() & 1) ? &st->left1 : &st->right1, st->window, st->fg_gc, + 0, 0, st->pix_w, st->pix_h, st->x, st->y); + return 1000L; +} + + +static void +fill_words (struct state *st) +{ + char *p = st->words + strlen(st->words); + char *c; + int lines = 0; + int max = MAXLINES; + + for (c = st->words; c < p; c++) + if (*c == '\n') + lines++; + + while (p < st->words + sizeof(st->words) - 1 && + lines < max) + { + int c = textclient_getc (st->tc); + if (c == '\n') + lines++; + if (c > 0) + *p++ = (char) c; + else + break; + } + *p = 0; + + st->lines = lines; +} + + + +static const char *noseguy_defaults [] = { + ".background: black", + ".foreground: #CCCCCC", + "*textForeground: black", + "*textBackground: #CCCCCC", + "*fpsSolid: true", + "*program: xscreensaver-text", + "*usePty: False", + ".font: -*-helvetica-medium-r-*-*-*-140-*-*-*-*-*-*", + 0 +}; + +static XrmOptionDescRec noseguy_options [] = { + { "-program", ".program", XrmoptionSepArg, 0 }, + { "-font", ".font", XrmoptionSepArg, 0 }, + { "-text-foreground", ".textForeground", XrmoptionSepArg, 0 }, + { "-text-background", ".textBackground", XrmoptionSepArg, 0 }, + { 0, 0, 0, 0 } +}; + + +static void * +noseguy_init (Display *d, Window w) +{ + struct state *st = (struct state *) calloc (1, sizeof(*st)); + unsigned long fg, bg, text_fg, text_bg; + XWindowAttributes xgwa; + Colormap cmap; + char *fontname; + XGCValues gcvalues; + st->dpy = d; + st->window = w; + st->first_time = 1; + + fontname = get_string_resource (st->dpy, "font", "Font"); + XGetWindowAttributes (st->dpy, st->window, &xgwa); + st->Width = xgwa.width + 2; + st->Height = xgwa.height + 2; + cmap = xgwa.colormap; + + st->tc = textclient_open (st->dpy); + { + int w = 40; + int h = 15; + textclient_reshape (st->tc, w, h, w, h, + /* Passing MAXLINES isn't actually necessary */ + 0); + } + + init_images(st); + + st->xftfont = XftFontOpenXlfd (st->dpy, screen_number (xgwa.screen), + fontname); + XftColorAllocName (st->dpy, xgwa.visual, xgwa.colormap, + get_string_resource (st->dpy, + "textForeground", "Foreground"), + &st->xftcolor); + st->xftdraw = XftDrawCreate (st->dpy, st->window, xgwa.visual, + xgwa.colormap); + + + fg = get_pixel_resource (st->dpy, cmap, "foreground", "Foreground"); + bg = get_pixel_resource (st->dpy, cmap, "background", "Background"); + text_fg = get_pixel_resource (st->dpy, cmap, "textForeground", "Foreground"); + text_bg = get_pixel_resource (st->dpy, cmap, "textBackground", "Background"); + /* notice when unspecified */ + if (! get_string_resource (st->dpy, "textForeground", "Foreground")) + text_fg = bg; + if (! get_string_resource (st->dpy, "textBackground", "Background")) + text_bg = fg; + + gcvalues.foreground = fg; + gcvalues.background = bg; + st->fg_gc = XCreateGC (st->dpy, st->window, + GCForeground|GCBackground, + &gcvalues); + gcvalues.foreground = bg; + gcvalues.background = fg; + st->bg_gc = XCreateGC (st->dpy, st->window, + GCForeground|GCBackground, + &gcvalues); + gcvalues.foreground = text_fg; + gcvalues.background = text_bg; + st->text_fg_gc = XCreateGC (st->dpy, st->window, + GCForeground|GCBackground, + &gcvalues); + gcvalues.foreground = text_bg; + gcvalues.background = text_fg; + st->text_bg_gc = XCreateGC (st->dpy, st->window, + GCForeground|GCBackground, + &gcvalues); + st->x = st->Width / 2; + st->y = st->Height / 2; + st->state = IS_MOVING; + st->next_fn = move; + st->walk_up = 1; + return st; +} + +static unsigned long +noseguy_draw (Display *dpy, Window window, void *closure) +{ + struct state *st = (struct state *) closure; + fill_words(st); + st->next_fn(st); + return (st->interval * 1000); +} + +static void +noseguy_reshape (Display *dpy, Window window, void *closure, + unsigned int w, unsigned int h) +{ + struct state *st = (struct state *) closure; + st->Width = w + 2; + st->Height = h + 2; +} + +static Bool +noseguy_event (Display *dpy, Window window, void *closure, XEvent *event) +{ + return False; +} + +static void +noseguy_free (Display *dpy, Window window, void *closure) +{ + struct state *st = (struct state *) closure; + textclient_close (st->tc); + free (st); +} + +XSCREENSAVER_MODULE ("NoseGuy", noseguy) -- cgit v1.2.3-55-g7522