App-screenorama
view release on metacpan or search on metacpan
script/screenorama view on Meta::CPAN
#!/usr/bin/env perl
BEGIN {
@Screenorama::ARGV = do {
my $i = 0;
for my $arg (@ARGV) {
last if $arg eq '--';
$i++;
}
splice @ARGV, $i, @ARGV, ();
};
shift @Screenorama::ARGV;
}
use Mojolicious::Lite;
use Mojo::IOLoop::ReadWriteFork;
my $config = $ENV{MOJO_CONFIG} ? plugin 'Config' : app->config;
my ($program, @program_args) = @Screenorama::ARGV ? @Screenorama::ARGV : split /\s/, ($ENV{SCREENORAMA_COMMAND} || '');
$config->{conduit} ||= $ENV{SCREENORAMA_CONDUIT} || 'pty';
$config->{kill_signal} ||= $ENV{SCREENORAMA_KILL_SIGNAL} || 15;
$config->{program_args} ||= \@program_args;
$config->{program} ||= $program or die "Usage: $0 -- <program> [args]\n";
$config->{single} //= $ENV{SCREENORAMA_SINGLE} || 0;
$config->{stdin} //= $ENV{SCREENORAMA_STDIN} || 0;
get '/' => sub {
my $c = shift;
my $url = $c->req->url->to_abs;
$c->render('index', stream_base => $url->clone->base->scheme($url->scheme =~ /https/ ? 'wss' : 'ws'));
};
if ($config->{single}) {
my $plugins = app->plugins;
my ($fork, $last);
websocket '/stream' => sub {
my $c = shift;
my $cb = sub { $c->send({json => $_[1]}) };
unless ($fork) {
$fork = _start_application($c);
$fork->on(close => sub { $plugins->emit(output => $last = {exit_value => $_[1], signal => $_[2]}) });
$fork->on(error => sub { $plugins->emit(output => $last = {error => $_[1]}) });
$fork->on(read => sub { $plugins->emit(output => {output => $_[1]}) });
}
$plugins->on(output => $cb);
$c->on(finish => sub { $plugins->unsubscribe(output => $cb); });
$c->send({json => $last}) if $last;
};
}
else {
websocket '/stream' => sub {
my $c = shift;
my $fork = _start_application($c);
Scalar::Util::weaken($c);
$c->on(finish => sub { $fork->kill($config->{kill_signal}); undef $fork; });
$fork->on(close => sub { $c->send({json => {exit_value => $_[1], signal => $_[2]}})->finish });
$fork->on(error => sub { $c->send({json => {error => $_[1]}})->finish });
$fork->on(read => sub { $c->send({json => {output => $_[1]}}) });
};
}
app->start;
#=============================================================================
sub _start_application {
my $c = shift;
my $fork = Mojo::IOLoop::ReadWriteFork->new;
Mojo::IOLoop->stream($c->tx->connection)->timeout(60);
$fork->start(conduit => $config->{conduit}, program => $config->{program}, program_args => $config->{program_args});
$c->on(json => sub { $fork->write(chr $_[1]->{key}) if $_[1]->{key} }) if $c->app->config->{stdin};
$c->send({json => {program => $program, program_args => \@program_args}});
$fork;
}
__DATA__
@@ index.html.ep
<!DOCTYPE html>
<html>
<head>
<title>screenorama - <%= app->config->{program} %> <%= join ' ', @{app->config->{program_args}} %></title>
%= stylesheet begin
body { background: #111; padding: 8px; }
body, .shell { font-size: 13px; font-family: monospace; color: #eee; margin: 0; padding: 0; }
pre { margin: 0; padding: 0; }
.status { border-top: 1px solid #333; margin-top: 20px; }
input { position: absolute; left: -600px; }
% end
%= javascript begin
var termColors = { // from http://flatuicolors.com/
'30': '#000000',
'31': '#c0392b',
'32': '#2ecc71',
'33': '#f1c40f',
'34': '#48a2df',
'35': '#9b59b6',
'36': '#1abc9c',
'37': '#ecf0f1',
};
var screenorama = function() {
var cursor = document.querySelector('.cursor');
var input = document.querySelector('input');
var attach = function(elem, events, cb) {
events = events.split(' ');
for (var i = 0; i < events.length; i++) elem.addEventListener(events[i], cb, false);
};
screenorama.buf = '';
screenorama.ws = new WebSocket('<%= $stream_base %>/stream');
% if (app->config->{stdin}) {
cursor.visible = true;
( run in 1.678 second using v1.01-cache-2.11-cpan-39bf76dae61 )