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