module Reflex.Dom.WebSocket.Foreign where
import Prelude hiding (div, span, mapM, mapM_, concat, concatMap, all, sequence)
import Control.Exception
import Control.Monad.State
import Data.ByteString (ByteString)
import Data.Text.Encoding
import Foreign.Marshal hiding (void)
import Foreign.Ptr
import Foreign.Storable
import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
import Graphics.UI.Gtk.WebKit.WebView
import qualified Data.ByteString as BS
import qualified Data.Text as T
import Reflex.Dom.Internal.Foreign
data JSWebSocket = JSWebSocket { wsValue :: JSValueRef
, wsContext :: JSContextRef
}
newWebSocket :: WebView -> String -> (ByteString -> IO ()) -> IO () -> IO () -> IO JSWebSocket
newWebSocket wv url onMessage onOpen onClose = withWebViewContext wv $ \c -> do
url' <- jsvaluemakestring c =<< jsstringcreatewithutf8cstring url
newWSArgs <- toJSObject c [url']
newWS <- jsstringcreatewithutf8cstring "(function(that) { var ws = new WebSocket(that[0]); ws['binaryType'] = 'arraybuffer'; return ws; })(this)"
ws <- jsevaluatescript c newWS newWSArgs nullPtr 1 nullPtr
onMessage' <- wrapper $ \_ _ _ _ args _ -> do
e <- peekElemOff args 0
dataProp <- jsstringcreatewithutf8cstring "data"
msg <- jsobjectgetproperty c e dataProp nullPtr
msg' <- fromJSStringMaybe c msg
case msg' of
Nothing -> return ()
Just m -> onMessage $ encodeUtf8 $ T.pack m
jsvaluemakeundefined c
onMessageCb <- jsobjectmakefunctionwithcallback c nullPtr onMessage'
onOpen' <- wrapper $ \_ _ _ _ _ _ -> do
onOpen
jsvaluemakeundefined c
onOpenCb <- jsobjectmakefunctionwithcallback c nullPtr onOpen'
onClose' <- wrapper $ \_ _ _ _ _ _ -> do
onClose
jsvaluemakeundefined c
onCloseCb <- jsobjectmakefunctionwithcallback c nullPtr onClose'
o <- toJSObject c [ws, onMessageCb, onOpenCb, onCloseCb]
addCbs <- jsstringcreatewithutf8cstring "this[0]['onmessage'] = this[1]; this[0]['onopen'] = this[2]; this[0]['onclose'] = this[3];"
_ <- jsevaluatescript c addCbs o nullPtr 1 nullPtr
return $ JSWebSocket ws c
webSocketSend :: JSWebSocket -> ByteString -> IO ()
webSocketSend (JSWebSocket ws c) bs = do
elems <- forM (BS.unpack bs) $ \x -> jsvaluemakenumber c $ fromIntegral x
let numElems = length elems
bs' <- bracket (mallocArray numElems) free $ \elemsArr -> do
pokeArray elemsArr elems
a <- jsobjectmakearray c (fromIntegral numElems) elemsArr nullPtr
newUint8Array <- jsstringcreatewithutf8cstring "new Uint8Array(this)"
jsevaluatescript c newUint8Array a nullPtr 1 nullPtr
send <- jsstringcreatewithutf8cstring "this[0]['send'](String['fromCharCode']['apply'](null, this[1]))"
sendArgs <- toJSObject c [ws, bs']
_ <- jsevaluatescript c send sendArgs nullPtr 1 nullPtr
return ()