File Coverage

blib/lib/Term/WinConsole.pm
Criterion Covered Total %
statement 179 615 29.1
branch 32 182 17.5
condition 2 99 2.0
subroutine 21 50 42.0
pod 39 43 90.7
total 273 989 27.6


line stmt bran cond sub pod time code
1             package Term::WinConsole;
2              
3             require 5.005_62;
4 1     1   21854 use strict;
  1         4  
  1         40  
5 1     1   5 use warnings;
  1         3  
  1         152  
6              
7             require Exporter;
8              
9             our @ISA = qw(Exporter);
10              
11             our %EXPORT_TAGS = ( 'all' => [ qw() ] );
12              
13             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
14              
15             our @EXPORT = qw();
16              
17             our $VERSION = '0.02';
18              
19             $|++;
20              
21             ############################################################
22             ## Declarations
23             #
24              
25 1     1   5 use vars qw( @ISA $AUTOLOAD %sequences %attcode $MAXWIN $USECOLOR %borderChr);
  1         7  
  1         4178  
26              
27             $MAXWIN = 20;
28             $USECOLOR = 1;
29              
30             # %borderChr contains the different chars used to draw borders.
31             %borderChr = ( 'ul' => '/', 'ur' => '\\', 'bl' => '\\', 'br' => '/', 'hrz' => '=', 'vrt' => '|' );
32              
33             # %sequence is used to convert a command into its corresponding ANSI sequence. During runtime the '?'
34             # is replaced by a parameter.
35             #
36             # Idea taken from Term::ANSIScreen
37             #
38             %sequences = (
39             'black' => '30m', 'on_black' => '40m',
40             'red' => '31m', 'on_red' => '41m',
41             'green' => '32m', 'on_green' => '42m',
42             'yellow' => '33m', 'on_yellow' => '43m',
43             'blue' => '34m', 'on_blue' => '44m',
44             'magenta' => '35m', 'on_magenta' => '45m',
45             'cyan' => '36m', 'on_cyan' => '46m',
46             'white' => '37m', 'on_white' => '47m',
47             'clear' => '0m', 'reset' => '0m',
48             'light' => '1m', 'dark' => '2m',
49             'underline' => '4m', 'underscore' => '4m',
50             'blink' => '5m',
51             'reverse' => '7m',
52             'hidden' => '8m',
53             'up' => '?A', 'down' => '?B',
54             'right' => '?C', 'left' => '?D',
55             'savepos' => 's' , 'loadpos' => 'u',
56             'saveatt' => '7' , 'loadatt' => '8',
57             'cls' => '2J', 'cll' => '2K',
58             'locate' => '?;?H', 'setmode' => '?h',
59             'font0' => '(', 'font1' => ')',
60             'wrapon' => '7h', 'wrapoff' => '7l',
61             'fullreset' => 'c',
62             );
63              
64             # attcode is used to convert a color attribute into a bit rank thus combined attributes
65             # can give a unique integer
66             #
67             # format is :
68             # attrib back fore
69             # chrbld rgb rgb
70            
71             %attcode = (
72             'on_red' => 32,
73             'on_green' => 16,
74             'on_yellow' => 48,
75             'on_blue' => 8,
76             'on_magenta'=> 40,
77             'on_cyan' => 24,
78             'on_white' => 56,
79             'black' => 0,
80             'red' => 4,
81             'green' => 2,
82             'yellow' => 6,
83             'blue' => 1,
84             'magenta' => 5,
85             'cyan' => 3,
86             'white' => 7,
87             'light' => 64,
88             'dark' => 128,
89             'underline' => 256,
90             'blink' => 512,
91             'reverse' => 1024,
92             'hidden' => 2048,
93             'clear' => 4096,
94             );
95              
96             ############################################################
97             ## Constructor
98             #
99              
100             sub new {
101 1     1 0 67 my $class = shift;
102 1         3 my $title = shift;
103 1         1 my $colsize = shift;
104 1         1 my $rowsize = shift;
105 1         2 my $border = shift;
106 1         2 my $cr = shift;
107 1         2 my $pattern = shift;
108 1 50       2 my $buffered = shift; if (!defined($buffered)) {$buffered=1}
  1         4  
  0         0  
109              
110 1         9 my %object = (
111             "winActive" => 0, #active miniwin index
112             "miniwin" => undef, #miniwin structures reference
113             "useBuffer" => $buffered,#flag
114             "lsAtt" => undef, #last sent attribute
115             "frontTxt" => undef, #production text buffer reference
116             "frontAtt" => undef, #production attributes buffer reference
117             "zBuffer" => undef, #Z-Buffer reference
118             "winStack" => undef, #miniwins stack reference
119             "stats" => 0, #stats
120             );
121              
122             # Init the miniwin structure with a fullscreen miniwin (miniwin #0)
123 1         6 &setWindow(\%object,$title, 1, 1, $colsize, $rowsize, $border, $cr, $pattern);
124              
125             # Init buffers
126 1         4 my (@frontTxt,@frontAtt,@zBuffer);
127              
128 1         3 for (1..$rowsize){
129 25         58 push @frontTxt , $pattern x $colsize;
130 25         228 push @frontAtt , [(0) x $colsize];
131 25         243 push @zBuffer , [(0) x $colsize];
132             }
133              
134 1         8 $object{'frontAtt'} = \@frontAtt;
135 1         4 $object{'frontTxt'} = \@frontTxt;
136 1         2 $object{'zBuffer'} = \@zBuffer;
137              
138 1         8 return bless \%object , $class;
139             }
140              
141             #################################################
142             ## AUTOLOAD implementation
143             #
144              
145             # using %sequence keys as function name return/print the corresponding ANSI sequence
146             # Idea taken from Term::ANSIScreen
147              
148             sub AUTOLOAD {
149 0     0   0 my $sub;
150 0         0 ($sub = $AUTOLOAD) =~ s/^.*:://;
151 0 0       0 if (my $seq = $sequences{$sub}) {
152 0         0 shift(@_);
153 0 0       0 $seq =~ s/\?/defined($_[0]) ? shift(@_) : 1/eg;
  0         0  
154 0 0       0 return (defined wantarray) ? "\e[$seq" : print("\e[$seq");
155             }else{
156 0         0 die "Undefined subroutine &$AUTOLOAD called";
157             }
158             }
159              
160             sub DESTROY
161 0     0   0 {}
162              
163             ###########################################################################################
164             ## MISC FUNCTIONS
165             #
166              
167             # the stat counter is incremented each time a character is printed by a display function
168             # ( currently refresh and flush).
169             # this feature is given for optimisation purposes.
170              
171             sub resetStat
172             {
173 0     0 1 0 my ($self) = @_;
174 0         0 $self->{'stats'} = 0;
175             }
176              
177             sub getStat
178             {
179 0     0 1 0 my ($self) = @_;
180 0         0 return $self->{'stats'};
181             }
182              
183             ###########################################################################################
184             ## WINDOW STACK HANDLING
185             #
186              
187             # this stack is used to store windows depth level
188              
189             # add $idx on top of the stack
190             sub stackAdd
191             {
192 3     3 1 28 my ($self,$idx) = @_;
193 3         3 unshift(@{$self->{'winStack'}},$idx);
  3         8  
194             }
195              
196             # supress $idx from the stack
197             sub stackDel
198             {
199 1     1 1 1 my ($self,$idx) = @_;
200 1         1 my ($offset);
201            
202 1         10 $offset = stackFind($self,$idx);
203 1 50       4 if (defined $offset){
204 1         1 splice(@{$self->{'winStack'}},$offset,1);
  1         3  
205 1         3 return 1;
206             }else{
207 0         0 return undef;
208             }
209             }
210              
211             # move $idx on top of the stack
212             sub stackFocus
213             {
214 1     1 1 1 my ($self,$idx) = @_;
215 1 50       3 if (!defined(stackDel($self,$idx))){
216 0         0 stackAdd($self,$idx);
217 0         0 return 1;
218             }else{
219 1         4 return undef;
220             }
221             }
222              
223             # return the offset of $idx in the stack
224             sub stackFind
225             {
226 2     2 1 3 my ($self,$idx) = @_;
227 2         1 my ($offset);
228            
229 2         3 $offset = 0;
230 2         2 for (@{$self->{'winStack'}}){
  2         5  
231 4 100       8 if ($_==$idx){
232 2         5 return $offset;
233             }
234 2         42 $offset++
235             }
236 0         0 return undef;
237             }
238              
239             # return the index of the miniwin with $title as title
240             sub indexFind
241             {
242 0     0 1 0 my ($self,$title) = @_;
243 0         0 my ($idx);
244            
245 0         0 $idx = 0;
246 0         0 for (@{$self->{'miniwin'}}){
  0         0  
247 0 0       0 if ($_->{'title'} eq $title){
248 0         0 return $idx;
249             }
250 0         0 $idx++
251             }
252 0         0 return undef;
253             }
254              
255             # useful aliases
256             sub showWindow
257             {
258 1     1 1 2 my ($self,$idx) = @_;
259 1         2 my ($offset);
260              
261 1 50       3 if (!defined $idx){
262 1         2 $idx = $self->{'winActive'};
263             }
264            
265 1         5 $offset = stackFind($self,$idx);
266 1 50       4 if (!defined $offset){
267 0         0 &stackAdd($self,$idx);
268             }else{
269 1         4 &stackFocus($self,$idx);
270             }
271             }
272              
273             sub hideWindow
274             {
275 0     0 1 0 my ($self,$idx) = @_;
276 0         0 my ($offset);
277              
278 0 0       0 if (!defined $idx){
279 0         0 $idx = $self->{'winActive'};
280             }
281            
282 0         0 $offset = stackFind($self,$idx);
283 0 0       0 if (defined $offset){
284 0         0 &stackDel($self,$idx);
285             }
286             }
287              
288             ###########################################################################################
289             ## DISPLAY HANDLING
290             #
291              
292             # every drawing operations are made in a local miniwin buffer. Each time a display request is done,
293             # all the miniwins'buffer are melt into a unique 'backbuffer'.
294             #
295             # there are two methods to start a display :
296             # - refresh : simply overwrite the production buffer with the back buffer and display it.
297             # (it needs less computations but send more data to the terminal)
298             # - flush : makes a diff between the production and the back buffer and display only differences
299             # (more computations needed but less data sent to the terminal)
300              
301             sub makeFullBackBuffer
302             {
303 0     0 1 0 my ($self) = @_;
304 0         0 my (@backAtt,@backTxt,$screen,$current, $destCol, $destRow, $active );
305              
306 0         0 $screen = $self->{'miniwin'}[0];
307 0         0 for (1..$screen->{'height'}){
308 0         0 push @backTxt , ' ' x $screen->{'width'};
309 0         0 push @backAtt , [(0) x $screen->{'width'}];
310             }
311              
312 0         0 for $active (reverse @{$self->{'winStack'}})
  0         0  
313             {
314 0         0 $current=$self->{'miniwin'}[$active];
315 0         0 for my $row (1..$current->{'height'})
316             {
317 0         0 for my $col (1..$current->{'width'})
318             {
319 0         0 $destCol = $current->{'colTop'}+$col-2;
320 0 0       0 if ($destCol>$screen->{'width'}) { $destCol = $screen->{'width'} };
  0         0  
321 0         0 $destRow = $current->{'rowTop'}+$row-2;
322 0 0       0 if ($destRow>$screen->{'height'}) { $destRow = $screen->{'height'} };
  0         0  
323 0         0 $backAtt[$destRow][$destCol]=$current->{'backAtt'}[$row-1][$col-1];
324 0         0 $self->{'zBuffer'}[$destRow][$destCol]=$active;
325 0         0 substr($backTxt[$destRow],$destCol,1)=substr($current->{'backTxt'}[$row-1],$col-1,1);
326             }
327             }
328             }
329 0         0 return \(@backAtt,@backTxt);
330             }
331              
332              
333             sub flush
334             {
335 0     0 1 0 my ($self, $win) = @_;
336 0         0 my ($current, $curAtt, $col, $row, $gathering, $backAtt, $backTxt, $chunk, @chunk, $result );
337              
338 0 0       0 if (defined $win){
339 0 0       0 if ($win<$#{$self->{'miniwin'}}){
  0         0  
340 0         0 $win = $self->{'winActive'};
341             }else{
342 0         0 $win = 0;
343             }
344             }else{
345 0         0 $win = 0;
346             }
347              
348 0         0 ($backAtt,$backTxt) = &makeFullBackBuffer($self);
349 0         0 $current = $self->{'miniwin'}[$win];
350 0         0 $curAtt = $self->{'lsAtt'};
351 0         0 $gathering = 0;
352            
353 0         0 for my $row ($current->{'rowTop'}..($current->{'rowTop'}+$current->{'height'}-1))
354             {
355 0         0 for my $col ($current->{'colTop'}..($current->{'colTop'}+$current->{'width'}-1))
356             {
357 0 0       0 if (!$gathering){
358 0 0 0     0 if (($self->{'frontAtt'}[$row-1][$col-1]!=${$backAtt}[$row-1][$col-1])
  0   0     0  
  0   0     0  
359             ||(substr($self->{'frontTxt'}[$row-1],$col-1,1) ne substr(@{$backTxt}[$row-1],$col-1,1))
360             &&(($self->{'zBuffer'}[$row-1][$col-1]==$win)||($win==0))
361             ){
362 0         0 $chunk = {
363             "col" => $col,
364             "row" => $row,
365 0         0 "att" => ${$backAtt}[$row-1][$col-1],
366 0         0 "txt" => substr(@{$backTxt}[$row-1],$col-1,1),
367             };
368 0         0 $curAtt = ${$backAtt}[$row-1][$col-1];
  0         0  
369 0         0 $self->{'frontAtt'}[$row-1][$col-1]=${$backAtt}[$row-1][$col-1];
  0         0  
370 0         0 substr($self->{'frontTxt'}[$row-1],$col-1,1) = substr(@{$backTxt}[$row-1],$col-1,1);
  0         0  
371 0         0 $gathering = 1;
372             }
373             }else{
374 0 0 0     0 if ( ($curAtt == ${$backAtt}[$row-1][$col-1])
  0   0     0  
      0        
      0        
375             &&( ($self->{'frontAtt'}[$row-1][$col-1]!=${$backAtt}[$row-1][$col-1])
376             ||
377             (substr($self->{'frontTxt'}[$row-1],$col-1,1) ne substr(@{$backTxt}[$row-1],$col-1,1))
378             )
379             &&
380             (($self->{'zBuffer'}[$row-1][$col-1]!=$win)&&($win!=0))
381             ){
382 0         0 ${$chunk}{'txt'}.=substr(@{$backTxt}[$row-1],$col-1,1);
  0         0  
  0         0  
383 0         0 $self->{'frontAtt'}[$row-1][$col-1]=${$backAtt}[$row-1][$col-1];
  0         0  
384 0         0 substr($self->{'frontTxt'}[$row-1],$col-1,1) = substr(@{$backTxt}[$row-1],$col-1,1);
  0         0  
385             }else{
386 0         0 push @chunk, $chunk;
387 0         0 undef($chunk);
388 0         0 $gathering = 0;
389             # at this point we're ending the current chunk
390             # but the current char is maybe the beginning of a new chunk
391             # so we redo the current loop
392 0         0 redo;
393             }
394             }
395             }
396 0 0       0 if ($gathering){
397 0         0 push @chunk, $chunk;
398 0         0 undef($chunk);
399 0         0 $gathering = 0;
400             }
401             }
402            
403 0         0 $result = "\e[s";
404              
405 0         0 for(@chunk){
406              
407 0 0       0 if (!$_) { next; }
  0         0  
408              
409 0         0 $result.="\e[0m";
410 0         0 $self->{'stats'}+= length("\e[0m");
411              
412 0         0 $result.="\e[".${$_}{'row'}.";".${$_}{'col'}."H";
  0         0  
  0         0  
413 0         0 $self->{'stats'}+= length("\e[".${$_}{'row'}.";".${$_}{'col'}."H");
  0         0  
  0         0  
414              
415 0         0 $result.=&att2seq(${$_}{'att'});
  0         0  
416 0         0 $self->{'stats'}+= length(&att2seq(${$_}{'att'}));
  0         0  
417              
418 0         0 $result.=${$_}{'txt'};
  0         0  
419 0         0 $self->{'stats'}+= length(${$_}{'txt'});
  0         0  
420              
421 0         0 $self->{'lsAtt'} = ${$_}{'att'};
  0         0  
422             }
423 0         0 $result.="\e[u";
424 0         0 undef (@chunk);
425 0 0       0 return (defined wantarray) ? $result : print $result;
426             }
427              
428              
429             sub fullDump
430             {
431 0     0 1 0 my ($self, $win) = @_;
432 0         0 my ($current, $lastAtt, $backAtt, $backTxt, $result);
433              
434 0 0       0 if (defined $win){
435 0 0       0 if ($win<$#{$self->{'miniwin'}}){
  0         0  
436 0         0 $win = $self->{'winActive'};
437             }else{
438 0         0 $win = 0;
439             }
440             }else{
441 0         0 $win = 0;
442             }
443              
444 0         0 $lastAtt=0;
445              
446 0         0 $current = $self->{'miniwin'}[$win];
447              
448 0         0 ($backAtt,$backTxt) = &makeFullBackBuffer($self);
449              
450 0         0 @{$self->{'frontAtt'}} = @{$backAtt};
  0         0  
  0         0  
451 0         0 @{$self->{'frontTxt'}} = @{$backTxt};
  0         0  
  0         0  
452              
453 0         0 $result = "\e[s";
454 0         0 for my $row ($current->{'rowTop'}..($current->{'rowTop'}+$current->{'height'}-1))
455             {
456 0         0 $result.="\e[".$row.";".$current->{'colTop'}."H";
457 0         0 $self->{'stats'}+= length("\e[".$row.";".$current->{'colTop'}."H");
458              
459 0         0 for my $col ($current->{'colTop'}..($current->{'colTop'}+$current->{'width'}-1))
460             {
461 0 0 0     0 if (
462             ($self->{'zBuffer'}[$row-1][$col-1]==$win)
463             ||
464             ($win==0)
465             )
466             {
467 0 0       0 if ($self->{'frontAtt'}[$row-1][$col-1]!=$lastAtt){
468 0         0 $result.="\e[0m";
469 0         0 $self->{'stats'}+= length("\e[0m");
470            
471 0         0 $result.=&att2seq($self->{'frontAtt'}[$row-1][$col-1]);
472 0         0 $self->{'stats'}+= length(&att2seq($self->{'frontAtt'}[$row-1][$col-1]));
473            
474 0         0 $lastAtt = $self->{'frontAtt'}[$row-1][$col-1];
475 0         0 $self->{'lsAtt'} = $lastAtt;
476             }
477            
478 0         0 $result.=substr($self->{'frontTxt'}[$row-1],$col-1,1);
479 0         0 $self->{'stats'}+= length(substr($self->{'frontTxt'}[$row-1],$col-1,1));
480             }
481             }
482             }
483 0         0 $result.="\e[u";
484              
485 0 0       0 return (defined wantarray) ? $result : print $result;
486             }
487              
488             ###########################################################################################
489             ## CURSOR HANDLING
490             #
491              
492             sub gotoCR
493             {
494 7     7 1 12 my ($self, $column, $row) = @_;
495 7         6 my ($current, $modif);
496              
497 7         13 $current = $self->{'miniwin'}[$self->{'winActive'}];
498              
499 7 50       16 if ($current->{'border'}) {
500 7         8 $modif = 2;
501             }else{
502 0         0 $modif = 1;
503             }
504              
505             # return on bad values
506 7 50 33     26 return undef if ((!$column)||(!$row));
507              
508             # -1 means the last column/row
509 7 50       15 if ($column<0){
510 0         0 $column = $current->{'width'}-$modif+$column+1;
511             }
512 7 50       11 if ($row<0){
513 0         0 $row = $current->{'height'}-$modif+$row+1;
514             }
515            
516 7 50       17 if ($row<=($current->{'height'}-$modif)){
517 7         8 $current->{'cursRow'}=$row;
518             }else{
519 0 0       0 if ($current->{'carrRet'}){
520 0         0 &scrollWin($self,'up',1);
521 0         0 $current->{'cursRow'}=$current->{'height'}-$modif;
522 0         0 $current->{'curscol'}=1;
523             }else{
524 0         0 return undef;
525             }
526             }
527              
528 7 50       13 if ($column<=($current->{'width'}-$modif)){
529 7         8 $current->{'cursCol'}=$column;
530             }else{
531 0 0       0 if ($current->{'carrRet'}){
532 0         0 &doCR($self);
533 0         0 ($column, $row) = &getCR;
534             }else{
535 0         0 return undef;
536             }
537             }
538 7         29 return 1;
539             }
540              
541             sub getCR
542             {
543 1     1 1 1 my ($self) = @_;
544 1         1 my $current;
545              
546 1         3 $current = $self->{'miniwin'}[$self->{'winActive'}];
547              
548 1         3 return $current->{'cursCol'},$current->{'cursRow'};
549             }
550              
551             sub getAbsCR
552             {
553 0     0 1 0 my ($self) = @_;
554 0         0 my ($current, $col, $row);
555              
556 0         0 $current = $self->{'miniwin'}[$self->{'winActive'}];
557 0         0 ($col, $row) = &getCR;
558            
559 0 0       0 if ($current->{'border'}){
560 0         0 $col++;
561 0         0 $row++;
562             }
563 0         0 return ($col + $current->{'colTop'} - 1),($row + $current->{'rowTop'} - 1 );
564             }
565              
566             sub doCR
567             {
568 1     1 1 2 my ($self) = @_;
569 1         2 my ($col,$row);
570              
571 1         2 ($col, $row) = &getCR;
572 1         2 $col = 1;
573 1         1 $row++;
574 1         2 &gotoCR($self, $col, $row);
575             }
576              
577             sub readyCurs
578             {
579 0     0 1 0 my ($self, $col, $row) = @_;
580 0         0 my ($current);
581              
582 0         0 $current = $self->{'miniwin'}[$self->{'winActive'}];
583              
584 0 0       0 if (!$col){
585 0         0 $col = $current->{'cursCol'};
586             }
587              
588 0 0       0 if (!$row){
589 0         0 $row = $current->{'cursRow'};
590             }
591              
592             # correction des coordonnées par gotoCR/getAbsCR
593 0         0 &gotoCR($self,$col,$row);
594 0         0 ($col,$row) = &getAbsCR;
595 0         0 print "\e[0m";
596 0         0 print &att2seq($self->{'frontAtt'}[$row-1][$col-1]);
597 0         0 $self->{'lsAtt'} = $self->{'frontAtt'}[$row-1][$col-1];
598 0         0 print("\e[".$row.";".$col."H");
599             }
600              
601             ###########################################################################################
602             ## COLOR HANDLING
603             #
604              
605             sub setWinColor
606             {
607 2     2 1 4 my ($self, $color) = @_;
608 2         7 return $self->{'miniwin'}[$self->{'winActive'}]->{'winCol'} = &codeAtt($color);
609            
610             }
611              
612             sub setCurrentColor
613             {
614 0     0 1 0 my ($self, $color) = @_;
615 0         0 return $self->{'miniwin'}[$self->{'winActive'}]->{'curCol'} = &codeAtt($color);
616            
617             }
618              
619             sub resetColor
620             {
621 2     2 1 4 my ($self) = @_;
622 2         12 return $self->{'miniwin'}[$self->{'winActive'}]->{'curCol'} = $self->{'miniwin'}[$self->{'winActive'}]->{'winCol'};
623             }
624              
625             ###########################################################################################
626             ## ATTRIBUTES STRING CODING/UNCODING
627             #
628             sub codeAtt
629             {
630 1     1   10 no warnings;
  1         2  
  1         106  
631 8     8 0 13 my ($attStr) = @_;
632 8         9 my ($code);
633            
634 8         8 $code=0;
635 8         24 foreach(split ' ',$attStr){
636 18         33 $code |= $attcode{$_};
637             }
638 8         61 return $code;
639 1     1   5 use warnings;
  1         2  
  1         17119  
640             }
641              
642             sub uncodeAtt
643             {
644 0     0 0 0 my ($code) = @_;
645 0         0 my ($attStr, $key, $val, $idx);
646              
647             #decimal to binary conversion
648 0         0 $code = unpack("B32", pack("N",$code));
649              
650 0 0       0 if ($USECOLOR){
651             #extracting background color
652 0         0 $val = unpack("N", pack("B32", substr("0"x32 .substr($code, -3, 3),-32)));
653 0 0       0 foreach $key (keys %attcode){ if ($attcode{$key}==$val){ $attStr.=" $key"; }}
  0         0  
  0         0  
654            
655             #extracting foreground color
656 0         0 $val = unpack("N", pack("B32", substr("0"x32 .substr($code, -6, 3),-32)));
657 0 0       0 foreach $key (keys %attcode){ if ($attcode{$key}==$val){ $attStr.=" on_$key"; }}
  0         0  
  0         0  
658             }
659              
660             #extracting attributes
661 0         0 $val = substr($code, -13, 7);
662 0         0 for $idx (1..length($val)){
663 0 0       0 if (substr($val,-$idx,1)){
664 0         0 foreach (keys %attcode){
665 0 0       0 if ($attcode{$_}==(2**($idx+5))){
666 0         0 $attStr.=" $_";
667             }
668             }
669             }
670             }
671 0         0 return $attStr;
672             }
673              
674             sub att2seq
675             {
676 0     0 0 0 my ($code) = @_;
677 0         0 my ($attStr, $sequence);
678            
679 0         0 $attStr= &uncodeAtt($code);
680 0         0 foreach(split ' ',$attStr){
681 0         0 $sequence .= "\e[".$sequences{$_};
682             }
683 0         0 return $sequence;
684             }
685              
686              
687             ###########################################################################################
688             ## MINIWINS HANDLING
689             #
690              
691             sub deleteWin
692             {
693 0     0 1 0 my ($self, $winId) = @_;
694 0 0 0     0 if (($winId>0)&&($winId<$#{$self->{'miniwin'}})){
  0         0  
695 0         0 $self->{'miniwin'}[$winId]= undef;
696 0 0       0 if ($self->{'winActive'}==$winId){
697 0         0 $self->{'winActive'}=0;
698             }
699 0         0 return 1;
700             }else{
701 0         0 return undef;
702             }
703             }
704              
705             sub setWinBorder
706             {
707 0     0 1 0 my ($self, $flag) = @_;
708 0         0 return $self->{'miniwin'}[$self->{'winActive'}]->{'border'} = $flag;
709             }
710              
711             sub setWinCarret
712             {
713 0     0 1 0 my ($self, $flag) = @_;
714 0         0 return $self->{'miniwin'}[$self->{'winActive'}]->{'carrRet'} = $flag;
715             }
716              
717             sub setActiveWin
718             {
719 3     3 1 6 my ($self, $winId) = @_;
720 3 50 33     3 if ( ($winId<=$#{$self->{'miniwin'}}) && (defined ($self->{'miniwin'}[$winId])))
  3         23  
721             {
722 3         10 $self->{'winActive'} = $winId;
723             }else{
724 0         0 return undef;
725             }
726             }
727              
728             sub setWinPattern
729             {
730 0     0 1 0 my ($self, $pattern) = @_;
731 0         0 return $self->{'miniwin'}[$self->{'winActive'}]->{'pattern'} = $pattern;
732             }
733              
734             sub setWinTitle
735             {
736 1     1 1 3 my ($self, $title) = @_;
737 1         5 return $self->{'miniwin'}[$self->{'winActive'}]->{'title'} = $title;
738             }
739              
740             sub setWinCol
741             {
742 0     0 1 0 my ($self, $col) = @_;
743            
744 0 0 0     0 if (($col>0)
745             &&($col<=($self->{'miniwin'}[0]->{'width'}-$self->{'miniwin'}[$self->{'winActive'}]->{'width'}+1))
746             ){
747 0         0 return $self->{'miniwin'}[$self->{'winActive'}]->{'colTop'} = $col;
748             }else{
749 0         0 return undef;
750             }
751             }
752              
753             sub setWinRow
754             {
755 0     0 1 0 my ($self, $row) = @_;
756            
757 0 0 0     0 if (($row>0)
758             &&($row<=($self->{'miniwin'}[0]->{'height'}-$self->{'miniwin'}[$self->{'winActive'}]->{'height'}+1))
759             ){
760 0         0 return $self->{'miniwin'}[$self->{'winActive'}]->{'rowTop'} = $row;
761             }else{
762 0         0 return undef;
763             }
764             }
765              
766             sub setWinWidth
767             {
768 0     0 1 0 my ($self, $width) = @_;
769 0         0 my ($current);
770              
771 0         0 $current = $self->{'miniwin'}[$self->{'winActive'}];
772 0 0 0     0 if (($width>0)
773             &&($width<=length($current->{'backTxt'}[0]))
774             ){
775 0         0 return $self->{'miniwin'}[$self->{'winActive'}]->{'width'} = $width;
776             }else{
777 0         0 return undef;
778             }
779             }
780              
781             sub setWinHeight
782             {
783 0     0 1 0 my ($self, $height) = @_;
784 0         0 my ($current);
785            
786 0         0 $current = $self->{'miniwin'}[$self->{'winActive'}];
787 0 0 0     0 if (($height>0)
  0         0  
788             &&($height<=$#{$current->{'backTxt'}}+1)
789             ){
790 0         0 return $self->{'miniwin'}[$self->{'winActive'}]->{'height'} = $height;
791             }else{
792 0         0 return undef;
793             }
794             }
795              
796             sub setWindow
797             {
798 3     3 1 8 my ($self, $title, $colTop, $rowTop, $width, $height, $border, $cr, $pattern) = @_;
799 3         4 my ($screen, $newwin, @backTxt, @backAtt);
800              
801 3 50       9 if (!defined($title)) {$title=''}
  0         0  
802 3 50       7 if (!defined($colTop)) {$colTop=1}
  0         0  
803 3 50       6 if (!defined($rowTop)) {$rowTop=1}
  0         0  
804 3 50       7 if (!defined($width)) {$width=80}
  0         0  
805 3 50       6 if (!defined($height)) {$height=25}
  0         0  
806 3 50       7 if (!defined($border)) {$border=0}
  0         0  
807 3 50       8 if (!defined($cr)) {$cr=1}
  0         0  
808 3 50       7 if (!defined($pattern)) {$pattern=' '}
  0         0  
809              
810 3 50       4 if ($#{$self->{'miniwin'}}<$MAXWIN)
  3         10  
811             {
812 3 100       4 if (defined(@{$self->{'miniwin'}}))
  3         10  
813             {
814 2         4 $screen = $self->{'miniwin'}[0];
815 2 50       7 if ($colTop>=$screen->{'width'}){
816 0         0 $colTop = 1;
817             }
818 2 50       5 if ($rowTop>=$screen->{'height'}){
819 0         0 $rowTop = 1;
820             }
821 2 50       7 if (($colTop+$width-1)>$screen->{'width'}){
822 0         0 $width = $screen->{'width'} - $colTop +1 ;
823             }
824 2 50       5 if (($height+$rowTop-1)>$screen->{'height'}){
825 0         0 $height = $screen->{'height'} - $rowTop +1 ;
826             }
827             }
828              
829 3         8 for (1..$height){
830 36         63 push @backTxt , $pattern x $width;
831 36         265 push @backAtt , [(0) x $width];
832             }
833              
834             $newwin = {
835 3         10 "title" => $title,
836             "colTop" => $colTop,
837             "rowTop" => $rowTop,
838             "width" => $width,
839             "height" => $height,
840             "border" => $border,
841             "cursRow" => 1,
842             "cursCol" => 1,
843             "carrRet" => $cr,
844             "winCol" => &codeAtt("white on_black"),
845             "curCol" => &codeAtt("white on_black"),
846             "pattern" => $pattern,
847             "backTxt" => \@backTxt,
848             "backAtt" => \@backAtt
849             };
850              
851 3 100       5 if (!defined(@{$self->{'miniwin'}}))
  3         19  
852             {
853 1         3 $self->{'miniwin'} = [$newwin];
854             }else{
855 2         3 push @{$self->{'miniwin'}} , $newwin;
  2         3  
856             }
857            
858 3         5 &stackAdd($self, $#{$self->{'miniwin'}});
  3         9  
859            
860 3         4 return $#{$self->{'miniwin'}};
  3         15  
861             }else{
862 0         0 return undef;
863             }
864             }
865              
866             ###########################################################################################
867             ## DISPLAY HANDLING
868             #
869             sub home
870             {
871 4     4 1 8 my ($self) = @_;
872 4         5 my ($current, $row, $col);
873              
874 4         8 $current = $self->{'miniwin'}[$self->{'winActive'}];
875              
876 4         10 for my $row (1..$current->{'height'})
877             {
878 41         61 for my $col (1..$current->{'width'})
879             {
880 2850         3530 $current->{'backAtt'}[$row-1][$col-1] = $current->{'winCol'};
881 2850         3991 substr($current->{'backTxt'}[$row-1],$col-1,1)= $current->{'pattern'};
882             }
883             }
884              
885 4 50       14 if ($current->{'border'})
886             {
887             # 201 upperleft corner symbol
888 4         5 $col = 1;
889 4         4 $row = 1;
890 4         10 $current->{'backAtt'}[$row-1][$col-1] = $current->{'winCol'};
891 4         8 substr($current->{'backTxt'}[$row-1],$col-1,1)= $borderChr{'ul'};
892              
893             # 187 upperright corner symbol
894 4         6 $col = $current->{'width'};
895 4         4 $row = 1;
896 4         10 $current->{'backAtt'}[$row-1][$col-1] = $current->{'winCol'};
897 4         18 substr($current->{'backTxt'}[$row-1],$col-1,1)= $borderChr{'ur'};
898              
899             # 188 bottomright corner symbol
900 4         6 $col = $current->{'width'};
901 4         5 $row = $current->{'height'};
902 4         8 $current->{'backAtt'}[$row-1][$col-1] = $current->{'winCol'};
903 4         8 substr($current->{'backTxt'}[$row-1],$col-1,1)= $borderChr{'br'};
904              
905             # 200 bottomleft corner symbol
906 4         6 $col = 1;
907 4         5 $row = $current->{'height'};
908 4         7 $current->{'backAtt'}[$row-1][$col-1] = $current->{'winCol'};
909 4         8 substr($current->{'backTxt'}[$row-1],$col-1,1)= $borderChr{'bl'};
910              
911             # 205 horizontal symbol
912 4         10 for (2..$current->{'width'}-1)
913             {
914 232         208 $col = $_;
915 232         193 $row = 1;
916 232         332 $current->{'backAtt'}[$row-1][$col-1] = $current->{'winCol'};
917 232         338 substr($current->{'backTxt'}[$row-1],$col-1,1)= $borderChr{'hrz'};
918 232         237 $row = $current->{'height'};
919 232         305 $current->{'backAtt'}[$row-1][$col-1] = $current->{'winCol'};
920 232         377 substr($current->{'backTxt'}[$row-1],$col-1,1)= $borderChr{'hrz'};
921             }
922              
923             # title
924 4         7 $col = 3;
925 4         6 $row = 1;
926 4         13 my $title = $current->{'title'}.($borderChr{'hrz'} x ($current->{'width'}-1));
927 4         24 substr($current->{'backTxt'}[$row-1],$col-1,$current->{'width'}-4)= substr ($title,0,$current->{'width'}-4);
928            
929              
930             # 186 vertical symbol
931 4         8 for (2..$current->{'height'}-1)
932             {
933 33         35 $row = $_;
934 33         31 $col = 1;
935 33         55 $current->{'backAtt'}[$row-1][$col-1] = $current->{'winCol'};
936 33         56 substr($current->{'backTxt'}[$row-1],$col-1,1)= $borderChr{'vrt'};
937 33         41 $col = $current->{'width'};
938 33         626 $current->{'backAtt'}[$row-1][$col-1] = $current->{'winCol'};
939 33         71 substr($current->{'backTxt'}[$row-1],$col-1,1)= $borderChr{'vrt'};
940             }
941             }
942 4         12 &gotoCR($self,1,1);
943             }
944              
945             sub deleteCh
946             {
947 0     0 1   my ($self, $col, $row) = @_;
948 0           my ($current);
949              
950 0           $current = $self->{'miniwin'}[$self->{'winActive'}];
951 0           return &printCh($self, $current->{'pattern'}, $col, $row);
952             }
953              
954             sub printCh
955             {
956 0     0 1   my ($self, $char , $col, $row) = @_;
957 0           my ($current, $oldCol, $oldRow, $modif);
958              
959 0           $current = $self->{'miniwin'}[$self->{'winActive'}];
960              
961 0 0         if ($current->{'border'}) {
962 0           $modif = 1;
963             }else{
964 0           $modif = 0;
965             }
966              
967 0 0         if ($char ne "\n"){
968 0           ($oldCol, $oldRow) = &getCR;
969 0 0         if (&gotoCR($self, $col, $row)){
970 0           substr ($current->{'backTxt'}[$row-1+$modif],$col-1+$modif,1) = $char;
971 0           $current->{'backAtt'}[$row-1+$modif][$col-1+$modif] = $current->{'curCol'};
972 0           &gotoCR($self,$oldCol,$oldRow);
973 0           return 1;
974             }else{
975 0           return undef;
976             }
977             }else{
978 0           &doCR($self);
979 0           return 1;
980             }
981             }
982              
983             sub streamCh
984             {
985 0     0 1   my ($self, $char) = @_;
986 0           my ($current, $col, $row, $modif);
987              
988 0           $current = $self->{'miniwin'}[$self->{'winActive'}];
989              
990 0 0         if ($current->{'border'}) {
991 0           $modif = 1;
992             }else{
993 0           $modif = 0;
994             }
995              
996 0 0         if ($char ne "\n"){
997 0           ($col,$row) = &getCR;
998 0           substr ($current->{'backTxt'}[$row-1+$modif],$col-1+$modif,1) = $char;
999 0           $current->{'backAtt'}[$row-1+$modif][$col-1+$modif] = $current->{'curCol'};
1000 0           ($col,$row) = &getCR;
1001 0           return &gotoCR($self,$col+1,$row);;
1002             }else{
1003 0           &doCR($self);
1004 0           return 1;
1005             }
1006              
1007             }
1008              
1009             sub printSt
1010             {
1011 0     0 1   my ($self, $chars) = @_;
1012 0           for (1..length($chars))
1013             {
1014 0           &streamCh($self,substr($chars,$_-1,1));
1015             }
1016             }
1017              
1018             sub centerSt
1019             {
1020 0     0 1   my ($self, $chars) = @_;
1021 0           my ($current, $modif, $pos, $slice);
1022              
1023 0           $current = $self->{'miniwin'}[$self->{'winActive'}];
1024              
1025 0 0         if ($current->{'border'}) {
1026 0           $modif = 2;
1027             }
1028            
1029 0           foreach(split "\n",$chars){
1030 0           $slice = $_;
1031 0 0         if (($current->{'width'}-$modif)>length($slice)){
1032 0           $pos = ($current->{'width'}-length($slice))/2;
1033             }else{
1034 0           $pos=1;
1035             }
1036 0           &gotoCR($self,$pos,$current->{'cursRow'});
1037 0           for (1..length($slice))
1038             {
1039 0           &streamCh($self,substr($slice,$_-1,1));
1040             }
1041 0 0         if ($chars=~/$slice/){
1042 0           &doCR($self);
1043             }
1044             }
1045            
1046             }
1047              
1048             ###########################################################################################
1049             #### SCROLL HANDLING
1050             #
1051              
1052             sub scrollWin # up, down, left right
1053             {
1054 0     0 1   my ($self, $dir, $dist) = @_;
1055 0           my ($current, @saveText, @saveAtt, $modif, %src, %clip, $colDest, $rowDest);
1056              
1057 0           $current = $self->{'miniwin'}[$self->{'winActive'}];
1058 0 0         if (!$dist){
1059 0           $dist=1;
1060             }
1061              
1062 0 0         if ($current->{'border'}){
1063 0           $modif = 1;
1064             }
1065              
1066 0 0 0       if (($dir eq 'down')||($dir eq 'd'))
1067             {
1068 0           $colDest = 1+$modif;
1069 0           $rowDest = 1+$modif+$dist;
1070 0           $src{'left'} = 1+$modif;
1071 0           $src{'top'} = 1+$modif;
1072 0           $src{'height'} = $current->{'height'}-$modif*2-$dist;
1073 0           $src{'width'} = $current->{'width'}-$modif*2;
1074 0           $clip{'left'} = 1+$modif;
1075 0           $clip{'top'} = 1+$modif;
1076 0           $clip{'width'} = $current->{'width'}-$modif*2;
1077 0           $clip{'height'}= $dist;
1078             }
1079              
1080 0 0 0       if (($dir eq 'right')||($dir eq 'r'))
1081             {
1082 0           $colDest = 1+$modif+$dist;
1083 0           $rowDest = 1+$modif;
1084 0           $src{'left'} = 1+$modif;
1085 0           $src{'top'} = 1+$modif;
1086 0           $src{'height'} = $current->{'height'}-$modif*2;
1087 0           $src{'width'} = $current->{'width'}-$dist-$modif*2;
1088 0           $clip{'left'} = 1+$modif;
1089 0           $clip{'top'} = 1+$modif;
1090 0           $clip{'width'} = $dist;
1091 0           $clip{'height'}= $current->{'height'}-$modif*2;
1092             }
1093              
1094 0 0 0       if (($dir eq 'up')||($dir eq 'u'))
1095             {
1096 0           $colDest = 1+$modif;
1097 0           $rowDest = 1+$modif;
1098 0           $src{'left'} = 1+$modif;
1099 0           $src{'top'} = 1+$modif+$dist;
1100 0           $src{'height'} = $current->{'height'}-$modif*2-$dist;
1101 0           $src{'width'} = $current->{'width'}-$modif*2;
1102 0           $clip{'left'} = 1+$modif;
1103 0           $clip{'top'} = 1+$current->{'height'}-$dist-$modif;
1104 0           $clip{'width'} = $current->{'width'}-$modif*2;
1105 0           $clip{'height'}= $dist;
1106             }
1107              
1108 0 0 0       if (($dir eq 'left')||($dir eq 'l'))
1109             {
1110 0           $colDest = 1+$modif;
1111 0           $rowDest = 1+$modif;
1112 0           $src{'left'} = 1+$modif+$dist;
1113 0           $src{'top'} = 1+$modif;
1114 0           $src{'height'} = $current->{'height'}-$modif*2;
1115 0           $src{'width'} = $current->{'width'}-$dist-$modif*2;
1116 0           $clip{'left'} = 1+$current->{'width'}-$dist-$modif;
1117 0           $clip{'top'} = 1+$modif;
1118 0           $clip{'width'} = $dist;
1119 0           $clip{'height'}= $current->{'height'}-$modif*2;
1120             }
1121              
1122             #Backup buffers creation
1123 0           for (0..$src{'height'}-1){
1124 0           push @saveAtt , [(0) x $src{'width'}];
1125 0           push @saveText , '' x $src{'width'};
1126             }
1127              
1128             #Save the data
1129 0           for my $row (0..$src{'height'}-1)
1130             {
1131 0           for my $col (0..$src{'width'}-1)
1132             {
1133 0           $saveAtt[$row][$col]=$current->{'backAtt'}[$src{'top'}+$row-1][$src{'left'}+$col-1];
1134 0           substr($saveText[$row],$col,1) = substr($current->{'backTxt'}[$src{'top'}+$row-1],$src{'left'}+$col-1,1);
1135             }
1136             }
1137              
1138 0           for my $row (0..$src{'height'}-1)
1139             {
1140 0           for my $col (0..$src{'width'}-1)
1141             {
1142 0           $current->{'backAtt'}[$rowDest+$row-1][$colDest+$col-1]=$saveAtt[$row][$col];
1143 0           substr($current->{'backTxt'}[$rowDest+$row-1],$colDest+$col-1,1) = substr($saveText[$row],$col,1);
1144             }
1145             }
1146 0           for my $row (0..$clip{'height'}-1)
1147             {
1148 0           for my $col (0..$clip{'width'}-1)
1149             {
1150 0           $current->{'backAtt'}[$clip{'top'}+$row-1][$clip{'left'}+$col-1]= $current->{'winCol'};
1151 0           substr($current->{'backTxt'}[$clip{'top'}+$row-1],$clip{'left'}+$col-1,1) = $current->{'pattern'};
1152             }
1153             }
1154             }
1155              
1156             sub pasteWin # up, down, left right
1157             {
1158 0     0 1   my ($self, $dir, $txtref, $attref) = @_;
1159 0           my ($current, @saveText, @saveAtt, $modif, %src, %clip, $colDest, $rowDest, $dist);
1160              
1161 0           $current = $self->{'miniwin'}[$self->{'winActive'}];
1162              
1163 0           @saveText = @{$txtref};
  0            
1164 0 0         if (defined $attref){
1165 0           @saveAtt = @{$attref};
  0            
1166             }
1167              
1168 0 0         if ($current->{'border'}){
1169 0           $modif = 1;
1170             }
1171              
1172 0 0 0       if (($dir eq 'up')||($dir eq 'u')||($dir eq 'down')||($dir eq 'd'))
      0        
      0        
1173             {
1174             # test hrz validity
1175 0 0 0       if ((length $saveText[0]<($current->{'width'}-$modif*2))
  0   0        
1176             or ( (defined $attref)
1177 0           and ($#{@{$attref}[0]}<($current->{'width'}-1-$modif*2))
1178             )
1179             ){
1180 0           return undef;
1181             }else{
1182 0           $dist=$#saveText+1;
1183             }
1184             }
1185              
1186 0 0 0       if (($dir eq 'left')||($dir eq 'l')||($dir eq 'right')||($dir eq 'r'))
      0        
      0        
1187             {
1188             # test vrt validity
1189 0 0 0       if (($#saveText<($current->{'height'}-1-$modif*2))
  0   0        
1190             or ( (defined $attref)
1191 0           and ($#{@{$attref}}<($current->{'height'}-1-$modif*2))
1192             )
1193             ){
1194 0           return undef;
1195             }else{
1196 0           $dist=length $saveText[0];
1197             }
1198             }
1199              
1200 0 0 0       if (($dir eq 'up')||($dir eq 'u'))
1201             {
1202 0           $clip{'left'} = 1+$modif;
1203 0           $clip{'top'} = 1+$modif;
1204 0           $clip{'width'} = $current->{'width'}-$modif*2;
1205 0           $clip{'height'}= $dist;
1206             }
1207              
1208 0 0 0       if (($dir eq 'left')||($dir eq 'l'))
1209             {
1210 0           $clip{'left'} = 1+$modif;
1211 0           $clip{'top'} = 1+$modif;
1212 0           $clip{'width'} = $dist;
1213 0           $clip{'height'}= $current->{'height'}-$modif*2;
1214             }
1215              
1216 0 0 0       if (($dir eq 'down')||($dir eq 'd'))
1217             {
1218 0           $clip{'left'} = 1+$modif;
1219 0           $clip{'top'} = 1+$current->{'height'}-$dist-$modif;
1220 0           $clip{'width'} = $current->{'width'}-$modif*2;
1221 0           $clip{'height'}= $dist;
1222             }
1223              
1224 0 0 0       if (($dir eq 'right')||($dir eq 'r'))
1225             {
1226 0           $clip{'left'} = 1+$current->{'width'}-$dist-$modif;
1227 0           $clip{'top'} = 1+$modif;
1228 0           $clip{'width'} = $dist;
1229 0           $clip{'height'}= $current->{'height'}-$modif*2;
1230             }
1231              
1232 0           for my $row (0..$clip{'height'}-1)
1233             {
1234 0           for my $col (0..$clip{'width'}-1)
1235             {
1236 0 0         if (@saveAtt){
1237 0           $current->{'backAtt'}[$clip{'top'}+$row-1][$clip{'left'}+$col-1]= $saveAtt[$row][$col];
1238             }else{
1239 0           $current->{'backAtt'}[$clip{'top'}+$row-1][$clip{'left'}+$col-1] = $current->{'winCol'};
1240             }
1241 0           substr($current->{'backTxt'}[$clip{'top'}+$row-1],$clip{'left'}+$col-1,1) = substr($saveText[$row],$col,1);
1242             #print " $col : $row '".substr($current->{'backTxt'}[$clip{'top'}+$row-1],$clip{'left'}+$col-1,1)."' on ".$saveAtt[$row][$col]."\n";
1243             }
1244             }
1245            
1246 0           return $dist;
1247             }
1248              
1249              
1250             1;
1251             __END__