File Coverage

blib/lib/App/optex/pingu.pm
Criterion Covered Total %
statement 41 80 51.2
branch 0 26 0.0
condition 0 6 0.0
subroutine 14 22 63.6
pod 0 5 0.0
total 55 139 39.5


line stmt bran cond sub pod time code
1             package App::optex::pingu;
2              
3             my $VERSION = '1.03';
4              
5 1     1   276553 use v5.24;
  1         4  
6 1     1   4 use warnings;
  1         2  
  1         68  
7 1     1   595 use utf8;
  1         221  
  1         5  
8 1     1   24 use Carp;
  1         1  
  1         84  
9 1     1   586 use open IO => 'utf8', ':std';
  1         1202  
  1         5  
10 1     1   610 use Data::Dumper;
  1         6889  
  1         160  
11              
12             =encoding utf8
13              
14             =head1 NAME
15              
16             pingu - optex make-everything-pingu filter
17              
18             =head1 VERSION
19              
20             Version 1.03
21              
22             =head1 SYNOPSIS
23              
24             B -Mpingu [ options -- ] I
25              
26             =head1 DESCRIPTION
27              
28             This B module is greatly inspired by L command and
29             make every command pingu not only L. As for original
30             command, see L section. All honor for this idea should go
31             to the original author.
32              
33             =begin html
34              
35            

36              
37             =end html
38              
39             =begin html
40              
41            

42              
43             =end html
44              
45             This module is a quite good example to demonstrate L command
46             features.
47              
48             =head1 OPTION
49              
50             =over 7
51              
52             =item B<-->[B]B
53              
54             Produce images. Enabled by default.
55              
56             =item B<--image>=I
57              
58             Set image file. File is searched at current directory and module
59             directory. Standard B image is stored as F. If
60             string C is specified, module search the file in the following
61             order.
62              
63             ./pingu
64             ./pingu.asc4
65             ./pingu.asc2
66             ./pingu.asc
67             module-dir/pingu
68             module-dir/pingu.asc4
69             module-dir/pingu.asc2
70             module-dir/pingu.asc
71              
72             =begin comment
73              
74             =item B<--char>=I
75              
76             Specify replacement character. Default is Unicode C
77             (U+2588: █).
78              
79             =end comment
80              
81             =item B<--interval>=I
82              
83             Specifies the interval time in seconds between outputting each line.
84             Default is 0.1 seconds.
85              
86             =back
87              
88             =head1 IMAGE FILE FORMAT
89              
90             =over 4
91              
92             =item ASCII (C<.asc>)
93              
94             Each [C] character is converted to specified letter
95             with color which the character itself describe. Upper-case character
96             represent normal ANSI color and lower-case means high-intensity color.
97              
98             R r Red
99             G g Green
100             B b Blue
101             C c Cyan
102             M m Magenta
103             Y y Yellow
104             K k Black
105             W w White
106              
107             Line start with C<#> is treated as a comment.
108              
109             C:
110              
111             ... . ... .. .. .........
112             ... .... .. .. ... ..... .. ..
113             ... ....... ... ... . ..... kkkkkkk
114             ..... ........ .kkkkkkkkkkkkkkk..... ... kkkkkkkkkk. .
115             .... ........kkkkkkkkkkkkkkkkkkkkk. ... kkkkkkkkkkk
116             ....... kkwwwwkkkkkkkkkkkkkkkk.... kkkkkkkkkkkk
117             . . .... kkwwkkwwkkkkkkkkkkwwwwkk... kkkkkkkkkkk
118             .. ....kkkkwwwwkkrrrrrrkkwwkkwwk.. .kkkkkkkkkkk
119             . kkkkkkkkrrrrrrrrrrkwwwwkk. .kkkkkkkkkk
120             .... .kkkkkkkkrrrrrrrrkkkkkkkk. kkkkkkkk
121             ..... . kkkkkkkkkkkkkkkkkkkk. kkkkkkk.
122             ...... .. . kkkkkkkkkkkkkkkkkk . . .kkkkkkk
123             ...... kkkkkkkkkkkkkkkkkkkkk . .kkkkkkk
124             ...... .kkkkkkkkkkkkkkkkkkyywwkkkkk .. kkkkkkk
125             ... . kkkkkkkkkkkkkkkkywwwwwwwwwkkkkkkkkkkkkkk.
126             kkkkkkkkkkkkkkkkywwwwwwwwwwwwwkkkkkkkkk .
127             kkkkkkkkkkkkkkkywwwwwwwwwwwwwwwwkk .
128             kkkkkkkkkkkkkkkywwwwwwwwwwwwwwwwwww ........
129             .kkkkkkkkkkkkkkkkywwwwwwwwwwwwwwwwwwww .........
130             .kkkkkkkkkkkkkkkkywwwwwwwwwwwwwwwwwwwwww .... . .
131              
132             =begin html
133              
134            

