/* xscreensaver, Copyright (c) 1992-2011 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ /* wormhole: * Animation of moving through a wormhole. Based on my own code written * a few years ago. * author: Jon Rafkind * date: 1/19/04 */ #include #include "screenhack.h" #ifndef debug #define debug printf("File:%s Line:%d\n", __FILE__, __LINE__ ); #endif typedef struct STAR{ int x, y; int calc_x, calc_y; int Z; int center_x, center_y; } star; typedef struct STARLINE{ star begin, end; } starline; /* typedef struct RGBHANDLE{ XColor mine; unsigned short rwant, gwant, bwant; } RGBHandle; */ typedef struct COLORCHANGER{ XColor * shade; int min, max; int shade_use; int shade_max; int min_want; /* RGBHandle handle_begin, handle_end; */ } color_changer; typedef struct WORMHOLE{ int diameter; int diameter_change; int actualx, actualy; double virtualx, virtualy; double speed; int ang; int want_ang; int want_x, want_y; int max_Z; int addStar; int spiral; color_changer changer; starline ** stars; int num_stars; /* top of array we are using */ XColor black; Pixmap work; } wormhole; struct state { Display *dpy; Window window; int SCREEN_X, SCREEN_Y; int z_speed; int make_stars; int delay; wormhole worm; GC gc; Colormap cmap; }; /*inline*/ static int rnd( int q ) { if (q < 1) q = 1; return random() % q; } static int gang( int x1, int y1, int x2, int y2 ) { int tang = 0; if ( x1 == x2 ) { if ( y1 < y2 ) tang = 90; else tang = 270; } else if ( y1 == y2 ) { if ( x1 < x2 ) tang = 0; else tang = 180; } else { tang = (int)(0.5+atan2( -(y2-y1), x2 - x1 ) * 180.0 / M_PI ); } while ( tang < 0 ) tang += 360; return tang % 360; } static void blend_palette( XColor * pal, int max, XColor * sc, XColor * ec ) { int q; int sc_r = sc->red; int sc_g = sc->green; int sc_b = sc->blue; int ec_r = ec->red; int ec_g = ec->green; int ec_b = ec->blue; for ( q = 0; q < max; q++ ) { float j = (float)( q ) / (float)( max ); int f_r = (int)( 0.5 + (float)( sc_r ) + (float)( ec_r-sc_r ) * j ); int f_g = (int)( 0.5 + (float)( sc_g ) + (float)( ec_g-sc_g ) * j ); int f_b = (int)( 0.5 + (float)( sc_b ) + (float)( ec_b-sc_b ) * j ); /* pal[q] = makecol( f_r, f_g, f_b ); */ pal[q].red = f_r; pal[q].blue = f_b; pal[q].green = f_g; } } /* static void initHandle( RGBHandle * handle ) { handle->mine.red = rnd( 65536 ); handle->mine.green = rnd( 65536 ); handle->mine.blue = rnd( 65536 ); handle->rwant = rnd( 65536 ); handle->gwant = rnd( 65536 ); handle->bwant = rnd( 65536 ); } */ static void initXColor( XColor * color ) { color->red = rnd( 50000 ) + 10000; color->blue = rnd( 50000 ) + 10000; color->green = rnd( 50000 ) + 10000; } static void initColorChanger( struct state *st, color_changer * ch ) { int q; XColor old_color, new_color; ch->shade_max = 2048; ch->shade_use = 128; ch->min = 0; ch->max = ch->shade_use; ch->min_want = rnd( ch->shade_max - ch->shade_use ); ch->shade = (XColor *)malloc( sizeof(XColor) * ch->shade_max ); memset( ch->shade, 0, sizeof(XColor) * ch->shade_max ); initXColor( &old_color ); initXColor( &new_color ); for ( q = 0; q < ch->shade_max; q += ch->shade_use ){ blend_palette( ch->shade + q, ch->shade_use, &old_color, &new_color ); old_color = new_color; initXColor( &new_color ); } for ( q = 0; q < ch->shade_max; q++ ) XAllocColor( st->dpy, st->cmap, &( ch->shade[q] ) ); /* initHandle( &(ch->handle_begin) ); initHandle( &(ch->handle_end) ); ch->shade = (XColor *)malloc( sizeof(XColor) * MAX_COLORS ); ch->max = MAX_COLORS; memset( ch->shade, 0, sizeof(XColor) * ch->max ); blend_palette( ch->shade, ch->max, &(ch->handle_begin.mine), &(ch->handle_end.mine) ); for ( q = 0; q < ch->max; q++ ) XAllocColor( st->dpy, *cmap, &( ch->shade[q] ) ); */ } /* static void changeColor( unsigned short * col, unsigned short * change, int min, int max ) { int RGB_GO_BLACK = 30; if ( *col < *change ) *col++; if ( *col > *change ) *col--; if ( *col == *change ){ if ( rnd( RGB_GO_BLACK ) == rnd( RGB_GO_BLACK ) ) *change = 0; else *change = rnd(max-min) + min; } } */ /* static void moveRGBHandle( RGBHandle * handle, int min, int max ) { unsigned short * want[ 3 ]; int q; int cy = 0; want[0] = &(handle->rwant); want[1] = &(handle->gwant); want[2] = &(handle->bwant); for ( q = 0; q < 10; q++ ){ changeColor( &( handle->mine.red ), &handle->rwant, min, max ); changeColor( &( handle->mine.green ), &handle->gwant, min, max ); changeColor( &( handle->mine.blue ), &handle->bwant, min, max ); } for ( q = 0; q < 3; q++ ) cy = cy || (*(want[q]) >= min && *(want[q]) <= max); if ( !cy ) *(want[rnd(3)]) = rnd(max-min)+min; cy = 1; for ( q = 0; q < 3; q++ ) cy = cy && *(want[q]) == 0; if ( cy ) *(want[rnd(3)]) = rnd(max-min)+min; if ( rnd( 30 ) == rnd( 30 ) ) *(want[rnd(3)]) = rnd(max-min)+min; } */ static void moveColorChanger( color_changer * ch ) { /* int q; */ if ( ch->min < ch->min_want ){ ch->min++; ch->max++; } if ( ch->min > ch->min_want ) { ch->min--; ch->max--; } if ( ch->min == ch->min_want ) ch->min_want = rnd( ch->shade_max - ch->shade_use ); /* for ( q = 0; q < ch->max; q++ ) XFreeColors( st->dpy, *cmap, &( ch->shade[q].pixel ), 1, 0 ); moveRGBHandle( &( ch->handle_begin ), 5000, 65500 ); moveRGBHandle( &( ch->handle_end ), 5000, 65500 ); blend_palette( ch->shade, ch->max, &(ch->handle_begin.mine), &(ch->handle_end.mine) ); for ( q = 0; q < ch->max; q++ ) XAllocColor( st->dpy, *cmap, &( ch->shade[q] ) ); */ } static void destroyColorChanger( struct state *st, color_changer * ch ) { int q; for ( q = 0; q < ch->max; q++ ) XFreeColors( st->dpy, st->cmap, &( ch->shade[q].pixel ), 1, 0 ); free( ch->shade ); } static void resizeWormhole( struct state *st, wormhole * worm ) { XWindowAttributes attr; XGetWindowAttributes( st->dpy, st->window, &attr ); st->cmap = attr.colormap; st->SCREEN_X = attr.width; st->SCREEN_Y = attr.height; # ifndef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */ XFreePixmap( st->dpy, worm->work ); worm->work = XCreatePixmap( st->dpy, st->window, st->SCREEN_X, st->SCREEN_Y, attr.depth ); # endif } static void initWormhole( struct state *st, wormhole * worm, Display * display, Window win ) { int i; XWindowAttributes attr; XGetWindowAttributes( st->dpy, st->window, &attr ); st->cmap = attr.colormap; st->SCREEN_X = attr.width; st->SCREEN_Y = attr.height; # ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */ worm->work = st->window; # else worm->work = XCreatePixmap( st->dpy, st->window, st->SCREEN_X, st->SCREEN_Y, attr.depth ); # endif worm->diameter = rnd( 10 ) + 15; worm->diameter_change = rnd( 10 ) + 15; /* worm->actualx = rnd( attr.width ); worm->actualy = rnd( attr.height ); */ worm->actualx = attr.width / 2; worm->actualy = attr.height / 2; worm->virtualx = worm->actualx; worm->virtualy = worm->actualy; worm->speed = (float)st->SCREEN_X / 180.0; /* z_speed = SCREEN_X / 120; */ worm->spiral = 0; worm->addStar = st->make_stars; worm->want_x = rnd( attr.width - 50 ) + 25; worm->want_y = rnd( attr.height - 50 ) + 25; worm->want_ang = gang( worm->actualx, worm->actualy, worm->want_x, worm->want_y ); worm->ang = worm->want_ang; worm->max_Z = 600; worm->black.red = 0; worm->black.green = 0; worm->black.blue = 0; XAllocColor( st->dpy, st->cmap, &worm->black ); initColorChanger( st, &(worm->changer) ); worm->num_stars = 64; worm->stars = (starline **)calloc(sizeof(starline *), worm->num_stars); for ( i = 0; i < worm->num_stars; i++ ) { if (worm->stars[i]) free (worm->stars[i]); worm->stars[i] = NULL; } } static void destroyWormhole( struct state *st, wormhole * worm ) { int i; destroyColorChanger( st, &(worm->changer)); if (worm->work != st->window) { XFreePixmap( st->dpy, worm->work ); worm->work = 0; } for (i = 0; i < worm->num_stars; i++) if (worm->stars[i]) free(worm->stars[i]); free(worm->stars); } static double Cos( int a ) { return cos( a * 180.0 / M_PI ); } static double Sine( int a ) { return sin( a * 180.0 / M_PI ); } static void calcStar( star * st ) { if ( st->center_x == 0 || st->center_y == 0 ){ st->Z = 0; return; } if ( st->Z <= 0 ){ st->calc_x = (st->x << 10) / st->center_x; st->calc_y = (st->y << 10) / st->center_y; } else { st->calc_x = (st->x << 10 ) / st->Z + st->center_x; st->calc_y = (st->y << 10 ) / st->Z + st->center_y; } } static void initStar( star * st, int Z, int ang, wormhole * worm ) { st->x = Cos( ang ) * worm->diameter; st->y = Sine( ang ) * worm->diameter; st->center_x = worm->actualx; st->center_y = worm->actualy; st->Z = Z; calcStar( st ); } static void addStar( wormhole * worm ) { starline * star_new; starline ** xstars; int old_stars; int q; int ang; star_new = (starline *)malloc( sizeof( starline ) ); ang = rnd( 360 ); initStar( &star_new->begin, worm->max_Z, ang, worm ); initStar( &star_new->end, worm->max_Z+rnd(6)+4, ang, worm ); for ( q = 0; q < worm->num_stars; q++ ){ if ( worm->stars[q] == NULL ){ worm->stars[q] = star_new; return; } } old_stars = worm->num_stars; worm->num_stars = worm->num_stars << 1; xstars = (starline **)malloc( sizeof(starline *) * worm->num_stars ); for ( q = 0; q < worm->num_stars; q++ ) xstars[q] = NULL; for ( q = 0; q < old_stars; q++ ) if ( worm->stars[q] != NULL ) xstars[q] = worm->stars[q]; free( worm->stars ); worm->stars = xstars; worm->stars[ old_stars ] = star_new; } static int moveStar( struct state *st, starline * stl ) { stl->begin.Z -= st->z_speed; stl->end.Z -= st->z_speed; calcStar( &stl->begin ); calcStar( &stl->end ); return ( stl->begin.Z <= 0 || stl->end.Z <= 0 ); } static int dist( int x1, int y1, int x2, int y2 ) { int xs, ys; xs = x1-x2; ys = y1-y2; return (int)sqrt( xs*xs + ys*ys ); } static void moveWormhole( struct state *st, wormhole * worm ) { int q; double dx, dy; /* int x1, y1, x2, y2; */ int min_dist = 100; int find = 0; dx = Cos( worm->ang ) * worm->speed; dy = Sine( worm->ang ) * worm->speed; worm->virtualx += dx; worm->virtualy += dy; worm->actualx = (int)worm->virtualx; worm->actualy = (int)worm->virtualy; if ( worm->spiral ){ if ( worm->spiral % 5 == 0 ) worm->ang = (worm->ang + 1 ) % 360; worm->spiral--; if ( worm->spiral <= 0 ) find = 1; } else { if ( dist( worm->actualx, worm->actualy, worm->want_x, worm->want_y ) < 20 ) find = 1; if ( rnd( 20 ) == rnd( 20 ) ) find = 1; if ( worm->actualx < min_dist ){ worm->actualx = min_dist; worm->virtualx = worm->actualx; find = 1; } if ( worm->actualy < min_dist ){ worm->actualy = min_dist; worm->virtualy = worm->actualy; find = 1; } if ( worm->actualx > st->SCREEN_X - min_dist ){ worm->actualx = st->SCREEN_X - min_dist; worm->virtualx = worm->actualx; find = 1; } if ( worm->actualy > st->SCREEN_Y - min_dist ){ worm->actualy = st->SCREEN_Y - min_dist; worm->virtualy = worm->actualy; find = 1; } if ( rnd( 500 ) == rnd( 500 ) ) worm->spiral = rnd( 30 ) + 50; } if ( find ){ worm->want_x = rnd( st->SCREEN_X - min_dist * 2 ) + min_dist; worm->want_y = rnd( st->SCREEN_Y - min_dist * 2 ) + min_dist; worm->ang = gang( worm->actualx, worm->actualy, worm->want_x, worm->want_y ); } /* worm->ang = ( worm->ang + 360 + rnd( 30 ) - 15 ) % 360; */ /* if ( worm->ang < worm->want_ang ) worm->ang++; if ( worm->ang > worm->want_ang ) worm->ang--; if ( worm->ang == worm->want_ang && rnd( 3 ) == rnd( 3 ) ) worm->want_ang = rnd( 360 ); */ /* if ( rnd( 25 ) == rnd( 25 ) ){ x1 = worm->actualx; y1 = worm->actualy; x2 = SCREEN_X / 2 + rnd( 20 ) - 10; y2 = SCREEN_Y / 2 + rnd( 20 ) - 10; worm->want_ang = gang(x1,y1,x2,y2); } */ /* if ( worm->actualx < min_dist || worm->actualx > SCREEN_X - min_dist || worm->actualy < min_dist || worm->actualy > SCREEN_Y - min_dist ){ x1 = worm->actualx; y1 = worm->actualy; x2 = SCREEN_X / 2 + rnd( 20 ) - 10; y2 = SCREEN_Y / 2 + rnd( 20 ) - 10; / * worm->ang = gang( worm->actualx, worm->actualy, SCREEN_X/2+rnd(20)-10, SCREEN_Y/2+rnd(20)-10 ); * / worm->ang = gang( x1, y1, x2, y2 ); worm->want_ang = worm->ang; / * printf("Angle = %d\n", worm->ang ); * / if ( worm->actualx < min_dist ) worm->actualx = min_dist; if ( worm->actualx > SCREEN_X - min_dist ) worm->actualx = SCREEN_X - min_dist; if ( worm->actualy < min_dist ) worm->actualy = min_dist; if ( worm->actualy > SCREEN_Y - min_dist ) worm->actualy = SCREEN_Y - min_dist; worm->virtualx = worm->actualx; worm->virtualy = worm->actualy; } */ for ( q = 0; q < worm->num_stars; q++ ){ if ( worm->stars[q] != NULL ){ if ( moveStar( st, worm->stars[q] ) ){ free( worm->stars[q] ); worm->stars[q] = NULL; } } } moveColorChanger( &worm->changer ); if ( worm->diameter < worm->diameter_change ) worm->diameter++; if ( worm->diameter > worm->diameter_change ) worm->diameter--; if ( rnd( 30 ) == rnd( 30 ) ) worm->diameter_change = rnd( 35 ) + 5; for ( q = 0; q < worm->addStar; q++ ) addStar( worm ); } static XColor * getColorShade( color_changer * ch ) { return ch->shade + ch->min; } static void drawWormhole( struct state *st, wormhole * worm ) { int i; int color; int z; starline * current; XColor * xcol; XColor * shade; XSetForeground( st->dpy, st->gc, worm->black.pixel ); XFillRectangle( st->dpy, worm->work, st->gc, 0, 0, st->SCREEN_X, st->SCREEN_Y ); for ( i = 0; i < worm->num_stars; i++ ) if ( worm->stars[i] != NULL ){ current = worm->stars[i]; z = current->begin.Z; color = z * worm->changer.shade_use / worm->max_Z; shade = getColorShade( &worm->changer ); /* xcol = &worm->changer.shade[ color ]; */ xcol = &shade[ color ]; XSetForeground( st->dpy, st->gc, xcol->pixel ); /* XDrawLine( st->dpy, st->window, *gc, current->begin.calc_x, current->begin.calc_y, current->end.calc_x, current->end.calc_y ); */ XDrawLine( st->dpy, worm->work, st->gc, current->begin.calc_x, current->begin.calc_y, current->end.calc_x, current->end.calc_y ); } if (worm->work != st->window) XCopyArea( st->dpy, worm->work, st->window, st->gc, 0, 0, st->SCREEN_X, st->SCREEN_Y, 0, 0 ); } /* static void eraseWormhole( Display * display, Window * st->window, wormhole * worm ){ starline * current; int i; XColor * xcol; for ( i = 0; i < worm->num_stars; i++ ) if ( worm->stars[i] != NULL ){ xcol = &worm->black; current = worm->stars[i]; XSetForeground( st->dpy, *gc, xcol->pixel ); XDrawLine( st->dpy, st->window, *gc, current->begin.calc_x, current->begin.calc_y, current->end.calc_x, current->end.calc_y ); } } */ static void * wormhole_init (Display *dpy, Window window) { struct state *st = (struct state *) calloc (1, sizeof(*st)); XGCValues gcv; XWindowAttributes attr; st->dpy = dpy; st->window = window; st->delay = get_integer_resource(st->dpy, "delay", "Integer" ); st->make_stars = get_integer_resource(st->dpy, "stars", "Integer" ); st->z_speed = get_integer_resource(st->dpy, "zspeed", "Integer" ); initWormhole( st, &st->worm, st->dpy, st->window ); st->gc = XCreateGC( st->dpy, st->window, 0, &gcv ); XGetWindowAttributes( st->dpy, st->window, &attr ); st->cmap = attr.colormap; return st; } static unsigned long wormhole_draw (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; moveWormhole( st, &st->worm ); drawWormhole( st, &st->worm ); return st->delay; } static void wormhole_reshape (Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { struct state *st = (struct state *) closure; resizeWormhole( st, &st->worm ); } static Bool wormhole_event (Display *dpy, Window window, void *closure, XEvent *event) { return False; } static void wormhole_free (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; destroyWormhole (st, &st->worm); XFreeGC (dpy, st->gc); free (st); } static const char *wormhole_defaults [] = { ".lowrez: true", ".background: Black", ".foreground: #E9967A", "*delay: 10000", "*zspeed: 10", "*stars: 20", #ifdef HAVE_MOBILE "*ignoreRotation: True", #endif 0 }; static XrmOptionDescRec wormhole_options [] = { { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-zspeed", ".zspeed", XrmoptionSepArg, 0 }, { "-stars", ".stars", XrmoptionSepArg, 0 }, { 0, 0, 0, 0 } }; XSCREENSAVER_MODULE ("Wormhole", wormhole)