/*
 * Loadmeter version 1.20
 * System statistics monitor for Linux/Solaris + X11.
 * Copyright 1998, Ben Buxton (bb@zip.com.au)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <strings.h>
#include "loadmeter.h"

void do_loop(), drawhostname(), dobevels(), usage();
void loop(), Getgeom(), dosizecursor(int,int,char,XColor *);
void drawnum(), do_popup(), draw_popup(), raiseit(); 
char *commanum();

double oldload = 0;
int blockheight = 2;

float oldnum = 0;
int showmax = 1;
int diskwarn = 0;
#define DISK_OK 0
#define DISK_WARN 1
#define DISK_WARNED 2
#define DISK_WARN_ACKED 3
static int olduse[MAXFILESYSTEMS];
int warn_disk = False;
int override = 0;
int nogeom = 0;
int loadheight;
struct itimerval timval;

static XrmOptionDescRec opts[] = {
{"-geometry",   ".geometry",  XrmoptionSepArg, (caddr_t) NULL},
{"-display",   ".display",  XrmoptionSepArg, (caddr_t) NULL},
{"-numfont",         ".numfont",  XrmoptionSepArg, (caddr_t) NULL},
{"-labelfont",         ".labelfont",         XrmoptionSepArg, (caddr_t) NULL},
{"-label",         ".label",         XrmoptionSepArg, (caddr_t) NULL},
{"-popupfont",         ".popupfont",         XrmoptionSepArg, (caddr_t) NULL},
{"-popupbg",         ".popupbg",         XrmoptionSepArg, (caddr_t) NULL},
{"-popupfg",         ".popupfg",         XrmoptionSepArg, (caddr_t) NULL},
{"-name",         ".name",         XrmoptionSepArg, (caddr_t) NULL},
{"-warn",         ".warn",         XrmoptionSepArg, (caddr_t) NULL},
{"-disk",         ".disk",         XrmoptionNoArg, (caddr_t) "True"},
{"-warnbg",         ".warnbg",         XrmoptionSepArg, (caddr_t) NULL},
{"-meter",         ".meter",         XrmoptionSepArg, (caddr_t) NULL},
{"-override",         ".override",         XrmoptionNoArg, (caddr_t) "True"},
{"-sync",         ".sync",         XrmoptionNoArg, (caddr_t) "True"},
{"-noshrink",         ".noshrink",         XrmoptionNoArg, (caddr_t) "True"},
{"-nogeom",         ".nogeom",         XrmoptionNoArg, (caddr_t) "True"},
{"-bh",         ".bh",         XrmoptionSepArg, (caddr_t) NULL},
{"-parent",         ".parent",         XrmoptionSepArg, (caddr_t) NULL},
};
XrmDatabase rdb, srvdb, cmdb;
XrmValue val;
char *str_type[50], buffer[50];

int main(int argc, char **argv)
{
XColor red, green, yellow, blue, grey, top, bot;
XColor popt, popb, popfg, warnbg, meter;
unsigned long mask;
Colormap cmap;
XGCValues vals;
Window parent;
XTextProperty prop;
XEvent event;
char buf;
char *display = NULL;
char *labelfont, *namefont, *popupfont;
char *label, *name;

XSizeHints hints;
int oy=65535, gstatus=0;
unsigned int x=0, y=0;
Bool highlighted = False;
Bool moving = False;
Bool resizing = False;
int px = 0;
int py = 0;
Window rr,cr;
XSetWindowAttributes wattr;
int rx,ry,wx,wy;
unsigned int mr;

labelfont = namefont = label = name = popupfont = NULL;
warnpct = DEFAULTWARNPCT;
menuhl = False;
shrinkpopup=1;

#ifndef LINUX
if (geteuid()) {
    printf("Solaris loadmeter must be run as root.\n");
    exit(1);
}

seteuid(getuid());
#endif

bzero(&hints, sizeof(hints));

XrmInitialize();
XrmParseCommand(&cmdb, opts, 19, "loadmeter", &argc, argv);

if (XrmGetResource(cmdb, "loadmeter.display", "Loadmeter.display", str_type, &val))
{
	display = (void *)malloc(val.size+1);
	strncpy(display, val.addr, val.size);
}

/* Open the display */
disp = XOpenDisplay(display);
if(disp == NULL)
{
		printf("Cannot open display!\n");
		exit(0);
}
/* Get some display constants */
screen = DefaultScreen(disp);
cmap = DefaultColormap(disp, screen);
rwidth = DisplayWidth(disp, screen);
rheight = DisplayHeight(disp, screen);
root = DefaultRootWindow(disp);
depth = DefaultDepth(disp, screen);

