#!/usr/bin/wish
package require Expect

#-----------------------------------------------------------------------------
#
# tclkeymon
#
# Created:  08/26/2003
# Modified: 09/05/2005
# Author:   Mace Moneta
# Version:  0.51
# License:  GPL
#
# Changelog:
# ----------
#
# 0.51 Automatically adjust machine for power savings on battery
#      Remove legacy Fn-F2 fan control (now automatic)
#      Add microphone mute toggle with Fn-space
#      If lid closed, keep HD idle, even on AC
#      If cpuspeed is in use, set low CPU speed when lid closed,
#          or running on batteries
#      Add definition of wired and wireless devices to the config file
# 0.50 Support use of Toshiba utilities tbacklight utility if the user
#      specifies the location of tbacklight in /etc/tclkeymon.conf.
#          For example:
#          set tbacklight "/usr/local/bin/tbacklight"
#      Allow polling to be disabled with a poll interval of 0 in
#          the configuration file. When this is done, only screensaver
#          related actions work
#      Allow DMA setting on CD/DVD to be disabled with /dev/null.
#           For example:
#           set cd_dvd "/dev/null"
#      Use Firefox instead of Mozilla
#      Provide temperature based fan control,with config file setting.
#          Manual fan control is no longer available.
#          For example:
#          set tempHigh 50
#          set tempLow 45
#          set tempOff 40
#          Note: tempOff ignored while the machine is active
#      Allow disk drive to idle when the screen saver kicks in;
#          after two minutes on AC power
#          after 5 seconds on battery
#      Restart xscreensaver and dpms if necessary on Fn-B (blank)
# 0.42 Move brightness setting to polling loop
#      Try multiple locations for xscreensaver-command
#      Improve MUTE handling for multi-channel audio
# 0.41 Correct handling of initial CD player state
# 0.40 Use aumix and save mixer setting when muting/unmuting
# 0.39 Configuration file (/etc/tclkeymon.conf) support
# 0.38 Only clear the key queue if there was a key queued.  Duh.
# 0.37 Add Fn-d key combination to enable/disable DMA on CD/DVD combo
# 0.36 Use ifup/ifdown on wlan0 instead of PCMCIA start/stop
#      and remove delay
# 0.35 Change to use wish and 'package load Expect' to workaround
#      the lack of an expectk in SUSE
# 0.34 Undo 0.27/0.32 changes; nVidia driver unhappy with that change
# 0.33 Add GUI pop-ups for operations, add expectk to requisites
# 0.32 Set minimal LCD brightness when fan off/low, CPU performance low
# 0.31 Add low CPU performance/low fan speed to cycle list
# 0.30 Beta test distribution
# 0.29 Try to handle lack of ACPI functions gracefully
# 0.28 Add separate Changelog, README and INSTALL documents 
# 0.27 Toggle CPU performance P1/P0 when the fan is off/on respectively
# 0.26 Use standard location for mozilla (/usr/bin/mozilla)
# 0.25 Insert brief delay when toggling wired/wireless 
# 0.24 Enable Fn-F2 fan control (sequentially: off, low, high)
# 0.23 Deactivate screensaver when lid opened
# 0.22 Initial (alpha test) public release
#
# Credits:
# --------
#
# Some of the code in this script is based on the C and PERL code writen by
# Rob Miller (http://www.janerob.com/rob/ts5100/index.shtml).  The code from
# multiple routines has been integrated into this single process, and,
# obviously, recoded into TCL.  Just because.  :-)
#
# None of this would be possible without the Toshiba-ACPI kernel code created
# by John Belmonte (http://memebeam.org/toys/ToshibaAcpiDriver), which is in
# turn based upon the work of Jonathan Buzzard (http://www.buzzard.org.uk/toshiba/).
#
# Of course, the kernel ACPI team (http://acpi.sourceforge.net/) have done most
# of the dirty work.  Thanks to all!
#
# Description:
# ------------
#
# Monitor the Toshiba keys, and respond appropriately.  All the predefined
# keys function as they are labeled (except for Function-F3: Suspend to RAM,
# and Function-F4: Hibernate).  Function-b activates 
# the screensaver (which in turn will cause the lcd to be powered off), and 
# Function-c toggles the cPad backlight.  Opening and closing the lid of the
# laptop will use the Festival text-to-speech software 
# (http://www.cstr.ed.ac.uk/projects/festival/) to say "Hello" and "Goodbye"
# as appropriate (just for a cuteness factor).
#
# Requisites:
# -----------
#
# In addition to any scripts invoked by the various functions, the following
# utilities are used:
#
# cdcd     - command line CD player, for interfacing the physical CD controls
#            obtained from: http://cdcd.undergrid.net/
# usr_cpad - Interface to the Synaptic cPad
#            obtained from: http://www.janerob.com/rob/ts5100/cPad/index.shtml
# festival - Text-to-speech software
#            obtained from: http://www.cstr.ed.ac.uk/projects/festival/
# expectk  - TCL/Expect with TK GUI support, provided by most distributions
#
# Functions implemented:
# ----------------------
#
# Key or Button:        Action:
# ------------------    --------------------------------------
# Toshiba world button  Run firefox
# Toshiba t button      Open a Gnome-Terminal
# Function+Escape       Toggle Mute Audio Out
# Function+F1           Lock Screen
# Function+F5           Switch Video Out
# Function+F6           Dim LCD; beep at minimum setting
# Function+F7           Brighten LCD; beep at maximum setting
# Function+F8           Toggle Wireless/Wired connection
# CD Stop button        Stop CD or Eject CD if stopped
# CD Previous button    Play Previous track
# CD Next button        Play Next track
# CD Play button        Play CD or pause/resume if playing
# Lid open/close        Use Festival for "Hello"/"Goodbye"
# Function+b            Blank Screen
# Function+c            Toggle cPad Backlight
# Function+d            Toggle DMA on/off for /dev/hdc
# Function+space        Toggle Mute Microphone
#
# Usage:
# ------
#
# This script should be run as root.  It can be started in /etc/rc.d/rc.local
# at system initialization (redirect all output to /dev/null if doing that).
# If run interactively, it will display the keys/buttons as they are processed
# and the action taken (useful for debugging).
#
# Notes:
# ------
#
# 1. Because of the way gnome-terminal is started from an su session, make sure
#    to set the XAUTHORITY in the ~/.bash_profile (of the person logged in, not 
#    root!).  For example:
#
#      export XAUTHORITY=/home/myuserid/.Xauthority
#
#    If this isn't done, the /root/.xauth?????? file will be used, causing a
#    20 second delay anytime xauth is invoked (for example, starting an ssh
#    session).
#
# 2. Be careful of the commands in your .bashrc and .bash_profile.  If this
#    script stops when attempting to open a firefox window or Gnome-terminal
#    window, those are the first place to check, because they are invoked via
#    an "su" command that starts a login shell.  Beware of commands that work
#    in foreground, but not in background (e.g., stty).
#
#-----------------------------------------------------------------------------

