Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell读取原始键盘输入_Haskell_Io_Terminal - Fatal编程技术网

Haskell读取原始键盘输入

Haskell读取原始键盘输入,haskell,io,terminal,Haskell,Io,Terminal,我正在用Haskell编写一个终端模式程序。我如何阅读原始按键信息 特别是,在Haskell之上似乎有一些提供行编辑功能的东西。如果我执行getLine,我似乎能够使用向上箭头来获取前几行,编辑文本,并且只有当我按Enter键时,文本才会对Haskell应用程序本身可见 我所追求的是阅读单个按键的能力,所以我可以自己实现行编辑功能 也许我的问题不清楚。基本上,我想构建一些类似Vi或Emacs(or)的东西。我已经知道有一些终端绑定可以让我进行奇特的控制台模式打印,所以输出端不应该是个问题。我只

我正在用Haskell编写一个终端模式程序。我如何阅读原始按键信息

特别是,在Haskell之上似乎有一些提供行编辑功能的东西。如果我执行
getLine
,我似乎能够使用向上箭头来获取前几行,编辑文本,并且只有当我按Enter键时,文本才会对Haskell应用程序本身可见

我所追求的是阅读单个按键的能力,所以我可以自己实现行编辑功能



也许我的问题不清楚。基本上,我想构建一些类似Vi或Emacs(or)的东西。我已经知道有一些终端绑定可以让我进行奇特的控制台模式打印,所以输出端不应该是个问题。我只是在寻找一种获取原始按键输入的方法,这样我就可以在用户按下字母K时将K添加到当前文本行中,或者在用户按下Ctrl+S时将文件保存到磁盘中。

听起来您需要readline支持。有几个包可以做到这一点,但可能是最容易使用的最受支持的平台

import Control.Monad.Trans
import System.Console.Haskeline

type Repl a = InputT IO a

process :: String -> IO ()
process = putStrLn

repl :: Repl ()
repl = do
  minput <- getInputLine "> "
  case minput of
    Nothing -> outputStrLn "Goodbye."
    Just input -> (liftIO $ process input) >> repl

main :: IO ()
main = runInputT defaultSettings repl
import Control.Monad.Trans
导入System.Console.Haskeline
类型Repl a=输入IO a
进程::字符串->IO()
进程=putStrLn
repl::repl()
repl=do
minput outputStrLn“再见”
只需输入->(liftIO$流程输入)>>repl
main::IO()
main=runinput defaultSettings repl

我想你在找。默认情况下,StdIn是行缓冲的,但是
你想马上收到钥匙

