やりたいこと: 方向キーでカーソルとか動かしたい (※画像はイメージです)
キー入力を得る
ターミナルでユーザーの入力を得る際、gets
やgetc
ではエンターが押されるまでキーの入力状況を得ることができません。
なので、カーソル移動等の操作には使えません。
cursesライブラリは色々とめんどいので使わない方法を調べました。
IO#getch (io/console)
require 'io/console'
するとIO#getch
が使えるようになります。
これでキーが押されるたびに標準入力から文字を得ることができます。
ただしControl + C
の終了もできなくなるので、Control + C
が押されたらループを抜けるようにします。
require 'io/console'
i = 0
while (key = STDIN.getch) != "\C-c"
puts " #{i += 1}: #{key.inspect} キーが押されました。"
end
a, b, c のキーを押した結果
$ ruby read_key.rb
1: "a" キーが押されました。
2: "b" キーが押されました。
3: "c" キーが押されました。
矢印キーも使ってみる
矢印キーを押すと、カーソルを移動するエスケープシーケンスが入力されます。
(MacのTerminalで動作確認してます。他も一緒かは調べてません)
3文字の文字列じゃなくて、1文字が3回。
$ ruby read_key.rb
1: "\e" キーが押されました。
2: "[" キーが押されました。
3: "A" キーが押されました。
方向 | エスケープシーケンス |
---|---|
上 | "\e[A" |
下 | "\e[B" |
右 | "\e[C" |
左 | "\e[D" |
適当に処理を変えて矢印キーを認識するようにします。
require 'io/console'
arrows = {A: "↑", B: "↓", C: "→", D: "←"}
i = 0
while (key = STDIN.getch) != "\C-c"
if key == "\e"
second_key = STDIN.getch
if second_key == "["
key = STDIN.getch
key = arrows[key.intern] || "esc: [#{key}"
else
key = "esc: #{second_key}"
end
end
puts " #{i += 1}: #{key.inspect} キーが押されました。"
end
$ ruby read_arrow_key.rb
1: "↑" キーが押されました。
2: "→" キーが押されました。
3: "↓" キーが押されました。
4: "←" キーが押されました
使ってみる
カーソル移動してみます。
WASD
とhjkl
にも対応したのでPCゲーマーやvim派も安心。
(追記:宗教論争を避ける為、せっかくなのでControl+f,b,n,p
にも対応)
require 'io/console'
require 'io/console/size'
#画面を消去して、真ん中に移動しておく
print "\e[2J"
print "\e[%d;%dH" % IO::console_size.map{|size| size / 2 }
while (key = STDIN.getch) != "\C-c"
# 方向キー以外は不要なので、エスケープ文字を得る処理は簡略化した
if key == "\e" && STDIN.getch == "["
key = STDIN.getch
end
# 方向を判断
direction = case key
when "A", "k", "w", "\u0010"; "A" #↑
when "B", "j", "s", "\u000E"; "B" #↓
when "C", "l", "d", "\u0006"; "C" #→
when "D", "h", "a", "\u0002"; "D" #←
else nil
end
# カーソル移動
print "\e[#{direction}" if direction
end
このままだと大文字のAとかを打った場合にも上下左右に移動してしまいます。
特に支障はないので対応しませんが。
require 'io/console/size'
とIO::console_size
、今回知ったのですがほとんど使われてませんね。
今までstty size
でやってました。GitHubでconsole_size
で検索しても全く使われてないっぽい?