File Coverage

blib/lib/Ragnetto/Console.pm
Criterion Covered Total %
statement 51 91 56.0
branch 12 48 25.0
condition 3 24 12.5
subroutine 16 22 72.7
pod 13 13 100.0
total 95 198 47.9


line stmt bran cond sub pod time code
1             # ------------------------------------------------------------------------------
2             # Ragnetto-Console
3             # ------------------------------------------------------------------------------
4             # File : console.pm
5             # Description : ANSI console control utility
6             # Language : Perl
7             # ------------------------------------------------------------------------------
8             # Project : ragnetto-console
9             # Author : Gabriele Secci
10             # Editor : Ragnetto(R) Software
11             # E-Mail : ragnettosoftware@gmail.com
12             # ------------------------------------------------------------------------------
13             # Notes
14             # - This module provides terminal manipulation functions.
15             # - It is part of the Ragnetto module suite.
16             # ------------------------------------------------------------------------------
17             # Copyright (C) 2026 - All Rights Reserved
18             # ------------------------------------------------------------------------------
19              
20             package Ragnetto::Console;
21             our $VERSION = '0.01';
22              
23 2     2   304133 use strict;
  2         5  
  2         73  
24 2     2   10 use warnings;
  2         7  
  2         125  
25 2     2   1077 use utf8;
  2         587  
  2         12  
26 2     2   97 use Exporter 'import';
  2         6  
  2         63  
27 2     2   1026 use IO::Handle;
  2         13942  
  2         354  
28              
29             our (@EXPORT_OK, %EXPORT_TAGS);
30              
31             BEGIN {
32 2     2   13 my @color_names = qw(BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE GRAY LIGHT_RED LIGHT_GREEN LIGHT_YELLOW LIGHT_BLUE LIGHT_MAGENTA LIGHT_CYAN LIGHT_WHITE);
33 2         4 my @state_names = qw(OFF ON);
34 2         5 my @shape_names = qw(BLOCK_BLINK BLOCK_STEADY UNDER_BLINK UNDER_STEADY BAR_BLINK BAR_STEADY);
35 2         7 my @func_names = qw(clear forecolor backcolor cursor caret position write reset title width height getkey putkey);
36              
37 2         13 @EXPORT_OK = (@color_names, @state_names, @shape_names, @func_names);
38 2         128 %EXPORT_TAGS = (
39             colors => \@color_names,
40             states => \@state_names,
41             shapes => \@shape_names,
42             all => \@EXPORT_OK,
43             );
44             }
45              
46             # ------------------------------------------------------------------------------
47             # CORE CONSTANTS
48             # ------------------------------------------------------------------------------
49              
50             use constant {
51 2         621 BLACK => 0, RED => 1, GREEN => 2, YELLOW => 3, BLUE => 4, MAGENTA => 5, CYAN => 6, WHITE => 7,
52             GRAY => 8, LIGHT_RED => 9, LIGHT_GREEN => 10, LIGHT_YELLOW => 11, LIGHT_BLUE => 12, LIGHT_MAGENTA => 13, LIGHT_CYAN => 14, LIGHT_WHITE => 15,
53             OFF => 0, ON => 1,
54             BLOCK_BLINK => 1, BLOCK_STEADY => 2, UNDER_BLINK => 3, UNDER_STEADY => 4, BAR_BLINK => 5, BAR_STEADY => 6,
55 2     2   19 };
  2         3  