if(XResourceManagerString(disp) != NULL)
{
	srvdb = XrmGetStringDatabase(XResourceManagerString(disp));
}
if(srvdb != NULL)
	XrmMergeDatabases(srvdb, &rdb);
XrmMergeDatabases(cmdb, &rdb);

if (XrmGetResource(rdb, "loadmeter.geometry", "Loadmeter.geometry", str_type, &val))
{
	strncpy(buffer, val.addr, (int) val.size);
	gstatus = XParseGeometry(buffer, &x, &y, &width, &height);
	if((gstatus & XValue) || (gstatus & YValue))
			hints.flags = USPosition;
	if((gstatus & WidthValue) || (gstatus & HeightValue))
			hints.flags |= USSize;
}
if (XrmGetResource(rdb, "loadmeter.numfont", "Loadmeter.numfont", str_type, &val))
{
	labelfont = (void *)malloc(val.size+1);
	strncpy(labelfont, val.addr, val.size);
}
if (XrmGetResource(rdb, "loadmeter.labelfont", "Loadmeter.labelfont", str_type, &val))
{
	namefont = (void *)malloc(val.size+1);
	strncpy(namefont, val.addr, val.size);
}
if (XrmGetResource(rdb, "loadmeter.popupfont", "Loadmeter.popupfont", str_type, &val))
{
	popupfont = (void *)malloc(val.size+1);
	strncpy(popupfont, val.addr, val.size);
}
if (XrmGetResource(rdb, "loadmeter.label", "Loadmeter.label", str_type, &val))
{
	label = (void *)malloc(val.size+1);
	strncpy(label, val.addr, val.size);
}
if (XrmGetResource(rdb, "loadmeter.parent", "Loadmeter.parent", str_type, &val))
{
	if(sscanf(val.addr, "%i", (unsigned int *)&parent) == 0)
		parent = 0;
} else parent = 0;

if (XrmGetResource(rdb, "loadmeter.popupbg", "Loadmeter.popupbg", str_type, &val))
{
	if(XParseColor(disp, cmap, val.addr, &popbg) == 0)
	{
		printf("Can't allocate popup bg!\n");
		exit(1);
	}
}
else
	XParseColor(disp, cmap, "grey30", &popbg);

if (XrmGetResource(rdb, "loadmeter.meter", "Loadmeter.meter", str_type, &val))
	XParseColor(disp, cmap, val.addr, &meter);
else
	XParseColor(disp, cmap, "red2", &meter);

if (XrmGetResource(rdb, "loadmeter.popupfg", "Loadmeter.popupfg", str_type, &val))
	XParseColor(disp, cmap, val.addr, &popfg);
else
	XParseColor(disp, cmap, "yellow", &popfg);

if (XrmGetResource(rdb, "loadmeter.warnbg", "Loadmeter.warnbg", str_type, &val))
	XParseColor(disp, cmap, val.addr, &warnbg);
else
	XParseColor(disp, cmap, "seagreen4", &warnbg);

