Files
simplex-chat/ChatTerminal/POSIX.hs
2021-04-08 20:20:06 +01:00

103 lines
2.9 KiB
Haskell

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
module ChatTerminal.POSIX where
import ChatTerminal.Core
import Control.Concurrent.STM
import Styled
import qualified System.Console.ANSI as C
import System.IO
initTTY :: IO ()
initTTY = do
hSetEcho stdin False
hSetBuffering stdin NoBuffering
hSetBuffering stdout NoBuffering
updateInput :: ChatTerminal -> IO ()
updateInput ct@ChatTerminal {termSize, termState, nextMessageRow} = do
C.hideCursor
ts <- readTVarIO termState
nmr <- readTVarIO nextMessageRow
let (th, tw) = termSize
ih = inputHeight ts ct
iStart = th - ih
prompt = inputPrompt ts
(cRow, cCol) = relativeCursorPosition tw $ length prompt + inputPosition ts
if nmr >= iStart
then atomically $ writeTVar nextMessageRow iStart
else clearLines nmr iStart
C.setCursorPosition (max nmr iStart) 0
putStr $ prompt <> inputString ts <> " "
C.clearFromCursorToLineEnd
C.setCursorPosition (iStart + cRow) cCol
C.showCursor
where
clearLines :: Int -> Int -> IO ()
clearLines from till
| from >= till = return ()
| otherwise = do
C.setCursorPosition from 0
C.clearFromCursorToLineEnd
clearLines (from + 1) till
relativeCursorPosition :: Int -> Int -> (Int, Int)
relativeCursorPosition width pos =
let row = pos `div` width
col = pos - row * width
in (row, col)
printMessage :: ChatTerminal -> StyledString -> IO ()
printMessage ChatTerminal {termSize, nextMessageRow} msg = do
nmr <- readTVarIO nextMessageRow
C.setCursorPosition nmr 0
let (th, tw) = termSize
lc <- printLines tw msg
atomically . writeTVar nextMessageRow $ min (th - 1) (nmr + lc)
where
printLines :: Int -> StyledString -> IO Int
printLines tw ss = do
let s = styledToANSITerm ss
ls
| null s = [""]
| otherwise = lines s <> ["" | last s == '\n']
print_ ls
return $ foldl (\lc l -> lc + (length l `div` tw) + 1) 0 ls
print_ :: [String] -> IO ()
print_ [] = return ()
print_ (l : ls) = do
putStr l
C.clearFromCursorToLineEnd
putStr "\n"
print_ ls
getKey :: IO Key
getKey = charsToKey . reverse <$> keyChars ""
where
charsToKey = \case
"\ESC" -> KeyEsc
"\ESC[A" -> KeyUp
"\ESC[B" -> KeyDown
"\ESC[D" -> KeyLeft
"\ESC[C" -> KeyRight
"\ESCb" -> KeyAltLeft
"\ESCf" -> KeyAltRight
"\ESC[1;5D" -> KeyCtrlLeft
"\ESC[1;5C" -> KeyCtrlRight
"\ESC[1;2D" -> KeyShiftLeft
"\ESC[1;2C" -> KeyShiftRight
"\n" -> KeyEnter
"\DEL" -> KeyBack
"\t" -> KeyTab
'\ESC' : _ -> KeyUnsupported
cs -> KeyChars cs
keyChars cs = do
c <- getChar
more <- hReady stdin
-- for debugging - uncomment this, comment line after:
-- (if more then keyChars else \c' -> print (reverse c') >> return c') (c : cs)
(if more then keyChars else return) (c : cs)