1 /* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997, 1998, 2004, 2006
2 * Jamie Zawinski <jwz@jwz.org>
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
14 * Animation of moving through a wormhole. Based on my own code written
16 * author: Jon Rafkind <jon@rafkind.com>
21 #include "screenhack.h"
24 #define debug printf("File:%s Line:%d\n", __FILE__, __LINE__ );
31 int center_x, center_y;
34 typedef struct STARLINE{
39 typedef struct RGBHANDLE{
41 unsigned short rwant, gwant, bwant;
45 typedef struct COLORCHANGER{
51 /* RGBHandle handle_begin, handle_end; */
54 typedef struct WORMHOLE{
58 double virtualx, virtualy;
66 color_changer changer;
68 int num_stars; /* top of array we are using */
77 int SCREEN_X, SCREEN_Y;
88 /*inline*/ static int rnd( int q )
95 static int gang( int x1, int y1, int x2, int y2 )
113 tang = (int)(0.5+atan2( -(y2-y1), x2 - x1 ) * 180.0 / M_PI );
121 static void blend_palette( XColor * pal, int max, XColor * sc, XColor * ec )
127 int sc_g = sc->green;
131 int ec_g = ec->green;
134 for ( q = 0; q < max; q++ ) {
135 float j = (float)( q ) / (float)( max );
136 int f_r = (int)( 0.5 + (float)( sc_r ) + (float)( ec_r-sc_r ) * j );
137 int f_g = (int)( 0.5 + (float)( sc_g ) + (float)( ec_g-sc_g ) * j );
138 int f_b = (int)( 0.5 + (float)( sc_b ) + (float)( ec_b-sc_b ) * j );
139 /* pal[q] = makecol( f_r, f_g, f_b ); */
149 static void initHandle( RGBHandle * handle )
152 handle->mine.red = rnd( 65536 );
153 handle->mine.green = rnd( 65536 );
154 handle->mine.blue = rnd( 65536 );
155 handle->rwant = rnd( 65536 );
156 handle->gwant = rnd( 65536 );
157 handle->bwant = rnd( 65536 );
162 static void initXColor( XColor * color )
164 color->red = rnd( 50000 ) + 10000;
165 color->blue = rnd( 50000 ) + 10000;
166 color->green = rnd( 50000 ) + 10000;
169 static void initColorChanger( struct state *st, color_changer * ch )
174 XColor old_color, new_color;
176 ch->shade_max = 2048;
179 ch->max = ch->shade_use;
180 ch->min_want = rnd( ch->shade_max - ch->shade_use );
181 ch->shade = (XColor *)malloc( sizeof(XColor) * ch->shade_max );
182 memset( ch->shade, 0, sizeof(XColor) * ch->shade_max );
184 initXColor( &old_color );
185 initXColor( &new_color );
187 for ( q = 0; q < ch->shade_max; q += ch->shade_use ){
189 max = q + ch->shade_use;
190 if ( max >= ch->shade_max ) max = ch->shade_max-1;
191 blend_palette( ch->shade + q, ch->shade_use, &old_color, &new_color );
192 old_color = new_color;
194 initXColor( &new_color );
197 for ( q = 0; q < ch->shade_max; q++ )
198 XAllocColor( st->dpy, st->cmap, &( ch->shade[q] ) );
201 initHandle( &(ch->handle_begin) );
202 initHandle( &(ch->handle_end) );
203 ch->shade = (XColor *)malloc( sizeof(XColor) * MAX_COLORS );
204 ch->max = MAX_COLORS;
205 memset( ch->shade, 0, sizeof(XColor) * ch->max );
207 blend_palette( ch->shade, ch->max, &(ch->handle_begin.mine), &(ch->handle_end.mine) );
208 for ( q = 0; q < ch->max; q++ )
209 XAllocColor( st->dpy, *cmap, &( ch->shade[q] ) );
215 static void changeColor( unsigned short * col, unsigned short * change, int min, int max )
217 int RGB_GO_BLACK = 30;
218 if ( *col < *change ) *col++;
219 if ( *col > *change ) *col--;
220 if ( *col == *change ){
221 if ( rnd( RGB_GO_BLACK ) == rnd( RGB_GO_BLACK ) )
223 else *change = rnd(max-min) + min;
229 static void moveRGBHandle( RGBHandle * handle, int min, int max )
232 unsigned short * want[ 3 ];
235 want[0] = &(handle->rwant);
236 want[1] = &(handle->gwant);
237 want[2] = &(handle->bwant);
239 for ( q = 0; q < 10; q++ ){
240 changeColor( &( handle->mine.red ), &handle->rwant, min, max );
241 changeColor( &( handle->mine.green ), &handle->gwant, min, max );
242 changeColor( &( handle->mine.blue ), &handle->bwant, min, max );
245 for ( q = 0; q < 3; q++ )
246 cy = cy || (*(want[q]) >= min && *(want[q]) <= max);
247 if ( !cy ) *(want[rnd(3)]) = rnd(max-min)+min;
249 for ( q = 0; q < 3; q++ )
250 cy = cy && *(want[q]) == 0;
251 if ( cy ) *(want[rnd(3)]) = rnd(max-min)+min;
253 if ( rnd( 30 ) == rnd( 30 ) )
254 *(want[rnd(3)]) = rnd(max-min)+min;
259 static void moveColorChanger( color_changer * ch )
264 if ( ch->min < ch->min_want ){
268 if ( ch->min > ch->min_want ) {
272 if ( ch->min == ch->min_want )
273 ch->min_want = rnd( ch->shade_max - ch->shade_use );
276 for ( q = 0; q < ch->max; q++ )
277 XFreeColors( st->dpy, *cmap, &( ch->shade[q].pixel ), 1, 0 );
279 moveRGBHandle( &( ch->handle_begin ), 5000, 65500 );
280 moveRGBHandle( &( ch->handle_end ), 5000, 65500 );
282 blend_palette( ch->shade, ch->max, &(ch->handle_begin.mine), &(ch->handle_end.mine) );
283 for ( q = 0; q < ch->max; q++ )
284 XAllocColor( st->dpy, *cmap, &( ch->shade[q] ) );
290 static void destroyColorChanger( color_changer * ch )
293 for ( q = 0; q < ch->max; q++ )
294 XFreeColors( st->dpy, *cmap, &( ch->shade[q].pixel ), 1, 0 );
299 static void resizeWormhole( struct state *st, wormhole * worm )
302 XWindowAttributes attr;
304 XGetWindowAttributes( st->dpy, st->window, &attr );
306 st->cmap = attr.colormap;
308 st->SCREEN_X = attr.width;
309 st->SCREEN_Y = attr.height;
311 # ifndef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
312 XFreePixmap( st->dpy, worm->work );
313 worm->work = XCreatePixmap( st->dpy, st->window, st->SCREEN_X, st->SCREEN_Y, attr.depth );
318 static void initWormhole( struct state *st, wormhole * worm, Display * display, Window win )
322 XWindowAttributes attr;
324 XGetWindowAttributes( st->dpy, st->window, &attr );
326 st->cmap = attr.colormap;
328 st->SCREEN_X = attr.width;
329 st->SCREEN_Y = attr.height;
331 # ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
332 worm->work = st->window;
334 worm->work = XCreatePixmap( st->dpy, st->window, st->SCREEN_X, st->SCREEN_Y, attr.depth );
337 worm->diameter = rnd( 10 ) + 15;
338 worm->diameter_change = rnd( 10 ) + 15;
339 /* worm->actualx = rnd( attr.width );
340 worm->actualy = rnd( attr.height ); */
341 worm->actualx = attr.width / 2;
342 worm->actualy = attr.height / 2;
343 worm->virtualx = worm->actualx;
344 worm->virtualy = worm->actualy;
345 worm->speed = (float)st->SCREEN_X / 180.0;
346 /* z_speed = SCREEN_X / 120; */
348 worm->addStar = st->make_stars;
349 worm->want_x = rnd( attr.width - 50 ) + 25;
350 worm->want_y = rnd( attr.height - 50 ) + 25;
351 worm->want_ang = gang( worm->actualx, worm->actualy, worm->want_x, worm->want_y );
352 worm->ang = worm->want_ang;
355 worm->black.green = 0;
356 worm->black.blue = 0;
357 XAllocColor( st->dpy, st->cmap, &worm->black );
358 initColorChanger( st, &(worm->changer) );
360 worm->num_stars = 64;
361 worm->stars = (starline **)malloc( sizeof(starline *) * worm->num_stars );
362 for ( i = 0; i < worm->num_stars; i++ )
363 worm->stars[i] = NULL;
368 static void destroyWormhole( wormhole * worm )
370 destroyColorChanger( &(worm->changer), st->dpy, cmap );
371 if (work->work != st->window)
372 XFreePixmap( st->dpy, worm->work );
377 static double Cos( int a )
379 return cos( a * 180.0 / M_PI );
382 static double Sine( int a )
384 return sin( a * 180.0 / M_PI );
389 static void calcStar( star * st )
391 if ( st->center_x == 0 || st->center_y == 0 ){
396 st->calc_x = (st->x << 10) / st->center_x;
397 st->calc_y = (st->y << 10) / st->center_y;
399 st->calc_x = (st->x << 10 ) / st->Z + st->center_x;
400 st->calc_y = (st->y << 10 ) / st->Z + st->center_y;
404 static void initStar( star * st, int Z, int ang, wormhole * worm )
407 st->x = Cos( ang ) * worm->diameter;
408 st->y = Sine( ang ) * worm->diameter;
409 st->center_x = worm->actualx;
410 st->center_y = worm->actualy;
416 static void addStar( wormhole * worm )
424 star_new = (starline *)malloc( sizeof( starline ) );
426 initStar( &star_new->begin, worm->max_Z, ang, worm );
427 initStar( &star_new->end, worm->max_Z+rnd(6)+4, ang, worm );
429 for ( q = 0; q < worm->num_stars; q++ ){
430 if ( worm->stars[q] == NULL ){
431 worm->stars[q] = star_new;
436 old_stars = worm->num_stars;
437 worm->num_stars = worm->num_stars << 1;
438 xstars = (starline **)malloc( sizeof(starline *) * worm->num_stars );
439 for ( q = 0; q < worm->num_stars; q++ )
442 for ( q = 0; q < old_stars; q++ )
443 if ( worm->stars[q] != NULL ) xstars[q] = worm->stars[q];
445 worm->stars = xstars;
447 worm->stars[ old_stars ] = star_new;
451 static int moveStar( struct state *st, starline * stl )
454 stl->begin.Z -= st->z_speed;
455 stl->end.Z -= st->z_speed;
457 calcStar( &stl->begin );
458 calcStar( &stl->end );
460 return ( stl->begin.Z <= 0 || stl->end.Z <= 0 );
464 static int dist( int x1, int y1, int x2, int y2 )
469 return (int)sqrt( xs*xs + ys*ys );
472 static void moveWormhole( struct state *st, wormhole * worm )
477 /* int x1, y1, x2, y2; */
480 dx = Cos( worm->ang ) * worm->speed;
481 dy = Sine( worm->ang ) * worm->speed;
483 worm->virtualx += dx;
484 worm->virtualy += dy;
485 worm->actualx = (int)worm->virtualx;
486 worm->actualy = (int)worm->virtualy;
490 if ( worm->spiral % 5 == 0 )
491 worm->ang = (worm->ang + 1 ) % 360;
493 if ( worm->spiral <= 0 ) find = 1;
497 if ( dist( worm->actualx, worm->actualy, worm->want_x, worm->want_y ) < 20 )
500 if ( rnd( 20 ) == rnd( 20 ) )
503 if ( worm->actualx < min_dist ){
504 worm->actualx = min_dist;
505 worm->virtualx = worm->actualx;
508 if ( worm->actualy < min_dist ){
509 worm->actualy = min_dist;
510 worm->virtualy = worm->actualy;
513 if ( worm->actualx > st->SCREEN_X - min_dist ){
514 worm->actualx = st->SCREEN_X - min_dist;
515 worm->virtualx = worm->actualx;
518 if ( worm->actualy > st->SCREEN_Y - min_dist ){
519 worm->actualy = st->SCREEN_Y - min_dist;
520 worm->virtualy = worm->actualy;
524 if ( rnd( 500 ) == rnd( 500 ) ) worm->spiral = rnd( 30 ) + 50;
528 worm->want_x = rnd( st->SCREEN_X - min_dist * 2 ) + min_dist;
529 worm->want_y = rnd( st->SCREEN_Y - min_dist * 2 ) + min_dist;
530 worm->ang = gang( worm->actualx, worm->actualy, worm->want_x, worm->want_y );
534 /* worm->ang = ( worm->ang + 360 + rnd( 30 ) - 15 ) % 360; */
537 if ( worm->ang < worm->want_ang ) worm->ang++;
538 if ( worm->ang > worm->want_ang ) worm->ang--;
539 if ( worm->ang == worm->want_ang && rnd( 3 ) == rnd( 3 ) ) worm->want_ang = rnd( 360 );
543 if ( rnd( 25 ) == rnd( 25 ) ){
546 x2 = SCREEN_X / 2 + rnd( 20 ) - 10;
547 y2 = SCREEN_Y / 2 + rnd( 20 ) - 10;
548 worm->want_ang = gang(x1,y1,x2,y2);
553 if ( worm->actualx < min_dist || worm->actualx > SCREEN_X - min_dist || worm->actualy < min_dist || worm->actualy > SCREEN_Y - min_dist ){
556 x2 = SCREEN_X / 2 + rnd( 20 ) - 10;
557 y2 = SCREEN_Y / 2 + rnd( 20 ) - 10;
558 / * worm->ang = gang( worm->actualx, worm->actualy, SCREEN_X/2+rnd(20)-10, SCREEN_Y/2+rnd(20)-10 ); * /
559 worm->ang = gang( x1, y1, x2, y2 );
560 worm->want_ang = worm->ang;
561 / * printf("Angle = %d\n", worm->ang ); * /
563 if ( worm->actualx < min_dist )
564 worm->actualx = min_dist;
565 if ( worm->actualx > SCREEN_X - min_dist )
566 worm->actualx = SCREEN_X - min_dist;
567 if ( worm->actualy < min_dist )
568 worm->actualy = min_dist;
569 if ( worm->actualy > SCREEN_Y - min_dist )
570 worm->actualy = SCREEN_Y - min_dist;
571 worm->virtualx = worm->actualx;
572 worm->virtualy = worm->actualy;
576 for ( q = 0; q < worm->num_stars; q++ ){
577 if ( worm->stars[q] != NULL ){
578 if ( moveStar( st, worm->stars[q] ) ){
579 free( worm->stars[q] );
580 worm->stars[q] = NULL;
585 moveColorChanger( &worm->changer );
587 if ( worm->diameter < worm->diameter_change )
589 if ( worm->diameter > worm->diameter_change )
591 if ( rnd( 30 ) == rnd( 30 ) )
592 worm->diameter_change = rnd( 35 ) + 5;
594 for ( q = 0; q < worm->addStar; q++ )
599 static XColor * getColorShade( color_changer * ch )
601 return ch->shade + ch->min;
604 static void drawWormhole( struct state *st, wormhole * worm )
614 XSetForeground( st->dpy, st->gc, worm->black.pixel );
615 XFillRectangle( st->dpy, worm->work, st->gc, 0, 0, st->SCREEN_X, st->SCREEN_Y );
617 for ( i = 0; i < worm->num_stars; i++ )
618 if ( worm->stars[i] != NULL ){
620 current = worm->stars[i];
621 z = current->begin.Z;
623 color = z * worm->changer.shade_use / worm->max_Z;
624 shade = getColorShade( &worm->changer );
625 /* xcol = &worm->changer.shade[ color ]; */
626 xcol = &shade[ color ];
628 XSetForeground( st->dpy, st->gc, xcol->pixel );
629 /* XDrawLine( st->dpy, st->window, *gc, current->begin.calc_x, current->begin.calc_y, current->end.calc_x, current->end.calc_y ); */
630 XDrawLine( st->dpy, worm->work, st->gc, current->begin.calc_x, current->begin.calc_y, current->end.calc_x, current->end.calc_y );
633 if (worm->work != st->window)
634 XCopyArea( st->dpy, worm->work, st->window, st->gc, 0, 0, st->SCREEN_X, st->SCREEN_Y, 0, 0 );
638 static void eraseWormhole( Display * display, Window * st->window, wormhole * worm ){
642 for ( i = 0; i < worm->num_stars; i++ )
643 if ( worm->stars[i] != NULL ){
645 current = worm->stars[i];
646 XSetForeground( st->dpy, *gc, xcol->pixel );
647 XDrawLine( st->dpy, st->window, *gc, current->begin.calc_x, current->begin.calc_y, current->end.calc_x, current->end.calc_y );
655 wormhole_init (Display *dpy, Window window)
657 struct state *st = (struct state *) calloc (1, sizeof(*st));
659 XWindowAttributes attr;
663 st->delay = get_integer_resource(st->dpy, "delay", "Integer" );
664 st->make_stars = get_integer_resource(st->dpy, "stars", "Integer" );
665 st->z_speed = get_integer_resource(st->dpy, "zspeed", "Integer" );
667 initWormhole( st, &st->worm, st->dpy, st->window );
669 st->gc = XCreateGC( st->dpy, st->window, 0, &gcv );
670 XGetWindowAttributes( st->dpy, st->window, &attr );
671 st->cmap = attr.colormap;
677 wormhole_draw (Display *dpy, Window window, void *closure)
679 struct state *st = (struct state *) closure;
681 moveWormhole( st, &st->worm );
682 drawWormhole( st, &st->worm );
687 wormhole_reshape (Display *dpy, Window window, void *closure,
688 unsigned int w, unsigned int h)
690 struct state *st = (struct state *) closure;
691 resizeWormhole( st, &st->worm );
695 wormhole_event (Display *dpy, Window window, void *closure, XEvent *event)
701 wormhole_free (Display *dpy, Window window, void *closure)
703 struct state *st = (struct state *) closure;
710 static const char *wormhole_defaults [] = {
711 ".background: Black",
712 ".foreground: #E9967A",
719 static XrmOptionDescRec wormhole_options [] = {
720 { "-delay", ".delay", XrmoptionSepArg, 0 },
721 { "-zspeed", ".zspeed", XrmoptionSepArg, 0 },
722 { "-stars", ".stars", XrmoptionSepArg, 0 },
726 XSCREENSAVER_MODULE ("Wormhole", wormhole)