	
/*
 ocean: a pleasant, undersea background program for SGI consoles
 Version 1.1

 Compile with:
     cc -cckr -O2 -s -o ocean ocean.c -lsun -lbsd -lgl_s -lfm_s -lm -lc_s

 Note: binaries are not interchangeable between IRIX versions or
	different hardware.  I don't have time to track this down.
	If you fix this, please send me the mods.
 Note: If you make any enhancements to ocean, I would greatly appreciate
	receiving a copy/diff of them so I can maintain an uptodate version
	If you just have good ideas but no time, I would appreciate an Email
	describing them anyway!!
 Note: Ocean is available via anonymous FTP from ftp.physics.mcgill.ca

 Unimplemented features (goes under sea_event()):
	- schools of fish = flashes of silver slivers in groups
	- submarine shadow passes by
	- rising bubbles = lighter boules rising from bottom (alpha planes?)
	- real fish = a raster image of a fish passing by on the screen
	- add hooks for playaiff of useful sounds (bubbles, surf, mail, etc.)

 Uncorrected problems/aspects:
	+ cannot use binary interchangeably between Indigo(v4.0.1) and(or)
	Personal(v3.3.2)
	+ optimize drawing by simplifying polygon coordinates and remove
	matrix calls
	+ improve font choice on sign
	+ for users who keep mail in /usr/mail/$USER, remove sign if mail
	becomes smaller or has been accessed since size change
	+ and/or have mail use famd instead of polling

 Based on the background program "night" as written and modified by
	Trevor Paquette, Reuel Nash, tristram@sgi.com & John Mitchell
 Hacked by:
	Loki Jorgenson, loki@Physics.McGill.CA
*/

#include <stdio.h>
#include <gl.h>
#include <device.h>
#include <time.h>
#include <sys/time.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <sys/sysmp.h>
#include <sys/sysinfo.h>

	/* screen divisions */
#define Y1 		0.05
#define Y2 		0.4
#define Y3 		0.7

struct {
  int active;
  float x0,y0,x1,y1;	/* fishing line */
  float x2,y2;		/* hook */
  float x3,y3;		/* lure */
  float x4,y4,x5,y5;	/* corners of sign */
  float radius;
  long boule,stripe;
  time_t lastmod;
  int lastsize;
} fishline;

float drop_point = 0.25;  /* relative lateral position for fishing line */
float drop_depth = Y3;    /* relative vertical position for fishing line */

typedef struct	{
  float x0,y0;
  int i0,in;	} Plant;

#define MAXNUMKELP 30
#define MAXNUMCORAL 20
#define MAXBRANCHS 20
#define MAXSTEPS 64

Plant *kelp[MAXNUMKELP],*shadow[MAXNUMKELP],*coral[MAXNUMCORAL];
float xwalk[MAXSTEPS],ywalk[MAXSTEPS];
int bangle[MAXBRANCHS];

#define MAILCHECKCTR 100	/* tics of the timer between checks */
#define	INTERVAL	1
#define	NSAMPLES (15 /* minutes */ * 60 /* sec/min */ / INTERVAL)

	/* default minimum/maximum kelp/coral length <= MAXSTEPS */
int kelp_shorth = 5,kelp_length = 50, clumpsize = 4;
int shadow_shorth = 4,shadow_length = 25;
int coral_shorth = 5,coral_length = 9, nbranchs = 7;
	/* default maximum number of kelp/coral strands <= MAXNUMKELP */
int nkelps = 15;
int nshadows = 15;
int ncorals = 7;
	/* default color scheme */
int colorscheme = 0;
	/* check mail or not */
int Mail = TRUE, MailNotify = FALSE;
	/* default load average turn off rate */
int loadaverage = 1.00;
	/* efficiency flag */
int Efficient = FALSE;
	/* mail checker vars; check mail almost immediately on start-up */
int Mailctr = 10;
char mailfile[100];
	/* load average vars */
