/*
 * wmavgload
 * totally inspired from wmload ... see the README file 
 * Anthony Mallet, mallet@laas.fr, November of 97
 * Not every platform well tested. Please tell me if you run into problem.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <math.h>
#include <fcntl.h>
#include <sys/signal.h>

#ifndef linux
#include <sys/param.h>
#include <rpcsvc/rstat.h>

#ifdef SVR4
#include <netdb.h>
#ifndef hpux
#include <sys/systeminfo.h>
#endif
#endif /* SVR4 */
#endif /* linux */

#include <X11/Xlib.h>
#include <X11/xpm.h>
#include <X11/extensions/shape.h>
#include <X11/Xatom.h>

#ifdef MANY_COLORS
#include "./back_full_color.xpm"
#else
#include "./back_less_color.xpm"
#endif
#include "./mask2.xbm"
#include "./mask.xpm"

#define major_VER 0
#define minor_VER 7
#define patch_VER 0

#define MW_EVENTS   (ExposureMask | ButtonPressMask | StructureNotifyMask)

#ifdef linux
#define FALSE 0
#endif

#define SIZE 52-6 /* x-size - 2 * 3 pixels for the bars */ 

/* x & y coordinates inthe pixmap */
#define Shape(num) (ONLYSHAPE ? num-5 : num)

/* load in pixel unit */
#define _(num) ((int)((num)*52.0/scale))

/* convert 16bit color to 8bit */
#define _r(c) ((int)((float)c/256.0))

/* clear enough ? :) */
#define max3(a,b,c) ((a)>(b)?((a)>(c)?(a):(c)):((b)>(c)?(b):(c)))
#define mid3(a,b,c) ((a)>(b)?((b)>(c)?(b):(c)):((a)>(c)?(a):(c)))
#define min3(a,b,c) ((a)>(b)?((b)>(c)?(c):(b)):((a)>(c)?(c):(a)))

/* Global Data storage/structures ********************************************/

int ONLYSHAPE=0; /* default value is noshape */

int updatespeed = 4; /* default */

static char *help_message[] = {
"where options include:",
"    -u <secs>               updatespeed",            
"    -exe <program>          program to start on click",
"    -led <color>            color of the led",
"    -1bar <color>           color of the first bar",
"    -2bar <color>           color of the second bar",
"    -a <color>              color of the scale marks",
"    -position [+|-]x[+|-]y  position of wmavgload",
"    -shape                  without groundplate",
"    -iconic                 start up as icon",
"    -withdrawn              start up withdrawn",
"    -ver                    output version",
NULL
};

int scale;   /* current scale */
float maxload; /* maximum value displayed */
float loads[SIZE]; /* the stored load values (for resizing) */
float loads_2,loads_3; /* for the bars */

#ifndef linux
extern char hostname[MAXHOSTNAMELEN];
extern struct statstime res;
#endif

/* X11 Variables *************************************************************/

Display *dpy;	  /* which DISPLAY */
Window Root;      /* Root window :) */
int screen;
int x_fd;
int d_depth;
XSizeHints mysizehints;
XWMHints mywmhints;
Pixel back_pix, fore_pix;
GC NormalGC;
Window iconwin, win;       /* My home is my window */
char *ProgName;
char *Geometry;

char *LedColor = "#a0a0c5";
char *Bar1Color = "Yellow";
char *Bar2Color = "Orange";
char *ScaleColor = "Red";

#define DEFAULTEXECUTE "echo no program has been specified >/dev/console"

char *Execute;
char *ERR_colorcells = "not enough free color cells\n";
char *ampers = " &";

/* XPM Structures & Variables ************************************************/

typedef struct _XpmIcon {
    Pixmap pixmap;
    Pixmap mask;
    XpmAttributes attributes;
}        XpmIcon;

XpmIcon wmavgload;
XpmIcon visible;
time_t actualtime;
long actualmin;

/* Function definitions ******************************************************/

void sig_alarm_handler(int);
void GetXPM(void);
Pixel GetColor(char *name);
void RedrawWindow( XpmIcon *v);
void InitLoad();
void InsertLoad();
void Change_Scale();

