-
Notifications
You must be signed in to change notification settings - Fork 1
/
PvdMonad.hs
148 lines (124 loc) · 3.31 KB
/
PvdMonad.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
module PvdMonad (
Pvd,
PvdConf,
CachedImg(..),
initPvd,
runPvd,
getSocket,
currentImage,
fetchNextPath,
putImgInCache,
modPlaylist,
getPlaylist,
setPlaylist,
getWin,
getDpy,
modIdx,
getIdx,
setIdx,
notifyChange,
waitForChange
) 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,
cChanges :: TVar Int
}
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
c <- newTVarIO 1
pl <- newTVarIO playlist
let conf = PvdConf {
cIdx = idx, cPlaylist = pl, cDpy = dpy, cWin = win, cImgCache = cache,
cImgCacheSize = cacheSize, cSocket = socket, cChanges = c
}
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)
modT f g = do
x <- readT f
writeT f (g 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
notifyChange = modT cChanges (+ 1)
waitForChange = do
c <- readT cChanges
when (c <= 0) (lift retry)
writeT cChanges 0