/* xscreensaver, Copyright (c) 1992-2008 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. */ /* Draw some fireworks. Inspired from TI Explorer Lisp code by John S. Pezaris */ #include #include "screenhack.h" struct projectile { int x, y; /* position */ int dx, dy; /* velocity */ int decay; int size; int fuse; Bool primary; Bool dead; XColor color; struct projectile *next_free; }; #define PI_2000 6284 struct state { Display *dpy; Window window; struct projectile *projectiles, *free_projectiles; struct projectile **sorted_projectiles; GC draw_gc, erase_gc; unsigned int default_fg_pixel; Colormap cmap; int how_many, frequency, scatter, delay; int sin_cache[PI_2000]; int cos_cache[PI_2000]; int draw_xlim, draw_ylim, real_draw_xlim, real_draw_ylim; unsigned long last_pixel; }; /* Slightly whacked, for better explosions */ static void cache(struct state *st) { /*needs to be run once. Could easily be */ int i; /*reimplemented to run and cache at compile-time,*/ double dA; /*saving on init_pyro time */ for (i=0; icos_cache[i]=(int) (cos(((double)i)/1000.0)*dA*2500.0); st->sin_cache[i]=(int) (sin(((double)i)/1000.0)*dA*2500.0); } } static struct projectile * get_projectile (struct state *st) { struct projectile *p; if (st->free_projectiles) { p = st->free_projectiles; st->free_projectiles = p->next_free; p->next_free = 0; p->dead = False; return p; } else return 0; } static void free_projectile (struct state *st, struct projectile *p) { p->next_free = st->free_projectiles; st->free_projectiles = p; p->dead = True; } static void launch (struct state *st, int xlim, int ylim, int g) { struct projectile *p = get_projectile (st); int x, dx, xxx; if (! p) return; do { x = (random () % xlim); dx = 30000 - (random () % 60000); xxx = x + (dx * 200); } while (xxx <= 0 || xxx >= xlim); p->x = x; p->y = ylim; p->dx = dx; p->size = 8000; p->decay = 0; p->dy = (random () % 4000) - 13000; p->fuse = ((((random () % 500) + 500) * abs (p->dy / g)) / 1000); p->primary = True; /* cope with small windows -- those constants assume big windows. */ { int dd = 1000000 / ylim; if (dd > 1) p->fuse /= dd; } if (! mono_p) { hsv_to_rgb (random () % 360, 1.0, 1.0, &p->color.red, &p->color.green, &p->color.blue); p->color.flags = DoRed | DoGreen | DoBlue; if (!XAllocColor (st->dpy, st->cmap, &p->color)) { p->color.pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy)); p->color.red = p->color.green = p->color.blue = 0xFFFF; } } } static struct projectile * shrapnel (struct state *st, struct projectile *parent) { struct projectile *p = get_projectile (st); int v; if (! p) return 0; p->x = parent->x; p->y = parent->y; v=random () % PI_2000; p->dx =(st->sin_cache[v]) + parent->dx; p->dy =(st->cos_cache[v]) + parent->dy; p->decay = (random () % 50) - 60; p->size = (parent->size * 2) / 3; p->fuse = 0; p->primary = False; p->color = parent->color; if (! mono_p) XAllocColor (st->dpy, st->cmap, &p->color); /* dup the lock */ return p; } static void * pyro_init (Display *dpy, Window window) { struct state *st = (struct state *) calloc (1, sizeof(*st)); int i; XGCValues gcv; XWindowAttributes xgwa; st->dpy = dpy; st->window = window; XGetWindowAttributes (st->dpy, st->window, &xgwa); st->last_pixel = ~0; st->cmap = xgwa.colormap; st->delay = get_integer_resource (st->dpy, "delay", "Integer"); st->how_many = get_integer_resource (st->dpy, "count", "Integer"); st->frequency = get_integer_resource (st->dpy, "frequency", "Integer"); st->scatter = get_integer_resource (st->dpy, "scatter", "Integer"); if (st->how_many <= 0) st->how_many = 100; if (st->frequency <= 0) st->frequency = 30; if (st->scatter <= 0) st->scatter = 20; st->projectiles = 0; st->free_projectiles = 0; st->projectiles = (struct projectile *) calloc (st->how_many, sizeof (*st->projectiles)); st->sorted_projectiles = (struct projectile **) calloc (st->how_many, sizeof (*st->sorted_projectiles)); for (i = 0; i < st->how_many; i++) free_projectile (st, &st->projectiles [i]); for (i = 0; i < st->how_many; i++) st->sorted_projectiles[i] = &st->projectiles[i]; gcv.foreground = st->default_fg_pixel = get_pixel_resource (st->dpy, st->cmap, "foreground", "Foreground"); st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv); gcv.foreground = get_pixel_resource (st->dpy, st->cmap, "background", "Background"); st->erase_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv); XClearWindow (st->dpy, st->window); cache(st); return st; } static int projectile_pixel_sorter (const void *a, const void *b) { struct projectile *pa = *(struct projectile **) a; struct projectile *pb = *(struct projectile **) b; if (pa->color.pixel == pb->color.pixel) return 0; else if (pa->color.pixel < pb->color.pixel) return -1; else return 1; } static void sort_by_pixel (struct state *st, int length) { qsort ((void *) st->sorted_projectiles, length, sizeof(*st->sorted_projectiles), projectile_pixel_sorter); } static unsigned long pyro_draw (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; XWindowAttributes xgwa; int g = 100; int resort = 0; int i; for (i = 0; i < st->how_many; i++) { struct projectile *p = st->sorted_projectiles [i]; int old_x, old_y, old_size; int size, x, y; if (p->dead) continue; old_x = p->x >> 10; old_y = p->y >> 10; old_size = p->size >> 10; size = (p->size += p->decay) >> 10; x = (p->x += p->dx) >> 10; y = (p->y += p->dy) >> 10; p->dy += (p->size >> 6); if (p->primary) p->fuse--; /* erase old one */ if (old_size > 0) { if (old_size == 1) XDrawPoint (st->dpy, st->window, st->erase_gc, old_x, old_y); else XFillRectangle (st->dpy, st->window, st->erase_gc, old_x, old_y, old_size, old_size); } if ((p->primary ? (p->fuse > 0) : (p->size > 0)) && x < st->real_draw_xlim && y < st->real_draw_ylim && x > 0 && y > 0) { if (size > 0) { unsigned long pixel; if (mono_p || p->primary) pixel = st->default_fg_pixel; else pixel = p->color.pixel; if (pixel != st->last_pixel) { st->last_pixel = pixel; XSetForeground (st->dpy, st->draw_gc, pixel); } if (size == 1) XDrawPoint (st->dpy, st->window, st->draw_gc, x, y); else if (size < 4) XFillRectangle (st->dpy, st->window, st->draw_gc, x, y, size, size); else XFillArc (st->dpy, st->window, st->draw_gc, x, y, size, size, 0, 360*64); } } else { free_projectile (st, p); if (! mono_p) if (p->color.pixel != WhitePixel (st->dpy, DefaultScreen (st->dpy))) XFreeColors (st->dpy, st->cmap, &p->color.pixel, 1, 0); } if (p->primary && p->fuse <= 0) { int j = (random () % st->scatter) + (st->scatter/2); while (j--) shrapnel (st, p); resort = 1; } } if ((random () % st->frequency) == 0) { XGetWindowAttributes (st->dpy, st->window, &xgwa); st->real_draw_xlim = xgwa.width; st->real_draw_ylim = xgwa.height; st->draw_xlim = st->real_draw_xlim * 1000; st->draw_ylim = st->real_draw_ylim * 1000; launch (st, st->draw_xlim, st->draw_ylim, g); resort = 1; } /* being sorted lets us avoid changing the GC's foreground color as often. */ if (resort) sort_by_pixel (st, st->how_many); return st->delay; } static void pyro_reshape (Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { } static Bool pyro_event (Display *dpy, Window window, void *closure, XEvent *event) { return False; } static void pyro_free (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; XFreeGC (dpy, st->draw_gc); XFreeGC (dpy, st->erase_gc); free (st->projectiles); free (st->sorted_projectiles); free (st); } static const char *pyro_defaults [] = { ".lowrez: true", ".background: black", ".foreground: white", "*fpsSolid: true", "*count: 600", "*delay: 10000", "*frequency: 30", "*scatter: 100", 0 }; static XrmOptionDescRec pyro_options [] = { { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-count", ".count", XrmoptionSepArg, 0 }, { "-frequency", ".frequency", XrmoptionSepArg, 0 }, { "-scatter", ".scatter", XrmoptionSepArg, 0 }, { 0, 0, 0, 0 } }; XSCREENSAVER_MODULE ("Pyro", pyro)