module Graphics.X11.Turtle.Layers(
Layers,
Layer,
Character,
newLayers,
addLayer,
addCharacter,
addLayerAction,
undoLayer,
clearLayer,
setCharacter,
) where
import System.IO.Unsafe
import Data.IORef
import Data.List.Tools
import Control.Concurrent
lockChan :: Chan ()
lockChan = unsafePerformIO $ do
c <- newChan
writeChan c ()
return c
withLock2 :: IO a -> IO a
withLock2 act = do
readChan lockChan
ret <- act
writeChan lockChan ()
return ret
withLock :: Layers -> (Layers -> IO a) -> IO a
withLock ls act = do
readChan $ lock ls
ret <- act ls
writeChan (lock ls) ()
return ret
data Layers = Layers{
undoNum :: Int,
undoLayersAction :: IO (),
clearLayersAction :: IO (),
clearCharactersAction :: IO (),
flush :: IO (),
buffed :: [IO ()],
layers :: [[(IO (), IO ())]],
characters :: [IO ()],
lock :: Chan ()
}
newLayers :: Int -> IO () -> IO () -> IO () -> IO () -> IO (IORef Layers)
newLayers un ula cla cca flsh = do
ls <- newLayers_ un ula cla cca flsh
newIORef ls
newLayers_ :: Int -> IO () -> IO () -> IO () -> IO () -> IO Layers
newLayers_ un ula cla cca flsh = do
l <- newChan
writeChan l ()
return Layers{
undoNum = un,
undoLayersAction = ula,
clearLayersAction = cla,
clearCharactersAction = cca,
flush = flsh,
buffed = [],
layers = [],
characters = [],
lock = l
}
data Layer = Layer{
layerId :: Int,
layerLayers :: IORef Layers
}
data Character = Character{
characterId :: Int,
characterLayers :: IORef Layers
}
addLayer :: IORef Layers -> IO Layer
addLayer rls = withLock2 $ do
ls <- readIORef rls
let (lid, nls) = addLayer_ ls
writeIORef rls nls
return Layer{layerId = lid, layerLayers = rls}
addLayer_ :: Layers -> (Int, Layers)
addLayer_ ls =
(length $ layers ls,
ls{layers = layers ls ++ [[]], buffed = buffed ls ++ [return ()]})
addLayerAction :: Layer -> (IO (), IO ()) -> IO ()
addLayerAction Layer{layerId = lid, layerLayers = rls} acts = withLock2 $
readIORef rls >>= flip withLock (\ls -> do
nls <- addLayerAction_ ls lid acts
writeIORef rls nls)
addLayerAction_ :: Layers -> Int -> (IO (), IO ()) -> IO Layers
addLayerAction_ ls l acts@(_, act) = do
let actNum = length $ layers ls !! l
act
clearCharactersAction ls
sequence_ $ characters ls
if actNum < undoNum ls then
return ls{layers =
modifyAt (layers ls) l (++ [acts])}
else do fst $ head $ layers ls !! l
return ls{
layers = modifyAt (layers ls) l
((++ [acts]) . tail),
buffed = modifyAt (buffed ls) l
(>> fst (head $ layers ls !! l))}
undoLayer :: Layer -> IO Bool
undoLayer Layer{layerId = lid, layerLayers = rls} = withLock2 $
readIORef rls >>= flip withLock (\ls -> do
mnls <- undoLayer_ ls lid
maybe (return False)
((>> return True) . writeIORef rls) mnls)
undoLayer_ :: Layers -> Int -> IO (Maybe Layers)
undoLayer_ ls l =
if null $ layers ls !! l then return Nothing else do
let nls = modifyAt (layers ls) l init
undoLayersAction ls
mapM_ snd $ concat nls
clearCharactersAction ls
sequence_ $ characters ls
return $ Just ls{layers = nls}
clearLayer :: Layer -> IO ()
clearLayer Layer{layerId = lid, layerLayers = rls} = withLock2 $ do
ls <- readIORef rls
nls <- clearLayer_ ls lid
writeIORef rls nls
clearLayer_ :: Layers -> Int -> IO Layers
clearLayer_ ls l = do
let nls = setAt (layers ls) l []
nbf = setAt (buffed ls) l $ return ()
clearLayersAction ls
sequence_ nbf
undoLayersAction ls
mapM_ snd $ concat nls
return ls{layers = nls, buffed = nbf}
addCharacter :: IORef Layers -> IO Character
addCharacter rls = withLock2 $ do
ls <- readIORef rls
let (cid, nls) = addCharacter_ ls
writeIORef rls nls
return Character{characterId = cid, characterLayers = rls}
addCharacter_ :: Layers -> (Int, Layers)
addCharacter_ ls =
(length $ characters ls,
ls{characters = characters ls ++ [return ()]})
setCharacter :: Character -> IO () -> IO ()
setCharacter Character{characterId = cid, characterLayers = rls} act = withLock2 $
readIORef rls >>= flip withLock (\ls -> do
nls <- setCharacter_ ls cid act
writeIORef rls nls
)
setCharacter_ :: Layers -> Int -> IO () -> IO Layers
setCharacter_ ls c act = do
let cs = setAt (characters ls) c act
clearCharactersAction ls
sequence_ cs
return ls{characters = cs}