summaryrefslogtreecommitdiff
path: root/xhsb/XCBExt.hs
blob: 983456205edfdbd11e762da8fe3602ab472ef111 (plain)
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
{-# OPTIONS -ffi #-}
module XCBExt(Readable(..), ReplyReader, readSize, readString, request, requestWithReply) where

import XCB
import Control.Exception
import System.IO.Unsafe(unsafeInterleaveIO)
import Foreign
import CForeign
import Control.Monad.Reader
import Control.Monad.State
import Debug.Trace

trace' s = trace $ " * " ++ s

type ReplyReader a = StateT Int (ReaderT (ForeignPtr Word32) IO) a

class Readable a where
    replyRead :: ReplyReader a
    replyReadLen :: Enum n => n -> ReplyReader [a]
    replyReadLen n = sequence $ replicate (fromEnum n) $ replyRead

instance Readable Bool where replyRead = readBool
instance Readable Word8 where replyRead = readStorable
instance Readable Word16 where replyRead = readStorable
instance Readable Word32 where replyRead = readStorable
instance Readable Int8 where replyRead = readStorable
instance Readable Int16 where replyRead = readStorable
instance Readable Int32 where replyRead = readStorable

readSize :: Storable a => Int -> ReplyReader a
readSize size = do
    last <- get
    let cur = (last + size - 1) .&. (-size)
    put $ cur + size
    p <- return . trace' "read pointer" =<< ask
    liftIO $ liftIO $ unsafeInterleaveIO $ withForeignPtr p $ \p'-> trace' "peek" $ peek $ plusPtr p' cur

retTypeM :: Monad m => m a -> a
retTypeM _ = undefined

readStorable :: Storable a => ReplyReader a
readStorable = action
    where action = readSize (sizeOf $ retTypeM action)

readBool :: ReplyReader Bool
readBool = (replyRead :: ReplyReader Word8) >>= return . toBool

readString :: Enum n => n -> ReplyReader String
readString n = do
    cur <- get
    put $ cur + fromEnum n
    p <- ask
    liftIO $ liftIO $ unsafeInterleaveIO $ withForeignPtr p $ \p'-> peekCStringLen (plusPtr p' cur, fromEnum n)

foreign import ccall "X11/XCB/xcbext.h XCBWaitForReply" _waitForReply :: Ptr XCBConnection -> Word32 -> Ptr (Ptr XCBGenericError) -> IO (Ptr Word32)

request :: IO Word32 -> IO Word32
request = return . trace' "sent request" =<< throwIf (== 0) (const "couldn't send request")

repeatIO :: IO a -> IO [a]
repeatIO f = unsafeInterleaveIO $ do x <- f; xs <- repeatIO f; return (x:xs)

requestWithReply :: Ptr XCBConnection -> ReplyReader reply -> IO Word32 -> IO [reply]
requestWithReply c readReply req = do
    cookie <- request req
    repeatIO $ throwIfNull "couldn't get reply" (_waitForReply c cookie nullPtr) >>= newForeignPtr finalizerFree >>= runReaderT (evalStateT readReply 0) >>= return . trace' "got reply"