#-----------------------
# Display startup header
#-----------------------
puts ""
puts "[clock format [clock seconds] -format "%a %b %d %H:%M:%S %Z %Y"] tclkeymon starting"
puts ""

#-----------------------------------------------------------------------------
# **************************** Global Variables ******************************
#-----------------------------------------------------------------------------

#------------------------------------------------------------
# If available, load the configuration in /etc/tclkeymon.conf
#------------------------------------------------------------
if {[file exists /etc/tclkeymon.conf]} {
   puts "Loading /etc/tclkeymon.conf"
   catch [source /etc/tclkeymon.conf]
   puts "Loading /etc/tclkeymon.conf completed"
}

#-----------------------------------
# The initial state of the CD player
#-----------------------------------
global cdState
set cdState "stop"
catch {exec /usr/local/bin/cdcd --device /dev/cdrom stop &}

#--------------------------------------------------------
# The command to execute fot the Toshiba "t" button
# (usually located next to the power and "world" buttons)
# Default: "/usr/bin/gnome-terminal"
#--------------------------------------------------------
global t_button
if {![info exists t_button]} {set t_button "/usr/bin/gnome-terminal"}
puts "Setting: t_button=$t_button"

#-------------------
# The pop-up message
#-------------------
global message

#-----------------------
# Saved audio out volume
#-----------------------
global saveMixer
set saveMixer 0

#------------------------
# Saved microphone volume
#------------------------
global saveMicrophone
set saveMicrophone 0

#------------------------------------
# The CD/DVD device, for toggling DMA
#------------------------------------
global cd_dvd
if {![info exists cd_dvd]} {set cd_dvd "/dev/hdc"}
puts "Setting: cd_dvd=$cd_dvd"

#-------------------------
# The location of the keys
#-------------------------
global keyFile
if {![info exists keyFile]} {set keyFile "/proc/acpi/toshiba/keys"}
puts "Setting: keyFile=$keyFile"
if {![file exists $keyFile]} {
   puts "Unable to open Toshiba key file: $keyFile"
   puts "Keyboard and button functions disabled"
   set keyFile "/dev/null"
}

#------------------------
# The location of the LCD
#------------------------
global lcdFile
if {![info exists lcdFile]} {set lcdFile "/proc/acpi/toshiba/lcd"}
puts "Setting: lcdFile=$lcdFile"
if {![file exists $lcdFile]} {
   puts "Unable to open Toshiba lcd file: $lcdFile"
   puts "LCD brightness functions disabled"
   set lcdFile "/dev/null"
}
puts "Initializing brightness to medium intensity"
catch {exec echo "brightness: 4" > $lcdFile}
global brightness
set brightness 4

