File: TermKey.pm

package info (click to toggle)
libterm-termkey-perl 0.19-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 232 kB
  • sloc: perl: 370; makefile: 5
file content (847 lines) | stat: -rw-r--r-- 21,944 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
#  You may distribute under the terms of either the GNU General Public License
#  or the Artistic License (the same terms as Perl itself)
#
#  (C) Paul Evans, 2009-2025 -- leonerd@leonerd.org.uk

package Term::TermKey 0.19;

use v5.14;
use warnings;

use Exporter 'import';

require XSLoader;
XSLoader::load( __PACKAGE__, our $VERSION );

=head1 NAME

C<Term::TermKey> - perl wrapper around C<libtermkey>

=head1 SYNOPSIS

=for highlighter language=perl

   use Term::TermKey;

   my $tk = Term::TermKey->new( \*STDIN );

   print "Press any key\n";

   $tk->waitkey( my $key );

   print "You pressed: " . $tk->format_key( $key, 0 );

=head1 DESCRIPTION

=over 4

B<Note> that C<libtermkey> itself is deprecated in favour of its eventual
merge into C<libtickit>. As a result, uses of this module should also be
considered deprecated. Consider rewriting code to use L<Tickit> instead;
either by creating a L<Tickit::Term> to receive key input events, or perform a
more wholescale rewrite into using C<Tickit> generally for all screen
interaction purposes.

=back

This module provides a light perl wrapper around the C<libtermkey> library.
This library attempts to provide an abstract way to read keypress events in
terminal-based programs by providing structures that describe keys, rather
than simply returning raw bytes as read from the TTY device.

This version of C<Term::TermKey> requires C<libtermkey> version at least 0.16.

=head2 Multi-byte keys, ambiguous keys, and waittime

Some keypresses generate multiple bytes from the terminal. There is also the
ambiguity between multi-byte CSI or SS3 sequences, and the Escape key itself.
The waittime timer is used to distinguish them.

When some bytes arrive that could be the start of possibly multiple different
keypress events, the library will attempt to wait for more bytes to arrive
that would finish it. If no more bytes arrive after this time, then the bytes
will be reported as events as they stand, even if this results in interpreting
a partially-complete Escape sequence as a literal Escape key followed by some
normal letters or other symbols.

Similarly, if the start of an incomplete UTF-8 sequence arrives when the
library is in UTF-8 mode, this will be reported as the UTF-8 replacement
character (U+FFFD) if it is incomplete after this time.

=cut

=head1 CONSTRUCTOR

=cut

=head2 new

   $tk = Term::TermKey->new( $fh, $flags );

Construct a new C<Term::TermKey> object that wraps the given term handle.
C<$fh> should be either an IO handle reference, an integer referring to a
plain POSIX file descriptor, of C<undef>. C<$flags> is optional, but if given
should contain the flags to pass to C<libtermkey>'s constructor. Assumes a
default of 0 if not supplied. See the C<FLAG_*> constants.

=head2 new_abstract

   $tk = Term::TermKey->new_abstract( $termtype, $flags );

Construct a new abstract C<Term::TermKey> object not associated with a
filehandle. Input may be fed to it using the C<push_bytes()> method
rather than C<waitkey()> or C<advisereadable()>. The name of the termtype
should be given in the C<$termtype> string.

=cut

=head1 METHODS

=cut

# The following documents the various XS-implemented methods in TermKey.xs in
# the same order

=head2 start

=head2 stop

   $success = $tk->start;

   $success = $tk->stop;

Start or stop IO interactions from the instance. Starting will send the
terminal initialisation sequence and set up C<termios(5)> settings, stopping
will send the terminal shutdown sequence and restore C<termios(5)> back to the
initial values. After construction, a C<Term::TermKey> instance is already
started, but these methods may be used to suspend and resume, for example, on
receipt of a C<SIGTSTP> signal requesting that the application background
itself.

Returns false if it fails; C<$!> will contain an error code.

=head2 is_started

   $started = $tk->is_started;

Returns true if the instance has been started, or false if it is stopped.

=head2 get_flags

=head2 set_flags

   $flags = $tk->get_flags;

   $tk->set_flags( $newflags );

Accessor and mutator for the flags. One of the C<FLAG_UTF8> or C<FLAG_RAW>
flags will be set, even if neither was present in the constructor, as in this
case the library will attempt to detect if the current locale is UTF-8 aware
or not.

=cut

=head2 get_canonflags

=head2 set_canonflags

   $canonflags = $tk->get_canonflags;

   $tk->set_canonflags( $newcanonflags );

Accessor and mutator for the canonicalisation flags.

=cut

