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