{-| Module : EventLoop.EventProcessor Description : Low-level eventloop framework. Uses 'IOMessage's to communicate. Copyright : (c) Sebastiaan la Fleur, 2014 License : BSD3 Maintainer : sebastiaan.la.fleur@gmail.com Stability : experimental Portability : All Low-level eventloop framework. Uses 'IOMessage's to communicate. This module can be used to express your own 'EventLoop.Input.InputEvent's and 'EventLoop.Output.OutputEvent's. For instance, we have a keyboard and mouse 'EventLoop.Input.InputEvent' example included in this module\'s implementation. But it might be that you want to define your own 'EventLoop.Input.InputEvent' structure. In that case, you can define your own 'FromJSON' class to express how the literal 'IOMessage' should be parsed into your own 'EventLoop.Input.InputEvent' datatype. The same goes for 'EventLoop.Output.OutputEvent' of course. An 'IOMessage' is a 'String' literal that the client sends. The accompanied standard webpage uses JSON as a protocol to express these 'String' literals. -} module EventLoop.EventProcessor(eventloop, IOMessage, readRequest, sendResponse) where import qualified Network.WebSockets as WS import qualified Data.Text as T import Data.String (String) import Data.Char (isLower, isDigit) import Control.Monad (sequence) import EventLoop.Json import EventLoop.Config -- | Type of the message that is used to communicate with the IO device. In this example implementation a webbrowser is used and the messages use JSON encoding. type IOMessage = JSONMessage {- | Low-level function to call when dealing with IOMessages directly. If you want to use the example implementation using the 'EventLoop.Input.InputEvent' and 'EventLoop.Output.OutputEvent' , you should use the 'EventLoop.Main.start' function. -} eventloop :: (a -> IOMessage -> ([IOMessage], a)) -- ^ The eventhandler eh that maps incoming 'IOMessage's to outgoing 'IOMessage's. It uses a variable of type a to store information in between calls to the event handler. -> a -- ^ The begin store. It should be changed in the eventhandler function. -> IO () eventloop eh beginState = WS.server ipadres port $ doEvents eh beginState {- | This function maps all incoming messages over the eventhandler and sends off the resulting output messages. Uses the connection to communicate with the IO device. Uses the Standard Output Mutex to print information to stdout in a safe multi-threaded way as each connection starts a thread that uses the 'doEvents' function. -} doEvents :: (a -> IOMessage -> ([IOMessage], a)) -- ^ The eventhandler eh that maps incoming 'IOMessage's to outgoing 'IOMessage's. It uses a variable of type a to store information in between calls to the event handler. -> a -- ^ The begin store. It should be changed in the eventhandler function. -> WS.Connection -- ^ Connection to the IO device. -> WS.StdOutMutex -- ^ Standard Output Mutex for printing information in a safe multi-threaded way to the standard out. -> WS.ConnectionSendMutex -- ^ Connection Send Mutex for sending 'IOMessage's to the IO device in a safe multi-threaded way. -> IO () doEvents eh state conn stdoutM connSendM = do request <- readRequest conn stdoutM let (resp, state') = eh state request sendActions = map (sendResponse connSendM conn stdoutM) resp sequence sendActions doEvents eh state' conn stdoutM connSendM {- | This functions reads a request from the 'WS.Connection' and parses it into an 'IOMessage'. -} readRequest :: WS.Connection -> WS.StdOutMutex -> IO IOMessage readRequest conn stdoutM = do msg <- WS.receiveData conn :: IO T.Text let string = T.unpack msg request = stringToJsonObject string --WS.safePutStr stdoutM string return request {- | This function takes an 'IOMessage' and parses it into a response. Then it sends the response to the 'WS.Connection'. -} sendResponse :: WS.ConnectionSendMutex -> WS.Connection -> WS.StdOutMutex -> IOMessage -> IO () sendResponse mu conn stdoutM response = do let string = show response text = T.pack string --WS.safePutStr stdoutM string WS.safeSendText mu conn text