
/*
 *  LOADAV.C
 *
 *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
 *
 *  LOADAV  [frequency]     Connect to remote UNIX system and display
 *			    load average.  Default is every 5 minutes.
 *
 *  frequency in seconds, default is every 60 seconds
 *
 */

#include "defs.h"
#include <local/deemu.h>

int __stdargs CXBRK(void);

short Deemu[] = {
    DMSTRT, 0, 0,
    DMNW  , 0, 10, 2, 2, -80, 40, 0xFFFF,
    DMEND , 0, 0
};

#define DMNWOFF 4

#define NA  0
#define GWIDTH	(sizeof(Graph)/sizeof(Graph[0]))
#define GDEPTH	(sizeof(GMap)/sizeof(GMap[0]))
#define GMASK	(GWIDTH-1)

uword	GMax[] = { 2*256, 50 };
uword	GIncr[]= { 2*256, 20 };
uword	GMap[2] = { 0, 1 };
uword	Graph[1024][GDEPTH];	/*  5Min,#users.	   */
uword	Gi;

ubyte	Scr[128];
ubyte	Initial;

ubyte Title[128];

extern void updatewindow();

NW Nw = {
    0, 0, 320, 50, -1, -1,
    NEWSIZE|CLOSEWINDOW,
    WINDOWSIZING|WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|NOCAREREFRESH,
    NULL, NULL, Title, NULL, NULL,
    32, 18, -1, -1, WBENCHSCREEN
};

WIN *Win;
RP  *Rp;

extern int Enable_Abort;

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

void clearwindow ARGS((void));
void updatewindow ARGS((char *));
void main ARGS((int, char **));

int
brk()
{
    return(0);
}

void
main(ac,av)
char *av[];
{
    void *chan = NULL;
    short numsecs = 60;
    long imask, tmask, dmask, mask;
    char notdone = 1;
    char *host = NULL;
    PORT *TimPort = CreatePort(NULL, 0);
    IOT Iot;

    onbreak(brk);
    sprintf(Title, "LoadAv V%s%s", VERSION, LOADAV_VERSION);
    {
	short i;
	for (i = 1; i < ac; ++i) {
	    if (strncmp(av[i], "-N", 2) == 0) {
		host = av[i]+2;
		continue;
	    }
	    numsecs = atoi(av[i]);
	}
    }


    Iot.tr_node.io_Device = NULL;

    if (OpenDevice("timer.device", UNIT_VBLANK, (IOR *)&Iot, 0))
	goto fail;
    Iot.tr_node.io_Command = TR_ADDREQUEST;
    Iot.tr_node.io_Message.mn_ReplyPort = TimPort;
    Iot.tr_time.tv_micro = 1;
    Iot.tr_time.tv_secs  = 0;
    SendIO((IOR *)&Iot);

#ifndef LATTICE
    Enable_Abort = 0;
#endif
    IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 0);
    GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0);

    chan = DOpen(host, PORT_LOADAV, 25, 25);
    if (chan == NULL) {
	puts("no connect");
	goto fail;
    }
    InitDeemuNW(Deemu+DMNWOFF, &Nw);
    Win = OpenWindow(&Nw);
    if (Win == NULL) {
	puts("Unable to open window");
	goto fail;
    }
    Rp = Win->RPort;
    imask   = 1 << Win->UserPort->mp_SigBit;
    dmask   = 1 << ((PORT *)chan)->mp_SigBit;
    tmask   = 1 << TimPort->mp_SigBit;

    clearwindow();
    while (notdone) {
	mask = Wait(imask|dmask|tmask|SIGBREAKF_CTRL_C);
	if (mask & SIGBREAKF_CTRL_C)
	    notdone = 0;
	if (mask & imask) {
	    IMESS *im;
	    while (im = (IMESS *)GetMsg(Win->UserPort)) {
		switch(im->Class) {
		case NEWSIZE:
		    clearwindow();
		    break;
		case CLOSEWINDOW:
		    notdone = 0;
		    break;
		}
		ReplyMsg((MSG *)im);
	    }
	}
	if (mask & dmask) {
	    char dummy;
	    if ((dummy = DNRead(chan, &dummy, 1)) != 0)
		notdone = 0;
	}
	while (mask & tmask) {      /*  while just so we can break */
	    char len = 0;

	    if (GetMsg(TimPort)) {
		Iot.tr_time.tv_micro = 0;
		Iot.tr_time.tv_secs  = numsecs;
		SendIO((IOR *)&Iot);
		if (DWrite(chan, &len, 1) == 1 && DRead(chan, &len, 1) == 1) {
		    if (len < sizeof(Title) && DRead(chan, Title, len) == len) {
			Title[len] = 0;
			updatewindow(Title);
			SetWindowTitles(Win, Title, (char *)-1);
			break;
		    }
		}
		notdone = 0;
	    }
	    break;
	}
    }