if (XrmGetResource(rdb, "loadmeter.name", "Loadmeter.name", str_type, &val))
{
	name = (void *)malloc(val.size+1);
	strncpy(name, val.addr, val.size);
}
if (XrmGetResource(rdb, "loadmeter.warn", "Loadmeter.warn", str_type, &val))
	warnpct = atoi(val.addr);
if (XrmGetResource(rdb, "loadmeter.override", "Loadmeter.override", str_type, &val))
{
	if(!strncasecmp("true", val.addr, val.size))
		override = 1;
	if(override && parent != 0)
		usage(argv);
}
if (XrmGetResource(rdb, "loadmeter.sync", "Loadmeter.sync", str_type, &val))
	if(!strncasecmp("true", val.addr, val.size))
		dosync = 1;
if (XrmGetResource(rdb, "loadmeter.nogeom", "Loadmeter.nogeom", str_type, &val))
	if(!strncasecmp("true", val.addr, val.size))
		nogeom = 1;
if (XrmGetResource(rdb, "loadmeter.noshrink", "Loadmeter.noshrink", str_type, &val))
	if(!strncasecmp("true", val.addr, val.size)) 
		shrinkpopup=0;
if (XrmGetResource(rdb, "loadmeter.disk", "Loadmeter.disk", str_type, &val))
	if(!strncasecmp("true", val.addr, val.size))
		diskwarn = True;
if (XrmGetResource(rdb, "loadmeter.bh", "Loadmeter.bh", str_type, &val))
	blockheight = atoi(val.addr);

if(argc != 1) 
	usage(argv);

/* Do stuff with '-geom' command line option */
if( !(gstatus & WidthValue))
	width = DEFAULTWIDTH;
if( !(gstatus & HeightValue))
	height = DEFAULTHEIGHT;

if( !(gstatus & XValue))
	x = 1;
else if (gstatus & XNegative)
	x = rwidth + x - width;
if( !(gstatus & YValue))
	y = 1;
else if (gstatus & YNegative)
	y = rheight + y - height;
	
hints.x = x;
hints.y = y;
hints.min_width = MINWIDTH;
hints.min_height = MINHEIGHT;
hints.flags |= PMinSize;

/* Allocate the colours - does no checking as to whether it was successful :) */
bzero(&red, sizeof(red));
bzero(&green, sizeof(green));
bzero(&yellow, sizeof(yellow));
bzero(&blue, sizeof(blue));
if(depth != 1)
{
	red.red = green.green = yellow.green = yellow.red = 65535;
	grey.red = grey.green = grey.blue = 18724;
	blue.red = 14563 ; blue.green = 42649 ; blue.blue = 65535;
}
else
{
	red.red = green.green = yellow.green = yellow.red =
	blue.red = blue.green = blue.blue = 0;
	grey.red = grey.green = grey.blue = 65535;
}

XAllocColor(disp, cmap, &yellow);
XAllocColor(disp, cmap, &red);
XAllocColor(disp, cmap, &green);

/* Main window + bevel colours */
XAllocColor(disp, cmap, &grey);
bot.red = grey.red/2;
bot.green = grey.green/2;
bot.blue = grey.blue/2;
top.red = grey.red*2;
top.green = grey.green*2;
top.blue = grey.blue*2;
if(depth == 1) {
	bot.red = bot.green = bot.blue = 0;
	top.red = top.green = top.blue = 0;
	popbg.red = popbg.blue = popbg.green = 65535;
	popfg.red = popfg.blue = popfg.green = 0;
	warnbg.red = warnbg.blue = warnbg.green = 65535;
}
XAllocColor(disp, cmap, &top);
XAllocColor(disp, cmap, &bot);
XAllocColor(disp, cmap, &blue);
XAllocColor(disp, cmap, &popbg);
XAllocColor(disp, cmap, &popfg);
XAllocColor(disp, cmap, &warnbg);
popb.red = popbg.red/2;
popb.blue = popbg.blue/2;
popb.green = popbg.green/2;
popt.red = popbg.red*2;
popt.blue = popbg.blue*2;
popt.green = popbg.green*2;
if(depth == 1) {
	popt.red = popt.green = popt.blue = 0;
	popb.red = popb.green = popb.blue = 0;
	meter.red = meter.green = meter.blue = 0;
}
XAllocColor(disp, cmap, &popt);
XAllocColor(disp, cmap, &popb);
XAllocColor(disp, cmap, &meter);

