{-# LANGUAGE ExistentialQuantification #-}
{-# OPTIONS_HADDOCK prune #-}
module Worm where
import Control.Concurrent (forkIO, threadDelay)
import Control.Concurrent.MVar (MVar, newEmptyMVar, newMVar, readMVar, isEmptyMVar, putMVar, swapMVar)
import Control.Monad (when, unless, forM_)
import Data.List (intercalate)
import System.IO (hPutStr, stderr)
wormWidth = 46
wormFrameDelayMillis = 230
data Progress = forall p. Progress_ p => Progress p
class Progress_ p where
formatProgress :: p -> IO String
instance Progress_ Progress where
formatProgress (Progress p) = formatProgress p
newtype RawProgress = RawProgress (MVar String)
instance Progress_ RawProgress where
formatProgress (RawProgress mV) = readMVar mV
rawProgress :: MVar String -> Progress
rawProgress s = Progress $ RawProgress s
newtype CollectingProgress = CollectingProgress (MVar Int, MVar Int)
instance Progress_ CollectingProgress where
formatProgress (CollectingProgress (done, total)) = do
done' <- readMVar done
total' <- readMVar total
return $ show done' ++ "/" ++ show total'
collectingProgress :: MVar Int -> MVar Int -> Progress
collectingProgress done total = Progress $ CollectingProgress (done, total)
_wormAnimationFrames :: [String]
_wormAnimationFrames =
concatMap (\indent -> map (replicate indent ' ' ++) wormStates) [0,2..wormWidth]
++ map (replicate wormWidth ' ' ++) turnAround
++ concatMap (\indent -> map (replicate indent ' ' ++) wormStates2) [wormWidth,(wormWidth-2)..0]
++ (map (drop 2 . reverse) turnAround)
where
wormStates = ["______, ", " __~__, ", " _/\\_, ", " __~__, "]
turnAround = [" __~_, ", " _~, ", " ,_ ", " ,~~_ ", " ,__~_ "]
wormStates2 = [" ,______ ", " ,__~__ ", " ,_/\\_ ", ",__~__ "]
type Canceller = IO ()
wormProgress :: [Progress] -> IO Canceller
wormProgress progress = do
shouldStop <- newEmptyMVar
stopped <- newEmptyMVar
forkIO $ _showWorm shouldStop stopped 0 (cycle _wormAnimationFrames)
return $ do
putMVar shouldStop True
readMVar stopped ; return ()
where
_showWorm shouldStop stopped prevLen (worm : nextWorms) = do
willStop <- not <$> isEmptyMVar shouldStop
when willStop $ do
hPutStr stderr $ "\r" ++ replicate prevLen ' ' ++ "\r\n"
putMVar stopped True
unless willStop $ do
progressChunks <- mapM formatProgress progress
let lineToDraw = worm ++ (intercalate " " $ map (("("++) . (++")")) $ filter (not.null) progressChunks)
let lengthDiff = max 0 (prevLen - length lineToDraw)
hPutStr stderr $ "\r" ++ lineToDraw ++ replicate (lengthDiff+2) ' ' ++ replicate lengthDiff '\b'
threadDelay $ wormFrameDelayMillis*1000
_showWorm shouldStop stopped (length lineToDraw) nextWorms
main = do
progress <- newMVar "loading..."
stopWorm <- wormProgress [rawProgress progress]
forM_ [0..300] $ \n -> do
let paddedPercent = reverse $ take 3 $ (reverse . show . floor $ n/3.0) ++ repeat ' '
swapMVar progress $ "loading (" ++ paddedPercent ++ "%)..."
threadDelay $ 80*1000
stopWorm