From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / strange.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* strange --- strange attractors */
3
4 #if 0
5 static const char sccsid[] = "@(#)strange.c     5.00 2000/11/01 xlockmore";
6 #endif
7
8 /*-
9 * Copyright (c) 1997 by Massimino Pascal <Pascal.Massimon@ens.fr>
10 *
11 * Permission to use, copy, modify, and distribute this software and its
12 * documentation for any purpose and without fee is hereby granted,
13 * provided that the above copyright notice appear in all copies and that
14 * both that copyright notice and this permission notice appear in
15 * supporting documentation.
16 *
17 * This file is provided AS IS with no warranties of any kind.  The author
18 * shall have no liability with respect to the infringement of copyrights,
19 * trade secrets or any patents by this file or any part thereof.  In no
20 * event will the author be liable for any lost revenue or profits or
21 * other special, indirect and consequential damages.
22 *
23 * Revision History:
24 * 01-Nov-2000: Allocation checks
25 * 10-May-1997: jwz@jwz.org: turned into a standalone program.
26 *              Made it render into an offscreen bitmap and then copy
27 *              that onto the screen, to reduce flicker.
28 *
29 * strange attractors are not so hard to find...
30 */
31
32 /* TODO: Bring over tweaks from 3.x version.
33 * For a good idea of what's missing, diff strange.c.20081107-good2 against strange.c-3.29
34 * We forked from the 3.29 series at 20081107, so anything added since then may be missing.
35 */
36
37 #ifdef STANDALONE
38 # define MODE_strange
39 # define DEFAULTS       "*delay: 10000 \n" \
40                                         "*ncolors: 100 \n" \
41                                         "*fpsSolid: True \n" \
42                                         "*ignoreRotation: True \n" \
43
44 # define SMOOTH_COLORS
45 # define refresh_strange 0
46 # include "xlockmore.h"         /* from the xscreensaver distribution */
47 #else /* !STANDALONE */
48 # include "xlock.h"             /* from the xlockmore distribution */
49 #endif /* !STANDALONE */
50
51 #ifdef MODE_strange
52 #define DEF_CURVE  "10"
53 #define DEF_POINTS "5500"
54
55 /*static int curve;*/
56 static int points;
57
58 static XrmOptionDescRec opts[] =
59 {
60 /*              {"-curve",  ".strange.curve",  XrmoptionSepArg, 0}, */
61                 {"-points", ".strange.points", XrmoptionSepArg, 0},
62 };
63 static argtype vars[] =
64 {
65 /*              {&curve,  "curve",  "Curve",  DEF_CURVE,  t_Int},*/
66                 {&points, "points", "Points", DEF_POINTS, t_Int},
67 };
68 static OptionStruct desc[] =
69 {
70 /*              {"-curve", "set the curve factor of the attractors"},*/
71                 {"-points", "change the number of points/iterations each frame"},
72 };
73 ENTRYPOINT ModeSpecOpt strange_opts =
74 {sizeof opts / sizeof opts[0], opts,
75 sizeof vars / sizeof vars[0], vars, desc};
76
77 #ifdef USE_MODULES
78 ModStruct   strange_description =
79 {"strange", "init_strange", "draw_strange", "release_strange",
80 "init_strange", "init_strange", (char *) NULL, &strange_opts,
81 1000, 1, 1, 1, 64, 1.0, "",
82 "Shows strange attractors", 0, NULL};
83 #endif
84
85 #ifdef HAVE_COCOA
86 # define NO_DBUF
87 #endif
88
89 typedef float DBL;
90 typedef int PRM;
91
92 #define UNIT (1<<12)
93 #define UNIT2 (1<<14)
94 /* #define UNIT2 (3140*UNIT/1000) */
95
96 #define SKIP_FIRST      100
97 #define DBL_To_PRM(x)  (PRM)( (DBL)(UNIT)*(x) )
98
99
100 #define DO_FOLD(a) (a)<0 ? -A->Fold[ (-(a))&(UNIT2-1) ] : A->Fold[ (a)&(UNIT2-1) ]
101
102 #if 0
103 #define DO_FOLD(a) (a)<-UNIT2 ? -A->Fold[(-(a))%UNIT2] : (a)<0 ? -A->Fold[ -(a) ] :\
104 (a)>UNIT2 ? A->Fold[ (a)%UNIT2 ] : A->Fold[ (a) ]
105 #define DO_FOLD(a) DBL_To_PRM( sin( (DBL)(a)/UNIT ) )
106 #define DO_FOLD(a) (a)<0 ? DBL_To_PRM( exp( 16.0*(a)/UNIT2 ) )-1.0 : \
107 DBL_To_PRM( 1.0-exp( -16.0*(a)/UNIT2 ) )
108 #endif
109
110 /* useAccumulator performs two functions:
111 * If it is defined, then support for the accumulator will be compiled.
112 * It is also the condition for which the accumulator renderer will engage.
113 */
114 #define useAccumulator (Root->Max_Pt > 6000)
115 #define ACC_GAMMA 10.0
116 #define NUM_COLS 150
117 /* Extra options: */
118 #define VARY_SPEED_TO_AVOID_BOREDOM
119 #define POINTS_HISTORY
120 #define MERGE_FRAMES 3
121
122 /******************************************************************/
123
124 #define MAX_PRM 3*5
125
126 typedef struct _ATTRACTOR {
127         DBL         Prm1[MAX_PRM], Prm2[MAX_PRM];
128         PRM         Prm[MAX_PRM], *Fold;
129         void        (*Iterate) (struct _ATTRACTOR *, PRM, PRM, PRM *, PRM *);
130         XPoint     *Buffer1, *Buffer2;
131         int         Cur_Pt, Max_Pt;
132         int         Col, Count, Speed;
133         int         Width, Height;
134         Pixmap      dbuf;       /* jwz */
135         GC          dbuf_gc;
136         #ifdef useAccumulator
137                 int **accMap;
138         #endif
139 } ATTRACTOR;
140
141 static ATTRACTOR *Root = (ATTRACTOR *) NULL; 
142
143 #ifdef useAccumulator
144 static XColor* cols;
145 #endif
146
147 #ifdef POINTS_HISTORY
148 static int numOldPoints;
149 static int* oldPointsX;
150 static int* oldPointsY;
151 static int oldPointsIndex;
152 static int startedClearing;
153 #endif
154
155 static DBL  Amp_Prm[MAX_PRM] =
156 {
157         1.0, 3.5, 3.5, 2.5, 4.7,
158         1.0, 3.5, 3.6, 2.5, 4.7,
159         1.0, 1.5, 2.2, 2.1, 3.5
160 };
161 static DBL  Mid_Prm[MAX_PRM] =
162 {
163         0.0, 1.5, 0.0, .5, 1.5,
164         0.0, 1.5, 0.0, .5, 1.5,
165         0.0, 1.5, -1.0, -.5, 2.5,
166 };
167
168 static      DBL
169 Gauss_Rand(DBL c, DBL A, DBL S)
170 {
171         DBL         y;
172
173         y = (DBL) LRAND() / MAXRAND;
174         y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S));
175         if (NRAND(2))
176                 return (c + y);
177         else
178                 return (c - y);
179 }
180
181 static void
182 Random_Prm(DBL * Prm)
183 {
184         int         i;
185
186         for (i = 0; i < MAX_PRM; ++i)
187                 Prm[i] = Gauss_Rand(Mid_Prm[i], Amp_Prm[i], 4.0);
188 }
189
190 /***************************************************************/
191
192   /* 2 examples of non-linear map */
193
194 static void
195 Iterate_X2(ATTRACTOR * A, PRM x, PRM y, PRM * xo, PRM * yo)
196 {
197         PRM         xx, yy, xy, x2y, y2x, Tmp;
198
199         xx = (x * x) / UNIT;
200         x2y = (xx * y) / UNIT;
201         yy = (y * y) / UNIT;
202         y2x = (yy * x) / UNIT;
203         xy = (x * y) / UNIT;
204
205         Tmp = A->Prm[1] * xx + A->Prm[2] * xy + A->Prm[3] * yy + A->Prm[4] * x2y;
206         Tmp = A->Prm[0] - y + (Tmp / UNIT);
207         *xo = DO_FOLD(Tmp);
208         Tmp = A->Prm[6] * xx + A->Prm[7] * xy + A->Prm[8] * yy + A->Prm[9] * y2x;
209         Tmp = A->Prm[5] + x + (Tmp / UNIT);
210         *yo = DO_FOLD(Tmp);
211 }
212
213 static void
214 Iterate_X3(ATTRACTOR * A, PRM x, PRM y, PRM * xo, PRM * yo)
215 {
216         PRM         xx, yy, xy, x2y, y2x, Tmp_x, Tmp_y, Tmp_z;
217
218         xx = (x * x) / UNIT;
219         x2y = (xx * y) / UNIT;
220         yy = (y * y) / UNIT;
221         y2x = (yy * x) / UNIT;
222         xy = (x * y) / UNIT;
223
224         Tmp_x = A->Prm[1] * xx + A->Prm[2] * xy + A->Prm[3] * yy + A->Prm[4] * x2y;
225         Tmp_x = A->Prm[0] - y + (Tmp_x / UNIT);
226         Tmp_x = DO_FOLD(Tmp_x);
227
228         Tmp_y = A->Prm[6] * xx + A->Prm[7] * xy + A->Prm[8] * yy + A->Prm[9] * y2x;
229         Tmp_y = A->Prm[5] + x + (Tmp_y / UNIT);
230
231         Tmp_y = DO_FOLD(Tmp_y);
232
233         Tmp_z = A->Prm[11] * xx + A->Prm[12] * xy + A->Prm[13] * yy + A->Prm[14] * y2x;
234         Tmp_z = A->Prm[10] + x + (Tmp_z / UNIT);
235         Tmp_z = UNIT + Tmp_z * Tmp_z / UNIT;
236
237         *xo = (Tmp_x * UNIT) / Tmp_z;
238         *yo = (Tmp_y * UNIT) / Tmp_z;
239 }
240
241 static void (*Funcs[2]) (ATTRACTOR *, PRM, PRM, PRM *, PRM *) = {
242         Iterate_X2, Iterate_X3
243 };
244
245 /***************************************************************/
246
247 static void
248 free_strange(Display *display, ATTRACTOR *A)
249 {
250         if (A->Buffer1 != NULL) {
251                 (void) free((void *) A->Buffer1);
252                 A->Buffer1 = (XPoint *) NULL;
253         }
254         if (A->Buffer2 != NULL) {
255                 (void) free((void *) A->Buffer2);
256                 A->Buffer2 = (XPoint *) NULL;
257         }
258         if (A->dbuf) {
259                 XFreePixmap(display, A->dbuf);
260                 A->dbuf = None;
261         }
262         if (A->dbuf_gc) {
263                 XFreeGC(display, A->dbuf_gc);
264                 A->dbuf_gc = None;
265         }
266         if (A->Fold != NULL) {
267                 (void) free((void *) A->Fold);
268                 A->Fold = (PRM *) NULL;
269         }
270 }
271
272 ENTRYPOINT void
273 draw_strange(ModeInfo * mi)
274 {
275         int         i, j, n, Cur_Pt;
276         PRM         x, y, xo, yo;
277         DBL         u;
278         XPoint     *Buf;
279         Display    *display = MI_DISPLAY(mi);
280         Window      window = MI_WINDOW(mi);
281         GC          gc = MI_GC(mi);
282         DBL         Lx, Ly;
283         void        (*Iterate) (ATTRACTOR *, PRM, PRM, PRM *, PRM *);
284         PRM         xmin, xmax, ymin, ymax;
285         ATTRACTOR  *A;
286
287         if (Root == NULL)
288                 return;
289         A = &Root[MI_SCREEN(mi)];
290         if (A->Fold == NULL)
291                 return;
292
293         Cur_Pt = A->Cur_Pt;
294         Iterate = A->Iterate;
295
296         u = (DBL) (A->Count) / 40000.0;
297         for (j = MAX_PRM - 1; j >= 0; --j)
298                 A->Prm[j] = DBL_To_PRM((1.0 - u) * A->Prm1[j] + u * A->Prm2[j]);
299
300         /* We collect the accumulation of the orbits in the 2d int array field. */
301 #ifndef POINTS_HISTORY
302         #ifdef useAccumulator
303                 if (useAccumulator) {
304                         for (i=0;i<A->Width;i++) {
305                                 for (j=0;j<A->Height;j++) {
306                                         A->accMap[i][j] = 0;
307
308                                 }
309                         }
310                 }
311         #endif
312 #endif
313
314         x = y = DBL_To_PRM(.0);
315         for (n = SKIP_FIRST; n; --n) {
316                 (*Iterate) (A, x, y, &xo, &yo);
317                 x = xo + NRAND(8) - 4;
318                 y = yo + NRAND(8) - 4;
319         }
320
321         xmax = 0;
322         xmin = UNIT * 4;
323         ymax = 0;
324         ymin = UNIT * 4;
325         A->Cur_Pt = 0;
326         Buf = A->Buffer2;
327         Lx = (DBL) A->Width / UNIT / 2.2;
328         Ly = (DBL) A->Height / UNIT / 2.2;
329         for (n = A->Max_Pt; n; --n) {
330                 (*Iterate) (A, x, y, &xo, &yo);
331                 #ifdef useAccumulator
332                 if (useAccumulator) {
333                         int mx,my;
334                         mx = (short) ( A->Width*0.1 + A->Width*0.8 * (xo - xmin) / (xmax - xmin) );
335                         my = (short) ( A->Width*0.1 + (A->Height - A->Width*0.2) * (yo - ymin) / (ymax - ymin) );
336                         if (mx>=0 && my>=0 && mx<A->Width && my<A->Height) {
337                                 A->accMap[mx][my]++;
338                         }
339 #ifdef POINTS_HISTORY
340                 /* #define clearOldPoint(i) { if (startedClearing) { field[oldPoints[i].x][oldPoints[i].y]--; } }
341                 #define saveUnplot(X,Y) { clearOldPoint(oldPointsIndex) oldPoints[oldPointsIndex].x = X; oldPoints[oldPointsIndex].y = Y; oldPointsIndex = (oldPointsIndex + 1) % numOldPoints; if (oldPointsIndex==0) { startedClearing=1; } }
342                 saveUnplot(mx,my) */
343                 if (startedClearing) {
344                         int oldX = oldPointsX[oldPointsIndex];
345                         int oldY = oldPointsY[oldPointsIndex];
346                         if (oldX>=0 && oldY>=0 && oldX<A->Width && oldY<A->Height) {
347                                 A->accMap[oldX][oldY]--;
348                         }
349                 }
350                 oldPointsX[oldPointsIndex] = mx;
351                 oldPointsY[oldPointsIndex] = my;
352                 oldPointsIndex = (oldPointsIndex + 1) % numOldPoints;
353                 if (oldPointsIndex==0) { startedClearing=1; }
354 #endif
355                 } else {
356                 #endif
357                         Buf->x = (int) (Lx * (x + DBL_To_PRM(1.1)));
358                         Buf->y = (int) (Ly * (DBL_To_PRM(1.1) - y));
359                         Buf++;
360                         A->Cur_Pt++;
361                 #ifdef useAccumulator
362                 }
363                 #endif
364                 /* (void) fprintf( stderr, "X,Y: %d %d    ", Buf->x, Buf->y ); */
365                 if (xo > xmax)
366                         xmax = xo;
367                 else if (xo < xmin)
368                         xmin = xo;
369                 if (yo > ymax)
370                         ymax = yo;
371                 else if (yo < ymin)
372                         ymin = yo;
373                 x = xo + NRAND(8) - 4;
374                 y = yo + NRAND(8) - 4;
375         }
376
377         MI_IS_DRAWN(mi) = True;
378
379         XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
380
381         #ifdef useAccumulator
382         if (useAccumulator) {
383                 float colorScale;
384                 int col;
385                 #ifdef VARY_SPEED_TO_AVOID_BOREDOM
386                 int pixelCount = 0;
387                 #endif
388                 colorScale = (A->Width*A->Height/640.0/480.0*800000.0/(float)A->Max_Pt*(float)NUM_COLS/256);
389                 if (A->dbuf != None) {
390                         XSetForeground(display, A->dbuf_gc, 0);
391                         XFillRectangle(display, A->dbuf, A->dbuf_gc, 0, 0, A->Width, A->Height);
392                 } else {
393                         XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
394                         XFillRectangle(display, window, gc, 0, 0, A->Width, A->Height);
395                 }
396                 for (i=0;i<A->Width;i++) {
397                         for (j=0;j<A->Height;j++) {
398                                 if (A->accMap[i][j]>0) {
399                                         col = (float)A->accMap[i][j] * colorScale;
400                                         if (col>NUM_COLS-1) {
401                                                 col = NUM_COLS-1;
402                                         }
403                                         #ifdef VARY_SPEED_TO_AVOID_BOREDOM
404                                         if (col>0) {
405                                                 if (col<NUM_COLS-1)  /* we don't count maxxed out pixels */
406                                                         pixelCount++;
407                                         }
408                                         #endif
409                                         if (MI_NPIXELS(mi) < 2)
410                                                 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
411                                         else
412                                                 /*XSetForeground(display, gc, MI_PIXEL(mi, A->Col % MI_NPIXELS(mi)));*/
413                                                 XSetForeground(display, gc, cols[col].pixel);
414                                         if (A->dbuf != None) {
415                                                 XSetForeground(display, A->dbuf_gc, cols[col].pixel);
416                                                 XDrawPoint(display, A->dbuf, A->dbuf_gc, i, j);
417                                         } else {
418                                                 XSetForeground(display, gc, cols[col].pixel);
419                                                 XDrawPoint(display, window, gc, i, j);
420                                         }
421                                 }
422                         }
423                 }
424                 if (A->dbuf != None) {
425                         XCopyArea(display, A->dbuf, window, gc, 0, 0, A->Width, A->Height, 0, 0);
426                 }
427                 #ifdef VARY_SPEED_TO_AVOID_BOREDOM
428                         /* Increaase the rate of change of the parameters if the attractor has become visually boring. */
429                         if ((xmax - xmin < DBL_To_PRM(.2)) && (ymax - ymin < DBL_To_PRM(.2))) {
430                                 A->Speed *= 1.25;
431                         } else if (pixelCount>0 && pixelCount<A->Width*A->Height/1000) {
432                                 A->Speed *= 1.25;  /* A->Count = 1000; */
433                         } else {
434                                 A->Speed = 4; /* reset to normal/default */
435                         }
436                         if (A->Speed > 32)
437                                 A->Speed = 32;
438                         A->Count += A->Speed;
439                         if (A->Count >= 1000) {
440                                 for (i = MAX_PRM - 1; i >= 0; --i)
441                                         A->Prm1[i] = A->Prm2[i];
442                                 Random_Prm(A->Prm2);
443                                 A->Count = 0;
444                         }
445                 #endif
446         } else {
447         #endif
448
449         if (A->dbuf != None) {          /* jwz */
450                 XSetForeground(display, A->dbuf_gc, 0);
451 /* XDrawPoints(display, A->dbuf, A->dbuf_gc, A->Buffer1,
452   Cur_Pt,CoordModeOrigin); */
453                 XFillRectangle(display, A->dbuf, A->dbuf_gc, 0, 0, A->Width, A->Height);
454         } else
455                 XDrawPoints(display, window, gc, A->Buffer1, Cur_Pt, CoordModeOrigin);
456
457         if (MI_NPIXELS(mi) < 2)
458                 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
459         else
460                 XSetForeground(display, gc, MI_PIXEL(mi, A->Col % MI_NPIXELS(mi)));
461
462         if (A->dbuf != None) {
463                 XSetForeground(display, A->dbuf_gc, 1);
464                 XDrawPoints(display, A->dbuf, A->dbuf_gc, A->Buffer2, A->Cur_Pt,
465                             CoordModeOrigin);
466                 XCopyPlane(display, A->dbuf, window, gc, 0, 0, A->Width, A->Height, 0, 0, 1);
467         } else
468                 XDrawPoints(display, window, gc, A->Buffer2, A->Cur_Pt, CoordModeOrigin);
469
470         #ifdef useAccumulator
471         }
472         #endif
473
474         Buf = A->Buffer1;
475         A->Buffer1 = A->Buffer2;
476         A->Buffer2 = Buf;
477
478         if ((xmax - xmin < DBL_To_PRM(.2)) && (ymax - ymin < DBL_To_PRM(.2)))
479                 A->Count += 4 * A->Speed;
480         else
481                 A->Count += A->Speed;
482         if (A->Count >= 1000) {
483                 for (i = MAX_PRM - 1; i >= 0; --i)
484                         A->Prm1[i] = A->Prm2[i];
485                 Random_Prm(A->Prm2);
486                 A->Count = 0;
487         }
488         A->Col++;
489     mi->recursion_depth = A->Count;
490 }
491
492
493 /***************************************************************/
494
495 ENTRYPOINT void
496 init_strange(ModeInfo * mi)
497 {
498         Display    *display = MI_DISPLAY(mi);
499         Window      window = MI_WINDOW(mi);
500 #ifndef NO_DBUF
501         GC          gc = MI_GC(mi);
502 #endif
503         ATTRACTOR  *Attractor;
504
505 #ifdef POINTS_HISTORY
506         startedClearing=0;
507         oldPointsIndex=0;
508 #endif
509
510         if (Root == NULL) {
511                 if ((Root = (ATTRACTOR *) calloc(MI_NUM_SCREENS(mi),
512                                 sizeof (ATTRACTOR))) == NULL)
513                         return;
514         }
515         Attractor = &Root[MI_SCREEN(mi)];
516
517         if (Attractor->Fold == NULL) {
518                 int         i;
519
520                 if ((Attractor->Fold = (PRM *) calloc(UNIT2 + 1,
521                                 sizeof (PRM))) == NULL) {
522                         free_strange(display, Attractor);
523                         return;
524                 }
525                 for (i = 0; i <= UNIT2; ++i) {
526                         DBL         x;
527
528                         /* x = ( DBL )(i)/UNIT2; */
529                         /* x = sin( M_PI/2.0*x ); */
530                         /* x = sqrt( x ); */
531                         /* x = x*x; */
532                         /* x = x*(1.0-x)*4.0; */
533                         x = (DBL) (i) / UNIT;
534                         x = sin(x);
535                         Attractor->Fold[i] = DBL_To_PRM(x);
536                 }
537         }
538
539         Attractor->Max_Pt = points;
540
541         if (Attractor->Buffer1 == NULL)
542                 if ((Attractor->Buffer1 = (XPoint *) calloc(Attractor->Max_Pt,
543                                 sizeof (XPoint))) == NULL) {
544                         free_strange(display, Attractor);
545                         return;
546                 }
547         if (Attractor->Buffer2 == NULL)
548                 if ((Attractor->Buffer2 = (XPoint *) calloc(Attractor->Max_Pt,
549                                 sizeof (XPoint))) == NULL) {
550                         free_strange(display, Attractor);
551                         return;
552                 }
553
554         Attractor->Width = MI_WIDTH(mi);
555         Attractor->Height = MI_HEIGHT(mi);
556         Attractor->Cur_Pt = 0;
557         Attractor->Count = 0;
558         Attractor->Col = NRAND(MI_NPIXELS(mi));
559         Attractor->Speed = 4;
560
561         Attractor->Iterate = Funcs[NRAND(2)];
562         Random_Prm(Attractor->Prm1);
563         Random_Prm(Attractor->Prm2);
564 #ifndef NO_DBUF
565         if (Attractor->dbuf != None)
566                 XFreePixmap(display, Attractor->dbuf);
567 #ifdef useAccumulator
568 #define colorDepth ( useAccumulator ? MI_DEPTH(mi) : 1 )
569 #else
570 #define colorDepth 1
571 #endif
572         Attractor->dbuf = XCreatePixmap(display, window,
573              Attractor->Width, Attractor->Height, colorDepth);
574         /* Allocation checked */
575         if (Attractor->dbuf != None) {
576                 XGCValues   gcv;
577
578                 gcv.foreground = 0;
579                 gcv.background = 0;
580 #ifndef HAVE_COCOA
581                 gcv.graphics_exposures = False;
582 #endif /* HAVE_COCOA */
583                 gcv.function = GXcopy;
584
585                 if (Attractor->dbuf_gc != None)
586                         XFreeGC(display, Attractor->dbuf_gc);
587
588                 if ((Attractor->dbuf_gc = XCreateGC(display, Attractor->dbuf,
589 #ifndef HAVE_COCOA
590                                 GCGraphicsExposures |
591 #endif /* HAVE_COCOA */
592                                GCFunction | GCForeground | GCBackground,
593                                 &gcv)) == None) {
594                         XFreePixmap(display, Attractor->dbuf);
595                         Attractor->dbuf = None;
596                 } else {
597                         XFillRectangle(display, Attractor->dbuf, Attractor->dbuf_gc,
598                                 0, 0, Attractor->Width, Attractor->Height);
599                         XSetBackground(display, gc, MI_BLACK_PIXEL(mi));
600                         XSetFunction(display, gc, GXcopy);
601                 }
602         }
603 #endif
604
605
606 #ifdef useAccumulator
607         #define A Attractor
608         if (useAccumulator) {
609                 XWindowAttributes xgwa;
610                 int i,j;
611                 XGetWindowAttributes (display, window, &xgwa);
612                 /* cmap = xgwa.colormap; */
613                 /* cmap = XCreateColormap(display, window, MI_VISUAL(mi), AllocAll); */
614                 Attractor->accMap = (int**)calloc(Attractor->Width,sizeof(int*));
615                 for (i=0;i<Attractor->Width;i++) {
616                         Attractor->accMap[i] = (int*)calloc(Attractor->Height,sizeof(int));
617                         for (j=0;j<Attractor->Height;j++) {
618                                 Attractor->accMap[i][j] = 0;
619                         }
620                 }
621 #ifdef POINTS_HISTORY
622                 numOldPoints = A->Max_Pt * MERGE_FRAMES;
623                 oldPointsX = (int*)calloc(numOldPoints,sizeof(int));
624                 oldPointsY = (int*)calloc(numOldPoints,sizeof(int));
625 #endif
626                 cols = (XColor*)calloc(NUM_COLS,sizeof(XColor));
627                 for (i=0;i<NUM_COLS;i++) {
628                         float li;
629                         #define MINBLUE 1
630                         #define FULLBLUE 128
631                         li = MINBLUE + (255.0-MINBLUE) * log(1.0 + ACC_GAMMA*(float)i/NUM_COLS) / log(1.0 + ACC_GAMMA);
632                         if (li<FULLBLUE) {
633                                 cols[i].red = 0;
634                                 cols[i].green = 0;
635                                 cols[i].blue = 65536*li/FULLBLUE;
636                         } else {
637                                 cols[i].red = 65536*(li-FULLBLUE)/(256-FULLBLUE);
638                                 cols[i].green = 65536*(li-FULLBLUE)/(256-FULLBLUE);
639                                 cols[i].blue = 65535;
640                         }
641                         XAllocColor (display, xgwa.colormap, &cols[i]);
642                         /*
643                         if (!XAllocColor(MI_DISPLAY(mi), cmap, &cols[i])) {
644                         if (!XAllocColor(display, cmap, &cols[i])) {
645                                 cols[i].pixel = WhitePixel (display, DefaultScreen (display));
646                                 cols[i].red = cols[i].green = cols[i].blue = 0xFFFF;
647                         }
648                         */
649                 }
650                 /*
651                 XSetWindowColormap(display, window, cmap);
652                 (void) XSetWMColormapWindows(display, window, &window, 1);
653                 XInstallColormap(display, cmap);
654                 XStoreColors(display, cmap, cols, 256);
655                 */
656         }
657         #undef A
658 #endif
659         MI_CLEARWINDOW(mi);
660
661         /* Do not want any exposure events from XCopyPlane */
662         XSetGraphicsExposures(display, MI_GC(mi), False);
663 }
664
665 ENTRYPOINT void
666 reshape_strange(ModeInfo * mi, int width, int height)
667 {
668   XClearWindow (MI_DISPLAY (mi), MI_WINDOW(mi));
669   init_strange (mi);
670 }
671
672 /***************************************************************/
673
674 ENTRYPOINT void
675 release_strange(ModeInfo * mi)
676 {
677         if (Root != NULL) {
678                 int         screen;
679
680 #ifdef useAccumulator
681                 int i;
682                 (void) free((void *) cols);
683                 for (i=0;i<Root->Width;i++) {
684                         (void) free((void *) Root->accMap[i]);
685                 }
686                 (void) free((void *) Root->accMap);
687 #endif
688 #ifdef POINTS_HISTORY
689                 free(oldPointsX);
690                 free(oldPointsY);
691 #endif
692                 for (screen = 0; screen < MI_NUM_SCREENS(mi); ++screen)
693                         free_strange(MI_DISPLAY(mi), &Root[screen]);
694                 (void) free((void *) Root);
695                 Root = (ATTRACTOR *) NULL; 
696         }
697 }
698
699 ENTRYPOINT Bool
700 strange_handle_event (ModeInfo *mi, XEvent *event)
701 {
702   if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
703     {
704       reshape_strange (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
705       return True;
706     }
707   return False;
708 }
709
710
711 XSCREENSAVER_MODULE ("Strange", strange)
712
713 #endif /* MODE_strange */