Julia - Terminal で 1文字ずつ入力を受ける (raw mode)

環境

  • Julia v1.4.2
  • OS: ArchLinux

readreadline を使うと EOF を入力 (Ctrl-D) したときや Enter を押したときにキーボードから入力したものを取得することができるが、何かしらのキーをタイプする度に入力を取得する方法を紹介する。

# ex.jl
using REPL

terminal = REPL.Terminals.TTYTerminal(get(ENV, "TERM", Sys.iswindows() ? "" : "dumb"), Base.stdin, Base.stdout, Base.stderr)

function readChar(term)
    c = Char(read(term.in_stream, 1)[1])
    println("$(c) $(Int(c))")
    return c
end

REPL.Terminals.raw!(terminal, true)
while true
    Char(readChar(terminal)[1]) == 'q' && break
end

while loop の print は1文字ずつ入力する毎に出力される。

$ julia ex.jl
a 97
b 98
c 99
d 100
e 101
q 113

一方で raw! を使わない場合だと、Enter が入力されるまで while loop の内容は出力されない。

# ex2.jl
using REPL

terminal = REPL.Terminals.TTYTerminal(get(ENV, "TERM", Sys.iswindows() ? "" : "dumb"), Base.stdin, Base.stdout, Base.stderr)

function readChar(term)
    c = Char(read(term.in_stream, 1)[1])
    println("$(c) $(Int(c))")
    return c
end

while true
    Char(readChar(terminal)[1]) == 'q' && break
end
$ julia ex2.jl
abcdeq
a 97
b 98
c 99
d 100
e 101
q 113

上記の例はスクリプトとして実行したが、これを REPL にコピペしても同じ動きにはならない。raw! を使っていてもカノニカルモードになってしまう。

julia> using REPL

julia> terminal = REPL.Terminals.TTYTerminal(get(ENV, "TERM", Sys.iswindows() ? "" : "dumb"), Base.stdin, Base.stdout, Base.stderr)
REPL.Terminals.TTYTerminal("xterm-termite", Base.TTY(RawFD(0x0000000a) paused, 0 bytes waiting), Base.TTY(RawFD(0x0000000d) open, 0 bytes waiting), Base.TTY(RawFD(0x0000000f) open, 0 bytes waiting))

julia> function readChar(term)
           c = Char(read(term.in_stream, 1)[1])
           println("$(c) $(Int(c))")
           return c
       end
readChar (generic function with 1 method)

julia> REPL.Terminals.raw!(terminal, true)
true

julia> while true
           Char(readChar(terminal)[1]) == 'q' && break
       end
abced
a 97
b 98
c 99
e 101
d 100

 10
q
q 113

しかし、while loop のなかで raw! を実行すると raw mode になる。

julia> while true
           REPL.Terminals.raw!(terminal, true)
           Char(readChar(terminal)[1]) == 'q' && break
       end
a 97
b 98
c 99
d 100
e 101
q 113

while loop 以外にも function, begin, let など新しくスコープを作るもののなかに raw! を入れると、そのスコープ内では raw mode になるようである。

参考