/*
**++
**----------------------------------------------------------------------------
**  Program written by
**
**	Holger Teutsch
**
**	c/o
**	Degussa AG
**	Weißfrauenstr. 9
**	D-6000 Frankfurt 11
**
**  Submitted to the public domain, on the condition that this copyright
**  notice is always included without alteration.
**
**----------------------------------------------------------------------------
**
**  FACILITY:  SPI SystemPerformanceInformation
**
**  MODULE DESCRIPTION:
**
**      Show spi in a DECwindows window
**
**  AUTHORS:
**
**      Holger Teutsch
**
**  CREATION DATE:  11-Jun-1990
**
**  DESIGN ISSUES:
**
**      {@tbs@}
**
**
**  MODIFICATION HISTORY:
**
**      {@tbs@}...
**--
*/

/* font for headlines */
#define HDL_FONT    "-Adobe-COURIER-BOLD-R-NORMAL--*-120-*-*-M-*-ISO8859-1"

/* standard font */
#define STD_FONT    "-Adobe-COURIER-MEDIUM-R-NORMAL--*-120-*-*-M-*-ISO8859-1"

/* constants */
#define MIN_INTERVAL	1   /* limits of interval */
#define MAX_INTERVAL	10

#define MAIN_W	    180	    /* screen layout */
#define MAIN_H	    340	    /* main window */

#define USAGE_X	    15	    /* usage 'window' */
#define USAGE_Y	    15
#define USAGE_W	    150
#define USAGE_H	    50
#define USAGE_TIC   2
#define N_USAGE	    ( USAGE_W / USAGE_TIC )

#define MODES_X	    15	    /* modes 'window' */
#define MODES_Y	    90
#define MODES_W	    102	    /* should be a multiple of 6 */
#define MODES_H	    70

#define TXT_X	    5	    /* text */
#define TXT_Y	    200
#define TXT_SPACING 12	    /* line spacing */

#define COUNTERS_X  85

#define MENU_EXIT	    1	/* tags for menu selection callback */
#define MENU_RESET	    2
#define MENU_INTERVAL	    3
#define MENU_SCALE_OK	    4
#define MENU_SCALE_CANCEL   5

/*
**
**  INCLUDE FILES
**
*/

#include descrip
#include lnmdef
#include syidef
#include jpidef

#include strdef
#include stdio
#include <decw$include/dwtappl.h>

#include "spi.h"
#include "icon.bitmap"

/*
**
**  VARIABLES
**
*/
static int tick = 5 * 1000;	    /* timer tick ( milliseconds ) */

static int usage[ N_USAGE ];	    /* save usage history here */

Font hdl_font,			    /* font headlines */
     std_font;			    /* standard */

static Widget interval_box,	    /* interval dialog box */
	      int_scale;	    /* interval scale widget */

static Display *display;
static Window work_window;

static GC work_gc;



/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      draw_background
**
**
**--
*/
static void draw_background (void)
{
    int y = TXT_Y;
    spi_value_t *sptr;


    XDrawRectangle( display, work_window, work_gc,
		    USAGE_X-1, USAGE_Y-1, USAGE_W+1, USAGE_H+1 );

    XSetFont( display, work_gc, hdl_font );

    XDrawString( display, work_window, work_gc,
	       USAGE_X + 5, USAGE_Y + USAGE_H + 12,
	       "CPU usage", 9 );

    XDrawRectangle( display, work_window, work_gc,
		    MODES_X-1, MODES_Y-1, MODES_W+1, MODES_H+1 );


    XDrawString( display, work_window, work_gc,
	       MODES_X + 3, MODES_Y + MODES_H + 12,
	       "I M K E S U N", 13 );

    XSetFont( display, work_gc, std_font );

    for ( sptr = spi_values; sptr < &spi_values[ n_spi_values ]; sptr++ )
    {
	XDrawString( display, work_window, work_gc,
		   TXT_X, y,
		   sptr->txt, strlen ( sptr->txt ) );
	y += TXT_SPACING;
    }
}



