File Coverage

blib/lib/Win32/Readch.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Win32::Readch;
2             $Win32::Readch::VERSION = '0.08';
3 1     1   730 use strict;
  1         2  
  1         28  
4 1     1   5 use warnings;
  1         1  
  1         28  
5              
6 1     1   446 use Win32::Console;
  0            
  0            
7             use Win32::IPC qw(wait_any);
8             use Unicode::Normalize;
9             use Win32::TieRegistry; $Registry->Delimiter('/');
10              
11             require Exporter;
12             our @ISA = qw(Exporter);
13             our %EXPORT_TAGS = ('all' => [qw(
14             readch_block readch_noblock readch_timeout
15             getstr_noecho getstr_echo keybd cpage
16             )]);
17             our @EXPORT = qw();
18             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
19              
20             my $CONS_INP = Win32::Console->new(STD_INPUT_HANDLE)
21             or die "Error in Win32::Readch - Can't Win32::Console->new(STD_INPUT_HANDLE)";
22              
23             sub keybd {
24             my $kb = $Registry->{'HKEY_CURRENT_USER/Keyboard Layout/Preload//1'} // '';
25             $kb =~ s{\A 0+}''xms;
26             $kb = '0' if $kb eq '';
27              
28             return $kb;
29             }
30              
31             sub cpage {
32             chomp(my $cp = qx{chcp});
33             $cp =~ m{: \s* (\d+) \.? \s* \z}xms ? $1 : '0';
34             }
35              
36             my $ZK_keybd = keybd;
37             my $ZK_cpage = cpage;
38              
39             my @Rc_Stack;
40             my $Rc_Code_Acc;
41              
42             my %Tf_Shift = (
43             29 => [ 'Ctrl' ],
44             42 => [ 'Shift-Left' ],
45             54 => [ 'Shift-Right' ],
46             56 => [ 'Alt-Gr' ],
47             58 => [ 'Shift-Lock' ],
48             69 => [ 'Num-Lock' ],
49             70 => [ 'Scroll-Lock' ],
50             91 => [ 'Win-Left' ],
51             92 => [ 'Win-Right' ],
52             93 => [ 'Win-List' ],
53             );
54              
55             my %Tf_Code_List;
56              
57             for my $n_code (192..255) {
58             my $nfd = NFD(chr($n_code));
59              
60             if (length($nfd) == 2) {
61             my $ch1 = substr($nfd, 0, 1);
62             my $ch2 = substr($nfd, 1, 1);
63              
64             my $a_code =
65             $ch2 eq "\x{300}" ? 96 : # Accent Grave
66             $ch2 eq "\x{301}" ? 180 : # Accent Aigue
67             $ch2 eq "\x{302}" ? 94 : # Hat / Circonflex
68             $ch2 eq "\x{303}" ? 126 : # Tilde
69             $ch2 eq "\x{308}" ? 168 : # Umlaut / Trema
70             $ch2 eq "\x{30a}" ? 186 : # Circle
71             0;
72              
73             $Tf_Code_List{$a_code, $ch1} = $n_code;
74             }
75             }
76              
77             my %Tf_Code_Local;
78             my %Tf_Code_Accent;
79             my %Tf_Chr_Letter;
80              
81             if ($ZK_keybd eq '40c') { # French keyboard
82             %Tf_Code_Local = (
83             '' .$;.'41' => 178, # Power 2
84             '' .$;. '3' => 233, # e Accent Aigue
85             '' .$;. '8' => 232, # e Accent Grave
86             '' .$;.'10' => 231, # c Cedille
87             '' .$;.'11' => 224, # a Accent Grave
88             '' .$;.'40' => 249, # u Accent Grave
89             'S' .$;.'12' => 186, # first circle
90             'CG'.$;.'27' => 164, # second circle
91             'S' .$;.'27' => 163, # Pound symbol
92             'S' .$;.'43' => 181, # Greek symbol
93             'S' .$;.'53' => 167, # Paragraph
94             'S' .$;.'26' => 168, # Umlaut / Trema
95             );
96              
97             %Tf_Code_Accent = (
98             '' .$;.'26' => 94, # Hat / Circonflex
99             'S' .$;.'26' => 168, # Umlaut / Trema
100             'CG'.$;. '8' => 96, # Accent Grave
101             'CG'.$;. '3' => 126, # Tilde
102             );
103              
104             %Tf_Chr_Letter = (
105             '' .$;.'16' => 'a',
106             'S' .$;.'16' => 'A',
107             '' .$;.'18' => 'e',
108             'S' .$;.'18' => 'E',
109             '' .$;.'23' => 'i',
110             'S' .$;.'23' => 'I',
111             '' .$;.'24' => 'o',
112             'S' .$;.'24' => 'O',
113             '' .$;.'22' => 'u',
114             'S' .$;.'22' => 'U',
115             '' .$;.'21' => 'y',
116             'S' .$;.'21' => 'Y',
117             '' .$;.'49' => 'n',
118             'S' .$;.'49' => 'N',
119             '' .$;.'57' => ' ',
120             );
121             }
122              
123             sub readch_noblock {
124             while ($CONS_INP->GetEvents) {
125             my @event = $CONS_INP->Input;
126              
127             my $ev1 = $event[1] // -1;
128              
129             if ($ev1 == 1) {
130             my $ev4 = $event[4];
131             my $ev5 = $event[5];
132             my $ev6 = $event[6];
133              
134             $ev5 += 256 if $ev5 < 0;
135              
136             unless ($ZK_cpage eq '65001') {
137             push @Rc_Stack, chr($ev5) unless $ev5 == 0;
138             next;
139             }
140              
141             next if $ev4 == 0 and $ev5 == 0;
142             next if $Tf_Shift{$ev4};
143              
144             my $K_AltGr = ($ev6 & (2 ** 0)) <=> 0;
145             my $K_Alt = ($ev6 & (2 ** 1)) <=> 0;
146             my $K_CtlRight = ($ev6 & (2 ** 2)) <=> 0;
147             my $K_CtlLeft = ($ev6 & (2 ** 3)) <=> 0;
148             my $K_Shift = ($ev6 & (2 ** 4)) <=> 0;
149             my $K_NumLock = ($ev6 & (2 ** 5)) <=> 0;
150             my $K_Scroll = ($ev6 & (2 ** 6)) <=> 0;
151             my $K_ShiftLock = ($ev6 & (2 ** 7)) <=> 0;
152              
153             my $SKey =
154             ($K_CtlRight || $K_CtlLeft ? 'C' : '').
155             ($K_Shift || $K_ShiftLock ? 'S' : '').
156             ($K_Alt ? 'A' : '').
157             ($K_AltGr ? 'G' : '');
158              
159             my $acc = $Tf_Code_Accent{$SKey, $ev4};
160              
161             if (defined($acc) and not defined($Rc_Code_Acc)) {
162             $Rc_Code_Acc = $acc;
163             next;
164             }
165              
166             $ev5 ||= $Tf_Code_Local{$SKey, $ev4} || 0;
167              
168             if ($ev5 == 0) {
169             if (defined $Rc_Code_Acc) {
170             my $letter = $Tf_Chr_Letter{$SKey, $ev4};
171              
172             if (defined $letter) {
173             if ($letter eq ' ') {
174             push @Rc_Stack, chr($Rc_Code_Acc);
175             }
176             else {
177             my $p_code = $Tf_Code_List{$Rc_Code_Acc, $letter};
178              
179             if (defined $p_code) {
180             push @Rc_Stack, chr($p_code);
181             }
182             }
183             }
184             }
185             }
186             else {
187             if (defined($Rc_Code_Acc) and $Rc_Code_Acc > 127) {
188             push @Rc_Stack, chr($Rc_Code_Acc);
189             }
190              
191             push @Rc_Stack, chr($ev5);
192             }
193              
194             unless ($ev4 == 0) {
195             $Rc_Code_Acc = undef;
196             }
197             }
198             }
199              
200             shift @Rc_Stack;
201             }
202              
203             sub readch_block {
204             my $ch = readch_noblock;
205              
206             # the wait_any() command waits for key-down as well as for key-up events...
207             # That means that for every keystroke we get two events: one for key-down and one for key-up.
208             # The key-down event delivers the character in readch_noblock, no problem.
209             # But the key-up event delivers undef. Therefore we have to skip the undef by
210             # using a while (!defined $ch) {...
211              
212             while (!defined $ch) {
213             # I want to sleep here until a key-down or key-up event is triggered...
214             # How can I achieve this under Windows... ???
215             # use Win32::IPC does the trick.
216              
217             # WaitForMultipleObjects([$CONS_INP]); # this works, but is deprecated.
218             wait_any(@{[$CONS_INP]}); # this works and is not deprecated
219              
220             $ch = readch_noblock;
221             }
222              
223             return $ch;
224             }
225              
226             sub readch_timeout {
227             my ($millisec) = @_;
228              
229             wait_any(@{[$CONS_INP]}, $millisec);
230             readch_noblock;
231             }
232              
233             sub getstr_echo {
234             my ($prompt) = @_;
235              
236             local $| = 1;
237              
238             print $prompt;
239              
240             chomp(my $txt = qx!set /p TXT=& perl -e "print \$ENV{'TXT'}"!);
241             $txt;
242             }
243              
244             sub getstr_noecho {
245             my ($prompt) = @_;
246              
247             my $password = '';
248              
249             local $| = 1;
250              
251             print $prompt;
252              
253             my $ascii = 0;
254              
255             while ($ascii != 13) {
256             my $ch = readch_block;
257             $ascii = ord($ch);
258              
259             if ($ascii == 8) { # Backspace was pressed, remove the last char from the password
260             if (length($password) > 0) {
261             chop($password);
262             print "\b \b"; # move the cursor back by one, print a blank character, move the cursor back by one
263             }
264             }
265             elsif ($ascii == 27) { # Escape was pressed, clear all input
266             print "\b" x length($password), ' ' x length($password), "\b" x length($password);
267             $password = '';
268             }
269             elsif ($ascii >= 32) { # a normal key was pressed
270             $password = $password.chr($ascii);
271             print '*';
272             }
273             }
274             print "\n";
275              
276             return $password;
277             }
278              
279             1;
280              
281             __END__