#--------------------------------
# The location of the video state
#--------------------------------
global videoFile
if {![info exists videoFile]} {set videoFile "/proc/acpi/toshiba/video"}
puts "Setting: videoFile=$videoFile"
if {![file exists $videoFile]} {
   puts "Unable to open Toshiba video file: $videoFile"
   puts "Video out functions disabled"
   set videoFile "/dev/null"
}

#---------------------------
# The location of tbacklight
#---------------------------
global tbacklight
global backlight
set backlight "n"

if {$tbacklight == "" || ![info exists tbacklight] || [file tail $tbacklight] != "tbacklight"} {
   puts "Toshiba utility tbacklight not specified; will not be used"
   set tbacklight ""
} else {
   puts "Using Toshiba utility tbacklight for backlight control"
   if {![file exists $tbacklight]} {
      puts "$tbacklight : does not exist"
      set tbacklight ""
   }
   if {![file executable $tbacklight]} {
      puts "$tbacklight : not executable"
      set tbacklight ""
   }
}

#------------------------------
# The location of the lid state
#------------------------------
global lidHello
if {![info exists lidHello]} {set lidHello "Hello"}
puts "Setting: lidHello=$lidHello"
global lidGoodbye
if {![info exists lidGoodbye]} {set lidGoodbye "Goodbye"}
puts "Setting: lidGoodbye=$lidGoodbye"

global lidFile
if {![info exists lidFile]} {set lidFile "/proc/acpi/button/lid/LID/state"}
puts "Setting: lidFile=$lidFile"
global lastLidState
set    lastLidState "open"
if {![file exists $lidFile]} {
   puts "Unable to open ACPI lid file: $lidFile"
   puts "Lid open/close functions disabled"
   set lidFile "/dev/null"
}

#-------------------------------------
# The location of the AC Adapter state
#-------------------------------------
global acAdapter
if {![info exists acAdapter]} {
   if {[file exists /proc/acpi/ac_adapter/ADP1/state]} {
      set acAdapter "/proc/acpi/ac_adapter/ADP1/state"
      puts "Setting: AC adapter file=$acAdapter"
   } else {
      set acAdapter "/dev/null"
      puts "Unable to open default ACPI AC adapter file: $acAdapter"
      puts "Setting: AC adapter checking disabled"
   }
} else {
   if {![file exists $acAdapter]} {
      puts "Unable to open ACPI AC adapter file: $acAdapter"
      puts "Setting: AC adapter checking disabled"
      set acAdapter "/dev/null"
   } else {
      puts "Setting: AC adapter file=$acAdapter"
   }
}

#--------------------------------
# The location of the fan control
#--------------------------------
global fanFile
if {![info exists fanFile]} {set fanFile "/proc/acpi/toshiba/fan"}
puts "Setting: fanFile=$fanFile"
global fanState
if {![info exists fanState]} {set fanState "low"}
puts "Setting: fanState=$fanState"
if {![file exists $fanFile]} {
   puts "Unable to open ACPI fan file: $fanFile"
   puts "Fan control disabled"
   set fanFile "/dev/null"
}
if {$fanState == "low"} {
   puts "Initializing fan on, low"
   catch {exec echo "force_on:1" > $fanFile}
}

#----------------------------------------------------
# How frequently, in milliseconds, we check for a key
#----------------------------------------------------
global pollInterval
if {![info exists pollInterval]} {set pollInterval 500}
puts "Setting: pollInterval=$pollInterval"

#-------------------------------------------------------
# Which xscreensaver and xscreensaver-command do we use?
#-------------------------------------------------------
global xscreensaverCommand
if {[file exists /usr/bin/X11/xscreensaver-command]} {
   set xscreensaverCommand "/usr/bin/X11/xscreensaver-command"
} elseif {[file exists /usr/bin/xscreensaver-command]} {
   set xscreensaverCommand "/usr/bin/xscreensaver-command"
   } elseif {[file exists /usr/X11R6/bin/xscreensaver-command]} {
         set xscreensaverCommand "/usr/X11R6/bin/xscreensaver-command"
}
global xscreensaver
if {[file exists /usr/bin/X11/xscreensaver]} {
   set xscreensaver "/usr/bin/X11/xscreensaver"
} elseif {[file exists /usr/bin/xscreensaver]} {
   set xscreensaver "/usr/bin/xscreensaver"
   } elseif {[file exists /usr/X11R6/bin/xscreensaver]} {
         set xscreensaver "/usr/X11R6/bin/xscreensaver"
}

