Reading_keys

Reading keys from the keyboard is a thing of itself.. because no programming language can read the keyboard. Instead they rely on the terminal or the window manager.

A terminal key can be read with Ruby by STDIN.getc.
If you press Return, the result is \r.

If you press an arrow-key, the result is \e.. and it’s the start of an escape sequence. If you read another key, it will return [.. and the next will return A, B, C or D for the four arrow keys.

Then you have the -echo / -raw to handle.

Anyway.. to make this simpler, there are options. You can import io/console; ncursesw or tty-reader. I will focus on tty-reader here.

tty-reader Link to heading

A good thing with tty-reader is that you decide what you want to read by the call. You can do read_char; read_keypress; read_line or read_multiline. And in the call, you can give an option.. like raw: true, echo: false.

An even better thing is how tty-reader is using whisper wisper to create listeners that receives the data produced by the reader.

The third great thing is that you can trigger a listener manually.. without the key being pressed.

Example:

require 'tty-reader'

# here is the reader
reader = TTY::Reader.new

# here is the listener.. it listens for three keys
reader.on(:keyleft, :keyright, :keydown) {|ev|
  (ev.key.name == :down) ?
    reader.trigger(:keyescape, 'Keydown => Esc triggered !!') :
    (print ev.key.name)
}

# here is another listener.. listening for just one key
reader.on(:keyescape) {|ev|
  print ev.is_a?(String) ? ev : ev.key.name; puts; exit
}

# the main loop.. reading keypresses
# (blocking since nonblock: true is not given)
loop {
  print '  => press a key ... '
  reader.read_keypress; puts
}

Each cycle of the loop is waiting for a keypress. If any listener is defined to respond.. the key is handled there. So..

  • :keyescape will exit.
  • :keydown will print ‘:down’ and trigger :keyescape (where it exists)
  • :keyleft and keyright will print ‘:left’ or ‘:right’