packages feed

pvd-1.1.1: PvdMonad.hs

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