EventMachine how to write a keyboard handler reacting to keystrokes
I am using the EventMachine LineText2 protocol and I would like to run the method receive_line
every time I press a character on my keyboard, not just when I enter a new line. Is there a way to change this default behavior?
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)
}
source to share
If you want to receive unbuffered input from the terminal, you must disable canonical mode on standard input. (I'll also disable echo to make the screen easier to read.) Add this before your code calls #open_keyboard
or inside 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
source to share
I haven't used EventMachine before, but this page on the EventMachine wiki indicates that you shouldn't use the protocol LineText2
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 it give you what you want?
source to share
Here's an update for Ruby 2.0+. In Ruby 2.0 we got io/console
that much easier handling the original keyboard, and it's cross-platform.
Here's a working example that reacts to raw keyboard events with io/console
:
require 'io/console'
require 'eventmachine'
module KB
def receive_data(d)
puts "GOT: #{d}\r"
# CTRL-C will not work in raw mode, so we need another way to exit
EM::stop if d == '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
source to share