File Coverage

blib/lib/Term/Screen.pm
Criterion Covered Total %
statement 15 223 6.7
branch 0 68 0.0
condition 0 36 0.0
subroutine 5 33 15.1
pod 26 27 96.3
total 46 387 11.8


line stmt bran cond sub pod time code
1             package Term::Screen;
2              
3 1     1   866 use 5.006;
  1         2  
  1         41  
4 1     1   6 use strict;
  1         1  
  1         39  
5 1     1   21 use warnings;
  1         1  
  1         1416  
6              
7             our $VERSION = '1.04';
8              
9             =head1 NAME
10              
11             Term::Screen - A Simple all perl Term::Cap based screen positioning module
12              
13             =head1 SYNOPSIS
14              
15             require Term::Screen;
16              
17             $scr = new Term::Screen;
18             unless ($scr) { die " Something's wrong \n"; }
19             $scr->clrscr();
20             $scr->at(5,3);
21             $scr->puts("this is some stuff");
22             $scr->at(10,10)->bold()->puts("hi!")->normal();
23             # you can concatenate many calls (not getch)
24             $c = $scr->getch(); # doesn't need Enter key
25             ...
26             if ($scr->key_pressed()) { print "ha you hit a key!"; }
27              
28              
29              
30             =head1 DESCRIPTION
31              
32             Term::Screen is a very simple screen positioning module that should
33             work wherever C does. It is set up for Unix using stty's but
34             these dependences are isolated by evals in the C constructor. Thus
35             you may create a child module implementing Screen with MS-DOS, ioctl,
36             or other means to get raw and unblocked input. This is not a replacement
37             for Curses -- it has no memory. This was written so that it could be
38             easily changed to fit nasty systems, and to be available first thing.
39              
40             The input functions getch, key_pressed, echo, and noecho are implemented
41             so as to work under a fairly standard Unix system. They use 'stty'
42             to set raw and no echo modes and turn on auto flush. All of these are
43             'eval'ed so that this class can be inherited for new definitions easily.
44              
45             Term::Screen was designed to be "required", then used with object syntax
46             as shown above. One quirk (which the author was used to so he didn't
47             care) is that for function key translation, no delay is set. So for many
48             terminals to get an esc character, you have to hit another char after it,
49             generally another esc.
50              
51             =head1 PUBLIC INTERFACE
52              
53             Term::Screen has a very minimal set of of fixed character terminal position
54             and character reading commands:
55              
56             =over 4
57              
58             =cut
59              
60             require Term::Cap;
61              
62             =item new()
63              
64             Initialize the screen. Does not clear the screen, but does home the cursor.
65              
66             =cut
67              
68             sub new
69             {
70 0     0 1   my ( $prototype, @args ) = @_;
71              
72 0   0       my $classname = ref($prototype) || $prototype;
73              
74 0           my ($ospeed);
75              
76             # adjust OSPEED below to your system.
77 0 0         if ( $^O ne "solaris" )
78             {
79 0           eval { $ospeed = `stty speed 2>/dev/null`; }; # Unixish way to get OSpeed - works
  0            
80             }
81             else
82             { # on Linux, Gnuish ...
83             # work around Solaris stty
84             eval
85 0           {
86 0           foreach (`stty 2>/dev/null`)
87             {
88 0 0         if (/^speed (\d+)/) { $ospeed = $1; last }
  0            
  0            
89             }
90             };
91             }
92 0 0 0       $ospeed = 9600 if ( !$ospeed || $@ );
93 0           my $term = Tgetent Term::Cap { 'TERM' => '', 'OSPEED' => $ospeed };
94              
95 0           my $this = {}; # create object
96 0           bless $this, $classname;
97 0           $this->term($term); # keep termcap entry reference
98 0           $this->{IN} = ''; # clear input buffer
99 0           $this->{ROWS} = 0;
100 0           $this->{COLS} = 0;
101 0           $this->resize(); # sets $this->{ROWS} & {COLS}
102 0           $this->{KEYS} = {}; # set up fn key hash of hashes
103 0           $this->get_fn_keys(); # define function key table from defaults
104 0           $this->at( 0, 0 ); # home cursor
105 0           $this->{ECHO} = 1; # start off echoing
106 0           $| = 1; # for output flush on writes
107             # wrapped so inherited versions can call with different input codes
108 0           eval { system('stty raw -echo 2>/dev/null'); }; # turn on raw input
  0            
109             # ignore errors
110 0           return $this;
111             }
112              
113             sub DESTROY
114             {
115 0     0     my $rc = $?;
116 0           system('stty -raw echo 2>/dev/null');
117 0           $? = $rc;
118             }
119              
120              
121             =item term(term)
122              
123             Sets or Gets the Term::Cap object used by this object.
124              
125             =cut
126              
127             sub term
128             {
129 0     0 1   my ( $self, $term ) = @_;
130              
131 0 0 0       if ( defined $term && ref $term and UNIVERSAL::isa( $term, 'Term::Cap' ) )
      0        
132             {
133 0           $self->{TERM} = $term;
134             }
135 0           return $self->{TERM};
136             }
137              
138             =item rows(rows)
139              
140             Returns and/or sets the number of rows on the terminal.
141              
142             =cut
143              
144             sub rows
145             {
146 0     0 1   my ( $self, $rows ) = @_;
147              
148 0 0 0       if ( defined $rows and $rows =~ /\d+/ )
149             {
150 0           $self->{ROWS} = $rows;
151             }
152              
153 0           return $self->{ROWS};
154             }
155              
156             =item cols(cols)
157              
158             Returns and/or sets the number of cols on the terminal.
159              
160             =cut
161              
162             sub cols
163             {
164 0     0 1   my ( $self, $cols ) = @_;
165              
166 0 0 0       if ( defined $cols and $cols =~ /\d+/ )
167             {
168 0           $self->{COLS} = $cols;
169             }
170              
171 0           return $self->{COLS};
172             }
173              
174             =item at(row,col)
175              
176             Moves cursor to (row,col) where (0,0) is upper left corner, - if the spot is
177             illegal does whatever 'cm' in termcap does, since that is what it uses.
178              
179             =cut
180              
181             sub at
182             {
183 0     0 1   my ( $this, $r, $c ) = @_;
184 0 0         if ( $r < 0 ) { $r = 0; }
  0            
185 0 0         if ( $c < 0 ) { $c = 0; }
  0            
186 0 0         if ( $r >= $this->{ROWS} ) { $r = $this->{ROWS} - 1; }
  0            
187 0 0         if ( $c >= $this->{COLS} ) { $c = $this->{COLS} - 1; }
  0            
188 0           $this->term()->Tgoto( 'cm', $c, $r, *STDOUT );
189 0           return $this;
190             }
191              
192             =item resize(r,c)
193              
194             Tell screen the new number of rows & cols physically you can skip the r & c
195             and get new checked vals from stty or termcap. Term::Screen does not
196             handle resize signals internally, but you can do it by checking and updating
197             screen size using this function.
198              
199             =cut
200              
201             sub resize
202             {
203 0     0 1   my ( $this, $r, $c ) = @_;
204 0           my $size = '';
205              
206             # find screen size -- trying different methods
207 0 0 0       if ( $#_ != 2 || $r <= 0 || $c <= 0 )
      0        
208             {
209 0           $r = 0;
210 0           $c = 0;
211              
212 0 0         if ( $^O ne "solaris" )
213             {
214 0           eval { $size = `stty size`; }; # not portable but most accurate
  0            
215 0 0         if ( $size =~ /^\s*(\d+)\s+(\d+)\s*/ )
216             {
217 0           ( $r, $c ) = ( $1, $2 );
218             }
219             }
220             else
221             {
222              
223             # work around Solaris stty
224 0           eval {
225 0           foreach (`stty`)
226             {
227 0 0         if (/^rows = (\d+); columns = (\d+)/) {
228 0           $r = $1;
229 0           $c = $2;
230 0           last;
231             }
232             }
233             };
234             }
235             }
236 0 0 0       if ( $r == 0 || $c == 0 ) # try getting rows and cols some other way
237             {
238 0 0         if ( exists $ENV{'LINES'} ) { $r = $ENV{'LINES'}; }
  0            
239 0           else { $r = $this->term()->{'_li'}; } # this is often wrong
240 0 0         if ( exists $ENV{'COLUMNS'} ) { $c = $ENV{'COLUMNS'}; }
  0            
241 0           else { $c = $this->term()->{'_co'}; }
242             }
243 0           $this->{ROWS} = $r;
244 0           $this->{COLS} = $c;
245 0           return $this;
246             }
247              
248             =item normal()
249              
250             Turn off any highlightling (bold, reverse)
251              
252             =cut
253              
254             sub normal
255             {
256 0     0 1   my $this = shift;
257 0           $this->term()->Tputs( 'me', 1, *STDOUT );
258 0           return $this;
259             }
260              
261             =item bold()
262              
263             The md value from termcap - turn on bold usually
264              
265             =cut
266              
267             sub bold
268             {
269 0     0 1   my $this = shift;
270 0           $this->term()->Tputs( 'md', 1, *STDOUT );
271 0           return $this;
272             }
273              
274             =item reverse()
275              
276             The mr value from termcap - turn on reverse text often. these last
277             two default to whatever is available.
278              
279             =cut
280              
281             sub reverse
282             {
283 0     0 1   my $this = shift;
284 0           $this->term()->Tputs( 'mr', 1, *STDOUT );
285 0           return $this;
286             }
287              
288             =item clrscr()
289              
290             Clear the screen and home cursor
291              
292             =cut
293              
294             sub clrscr
295             {
296 0     0 1   my $this = shift;
297 0           $this->term()->Tputs( 'cl', 1, *STDOUT );
298 0           $this->{'rc'} = [ 0, 0 ];
299 0           return $this;
300             }
301              
302             =item clreol()
303              
304             Clear to the end of the line - cursor doesn't move
305              
306             =cut
307              
308             sub clreol
309             {
310 0     0 1   my $this = shift;
311 0 0         if ( exists( $this->term()->{'_ce'} ) )
312             {
313 0           $this->term()->Tputs( 'ce', 1, *STDOUT );
314             }
315 0           return $this;
316             }
317              
318             =item clreos()
319              
320             Clear to end of screen - right and down, cursor doesn't move.
321              
322             =cut
323              
324             sub clreos
325             {
326 0     0 1   my $this = shift;
327 0 0         if ( exists( $this->term()->{'_cd'} ) )
328             {
329 0           $this->term()->Tputs( 'cd', 1, *STDOUT );
330             }
331 0           return $this;
332             }
333              
334             =item il()
335              
336             Insert blank line before line cursor is on, moving lower lines down.
337              
338             =cut
339              
340             sub il
341             {
342 0     0 1   my $this = shift;
343 0           $this->term()->Tputs( 'al', 1, *STDOUT );
344 0           return $this;
345             }
346              
347             =item dl()
348              
349             Delete line cursor is on, moving lower lines up.
350              
351             =cut
352              
353             sub dl
354             {
355 0     0 1   my $this = shift;
356 0           $this->term()->Tputs( 'dl', 1, *STDOUT );
357 0           return $this;
358             }
359              
360             =item ic_exists()
361              
362             Insert character option is available.
363              
364             =cut
365              
366 0 0   0 1   sub ic_exists { ( exists( $_[0]->term()->{'ic'} ) ? 1 : 0 ); }
367              
368             {
369 1     1   7 no warnings 'once';
  1         1  
  1         152  
370             *exists_ic = \&ic_exists;
371             }
372              
373             =item ic()
374              
375             Insert character at current position move rest to the right.
376              
377             =cut
378              
379             sub ic
380             {
381 0     0 1   my $this = shift;
382 0           $this->term()->Tputs( 'ic', 1, *STDOUT );
383 0           $this;
384             }
385              
386             =item dc_exists()
387              
388             Delete char option exists and is available.
389              
390             =cut
391              
392 0 0   0 1   sub dc_exists { ( exists( $_[0]->term()->{'dc'} ) ? 1 : 0 ); }
393              
394             {
395 1     1   6 no warnings 'once';
  1         1  
  1         1127  
396             *exists_dc = \&dc_exists;
397             }
398              
399             =item dc()
400              
401             Delete character at current position moving rest to the left.
402              
403             =cut
404              
405             sub dc
406             {
407 0     0 1   my $this = shift;
408 0           $this->term()->Tputs( 'dc', 1, *STDOUT );
409 0           return $this;
410             }
411              
412             =back
413              
414             The following are the I/O functions. They provide standard useful
415             single character reading values. getch returns either a single char or
416             the name of a function key when a key is pressed. The only exception is
417             when you hit a character that is the start of a function key sequence.
418             In this case getch keeps waiting for the next char to see if it is fn key.
419             Generally this is the escape key, and why you need to hit esc twice.
420             To get a stright char, just use the regular 'gets' perl function. You
421             will need to echo it yourself if you want.
422              
423             =over 4
424              
425             =item puts(str)
426              
427             Prints $s and returns the screen object. Used to do things like
428             C<$scr->at(10,0)->puts("Hi!")->at(0,0);>. You can just use
429             print if you want.
430              
431             =cut
432              
433 0     0 1   sub puts { print $_[1]; return $_[0]; }
  0            
434              
435             =item getch()
436              
437             Returns just a char in raw mode. Function keys are returned as their
438             capability names, e.g. the up key would return "ku". See the
439             C function for what a lot of the names are. This will wait
440             for next char if in a possible fn key string, so you would need to type
441             'esc' 'esc' most likely to get out of getch, since 'esc' is usually the
442             leading char for function keys. You can use perl's getc, to go 'underneath'
443             getch if you want. See the table in Screen::get_fn_keys() for more
444             information.
445              
446             =cut
447              
448             sub getch
449             {
450 0     0 1   my $this = shift;
451 0           my ( $c, $fn_flag ) = ( '', 0 );
452 0           my $partial_fn_str = '';
453              
454 0 0         if ( $this->{IN} ) { $c = chop( $this->{IN} ); }
  0            
455 0           else { $c = getc(STDIN); }
456              
457 0           $partial_fn_str = $c;
458 0           while ( exists( $this->{KEYS}{$partial_fn_str} ) )
459             { # in a possible function key sequence
460 0           $fn_flag = 1;
461 0 0         if ( $this->{KEYS}{$partial_fn_str} ) # key found
462             {
463 0           $c = $this->{KEYS}{$partial_fn_str};
464 0           $partial_fn_str = '';
465 0           last;
466             }
467             else # wait for another key to see if were in FN yet
468             {
469 0 0         if ( $this->{IN} ) { $partial_fn_str .= chop( $this->{IN} ); }
  0            
470 0           else { $partial_fn_str .= getc(); }
471             }
472             }
473 0 0 0       if ($fn_flag) # seemed like a fn key
    0          
474             {
475 0 0         if ($partial_fn_str) # oops not a fn key
476             {
477 0 0         if ( $partial_fn_str eq "\e\e" ) # take care of funny ESC case
478             {
479 0           $c = "\e";
480 0           $partial_fn_str = "";
481             }
482             else # buffer up the received chars
483             {
484 0           $this->{IN} = CORE::reverse($partial_fn_str) . $this->{IN};
485 0           $c = chop( $this->{IN} );
486 0 0 0       $this->puts($c) if ( $this->{ECHO} && ( $c ne "\e" ) );
487             }
488             }
489              
490             # if fn_key then never echo so do nothing here
491             }
492 0           elsif ( $this->{ECHO} && ( $c ne "\e" ) ) { $this->puts($c); } # regular key
493 0           return $c;
494             }
495              
496             =item def_key('name','input string')
497              
498             Lets you define your own function key sequence. 'name' is what will be
499             returned by getch. 'input string' is what the fn key sends literally. This
500             will override any prev definitions of the input. A whole bunch of defaults
501             are defined for xterms rxvt's, etc. in the get_fn_keys function.
502              
503             =cut
504              
505             sub def_key
506             {
507 0     0 1   my ( $this, $fn, $str ) = @_;
508              
509 0 0         $this->{KEYS}{$str} = $fn if ( $str ne '' );
510 0           while ( $str ne '' )
511             {
512 0           chop($str);
513 0 0         $this->{KEYS}{$str} = '' if ( $str ne '' );
514             }
515 0           return $this;
516             }
517              
518             =item key_pressed([sec])
519              
520             Returns true if there is a character waiting. You can pass an option time in
521             seconds to wait.
522              
523             =cut
524              
525             sub key_pressed
526             {
527 0     0 1   my ( $this, $seconds ) = @_;
528 0           my $readfields = '';
529 0           my $ready = 0;
530              
531 0 0         $seconds = 0 if ( !defined $seconds );
532 0           vec( $readfields, fileno(STDIN), 1 ) = 1; # set up to check STDIN
533 0           eval { $ready = select( $readfields, undef, undef, $seconds ); };
  0            
534 0           return $ready;
535             }
536              
537             =item echo()
538              
539             Tells getch to echo the input to the screen. (the default.)
540              
541             =cut
542              
543 0     0 1   sub echo { my $this = shift; $this->{ECHO} = 1; return $this; }
  0            
  0            
544              
545             =item noecho()
546              
547             Tells getch NOT to echo input to the screen.
548              
549             =cut
550              
551 0     0 1   sub noecho { my $this = shift; $this->{ECHO} = 0; return $this; }
  0            
  0            
552              
553             =item flush_input()
554              
555             Clears input buffer and removes any incoming chars.
556              
557             =cut
558              
559             sub flush_input
560             {
561 0     0 1   my $this = shift;
562 0           $this->{IN} = '';
563 0           while ( $this->key_pressed() ) { getc(); }
  0            
564 0           return $this;
565             }
566              
567             =item stuff_input(str)
568              
569             Lets you stuff chars into the input buffer to be read like keystrokes.
570             This is only the C method buffer, the underlying getc stuff
571             is not touched.
572              
573             =cut
574              
575             sub stuff_input
576             {
577 0     0 1   my ( $this, $str ) = @_;
578 0           $this->{IN} = CORE::reverse($str) . $this->{IN};
579 0           return $this;
580             }
581              
582             # internal functions
583              
584             # This function sets up the arrow keys from { ku kd kr kl }
585             # and the function keys from {k0 .. k9} with labels from { l0 .. l9}
586             # (if they exist of course.)
587             # This is all encoded in a funny way -- as a hash with the
588             # characters as keys - check the code. It makes checking fn keys easy.
589              
590             sub get_fn_keys
591             {
592 0     0 0   my $this = shift;
593 0           my $term = $this->term();
594 0           my @keys = qw/ke kh ku kd kl kr k0 k1 k2 k3 k4 k5 k6 k7 k8 k9/;
595 0           my ( $fn, $ufn, $lfn );
596              
597             # throw in some defaults (xterm & rxvt arrows);
598 0           $this->def_key( "ku", "\e[A" );
599 0           $this->def_key( "kd", "\e[B" );
600 0           $this->def_key( "kr", "\e[C" );
601 0           $this->def_key( "kl", "\e[D" );
602              
603             # PC keyboard fn keys for xterm (some of them)
604 0           $this->def_key( "k1", "\e[11~" );
605 0           $this->def_key( "k2", "\e[12~" );
606 0           $this->def_key( "k3", "\e[13~" );
607 0           $this->def_key( "k4", "\e[14~" );
608 0           $this->def_key( "k5", "\e[15~" );
609 0           $this->def_key( "k6", "\e[17~" );
610 0           $this->def_key( "k7", "\e[18~" );
611 0           $this->def_key( "k8", "\e[19~" );
612 0           $this->def_key( "k9", "\e[20~" );
613 0           $this->def_key( "k10", "\e[21~" );
614 0           $this->def_key( "k11", "\e[23~" );
615 0           $this->def_key( "k12", "\e[24~" );
616              
617 0           $this->def_key( "ins", "\e[2~" );
618 0           $this->def_key( "del", "\e[3~" );
619              
620 0           $this->def_key( "home", "\e[H" ); # mult defs are no problem
621 0           $this->def_key( "home", "\eO" ); # these are some I have found
622 0           $this->def_key( "end", "\eOw" );
623 0           $this->def_key( "end", "\eOe" );
624 0           $this->def_key( "pgup", "\e[5~" );
625 0           $this->def_key( "pgdn", "\e[6~" );
626              
627             # try to get anything useful out of termcap
628             # (not too accurate in many cases
629              
630 0           foreach $fn (@keys)
631             {
632 0           $ufn = '_' . $fn;
633 0           $lfn = $ufn;
634 0           $lfn =~ s/_k/_l/;
635              
636 0 0         if ( exists $term->{$ufn} )
637             {
638 0 0 0       if ( ( exists $term->{$lfn} ) && ( $term->{$lfn} ) )
639             {
640 0           $fn = substr( $lfn, 1 );
641             }
642 0           $this->def_key( $fn, $term->{$ufn} );
643             }
644             }
645 0           return $this;
646             }
647              
648             1;
649              
650             __END__