module Graphics.Blank
(
blankCanvas
, Context
, send
, events
, Canvas
, module Graphics.Blank.Generated
, readEvent
, tryReadEvent
, size
, Event(..)
, EventName(..)
, EventQueue
, readEventQueue
, tryReadEventQueue
) where
import Control.Concurrent
import Control.Monad.IO.Class (liftIO)
import Network.Wai.Handler.Warp (run)
import Network.Wai (Middleware,remoteHost, responseLBS)
import qualified Network.HTTP.Types as H
import Network.Socket (SockAddr(..))
import Web.Scotty as S
import qualified Data.Text.Lazy as T
import qualified Data.Map as Map
import Graphics.Blank.Events
import Graphics.Blank.Context
import Graphics.Blank.Canvas
import Graphics.Blank.Generated
import Paths_blank_canvas
blankCanvas :: Int -> (Context -> IO ()) -> IO ()
blankCanvas port actions = do
dataDir <- getDataDir
uVar <- newMVar 0
let getUniq :: IO Int
getUniq = do
u <- takeMVar uVar
putMVar uVar (u + 1)
return u
contextDB <- newMVar $ (Map.empty :: Map.Map Int Context)
let newContext :: (Float,Float) -> IO Int
newContext (w,h) = do
uq <- getUniq
picture <- newEmptyMVar
callbacks <- newMVar $ Map.empty
let cxt = Context (w,h) picture callbacks uq
db <- takeMVar contextDB
putMVar contextDB $ Map.insert uq cxt db
_ <- forkIO $ actions cxt
return uq
app <- scottyApp $ do
middleware local_only
get "/" $ file $ dataDir ++ "/static/index.html"
get "/jquery.js" $ file $ dataDir ++ "/static/jquery.js"
get "/jquery-json.js" $ file $ dataDir ++ "/static/jquery-json.js"
post "/start" $ do
req <- jsonData
uq <- liftIO $ newContext req
text (T.pack $ "session = " ++ show uq ++ ";redraw();")
post "/event/:num" $ do
addHeader "Cache-Control" "max-age=0, no-cache, private, no-store, must-revalidate"
num <- param "num"
NamedEvent nm event <- jsonData
db <- liftIO $ readMVar contextDB
case Map.lookup num db of
Nothing -> json ()
Just (Context _ _ callbacks _) -> do
db' <- liftIO $ readMVar callbacks
case Map.lookup nm db' of
Nothing -> json ()
Just var -> do liftIO $ writeEventQueue var event
json ()
get "/canvas/:num" $ do
addHeader "Cache-Control" "max-age=0, no-cache, private, no-store, must-revalidate"
num <- param "num"
let tryPicture picture n = do
res <- liftIO $ tryTakeMVar picture
case res of
Just js -> do
text $ T.pack js
Nothing | n == 0 ->
text (T.pack "")
Nothing -> do
liftIO $ threadDelay (100 * 1000)
tryPicture picture (n 1 :: Int)
db <- liftIO $ readMVar contextDB
case Map.lookup num db of
Nothing -> text (T.pack $ "alert('/canvas/, can not find " ++ show num ++ "');")
Just (Context _ pic _ _) -> tryPicture pic 10
run port app
send :: Context -> Canvas a -> IO a
send cxt@(Context (h,w) _ _ _) commands = send' commands id
where
send' :: Canvas a -> (String -> String) -> IO a
send' (Bind (Return a) k) cmds = send' (k a) cmds
send' (Bind (Bind m k1) k2) cmds = send' (Bind m (\ r -> Bind (k1 r) k2)) cmds
send' (Bind (Command cmd) k) cmds = send' (k ()) (cmds . shows cmd)
send' (Bind Size k) cmds = send' (k (h,w)) cmds
send' (Bind other k) cmds = do
res <- send' other cmds
send' (k res) id
send' (Get a op) cmds = do
sendToCanvas cxt cmds
chan <- events cxt a
op chan
send' (Return a) cmds = do
sendToCanvas cxt cmds
return a
send' other cmds = send' (Bind other Return) cmds
local_only :: Middleware
local_only f r = case remoteHost r of
SockAddrInet _ h | h == fromIntegral home
-> f r
SockAddrUnix _ -> f r
_ -> return $ responseLBS H.status403
[("Content-Type", "text/plain")]
"local access only"
where
home :: Integer
home = 127 + (256 * 256 * 256) * 1