前回のユーザー入力とクロックを両方待つプログラムを少し改造します。
- Enter以外の入力を受け取る
- カーソルを移動せずに書き換える
後者はRactorは関係ないです。
Enter以外の入力を受け取る
任意の1文字を受け取るためにSTDIN.getch
を使いたいです。
STDINは共有不可能オブジェクトです。
次のようなRactorを書くとcan not access non-sharable objects in constant STDIN by non-main Ractors (NameError)
が発生します。
# ユーザー入力を待つRactor input = Ractor.new do while "\C-c" != STDIN.getch Ractor.yield :reset end end
共有不可能オブジェクトをRactorに渡すときはmoveします。
# ユーザー入力を待つRactor input = Ractor.new do io = Ractor.recv while "\C-c" != io.getch Ractor.yield :reset end end input.send STDIN, move: true
カーソルを移動せずに書き換える
エスケープシーケンスを使ってカーソルを操作します。
# 行をクリア print "\e[2K" # 行頭へ移動 print "\e[0G" # 出力 print val
完成形
require 'io/console' # ユーザー入力を待つRactor input = Ractor.new do # moveされたSTDIOを使って文字入力を待つ io = Ractor.recv while "\C-c" != io.getch Ractor.yield :reset end end # クロックイベントを発生するRactor clock = Ractor.new do loop do Ractor.yield nil sleep 0.3 end end # 共有不可能オブジェクトSTDINをRactorにmoveする input.send STDIN, move: true # 初期値 val = 0 loop do # ユーザー入力とクロックを両方待ちます。 _, flag = Ractor.select input, clock # イベントがユーザー入力だったら値をリセットします。 val = 0 if flag == :reset # カウントアップします。 val += 1 # 行をクリア print "\e[2K" # 行頭へ移動 print "\e[0G" # 出力 print val end
次のように動きます。一定のペースでカウントアップし、何か入力すると1に戻ります。