Ace128 has asked for the wisdom of the Perl Monks concerning the following question:

Greetings Monks,

Ok, I'm trying to do this Console/Teminal interaction shell... So far I've done the following which is the closest I've gotten to what I want:
#!/usr/bin/perl use Term::ReadKey; use strict; use warnings; startListening(); #$| = 1; #$|++; #select(STDOUT); #$| = 1; sub startListening { # Disable CTRL keys ReadMode(4); my $word = ''; print "> "; while(1) { my $char; if (defined ($char = ReadKey(0))){ print $char; } my $ord = ord($char); last if ($ord == 3); if ($ord == 10 or $ord == 13) { # "Enter" enterPressed($word); $word = ""; print "> "; next; } elsif ($ord == 127) { # ie "Backspace" #chop($word); #print chr(8),' ',chr(8); print "BACKSPACE!\n"; next; } elsif ($ord == 27) { #clearstream(); #$ord="\0"; } elsif ($ord == 8) { print "BACKSPACE!\n"; } #if($ord != "\0"){ #$word .= chr($ord); #print '*'; #} } ReadMode(0); } sub clearstream { while(1) { my $char; if (defined ($char = ReadKey(-1)) ){}else{return} } return; } sub enterPressed { my $word = shift; print "WORD: " . $word . "\n"; }
Now, the problem I have here running this in Window XP, is that I have to press another key (several times) just after a Enter/Return press to show the "WORD..." output. Why is that and how to fix this? Seems like its cached or not flushed properly?

I've tested out with ReadMode(0); but then I can't make the "BACKSPACE!" work. Although I may not need all those special keys, it would be nice to make it work just in case...

Oh, and yea, I know about Term::ReadLine and all those Zoid and Gnu submodules... but Zoid didn't really work in Windows... Got some wierd output by keypress which I shouldn't get... And, with ReadKey its not that big step to make it work as ReadLine (that is, save all keypresses in a scalar, and use this "command" saved in a scalar when enter is pressed)...plus that ReadKey can be non-blocking aswell!

Thanks,
Ace

Replies are listed 'Best First'.
Re: Term::ReadKey problem!
by roboticus (Chancellor) on Jun 15, 2006 at 12:17 UTC
    Ace128:

    I had a little fun playing with your program. I don't know if I solved the problem you're having or not. (Mostly because I was having so much fun, I forgot what the problem was while playing around.) The original version had a few quirks, but this one works better on my machine. Please give it a try and let me know what you think...

    #!/usr/bin/perl -w use strict; use warnings; use Term::ReadKey; startListening(); sub startListening { ReadMode(4); # RAW mode (we'll do all the work!) my $echofl=1; # 1=Normal, 0=Password mode my $echoPwChar='#'; # Password mode char my $word = ''; # Input accumulator my $prompt = "> "; print $prompt; while (1) { my $char = ReadKey(0); my $ord = ord($char); if ($ord == 3) { # "\003" (^C) : STOP! print "\n^C -- terminated!\n"; last; } elsif ($ord == 10 or $ord == 13) { # "\n" (^J), "\r" (^M) : Finish line enterPressed($word); $word = ""; print $prompt; } elsif ($ord == 5) { # "\005" (^E) : Toggle echo mode $echofl = 1-$echofl; } elsif ($ord==127 or $ord==8) { # "\177" (DEL), "\010" (^H) : Kill last char print chr(8),' ',chr(8) if length($word)>0; chop($word); } elsif ($ord == 27) { # "\033" (Esc) : Clear line print chr(8) x length($word) . ' ' x length($word) . "\r" . $prompt; $word=""; } else { #if ($ord != 0) { # Otherwise add to word & update display $word .= $char; $char = $echoPwChar if !$echofl; print $char; } } ReadMode(0); } sub enterPressed { my $word = shift; print "\n '" . $word . "'\n"; }
    --roboticus
      Hey, cool! Fun when code inspire ;)

      Anyway, no, didn't solve my problem, but hey, that the ESC empties the line is quite a neat idea!
Re: Term::ReadKey problem!
by Joost (Canon) on Jun 15, 2006 at 08:43 UTC
    Well, it sort of works for me on linux. That is: i can enter chars, see the "BACKSPACE!" output when i press backspace and see "WORD:" when i press enter. Ofcourse, since you're interpreting every keystroke yourself it fails to recognize EOF (^D) and a lot of other standard console shortcuts.

    If you just want a lowlevel line-reading routine, you're probably better off with something like:

    print "> "; while (<STDIN>) { chomp; print "WORD: $_\n> "; }

    Have you tried re-insterting the $|=1 line?

    updated: fixed some grammar and spelling mistakes

      Seems to be working fine in Linux, but not in Windows (as usuall). So typical! What to do now? This way I could do whatever on those special keys. So, yep, bug in windows. Enter key is eaten up in readmode 4.
Re: Term::ReadKey problem!
by strat (Canon) on Jun 15, 2006 at 11:32 UTC

    IO::Prompt might be interesting for you

    Best regards,
    perl -e "s>>*F>e=>y)\*martinF)stronat)=>print,print v8.8.8.32.11.32"

      Runing the example provided by the module:
      use IO::Prompt; use strict; use warnings; while( prompt "next: " ) { print "You said '$_'\n"; }
      gives: Cannot write to terminal: No such file or directory at ioPromptTests.pl line 2

      In windows XP...

        Sorry, I forgot to check it under windows as well. But since it uses /dev/tty, it won't run :-(

        Best regards,
        perl -e "s>>*F>e=>y)\*martinF)stronat)=>print,print v8.8.8.32.11.32"

Re: Term::ReadKey problem!
by Ace128 (Hermit) on Jun 15, 2006 at 21:16 UTC
    Ok, after some work I solved most problems for the Win XP. However, now there is problems with the special keys like HOME/END/PGUP/LEFT ARROW ... and those... All I get back from ReadKey is 0. I've found some tricks that was supposed to solve it on the net (urls included in the code), but they didn't work! :(
    #!/usr/bin/env perl use Term::ReadKey; use Time::HiRes qw( usleep ); use Data::Dumper; #use IO::Handle; use strict; use warnings; # http://www.perl.com/doc/FAQs/FAQ/oldfaq-html/Q4.31.html # http://www.rocketaware.com/perl/perlfaq5/How_can_I_read_a_single_cha +racte.htm # Works only in linux: # http://groups.google.com/group/linux.debian.user/browse_thread/threa +d/eaaedcaae8bc3e9a/8109745493b41a58?lnk=st&q=pgup+perl+readkey&rnum=5 +&hl=sv#8109745493b41a58 # PC 2-byte keycodes = ^@ + the following: # HEX KEYS # --- ---- # 0F SHF TAB # 10-19 ALT QWERTYUIOP # 1E-26 ALT ASDFGHJKL # 2C-32 ALT ZXCVBNM # 3B-44 F1-F10 # 47-49 HOME,UP,PgUp # 4B LEFT # 4D RIGHT # 4F-53 END,DOWN,PgDn,Ins,Del # 54-5D SHF F1-F10 # 5E-67 CTR F1-F10 # 68-71 ALT F1-F10 # 73-77 CTR LEFT,RIGHT,END,PgDn,HOME # 78-83 ALT 1234567890-= # 84 CTR PgUp $| = 1; #$|++; #select(STDOUT); #$| = 1; #my $io = new IO::Handle; #if ($io->fdopen(fileno(STDOUT),"w")) { # $io->print("Some text\n"); #} #autoflush STDOUT 1; #my $old_ioctl; #BEGIN { # $old_ioctl = ioctl(STDIN,0,0) || -1; # Gets device info # $old_ioctl &= 0xff; # ioctl(STDIN,1,$old_ioctl | 32); # Writes it back, setting bit +5 #} my %key_cmd_map = ( # escseq command '[A' => sub { print "Up\n" }, # those escape sequences shoul +d '[B' => sub { print "Down\n" }, # work in the linux console '[C' => sub { print "Right\n" }, '[D' => sub { print "Left\n" }, '[G' => sub { print "Center\n" }, '[5~' => sub { print "PgUp\n" }, '[6~' => sub { print "PgDn\n" }, '[1~' => sub { print "Home\n" }, '[4~' => sub { print "End\n" }, '[[A' => sub { print "F1\n" }, '[2~' => sub { print "Insert Key" }, # Insert key '[3~' => sub { print "Delete Key" }, # Delete key ); startListening(); sub startListening { # Disable CTRL keys ReadMode(4); my $word = ''; my $prompt = "> "; my $pos = 0; print $prompt; while(1){ my $char; while (!defined ($char = ReadKey(-1))){ usleep 1_100; } #sysread(STDIN,$char,1); # Read a single charact +er my $ord = ord($char); if ($ord == 0) { #ioctl(STDIN,1,$old_ioctl); # Sets it back to cook +ed mode. #print Dumper($old_ioctl); #sysread(STDIN,$char,1); #while (!defined ($char = ReadKey(-1))){ # sleep 1; # print "#\n"; #} #print "NEW: " . hex(ord($char)) . "\n"; } last if ($ord == 3); last if ($ord == 4); print $char; #print $ord; if ($ord == 10 or $ord == 13) { # "Enter" print chr(8) x (length($word) + length($prompt)) . ' ' x (length($word) + length($prompt)) . "\r" ; enterPressed($word); $word = ""; print $prompt; $pos = 0; next; } elsif ($ord == 127) { # ie "Backspace" #chop($word); #print chr(8),' ',chr(8); #print "BACKSPACE!\n"; next; } elsif ($ord == 27) { # "ESC" print chr(8) x (length($word) + 1) . ' ' x (length($word) + 1) . "\r" . $prompt; $word=""; $pos = 0; } elsif ($ord == 8) { if ($pos == 0) { print " "; next; } --$pos; #print "BACKSPACE!\n"; #print chr(8) . ' ' . chr(8); chop($word); print " " . chr(8); next; } if($ord >= 32 && $ord < 155) { ++$pos; $word .= chr($ord); #print '*'; } } ReadMode(0); } sub clearstream { while(1) { my $char; if (defined ($char = ReadKey(-1)) ){}else{return} } return; } sub enterPressed { my $word = shift; print "WORD: " . $word . "\n"; }
    ANY suggestion is warmly welcomed! Since all that is left now is to somehow make it possible for me to know what "special" key is pressed.

    Thanks,
    Ace
A reply falls below the community's threshold of quality. You may see it by logging in.