Updated 2013-08-02 20:26:28 by RJH

I am sure that I am not the only linux user who has a small window in the corner of the screen quietly purring away with status information provided by a useful little tool called conky.

While learning tck/tk, I wanted a small project on which to work and replacing conky with a home-grown version seemed like a good idea. Perhaps someone else might be interested in some small part of this code?

My needs from conky are pretty basic and so easy to reproduce: battery status, temperature, wireless connection status and time. I'm sure that canvas could be used in order to produce fancy graphs etc, but simple text will suffice for me.

Apart from the fun involved in doing so, developing this in tk produced other benefits; a tk solution can be interactive, for my version I have a simple 'df' panel which appears on a mouse click.
# Actually a rune - but resembles a symbol for an antenna.
set ip-ok  [format %c 5848]
# Cross.
set ip-bad [format %c 10007]
# Degree sign.
set deg-c  [format "%cC" 730]

# Got this from the wiki pages on combinatorial logic.
# I don't have a clue what the S/K combinator stuff was about - I was completely lost -
# but I do remember prog1 being useful from lisp...
proc prog1 {value args} {set value}

proc do: {v f1 f2} {prog1 [$f1 $v] [$f2 $v]}
proc slurp {f} {do: [open $f] read close}

# Did not know about mathop % (remainder) when I wrote this. Could be renamed...?
proc % {n d} {expr {(100 * $n)/$d}}

proc value-range {value range} {
  foreach {item limit} $range {
    if {$limit==""||$value<$limit} {
      return $item

proc parse {data match first last var pos} {
  upvar 1 $var var-ptr
  if {[lrange $data $first $last]==$match} {set var-ptr [lindex $data $pos]}

proc refresh "" {
  set design ""
  set remain ""
  set last ""
  set ip ""

  # Gather the input.
  catch {exec /sbin/ifconfig wlan0} result dummy

  catch {foreach s [split "$result" \n] { parse $s "inet" 0 0 ip 1}}

  foreach s [split [slurp /proc/acpi/battery/BAT1/state] \n] {
    parse $s "remaining capacity:" 0 1 remain 2

  foreach s [split [slurp /proc/acpi/battery/BAT1/info] \n] {
    parse $s "design capacity:" 0 1 design 2
    parse $s "last full capacity:" 0 2 last 3

  set temp [slurp /sys/class/thermal/thermal_zone0/temp] 

  # Present the output.
  .top.time config -text [clock format [clock seconds] -format %H:%M]

  if {[string length $ip] > 1} {
    .top.ip config -text "${::ip-ok}" -fg darkgreen
  } else {
    .top.ip config -text "${::ip-bad}" -fg red

  .top.power config -text "[% $remain $design]([% $last $design])%"
  .top.power config -fg [value-range [% $remain $last] {red 20 orange 80 darkgreen}]

  .top.temp config -text "[expr {$temp / 1000}]${::deg-c}"
  .top.temp config -fg [value-range $temp {darkgreen 50000 orange 80000 red}]

  # and again, and again, and again, and again...
  after 10000 refresh

proc toggle-bottom "" {
  if {[winfo ismapped .bottom]} {
    pack forget .bottom
  } else {
    catch {exec df -h} a b
    .bottom.text config -text $a
    pack .bottom -side bottom -anchor se

# Make a gui.
pack [frame .top] -side top -fill x 
pack [label .top.time]  -side right
pack [label .top.ip]    -side right
pack [label .top.power] -side right
pack [label .top.temp]  -side right

frame .bottom
pack [label .bottom.text -justify left]

# I wrote this some weeks ago.
# In hindsight there's probably an event propagation mechanism which would allow a single
# binding to .top but I'll leave that for now...
bind .top.time <ButtonRelease-1> toggle-bottom
bind .top.ip <ButtonRelease-1> toggle-bottom
bind .top.power <ButtonRelease-1> toggle-bottom
bind .top.temp <ButtonRelease-1> toggle-bottom
bind .top <ButtonRelease-1> toggle-bottom
bind .bottom.text <ButtonRelease-1> toggle-bottom