STDIN.getch as a non block event (it's possible?) - ruby

I'm trying to read a character instantly from command line without use of Enter. The ruby (ruby 1.9.3p374) code that I'm using is the following:
require 'io/console'
ch = STDIN.getch
puts ch
until now everithing is working fine but now i want to put this code inside an infinite loop doing some other stuff, something like
loop do
puts "..doing stuff.."
ch = STDIN.getch
if ch == 'q'
break
end
end
but this code always force that we press a key between each printing. I want a behaviour similar to STDIN.read_nonblock method but without having to press enter key after pressing one char.
Basically I want to print "..doing stuff.." until I press a certain key on keyboard but i don't want to use enter.
Any help will be appreciated. Thanks

You could always use the built-in curses library to handle your interaction. It's very powerful and is used to construct keyboard-driven tools such as text editors.
The alternative is to use select to poll if STDIN is readable. Your terminal might be in a line-buffered state, so you'd need to adjust that before single keystrokes are received. This is something that Curses can handle for you.

Related

How can I get Ruby curses to respond properly to arrow keys?

TL;DR
How can I get Ruby curses to respond properly to arrow keys? The KEY_UP constant doesn't seem to match my input.
Environment and Problem Descriptions
I am running Ruby 2.1.2 with the curses 1.0.1 gem. I'm trying to enable arrow-key navigation with curses. I've enabled Curses#getch to fetch a single key without waiting for the carriage return by calling Curses#cbreak, and this is working fine for the k character. However, I really want to enable arrow key navigation, and not just HJKL for movement.
Currently, the up-arrow prints 27 within my program, which seems like the correct ordinal value my keyboard gives for the up-arow key:
"^[[A".ord
#=> 27
and which should be matched by the Curses KEY_UP constant. It isn't, and so falls through to the else statement to display the ordinal value. The up-arrow key also leaves [A as two separate characters at the command prompt when the ruby program exits, which might indicate that Curses#getch isn't capturing the key press properly.
My Ruby Code
require 'curses'
include Curses
begin
init_screen
cbreak
noecho
keypad = true
addstr 'Check for up arrow or letter k.'
refresh
ch = getch
addch ?\n
case ch
when KEY_UP
addstr "up arrow \n"
when ?k
addstr "up char \n"
else
addstr "%s\n" % ch
end
refresh
sleep 1
ensure
close_screen
end
In the line to enable the keypad, you're actually creating a local variable called 'keypad' because that method is on the class Curses::Window.
Since you're not making your own windows (apart from with init_screen), you can just refer to the standard one using the stdscr method. If I change line 8 to:
stdscr.keypad = true
then you sample code works for me.

What is the best way to get keyboard events (input without press 'enter') in a Ruby console application?

I've been looking for this answer in the internet for a while and have found other people asking the same thing, even here. So this post will be a presentation of my case and a response to the "solutions" that I have found.
I am such new in Ruby, but for learning purposes I decided to create a gem, here.
I am trying to implement a keyboard navigation to this program, that will allow the user use short-cuts to select what kind of request he want to see. And in the future, arrow navigations, etc.
My problem: I can't find a consistent way to get the keyboard events from the user's console with Ruby.
Solutions that I have tried:
Highline gem: Seems do not support this feature anymore. Anyway it uses the STDIN, keep reading.
STDIN.getch: I need to run it in a parallel loop, because at the same time that the user can use a short-cut, more data can be created and the program needs to show it. And well, I display formated text in the console, (Rails log). When this loop is running, my text lost the all the format.
Curses: Cool but I need to set position(x,y) to display my text every time? It will get confusing.
Here is where I am trying to do it.
You may note that I am using "stty -raw echo" (turns raw off) before show my text and "stty raw -echo" (turns raw on) after. That keeps my text formated.
But my key listener loop is not working. I mean, It works in sometimes but is not consistent. If a press a key twice it don't work anymore and sometimes it stops alone too.
Let me put one part of the code here:
def run
# Two loops run in parallel using Threads.
# stream_log loops like a normal stream in the file, but it also parser the text.
# break it into requests and store in #requests_queue.
# stream_parsed_log stream inside the #requests_queue and shows it in the screen.
#requests_queue = Queue.new
#all_requests = Array.new
# It's not working yet.
Thread.new { listen_keyboard }
Thread.new { stream_log }
stream_parsed_log
end
def listen_keyboard
# not finished
loop do
char = STDIN.getch
case char
when 'q'
puts "Exiting."
exit
when 'a'
#types_to_show = ['GET', 'POST', 'PUT', 'DELETE', 'ASSET']
requests_to_show = filter_to_show(#all_requests)
command = true
when 'p'
#types_to_show = ['POST']
requests_to_show = filter_to_show(#all_requests)
command = true
end
clear_screen if command
#requests_queue += requests_to_show if command
command = false
end
end
I need a light in my path, what should I do?
That one was my mistake.
It's just a logic error in another part of code that was running in another thread so the ruby don't shows the error by default. I used ruby -d and realized what was wrong. This mistake was messing my keyboard input.
So now it's fixed and I am using STDIN.getch with no problem.
I just turn the raw mode off before show any string. And everything is ok.
You can check here, or in the gem itself.
That's it.

Reading user input without halting execution

I'm trying to read user input in a console interface in a Lua application.
However, I do not want to halt program execution as the title suggests. Using read() would interfere with the normal handling of ongoing events, and interface updates, while the user's typing.
The best solution I could come up with was maintaining a temporary variable I apply any user input to that isn't a function key. However, my event handler returns scancodes, and I know of no way to map these to ASCII codes short of maintaining a table for each key, which is something I really want to avoid.
Any ideas?
Edit
To give an example of what I mean:
function read_input()
local input = read()
do_something_with(input)
end
while true do
e,param1,param2,param3 = os.pullEvent()
if (e=='key' and param1=='201') then
read_input()
elseif (e=='something_else') then
do_something_else()
end
end
As you can see, the user can at some point land in a function demanding user input. While this input is taken, I can't have any other (event-based) functions of the program being impeded by this.
You would need to read the keys continuously from the os.pullEvent()
local input
while true do
e,param1,param2,param3 = os.pullEvent()
if (e=='key') then
-- do some fancy stuff for checking which key has pressed and append it to input
-- additionally if it's enter do something other fancy
else
-- blah blah
end
end
I'm not sure but I thought the character of which key was pressed is in one of the params of the pullEvent, I might be mistaken tho.
Additionally if you want to do something every second or so, start a timer which also fires the pullEvent.

Multi-line input problems when using STDIN.gets

Having looked at this question, I have the following code:
$/ = "\0"
answer = STDIN.gets
Now, I was hoping that this would allow the user to:
enter a multi-line input, terminating by pressing Ctrl-D.
enter a single line input, terminating by pressing Ctrl-D.
enter a "nothing" input, terminating by pressing Ctrl-D.
However, the behaviour I actually see is that:
The user can enter a multi-line input fine.
The user can not enter a single line input, unless they hit Ctrl-D twice.
The user can enter a "nothing" input if they hit Ctrl-D straight away.
So, why does the single line situation (i.e. if the user has entered some text but no newline and then hit Ctrl-D) require two presses of Ctrl-D? And why does it work then if the user enters nothing? (I have noted that if they enter nothing and hit Ctrl-D, I don't get an empty string but the nil class - I discovered this when trying to call .empty? on the result, since it suddenly failed horribly. If there is a way to get it to return an empty string as well, that would be nice. I prefer checking .empty? to ==, and don't particularly want to define .empty? for the nil class.)
EDIT: Since I really would like to know the "correct way" to do this in Ruby, I am offering a bounty of 200 rep. I will also accept answers that give another way of entering terminal multi-line input with a sensible "submit" procedure - I will be the judge of 'suitable'. For example, we're currently using two "\n"s, but that's not suitable, as it blocks paragraphs and is unintuitive.
The basic problem is the terminal itself. See many of the related links to the right of your post. To get around this you need to put the terminal in a raw state. The following worked for me on a Solaris machine:
#!/usr/bin/env ruby
# store the old stty settings
old_stty = `stty -g`
# Set up the terminal in non-canonical mode input processing
# This causes the terminal to process one character at a time
system "stty -icanon min 1 time 0 -isig"
answer = ""
while true
char = STDIN.getc
break if char == ?\C-d # break on Ctrl-d
answer += char.chr
end
system "stty #{old_stty}" # restore stty settings
answer
I'm not sure if the storing and restoring of the stty settings is necessary but I've seen other people do it.
When reading STDIN from a terminal device you are working in a slightly different mode to reading STDIN from a file or a pipe.
When reading from a tty Control-D (EOF) only really sends EOF if the input buffer is empty. If it is not empty it returns data to the read system call but does not send EOF.
The solution is to use some lower level IO and read a character at a time. The following code (or somethings similar) will do what you want
#!/usr/bin/env ruby
answer = ""
while true
begin
input = STDIN.sysread(1)
answer += input
rescue EOFError
break
end
end
puts "|#{answer.class}|#{answer}|"
The results of running this code with various inputs are as follows :-
INPUT This is a line<CR><Ctrl-D>
|String|This is a line
|
INPUT This is a line<Ctrl-D>
|String|This is a line|
INPUT<Ctrl-D>
|String||

How can I programatically call the ruby debugger from within a ruby program in Ruby 1.9?

I am writing a web based debugger for Ruby, but in order to do this I need to be able to call the Ruby debugger from within a Ruby program on the server side. Has this ever been done? Is this even possible?
The end product being built will allow Ruby code to be edited, executed and stepped through using just a web browser. The ruby code that is to be debugged will be "eval"ed on the server side.
I have since been pointed in the right direction by one of the stackoverflow users who has suggested using popen or expect. I have tried both of these now but have encountered the following problems:
popen: When waiting for the console you have to use a timeout block to signal the end of the debug console's output (The command line terminal can detect this, so why can't ruby).
expect: In the program below the debugger inputs get out of sync with the debugger. Why is that?
require 'pty'
require 'expect'
$expect_verbose = true
PTY.spawn("rdebug deb.rb") do |from_debugger, to_debugger, pid|
a=nil
while ( a != "end" ) do
from_debugger.expect(/\(rdb:1\)/ ) do |input|
a = gets
to_debugger.puts( a + "\n" )
end
from_debugger.flush
end
end
I think you could make use of ruby-debug. I would imagine this as opening the same script on the server-side, and sending keystrokes to it. I think this is how I'd do it.
Think about it this way: the client pastes his code in your form. Optionally, he may click on some button that places a breakpoint on some line. Then he submits the whole thing. On server side, you process that form, and create a .rb file with the contents of the form. At the line where he put the breakpoint, you insert a debugger call. Then you run :
ruby client_script.rb
and this will stop the process once it reaches the debugger line. I would suggest you do all of this with Expect for Ruby or whatever. So, when the client presses step over on the page or inspect whatever variable, you send keystrokes to the process you spawned.
For example, if he wants to inspect the value of the variable x, you'd send this to the process:
p x
and you'd have to capture that output and send it back to the client's browser. Expect is kind of like popen , but a bit better.
I finally figured out how to do this using expect:
require 'pty'
require 'expect'
PTY.spawn("rdebug deb.rb") do |output,input,pid|
while true do
begin
buffer = ""
puts "-------"
puts output.readpartial(1024,buffer) until buffer =~ /\(rdb:1\)/
a = gets
input.puts a
rescue
break
end
end
end

Resources