56              
57             my %COLORS;
58             BEGIN {
59 2     2   2532 %COLORS = (
60             BLACK => 0, RED => 1, GREEN => 2, YELLOW => 3, BLUE => 4, MAGENTA => 5, CYAN => 6, WHITE => 7,
61             GRAY => 8, LIGHT_RED => 9, LIGHT_GREEN => 10, LIGHT_YELLOW => 11, LIGHT_BLUE => 12, LIGHT_MAGENTA => 13, LIGHT_CYAN => 14, LIGHT_WHITE => 15
62             );
63             }
64              
65             STDOUT->autoflush(1);
66              
67             # ------------------------------------------------------------------------------
68             # HELPER FUNCTION
69             # ------------------------------------------------------------------------------
70              
71             sub _get_color_val {
72 2     2   4 my ($val) = @_;
73              
74 2 100       15 return $val if $val =~ /^\d+$/;
75 1 50       10 return $COLORS{uc($val)} if defined $val;
76 0         0 return undef;
77             }
78              
79             # ------------------------------------------------------------------------------
80             # CORE FUNCTION
81             # ------------------------------------------------------------------------------
82              
83             # Clears the screen completely and resets the cursor
84             sub clear {
85 1     1 1 157523 print "\e[2J\e[H";
86             }
87              
88             # Set the background color (0-15)
89             sub backcolor {
90 1     1 1 648 my ($color) = @_;
91 1         3 my $c = _get_color_val($color);
92              
93 1 50       4 return unless defined $c;
94              
95 1 50       8 printf("\e[%dm", ($c < 8 ? 40 + $c : 100 + ($c - 8)));
96             }
97              
98             # Set the text color (0-15)
99             sub forecolor {
100 1     1 1 701 my ($color) = @_;
101 1         4 my $c = _get_color_val($color);
102              
103 1 50       6 return unless defined $c;
104              
105 1 50       13 printf("\e[%dm", ($c < 8 ? 30 + $c : 90 + ($c - 8)));
106             }
107              
108             # Manages the visibility of the cursor
109             sub cursor {
110 1     1 1 730 my ($state) = @_;
111 1   50     5 my $s = uc($state // '');
112              
113 1 50 33     13 if ($s eq '1' || $s eq 'ON' || $s eq 'TRUE') {
      33        
114 0         0 print "\e[?25h";
115             }
116             else {
117 1         4 print "\e[?25l";
118             }
119             }
120              
121             # Change the shape of the cursor (Caret)
122             sub caret {
123 0     0 1 0 my ($shape) = @_;
124 0         0 my %shapes = (BLOCK_BLINK => 1, BLOCK_STEADY => 2, UNDER_BLINK => 3, UNDER_STEADY => 4, BAR_BLINK => 5, BAR_STEADY => 6);
125 0 0       0 my $s = $shape =~ /^\d+$/ ? $shape : $shapes{uc($shape)};
126              
127 0 0 0     0 print "\e[$s q" if $s && $s >= 1 && $s <= 6;
      0        
128             }
129              
130             # Move the cursor to a specific position
131             sub position {
132 1     1 1 722 my ($x, $y) = @_;
133              
134 1         13 printf("\e[%d;%dH", $y, $x);
135             }
136              
137             # Writes text with attributes and positioning
138             sub write {
139 0     0 1 0 my ($text, $fore, $back, $x, $y) = @_;
140 0         0 my $out = "";
141              
142 0 0 0     0 $out .= sprintf("\e[%d;%dH", $y, $x) if defined $x && defined $y;
143              
144 0 0       0 if (defined $back) {
145 0         0 my $c = _get_color_val($back);
146              
147 0 0       0 $out .= sprintf("\e[%dm", ($c < 8 ? 40 + $c : 100 + ($c - 8))) if defined $c;
    0          
148             }
149              
150 0 0       0 if (defined $fore) {
151 0         0 my $c = _get_color_val($fore);
152              
153 0 0       0 $out .= sprintf("\e[%dm", ($c < 8 ? 30 + $c : 90 + ($c - 8))) if defined $c;
    0          
154             }
155              
156 0         0 print $out . $text . "\e[0m";
157             }
158              
159             # Reads a single character
160             sub getkey {
161 0     0 1 0 my $char;
162              
163 0 0       0 if ($^O eq 'MSWin32') {
164 0         0 $char = `powershell -Command "[console]::ReadKey(\$true).KeyChar"`;
165 0         0 $char =~ s/[\r\n]+$//;
166             }
167             else {
168 0         0 system("stty -icanon -echo");
169 0         0 sysread(STDIN, $char, 1);
170 0         0 system("stty icanon echo");
171             }
172              
173 0         0 return $char;
174             }
175              
176             # Reads a single character and prints it to the screen
177             sub putkey {
178 0     0 1 0 my $char;
179              
180 0 0       0 if ($^O eq 'MSWin32') {
181 0         0 $char = `powershell -Command "[console]::ReadKey(\$true).KeyChar"`;
182 0         0 $char =~ s/[\r\n]+$//;
183             }
184             else {
185 0         0 system("stty -icanon -echo");
186 0         0 sysread(STDIN, $char, 1);
187 0         0 system("stty icanon");
188             }
189              
190 0 0 0     0 if (defined $char && $char ne '') {
191 0         0 local $| = 1;
192              
193 0         0 print $char;
194             }
195              
196 0         0 return $char;
197             }
198              
199             # Set the terminal window title
200             sub title {
201 0     0 1 0 my ($title) = @_;
202              
203 0 0       0 print "\e]0;$title\a" if defined $title;
204             }
205              
206             # Gets the current width of the console (columns)
207             sub width {
208 1     1 1 2996 my $w;
209              
210 1 50       8 if ($^O eq 'MSWin32') {
211 0   0     0 $w = `powershell -command "\$host.ui.rawui.WindowSize.Width"` || 80;
212             }
213             else {
214 1 50       6522 $w = `stty size 2>/dev/null` =~ /\d+\s+(\d+)/ ? $1 : 80;
215             }
216              
217 1         45 return int($w);
218             }
219              
220             # Gets the current height of the console (rows)
221             sub height {
222 1     1 1 4 my $h;
223              
224 1 50       14 if ($^O eq 'MSWin32') {
225 0   0     0 $h = `powershell -command "\$host.ui.rawui.WindowSize.Height"` || 24;
226             }
227             else {
228 1 50       5043 $h = `stty size 2>/dev/null` =~ /(\d+)/ ? $1 : 24;
229             }
230              
231 1         60 return int($h);
232             }
233              
234             # Reset all styles to default values
235             sub reset {
236 0     0 1   print "\e[0m\e[?25h\e[0 q";
237             }
238              
239             1;
240              
241             __END__