Re: y/n input in a captive interface
by johngg (Canon) on Feb 07, 2007 at 23:23 UTC
|
Firstly, Term::Readkey is a pretty small module so it's no great overhead to install and use it and it seemed to do everything right whenever I used it. My advice would be to go with that.
However, if you are unable to do that you probably have to go with changing terminal settings by hand. I copied a recipe out of the 2nd edition Camel Book a long, long time ago and I haven't used it in years. It may give you a start though so here it is.
# RawTerm.pm
#
# Adapted from "Programming PERL", Wall/Christiansen/Schwartz, 2nd Edn
+. pp.474
#
package RawTerm;
use Exporter;
@ISA = ('Exporter');
@EXPORT = ('getone');
BEGIN
{
use POSIX qw(:termios_h);
my($term, $oterm, $echo, $noecho, $fd_stdin);
$fd_stdin = fileno(STDIN);
$term = POSIX::Termios->new();
$term->getattr($fd_stdin);
$oterm = $term->getlflag();
$echo = ECHO | ECHOK | ICANON;
$noecho = $oterm & ~$echo;
sub cbreak
{
$term->setlflag($noecho);
$term->setcc(VTIME, 1);
$term->setattr($fd_stdin, TCSANOW);
}
sub cooked
{
$term->setlflag($oterm);
$term->setcc(VTIME, 0);
$term->setattr($fd_stdin, TCSANOW);
}
sub getone
{
my $key = "";
cbreak();
sysread(STDIN, $key, 1);
cooked();
return $key;
}
}
END
{
cooked();
}
1;
If I remember correctly you just call the getone() subroutine to get a single character without needing the <Enter> key. I hope this is of use. Cheers, JohnGG | [reply] [d/l] [select] |
|
|
Firstly, Term::Readkey is a pretty small module so it's no great overhead to install and use it and it seemed to do everything right whenever I used it. My advice would be to go with that.
It's not much overhead for the processor or RAM, but it's a fair bit of overhead for someone who isn't a Perl programmer and is thus unlikely to be familiar with the task of installing nonstandard Perl modules. It's more end-user overhead that I am to avoid, rather than system overhead, in this case.
As things currently stand, I have a workaround built into the thing so that it uses Term::ReadKey if it exists on the system (using an eval), but it's pretty kludgey, and it ends up having less slick, intuitive behavior for the less technically savvy user as a result -- pretty much the exact opposite of a desirable state of affairs.
As for your example code, it unfortunately looks pretty environment-specific (POSIX::Termios makes me think so, anyway). I prefer to avoid assumptions about the operating environment as much as possible when writing stuff like this, where its usefulness is not limited to a particular OS by definition. I appreciate the attempt at helping, though it ultimately doesn't really provide what I need.
| print substr("Just another Perl hacker", 0, -2); |
|
- apotheon
CopyWrite Chad Perrin |
| [reply] [d/l] |
Re: y/n input in a captive interface
by liverpole (Monsignor) on Feb 07, 2007 at 23:33 UTC
|
Hi apotheon,
I'm assuming, since you mentioned bash and tcsh that you're in Linux or Unix.
I've always used stty in conjunction with dd for this kind of thing. For example, the following should help you get there:
use strict;
use warnings;
$| = 1;
print "Enter some characters below, <Escape> to quit\n";
chomp(my $stty = `stty -g`); # Save state
system("stty -icrnl -icanon -echo min 0 time 0"); # Raw mode
while (1) {
my $k = `dd bs=1 count=1 <&0 2>/dev/null`;
next unless $k; # No character entered
last if (27 == ord $k); # Finish if char is <Escape>
printf "Ascii = %d\n", ord($k);
}
system("stty $stty"); # Restore cooked mode (thanks, sgt!)
print "\nDone!\n";
It reads characters one at a time, displaying their ascii value, until you type an Escape. Of course, you could easily modify it to read only a single character, or as many as you need.
Good luck!
Update: Good catch sgt. I've added the line system("stty $stty"); to restore "cooked" mode.
s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
| [reply] [d/l] [select] |
|
|
| [reply] |
|
|
Thanks for the suggestion, but unfortunately something that requires backticks or something like system() tends to be exactly the sort of thing (other than non-core modules) I wanted to avoid. It really hurts portability.
Your assumption about using a Unixlike OS is correct -- I'm on FreeBSD -- but I don't want to limit the usefulness of this script to OSes that include the standard unixy core utilities. It's bad enough requiring a specific language interpreter (Perl's, specifically) to run the thing.
| print substr("Just another Perl hacker", 0, -2); |
|
- apotheon
CopyWrite Chad Perrin |
| [reply] [d/l] |
Re: y/n input in a captive interface
by shmem (Chancellor) on Feb 08, 2007 at 12:53 UTC
|
None of the answers so far seem to be to your liking. Sounds like a "wash but don't wet" challenge.
The problem you have is the line discipline of the terminal. To read a single char
from it, you must set it into cbreak mode.
With the perl core, you can do that only with POSIX. With system utilities, use stty
(afaik there's no UNIX or UNIX like or Linux out there which doesn't have that
basic utility). After having set your terminal with either method, it's just sysread:
#!/usr/bin/perl
system 'stty','cbreak' and die;
sysread STDIN,$a,1,0;
print "\n",$a =~ /y/ ? "yup\n" : "nope\n";
system 'stty','-cbreak' and die;
For the same with POSIX see johngg's answer. POSIX::Termios is in $Config::Config{installarchlib}/auto/POSIX.so, so it is available with every *nix perl.
--shmem
_($_=" "x(1<<5)."?\n".q·/)Oo. G°\ /
/\_¯/(q /
---------------------------- \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
| [reply] [d/l] [select] |
|
|
Does POSIX work for, say, Windows and Plan 9, too? I'm not too worried about BeOS, I guess. I'm asking because I honestly don't know.
| print substr("Just another Perl hacker", 0, -2); |
|
- apotheon
CopyWrite Chad Perrin |
| [reply] |
|
|
No - at least not within ActiveState Perl:
POSIX::termios not implemented on this architecture at C:/Perl/site/li
+b/RawTerm.
pm line 15.
BEGIN failed--compilation aborted at C:/Perl/site/lib/RawTerm.pm line
+43.
--shmem
_($_=" "x(1<<5)."?\n".q·/)Oo. G°\ /
/\_¯/(q /
---------------------------- \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
| [reply] [d/l] |
Re: y/n input in a captive interface
by Moron (Curate) on Feb 08, 2007 at 13:42 UTC
|
You could always use the source of Term::Readkey as a reference to build what you want in whatever way does meet your criteria - assuming that's possible of course ;)
| [reply] |
|
|
I'm sure it does, since Term::ReadKey (now that I've read more about it) seems to only use modules in the core Perl distribution and actually seems to do what I want. I just need to get over the fact that it's not nearly as easy to accomplish as it seems like it should be. I guess I'm just mystified at the fact that Perl can handle automatic translation of newline characters and (back)slashes between systems invisibly, but can't do the same for terminal interactions like this.
| print substr("Just another Perl hacker", 0, -2); |
|
- apotheon
CopyWrite Chad Perrin |
| [reply] |
Re: y/n input in a captive interface
by Corion (Patriarch) on Feb 08, 2007 at 12:56 UTC
|
I guess you won't get better than ExtUtils::MakeMaker::prompt, which doesn't do the "one-key" feature you want.
| [reply] |