268 lines
6.5 KiB
Perl
268 lines
6.5 KiB
Perl
|
# ex:ts=8 sw=4:
|
||
|
# $OpenBSD: MiniCurses.pm,v 1.19 2023/07/03 14:01:58 espie Exp $
|
||
|
#
|
||
|
# Copyright (c) 2010-2013 Marc Espie <espie@openbsd.org>
|
||
|
#
|
||
|
# Permission to use, copy, modify, and distribute this software for any
|
||
|
# purpose with or without fee is hereby granted, provided that the above
|
||
|
# copyright notice and this permission notice appear in all copies.
|
||
|
#
|
||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
use v5.36;
|
||
|
|
||
|
package DPB::MiniCurses;
|
||
|
use Term::Cap;
|
||
|
use Term::ReadKey;
|
||
|
use constant {
|
||
|
BLACK => 0,
|
||
|
RED => 1,
|
||
|
GREEN => 2,
|
||
|
YELLOW => 3,
|
||
|
BLUE => 4,
|
||
|
PURPLE => 5,
|
||
|
TURQUOISE => 6,
|
||
|
WHITE => 7 };
|
||
|
|
||
|
sub refresh($self)
|
||
|
{
|
||
|
$self->{write} = 'go_write_home';
|
||
|
$self->{force} = 1;
|
||
|
}
|
||
|
|
||
|
sub handle_window($self)
|
||
|
{
|
||
|
$self->refresh;
|
||
|
}
|
||
|
|
||
|
sub width($self)
|
||
|
{
|
||
|
return $self->{state}->width;
|
||
|
}
|
||
|
|
||
|
sub height($self)
|
||
|
{
|
||
|
return $self->{state}->height;
|
||
|
}
|
||
|
|
||
|
sub create_terminal($self)
|
||
|
{
|
||
|
my $oldfh = select(STDOUT);
|
||
|
$| = 1;
|
||
|
# XXX go back to totally non-buffered raw shit
|
||
|
binmode(STDOUT, ':pop');
|
||
|
select($oldfh);
|
||
|
use POSIX;
|
||
|
my $termios = POSIX::Termios->new;
|
||
|
$termios->getattr(0);
|
||
|
$self->{terminal} = Term::Cap->Tgetent({ OSPEED =>
|
||
|
$termios->getospeed });
|
||
|
$self->{home} = $self->{terminal}->Tputs("ho", 1);
|
||
|
$self->{clear} = $self->{terminal}->Tputs("cl", 1);
|
||
|
$self->{down} = $self->{terminal}->Tputs("do", 1);
|
||
|
$self->{glitch} = exists $self->{terminal}{_xn};
|
||
|
$self->{cleareol} = $self->{terminal}->Tputs("ce", 1);
|
||
|
if ($self->{state}{color}) {
|
||
|
$self->{bg} = $self->{terminal}->Tputs('AB', 1);
|
||
|
$self->{fg} = $self->{terminal}->Tputs('AF', 1);
|
||
|
$self->{blink} = $self->{terminal}->Tputs('mb', 1);
|
||
|
$self->{dontblink} = $self->{terminal}->Tputs('me', 1);
|
||
|
$self->{clear} = sprintf($self->{fg}, WHITE).
|
||
|
sprintf($self->{bg}, BLACK).$self->{clear};
|
||
|
}
|
||
|
if ($self->{state}{nocursor}) {
|
||
|
$self->{invisible} =
|
||
|
$self->{terminal}->Tputs("vi", 1);
|
||
|
$self->{visible} =
|
||
|
$self->{terminal}->Tputs("ve", 1);
|
||
|
}
|
||
|
if ($self->{home}) {
|
||
|
$self->{write} = "go_write_home";
|
||
|
} else {
|
||
|
$self->{write} = "write_clear";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub write_clear($self, $msg)
|
||
|
{
|
||
|
my $r = $self->{clear};
|
||
|
$self->{oldlines} = [$self->cut_lines($msg)];
|
||
|
my $n = 2;
|
||
|
for my $line (@{$self->{oldlines}}) {
|
||
|
last if $n++ > $self->height;
|
||
|
$r .= $self->clamped($line);
|
||
|
}
|
||
|
print $r;
|
||
|
}
|
||
|
|
||
|
sub cut_lines($self, $msg)
|
||
|
{
|
||
|
my @lines = ();
|
||
|
for my $line (split("\n", $msg)) {
|
||
|
while (length $line > $self->width) {
|
||
|
push(@lines, substr($line, 0, $self->width));
|
||
|
$line = substr($line, $self->width);
|
||
|
}
|
||
|
push(@lines, $line);
|
||
|
}
|
||
|
return @lines;
|
||
|
}
|
||
|
|
||
|
sub default_fg($self, $color)
|
||
|
{
|
||
|
$self->{resetfg} = sprintf($self->{fg}, $color);
|
||
|
}
|
||
|
|
||
|
sub default_bg($self, $color)
|
||
|
{
|
||
|
$self->{resetbg} = sprintf($self->{bg}, $color);
|
||
|
}
|
||
|
sub color($self, $expr, $color)
|
||
|
{
|
||
|
return sprintf($self->{fg}, $color).$expr.$self->{resetfg};
|
||
|
}
|
||
|
|
||
|
sub bg($self, $expr, $color)
|
||
|
{
|
||
|
return sprintf($self->{bg}, $color).$expr.$self->{resetbg};
|
||
|
}
|
||
|
|
||
|
sub blink($self, $expr)
|
||
|
{
|
||
|
return $self->{blink}.$expr.$self->{dontblink};
|
||
|
}
|
||
|
|
||
|
sub mogrify($self, $line)
|
||
|
{
|
||
|
my $percent = PURPLE;
|
||
|
$self->default_bg(BLACK);
|
||
|
$self->default_fg(WHITE);
|
||
|
if ($line =~ m/waiting-for-lock/) {
|
||
|
$line = $self->color($line, BLUE);
|
||
|
$self->default_fg(BLUE);
|
||
|
} elsif ($line =~ m/stuck on/ || $line =~ m/locked by/) {
|
||
|
$line = $self->bg($self->color($line, BLACK), RED);
|
||
|
$self->default_bg(RED);
|
||
|
$self->default_fg(BLACK); $percent = WHITE;
|
||
|
} elsif ($line =~ m/frozen/) {
|
||
|
if ($line =~ m/for\s+\d+\s*(mn|HOURS)/) {
|
||
|
$line = $self->bg($self->color($line, BLACK), RED);
|
||
|
$self->default_bg(RED);
|
||
|
$self->default_fg(BLACK);
|
||
|
$percent = WHITE;
|
||
|
} else {
|
||
|
$line = $self->color($line, RED);
|
||
|
$self->default_fg(RED);
|
||
|
}
|
||
|
} elsif ($line =~ m/^\</) {
|
||
|
$line = $self->color($line, TURQUOISE);
|
||
|
$self->default_fg(TURQUOISE);
|
||
|
} elsif ($line =~ m/^(LISTING|UPDATING)/) {
|
||
|
$line = $self->bg($self->color($line, WHITE), BLUE);
|
||
|
$self->default_bg(BLUE);
|
||
|
$self->default_fg(WHITE);
|
||
|
} elsif ($line =~ m/^I=/) {
|
||
|
$line = $self->bg($self->color($line, WHITE), BLUE);
|
||
|
} elsif ($line =~ m/^E=/) {
|
||
|
$line = $self->color($line, RED);
|
||
|
$self->default_fg(RED);
|
||
|
} elsif ($line =~ m/^Hosts:/) {
|
||
|
$line =~ s/([\@\w\.\-]*[\@\w.])(\s|\(|$)/$self->color($1, RED).$2/ge;
|
||
|
$line =~ s/([\/\@\w\.\-]+\-)(\s|\(|$)/$self->blink($self->bg($self->color($1, BLACK), RED)).$2/ge;
|
||
|
$line =~ s/(^Hosts:)/$self->color($1, BLUE)/ge;
|
||
|
}
|
||
|
$line =~ s/(STOPPED!)/$self->bg($self->color($1, BLACK), RED)/ge;
|
||
|
$line =~ s/(\[\d+\])/$self->color($1, GREEN)/ge;
|
||
|
$line =~ s/(\(.*?\))/$self->color($1, YELLOW)/ge;
|
||
|
$line =~ s/(\d+\%)/$self->color($1, $percent)/ge;
|
||
|
return $line;
|
||
|
}
|
||
|
|
||
|
sub clamped($self, $line)
|
||
|
{
|
||
|
my $l2 = $line;
|
||
|
if (defined $self->{fg}) {
|
||
|
$l2 = $self->mogrify($l2);
|
||
|
}
|
||
|
if (!$self->{glitch} && length $line == $self->width) {
|
||
|
return $l2;
|
||
|
} else {
|
||
|
return $l2."\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub clear_clamped($self, $line)
|
||
|
{
|
||
|
my $l2 = $line;
|
||
|
if (defined $self->{fg}) {
|
||
|
$l2 = $self->mogrify($l2);
|
||
|
}
|
||
|
if (!$self->{glitch} && length $line == $self->width) {
|
||
|
return $l2;
|
||
|
} else {
|
||
|
return $self->{cleareol}.$l2."\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub do_line($self, $new, $old)
|
||
|
{
|
||
|
# line didn't change: try to go down
|
||
|
if (defined $old && $old eq $new) {
|
||
|
if ($self->{down}) {
|
||
|
return $self->{down};
|
||
|
}
|
||
|
}
|
||
|
# adjust newline to correct length
|
||
|
if (defined $old && (length $old) > (length $new)) {
|
||
|
if ($self->{cleareol}) {
|
||
|
return $self->clear_clamped($new);
|
||
|
}
|
||
|
$new .= " "x ((length $old) - (length $new));
|
||
|
}
|
||
|
return $self->clamped($new);
|
||
|
}
|
||
|
|
||
|
sub lines($self, @new)
|
||
|
{
|
||
|
my $n = 2;
|
||
|
my $r = '';
|
||
|
|
||
|
while (@new > 0) {
|
||
|
return $r if $n++ > $self->height;
|
||
|
$r .= $self->do_line(shift @new, shift @{$self->{oldlines}});
|
||
|
}
|
||
|
# extra lines must disappear
|
||
|
while (@{$self->{oldlines}} > 0) {
|
||
|
my $line = shift @{$self->{oldlines}};
|
||
|
if ($self->{cleareol}) {
|
||
|
$r .= $self->clear_clamped('');
|
||
|
} else {
|
||
|
$line = " "x (length $line);
|
||
|
$r .= $self->clamped($line);
|
||
|
}
|
||
|
last if $n++ > $self->height;
|
||
|
}
|
||
|
return $r;
|
||
|
}
|
||
|
|
||
|
sub write_home($self,$msg)
|
||
|
{
|
||
|
my @new = $self->cut_lines($msg);
|
||
|
print $self->{home}.$self->lines(@new);
|
||
|
$self->{oldlines} = \@new;
|
||
|
}
|
||
|
|
||
|
sub go_write_home($self, $msg)
|
||
|
{
|
||
|
# first report has to clear the screen
|
||
|
$self->write_clear($msg);
|
||
|
$self->{write} = 'write_home';
|
||
|
}
|
||
|
|
||
|
1;
|