$ cat funky_i_o.pl use strict; use warnings; use Term::ReadKey; use Time::HiRes qw( alarm ); # Buffer used to build our command line my $command_line = ""; # Buffer for our (fake) serial data my @serial_data = (); # Put console into raw mode ReadMode 4; setup_serial_interface(); my $are_we_done_yet = 0; while (!$are_we_done_yet) { my $con_key = ReadKey(-1); if (defined $con_key) { if ($con_key eq "\r" or $con_key eq "\n") { # user finished entering the command handle_command($command_line); $command_line = ""; } else { # Not end of line, so add to command line. # NOTE: you'll want to handle backspace and/or # other line editing commands. But that's too # much work for a quick demo... $command_line .= $con_key; } } elsif (is_serial_data_ready()) { # Serial device gave us something to do handle_serial_data(); } else { # Don't eat *ALL* the cpu, since nothing came in from # the console or serial line, pause a brief time to let # something arrive # Here, you'd normally use a sleep or usleep. But alarm # and sleep aren't necessarily on friendly terms with each # other, and we're using alarm to fake a serial device. So # we're not actually going to delay here, but you would want # to in a real application. } } # Restore normal console mode ReadMode(0); sub handle_command { my $cmd = shift; if ($cmd =~ /^q/i) { print "Quitting!\n"; $are_we_done_yet = 1; } elsif ($cmd =~ /^d.*(\d+)/i) { print "Starting a task that takes $1 seconds!\n"; my $t = time; while ($t + $1 > time) { ; # fake process ... just wait until time expires } print "...and now we're done!\n"; } else { print "Unknown command: $cmd\n"; } } ### # Our fake serial interface ### sub setup_serial_interface { my $seconds_per_transmission = 0.567; $SIG{ALRM} = sub { push @serial_data, rand; alarm $seconds_per_transmission; }; # First packet of data should occur in a couple seconds alarm 2.5; } sub is_serial_data_ready { return scalar @serial_data; } sub handle_serial_data { print "We haz teh dataz! (", join(", ", @serial_data), ")\n"; @serial_data = (); }