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

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.

Related

Injecting key combinations into Bash tty using TIOCSTI in Python

I am trying to inject key combinations (like ALT+.) into a tty using the TIOCSTI in Python.
For some key combinations I have found the corresponding hex code for Bash shells using the following table which works good.
From this table I can see that for example CTRL+A is '\x01' etc.
import sys,os,Queue
import termios,fcntl
# replace xx with a tty num
tty_name = "/dev/pts/xx";
parent_fd = os.open(tty_name, os.O_RDWR)
special_char = "Ctrl_a"
if special_char == "Ctrl_a":
send_char = '\x01'
if special_char == "Ctrl_e":
send_char = '\x05'
if special_char == "Ctrl_c":
send_char = '\x03'
fcntl.ioctl(self.parent_fd, termios.TIOCSTI, send_char)
But how can I get the hex codes for other combinations such as
ALT+f etc. I need a full list or a way how to get this information for any possible combo as I want to implement most bash shortcuts for moving, manipulating the history etc. to inject.
Or is there any other way to inject key-combinations using TIOCSTI ?
As I can only send single chars to a tty I wonder if there is anything else possible.
Thank you very much for your help!
The usual working of "control codes" is that the "control" modifier substracts 64 from the character code.
"A" is ASCII character 65, so "Ctrl-A" is "65-64=1".
Is it enough for you to extend this scheme to your situation?
So, if you need the control code for, for example, "Device Control 4" (ASCII code 20), you'd add 64, to obtain "84", which is "T".
Therefore, the control-code for DC4 would be "Control+T".
In the reverse direction, the value for "Control+R" (history search in BASH) is R-64, so 82-64=18 (Device Control 2)
ASCIItable.com can help with a complete listing of all character codes in ASCII
Update: Since you were asking specifically for "alt+.":
The 'Control mean minus 64" doesn't apply to Alt, unfortunately; that seems to be handled completely differently, by the keyboard driver, by generating "key codes" (also called "scancodes", variably written with or without spaces) that don't necessarily map to ASCII. (Keycodes just happen to map to ASCII for 0-9 and A-Z, which leads to much confusion)
This page lists some more keycodes, including "155" for "alt+."

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.

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

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.

KeyPress event in Lua?

is it possible to get users keypress on lua?
fe.
while true do
if keyPress(27)==true then
print("You just pressed ESC")
end
end
Lua is predicated on extreme portability. As such it's based on supplying, essentially, only that which is available in ANSI C in terms of capabilities. (I think the sole exception to that is dynamic linking which is a non-ANSI feature not available on all platforms, but is so useful that they've put it in for many.)
ANSI C doesn't provide keypress functionality so the default Lua library doesn't either.
That being said, the LuaRocks repository might lead you to a library with this capability. For example it could be that ltermbox, found on the LuaRocks page there, has the functionality you need. (You'll probably have to remove the bits you don't want, mind.) There may be other libraries available. Go digging.
Failing that, the whole point of Lua is extensibility. It's an extensible extension language. It's not actually all that hard to hand-roll your own extension that provides the functionality you want.
Not in stock Lua. Probably with an additional library.
There is a binding to getkey() in the NTLua project. You can get some sources from there.
(it just wraps getch())
It seems like you are trying to make a game. For 2D games you might want to consider love2d. It looks a little weird, but it works and it's relatively easy compared to other languages such as C.
First thing's first: if you're using my method of doing this, you need to put the script(s) you use in a LocalScript. Not doing this will cause the key(s) to not show up in the console (F9 to see console).
Alright, now that we know it's in a LocalScript, here's the script:
local player = game.Players.LocalPlayer -- Gets the LocalPlayer
local mouse = player:GetMouse() -- Gets the player's mouse
mouse.KeyDown:connect(function(key) -- Gets mouse, then gets the keyboard
if key:lower() == "e" or key:upper() == "E" then -- Checks for selected key (key:lower = lowercase keys, key:upper = uppercase keys)
print('You pressed e') -- Prints the key pressed
end -- Ends if statement
end) -- Ends function
If you're wanting to signal only one key (lowercase only, or uppercase only) check below.
Lowercase only:
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
mouse.KeyDown:connect(function(key)
if key == "e" then
print('You pressed e')
end
end)
Uppercase only:
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
mouse.KeyDown:connect(function(key)
if key == "E" then
print('You pressed E')
end
end)
Or, if you want to just signal any key in general, you can also do this:
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
mouse.KeyDown:connect(function(key)
print('You pressed '..key)
end)
I hope I helped answer your question.
if keypress=(29)==true then
print("hello")
end

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||

Resources