1 /* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997, 1998, 2004
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>
23 #include "screenhack.h"
26 #define debug printf("File:%s Line:%d\n", __FILE__, __LINE__ );
29 int SCREEN_X, SCREEN_Y;
37 int center_x, center_y;
40 typedef struct STARLINE{
45 typedef struct RGBHANDLE{
47 unsigned short rwant, gwant, bwant;
51 typedef struct COLORCHANGER{
57 /* RGBHandle handle_begin, handle_end; */
60 typedef struct WORMHOLE{
64 double virtualx, virtualy;
72 color_changer changer;
74 int num_stars; /* top of array we are using */
79 inline int rnd( int q ){
85 static int gang( int x1, int y1, int x2, int y2 ){
102 tang = (int)(0.5+atan2( -(y2-y1), x2 - x1 ) * 180.0 / M_PI );
110 void blend_palette( XColor * pal, int max, XColor * sc, XColor * ec ) {
115 int sc_g = sc->green;
119 int ec_g = ec->green;
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 ); */
137 static void initHandle( RGBHandle * handle ){
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 );
149 static void initXColor( XColor * color ){
150 color->red = rnd( 50000 ) + 10000;
151 color->blue = rnd( 50000 ) + 10000;
152 color->green = rnd( 50000 ) + 10000;
155 static void initColorChanger( color_changer * ch, Display * display, Colormap * cmap ){
159 XColor old_color, new_color;
161 ch->shade_max = 2048;
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 );
169 initXColor( &old_color );
170 initXColor( &new_color );
172 for ( q = 0; q < ch->shade_max; q += ch->shade_use ){
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;
179 initXColor( &new_color );
182 for ( q = 0; q < ch->shade_max; q++ )
183 XAllocColor( display, *cmap, &( ch->shade[q] ) );
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 );
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] ) );
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 ) )
207 else *change = rnd(max-min) + min;
213 static void moveRGBHandle( RGBHandle * handle, int min, int max ){
215 unsigned short * want[ 3 ];
218 want[0] = &(handle->rwant);
219 want[1] = &(handle->gwant);
220 want[2] = &(handle->bwant);
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 );
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;
232 for ( q = 0; q < 3; q++ )
233 cy = cy && *(want[q]) == 0;
234 if ( cy ) *(want[rnd(3)]) = rnd(max-min)+min;
236 if ( rnd( 30 ) == rnd( 30 ) )
237 *(want[rnd(3)]) = rnd(max-min)+min;
242 static void moveColorChanger( color_changer * ch, Display * display, Colormap * cmap ){
246 if ( ch->min < ch->min_want ){
250 if ( ch->min > ch->min_want ) {
254 if ( ch->min == ch->min_want )
255 ch->min_want = rnd( ch->shade_max - ch->shade_use );
258 for ( q = 0; q < ch->max; q++ )
259 XFreeColors( display, *cmap, &( ch->shade[q].pixel ), 1, 0 );
261 moveRGBHandle( &( ch->handle_begin ), 5000, 65500 );
262 moveRGBHandle( &( ch->handle_end ), 5000, 65500 );
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] ) );
271 static void destroyColorChanger( color_changer * ch, Display * display, Colormap * cmap ){
273 for ( q = 0; q < ch->max; q++ )
274 XFreeColors( display, *cmap, &( ch->shade[q].pixel ), 1, 0 );
278 static void resizeWormhole( wormhole * worm, Display * display, Window * win ){
280 XWindowAttributes attr;
283 XGetWindowAttributes( display, *win, &attr );
285 cmap = attr.colormap;
287 SCREEN_X = attr.width;
288 SCREEN_Y = attr.height;
290 XFreePixmap( display, worm->work );
291 worm->work = XCreatePixmap( display, *win, SCREEN_X, SCREEN_Y, attr.depth );
295 static void initWormhole( wormhole * worm, Display * display, Window * win ){
298 XWindowAttributes attr;
301 XGetWindowAttributes( display, *win, &attr );
303 cmap = attr.colormap;
305 SCREEN_X = attr.width;
306 SCREEN_Y = attr.height;
308 worm->work = XCreatePixmap( display, *win, SCREEN_X, SCREEN_Y, attr.depth );
310 worm->diameter = rnd( 10 ) + 15;
311 worm->diameter_change = rnd( 10 ) + 15;
312 /* worm->actualx = rnd( attr.width );
313 worm->actualy = rnd( attr.height ); */
314 worm->actualx = attr.width / 2;
315 worm->actualy = attr.height / 2;
316 worm->virtualx = worm->actualx;
317 worm->virtualy = worm->actualy;
318 worm->speed = (float)SCREEN_X / 180.0;
319 /* z_speed = SCREEN_X / 120; */
321 worm->addStar = make_stars;
322 worm->want_x = rnd( attr.width - 50 ) + 25;
323 worm->want_y = rnd( attr.height - 50 ) + 25;
324 worm->want_ang = gang( worm->actualx, worm->actualy, worm->want_x, worm->want_y );
325 worm->ang = worm->want_ang;
328 worm->black.green = 0;
329 worm->black.blue = 0;
330 XAllocColor( display, cmap, &worm->black );
331 initColorChanger( &(worm->changer), display, &cmap );
333 worm->num_stars = 64;
334 worm->stars = (starline **)malloc( sizeof(starline *) * worm->num_stars );
335 for ( i = 0; i < worm->num_stars; i++ )
336 worm->stars[i] = NULL;
340 static void destroyWormhole( wormhole * worm, Display * display, Colormap * cmap ){
341 destroyColorChanger( &(worm->changer), display, cmap );
342 XFreePixmap( display, worm->work );
346 static double Cos( int a ){
347 return cos( a * 180.0 / M_PI );
350 static double Sine( int a ){
351 return sin( a * 180.0 / M_PI );
356 static void calcStar( star * st ){
357 if ( st->center_x == 0 || st->center_y == 0 ){
362 st->calc_x = (st->x << 10) / st->center_x;
363 st->calc_y = (st->y << 10) / st->center_y;
365 st->calc_x = (st->x << 10 ) / st->Z + st->center_x;
366 st->calc_y = (st->y << 10 ) / st->Z + st->center_y;
370 static void initStar( star * st, int Z, int ang, wormhole * worm ){
372 st->x = Cos( ang ) * worm->diameter;
373 st->y = Sine( ang ) * worm->diameter;
374 st->center_x = worm->actualx;
375 st->center_y = worm->actualy;
381 static void addStar( wormhole * worm ){
388 star_new = (starline *)malloc( sizeof( starline ) );
390 initStar( &star_new->begin, worm->max_Z, ang, worm );
391 initStar( &star_new->end, worm->max_Z+rnd(6)+4, ang, worm );
393 for ( q = 0; q < worm->num_stars; q++ ){
394 if ( worm->stars[q] == NULL ){
395 worm->stars[q] = star_new;
400 old_stars = worm->num_stars;
401 worm->num_stars = worm->num_stars << 1;
402 xstars = (starline **)malloc( sizeof(starline *) * worm->num_stars );
403 for ( q = 0; q < worm->num_stars; q++ )
406 for ( q = 0; q < old_stars; q++ )
407 if ( worm->stars[q] != NULL ) xstars[q] = worm->stars[q];
409 worm->stars = xstars;
411 worm->stars[ old_stars ] = star_new;
415 static int moveStar( starline * stl ){
417 stl->begin.Z -= z_speed;
418 stl->end.Z -= z_speed;
420 calcStar( &stl->begin );
421 calcStar( &stl->end );
423 return ( stl->begin.Z <= 0 || stl->end.Z <= 0 );
427 static int dist( int x1, int y1, int x2, int y2 ){
431 return (int)sqrt( xs*xs + ys*ys );
434 static void moveWormhole( wormhole * worm, Display * display, Colormap * cmap ){
438 /* int x1, y1, x2, y2; */
441 dx = Cos( worm->ang ) * worm->speed;
442 dy = Sine( worm->ang ) * worm->speed;
444 worm->virtualx += dx;
445 worm->virtualy += dy;
446 worm->actualx = (int)worm->virtualx;
447 worm->actualy = (int)worm->virtualy;
451 if ( worm->spiral % 5 == 0 )
452 worm->ang = (worm->ang + 1 ) % 360;
454 if ( worm->spiral <= 0 ) find = 1;
458 if ( dist( worm->actualx, worm->actualy, worm->want_x, worm->want_y ) < 20 )
461 if ( rnd( 20 ) == rnd( 20 ) )
464 if ( worm->actualx < min_dist ){
465 worm->actualx = min_dist;
466 worm->virtualx = worm->actualx;
469 if ( worm->actualy < min_dist ){
470 worm->actualy = min_dist;
471 worm->virtualy = worm->actualy;
474 if ( worm->actualx > SCREEN_X - min_dist ){
475 worm->actualx = SCREEN_X - min_dist;
476 worm->virtualx = worm->actualx;
479 if ( worm->actualy > SCREEN_Y - min_dist ){
480 worm->actualy = SCREEN_Y - min_dist;
481 worm->virtualy = worm->actualy;
485 if ( rnd( 500 ) == rnd( 500 ) ) worm->spiral = rnd( 30 ) + 50;
489 worm->want_x = rnd( SCREEN_X - min_dist * 2 ) + min_dist;
490 worm->want_y = rnd( SCREEN_Y - min_dist * 2 ) + min_dist;
491 worm->ang = gang( worm->actualx, worm->actualy, worm->want_x, worm->want_y );
495 /* worm->ang = ( worm->ang + 360 + rnd( 30 ) - 15 ) % 360; */
498 if ( worm->ang < worm->want_ang ) worm->ang++;
499 if ( worm->ang > worm->want_ang ) worm->ang--;
500 if ( worm->ang == worm->want_ang && rnd( 3 ) == rnd( 3 ) ) worm->want_ang = rnd( 360 );
504 if ( rnd( 25 ) == rnd( 25 ) ){
507 x2 = SCREEN_X / 2 + rnd( 20 ) - 10;
508 y2 = SCREEN_Y / 2 + rnd( 20 ) - 10;
509 worm->want_ang = gang(x1,y1,x2,y2);
514 if ( worm->actualx < min_dist || worm->actualx > SCREEN_X - min_dist || worm->actualy < min_dist || worm->actualy > SCREEN_Y - min_dist ){
517 x2 = SCREEN_X / 2 + rnd( 20 ) - 10;
518 y2 = SCREEN_Y / 2 + rnd( 20 ) - 10;
519 / * worm->ang = gang( worm->actualx, worm->actualy, SCREEN_X/2+rnd(20)-10, SCREEN_Y/2+rnd(20)-10 ); * /
520 worm->ang = gang( x1, y1, x2, y2 );
521 worm->want_ang = worm->ang;
522 / * printf("Angle = %d\n", worm->ang ); * /
524 if ( worm->actualx < min_dist )
525 worm->actualx = min_dist;
526 if ( worm->actualx > SCREEN_X - min_dist )
527 worm->actualx = SCREEN_X - min_dist;
528 if ( worm->actualy < min_dist )
529 worm->actualy = min_dist;
530 if ( worm->actualy > SCREEN_Y - min_dist )
531 worm->actualy = SCREEN_Y - min_dist;
532 worm->virtualx = worm->actualx;
533 worm->virtualy = worm->actualy;
537 for ( q = 0; q < worm->num_stars; q++ ){
538 if ( worm->stars[q] != NULL ){
539 if ( moveStar( worm->stars[q] ) ){
540 free( worm->stars[q] );
541 worm->stars[q] = NULL;
546 moveColorChanger( &worm->changer, display, cmap );
548 if ( worm->diameter < worm->diameter_change )
550 if ( worm->diameter > worm->diameter_change )
552 if ( rnd( 30 ) == rnd( 30 ) )
553 worm->diameter_change = rnd( 35 ) + 5;
555 for ( q = 0; q < worm->addStar; q++ )
560 static XColor * getColorShade( color_changer * ch ){
561 return ch->shade + ch->min;
564 static void drawWormhole( Display * display, Window * win, GC * gc, wormhole * worm ){
572 for ( i = 0; i < worm->num_stars; i++ )
573 if ( worm->stars[i] != NULL ){
575 current = worm->stars[i];
576 z = current->begin.Z;
578 color = z * worm->changer.shade_use / worm->max_Z;
579 shade = getColorShade( &worm->changer );
580 /* xcol = &worm->changer.shade[ color ]; */
581 xcol = &shade[ color ];
583 XSetForeground( display, *gc, xcol->pixel );
584 /* XDrawLine( display, *win, *gc, current->begin.calc_x, current->begin.calc_y, current->end.calc_x, current->end.calc_y ); */
585 XDrawLine( display, worm->work, *gc, current->begin.calc_x, current->begin.calc_y, current->end.calc_x, current->end.calc_y );
588 XCopyArea( display, worm->work, *win, *gc, 0, 0, SCREEN_X, SCREEN_Y, 0, 0 );
589 XSetForeground( display, *gc, worm->black.pixel );
590 XFillRectangle( display, worm->work, *gc, 0, 0, SCREEN_X, SCREEN_Y );
595 static void eraseWormhole( Display * display, Window * win, GC * gc, wormhole * worm ){
599 for ( i = 0; i < worm->num_stars; i++ )
600 if ( worm->stars[i] != NULL ){
602 current = worm->stars[i];
603 XSetForeground( display, *gc, xcol->pixel );
604 XDrawLine( display, *win, *gc, current->begin.calc_x, current->begin.calc_y, current->end.calc_x, current->end.calc_y );
609 char *progclass = "Wormhole";
611 char *defaults [] = {
612 ".background: Black",
613 ".foreground: #E9967A",
620 XrmOptionDescRec options [] = {
621 { "-delay", ".delay", XrmoptionSepArg, 0 },
622 { "-zspeed", ".zspeed", XrmoptionSepArg, 0 },
623 { "-stars", ".stars", XrmoptionSepArg, 0 },
627 static int handle_event( Display * display, XEvent * event ){
629 if ( event->xany.type == ConfigureNotify ){
632 screenhack_handle_event( display, event );
637 void screenhack (Display *dpy, Window window) {
642 XWindowAttributes attr;
645 int delay = get_integer_resource( "delay", "Integer" );
646 make_stars = get_integer_resource( "stars", "Integer" );
647 z_speed = get_integer_resource( "zspeed", "Integer" );
649 initWormhole( &worm, dpy, &window );
653 gc = XCreateGC( dpy, window, GCForeground, &gcv );
654 XGetWindowAttributes( dpy, window, &attr );
655 cmap = attr.colormap;
659 moveWormhole( &worm, dpy, &cmap );
660 drawWormhole( dpy, &window, &gc, &worm );
663 /* handle my own friggin events. mmmlaaaa */
664 while ( XPending(dpy) ){
666 XNextEvent( dpy, &event );
667 if ( handle_event( dpy, &event ) == 1 ){
668 resizeWormhole( &worm, dpy, &window );
671 /* screenhack_handle_events (dpy); */
673 if (delay) usleep (delay);
674 /* eraseWormhole( dpy, &window, &gc, &worm ); */
676 XSetForeground( dpy, gc, worm.black.pixel );
677 XFillRectangle( dpy, window, gc, 0, 0, SCREEN_X, SCREEN_Y );
681 destroyWormhole( &worm, dpy, &cmap );