Doinit(label);

if(parent == 0)
	parent = root;

/* Create the main window - override_redirect window with -ov option */
if(!override) 
	win = XCreateSimpleWindow(disp, parent, x, y, 
			width, height, 0, grey.pixel, grey.pixel);
else
	win = MakePopup(x, y, width, height, grey.pixel, 0);

/* Set the size hints for the window */
XSetWMNormalHints(disp, win, &hints);
/* Set the name of the app for the WM */
if(name == NULL)
	prop.value = "Loadmeter";
else
	prop.value = name;
prop.encoding = XA_STRING;
prop.format = 8;
prop.nitems = strlen(prop.value);
XSetWMName(disp, win, &prop);

/* Allocate the various GC's that are needed */
mask = GCForeground;
vals.foreground = red.pixel;
rgc = XCreateGC(disp, win, mask, &vals);

/* Hostname label font */
if(namefont == NULL)
{
	namefont = (void *)malloc(80);
	strcpy(namefont, HOSTFONT);
}
font = XLoadQueryFont(disp, namefont);
if(font == NULL)
{
	fprintf(stderr, "Can't load font %s!\n", namefont);
	exit(1);
}
fontheight = font->max_bounds.ascent+font->max_bounds.descent;

/* Load digits font */
if(labelfont == NULL)
{
	labelfont = (void *)malloc(80);
	strcpy(labelfont, DIGITFONT);
}
font2 = XLoadQueryFont(disp, labelfont);
if(font2 == NULL)
{
	fprintf(stderr, "Can't load font %s!\n", labelfont);
	exit(1);
}
loadheight = font2->max_bounds.ascent+font2->max_bounds.descent+2;

mask |= GCFont;
vals.font = font2->fid;
vals.foreground = green.pixel;
ggc = XCreateGC(disp, win, mask, &vals);

vals.font = font->fid;
vals.foreground = yellow.pixel;
vals.background = BlackPixel(disp, DefaultScreen(disp));
mask |= GCBackground;
yel = yellow.pixel;	/* Grab the pixel value for the popup */
ygc = XCreateGC(disp, win, mask, &vals);

vals.foreground = popbg.pixel;
vals.background = popbg.pixel;
popgc = XCreateGC(disp, win, mask, &vals);
vals.foreground = popt.pixel;
poptgc = XCreateGC(disp, win, mask, &vals);
vals.foreground = popb.pixel;
popbgc = XCreateGC(disp, win, mask, &vals);

vals.foreground = warnbg.pixel;
warngc = XCreateGC(disp, win, mask, &vals);
vals.foreground = meter.pixel;
metergc = XCreateGC(disp, win, mask, &vals);

vals.foreground = top.pixel;
tgc = XCreateGC(disp, win, mask, &vals);
vals.foreground = bot.pixel;
bgc = XCreateGC(disp, win, mask, &vals);
vals.foreground = blue.pixel;
blgc = XCreateGC(disp, win, mask, &vals);
vals.foreground = BlackPixel(disp, DefaultScreen(disp));
if(popupfont == NULL)
{
	popupfont = (void *)malloc(80);
	strcpy(popupfont, POPUPFONT);
}
pfont = XLoadQueryFont(disp, popupfont);
if(pfont == NULL)
{
	fprintf(stderr, "Can't load font %s!\n", popupfont);
	exit(1);
}
vals.font = pfont->fid;
vals.foreground = popfg.pixel;
ptextgc = XCreateGC(disp, win, mask, &vals);