extern void GetLoad(float *,float*,float *);

/*****************************************************************************/
/* Source Code <--> Function Implementations                                 */
/*****************************************************************************/

void
sig_alarm_handler(int dummy)
{
   InsertLoad();
   RedrawWindow(&visible);
   signal(SIGALRM, sig_alarm_handler);
   alarm(updatespeed);
}

void usage()
{
  char **cpp;

  fprintf(stderr,"\nusage:  %s [-options ...] \n", ProgName);
  for (cpp = help_message; *cpp; cpp++) {
    fprintf(stderr, "%s\n", *cpp);
  }
  fprintf(stderr,"\n");
  exit(1);
}

int main(int argc,char *argv[])
{
  int i;
  unsigned int borderwidth ;
  char *display_name = NULL; 
  char *wname = "wmavgload";
  XGCValues gcv;
  unsigned long gcm;
  XEvent Event;
  XTextProperty name;
  XClassHint classHint;
  Pixmap pixmask;
  Geometry = "";
  mywmhints.initial_state = NormalState;

  /* Parse command line options */
  ProgName = argv[0];

  /* must use malloc() for default execute string because realloc()
     requires a memory block previously obtained using malloc(). */

  if (! (Execute = malloc(strlen(DEFAULTEXECUTE)+1)) ) {
     fprintf(stderr, "No memory for execute string\n");
     exit(1);
  }

  strcpy(Execute, DEFAULTEXECUTE); 

  for(i=1;i<argc;i++) {
    char *arg= argv[i];

    if (arg[0] == '-') {
      switch(arg[1]) {
	 case 'u':
	    if(++i >=argc) usage();
	    sscanf(argv[i], "%d", &updatespeed);
	    continue;
	 case 'e':
	    if(++i >=argc) usage();
	    if (strlen(argv[i])+3 > strlen(Execute))
	       if (! (Execute = realloc(Execute, strlen(argv[i])+3)) ) {
		  fprintf(stderr, "No memory for execute string\n");
	       }
	    strcpy(Execute, argv[i]);
	    strcat(Execute, " &");
	    continue;
	 case 's':
	    ONLYSHAPE=1;
	    continue;
	 case 'p':
	    if(++i >=argc) usage();
	    Geometry = argv[i];
	    continue;
	 case 'i':
	    mywmhints.initial_state = IconicState;
	    continue;
	 case 'w':
	    mywmhints.initial_state = WithdrawnState;
	    continue;
	 case 'l':
	    if(++i >=argc) usage();
	    LedColor = argv[i];
	    continue;
	 case '1':
	    if(++i >=argc) usage();
	    Bar1Color = argv[i];
	    continue;
	 case '2':
	    if(++i >=argc) usage();
	    Bar2Color = argv[i];
	    continue;
	 case 'a':
	    if(++i >=argc) usage();
	    ScaleColor = argv[i];
	    continue;
	 case 'v':
	    fprintf(stdout, "\nwmavgload version: %i.%i.%i\n",
		    major_VER, minor_VER, patch_VER);
	    if(argc == 2) exit(0);
	    continue;
	 default:
	    usage();
      }
    }
    else
    {
       fprintf(stderr, "\nInvalid argument: %s\n", arg);
       usage();
    }
  }
  
  /* Open the display */
  if (!(dpy = XOpenDisplay(display_name))) { 
     fprintf(stderr,"wmavgload: can't open display %s\n", 
	     XDisplayName(display_name)); 
     exit (1); 
  } 
  
  screen= DefaultScreen(dpy);
  Root = RootWindow(dpy, screen);
  d_depth = DefaultDepth(dpy, screen);
  x_fd = XConnectionNumber(dpy);
  
  /* Convert XPM Data to XImage */
  GetXPM();
  
  /* Create a window to hold the banner */
  mysizehints.flags= USSize|USPosition;
  mysizehints.x = 0;
  mysizehints.y = 0;

  back_pix = GetColor("white");
  fore_pix = GetColor("black");

  XWMGeometry(dpy, screen, Geometry, NULL, (borderwidth =1), &mysizehints,
	      &mysizehints.x,&mysizehints.y,&mysizehints.width,&mysizehints.height, &i); 

  mysizehints.width = wmavgload.attributes.width;
  mysizehints.height= wmavgload.attributes.height;

  win = XCreateSimpleWindow(dpy,Root,mysizehints.x,mysizehints.y,
			    mysizehints.width,mysizehints.height,
			    borderwidth,fore_pix,back_pix);
  iconwin = XCreateSimpleWindow(dpy,win,mysizehints.x,mysizehints.y,
				mysizehints.width,mysizehints.height,
				borderwidth,fore_pix,back_pix);

  /* activate hints */
  XSetWMNormalHints(dpy, win, &mysizehints);
  classHint.res_name =  "wmavgload";
  classHint.res_class = "WMAvgload";
  XSetClassHint(dpy, win, &classHint);

  XSelectInput(dpy,win,MW_EVENTS);
  XSelectInput(dpy,iconwin,MW_EVENTS);
  XSetCommand(dpy,win,argv,argc);
  
  if (XStringListToTextProperty(&wname, 1, &name) ==0) {
    fprintf(stderr, "wmavgload: can't allocate window name\n");
    exit(-1);
  }
  XSetWMName(dpy, win, &name);
  
  /* Create a GC for drawing */
  gcm = GCForeground|GCBackground|GCGraphicsExposures;
  gcv.foreground = fore_pix;
  gcv.background = back_pix;
  gcv.graphics_exposures = FALSE;
  NormalGC = XCreateGC(dpy, Root, gcm, &gcv);  

  if (ONLYSHAPE) { /* try to make shaped window here */
    pixmask = XCreateBitmapFromData(dpy, win, mask2_bits, mask2_width, 
				    mask2_height);
    XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, pixmask, ShapeSet);
    XShapeCombineMask(dpy, iconwin, ShapeBounding, 0, 0, pixmask, ShapeSet);
  }
  
  mywmhints.icon_window = iconwin;
  mywmhints.icon_x = mysizehints.x;
  mywmhints.icon_y = mysizehints.y;
  mywmhints.window_group = win;
  mywmhints.flags = StateHint | IconWindowHint | IconPositionHint
      | WindowGroupHint;
  XSetWMHints(dpy, win, &mywmhints); 

  XMapWindow(dpy,win);
  InitLoad();
  InsertLoad();
  RedrawWindow(&visible);
  signal(SIGALRM, sig_alarm_handler);
  alarm(updatespeed);
  while(1)
  {
     XNextEvent(dpy,&Event);
     switch(Event.type)
     {
	case Expose:
	   if(Event.xexpose.count == 0 )
	      RedrawWindow(&visible);
	   break;
	case ButtonPress:
	   system(Execute);
	   break;
	case DestroyNotify:
	   XFreeGC(dpy, NormalGC);
	   XDestroyWindow(dpy, win);
	   XDestroyWindow(dpy, iconwin);
	   XCloseDisplay(dpy);
	   exit(0); 
	default:
	   break;      
     }
  }
}

