3

I use EventMachine LineText2 protocol and I would like to fire receive_line method everytime I press a character on my keyboard and not just when I enter a new line. Is there a way to change that default behaviour?

class KeyboardHandler < EM::Connection
  include EM::Protocols::LineText2

  def initialize(q)
    @queue = q
  end

  def receive_line(data)
    @queue.push(data)
  end
end

EM.run {
  q = EM::Queue.new

  callback = Proc.new do |line|
    # puts on every keypress not on "\n"
    puts line
    q.pop(&callback)
  end
  q.pop(&callback)

  EM.open_keyboard(KeyboardHandler, q)
}
4

3 回答 3

6

If you want to receive unbuffered input from the terminal, you should turn off canonical-mode on standard input. (I also turn off echo to make the screen easier to read.) Add this before your code calls #open_keyboard or within your handler initializer:

require 'termios'
# ...
attributes = Termios.tcgetattr($stdin).dup
attributes.lflag &= ~Termios::ECHO # Optional.
attributes.lflag &= ~Termios::ICANON
Termios::tcsetattr($stdin, Termios::TCSANOW, attributes)

For example:

require 'termios'
require 'eventmachine'

module UnbufferedKeyboardHandler
  def receive_data(buffer)
    puts ">>> #{buffer}"
  end
end

EM.run do
  attributes = Termios.tcgetattr($stdin).dup
  attributes.lflag &= ~Termios::ECHO
  attributes.lflag &= ~Termios::ICANON
  Termios::tcsetattr($stdin, Termios::TCSANOW, attributes)

  EM.open_keyboard(UnbufferedKeyboardHandler)
end
于 2013-01-31T09:42:48.243 回答
1

Here's an update for Ruby 2.0+. In Ruby 2.0 we have io/console, which makes handling raw keyboard much easier, and it's cross-platform.

Here's a working example that reacts to raw keyboard events using io/console:

require 'io/console'
require 'eventmachine'

module KB
  def receive_data(key)
    puts "GOT: #{key}\r"

    # CTRL-C will not work in raw mode, so we need another way to exit.    
    EM::stop if key == 'q'
  end
end

begin
  EM.run {
    # Put console in raw mode (no echo, no line buffering).
    IO.console.raw!
    EM.open_keyboard(KB)
  }
ensure
  # Ensure cooked, otherwise console will be unusable after exit.
  IO.console.cooked!
end
于 2017-07-02T15:17:31.560 回答
0

I've not used EventMachine before, but this page on the EventMachine wiki seems to indicate that you should not use the LineText2 protocol as it sounds like you don't want buffered lines.

They give this example:

module MyKeyboardHandler
  def receive_data(keystrokes)
    puts "I received the following data from the keyboard: #{keystrokes}"
  end
end

EM.run {
  EM.open_keyboard(MyKeyboardHandler)
}

Does that give you what you want?

于 2013-01-29T23:09:19.177 回答