summaryrefslogtreecommitdiff
path: root/wayland/Wayland
diff options
context:
space:
mode:
Diffstat (limited to 'wayland/Wayland')
-rw-r--r--wayland/Wayland/Server.hs1
-rw-r--r--wayland/Wayland/Server/Display.hs16
-rw-r--r--wayland/Wayland/Server/Object.hs161
3 files changed, 118 insertions, 60 deletions
diff --git a/wayland/Wayland/Server.hs b/wayland/Wayland/Server.hs
index 07fc3d7..0e18048 100644
--- a/wayland/Wayland/Server.hs
+++ b/wayland/Wayland/Server.hs
@@ -54,6 +54,7 @@ module Wayland.Server
, EventClass
, ArgumentMarshaller(..)
, ArgumentDemarshaller(..)
+ , Marshallable(..)
) where
import Wayland.Server.Interface
diff --git a/wayland/Wayland/Server/Display.hs b/wayland/Wayland/Server/Display.hs
index 527d9da..2594330 100644
--- a/wayland/Wayland/Server/Display.hs
+++ b/wayland/Wayland/Server/Display.hs
@@ -338,7 +338,8 @@ flushClientEvents c = liftIO (unwrapClient c >>= wlClientFlush)
-- The object is published through the global wl_registry instance. When a client wants to instantiate the global
-- object, the given "bind" message will be dispatched in the message loop.
registerGlobal
- :: forall o bindMsg s. Object o
+ :: forall o bindMsg s
+ . Object o
=> MessageClass bindMsg
=> (Global -> NewObject o -> bindMsg) -- ^ Function used to create a message to bind the global object for a client.
-> DisplayM s Global -- ^ Display monad wrapping a handle to the global which can be used to unregister it.
@@ -394,7 +395,7 @@ createObject o destroyHook = do
-- This will invoke the object's "destroy" hook.
destroyObject :: Object o => o -> DisplayM s ()
destroyObject o = liftIO $ do
- ptr <- unwrapResource o
+ ptr <- unwrapResource (toAny o)
wlResourceDestroy ptr
-- | Send an event for an object to the client it is associated with.
@@ -510,11 +511,12 @@ unregisterFileSource file = liftIO $ do
freeHaskellFunPtr (fileFunc file)
fromFileEventMask :: FileEventMask -> Word32
-fromFileEventMask events = conditionalMask wlEventReadable (fileEventReadable events)
- $ conditionalMask wlEventWritable (fileEventWritable events)
- $ conditionalMask wlEventHangup (fileEventHangup events)
- $ conditionalMask wlEventError (fileEventError events) 0
- where conditionalMask mask enabled x = if enabled then x .|. mask else x
+fromFileEventMask events = 0
+ .|. conditionalMask wlEventReadable (fileEventReadable events)
+ .|. conditionalMask wlEventWritable (fileEventWritable events)
+ .|. conditionalMask wlEventHangup (fileEventHangup events)
+ .|. conditionalMask wlEventError (fileEventError events)
+ where conditionalMask mask enabled = if enabled then mask else 0
toFileEventMask :: Word32 -> FileEventMask
toFileEventMask bits = FileEventMask { fileEventReadable = testMask wlEventReadable
diff --git a/wayland/Wayland/Server/Object.hs b/wayland/Wayland/Server/Object.hs
index b13e36f..1fec556 100644
--- a/wayland/Wayland/Server/Object.hs
+++ b/wayland/Wayland/Server/Object.hs
@@ -4,6 +4,7 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE FlexibleInstances #-}
-- |
-- Module : Wayland.Server.Object
@@ -23,6 +24,7 @@ module Wayland.Server.Object
, EventClass
, ArgumentMarshaller(..)
, ArgumentDemarshaller(..)
+ , Marshallable(..)
, -- * State
ObjectCache
, ObjectCacheState
@@ -50,6 +52,7 @@ import Control.Monad.State.Strict (StateT(..), MonadState(..))
import Control.Monad.Cont (ContT(..))
import Control.Monad.Trans (MonadIO(..), MonadTrans(..))
import Control.Monad.Reader (ReaderT (..), MonadReader(..))
+import Control.Monad.Except (MonadError (..), ExceptT, runExceptT)
import Wayland.Server.System
import Wayland.Server.Interface (MessageDesc(..), InterfaceDesc(..), InterfaceCache, newInterfaceCache, destroyInterfaceCache, resolveInterface)
import Wayland.Server.Client (Client, unwrapClient, wrapClient)
@@ -114,32 +117,33 @@ class MessageClass e => EventClass e where
-- For libwayland, the arguments are stored into wl_arguments. It is considered a programmer error to write more
-- arguments than expected by an event or different argument types than the ones specified.
class Monad m => ArgumentMarshaller m where
- marshalInt :: Int -> m ()
- marshalUInt :: Word -> m ()
- marshalFixed :: Float -> m ()
- marshalString :: String -> m ()
- marshalMaybeString :: Maybe String -> m ()
- marshalObject :: Object o => o -> m ()
- marshalMaybeObject :: Object o => Maybe o -> m ()
- marshalNewID :: Object o => NewObject o -> m ()
- marshalArray :: ByteString -> m ()
- marshalFD :: Fd -> m ()
+ marshalInt :: Int32 -> m ()
+ marshalUInt :: Word32 -> m ()
+ marshalFixed :: Float -> m ()
+ marshalString :: Maybe String -> m ()
+ marshalObject :: Maybe AnyObject -> m ()
+ marshalNewID :: Object o => NewObject o -> m ()
+ marshalArray :: ByteString -> m ()
+ marshalFD :: Fd -> m ()
-- | A monad providing an environment for reading a fixed number of arguments of various types.
--
-- For libwayland, the arguments are read from wl_arguments. It is considered a programmer error to read more arguments
-- than expected by a request or different argument types than the ones specified.
-class Monad m => ArgumentDemarshaller m where
- demarshalInt :: m Int
- demarshalUInt :: m Word
- demarshalFixed :: m Float
- demarshalString :: m String
- demarshalMaybeString :: m (Maybe String)
- demarshalObject :: Object o => m o
- demarshalMaybeObject :: Object o => m (Maybe o)
- demarshalNewID :: Object o => m (NewObject o)
- demarshalArray :: m ByteString
- demarshalFD :: m Fd
+class MonadError String m => ArgumentDemarshaller m where
+ demarshalInt :: m Int32
+ demarshalUInt :: m Word32
+ demarshalFixed :: m Float
+ demarshalString :: m (Maybe String)
+ demarshalObject :: m (Maybe AnyObject)
+ demarshalNewID :: Object o => m (NewObject o)
+ demarshalArray :: m ByteString
+ demarshalFD :: m Fd
+
+-- | A type that can be marshalled or demarshalled in the corresponding monad.
+class Marshallable t where
+ marshal :: ArgumentMarshaller m => t -> m ()
+ demarshal :: ArgumentDemarshaller m => m t
-- | An opaque handle to a Wayland object.
--
@@ -187,8 +191,8 @@ newtype ArgumentWriter r a = ArgumentWriter { runArgumentWriter :: StateT Argume
deriving (Functor, Applicative, Monad, MonadState ArgumentIterator, MonadIO)
-- | Monad used for reading arguments from a wl_argument buffer.
-newtype ArgumentReader a = ArgumentReader { runArgumentReader :: StateT ArgumentIterator (ReaderT Client IO) a }
- deriving (Functor, Applicative, Monad, MonadState ArgumentIterator, MonadIO, MonadReader Client)
+newtype ArgumentReader a = ArgumentReader { runArgumentReader :: ExceptT String (StateT ArgumentIterator (ReaderT Client IO)) a }
+ deriving (Functor, Applicative, Monad, MonadState ArgumentIterator, MonadIO, MonadError String, MonadReader Client)
makeLenses ''ObjectCache
@@ -210,6 +214,52 @@ instance Show (NewObject o) where
instance Eq (NewObject o) where
a == b = newObject a == newObject b
+instance Marshallable Int32 where
+ marshal = marshalInt
+ demarshal = demarshalInt
+
+instance Marshallable Word32 where
+ marshal = marshalUInt
+ demarshal = demarshalUInt
+
+instance Marshallable Float where
+ marshal = marshalFixed
+ demarshal = demarshalFixed
+
+instance Marshallable String where
+ marshal = marshalString . Just
+ demarshal = do str <- demarshalString
+ case str of
+ Just s -> pure s
+ Nothing -> throwError "Expected string, got <null>"
+
+instance Marshallable (Maybe String) where
+ marshal = marshalString
+ demarshal = demarshalString
+
+instance Marshallable AnyObject where
+ marshal = marshalObject . Just
+ demarshal = do obj <- demarshalObject
+ case obj of
+ Just o -> pure o
+ Nothing -> throwError "Expected object, got <null>"
+
+instance Marshallable (Maybe AnyObject) where
+ marshal = marshalObject
+ demarshal = demarshalObject
+
+instance Object o => Marshallable (NewObject o) where
+ marshal = marshalNewID
+ demarshal = demarshalNewID
+
+instance Marshallable ByteString where
+ marshal = marshalArray
+ demarshal = demarshalArray
+
+instance Marshallable Fd where
+ marshal = marshalFD
+ demarshal = demarshalFD
+
-- | Create a handle to an object that has yet to be created.
createNewObject :: Client -> Word32 -> IO (NewObject o)
createNewObject client objID = do
@@ -241,13 +291,13 @@ destroyObjectCache c = do
-- | Get the underlying Wayland resource wrapped by a typed object.
--
-- This function may raise an error if the underlying resource is no longer alive.
-unwrapResource :: Object o => o -> IO WLResource
-unwrapResource o = resolveHandle (objHandle (toAny o))
+unwrapResource :: AnyObject -> IO WLResource
+unwrapResource o = resolveHandle (objHandle o)
-- | Get the typed object wrapping a Wayland resource.
--
-- The object wrapper is stored as a StablePtr in the resource's userdata.
-wrapResource :: Object o => WLResource -> IO o
+wrapResource :: WLResource -> IO AnyObject
wrapResource o = do
objPtr <- castPtrToStablePtr <$> wlResourceGetUserData o
deRefStablePtr objPtr
@@ -267,7 +317,8 @@ destroyResource res = do
--
-- This not only creates the object but also attaches a dispatch function to it and updates the destroy callback.
createResource
- :: forall o destroyMsg. Object o
+ :: forall o destroyMsg
+ . Object o
=> MessageClass destroyMsg
=> (Message -> IO ()) -- ^ Message handler used for dispatching object requests and the destroy hook.
-> NewObject o -- ^ Handle describing the object to be created
@@ -284,13 +335,13 @@ createResource handler newObj destroyHook = do
client <- unwrapClient (newClient newObj)
res <- wlResourceCreate client (typeInterface typeInfo) (fromIntegral version) (newID newObj)
handle <- createHandle res
- let obj = fromAny $ AnyObject { objHandle = handle
- , objDestroyHook = handler . Message . destroyHook . fromAny
- }
+ let obj = AnyObject { objHandle = handle
+ , objDestroyHook = handler . Message . destroyHook . fromAny
+ }
objPtr <- newStablePtr obj
wlResourceSetDispatcher res (typeDispatchFunc typeInfo) nullPtr (castStablePtrToPtr objPtr) destroyFunc
- writeIORef (newObject newObj) (Just obj)
- pure obj
+ writeIORef (newObject newObj) (Just (fromAny obj))
+ pure (fromAny obj)
-- | Send an event for a typed object.
--
@@ -298,7 +349,7 @@ createResource handler newObj destroyHook = do
sendResourceEvent :: forall o. Object o => o -> ObjectEvent o -> IO ()
sendResourceEvent o e = do
let paramCount = length $ msgArguments (eventSignature (Proxy :: Proxy o) e)
- res <- unwrapResource o
+ res <- unwrapResource (toAny o)
allocaArray0 paramCount $ \buffer ->
runContT (runStateT (runArgumentWriter (marshalEvent (Proxy :: Proxy o) e)) (ArgumentIterator buffer)) $ \(opcode, _) ->
wlResourcePostEventArray res (fromIntegral opcode) buffer
@@ -306,12 +357,24 @@ sendResourceEvent o e = do
-- | Request handler for a typed object.
--
-- This provides a WLDispatcherFunc for an object type.
-dispatchResourceRequest :: forall o. Object o => Proxy o -> (Message -> IO ()) -> Ptr () -> WLResource -> Word32 -> Ptr WLMessage -> Ptr WLArgument -> IO CInt
+dispatchResourceRequest
+ :: forall o
+ . Object o
+ => Proxy o
+ -> (Message -> IO ())
+ -> Ptr ()
+ -> WLResource
+ -> Word32
+ -> Ptr WLMessage
+ -> Ptr WLArgument
+ -> IO CInt
dispatchResourceRequest _ handler _ res opcode _ args = do
- obj :: o <- wrapResource res
+ obj :: o <- fromAny <$> wrapResource res
client <- wlResourceGetClient res >>= wrapClient
- (req, _) <- runReaderT (runStateT (runArgumentReader (demarshalRequest obj (fromIntegral opcode))) (ArgumentIterator args)) client
- handler $ Message req
+ (req, _) <- runReaderT (runStateT (runExceptT (runArgumentReader (demarshalRequest obj (fromIntegral opcode)))) (ArgumentIterator args)) client
+ case req of
+ Left err -> putStrLn $ "Received invalid request: " ++ err
+ Right r -> handler $ Message r
pure 0
-- | Resolve a type information for an object type.
@@ -360,30 +423,28 @@ liftCont = ArgumentWriter . lift . ContT
instance ArgumentMarshaller (ArgumentWriter r) where
marshalInt v = do
arg <- advanceIterator
- liftIO $ wlArgumentPokeInt arg (fromIntegral v)
+ liftIO $ wlArgumentPokeInt arg v
marshalUInt v = do
arg <- advanceIterator
- liftIO $ wlArgumentPokeUInt arg (fromIntegral v)
+ liftIO $ wlArgumentPokeUInt arg v
marshalFixed v = do
arg <- advanceIterator
liftIO $ wlArgumentPokeFixed arg (WLFixed (round (v * 256)))
marshalFD (Fd v) = do
arg <- advanceIterator
liftIO $ wlArgumentPokeFD arg (fromIntegral v)
- marshalString v = do
+ marshalString (Just v) = do
arg <- advanceIterator
str <- liftCont $ withCString v
liftIO $ wlArgumentPokeString arg str
- marshalMaybeString (Just v) = marshalString v
- marshalMaybeString Nothing = do
+ marshalString Nothing = do
arg <- advanceIterator
liftIO $ wlArgumentPokeString arg nullPtr
- marshalObject v = do
+ marshalObject (Just v) = do
arg <- advanceIterator
obj <- liftIO $ unwrapResource v
liftIO $ wlArgumentPokeObject arg obj
- marshalMaybeObject (Just v) = marshalObject v
- marshalMaybeObject Nothing = do
+ marshalObject Nothing = do
arg <- advanceIterator
liftIO $ wlArgumentPokeObject arg (WLResource nullPtr)
marshalNewID v = do
@@ -399,20 +460,14 @@ instance ArgumentMarshaller (ArgumentWriter r) where
liftIO $ wlArgumentPokeArray arg arr
instance ArgumentDemarshaller ArgumentReader where
- demarshalInt = fromIntegral <$> (advanceIterator >>= liftIO . wlArgumentPeekInt)
- demarshalUInt = fromIntegral <$> (advanceIterator >>= liftIO . wlArgumentPeekUInt)
+ demarshalInt = advanceIterator >>= liftIO . wlArgumentPeekInt
+ demarshalUInt = advanceIterator >>= liftIO . wlArgumentPeekUInt
demarshalFixed = wlFixedToFloat <$> (advanceIterator >>= liftIO . wlArgumentPeekFixed)
demarshalString = do
ptr <- advanceIterator >>= liftIO . wlArgumentPeekString
- liftIO $ peekCString ptr
- demarshalMaybeString = do
- ptr <- advanceIterator >>= liftIO . wlArgumentPeekString
if ptr == nullPtr then pure Nothing else Just <$> liftIO (peekCString ptr)
demarshalObject = do
ptr <- advanceIterator >>= liftIO . wlArgumentPeekObject
- liftIO $ wrapResource ptr
- demarshalMaybeObject = do
- ptr <- advanceIterator >>= liftIO . wlArgumentPeekObject
if ptr == WLResource nullPtr then pure Nothing else Just <$> liftIO (wrapResource ptr)
demarshalNewID = do
objID <- advanceIterator >>= liftIO . wlArgumentPeekNewID