135              
136             =end html
137              
138             =begin html
139              
140            

141              
142             =end html
143              
144             =item ASCII2 (C<.asc2>)
145              
146             Each pixel is represented by two blocks, one in the upper half and one
147             in the lower half, with each color represented by two lines of data.
148              
149             C:
150              
151             ... ....... ... ... . ..... kkkkk
152             ... ....... kkkkkkk ... . ..... kkkkkkk
153             ..... ........ . kkkkkkkkkkkkk ..... ... kkkkkkkkk. .
154             ..... ........ kkkkkkkkkkkkkkkkk.... ... kkkkkkkkkk. .
155             .... ........ kkkkkkkkkkkkkkkkkkk . ... kkkkkkkkkkkk
156             .... ........kkkkkkkkkkkkkkkkkkkkk. ... kkkkkkkkkkkk
157             ....... kkkwwkkkkkkkkkkkkkkkkk.... kkkkkkkkkkkkk
158             .......kkkwwwwkkkkkkkkkkkkkkkk.... kkkkkkkkkkkk
159             . . .... kkwwKKwwkkkkkkkkkkkwwkkk...kkkkkkkkkkkkk
160             . . ....kkkwwKKwwkkkkkkkkkkwwwwkk...kkkkkkkkkkkkk
161             .. ....kkkkwwwwkkkkkkkkkkwwKKwwkk. .kkkkkkkkkkkk
162             .. ....kkkkkwwkkkkrrrrkkkwwKKwwkk. .kkkkkkkkkkk
163             . kkkkkkkkkrrrrrrrrkkwwwwkkk .kkkkkkkkkk
164             . kkkkkkkrrrrrrrrrrkkwwkkkk . kkkkkkkkk
165             .... .kkkkkkkrrrrrrrrrrkkkkkkk. kkkkkkkk
166             .... . kkkkkkkrrrrrrrrkkkkkkkk. kkkkkkkk
167             ..... . kkkkkkkkrrrrkkkkkkkkk. kkkkkkk.
168             ..... . kkkkkkkkkkkkkkkkkkkk. kkkkkkk.
169             ...... .. . kkkkkkkkkkkkkkkkkk. . .kkkkkkk
170             ...... .. . kkkkkkkkkkkkkkkkk . . .kkkkkk
171             ...... kkkkkkkkkkkkkkkkkkkk . .kkkkkkk
172             ...... kkkkkkkkkkkkkkkkkkkkkkk . .kkkkkkk
173             ...... . kkkkkkkkkkkkkkkkkyyykkkkk .. kkkkkkk
174             ...... .kkkkkkkkkkkkkkkkyyyWWWWkkkk .. kkkkkkkk
175             ... . kkkkkkkkkkkkkkkkyyWWWWWWWkkkkk kkkkkkkk.
176             ... .kkkkkkkkkkkkkkkkyyWWWWWWWWWkkkkkkkkkkkkk .
177             kkkkkkkkkkkkkkkkyyWWWWWWWWWWWkkkkkkkkkk .
178             kkkkkkkkkkkkkkkyyWWWWWWWWWWWWWkkkkkkkk .
179             kkkkkkkkkkkkkkkyyWWWWWWWWWWWWWWWkkkkk .
180             kkkkkkkkkkkkkkkkyWWWWWWWWWWWWWWWWWkk .
181              
182             =begin html
183              
184            

185              
186             =end html
187              
188             =begin html
189              
190            