#------------------------------
# Temperature based fan control
#------------------------------
global tempHigh
if {![info exists tempHigh]} {set tempHigh 50}
global tempLow
if {![info exists tempLow]} {set tempLow 45}
global tempOff
if {![info exists tempOff]} {set tempHigh 40}
global saveTempOff
set saveTempOff $tempOff
global tempFile
if {![info exists tempFile]} {set tempFile "/proc/acpi/thermal_zone/THRM/temperature"}
global temp
set temp 0
global tempLast
set tempLast 0

#-----------------------
# Idle/Active user exits
#-----------------------
global callOnIdle
if {![info exists callOnIdle]} {
   set callOnIdle ""
} else {
   if {![file executable $callOnIdle]} {
      set callOnIdle ""
      puts "callOnIdle file: $callOnIdle not executable"
   }
}
global callOnActive
if {![info exists callOnActive]} {
   set callOnActive ""
} else {
   if {![file executable $callOnActive]} {
      set callOnActive ""
      puts "callOnActive file: $callOnActive not executable"
   }
}

#------------------------
# AC Adaper initial state
#------------------------
global ACstate
set ACstate "on-line"
global lastACstate
set lastACstate ""

#-----------------
# Ethernet devices
#-----------------
global WiredEthernet
global WirelessEthernet
if {![info exists WiredEthernet]} {
   set WiredEthernet "eth0"
}
if {![info exists WirelessEthernet]} {
   set WirelessEthernet "eth1"
}
puts "Setting: WiredEthernet=$WiredEthernet"
puts "Setting: WirelessEthernet=$WirelessEthernet"

#-----------------------------------------------------------------------------
# **************************** Subroutines ***********************************
#-----------------------------------------------------------------------------

#-------------------------------------------
# Any required cleanup before exit goes here
#-------------------------------------------
proc cleanup {} {
   puts ""
   puts "[clock format [clock seconds] -format "%a %b %d %H:%M:%S %Z %Y"] tclkeymon Exiting on interrupt"
   puts ""
   exit
}
 
#-------------------------------
# Exit gracefully when requested
#-------------------------------
trap cleanup {SIGINT SIGHUP SIGQUIT SIGUSR1 SIGTERM}
 
#--------------------------
# Remove the pop-up message
#--------------------------
proc {removeMessage} {} {
   catch {wm withdraw .}
   catch {destroy .popup}
   update
}

#---------------------
# Get AC Adapter state
#---------------------
proc {getAC} {} {
global acAdapter
   if {$acAdapter != "/dev/null"} {
      set fileId [open $acAdapter r]
      foreach line [split [read $fileId] "\n"] {
         switch -regexp -- [lindex $line 0] {
            "state:" {
             set adapterState [lindex $line 1]
            }
         }
      }
      close $fileId
   } else {
      set adapterState "off-line"
   }
   return $adapterState
}

#-----------------
# Idle the machine
#-----------------
proc {stateIdle} {} {
global saveTempOff
global tempOff
global callOnIdle
global fanState
global fanFile
global lidFile
   # Does the user want to do something at idle?
   if {$callOnIdle !=""} {
      puts "Running: $callOnIdle"
      catch {exec $callOnIdle}
   }
   # Allow fan to shut off
   set tempOff $saveTempOff
   puts "Turning off fan at $tempOff degrees Centigrade"
   catch {exec echo "force_on:0" > $fanFile}
   set fanState "off"
   # Idle the hard drive
   set AC [getAC]
   set lid [getLid]
   if {($AC == "on-line") && ($lid == "open")} {
      puts "Idling hard drive after two minutes, because AC is $AC and lid is $lid"
      catch {exec /sbin/hdparm -S 24 /dev/hda}
   } else {
      puts "Idling hard drive after five seconds, because AC is $AC and lid is $lid"
      catch {exec /sbin/hdparm -S 1 /dev/hda}
      # Set low CPU speed
      catch {exec /usr/bin/killall -USR2 cpuspeed}
   }
   # Sync disk after  30 minutes (1800 seconds)
   catch {exec echo "1800" > /proc/sys/vm/laptop_mode}
   catch {exec echo "180000" > /proc/sys/vm/dirty_writeback_centisecs}
   catch {exec echo "180000" > /proc/sys/vm/dirty_expire_centisecs}
   catch {exec echo "80" > /proc/sys/vm/dirty_ratio}
   catch {exec /bin/sync}
   # Spin down now
   catch {exec /sbin/hdparm -y /dev/hda}
}

