Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Non Blocking input on Win32 platforms

by MisterH (Novice)
on Oct 31, 2008 at 10:53 UTC ( #720675=perlquestion: print w/replies, xml ) Need Help??
MisterH has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to spoof non-blocking reads on a Win32 client. I have spent a lot of time looking around at various code examples. The vec() method on <STDIN> does not work. ReadLine in TERM::Readkey doesn't work either. but the ReadKey function does. Here is my code;
#!/usr/bin/perl use Term::ReadKey; until ($Name){ print "Your callsign?>"; chomp ($Name = <>); if ($Name) { print "say: !! to exit\n\n"; open (TX, ">>d:/telnet/say.txt"); printf TX time." $Name entered.\n"; close (TX); } } $Lastime = time; while($key.$Input ne "!!"){ open (RX, "<d:/telnet/say.txt"); @Said = <RX>; close (RX); foreach $Said(@Said){ $Said =~ m/(^\d+)(\s[\D \d]*)/; if ($1 >= $Lastime){ print $2; Tmptime = $1; } } $Lastime = $Tmptime + 1; if ($key = ReadKey 2){ print $key; if (ord($key) != 13){ chomp($Input = <>); } else { $key=""; $Input = ""; } open (TX, ">>d:/telnet/say.txt"); printf TX time." $Name said: $key$Input\n"; close (TX); } } open (TX, ">>d:/telnet/say.txt"); printf TX time." $Name left.\n"; close (TX); print "\n\nBye.";
It's a basic realtime chat client. It's not entirely bulletproof and it's not very efficient because it reads the whole say.txt file every 2 seconds. I can work on these limitations later.

The part I really need to fix is; Because the first keypress interrupts the ReadKey block it has to be printed to the console seperately. Thus if the user wants to delete their input, they can't delete the first character. Is there any way to inject it into the console instead?

I know this thing is built with dark magic and it has inherrent corruption but I have been on this quest for weeks and this is the best solution I have come up with so far. All suggestions for improvement would be greatly appreciated.

Replies are listed 'Best First'.
Re: Non Blocking input on Win32 platforms
by BrowserUk (Pope) on Oct 31, 2008 at 13:23 UTC

      It's worth mentioning that select only works on sockets under win32. Win32::Socketpair, provides a work around.

      I've been working with Win32::Socketpair and I ran into a few snags. I haven't had time to contact the author with a proper bug report yet, but it is important to know that because winopen2 uses system(1, ...) to spawn child processes, you can only launch 64 processes under windows before it stops working. It is a simple matter to change the code to use Win32::Process::Create to spawn children. The other snag that I just encountered yesterday and haven't found a work around for yet is that using PerlApp 6 to bind your executables as GUI programs, means that STDIO to any children gets messed up in some mysterious way.

      With those caveats, IO::Select makes it easy to do non-blocking IO.

      TGI says moo

      that doesn't require any graphics libraries to be downloaded*

      Once again, Glib is NOT a graphics library, it is a basic eventloop system, upon which the Gtk2 graphic system is based. The eventloop system makes it easier to watch the filehandle from the main thread, AND to do other things in the main thread without a cumbersome while(1) loop. It also works cross-platform, which can't be said for the Win32 modules.

      I'm not really a human, but I play one on earth Remember How Lucky You Are

        Let's see what you are advocating here.

        use Glib; use Term::ReadKey; use threads; #use threads::shared; $|++; ReadMode('cbreak'); # works non-blocking if read stdin is in a thread my $count = 0; my $thr = threads->new(\&read_in)->detach; my $main_loop = Glib::MainLoop->new; my $timer = Glib::Timeout->add (1000, \&timer_callback, undef, 1 ); # can also have filehandle watches #my $watcher; #$watcher = Glib::IO->add_watch( fileno( $pty ), ['in', 'hup'], \&call +back); $main_loop->run; ReadMode('normal'); # restore normal tty settings sub timer_callback{ #do stuff $count++; print "\n$count\n"; return 1; } sub read_in{ while(1){ my $char; if (defined ($char = ReadKey(0)) ) { print "\t\t$char->", ord($char),"\n"; #process key presses here #if($char eq 'q'){exit} if(length $char){exit} # panic button on any key :-) } } }
        1. use threads;
        2. and use Term::ReadKey;
        3. and use Glib;
        4. and my $timer  = Glib::Timeout->add (1000, \&timer_callback, undef, 1 );
        5. and (despite your "...without a cumbersome while(1) loop."), while(1){

        And what does all of that get you? Not a lot!

        You still have to write your own line editing, command line history, command line aliases etc. etc.

        And if you need to do something that'l take a few seconds, your going to have to break it up into iddy biddy chunks, or lace it through with some do_one_event() call or similar.

        when the alternative is:

        my $Qstdin = new Thread::Queue; async { $Qstdin->enqueue( $_ ) while defined( $_ = <STDIN> ); }->detach; ... my $kbinput = $Qstdin->dequeue;

        Fully cross platform; one thread and no faffing around with iddy biddy callbacks; and all the command line handling your local shell provides--that works in exactly whatever way the local shell provides it.

        AND to do other things in the main thread

        Rubbish! As soon as you enter that MainLoop-run, your main "thread" is dead until something signals stop.

        And you might want to revise your "It also works cross-platform,". From the GLib docs:

        The GIOChannel data type aims to provide ... support for Windows is only partially complete.
        which can't be said for the Win32 modules.

        Oh! And what Win32 modules would that be?

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Non Blocking input on Win32 platforms
by zentara (Archbishop) on Oct 31, 2008 at 11:35 UTC
    I don't use win32, but have seen the problem on linux. The solution is to spawn a thread to read STDIN, see Readkey with timer using Glib. You may not need the Glib stuff, but it's only a 3 meg download, using the separate thread is the key.

    I'm not really a human, but I play one on earth Remember How Lucky You Are
Re: Non Blocking input on Win32 platforms
by zentara (Archbishop) on Nov 01, 2008 at 19:29 UTC
    I may not be addressing your specific problem, but I will show you this other method of reading stdin with Glib. The previous mentioned code I showed, only needed a thread if you needed a single key read. If you are content with readline, this is much simpler. One of the big problems with win32, is that select won't work on pipes(only sockets)....but the Glib lib is only 3 megs, and works on windows and avoids all the problems. See also Glib based forking server with root messaging Users on the Perl/Gtk2 maillist say that the IO->add_watch works on win32, see if this works.
    #!/usr/bin/perl use warnings; use strict; use Glib; my $main_loop = Glib::MainLoop->new; Glib::IO->add_watch (fileno 'STDIN', [qw/in/], \&watch_callback, 'STDI +N'); #just to show it's non blocking my $timer1 = Glib::Timeout->add (1000, \&testcallback, undef, 1 ); $main_loop->run; sub watch_callback { # print "@_\n"; my ($fd, $condition, $fh) = @_; my $line = readline STDIN; print $line; #always return TRUE to continue the callback return 1; } sub testcallback{ print "\t\t\t".time."\n"; } __END__

    I'm not really a human, but I play one on earth Remember How Lucky You Are

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://720675]
Approved by marto
Front-paged by tye
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (6)
As of 2018-06-23 18:49 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (125 votes). Check out past polls.