#!/usr/bin/perl
# psxmms by Anders Dubgaard <anders@dubgard.net>, 2005.
#
# Reads commands from my custom-built Wireless PlayStation Controller
# Seriel-via-USB interface (on /dev/ttyUSB0 at the moment).
#
# See http://www.dubgaard.net/projects for more information.
# 
# Bugs / Missing features:
# - Needs a good rewrite, as it's still a little in-efficient even
#   though it's functional.
#
# Based on ctrlxmms by Robin Smidsrød:
#
# ctrlxmms 1.1 (Mon Jun 19 20:27:38 CEST 2000)
# (C) Robin Smidsrød <robin@smidsrod.no> June 2000
#
# This program is licensed according to the GPL.

$version="psxmms.pl by Anders Dubgaard <anders\@dubgaard.net";

use strict;
use warnings;

use Xmms::Remote (); # perl module from CPAN
use POSIX; # ceil and floor for rounding
my $jtime;

my $remote=Xmms::Remote->new;

my $joyinput = "/dev/ttyUSB0";
my $baudrate = 230400;

# set baud rate, assumes a Linux-like system
`stty -F $joyinput $baudrate`;

open PSJOY, "$joyinput" or die "$!";
#open MOUSE, "| xte";

my $line;
my $mask;

my %psx = (
  "type" => 0,
  "b1" => 0,
  "b2" => 0,
  "r_horz" => 0,
  "r_vert" => 0,
  "l_horz" => 0,
  "l_vert" => 0,
);

# button-press timeout vars
my $timeout = 4; # button press is ignored $timeout-times before reading again
my $timeout2x = $timeout * 2; # for use with the squischy rubber buttons

# X11 vars, using xte.
my $analog_id = 115; # Analog_red_mode = 115 = 0x73
my $x_rel;
my $y_rel;
my $xmove = 0;
my $div = 4;
my $mid;

# buttons1
my $slct = 0;
my $joy_r = 0;
my $joy_l = 0;
my $start = 0;
my $up = 0;
my $right = 0;
my $down = 0;
my $left = 0;

# buttons2
my $l2 = 0;
my $r2 = 0;
my $l1 = 0;
my $r1 = 0;
my $triangle = 0;
my $circle = 0;
my $cross = 0;
my $square = 0;

$ARGV[1] = "";  # $ARGV[1] is hardcoded to be the optional argument

$line = <PSJOY>;
$line = <PSJOY>;
while(1) {
  read_joystick();
  $ARGV[1] = "";

} #end main loop

