The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl
#
# Example script showing how to use Term::VT102 with Net::Telnet. Telnets to
# localhost and dumps what Term::VT102 thinks should be on the screen.
#

use Net::Telnet qw(TELOPT_TTYPE);
use Term::VT102;
use IO::Handle;
use strict;

$| = 1;

my ($host, $port) = ('localhost', 23);

my $t = new Net::Telnet (
  'Host' => $host,
  'Port' => $port,
  'Errmode' => 'return',
  'Timeout' => 1,
  'Output_record_separator' => '',
);

die "failed to connect to $host:$port" if (not defined $t);

$t->option_callback (\&opt_callback);
$t->option_accept ('Do' => TELOPT_TTYPE);
$t->suboption_callback (\&subopt_callback);

my $vt = Term::VT102->new (
  'cols' => 80,
  'rows' => 23,
);

# Convert linefeeds to linefeed + carriage return.
#
$vt->option_set ('LFTOCRLF', 1);

# Make sure line wrapping is switched on.
#
$vt->option_set ('LINEWRAP', 1);

# Set up the callback for OUTPUT; this callback function simply sends
# whatever the Term::VT102 module wants to send back to the terminal and
# sends it to Net::Telnet - see its definition below.
#
$vt->callback_set ('OUTPUT', \&vt_output, $t);

my ($telnetbuf, $io, $stdinbuf);

$io = new IO::Handle;
$io->fdopen (fileno(STDIN), 'r');
$io->blocking (0);

while (1) {
	$telnetbuf = $t->get ('Timeout' => 1);

	last if ($t->eof ());

	$vt->process ($telnetbuf) if (defined $telnetbuf);

# Do your stuff here - use $vt->row_plaintext() to see what's on various
# rows of the screen, for instance, or before this main loop you could set
# up a ROWCHANGE callback which checks the changed row, or whatever.
#
# In this example, we just pass standard input to the telnet stream, we take
# the data coming back from Net::Telnet and pass it to the Term::VT102
# object, and then we repeatedly dump the Term::VT102 screen.
#
# Note that using this example as a telnet program isn't really a good idea
# since reading standard input as well as reading from Net::Telnet is a bit
# fiddly and we're doing it wrong here. This is just proof-of-concept stuff.

	# Read key presses from standard input (you'll probably need to hit
	# Return before anything appears, though) and pass them to
	# Net::Telnet.
	#
	$stdinbuf = '';
	if (defined $io->sysread ($stdinbuf, 16)) {
		$t->print ($stdinbuf);
	}

	# Dump what Term::VT102 thinks is on the screen.
	#
	print "\e[H";	# return to the top left of the (real) screen
	for (my $row = 1; $row <= $vt->rows (); $row++) {
		printf "%s\n", $vt->row_plaintext ($row);
	}
	printf "Cursor position: (%d, %d)",
	  $vt->x (),
	  $vt->y ();
}

$t->close ();
print "\n";


# Callback for "DO" handling - for Net::Telnet.
#
sub opt_callback {
	my ($obj,$opt,$is_remote,$is_enabled,$was_enabled,$buf_position) = @_;

	if ($opt == TELOPT_TTYPE and $is_enabled and !$is_remote) {
		#
		# Perhaps do something if we get TELOPT_TTYPE switched on?
		#
	}

	return 1;
}


# Callback for sub-option handling - for Net::Telnet.
#
sub subopt_callback {
	my ($obj, $opt, $parameters) = @_;
	my ($ors_old, $otm_old);

	# Respond to TELOPT_TTYPE with "I'm a VT102".
	#
	if ($opt == TELOPT_TTYPE) {
		$ors_old = $obj->output_record_separator ('');
		$otm_old = $obj->telnetmode (0);
		$obj->print (
		  "\xff\xfa",
		  pack ('CC', $opt, 0),
		  'vt102',
		  "\xff\xf0"
		);
		$obj->telnetmode ($otm_old);
		$obj->output_record_separator ($ors_old);
	}

	return 1;
}


# Callback for OUTPUT events - for Term::VT102.
#
sub vt_output {
	my ($vtobject, $type, $arg1, $arg2, $private) = @_;

	if ($type eq 'OUTPUT') {
		$private->print ($arg1);
	}
}

# EOF