#------------------------------------
# Restore machine to normal operation
#------------------------------------
proc {stateActive} {} {
global tempOff
global callOnActive
global fanFile
global fanState
   # Only do this on AC power
   if {[getAC] == "on-line"} {
      # Set normal CPU speed
      catch {exec /usr/bin/killall -HUP cpuspeed}
      # Keep the HD active
      puts "Activating hard drive"
      catch {exec /sbin/hdparm -S 0 /dev/hda}
      # Restore normal disk sync
      catch {exec echo "0" > /proc/sys/vm/laptop_mode}
      catch {exec echo "500" > /proc/sys/vm/dirty_writeback_centisecs}
      catch {exec echo "3000" > /proc/sys/vm/dirty_expire_centisecs}
      catch {exec echo "40" > /proc/sys/vm/dirty_ratio}
      catch {exec /bin/sync}
      # Don't shut off fan
      set tempOff 0
      puts "Turning off fan disabled"
      catch {exec echo "force_on:1" > $fanFile}
      set fanState "low"
      # Does the user want to do something when active?
      if {$callOnActive !=""} {
         puts "Running: $callOnActive"
         catch {exec $callOnActive}
      }
   }
}

#---------------------------------------
# Center the pop-up window on the screen
#---------------------------------------
proc {center} {wname} {
global message
set s_width [winfo screenwidth .]
set s_height [winfo screenheight .]
set w_width [expr [string length $message] * 5]
set w_height 20
set xpos [expr ($s_width - $w_width) / 2]
set ypos [expr ($s_height - $w_height) / 2]
catch {wm geometry $wname +$xpos+$ypos}
}

#---------------------------
# Display the pop-up message
#---------------------------
proc {displayPopUp} {msg} {
global message
removeMessage
set message $msg
pack [label .popup -text $message]
center .
catch {wm deiconify .}
update
}

#-------------------------------
# Subroutine: Retrieve lid state
#-------------------------------
proc {getLid} {} {
global lidFile

   set fileId [open $lidFile r]

   foreach line [split [read $fileId] "\n"] {
      switch -regexp -- [lindex $line 0] {
         "state:" {
            set lidState [lindex $line 1]
         }
      }
   }
   close $fileId
   return $lidState
}

#-----------------------------------------------
# Subroutine: Retrieve current hotkey from queue
#-----------------------------------------------
proc {getKey} {} {
global keyFile

   set fileId [open $keyFile r]

   foreach line [split [read $fileId] "\n"] {
      switch -regexp -- [lindex $line 0] {
         "hotkey_ready:" {
            set hotkey_ready [lindex $line 1]
         }
         "hotkey:" {
            set hotkey [lindex $line 1]
         }
      }
   }
   close $fileId
   if {$hotkey_ready == 0} {
      set hotkey ""
   } else {
      clearKey
   }
   return $hotkey
}

#--------------------
# Subroutine: getTemp
#--------------------
proc {getTemp} {} {
global tempFile

   set fileId [open $tempFile r]
   set temp [lindex [read $fileId] 1]
   close $fileId
   return $temp
}

#-----------------------------------------------
# Subroutine: Clear the current key in the queue
#-----------------------------------------------
proc {clearKey} {} {
global keyFile
   catch {exec echo "hotkey_ready:0" > $keyFile}
}

#--------------------------------------------------
# Subroutine: Clear the queue of pending keystrokes
#--------------------------------------------------
proc {clearQueue} {} {
   while {[getKey] != ""} {
      clearKey
   }
}

#-----------------------------
# Subroutine: Switch video out
#-----------------------------
proc {switchVideo} {} {
global videoFile

   set lcd_out 0
   set crt_out 0
   set tv_out 0

   #------------------------------
   # Get the current active device
   #------------------------------
   set fileId [open $videoFile r]

   foreach line [split [read $fileId] "\n"] {
      switch -regexp -- [lindex $line 0] {
         "lcd_out:" {
            set lcd_out [lindex $line 1]
         }
         "crt_out:" {
            set crt_out [lindex $line 1]
         }
         "tv_out:" {
            set tv_out [lindex $line 1]
         }
      }
   }
   close $fileId

   #-------------------------
   # Rotate the active device
   #-------------------------
   set test $tv_out
   set tv_out $crt_out
   set crt_out $lcd_out
   set lcd_out $test
   
   #---------------------------------------------------------
   # If no video device is currently active, activate the lcd
   #---------------------------------------------------------
   if {($lcd_out == 0) && ($crt_out == 0) && ($tv_out == 0)} {
      set lcd_out 1
   }
   
   #-------------------------------
   # Set all inactive devices first
   #-------------------------------
   if {$lcd_out == 0} {catch {exec echo "lcd_out: $lcd_out" > $videoFile}}
   if {$crt_out == 0} {catch {exec echo "crt_out: $crt_out" > $videoFile}}
   if {$tv_out == 0}  {catch {exec echo "tv_out: $tv_out" > $videoFile}}

   #--------------------------
   # Now set the active device
   #--------------------------
   if {$lcd_out == 1} {catch {exec echo "lcd_out: $lcd_out" > $videoFile}}
   if {$crt_out == 1} {catch {exec echo "crt_out: $crt_out" > $videoFile}}
   if {$tv_out == 1}  {catch {exec echo "tv_out: $tv_out" > $videoFile}}

}