sub read_joystick {
$line = <PSJOY>;
#print "B1: " . substr($line, 0, 8);
$psx{'type'}   = substr($line, 0, 8);
$psx{'b1'}     = substr($line, 8, 8);
$psx{'b2'}     = substr($line, 16, 8);
$psx{'r_horz'} = substr($line, 24, 8);
$psx{'r_vert'} = substr($line, 32, 8);
$psx{'l_horz'} = substr($line, 40, 8);
$psx{'l_vert'} = substr($line, 48, 8);

$psx{'type'}   = asc2int($psx{'type'});
$psx{'b1'}     = asc2int($psx{'b1'});
$psx{'b2'}     = asc2int($psx{'b2'});
$psx{'r_horz'} = asc2int($psx{'r_horz'});
$psx{'r_vert'} = asc2int($psx{'r_vert'});
$psx{'l_horz'} = asc2int($psx{'l_horz'});
$psx{'l_vert'} = asc2int($psx{'l_vert'});

#print sprintf("%lx ", $psx{'type'}  );
#print sprintf("%lx ", $psx{'b1'}    );
#print sprintf("%lx ", $psx{'b2'}    );
#print sprintf("%lx ", $psx{'r_horz'});
#print sprintf("%lx ", $psx{'r_vert'});
#print sprintf("%lx ", $psx{'l_horz'});
#print sprintf("%lx ", $psx{'l_vert'});
#print "\n";

# Buttons, active low
#     Bit0 Bit1 Bit2 Bit3 Bit4 Bit5 Bit6 Bit7
# b1: SLCT JOYR JOYL STRT UP   RGHT DOWN LEFT
# b2:  L2   R2    L1  R1   /\   O    X    |_|

$mask = 1;
if(0 == ($psx{'b1'} & $mask)) {
  if($slct == 0) { $slct = $timeout2x;
  } else { $slct -= 1; }
} # "slct";

$mask <<= 1;
if(0 == ($psx{'b1'} & $mask)) {
  if($joy_l == 0) { $joy_l = $timeout2x;
    `echo mouseclick 1 | xte`;
  } else { $joy_l -= 1; }
} # "joy_l";

$mask <<= 1;
if(0 == ($psx{'b1'} & $mask)) {
  if($joy_r == 0) { $joy_r = $timeout2x;
    `echo mouseclick 2 | xte`;
  } else { $joy_r -= 1; }
} # "joy_r";

$mask <<= 1;
if(0 == ($psx{'b1'} & $mask)) {
  if($start == 0) { $start = $timeout2x; &do_playpause;
  } else { $start -= 1; }
} # "start";

$mask <<= 1;
if(0 == ($psx{'b1'} & $mask)) {
  if($up == 0) { $up = $timeout; &do_prev;
  } else { $up -= 1; }
} # "up";
  
$mask <<= 1;
if(0 == ($psx{'b1'} & $mask)) { $ARGV[1]="10";
  if($right == 0) { $right = $timeout; &do_time;
  } else { $right -= 1; }
} # "right";

$mask <<= 1;
if(0 == ($psx{'b1'} & $mask)) {

  if($down == 0) { $down = $timeout; &do_next;
  } else { $down -= 1; }
} # "down";

$mask <<= 1;
if(0 == ($psx{'b1'} & $mask)) { $ARGV[1]="-10";
  if($left == 0) { $left = $timeout; &do_time;
  } else { $left -= 1; }
} # "left";

$mask = 1;
if(0 == ($psx{'b2'} & $mask)) { #$ARGV[1]="20"; # default = 10
  if($l2 == 0) { $l2 = $timeout2x;
    &do_prev; &do_prev; &do_prev; &do_prev; &do_prev;
    &do_prev; &do_prev; &do_prev; &do_prev; &do_prev;
  } else { $l2 -= 1; }
} # "l2"; # jump 10 songs back

$mask <<= 1;
if(0 == ($psx{'b2'} & $mask)) {
  if($r2 == 0) { $r2 = $timeout;
    system("aumix", "-w-5");
  } else { $r2 -= 1; }
} # "r2"; # vol up
    

$mask <<= 1;
if(0 == ($psx{'b2'} & $mask)) { # $ARGV[1]="20"; # default = 10
  if($l1 == 0) { $l1 = $timeout2x;
    &do_next; &do_next; &do_next; &do_next; &do_next;
    &do_next; &do_next; &do_next; &do_next; &do_next;
  } else { $l1 = $l1 - 1; }
} # "l1"; # jump 10 songs forward

$mask <<= 1;
if(0 == ($psx{'b2'} & $mask)) {
  if($r1 == 0) { $r1 = $timeout;
    system("aumix", "-w+5");
  } else { $r1 -= 1; }
} # "r1"; # vol up

$mask <<= 1;
if(0 == ($psx{'b2'} & $mask)) {
  if($triangle == 0) { $triangle = $timeout; &do_stop;
  } else { $triangle -= 1; }
} # "triangle";

$mask <<= 1;
if(0 == ($psx{'b2'} & $mask)) {
  if($circle == 0) { $circle = $timeout2x; &do_repeat;
  } else { $circle--; } #= $circle - 1; 
} # "circle";

$mask <<= 1;
if(0 == ($psx{'b2'} & $mask)) {
  if($cross == 0) { $cross = $timeout; &do_play;
  } else { $cross -= 1; }
} # "cross";

$mask <<= 1;
if(0 == ($psx{'b2'} & $mask)) {
  if($square == 0) { $square = $timeout2x; &do_shuffle;
  } else { $square -= 1; }
} # "square";

# No buttons, then maybe mouse movement!
# sprintf("%lx", n) converts from hex
#$x_rel = sprintf("%lx", $psx{'l_horz'});
#$y_rel = sprintf("%lx", $psx{'l_vert'});
if($psx{'type'} == $analog_id){
  if($xmove == 0) {
    $xmove = 1; # skip the next mouse move(s)
    if($psx{'l_horz'} > 100 && $psx{'l_horz'} < 150){ $psx{'l_horz'} = 127; }
    if($psx{'l_vert'} > 100 && $psx{'l_vert'} < 150){ $psx{'l_vert'} = 127; }

    $psx{'l_horz'} = ceil($psx{'l_horz'} / 4 - 32);
    $psx{'l_vert'} = ceil($psx{'l_vert'} / 4 - 32);

    #print MOUSE "mousermove $psx{'l_horz'} $psx{'l_vert'}\n";
    `echo mousermove $psx{'l_horz'} $psx{'l_vert'} | xte`;
  } else { $xmove--; }
} else {
  $xmove = 0;
}

}
 
# asc2int: convert from str to int: "01110011" to 115 (0x73)
sub asc2int {
  my @parms = @_;
  my $str = $parms[0];
  my $i;
  $i = 0;
  my $sum = 0;
  my $mask = 1;

  for($i=0; $i<8; $i++) {
    if(substr($str, 7 - $i, 1)) { $sum += $mask; }
    $mask <<= 1;
  }
  #print "Total sum: " . sprintf("%lx", $sum) . "\n";
  return $sum;
}

sub do_playpause {
	my ($is_playing,$is_paused)=&songstatus;

	if ($is_playing eq 1) {
		if($is_paused eq 1) {
			&do_pause;
		} else {
			&do_pause;
		} 
	} else {
		&do_play;
	}
}

sub do_play { $remote->play; }

sub do_pause { $remote->pause; }

sub do_stop { $remote->stop; }

sub do_next { $remote->playlist_next; }

sub do_prev { $remote->playlist_prev; }

sub do_forward {
	if (defined($ARGV[1])) {
		$jtime=$ARGV[1];
	} else {
		$jtime=5;
	}
	&jtime($jtime);
}

sub do_backward {
	if (defined($ARGV[1])) {
		$jtime="-" . $ARGV[1];
	} else {
		$jtime=-5;
	}
	&jtime($jtime);
}

sub jtime {
  my $jtime=shift @_;
  my $time=$remote->get_output_time;
  my $factor=1000;    # Equals 1 second
  my $newtime=$time+($factor*$jtime);
  $remote->jump_to_time($newtime);

}
sub do_repeat { $remote->toggle_repeat; }

sub do_shuffle { $remote->toggle_shuffle; }

sub songstatus {
  my $playstatus=$remote->is_playing;
  my $pausestatus=$remote->is_paused;
  return ($playstatus,$pausestatus);
}

sub do_time { &jtime($ARGV[1]); }