/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      draw_modes_counters
**
**
**--
*/
static void draw_modes_counters(void)
{
    int x,y,delta_x,delta,modes_w,*mptr;
    spi_value_t *sptr;
    char *buff[50];

    delta_x = MODES_W / N_MODES;
    modes_w = delta_x - 2;

    x = MODES_X + 1;

    for ( mptr = modes;  mptr < &modes[ N_MODES ]; )
    {
	XClearArea( display, work_window,
		    x, MODES_Y, modes_w, MODES_H, FALSE );

        delta = ( 100 - *mptr++ ) * MODES_H; delta /= 100;

	XFillRectangle( display, work_window, work_gc,
			x, MODES_Y + delta, modes_w, MODES_H - delta );

	x += delta_x;
    }

  /* write counters */

    y = TXT_Y;
    for ( sptr = spi_values; sptr < &spi_values[n_spi_values];  sptr++ )
    {
	if ( sptr->flags != VALUE_SEP )	    /* not a separator line */
	{
	    if ( sptr->flags & VALUE_RATE )
		sprintf( buff, "%6d %5.1f", *sptr->value, sptr->rate );
	    else
		sprintf( buff, "%6d", *sptr->value );

	    XDrawImageString( display, work_window, work_gc,
			      COUNTERS_X, y,
			      buff, strlen ( buff ) );
	}

	y += TXT_SPACING;
    }
}


/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      draw_usage
**
**  FORMAL PARAMETERS:
**
**      expose	<==> called by expose, redraw all
**
**
**--
*/
static void draw_usage( int expose )
{
    int x,delta,*uptr;

    if ( !expose )
    {
	XCopyArea( display, work_window, work_window, work_gc,
		   USAGE_X + USAGE_TIC, USAGE_Y,	/* shift display left */
		   USAGE_W - USAGE_TIC, USAGE_H,
		   USAGE_X, USAGE_Y );

        uptr = &usage[ N_USAGE - 1 ];
	x = USAGE_X + USAGE_W - USAGE_TIC;
    }
    else
    {
	uptr = usage;
	x = USAGE_X;
    }


    for ( ; uptr < &usage[ N_USAGE ]; )
    {

	delta = ( 100 - *uptr++ ) * USAGE_H; delta /= 100;

	if ( ! expose )
	    XClearArea( display, work_window,
			x, USAGE_Y, USAGE_TIC, USAGE_H, FALSE );

	XFillRectangle( display, work_window, work_gc,
			x, USAGE_Y + delta, USAGE_TIC, USAGE_H - delta );

	x += USAGE_TIC;
    }
}


/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      expose
**	handle exposure event
**
**--
*/
static void expose ( Widget *widget, int *tag, DwtWindowCallbackStruct *cbt )
{
    register XExposeEvent *exev;

    exev = cbt->event;
    if ( exev->count ) return;	/* simple expose procedure: */
				/* update all on last call */

    draw_background();
    draw_modes_counters();
    draw_usage( TRUE );
}



/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      menu_select
**
**--
*/
static void menu_select( Widget *widget, int tag )
{
    static int interval_mapped;

    switch ( tag )
    {
        case MENU_RESET :
            reset = 1;
	    break;

        case MENU_INTERVAL :
	    if ( interval_mapped ) return;
	    XtManageChild( interval_box );
	    break;

        case MENU_SCALE_OK :
	    DwtScaleGetSlider( int_scale, &tick );
	    tick *= 1000;

        case MENU_SCALE_CANCEL :
	    XtUnmanageChild( interval_box );
	    interval_mapped = 0;
	    break;

        case MENU_EXIT :
	    exit( 0 );
	    break;
    }
}




/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      timer_tick
**
**--
*/
static void timer_tick (void)
{
    int r0,*uptr,*uptr1;
    static int faults,last_faults,ws_size,ws_wait;

    static itmslot jpiitm[ 3 ] =
	{ { 4, JPI$_PAGEFLTS, &faults, 0 },
	  { 4, JPI$_WSSIZE, &ws_size, 0 },
	  0 };

    (void)XtAddTimeOut( tick, timer_tick, 1 );	/* restart timer */

    get_spi();			    /* get performance information */

    draw_modes_counters();	    /* draw modes and counters */

				    /* shift usage array */
    for ( uptr = usage, uptr1 = uptr + 1;  uptr < &usage[ N_USAGE - 1 ];)
    {
        *uptr++ = *uptr1++;
    }

    usage[ N_USAGE - 1 ] = 100 - modes[ N_MODES - 1 ];	/* new usage value */

    draw_usage( FALSE );	    /* draw usage */

  /* adjust working set */

    r0 = sys$getjpiw (		    /* get current values of WSSIZE, FAULTS */
             0, 0, 0,
             jpiitm,
             0, 0, 0 );

    if ( ( r0 & 1 ) == 0 ) lib$stop( r0 );


    if ( --ws_wait <= 0 )   /* no wait in progress */
    {
	if ( faults == last_faults )
	    sys$adjwsl (		    /* adjust downward */
		-ws_size / 10,	    /* 10 % */
		0 );
	else
	    ws_wait = 3;		    /* there were faults, wait */
    }

    last_faults = faults;
}



