/* xscreensaver, Copyright (c) 2008-2015 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. * * Draws repetitive patterns that should undo burned in LCD screens. * Concept shamelessly cloned from * http://toastycode.com/blog/2008/02/05/lcd-scrub/ */ #include "screenhack.h" #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) struct state { Display *dpy; Window window; XWindowAttributes xgwa; enum { HORIZ_W, HORIZ_B, VERT_W, VERT_B, DIAG_W, DIAG_B, WHITE, BLACK, RGB, RANDOM, END } mode; unsigned int enabled_mask; int count; GC fg, bg, bg2; int color_tick; int delay; int spread; int cycles; XImage *collisions; long ncollisions; }; static void pick_mode (struct state *st) { st->count = 0; while (1) { if (++st->mode == END) st->mode = 0; if (st->enabled_mask & (1 << st->mode)) break; } } static void * lcdscrub_init (Display *dpy, Window window) { struct state *st = (struct state *) calloc (1, sizeof(*st)); XGCValues gcv; unsigned long fgp, bgp; st->dpy = dpy; st->window = window; st->delay = get_integer_resource (st->dpy, "delay", "Integer"); st->spread = get_integer_resource (st->dpy, "spread", "Integer"); st->cycles = get_integer_resource (st->dpy, "cycles", "Integer"); XGetWindowAttributes (st->dpy, st->window, &st->xgwa); fgp = get_pixel_resource (st->dpy, st->xgwa.colormap, "foreground", "Foreground"); bgp = get_pixel_resource (st->dpy, st->xgwa.colormap, "background", "Background"); gcv.foreground = bgp; gcv.background = fgp; st->bg = XCreateGC (st->dpy, st->window, GCForeground, &gcv); st->bg2 = XCreateGC (st->dpy, st->window, GCForeground, &gcv); gcv.foreground = fgp; gcv.background = bgp; st->fg = XCreateGC (st->dpy, st->window, GCForeground, &gcv); #ifdef HAVE_JWXYZ jwxyz_XSetAntiAliasing (st->dpy, st->fg, False); jwxyz_XSetAntiAliasing (st->dpy, st->bg, False); jwxyz_XSetAntiAliasing (st->dpy, st->bg2, False); #endif st->enabled_mask = 0; # define PREF(R,F) \ if (get_boolean_resource (st->dpy, R, "Mode")) st->enabled_mask |= (1 << F) PREF("modeHW", HORIZ_W); PREF("modeHB", HORIZ_B); PREF("modeVW", VERT_W); PREF("modeVB", VERT_B); PREF("modeDW", DIAG_W); PREF("modeDB", DIAG_B); PREF("modeW", WHITE); PREF("modeB", BLACK); PREF("modeRGB", RGB); PREF("modeRandom", RANDOM); # undef PREF if (! st->enabled_mask) { fprintf (stderr, "%s: no modes enabled\n", progname); exit (1); } pick_mode (st); return st; } /* A test harness for visualizing different random number generators. This doesn't really belong in lcdscrub, but it was a convenient place to put it. */ #if 0 /* mwc1616 */ static unsigned long mwc1616_x = 1; static unsigned long mwc1616_y = 2; static void mwc1616_srand (unsigned long seed) { mwc1616_x = seed | 1; mwc1616_y = seed | 2; } static unsigned long mwc1616 (void) { mwc1616_x = 18000 * (mwc1616_x & 0xFFFF) + (mwc1616_x >> 16); mwc1616_y = 30903 * (mwc1616_y & 0xFFFF) + (mwc1616_y >> 16); return (mwc1616_x << 16) + (mwc1616_y & 0xFFFF); } # undef random # undef srand # define srand mwc1616_srand # define random() ((unsigned int) (mwc1616() & (unsigned int) (~0))) #elif 0 /* xorshift128plus */ static uint64_t xo_state0 = 1; static uint64_t xo_state1 = 2; static void xorshift128plus_srand (unsigned long seed) { xo_state0 = seed | 1; xo_state1 = seed | 2; } static uint64_t xorshift128plus (void) { register uint64_t s1 = xo_state0; register uint64_t s0 = xo_state1; xo_state0 = s0; s1 ^= s1 << 23; s1 ^= s1 >> 17; s1 ^= s0; s1 ^= s0 >> 26; xo_state1 = s1; return s1; } # undef random # undef srand # define srand xorshift128plus_srand # define random() ((unsigned int) (xorshift128plus() & (unsigned int) (~0))) #else /* ya_random */ # undef srand # define srand(n) #endif /* ya_random */ /* If you see patterns in this image, the PRNG sucks. */ static void lcdscrub_random (struct state *st) { unsigned long steps_per_frame = 3000000; unsigned long segments = 0x8000; /* 2^15 */ if (! st->collisions) { struct timeval tp; # if GETTIMEOFDAY_TWO_ARGS gettimeofday (&tp, 0); # else gettimeofday (&tp); # endif srand ((unsigned int) (tp.tv_sec ^ tp.tv_usec)); st->collisions = XCreateImage (st->dpy, st->xgwa.visual, 1, XYPixmap, 0, NULL, segments, segments, 8, 0); if (! st->collisions) abort(); st->collisions->data = (char *) calloc (segments, st->collisions->bytes_per_line); /* 128 MB */ if (! st->collisions->data) abort(); } while (--steps_per_frame) { unsigned long x = random() & (segments-1); unsigned long y = random() & (segments-1); unsigned long p = XGetPixel (st->collisions, x, y) ? 0 : 1; XPutPixel (st->collisions, x, y, p); st->ncollisions += (p ? 1 : -1); } { int w, h; Pixmap p; GC gc; XGetWindowAttributes (st->dpy, st->window, &st->xgwa); w = st->xgwa.width; h = st->xgwa.height; p = XCreatePixmap (st->dpy, st->window, w, h, 1); gc = XCreateGC (st->dpy, p, 0, 0); XSetBackground (st->dpy, gc, 0); XSetForeground (st->dpy, gc, 1); XPutImage (st->dpy, p, gc, st->collisions, 0, 0, 0, 0, w, h); XFreeGC (st->dpy, gc); gc = st->fg; XClearWindow (st->dpy, st->window); XSetClipMask (st->dpy, gc, p); XFillRectangle (st->dpy, st->window, gc, 0, 0, w, h); XFreePixmap (st->dpy, p); } /* fprintf(stderr, "%.2f\n", st->ncollisions / (float) (segments*segments)); */ } static unsigned long lcdscrub_draw (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; int count = st->count % st->spread; int i; GC fg = (st->mode & 1 ? st->fg : st->bg); GC bg = (st->mode & 1 ? st->bg : st->fg); switch (st->mode) { case HORIZ_W: case HORIZ_B: XFillRectangle (st->dpy, st->window, bg, 0, 0, st->xgwa.width, st->xgwa.height); for (i = count; i < st->xgwa.height; i += st->spread) XDrawLine (st->dpy, st->window, fg, 0, i, st->xgwa.width, i); break; case VERT_W: case VERT_B: XFillRectangle (st->dpy, st->window, bg, 0, 0, st->xgwa.width, st->xgwa.height); for (i = count; i < st->xgwa.width; i += st->spread) XDrawLine (st->dpy, st->window, fg, i, 0, i, st->xgwa.height); break; case DIAG_W: case DIAG_B: XFillRectangle (st->dpy, st->window, bg, 0, 0, st->xgwa.width, st->xgwa.height); for (i = count; i < st->xgwa.width; i += st->spread) XDrawLine (st->dpy, st->window, fg, i, 0, i + st->xgwa.width, st->xgwa.width); for (i = -count; i < st->xgwa.height; i += st->spread) XDrawLine (st->dpy, st->window, fg, 0, i, st->xgwa.height, i + st->xgwa.height); break; case RGB: { int scale = 10 * 8; /* 8 sec */ static const unsigned short colors[][3] = { { 0xFFFF, 0x0000, 0x0000 }, { 0x0000, 0xFFFF, 0x0000 }, { 0x0000, 0x0000, 0xFFFF }, { 0xFFFF, 0xFFFF, 0x0000 }, { 0xFFFF, 0x0000, 0xFFFF }, { 0x0000, 0xFFFF, 0xFFFF }, { 0xFFFF, 0xFFFF, 0xFFFF }, { 0x0000, 0x0000, 0x0000 }, }; static unsigned long last = 0; XColor xc; bg = st->bg2; xc.red = colors[st->color_tick / scale][0]; xc.green = colors[st->color_tick / scale][1]; xc.blue = colors[st->color_tick / scale][2]; if (last) XFreeColors (st->dpy, st->xgwa.colormap, &last, 1, 0); XAllocColor (st->dpy, st->xgwa.colormap, &xc); last = xc.pixel; XSetForeground (st->dpy, bg, xc.pixel); st->color_tick = (st->color_tick + 1) % (countof(colors) * scale); /* fall through */ } case WHITE: case BLACK: XFillRectangle (st->dpy, st->window, bg, 0, 0, st->xgwa.width, st->xgwa.height); break; case RANDOM: lcdscrub_random (st); break; default: abort(); break; } st->count++; if (st->count > st->spread * st->cycles) pick_mode (st); return st->delay; } static void lcdscrub_reshape (Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { } static Bool lcdscrub_event (Display *dpy, Window window, void *closure, XEvent *event) { return False; } static void lcdscrub_free (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; XFreeGC (dpy, st->fg); XFreeGC (dpy, st->bg); XFreeGC (dpy, st->bg2); if (st->collisions) { free (st->collisions->data); st->collisions->data = 0; XDestroyImage (st->collisions); } free (st); } static const char *lcdscrub_defaults [] = { ".background: black", ".foreground: white", "*fpsSolid: True", "*delay: 100000", "*spread: 8", "*cycles: 60", "*modeHW: True", "*modeHB: True", "*modeVW: True", "*modeVB: True", "*modeDW: True", "*modeDB: True", "*modeW: True", "*modeB: True", "*modeRGB: True", "*modeRandom: False", 0 }; static XrmOptionDescRec lcdscrub_options [] = { { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-spread", ".spread", XrmoptionSepArg, 0 }, { "-cycles", ".cycles", XrmoptionSepArg, 0 }, { "-no-hw", ".modeHW", XrmoptionNoArg, "False" }, { "-no-hb", ".modeHB", XrmoptionNoArg, "False" }, { "-no-vw", ".modeVW", XrmoptionNoArg, "False" }, { "-no-vb", ".modeVB", XrmoptionNoArg, "False" }, { "-no-dw", ".modeDW", XrmoptionNoArg, "False" }, { "-no-db", ".modeDB", XrmoptionNoArg, "False" }, { "-no-w", ".modeW", XrmoptionNoArg, "False" }, { "-no-b", ".modeB", XrmoptionNoArg, "False" }, { "-no-rgb", ".modeRGB", XrmoptionNoArg, "False" }, { "-random", ".modeRandom", XrmoptionNoArg, "True" }, { 0, 0, 0, 0 } }; XSCREENSAVER_MODULE ("LCDscrub", lcdscrub)