e7f220c08aa28e242a2a1e1e574409ae3239ea61
[xscreensaver] / hacks / lcdscrub.c
1 /* xscreensaver, Copyright (c) 2008-2013 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          END } mode;
31   unsigned int enabled_mask;
32   int count;
33   GC fg, bg, bg2;
34   int color_tick;
35   int delay;
36   int spread;
37   int cycles;
38 };
39
40
41 static void
42 pick_mode (struct state *st)
43 {
44   st->count = 0;
45   while (1)
46     {
47       if (++st->mode == END)
48         st->mode = 0;
49       if (st->enabled_mask & (1 << st->mode))
50         break;
51     }
52 }
53
54 static void *
55 lcdscrub_init (Display *dpy, Window window)
56 {
57   struct state *st = (struct state *) calloc (1, sizeof(*st));
58   XGCValues gcv;
59   st->dpy = dpy;
60   st->window = window;
61   st->delay  = get_integer_resource (st->dpy, "delay",  "Integer");
62   st->spread = get_integer_resource (st->dpy, "spread", "Integer");
63   st->cycles = get_integer_resource (st->dpy, "cycles", "Integer");
64
65   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
66   gcv.foreground = BlackPixelOfScreen (st->xgwa.screen);
67   gcv.background = WhitePixelOfScreen (st->xgwa.screen);
68   st->bg  = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
69   st->bg2 = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
70   gcv.foreground = WhitePixelOfScreen (st->xgwa.screen);
71   gcv.background = BlackPixelOfScreen (st->xgwa.screen);
72   st->fg = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
73
74 #ifdef HAVE_COCOA
75   jwxyz_XSetAntiAliasing (st->dpy, st->fg,  False);
76   jwxyz_XSetAntiAliasing (st->dpy, st->bg,  False);
77   jwxyz_XSetAntiAliasing (st->dpy, st->bg2, False);
78 #endif
79
80   st->enabled_mask = 0;
81 # define PREF(R,F) \
82    if (get_boolean_resource (st->dpy, R, "Mode")) st->enabled_mask |= (1 << F)
83   PREF("modeHW", HORIZ_W);
84   PREF("modeHB", HORIZ_B);
85   PREF("modeVW", VERT_W);
86   PREF("modeVB", VERT_B);
87   PREF("modeDW", DIAG_W);
88   PREF("modeDB", DIAG_B);
89   PREF("modeW",  WHITE);
90   PREF("modeB",  BLACK);
91   PREF("modeRGB", RGB);
92 # undef PREF
93   if (! st->enabled_mask) 
94     {
95       fprintf (stderr, "%s: no modes enabled\n", progname);
96       exit (1);
97     }
98
99   pick_mode (st);
100
101   return st;
102 }
103
104 static unsigned long
105 lcdscrub_draw (Display *dpy, Window window, void *closure)
106 {
107   struct state *st = (struct state *) closure;
108   int count = st->count % st->spread;
109   int i;
110   GC fg = (st->mode & 1 ? st->fg : st->bg);
111   GC bg = (st->mode & 1 ? st->bg : st->fg);
112
113   switch (st->mode) {
114   case HORIZ_W:
115   case HORIZ_B:
116     XFillRectangle (st->dpy, st->window, bg, 0, 0,
117                     st->xgwa.width, st->xgwa.height);
118     for (i = count; i < st->xgwa.height; i += st->spread)
119       XDrawLine (st->dpy, st->window, fg, 0, i, st->xgwa.width, i);
120     break;
121   case VERT_W:
122   case VERT_B:
123     XFillRectangle (st->dpy, st->window, bg, 0, 0,
124                     st->xgwa.width, st->xgwa.height);
125     for (i = count; i < st->xgwa.width; i += st->spread)
126       XDrawLine (st->dpy, st->window, fg, i, 0, i, st->xgwa.height);
127     break;
128   case DIAG_W:
129   case DIAG_B:
130     XFillRectangle (st->dpy, st->window, bg, 0, 0,
131                     st->xgwa.width, st->xgwa.height);
132     for (i = count; i < st->xgwa.width; i += st->spread)
133       XDrawLine (st->dpy, st->window, fg, i, 0, 
134                  i + st->xgwa.width, st->xgwa.width);
135     for (i = -count; i < st->xgwa.height; i += st->spread)
136       XDrawLine (st->dpy, st->window, fg, 0, i,
137                  st->xgwa.height, i + st->xgwa.height);
138     break;
139   case RGB:
140     {
141       int scale = 10 * 8; /* 8 sec */
142       static const unsigned short colors[][3] = {
143         { 0xFFFF, 0x0000, 0x0000 },
144         { 0x0000, 0xFFFF, 0x0000 },
145         { 0x0000, 0x0000, 0xFFFF },
146         { 0xFFFF, 0xFFFF, 0x0000 },
147         { 0xFFFF, 0x0000, 0xFFFF },
148         { 0x0000, 0xFFFF, 0xFFFF },
149         { 0xFFFF, 0xFFFF, 0xFFFF },
150         { 0x0000, 0x0000, 0x0000 },
151       };
152       static unsigned long last = 0;
153       XColor xc;
154       bg = st->bg2;
155       xc.red   = colors[st->color_tick / scale][0];
156       xc.green = colors[st->color_tick / scale][1];
157       xc.blue  = colors[st->color_tick / scale][2];
158       if (last) XFreeColors (st->dpy, st->xgwa.colormap, &last, 1, 0);
159       XAllocColor (st->dpy, st->xgwa.colormap, &xc);
160       last = xc.pixel;
161       XSetForeground (st->dpy, bg, xc.pixel);
162       st->color_tick = (st->color_tick + 1) % (countof(colors) * scale);
163       /* fall through */
164     }
165   case WHITE:
166   case BLACK:
167     XFillRectangle (st->dpy, st->window, bg, 0, 0,
168                     st->xgwa.width, st->xgwa.height);
169     break;
170   default: 
171     abort(); 
172     break;
173   }
174
175   st->count++;
176
177   if (st->count > st->spread * st->cycles)
178     pick_mode (st);
179
180   return st->delay;
181 }
182
183 static void
184 lcdscrub_reshape (Display *dpy, Window window, void *closure, 
185                  unsigned int w, unsigned int h)
186 {
187 }
188
189 static Bool
190 lcdscrub_event (Display *dpy, Window window, void *closure, XEvent *event)
191 {
192   return False;
193 }
194
195 static void
196 lcdscrub_free (Display *dpy, Window window, void *closure)
197 {
198   struct state *st = (struct state *) closure;
199   XFreeGC (dpy, st->fg);
200   XFreeGC (dpy, st->bg);
201   XFreeGC (dpy, st->bg2);
202   free (st);
203 }
204
205
206 static const char *lcdscrub_defaults [] = {
207   ".background:         black",
208   ".foreground:         white",
209   "*delay:              100000",
210   "*spread:             8",
211   "*cycles:             60",
212   "*modeHW:             True",
213   "*modeHB:             True",
214   "*modeVW:             True",
215   "*modeVB:             True",
216   "*modeDW:             True",
217   "*modeDB:             True",
218   "*modeW:              True",
219   "*modeB:              True",
220   "*modeRGB:            True",
221   0
222 };
223
224 static XrmOptionDescRec lcdscrub_options [] = {
225   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
226   { "-spread",          ".spread",      XrmoptionSepArg, 0 },
227   { "-cycles",          ".cycles",      XrmoptionSepArg, 0 },
228   { "-no-hw",           ".modeHW",      XrmoptionNoArg, "False" },
229   { "-no-hb",           ".modeHB",      XrmoptionNoArg, "False" },
230   { "-no-vw",           ".modeVW",      XrmoptionNoArg, "False" },
231   { "-no-vb",           ".modeVB",      XrmoptionNoArg, "False" },
232   { "-no-dw",           ".modeDW",      XrmoptionNoArg, "False" },
233   { "-no-db",           ".modeDB",      XrmoptionNoArg, "False" },
234   { "-no-w",            ".modeW",       XrmoptionNoArg, "False" },
235   { "-no-b",            ".modeB",       XrmoptionNoArg, "False" },
236   { "-no-rgb",          ".modeRGB",     XrmoptionNoArg, "False" },
237   { 0, 0, 0, 0 }
238 };
239
240
241 XSCREENSAVER_MODULE ("LCDscrub", lcdscrub)