#!/usr/local/bin/g -file

#
# a toy perfmeter like program that shows you the cpu statistics using vmstat
# your mileage will vary on this program since vmstat's are a bit different
# for the various UNIX flavors. This one works for sunOS 4.whatever
#
# see XXX strings below for more
#

proc legendObject {title} {
    g_makeobj [set obj [g_genobj]]
      g_pushmatrix
	g_font small
	# to keep vogle from scaling the text
	g_viewport -100 100 -100 100
	g_move -0.01 -0.0097
        g_drawstr $title
      g_popmatrix
    g_closeobj
    return $obj
}
set NIL ","
proc stripchartObject {objID data {minVal ""} {maxVal ""}} {
    global NIL
    # if not supplied, determine the range to use for the y axis
    if {$minVal=="" || $maxVal==""} {
	set minVal [set maxVal 0.0]
	foreach p $data {
	    if {$p=="$NIL"} {continue}
	    if {$p<$minVal} {set minVal $p}
	    if {$p>$maxVal} {set maxVal $p}
	}
    }
    if {[set yScale [expr $maxVal-$minVal]]==0.0} {
	set yScale 1.0
    } else {
	set yScale [expr 1.0/$yScale]
    }
    set xScale [expr 1.0/([llength $data]-1)]

    # build an object out of the data points
    if {$objID != "-"} { g_makeobj $objID }
    g_pushmatrix
	g_translate -1.0 -1.0
	g_scale [expr 2*$xScale] [expr 2*$yScale]
	g_translate .0 [expr .0-$minVal]
	g_move 0 0
	set x 0
	foreach p $data {
	    if {$p!="$NIL"} {
		g_draw $x $p
		incr x
	    }
	}
    g_popmatrix
    if {$objID != "-"} { g_closeobj }
    return $objID
}
set Colors(us) green
set Colors(sy) yellow
set Colors(id) red
set Colors(background) black
set Colors(legend) cyan

proc paintMe {xlist {notcallback 0}} {
    global Points Colors Legend
    if {[winfo ismapped .Cpu]} {
	g_color $Colors(background); g_clear
	foreach x $xlist { 
	    g_color $Colors($x)
	    stripchartObject - $Points($x) 0 100 
	}
	g_color $Colors(legend)
	g_callobj $Legend
    }
}

proc vmstat_consumer {} {
    global Legend Points Colors
    set xlist { us sy id }

    wm minsize . 64 64 
    wm maxsize . 2000 2000 

    foreach x $xlist {
	set Points($x) {}; 
    }
    for {set i 0; } {$i<100} {incr i} { 
	foreach x $xlist { lappend Points($x) 0.0 }
    }
    set Legend [legendObject "cpu"]

    # see wmstuff package on tcl archive for wm protocol support
    catch { 
	wm protocol . delete killProducer 
    }

    foreach w {.Cpu} { 
	graphic .Cpu \
	    -squareaspect false \
	    -doublebuffered true \
	    -geometry 800x80 \
	    -font lucidasanstypewriter-12 \
	    -command "paintMe { $xlist }"

	pack append . $w {top fill expand} 
    }
    update
}

proc killProducer {} {
    send vmstatMeter { after 4 { destroy .; exit 0 } }
    destroy .
}

proc vmstat_producer {} {
    # start up our consumer counterpart...
    global consumer_Interp Pin
    set consumer_Interp [format "vmstat_consumer_%s" x]
    exec /usr/local/bin/g -file vmstatMeter -name $consumer_Interp consumer &
    # do not show any windows, we just generate data...
    wm withdraw .

    # --- XXX
    ####################################################
    ## these will need to suit your system, see kludge alert below
    set Cpu(start) 69
    set Cpu(end) end
    set Cpu(format) "%3d %3d %3d"
    ####################################################

    set Interval 5
    set HeaderLines 2

    set h ""
    after 5000
    set Pin [open "|vmstat $Interval" r]
    for {} {1} {} {
	update
	if {[gets $Pin ln]<0} {
	    puts stderr "error getting data from vmstat"
	    exit 1
	}
	set ln [string trimleft $ln " \t"]
	case $ln {[0-9]*} {
	    # XXX -- kludge alert --
	    # we only scan a piece of the output because scan 
	    # cannot scan the whole set of fields. hence the Cpu(start)
	    # nonsense. yuck !!
	    scan [string range $ln $Cpu(start) $Cpu(end)] $Cpu(format) \
		Cpu(us) Cpu(sy) Cpu(id) 
	    catch { 
		send $consumer_Interp cpuData $Cpu(us) $Cpu(sy) $Cpu(id)
	    } err
	} default {
	    append h "$ln\n"
	}
    }
    exit 0
}

proc cpuData {us sy id} {
    after 5 _cpuData $us $sy $id
}

proc _cpuData {us sy id} {
    global Points
    wm title . "$us $sy $id"
    foreach x {us sy id}  {
	lappend Points($x) [set $x]
	set Points($x) [lreplace  $Points($x) 0 0]
    }
    .Cpu paint "paintMe {us sy id} 1"
}

case $argc {1} {
    if {[lindex $argv 0]!="consumer"} {
	puts stderr "illegal argument '[lindex $argv 0]'"
	exit 2
    }
    vmstat_consumer
} {0} {
    vmstat_producer
} default {
    puts stderr "wrong number of arguments"; exit 1
}