int ringload[NSAMPLES];
int ringindex = 0;
int loadflag = 0;
	/* event noise rate */
int rise_rate = 30;

	/* colors now global so get_sea() works */
long sand[][3] = {
  {123,  97,  34},
  {96, 69,  26},
};
long deepblue[][3] = {
  {0, 44,  42},
  {  0,  42,  58},
};
long midblue[][3] = {
  {  0, 82, 56},
  {  0, 75,102},
};
long jacuzzi[][3] = {
  {  10,  166,  112},
  {  15,  193,  210},
};
	/* cpack colors:  aabbggrr in hexadecimal */
long BrightGreen =	0x0000FF00;
long KelpGreen =	0x00142D26;
long ShadowGreen =	0x002A2C00;
long CoinGold =		0x00006C60;
long CoralRed =		0x00040444;
long ChestBrown1 =	0x001A3030;
long ChestBrown2 =	0x00132323;
long Black =		0x00000000;
long White =		0x00FFFFFF;

/* the sunken wreck is a 12-point polygon and a few thick lines */
int Wreck = TRUE;
float wreck_scale=1.0;
float wreck[][2] = {
  { 0.365, 0.150 }, { 0.315, 0.220 }, { 0.210, 0.210 },
  { 0.135, 0.145 }, { 0.050, 0.155 }, { 0.000, 0.170 },
  { 0.000, 0.060 }, { 0.105, 0.035 }, { 0.240, 0.025 },
  { 0.255, 0.030 }, { 0.295, 0.045 }, { 0.325, 0.065 }, },
mast1[][2] = {
  { 0.095, 0.150 }, { 0.180, 0.250 }, { 0.200, 0.200 }, },
mast2[][2] = {
  { 0.050, 0.155 }, { 0.215, 0.360 }, },
mast3[][2] = {
  { 0.015, 0.170 }, { 0.110, 0.285 }, };

long width, height, owidth, oheight;
float aspect,xdimen,ydimen;

