File Coverage

blib/lib/POE/Wheel/TermKey.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.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, 2011 -- leonerd@leonerd.org.uk
5              
6             package POE::Wheel::TermKey;
7              
8 1     1   840 use strict;
  1         3  
  1         46  
9 1     1   5 use warnings;
  1         2  
  1         41  
10 1     1   16 use base qw( POE::Wheel );
  1         2  
  1         672  
11              
12             our $VERSION = '0.02';
13              
14 1     1   7 use Carp;
  1         1  
  1         82  
15              
16 1     1   428 use POE;
  0            
  0            
17             use Term::TermKey;
18              
19             =head1 NAME
20              
21             C - terminal key input using C with C
22              
23             =head1 SYNOPSIS
24              
25             use Term::TermKey qw( FORMAT_VIM KEYMOD_CTRL );
26             use POE qw(Wheel::TermKey);
27            
28             POE::Session->create(
29             inline_states => {
30             _start => sub {
31             $_[HEAP]{termkey} = POE::Wheel::TermKey->new(
32             InputEvent => 'got_key',
33             );
34             },
35             got_key => sub {
36             my $key = $_[ARG0];
37             my $termkey = $_[HEAP]{termkey};
38            
39             print "Got key: ".$termkey->format_key( $key, FORMAT_VIM )."\n";
40            
41             # Gotta exit somehow.
42             delete $_[HEAP]{termkey} if $key->type_is_unicode and
43             $key->utf8 eq "C" and
44             $key->modifiers & KEYMOD_CTRL;
45             },
46             }
47             );
48              
49             POE::Kernel->run;
50              
51             =head1 DESCRIPTION
52              
53             This class implements an asynchronous perl wrapper around the C
54             library, which provides an abstract way to read keypress events in
55             terminal-based programs. It yields structures that describe keys, rather than
56             simply returning raw bytes as read from the TTY device.
57              
58             This class is a subclass of L, which internally uses an instance
59             of L to access the underlying C library. For details of on
60             general operation, including the representation of keypress events as objects,
61             see the documentation on C instead.
62              
63             Proxy methods exist for normal acessors of C, and the usual
64             behaviour of C or other methods is instead replaced by the
65             C.
66              
67             =cut
68              
69             =head1 CONSTRUCTOR
70              
71             =cut
72              
73             =head2 $wheel = POE::Wheel::TermKey->new( %args )
74              
75             Returns a new instance of a C object. It takes the
76             following named parameters:
77              
78             =over 8
79              
80             =item Term => IO or INT
81              
82             Optional. File handle or POSIX file descriptor number for the filehandle to
83             use as the connection to the terminal. If not supplied C will be used.
84              
85             =item Flags => INT
86              
87             C flags to pass to the C constructor.
88              
89             =item InputEvent => STRING
90              
91             Name of the session event to emit when a key is received. The event will be
92             given a single argument, the C event object, as
93             C<$_[ARG0]>.
94              
95             =back
96              
97             =cut
98              
99             sub new
100             {
101             my $class = shift;
102             my %args = @_;
103              
104             # TODO: Find a better algorithm to hunt my terminal
105             my $term = delete $args{Term} || \*STDIN;
106              
107             my $termkey = Term::TermKey->new( $term, delete $args{Flags} || 0 );
108             if( !defined $termkey ) {
109             croak "Cannot construct a termkey instance\n";
110             }
111              
112             my $self = bless {
113             inputevent => $args{InputEvent},
114             id => POE::Wheel::allocate_wheel_id,
115             term => $term,
116             termkey => $termkey,
117             }, $class;
118              
119             $self->{states}{read} = ref($self) . "($self->{id}) -> select read";
120             $self->{states}{timeout} = ref($self) . "($self->{id}) -> timeout";
121             my $state_timeout = $self->{states}{timeout};
122              
123             my $inputeventr = \$self->{inputevent};
124             $poe_kernel->state( $self->{states}{read} => sub {
125             my ( $kernel, $session ) = @_[KERNEL, SESSION];
126              
127             return unless $termkey->advisereadable == RES_AGAIN;
128              
129             $kernel->alarm( $state_timeout => undef );
130              
131             my $key;
132              
133             my $ret;
134             while( ( $ret = $termkey->getkey( $key ) ) == RES_KEY ) {
135             $kernel->call( $session, $$inputeventr, $key );
136             }
137              
138             if( $ret == RES_AGAIN ) {
139             $kernel->delay( $state_timeout => $termkey->get_waittime / 1000 );
140             }
141             } );
142             $poe_kernel->state( $self->{states}{timeout} => sub {
143             my ( $kernel, $session ) = @_[KERNEL, SESSION];
144              
145             if( $termkey->getkey_force( my $key ) == RES_KEY ) {
146             $kernel->call( $session, $$inputeventr, $key );
147             }
148             } );
149              
150             $poe_kernel->select_read( $self->{term}, $self->{states}{read} );
151              
152             return $self;
153             }
154              
155             sub DESTROY
156             {
157             my $self = shift;
158              
159             $poe_kernel->select( $self->{term}, undef );
160              
161             $poe_kernel->state( $_ => undef ) for values %{ $self->{states} };
162              
163             POE::Wheel::free_wheel_id( $self->{id} );
164             }
165              
166             =head1 METHODS
167              
168             =cut
169              
170             =head2 $tk = $wheel->termkey
171              
172             Returns the C object being used to access the C
173             library. Normally should not be required; the proxy methods should be used
174             instead. See below.
175              
176             =cut
177              
178             sub termkey
179             {
180             my $self = shift;
181             return $self->{termkey};
182             }
183              
184             =head2 $flags = $wheel->get_flags
185              
186             =head2 $wheel->set_flags( $flags )
187              
188             =head2 $canonflags = $wheel->get_canonflags
189              
190             =head2 $wheel->set_canonflags( $canonflags )
191              
192             =head2 $msec = $wheel->get_waittime
193              
194             =head2 $wheel->set_waittime( $msec )
195              
196             =head2 $str = $wheel->get_keyname( $sym )
197              
198             =head2 $sym = $wheel->keyname2sym( $keyname )
199              
200             =head2 ( $ev, $button, $line, $col ) = $wheel->interpret_mouse( $key )
201              
202             =head2 $str = $wheel->format_key( $key, $format )
203              
204             =head2 $key = $wheel->parse_key( $str, $format )
205              
206             =head2 $key = $wheel->parse_key_at_pos( $str, $format )
207              
208             =head2 $cmp = $wheel->keycmp( $key1, $key2 )
209              
210             These methods all proxy to the C object, and allow transparent
211             use of the C object as if it was a subclass. Their
212             arguments, behaviour and return value are therefore those provided by that
213             class. For more detail, see the L documentation.
214              
215             =cut
216              
217             # Proxy methods for normal Term::TermKey access
218             foreach my $method (qw(
219             get_flags
220             set_flags
221             get_canonflags
222             set_canonflags
223             get_waittime
224             set_waittime
225             get_keyname
226             keyname2sym
227             interpret_mouse
228             format_key
229             parse_key
230             parse_key_at_pos
231             keycmp
232             )) {
233             no strict 'refs';
234             *{$method} = sub {
235             my $self = shift;
236             $self->termkey->$method( @_ );
237             };
238             }
239              
240             =head1 AUTHOR
241              
242             Paul Evans
243              
244             =cut
245              
246             0x55AA;