397 lines
12 KiB
Perl
Executable file
397 lines
12 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
|
|
# shellshow: terminal slideshow program with a few basic wipes
|
|
# Probably the next version will be written in C, this was written in
|
|
# perl for quick prototyping and for my @climagic presentation at
|
|
# Indiana Linux Fest 2012.
|
|
|
|
# Copyright (C) 2012 Mark Krenz (Deltaray)
|
|
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
#
|
|
# You may contact the author at <deltaray@slugbug.org>
|
|
|
|
my $VERSION = 0.1;
|
|
|
|
$| = 1;
|
|
my $rows = `tput lines`;
|
|
my $cols = `tput cols`;
|
|
|
|
&setupterminal();
|
|
|
|
$SIG{INT} = sub { &restoreterminal(); };
|
|
$SIG{TERM} = sub { &restoreterminal(); };
|
|
|
|
if (@ARGV < 2 || $ARGV[0] eq "-h" || $ARGV[0] eq "--help") {
|
|
&showhelp();
|
|
}
|
|
|
|
# A simple timing array to use for the slides to give them
|
|
# a little acceleration.
|
|
my @timing = ();
|
|
for($i = 0; $i < $cols/2; $i++) {
|
|
$time = 0.1 / ($i + 1);
|
|
$timing[$i] = $time;
|
|
}
|
|
# Now make the other half of the timing array.
|
|
for($i = $cols/2 - 1; $i >= 0; $i--) {
|
|
# print "pushing " . $timing[$i] . " onto the list.\n";
|
|
push(@timing, $timing[$i]);
|
|
}
|
|
|
|
my @graydient = ();
|
|
for ($i = 255; $i >= 232; $i--) { # Gray colors in the ansi color spectrum
|
|
push(@graydient, $i);
|
|
}
|
|
|
|
my @frames = ();
|
|
my $frame = 0;
|
|
|
|
|
|
foreach $file (@ARGV) {
|
|
open(my $fh, $file);
|
|
my $thisline;
|
|
my $lineno = 0;
|
|
LINE: while (<$fh>) {
|
|
chomp($_);
|
|
if ($lineno > $rows - 1) {
|
|
last LINE;
|
|
}
|
|
$_ =~ s/\t/ /g; # Convert tabs into 4 spaces.
|
|
my $thisline = substr($_, 0, $cols);
|
|
if (length($_) > $cols) {
|
|
$thisline = substr($thisline, $cols-1,1, "+"); # This does the pico/nano like behavior of showing that a line is overlength.
|
|
}
|
|
my $diff = $cols - length($thisline);
|
|
if ($diff) {
|
|
$thisline .= " " x $diff;
|
|
}
|
|
$frames[$frame][$lineno] = $thisline;
|
|
$lineno++;
|
|
}
|
|
if ($lineno <= $rows) {
|
|
my $thisline = " " x $cols;
|
|
foreach ($lineno; $lineno <= $rows; $lineno++) {
|
|
$frames[$frame][$lineno] = $thisline;
|
|
}
|
|
}
|
|
$frame++;
|
|
close($fh);
|
|
}
|
|
|
|
my $totalframes = scalar @frames;
|
|
|
|
print "\033[2J";
|
|
#foreach $frameno (keys @frames) {
|
|
$frameno = 0;
|
|
while ($frameno < $totalframes && $frameno >= 0) {
|
|
poscursor(1,1);
|
|
displayframe(\@frames,$frameno,$cols,$rows);
|
|
|
|
# Seems crazy to have to run system commands twice just to read a char.
|
|
# We'll make this more efficient in the future and/or switch to C.
|
|
if ($BSD_STYLE) {
|
|
system "stty cbreak </dev/tty >/dev/tty 2>&1";
|
|
} else {
|
|
system "stty", '-icanon', 'eol', "\001";
|
|
}
|
|
$read = getc();
|
|
if ($BSD_STYLE) {
|
|
system "stty -cbreak </dev/tty >/dev/tty 2>&1";
|
|
} else {
|
|
system 'stty', 'icanon', 'eol', '^@'; # ASCII NUL
|
|
}
|
|
|
|
# Do the transition. Perl needs a case to store all the Pearls.
|
|
if ($read eq "\n" or $read eq " ") { # Return or space to move forward.
|
|
$oldframe = $frameno;
|
|
$newframe = $frameno+1;
|
|
slideright(\@frames,$oldframe,$newframe, $cols, $rows, \@timing);
|
|
$frameno = $newframe;
|
|
} elsif ($read eq "\177" or $read eq "b") { # Backspace to go back.
|
|
$oldframe = $frameno;
|
|
$newframe = $frameno-1;
|
|
slideleft(\@frames,$oldframe,$newframe, $cols, $rows, \@timing);
|
|
$frameno = $newframe;
|
|
} elsif ($read eq "l") { # l to move forward using line at a time wipe
|
|
$oldframe = $frameno;
|
|
$newframe = $frameno+1;
|
|
slidelineright(\@frames,$oldframe,$newframe, $cols, $rows, \@timing);
|
|
$frameno = $newframe;
|
|
} elsif ($read eq "k") { # l to move forward using line at a time wipe
|
|
$oldframe = $frameno;
|
|
$newframe = $frameno-1;
|
|
slidelineleft(\@frames,$oldframe,$newframe, $cols, $rows, \@timing);
|
|
$frameno = $newframe;
|
|
} elsif ($read eq "f") { # f to move forward using fade method.
|
|
$oldframe = $frameno;
|
|
$newframe = $frameno+1;
|
|
fadeoutfadein(\@frames,$oldframe,$newframe, $cols, $rows, \@graydient, 0.01);
|
|
$frameno = $newframe;
|
|
} elsif ($read eq "d") { # f to move forward using fade method.
|
|
$oldframe = $frameno;
|
|
$newframe = $frameno-1;
|
|
fadeoutfadein(\@frames,$oldframe,$newframe, $cols, $rows, \@graydient, 0.01);
|
|
$frameno = $newframe;
|
|
}
|
|
# Maybe have an r for random. Later of course we should allow a YAML config
|
|
# file or something to setup a saved show so you can just play that with
|
|
# predetermined wipes and waittimes, etc.
|
|
}
|
|
|
|
&restoreterminal();
|
|
exit 0;
|
|
|
|
|
|
sub showhelp {
|
|
print <<"EOF";
|
|
--------------------------------------------------------------------------------
|
|
shellshow: A program to show "slides" in an interesting way inside the terminal
|
|
Version: $VERSION
|
|
--------------------------------------------------------------------------------
|
|
Usage:
|
|
shellshow <file1> <file2> [file3 [, file4, [ ... ]]]
|
|
|
|
Movement/Wipes:
|
|
<space>, <enter> = Move forward a frame in slide motion.
|
|
<b>, <backspace> = Move backward a frame in slide motion.
|
|
<l> = Move forward with slideline wipe. (slow)
|
|
<k> = Move backward with slideline wipe. (slow)
|
|
<f> = Move forward with fadeout/fadein wipe. (req. black bg)
|
|
<d> = Move backward with fadeout/fadein wipe. (req. black bg)
|
|
|
|
Description:
|
|
Shellshow determines the size of your terminal window and reads in
|
|
files given as args as frames, storing only the part of the file that
|
|
will fit inside the terminal window. You must at least give two filenames
|
|
as arguments. You can use shell glob patterns/wildcards if you want.
|
|
|
|
Limitations:
|
|
Right now this program can't handle files with ANSI escapes or multibyte
|
|
characters like UTF-8 or binary characters.
|
|
|
|
I'd recommend using a black background with white forground text for now.
|
|
Eventually we'll have options for working with various background types, etc.
|
|
|
|
EOF
|
|
exit(0);
|
|
}
|
|
|
|
sub setupterminal {
|
|
# I could have used Curses, but decided not to go that route
|
|
# for a bit more simplicity right now.
|
|
system('tput', 'smcup'); # Must be called using system.
|
|
# Backticks won't work.
|
|
# The codes for smcup were listed as [?1049h on some page as well. :-(
|
|
# printf "\0337\033[?47h"; # Switch to alternate screen (smcup)
|
|
printf "\033[?25l"; # Hide the cursor (civis)
|
|
`stty -echo`; # Turn off input echo.
|
|
}
|
|
|
|
sub restoreterminal {
|
|
system('tput', 'rmcup');
|
|
# printf "\033[2J\033[?47l"; # Switch back to normal screen (rmcup)
|
|
printf "\033[?25h"; # show the cursor again. (cnorm)
|
|
`stty echo`; # Turn input echo back on.
|
|
exit 0;
|
|
}
|
|
|
|
sub displayframe {
|
|
my $framesref = shift;
|
|
my $frame = shift;
|
|
my $cols = shift;
|
|
my $rows = shift;
|
|
|
|
my $line = "";
|
|
poscursor(1, 1);
|
|
for ($y = 0; $y < $rows; $y++) {
|
|
$line = substr($$framesref[$frame][$y], 0, $cols);
|
|
if ($y + 1 == $rows) {
|
|
print "$line"; # Don't put a newline on the last line.
|
|
} else {
|
|
print "$line\n";
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
sub slideright {
|
|
$framesref = shift;
|
|
$oldframe = shift;
|
|
$newframe = shift;
|
|
$cols = shift;
|
|
$rows = shift;
|
|
$timingref = shift;
|
|
|
|
if (defined($$framesref[$newframe])) {
|
|
my $leftline = "";
|
|
my $rightline = "";
|
|
for ($x = 1; $x < $cols; $x++) {
|
|
poscursor(1, 1);
|
|
for ($y = 0; $y < $rows; $y++) {
|
|
$leftline = substr($$framesref[$oldframe][$y], $x);
|
|
$rightline = substr($$framesref[$newframe][$y], 0, $x);
|
|
if ($y + 1 == $rows) {
|
|
print "$leftline$rightline"; # Don't put a newline on the last line.
|
|
} else {
|
|
print "$leftline$rightline\n";
|
|
}
|
|
}
|
|
select(undef,undef, undef, $$timingref[$x]);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
sub slideleft {
|
|
$framesref = shift;
|
|
$oldframe = shift;
|
|
$newframe = shift;
|
|
$cols = shift;
|
|
$rows = shift;
|
|
$timingref = shift;
|
|
|
|
if (defined($$framesref[$oldframe])) {
|
|
my $leftline = "";
|
|
my $rightline = "";
|
|
for ($x = $cols - 1; $x > 0; $x--) {
|
|
poscursor(1, 1);
|
|
for ($y = 0; $y < $rows; $y++) {
|
|
$leftline = substr($$framesref[$newframe][$y], $x);
|
|
$rightline = substr($$framesref[$oldframe][$y], 0, $x);
|
|
if ($y + 1 == $rows) {
|
|
print "$leftline$rightline"; # Don't put a newline on the last line.
|
|
} else {
|
|
print "$leftline$rightline\n";
|
|
}
|
|
}
|
|
select(undef,undef, undef, $$timingref[$x]);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
# These are too slow.
|
|
sub slidelineright {
|
|
$framesref = shift;
|
|
$oldframe = shift;
|
|
$newframe = shift;
|
|
$cols = shift;
|
|
$rows = shift;
|
|
$timingref = shift;
|
|
|
|
if (defined($$framesref[$newframe])) {
|
|
my $leftline = "";
|
|
my $rightline = "";
|
|
for ($y = 0; $y < $rows; $y++) {
|
|
for ($x = 1; $x <= $cols; $x++) {
|
|
poscursor(1,$y + 1);
|
|
$leftline = substr($$framesref[$oldframe][$y], $x);
|
|
$rightline = substr($$framesref[$newframe][$y], 0, $x);
|
|
print "$leftline$rightline"; # Don't put a newline on the last line.
|
|
select(undef,undef,undef, 0.001);
|
|
}
|
|
unless ($y + 1 == rows) {
|
|
print "\n";
|
|
}
|
|
#select(undef,undef, undef, $$timingref[$y]);
|
|
select(undef,undef, undef, 0.0001);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
sub slidelineleft {
|
|
$framesref = shift;
|
|
$oldframe = shift;
|
|
$newframe = shift;
|
|
$cols = shift;
|
|
$rows = shift;
|
|
$timingref = shift;
|
|
|
|
if (defined($$framesref[$oldframe])) {
|
|
my $leftline = "";
|
|
my $rightline = "";
|
|
for ($y = 0; $y < $rows; $y++) {
|
|
for ($x = $cols; $x >= 0; $x--) {
|
|
poscursor(1,$y + 1);
|
|
$leftline = substr($$framesref[$newframe][$y], $x);
|
|
$rightline = substr($$framesref[$oldframe][$y], 0, $x);
|
|
print "$leftline$rightline"; # Don't put a newline on the last line.
|
|
select(undef,undef,undef, 0.001);
|
|
}
|
|
unless ($y + 1 == rows) {
|
|
print "\n";
|
|
}
|
|
#select(undef,undef, undef, $$timingref[$y]);
|
|
select(undef,undef, undef, 0.0001);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
sub fadeoutfadein {
|
|
$framesref = shift;
|
|
$oldframe = shift;
|
|
$newframe = shift;
|
|
$cols = shift;
|
|
$rows = shift;
|
|
$graydientref = shift;
|
|
$wait = shift || 0.03;
|
|
|
|
if (defined($$framesref[$newframe])) {
|
|
foreach my $color (@$graydientref) {
|
|
poscursor(1,1);
|
|
|
|
for ($y = 0; $y < $rows; $y++) {
|
|
print "\033[38;5;${color}m" . $$framesref[$oldframe][$y];
|
|
unless ($y + 1 == $rows) {
|
|
print "\n";
|
|
}
|
|
}
|
|
select(undef,undef,undef, $wait);
|
|
}
|
|
foreach my $color (reverse @$graydientref) {
|
|
poscursor(1,1);
|
|
|
|
for ($y = 0; $y < $rows; $y++) {
|
|
print "\033[38;5;${color}m" . $$framesref[$newframe][$y];
|
|
unless ($y + 1 == $rows) {
|
|
print "\n";
|
|
}
|
|
}
|
|
select(undef,undef,undef, $wait);
|
|
}
|
|
|
|
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
sub poscursor {
|
|
my $x = shift;
|
|
my $y = shift;
|
|
print "\033[${y};${x}H";
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|