main(argc, argv)
int argc;
char *argv[];
{
 int gid, sea_event(), i;
 short val;
 long dev;
 char *malloc(), *user;
 
   /* parse arguments */
 parse: {
    extern char *optarg;
    extern int optind;
    int c, usage=0;

    while ((c = getopt(argc, argv, "b:c:d:D:eg:hk:lmMr:s:w01")) != -1) {
      switch (c) {
       case 'e': /* -e = efficient mode */
	  Efficient = TRUE;
	  Wreck = FALSE;
	  Mail = FALSE;
	  ncorals = nkelps = nshadows = 0;
	  break;
       case 'g': /* -g (n) = set clump size of kelp */
	  if((clumpsize = atoi(optarg))<1) usage++;
	  break;
       case 'k': /* -k (n) = set number of individual kelp strands */
	  if((nkelps = atoi(optarg)%MAXNUMKELP)<0) usage++;
	  break;
       case 's': /* -s (n) = set number of shadow kelp strands */
	  if((nshadows = atoi(optarg)%MAXNUMKELP)<0) usage++;
	  break;
       case 'w': /* -w = show the ship wreck */
	  Wreck = TRUE;
	  break;
       case 'b': /* -b (n) = set number of branchs in a coral stands */
	  if((nbranchs = atoi(optarg)%MAXBRANCHS)<0) usage++;
	  break;
       case 'c': /* -c (n) = set number of coral stands */
	  if((ncorals = atoi(optarg)%MAXNUMCORAL)<0) usage++;
	  break;
       case 'r': /* -r (n) = set rate of rise */
	  rise_rate = atoi(optarg);
	  break;
       case 'm': /* -m/M = check mail or not */
	  Mail = TRUE;
	  break;
       case 'M':
	  Mail = TRUE;
	  MailNotify = TRUE;
	  break;
       case 'd': /* -d (n) = where along X-axis to drop fishing line */
	  drop_point = atof(optarg);
	  if(drop_point<0.||drop_point>1.0) usage++;
	  break;
       case 'D': /* -D (n) = where along Y-axis to drop fishing line */
	  drop_depth = atof(optarg);
	  if(drop_depth<0.||drop_depth>1.0) usage++;
	  break;
       case 'l': /* -l = toggle load average checker */
	  loadaverage = 100000;
	  break;
       case '0': /* -0/1 = colorscheme number */
	  colorscheme = 0;
          break;
       case '1':
	  colorscheme = 1;
          break;

       default:
	  usage++;
       }
       if (usage) {
	  fprintf(stderr, "usage: %s [-b #branchs] [-c #coral] [-d drop_point] [-D drop_depth] [-e]\n	[-g clumpsize] [-h] [-k #kelp] [-l] [-m/M] [-s #shadows] [-w] [-0/1]\n",argv[0]);
	  fprintf(stderr,"             b: # of branches per coral stand; [1-%d] (default %d)\n",MAXBRANCHS,nbranchs);
	  fprintf(stderr,"             c: # of coral stands ; [0-%d] (default %d)\n", MAXNUMCORAL, ncorals);
	  fprintf(stderr,"             d: lateral drop point of fishing line from left; [0.-1.] (default %3g)\n",drop_point);
	  fprintf(stderr,"             D: vertical drop point of fishing line from left; [0.-1.] (default %3g)\n",drop_depth);
	  fprintf(stderr,"             e: toggle efficiency mode; limits features to minimum (default off)\n");
	  fprintf(stderr,"                (set first and then modify with other options; timer is off)\n");
	  fprintf(stderr,"             g: kelp strands per clump; [1- ] (default %d)\n", clumpsize);
	  fprintf(stderr,"             h: print this output\n");
	  fprintf(stderr,"             k: # of kelp strands ; [0-%d] (default %d)\n", MAXNUMKELP, nkelps);
	  fprintf(stderr,"             l: toggle load average slowing down rate(default on)\n");
	  fprintf(stderr,"                (when load increases, less cpu is used by %s)\n",argv[0]);
	  fprintf(stderr,"             m: mail checker (fishing line; default on)\n");
	  fprintf(stderr,"             M: mail checker with bell; (default off)\n");
	  fprintf(stderr,"             s: # of kelp shadows ; [0-%d] (default %d)\n", MAXNUMKELP, nshadows);
	  fprintf(stderr,"             w: toggle ship wreck shadow (default on)\n");
	  fprintf(stderr,"             0/1: colorscheme (default %d)\n",colorscheme);
	  exit(0);
       }
    }
 }

 user = (char *)getlogin(NULL);
 if(!user)
    user = getpwuid(getuid())->pw_name;
 sprintf(mailfile, "/usr/mail/%s", user);

#ifdef DEBUG
 foreground();
#else
 imakebackground() ;
#endif
 gid = winopen("") ;
#ifdef DEBUG
 getsize(&owidth, &oheight);
#else
 owidth = XMAXSCREEN;
 oheight = YMAXSCREEN;
#endif
 RGBmode();
 gconfig();
 shademodel(GOURAUD);
	
 aspect = ((float)owidth/(float)oheight);
 xdimen = (float)XMAXMEDIUM/(float)getgdesc(GD_XPMAX);
 ydimen = (float)YMAXMEDIUM/(float)getgdesc(GD_YPMAX);
 ortho2(0.0, aspect, 0.0, 1.0) ;

 glcompat(GLC_OLDPOLYGON,0);

 if(!Efficient)	{
 	qdevice(TIMER1);
	noise(TIMER1, rise_rate);
		}
 qenter(REDRAW,gid);

 setup_sea();

 while (1) {
   dev = qread(&val) ;
   switch(dev) {
   case REDRAW:
#ifdef DEBUG
        getsize(&width, &height);
	if((width != owidth) || (height != oheight)) {
          reshapeviewport();
          owidth = width;
	  oheight = height;
	}
#endif
	draw_background();
	break;
   case TIMER1:    
	sea_event();
	break;
   }
 }
}


