module PvdMonad ( Pvd, PvdConf, CachedImg(..), initPvd, runPvd, getSocket, currentImage, fetchNextPath, putImgInCache, modPlaylist, getPlaylist, setPlaylist, getWin, getDpy, modIdx, getIdx, setIdx ) where import Control.Concurrent.STM import Control.Monad.Reader import Control.Monad (liftM) import Data.Ord (comparing) import Data.Function (on) import Data.List ((\\), sortBy, elemIndex) import Graphics.X11.Xlib (Display, Window) import Network.Socket (Socket) import XUtils (XImg) data PvdConf = PvdConf { cIdx :: TVar Int, cPlaylist :: TVar [String], cDpy :: Display, cWin :: Window, cImgCache :: TVar [(String, CachedImg)], cImgCacheSize :: Int, cSocket :: Socket } data CachedImg = CachedImg XImg | LoadingImg | LoadFailed type Pvd = ReaderT PvdConf STM runPvd :: PvdConf -> Pvd a -> IO a runPvd conf pvd = atomically $ runReaderT pvd conf initPvd playlist dpy win cacheSize socket = do cache <- newTVarIO [] idx <- newTVarIO 0 pl <- newTVarIO playlist let conf = PvdConf { cIdx = idx, cPlaylist = pl, cDpy = dpy, cWin = win, cImgCache = cache, cImgCacheSize = cacheSize, cSocket = socket } return conf readT :: (PvdConf -> TVar a) -> Pvd a readT f = liftM f ask >>= (lift . readTVar) writeT :: (PvdConf -> TVar a) -> a -> Pvd () writeT f x = liftM f ask >>= (lift . flip writeTVar x) getSocket :: Pvd Socket getSocket = liftM cSocket ask getDpy :: Pvd Display getDpy = liftM cDpy ask getWin :: Pvd Window getWin = liftM cWin ask getPlaylist :: Pvd [String] getPlaylist = readT cPlaylist setPlaylist :: [String] -> Pvd Bool setPlaylist p = do p0 <- readT cPlaylist writeT cPlaylist p return (p /= p0) getIdx :: Pvd Int getIdx = readT cIdx setIdx :: Int -> Pvd Bool setIdx i = do i0 <- readT cIdx writeT cIdx i return (i /= i0) currentImage = do idx <- getIdx pl <- getPlaylist path <- if idx >= 0 && idx < length pl then return (pl !! idx) else lift retry cache <- readT cImgCache case lookup path cache of Just (CachedImg img) -> return img _ -> lift retry fetchNextPath = do pl <- getPlaylist cache <- readT cImgCache idx <- getIdx sz <- liftM cImgCacheSize ask let paths = take sz (sortBy (comparePaths pl idx) pl) \\ fst (unzip cache) if null paths then lift retry else return (head paths) putImgInCache img path = do cache <- readT cImgCache pl <- getPlaylist idx <- getIdx cacheSize <- liftM cImgCacheSize ask let cache' = (path,img) : filter ((path /=) . fst) cache scache = sortBy (comparePaths pl idx `on` fst) cache' writeT cImgCache (take cacheSize scache) return path comparePaths playlist idx p1 p2 = case (i1,i2) of (Nothing, Nothing) -> EQ (Nothing, Just _) -> GT (Just _, Nothing) -> LT (Just n1, Just n2) | idx `elem` [n1,n2] -> comparing abs (n1-idx) (n2-idx) (Just n1, Just n2) -> comparing abs (n1-idx-1) (n2-idx-1) where i1 = elemIndex p1 playlist i2 = elemIndex p2 playlist modIdx f = do i <- getIdx l <- liftM length getPlaylist setIdx $ max 0 (min (l-1) (f (l,i))) modPlaylist f = liftM f getPlaylist >>= setPlaylist