191              
192             =end html
193              
194             =item ASCII4 (C<.asc4>)
195              
196             Each pixel is made by four blocks, with each color represented by 2x2
197             characters.
198              
199             C:
200              
201             ............ kkkkkkkkkkkkkk ...... .. .......... k
202             ............ .. kkkkkkkkkkkkkkkkkkkkkkkkkk .......... ...... kkkk
203             ............ kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk........ ...... kkkkkk
204             .......... kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk .. ...... kkkkkkkk
205             ..........kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk.. ...... kkkkkkkkk
206             ........ kkkkkkwwwwwwkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk........ kkkkkkkkkk
207             ........kkkkkwwwwwwwwwwkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk....... kkkkkkkkkkk
208             ...... kkkkkwwwwKKKKwwwwkkkkkkkkkkkkkkkkkkkkkwwwwwwkkkkk......kkkkkkkkkkkk
209             ......kkkkkkwwwwKKKKwwwwkkkkkkkkkkkkkkkkkkkwwwwwwwwwwkkkk.....kkkkkkkkkkkk
210             ......kkkkkkkwwwwwwwwwwkkkkkkkkkkkkkkkkkkkwwwwKKKKwwwwkkk .. .kkkkkkkkkkk
211             ......kkkkkkkkkwwwwwwkkkkkkkrrrrrrrrkkkkkkwwwwKKKKwwwwkkkk.. ..kkkkkkkkkk
212             kkkkkkkkkkkkkkkkkrrrrrrrrrrrrrrrrkkkwwwwwwwwwwkkkkk ..kkkkkkkk
213             kkkkkkkkkkkkkkkrrrrrrrrrrrrrrrrrrrrkkkwwwwwwkkkkkkk .. kkkkkk
214             ..kkkkkkkkkkkkkkrrrrrrrrrrrrrrrrrrrrkkkkkkkkkkkkkkk. kkkkk
215             .. kkkkkkkkkkkkkkrrrrrrrrrrrrrrrrkkkkkkkkkkkkkkkk.. kkkk
216             .. kkkkkkkkkkkkkkkkrrrrrrrrkkkkkkkkkkkkkkkkkkk. kkk
217             .. kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk .. kk
218             .... .. kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk .. .. ..kk
219             .... .. kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk .. .. .kkk
220             kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk .. .. kkk
221             kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk .. ..kkkk
222             .. kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkyyyyyykkkkkkkkkk .... kkkkk
223             ..kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkyyyyyyWWWWWWWWkkkkkkkkk .... kkkkkkk
224             kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkyyyyWWWWWWWWWWWWWWkkkkkkkkkk kkkkkkkkk
225             kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkyyyyWWWWWWWWWWWWWWWWWWkkkkkkkkkkkkkkkkkkkkkk
226             kkkkkkkkkkkkkkkkkkkkkkkkkkkkyyyyWWWWWWWWWWWWWWWWWWWWWWkkkkkkkkkkkkkkkkkkkk
227             kkkkkkkkkkkkkkkkkkkkkkkkkkyyyyWWWWWWWWWWWWWWWWWWWWWWWWWWkkkkkkkkkkkkkkkk
228             kkkkkkkkkkkkkkkkkkkkkkkkkyyyWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWkkkkkkkkkkk ..
229             kkkkkkkkkkkkkkkkkkkkkkkkyyyWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWkkkkkk ..
230             kkkkkkkkkkkkkkkkkkkkkkkyyyWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWkkk ........
231              
232             =back
233              
234             Coloring is done by L module. See its
235             document for detail.
236              
237             =head1 INSTALL
238              
239             Use L command:
240              
241             cpanm App::optex::pingu
242              
243             =head1 PINGU ALIAS
244              
245             You can set shell alias B to call L command through
246             B.
247              
248             alias pingu='optex -Mpingu --pingu ping'
249              
250             However, there is more sophisticated way to use B alias
251             function. Next command will make symbolic link C<< pingu->optex >> in
252             F<~/.optex.d/bin> directory:
253              
254             $ optex --ln pingu
255              
256             Executing this symbolic link, optex will call system installed
257             B command. So make an alias in F<~/.optex.d/config.toml> to
258             call L command instead:
259              
260             [alias]
261             pingu = "ping -Mpingu"
262              
263             =head1 MAKING NEW PING OPTION
264              
265             You can add, say, B<--pingu> option to the original L
266             command. Make a symbolic link C<< ping->optex >> in F<~/.optex.d/bin>
267             directory:
268              
269             $ optex --ln ping
270              
271             And create an rc file F<~/.optex.d/ping.rc> for B:
272              
273             option --pingu -Mpingu
274              
275             Then pingu will show up when you use B<--pingu> option to execute
276             L command:
277              
278             $ ping --pingu localhost -c15
279              
280             If you want to enable this option always (really?), put next line in
281             your F<~/.optex.d/ping.rc>:
282              
283             option default --pingu
284              
285             =head1 SEE ALSO
286              
287             L
288              
289             L,
290             L
291              
292             L,
293             L
294              
295             =head2 ARTICLES
296              
297             L
298              
299             =head1 AUTHOR
300              
301             Kazumasa Utashiro
302              
303             =head1 LICENSE
304              
305             Copyright ©︎ 2022-2024 Kazumasa Utashiro.
306              
307             You can redistribute it and/or modify it under the same terms
308             as Perl itself.
309              
310             =cut
311              
312 1     1   628 use File::Share qw(dist_dir);
  1         26938  
  1         70  