draw_background()
{
 int i00,inum,sea_event();
 float v1[2],v2[2],v3[2],v4[2];
 register int i,j,k;

 v1[0] = v4[0] = 0.0;
 v2[0] = v3[0] = aspect;

 v1[1] = v2[1] = 0.0;
 v3[1] = v4[1] = Y1;
	/* draw the Gouraud-shaded background in three sections */
 bgnpolygon();
   c3i(sand[colorscheme]);
   v2f(v1);
   v2f(v2);
   c3i(deepblue[colorscheme]);
   v2f(v3);
   v2f(v4);
 endpolygon();

 v1[1] = v2[1] = Y2;
 bgnpolygon();
   c3i(deepblue[colorscheme]);
   v2f(v4);
   v2f(v3);
   c3i(midblue[colorscheme]);
   v2f(v2);
   v2f(v1);
 endpolygon();

 v3[1] = v4[1] = 1.0;
 bgnpolygon();
   c3i(midblue[colorscheme]);
   v2f(v1);
   v2f(v2);
   c3i(jacuzzi[colorscheme]);
   v2f(v3);
   v2f(v4);
 endpolygon();

	/* draw the shadow outline of a sunken wreck FIRST!! */
if(Wreck)	{
 cpack(get_sea(Y1+wreck[8][1]*wreck_scale));
 pushmatrix();
 rot(180.0,'y');
 translate(-aspect,wreck[8][1],0.0);
 scale(wreck_scale,wreck_scale,wreck_scale);
 bgnpolygon();
   for(i=0;i<12;i++) v2f(wreck+i);
 endpolygon();
 linewidth((short)(10*wreck_scale));
 bgnline();
   v2f(mast2);  v2f(mast2+1);
 endline();
 linewidth((short)(8*wreck_scale));
 bgnline();
   v2f(mast1);  v2f(mast1+1);  v2f(mast1+2);
 endline();
 bgnline();
   v2f(mast3);  v2f(mast3+1);
 endline();
 popmatrix();	}
	/* draw shadows of kelp */
 linewidth(5);
 for(j=0; j<nshadows; j++){
   v1[0] = shadow[j]->x0;
   v1[1] = shadow[j]->y0;
   cpack(get_sea(shadow[j]->y0));
   bgnline();
     v2f(v1);
     for(i=shadow[j]->i0;i<shadow[j]->in;i++)	{
       v1[0] += xwalk[i%MAXSTEPS];
       v1[1] += ywalk[i%MAXSTEPS];
       v2f(v1);			 		}
   endline();	
	/* now shift i0 parameter slightly to get moving effect */
   shadow[j]->i0 = shadow[j]->i0++;
   shadow[j]->in = shadow[j]->in++;
			}
 linewidth(10);
 cpack(KelpGreen);
 for(j=0; j<nkelps; j++){
   v1[0] = kelp[j]->x0;
   v1[1] = kelp[j]->y0;
   bgnline();
     v2f(v1);
     for(i=kelp[j]->i0;i<kelp[j]->in;i++)	{
       v1[0] += xwalk[i%MAXSTEPS];
       v1[1] += ywalk[i%MAXSTEPS];
       v2f(v1);			 		}
   endline();	
	/* now shift i0 parameter slightly to get moving effect */
   kelp[j]->i0 = kelp[j]->i0++;
   kelp[j]->in = kelp[j]->in++;
			}

 linewidth(3);
 cpack(CoralRed);
 for(j=0; j<ncorals; j++)	{
  pushmatrix();
    translate(coral[j]->x0,coral[j]->y0,0);
    i00 = coral[j]->i0;
    inum = coral[j]->in-coral[j]->i0;
    for(k=0;k<nbranchs;k++)	{
     pushmatrix();
      rotate(bangle[k],'z');
      bgnline();
       v1[0] = v1[1] = 0.; v2f(v1);
       for(i=0;i<inum;i++,i00++)	{
         v1[0] += xwalk[i00%MAXSTEPS];
         v1[1] += ywalk[i00%MAXSTEPS];
         v2f(v1);		 		}
      endline();	
     popmatrix();		}
   popmatrix();
	/* now shift coral parameters slightly to get moving effect */
   coral[j]->i0 = coral[j]->i0++;
   coral[j]->in = coral[j]->in++;
				}
	/* if Mail is on and user has mail, draw fishing line */
 if(fishline.active) {
   linewidth(1);
   cpack(ShadowGreen);
   bgnline();
     v2f(&fishline.x0);
     v2f(&fishline.x1);
     v2f(&fishline.x2);
   endline();
   cpack(fishline.boule);
   circf(fishline.x3,fishline.y3,fishline.radius);
   cpack(fishline.stripe);
   rectf(fishline.x3-fishline.radius,fishline.y3-fishline.radius*0.5,fishline.x3+fishline.radius,fishline.y3+fishline.radius*0.5);
   cpack(White);
   rectf(fishline.x4,fishline.y4,fishline.x5,fishline.y5);
   cpack(Black);
   cmov2(fishline.x4,fishline.y4-0.02);	charstr("  You");
   cmov2(fishline.x4,fishline.y4-0.033); charstr(" have");
   cmov2(fishline.x4,fishline.y4-0.048); charstr(" mail");
   cpack(ShadowGreen);
   bgnline();
     v2f(&fishline.x1);
     v2f(&fishline.x2);
   endline();
 		   }
}