/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      get_nodename
**
**  FORMAL PARAMETERS:
**
**      void
**
**  RETURN VALUE:
**
**      pointer to nodename
**
**  SIDE EFFECTS:
**
**      var nodename updated
**
**--
*/
static char *get_nodename (void)
{
    static $DESCRIPTOR( sys_node_desc, "SYS$NODE" );
    static $DESCRIPTOR( file_dev_desc, "LNM$FILE_DEV" );

    static char nodename[ 16 ] = "???";

    static int len;

    static struct {
	itmslot string;
	int termin;
    } itm = { {sizeof nodename - 1, SYI$_NODENAME, nodename, &len },
	      0 };

    int r0;
    char *cptr;


    r0 = sys$getsyiw (
             0, 0, 0, 
             &itm, 
             0, 0, 0 );

    if ( ( r0 & 1 ) == 0 )
        lib$stop( r0 );

    if ( len != 0 )		    /* NODENAME != "" ? */
    {
	nodename[ len ] = 0;	    /* yes, make it asciz */
	return nodename;
    }


    itm.string.code = LNM$_STRING;  /* no, translate SYS$NODE */

    r0 = sys$trnlnm (
             0,
             &file_dev_desc,
             &sys_node_desc,
             0,
             &itm );

    if ( ( r0 & 1 ) == 0 )
        lib$stop( r0 );

    if ( len != 0 )
    {
	if ( ( cptr = strchr ( nodename, ':' ) ) != 0 )	/* delete trailing :: */
	    *cptr = 0;
    }

    return nodename;
}