/*****************************************************************************/
void nocolor(char *a, char *b)
{
 fprintf(stderr,"wmavgload: can't %s %s\n", a,b);
}

/*****************************************************************************/
/* convert the XPMIcons to XImage */
void GetXPM(void)
{
  static char **alt_xpm;
  XColor col;
  XWindowAttributes attributes;
  int ret;
  static char tempc1[12],tempc2[12],tempc3[12],tempc4[12];

  alt_xpm =ONLYSHAPE ? mask_xpm : back_xpm;

  /* for the colormap */
  XGetWindowAttributes(dpy,Root,&attributes);

  if (!XParseColor (dpy, attributes.colormap, LedColor, &col)) 
      nocolor("parse",LedColor);
  else
  {
     sprintf(tempc1, "Q c #%.2x%.2x%.2x",_r(col.red),_r(col.green),_r(col.blue));
#ifdef MANY_COLORS
     back_xpm[45] = tempc1;
#else
     back_xpm[5] = tempc1;
#endif
  }

  if (!XParseColor (dpy, attributes.colormap, Bar1Color, &col)) 
      nocolor("parse",Bar1Color);
  else
  {
     sprintf(tempc2, "R c #%.2x%.2x%.2x",_r(col.red),_r(col.green),_r(col.blue));
#ifdef MANY_COLORS
     back_xpm[46] = tempc2;
#else
     back_xpm[6] = tempc2;
#endif
  }

  if (!XParseColor (dpy, attributes.colormap, Bar2Color, &col)) 
      nocolor("parse",Bar2Color);
  else
  {
     sprintf(tempc3, "S c #%.2x%.2x%.2x",_r(col.red),_r(col.green),_r(col.blue));
#ifdef MANY_COLORS
     back_xpm[47] = tempc3;
#else
     back_xpm[7] = tempc3;
#endif
  }

  if (!XParseColor (dpy, attributes.colormap, ScaleColor, &col)) 
      nocolor("parse",ScaleColor);
  else
  {
     sprintf(tempc4, "T c #%.2x%.2x%.2x",_r(col.red),_r(col.green),_r(col.blue));
#ifdef MANY_COLORS
     back_xpm[48] = tempc4;
#else
     back_xpm[8] = tempc4;
#endif
  }
     
  wmavgload.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
  ret = XpmCreatePixmapFromData(dpy, Root, alt_xpm, &wmavgload.pixmap, 
				&wmavgload.mask, &wmavgload.attributes);
  if(ret != XpmSuccess)
    {fprintf(stderr, ERR_colorcells);exit(1);}

  visible.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
  ret = XpmCreatePixmapFromData(dpy, Root, back_xpm, &visible.pixmap, 
				&visible.mask, &visible.attributes);
  if(ret != XpmSuccess)
    {fprintf(stderr, ERR_colorcells);exit(1);}

}

