如何在Ruby和Windows控制台中检查是否已按下箭头键

时间:2015-03-22 20:33:56

标签: ruby windows

我正在使用Ruby 2.1.5和Windows控制台。我需要能够扫描按键而不停止执行。我需要能够专门检测箭头键。我尝试了其他问题的许多方法,但没有一个在Windows控制台上工作或者没有检测到箭头键。

不显示箭头键

require 'io/console'
loop do
  p STDIN.getch
end

在没有错误的情况下运行,但似乎没有从输入缓冲区读取,因为在使用Ctrl-C退出程序后,控制台上显示了所有按键

require 'io/wait'

def char_if_pressed
  begin

    system("stty raw -echo") # turn raw input on
    c = nil
    if $stdin.ready?
      c = $stdin.getc
    end
    c.chr if c
  ensure
      system "stty -raw echo" # turn raw input off
  end
end

while true
  c = char_if_pressed
  puts "[#{c}]" if c
  sleep 1
  puts "tick"
end

这个也运行没有错误,但需要按下回车键才能继续运行。我需要非阻塞输入

# read a character without pressing enter and without printing to the screen
def read_char
  begin
    # save previous state of stty
    old_state = "stty raw -g"
    # disable echoing and enable raw (not having to press enter)
    system "stty raw -echo"
    c = STDIN.getc.chr
    # gather next two characters of special keys
    if(c=="\e")
      puts "checking for non alpha"
      extra_thread = Thread.new{
        c = c + STDIN.getc.chr
        c = c + STDIN.getc.chr
      }
      # wait just long enough for special keys to get swallowed
      extra_thread.join(0.00001)
      # kill thread so not-so-long special keys don't wait on getc
      extra_thread.kill
    end
  rescue => ex
    puts "#{ex.class}: #{ex.message}"
    puts ex.backtrace
  ensure
    # restore previous state of stty
    system "stty #{old_state}"
  end
  return c
end

# takes a single character command
def show_single_key
  c = read_char
  case c
    when " "
      puts "SPACE"
    when "\t"
      puts "TAB"
    when "\r"
      puts "RETURN"
    when "\n"
      puts "LINE FEED"
    when "\e"
      puts "ESCAPE"
    when "\e[A"
      puts "UP ARROW"
    when "\e[B"
      puts "DOWN ARROW"
    when "\e[C"
      puts "RIGHT ARROW"
    when "\e[D"
      puts "LEFT ARROW"
    when "\177"
      puts "BACKSPACE"
    when "\004"
      puts "DELETE"
    when /^.$/
      puts "SINGLE CHAR HIT: #{c.inspect}"
    else
      puts "SOMETHING ELSE: #{c.inspect}"
  end
end
show_single_key while(true)

这似乎是最后一个的变体,它对所有ASCII代表的键都有效,但不适用于箭头键,并且输入有问题。

require 'io/console'

# Reads keypresses from the user including 2 and 3 escape character sequences.
def read_char
  STDIN.echo = false
  STDIN.raw!

  input = STDIN.getc.chr
  puts input
  if input == "\e" then
    puts "getting additional characters"
    input << STDIN.read_nonblock(3) rescue nil
    input << STDIN.read_nonblock(2) rescue nil
  end
ensure
  #STDIN.echo = true
  STDIN.cooked!

  return input
end

# oringal case statement from:
# http://www.alecjacobson.com/weblog/?p=75
def show_single_key
  c = read_char

  case c
  when " "
    puts "SPACE"
  when "\t"
    puts "TAB"
  when "\r"
    puts "RETURN"
  when "\n"
    puts "LINE FEED"
  when "\e"
    puts "ESCAPE"
  when "\e[A"
    puts "UP ARROW"
  when "\e[B"
    puts "DOWN ARROW"
  when "\e[C"
    puts "RIGHT ARROW"
  when "\e[D"
    puts "LEFT ARROW"
  when "\177"
    puts "BACKSPACE"
  when "\004"
    puts "DELETE"
  when "\e[3~"
    puts "ALTERNATE DELETE"
  when "\u0003"
    puts "CONTROL-C"
    exit 0
  when /^.$/
    puts "SINGLE CHAR HIT: #{c.inspect}"
  else
    puts "SOMETHING ELSE: #{c.inspect}"
  end
end

show_single_key while(true)

我还试过一对有Win32API调用的人,但他们都无法正常工作,并且有关于被弃用的警告。我想要一个跨平台的解决方案,但在这一点上,我将满足它在Windows控制台上正常工作。此外,我不能使用任何Ruby Gems。

1 个答案:

答案 0 :(得分:0)

我已经尝试解决这一问题了一段时间了,这对我有用:

require 'io/console'
while true
    user_input = $stdin.getch         
    case user_input
    when 'q'
        exit
    when '['        
        print '' # I believe that by entering this block of code user_input drops the first char. What is left can be used to determine what key has been pressed.  
    else
        case user_input  
        when 'A'
            puts "up"
        when 'B'
            puts "down"
        when 'C'
            puts "right"
        when 'D'
            puts "left"
        end
    end
end

我正在使用ruby 2.4.1。希望能帮助到你。