/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      spi main
**
**--
*/
main (int argc, char *argv[])
{
    static char title[ 80 ] = "Spi on ";

    int i;

    Arg arglist[ 10 ];
    int narg;

    Screen *screen;
    Pixmap icon;

    DwtCallback callback[ 10 ];

    Widget toplevel,	    /* shell widget */
	   main_window,	    /* main window */
	   work_area,	    /* work area in main window */
	   menu_bar,	    /* menu bar in main window */

	   file_pde,	    /* FILE pulldown entry */
	   file_pd,	    /* FILE pulldown */

	   option_pde,	    /* OPTION pulldown entry */
	   option_pd;	    /* OPTION pulldown */

    Widget menu_item[ 20 ]; /* menu items */
    int nmenu;

    XGCValues gcval;


    get_spi();		    /* get first set of values */

    strcat ( title, get_nodename() );	/* build title */


  /* create toplevel widget */

    toplevel = XtInitialize( title, "spi", NULL, 0, &argc, argv );

    display = XtDisplay( toplevel );

    screen = DefaultScreenOfDisplay( display );

    icon = XCreateBitmapFromData( display, DefaultRootWindow( display ),
				  ICON_bits, ICON_width, ICON_height );

    narg = 0;
    XtSetArg( arglist[ narg ], XtNallowShellResize, TRUE ); narg++;
    XtSetArg( arglist[ narg ], XtNx,
	      WidthOfScreen( screen ) - MAIN_W - 10 ); narg++;
    XtSetArg( arglist[ narg ], XtNy, 120 ); narg++;
    XtSetArg( arglist[ narg ], XtNmaxWidth, MAIN_W ); narg++;
    XtSetArg( arglist[ narg ], XtNmaxHeight, MAIN_H ); narg++;
    XtSetArg( arglist[ narg ], DwtNiconifyPixmap, icon ); narg++;

    XtSetValues( toplevel, arglist, narg );


  /* create main window */

    narg = 0;
    XtSetArg( arglist[ narg ], XtNwidth, MAIN_W ); narg++;
    XtSetArg( arglist[ narg ], XtNheight, MAIN_H ); narg++;
    XtSetArg( arglist[ narg ], DwtNacceptFocus, FALSE ); narg++;

    main_window = DwtMainWindowCreate( toplevel, title, arglist, narg );

    XtManageChild( main_window );


  /* create work area */

    callback[ 0 ].proc = expose;
    callback[ 0 ].tag = 0;
    callback[ 1 ].proc = 0;

    work_area = DwtWindow( main_window, "work", 0, 0, 0, 0, callback );

    XtManageChild( work_area );


  /* create menu bar */

    menu_bar = DwtMenu( main_window, "Menu", 0, 0,
		           DwtMenuWorkArea, DwtOrientationHorizontal );
    XtSetArg( arglist[ 0 ], DwtNbordHighlight, TRUE );
    XtSetValues( menu_bar, arglist, 1 );

    XtManageChild( menu_bar );


  /* create 'File' pulldown menu */

    file_pd = DwtMenuPulldownCreate( menu_bar, "filePD", arglist, 0 );

    callback[ 0 ].proc = menu_select;
    callback[ 1 ].proc = 0;
    XtSetArg( arglist[ 1 ], DwtNactivateCallback, callback );
    narg = 2;

    callback[ 0 ].tag = MENU_EXIT;
    XtSetArg( arglist[ 0 ], DwtNlabel, DwtLatin1String( "Exit" ) );

    nmenu = 0;
    menu_item[ nmenu++ ] = DwtPushButtonGadgetCreate( file_pd, "exit",
						      arglist, narg );

    XtManageChildren( menu_item, nmenu );


    file_pde = DwtPullDownMenuEntry( menu_bar, "file", 0, 20,
			DwtLatin1String( "File" ),
			file_pd );

    XtManageChild( file_pde );


  /* create 'Option' pulldown menu */

    option_pd = DwtMenuPulldownCreate( menu_bar, "optionPD", arglist, 0 );

    callback[ 0 ].tag = MENU_RESET;
    XtSetArg( arglist[ 0 ], DwtNlabel, DwtLatin1String( "Reset" ) );

    nmenu = 0;
    menu_item[ nmenu++ ] = DwtPushButtonGadgetCreate( option_pd, "reset",
						      arglist, narg );

    callback[ 0 ].tag = MENU_INTERVAL;
    XtSetArg( arglist[ 0 ], DwtNlabel, DwtLatin1String( "Interval" ) );
    menu_item[ nmenu++ ] = DwtPushButtonGadgetCreate( option_pd, "interval",
						      arglist, narg );

    XtManageChildren( menu_item, nmenu );


    option_pde = DwtPullDownMenuEntry( menu_bar, "option", 0, 0,
			DwtLatin1String( "Option" ),
			option_pd );

    XtManageChild( option_pde );


  /* create 'interval_box' */

    interval_box = DwtDialogBox
			    ( main_window, "interval",
			      TRUE,
			      0, 0,
			      DwtLatin1String( "Set interval" ),
			      DwtModeless );

    nmenu = 0;

    callback[ 0 ].proc = menu_select;
    callback[ 0 ].tag = MENU_SCALE_OK;
    callback[ 1 ].proc = NULL;

    menu_item[ nmenu++ ] = int_scale =
	DwtScale( interval_box, "scale",
		      30, 5, 0, 0,
		      100, 30,
		      DwtLatin1String( "seconds" ),
		      MIN_INTERVAL, MAX_INTERVAL,
		      0,
		      tick / 1000,
		      DwtOrientationHorizontal );


    menu_item[ nmenu++ ] =
	DwtPushButton( interval_box, "ok",
		       2, 60,
		       DwtLatin1String ( "Ok" ),
		       callback );

    callback[ 0 ].tag = MENU_SCALE_CANCEL;

    menu_item[ nmenu++ ] =
	DwtPushButton( interval_box, "cancel",
		       80, 60,
		       DwtLatin1String ( "Cancel" ),
		       callback );

    XtManageChildren( menu_item, nmenu );


  /* realize widget */

    get_spi();			/* second call BEFORE window exposure */
				/* is necessary for rate computation */

    XtRealizeWidget( toplevel );

    work_window = XtWindow( work_area );


  /* create GC */

    gcval.foreground = BlackPixelOfScreen( screen );
    gcval.background = WhitePixelOfScreen( screen );

    work_gc = XCreateGC( display, work_window, GCForeground | GCBackground,
			 &gcval );


  /* load fonts */

    hdl_font = XLoadFont( display, HDL_FONT );
    std_font = XLoadFont( display, STD_FONT );


  /* start timer */

    (void)XtAddTimeOut( tick, timer_tick, 1 );


  /* loop forever */

    XtMainLoop();

}