313 1     1   11 use List::Util qw(first pairmap);
  1         2  
  1         87  
314 1     1   6 use Time::HiRes qw(usleep);
  1         1  
  1         12  
315 1     1   76 use Scalar::Util;
  1         3  
  1         27  
316 1     1   600 use Hash::Util qw(lock_keys);
  1         3189  
  1         7  
317             *is_number = \&Scalar::Util::looks_like_number;
318              
319 1     1   740 use App::optex::pingu::Picture;
  1         3  
  1         289  
320              
321             my $image_dir = $ENV{OPTEX_PINGU_IMAGEDIR} //= dist_dir 'App-optex-pingu';
322              
323             our %opt = (
324             pingu => \(our $pingu = 1),
325             image => 'pingu',
326             char => '█',
327             repeat => 1,
328             interval => 0.1,
329             );
330             lock_keys %opt;
331              
332             sub hash_to_spec {
333             pairmap {
334 0     0     my $ref = ref $b;
335 0 0         if (not defined $b) { "$a!" }
  0 0          
    0          
336 0           elsif ($ref eq 'SCALAR') { "$a!" }
337 0           elsif (is_number($b)) { "$a=f" }
338 0           else { "$a=s" }
339 0     0 0   } shift->%*;
340             }
341              
342 1     1   1872 use App::optex::util::filter qw(io_filter);
  1         46560  
  1         207  
343              
344             sub finalize {
345 0     0 0   our($mod, $argv) = @_;
346             #
347             # private option handling
348             #
349 0 0 0       if (@$argv and $argv->[0] !~ /^-M/ and
      0        
350 0     0     defined(my $i = first { $argv->[$_] eq '--' } keys @$argv)) {
351 0           splice @$argv, $i, 1; # remove '--'
352 0 0         if (local @ARGV = splice @$argv, 0, $i) {
353 1     1   633 use Getopt::Long qw(GetOptionsFromArray);
  1         11200  
  1         10  
354 0           Getopt::Long::Configure qw(bundling);
355 0 0         GetOptions \%opt, hash_to_spec \%opt or die "Option parse error.\n";
356             }
357             }
358 0           io_filter(\&pingu, STDOUT => 1);
359             }
360              
361             sub get_image {
362 0     0 0   my $name = shift;
363 0           my $file = do {
364 0     0     first { -s }
365             map {
366 0           my $dir = $_;
  0            
367 0           map { "${dir}${name}$_" } '', '.asc4', '.asc2', '.asc';
  0            
368             } '', "$image_dir/";
369             };
370 0 0         die "$name: image file not found.\n" unless $file;
371 0           App::optex::pingu::Picture::load($file);
372             }
373              
374             sub pingu {
375 0 0   0 0   @_ = map { utf8::is_utf8($_) ? $_ : decode('utf8', $_) } @_;
  0            
376 0           my %param = @_;
377 0           my @image = get_image($opt{image});
378 0           my $i = 0;
379 0 0         my $sleep = $opt{interval} > 0 ? $opt{interval} * 1000000 : 0;
380 0           while (<>) {
381 0 0         print $image[$i++ % @image] if $pingu;
382 0           print $_;
383 0 0         last if eof;
384 0 0         usleep $sleep if $sleep > 0;
385             }
386             }
387              
388             sub set {
389 0     0 0   while (my($k, $v) = splice(@_, 0, 2)) {
390 0 0         exists $opt{$k} or die "$k: invaid paraeter.\n";
391 0           $opt{$k} = $v;
392             }
393 0           ();
394             }
395              
396             1;
397              
398             __DATA__