From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / fireworkx.c
index 78a4e9017c7bf08674cafe5a0d930eb4a3cabb83..faf3745e23a160240ba3f391e85c20861ee86efe 100644 (file)
 /*
- * Fireworkx 1.3 - pyrotechnics simulation program
- * Copyright (c) 1999-2004 Rony B Chandran <ronybc@asia.com>
- *
- * url: http://www.ronybc.8k.com
- *
- * 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.
- *
- * Additional programming: 
- * ------------------------
- * Support for different display color modes: 
- * Jean-Pierre Demailly <Jean-Pierre.Demailly@ujf-grenoble.fr>
- *
- * Fixed array access problems by beating on it with a large hammer.
- * Nicholas Miell <nmiell@gmail.com>
- *
+ Fireworkx 2.2 - Pyrotechnic explosions simulation,
+ an eyecandy, live animating colorful fireworks super-blasts..!
+ Copyright (GPL) 1999-2013 Rony B Chandran <ronybc@gmail.com>
+
+ From Kerala, INDIA
+ Website: http://www.ronybc.com
+
+ 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.
+
+ 2004-OCT: ronybc: Landed on Xscreensaver..!
+ 2012-DEC: ronybc: Almost rewrite of the last version (>4 years old)
+           with SSE2 optimization, colored light flashes,
+           HSV color and many visual and speed improvements.
+
+ Additional coding:
+ ---------------------------------------------------------------------------------
+ Support for different display color modes: put_image()
+ Jean-Pierre Demailly <Jean-Pierre.Demailly@ujf-grenoble.fr>
+
+ Fixed array access problems by beating on it with a large hammer.
+ Nicholas Miell <nmiell@gmail.com>
+
+ Help 'free'ing up of memory with needed 'XSync's.
+ Renuka S <renuka@ronybc.com>
+ Rugmini R Chandran <rugmini@ronybc.com>
+\
  */
 
-#include <math.h>
 #include "screenhack.h"
-#include <X11/Xutil.h>
-
-#define FWXVERSION "1.3"
-
-#define WIDTH 640
-#define HEIGHT 480
-#define SHELLCOUNT 3
-#define PIXCOUNT 500                   /* 500     */
-#define RNDLIFE0 250                   /* violent */
-#define RNDLIFE1 1200                  /* 1200    */
-#define MINLIFE0 50                    /* violent */
-#define MINLIFE1 500                   /* 500     */
-#define POWER 5                        /* 5       */
-#define FTWEAK 12                      /* 12      */
-
-static int depth;
-static int bigendian;
-static Bool light_on = True;
-static Bool fade_on  = False;
-static Bool shoot    = False;
-static Bool verbose  = False;
-static int delay     = 0;
-static int fsc_width = 0;
-static int fsc_height= 0;
-static int rndlife = RNDLIFE1;
-static int minlife = MINLIFE1;
-static float light_fade = 0.99;
-static unsigned char *real_palaka1 = NULL;
-static unsigned char *real_palaka2 = NULL;
-static unsigned char *palaka1=NULL;
-static unsigned char *palaka2=NULL;
-static XImage *xim=NULL;
-static XColor *colors;
-static int ncolors = 255;
-
-typedef struct {
-  unsigned int burn;
-  float x;
-  float y;
-  float xv;
-  float yv;}firepix;
-
-typedef struct {
-  unsigned int life;
-  unsigned int color;
-  unsigned int cx,cy;
-  unsigned int special;
-  unsigned int cshift;
-  unsigned int vgn,shy;
-  float air,lum;
-  firepix *fpix; }fireshell;
-
-int seed = 2387776;
-int rnd(int x)
-{   /* xscreensaver */
-  if ((seed = seed % 44488 * 48271 - seed / 44488 * 3399) < 0)
-  seed += 2147483647;
-  return (seed-1) % x;
+
+#ifdef __SSE2__
+# include <emmintrin.h>
+#endif
+
+#define FWXVERSION "2.2"
+
+#define WIDTH 1024                /* 888     */
+#define HEIGHT 632                /* 548     */
+#define SHELLCOUNT 4              /* FIXED NUMBER; for SSE optimization */
+#define PIXCOUNT 500              /* 500     */
+#define SHELL_LIFE_DEFAULT 32     /* 32      */
+#define SHELL_LIFE_RATIO 6        /* 6       */
+#define POWDER 5.0                /* 5.0     */
+#define FTWEAK 12                 /* 12      */
+#define FLASH_ZOOM 0.8            /* 1.0     */
+#define G_ACCELERATION 0.001      /* GRAVITY */
+
+typedef struct
+{
+       unsigned int burn;
+       float x, y;
+       float xv, yv;
+} firepix;
+
+typedef struct
+{
+       unsigned int cx, cy;
+       unsigned int seq_number, life;
+       unsigned int bicolor, flies, hshift, vshift;
+       unsigned int mortar_fired, explode_y;
+       float air_drag, vshift_phase;
+       float flash_r, flash_g, flash_b;
+       unsigned int h;
+       double s, v;
+       unsigned char r, g, b;
+       firepix *fpix;
+} fireshell;
+
+struct state
+{
+       unsigned int fps_on;
+       unsigned int flash_on;
+       unsigned int shoot;
+       unsigned int verbose;
+       unsigned int width;
+       unsigned int height;
+       unsigned int fullscreen;
+       unsigned int max_shell_life;
+       unsigned int delay;
+       float flash_fade;
+       float *light_map;
+       unsigned char *palaka1;
+       unsigned char *palaka2;
+       void *mem1;
+       void *mem2;
+       fireshell *fireshell_array;
+
+       Display *dpy;
+       Window window;
+       XImage *xim;
+       GC gc;
+       XColor *colors;
+       int depth;
+       int bigendian;
+       int ncolors;
+       Bool button_down_p;
+       int deferred;
+
+};
+
+/*
+       will return zero.. divide with care.
+*/
+static unsigned int rnd(unsigned int x)
+{
+       return(random() % x);
 }
 
-int explode(fireshell *fs)
+static void fs_roll_rgb(fireshell *fs)
 {
-  float air,adg = 0.001;     /* gravity */
-  unsigned int n,c;
-  unsigned int h = fsc_height;
-  unsigned int w = fsc_width;
-  unsigned int *prgb;
-  unsigned char *palaka = palaka1;
-  firepix *fp = fs->fpix;
-  if(fs->vgn){
-    if(--fs->cy == fs->shy){  
-      fs->vgn = 0;
-      fs->lum = 20000;}    /* millions of jouls..! */
-    else{  
-      fs->lum = 50+(fs->cy - fs->shy)*2;
-      return(1);}}    
-  if(fs->cshift) --fs->cshift;
-  if((fs->cshift+1)%50==0) fs->color = ~fs->color;
-  c = fs->color;
-  air = fs->air;
-  fs->lum *= light_fade;
-  for(n=PIXCOUNT;n;n--){
-  if(fp->burn){ --fp->burn; 
-  if(fs->special){
-  fp->x += fp->xv = fp->xv * air + (float)(rnd(200)-100)/2000;
-  fp->y += fp->yv = fp->yv * air + (float)(rnd(200)-100)/2000 +adg; }
-  else{
-  fp->x += fp->xv = fp->xv * air + (float)(rnd(200)-100)/20000;
-  fp->y += fp->yv = fp->yv * air + adg; }
-  if(fp->y > h){
-  if(rnd(5)==3) {fp->yv *= -0.24; fp->y = h;}
-  else fp->burn=0;} /* touch muddy ground :) */
-  if(fp->x < w && fp->x > 0 && fp->y < h && fp->y > 0){
-     prgb = (unsigned int *)(palaka + ((int)fp->y * w + (int)fp->x)*4);
-    *prgb = c; }
-  } fp++;
-  } return(--(fs->life));
+       unsigned short r, g, b;
+       hsv_to_rgb (fs->h, fs->s, fs->v, &r, &g, &b);
+       fs->r = (unsigned char) (r >> 8);
+       fs->g = (unsigned char) (g >> 8);
+       fs->b = (unsigned char) (b >> 8);
 }
 
-void recycle(fireshell *fs,int x,int y)
+static void mix_colors(fireshell *fs)
 {
-  unsigned int n,pixlife;
-  firepix *fp = fs->fpix;
-  fs->vgn = shoot;
-  fs->shy = y;
-  fs->cx = x;
-  fs->cy = shoot ? fsc_height : y ;
-  fs->color = (rnd(155)+100) <<16 |
-              (rnd(155)+100) <<8  |
-               rnd(255);
-  fs->life = rnd(rndlife)+minlife;
-  fs->air  = 1-(float)(rnd(200))/10000;
-  fs->lum  = 20000;
-  fs->cshift  = !rnd(5) ? 120:0; 
-  fs->special = !rnd(10) ? 1:0; 
-  pixlife = rnd(fs->life)+fs->life/10+1;    /* ! */
-  for(n=0;n<PIXCOUNT;n++){
-  fp->burn = rnd(pixlife)+32;
-  fp->xv = POWER*(float)(rnd(20000)-10000)/10000;
-  fp->yv = sqrt(POWER*POWER - fp->xv * fp->xv) *
-               (float)(rnd(20000)-10000)/10000;
-  fp->x = x;
-  fp->y = y; 
-  fp++;             }
+       float flash;
+       fs->h = rnd(360);
+       fs->s = frand(0.4) + 0.6;
+       fs->v = 1.0;
+       fs_roll_rgb(fs);
+
+       flash = rnd(444) + 111; /* Mega Jouls ! */
+       fs->flash_r = fs->r * flash;
+       fs->flash_g = fs->g * flash;
+       fs->flash_b = fs->b * flash;
 }
 
-void blur_best(void)
+static void render_light_map(struct state *st, fireshell *fs)
 {
-  int n;
-  unsigned int w = fsc_width;
-  unsigned int h = fsc_height;
-  unsigned char *pa, *pb, *pm;
-  pm = palaka1;
-  for(n=0;n<w*4;n++) pm[n]=0;    /* clean first line */
-  pm+=n;
-  h-=2; 
-  pa = pm-(w*4);
-  pb = pm+(w*4);
-  if(fade_on){
-  for(n=0;n<w*h*4;n++){
-  pm[n]=(pm[n-4] + pm[n] + pm[n+4] + 
-         pa[n-4] + pa[n] + pa[n+4] + 
-         pb[n-4] + pb[n] + pb[n+4] +
-         pm[n] + pm[n] + (pm[n]<<2))/ 16;}}
-  else{
-  for(n=0;n<w*h*4;n++){
-  pm[n]=(pm[n-4] + pm[n] + pm[n+4] + 
-         pa[n-4] + pa[n] + pa[n+4] + 
-         pb[n-4] + pb[n] + pb[n+4])/ 9;}}
-  pm+=n;
-  for(n=0;n<w*4;n++) pm[n]=0;    /* clean last line */
+       signed int x, y, v = 0;
+       for (y = 0, v = fs->seq_number; y < st->height; y += 2)
+       {
+               for (x = 0; x < st->width; x += 2, v += SHELLCOUNT)
+               {
+                       double f;
+                       f = sqrt((fs->cx - x) * (fs->cx - x) + (fs->cy - y) * (fs->cy - y)) + 4.0;
+                       f = FLASH_ZOOM / f;
+                       f += pow(f,0.1) * frand(0.0001); /* dither */
+                       st->light_map[v] = f;
+               }
+       }
 }
 
-void light_2x2(fireshell *fss)
+static void recycle(struct state *st, fireshell *fs, unsigned int x, unsigned int y)
 {
-  unsigned int l,t,n,x,y;
-  float s;
-  int w = fsc_width;
-  int h = fsc_height;
-  unsigned char *dim = palaka2;
-  unsigned char *sim = palaka1;
-  int nl = w*4;
-  fireshell *f;
-  for(y=0;y<h;y+=2){
-  for(x=0;x<w;x+=2){
-  f = fss; s = 0;
-  for(n=SHELLCOUNT;n;n--,f++){
-  s += f->lum / ( 1 + sqrt((f->cx - x)*(f->cx - x)+(f->cy - y)*(f->cy - y))); }
-  l = s;
-
-  t = l + sim[0];
-  dim[0] = (t > 255 ? 255 : t);         /* cmov's */
-  t = l + sim[1];
-  dim[1] = (t > 255 ? 255 : t);
-  t = l + sim[2];
-  dim[2] = (t > 255 ? 255 : t);
-
-  t = l + sim[4];
-  dim[4] = (t > 255 ? 255 : t);
-  t = l + sim[5];
-  dim[5] = (t > 255 ? 255 : t);
-  t = l + sim[6];
-  dim[6] = (t > 255 ? 255 : t);
-
-  t = l + sim[nl+0];
-  dim[nl+0] = (t > 255 ? 255 : t);
-  t = l + sim[nl+1];
-  dim[nl+1] = (t > 255 ? 255 : t);
-  t = l + sim[nl+2];
-  dim[nl+2] = (t > 255 ? 255 : t);
-
-  t = l + sim[nl+4];
-  dim[nl+4] = (t > 255 ? 255 : t);
-  t = l + sim[nl+5];
-  dim[nl+5] = (t > 255 ? 255 : t);
-  t = l + sim[nl+6];
-  dim[nl+6] = (t > 255 ? 255 : t);
-
-  sim += 8; dim += 8; } sim += nl; dim += nl;}
+       unsigned int n, pixlife;
+       firepix *fp = fs->fpix;
+       fs->mortar_fired = st->shoot;
+       fs->explode_y = y;
+       fs->cx = x;
+       fs->cy = st->shoot ? st->height : y ;
+       fs->life = rnd(st->max_shell_life) + (st->max_shell_life/SHELL_LIFE_RATIO);
+       fs->life += !rnd(25) ? st->max_shell_life * 5 : 0;
+       fs->air_drag = 1.0 - (float)(rnd(200)) / (10000.0 + fs->life);
+       fs->bicolor = !rnd(5) ? 120 : 0;
+       fs->flies = !rnd(10) ? 1 : 0; /* flies' motion */
+       fs->hshift = !rnd(5) ? 1 : 0; /* hue shifting  */
+       fs->vshift = !rnd(10) ? 1 : 0; /* value shifting */
+       fs->vshift_phase = M_PI/2.0;
+       pixlife = rnd(fs->life) + fs->life / 10 + 1;    /* ! */
+       for (n = 0; n < PIXCOUNT; n++)
+       {
+               fp->burn = rnd(pixlife) + 32;
+               fp->xv = frand(2.0) * POWDER - POWDER;
+               fp->yv = sqrt(POWDER * POWDER - fp->xv * fp->xv) * (frand(2.0) - 1.0);
+               fp->x = x;
+               fp->y = y;
+               fp++;
+       }
+       mix_colors(fs);
+       render_light_map(st, fs);
 }
 
-void resize(Display *display, Window win)
+static void recycle_oldest(struct state *st, unsigned int x, unsigned int y)
 {
-  XWindowAttributes xwa;
-  XGetWindowAttributes (display, win, &xwa);
-  xwa.width  -= xwa.width % 4;
-  xwa.height -= xwa.height % 4;
-  if(xwa.height != fsc_height || xwa.width != fsc_width) {
-  fsc_width  = xwa.width;
-  fsc_height = xwa.height;
-  if (xim) {
-  if (xim->data==(char *)palaka2) xim->data=NULL;  
-  XDestroyImage(xim);
-  if (palaka2!=palaka1) free(real_palaka2);
-  free(real_palaka1); 
-  }
-  palaka1 = NULL;     
-  palaka2 = NULL; 
-  xim = XCreateImage(display, xwa.visual, xwa.depth, ZPixmap, 0, 0,
-                    fsc_width, fsc_height, 32, 0);
-  real_palaka1 = calloc(xim->height + 4,xim->width*4);
-  palaka1 = real_palaka1 + xim->width * 4 * 2;
-  if(light_on) {
-       real_palaka2 = calloc(xim->height + 4,xim->width*4);
-       palaka2 = real_palaka2 + xim->width * 4 * 2;
-  } else {
-       palaka2 = palaka1;
-  }
-  if (depth>=24)
-  xim->data = (char *)palaka2;
-  else
-  xim->data = calloc(xim->height,xim->bytes_per_line);       
- }
+       unsigned int n;
+       fireshell *fs, *oldest;
+       fs = oldest = st->fireshell_array;
+       for (n = 0; n < SHELLCOUNT; n++)
+       {
+               if(fs[n].life < oldest->life) oldest = &fs[n];
+       }
+       recycle(st, oldest, x, y);
 }
 
-void put_image(Display *display, Window win, GC gc, XImage *xim)
+static void rotate_hue(fireshell *fs, int dh)
 {
-  int x,y,i,j;
-  unsigned char r, g, b;
-  i = 0;
-  j = 0;
-  if (depth==16) {
-     if(bigendian)
-     for (y=0;y<xim->height; y++)
-     for (x=0;x<xim->width; x++) {
-     r = palaka2[j++];
-     g = palaka2[j++];
-     b = palaka2[j++];
-     j++;
-     xim->data[i++] = (g&224)>>5 | (r&248);
-     xim->data[i++] = (b&248)>>3 | (g&28)<<3;
-     }
-     else
-     for (y=0;y<xim->height; y++)
-     for (x=0;x<xim->width; x++) {
-     r = palaka2[j++];
-     g = palaka2[j++];
-     b = palaka2[j++];
-     j++;
-     xim->data[i++] = (b&248)>>3 | (g&28)<<3;
-     xim->data[i++] = (g&224)>>5 | (r&248);
-     }
-  }
-  if (depth==15) {
-     if(bigendian)
-     for (y=0;y<xim->height; y++)
-     for (x=0;x<xim->width; x++) {
-     r = palaka2[j++];
-     g = palaka2[j++];
-     b = palaka2[j++];
-     j++;
-     xim->data[i++] = (g&192)>>6 | (r&248)>>1;
-     xim->data[i++] = (b&248)>>3 | (g&56)<<2;
-     }
-     else
-     for (y=0;y<xim->height; y++)
-     for (x=0;x<xim->width; x++) {
-     r = palaka2[j++];
-     g = palaka2[j++];
-     b = palaka2[j++];
-     j++;
-     xim->data[i++] = (b&248)>>3 | (g&56)<<2;
-     xim->data[i++] = (g&192)>>6 | (r&248)>>1;
-     }
-  }
-  if (depth==8) {
-     for (y=0;y<xim->height; y++)
-     for (x=0;x<xim->width; x++) {
-     r = palaka2[j++];
-     g = palaka2[j++];
-     b = palaka2[j++];
-     j++;     
-     xim->data[i++] = (((7*g)/256)*36)+(((6*r)/256)*6)+((6*b)/256);
-     }
-  }
-  XPutImage(display,win,gc,xim,0,0,0,0,xim->width,xim->height); 
+       fs->h = fs->h + dh;
+       fs->s = fs->s - 0.001;
+       fs_roll_rgb(fs);
 }
 
-void sniff_events(Display *dis, Window win, fireshell *fss)
+static void wave_value(fireshell *fs)
 {
-  XEvent e;
-  while (XPending(dis)){
-  XNextEvent (dis, &e);
-  if (e.type == ConfigureNotify) resize(dis,win);
-  if (e.type == ButtonPress)     recycle(fss,e.xbutton.x, e.xbutton.y);
-  screenhack_handle_event(dis,&e);}
+       fs->vshift_phase = fs->vshift_phase + 0.008;
+       fs->v = fabs(sin(fs->vshift_phase));
+       fs_roll_rgb(fs);
 }
 
+static int explode(struct state *st, fireshell *fs)
+{
+       float air_drag;
+       unsigned int n;
+       unsigned int h = st->height;
+       unsigned int w = st->width;
+       unsigned char r, g, b;
+       unsigned char *prgba;
+       unsigned char *palaka = st->palaka1;
+       firepix *fp = fs->fpix;
+       if (fs->mortar_fired)
+       {
+               if (--fs->cy == fs->explode_y)
+               {
+                       fs->mortar_fired = 0;
+                       mix_colors(fs);
+                       render_light_map(st, fs);
+               }
+               else
+               {
+                       fs->flash_r =
+                           fs->flash_g =
+                               fs->flash_b = 50 + (fs->cy - fs->explode_y) * 10;
+                       prgba = palaka + (fs->cy * w + fs->cx + rnd(5) - 2) * 4;
+                       prgba[0] = (rnd(32) + 128);
+                       prgba[1] = (rnd(32) + 128);
+                       prgba[2] = (rnd(32) + 128);
+                       return(1);
+               }
+       }
+       if ((fs->bicolor + 1) % 50 == 0) rotate_hue(fs, 180);
+       if (fs->bicolor) --fs->bicolor;
+       if (fs->hshift) rotate_hue(fs, rnd(8));
+       if (fs->vshift) wave_value(fs);
+       if (fs->flash_r > 1.0) fs->flash_r *= st->flash_fade;
+       if (fs->flash_g > 1.0) fs->flash_g *= st->flash_fade;
+       if (fs->flash_b > 1.0) fs->flash_b *= st->flash_fade;
+       air_drag = fs->air_drag;
+       r = fs->r;
+       g = fs->g;
+       b = fs->b;
+       for (n = 0; n < PIXCOUNT; n++, fp++)
+       {
+               if (!fp->burn) continue;
+               --fp->burn;
+               if (fs->flies)
+               {
+                       fp->x += fp->xv = fp->xv * air_drag + frand(0.1) - 0.05;
+                       fp->y += fp->yv = fp->yv * air_drag + frand(0.1) - 0.05 + G_ACCELERATION;
+               }
+               else
+               {
+                       fp->x += fp->xv = fp->xv * air_drag + frand(0.01) - 0.005;
+                       fp->y += fp->yv = fp->yv * air_drag + frand(0.005) - 0.0025 + G_ACCELERATION;
+               }
+               if (fp->y > h)
+               {
+                       if (rnd(5) == 3)
+                       {
+                               fp->yv *= -0.24;
+                               fp->y = h;
+                       }
+                       /* touch muddy ground :) */
+                       else fp->burn = 0;
+               }
+               if (fp->x < w && fp->x > 0 && fp->y < h && fp->y > 0)
+               {
+                       prgba = palaka + ((int)fp->y * w + (int)fp->x) * 4;
+                       prgba[0] = b;
+                       prgba[1] = g;
+                       prgba[2] = r;
+               }
+       }
+       return(--fs->life);
+}
 
-char *progclass = "Fireworkx";
+#ifdef __SSE2__
 
-char *defaults [] = {
-  ".background:        black",
-  ".foreground:        white",
-  "*delay:     5000",
-  "*maxlife:   1200",
-  "*light:     True",
-  "*fade:      False",
-  "*shoot:     False",
-  "*verbose:   False",
-  0
-};
+/* SSE2 optimized versions of glow_blur() and chromo_2x2_light() */
 
-XrmOptionDescRec options [] = {
-  { "-delay",          ".delay",       XrmoptionSepArg, 0 },
-  { "-maxlife",                ".maxlife",     XrmoptionSepArg, 0 },
-  { "-nolight",                ".light",       XrmoptionNoArg, "False" },
-  { "-fade",           ".fade",        XrmoptionNoArg, "True" },
-  { "-shoot",          ".shoot",       XrmoptionNoArg, "True" },
-  { "-verbose",                ".verbose",     XrmoptionNoArg, "True" },
-  { 0, 0, 0, 0 }
-};
+static void glow_blur(struct state *st)
+{
+       unsigned int n, nn;
+       unsigned char *ps = st->palaka1;
+       unsigned char *pd = st->palaka2;
+       unsigned char *pa = st->palaka1 - (st->width * 4);
+       unsigned char *pb = st->palaka1 + (st->width * 4);
+       __m128i xmm0, xmm1, xmm2, xmm3, xmm4;
+
+       xmm0 = _mm_setzero_si128();
+       nn = st->width * st->height * 4;
+       for (n = 0; n < nn; n+=16)
+       {
+               _mm_prefetch((const void *)&ps[n+16],_MM_HINT_T0);
+               _mm_prefetch((const void *)&pa[n+16],_MM_HINT_T0);
+               _mm_prefetch((const void *)&pb[n+16],_MM_HINT_T0);
+
+               xmm1 = _mm_load_si128((const __m128i*)&ps[n]);
+               xmm2 = xmm1;
+               xmm1 = _mm_unpacklo_epi8(xmm1,xmm0);
+               xmm2 = _mm_unpackhi_epi8(xmm2,xmm0);
+               xmm3 = _mm_loadu_si128((const __m128i*)&ps[n+4]);
+               xmm4 = xmm3;
+               xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
+               xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
+               xmm3 = _mm_slli_epi16(xmm3,3);
+               xmm4 = _mm_slli_epi16(xmm4,3);
+               xmm1 = _mm_add_epi16(xmm1,xmm3);
+               xmm2 = _mm_add_epi16(xmm2,xmm4);
+               xmm3 = _mm_loadu_si128((const __m128i*)&ps[n+8]);
+               xmm4 = xmm3;
+               xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
+               xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
+               xmm1 = _mm_add_epi16(xmm1,xmm3);
+               xmm2 = _mm_add_epi16(xmm2,xmm4);
+
+               xmm3 = _mm_load_si128((const __m128i*)&pa[n]);
+               xmm4 = xmm3;
+               xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
+               xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
+               xmm1 = _mm_add_epi16(xmm1,xmm3);
+               xmm2 = _mm_add_epi16(xmm2,xmm4);
+               xmm3 = _mm_loadu_si128((const __m128i*)&pa[n+4]);
+               xmm4 = xmm3;
+               xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
+               xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
+               xmm1 = _mm_add_epi16(xmm1,xmm3);
+               xmm2 = _mm_add_epi16(xmm2,xmm4);
+               xmm3 = _mm_loadu_si128((const __m128i*)&pa[n+8]);
+               xmm4 = xmm3;
+               xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
+               xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
+               xmm1 = _mm_add_epi16(xmm1,xmm3);
+               xmm2 = _mm_add_epi16(xmm2,xmm4);
+
+               xmm3 = _mm_load_si128((const __m128i*)&pb[n]);
+               xmm4 = xmm3;
+               xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
+               xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
+               xmm1 = _mm_add_epi16(xmm1,xmm3);
+               xmm2 = _mm_add_epi16(xmm2,xmm4);
+               xmm3 = _mm_loadu_si128((const __m128i*)&pb[n+4]);
+               xmm4 = xmm3;
+               xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
+               xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
+               xmm1 = _mm_add_epi16(xmm1,xmm3);
+               xmm2 = _mm_add_epi16(xmm2,xmm4);
+               xmm3 = _mm_loadu_si128((const __m128i*)&pb[n+8]);
+               xmm4 = xmm3;
+               xmm3 = _mm_unpacklo_epi8(xmm3,xmm0);
+               xmm4 = _mm_unpackhi_epi8(xmm4,xmm0);
+               xmm1 = _mm_add_epi16(xmm1,xmm3);
+               xmm2 = _mm_add_epi16(xmm2,xmm4);
+
+               xmm3 = xmm1;
+               xmm4 = xmm2;
+               xmm1 = _mm_srli_epi16(xmm1,4);
+               xmm2 = _mm_srli_epi16(xmm2,4);
+               xmm3 = _mm_srli_epi16(xmm3,3);
+               xmm4 = _mm_srli_epi16(xmm4,3);
+               xmm1 = _mm_packus_epi16(xmm1,xmm2);
+               xmm3 = _mm_packus_epi16(xmm3,xmm4);
+
+               _mm_storeu_si128((__m128i*)&ps[n+4], xmm1);
+               _mm_storeu_si128((__m128i*)&pd[n+4], xmm3);
+       }
+}
+
+static void chromo_2x2_light(struct state *st)
+{
+       __m128 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6;
+       __m128i xmi4, xmi5, xmi6, xmi7;
+
+       unsigned int x, y, v = 0;
+       unsigned int nl = st->width * 4;
+       unsigned char *mem = st->palaka2;
+       fireshell *fs = st->fireshell_array;
+
+       xmm0 = _mm_setr_ps(fs[0].flash_b, fs[0].flash_g, fs[0].flash_r, 0.0);
+       xmm1 = _mm_setr_ps(fs[1].flash_b, fs[1].flash_g, fs[1].flash_r, 0.0);
+       xmm2 = _mm_setr_ps(fs[2].flash_b, fs[2].flash_g, fs[2].flash_r, 0.0);
+       xmm3 = _mm_setr_ps(fs[3].flash_b, fs[3].flash_g, fs[3].flash_r, 0.0);
+
+       for (y = st->height/2; y; y--, mem += nl)
+       {
+               for (x = st->width/4; x; x--, v += 8, mem += 16)
+               {
+                       xmm4 = _mm_set1_ps(st->light_map[v+0]);
+                       xmm5 = xmm0;
+                       xmm5 = _mm_mul_ps(xmm5,xmm4);
+                       xmm4 = _mm_set1_ps(st->light_map[v+1]);
+                       xmm4 = _mm_mul_ps(xmm4,xmm1);
+                       xmm5 = _mm_add_ps(xmm5,xmm4);
+                       xmm4 = _mm_set1_ps(st->light_map[v+2]);
+                       xmm4 = _mm_mul_ps(xmm4,xmm2);
+                       xmm5 = _mm_add_ps(xmm5,xmm4);
+                       xmm4 = _mm_set1_ps(st->light_map[v+3]);
+                       xmm4 = _mm_mul_ps(xmm4,xmm3);
+                       xmm5 = _mm_add_ps(xmm5,xmm4);
+
+                       xmm4 = _mm_set1_ps(st->light_map[v+4]);
+                       xmm6 = xmm0;
+                       xmm6 = _mm_mul_ps(xmm6,xmm4);
+                       xmm4 = _mm_set1_ps(st->light_map[v+5]);
+                       xmm4 = _mm_mul_ps(xmm4,xmm1);
+                       xmm6 = _mm_add_ps(xmm6,xmm4);
+                       xmm4 = _mm_set1_ps(st->light_map[v+6]);
+                       xmm4 = _mm_mul_ps(xmm4,xmm2);
+                       xmm6 = _mm_add_ps(xmm6,xmm4);
+                       xmm4 = _mm_set1_ps(st->light_map[v+7]);
+                       xmm4 = _mm_mul_ps(xmm4,xmm3);
+                       xmm6 = _mm_add_ps(xmm6,xmm4);
+
+                       xmi6 = _mm_cvtps_epi32(xmm5);
+                       xmi7 = _mm_cvtps_epi32(xmm6);
+                       xmi6 = _mm_packs_epi32(xmi6,xmi6);
+                       xmi7 = _mm_packs_epi32(xmi7,xmi7);
+
+                       xmi4 = _mm_load_si128((const __m128i*) mem);
+                       xmi5 = _mm_unpacklo_epi8(xmi5,xmi4);
+                       xmi5 = _mm_srli_epi16(xmi5,8);
+                       xmi4 = _mm_unpackhi_epi8(xmi4,xmi4);
+                       xmi4 = _mm_srli_epi16(xmi4,8);
+                       xmi5 = _mm_add_epi16(xmi5,xmi6);
+                       xmi4 = _mm_add_epi16(xmi4,xmi7);
+                       xmi5 = _mm_packus_epi16(xmi5,xmi4);
+                       _mm_store_si128((__m128i*) mem, xmi5);
+
+                       xmi4 = _mm_load_si128((const __m128i*) &mem[nl]);
+                       xmi5 = _mm_unpacklo_epi8(xmi5,xmi4);
+                       xmi5 = _mm_srli_epi16(xmi5,8);
+                       xmi4 = _mm_unpackhi_epi8(xmi4,xmi4);
+                       xmi4 = _mm_srli_epi16(xmi4,8);
+                       xmi5 = _mm_add_epi16(xmi5,xmi6);
+                       xmi4 = _mm_add_epi16(xmi4,xmi7);
+                       xmi5 = _mm_packus_epi16(xmi5,xmi4);
+                       _mm_store_si128((__m128i*) &mem[nl], xmi5);
+               }
+       }
+}
+
+#else
+
+static void glow_blur(struct state *st)
+{
+       unsigned int n, q;
+       unsigned char *pm = st->palaka1;
+       unsigned char *po = st->palaka2;
+       unsigned char *pa = pm - (st->width * 4);
+       unsigned char *pb = pm + (st->width * 4);
+       /*
+               unsigned int rgba = 0;
+               for (n = st->width*st->height*4; n; n--, pm++, pa++, pb++, po++)
+               {
+                       if(++rgba > 3)
+                       {
+                               rgba = 0;
+                               continue;
+                       }
+                       q     = pm[0] + pm[4] * 8 + pm[8] +
+                               pa[0] + pa[4] + pa[8] +
+                               pb[0] + pb[4] + pb[8];
+                       pm[4] = q >> 4;
+                       po[4] = q > 2047 ? 255 : q >> 3;
+               }
+                       --- using unrolled version ------------
+       */
+       for (n = st->width*st->height*4; n; n-=4)
+       {
+               q = pm[0] + pm[4] * 8 + pm[8] +
+                   pa[0] + pa[4] + pa[8] +
+                   pb[0] + pb[4] + pb[8];
+               pm[4] = q >> 4;
+               po[4] = q > 2047 ? 255 : q >> 3;
+               q = pm[1] + pm[5] * 8 + pm[9] +
+                   pa[1] + pa[5] + pa[9] +
+                   pb[1] + pb[5] + pb[9];
+               pm[5] = q >> 4;
+               po[5] = q > 2047 ? 255 : q >> 3;
+               q = pm[2] + pm[6] * 8 + pm[10] +
+                   pa[2] + pa[6] + pa[10] +
+                   pb[2] + pb[6] + pb[10];
+               pm[6] = q >> 4;
+               po[6] = q > 2047 ? 255 : q >> 3;
+
+               pm+=4, pa+=4, pb+=4, po+=4;
+       }
+}
+
+static inline unsigned char addbs(unsigned char c, unsigned int i)
+{
+       i += c;
+       return(i > 255 ? 255 : i);
+}
+
+static void chromo_2x2_light(struct state *st)
+{
+       unsigned int n, x, y, v = 0;
+       unsigned int nl = st->width * 4;
+       unsigned char *mem = st->palaka2;
+       float r, g, b;
+       float rgb[SHELLCOUNT*4];
+       fireshell *fs = st->fireshell_array;
+
+       for (n = 0, x = 0; n < SHELLCOUNT; n++, x += 4, fs++)
+       {
+               rgb[x  ] = fs->flash_r;
+               rgb[x+1] = fs->flash_g;
+               rgb[x+2] = fs->flash_b;
+       }
+
+       for (y = st->height/2; y; y--)
+       {
+               for (x = st->width/2; x; x--, v += 4)
+               {
+                       r = rgb[0] * st->light_map[v] + rgb[4] * st->light_map[v+1]
+                           + rgb[ 8] * st->light_map[v+2] + rgb[12] * st->light_map[v+3];
+                       g = rgb[1] * st->light_map[v] + rgb[5] * st->light_map[v+1]
+                           + rgb[ 9] * st->light_map[v+2] + rgb[13] * st->light_map[v+3];
+                       b = rgb[2] * st->light_map[v] + rgb[6] * st->light_map[v+1]
+                           + rgb[10] * st->light_map[v+2] + rgb[14] * st->light_map[v+3];
+
+                       mem[0] = addbs(mem[0], b);
+                       mem[1] = addbs(mem[1], g);
+                       mem[2] = addbs(mem[2], r);
+                       mem[4] = addbs(mem[4], b);
+                       mem[5] = addbs(mem[5], g);
+                       mem[6] = addbs(mem[6], r);
+
+                       mem += nl;
+
+                       mem[0] = addbs(mem[0], b);
+                       mem[1] = addbs(mem[1], g);
+                       mem[2] = addbs(mem[2], r);
+                       mem[4] = addbs(mem[4], b);
+                       mem[5] = addbs(mem[5], g);
+                       mem[6] = addbs(mem[6], r);
+
+                       mem -= nl - 8;
+               }
+               mem += nl;
+       }
+}
+
+#endif
 
-void
-screenhack (Display *display, Window win)
+static void resize(struct state *st)
 {
-  unsigned int n,q;
-  Visual *vi;
-  Colormap cmap;
-  Bool writable;
-  XWindowAttributes xwa;
-  GC gc;
-  XGCValues gcv;
-  firepix *fpixs, *fpix;
-  fireshell *fshells, *fshell;
-  fade_on  = get_boolean_resource("fade"    , "Boolean");
-  light_on = get_boolean_resource("light"   , "Boolean");
-  shoot    = get_boolean_resource("shoot"   , "Boolean");
-  verbose  = get_boolean_resource("verbose" , "Boolean");
-  rndlife  = get_integer_resource("maxlife" , "Integer");
-  delay    = get_integer_resource("delay"   , "Integer");
-  minlife  = rndlife/4;
-  if(rndlife<1000) light_fade=0.98;
-  if(rndlife<500) light_fade=0.97;
-  if(fade_on) light_fade=0.97;
-  if(verbose){
-  printf("Fireworkx %s - pyrotechnics simulation program \n", FWXVERSION);
-  printf("Copyright (c) 1999-2004 Rony B Chandran <ronybc@asia.com> \n\n");
-  printf("url: http://www.ronybc.8k.com \n\n");}
-
-  XGetWindowAttributes(display,win,&xwa);
-  depth     = xwa.depth;
-  vi        = xwa.visual;
-  cmap      = xwa.colormap;
-  bigendian = (ImageByteOrder(display) == MSBFirst);
-  if(depth==8){
-  if(verbose){
-  printf("Pseudocolor color: use '-fade' & '-nolight' for better results.\n");}
-  colors = (XColor *) calloc(sizeof(XColor),ncolors+1);
-  writable = False;
-  make_smooth_colormap(display, vi, cmap, colors, &ncolors,
-                                False, &writable, True);
-  }
-  gc = XCreateGC(display, win, 0, &gcv);
-
-  resize(display,win);   /* initialize palakas */ 
-  seed += time(0);
-  
-  fpixs = malloc(sizeof(firepix) * PIXCOUNT * SHELLCOUNT);
-  fshells = malloc(sizeof(fireshell) * SHELLCOUNT);
-  fshell = fshells;
-  fpix = fpixs;
-  for (n=0;n<SHELLCOUNT;n++){
-  fshell->fpix = fpix;
-  recycle (fshell,rnd(fsc_width),rnd(fsc_height));
-  fshell++; 
-  fpix += PIXCOUNT; }
-  
-  while(1) {
-  for(q=FTWEAK;q;q--){
-  fshell=fshells;
-  for(n=SHELLCOUNT;n;n--){
-  if (!explode(fshell)){
-       recycle(fshell,rnd(fsc_width),rnd(fsc_height)); }
-       fshell++; }}
-  if(light_on) light_2x2(fshells);
-  put_image(display,win,gc,xim);
-  usleep(delay);
-  XSync(display,0);
-  sniff_events(display, win, fshells);
-  blur_best();}
+       unsigned int n;
+       fireshell *fs = st->fireshell_array;
+       XWindowAttributes xwa;
+       XGetWindowAttributes (st->dpy, st->window, &xwa);
+       xwa.width  -= xwa.width % 4;
+       xwa.height -= xwa.height % 2;
+       st->width  = xwa.width;
+       st->height = xwa.height;
+       if (st->verbose)
+       {
+               printf("resolution: %d x %d \n",st->width,st->height);
+       }
+       XSync(st->dpy, 0);
+       if (st->xim)
+       {
+               if (st->xim->data == (char *)st->palaka2) st->xim->data = NULL;
+               XDestroyImage(st->xim);
+               XSync(st->dpy, 0);
+               free(st->mem2);
+               free(st->mem1);
+       }
+       st->xim = XCreateImage(st->dpy, xwa.visual, xwa.depth, ZPixmap, 0, 0,
+                              st->width, st->height, 8, 0);
+       if (!st->xim) return;
+
+#ifdef __SSE2___ABANDONED /* causes __ERROR_use_memset_not_bzero_in_xscreensaver__ */
+       st->mem1 = _mm_malloc(((st->height + 2) * st->width + 8)*4, 16);
+       bzero(st->mem1, ((st->height + 2) * st->width + 8)*4);
+       st->mem2 = _mm_malloc(((st->height + 2) * st->width + 8)*4, 16);
+       bzero(st->mem2, ((st->height + 2) * st->width + 8)*4);
+#else
+       st->mem1 = calloc((st->height + 2) * st->width + 8, 4);
+       st->mem2 = calloc((st->height + 2) * st->width + 8, 4);
+#endif
+       st->palaka1 = (unsigned char *) st->mem1 + (st->width * 4 + 16);
+       st->palaka2 = (unsigned char *) st->mem2 + (st->width * 4 + 16);
+
+       if (xwa.depth >= 24)
+       {
+               st->xim->data = (char *)st->palaka2;
+       }
+       else
+       {
+               st->xim->data = calloc(st->height, st->xim->bytes_per_line);
+       }
 
+       if (st->light_map) free(st->light_map);
+       st->light_map = calloc((st->width * st->height * SHELLCOUNT)/4, sizeof(float));
+       for (n = 0; n < SHELLCOUNT; n++, fs++)
+       {
+               render_light_map(st, fs);
+       }
+}
+
+static void put_image(struct state *st)
+{
+       int x,y,i,j;
+       unsigned char r, g, b;
+       if (!st->xim) return;
+       i = 0;
+       j = 0;
+       if (st->depth==16)
+       {
+               if(st->bigendian)
+                       for (y=0; y<st->xim->height; y++)
+                               for (x=0; x<st->xim->width; x++)
+                               {
+                                       r = st->palaka2[j++];
+                                       g = st->palaka2[j++];
+                                       b = st->palaka2[j++];
+                                       j++;
+                                       st->xim->data[i++] = (g&224)>>5 | (r&248);
+                                       st->xim->data[i++] = (b&248)>>3 | (g&28)<<3;
+                               }
+               else
+                       for (y=0; y<st->xim->height; y++)
+                               for (x=0; x<st->xim->width; x++)
+                               {
+                                       r = st->palaka2[j++];
+                                       g = st->palaka2[j++];
+                                       b = st->palaka2[j++];
+                                       j++;
+                                       st->xim->data[i++] = (b&248)>>3 | (g&28)<<3;
+                                       st->xim->data[i++] = (g&224)>>5 | (r&248);
+                               }
+       }
+       if (st->depth==15)
+       {
+               if(st->bigendian)
+                       for (y=0; y<st->xim->height; y++)
+                               for (x=0; x<st->xim->width; x++)
+                               {
+                                       r = st->palaka2[j++];
+                                       g = st->palaka2[j++];
+                                       b = st->palaka2[j++];
+                                       j++;
+                                       st->xim->data[i++] = (g&192)>>6 | (r&248)>>1;
+                                       st->xim->data[i++] = (b&248)>>3 | (g&56)<<2;
+                               }
+               else
+                       for (y=0; y<st->xim->height; y++)
+                               for (x=0; x<st->xim->width; x++)
+                               {
+                                       r = st->palaka2[j++];
+                                       g = st->palaka2[j++];
+                                       b = st->palaka2[j++];
+                                       j++;
+                                       st->xim->data[i++] = (b&248)>>3 | (g&56)<<2;
+                                       st->xim->data[i++] = (g&192)>>6 | (r&248)>>1;
+                               }
+       }
+       if (st->depth==8)
+       {
+               for (y=0; y<st->xim->height; y++)
+                       for (x=0; x<st->xim->width; x++)
+                       {
+                               r = st->palaka2[j++];
+                               g = st->palaka2[j++];
+                               b = st->palaka2[j++];
+                               j++;
+                               st->xim->data[i++] = (((7*g)/256)*36)+(((6*r)/256)*6)+((6*b)/256);
+                       }
+       }
+       XPutImage(st->dpy,st->window,st->gc,st->xim,0,0,0,0,st->xim->width,st->xim->height);
 }
+
+static void *
+fireworkx_init (Display *dpy, Window win)
+{
+       struct state *st = (struct state *) calloc (1, sizeof(*st));
+       unsigned int n;
+       Visual *vi;
+       Colormap cmap;
+       Bool writable;
+       XWindowAttributes xwa;
+       XGCValues gcv;
+       firepix *fp;
+       fireshell *fs;
+
+       st->dpy = dpy;
+       st->window = win;
+       st->xim = NULL;
+       st->flash_on = 1;
+       st->shoot = 0;
+       st->width = 0;
+       st->height = 0;
+       st->max_shell_life = SHELL_LIFE_DEFAULT;
+       st->flash_fade = 0.995;
+       st->light_map = NULL;
+       st->palaka1 = NULL;
+       st->palaka2 = NULL;
+
+       st->flash_on       = get_boolean_resource(st->dpy, "flash"   , "Boolean");
+       st->shoot          = get_boolean_resource(st->dpy, "shoot"   , "Boolean");
+       st->verbose        = get_boolean_resource(st->dpy, "verbose" , "Boolean");
+       st->max_shell_life = get_integer_resource(st->dpy, "maxlife" , "Integer");
+        /* transition from xscreensaver <= 5.20 */
+       if (st->max_shell_life > 100) st->max_shell_life = 100;
+
+       st->delay          = get_integer_resource(st->dpy, "delay"   , "Integer");
+
+       st->max_shell_life = pow(10.0,(st->max_shell_life/50.0)+2.7);
+       if(st->max_shell_life < 1000) st->flash_fade = 0.998;
+
+       if(st->verbose)
+       {
+               printf("Fireworkx %s - Pyrotechnics explosions simulation \n", FWXVERSION);
+               printf("Copyright (GPL) 1999-2013 Rony B Chandran <ronybc@gmail.com> \n\n");
+               printf("url: http://www.ronybc.com \n\n");
+               printf("Life = %u\n", st->max_shell_life);
+#ifdef __SSE2__
+               printf("Using SSE2 optimization.\n");
+#endif
+       }
+
+       XGetWindowAttributes(st->dpy,win,&xwa);
+       st->depth = xwa.depth;
+       vi        = xwa.visual;
+       cmap      = xwa.colormap;
+       st->bigendian = (ImageByteOrder(st->dpy) == MSBFirst);
+
+       if(st->depth==8)
+       {
+               st->colors = (XColor *) calloc(sizeof(XColor),st->ncolors+1);
+               writable = False;
+               make_smooth_colormap(xwa.screen, vi, cmap,
+                                     st->colors, &st->ncolors,
+                                    False, &writable, True);
+       }
+       st->gc = XCreateGC(st->dpy, win, 0, &gcv);
+
+       fs = calloc(SHELLCOUNT, sizeof(fireshell));
+       fp = calloc(PIXCOUNT * SHELLCOUNT, sizeof(firepix));
+       st->fireshell_array = fs;
+
+       XGetWindowAttributes (st->dpy, st->window, &xwa);
+       st->depth = xwa.depth;
+
+       resize(st);   /* initialize palakas */
+
+       for (n = 0; n < SHELLCOUNT; n++, fs++)
+       {
+               fs->seq_number = n;
+               fs->fpix = fp;
+               recycle (st, fs, rnd(st->width), rnd(st->height));
+               fp += PIXCOUNT;
+       }
+
+       return st;
+}
+
+static unsigned long
+fireworkx_draw (Display *dpy, Window win, void *closure)
+{
+       struct state *st = (struct state *) closure;
+       fireshell *fs;
+       unsigned int n, q;
+       for (q = FTWEAK; q; q--)
+       {
+               fs = st->fireshell_array;
+               for (n = 0; n < SHELLCOUNT; n++, fs++)
+               {
+                       if (!explode(st, fs))
+                       {
+                               if (st->button_down_p)
+                                  st->deferred++;
+                                else
+                                 recycle(st, fs, rnd(st->width), rnd(st->height));
+                       }
+               }
+       }
+
+        while (!st->button_down_p && st->deferred) {
+          st->deferred--;
+          recycle_oldest(st, rnd(st->width), rnd(st->height));
+        }
+
+       glow_blur(st);
+
+       if (st->flash_on)
+       {
+               chromo_2x2_light(st);
+       }
+
+       put_image(st);
+       return st->delay;
+}
+
+static void
+fireworkx_reshape (Display *dpy, Window window, void *closure,
+                   unsigned int w, unsigned int h)
+{
+       struct state *st = (struct state *) closure;
+       st->width  = w;
+       st->height = h;
+       resize(st);
+}
+
+static Bool
+fireworkx_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+       struct state *st = (struct state *) closure;
+       if (event->type == ButtonPress)
+       {
+               recycle_oldest(st, event->xbutton.x, event->xbutton.y);
+               st->button_down_p = True;
+               return True;
+       }
+       else if (event->type == ButtonRelease)
+       {
+               st->button_down_p = False;
+               return True;
+       }
+
+       return False;
+}
+
+static void
+fireworkx_free (Display *dpy, Window window, void *closure)
+{
+       struct state *st = (struct state *) closure;
+       free(st->mem2);
+       free(st->mem1);
+       free(st->fireshell_array->fpix);
+       free(st->fireshell_array);
+}
+
+static const char *fireworkx_defaults [] =
+{
+       ".background: black",
+       ".foreground: white",
+       "*delay: 10000",  /* never default to zero! */
+       "*maxlife: 32",
+       "*flash: True",
+       "*shoot: False",
+       "*verbose: False",
+       0
+};
+
+static XrmOptionDescRec fireworkx_options [] =
+{
+       { "-delay", ".delay", XrmoptionSepArg, 0 },
+       { "-maxlife", ".maxlife", XrmoptionSepArg, 0 },
+       { "-no-flash", ".flash", XrmoptionNoArg, "False" },
+       { "-shoot", ".shoot", XrmoptionNoArg, "True" },
+       { "-verbose", ".verbose", XrmoptionNoArg, "True" },
+       { 0, 0, 0, 0 }
+};
+
+XSCREENSAVER_MODULE ("Fireworkx", fireworkx)