/* ----------------------------------------------------------------------
 *  Random number generator
 *  Author: Trevor Paquette
 *  Modified 6/25: Reuel Nash
 *  Purpose: returns a random number between x and y
 *           (positive only)
 */

long
trand(x,y)
int x,y;
{
 long ret;
 static int seeded = 0;

 if(!seeded) {
   srand((int)time(0));
   seeded = 1;
 }

 ret = x + rand() % (y-x+1);
 return(ret);
}
						       

sea_event()
{
 /* anything like bubbles, subs, fish, etc. should be added here */
 long event, trand(), tmp;
 char *malloc();
 float load(), l;

 l = load();
 if(l > (float)loadaverage) {
   noise(TIMER1, rise_rate * (int)(l*2.5));
 }
 else {
    noise(TIMER1, rise_rate);
 }

   /* check for mail.  If there is, drop the fishing line */
 if(Mail)	{
   if(Mailctr-- < 0)	{
     struct stat statb;
     int prevstat = fishline.active,prevsize = fishline.lastsize;

     if(!stat(mailfile, &statb))	{
	if((fishline.lastsize=statb.st_size)) fishline.active = TRUE;
	else fishline.active = FALSE;
     		} else {
	fishline.lastsize = 0;
	fishline.active = FALSE;	}
     if(fishline.lastsize>prevsize&&MailNotify)
	{ setbell(2); ringbell(); setbell(1); }
     if(fishline.active != prevstat) reel_inout(fishline.active);
	/* check thrice as often if mail has already been detected */
     if(fishline.active) Mailctr = MAILCHECKCTR/3;
     else Mailctr = MAILCHECKCTR;
   			 }
		}
}

 	/* drop/raise fishing line event */
