Chitra 0.2.1 → 0.2.2
raw patch · 10 files changed
+594/−2 lines, 10 files
Files
- Chitra.cabal +4/−2
- Chitra/Canvas.hs +27/−0
- RFB/ClientToServer.hs +109/−0
- RFB/CommandLoop.hs +129/−0
- RFB/Constants.hs +11/−0
- RFB/Encoding.hs +67/−0
- RFB/Handshake.hs +77/−0
- RFB/Server.hs +56/−0
- RFB/State.hs +77/−0
- Utilities/ParseByteString.hs +37/−0
Chitra.cabal view
@@ -7,7 +7,7 @@ -- The package version. See the Haskell package versioning policy -- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for -- standards guiding when and how versions should be incremented.-Version: 0.2.1+Version: 0.2.2 -- A short (one-line) description of the package. Synopsis: A platform independent mechanism to render graphics using vnc.@@ -56,7 +56,9 @@ -- Modules not exported by this package.- -- Other-modules: + Other-modules: Chitra.Canvas, RFB.ClientToServer, RFB.CommandLoop,+ RFB.Constants, RFB.Encoding, RFB.Handshake, RFB.Server, + RFB.State, Utilities.ParseByteString -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source. -- Build-tools:
+ Chitra/Canvas.hs view
@@ -0,0 +1,27 @@+module Chitra.Canvas where++import Control.Monad+import System.IO++import qualified Data.ByteString.Lazy as BS+import qualified Data.ByteString.Lazy.Char8 as B+import Data.Char+import Data.Binary.Get+import Data.Binary.Put+import Data.Word++import qualified RFB.Constants as RFB+import qualified RFB.Server as SERVER+import qualified RFB.Handshake as Handshake+import qualified RFB.CommandLoop as CommandLoop++++start w h p= do+ SERVER.serve w h p doit++doit w h hnd = do+ Handshake.handshake w h hnd+ CommandLoop.commandLoop hnd w h+ +
+ RFB/ClientToServer.hs view
@@ -0,0 +1,109 @@+module RFB.ClientToServer (+ Command+ (+ SetPixelFormat ,+ SetEncodings ,+ FramebufferUpdate ,+ KeyEvent ,+ PointerEvent ,+ ClientCutText ,+ BadCommand+ ),+ CommandData+ (+ SetPixelFormatData ,+ SetEncodingsData ,+ FramebufferUpdateData ,+ KeyEventData ,+ PointerEventData ,+ ClientCutTextData+ ),+ bytesToRead,+ parseCommandByteString,+ getCommandData,+ word8ToCommand,+) where++import Data.Word++import qualified Utilities.ParseByteString as PBS+import qualified Data.ByteString.Lazy as BS+++data Command = SetPixelFormat | SetEncodings | FramebufferUpdate | KeyEvent | PointerEvent | ClientCutText | BadCommand deriving (Show)++data CommandData = SetPixelFormatData {+ bitsPerPixel :: Int,+ depth :: Int,+ bigEndian :: Int,+ trueColor :: Int,+ redMax :: Int,+ greenMax :: Int,+ blueMax :: Int,+ redShift :: Int,+ greenShift :: Int,+ blueShift :: Int+} | ClientCutTextData {+ length :: Int+} | SetEncodingsData {+ count :: Int+} | FramebufferUpdateData {+ incremental :: Int,+ x :: Int,+ y :: Int,+ width :: Int,+ height :: Int+} | KeyEventData {+ downFlag :: Int,+ key :: Int+} | PointerEventData {+ buttonMask :: Int,+ x :: Int,+ y :: Int+} | BadCommandData deriving (Show)++++bytesToRead :: Command -> Int+bytesToRead c = foldr (+) 0 (map (\x -> if x == 0 then 1 else x) (commandFormat c))++commandFormat :: Command -> [Int] -- 0 for padding bytes+commandFormat c = case c of+ SetPixelFormat -> [0,0,0,1,1,1,1,2,2,2,1,1,1,0,0,0]+ SetEncodings -> [0,2]+ FramebufferUpdate -> [1,2,2,2,2]+ KeyEvent -> [1,0,0,4]+ PointerEvent -> [1,2,2]+ ClientCutText -> [0,0,0,4]+ BadCommand -> []+++parseCommandByteString :: BS.ByteString -> Command -> [Int]+parseCommandByteString byteString command =+ PBS.parseByteString list byteString+ where + list = (commandFormat command)++getCommandData :: BS.ByteString -> Command -> CommandData+getCommandData byteString command = commandDataFromList list+ where+ list = PBS.parseByteString (commandFormat command) byteString+ commandDataFromList xs = case command of+ SetPixelFormat -> SetPixelFormatData (xs!!0) (xs!!1) (xs!!2) (xs!!3) (xs!!4) (xs!!5) (xs!!6) (xs!!7) (xs!!8) (xs!!9)+ SetEncodings -> SetEncodingsData (xs!!0)+ FramebufferUpdate -> FramebufferUpdateData (xs!!0) (xs!!1) (xs!!2) (xs!!3) (xs!!4)+ KeyEvent -> KeyEventData (xs!!0) (xs!!1)+ PointerEvent -> PointerEventData (xs!!0) (xs!!1) (xs!!2)+ ClientCutText -> ClientCutTextData (xs!!0)+ BadCommand -> BadCommandData+++word8ToCommand :: Word8 -> Command+word8ToCommand w = case w of+ 0 -> SetPixelFormat+ 2 -> SetEncodings+ 3 -> FramebufferUpdate+ 4 -> KeyEvent+ 5 -> PointerEvent+ 6 -> ClientCutText+ _ -> BadCommand
+ RFB/CommandLoop.hs view
@@ -0,0 +1,129 @@+{-# LANGUAGE NamedFieldPuns #-}+module RFB.CommandLoop (commandLoop) where++import System.IO+import qualified Data.ByteString.Lazy as BS++import Data.Char+import Data.Binary.Get+import Data.Binary.Put+import Data.Word++import qualified RFB.ClientToServer as RFBClient+import qualified RFB.Encoding as Encoding+import qualified RFB.State as RFBState++import Control.Monad.State+++type MyState a = StateT RFBState.RFBState IO a++commandLoop :: Handle -> Int -> Int -> IO ()+commandLoop h width height= do+ runStateT (commandLoop1 h) (RFBState.initialState width height)+ return ()+++commandLoop1 :: Handle -> MyState ()+commandLoop1 handle = do+ commandByte <- liftIO$ BS.hGet handle 1+ let command = RFBClient.word8ToCommand $ runGet (do {x<-getWord8;return(x);}) commandByte+ byteStream <- liftIO $ BS.hGet handle (RFBClient.bytesToRead command)+ let commandData = RFBClient.getCommandData byteStream command+ --let commandData = RFBClient.parseCommandByteString byteStream command+ liftIO $ putStrLn $ "Client said -> " ++ (show command)+ liftIO $ putStrLn (show commandData)+ case command of+ RFBClient.SetPixelFormat -> processSetPixelFormat commandData+ RFBClient.SetEncodings -> liftIO $ processSetEncodings handle commandData + RFBClient.FramebufferUpdate -> processFrameBufferUpdateRequest handle commandData+ RFBClient.PointerEvent -> processPointerEvent handle commandData+ RFBClient.ClientCutText -> processClientCutTextEvent handle commandData+ RFBClient.KeyEvent -> processKeyEvent handle commandData+ RFBClient.BadCommand -> liftIO $ putStrLn (show command)+ commandLoop1 handle++++processClientCutTextEvent handle (RFBClient.ClientCutTextData length) = do+ x <- liftIO $ BS.hGet handle length+ liftIO $ putStrLn (show x)++ ++processKeyEvent handle commandData = do+ return ()++processPointerEvent handle (RFBClient.PointerEventData buttonMask x y)= do+ state@(RFBState.RFBState d i p pend u) <- get+ let newState=if buttonMask == 1 then Encoding.putPixel state (x,y) else state+ put newState+ if pend && ((length u) > 0) then do+ liftIO $ putStrLn $ "Updating -> " ++ (show u)+ liftIO $ BS.hPutStr handle (updateRectangles state)+ liftIO $ hFlush handle+ put (RFBState.RFBState d i p False [])+ return ()+ else + return ()++++processSetPixelFormat (RFBClient.SetPixelFormatData bitsPerPixel depth bigEndian trueColor redMax greenMax blueMax redShift greenShift blueShift)+ = do+ state <- get+ let newState=RFBState.setPixelFormat state bitsPerPixel bigEndian redMax greenMax blueMax redShift greenShift blueShift+ put newState+ return ()++++processSetEncodings handle (RFBClient.SetEncodingsData count) = do+ x <- BS.hGet handle (4*count)+ let e = runGet (do {x<-getWord32le; return x}) x+ putStrLn (show e)+ return ()+++fullScreen :: Int -> Int -> Int -> Int -> RFBState.RFBState -> BS.ByteString+fullScreen x y width height state = runPut $ do+ putWord8 0+ putWord8 0+ putWord16be 1+ putWord16be (fromIntegral x)+ putWord16be (fromIntegral y)+ putWord16be (fromIntegral width)+ putWord16be (fromIntegral height)+ putWord32be 0+ Encoding.getImageByteString state x y width height+ return ()++++updateRectangles state@(RFBState.RFBState _ _ (RFBState.PixelFormat bpp _ _ _ _ _ _ _) pend updateList) = runPut $ do+ putWord8 0+ putWord8 0+ putWord16be (fromIntegral (length updateList))+ Encoding.encodeRectagles bpp updateList+ return ()++empty = runPut $ return ()+++ ++processFrameBufferUpdateRequest handle (RFBClient.FramebufferUpdateData incremental x y width height) = do -- this produces black+ state@(RFBState.RFBState d i p pend u) <- get+ let byteString=if incremental == 0 then fullScreen x y width height state else (if (length u > 0) then updateRectangles state else empty)+ --let newState = if incremental == 0 then state else RFBState.RFBState d i p pend []+ let newState = if incremental == 0 then RFBState.RFBState d i p False [] else RFBState.RFBState d i p (if (length u > 0) then False else True) [] + put newState+ (RFBState.RFBState _ _ _ newPend _) <- get+ liftIO $ putStrLn (show pend)+ liftIO $ putStrLn (show newPend)+ --liftIO $ putStrLn (show state)+ --liftIO $ putStrLn (show newState)+ liftIO $ BS.hPutStr handle byteString+ liftIO $ hFlush handle++
+ RFB/Constants.hs view
@@ -0,0 +1,11 @@+module RFB.Constants where++import Data.Word++-- Client to Server messages+setPixelFormat = 0 :: Word8+setEncodings = 2 :: Word8+framebufferUpdateRequest = 3 :: Word8+keyEvent = 4 :: Word8+pointerEvent = 5 :: Word8+clientCutText = 6 :: Word8
+ RFB/Encoding.hs view
@@ -0,0 +1,67 @@+module RFB.Encoding (getImageByteString,putPixel,encodeRectagles) where++import qualified Data.ByteString.Lazy as BS+import Data.Binary.Get+import Data.Binary.Put+import Data.Word+import Data.Bits++import qualified RFB.State as RFBState+++getImageByteString (RFBState.RFBState (totalWidth,_) imageData (RFBState.PixelFormat bpp _ _ _ _ _ _ _) pending updateList) x y width height = encodeImage (clip imageData x y width height totalWidth) bpp+++++clip imageData x y width height totalWidth = horizontolClip verticalClip+ where+ verticalClip = take (height*totalWidth) (drop (y*totalWidth) imageData)+ horizontolClip [] = []+ horizontolClip image = (take width (drop x image)) ++ (horizontolClip (drop totalWidth image))++++encodeImage [] _ = return ()+encodeImage ((r,g,b):xs) bpp = do+ encode (r,g,b) bpp+ encodeImage xs bpp++++encode (r,g,b) bitsPerPixel = do+ case bitsPerPixel of+ RFBState.Bpp8 -> putWord8 z8+ RFBState.Bpp16 -> putWord16le z16+ RFBState.Bpp32 -> putWord32le z32+ where + z8 = (fromIntegral $ nr + ng + nb) :: Word8+ z16 = (fromIntegral $ nr + ng + nb) :: Word16+ z32 = (fromIntegral $ nr + ng + nb) :: Word32+ nr = scale r (RFBState.r_max bitsPerPixel) (RFBState.r_shift bitsPerPixel)+ ng = scale g (RFBState.g_max bitsPerPixel) (RFBState.g_shift bitsPerPixel)+ nb = scale b (RFBState.b_max bitsPerPixel) (RFBState.b_shift bitsPerPixel)+ scale c cm cs = (c * cm `div` 255) `shift` cs+++encodeRectagles _ [] = return ()+encodeRectagles bpp ((RFBState.Rectangle x y w h):xs) = do+ putWord16be (fromIntegral x)+ putWord16be (fromIntegral y)+ putWord16be (fromIntegral w)+ putWord16be (fromIntegral h)+ putWord32be 0+ encode (r,g,b) bpp+ encodeRectagles bpp xs+ where+ r = 255 :: Word32+ g = 255 :: Word32+ b = 255 :: Word32++++putPixel (RFBState.RFBState dim@(width,_) imageData pf pending updateList) (x,y) = RFBState.RFBState dim newImgeData pf pending ((RFBState.Rectangle x y 1 1):updateList)+ where+ newImgeData = (take count imageData) ++ [(255,255,255)] ++ (drop (count+1) imageData)+ count = width*y + x+
+ RFB/Handshake.hs view
@@ -0,0 +1,77 @@+module RFB.Handshake (handshake) where++import System.IO+import qualified Data.ByteString.Lazy as BS+import qualified Data.ByteString.Lazy.Char8 as B+import Data.Char+import Data.Binary.Get+import Data.Binary.Put+import Data.Word++import Control.Monad.State++type MyState a = StateT Int IO a++++handshake :: Int -> Int -> Handle -> IO ()+handshake w h hnd = do+ runStateT (handshake1 w h hnd) 0+ return ()+ ++handshake1 :: Int -> Int -> Handle -> MyState ()+handshake1 width height handle = do+ liftIO $ hPutStrLn handle "RFB 003.003"+ liftIO $ hFlush handle++ byteStream <- liftIO $ hGetLine handle+ liftIO $ putStrLn ("Client Said :: " ++ (show byteStream))+ let majorVersion = read $ (take 3).(drop 4) $ byteStream :: Int+ if majorVersion /= 3 then liftIO (fail ("Expected 3 but the Client sent " ++ (show majorVersion)))+ else liftIO $ return () -- contiue down the function++ -- Send 1 to the client, meaning, no auth required+ liftIO $ BS.hPutStr handle (BS.pack [0,0,0,1])+ liftIO $ hFlush handle++ clientInitMessage <- liftIO $ BS.hGet handle 1++ let sharedOrNot = runGet (do {x<-getWord8;return(x);}) clientInitMessage++ if sharedOrNot==1 then liftIO $ putStrLn "Sharing enabled"+ else liftIO $ putStrLn "Sharing disabled"++ liftIO $ BS.hPutStr handle (serverInitMessage width height)+ liftIO $ hFlush handle+++++serverInitMessage :: Int -> Int -> BS.ByteString+serverInitMessage width height = runPut $ do+ putWord16be (fromIntegral width::Word16) -- width+ putWord16be (fromIntegral height::Word16) -- height+ --pixel format+ putWord8 (32::Word8) -- bits per pixl+ putWord8 (24::Word8) -- depth+ putWord8 (1::Word8) -- big endian+ putWord8 (1::Word8) -- true color+ putWord16be (255::Word16) -- red max+ putWord16be (255::Word16) -- green max+ putWord16be (255::Word16) -- blue max+ putWord8 (16::Word8) -- red shift+ putWord8 (8::Word8) -- green shift+ putWord8 (0::Word8) -- blue shift++ --padding+ putWord8 (0::Word8)+ putWord8 (0::Word8)+ putWord8 (0::Word8)++ --name length+ let name = "Haskell Framebuffer"+ putWord32be (((fromIntegral.length) name)::Word32)+ --mapM_ (putWord8.fromIntegral.ord) name --this works+ putLazyByteString (B.pack name)+ --putLazyByteString (stringToByteString name) --this works
+ RFB/Server.hs view
@@ -0,0 +1,56 @@+module RFB.Server (serve) where++import Data.Bits+import Network.Socket+import Network.BSD+import Data.List+import Control.Concurrent+import Control.Concurrent.MVar+import System.IO+++-- HandlerFunc Width Height+type HandlerFunc = Int -> Int -> Handle -> IO ()++serve :: Int -> Int -> String -> HandlerFunc -> IO ()+serve width height port handlerFunc = withSocketsDo $+ do -- Look up the port. Either raises an exception or returns+ -- a nonempty list. + addrinfos <- getAddrInfo + (Just (defaultHints {addrFlags = [AI_PASSIVE]}))+ Nothing (Just port)+ let serveraddr = head addrinfos++ -- Create a socket+ sock <- socket (addrFamily serveraddr) Stream defaultProtocol++ -- Bind it to the address we're listening to+ bindSocket sock (addrAddress serveraddr)++ -- Start listening for connection requests. Maximum queue size+ -- of 5 connection requests waiting to be accepted.+ listen sock 5++ -- Create a lock to use for synchronizing access to the handler+ lock <- newMVar ()++ -- Loop forever waiting for connections. Ctrl-C to abort.+ procRequests lock sock+ where+ -- | Process incoming connection requests+ procRequests :: MVar () -> Socket -> IO ()+ procRequests lock mastersock = + do (connsock, clientaddr) <- accept mastersock+ forkIO $ procMessages lock connsock clientaddr+ procRequests lock mastersock++ -- | Process incoming messages+ procMessages :: MVar () -> Socket -> SockAddr -> IO ()+ procMessages lock connsock clientaddr =+ do connhdl <- socketToHandle connsock ReadWriteMode+ --hSetBuffering connhdl LineBuffering+ --messages <- hGetContents connhdl+ --mapM_ (handle lock clientaddr) (lines messages)+ handlerFunc width height connhdl+ hClose connhdl+
+ RFB/State.hs view
@@ -0,0 +1,77 @@+module RFB.State (+ RFBState (RFBState),+ PixelFormat (PixelFormat),+ initialState,+ setPixelFormat,+ r_max,+ g_max,+ b_max,+ r_shift,+ g_shift,+ b_shift,+ BitsPerPixel( Bpp8, Bpp16, Bpp32),+ Rectangle (Rectangle)+ ) where++type Red = Int+type Green = Int+type Blue = Int+type Color = (Red,Green,Blue)++type ImageData = [Color]+type ImageDimension = (Int,Int)++type BigEndian = Int+type RedMax = Int+type GreenMax = Int+type BlueMax = Int+type RedShift = Int+type GreenShift = Int+type BlueShift = Int++type FrameBufferUpdatePending = Bool++data BitsPerPixel = Bpp8 | Bpp16 | Bpp32 deriving (Show)++data PixelFormat = PixelFormat BitsPerPixel BigEndian RedMax GreenMax BlueMax RedShift GreenShift BlueShift deriving (Show)++data Rectangle = Rectangle Int Int Int Int deriving (Show)++data RFBState = RFBState ImageDimension ImageData PixelFormat FrameBufferUpdatePending [Rectangle] deriving(Show)++initialState width height = RFBState (width,height) imageData pixelFormat False []+ where+ imageData = replicate (width*height) (0,0,255)+ pixelFormat = PixelFormat Bpp32 1 255 255 255 16 8 0++setPixelFormat (RFBState dim imageData _ pending updateList) bpp bigEndian rm gm bm rs gs bs = RFBState dim imageData (PixelFormat (int2bpp bpp) bigEndian rm gm bm rs gs bs) pending updateList++int2bpp i+ | i == 8 = Bpp8+ | i == 16 = Bpp16+ | otherwise = Bpp32+++r_max Bpp8 = 7+r_max Bpp16 = 31+r_max Bpp32 = 255++g_max Bpp8 = 7+g_max Bpp16 = 63+g_max Bpp32 = 255++b_max Bpp8 = 3+b_max Bpp16 = 31+b_max Bpp32 = 255++r_shift Bpp8 = 0+r_shift Bpp16 = 11+r_shift Bpp32 = 16++g_shift Bpp8 = 3+g_shift Bpp16 = 5+g_shift Bpp32 = 8++b_shift Bpp8 = 6+b_shift Bpp16 = 0+b_shift Bpp32 = 0
+ Utilities/ParseByteString.hs view
@@ -0,0 +1,37 @@+module Utilities.ParseByteString (parseByteString) where++import Control.Applicative ((<$>))+import Data.Word+import Data.Binary.Get+import qualified Data.ByteString.Lazy as BS++data Action m = A1 (m Word8) | A2 (m Word16) | A4 (m Word32)++action 1 = A1 getWord8+action 2 = A2 getWord16be+action 4 = A4 getWord32be+action _ = A1 getWord8+++getActionList xs = map action xs++newtype Id a = Id a++getAction :: Action Get -> Get (Action Id)+getAction (A1 act) = A1 . Id <$> act +getAction (A2 act) = A2 . Id <$> act+getAction (A4 act) = A4 . Id <$> act++getActions :: [Action Get] -> Get [Action Id]+getActions = mapM getAction++toVal (A1 (Id v)) = fromIntegral v :: Int+toVal (A2 (Id v)) = fromIntegral v :: Int+toVal (A4 (Id v)) = fromIntegral v :: Int++parseByteString :: (Num a) => [a] -> BS.ByteString -> [Int]+parseByteString list byteString = cleanup solution+ where + cleanup list1 = map snd . filter ((/= 0) . fst) $ zip list list1+ solution = map toVal $ runGet actionList byteString+ actionList = getActions (getActionList list)