fail:
    if (Iot.tr_node.io_Device) {
	AbortIO((IOR *)&Iot);
	WaitIO((IOR *)&Iot);
	CloseDevice((IOR *)&Iot);
    }
    DeletePort(TimPort);
    if (Win)
	CloseWindow(Win);
    if (chan)
	DClose(chan);
    if (IntuitionBase)
	CloseLibrary((LIB *)IntuitionBase);
    if (GfxBase)
	CloseLibrary((LIB *)GfxBase);
}

/*
 *  Graphics routines.	************************************************
 */

short WOx, WOy, Ww, Wh;

void
clearwindow()
{
    short i, j, d;

    WOx = Win->BorderLeft;
    WOy = Win->BorderTop;
    Ww	= Win->Width - Win->BorderRight - Win->BorderLeft;
    Wh	= Win->Height- Win->BorderTop	- Win->BorderBottom;

    SetAPen(Rp, 0);
    RectFill(Rp, WOx, WOy, Ww + WOx, Wh + WOy);
    WOx += 2;
    WOy += 2;
    Ww	-= 4;
    Wh	-= 4;

    /*
     *	Redraw the graph.  Scale values relative to GMax[?] and Wh.
     *
     *	ypos = (WOy + Wh) - (Wh * value / GMax[d])
     *
     */

    for (d = 0; d < GDEPTH; ++d) {
	char move = 1;
	SetAPen(Rp, (d & 1) ? 3 : 1);
	for (i = Ww - 1, j = Gi - 1; i >= 0; --i, --j) {
	    uword value = Wh * Graph[j&GMASK][d] / GMax[GMap[d]];
	    (move) ?
		Move(Rp, WOx + i, WOy + Wh - value - 1) :
		Draw(Rp, WOx + i, WOy + Wh - value - 1)
	    ;
	    move = 0;
	}
    }
}

/*
 *  5:44pm up 2 days, 22:30, 37 users, load average: 5.98, 7.93, 7.97
 *		      7 mins,
 */

void
updatewindow(str)
char *str;
{
    short d;
    char refresh = 0;
    uword ary[GDEPTH];

    {
	long nusers, i1, f1, i5, f5;
	char *ptr = str;

	while (strncmp(ptr, "users", 5) && *ptr)
	    ++ptr;
	while (*--ptr == ' ' && ptr != str)
	    --ptr;
	while (*--ptr != ' ' && ptr != str)
	    --ptr;
	nusers = atoi(ptr+1);
	while (strncmp(ptr, "load", 4) && *ptr)
	    ++ptr;
	sscanf(ptr, "load average: %ld.%ld, %ld.%ld,",
	    &i1, &f1, &i5, &f5
	);
	/*
	ary[0] = (i1 << 8) | ((f1 << 8) / 100);
	*/
	ary[0] = (i5 << 8) | ((f5 << 8) / 100);
	ary[1] = nusers;
    }
    for (d = 0; d < GDEPTH; ++d) {
	while (ary[d] > GMax[GMap[d]] && ary[d] < 65000) {
	    GMax[GMap[d]] += GIncr[GMap[d]];
	    refresh = 1;
	}
	Graph[Gi][d] = ary[d];
    }
    if (!Initial) {
	short i;
	Initial = 1;
	for (i = 0; i < GWIDTH; ++i) {
	    for (d = 0; d < GDEPTH; ++d)
		Graph[i][d] = Graph[Gi][d];
	}
    }
    Gi = (Gi + 1) & GMASK;
    if (refresh) {
	clearwindow();
	return;
    }
    ScrollRaster(Rp, 1, 0, WOx, WOy, WOx + Ww - 1, WOy + Wh - 1);
    for (d = 0; d < GDEPTH; ++d) {
	uword value1 = Wh * Graph[(Gi-2)&GMASK][d] / GMax[GMap[d]];
	uword value2 = Wh * Graph[(Gi-1)&GMASK][d] / GMax[GMap[d]];
	SetAPen(Rp, (d & 1) ? 3 : 1);
	Move(Rp, Ww + WOx - 2, WOy + Wh - value1 - 1);
	Draw(Rp, Ww + WOx - 1, WOy + Wh - value2 - 1);
    }
}