reel_inout(dirtn)
int dirtn;
{
float inc=0.01,dinc,b1[2],b2[2],b3[2],b4[2],f1[2],f2[2],start,stop,depth;

if(dirtn==FALSE)
	{ dinc = inc; start = fishline.y1; stop = 1.0;	/* raise line */
		/* erase the sign first */
	b4[0] = fishline.x4; b4[1] = fishline.y5;
	b1[0] = fishline.x5; b1[1] = fishline.y4;
	bgnpolygon();
	   cpack(get_sea(b4[1]));	v2f(b4); v2f(&(fishline.x5));
	   cpack(get_sea(b1[1]));	v2f(b1); v2f(&(fishline.x4));
	endpolygon();
					}
else	
	{ dinc = -inc; start = -1.0; stop = -fishline.y1; }/* drop line */
	/* set up background polygons */
   b1[0] = b3[0] = fishline.x0 - fishline.radius*1.5;
   b2[0] = b4[0] = fishline.x0 + fishline.radius*1.5;
   b1[1] = b2[1] = 1.0;
   b3[1] = b4[1] = fabs(start) - 2*inc;
	/* set up fishing line vectors */
   f1[0] = fishline.x1;	f2[0] = f1[0] + fishline.radius;
   f1[1] = fabs(start);	f2[1] = f1[1] + fishline.radius;

   linewidth(1);
   for(depth=start;depth<=stop;depth+=inc)	{
      /* first draw Gouraud-shaded box over background */
	bgnpolygon();
	   cpack(get_sea(b4[1]));	v2f(b4); v2f(b3);
	   c3i(jacuzzi[colorscheme]);	v2f(b1); v2f(b2);
	endpolygon();
      /* then draw fishing line */
	cpack(ShadowGreen);
	bgnline();
		v2f(&fishline.x0); v2f(f1); v2f(f2);
	endline();
     /* finally draw the lure; a striped ball */
	cpack(fishline.boule);
	circf(f1[0],f1[1]+3*fishline.radius,fishline.radius);
	cpack(fishline.stripe);
	rectf(f1[0]-fishline.radius,f1[1]+fishline.radius*2.5,f1[0]+fishline.radius,f1[1]+fishline.radius*3.5);
	sginap(10);
	f1[1] += dinc; f2[1] += dinc;
	b3[1] += dinc; b4[1] += dinc;			}
	/* draw sign and barb */
if(dirtn==TRUE)	{
   cpack(White);
   rectf(fishline.x4,fishline.y4,fishline.x5,fishline.y5);
   cpack(Black);
   cmov2(fishline.x4,fishline.y4-0.02); charstr("  You");
   cmov2(fishline.x4,fishline.y4-0.033); charstr(" have");
   cmov2(fishline.x4,fishline.y4-0.048); charstr(" mail");
   cpack(ShadowGreen);
   bgnline();
     v2f(&fishline.x1);
     v2f(&fishline.x2);
   endline();
		}
}

