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.09';
3 1     1   977 use strict;
  1         3  
  1         37  
4 1     1   6 use warnings;
  1         2  
  1         37  
5              
6 1     1   507 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 funckey
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             my $ZK_FuncKey = '';
39              
40             sub funckey { $ZK_FuncKey }
41              
42             my @Rc_Stack;
43             my $Rc_Code_Acc;
44              
45             my %Tf_Shift = (
46             29 => [ 'Ctrl' ],
47             42 => [ 'Shift-Left' ],
48             54 => [ 'Shift-Right' ],
49             56 => [ 'Alt-Gr' ],
50             58 => [ 'Shift-Lock' ],
51             69 => [ 'Num-Lock' ],
52             70 => [ 'Scroll-Lock' ],
53             91 => [ 'Win-Left' ],
54             92 => [ 'Win-Right' ],
55             93 => [ 'Win-List' ],
56             );
57              
58             my %Tf_Func = (
59             59 => [ 'F', 1 ],
60             60 => [ 'F', 2 ],
61             61 => [ 'F', 3 ],
62             62 => [ 'F', 4 ],
63             63 => [ 'F', 5 ],
64             64 => [ 'F', 6 ],
65             65 => [ 'F', 7 ],
66             66 => [ 'F', 8 ],
67             67 => [ 'F', 9 ],
68             68 => [ 'F', 10 ],
69             87 => [ 'F', 11 ],
70             88 => [ 'F', 12 ],
71             71 => [ 'Home', 25 ],
72             72 => [ 'Arr-Up', 26 ],
73             73 => [ 'Pg-Up', 27 ],
74             75 => [ 'Arr-Left', 28 ],
75             77 => [ 'Arr-Right', 29 ],
76             79 => [ 'End', 30 ],
77             80 => [ 'Arr-Down', 31 ],
78             81 => [ 'Pg-Down', 32 ],
79             82 => [ 'Ins', 33 ],
80             83 => [ 'Del', 34 ],
81             );
82              
83             my %Tf_FRev;
84              
85             for (keys %Tf_Func) {
86             my ($d, $n1) = @{$Tf_Func{$_}};
87              
88             if ($d eq 'F') {
89             my $n2 = $n1 + 12;
90              
91             $Tf_FRev{$n1} = 'F'.$n1;
92             $Tf_FRev{$n2} = 'F'.$n2;
93             }
94             else {
95             $Tf_FRev{$n1} = $d;
96             }
97             }
98              
99             my %Tf_Code_List;
100              
101             for my $n_code (192..255) {
102             my $nfd = NFD(chr($n_code));
103              
104             if (length($nfd) == 2) {
105             my $ch1 = substr($nfd, 0, 1);
106             my $ch2 = substr($nfd, 1, 1);
107              
108             my $a_code =
109             $ch2 eq "\x{300}" ? 96 : # Accent Grave
110             $ch2 eq "\x{301}" ? 180 : # Accent Aigue
111             $ch2 eq "\x{302}" ? 94 : # Hat / Circonflex
112             $ch2 eq "\x{303}" ? 126 : # Tilde
113             $ch2 eq "\x{308}" ? 168 : # Umlaut / Trema
114             $ch2 eq "\x{30a}" ? 186 : # Circle
115             0;
116              
117             $Tf_Code_List{$a_code, $ch1} = $n_code;
118             }
119             }
120              
121             my %Tf_Code_Local;
122             my %Tf_Code_Accent;
123             my %Tf_Chr_Letter;
124              
125             if ($ZK_keybd eq '40c') { # French keyboard
126             %Tf_Code_Local = (
127             '' .$;.'41' => 178, # Power 2
128             '' .$;. '3' => 233, # e Accent Aigue
129             '' .$;. '8' => 232, # e Accent Grave
130             '' .$;.'10' => 231, # c Cedille
131             '' .$;.'11' => 224, # a Accent Grave
132             '' .$;.'40' => 249, # u Accent Grave
133             'S' .$;.'12' => 186, # first circle
134             'CG'.$;.'27' => 164, # second circle
135             'S' .$;.'27' => 163, # Pound symbol
136             'S' .$;.'43' => 181, # Greek symbol
137             'S' .$;.'53' => 167, # Paragraph
138             'S' .$;.'26' => 168, # Umlaut / Trema
139             );
140              
141             %Tf_Code_Accent = (
142             '' .$;.'26' => 94, # Hat / Circonflex
143             'S' .$;.'26' => 168, # Umlaut / Trema
144             'CG'.$;. '8' => 96, # Accent Grave
145             'CG'.$;. '3' => 126, # Tilde
146             );
147              
148             %Tf_Chr_Letter = (
149             '' .$;.'16' => 'a',
150             'S' .$;.'16' => 'A',
151             '' .$;.'18' => 'e',
152             'S' .$;.'18' => 'E',
153             '' .$;.'23' => 'i',
154             'S' .$;.'23' => 'I',
155             '' .$;.'24' => 'o',
156             'S' .$;.'24' => 'O',
157             '' .$;.'22' => 'u',
158             'S' .$;.'22' => 'U',
159             '' .$;.'21' => 'y',
160             'S' .$;.'21' => 'Y',
161             '' .$;.'49' => 'n',
162             'S' .$;.'49' => 'N',
163             '' .$;.'57' => ' ',
164             );
165             }
166              
167             sub _readkey {
168             while ($CONS_INP->GetEvents) {
169             my @event = $CONS_INP->Input;
170              
171             my $ev1 = $event[1] // -1;
172              
173             if ($ev1 == 1) {
174             my $ev4 = $event[4];
175             my $ev5 = $event[5];
176             my $ev6 = $event[6];
177              
178             $ev5 += 256 if $ev5 < 0;
179              
180             unless ($ZK_cpage eq '65001') {
181             push @Rc_Stack, $ev5 unless $ev5 == 0;
182             next;
183             }
184              
185             next if $ev4 == 0 and $ev5 == 0;
186             next if $Tf_Shift{$ev4};
187              
188             my $K_AltGr = ($ev6 & (2 ** 0)) <=> 0;
189             my $K_Alt = ($ev6 & (2 ** 1)) <=> 0;
190             my $K_CtlRight = ($ev6 & (2 ** 2)) <=> 0;
191             my $K_CtlLeft = ($ev6 & (2 ** 3)) <=> 0;
192             my $K_Shift = ($ev6 & (2 ** 4)) <=> 0;
193             my $K_NumLock = ($ev6 & (2 ** 5)) <=> 0;
194             my $K_Scroll = ($ev6 & (2 ** 6)) <=> 0;
195             my $K_ShiftLock = ($ev6 & (2 ** 7)) <=> 0;
196              
197             my $SKey =
198             ($K_CtlRight || $K_CtlLeft ? 'C' : '').
199             ($K_Shift || $K_ShiftLock ? 'S' : '').
200             ($K_Alt ? 'A' : '').
201             ($K_AltGr ? 'G' : '');
202              
203             #~ printf "Key: %-6s => %3d\n", "[$SKey]", $ev4;
204              
205             my $acc = $Tf_Code_Accent{$SKey, $ev4};
206              
207             if (defined($acc) and not defined($Rc_Code_Acc)) {
208             $Rc_Code_Acc = $acc;
209             next;
210             }
211              
212             $ev5 ||= $Tf_Code_Local{$SKey, $ev4} || 0;
213              
214             if ($ev5 == 0) {
215             my $arr = $Tf_Func{$ev4};
216              
217             if ($arr) {
218             my ($d, $n) = @$arr;
219             $n += 12 if $d eq 'F' and $SKey eq 'S';
220              
221             push @Rc_Stack, 400 + $n;
222             next;
223             }
224              
225             if (defined $Rc_Code_Acc) {
226             my $letter = $Tf_Chr_Letter{$SKey, $ev4};
227              
228             if (defined $letter) {
229             if ($letter eq ' ') {
230             push @Rc_Stack, $Rc_Code_Acc;
231             }
232             else {
233             my $p_code = $Tf_Code_List{$Rc_Code_Acc, $letter};
234              
235             if (defined $p_code) {
236             push @Rc_Stack, $p_code;
237             }
238             }
239             }
240             }
241             }
242             else {
243             if (defined($Rc_Code_Acc) and $Rc_Code_Acc > 127) {
244             push @Rc_Stack, $Rc_Code_Acc;
245             }
246              
247             push @Rc_Stack, $ev5;
248             }
249              
250             unless ($ev4 == 0) {
251             $Rc_Code_Acc = undef;
252             }
253             }
254             }
255              
256             shift @Rc_Stack;
257             }
258              
259             sub readch_noblock {
260             $ZK_FuncKey = '';
261              
262             my $rk = _readkey;
263             return unless defined $rk;
264             return chr($rk) if $rk <= 255;
265              
266             my $d = $Tf_FRev{$rk - 400};
267             return unless defined $d;
268              
269             $ZK_FuncKey = $d;
270             return "\x{01}";
271             }
272              
273             sub readch_block {
274             my $ch = readch_noblock;
275              
276             # the wait_any() command waits for key-down as well as for key-up events...
277             # That means that for every keystroke we get two events: one for key-down and one for key-up.
278             # The key-down event delivers the character in readch_noblock, no problem.
279             # But the key-up event delivers undef. Therefore we have to skip the undef by
280             # using a while (!defined $ch) {...
281              
282             while (!defined $ch) {
283             # I want to sleep here until a key-down or key-up event is triggered...
284             # How can I achieve this under Windows... ???
285             # use Win32::IPC does the trick.
286              
287             # WaitForMultipleObjects([$CONS_INP]); # this works, but is deprecated.
288             wait_any(@{[$CONS_INP]}); # this works and is not deprecated
289              
290             $ch = readch_noblock;
291             }
292              
293             return $ch;
294             }
295              
296             sub readch_timeout {
297             my ($millisec) = @_;
298              
299             wait_any(@{[$CONS_INP]}, $millisec);
300             readch_noblock;
301             }
302              
303             sub getstr_echo {
304             my ($prompt) = @_;
305              
306             local $| = 1;
307              
308             print $prompt;
309              
310             chomp(my $txt = qx!set /p TXT=& perl -e "print \$ENV{'TXT'}"!);
311             $txt;
312             }
313              
314             sub getstr_noecho {
315             my ($prompt) = @_;
316              
317             my $password = '';
318              
319             local $| = 1;
320              
321             print $prompt;
322              
323             my $ascii = 0;
324              
325             while ($ascii != 13) {
326             my $ch = readch_block;
327             $ascii = ord($ch);
328              
329             if ($ascii == 8) { # Backspace was pressed, remove the last char from the password
330             if (length($password) > 0) {
331             chop($password);
332             print "\b \b"; # move the cursor back by one, print a blank character, move the cursor back by one
333             }
334             }
335             elsif ($ascii == 27) { # Escape was pressed, clear all input
336             print "\b" x length($password), ' ' x length($password), "\b" x length($password);
337             $password = '';
338             }
339             elsif ($ascii >= 32) { # a normal key was pressed
340             $password = $password.chr($ascii);
341             print '*';
342             }
343             }
344             print "\n";
345              
346             return $password;
347             }
348              
349             1;
350              
351             __END__