/*****************************************************************************/
/* Removes expose events for a specific window from the queue */
int flush_expose (Window w)
{
  XEvent dummy;
  int i=0;
  
  while (XCheckTypedWindowEvent (dpy, w, Expose, &dummy))i++;
  return i;
}

/*****************************************************************************/
/* Draws the icon window */
void RedrawWindow( XpmIcon *v)
{
  flush_expose (iconwin);
  XCopyArea(dpy,v->pixmap,iconwin,NormalGC,
	    0,0,v->attributes.width, v->attributes.height,0,0);
  flush_expose (win);
  XCopyArea(dpy,v->pixmap,win,NormalGC,
	    0,0,v->attributes.width, v->attributes.height,0,0);

}

/*****************************************************************************/
Pixel GetColor(char *name)
{
  XColor color;
  XWindowAttributes attributes;

  XGetWindowAttributes(dpy,Root,&attributes);
  color.pixel = 0;
   if (!XParseColor (dpy, attributes.colormap, name, &color)) 
     {
       nocolor("parse",name);
     }
   else if(!XAllocColor (dpy, attributes.colormap, &color)) 
     {
       nocolor("alloc",name);
     }
  return color.pixel;
}

/** True stuff begins here ****************************************************/
void InitLoad()
{
  /* Save the 5 base colors in wmavgload */
  XCopyArea(dpy, visible.pixmap, wmavgload.pixmap, NormalGC,
            6,6,11,52, Shape(6), Shape(6));

  /* Copy the base panel to visible */
  XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
	    0,0,mysizehints.width, mysizehints.height, 0 ,0);

  /* Remove the 5 base colors from visible */
  XCopyArea(dpy, visible.pixmap, visible.pixmap, NormalGC,
	    Shape(19),Shape(6),11,52, Shape(6), Shape(6));  

  /* initial scale */
  scale = 1;
  maxload = 0.0;

  /* initial values */
  memset(loads,0,SIZE*sizeof(float));
  loads_2 = 0.0;
  loads_3 = 0.0;