vals.foreground = BlackPixel(disp, DefaultScreen(disp));
bkgc = XCreateGC(disp, win, mask, &vals);

/* Map the window ; tell the server what events to report */
XMapWindow(disp, win);
XSelectInput(disp, win, ExposureMask|ButtonPressMask|StructureNotifyMask|
		ButtonReleaseMask|PointerMotionMask|KeyPressMask|EnterWindowMask);

/* Set signal handler and time stuff */
signal(SIGALRM, loop);
signal(SIGUSR1, raiseit);
maxload.time = time(NULL);
timval.it_value.tv_sec = 0;
timval.it_value.tv_usec = 500000;

do_loop(True);  /* Initially force a redraw */
dobevels(win, 0, 0, width, height, tgc, bgc);

for(mask = 0 ; mask < MAXFILESYSTEMS ; mask++)
{
	usages[mask].path = 0;
	usages[mask].dev = 0;
	usages[mask].type = 0;
	usages[mask].blocks = 0;
}
memory.muse = (void *)malloc(80);
memory.suse = (void *)malloc(80);


/* Continuous event loop - handle events, setup signals, draw bevel */
 while(1)
{
	setitimer(ITIMER_REAL, &timval, 0);
	XNextEvent(disp, &event);
	switch(event.type)
	{
	case ConfigureNotify:
	/* Main window resized */
		width = event.xconfigure.width;
		height = event.xconfigure.height;
	case Expose:
	/* Main window receives expose */
	/* Clear any further expose events from the queue */
		while(XCheckTypedEvent(disp, Expose, &event));
		dobevels(win, 0, 0, width, height, tgc, bgc);
		break;
	case KeyPress:
	/* Exit if escape key pressed */
		XLookupString(&event.xkey, &buf, 1, NULL, NULL);
		if(buf == 27)
			exit(0);
		continue;
	case ButtonPress:
	/* Pointer button pressed */
		XSetInputFocus(disp, win, RevertToNone, CurrentTime);
		if(event.xbutton.button == Button2)
		{
		/* Button2 with no modifier resets max indicator,
		 * however with Control modifier and override set
		 * to True, , initiates moveing for the window. 
	 	 */
			if ((override || parent != root) && event.xbutton.state==ControlMask) {
				XQueryPointer(disp,win,&rr,&cr,&rx,&ry,&px,&py,&mr);
				XRaiseWindow(disp, win);
				moving = True;
			}
			else {
				maxload.load = 0;
				maxload.num = 0;
				maxload.time = time(NULL);
				do_popdown();
			}
		}
		else if(event.xbutton.button == Button3)
		{
		/* Button3 with no modifier toggles max indicator on/off
		 * however with Control modifier and override set
		 * to True, , initiates resizing for the window. 
	 	 */
			if ((override || parent != root) && event.xbutton.state==ControlMask) {
                                XQueryPointer(disp,win,&rr,&cr,&rx,&ry,&px,&py,&mr);
                                XRaiseWindow(disp, win);
				resizing = True;
			}
			else
			{
				showmax = 1-showmax;
				do_popdown();
			}
		}
		else
		{
		/* Button1 pops up stats window, or lowers if ctrl is
		 * down and override is on
		 */
		for(rx = 0 ; rx < MAXFILESYSTEMS ; rx++) {
		 	if (olduse[rx] == DISK_WARN)
				olduse[rx] = DISK_WARN_ACKED;
		}
			if ((override && parent == root) && event.xbutton.state==ControlMask) {
				XLowerWindow(disp, win);
				continue;
			}
			menuhl = False;
			do_popup();
		}
		continue;
	case ButtonRelease:
	/* Button released - pop down popup window, set cursor to
	 * the default root one, cancel any moving/resizing
	 */
		do_popdown();
		moving = resizing = menuhl = False;
		mask = CWCursor;
		wattr.cursor = 0;
		XChangeWindowAttributes(disp, win, mask, &wattr);
		oy = 65535;
		break;
	case MotionNotify:
	/* Pointer moved - do any resizing or moving of the window,
	 * or if in the popup window, handle as appropriate
	 */
		if(popup == 0)
		{
		/* Junk any other motion events waiting */
		  while(XCheckTypedEvent(disp, MotionNotify, &event));
		  if (moving)
		  {
			XQueryPointer(disp,parent,&rr,&cr,&rx,&ry,&wx,&wy,&mr);
                        dosizecursor(wx-px, wy-py, '+', &yellow);
			XMoveWindow(disp, win, wx-px, wy-py);
		  }
		 else if (resizing) {
		 	XQueryPointer(disp,win,&rr,&cr,&rx,&ry,&wx,&wy,&mr);
		 	if(wx<MINWIDTH) wx=MINWIDTH;
		 	if(wy<MINHEIGHT) wy=MINHEIGHT;
		 	dosizecursor(wx, wy, 'x', &yellow);
		 	XResizeWindow(disp, win, wx, wy);
		 }
	         continue;
		}
		x = (pfont->max_bounds.ascent+pfont->max_bounds.descent);
		if(event.xany.window == popup && event.xmotion.y <= pheight
				&& event.xmotion.x <= pwidth && event.xmotion.y >= 0
				&& event.xmotion.x >= 0)
		{
			if(event.xmotion.y < 2)
				event.xmotion.y = 2;
			y = ((event.xmotion.y-2) / x) - 1;
			menuhl = True;
			if(y == oy)
				continue;
			ShowInfo(y);
			draw_popup(y);
			highlighted = True;
			oy = y;
		}
		else if (highlighted)
		{
			highlighted = False;
			menuhl = False;
			oy = 65535;
			infodown();
			XClearWindow(disp, popup);
			draw_popup(0);
		}
		continue;
	default:
		/* Nothing */
	}
	do_loop(True); /* Force a redraw */
	XSync(disp, False);
}
}