setup_sea()
{
 register int i, j;
 long tmp, trand();
 float t,X0=0.0;

 /* establish random walk list for quick look-up; each step is in (0,1) */
 for(i=0;i<MAXSTEPS;i++)	{
   xwalk[i] = 0.01*(((float)trand(0,32767))/16383.0-1.0);
   ywalk[i] = 0.02*((float)trand(0,32767))/32767.0;
				}
 /* establish list of kelp and shadow strands and anchor positions */
 for(i=0;i<nshadows;i++)	{
   if(!(i%clumpsize)) X0 = aspect*((float)trand(0,32767))/32767.0;
   shadow[i] = (Plant *) malloc(sizeof(Plant));
   shadow[i]->x0 = 0.05*((float)trand(0,32767))/32767.0 + X0;
   shadow[i]->y0 = Y1*(1.0+0.2*((float)trand(0,32767))/16383.0);
   shadow[i]->i0 = trand(0,MAXSTEPS);
   shadow[i]->in = shadow[i]->i0 + trand(shadow_shorth,shadow_length);
			}
 for(i=0;i<nkelps;i++)	{
   if(!(i%clumpsize)) X0 = aspect*((float)trand(0,32767))/32767.0;
   kelp[i] = (Plant *) malloc(sizeof(Plant));
   kelp[i]->x0 = 0.05*((float)trand(0,32767))/32767.0 + X0;
   kelp[i]->y0 = Y1*(0.7+0.2*(((float)trand(0,32767))/16383.0-1.0));
   kelp[i]->i0 = trand(0,MAXSTEPS);
   kelp[i]->in = kelp[i]->i0 + trand(kelp_shorth,kelp_length);
			}
 /* establish list of coral stands and anchor positions */
 for(i=0;i<ncorals;i++)	{
   coral[i] = (Plant *) malloc(sizeof(Plant));
   coral[i]->x0 = aspect*((float)trand(0,32767))/32767.0;
   coral[i]->y0 = Y1*(0.3+0.1*(((float)trand(0,32767))/16383.0-1.0));
   coral[i]->i0 = trand(0,MAXSTEPS);
   coral[i]->in = coral[i]->i0 + trand(coral_shorth,coral_length);
			}
  /* angles between branches on coral stands */
 for(j=1800/(nbranchs+2),i=0;i<nbranchs;i++)
   bangle[i] = j + trand(i*j,(i+1)*j) - 900;

	/* set up fishing line */
     fishline.x0 = fishline.x1 = drop_point*aspect;
     fishline.y0 = 1.0; fishline.y1 = drop_depth;
     fishline.radius = 0.005;
		/* barb position */
     fishline.x2 = fishline.x1 + fishline.radius;
     fishline.y2 = fishline.y1 + fishline.radius;
		/* lure position */
     fishline.x3 = fishline.x1;
     fishline.y3 = fishline.y1 + 3*fishline.radius;
		/* sign position */
     fishline.x4 = fishline.x1 - 0.035*xdimen;
     fishline.y4 = fishline.y1 + fishline.radius;
     fishline.x5 = fishline.x1 + 0.035*xdimen;
     fishline.y5 = fishline.y1 - 0.05;
     fishline.boule = CoralRed; 
     fishline.stripe = CoinGold;
     fishline.active = FALSE;
     fishline.lastsize = 0;
     fishline.lastmod = time(0);
}

long
get_sea(y)
float y;
{
    int r,g,b;

    if(y < Y1) {
	y /= Y1;
	r = sand[colorscheme][0] * (1.-y) + deepblue[colorscheme][0] * y;
	g = sand[colorscheme][1] * (1.-y) + deepblue[colorscheme][1] * y;
	b = sand[colorscheme][2] * (1.-y) + deepblue[colorscheme][2] * y;
    } else if( y < Y2) {
	y = (y - Y1)/(Y2 - Y1);
	r = deepblue[colorscheme][0] * (1.-y) + midblue[colorscheme][0] * y;
	g = deepblue[colorscheme][1] * (1.-y) + midblue[colorscheme][1] * y;
	b = deepblue[colorscheme][2] * (1.-y) + midblue[colorscheme][2] * y;
    } else {
	y = (y - Y2)/(1 - Y2);
	r = midblue[colorscheme][0] * (1.-y) + jacuzzi[colorscheme][0] * y;
	g = midblue[colorscheme][1] * (1.-y) + jacuzzi[colorscheme][1] * y;
	b = midblue[colorscheme][2] * (1.-y) + jacuzzi[colorscheme][2] * y;
    }

    return (r&0xff) + ((g&0xff)<<8) + ((b&0xff)<<16);
}

float load()
{
/*
 From: rayan@ai.toronto.edu (Rayan Zachariassen)
 Subject: Re: Finding load average on Iris 4D
*/
 struct sysinfo sinfo;
 int now, last1, last5, last15;

 now = ringindex;
 sysmp(MP_SAGET, MPSA_SINFO, &sinfo, sizeof sinfo);
 ringload[ringindex++] = sinfo.runque;
 if(!loadflag) {
   while(loadflag < NSAMPLES)
     ringload[loadflag++] = sinfo.runque;
 }
 ringindex %= NSAMPLES;
 last1 = (NSAMPLES + now - 60 / INTERVAL)%NSAMPLES;
 return((float)((ringload[now] - ringload[last1])/60.0));
}

