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*/ static 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] ) );
272 static void destroyColorChanger( color_changer * ch, Display * display, Colormap * cmap ){
274 for ( q = 0; q < ch->max; q++ )
275 XFreeColors( display, *cmap, &( ch->shade[q].pixel ), 1, 0 );
280 static void resizeWormhole( wormhole * worm, Display * display, Window * win ){
282 XWindowAttributes attr;
285 XGetWindowAttributes( display, *win, &attr );
287 cmap = attr.colormap;
289 SCREEN_X = attr.width;
290 SCREEN_Y = attr.height;
292 XFreePixmap( display, worm->work );
293 worm->work = XCreatePixmap( display, *win, SCREEN_X, SCREEN_Y, attr.depth );
297 static void initWormhole( wormhole * worm, Display * display, Window * win ){
300 XWindowAttributes attr;
303 XGetWindowAttributes( display, *win, &attr );
305 cmap = attr.colormap;
307 SCREEN_X = attr.width;
308 SCREEN_Y = attr.height;
310 worm->work = XCreatePixmap( display, *win, SCREEN_X, SCREEN_Y, attr.depth );
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; */
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;
330 worm->black.green = 0;
331 worm->black.blue = 0;
332 XAllocColor( display, cmap, &worm->black );
333 initColorChanger( &(worm->changer), display, &cmap );
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;
343 static void destroyWormhole( wormhole * worm, Display * display, Colormap * cmap ){
344 destroyColorChanger( &(worm->changer), display, cmap );
345 XFreePixmap( display, worm->work );
350 static double Cos( int a ){
351 return cos( a * 180.0 / M_PI );
354 static double Sine( int a ){
355 return sin( a * 180.0 / M_PI );
360 static void calcStar( star * st ){
361 if ( st->center_x == 0 || st->center_y == 0 ){
366 st->calc_x = (st->x << 10) / st->center_x;
367 st->calc_y = (st->y << 10) / st->center_y;
369 st->calc_x = (st->x << 10 ) / st->Z + st->center_x;
370 st->calc_y = (st->y << 10 ) / st->Z + st->center_y;
374 static void initStar( star * st, int Z, int ang, wormhole * worm ){
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;
385 static void addStar( wormhole * worm ){
392 star_new = (starline *)malloc( sizeof( starline ) );
394 initStar( &star_new->begin, worm->max_Z, ang, worm );
395 initStar( &star_new->end, worm->max_Z+rnd(6)+4, ang, worm );
397 for ( q = 0; q < worm->num_stars; q++ ){
398 if ( worm->stars[q] == NULL ){
399 worm->stars[q] = star_new;
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++ )
410 for ( q = 0; q < old_stars; q++ )
411 if ( worm->stars[q] != NULL ) xstars[q] = worm->stars[q];
413 worm->stars = xstars;
415 worm->stars[ old_stars ] = star_new;
419 static int moveStar( starline * stl ){
421 stl->begin.Z -= z_speed;
422 stl->end.Z -= z_speed;
424 calcStar( &stl->begin );
425 calcStar( &stl->end );
427 return ( stl->begin.Z <= 0 || stl->end.Z <= 0 );
431 static int dist( int x1, int y1, int x2, int y2 ){
435 return (int)sqrt( xs*xs + ys*ys );
438 static void moveWormhole( wormhole * worm, Display * display, Colormap * cmap ){
442 /* int x1, y1, x2, y2; */
445 dx = Cos( worm->ang ) * worm->speed;
446 dy = Sine( worm->ang ) * worm->speed;
448 worm->virtualx += dx;
449 worm->virtualy += dy;
450 worm->actualx = (int)worm->virtualx;
451 worm->actualy = (int)worm->virtualy;
455 if ( worm->spiral % 5 == 0 )
456 worm->ang = (worm->ang + 1 ) % 360;
458 if ( worm->spiral <= 0 ) find = 1;
462 if ( dist( worm->actualx, worm->actualy, worm->want_x, worm->want_y ) < 20 )
465 if ( rnd( 20 ) == rnd( 20 ) )
468 if ( worm->actualx < min_dist ){
469 worm->actualx = min_dist;
470 worm->virtualx = worm->actualx;
473 if ( worm->actualy < min_dist ){
474 worm->actualy = min_dist;
475 worm->virtualy = worm->actualy;
478 if ( worm->actualx > SCREEN_X - min_dist ){
479 worm->actualx = SCREEN_X - min_dist;
480 worm->virtualx = worm->actualx;
483 if ( worm->actualy > SCREEN_Y - min_dist ){
484 worm->actualy = SCREEN_Y - min_dist;
485 worm->virtualy = worm->actualy;
489 if ( rnd( 500 ) == rnd( 500 ) ) worm->spiral = rnd( 30 ) + 50;
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 );
499 /* worm->ang = ( worm->ang + 360 + rnd( 30 ) - 15 ) % 360; */
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 );
508 if ( rnd( 25 ) == rnd( 25 ) ){
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);
518 if ( worm->actualx < min_dist || worm->actualx > SCREEN_X - min_dist || worm->actualy < min_dist || worm->actualy > SCREEN_Y - min_dist ){
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 ); * /
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;
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;
550 moveColorChanger( &worm->changer, display, cmap );
552 if ( worm->diameter < worm->diameter_change )
554 if ( worm->diameter > worm->diameter_change )
556 if ( rnd( 30 ) == rnd( 30 ) )
557 worm->diameter_change = rnd( 35 ) + 5;
559 for ( q = 0; q < worm->addStar; q++ )
564 static XColor * getColorShade( color_changer * ch ){
565 return ch->shade + ch->min;
568 static void drawWormhole( Display * display, Window * win, GC * gc, wormhole * worm ){
576 for ( i = 0; i < worm->num_stars; i++ )
577 if ( worm->stars[i] != NULL ){
579 current = worm->stars[i];
580 z = current->begin.Z;
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 ];
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 );
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 );
599 static void eraseWormhole( Display * display, Window * win, GC * gc, wormhole * worm ){
603 for ( i = 0; i < worm->num_stars; i++ )
604 if ( worm->stars[i] != NULL ){
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 );
613 char *progclass = "Wormhole";
615 char *defaults [] = {
616 ".background: Black",
617 ".foreground: #E9967A",
624 XrmOptionDescRec options [] = {
625 { "-delay", ".delay", XrmoptionSepArg, 0 },
626 { "-zspeed", ".zspeed", XrmoptionSepArg, 0 },
627 { "-stars", ".stars", XrmoptionSepArg, 0 },
631 static int handle_event( Display * display, XEvent * event ){
633 if ( event->xany.type == ConfigureNotify ){
636 screenhack_handle_event( display, event );
641 void screenhack (Display *dpy, Window window) {
646 XWindowAttributes attr;
649 int delay = get_integer_resource( "delay", "Integer" );
650 make_stars = get_integer_resource( "stars", "Integer" );
651 z_speed = get_integer_resource( "zspeed", "Integer" );
653 initWormhole( &worm, dpy, &window );
657 gc = XCreateGC( dpy, window, GCForeground, &gcv );
658 XGetWindowAttributes( dpy, window, &attr );
659 cmap = attr.colormap;
663 moveWormhole( &worm, dpy, &cmap );
664 drawWormhole( dpy, &window, &gc, &worm );
667 /* handle my own friggin events. mmmlaaaa */
668 while ( XPending(dpy) ){
670 XNextEvent( dpy, &event );
671 if ( handle_event( dpy, &event ) == 1 ){
672 resizeWormhole( &worm, dpy, &window );
675 /* screenhack_handle_events (dpy); */
677 if (delay) usleep (delay);
678 /* eraseWormhole( dpy, &window, &gc, &worm ); */
680 XSetForeground( dpy, gc, worm.black.pixel );
681 XFillRectangle( dpy, window, gc, 0, 0, SCREEN_X, SCREEN_Y );
685 /* not reached: destroyWormhole( &worm, dpy, &cmap ); */