Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Using Term::ReadLine in an event loop other than Tk [SOLVED]

by Tanktalus (Canon)
on Jan 17, 2012 at 06:14 UTC ( #948255=perlquestion: print w/ replies, xml ) Need Help??
Tanktalus has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to see if I can get Term::ReadLine to work in an event loop other than Tk, but the authors have made it somewhat difficult. And I've been having a bit of trouble trying to override things appropriately.

The first step was simply to test that something was happening in the background while waiting, just with the Tk loop. I found it easiest just to put a timer in with some ANSI control codes to move the cursor around - not the best way of doing this, but it works.

#!/usr/bin/perl use 5.12.1; my $esc; BEGIN { $esc = "\x1b["; print "${esc}2J${esc}3H"; } use Tk; use AnyEvent; use Term::ReadLine; my $t = 0; my $w = AE::timer (0,1,sub {print "${esc}s${esc}1H$t s ${esc}u";+ ++$t}); my $term = Term::ReadLine->new('...'); $term->tkRunning(1); my $x = $term->readline('> ');
This works. I get a prompt, I can type in it, and the timer triggers counting the seconds at the top of the console. Unfortunately, it's using Tk. I'm using AnyEvent for the timer at least, because I really don't want to be looking at all the event loops to figure out how to get that to work.

My next attempt was to try to get the loop itself done in AnyEvent. Here things start to go downhill. Note that I'm still using Tk, just in case.

#!/usr/bin/perl use Tk; use AnyEvent; use Term::ReadLine; use IO::File; use 5.12.1; sub LOG { state $fh; open $fh, '>', '/tmp/sopw.out' unless $fh; die $! unless $fh; autoflush $fh 1; say $fh @_; } package Term::ReadLine::AE; # mangle their tree, inserting ourselves in the front. unshift @Term::ReadLine::Stub::ISA, __PACKAGE__; #use Data::Dump qw(ddx); #ddx(\%INC); if (not defined &Tk::DoOneEvent) { ::LOG("overriding"); *Tk::DoOneEvent = sub { AnyEvent->_poll(); }; ::LOG("Not created?") unless defined &Tk::DoOneEvent; } our $cv; sub Tk_loop { my $self = shift; ::LOG("Entering Tk_loop");# unless $cv; $cv = AE::cv; $cv->recv(); } sub register_Tk { my $self = shift; ::LOG("Registering"); $Term::ReadLine::registered++ or AE::io($self->IN, 0, sub { $cv->send() }); } package main; use 5.12.1; my $esc; BEGIN { $esc = "\x1b["; print "${esc}2J${esc}3H"; } my $t = 0; my $w = AE::timer (0,1,sub {print "${esc}s${esc}1H$t s ${esc}u";+ ++$t}); my $term = Term::ReadLine->new('...'); $term->tkRunning(1); my $x = $term->readline('> ');
The basic code is the same, I've merely inserted a new package to override the Tk_loop and register_Tk functions - register seems to register the filehandle for monitoring while the loop, well, loops until characters are ready on the input. However, it "doesn't work." Specifically, the timer still fires, but the input isn't recognised. The io watcher never fires. (The override on Tk::DoOneEvent also isn't used - that was there for what I was hoping to be the next step - switching Tk to EV.)

It looks like AnyEvent's Tk implementation duplicates filehandles all over the place for monitoring, the author claims it's to workaround some Tk bugs. I don't know, but even if I switch out EV for Tk, I get the same behaviour: my typing isn't recognised but the timer fires fine. I'm kind of at a loss to explain what's going on here and hoping someone else has the required tuits.

Thanks!

Update: Nevermind. Found the solution. I forgot that AE events have watcher objects. I need to keep the return from AE::io, otherwise the object goes out of scope and is deleted. I'll have to store the watcher:

our $fe; sub register_Tk { my $self = shift; ::LOG("Registering"); $fe ||= AE::io($self->IN, 0, sub { $cv->send() }); }
And suddenly they all work. Now to figure out how to make this suggestion to the owners of TRL :-)

