--- AnyEvent-MPV/MPV.pm 2023/03/20 12:23:21 1.9 +++ AnyEvent-MPV/MPV.pm 2023/03/20 14:42:40 1.13 @@ -149,34 +149,36 @@ my $mpv = AnyEvent::MPV->new ( trace => 1, args => ["--pause", "--idle=yes"], - on_event => sub { - my ($mpv, $event, $data) = @_; - - if ($event eq "start-file") { - $mpv->cmd ("set", "pause", "no"); - } elsif ($event eq "end-file") { - print "end-file<$data->{reason}>\n"; - $quit->send; - } - }, ); $mpv->start; + + $mpv->register_event (start_file => sub { + $mpv->cmd ("set", "pause", "no"); + }); + + $mpv->register_event (end_file => sub { + my ($mpv, $event, $data) = @_; + + print "end-file<$data->{reason}>\n"; + $quit->send; + }); + $mpv->cmd (loadfile => $mpv->escape_binary ($videofile)); $quit->recv; This example uses a global condvar C<$quit> to wait for the file to finish -playing. Also, most of the logic is now in an C callback, which -receives an event name and the actual event object. +playing. Also, most of the logic is now implement in event handlers. -The two events we handle are C, which is emitted by F -once it has loaded a new file, and C, which signals the end -of a file. - -In the former event, we again set the C property to C so the -movie starts playing. For the latter event, we tell the main program to -quit by invoking C<$quit>. +The two events handlers we register are C, which is emitted by +F once it has loaded a new file, and C, which signals the +end of a file (underscores are internally replaced by minus signs, so you +cna speicfy event names with either). + +In the C event, we again set the C property to C +so the movie starts playing. For the C event, we tell the main +program to quit by invoking C<$quit>. This should conclude the basics of operation. There are a few more examples later in the documentation. @@ -211,14 +213,15 @@ use AnyEvent (); use AnyEvent::Util (); -our $VERSION = '0.1'; +our $VERSION = '0.2'; sub OBSID() { 0x10000000000000 } # 2**52 our $JSON = eval { require JSON::XS; JSON::XS:: } || do { require JSON::PP; JSON::PP:: }; -our $JSON_CODER = +our $JSON_ENCODER = $JSON->new->utf8; +our $JSON_DECODER = $JSON->new->latin1; our $mpv_path; # last mpv path used our $mpv_optionlist; # output of mpv --list-options @@ -355,8 +358,6 @@ return 0 if $self->{fh}; - $self->{obscb} = {}; - # cache optionlist for same "path" ($mpv_path, $mpv_optionlist) = ($self->{mpv}, scalar qx{\Q$self->{mpv}\E --list-options}) if $self->{mpv} ne $mpv_path; @@ -402,11 +403,11 @@ if ("{" eq substr $1, 0, 1) { eval { - my $reply = $JSON->new->latin1->decode ($1); + my $reply = $JSON_DECODER->decode ($1); - if (exists $reply->{event}) { + if (defined (my $event = delete $reply->{event})) { if ( - $reply->{event} eq "client-message" + $event eq "client-message" and $reply->{args}[0] eq "AnyEvent::MPV" ) { if ($reply->{args}[1] eq "key") { @@ -414,14 +415,22 @@ $self->on_key ($key); } } elsif ( - $reply->{event} eq "property-change" + $event eq "property-change" and OBSID <= $reply->{id} ) { if (my $cb = $self->{obscb}{$reply->{id}}) { - $cb->($self, $reply->{name}, $reply->{data}); + $cb->($self, $event, $reply->{data}); } } else { - $self->on_event (delete $reply->{event}, $reply); + if (my $cbs = $self->{evtcb}{$event}) { + for my $evtid (keys %$cbs) { + my $cb = $cbs->{$evtid} + or next; + $cb->($self, $event, $reply); + } + } + + $self->on_event ($event, $reply); } } elsif (exists $reply->{request_id}) { my $cv = delete $self->{cmdcv}{$reply->{request_id}}; @@ -462,7 +471,7 @@ $self->{cmdcv}{++$reqid} = $cv; - my $cmd = $JSON->new->utf8->encode ({ command => ref $_[0] ? $_[0] : \@_, request_id => $reqid*1 }); + my $cmd = $JSON_ENCODER->encode ({ command => ref $_[0] ? $_[0] : \@_, request_id => $reqid*1 }); # (un-)apply escape_binary hack $cmd =~ s/\xf4\x8e\x97\x9f(..)/sprintf sprintf "\\x%02x", hex $1/ges; # f48e979f == 10e5df in utf-8 @@ -510,6 +519,8 @@ delete $self->{pid}; delete $self->{cmdcv}; + delete $self->{evtid}; + delete $self->{evtcb}; delete $self->{obsid}; delete $self->{obscb}; delete $self->{wbuf}; @@ -632,9 +643,11 @@ =item $mpv->bind_key ($INPUT => $string) -This is an extension implement by this module to make it easy to get key events. The way this is implemented -is to bind a C witha first argument of C and the C<$string> you passed. This C<$string> is then -passed to the C handle when the key is proessed, e.g.: +This is an extension implement by this module to make it easy to get key +events. The way this is implemented is to bind a C witha +first argument of C and the C<$string> you passed. This +C<$string> is then passed to the C handle when the key is +proessed, e.g.: my $mpv = AnyEvent::MPV->new ( on_key => sub { @@ -648,6 +661,9 @@ $mpv_>bind_key (ESC => "letmeout"); +You cna find a list of key names L. + The key configuration is lost when F is stopped and must be (re-)done after every C. @@ -660,14 +676,44 @@ $self->cmd (keybind => $key => "no-osd script-message AnyEvent::MPV key $event"); } -sub AnyEvent::MPV::Unobserve::DESTROY { - my ($mpv, $obscb, $obsid) = @{$_[0]}; +=item [$guard] = $mpv->register_event ($event => $coderef->($mpv, $event, $data)) - delete $obscb->{$obsid}; +This method registers a callback to be invoked for a specific +event. Whenever the event occurs, it calls the coderef with the C<$mpv> +object, the C<$event> name and the event object, just like the C +method. - if ($obscb == $mpv->{obscb}) { - $mpv->cmd (unobserve_property => $obsid+0); - } +For a lst of events, see L. Any +underscore in the event name is replaced by a minus sign, so you can +specify event names using underscores for easier quoting in Perl. + +In void context, the handler stays registered until C is called. In +any other context, it returns a guard object that, when destroyed, will +unregister the handler. + +You can register multiple handlers for the same event, and this method +does not interfere with the C mechanism. That is, you can +completely ignore this method and handle events in a C handler, +or mix both approaches as you see fit. + +=cut + +sub AnyEvent::MPV::Unevent::DESTROY { + my ($evtcb, $event, $evtid) = @{$_[0]}; + delete $evtcb->{$event}{$evtid}; +} + +sub register_event { + my ($self, $event, $cb) = @_; + + $event =~ y/_/-/; + + my $evtid = ++$self->{evtid}; + $self->{evtcb}{$event}{$evtid} = $cb; + + defined wantarray + and bless [$self->{evtcb}, $event, $evtid], AnyEvent::MPV::Unevent:: } =item [$guard] = $mpv->observe_property ($name => $coderef->($mpv, $name, $value)) @@ -693,6 +739,12 @@ object that, when it goes out of scope, unregisters the observe using C. +Internally, this method uses observer ids of 2**52 (0x10000000000000) or +higher - it will not interfere with lower ovserver ids, so it is possible +to completely ignore this system and execute C commands +yourself, whilst listening to C events - as long as your +ids stay below 2**52. + Example: register observers for changtes in C and C. Note that a dummy statement is added to make sure the method is called in void context. @@ -715,6 +767,16 @@ =cut +sub AnyEvent::MPV::Unobserve::DESTROY { + my ($mpv, $obscb, $obsid) = @{$_[0]}; + + delete $obscb->{$obsid}; + + if ($obscb == $mpv->{obscb}) { + $mpv->cmd (unobserve_property => $obsid+0); + } +} + sub _observe_property { my ($self, $type, $property, $cb) = @_;