--- AnyEvent-MPV/MPV.pm 2023/03/20 12:23:21 1.9 +++ AnyEvent-MPV/MPV.pm 2023/03/22 01:00:36 1.15 @@ -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. @@ -187,7 +189,7 @@ is expected to be in unicode. To pass something that isn't, you need to escape it using C. -Data received from C<$mpv>, however, is I decoded to unicode, as data +Data received from F, however, is I decoded to unicode, as data returned by F is not generally encoded in unicode, and the encoding is usually unspecified. So if you receive data and expect it to be in unicode, you need to first decode it from UTF-8, but note that this might @@ -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,50 @@ $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. + +Note that unlike commands, event handlers are registered immediately, that +is, you can issue a command, then register an event handler and then get +an event for this handler I the command is even sent to F. If +this kind of race is an issue, you can issue a dummy command such as +C and register the handler when the reply is received. + +=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 +745,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 +773,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) = @_;