File Coverage

blib/lib/Term/TermKey/Async.pm
Criterion Covered Total %
statement 9 9 100.0
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 12 12 100.0


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2008-2013 -- leonerd@leonerd.org.uk
5              
6             package Term::TermKey::Async;
7              
8 2     2   20419 use strict;
  2         5  
  2         71  
9 2     2   11 use warnings;
  2         3  
  2         59  
10 2     2   18 use base qw( IO::Async::Handle );
  2         4  
  2         2549  
11              
12             our $VERSION = '0.08';
13              
14             use Carp;
15              
16             use IO::Async::Timer::Countdown;
17             use Term::TermKey qw( RES_EOF RES_KEY RES_AGAIN );
18              
19             =head1 NAME
20              
21             C - terminal key input using C with
22             C
23              
24             =head1 SYNOPSIS
25              
26             use Term::TermKey::Async qw( FORMAT_VIM KEYMOD_CTRL );
27             use IO::Async::Loop;
28            
29             my $loop = IO::Async::Loop->new();
30            
31             my $tka = Term::TermKey::Async->new(
32             term => \*STDIN,
33              
34             on_key => sub {
35             my ( $self, $key ) = @_;
36            
37             print "Got key: ".$self->format_key( $key, FORMAT_VIM )."\n";
38            
39             $loop->loop_stop if $key->type_is_unicode and
40             $key->utf8 eq "C" and
41             $key->modifiers & KEYMOD_CTRL;
42             },
43             );
44            
45             $loop->add( $tka );
46            
47             $loop->loop_forever;
48              
49             =head1 DESCRIPTION
50              
51             This class implements an asynchronous perl wrapper around the C
52             library, which provides an abstract way to read keypress events in
53             terminal-based programs. It yields structures that describe keys, rather than
54             simply returning raw bytes as read from the TTY device.
55              
56             This class is a subclass of C, allowing it to be put in an
57             C object and used alongside other objects in an C
58             program. It internally uses an instance of L to access the
59             underlying C library. For details on general operation, including the
60             representation of keypress events as objects, see the documentation on that
61             class.
62              
63             Proxy methods exist for normal accessors of C, and the usual
64             behaviour of the C or other methods is instead replaced by the
65             C event.
66              
67             =head1 EVENTS
68              
69             The following events are invoked, either using subclass methods or CODE
70             references in parameters:
71              
72             =head2 on_key $key
73              
74             Invoked when a key press is received from the terminal. The C<$key> parameter
75             will contain an instance of C representing the keypress
76             event.
77              
78             =cut
79              
80             # Forward any requests for symbol imports on to Term::TermKey
81             sub import {
82             shift; unshift @_, "Term::TermKey";
83             my $import = $_[0]->can( "import" );
84             goto &$import; # So as not to have to fiddle with Sub::UpLevel
85             }
86              
87             =head1 CONSTRUCTOR
88              
89             =cut
90              
91             =head2 $tka = Term::TermKey::Async->new( %args )
92              
93             This function returns a new instance of a C object. It
94             takes the following named arguments:
95              
96             =over 8
97              
98             =item term => IO or INT
99              
100             Optional. File handle or POSIX file descriptor number for the file handle to
101             use as the connection to the terminal. If not supplied C will be used.
102              
103             =back
104              
105             =cut
106              
107             sub new
108             {
109             my $class = shift;
110             my %args = @_;
111              
112             # TODO: Find a better algorithm to hunt my terminal
113             my $term = delete $args{term} || \*STDIN;
114              
115             my $termkey = Term::TermKey->new( $term, delete $args{flags} || 0 );
116             if( !defined $termkey ) {
117             croak "Cannot construct a termkey instance\n";
118             }
119              
120             my $self = $class->SUPER::new(
121             read_handle => $term,
122             %args,
123             );
124              
125             $self->can_event( "on_key" ) or
126             croak 'Expected either a on_key callback or an ->on_key method';
127              
128             $self->{termkey} = $termkey;
129              
130             $self->add_child( $self->{timer} = IO::Async::Timer::Countdown->new(
131             notifier_name => "force_key",
132             on_expire => $self->_capture_weakself( "_force_key" ),
133             ) );
134              
135             return $self;
136             }
137              
138             =head1 PARAMETERS
139              
140             The following named parameters may be passed to C or C:
141              
142             =over 8
143              
144             =item flags => INT
145              
146             C flags to pass to constructor or C.
147              
148             =item on_key => CODE
149              
150             CODE reference for the C event.
151              
152             =back
153              
154             =cut
155              
156             sub configure
157             {
158             my $self = shift;
159             my %params = @_;
160              
161             if( exists $params{on_key} ) {
162             $self->{on_key} = delete $params{on_key};
163             }
164              
165             if( exists $params{flags} ) {
166             $self->termkey->set_flags( delete $params{flags} );
167             }
168              
169             $self->SUPER::configure( %params );
170             }
171              
172             sub on_read_ready
173             {
174             my $self = shift;
175              
176             my $timer = $self->{timer};
177             $timer->stop;
178              
179             my $termkey = $self->{termkey};
180              
181             return unless $termkey->advisereadable == RES_AGAIN;
182              
183             my $key;
184              
185             my $ret;
186             while( ( $ret = $termkey->getkey( $key ) ) == RES_KEY ) {
187             $self->invoke_event( on_key => $key );
188             }
189              
190             if( $ret == RES_AGAIN ) {
191             $timer->configure( delay => $termkey->get_waittime / 1000 );
192             $timer->start;
193             }
194             elsif( $ret == RES_EOF ) {
195             $self->close;
196             }
197             }
198              
199             sub _force_key
200             {
201             my $self = shift;
202              
203             my $termkey = $self->{termkey};
204              
205             my $key;
206             if( $termkey->getkey_force( $key ) == RES_KEY ) {
207             $self->invoke_event( on_key => $key );
208             }
209             }
210              
211             =head1 METHODS
212              
213             =cut
214              
215             =head2 $tk = $tka->termkey
216              
217             Returns the C object being used to access the C
218             library. Normally should not be required; the proxy methods should be used
219             instead. See below.
220              
221             =cut
222              
223             sub termkey
224             {
225             my $self = shift;
226             return $self->{termkey};
227             }
228              
229             =head2 $flags = $tka->get_flags
230              
231             =head2 $tka->set_flags( $flags )
232              
233             =head2 $canonflags = $tka->get_canonflags
234              
235             =head2 $tka->set_canonflags( $canonflags )
236              
237             =head2 $msec = $tka->get_waittime
238              
239             =head2 $tka->set_waittime( $msec )
240              
241             =head2 $str = $tka->get_keyname( $sym )
242              
243             =head2 $sym = $tka->keyname2sym( $keyname )
244              
245             =head2 ( $ev, $button, $line, $col ) = $tka->interpret_mouse( $key )
246              
247             =head2 $str = $tka->format_key( $key, $format )
248              
249             =head2 $key = $tka->parse_key( $str, $format )
250              
251             =head2 $key = $tka->parse_key_at_pos( $str, $format )
252              
253             =head2 $cmp = $tka->keycmp( $key1, $key2 )
254              
255             These methods all proxy to the C object, and allow transparent
256             use of the C object as if it was a subclass.
257             Their arguments, behaviour and return value are therefore those provided by
258             that class. For more detail, see the L documentation.
259              
260             =cut
261              
262             # Proxy methods for normal Term::TermKey access
263             foreach my $method (qw(
264             get_flags
265             set_flags
266             get_canonflags
267             set_canonflags
268             get_waittime
269             set_waittime
270             get_keyname
271             keyname2sym
272             interpret_mouse
273             format_key
274             parse_key
275             parse_key_at_pos
276             keycmp
277             )) {
278             no strict 'refs';
279             *{$method} = sub {
280             my $self = shift;
281             $self->termkey->$method( @_ );
282             };
283             }
284              
285             =head1 AUTHOR
286              
287             Paul Evans
288              
289             =cut
290              
291             0x55AA;