From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / wormhole.c
1 /* xscreensaver, Copyright (c) 1992-2011 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
12 /* wormhole:
13  * Animation of moving through a wormhole. Based on my own code written
14  * a few years ago.
15  * author: Jon Rafkind <jon@rafkind.com>
16  * date: 1/19/04
17  */
18
19 #include <math.h>
20 #include "screenhack.h"
21
22 #ifndef debug
23 #define debug printf("File:%s Line:%d\n", __FILE__, __LINE__ );
24 #endif
25
26 typedef struct STAR{
27         int x, y;
28         int calc_x, calc_y;
29         int Z;
30         int center_x, center_y;
31 } star;
32
33 typedef struct STARLINE{
34         star begin, end;
35 } starline;
36
37 /*
38 typedef struct RGBHANDLE{
39         XColor mine;
40         unsigned short rwant, gwant, bwant;
41 } RGBHandle;
42 */
43
44 typedef struct COLORCHANGER{
45         XColor * shade;
46         int min, max; 
47         int shade_use;
48         int shade_max;
49         int min_want;
50         /* RGBHandle handle_begin, handle_end; */
51 } color_changer;
52
53 typedef struct WORMHOLE{
54         int diameter;
55         int diameter_change;
56         int actualx, actualy;
57         double virtualx, virtualy;
58         double speed;
59         int ang;
60         int want_ang;
61         int want_x, want_y;
62         int max_Z;
63         int addStar;
64         int spiral;
65         color_changer changer;
66         starline ** stars;
67         int num_stars; /* top of array we are using */
68         XColor black;
69         Pixmap work;
70 } wormhole;
71
72 struct state {
73   Display *dpy;
74   Window window;
75
76   int SCREEN_X, SCREEN_Y;
77   int z_speed;
78   int make_stars;
79
80   int delay;
81   wormhole worm;
82   GC gc;
83   Colormap cmap;
84 };
85
86
87 /*inline*/ static int rnd( int q )
88 {
89         if (q < 1) q = 1;
90         return random() % q;
91
92 }
93
94 static int gang( int x1, int y1, int x2, int y2 )
95 {
96
97         int tang = 0;
98         if ( x1 == x2 ) {
99                 if ( y1 < y2 )
100                         tang = 90;
101                 else
102                         tang = 270;
103         } else if ( y1 == y2 ) {
104
105                 if ( x1 < x2 )
106                         tang = 0;
107                 else
108                         tang = 180;
109
110         } else {
111           tang = (int)(0.5+atan2( -(y2-y1), x2 - x1 ) * 180.0 / M_PI );
112         }
113
114         while ( tang < 0 )
115                 tang += 360;
116         return tang % 360;
117
118 }
119
120 static void blend_palette( XColor * pal, int max, XColor * sc, XColor * ec ) 
121 {
122
123         int q;
124
125         int sc_r = sc->red;
126         int sc_g = sc->green;
127         int sc_b = sc->blue;
128
129         int ec_r = ec->red;
130         int ec_g = ec->green;
131         int ec_b = ec->blue;
132
133         for ( q = 0; q < max; q++ ) {
134                 float j = (float)( q ) / (float)( max );
135                 int f_r = (int)( 0.5 + (float)( sc_r ) + (float)( ec_r-sc_r ) * j );
136                 int f_g = (int)( 0.5 + (float)( sc_g ) + (float)( ec_g-sc_g ) * j );
137                 int f_b = (int)( 0.5 + (float)( sc_b ) + (float)( ec_b-sc_b ) * j );
138                 /* pal[q] = makecol( f_r, f_g, f_b ); */
139                 pal[q].red = f_r;
140                 pal[q].blue = f_b;
141                 pal[q].green = f_g; 
142         }
143
144 }
145
146
147 /*
148 static void initHandle( RGBHandle * handle )
149 {
150
151         handle->mine.red = rnd( 65536 );
152         handle->mine.green = rnd( 65536 );
153         handle->mine.blue = rnd( 65536 );
154         handle->rwant = rnd( 65536 );
155         handle->gwant = rnd( 65536 );
156         handle->bwant = rnd( 65536 );
157
158 }
159 */
160
161 static void initXColor( XColor * color )
162 {
163         color->red = rnd( 50000 ) + 10000;
164         color->blue = rnd( 50000 ) + 10000;
165         color->green = rnd( 50000 ) + 10000;
166 }
167
168 static void initColorChanger( struct state *st, color_changer * ch )
169 {
170
171         int q;
172         XColor old_color, new_color;
173
174         ch->shade_max = 2048;
175         ch->shade_use = 128;
176         ch->min = 0;
177         ch->max = ch->shade_use;
178         ch->min_want = rnd( ch->shade_max - ch->shade_use );
179         ch->shade = (XColor *)malloc( sizeof(XColor) * ch->shade_max );
180         memset( ch->shade, 0, sizeof(XColor) * ch->shade_max );
181
182         initXColor( &old_color );
183         initXColor( &new_color );
184         
185         for ( q = 0; q < ch->shade_max; q += ch->shade_use ){
186                 blend_palette( ch->shade + q, ch->shade_use, &old_color, &new_color );
187                 old_color = new_color;
188
189                 initXColor( &new_color );
190         }
191
192         for ( q = 0; q < ch->shade_max; q++ )
193                 XAllocColor( st->dpy, st->cmap, &( ch->shade[q] ) );
194
195         /*
196         initHandle( &(ch->handle_begin) );
197         initHandle( &(ch->handle_end) );
198         ch->shade = (XColor *)malloc( sizeof(XColor) * MAX_COLORS );
199         ch->max = MAX_COLORS;
200         memset( ch->shade, 0, sizeof(XColor) * ch->max );
201
202         blend_palette( ch->shade, ch->max, &(ch->handle_begin.mine), &(ch->handle_end.mine) );
203         for ( q = 0; q < ch->max; q++ )
204                 XAllocColor( st->dpy, *cmap, &( ch->shade[q] ) );
205         */
206
207 }
208
209 /*
210 static void changeColor( unsigned short * col, unsigned short * change, int min, int max )
211 {
212         int RGB_GO_BLACK = 30;
213         if ( *col < *change ) *col++;
214         if ( *col > *change ) *col--;
215         if ( *col == *change ){
216                 if ( rnd( RGB_GO_BLACK ) == rnd( RGB_GO_BLACK ) )
217                         *change = 0;
218                 else    *change = rnd(max-min) + min;
219         }
220 }
221 */
222
223 /*
224 static void moveRGBHandle( RGBHandle * handle, int min, int max )
225 {
226
227         unsigned short * want[ 3 ];
228         int q;
229         int cy = 0;
230         want[0] = &(handle->rwant);
231         want[1] = &(handle->gwant);
232         want[2] = &(handle->bwant);
233
234         for ( q = 0; q < 10; q++ ){
235                 changeColor( &( handle->mine.red ), &handle->rwant, min, max );
236                 changeColor( &( handle->mine.green ), &handle->gwant, min, max );
237                 changeColor( &( handle->mine.blue ), &handle->bwant, min, max );
238         }
239         
240         for ( q = 0; q < 3; q++ )
241                 cy = cy || (*(want[q]) >= min && *(want[q]) <= max);
242         if ( !cy ) *(want[rnd(3)]) = rnd(max-min)+min;
243         cy = 1;
244         for ( q = 0; q < 3; q++ )
245                 cy = cy && *(want[q]) == 0;
246         if ( cy ) *(want[rnd(3)]) = rnd(max-min)+min;
247
248         if ( rnd( 30 ) == rnd( 30 ) )
249                 *(want[rnd(3)]) = rnd(max-min)+min;
250
251 }
252 */
253
254 static void moveColorChanger( color_changer * ch )
255 {
256
257         /* int q; */
258
259         if ( ch->min < ch->min_want ){
260                 ch->min++;
261                 ch->max++;
262         }
263         if ( ch->min > ch->min_want ) {
264                 ch->min--;
265                 ch->max--;
266         }
267         if ( ch->min == ch->min_want )
268                 ch->min_want = rnd( ch->shade_max - ch->shade_use );
269
270         /*
271         for ( q = 0; q < ch->max; q++ )
272                 XFreeColors( st->dpy, *cmap, &( ch->shade[q].pixel ), 1, 0 );
273
274         moveRGBHandle( &( ch->handle_begin ), 5000, 65500 );
275         moveRGBHandle( &( ch->handle_end ), 5000, 65500 );
276         
277         blend_palette( ch->shade, ch->max, &(ch->handle_begin.mine), &(ch->handle_end.mine) );
278         for ( q = 0; q < ch->max; q++ )
279                 XAllocColor( st->dpy, *cmap, &( ch->shade[q] ) );
280         */
281
282 }
283
284 #if 0
285 static void destroyColorChanger( color_changer * ch )
286 {
287         int q;
288         for ( q = 0; q < ch->max; q++ )
289                 XFreeColors( st->dpy, *cmap, &( ch->shade[q].pixel ), 1, 0 );
290         free( ch->shade );
291 }
292 #endif
293
294 static void resizeWormhole( struct state *st, wormhole * worm )
295 {
296         
297         XWindowAttributes attr;
298
299         XGetWindowAttributes( st->dpy, st->window, &attr );
300
301         st->cmap = attr.colormap;
302         
303         st->SCREEN_X = attr.width;
304         st->SCREEN_Y = attr.height;
305
306 # ifndef HAVE_COCOA     /* Don't second-guess Quartz's double-buffering */
307         XFreePixmap( st->dpy, worm->work );
308         worm->work = XCreatePixmap( st->dpy, st->window, st->SCREEN_X, st->SCREEN_Y, attr.depth );
309 # endif
310
311 }
312
313 static void initWormhole( struct state *st, wormhole * worm, Display * display, Window win )
314 {
315         
316         int i;
317         XWindowAttributes attr;
318
319         XGetWindowAttributes( st->dpy, st->window, &attr );
320
321         st->cmap = attr.colormap;
322         
323         st->SCREEN_X = attr.width;
324         st->SCREEN_Y = attr.height;
325
326 # ifdef HAVE_COCOA      /* Don't second-guess Quartz's double-buffering */
327         worm->work = st->window;
328 # else
329         worm->work = XCreatePixmap( st->dpy, st->window, st->SCREEN_X, st->SCREEN_Y, attr.depth );
330 # endif
331
332         worm->diameter = rnd( 10 ) + 15;
333         worm->diameter_change = rnd( 10 ) + 15;
334         /* worm->actualx = rnd( attr.width );
335         worm->actualy = rnd( attr.height ); */
336         worm->actualx = attr.width / 2;
337         worm->actualy = attr.height / 2;
338         worm->virtualx = worm->actualx;
339         worm->virtualy = worm->actualy;
340         worm->speed = (float)st->SCREEN_X / 180.0;
341         /* z_speed = SCREEN_X / 120; */
342         worm->spiral = 0;
343         worm->addStar = st->make_stars;
344         worm->want_x = rnd( attr.width - 50 ) + 25;
345         worm->want_y = rnd( attr.height - 50 ) + 25;
346         worm->want_ang = gang( worm->actualx, worm->actualy, worm->want_x, worm->want_y );
347         worm->ang = worm->want_ang;
348         worm->max_Z = 600;
349         worm->black.red = 0;
350         worm->black.green = 0;
351         worm->black.blue = 0;
352         XAllocColor( st->dpy, st->cmap, &worm->black );
353         initColorChanger( st, &(worm->changer) );
354
355         worm->num_stars = 64;
356         worm->stars = (starline **)malloc( sizeof(starline *) * worm->num_stars );
357         for ( i = 0; i < worm->num_stars; i++ )
358                 worm->stars[i] = NULL;
359
360 }
361
362 #if 0
363 static void destroyWormhole( wormhole * worm )
364 {
365         destroyColorChanger( &(worm->changer), st->dpy, cmap );
366         if (work->work != st->window)
367           XFreePixmap( st->dpy, worm->work );
368         free( worm->stars );
369 }
370 #endif
371
372 static double Cos( int a )
373 {
374         return cos( a * 180.0 / M_PI );
375 }
376
377 static double Sine( int a )
378 {
379         return sin( a * 180.0 / M_PI );
380 }
381
382
383
384 static void calcStar( star * st )
385 {
386         if ( st->center_x == 0 || st->center_y == 0 ){
387                 st->Z = 0;
388                 return;
389         }
390         if ( st->Z <= 0 ){
391                 st->calc_x = (st->x << 10) / st->center_x;
392                 st->calc_y = (st->y << 10) / st->center_y;
393         } else {
394                 st->calc_x = (st->x << 10 ) / st->Z + st->center_x;
395                 st->calc_y = (st->y << 10 ) / st->Z + st->center_y;
396         }
397 }
398
399 static void initStar( star * st, int Z, int ang, wormhole * worm )
400 {
401
402         st->x = Cos( ang ) * worm->diameter;
403         st->y = Sine( ang ) * worm->diameter;
404         st->center_x = worm->actualx;
405         st->center_y = worm->actualy;
406         st->Z = Z;
407         calcStar( st );
408
409 }
410
411 static void addStar( wormhole * worm )
412 {
413
414         starline * star_new;
415         starline ** xstars;
416         int old_stars;
417         int q;
418         int ang;
419         star_new = (starline *)malloc( sizeof( starline ) );
420         ang = rnd( 360 );
421         initStar( &star_new->begin, worm->max_Z, ang, worm );
422         initStar( &star_new->end, worm->max_Z+rnd(6)+4, ang, worm );
423
424         for ( q = 0; q < worm->num_stars; q++ ){
425                 if ( worm->stars[q] == NULL ){
426                         worm->stars[q] = star_new;
427                         return;
428                 }
429         }
430
431         old_stars = worm->num_stars;
432         worm->num_stars = worm->num_stars << 1;
433         xstars = (starline **)malloc( sizeof(starline *) * worm->num_stars );
434         for ( q = 0; q < worm->num_stars; q++ )
435                 xstars[q] = NULL;
436
437         for ( q = 0; q < old_stars; q++ )
438                 if ( worm->stars[q] != NULL ) xstars[q] = worm->stars[q];
439         free( worm->stars );
440         worm->stars = xstars;
441
442         worm->stars[ old_stars ] = star_new;
443
444 }
445
446 static int moveStar( struct state *st, starline * stl )
447 {
448
449         stl->begin.Z -= st->z_speed;    
450         stl->end.Z -= st->z_speed;
451
452         calcStar( &stl->begin );
453         calcStar( &stl->end );
454
455         return ( stl->begin.Z <= 0 || stl->end.Z <= 0 );
456
457
458
459 static int dist( int x1, int y1, int x2, int y2 )
460 {
461         int xs, ys;
462         xs = x1-x2;
463         ys = y1-y2;
464         return (int)sqrt( xs*xs + ys*ys );
465 }
466
467 static void moveWormhole( struct state *st, wormhole * worm )
468 {
469
470         int q;
471         double dx, dy;
472         /* int x1, y1, x2, y2; */
473         int min_dist = 100;
474         int find = 0;
475         dx = Cos( worm->ang ) * worm->speed;
476         dy = Sine( worm->ang ) * worm->speed;
477
478         worm->virtualx += dx;
479         worm->virtualy += dy;
480         worm->actualx = (int)worm->virtualx;
481         worm->actualy = (int)worm->virtualy;
482
483         if ( worm->spiral ){
484
485                 if ( worm->spiral % 5 == 0 )
486                         worm->ang = (worm->ang + 1 ) % 360;
487                 worm->spiral--;
488                 if ( worm->spiral <= 0 ) find = 1;
489
490         } else {
491
492                 if ( dist( worm->actualx, worm->actualy, worm->want_x, worm->want_y ) < 20 )
493                         find = 1;
494
495                 if ( rnd( 20 ) == rnd( 20 ) )
496                         find = 1;
497
498                 if ( worm->actualx < min_dist ){
499                         worm->actualx = min_dist;
500                         worm->virtualx = worm->actualx;
501                         find = 1;
502                 }
503                 if ( worm->actualy < min_dist ){
504                         worm->actualy = min_dist;
505                         worm->virtualy = worm->actualy;
506                         find = 1;
507                 }
508                 if ( worm->actualx > st->SCREEN_X - min_dist ){
509                         worm->actualx = st->SCREEN_X - min_dist;
510                         worm->virtualx = worm->actualx;
511                         find = 1;
512                 }
513                 if ( worm->actualy > st->SCREEN_Y - min_dist ){
514                         worm->actualy = st->SCREEN_Y - min_dist;
515                         worm->virtualy = worm->actualy;
516                         find = 1;
517                 }
518         
519                 if ( rnd( 500 ) == rnd( 500 ) ) worm->spiral = rnd( 30 ) + 50;
520         }
521
522         if ( find ){
523                 worm->want_x = rnd( st->SCREEN_X - min_dist * 2 ) + min_dist;
524                 worm->want_y = rnd( st->SCREEN_Y - min_dist * 2 ) + min_dist;
525                 worm->ang = gang( worm->actualx, worm->actualy, worm->want_x, worm->want_y );
526         }
527
528
529         /* worm->ang = ( worm->ang + 360 + rnd( 30 ) - 15 ) % 360; */
530
531         /*
532         if ( worm->ang < worm->want_ang ) worm->ang++;
533         if ( worm->ang > worm->want_ang ) worm->ang--;
534         if ( worm->ang == worm->want_ang && rnd( 3 ) == rnd( 3 ) ) worm->want_ang = rnd( 360 );
535         */
536
537         /*
538         if ( rnd( 25 ) == rnd( 25 ) ){
539                 x1 = worm->actualx;
540                 y1 = worm->actualy;
541                 x2 = SCREEN_X / 2 + rnd( 20 ) - 10;
542                 y2 = SCREEN_Y / 2 + rnd( 20 ) - 10;
543                 worm->want_ang = gang(x1,y1,x2,y2);
544         }
545         */
546
547         /*
548         if ( worm->actualx < min_dist || worm->actualx > SCREEN_X - min_dist || worm->actualy < min_dist || worm->actualy > SCREEN_Y - min_dist ){
549                 x1 = worm->actualx;
550                 y1 = worm->actualy;
551                 x2 = SCREEN_X / 2 + rnd( 20 ) - 10;
552                 y2 = SCREEN_Y / 2 + rnd( 20 ) - 10;
553                 / * worm->ang = gang( worm->actualx, worm->actualy, SCREEN_X/2+rnd(20)-10, SCREEN_Y/2+rnd(20)-10 ); * /
554                 worm->ang = gang( x1, y1, x2, y2 );
555                 worm->want_ang = worm->ang;
556                 / * printf("Angle = %d\n", worm->ang ); * /
557
558                 if ( worm->actualx < min_dist )
559                         worm->actualx = min_dist;
560                 if ( worm->actualx > SCREEN_X - min_dist )
561                         worm->actualx = SCREEN_X - min_dist;
562                 if ( worm->actualy < min_dist )
563                         worm->actualy = min_dist;
564                 if ( worm->actualy > SCREEN_Y - min_dist )
565                         worm->actualy = SCREEN_Y - min_dist;
566                 worm->virtualx = worm->actualx;
567                 worm->virtualy = worm->actualy;
568         }
569         */
570
571         for ( q = 0; q < worm->num_stars; q++ ){
572                 if ( worm->stars[q] != NULL ){
573                         if ( moveStar( st, worm->stars[q] ) ){
574                                 free( worm->stars[q] );
575                                 worm->stars[q] = NULL;
576                         }
577                 }
578         }
579
580         moveColorChanger( &worm->changer );
581
582         if ( worm->diameter < worm->diameter_change )
583                 worm->diameter++;
584         if ( worm->diameter > worm->diameter_change )
585                 worm->diameter--;
586         if ( rnd( 30 ) == rnd( 30 ) )
587                 worm->diameter_change = rnd( 35 ) + 5;
588
589         for ( q = 0; q < worm->addStar; q++ )
590                 addStar( worm );
591
592 }
593
594 static XColor * getColorShade( color_changer * ch )
595 {
596         return ch->shade + ch->min;
597 }
598
599 static void drawWormhole( struct state *st, wormhole * worm )
600 {
601
602         int i;
603         int color;
604         int z;
605         starline * current;
606         XColor * xcol;
607         XColor * shade;
608
609         XSetForeground( st->dpy, st->gc, worm->black.pixel );
610         XFillRectangle( st->dpy, worm->work, st->gc, 0, 0, st->SCREEN_X, st->SCREEN_Y );
611
612         for ( i = 0; i < worm->num_stars; i++ )
613                 if ( worm->stars[i] != NULL ){
614         
615                         current = worm->stars[i];
616                         z = current->begin.Z;
617
618                         color = z * worm->changer.shade_use / worm->max_Z;
619                         shade = getColorShade( &worm->changer );
620                         /* xcol = &worm->changer.shade[ color ]; */
621                         xcol = &shade[ color ];
622
623                         XSetForeground( st->dpy, st->gc, xcol->pixel );
624                         /* XDrawLine( st->dpy, st->window, *gc, current->begin.calc_x, current->begin.calc_y, current->end.calc_x, current->end.calc_y ); */
625                         XDrawLine( st->dpy, worm->work, st->gc, current->begin.calc_x, current->begin.calc_y, current->end.calc_x, current->end.calc_y );
626
627                 }
628         if (worm->work != st->window)
629           XCopyArea( st->dpy, worm->work, st->window, st->gc, 0, 0, st->SCREEN_X, st->SCREEN_Y, 0, 0 );
630 }
631
632 /*
633 static void eraseWormhole( Display * display, Window * st->window, wormhole * worm ){
634         starline * current;
635         int i;
636         XColor * xcol;
637         for ( i = 0; i < worm->num_stars; i++ )
638                 if ( worm->stars[i] != NULL ){
639                         xcol = &worm->black;
640                         current = worm->stars[i];
641                         XSetForeground( st->dpy, *gc, xcol->pixel );
642                         XDrawLine( st->dpy, st->window, *gc, current->begin.calc_x, current->begin.calc_y, current->end.calc_x, current->end.calc_y );
643                 }
644 }
645 */
646
647
648
649 static void *
650 wormhole_init (Display *dpy, Window window)
651 {
652         struct state *st = (struct state *) calloc (1, sizeof(*st));
653         XGCValues gcv;
654         XWindowAttributes attr;
655
656         st->dpy = dpy;
657         st->window = window;
658         st->delay = get_integer_resource(st->dpy,  "delay", "Integer" );
659         st->make_stars = get_integer_resource(st->dpy,  "stars", "Integer" );
660         st->z_speed = get_integer_resource(st->dpy,  "zspeed", "Integer" );
661
662         initWormhole( st, &st->worm, st->dpy, st->window );
663
664         st->gc = XCreateGC( st->dpy, st->window, 0, &gcv );
665         XGetWindowAttributes( st->dpy, st->window, &attr );
666         st->cmap = attr.colormap;
667
668         return st;
669 }
670
671 static unsigned long
672 wormhole_draw (Display *dpy, Window window, void *closure)
673 {
674   struct state *st = (struct state *) closure;
675
676   moveWormhole( st, &st->worm );
677   drawWormhole( st, &st->worm );
678   return st->delay;
679 }
680
681 static void
682 wormhole_reshape (Display *dpy, Window window, void *closure, 
683                  unsigned int w, unsigned int h)
684 {
685   struct state *st = (struct state *) closure;
686   resizeWormhole( st, &st->worm );
687 }
688
689 static Bool
690 wormhole_event (Display *dpy, Window window, void *closure, XEvent *event)
691 {
692   return False;
693 }
694
695 static void
696 wormhole_free (Display *dpy, Window window, void *closure)
697 {
698   struct state *st = (struct state *) closure;
699   free (st);
700 }
701
702
703
704
705 static const char *wormhole_defaults [] = {
706   ".background: Black",
707   ".foreground: #E9967A",
708   "*delay:      10000",
709   "*zspeed:     10",
710   "*stars:      20",
711 #ifdef USE_IPHONE
712   "*ignoreRotation: True",
713 #endif
714   0
715 };
716
717 static XrmOptionDescRec wormhole_options [] = {
718   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
719   { "-zspeed",          ".zspeed",      XrmoptionSepArg, 0 },
720   { "-stars",           ".stars",       XrmoptionSepArg, 0 },
721   { 0, 0, 0, 0 }
722 };
723
724 XSCREENSAVER_MODULE ("Wormhole", wormhole)