void usage(char **argv)
{
	printf("Loadmeter v1.20 (c)1998 Ben Buxton (bb@zip.com.au)\n");
	printf("Usage: %s [-disp display] [-geom geometry] [-name name]\n", argv[0]);
	printf("          [-numfont font] [-labelfont font] [-label label] [-disk]\n");
	printf("          [-popupfont font] [-popupbg bg] [-popupfg fg] [-name name]\n");
	printf("          [-warn pct] [-warnbg bg] [-meter colour] [-override]\n");
	printf("          [-sync] [-bh blockheight] [-parent window] [-noshrink]\n");
	printf("          [-nogeom]\n");
	exit(1);
}

/* Do the hard work of drawing and calculating things */
void do_loop(force)
Bool force;	/* Force a full redraw? */
{
int numblocks, totblocks;
int where;
int x, y, y2, mark, mark2, wd;
static int avgb, oavgb;

/* Grab the actual load averages */
	GetLoadPoint(&load, &avg, &davg, &processes);
	totblocks = (height - loadheight -3 )/(blockheight+1);

/* totblocks/3 blocks per load point */
	numblocks = totblocks/3*load;
/* Draw digits if forced or load != oldload */
	if(load != oldload || force)
		drawnum(load);
	oldload = load;
	avgb = totblocks/3*avg;
/* Only redraw if forced or changed */
	if(oldnum == numblocks && avgb == oavgb && !force)	
		return;
/* Can only fit a max of 'totblocks' blocks */
	if(numblocks > totblocks)
		numblocks = totblocks;
/* See if the load is at a maximum */
	if((load > maxload.load || numblocks > maxload.num) && showmax == 1 )
	{
		maxload.num = numblocks;
		maxload.load = load;
		maxload.time = time(NULL);
	}
	oldnum = numblocks;
	oavgb = avgb;
	XClearArea(disp, win, 1, loadheight, width-2, height-loadheight-2, False);
/* Draw each of the 'totblocks' blocks in the correct colour */
	for(where = 0; where < totblocks ; where++)
	{
		x = 4;
		y = height-blockheight - where-2 - where * blockheight;
/* Find the colour to draw the bar in */
		if(where < totblocks/3)
			gc = ggc;
		else if(where < blockheight*(int)(totblocks/3))
			gc = ygc;
		else
			gc = rgc;
/* Grey for the unused ones */
		if(where > numblocks)
		{
			if(depth == 1)
				gc = popgc;
			else
				gc = bgc;
		}

		XFillRectangle(disp, win, gc, x, y, width-8, blockheight);
	}
/* Draw things like integer marks and max/avg marks */
	mark = height-2 - (int)(totblocks/3) * (blockheight+1);
	XDrawLine(disp, win, tgc, 1, mark, width-2, mark);
	mark2 = height-2 - 2*(int)(totblocks/3) * (blockheight+1);
	XDrawLine(disp, win, tgc, 1, mark2, width-2, mark2);
	for(wd = 0 ; wd < MAXFILESYSTEMS ; wd++) {
		if(olduse[wd] == DISK_WARN || olduse[wd] == DISK_WARNED)
			XDrawString(disp, win, rgc, 7, (height-mark)/2 + mark, "disk", 4);
	}
	y = height-blockheight - maxload.num-2 - maxload.num * blockheight;
	if(showmax == 1)
		XDrawLine(disp, win, rgc, 1, y, width/5, y);
	y2 = height-blockheight - 2 - avgb * (blockheight + 1);
	XDrawLine(disp, win, blgc, 4*width/5, y2, width-2, y2);
/* Hostname text and then sync up */
	drawhostname();
	XSync(disp, False);
}

