File Coverage

blib/lib/Term/ANSIScreen.pm
Criterion Covered Total %
statement 89 118 75.4
branch 32 62 51.6
condition 10 15 66.6
subroutine 13 21 61.9
pod 4 12 33.3
total 148 228 64.9


line stmt bran cond sub pod time code
1 2     2   1388 use 5.005;
  2         7  
  2         120  
2             package Term::ANSIScreen;
3             $Term::ANSIScreen::VERSION = '1.50';
4              
5 2     2   10 use strict;
  2         4  
  2         97  
6 2         322 use vars qw/@ISA @EXPORT %EXPORT_TAGS $VERSION $AUTOLOAD
7 2     2   10 %attributes %attributes_r %sequences $AUTORESET $EACHLINE/;
  2         4  
8 2     2   11 use Exporter;
  2         4  
  2         722  
9              
10             =head1 NAME
11              
12             Term::ANSIScreen - Terminal control using ANSI escape sequences
13              
14             =head1 SYNOPSIS
15              
16             # qw/:color/ is exported by default, i.e. color() & colored()
17              
18             use Term::ANSIScreen qw/:color :cursor :screen :keyboard/;
19              
20             print setmode(1), setkey('a','b');
21             print "40x25 mode now, with 'a' mapped to 'b'.";
22             ; resetkey; setmode 3; cls;
23              
24             locate 1, 1; print "@ This is (1,1)", savepos;
25             print locate(24,60), "@ This is (24,60)"; loadpos;
26             print down(2), clline, "@ This is (3,15)\n";
27              
28             setscroll 1, 20;
29              
30             color 'black on white'; clline;
31             print "This line is black on white.\n";
32             print color 'reset'; print "This text is normal.\n";
33              
34             print colored ("This text is bold blue.\n", 'bold blue');
35             print "This text is normal.\n";
36             print colored ['bold blue'], "This text is bold blue.\n";
37             print "This text is normal.\n";
38              
39             use Term::ANSIScreen qw/:constants/; # constants mode
40             print BLUE ON GREEN . "Blue on green.\n";
41              
42             $Term::ANSIScreen::AUTORESET = 1;
43             print BOLD GREEN . ON_BLUE "Bold green on blue.", CLEAR;
44             print "\nThis text is normal.\n";
45              
46             # Win32::Console emulation mode
47             # this returns a Win32::Console object on a Win32 platform
48             my $console = Term::ANSIScreen->new;
49             $console->Cls; # also works on non-Win32 platform
50              
51             =cut
52              
53             # -----------------------
54             # Internal data structure
55             # -----------------------
56              
57             %attributes = (
58             'clear' => 0, 'reset' => 0,
59             'bold' => 1, 'dark' => 2,
60             'underline' => 4, 'underscore' => 4,
61             'blink' => 5, 'reverse' => 7,
62             'concealed' => 8,
63              
64             'black' => 30, 'on_black' => 40,
65             'red' => 31, 'on_red' => 41,
66             'green' => 32, 'on_green' => 42,
67             'yellow' => 33, 'on_yellow' => 43,
68             'blue' => 34, 'on_blue' => 44,
69             'magenta' => 35, 'on_magenta' => 45,
70             'cyan' => 36, 'on_cyan' => 46,
71             'white' => 37, 'on_white' => 47,
72             );
73              
74             %sequences = (
75             'up' => '?A', 'down' => '?B',
76             'right' => '?C', 'left' => '?D',
77             'savepos' => 's', 'loadpos' => 'u',
78             'cls' => '2J', 'clline' => 'K',
79             'cldown' => '0J', 'clup' => '1J',
80             'locate' => '?;?H', 'setmode' => '?h',
81             'wrapon' => '7h', 'wrapoff' => '7l',
82             'setscroll' => '?;?r',
83             );
84              
85             my %mapped;
86              
87             # ----------------
88             # Exporter section
89             # ----------------
90              
91             @ISA = qw/Exporter/;
92             %EXPORT_TAGS = (
93             'color' => [qw/color colored uncolor/],
94             'cursor' => [qw/locate up down right left savepos loadpos/],
95             'screen' => [qw/cls clline cldown clup setmode wrapon wrapoff setscroll/],
96             'keyboard' => [qw/setkey resetkey/],
97             'constants' => [map {uc($_)} keys(%attributes), 'ON'],
98             );
99              
100             $EXPORT_TAGS{all} = [map {@{$_}} values (%EXPORT_TAGS)];
101              
102             @EXPORT = qw(color colored);
103             Exporter::export_ok_tags (keys(%EXPORT_TAGS));
104              
105             sub new {
106 1     1 0 19 my $class = shift;
107              
108 1 50 33     9 if ($^O eq 'MSWin32' and eval { require Win32::Console } ) {
  0         0  
109 0         0 return Win32::Console->new(@_);
110             }
111              
112 2     2   12 no strict 'refs';
  2         3  
  2         3171  
113 1 50       3 unless ($main::FG_WHITE) {
114 1         10 foreach my $color (grep { $attributes{$_} >= 30 } keys %attributes) {
  25         40  
115 16         27 my $name = "FG_\U$color";
116 16         30 $name =~ s/^FG_ON_/BG_/;
117 16         26 ${"main::$name"} = color($color);
  16         97  
118 16         39 $name =~ s/_/_LIGHT/;
119 16         28 ${"main::$name"} = color('bold', $color);
  16         75  
120             }
121 1         3 $main::FG_LIGHTWHITE = $main::FG_WHITE;
122 1         2 $main::FG_BROWN = $main::FG_YELLOW;
123 1         2 $main::FG_YELLOW = $main::FG_LIGHTYELLOW;
124 1         10 $main::FG_WHITE = color('clear');
125             }
126            
127 1         12 return bless([ @_ ], $class);
128             }
129              
130             sub Attr {
131 0     0 0 0 shift;
132 0         0 print STDERR @_;
133             }
134              
135             sub Cls {
136 0     0 0 0 print STDERR cls();
137             }
138              
139             sub Cursor {
140 0     0 0 0 shift;
141 0         0 print STDERR locate($_[1]+1, $_[0]+1);
142             }
143              
144             sub Write {
145 0     0 0 0 shift;
146 0         0 print STDERR @_;
147             }
148              
149 0     0 0 0 sub Display {
150             }
151              
152              
153             # --------------
154             # Implementation
155             # --------------
156              
157             sub AUTOLOAD {
158 13     13   191 my $enable_colors = !defined $ENV{ANSI_COLORS_DISABLED};
159 13         16 my $sub = $AUTOLOAD;
160 13         59 $sub =~ s/^.*:://;
161              
162 13 100 33     106 if (my $seq = $sequences{$sub}) {
    50          
163 4 50       9 return '' unless $enable_colors;
164              
165 4 50       7 $seq =~ s/\?/defined($_[0]) ? shift(@_) : 1/eg;
  2         6  
166 4 50       16 return((defined wantarray) ? "\e[$seq"
167             : print("\e[$seq"));
168             }
169             elsif (defined(my $attr = $attributes{lc($sub)}) and $sub =~ /^[A-Z_]+$/) {
170 9         23 my $out = "@_";
171 9 100       21 if ($enable_colors) {
172 8         23 $out = "\e[${attr}m" . $out;
173 8 100 66     50 $out .= "\e[0m" if ($AUTORESET and @_ and $out !~ /\e\[0m$/s);
      100        
174             }
175 9 50       43 return((defined wantarray) ? $out
176             : print($out));
177             }
178             else {
179 0         0 require Carp;
180 0         0 Carp::croak("Undefined subroutine &$AUTOLOAD called");
181             }
182             }
183              
184             # ------------------------------------------------
185             # Convert foreground constants to background ones,
186             # for sequences like (XXX ON YYY "text")
187             # ------------------------------------------------
188              
189             sub ON {
190 1 50   1 0 5 return '' if defined $ENV{ANSI_COLORS_DISABLED};
191              
192 1         4 my $out = "@_";
193 1         19 $out =~ s/^\e\[3(\d)m/\e\[4$1m/;
194 1         6 return $out;
195             }
196              
197             # ---------------------------------------
198             # Color subroutines, from Term::ANSIColor
199             # ---------------------------------------
200              
201             sub color {
202 40 100   40 1 152 return '' if defined $ENV{ANSI_COLORS_DISABLED};
203              
204 38         46 my @codes = map { split } @_;
  57         128  
205 38         43 my $attribute;
206              
207 2     2   19 no warnings 'uninitialized';
  2         4  
  2         1717  
208 38         89 while (my $code = lc(shift(@codes))) {
209 58 50       95 $code .= '_' . shift(@codes) if ($code eq 'on');
210              
211 58 50       125 if (defined $attributes{$code}) {
212 58         181 $attribute .= $attributes{$code} . ';';
213             }
214             else {
215 0         0 warn "Invalid attribute name $code";
216             }
217             }
218              
219 38 50       81 if ($attribute) {
220 38         44 chop $attribute;
221 38 50       105 return (defined wantarray) ? "\e[${attribute}m"
222             : print("\e[${attribute}m");
223             }
224             }
225              
226             sub colored {
227 5     5 1 44 my $output;
228 1         4 my ($string, $attr) = (ref $_[0])
229 5 100       18 ? (join('', @_[1..$#_]), color(@{$_[0]}))
230             : (+shift, color(@_));
231              
232 5 100       17 return $string if defined $ENV{ANSI_COLORS_DISABLED};
233              
234 4 100       9 if (defined $EACHLINE) {
235 14 100 100     76 $output = join '',
236 3         59 map { ($_ && $_ ne $EACHLINE) ? $attr . $_ . "\e[0m" : $_ }
237             split (/(\Q$EACHLINE\E)/, $string);
238             } else {
239 1         4 $output = "$attr$string\e[0m";
240             }
241              
242 4 50       22 return (defined wantarray) ? $output
243             : print($output);
244             }
245              
246             sub uncolor {
247 1     1 0 13 my (@nums, @result);
248              
249 1         3 foreach my $seq (@_) {
250 4         7 my $escape = $seq;
251 4         10 $escape =~ s/^\e\[//;
252 4         10 $escape =~ s/m$//;
253 4 50       20 unless ($escape =~ /^((?:\d+;)*\d*)$/) {
254 0         0 require Carp;
255 0         0 Carp::croak("Bad escape sequence $seq");
256             }
257 4         14 push (@nums, split (/;/, $1));
258             }
259              
260 1         4 _init_attributes_r();
261              
262 1         3 foreach my $num (@nums) {
263 3         7 $num += 0; # Strip leading zeroes
264 3         4 my $name = $attributes_r{$num};
265 3 50       15 if (!defined $name) {
266 0         0 require Carp;
267 0         0 Carp::croak("No name for escape sequence $num" );
268             }
269 3         7 push (@result, $name);
270             }
271              
272 1         5 return @result;
273             }
274              
275             sub _init_attributes_r {
276 1 50   1   4 return if %attributes_r;
277              
278             # Reverse lookup. Alphabetically first name for a sequence is preferred.
279 1         21 for (reverse sort keys %attributes) {
280 25         84 $attributes_r{$attributes{$_}} = $_;
281             }
282             }
283              
284             sub setkey {
285 0     0 1   my ($key, $mapto) = @_;
286              
287 0 0         if ($key eq $mapto) {
288 0 0         delete $mapped{$key} if exists $mapped{$key};
289             }
290             else {
291 0           $mapped{$key} = 1;
292             }
293              
294 0 0         $key = ord($key) unless ($key =~ /^\d+;\d+$/);
295 0 0         $mapto = qq("$mapto") unless ($mapto =~ /^\d+;\d+$/);
296              
297 0 0         return (defined wantarray) ? "\e[$key;${mapto}p"
298             : print("\e[$key;${mapto}p");
299             }
300              
301             sub resetkey {
302 0     0 1   my $output;
303              
304 0 0         foreach my $key (@_ ? @_ : keys(%mapped)) {
305 0           $output .= setkey($key, $key);
306             }
307              
308 0 0         return (defined wantarray) ? $output
309             : print($output);
310             }
311              
312             sub DESTROY {
313 0     0     return;
314             }
315              
316             1;
317              
318             __END__