Skip to content

Instantly share code, notes, and snippets.

@jrockway
Created December 13, 2011 16:05
Show Gist options
  • Save jrockway/1472697 to your computer and use it in GitHub Desktop.
Save jrockway/1472697 to your computer and use it in GitHub Desktop.
xmms2 + powermate
#!/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