// This is a program that operates on a very low level. It draws
// things in the root window.


// The lack of comments is somewhat intentional.  Basically: Draw a
// lot of snow falling on the background, more or less like the old
// xsnow program.

// This program is the only GTK program I have written that is not
// event driven. There is not much GTK in here, though, this program
// use almost exclusively GDK function calls. /per

GDK.Window root;
float mdx, mdy; // Size of the root window. 
int windy, edx; // is it windy today? edx is the wind direction.

#define I(X) Image.PNM.decode(Stdio.read_bytes("snow0" #X ".pbm"))->invert()

array (GDK.GC) snow_flake_gcs = ({});
array (Image.image) snow_flakes=({I(0), I(1), I(2),  I(3), I(4), I(5), I(6)});

class Snowflake
{
  GDK.GC gc;
  static float x, y, dx, dy, dx2;
  static int ox, oy, xsize, ysize;
  void step()
  {
    dx += (random(10)-4.5)/2.0;
    if(abs(dx) > 10.0) dx = 10.0*sgn(dx);
    x += dx; y += dy;
    if(windy)
    {
      if(windy > 100) dx2 += edx * 0.2;
      else if(windy<=100) dx2 -= edx * 0.2;
      x+=dx2;
    }
    int ix = (int)x;
    int iy = (int)y;
    if(ix != ox || iy != oy)
    {
      if(y > mdy)
      {
        dx = (random(20)/10.0)-2.0;
        dy = (random(90)/10.0)+0.5;
        y = 0.0;
      }
      else if(x<0.0)
        x = mdx;
      else if(x>mdx)
        x = 0.0;
      root->clear( ox,oy, xsize, ysize );
      ox = ix; oy = iy;
      gc->set_clip_origin(ix,iy);
      root->draw_rectangle(gc,1,ix,iy,xsize,ysize);
    }
  }

  void create()
  {
    int num = random(7);
    x = (float)random(root->xsize()-8); y = (float)random(100);
    dx = (random(20)/10.0)-2.0;         dy = (random(90)/10.0)+0.5;
    xsize = snow_flakes[num]->xsize();  ysize = snow_flakes[num]->ysize();
    gc = snow_flake_gcs[ num ];
  }
}

array snow = ({});
void make_some_snow()
{
  GDK.Color white = GDK.Color( 255,255,255 );
  for(int i = 0; i<sizeof(snow_flakes); i++)
  {
    GDK.Bitmap b = GDK.Bitmap( snow_flakes[i] );
    snow_flake_gcs += ({ GDK.GC( root ) });
    snow_flake_gcs[i]->set_clip_mask( b );
    snow_flake_gcs[i]->set_foreground( white );
  }
  snow = Array.map(allocate(50),Snowflake);
}

void move_snow()
{
  for(;;)
  {  
    if(!windy)
    {
      if(!random(1000))
      {
        windy = 200;
        edx = (random(2)?-1:1);
      }
    } else
      windy--;
    snow->step();
    GTK.flush();
    sleep(0.03);
  }
}

int main()
{
  GTK.setup_gtk();
  root = GTK.root_window();

  // Is there any 'fake' root windows out there to draw the snow in?
  array(mapping) maybe
    =root->children()->get_property(GDK.Atom.__SWM_VROOT)-({0});
  if(sizeof(maybe))
      root = GDK.Window( maybe[0]->data[0] );
  mdx = (float)root->xsize(); mdy = (float)root->ysize();
  make_some_snow();
  move_snow();
}
