From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / hacks / deco.c
1 /* xscreensaver, Copyright (c) 1997-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  * Concept snarfed from Michael D. Bayne in
12  * http://www.go2net.com/internet/deep/1997/04/16/body.html
13  *
14  * Changes by Lars Huttar, http://www.huttar.net:
15  * - allow use of golden ratio for dividing rectangles instead of 1/2.
16  * - allow smooth colors instead of random
17  * - added line thickness setting
18  * - added "Mondrian" mode
19  * Other ideas:
20  * - allow recomputing the colormap on each new frame (especially useful
21  *   when ncolors is low)
22  */
23
24 #include "screenhack.h"
25 #include <stdio.h>
26
27 struct state {
28   XColor colors[255];
29   int ncolors;
30   int max_depth;
31   int min_height;
32   int min_width;
33   int line_width;
34   int old_line_width;
35   Bool goldenRatio;
36   Bool mondrian;
37   Bool smoothColors;
38
39   int delay;
40   XWindowAttributes xgwa;
41   GC fgc, bgc;
42   int current_color;
43 };
44
45 /* Golden Ratio
46  *   Suppose you're dividing a rectangle of length A+B
47  *   into two parts, of length A and B respectively. You want the ratio of
48  *   A to B to be the same as the ratio of the whole (A+B) to A. The golden
49  *   ratio (phi) is that ratio. Supposed to be visually pleasing. */
50 #define PHI 1.61803
51 #define PHI1 (1.0/PHI)
52 #define PHI2 (1.0 - PHI1)
53
54 /* copied from make_random_colormap in colors.c */
55 static void
56 make_mondrian_colormap (Screen *screen, Visual *visual, Colormap cmap,
57                       XColor *colors, int *ncolorsP,
58                       Bool allocate_p,
59                       Bool *writable_pP,
60                       Bool verbose_p)
61 {
62   Display *dpy = DisplayOfScreen (screen);
63   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
64   int ncolors = 8;
65   int i;
66
67   if (*ncolorsP <= 0) return;
68
69   /* If this visual doesn't support writable cells, don't bother trying. */
70   if (wanted_writable && !has_writable_cells(screen, visual))
71     *writable_pP = False;
72
73   for (i = 0; i < ncolors; i++)
74   {
75       colors[i].flags = DoRed|DoGreen|DoBlue;
76       colors[i].red = 0;
77       colors[i].green = 0;
78       colors[i].blue = 0;
79
80       switch(i) {
81           case 0: case 1: case 2: case 3: case 7: /* white */
82               colors[i].red = 0xE800;
83               colors[i].green = 0xE800;
84               colors[i].blue  = 0xE800;
85               break;
86           case 4:
87               colors[i].red = 0xCFFF; break; /* red */
88           case 5:
89               colors[i].red = 0x2000;
90               colors[i].blue = 0xCFFF; break; /* blue */
91           case 6:
92               colors[i].red = 0xDFFF; /* yellow */
93               colors[i].green = 0xCFFF; break;
94       }
95   }
96
97   if (!allocate_p)
98     return;
99
100  RETRY_NON_WRITABLE:
101   if (writable_pP && *writable_pP)
102     {
103       unsigned long *pixels = (unsigned long *)
104         malloc(sizeof(*pixels) * (ncolors + 1));
105
106       allocate_writable_colors (screen, cmap, pixels, &ncolors);
107       if (ncolors > 0)
108         for (i = 0; i < ncolors; i++)
109           colors[i].pixel = pixels[i];
110       free (pixels);
111       if (ncolors > 0)
112         XStoreColors (dpy, cmap, colors, ncolors);
113     }
114   else
115     {
116       for (i = 0; i < ncolors; i++)
117         {
118           XColor color;
119           color = colors[i];
120           if (!XAllocColor (dpy, cmap, &color))
121             break;
122           colors[i].pixel = color.pixel;
123         }
124       ncolors = i;
125     }
126
127   /* If we tried for writable cells and got none, try for non-writable. */
128   if (allocate_p && ncolors == 0 && writable_pP && *writable_pP)
129     {
130       ncolors = *ncolorsP;
131       *writable_pP = False;
132       goto RETRY_NON_WRITABLE;
133     }
134
135 #if 0
136   /* I don't think we need to bother copying or linking to the complain
137      function. */
138   if (verbose_p)
139     complain(*ncolorsP, ncolors, wanted_writable,
140              wanted_writable && *writable_pP);
141 #endif
142
143   *ncolorsP = ncolors;
144 }
145
146 static void
147 mondrian_set_sizes (struct state *st, int w, int h)
148 {
149     if (w > h) {
150         st->line_width = w/50;
151         st->min_height = st->min_width = w/8;
152     } else {
153         st->line_width = h/50;
154         st->min_height = st->min_width = h/8;
155     }
156 }
157  
158 static void
159 deco (Display *dpy, Window window, struct state *st,
160       int x, int y, int w, int h, int depth)
161 {
162   if (((random() % st->max_depth) < depth) || (w < st->min_width) || (h < st->min_height))
163     {
164       if (!mono_p)
165         {
166           if (++st->current_color >= st->ncolors)
167             st->current_color = 0;
168           XSetForeground(dpy, st->bgc, st->colors[st->current_color].pixel);
169         }
170       XFillRectangle (dpy, window, st->bgc, x, y, w, h);
171       XDrawRectangle (dpy, window, st->fgc, x, y, w, h);
172     }
173   else
174     {
175       if ((st->goldenRatio || st->mondrian) ? (w > h) : (random() & 1))
176         { /* Divide the rectangle side-by-side */
177           int wnew = (st->goldenRatio ? (w * (random() & 1 ? PHI1 : PHI2)) : w/2);
178           deco (dpy, window, st, x, y, wnew, h, depth+1);
179           deco (dpy, window, st, x+wnew, y, w-wnew, h, depth+1);
180         }
181       else
182         { /* Divide the rectangle top-to-bottom */
183           int hnew = (st->goldenRatio ? (h * (random() & 1 ? PHI1 : PHI2)) : h/2);
184           deco (dpy, window, st, x, y, w, hnew, depth+1);
185           deco (dpy, window, st, x, y+hnew, w, h-hnew, depth+1);
186         }
187     }
188 }
189
190 static void *
191 deco_init (Display *dpy, Window window)
192 {
193   struct state *st = (struct state *) calloc (1, sizeof(*st));
194   XGCValues gcv;
195
196   st->delay = get_integer_resource (dpy, "delay", "Integer");
197
198   st->smoothColors = get_boolean_resource(dpy, "smoothColors", "Boolean");
199   st->old_line_width = 1;
200
201   st->goldenRatio = get_boolean_resource (dpy, "goldenRatio", "Boolean");
202
203   st->max_depth = get_integer_resource (dpy, "maxDepth", "Integer");
204   if (st->max_depth < 1) st->max_depth = 1;
205   else if (st->max_depth > 1000) st->max_depth = 1000;
206
207   st->min_width = get_integer_resource (dpy, "minWidth", "Integer");
208   if (st->min_width < 2) st->min_width = 2;
209   st->min_height = get_integer_resource (dpy, "minHeight", "Integer");
210   if (st->min_height < 2) st->min_height = 2;
211
212   st->line_width = get_integer_resource (dpy, "lineWidth", "Integer");
213
214   XGetWindowAttributes (dpy, window, &st->xgwa);
215
216   st->ncolors = get_integer_resource (dpy, "ncolors", "Integer");
217
218   gcv.foreground = get_pixel_resource(dpy, st->xgwa.colormap,
219                                       "foreground", "Foreground");
220   st->fgc = XCreateGC (dpy, window, GCForeground, &gcv);
221
222   gcv.foreground = get_pixel_resource(dpy, st->xgwa.colormap,
223                                       "background", "Background");
224   st->bgc = XCreateGC (dpy, window, GCForeground, &gcv);
225
226   if (st->ncolors <= 2)
227     mono_p = True;
228
229   if (!mono_p)
230     {
231       GC tmp = st->fgc;
232       st->fgc = st->bgc;
233       st->bgc = tmp;
234     }
235
236   st->mondrian = get_boolean_resource(dpy, "mondrian", "Boolean");
237   if (st->mondrian) {
238       /* Mondrian, if true, overrides several other options. */
239       mondrian_set_sizes(st, st->xgwa.width, st->xgwa.height);
240
241       /** set up red-yellow-blue-black-white colormap and fgc **/
242       make_mondrian_colormap(st->xgwa.screen, st->xgwa.visual,
243                              st->xgwa.colormap,
244                              st->colors, &st->ncolors, True, 0, True);
245
246       /** put white in several cells **/
247       /** set min-height and min-width to about 10% of total w/h **/
248   }
249   else if (st->smoothColors)
250       make_smooth_colormap (st->xgwa.screen, st->xgwa.visual,
251                             st->xgwa.colormap,
252                             st->colors, &st->ncolors, True, 0, True);
253   else
254       make_random_colormap (st->xgwa.screen, st->xgwa.visual,
255                             st->xgwa.colormap,
256                             st->colors, &st->ncolors, False, True, 0, True);
257
258   gcv.line_width = st->old_line_width = st->line_width;
259   XChangeGC(dpy, st->fgc, GCLineWidth, &gcv);
260
261   return st;
262 }
263
264 static unsigned long
265 deco_draw (Display *dpy, Window window, void *closure)
266 {
267   struct state *st = (struct state *) closure;
268   XFillRectangle (dpy, window, st->bgc, 0, 0, st->xgwa.width, st->xgwa.height);
269   if (st->mondrian) {
270       mondrian_set_sizes(st, st->xgwa.width, st->xgwa.height);
271       if (st->line_width != st->old_line_width) {
272           XSetLineAttributes(dpy, st->fgc, st->line_width,
273                              LineSolid, CapButt, JoinBevel);
274           st->old_line_width = st->line_width;
275       }
276   }
277   deco (dpy, window, st, 0, 0, st->xgwa.width, st->xgwa.height, 0);
278   return 1000000 * st->delay;
279 }
280
281 static void
282 deco_reshape (Display *dpy, Window window, void *closure, 
283                  unsigned int w, unsigned int h)
284 {
285   struct state *st = (struct state *) closure;
286   st->xgwa.width = w;
287   st->xgwa.height = h;
288 }
289
290 static Bool
291 deco_event (Display *dpy, Window window, void *closure, XEvent *event)
292 {
293   return False;
294 }
295
296 static void
297 deco_free (Display *dpy, Window window, void *closure)
298 {
299   struct state *st = (struct state *) closure;
300   free (st);
301 }
302
303 \f
304 static const char *deco_defaults [] = {
305   ".background:         black",
306   ".foreground:         white",
307   "*maxDepth:           12",
308   "*minWidth:           20",
309   "*minHeight:          20",
310   "*lineWidth:          1",
311   "*delay:              5",
312   "*ncolors:            64",
313   "*goldenRatio:        False",
314   "*smoothColors:       False",
315   "*mondrian:           False",
316 #ifdef USE_IPHONE
317   "*ignoreRotation:     True",
318 #endif
319   0
320 };
321
322 static XrmOptionDescRec deco_options [] = {
323   { "-max-depth",       ".maxDepth",    XrmoptionSepArg, 0 },
324   { "-min-width",       ".minWidth",    XrmoptionSepArg, 0 },
325   { "-min-height",      ".minHeight",   XrmoptionSepArg, 0 },
326   { "-line-width",      ".lineWidth",   XrmoptionSepArg, 0 },
327   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
328   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
329   { "-golden-ratio",    ".goldenRatio", XrmoptionNoArg, "True" },
330   { "-no-golden-ratio", ".goldenRatio", XrmoptionNoArg, "False" },
331   { "-smooth-colors",   ".smoothColors",XrmoptionNoArg, "True" },
332   { "-no-smooth-colors",".smoothColors",XrmoptionNoArg, "False" },
333   { "-mondrian",        ".mondrian",    XrmoptionNoArg, "True" },
334   { "-no-mondrian",     ".mondrian",    XrmoptionNoArg, "False" },
335   { 0, 0, 0, 0 }
336 };
337
338 XSCREENSAVER_MODULE ("Deco", deco)