#!/usr/bin/perl
###########
#
# Daemon to monitor sonypi events and do things, on machines that have
# the sony programmable I/O device. Requires the "sonypid" daemon as 
# input, and the "spicctrl" and "readeontool" programs for output.
#
# There is a table of actions to take for each sonypi function-key event.  
# Only a few of the events (notably eject, brightness, video out, and 
# suspend to disk) are used.
# 
# The path may need customization for your system; it works on my VGN-A197
# machine with Debian sarge and radeontool and sonypid installed in
# /usr/local/bin.
#
# Best of luck; questions, comments, or revisions may be directed to 
# "deforest@boulder.swri.edu".
#
# Copyright Craig DeForest, 2003
#
# Minor adjustments to fit into Debian's framework and support 
# Sony's VAIO A-Series by Denny Priebe, 2004
#
# You may distribute this under the terms of the GPL version 2; the 
# complete license is available at http://www.gnu.org/licenses/gpl.txt
#

$ENV{PATH}  = '/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
delete @ENV{qw(IFS CDPATH ENV BASH_ENV LD_LIBRARY_PATH)};

##########
# Some setup variables collected here for convenience

$bright_steps = 10;
$spicctrl = 'spicctrl';

# /dev/cdrom is usally a good idea. Change this if needed.
$cdrom = "/dev/cdrom";

# Change this to the command that usually suspends your machine.
# It differs between the existing implementations (pmdisk, swsusp, swsusp2)
$suspend_command = "true";

##########
# Some brightness settings
$minbs = 0;
$maxbs = 255;
$bs = ($maxbs-$minbs+1)/$bright_steps;

##########
# What to do when the raise brightness key is hit
$brighter = sub {
    $B =  int(`$spicctrl -B`) + int($bs);
    if ($B <= $maxbs) {
       `$spicctrl -b $B`;
    }
};

##########
# What to do when the lower brightness key is hit
$darker = sub {
    $B = int(`$spicctrl -B`) - int($bs);
    if ($B >= $minbs) {
       `$spicctrl -b $B`;
    }
};

##########
# What to do when the toggle video out key is hit
$toggle_vo = sub {
    $rv = `radeontool dac`;
    @words = split(m/\s+/,$rv);
    $status = $words[$#words];
    $ns = "off" if $status eq "on";
    $ns = "on" if $status eq "off";
    `radeontool dac $ns && logger -t sonypidd "Video out is $ns"`;
};

##########
# What to do when the suspend key is hit
$suspend = sub {
    `logger -t sonypidd "Suspending"`;
    `$suspend_command`;
};

##########
# Table of function-key actions.  List ref containing strings 
# executes strings as shell commands; code ref gets executed in situ.

%cmds = (
          "F5"  => $darker,                  # Fn-F5 - decrease brightness
          "F6"  => $brighter,                # Fn-F6 - increase brightness
          "F7"  => $toggle_vo,               # Fn-F7 - toggle video out
          "F12" => $suspend,                 # Fn-F12 - suspend to disk
          "E"   => ["eject $cdrom"],         # Eject button aka Fn-E
        );

#
# Daemon stuff -- check the lockfile (There Can Only Be One!) and 
# spawn twice to disassociate from the terminal.
# 
if(open FILE,"</var/run/sonypidd.pid" ) {
    chomp($otherpid = <FILE>);
    @lines = `ps -e`;
    @found = grep(/^\s*$otherpid\s/o && /sonypidd/,@lines);
    if(@found) {
	print STDERR "Sonypidd is already running (PID $otherpid)\n";
	exit(1);
    }
}

open FILE,">/var/run/sonypidd.pid" || die "Couldn't open lock file\n";
exit 0 if(fork);
exit 0 if(fork);
print FILE $$,"\n";
close FILE;


##########
# Open a sonypid process to read the sonypi device
pipe READ,WRITE;
$pid = fork();
$| = 1;
if(!$pid) {
    close READ;
    open STDERR,">&WRITE";
    open STDOUT,">&WRITE";
    exec('sonypid');
    die;
}
close WRITE;

##########
# Interrupt handling -- clean up the sonypid before dying...
$die = sub{ kill(9,$pid); unlink("/var/run/sonypidd.pid"); exit 1; };
$SIG{HUP} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = $die;


##########
# Event loop -- grab the function-key events, look 'em up, and do 'em.
# Go away as soon as the sonypid dies.

while(defined(  $_ = <READ> )){
  s/^Event:\s*//;
  @words = split(m/\s+/,$_);

  if($words[0] =~ m/^Fn-([0-9A-Za-z]+)$/) {
      my $f_no = $1;
      if(defined($cmds{$f_no})) {
	  if(ref $cmds{$f_no} eq 'CODE') {
	      &{$cmds{$f_no}};
	  } elsif(ref $cmds{$f_no} eq 'ARRAY') {
	      for(@{$cmds{$f_no}}) {
		  `$_`;
	      }
	  }
      }
  }
}

exit 0;