#--------------------------------------------------
# Subroutine: Monitor for incoming keys and buttons
#--------------------------------------------------
proc keyScan {} {
global keyFile
global lcdFile
global lidFile
global videoFile
global tbacklight
global backlight
global lastLidState
global pollInterval
global fanState
global fanFile
global t_button
global saveMixer
global saveMicrophone
global cd_dvd
global lidHello
global lidGoodbye
global cdState
global brightness
global xscreensaver
global xscreensaverCommand
global tempFile
global temp
global tempLast
global tempHigh
global tempLow
global tempOff
global ACstate
global lastACstate
global WiredEthernet
global WirelessEthernet
   
   set key [getKey]
   if {$key != ""} {
      switch -glob -- $key {

         {*b86} {
            puts "Toshiba world key: Run firefox"
            set owner [lindex [file attributes /dev/console] 3]
            if {"$owner" != "root"} {
               puts "Performing function on behalf of user $owner"
               catch {exec /bin/su - $owner -c {/usr/bin/firefox -remote "ping()"}} test
               if {"$test" == ""} {
                  puts "Existing firefox process found - opening new window"
                  catch {exec /bin/su - $owner -c {/usr/bin/firefox -remote "openURL(about:blank,new-window)"} &}
               } else {
                  puts "No existing firefox process found - starting application"
                  catch {exec /bin/su - $owner -c {/usr/bin/firefox about:blank} &}
               }
            } else {
               puts "Performing function on behalf of user $owner"
               catch {exec {/usr/bin/firefox -remote "ping()"}} test
               if {"$test" == ""} {
                  puts "Existing firefox process found - opening new window"
                  catch {exec {/usr/bin/firefox -remote "openURL(about:blank,new-window)"} &}
               } else {
                  puts "No existing firefox process found - starting application"
                  catch {exec {/usr/bin/firefox about:blank} &}
               }
            }
         }

         {*b87} {
            puts "Toshiba t key: run $t_button"
            set owner [lindex [file attributes /dev/console] 3]
            if {"$owner" != "root"} {
               puts "Performing function on behalf of user $owner"
               catch {exec /bin/su - $owner -c $t_button &}
            } else {
               puts "Performing function on behalf of user $owner"
               catch {exec $t_button &}
            }
         }

         {*100} {
            puts "Function key release"
            removeMessage
         }

         {*101} {
            puts "Function+Escape: Toggle Mute Audio Out"
            set test [exec /usr/bin/aumix -v q]
            set test [lindex $test 2]
            if {[string first "," $test] != -1} {
               set test [string replace $test [string first "," $test] [string first "," $test]]
            }
            if {"$test" != "0"} {
               puts "Mute - saved audio out volume level $test"
               set saveMixer $test
               catch {exec /usr/bin/aumix -v 0}
            } else {
               puts "Unmute - restored audio out volume level $saveMixer"
               catch {exec /usr/bin/aumix -v $saveMixer}
            }
         }

         {*120} {
            puts "Function+d: toggle DMA on $cd_dvd"
            if {"$cd_dvd" != "/dev/null"} {
               set test [lindex [exec /sbin/hdparm -v $cd_dvd 2>/dev/null | /bin/grep "using_dma"] 2]
               if {"$test" == "0"} {
                  puts "DMA on"
                  catch {exec /sbin/hdparm -d1 $cd_dvd}
                  displayPopUp "DMA on for DVD playback"
               } else {
                  puts "DMA off"
                  catch {exec /sbin/hdparm -d0 $cd_dvd}
                  displayPopUp "DMA off for CD use"
               }
            } else {
               puts "Function+d: disabled. cd_dvd set to $cd_dvd"
            }
         }

         {*12e} {
            puts "Function+c: Toggle cPad Backlight"
            set test [exec /usr/local/bin/usr_cpad -c]
            if {"$test" == "0"} {
               puts "cPad on"
               catch {exec /usr/local/bin/usr_cpad -b 1 &}
            } else {
               puts "cPad off"
               catch {exec /usr/local/bin/usr_cpad -b 0 &}
            }
         }

         {*130} {
            puts "Function+b: Blank Screen"
            catch {exec $xscreensaverCommand -activate} test
            if {[string match "*no screensaver is running*" $test]} {
               puts "Screensaver not active, restarting (5 seconds)"
               set owner [lindex [file attributes /dev/console] 3]
               catch {exec /bin/su - $owner -c $xscreensaver >/dev/null 2>/dev/null &}
               after 5000
               catch {exec $xscreensaverCommand -activate &}               
            }
         }

         {*139} {
            puts "Function+space: Toggle Microphone Mute"
            set test [exec /usr/bin/aumix -m q]
            set test [lindex $test 2]
            if {[string first "," $test] != -1} {
               set test [string replace $test [string first "," $test] [string first "," $test]]
            }
            if {"$test" != "0"} {
               puts "Mute - saved microphone volume level $test"
               set saveMicrophone $test
               catch {exec /usr/bin/aumix -m 0}
            } else {
               puts "Unmute - restored microphone volume level $saveMicrophone"
               catch {exec /usr/bin/aumix -m $saveMicrophone}
            }
         }

         {*13b} {
            puts "Function+F1: Lock Screen"
            catch {exec $xscreensaverCommand -lock} test
            if {[string match "*no screensaver is running*" $test]} {
               puts "Screensaver not active, restarting (5 seconds)"
               set owner [lindex [file attributes /dev/console] 3]
               catch {exec /bin/su - $owner -c $xscreensaver >/dev/null 2>/dev/null &}
               after 5000
               catch {exec $xscreensaverCommand -lock &}               
            }
         }

         {*13f} {
            puts "Function+F5: Switch Video Out"
            switchVideo
         }

         {*140} {
            puts "Function+F6: Dim LCD"
            set fileId [open $lcdFile r]

            foreach line [split [read $fileId] "\n"] {
               switch -regexp -- [lindex $line 0] {
                  "brightness:" {
                     set brightness [lindex $line 1]
                  }
                  default {}
               }
            }
            close $fileId
            if {"$brightness" == "0"} {
               displayPopUp "brightness: $brightness"
               bell
            } else {
               incr brightness -1
               catch {exec echo "brightness: $brightness" > $lcdFile}
               displayPopUp "brightness: $brightness"
            }
         }

         {*141} {
            puts "Function+F7: Brighten LCD"
            set fileId [open $lcdFile r]

            foreach line [split [read $fileId] "\n"] {
               switch -regexp -- [lindex $line 0] {
                  "brightness:" {
                     set brightness [lindex $line 1]
                  }
                  default {}
               }
            }
            close $fileId
            if {"$brightness" == "7"} {
               displayPopUp "brightness: $brightness"
               bell
            } else {
               incr brightness
               catch {exec echo "brightness: $brightness" > $lcdFile}
               displayPopUp "brightness: $brightness"
            }
         }

         {*142} {
            puts "Function+F8: Toggle Wireless/Wired connection"
            catch {exec /sbin/ifconfig $WiredEthernet | /bin/grep "UP"} test
            set test [lindex $test 0]
            if {"$test" != "UP"} {
               puts "Switching to $WiredEthernet"
               displayPopUp "Switching to $WiredEthernet"
               catch {exec /etc/sysconfig/network-scripts/ifdown $WirelessEthernet}
               catch {exec /etc/sysconfig/network-scripts/ifup $WiredEthernet}
            } else {
               puts "Switching to $WirelessEthernet"
               displayPopUp "Switching to $WirelessEthernet"
               catch {exec /etc/sysconfig/network-scripts/ifdown $WiredEthernet}
               catch {exec /etc/sysconfig/network-scripts/ifup $WirelessEthernet}
            }
         }

         {*b30} {
            switch -- $cdState {
               {pause} {
                  puts "CD Player Stop"
                  set cdState "stop"
                  catch {exec /usr/local/bin/cdcd --device /dev/cdrom stop &}
               }
               {play} {
                  puts "CD Player Stop"
                  set cdState "stop"
                  catch {exec /usr/local/bin/cdcd --device /dev/cdrom stop &}
               }
               {stop} {
                  puts "CD Player Eject"
                  catch {exec /usr/bin/eject /dev/cdrom &}
               }
               default {
                  puts "CD Player Stop - ignored, cdState $cdState"
               }
            }
         }

         {*b31} {
            if {"$cdState" == "play"} {
               puts "CD Player Previous"
               catch {exec /usr/local/bin/cdcd --device /dev/cdrom prev &}
            } else {
               puts "CD Player Previous - ignored, cdState $cdState"
            }
         }

         {*b32} {
            if {"$cdState" == "play"} {
               puts "CD Player Next"
               catch {exec /usr/local/bin/cdcd --device /dev/cdrom next &}
            } else {
               puts "CD Player Next - ignored, cdState $cdState"
            }
         }

         {*b33} {
            switch -- $cdState {
               {stop} {
                  puts "CD Player Play"
                  set cdState "play"
                  catch {exec /usr/local/bin/cdcd --device /dev/cdrom play} test
                  if {"$test" != ""} {
                     puts "CD Player Auto-Stop"
                     set cdState "stop"
                  }
               }
               {play} {
                  puts "CD Player Pause"
                  set cdState "pause"
                  catch {exec /usr/local/bin/cdcd --device /dev/cdrom pause &}
               }
               {pause} {
                  puts "CD Player Resume"
                  set cdState "play"
                  catch {exec /usr/local/bin/cdcd --device /dev/cdrom resume &}
               }
               default {
                  puts "CD Player undefined state: $cdState"
                  set cdState "stop"
               }
            }
         }

         default {
            puts "Unknown: $key"
         }
      }
   }

   #-----------------------------------------------------
   # Check to see if the laptop has been opened or closed
   #-----------------------------------------------------
   set lid [getLid]
   if { $lid != $lastLidState } {
      puts "Laptop lid state change: $lid"
      set lastLidState $lid
      if { $lid == "open" } {
         puts "Saying: $lidHello"
         catch "exec echo \"$lidHello\" | /usr/bin/festival --tts"
         catch {exec $xscreensaverCommand -deactivate &}
      } else {
         puts "Saying: $lidGoodbye"
         catch "exec echo \"$lidGoodbye\" | /usr/bin/festival --tts"
         catch {exec $xscreensaverCommand -activate &}
      }
   }

   #-----------------------------------------------
   # Check to see if we're running on AC or battery
   #-----------------------------------------------
   set ACstate [getAC]
   if { $ACstate != $lastACstate } {
      puts "AC state change: $ACstate"
      set lastACstate $ACstate
      if { $ACstate == "on-line" } {
         catch {exec echo "brightness: 5" > $lcdFile}
         set brightness "4"
         catch {exec echo "brightness: $brightness" > $lcdFile}
         stateActive
      } else {
         catch {exec echo "brightness: 1" > $lcdFile}
         set brightness "0"
         catch {exec echo "brightness: $brightness" > $lcdFile}
         stateIdle
      }
   }


   #---------------------------------------------------------
   # Check to see if we need to speed up or slow down the fan
   #---------------------------------------------------------
   set temp [getTemp]
   if {$temp >= $tempHigh} {
      set fanState "high"
      catch {exec echo "force_on:0" > $fanFile}
      catch {exec echo "force_on:1" > $fanFile}
      set tempLast $temp
   }
   if {($temp < $tempHigh) && ($temp >= $tempLow) && ($temp != $tempLast)} {
      set fanState "low"
      catch {exec echo "force_on:1" > $fanFile}
      set tempLast $temp
   }
   if {($temp <= $tempOff) && ($temp != $tempLast)} {
      catch {exec echo "force_on:0" > $fanFile}
      set fanState "off"
      set tempLast $temp
   }

   #---------------------------------------------------------
   # Brightness setting must be reset on each poll or the
   # BIOS will return to value a medium setting automatically
   # on some machines
   #---------------------------------------------------------
   catch {exec echo "brightness: $brightness" > $lcdFile}      

   #-----------------------
   # Schedule the next poll
   #-----------------------
   after $pollInterval keyScan
}