不完整:

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
经过几个小时的网上冲浪,我可以报告如下:

  • readline
    有一个巨大的界面,几乎没有任何文档。从函数名和类型签名,你也许可以猜出这些东西是做什么的。。。但这绝非小事。无论如何,这个库似乎提供了一个高级编辑界面——这正是我试图自己实现的。我需要更低级的东西

  • 在涉猎了haskeline的源代码之后,它似乎有一个巨大而复杂的底层代码,分别针对Win32和POSIX。如果有一种简单的方法来进行控制台I/O,那么这个库不会演示它。这段代码似乎是如此紧密地集成在一起,并且高度特定于
    haskeline
    ,以至于我怀疑我能否重用其中任何一段。但也许通过阅读,我可以学到足够的东西来写我自己的

  • 易是。。。惊人的巨大。阴谋集团文件列出了超过150个暴露的模块。(!!)不过,它下面似乎使用了一个名为
    vty
    的包,该包仅限于POSIX。(我想知道易建联到底是如何在Windows上工作的?
    vty
    看起来它可能对我直接有用,无需进一步修改。(但同样,不是在Windows上。)

  • unix
    已。。。基本上没什么有趣的。它有很多东西可以在终端上进行设置,但绝对没有任何东西可以从终端上读取。(除了可能检查echo是否打开等,按键没有任何问题。)

  • unix compat
    绝对没有什么值得关注的

我认为该库为此提供了最轻量级的解决方案,特别是如果您对
termios
有一定的了解,它由
System.Posix.Terminal
模块反映出来

在gnu.org上有一个很好的页面,介绍了如何使用
termios
设置终端,您可以使用
System.Posix.terminal
来实现这一点

下面是我的解决方案,它将
IO
中的计算转换为使用非规范模式:

{- from unix library -}
import System.Posix.Terminal
import System.Posix.IO (fdRead, stdInput)

{- from base -}
import System.IO (hFlush, stdout)
import Control.Exception (finally, catch, IOException)

{- run an application in raw input / non-canonical mode with given
 - VMIN and VTIME settings. for a description of these, see:
 - http://www.gnu.org/software/libc/manual/html_node/Noncanonical-Input.html
 - as well as `man termios`.
 -}
withRawInput :: Int -> Int -> IO a -> IO a
withRawInput vmin vtime application = do

  {- retrieve current settings -}
  oldTermSettings <- getTerminalAttributes stdInput

  {- modify settings -}
  let newTermSettings = 
        flip withoutMode  EnableEcho   . -- don't echo keystrokes
        flip withoutMode  ProcessInput . -- turn on non-canonical mode
        flip withTime     vtime        . -- wait at most vtime decisecs per read
        flip withMinInput vmin         $ -- wait for >= vmin bytes per read
        oldTermSettings

  {- install new settings -}
  setTerminalAttributes stdInput newTermSettings Immediately

  {- restore old settings no matter what; this prevents the terminal
   - from becoming borked if the application halts with an exception
   -}
  application 
    `finally` setTerminalAttributes stdInput oldTermSettings Immediately

{- sample raw input method -}
tryGetArrow = (do
  (str, bytes) <- fdRead stdInput 3
  case str of
    "\ESC[A" -> putStrLn "\nUp"
    "\ESC[B" -> putStrLn "\nDown"
    "\ESC[C" -> putStrLn "\nRight"
    "\ESC[D" -> putStrLn "\nLeft"
    _        -> return ()
  ) `catch` (
    {- if vmin bytes have not been read by vtime, fdRead will fail
     - with an EOF exception. catch this case and do nothing. 
     - The type signature is necessary to allow other exceptions 
     - to get through.
     -}
    (const $ return ()) :: IOException -> IO ()
  ) 

{- sample application -}
loop = do
  tryGetArrow 
  putStr "." >> hFlush stdout
  loop 

{- run with:
 - VMIN  = 0 (don't wait for a fixed number of bytes)
 - VTIME = 1 (wait for at most 1/10 sec before fdRead returns)
 -}
main = withRawInput 0 1 $ loop
{-来自unix库-}
导入System.Posix.Terminal
导入System.Posix.IO(fdRead、stdInput)
{-自基-}
导入System.IO(hFlush、stdout)
导入控制.Exception(最后是catch,IOException)
{-在原始输入/非规范模式下以给定的
-VMIN和VTIME设置。有关这些设置的说明,请参阅:
- http://www.gnu.org/software/libc/manual/html_node/Noncanonical-Input.html
-还有“男人的termios”。
-}
withRawInput::Int->Int->IO a->IO a
withRawInput vmin vtime application=do
{-检索当前设置-}

oldTermSettings这可能是最简单的解决方案,类似于其他编程语言中的典型代码:

import System.IO(标准输入法,线程)
getKey::IO[Char]
getKey=反向getKey''
其中getKey'chars=do

char一个选项是使用。一个极简主义的例子:

import Control.Monad
import UI.NCurses

main :: IO ()
main = runCurses $ do
    w <- defaultWindow
    forever $ do
        e <- getEvent w Nothing
        updateWindow w $ do
            moveCursor 0 0
            drawString (show e)
        render
import-Control.Monad
导入UI.NCurses
main::IO()
main=runCurses$do

也许是软件包?Haskeline并不是我想要的,但看起来我可以通过阅读源代码得到我想要的…我错误地认为Haskell RTS做了一些奇怪的事情。然而,在编写了执行原始系统调用的C程序之后,我得到了类似的行为。似乎终端本身确实在缓冲按键,除非您将其置于正确的模式(这大概是
readline
所做的)。