/* Raise the window (called on a SIGUSR1) */
void raiseit()
{
	signal(SIGUSR1, raiseit);
	XRaiseWindow(disp, win);
	do_loop(True);
}

/* Draw the hostname text vertically over the bargraph */
void drawhostname()
{
int which;
int y = loadheight*2;
	for (which = 0 ; which < strlen(hostname) ; which++)
	{
		XDrawString(disp, win, ygc, width/2 - 2, y, &hostname[which], 1);
		y+= fontheight;
	}
}


/* Handler for sigalrm */
void loop()
{
int which;
int force = False;
static int inv = 0;
float pct;

	if(diskwarn && inv > 9) {
		inv = 0;
		GetDiskUsage();
		which = 0;
		while (usages[which].path != 0 && which < MAXFILESYSTEMS) {
			pct = (float)(usages[which].blocks-usages[which].bfree)*100/usages[which].blocks;
			if(pct >= warnpct) {
				if(olduse[which] != DISK_OK) {
					which++;
					continue;
				}
				olduse[which] = DISK_WARN;
				warn_disk = True;
				force = True;
			} else
				olduse[which] = DISK_OK;
			which++;
		}
	}
	do_loop(force);
	signal(SIGALRM, loop);
	setitimer(ITIMER_REAL, &timval, 0);
	inv++;
}

/* Draw the load digits */
void drawnum(ld)
float ld;
{
char loadstring[9];
int x;

	x = (width - XTextWidth(font2, loadstring, 4)) / 2 + 1;
	XClearArea(disp, win, 2, 2, width-3, loadheight, False);
	sprintf(loadstring, "%f", ld);
	XDrawString(disp, win, ggc, x, loadheight, loadstring, 4);
}

/* Set the cursor to the toplevel window to a string of
 * the form "wseph" eg "25x50". Cursor colour is given
 * by 'col'.
 */
