-
Notifications
You must be signed in to change notification settings - Fork 0
/
FlipdotsWeb.hs
110 lines (97 loc) · 3.92 KB
/
FlipdotsWeb.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
{-# LANGUAGE TemplateHaskell, QuasiQuotes, TypeFamilies, OverloadedStrings #-}
module Main where
import Graphics.Flipdots
import Paths_flipdots (getDataDir)
import qualified Yesod.Core as YC
import qualified Yesod.Static as YS
import Options.Applicative
import qualified Network.SocketIO as SocketIO
import qualified Network.EngineIO.Yesod as EIOYesod
import qualified Control.Concurrent.STM as STM
import qualified Data.ByteString as BS
import Control.Monad.Trans.State.Strict (StateT)
import Control.Monad.Trans.Reader (ReaderT)
import Control.Monad.IO.Class (liftIO)
import Data.Aeson
import Control.Exception (catch, IOException)
newtype FlipboardImg = FlipboardImg BS.ByteString
instance FromJSON FlipboardImg where
parseJSON = fmap (FlipboardImg . BS.pack) . parseJSON
instance ToJSON FlipboardImg where
toJSON (FlipboardImg img) = toJSON (BS.unpack img)
data FlipdotsWeb =
FlipdotsWeb { flipboardConfig :: FlipboardConfig
, flipboardState :: STM.TVar FlipboardImg
, getStatic :: YS.Static
, socketIoHandler :: YC.HandlerT FlipdotsWeb IO ()
}
YC.mkYesod "FlipdotsWeb" [YC.parseRoutesNoCheck|
/ IndexR GET
/static/ StaticR YS.Static getStatic
/socket.io/ SocketIOR
|]
instance YC.Yesod FlipdotsWeb where
-- do not redirect /socket.io/?bla=blub to /socket.io?bla=blub
cleanPath _ ["socket.io",""] = Right ["socket.io"]
cleanPath _ p = Right p
getIndexR :: Handler ()
getIndexR = do
dataDir <- YC.liftIO getDataDir
YC.sendFile "text/html" $ dataDir ++ "/index.html"
handleSocketIOR :: Handler ()
handleSocketIOR = YC.getYesod >>= socketIoHandler
socketApp :: StateT SocketIO.RoutingTable
(ReaderT SocketIO.Socket
(YC.HandlerT FlipdotsWeb IO)) ()
socketApp = do
FlipdotsWeb config state _ _ <- YC.getYesod
SocketIO.on "new image" $ \img@(FlipboardImg bs) -> do
liftIO $ do
putStrLn "##### New image #####"
STM.atomically $ STM.writeTVar state img
catch (sendImage' bs config) handle
SocketIO.broadcast "new image" img
currImg <- liftIO $ STM.atomically $ STM.readTVar state
SocketIO.emit "flipboard" $ object
[ "dims" .= flipboardDims config
, "image" .= currImg
]
where
handle :: IOException -> IO ()
handle e = putStrLn (show e)
main :: IO ()
main = do
config <- execParser opts
let initialImg = FlipboardImg $ renderImage (\_x _y -> False) (flipboardDims config)
flipboardState <- STM.newTVarIO initialImg
socketIOHandler <- SocketIO.initialize EIOYesod.yesodAPI socketApp
static <- YS.static =<< getDataDir
let app = FlipdotsWeb config flipboardState static socketIOHandler
putStrLn "Application running at http://localhost:8000/\n"
YC.warp 8000 app
where
positiveInt = do
a <- auto
if a > 0
then return a
else fail "expected a positive integer"
dimsP :: Parser Dims
dimsP =
Dims <$> option positiveInt ( long "rows" <> short 'r'
<> value 16 <> showDefault
<> metavar "ROWS" <> help "number of rows")
<*> option positiveInt ( long "cols" <> short 'c'
<> value 80 <> showDefault
<> metavar "COLS" <> help "number of columns")
flipboardConfigP :: Parser FlipboardConfig
flipboardConfigP =
FlipboardConfig
<$> strOption ( long "host" <> short 'h'
<> value "flipdot.openlab.lan" <> showDefault
<> metavar "HOSTNAME" <> help "the network address of the flipboard")
<*> option positiveInt ( long "port" <> short 'p'
<> value 2323 <> showDefault
<> metavar "PORTNUM" <> help "port number")
<*> dimsP
opts = info (helper <*> flipboardConfigP)
(fullDesc <> progDesc "Web interface for drawing on a flipboard" <> header "flipdots webinterface")