=head2 get_waittime

=head2 set_waittime

   $msec = $tk->get_waittime;

   $tk->set_waittime( $msec );

Accessor and mutator for the maximum wait time in miliseconds. The underlying
C<libtermkey> library will have specified a default value when the object was
constructed.

=cut

=head2 get_buffer_remaining

   $bytes = $tk->get_buffer_remaining;

Accessor returning the number of bytes of buffer space remaining in the
buffer; the space in which C<push_bytes> can write.

=head2 get_buffer_size

=head2 set_buffer_size

   $bytes = $tk->get_buffer_size;

   $tk->set_buffer_size( $size );

Accessor and mutator to for the total buffer size to store pending bytes. If
the underlying C<termkey_set_buffer_size(3)> call fails, the
C<set_buffer_size> method will throw an exception.

=cut

=head2 getkey

   $res = $tk->getkey( $key );

Attempt to retrieve a single keypress event from the buffer, and put it in
C<$key>. If successful, will return C<RES_KEY> to indicate that the C<$key>
structure now contains a new keypress event. If C<$key> is an undefined lvalue
(such as a new scalar variable) it will be initialised to contain a new key
structure.

If nothing is in the buffer it will return C<RES_NONE>. If the buffer contains
a partial keypress event which does not yet contain all the bytes required, it
will return C<RES_AGAIN> (see above section about multibyte events). If no
events are ready and the input stream is now closed, will return C<RES_EOF>.

This method will not block, nor will it perform any IO on the underlying file
descriptor. For a normal blocking read, see C<waitkey()>.

=cut

=head2 getkey_force

   $res = $tk->getkey_force( $key );

Similar to C<getkey()>, but will not return C<RES_AGAIN> if a partial match
was found. Instead, it will force an interpretation of the bytes, even if this
means interpreting the start of an C<< <Esc> >>-prefixed multibyte sequence as
a literal C<Escape> key followed by normal letters. If C<$key> is an undefined
lvalue (such as a new scalar variable) it will be initialised to contain a new
key structure.

This method will not block, nor will it perform any IO on the underlying file
descriptor. For a normal blocking read, see C<waitkey()>.

=cut

=head2 waitkey

   $res = $tk->waitkey( $key );

Attempt to retrieve a single keypress event from the buffer, or block until
one is available. If successful, will return C<RES_KEY> to indicate that the
C<$key> structure now contains a new keypress event. If an IO error occurs it
will return C<RES_ERROR>, and if the input stream is now closed it will return
C<RES_EOF>.

If C<$key> is an undefined lvalue (such as a new scalar variable) it will be
initialised to contain a new key structure.

=cut

=head2 advisereadable

   $res = $tk->advisereadable;

Inform the underlying library that new input may be available on the
underlying file descriptor and so it should call C<read()> to obtain it.
Will return C<RES_AGAIN> if it read at least one more byte, C<RES_NONE> if no
more input was found, or C<RES_ERROR> if an IO error occurs.

Normally this method would only be used in programs that want to use
C<Term::TermKey> asynchronously; see the EXAMPLES section. This method
gracefully handles an C<EAGAIN> error from the underlying C<read()> syscall.

=cut

=head2 push_bytes

   $len = $tk->push_bytes( $bytes );

Feed more bytes into the input buffer. This is primarily useful for feeding
input into filehandle-less instances, constructed by passing C<undef> or C<-1>
as the filehandle to the constructor. After calling this method, these bytes
will be available to read as keypresses by the C<getkey> method.

=cut

=head2 get_keyname

   $str = $tk->get_keyname( $sym );

Returns the name of a key sym, such as returned by
C<< Term::TermKey::Key->sym() >>.

=cut

=head2 keyname2sym

   $sym = $tk->keyname2sym( $keyname );

Look up the sym for a named key. The result of this method call can be
compared directly against the value returned by
C<< Term::TermKey::Key->sym() >>. Because this method has to perform a linear
search of key names, it is best called rarely, perhaps during program
initialisation, and the result stored for easier comparisons during runtime.

=cut

=head2 interpret_unknown_csi

   ( $cmd, @args ) = $tk->interpret_unknown_csi( $key );

If C<$key> contains an unknown CSI event then its command and arguments are
returned in a list. C<$cmd> will be a string of 1 to 3 characters long,
containing the initial and intermediate characters if present, followed by the
main command character. C<@args> will contain the numerical arguments, where
missing arguments are replaced by -1. If C<$key> does not contain an unknown
CSI event then an empty list is returned.

Note that this method needs to be called immediately after C<getkey> or
C<waitkey>, or at least, before calling either of those methods again. The
actual CSI sequence is retained in the F<libtermkey> buffer, and only
retrieved by this method call. Calling C<getkey> or C<waitkey> again may
overwrite that buffer.

=cut

=head2 format_key

   $str = $tk->format_key( $key, $format );

Return a string representation of the keypress event in C<$key>, following the
flags given. See the descriptions of the flags, below, for more detail.

This may be useful for matching keypress events against keybindings stored in
a hash. See EXAMPLES section for more detail.

=cut

=head2 parse_key

   $key = $tk->parse_key( $str, $format );

Return a keypress event by parsing the string representation in C<$str>,
following the flags given. This method is an inverse of C<format_key>.

This may be useful for parsing entries from a configuration file or similar.

=cut

=head2 parse_key_at_pos

   $key = $tk->parse_key_at_pos( $str, $format );

Return a keypress event by parsing the string representation in a region of
C<$str>, following the flags given.

Where C<parse_key> will start at the beginning of the string and requires the
entire input to be consumed, this method will start at the current C<pos()>
position in C<$str> (or at the beginning of the string if none is yet set),
and after a successful parse, will update it to the end of the matched
section. This position does not have to be at the end of the string. C<$str>
must therefore be a real scalar variable, and not a string literal.

This may be useful for incremental parsing of configuration or other data, out
of a larger string.

=cut

=head2 keycmp

   $cmp = $tk->keycmp( $key1, $key2 );

Compares the two given keypress events, returning a number less than, equal
to, or greater than zero, depending on the ordering. Keys are ordered first by
type (unicode, keysym, function, mouse), then by value within that type, then
finally by modifier bits.

This may be useful in C<sort> expressions:

   my @sorted_keys = sort { $tk->keycmp( $a, $b ) } @keys;

=cut

=head1 KEY OBJECTS

The C<Term::TermKey::Key> subclass is used to store a single keypress event.
Objects in this class cannot be changed by perl code. C<getkey()>,
C<getkey_force()> or C<waitkey()> will overwrite the contents of the structure
with a new value.

Keys cannot be constructed, but C<getkey()>, C<getkey_force()> or C<waitkey()>
will place a new key structure in the C<$key> variable if it is undefined when
they are called. C<parse_key()> and C<parse_key_at_pos()> will return new
keys.

=head2 type

   $key->type;

The type of event. One of C<TYPE_UNICODE>, C<TYPE_FUNCTION>, C<TYPE_KEYSYM>,
C<TYPE_MOUSE>, C<TYPE_POSITION>, C<TYPE_MODEREPORT>, C<TYPE_UNKNOWN_CSI>.

=head2 type_is_...

   $key->type_is_unicode;

   $key->type_is_function;

   $key->type_is_keysym;

   $key->type_is_mouse;

   $key->type_is_position;

   $key->type_is_modereport;

   $key->type_is_unknown_csi;

Shortcuts which return a boolean.

=head2 codepoint

   $key->codepoint;

The Unicode codepoint number for C<TYPE_UNICODE>, or 0 otherwise.

=head2 number

   $key->number;

The function key number for C<TYPE_FUNCTION>, or 0 otherwise.

=head2 sym

   $key->sym;

The key symbol number for C<TYPE_KEYSYM>, or 0 otherwise. This can be passed
to C<< Term::TermKey->get_keyname() >>, or compared to a result earlier
obtained from C<< Term::TermKey->keyname2sym() >>.

=head2 modifiers

   $key->modifiers;

The modifier bitmask. Can be compared against the C<KEYMOD_*> constants.

=head2 modifier_...

   $key->modifier_shift;

   $key->modifier_alt;

   $key->modifier_ctrl;

Shortcuts which return a boolean if the appropriate modifier is present.

=head2 utf8

   $key->utf8;

A string representation of the given Unicode codepoint. If the underlying
C<termkey> library is in UTF-8 mode then this will be a UTF-8 string. If it is
in raw mode, then this will be a single raw byte.

=head2 mouseev

=head2 button

   $key->mouseev;

   $key->button;

The details of a mouse event for C<TYPE_MOUSE>, or C<undef> for other types of
event.

=head2 line

=head2 col

   $key->line;

   $key->col;

The details of a mouse or position event, or C<undef> for other types of
event.

=head2 termkey

   $key->termkey;

Return the underlying C<Term::TermKey> object this key was retrieved from.

=head2 format

   $str = $key->format( $format );

Returns a string representation of the keypress event, identically to calling
C<format_key> on the underlying C<Term::TermKey> object.

=cut

sub Term::TermKey::Key::format
{
   my $self = shift;
   return $self->termkey->format_key( $self, @_ );
}

=head1 EXPORTED CONSTANTS

The following constant names are all derived from the underlying C<libtermkey>
library. For more detail see the documentation on the library.

These constants are possible values of C<< $key->type >>

=over 4

=item C<TYPE_UNICODE>

a Unicode codepoint

=item C<TYPE_FUNCTION>

a numbered function key

=item C<TYPE_KEYSYM>

a symbolic key

=item C<TYPE_MOUSE>

a mouse movement or button press or release

=item C<TYPE_POSITION>

a cursor position report

=item C<TYPE_MODEREPORT>

an ANSI or DEC mode report

=item C<TYPE_UNKNOWN_CSI>

an unrecognised CSI sequence

=back

These constants are result values from C<getkey()>, C<getkey_force()>,
C<waitkey()> or C<advisereadable()>

=over 4

=item C<RES_NONE>

No key event is ready.

=item C<RES_KEY>

A key event has been provided.

=item C<RES_EOF>

No key events are ready and the terminal has been closed, so no more will
arrive.

=item C<RES_AGAIN>

No key event is ready yet, but a partial one has been found. This is only
returned by C<getkey()>. To obtain the partial result even if it never
completes, call C<getkey_force()>.

=item C<RES_ERROR>

Returned by C<waitkey> or C<advisereadable> if an IO error occurs while trying
to read another key event.

=back

These constants are key modifier masks for C<< $key->modifiers >>

=over 4

=item C<KEYMOD_SHIFT>

=item C<KEYMOD_ALT>

=item C<KEYMOD_CTRL>

Should be obvious ;)

=back

These constants are types of mouse event which may be returned by
C<< $key->mouseev >> or C<interpret_mouse>:

=over 4

=item C<MOUSE_UNKNOWN>

The type of mouse event was not recognised

=item C<MOUSE_PRESS>

The event reports a mouse button being pressed

=item C<MOUSE_DRAG>

The event reports the mouse being moved while a button is held down

=item C<MOUSE_RELEASE>

The event reports the mouse buttons being released, or the mouse moved without
a button held.

=back

These constants are flags for the constructor, C<< Term::TermKey->new >>

=over 4

=item C<FLAG_NOINTERPRET>

Do not attempt to interpret C0 codes into keysyms (ie. C<Backspace>, C<Tab>,
C<Enter>, C<Escape>). Instead report them as plain C<Ctrl-letter> events.

=item C<FLAG_CONVERTKP>

Convert xterm's alternate keypad symbols into the plain ASCII codes they would
represent.

=item C<FLAG_RAW>

Ignore locale settings; do not attempt to recombine UTF-8 sequences. Instead
report only raw values.

=item C<FLAG_UTF8>

Ignore locale settings; force UTF-8 recombining on.

=item C<FLAG_NOTERMIOS>

Even if the terminal file descriptor represents a TTY device, do not call the
C<tcsetattr()> C<termios> function on it to set in canonical input mode.

=item C<FLAG_SPACESYMBOL>

Sets the C<CANON_SPACESYMBOL> canonicalisation flag. See below.

=item C<FLAG_CTRLC>

Disable the C<SIGINT> behaviour of the C<Ctrl-C> key, allowing it to be read
as a modified Unicode keypress.

=item C<FLAG_EINTR>

Disable retry on signal interrupt; instead report it as an error with
C<RES_ERROR> and C<$!> set to C<EINTR>. Without this flag, IO operations will
be retried if interrupted.

=back

These constants are canonicalisation flags for C<set_canonflags> and
C<get_canonflags>

=over 4

=item C<CANON_SPACESYMBOL>

With this flag set, the Space key will appear as a C<TYPE_KEYSYM> key event
whose symname is C<"Space">. Without this flag, it appears as a normal
C<TYPE_UNICODE> character.

=item C<CANON_DELBS>

With this flag set, the ASCII C<DEL> byte is interpreted as the C<"Backspace">
keysym, rather than C<"DEL">. This flag does not affect the interpretation of
ASCII C<BS>, which is always represented as C<"Backspace">.

=back

These constants are flags to C<format_key>

=over 4

=item C<FORMAT_LONGMOD>

Print full modifier names e.g. C<Shift-> instead of abbreviating to C<S->.

=item C<FORMAT_CARETCTRL>

If the only modifier is C<Ctrl> on a plain character, render it as C<^X>.

=item C<FORMAT_ALTISMETA>

Use the name C<Meta> or the letter C<M> instead of C<Alt> or C<A>.

=item C<FORMAT_WRAPBRACKET>

If the key event is a special key instead of unmodified Unicode, wrap it in
C<< <brackets> >>.

=item C<FORMAT_MOUSE_POS>

If the event is a mouse event, also include the cursor position; rendered as
C<@ ($col,$line)>

=item C<FORMAT_VIM>

Shortcut to C<FORMAT_ALTISMETA|FORMAT_WRAPBRACKET>; which gives an output
close to the format the F<vim> editor uses.

=back

=cut

=head1 EXAMPLES

=head2 A simple print-until-C<Ctrl-C> loop

This program just prints every keypress until the user presses C<Ctrl-C>.

   use Term::TermKey qw( FLAG_UTF8 RES_EOF FORMAT_VIM );

   my $tk = Term::TermKey->new(\*STDIN);

   # ensure perl and libtermkey agree on Unicode handling
   binmode( STDOUT, ":encoding(UTF-8)" ) if $tk->get_flags & FLAG_UTF8;

   while( ( my $ret = $tk->waitkey( my $key ) ) != RES_EOF ) {
      print "Got key: ".$tk->format_key( $key, FORMAT_VIM )."\n";
   }

=head2 Configuration of custom keypresses

Because C<format_key()> yields a plain string representation of a keypress it
can be used as a hash key to look up a "handler" routine for the key.

The following implements a simple line input program, though obviously lacking
many features in a true line editor like F<readline>.

   use Term::TermKey qw( FLAG_UTF8 RES_EOF FORMAT_LONGMOD );

   my $tk = Term::TermKey->new(\*STDIN);

   # ensure perl and libtermkey agree on Unicode handling
   binmode( STDOUT, ":encoding(UTF-8)" ) if $tk->get_flags & FLAG_UTF8;

   my $line = "";

   $| = 1;

   my %key_handlers = (
      "Enter"  => sub { 
         print "\nThe line is: $line\n";
         $line = "";
      },

      "Backspace" => sub {
         return unless length $line;
         substr( $line, -1, 1 ) = "";
         print "\cH \cH"; # erase it
      },

      # other handlers ...
   );

   while( ( my $ret = $tk->waitkey( my $key ) ) != RES_EOF ) {
      my $handler = $key_handlers{ $tk->format_key( $key, FORMAT_LONGMOD ) };
      if( $handler ) {
         $handler->( $key );
      }
      elsif( $key->type_is_unicode and !$key->modifiers ) {
         my $char = $key->utf8;

         $line .= $char;
         print $char;
      }
   }

=head2 Asynchronous operation

Because the C<getkey()> method performs no IO itself, it can be combined with
the C<advisereadable()> method in an asynchronous program.

   use IO::Select;
   use Term::TermKey qw(
      FLAG_UTF8 RES_KEY RES_AGAIN RES_EOF FORMAT_VIM
   );

   my $select = IO::Select->new();

   my $tk = Term::TermKey->new(\*STDIN);
   $select->add(\*STDIN);

   # ensure perl and libtermkey agree on Unicode handling
   binmode( STDOUT, ":encoding(UTF-8)" ) if $tk->get_flags & FLAG_UTF8;

   sub on_key
   {
      my ( $tk, $key ) = @_;

      print "You pressed " . $tk->format_key( $key, FORMAT_VIM ) . "\n";
   }

   my $again = 0;

   while(1) {
      my $timeout = $again ? $tk->get_waittime/1000 : undef;
      my @ready = $select->can_read($timeout);

      if( !@ready ) {
         my $ret;
         while( ( $ret = $tk->getkey_force( my $key ) ) == RES_KEY ) {
            on_key( $tk, $key );
         }
      }

      while( my $fh = shift @ready ) {
         if( $fh == \*STDIN ) {
            $tk->advisereadable;
            my $ret;
            while( ( $ret = $tk->getkey( my $key ) ) == RES_KEY ) {
               on_key( $tk, $key );
            }

            $again = ( $ret == RES_AGAIN );
            exit if $ret == RES_EOF;
         }
         # Deal with other filehandles here
      }
   }

There may also be more appropriate modules on CPAN for particular event
frameworks; see the C<SEE ALSO> section below.

=cut

=head1 SEE ALSO

=over 4

=item *

L<http://www.leonerd.org.uk/code/libtermkey/> - C<libtermkey> home page

=item *

L<Term::TermKey::Async> - terminal key input using C<libtermkey> with
L<IO::Async>

=item *

L<POE::Wheel::TermKey> - terminal key input using C<libtermkey> with L<POE>

=item *

L<AnyEvent::TermKey> - terminal key input using C<libtermkey> with L<AnyEvent>

=back

=head1 AUTHOR

Paul Evans <leonerd@leonerd.org.uk>

=cut

0x55AA;