From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / lcdscrub.c
1 /* xscreensaver, Copyright (c) 2008-2015 Jamie Zawinski <jwz@jwz.org>
2  *
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 
9  * implied warranty.
10  *
11  * Draws repetitive patterns that should undo burned in LCD screens.
12  * Concept shamelessly cloned from
13  * http://toastycode.com/blog/2008/02/05/lcd-scrub/
14  */
15
16 #include "screenhack.h"
17
18 #undef countof
19 #define countof(x) (sizeof((x))/sizeof((*x)))
20
21 struct state {
22   Display *dpy;
23   Window window;
24   XWindowAttributes xgwa;
25   enum { HORIZ_W, HORIZ_B, 
26          VERT_W, VERT_B, 
27          DIAG_W, DIAG_B, 
28          WHITE, BLACK,
29          RGB,
30          RANDOM,
31          END } mode;
32   unsigned int enabled_mask;
33   int count;
34   GC fg, bg, bg2;
35   int color_tick;
36   int delay;
37   int spread;
38   int cycles;
39   XImage *collisions;
40   long ncollisions;
41 };
42
43
44 static void
45 pick_mode (struct state *st)
46 {
47   st->count = 0;
48   while (1)
49     {
50       if (++st->mode == END)
51         st->mode = 0;
52       if (st->enabled_mask & (1 << st->mode))
53         break;
54     }
55 }
56
57 static void *
58 lcdscrub_init (Display *dpy, Window window)
59 {
60   struct state *st = (struct state *) calloc (1, sizeof(*st));
61   XGCValues gcv;
62   unsigned long fgp, bgp;
63
64   st->dpy = dpy;
65   st->window = window;
66   st->delay  = get_integer_resource (st->dpy, "delay",  "Integer");
67   st->spread = get_integer_resource (st->dpy, "spread", "Integer");
68   st->cycles = get_integer_resource (st->dpy, "cycles", "Integer");
69
70   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
71   fgp = get_pixel_resource (st->dpy, st->xgwa.colormap, 
72                             "foreground", "Foreground");
73   bgp = get_pixel_resource (st->dpy, st->xgwa.colormap,
74                             "background", "Background");
75
76   gcv.foreground = bgp;
77   gcv.background = fgp;
78   st->bg  = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
79   st->bg2 = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
80   gcv.foreground = fgp;
81   gcv.background = bgp;
82   st->fg = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
83
84 #ifdef HAVE_JWXYZ
85   jwxyz_XSetAntiAliasing (st->dpy, st->fg,  False);
86   jwxyz_XSetAntiAliasing (st->dpy, st->bg,  False);
87   jwxyz_XSetAntiAliasing (st->dpy, st->bg2, False);
88 #endif
89
90   st->enabled_mask = 0;
91 # define PREF(R,F) \
92    if (get_boolean_resource (st->dpy, R, "Mode")) st->enabled_mask |= (1 << F)
93   PREF("modeHW", HORIZ_W);
94   PREF("modeHB", HORIZ_B);
95   PREF("modeVW", VERT_W);
96   PREF("modeVB", VERT_B);
97   PREF("modeDW", DIAG_W);
98   PREF("modeDB", DIAG_B);
99   PREF("modeW",  WHITE);
100   PREF("modeB",  BLACK);
101   PREF("modeRGB", RGB);
102   PREF("modeRandom", RANDOM);
103 # undef PREF
104   if (! st->enabled_mask) 
105     {
106       fprintf (stderr, "%s: no modes enabled\n", progname);
107       exit (1);
108     }
109
110   pick_mode (st);
111
112   return st;
113 }
114
115
116 /* A test harness for visualizing different random number generators.
117    This doesn't really belong in lcdscrub, but it was a convenient
118    place to put it.
119  */
120 #if 0                                                           /* mwc1616 */
121
122 static unsigned long mwc1616_x = 1;
123 static unsigned long mwc1616_y = 2;
124
125 static void
126 mwc1616_srand (unsigned long seed)
127 {
128   mwc1616_x = seed | 1;
129   mwc1616_y = seed | 2;
130 }
131
132 static unsigned long
133 mwc1616 (void)
134 {
135   mwc1616_x = 18000 * (mwc1616_x & 0xFFFF) + (mwc1616_x >> 16);
136   mwc1616_y = 30903 * (mwc1616_y & 0xFFFF) + (mwc1616_y >> 16);
137   return (mwc1616_x << 16) + (mwc1616_y & 0xFFFF);
138 }
139
140 # undef random
141 # undef srand
142 # define srand mwc1616_srand
143 # define random() ((unsigned int) (mwc1616() & (unsigned int) (~0)))
144
145
146 #elif 0                                         /* xorshift128plus */
147
148
149 static uint64_t xo_state0 = 1;
150 static uint64_t xo_state1 = 2;
151
152 static void
153 xorshift128plus_srand (unsigned long seed)
154 {
155   xo_state0 = seed | 1;
156   xo_state1 = seed | 2;
157 }
158
159 static uint64_t
160 xorshift128plus (void)
161 {
162   register uint64_t s1 = xo_state0;
163   register uint64_t s0 = xo_state1;
164   xo_state0 = s0;
165   s1 ^= s1 << 23;
166   s1 ^= s1 >> 17;
167   s1 ^= s0;
168   s1 ^= s0 >> 26;
169   xo_state1 = s1;
170   return s1;
171 }
172
173 # undef random
174 # undef srand
175 # define srand xorshift128plus_srand
176 # define random() ((unsigned int) (xorshift128plus() & (unsigned int) (~0)))
177
178
179 #else                                                           /* ya_random */
180 # undef srand
181 # define srand(n)
182
183 #endif                                                          /* ya_random */
184
185
186
187 /* If you see patterns in this image, the PRNG sucks.
188  */
189 static void
190 lcdscrub_random (struct state *st)
191 {
192   unsigned long steps_per_frame = 3000000;
193   unsigned long segments = 0x8000;  /* 2^15 */
194
195   if (! st->collisions)
196     {
197       struct timeval tp;
198 # if GETTIMEOFDAY_TWO_ARGS
199       gettimeofday (&tp, 0);
200 # else
201       gettimeofday (&tp);
202 # endif
203       srand ((unsigned int) (tp.tv_sec ^ tp.tv_usec));
204
205       st->collisions = 
206         XCreateImage (st->dpy, st->xgwa.visual, 1, XYPixmap,
207                       0, NULL, segments, segments, 8, 0);
208       if (! st->collisions) abort();
209       st->collisions->data = (char *)
210         calloc (segments, st->collisions->bytes_per_line);  /* 128 MB */
211       if (! st->collisions->data) abort();
212     }
213
214   while (--steps_per_frame)
215     {
216       unsigned long x = random() & (segments-1);
217       unsigned long y = random() & (segments-1);
218       unsigned long p = XGetPixel (st->collisions, x, y) ? 0 : 1;
219       XPutPixel (st->collisions, x, y, p);
220       st->ncollisions += (p ? 1 : -1);
221     }
222
223   {
224     int w, h;
225     Pixmap p;
226     GC gc;
227
228     XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
229     w = st->xgwa.width;
230     h = st->xgwa.height;
231
232     p = XCreatePixmap (st->dpy, st->window, w, h, 1);
233     gc = XCreateGC (st->dpy, p, 0, 0);
234     XSetBackground (st->dpy, gc, 0);
235     XSetForeground (st->dpy, gc, 1);
236     XPutImage (st->dpy, p, gc, st->collisions, 0, 0, 0, 0, w, h);
237     XFreeGC (st->dpy, gc);
238
239     gc = st->fg;
240     XClearWindow (st->dpy, st->window);
241     XSetClipMask (st->dpy, gc, p);
242     XFillRectangle (st->dpy, st->window, gc, 0, 0, w, h);
243     XFreePixmap (st->dpy, p);
244   }
245
246   /*
247     fprintf(stderr, "%.2f\n", st->ncollisions / (float) (segments*segments));
248   */
249 }
250
251
252 static unsigned long
253 lcdscrub_draw (Display *dpy, Window window, void *closure)
254 {
255   struct state *st = (struct state *) closure;
256   int count = st->count % st->spread;
257   int i;
258   GC fg = (st->mode & 1 ? st->fg : st->bg);
259   GC bg = (st->mode & 1 ? st->bg : st->fg);
260
261   switch (st->mode) {
262   case HORIZ_W:
263   case HORIZ_B:
264     XFillRectangle (st->dpy, st->window, bg, 0, 0,
265                     st->xgwa.width, st->xgwa.height);
266     for (i = count; i < st->xgwa.height; i += st->spread)
267       XDrawLine (st->dpy, st->window, fg, 0, i, st->xgwa.width, i);
268     break;
269   case VERT_W:
270   case VERT_B:
271     XFillRectangle (st->dpy, st->window, bg, 0, 0,
272                     st->xgwa.width, st->xgwa.height);
273     for (i = count; i < st->xgwa.width; i += st->spread)
274       XDrawLine (st->dpy, st->window, fg, i, 0, i, st->xgwa.height);
275     break;
276   case DIAG_W:
277   case DIAG_B:
278     XFillRectangle (st->dpy, st->window, bg, 0, 0,
279                     st->xgwa.width, st->xgwa.height);
280     for (i = count; i < st->xgwa.width; i += st->spread)
281       XDrawLine (st->dpy, st->window, fg, i, 0, 
282                  i + st->xgwa.width, st->xgwa.width);
283     for (i = -count; i < st->xgwa.height; i += st->spread)
284       XDrawLine (st->dpy, st->window, fg, 0, i,
285                  st->xgwa.height, i + st->xgwa.height);
286     break;
287   case RGB:
288     {
289       int scale = 10 * 8; /* 8 sec */
290       static const unsigned short colors[][3] = {
291         { 0xFFFF, 0x0000, 0x0000 },
292         { 0x0000, 0xFFFF, 0x0000 },
293         { 0x0000, 0x0000, 0xFFFF },
294         { 0xFFFF, 0xFFFF, 0x0000 },
295         { 0xFFFF, 0x0000, 0xFFFF },
296         { 0x0000, 0xFFFF, 0xFFFF },
297         { 0xFFFF, 0xFFFF, 0xFFFF },
298         { 0x0000, 0x0000, 0x0000 },
299       };
300       static unsigned long last = 0;
301       XColor xc;
302       bg = st->bg2;
303       xc.red   = colors[st->color_tick / scale][0];
304       xc.green = colors[st->color_tick / scale][1];
305       xc.blue  = colors[st->color_tick / scale][2];
306       if (last) XFreeColors (st->dpy, st->xgwa.colormap, &last, 1, 0);
307       XAllocColor (st->dpy, st->xgwa.colormap, &xc);
308       last = xc.pixel;
309       XSetForeground (st->dpy, bg, xc.pixel);
310       st->color_tick = (st->color_tick + 1) % (countof(colors) * scale);
311       /* fall through */
312     }
313   case WHITE:
314   case BLACK:
315     XFillRectangle (st->dpy, st->window, bg, 0, 0,
316                     st->xgwa.width, st->xgwa.height);
317     break;
318   case RANDOM:
319     lcdscrub_random (st);
320     break;
321   default: 
322     abort(); 
323     break;
324   }
325
326   st->count++;
327
328   if (st->count > st->spread * st->cycles)
329     pick_mode (st);
330
331   return st->delay;
332 }
333
334 static void
335 lcdscrub_reshape (Display *dpy, Window window, void *closure, 
336                  unsigned int w, unsigned int h)
337 {
338 }
339
340 static Bool
341 lcdscrub_event (Display *dpy, Window window, void *closure, XEvent *event)
342 {
343   return False;
344 }
345
346 static void
347 lcdscrub_free (Display *dpy, Window window, void *closure)
348 {
349   struct state *st = (struct state *) closure;
350   XFreeGC (dpy, st->fg);
351   XFreeGC (dpy, st->bg);
352   XFreeGC (dpy, st->bg2);
353   if (st->collisions)
354     {
355       free (st->collisions->data);
356       st->collisions->data = 0;
357       XDestroyImage (st->collisions);
358     }
359   free (st);
360 }
361
362
363 static const char *lcdscrub_defaults [] = {
364   ".background:         black",
365   ".foreground:         white",
366   "*fpsSolid:           True",
367   "*delay:              100000",
368   "*spread:             8",
369   "*cycles:             60",
370   "*modeHW:             True",
371   "*modeHB:             True",
372   "*modeVW:             True",
373   "*modeVB:             True",
374   "*modeDW:             True",
375   "*modeDB:             True",
376   "*modeW:              True",
377   "*modeB:              True",
378   "*modeRGB:            True",
379   "*modeRandom:         False",
380   0
381 };
382
383 static XrmOptionDescRec lcdscrub_options [] = {
384   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
385   { "-spread",          ".spread",      XrmoptionSepArg, 0 },
386   { "-cycles",          ".cycles",      XrmoptionSepArg, 0 },
387   { "-no-hw",           ".modeHW",      XrmoptionNoArg, "False" },
388   { "-no-hb",           ".modeHB",      XrmoptionNoArg, "False" },
389   { "-no-vw",           ".modeVW",      XrmoptionNoArg, "False" },
390   { "-no-vb",           ".modeVB",      XrmoptionNoArg, "False" },
391   { "-no-dw",           ".modeDW",      XrmoptionNoArg, "False" },
392   { "-no-db",           ".modeDB",      XrmoptionNoArg, "False" },
393   { "-no-w",            ".modeW",       XrmoptionNoArg, "False" },
394   { "-no-b",            ".modeB",       XrmoptionNoArg, "False" },
395   { "-no-rgb",          ".modeRGB",     XrmoptionNoArg, "False" },
396   { "-random",          ".modeRandom",  XrmoptionNoArg, "True" },
397   { 0, 0, 0, 0 }
398 };
399
400
401 XSCREENSAVER_MODULE ("LCDscrub", lcdscrub)