Haskell read raw keyboard input

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 "←"
      "\n"     -> 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 putStrd to actually move the cursor programmatically.

Leave a Comment