Non-blocking 'gets'?

On 10/3/07, Stephen W. [email protected] wrote:

Is there a way to perform a non-blocking gets or getc? As in a method
that reads a character from stdin if there is one in the buffer or
returns nil otherwise?

If I was programming a Windows console app in C, I would use kbhit()
to check if there was a character in the buffer. So I decided this
was a good time to try writing my first ruby c extension.

on Windows, save and complie conio.c into conio.dll, and then the
following example should work.
-Adam

— test_conio.rb —
require ‘conio’
#background work:
Thread.new { print(‘.’) or sleep(1) while true }
#check for input in this thread
Thread.new do
while 1
while !CONIO::kbhit?; end #–wait for key, nonblocking
#s = STDIN::getc #–this blocks until return pressed
s = CONIO::getch #–this doesn’t
p “#{s} (#{s.chr})”
exit if s==?q
end
end
#do some foreground work
puts(“waiting”) or sleep(10) while 1


—conio.c—
/* conio.c – Windows conio functions
3 Oct 2007, Adam S.
Ruby license applies
*/
#include “ruby.h”
#include <conio.h>

static VALUE c_kbhit(VALUE self)
{
return _kbhit()? Qtrue : Qfalse;
}

static VALUE c_getch(VALUE self)
{
int n = _getch();
return INT2FIX(n);
}
static VALUE c_ungetch(VALUE self, VALUE cv)
{
int c = FIX2INT(cv);
int n = _ungetch(c);
return INT2FIX(n);
}

VALUE mConio;

__declspec( dllexport ) void Init_conio()
{
mConio = rb_define_module(“CONIO”);
rb_define_module_function(mConio, “kbhit?”, c_kbhit, 0);
rb_define_module_function(mConio, “getch”, c_getch, 0);
rb_define_module_function(mConio, “ungetch”, c_ungetch, 1);
}

On Oct 3, 2007, at 12:46 PM, Stephen W. wrote:

Is there a way to perform a non-blocking gets or getc?

I vote that you let HighLine do the work for you:

http://blog.grayproductions.net/articles/i_just_want_one_character

James Edward G. II

Wow, that is quite complex. Thank you!

I also managed to discover another usable method: run a ‘system’ command
in a new thread. That seems to release the blocking on gets by creating
a second process, and as far as I can tell will work on both Windows and
*nix. The downside is that the two processes can’t really communicate
with each other much, but in my case I don’t need them to.

Thanks everyone for you help :slight_smile:

Why don’t you just bind a keypress event? That would capture any given
keystroke in a non blocking way.

Stephen W. wrote:

Wow, that is quite complex. Thank you!

I also managed to discover another usable method: run a ‘system’ command
in a new thread. That seems to release the blocking on gets by creating
a second process, and as far as I can tell will work on both Windows and
*nix. The downside is that the two processes can’t really communicate
with each other much, but in my case I don’t need them to.

Thanks everyone for you help :slight_smile:

Stephen,

Can you please provide a very simple ‘system’ command example as I’m
having the same problem.

Lloyd L. wrote:

Why don’t you just bind a keypress event? That would capture any given
keystroke in a non blocking way.

Can you provide an example?

Wurzel Cidermaker wrote:

Lloyd L. wrote:

Why don’t you just bind a keypress event? That would capture any given
keystroke in a non blocking way.

Can you provide an example?

I got the idea from the pickaxe book starting on page 246.

Stephen W. wrote in post #565434:

Hi guys, sorry if this is a stupid question, but I’ve been reading
around online and in ‘Programming Ruby,’ and I can’t find anything.

Is there a way to perform a non-blocking gets or getc? As in a method
that reads a character from stdin if there is one in the buffer or
returns nil otherwise? I’m writing a multi-threaded application that
communicates over the internet using sockets, but I want the person who
runs the script on the host machine to be able to enter commands via
stdin. The ‘gets’ command hangs all my threads until it returns.

Any suggestions? Thanks!
-Stephen

I know this is an old post, but still appears among Google’s top
results.

By combinig the various solutions I just read, I came up with a
cross-platform way to solve that problem.
Details here:

The relevant piece of code: a GetKey.getkey method
returning the ASCII code or nil if none was pressed.

Should work both on Windows and Unix.

module GetKey

Check if Win32API is accessible or not

@use_stty = begin
require ‘Win32API’
false
rescue LoadError
# Use Unix way
true
end

Return the ASCII code last key pressed, or nil if none

Return::

* Integer: ASCII code of the last key pressed, or nil if none

def self.getkey
if @use_stty
system(‘stty raw -echo’) # => Raw mode, no echo
char = (STDIN.read_nonblock(1).ord rescue nil)
system(‘stty -raw echo’) # => Reset terminal mode
return char
else
return Win32API.new(‘crtdll’, ‘_kbhit’, [ ], ‘I’).Call.zero? ? nil
: Win32API.new(‘crtdll’, ‘_getch’, [ ], ‘L’).Call
end
end

end

And here is a simple program to test it:

loop do
k = GetKey.getkey
puts “Key pressed: #{k.inspect}”
sleep 1
end

In the link provided above, I also show how to use the curses library,
but the result gets a bit whacky on Windows.

Hope this will help others.