#-----------------------------------------------------------------------------
# **************************** Mainline code *********************************
#-----------------------------------------------------------------------------

#----------------------------
# Initialize the pop-up state
#----------------------------
catch {wm withdraw .}

#-------------------------------------
# Clear the key and button press queue
#-------------------------------------
clearQueue
stateActive

spawn -noecho $xscreensaverCommand -watch 
set timeout -1
if {$pollInterval != 0} {
   after $pollInterval keyScan
}
while {1} {
   expect {
      "UNBLANK" {
         puts "Unblank screen"
         if {$tbacklight != ""} {
            puts "Running :  $tbacklight -n"
            catch {exec $tbacklight -n}
         } else {
            switchVideo
            catch {exec /usr/X11R6/bin/xset dpms force on}
         }
         stateActive
         catch {exec /usr/X11R6/bin/xset s reset}
      }
      "BLANK"   {
         puts "Blank screen"
         if {$tbacklight != ""} {
            puts "Running :  $tbacklight -f"
            catch {exec $tbacklight -f}
         } else {
            catch {exec echo "lcd_out: 0" > $videoFile}
            catch {exec /usr/X11R6/bin/xset dpms force off}
         }
         stateIdle
      }
      "LOCK"    {
         puts "Lock screen"
         if {$tbacklight != ""} {
            catch {exec $tbacklight -f}
         } else {
            catch {exec echo "lcd_out: 0" > $videoFile}
            catch {exec /usr/X11R6/bin/xset dpms force off}
         }
         stateIdle
      }
   }
}
