This might be the simplest solution, resembling typical code in other programming languages:
import System.IO (stdin, hReady)
getKey :: IO [Char]
getKey = reverse <$> getKey' ""
where getKey' chars = do
char <- getChar
more <- hReady stdin
(if more then getKey' else return) (char:chars)
It works by reading more than one character “at a time”. Allowing E.g. the ↑
key, which consists of the three characters ['ESC','[','A']
to be distinguished from an actual ESC
character input.
Usage example:
import System.IO (stdin, hSetEcho, hSetBuffering, NoBuffering)
import Control.Monad (when)
-- Simple menu controller
main = do
hSetBuffering stdin NoBuffering
hSetEcho stdin False
key <- getKey
when (key /= "ESC") $ do
case key of
"ESC[A" -> putStr "↑"
"ESC[B" -> putStr "↓"
"ESC[C" -> putStr "→"
"ESC[D" -> putStr "←"
"
" -> putStr "?"
"DEL" -> putStr "?"
_ -> return ()
main
This is a bit hackish, since in theory, a user could input more keys before the program gets to the hReady
. Which could happen if the terminal allows pasting. But in practice, for interactive input, this is not a realistic scenario.
Fun fact: The cursor strings can be putStr
d to actually move the cursor programmatically.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…