From http://www.jwz.org/xscreensaver/xscreensaver-5.18.tar.gz
[xscreensaver] / hacks / truchet.c
1 /* truchet --- curved and straight tilings
2  * Copyright (c) 1998 Adrian Likins <adrian@gimp.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* This screensaver draws two varieties of truchet patterns, a curved one and
14    a straight one. There are lots and lots of command line options to play
15    with.
16
17    If your running remotely or on a slow machine or slow xserver, some of the
18    settings will be way too much. The default settings should be okay though. 
19
20    This screensaver doesnt use anything bizarre or special at all, just a few
21    standard xlib calls.
22
23    A few suggested commandline combos..All these were tested on a k6-200
24    running XFree86 3.3 on a ark2000, so your mileage may vary...
25
26       truchet -delay 200 -no-curves
27       truchet -delay 500 -no-curves -square -no-erase
28       truchet -delay 500 -no-erase -square -erase-count 5
29       truchet -scroll
30       truchet -scroll -no-erase -anim-step-size 9
31       truchet -delay 200 -no-angles -min-width 36 -max-width 36
32       truchet -delay 200 -no-curves -min-width 12 -max-width 12
33       truchet -delay 200 -no-curves -min-width 36 -max-width 36 -no-erase
34       truchet -delay 100 -min-width 256 -max-width 512 -no-erase \
35               -min-linewidth 96 -root
36       truchet -min-width 64 -max-width 128 -no-erase -max-linewidth 4 \
37               -root -no-angles
38       truchet -min-width 64 -max-width 128 -no-erase -max-linewidth 4 \
39               -root -no-curves -delay 25
40  */
41
42 #include "screenhack.h"
43
44 #define MAXRATIO 2
45
46 static const char *truchet_defaults [] = {
47   "*minWidth:                 40",
48   "*minHeight:                40",
49   "*max-Width:                150",
50   "*max-Height:               150",
51   "*maxLineWidth:             25",
52   "*minLineWidth:             2",
53   "*erase:                    True",
54   "*eraseCount:               25",        
55   "*square:                   True",
56   "*delay:                    400000",
57   "*curves:                   True",
58   "*angles:                   True",
59   "*scroll:                   False",
60   "*scroll-overlap:           400",
61   "*anim-delay:               100",
62   "*anim-step-size:           3",
63   "*randomize:                true",
64    0
65 };
66
67 /* options passed to this program */
68 static XrmOptionDescRec truchet_options [] = {
69   { "-min-width",      ".minWidth",       XrmoptionSepArg, 0 },
70   { "-max-height",     ".max-Height",      XrmoptionSepArg, 0 },
71   { "-max-width",      ".max-Width",       XrmoptionSepArg, 0 },
72   { "-min-height",     ".minHeight",      XrmoptionSepArg, 0 },
73   { "-max-linewidth",  ".maxLineWidth",   XrmoptionSepArg, 0 },
74   { "-min-linewidth",  ".minLineWidth",   XrmoptionSepArg, 0 },
75   { "-erase",          ".erase",          XrmoptionNoArg, "True" },
76   { "-no-erase",       ".erase",          XrmoptionNoArg, "False" },
77   { "-erase-count",    ".eraseCount",     XrmoptionSepArg, 0 },
78   { "-square",         ".square",         XrmoptionNoArg, "True" },
79   { "-not-square",     ".square",         XrmoptionNoArg, "False" },
80   { "-curves",         ".curves",         XrmoptionNoArg, "True" },
81   { "-angles",         ".angles",         XrmoptionNoArg,  "True" },
82   { "-no-angles",      ".angles",         XrmoptionNoArg,  "False" },
83   { "-no-curves",      ".curves",         XrmoptionNoArg, "False" },
84   { "-delay",          ".delay",          XrmoptionSepArg, 0 },
85   { "-scroll",         ".scroll",         XrmoptionNoArg, "True" },
86   { "-scroll-overlap", ".scroll-overlap", XrmoptionSepArg, 0 },
87   { "-anim-delay",     ".anim-delay",     XrmoptionSepArg, 0 },
88   { "-anim-step-size",  ".anim-step-size", XrmoptionSepArg, 0 },
89   { "-randomize",       ".randomize",     XrmoptionNoArg, "True" },
90   { 0, 0, 0, 0 }
91 };
92
93 struct state {
94   Display *dpy;
95   Window window;
96
97   XGCValues gcv;
98   GC agc, bgc;
99   int linewidth;
100   int width, height;
101   XWindowAttributes xgwa;
102   Pixmap frame;
103   int overlap;
104
105   int maxlinewidth;
106   int minlinewidth;
107   int minwidth;
108   int minheight;
109   int max_height; 
110   int max_width; 
111   int delay;
112   int count;
113   int anim_delay;
114   int anim_step_size;
115
116   Colormap cmap;
117   XColor fgc;
118   Bool curves;
119   Bool square;
120   Bool angles;
121   Bool erase;
122   Bool eraseCount;
123   Bool scroll;
124   int scrolling;
125 };
126
127 static void draw_truchet(struct state *st);
128 static void draw_angles(struct state *st);
129 static void scroll_area(struct state *st, int step_size);
130
131 static void draw_angles(struct state *st)
132 {
133   int cx = 0, cy = 0;
134   
135   while((st->xgwa.height+st->overlap) > cy*st->height)
136         {
137           while((st->xgwa.width+st->overlap) > cx*st->width)
138             {
139               if(random()%2)
140               {
141                 /* block1 */
142                 XDrawLine(st->dpy,st->frame,st->agc,
143                           (cx*st->width)+(st->width/2),
144                           (cy*st->height), 
145                           (cx*st->width)+(st->width),
146                           (cy*st->height)+(st->height/2));
147                 XDrawLine(st->dpy,st->frame,st->agc,
148                           (cx*st->width), 
149                           (cy*st->height)+(st->height/2),
150                           (cx*st->width)+(st->width/2),
151                           (cy*st->height)+(st->height));
152               }
153             else
154               {
155                 /* block 2 */
156                 XDrawLine(st->dpy,st->frame,st->agc, 
157                           (cx*st->width)+(st->width/2),
158                           (cy*st->height),
159                           (cx*st->width),
160                           (cy*st->height)+(st->height/2));
161                 XDrawLine(st->dpy,st->frame,st->agc,
162                           (cx*st->width)+(st->width),
163                           (cy*st->height)+(st->height/2),
164                           (cx*st->width)+(st->width/2),
165                           (cy*st->height)+(st->height)); 
166               }
167               cx++;
168             }
169           cy++;
170           cx=0;
171         }
172 }
173   
174
175 static void draw_truchet(struct state *st)
176 {
177   int cx = 0, cy = 0;
178
179   while(st->xgwa.height+st->overlap > cy*st->height)
180         {
181           while(st->xgwa.width+st->overlap > cx*st->width)
182             {
183               if(random()%2)
184               {
185                 /* block1 */
186                 XDrawArc(st->dpy, st->frame, st->agc,
187                          ((cx*st->width)-(st->width/2)),
188                          ((cy*st->height)-(st->height/2)),
189                          st->width,
190                          st->height,
191                          0, -5760);
192                 XDrawArc(st->dpy,st->frame, st->agc,
193                          ((cx*st->width)+(st->width/2)),
194                          ((cy*st->height)+(st->height/2)),
195                          st->width,
196                          st->height,
197                          11520,
198                          -5760);
199               }
200             else
201               {
202                 /* block 2 */
203                 XDrawArc(st->dpy,st->frame,st->agc,
204                          ((cx*st->width)+(st->width/2)),
205                          ((cy*st->height)-(st->height/2)),
206                          st->width,
207                          st->height,
208                          17280,
209                          -5760);
210                 XDrawArc(st->dpy,st->frame,st->agc,
211                          ((cx*st->width)-(st->width/2)),
212                          ((cy*st->height)+(st->height/2)),
213                          st->width,
214                          st->height,
215                          0,
216                          5760);
217               }
218               cx++;
219             }
220           cy++;
221           cx=0;
222         }
223 }
224
225
226 static void scroll_area(struct state *st, int step_size)
227 {
228   int scrollcount_x;
229   int scrollcount_y;
230   int offset;
231   int scroll;
232   int direction;
233   int progress;
234
235   offset=st->overlap/2;
236   scroll=st->overlap/4;
237   
238   /* This runs in a loop, starting with
239    *  st->scrolling = (scroll / st->anim_step_size) * 4 - 1;
240    * and going all the way down to st->scrolling = 0.
241    */
242
243   /* if anyone knows a good way to generate
244    * a more random scrolling motion... */
245
246   direction = st->scrolling / (scroll / st->anim_step_size);
247   progress = (st->scrolling % (scroll / st->anim_step_size)) * st->anim_step_size;
248
249   if (direction & 1) {
250     scrollcount_x = progress - scroll;
251     scrollcount_y = progress;
252   } else {
253     scrollcount_x = -progress;
254     scrollcount_y = progress - scroll;
255   }
256
257   if (direction & 2) {
258     scrollcount_x = -scrollcount_x;
259     scrollcount_y = -scrollcount_y;
260   }
261
262   XCopyArea(st->dpy, st->frame, st->window, st->agc,scrollcount_x+offset,scrollcount_y+offset, st->xgwa.width, st->xgwa.height, 0,0);
263 }
264
265
266 static void *
267 truchet_init (Display *dpy, Window window)
268 {
269   struct state *st = (struct state *) calloc (1, sizeof(*st));
270
271   st->dpy = dpy;
272   st->window = window;
273
274   st->maxlinewidth = get_integer_resource (st->dpy, "maxLineWidth", "Integer");
275   st->minlinewidth = get_integer_resource (st->dpy, "minLineWidth", "Integer");
276   st->minwidth = get_integer_resource (st->dpy, "minWidth", "Integer");
277   st->minheight = get_integer_resource (st->dpy, "minHeight", "Integer");
278   st->max_width = get_integer_resource (st->dpy, "max-Width", "Integer"); 
279   st->max_height = get_integer_resource (st->dpy, "max-Height", "Integer" ); 
280   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
281   st->eraseCount = get_integer_resource (st->dpy, "eraseCount", "Integer");
282   st->square = get_boolean_resource (st->dpy, "square", "Boolean");
283   st->curves = get_boolean_resource (st->dpy, "curves", "Boolean");
284   st->angles = get_boolean_resource (st->dpy, "angles", "Boolean");
285   st->erase = get_boolean_resource (st->dpy, "erase", "Boolean");
286   st->scroll = get_boolean_resource (st->dpy, "scroll", "Boolean");
287   st->overlap = get_integer_resource (st->dpy, "scroll-overlap", "Integer");
288   st->anim_delay = get_integer_resource (st->dpy, "anim-delay", "Integer");
289   st->anim_step_size = get_integer_resource (st->dpy, "anim-step-size", "Integer");
290
291   if (get_boolean_resource(st->dpy, "randomize", "Randomize"))
292     {
293       int i = (random() % 12);
294       switch(i) {
295       case 0:
296         break;
297       case 1:
298         st->curves = False;
299         break;
300       case 2:
301         st->curves = False;
302         st->square = True;
303         st->erase = False;
304         break;
305       case 3:
306         st->square = True;
307         st->erase = False;
308         st->eraseCount = 5;
309         break;
310       case 4:
311         st->scroll = True;
312         break;
313       case 5:
314         st->scroll = True;
315         st->erase = False;
316         st->anim_step_size = 9;
317         break;
318       case 6:
319         st->angles = False;
320         st->minwidth = st->max_width = 36;
321         break;
322       case 7:
323         st->curves = False;
324         st->minwidth = st->max_width = 12;
325         break;
326       case 8:
327         st->curves = False;
328         st->erase = False;
329         st->minwidth = st->max_width = 36;
330         break;
331       case 9:
332         st->erase = False;
333         st->minwidth = 256;
334         st->max_width = 512;
335         st->minlinewidth = 96;
336         break;
337       case 10:
338         st->angles = False;
339         st->minwidth = 64;
340         st->max_width = 128;
341         st->maxlinewidth = 4;
342         break;
343       case 11:
344         st->curves = False;
345         st->minwidth = 64;
346         st->max_width = 128;
347         st->maxlinewidth = 4;
348         break;
349       default:
350         abort();
351         break;
352       }
353     }
354
355   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
356   st->gcv.foreground = BlackPixel(st->dpy,0);
357   st->gcv.background = WhitePixel(st->dpy,0);
358   st->gcv.line_width = 25;
359   st->cmap = st->xgwa.colormap;
360
361   st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
362                                       "background", "Background");
363
364   st->bgc = XCreateGC (st->dpy, st->window, GCForeground, &st->gcv);
365   st->agc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
366
367   XFillRectangle(st->dpy, st->window, st->bgc, 0, 0, st->xgwa.width, st->xgwa.height);
368
369  
370   st->width=60;
371   st->height=60;
372   st->linewidth=1;
373   st->count=0;
374   XSetForeground(st->dpy, st->agc, st->gcv.background);
375   
376   
377   st->frame = XCreatePixmap(st->dpy,st->window, st->xgwa.width+st->overlap, st->xgwa.height+st->overlap, st->xgwa.depth); 
378   
379   return st;
380 }
381
382 static unsigned long
383 truchet_draw (Display *dpy, Window window, void *closure)
384 {
385   struct state *st = (struct state *) closure;
386
387   if (st->scrolling)
388     {
389       st->scrolling--;
390       scroll_area(st, st->anim_step_size);
391       return st->anim_delay*1000;
392     }
393
394   if (!mono_p)
395     {
396       /* XXX there are probably bugs with this. */
397       /* could be...I just borrowed this code from munch */
398
399       st->fgc.red = random() % 65535;
400       st->fgc.green = random() % 65535;
401       st->fgc.blue = random() % 65535;
402         
403       if (XAllocColor(st->dpy, st->cmap, &st->fgc)) 
404         {
405           XSetForeground(st->dpy, st->agc, st->fgc.pixel);
406         }
407       else
408         {
409           /* use white if all else fails  */
410           XSetForeground(st->dpy,st->agc, st->gcv.background);
411         }
412     }
413
414       
415       
416
417   /* generate a random line width */
418   st->linewidth=(random()% st->maxlinewidth);
419
420   /* check for lower bound */
421   if(st->linewidth < st->minlinewidth)
422     st->linewidth = st->minlinewidth;
423
424   /* try to get an odd linewidth as it seem to work a little better */
425   if(st->linewidth%2)
426     st->linewidth++;
427
428   /* grab a random height and width */ 
429   st->width=(random()%st->max_width);
430   st->height=(random()%st->max_height);
431
432   /* make sure we dont get a 0 height or width */
433   if(st->width == 0 || st->height == 0)
434     {
435       st->height=st->max_height;
436       st->width=st->max_width;
437     }
438
439
440   /* check for min height and width */
441   if(st->height < st->minheight)
442     {
443       st->height=st->minheight;
444     }
445   if(st->width < st->minwidth)
446     {
447       st->width=st->minwidth;
448     }
449
450   /* if tiles need to be square, fix it... */
451   if(st->square)
452     st->height=st->width;
453
454   /* check for sane aspect ratios */
455   if((st->width/st->height) > MAXRATIO) 
456     st->height=st->width;
457   if((st->height/st->width) > MAXRATIO)
458     st->width=st->height;
459       
460   /* to avoid linewidths of zero */
461   if(st->linewidth == 0 || st->linewidth < st->minlinewidth)
462     st->linewidth = st->minlinewidth;
463
464   /* try to keep from getting line widths that would be too big */
465   if(st->linewidth > 0 && st->linewidth >= (st->height/5))
466     st->linewidth = st->height/5;
467   
468   XSetLineAttributes(st->dpy, st->agc, st->linewidth, LineSolid, CapRound, JoinRound);
469
470   if(st->erase || (st->count >= st->eraseCount))
471     {
472       /*  XClearWindow(dpy,window); */
473       XFillRectangle(st->dpy, st->frame, st->bgc, 0, 0, st->xgwa.width+st->overlap, st->xgwa.height+st->overlap);
474       st->count=0;
475     }
476             
477   if(!st->scroll)
478     st->overlap=0;
479             
480   /* do the fun stuff...*/
481   if(st->curves && st->angles)
482     {
483       if(random()%2)
484         draw_truchet(st);
485       else
486         draw_angles(st);
487     }
488   else if(st->curves && !st->angles)
489     draw_truchet(st);
490   else if(!st->curves && st->angles)
491     draw_angles(st);
492
493    
494   st->count++;
495
496   if(st->scroll)
497     {
498       st->scrolling = ((st->overlap / 4) / st->anim_step_size) * 4;
499       return 0;
500     }
501
502   XCopyArea(st->dpy,st->frame,st->window,st->agc,0,0,st->xgwa.width,st->xgwa.height,0,0);
503
504   return st->delay;
505 }
506
507 static void
508 truchet_reshape (Display *dpy, Window window, void *closure, 
509                  unsigned int w, unsigned int h)
510 {
511   struct state *st = (struct state *) closure;
512   st->width = w;
513   st->height = h;
514   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
515 }
516
517 static Bool
518 truchet_event (Display *dpy, Window window, void *closure, XEvent *event)
519 {
520   return False;
521 }
522
523 static void
524 truchet_free (Display *dpy, Window window, void *closure)
525 {
526 }
527
528
529 XSCREENSAVER_MODULE ("Truchet", truchet)
530