void dosizecursor(int w, int h, char sep, XColor *col)
{
int width,height;
Pixmap pix, mask;
GC gc;
XGCValues gcv;
XSetWindowAttributes wattr;
unsigned long msk;
char geom[15];
XPoint points[3];

	if (nogeom)
		return;
	sprintf(geom, "%d%c%d", w, sep, h);
/* Obtain required geometryies and create 2 suitably-sized pixmaps of
 * depth 1, one for the cursor, the other for the mask.
 */
	width = XTextWidth(pfont, geom, strlen(geom)) +7;
	height = pfont->max_bounds.ascent + pfont->max_bounds.descent +2;
	pix = XCreatePixmap(disp, root, width, height, 1);
	mask = XCreatePixmap(disp, root, width, height, 1);
/* Create the GC with foreground 0 and suitable font */
	msk = GCForeground | GCFont;
	gcv.foreground = 0;
	gcv.font = pfont->fid;
	gc = XCreateGC(disp, pix, msk, &gcv);
/* Fill both pixmaps with 0 */
	XFillRectangle(disp, mask, gc, 0, 0, width, height);
	XFillRectangle(disp, pix, gc, 0, 0, width, height);
/* Chenge the foreground to '1' and draw the string */
	XSetForeground(disp, gc, 1);
	points[0].x = points[0].y = 0;
	points[1].y = points[2].x = 2;
	points[1].x = points[2].y = 5;
	XFillPolygon(disp, mask, gc, points, 3, Convex, CoordModeOrigin);
	XDrawString(disp, mask, gc, 6, height-1, geom, strlen(geom));
/* Generate the cursor from the pixmaps and set it for the window */
	wattr.cursor = XCreatePixmapCursor(disp, pix, mask, col, col, 0, 0);
	msk = CWCursor;
	XChangeWindowAttributes(disp, win, msk, &wattr);
/* Free the resources */
	XFreePixmap(disp, pix);
	XFreePixmap(disp, mask);
	XFreeGC(disp, gc);
}

/*
 * Given a window, obtains the width, height, x and y
 */
void Getgeom(Window win, int *x, int *y, unsigned int *width, unsigned int *h)
{
int xr, yr;
unsigned int bwr, dr;
Window rr;

/* Obtain the absolute position of the window WRT root */
	XGetGeometry(disp, win, &rr, &xr, &yr, width, h, &bwr, &dr);
	XTranslateCoordinates(disp, win, root, 0, 0, x, y, &rr);
}

/* Draw bevels on window 'win' that has given co-ords and given gcs */
void dobevels(Window win, int x, int y, int w, int h, GC top, GC bot)
{
XPoint points1[3];
XPoint points2[3];

	/* Window bevel coordinates */
	points1[0].x = points1[1].x  = x;
	points1[1].y = points1[2].y = y;
	points1[0].y = y+h-1;
	points1[2].x = x+w-1;

	points2[0].x = x;
	points2[0].y = points2[1].y = y+h-1;
	points2[1].x = points2[2].x = x+w-1;
	points2[2].y = y+1;
	XDrawLines(disp, win, top, points1, 3, CoordModeOrigin);
	XDrawLines(disp, win, bot, points2, 3, CoordModeOrigin);
}

/*
 * Add commas to a number for readability
 *
 * eg: commanum(5423857) = "5,423,857"
 *
 * Note that the returned 'char *' is only lent! The returned variable
 * must not be modified as it points to a bit of static memory.
 */
char *commanum(long num) {
int i=0, z=0, ln;
char *number;
static char *string;

/* A 60+ digit number isn't very likely.. */
	number = (void *)malloc(64);
	sprintf(number, "%ld", num);
        ln = strlen(number);
	if (!string)
		string = (void *)malloc(64);
	if(ln < 4) {
		sprintf(string, "%ld", num);
		free(number);
		return string;
	}
        for(i=0;i<ln;i++) {
                if((ln-i)%3==0 && i < ln-1 && i > 0)
                        string[z++] = ',';
                string[z++] = number[i];
        }
	string[z++] = '\0';
	free(number);
	return string;
}

