From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / hacks / popsquares.c
1 /* Copyright (c) 2003 Levi Burton <donburton@sbcglobal.net>
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
12 #include "screenhack.h"
13 #include "colors.h"
14
15 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
16 # include "xdbe.h"
17 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
18
19 typedef struct _square {
20   int x, y, w, h; 
21   int color;
22 } square;
23
24 static void
25 randomize_square_colors(square *squares, int nsquares, int ncolors)
26 {
27   int i;
28   square *s = squares;
29   for (i = 0; i < nsquares; i++) 
30     s[i].color = random() % ncolors;
31 }
32
33
34 struct state {
35   Display *dpy;
36   Window window;
37
38    int delay, subdivisionx, subdivisiony, border, ncolors, twitch, dbuf;
39     XWindowAttributes xgwa;
40     GC gc; 
41     XColor *colors;
42     int sw, sh, gw, gh, nsquares;
43     square *squares;
44     Pixmap b, ba, bb;
45 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
46     XdbeBackBuffer backb;
47 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
48 };
49
50 static void
51 popsquares_reshape (Display *dpy, Window window, void *closure, 
52                  unsigned int w, unsigned int h)
53 {
54   struct state *st = (struct state *) closure;
55   int s = get_integer_resource(st->dpy, "subdivision", "Integer");
56   int x, y;
57   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
58
59   if (st->xgwa.width < 100 || st->xgwa.height < 100) /* tiny window */
60     {
61       int ss = (st->xgwa.width < st->xgwa.height
62                 ? st->xgwa.width : st->xgwa.height);
63       s = ss / 15;
64       if (s < 1) s = 1;
65     }
66
67   if (st->xgwa.width > st->xgwa.height * 5 ||  /* weird aspect ratio */
68       st->xgwa.height > st->xgwa.width * 5)
69     {
70       double r = st->xgwa.width / (double) st->xgwa.height;
71       if (r > 1)
72         {
73           st->subdivisiony = s;
74           st->subdivisionx = s * r;
75         }
76       else
77         {
78           st->subdivisionx = s;
79           st->subdivisiony = s / r;
80         }
81     }
82   else
83     {
84       st->subdivisionx = st->subdivisiony = s;
85     }
86
87   st->sw = st->xgwa.width / st->subdivisionx;
88   st->sh = st->xgwa.height / st->subdivisiony;
89   st->gw = st->sw ? st->xgwa.width / st->sw : 0;
90   st->gh = st->sh ? st->xgwa.height / st->sh : 0;
91   st->nsquares = st->gw * st->gh;
92   free (st->squares);
93   if (st->nsquares < 1) st->nsquares = 1;
94   st->squares = (square *) calloc (st->nsquares, sizeof(square));
95
96   for (y = 0; y < st->gh; y++)
97     for (x = 0; x < st->gw; x++) 
98       {
99         square *s = (square *) &st->squares[st->gw * y + x];
100         s->w = st->sw;
101         s->h = st->sh;
102         s->x = x * st->sw;
103         s->y = y * st->sh;
104       }
105
106   randomize_square_colors(st->squares, st->nsquares, st->ncolors);
107
108   if (st->dbuf) {
109     XFreePixmap (dpy, st->ba);
110     XFreePixmap (dpy, st->bb);
111     st->ba = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height, st->xgwa.depth);
112     st->bb = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height, st->xgwa.depth);
113     st->b = st->ba;
114   }
115 }
116
117 static void *
118 popsquares_init (Display *dpy, Window window)
119 {
120   struct state *st = (struct state *) calloc (1, sizeof(*st));
121   int x, y;
122   double s1, v1, s2, v2 = 0;
123   int h1, h2 = 0;
124   /* Not sure how to use DBEClear */
125   /* Bool dbeclear_p = get_boolean_resource(dpy, "useDBEClear", "Boolean"); */
126   XColor fg, bg;
127   XGCValues gcv;
128   
129   st->dpy = dpy;
130   st->window = window;
131
132   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
133   st->subdivisionx = get_integer_resource(st->dpy, "subdivision", "Integer");
134   st->subdivisiony = st->subdivisionx;
135   st->border = get_integer_resource(st->dpy, "border", "Integer");
136   st->ncolors = get_integer_resource(st->dpy, "ncolors", "Integer");
137   st->twitch = get_boolean_resource(st->dpy, "twitch", "Boolean");
138   st->dbuf = get_boolean_resource(st->dpy, "doubleBuffer", "Boolean");
139
140 # ifdef HAVE_JWXYZ      /* Don't second-guess Quartz's double-buffering */
141   st->dbuf = False;
142 # endif
143
144   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
145
146   fg.pixel = get_pixel_resource (st->dpy, st->xgwa.colormap, "foreground", "Foreground");
147   bg.pixel = get_pixel_resource (st->dpy, st->xgwa.colormap, "background", "Background");
148
149   XQueryColor (st->dpy, st->xgwa.colormap, &fg);
150   XQueryColor (st->dpy, st->xgwa.colormap, &bg);
151
152   st->sw = st->xgwa.width / st->subdivisionx;
153   st->sh = st->xgwa.height / st->subdivisiony;
154   st->gw = st->sw ? st->xgwa.width / st->sw : 0;
155   st->gh = st->sh ? st->xgwa.height / st->sh : 0;
156   st->nsquares = st->gw * st->gh;
157   if (st->nsquares < 1) st->nsquares = 1;
158   if (st->ncolors < 1) st->ncolors = 1;
159
160   gcv.foreground = fg.pixel;
161   gcv.background = bg.pixel;
162   st->gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
163
164   st->colors = (XColor *) calloc (st->ncolors, sizeof(XColor));
165   st->squares = (square *) calloc (st->nsquares, sizeof(square));
166
167   rgb_to_hsv (fg.red, fg.green, fg.blue, &h1, &s1, &v1);
168   rgb_to_hsv (bg.red, bg.green, bg.blue, &h2, &s2, &v2);
169   make_color_ramp (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
170                    h1, s1, v1,
171                    h2, s2, v2,
172                    st->colors, &st->ncolors,  /* would this be considered a value-result argument? */
173                    True, True, False);
174   if (st->ncolors < 2)
175     {
176       fprintf (stderr, "%s: insufficient colors!\n", progname);
177       exit (1);
178     }
179
180   for (y = 0; y < st->gh; y++)
181     for (x = 0; x < st->gw; x++) 
182       {
183         square *s = (square *) &st->squares[st->gw * y + x];
184         s->w = st->sw;
185         s->h = st->sh;
186         s->x = x * st->sw;
187         s->y = y * st->sh;
188       }
189
190   randomize_square_colors(st->squares, st->nsquares, st->ncolors);
191
192   if (st->dbuf)
193     {
194 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
195       st->b = xdbe_get_backbuffer (st->dpy, st->window, XdbeUndefined);
196       st->backb = st->b;
197 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
198       if (!st->b)                      
199         {
200           st->ba = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height, st->xgwa.depth);
201           st->bb = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height, st->xgwa.depth);
202           st->b = st->ba;
203         }
204     }
205   else 
206     {
207       st->b = st->window;
208     }
209
210   popsquares_reshape (dpy, window, st, st->xgwa.width, st->xgwa.height);
211
212   return st;
213 }
214
215 static unsigned long
216 popsquares_draw (Display *dpy, Window window, void *closure)
217 {
218   struct state *st = (struct state *) closure;
219   int x, y;
220   for (y = 0; y < st->gh; y++)
221     for (x = 0; x < st->gw; x++) 
222       {
223         square *s = (square *) &st->squares[st->gw * y + x];
224         XSetForeground (st->dpy, st->gc, st->colors[s->color].pixel);
225         XFillRectangle (st->dpy, st->b, st->gc, s->x, s->y, 
226                         st->border ? s->w - st->border : s->w, 
227                         st->border ? s->h - st->border : s->h);
228         s->color++;
229         if (s->color == st->ncolors)
230           {
231             if (st->twitch && ((random() % 4) == 0))
232               randomize_square_colors (st->squares, st->nsquares, st->ncolors);
233             else
234               s->color = random() % st->ncolors;
235           }
236       }
237 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
238   if (st->backb) 
239     {
240       XdbeSwapInfo info[1];
241       info[0].swap_window = st->window;
242       info[0].swap_action = XdbeUndefined;
243       XdbeSwapBuffers (st->dpy, info, 1);
244     }
245   else
246 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
247     if (st->dbuf)
248       {
249         XCopyArea (st->dpy, st->b, st->window, st->gc, 0, 0, 
250                    st->xgwa.width, st->xgwa.height, 0, 0);
251         st->b = (st->b == st->ba ? st->bb : st->ba);
252       }
253
254   return st->delay;
255 }
256
257
258 static Bool
259 popsquares_event (Display *dpy, Window window, void *closure, XEvent *event)
260 {
261   return False;
262 }
263
264 static void
265 popsquares_free (Display *dpy, Window window, void *closure)
266 {
267   struct state *st = (struct state *) closure;
268   free (st);
269 }
270
271
272 static const char *popsquares_defaults [] = {
273   ".background: #0000FF",
274   ".foreground: #00008B",
275   "*delay: 25000",
276   "*subdivision:  5",
277   "*border: 1",
278   "*ncolors: 128",
279   "*twitch: False",
280   "*doubleBuffer: False",
281 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
282   "*useDBE: True",
283   "*useDBEClear: True",
284 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
285 #ifdef HAVE_MOBILE
286   "*ignoreRotation: True",
287 #endif
288   0
289 };
290
291 static XrmOptionDescRec popsquares_options [] = {
292   { "-fg", ".foreground", XrmoptionSepArg, 0},
293   { "-bg", ".background", XrmoptionSepArg, 0},
294   { "-delay",     ".delay", XrmoptionSepArg, 0 },
295   { "-subdivision", ".subdivision", XrmoptionSepArg, 0 },
296   { "-border", ".border", XrmoptionSepArg, 0},
297   { "-ncolors",   ".ncolors", XrmoptionSepArg, 0 },
298   { "-twitch",    ".twitch", XrmoptionNoArg, "True" },
299   { "-no-twitch", ".twitch", XrmoptionNoArg, "False" },
300   { "-db",        ".doubleBuffer", XrmoptionNoArg, "True" },
301   { "-no-db",     ".doubleBuffer", XrmoptionNoArg, "False" },
302   { 0, 0, 0, 0 }
303 };
304
305
306 XSCREENSAVER_MODULE ("PopSquares", popsquares)