Comment on Using Term::ReadLine in an event loop other than Tk [SOLVED]
Select or Download Code
Re: Using Term::ReadLine in an event loop other than Tk
by Anonymous Monk on Jan 17, 2012 at 07:46 UTC

      I'm sorry, I don't follow. I've read the last three before, and don't see how they help me get Term::ReadLine to use another event loop. The second one is interesting, though dragging in Moose seems a bit overkill. And putting it in a subprocess is kind of a hack - then again, AnyEvent::DBI does about the same thing. I was hoping to avoid the overhead of a subprocess, though, partly to see if it could be done. The first one doesn't help at all, I think, as it would practically require re-implementing TRL. And TRL is horrendously complex, I'd rather not do that.

      I was also hoping to be able to submit changes back to the owners of TRL to see if they'd consider removing Tk and adding AnyEvent (which would still allow Tk to work, theoretically). Obviously not in the current state, and I doubt this scratches a big enough itch for them to attempt merely with a bug report/feature request.

Re: Using Term::ReadLine in an event loop other than Tk
by Khen1950fx (Canon) on Jan 17, 2012 at 11:41 UTC
    Would this help?
    #!/usr/bin/perl -slw use strict; use Tk; use AnyEvent; use POSIX qw(:sys_wait_h); use Term::ReadLine; use IO::File; use 5.12.1; sub LOG { state $fh; open $fh, '>', '/tmp/sopw.out' unless $fh; die $! unless $fh; autoflush $fh 1; say $fh @_; } package Term::ReadLine::AE; unshift @Term::ReadLine::Stub::ISA, __PACKAGE__; #use Data::Dump qw(ddx); #ddx(\%INC); if ( not defined &Tk::DoOneEvent ) { ::LOG("overriding"); *Tk::DoOneEvent = sub { AnyEvent->_poll(); }; ::LOG("Not created?") unless defined &Tk::DoOneEvent; } our $cv; sub Tk_loop { my $self = shift; ::LOG("Entering Tk_loop"); $cv = AE::cv; $cv->recv(); } sub register_Tk { my $self = shift; ::LOG("Registering"); $Term::ReadLine::registered++ or AE::io( $self->IN, 0, sub { $cv->send() } ); } package main; use 5.12.1; my $esc; BEGIN { $esc = "\x1b["; print "${esc}2J"; } my $t = 0; my $w = AE::timer( 0, 1, sub { print "${esc}s${esc}1H$t s ${esc}u"; +++$t } ); my $term = Term::ReadLine->new('AE'); my $OUT = $term->OUT || \*STDOUT; my $prompt = "Finished"; my $cmd; while (defined( $cmd = $term->readline('>') )) { my @output = `$cmd`; my $exit_value = $? >> 8; my $signal_num = $? & 127; my $dumped_core = $? & 128; printf $OUT "Program terminated with status %d from signal %d%s\n" +, $exit_value, $signal_num, $dumped_core ? " (core dumped)" : ""; say @output; $term->addhistory; $term->tkRunning(1); say $prompt; exit; }

      Thanks, but your code doesn't seem to work, either, probably because the tkRunning is too late. If I move the tkRunning call up to before the $term->readline call, then the timer kinda seems to fire, but I again cannot type anything. I have to kill the process, and then the shell gets what was in the STDIN buffer just as with my second piece of code above.

Re: Using Term::ReadLine in an event loop other than Tk
by zentara (Archbishop) on Jan 17, 2012 at 12:13 UTC

      Thanks, zentara - but if you look at the first piece of code in my original node, using TRL with Tk without trying to insert AE loops, that I got to work. I was merely hoping to use AE to catch all the events allowing me to switch event engines transparently.

Using AnyEvent with Term::ReadLine::Gnu
by gnosti (Friar) on Jan 22, 2012 at 03:18 UTC
    Here is a solution that depends on Term::ReadLine::Gnu.

    # Sample code showing how to integrate # Event with Term::ReadLine::Gnu (uses AnyEvent for watcher syntax) use Term::ReadLine; # automatically loads Term::ReadLine::Gnu, # which must be present use Event; use AnyEvent; my $term = new Term::ReadLine("Test Event"); my $attribs = $term->Attribs; $term->callback_handler_install( "> ", sub{ print "got: @_\n" }); my $stdin_watcher = AE::io(*STDIN, 0, sub { $attribs->{'callback_read_char'}->(); } ); my $timer = AE::timer(5,0, sub { print "....timed out\n"; $term->rl_deprep_terminal(); exit; } ); Event::loop(); __END__

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://948255]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (12)
As of 2014-12-22 15:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (120 votes), past polls