I second pseudo-terminals. I find the System.Posix.Terminal to be very usable (though I've admittedly never tried System.Posix.Pty). I occasionally dabble with "bots" for terminal-based games (e.g., Nethack), and have used PTYs with some deal of success.I've included some code from a Nethack bot attempt from several years ago. Hopefully you'll find it useful. As a quick usage/rationale overview, I feed an attoparsec parser by repeatedly calling receive (intermixed with some intelligent use of `transmit` to deal with messages and other incomplete screen updates) until I receive a valid map. `hGetNonBlocking` bypasses the standard buffering mechanisms and returns whatever is in the PTY buffer (which may be nothing) rather than waiting for the specified line/block/whatever buffer to fill. `stop` and `start` should be obvious in their purpose, if not their implementation.data Local = Local { pty :: Handle }class Connection a wheretransmit :: a -> ByteString -> IO ()receive :: a -> IO ByteStringstop :: a -> IO ()start :: IO ainstance Connection Local wheretransmit l s = B.hPut (pty l) sreceive l = B.hGetNonBlocking (pty l) 4096stop l = hClose $ pty lstart = do(fd1, fd2) <- openPseudoTerminal(hPty) <- fdToHandle fd1slave <- fdToHandle fd2_<- createProcess (proc "sh" ["-c", "/usr/games/bin/nethack"]){ std_in = (UseHandle slave), std_out = (UseHandle slave) }return $ Local hPtyCheers,Elliot RobinsonPhone: (321) 252-9660Site: www.argiopetech.com
PGP Fingerprint: 0xD1E72E6A9D0610FFBBF838A6FFB5205A9FEDE59AOn Fri, Aug 1, 2014 at 11:20 PM, Brandon Allbery <allbery.b@gmail.com> wrote:
_______________________________________________We discussed this on IRC the other day. Haskell is doing the same thing that C/C++ stdio / iostreams, and most other buffering systems, do: line buffering on terminal-like devices, block buffering on files and pipes. This is generally expected behavior; although it can be confusing to new programmers, ultimately it is more efficient for most programs.On Fri, Aug 1, 2014 at 11:07 PM, Chris Myzie <cmyzie28@gmail.com> wrote:
As a workaround, I am able to trick the child haskell process into thinking it's running in an interactive terminal by wrapping it with /usr/bin/script:
Interactive use like this, especially over pipes, is fairly unusual; normally you're just copying data around /en masse/, and block buffering is far more efficient. Note that line buffering is not and can not be implemented at the kernel level for ordinary files or pipes, so the kernel interface is actually character buffering which is extremely inefficient (at least one context switch per individual character).You might want to search for something like "buffering line block pipes files" to see quite a lot of discussion about it, in pretty much every language you can think of.By the way, more efficient than using script(1) is, as I told you in IRC, to use the mechanism it is using directly: pseudo-terminals (ptys). See http://hackage.haskell.org/package/unix-2.7.0.1/docs/System-Posix-Terminal.html#g:6 for the standard pty stuff or http://hackage.haskell.org/package/posix-pty-0.1.0/docs/System-Posix-Pty.html for what is claimed to be a simpler interface intended for what you are doing.--brandon s allbery kf8nh sine nomine associatesunix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe