use 5.010; { package Ask; use Carp qw(croak); use File::Which qw(which); sub new { if (-t STDIN and -t STDOUT) { return "Ask::STDIO"->new; } if (my $zenity = which('zenity')) { return "Ask::Zenity"->new(zenity => $zenity); } croak "Could not establish an appropriate Ask backend"; } } { package Ask::API; use Moo::Role; requires 'entry'; # get a string of text requires 'info'; # display a string of text sub warning { my ($self, %o) = @_; $o{text} = "WARNING: $o{text}"; return $self->info(%o); } sub error { my ($self, %o) = @_; $o{text} = "ERROR: $o{text}"; return $self->info(%o); } sub question { my ($self, %o) = @_; $o{cancel} //= qr{^(no|n|cancel)$}i; my $response = $self->entry(text => $o{text}); return !!1 if $response ~~ $o{ok}; return !!0 if $response ~~ $o{cancel}; return !!1; } sub file_selection { my ($self, %o) = @_; if ($o{multiple}) { $self->info(text => $o{text} // 'Enter file names (blank to finish)'); my @filenames; while (my $f = $self->entry) { push @filenames, $f; } return @filenames; } else { return $self->entry(text => ($o{text} // 'Enter file name')); } } } { package Ask::STDIO; use Moo; use namespace::sweep; with 'Ask::API'; sub entry { my ($self, %o) = @_; $self->info(text => $o{text}) if exists $o{text}; my $line; if ($o{hide_text}) { require Term::ReadKey; Term::ReadKey::ReadMode('noecho'); chomp( $line = ); Term::ReadKey::ReadMode(0); } else { chomp( $line = ); } return $line; } sub info { my ($self, %o) = @_; say STDOUT $o{text}; } sub warning { my ($self, %o) = @_; say STDERR "WARNING: $o{text}"; } sub error { my ($self, %o) = @_; say STDERR "ERROR: $o{text}"; } } { package Ask::Zenity; use Moo; use aliased 'System::Command'; use namespace::sweep; has zenity_path => ( is => 'ro', isa => sub { die "$_[0] not executable" unless -x $_[0] }, default => sub { '/usr/bin/zenity' }, ); with 'Ask::API'; sub _optionize { my $opt = shift; $opt =~ s/_/-/g; return "--$opt"; } sub _zenity { my ($self, $cmd, %o) = @_; my $zen = Command->new( $self->zenity_path, _optionize($cmd), map sprintf('%s=%s', _optionize($_), $o{$_}), keys %o, ); # warn join q[ ], $zen->cmdline; return $zen; } sub entry { my $self = shift; my $text = readline($self->_zenity(entry => @_)->stdout); chomp $text; return $text; } sub info { my $self = shift; $self->_zenity(info => @_); } sub warning { my $self = shift; $self->_zenity(warning => @_); } sub error { my $self = shift; $self->_zenity(error => @_); } sub question { my $self = shift; my $zen = $self->_zenity(error => @_); $zen->close; return not $zen->exit; } sub file_selection { my $self = shift; my $text = readline($self->_zenity(file_selection => @_)->stdout); chomp $text; return split m#[|]#, $text; } } #### # Usage... my $ask = Ask->new; say "GOT: ", $ask->entry(text => "Please enter your password!", hide_text => 1);