Created
December 13, 2011 16:05
-
-
Save jrockway/1472697 to your computer and use it in GitHub Desktop.
xmms2 + powermate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
use strict; | |
use warnings; | |
use feature ':5.10'; | |
use Config; | |
my $event_size = | |
($Config{longsize} * 2) + # input_event.time (struct timeval) | |
($Config{i16size} * 2) + # input_event.type, input_event.code | |
($Config{i32size}); # input_event.value | |
{ package Powermate; | |
use Moose; | |
use MooseX::Types::Path::Class qw(File); | |
use Scalar::Util qw/weaken/; | |
has 'device' => ( | |
is => 'ro', | |
isa => File, | |
coerce => 1, | |
default => '/dev/input/by-id/usb-Griffin_Technology__Inc._Griffin_PowerMate-event-if00', | |
); | |
has 'fh' => ( | |
is => 'ro', | |
lazy_build => 1, | |
); | |
sub _build_fh { | |
my $self = shift; | |
return $self->device->open('r+'); | |
} | |
has 'on_event' => ( | |
is => 'rw', | |
isa => 'CodeRef', | |
predicate => 'has_event_callback', | |
); | |
has 'read_watcher' => ( | |
is => 'ro', | |
lazy_build => 1, | |
); | |
sub _build_read_watcher { | |
my $self = shift; | |
weaken $self; | |
return AnyEvent->io( fh => $self->fh, poll => 'r', cb => sub { | |
my $event = $self->read_event; | |
$self->handle_event($event); | |
}); | |
} | |
sub read_event { | |
use autodie 'sysread'; | |
my $self = shift; | |
# cribbed from Linux::Input | |
my $len = sysread($self->fh, my $buffer, $event_size); | |
my ($sec, $usec, $type, $code, $value) = | |
unpack('L!L!S!S!i!', $buffer); | |
return { | |
tv_sec => $sec, | |
tv_usec => $usec, | |
type => $type, | |
code => $code, | |
value => $value, | |
}; | |
} | |
sub handle_event { | |
my ($self, $event) = @_; | |
my $type = $event->{type}; | |
my $value = $event->{value}; | |
my $name; | |
return if $type == 0; # null? | |
given($type){ | |
when (1) { | |
if ($value){ | |
$name = 'button_down'; | |
} | |
else { | |
$name = 'button_up'; | |
} | |
} | |
when (2) { | |
if($value < 0){ | |
$name = 'scroll_counterclockwise'; | |
} | |
else { | |
$name = 'scroll_clockwise'; | |
} | |
} | |
when (4) { | |
# LED state | |
} | |
default { | |
use DDS; | |
print "unknown event "; | |
print Dump($event); | |
return; | |
} | |
} | |
# say "handling event '$name'"; | |
if($self->has_event_callback){ | |
$self->on_event->($name, $value); | |
} | |
} | |
sub set_led_state { | |
use autodie 'syswrite'; | |
my ($self, $is_on) = @_; | |
syswrite $self->fh, pack("L!L!S!S!i!", 0, 0, 4, 1, $is_on*255); | |
say "LED set to $is_on"; | |
return $is_on; | |
} | |
} | |
{ package State; | |
use MooseX::StateMachine; | |
no feature 'state'; | |
use Scalar::Util qw/weaken/; | |
has 'powermate' => ( | |
is => 'ro', | |
isa => 'Powermate', | |
required => 1, | |
handles => ['set_led_state'], | |
); | |
has 'xmms' => ( | |
is => 'ro', | |
isa => 'Audio::XMMSClient', | |
required => 1, | |
handles => [qw/playback_pause playback_start/], | |
); | |
has 'xmms_status_watcher' => ( | |
is => 'ro', | |
lazy_build => 1, | |
); | |
sub _build_xmms_status_watcher { | |
my $self = shift; | |
my $b = $self->xmms->broadcast_playback_status; | |
weaken $self; | |
$b->notifier_set(sub { | |
$self->handle_xmms_event($_[0]); | |
return 1; | |
}); | |
return $b; | |
} | |
sub BUILD { | |
my $self = shift; | |
weaken $self; | |
$self->powermate->on_event(sub { | |
$self->handle_event(@_); | |
}); | |
$self->xmms->playback_status->notifier_set(sub { | |
$self->handle_xmms_event($_[0]); | |
$self->xmms_status_watcher; | |
}); | |
} | |
sub handle_xmms_event { | |
my ($self, $status) = @_; | |
if($status == 1){ | |
$self->handle_event('playing'); | |
} | |
else { | |
$self->handle_event('paused'); | |
} | |
} | |
sub handle_event { | |
my ($self, $name) = @_; | |
warn $name; | |
if ($self->can($name)){ | |
$self->$name; | |
} | |
# otherwise, unsupported operation for that state | |
} | |
start_state 'start' => ( | |
transitions => { | |
playing => sub {}, | |
paused => sub {}, | |
}, | |
); | |
state 'playing' => ( | |
on_enter => sub { $_[0]->set_led_state(1) }, | |
transitions => { | |
paused => sub {}, | |
playing_press => sub {}, | |
}, | |
methods => { | |
button_down => sub { $_[0]->playing_press }, | |
}, | |
); | |
state 'paused' => ( | |
on_enter => sub { $_[0]->set_led_state(0) }, | |
transitions => { | |
playing => sub {}, | |
paused_press => sub {}, | |
}, | |
methods => { | |
button_down => sub { $_[0]->paused_press }, | |
}, | |
); | |
state 'paused_press' => ( | |
transitions => { | |
playing => sub {}, | |
}, | |
methods => { | |
button_up => sub { | |
$_[0]->playback_start; | |
}, | |
}, | |
); | |
state 'playing_press' => ( | |
transitions => { | |
paused => sub {}, | |
}, | |
methods => { | |
button_up => sub { | |
$_[0]->playback_pause; | |
}, | |
}, | |
); | |
} | |
use AnyEvent; | |
use Audio::XMMSClient::Glib; | |
my $powermate = Powermate->new; | |
my $xmms = Audio::XMMSClient::Glib->new('powermate'); | |
$xmms->connect or die $xmms->get_last_error; | |
my $machine = State->new( powermate => $powermate, xmms => $xmms ); | |
$machine->start; | |
$powermate->read_watcher; | |
$xmms->loop; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment