Detect key press (non-blocking) w/o getc/gets in Ruby
Here's one way to do it, using IO#read_nonblock
:
def quit? begin # See if a 'Q' has been typed yet while c = STDIN.read_nonblock(1) puts "I found a #{c}" return true if c == 'Q' end # No 'Q' found false rescue Errno::EINTR puts "Well, your device seems a little slow..." false rescue Errno::EAGAIN # nothing was ready to be read puts "Nothing to be read..." false rescue EOFError # quit on the end of the input stream # (user hit CTRL-D) puts "Who hit CTRL-D, really?" true endendloop do puts "I'm a loop!" puts "Checking to see if I should quit..." break if quit? puts "Nope, let's take a nap" sleep 5 puts "Onto the next iteration!"endputs "Oh, I quit."
Bear in mind that even though this uses non-blocking IO, it's still buffered IO.That means that your users will have to hit Q
then <Enter>
. If you want to dounbuffered IO, I'd suggest checking out ruby's curses library.
You can also do this without the buffer. In unix based systems it is easy:
system("stty raw -echo") #=> Raw mode, no echochar = STDIN.getcsystem("stty -raw echo") #=> Reset terminal modeputs char
This will wait for a key to be pressed and return the char code. No need to press .
Put the char = STDIN.getc
into a loop and you've got it!
If you are on windows, according to The Ruby Way, you need to either write an extension in C or use this little trick (although this was written in 2001, so there might be a better way)
require 'Win32API'char = Win32API.new('crtdll','_getch', [], 'L').Call
Here is my reference: great book, if you don't own it you should
A combination of the other answers gets the desired behavior. Tested in ruby 1.9.3 on OSX and Linux.
loop do puts 'foo' system("stty raw -echo") char = STDIN.read_nonblock(1) rescue nil system("stty -raw echo") break if /q/i =~ char sleep(2)end