File Coverage

blib/lib/Term/ReadLine/Event.pm
Criterion Covered Total %
statement 18 104 17.3
branch 0 6 0.0
condition n/a
subroutine 6 33 18.1
pod 7 7 100.0
total 31 150 20.6


line stmt bran cond sub pod time code
1             package Term::ReadLine::Event;
2             {
3             $Term::ReadLine::Event::VERSION = '0.04';
4             }
5              
6 8     8   258595 use 5.006;
  8         33  
  8         332  
7 8     8   43 use strict;
  8         16  
  8         445  
8 8     8   44 use warnings;
  8         24  
  8         280  
9              
10 8     8   1393 use Term::ReadLine 1.09;
  8         8003  
  8         193  
11 8     8   50 use Scalar::Util qw(blessed);
  8         13  
  8         13982  
12              
13             # ABSTRACT: Wrappers for Term::ReadLine's new event_loop model.
14              
15              
16             sub _new {
17 0     0     my $class = shift;
18 0           my $app = shift;
19              
20 0           my $self = bless {@_}, $class;
21              
22 0 0         $self->{_term} = blessed $app ? $app :
    0          
23             Term::ReadLine->new(ref $app ? @$app : $app);
24 0           $self;
25             }
26              
27              
28             sub with_AnyEvent {
29 0     0 1   my $self = _new(@_);
30              
31             $self->trl->event_loop(
32             sub {
33 0     0     my $data = shift;
34 0           $data->[0] = AE::cv();
35 0           $data->[0]->recv();
36             }, sub {
37 0     0     my $fh = shift;
38              
39             # The data for AE are: the file event watcher (which
40             # cannot be garbage collected until we're done) and
41             # a placeholder for the condvar we're sharing between
42             # the AE::io callback created here and the wait
43             # callback above.
44 0           my $data = [];
45 0           $data->[1] = AE::io($fh, 0, sub { $data->[0]->send() });
  0            
46 0           $data;
47             }
48 0           );
49              
50 0           $self;
51             }
52              
53              
54             sub with_Coro {
55 0     0 1   my $self = _new(@_);
56 0           require Coro::Handle;
57              
58             $self->trl->event_loop(
59             sub {
60             # Tell Coro to wait until we have something to read,
61             # and then we can return.
62 0     0     shift->readable();
63             }, sub {
64             # in Coro, we just need to unblock the filehandle,
65             # and save the unblocked filehandle.
66 0     0     my $fh = shift;
67 0           Coro::Handle::unblock $fh;
68             }
69 0           );
70 0           $self;
71             }
72              
73              
74             sub with_IO_Async {
75 0     0 1   my $self = _new(@_);
76              
77             $self->trl->event_loop(
78             sub {
79 0     0     my $ready = shift;
80 0           $$ready = 0;
81 0           $self->{loop}->loop_once while !$$ready;
82             },
83             sub {
84 0     0     my $fh = shift;
85              
86             # The data for IO::Async is just the ready flag. To
87             # ensure we're referring to the same value, this is
88             # a SCALAR ref.
89 0           my $ready = \ do{my $dummy};
  0            
90             $self->{loop}->add(
91             $self->{watcher} =
92             IO::Async::Handle->new(
93             read_handle => $fh,
94 0           on_read_ready => sub { $$ready = 1 },
95             )
96 0           );
97 0           $ready;
98             }
99 0           );
100              
101             $self->{_cleanup} = sub {
102 0     0     my $s = shift;
103 0           $s->{loop}->remove($s->{watcher});
104 0           };
105              
106 0           $self;
107             }
108              
109              
110             sub with_POE
111             {
112 0     0 1   my $self = _new(@_);
113              
114 0           my $waiting_for_input;
115              
116             POE::Session->create(
117             inline_states => {
118              
119             # Initialize the session that will drive Term::ReadLine.
120             # Tell Term::ReadLine to invoke a couple POE event handlers when
121             # it's ready to wait for input, and when it needs to register an
122             # I/O watcher.
123              
124             _start => sub {
125 0     0     $self->trl->event_loop(
126             $_[POE::Session->SESSION]->callback('term_readline_waitfunc'),
127             $_[POE::Session->SESSION]->callback('term_readline_regfunc'),
128             );
129             },
130              
131             # This callback is invoked every time Term::ReadLine wants to
132             # read something from its input file handle. It blocks
133             # Term::ReadLine until input is seen.
134             #
135             # It sets a flag indicating that input hasn't arrived yet.
136             # It watches Term::ReadLine's input filehandle for input.
137             # It runs while it's waiting for input.
138             # It turns off the input watcher when it's no longer needed.
139             #
140             # POE::Kernel's run_while() dispatches other events (including
141             # "term_readline_readable" below) until $waiting_for_input goes
142             # to zero.
143              
144             term_readline_waitfunc => sub {
145 0     0     my $input_handle = $_[POE::Session->ARG1][0];
146 0           $waiting_for_input = 1;
147 0           $_[POE::Session->KERNEL]->select_read($input_handle => 'term_readline_readable');
148 0           $_[POE::Session->KERNEL]->run_while(\$waiting_for_input);
149 0           $_[POE::Session->KERNEL]->select_read($input_handle => undef);
150             },
151              
152             # This callback is invoked as Term::ReadLine is starting up for
153             # the first time. It saves the exposed input filehandle where
154             # the "term_readline_waitfunc" callback can see it.
155              
156             term_readline_regfunc => sub {
157 0     0     my $input_handle = $_[POE::Session->ARG1][0];
158 0           return $input_handle;
159             },
160              
161             # This callback is invoked when data is seen on Term::ReadLine's
162             # input filehandle. It clears the $waiting_for_input flag.
163             # This causes run_while() to return in "term_readline_waitfunc".
164              
165             term_readline_readable => sub {
166 0     0     $waiting_for_input = 0;
167             },
168             },
169 0           );
170 0           $self;
171             }
172              
173              
174             sub with_Reflex
175             {
176 0     0 1   my $self = _new(@_);
177              
178             $self->trl->event_loop(
179             sub {
180 0     0     my $input_watcher = shift();
181 0           $input_watcher->next();
182             },
183             sub {
184 0     0     my $input_handle = shift();
185 0           my $input_watcher = Reflex::Filehandle->new(
186             handle => $input_handle,
187             rd => 1,
188             );
189 0           return $input_watcher;
190             },
191 0           );
192              
193              
194 0           $self;
195             }
196              
197              
198             sub with_Tk
199             {
200 0     0 1   my $self = _new(@_);
201              
202             $self->trl->event_loop(
203             sub {
204 0     0     my $data = shift;
205 0           Tk::DoOneEvent(0) until $$data;
206 0           $$data = 0;
207             },
208             sub {
209             # save filehandle for unhooking later.
210 0     0     $self->{tkFH} = shift;
211 0           my $data;
212 0           $$data = 0;
213 0           Tk->fileevent($self->{tkFH}, 'readable', sub { $$data = 1 });
  0            
214 0           $data;
215             },
216 0           );
217              
218             $self->{_cleanup} = sub {
219 0     0     my $s = shift;
220 0           Tk->fileevent($s->{tkFH}, 'readable', "");
221 0           };
222              
223 0           $self;
224             }
225              
226              
227             sub DESTROY
228             {
229 0     0     my $self = shift;
230              
231 0           local $@;
232 0           eval {
233 0           $self->trl->event_loop(undef);
234              
235 0 0         $self->{_cleanup}->($self) if $self->{_cleanup};
236             };
237             }
238              
239              
240             sub trl
241             {
242 0     0 1   my $self = shift;
243 0           $self->{_term};
244             }
245              
246             our $AUTOLOAD;
247             sub AUTOLOAD
248             {
249 0     0     (my $f = $AUTOLOAD) =~ s/.*:://;
250              
251 8     8   62 no strict 'refs';
  8         16  
  8         1064  
252 0           *{$f} = sub {
253 0     0     shift->trl()->$f(@_);
254 0           };
255              
256 0           goto &$f;
257             }
258              
259              
260             1; # End of Term::ReadLine::Event
261              
262             __END__