#if !defined(linux) && \
  !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
#ifndef SVR4
    if (gethostname(hostname, MAXHOSTNAMELEN) != 0) {
        perror("gethostname");
	exit(10);
    }
#else
    if (sysinfo(SI_HOSTNAME, hostname, MAXHOSTNAMELEN) < 0) {
	perror("sysinfo(SI_HOSTNAME)");
	exit(10);
    }
#endif
#endif
}

void InsertLoad()
{
   int i;

   /* 
    * check out if we are deleting the curent max
    */

   if (loads[0]>=maxload || loads_2>=maxload || loads_3>=maxload)
   {
      maxload = 0.0;
      
      for(i=1;i<SIZE;i++)
	 if (maxload < loads[i]) maxload = loads[i];
   }

   /*
    * get a new value
    */

   memmove(loads,loads+1,(SIZE-1)*sizeof(float));   

   GetLoad(loads+SIZE-1, &loads_2, &loads_3);

   if (maxload < max3(loads[SIZE-1],loads_2,loads_3))
      maxload = max3(loads[SIZE-1],loads_2,loads_3); 

   /*
    * check the scale
    */

  if ((int)maxload+1 != scale) Change_Scale((int)maxload+1);

  /* Move the area */
  XCopyArea(dpy, visible.pixmap, visible.pixmap, NormalGC,
	    Shape(13), Shape(6), 45, 52, Shape(12), Shape(6));

  /* draw the Free Time */

  if(_(loads[SIZE-1]) < 52)
     XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
	       Shape(19), Shape(6), 1, 52-_(loads[SIZE-1]),
	       Shape(57), Shape(6)); 

  /* draw the small value */

  if(_(loads[SIZE-1]) > 0)
     XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
	       Shape(6), Shape(6), 1, _(loads[SIZE-1]),
	       Shape(57), Shape(58-_(loads[SIZE-1])));


  /* draw the medium bar */

  if(_(loads_2) < 52)
     XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
	       Shape(19), Shape(6), 2, 52-_(loads_2),
	       Shape(9), Shape(6)); 
  if(_(loads_2) > 0)
     XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
	       Shape(7), Shape(6), 2, _(loads_2),
	       Shape(9), Shape(58-_(loads_2)));

  /* draw the large bar */

  if(_(loads_3) < 52)
     XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
	       Shape(19), Shape(6), 2, 52-_(loads_3),
	       Shape(6), Shape(6)); 
  if(_(loads_3) > 0)
     XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
	       Shape(9), Shape(6), 2, _(loads_3),
	       Shape(6), Shape(58-_(loads_3)));

  /* draw the scale for the bars */

  for(i=1;i<scale;i++)
  {
     XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
	       Shape(11), Shape(6), 1, 1,
	       Shape(57), Shape(58-_(i)));
     XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
	       Shape(11), Shape(6), 6, 1,
	       Shape(6), Shape(58-_(i)));
  }
}

/* resizes the current displayed load to fit the new scale */
void Change_Scale(int new_scale)
{
   int i,j;
   
   scale = new_scale;

   /* clear the bars */

   XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
	     Shape(19), Shape(6), 6, 52,
	     Shape(6), Shape(6)); 

   for(i=0;i<SIZE;i++)
   {
      /* draw the Free Time */

      if(_(loads[i]) < 52)
	 XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
		   Shape(19), Shape(6), 1, 52-_(loads[i]),
		   Shape(i+12), Shape(6)); 

      /* draw the small value */

      if(_(loads[i]) > 0)
	 XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
		   Shape(6), Shape(6), 1, _(loads[i]),
		   Shape(i+12), Shape(58-_(loads[i])));

      /* draw the scale */

      for(j=1;j<scale;j++)
	 XCopyArea(dpy, wmavgload.pixmap, visible.pixmap, NormalGC,
		   Shape(11), Shape(6), 1, 1,
		   Shape(i+12), Shape(58-_(j)));
   }
}


syntax highlighted by Code2HTML, v. 0.9.1