Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
571 views
in Technique[技术] by (71.8m points)

io - Haskell read raw keyboard input

I'm writing a terminal-mode program in Haskell. How would I go about reading raw keypress information?

In particular, there seems to be something providing line-editing facilities on top of Haskell. If I do getLine, I seem to be able to use the up-arrow to get previous lines, edit the text, and only when I press Enter does the text become visible to the Haskell application itself.

What I'm after is the ability to read individual keypresses, so I can implement the line-editing stuff myself.


Perhaps my question was unclear. Basically I want to build something like Vi or Emacs (or Yi). I already know there are terminal bindings that will let me do fancy console-mode printing, so the output side shouldn't be an issue. I'm just looking for a way to get at raw keypress input, so I can do things like (for example) add K to the current line of text when the user presses the letter K, or save the file to disk when the user presses Ctrl+S.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...