summaryrefslogtreecommitdiff
path: root/wayland/Wayland/Server.hs
diff options
context:
space:
mode:
authorHannes Schulze <haschu0103@outlook.de>2024-08-08 00:43:43 +0200
committerHannes Schulze <haschu0103@outlook.de>2024-08-08 00:43:43 +0200
commit4d51231141d9f35074660e101c478b664b96683d (patch)
treecc6d7014e51f31aa30601db6a57a4d78ca3fdcb3 /wayland/Wayland/Server.hs
parent5b086e236ab3903a717dfac115bebc6e5d006c07 (diff)
wip refactoring
Diffstat (limited to 'wayland/Wayland/Server.hs')
-rw-r--r--wayland/Wayland/Server.hs450
1 files changed, 34 insertions, 416 deletions
diff --git a/wayland/Wayland/Server.hs b/wayland/Wayland/Server.hs
index 30300de..61486b0 100644
--- a/wayland/Wayland/Server.hs
+++ b/wayland/Wayland/Server.hs
@@ -1,426 +1,44 @@
-{-# LANGUAGE ExistentialQuantification #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE UndecidableInstances #-}
-{-# LANGUAGE TemplateHaskell #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE FlexibleContexts #-}
+-- |
+-- Module : Wayland.Server
+-- Description: Low-level API for implementing the Wayland protocol.
module Wayland.Server
- ( Request(..)
- , ObjectID
- , Global
- , ArgumentMarshaller(..)
- , ArgumentDemarshaller(..)
- , DisplayM(..)
- , ResourceClass(..)
- , EventClass
- , RequestClass
- , InterfaceDesc(..)
- , MessageDesc(..)
- , ArgumentDesc(..)
- , Nullability(..)
- , fromRequest
+ ( -- * The Display Monad
+ DisplayM
, withDisplay
, bindSocket
+ , -- * Processing Requests
+ Request(..)
+ , RequestHandler
+ , fromRequest
, processRequests
- , flushEvents
- , sendEvent
- , destroyResource
+ , requestTermination
+ , -- * Managing Objects
+ Global
, registerGlobal
, unregisterGlobal
+ , createObject
+ , destroyObject
+ , sendEvent
+ , flushEvents
+ , -- * Event Sources
+ EventSource
, registerSignalEvent
- , requestTermination
+ , -- * Interface Descriptions
+ InterfaceDesc(..)
+ , MessageDesc(..)
+ , ArgumentDesc(..)
+ , Nullability(..)
+ , -- * Object Types
+ Object(..)
+ , AnyObject
+ , NewObject
+ , RequestClass
+ , EventClass
+ , ArgumentMarshaller(..)
+ , ArgumentDemarshaller(..)
) where
-import Data.Typeable (Typeable, Proxy(..), cast, TypeRep, typeRep)
-import Data.Kind (Type)
-import Control.Monad.State.Strict (StateT, MonadState(..), MonadIO(..), runStateT)
-import Lens.Micro.Mtl.Internal (Zoom(..), Zoomed)
-import Lens.Micro.TH (makeLenses)
-import Lens.Micro.Mtl ((%=), use, (<<%=))
-import qualified Wayland.Server.System as Sys
-import Control.Monad.Trans (MonadTrans(..))
-import Foreign.C (peekCString, newCString, withCString)
-import Foreign.C.Types (CInt)
-import Foreign (Ptr, nullPtr, new, newStablePtr, castStablePtrToPtr, castPtrToStablePtr, deRefStablePtr, freeStablePtr, Storable(..), Int32, castPtr, withArray, with, allocaArray0, FunPtr, plusPtr)
-import Data.IORef (IORef, newIORef, writeIORef, readIORef)
-import Control.Monad.Reader (ReaderT, MonadReader(..), runReaderT)
-import Control.Exception (bracket)
-import Data.Functor (($>))
-import Control.Monad (when)
-import Data.Maybe (isJust)
-import qualified Data.Map as M
-import Foreign.Marshal (newArray)
-import Data.Word (Word32)
-import System.Mem.StableName (StableName, makeStableName)
-import Data.Hashable (Hashable(..))
-import Control.Monad.Cont (ContT(..))
-
-data EventSource = EventSource (FunPtr Sys.WLEventLoopSignalFunc) (IORef Sys.WLEventSource)
- deriving (Eq)
-
-data Global = Global (FunPtr Sys.WLGlobalBindFunc) (IORef Sys.WLGlobal)
- deriving (Eq)
-
--- IORef set to nullPtr once destroyed.
-data ObjectID = ObjectID (StableName (IORef Sys.WLResource)) (IORef Sys.WLResource)
- deriving (Eq)
-
-instance Ord ObjectID where
- (ObjectID nameA _) <= (ObjectID nameB _) = hash nameA <= hash nameB
-
-instance Hashable ObjectID where
- hashWithSalt s (ObjectID name _) = hashWithSalt s name
-
-instance Show ObjectID where
- show (ObjectID name _) = "ObjectID " ++ show (hash name)
-
-data Nullability
- = AllowNull
- | NotNull
- deriving (Show, Eq, Ord)
-
-data ArgumentDesc
- = ArgumentInt
- | ArgumentUInt
- | ArgumentFixed
- | ArgumentString !Nullability
- | ArgumentObject !Nullability !InterfaceDesc
- | ArgumentNewID !InterfaceDesc
- | ArgumentArray
- | ArgumentFD
- deriving (Show, Eq, Ord)
-
-data MessageDesc
- = MessageDesc { msgName :: !String
- , msgSignature :: ![ArgumentDesc]
- , msgSince :: !Int
- , msgIsDestructor :: !Bool
- }
- deriving (Show, Eq, Ord)
-
-data InterfaceDesc
- = InterfaceDesc { ifName :: !String
- , ifVersion :: !Int
- , ifRequests :: ![MessageDesc]
- , ifEvents :: ![MessageDesc]
- }
- deriving (Show, Eq, Ord)
-
-class (Typeable r, Show r) => RequestClass r where
-
-class (Typeable e, Show e) => EventClass e where
-
-data Request = forall a. RequestClass a => Request a
-
-newtype DisplayM s a = DisplayM { runDisplayM :: StateT s (StateT DisplayState (ReaderT Display IO)) a }
- deriving (Functor, Applicative, Monad, MonadIO, MonadState s, MonadFail)
-
-type RequestHandler s = Request -> DisplayM s ()
-
-newtype EventLoop = EventLoop { handleEvent :: Request -> IO () }
-
-data Display
- = Display { display :: !Sys.WLDisplay
- , activeEventLoop :: !(IORef (Maybe EventLoop))
- }
-
-data DisplayState
- = DisplayState { _typeInterfaces :: !(M.Map TypeRep (Ptr Sys.WLInterface))
- , _nativeInterfaces :: !(M.Map InterfaceDesc (Ptr Sys.WLInterface))
- }
-
-makeLenses ''DisplayState
-
-class (Typeable r, RequestClass (ResourceRequest r), EventClass (ResourceEvent r)) => ResourceClass r where
- type ResourceRequest r :: Type
- type ResourceEvent r :: Type
- objectID :: r -> ObjectID
- fromID :: ObjectID -> r
- interface :: Proxy r -> InterfaceDesc
- marshalEvent :: ArgumentMarshaller m => Proxy r -> ResourceEvent r -> m Int
- demarshalRequest :: ArgumentDemarshaller m => r -> Int -> m (ResourceRequest r)
- eventSignature :: Proxy r -> ResourceEvent r -> MessageDesc
- requestSignature :: Proxy r -> ResourceRequest r -> MessageDesc
-
-class (Monad m, MonadFail m) => ArgumentMarshaller m where
- marshalInt :: Int -> m ()
- marshalUInt :: Word -> m ()
- marshalFixed :: Float -> m ()
- marshalString :: String -> m ()
- marshalObject :: ResourceClass r => r -> m ()
- marshalNewID :: ResourceClass r => r -> m ()
- marshalArray :: Storable a => [a] -> m ()
- marshalFD :: Int32 -> m ()
-
-class (Monad m, MonadFail m) => ArgumentDemarshaller m where
- demarshalInt :: m Int
- demarshalUInt :: m Word
- demarshalFixed :: m Float
- demarshalString :: m String
- demarshalObject :: ResourceClass r => m r
- demarshalNewID :: ResourceClass r => m r
- demarshalArray :: Storable a => m [a]
- demarshalFD :: m Int32
-
-instance Show Request where
- show (Request r) = show r
-
-type instance Zoomed (DisplayM s) = Zoomed (StateT s (StateT DisplayState (ReaderT Display IO)))
-
-instance Zoom (DisplayM s) (DisplayM t) s t where
- zoom l (DisplayM m) = DisplayM (zoom l m)
-
-getDisplay :: DisplayM s Display
-getDisplay = DisplayM $ lift $ lift ask
-
-liftDisplayState :: StateT DisplayState (ReaderT Display IO) a -> DisplayM s a
-liftDisplayState m = DisplayM $ lift m
-
-fromRequest :: Typeable a => Request -> Maybe a
-fromRequest (Request x) = cast x
-
-withDisplay :: s -> DisplayM s a -> IO (a, s)
-withDisplay initialState f = bracket Sys.wlDisplayCreate Sys.wlDisplayDestroy $ \sysDisp -> do
- eventLoop <- newIORef Nothing
- let disp = Display { display = sysDisp
- , activeEventLoop = eventLoop
- }
- dispState = DisplayState { _typeInterfaces = M.empty
- , _nativeInterfaces = M.empty
- }
- (res, _) <- runReaderT (runStateT (runStateT (runDisplayM f) initialState) dispState) disp
- pure res
-
-bindSocket :: DisplayM s String
-bindSocket = do
- disp <- getDisplay
- name <- liftIO $ Sys.wlDisplayAddSocketAuto (display disp)
- liftIO $ peekCString name
-
-processRequests :: RequestHandler s -> DisplayM s ()
-processRequests h = do
- disp <- getDisplay
- stateBefore <- get
- displayStateBefore <- liftDisplayState get
- stateRef <- liftIO $ newIORef (stateBefore, displayStateBefore)
- let handler :: Request -> IO ()
- handler r = do
- (s, ds) <- readIORef stateRef
- ((_, s'), ds') <- runReaderT (runStateT (runStateT (runDisplayM (h r)) s) ds) disp
- writeIORef stateRef (s', ds')
- loop <- liftIO $ readIORef (activeEventLoop disp)
- when (isJust loop) (fail "Cannot start an event loop from within an event handler.")
- liftIO $ writeIORef (activeEventLoop disp) (Just (EventLoop handler))
- liftIO $ Sys.wlDisplayRun (display disp)
- (stateAfter, displayStateAfter) <- liftIO $ readIORef stateRef
- put stateAfter
- liftDisplayState $ put displayStateAfter
-
-registerGlobal :: forall r bindReq unbindReq s. ResourceClass r => RequestClass bindReq => RequestClass unbindReq => (r -> bindReq) -> (r -> unbindReq) -> Int -> DisplayM s Global
-registerGlobal mkBindReq mkUnbindReq version = do
- disp <- getDisplay
- iface <- getInterface (Proxy :: Proxy r)
- let dispatcher :: Ptr () -> Sys.WLResource -> Word32 -> Ptr Sys.WLMessage -> Ptr Sys.WLArgument -> IO CInt
- dispatcher _ res opcode _ _ = do
- handlePtr <- castPtrToStablePtr <$> Sys.wlResourceGetUserData res
- objID@(ObjectID _ _) <- deRefStablePtr handlePtr
- putStrLn $ "Handling request " ++ show opcode ++ " for " ++ show objID
- -- TODO: Handle request
- pure 0
- let destroy :: Sys.WLResource -> IO ()
- destroy res = do
- handlePtr <- castPtrToStablePtr <$> Sys.wlResourceGetUserData res
- objID@(ObjectID _ handle) <- deRefStablePtr handlePtr
- writeIORef handle (Sys.WLResource nullPtr)
- freeStablePtr handlePtr
- let req = mkUnbindReq (fromID objID)
- runRequest disp (Request req)
- pure ()
- let createObject :: Sys.WLClient -> Ptr () -> Word32 -> Word32 -> IO ()
- createObject client _ ver protID = do
- res <- Sys.wlResourceCreate client iface (fromIntegral ver) protID
- handle <- newIORef res
- name <- makeStableName handle
- let objID = ObjectID name handle
- handlePtr <- newStablePtr objID
- dis <- Sys.wrapWLDispatcherFunc dispatcher
- des <- Sys.wrapWLResourceDestroyFunc destroy
- -- TODO: Free dis and des
- Sys.wlResourceSetDispatcher res dis nullPtr (castStablePtrToPtr handlePtr) des
- let req = mkBindReq (fromID objID)
- runRequest disp (Request req)
- pure ()
- liftIO $ do
- func <- Sys.wrapWLGlobalBindFunc createObject
- global <- Sys.wlGlobalCreate (display disp) iface (fromIntegral version) nullPtr func
- globalRef <- newIORef global
- -- TODO: Free func
- pure $ Global func globalRef
-
-unregisterGlobal :: Global -> DisplayM s ()
-unregisterGlobal _ = pure ()
-
-runRequest :: Display -> Request -> IO ()
-runRequest disp r = do
- loop <- readIORef (activeEventLoop disp)
- case loop of
- Just handler -> handleEvent handler r
- Nothing -> putStrLn $ "Lost request because no event loop is active: " ++ show r
-
-registerSignalEvent :: RequestClass req => req -> CInt -> DisplayM s EventSource
-registerSignalEvent r sig = do
- disp <- getDisplay
- liftIO $ do
- loop <- Sys.wlDisplayGetEventLoop (display disp)
- func <- Sys.wrapWLEventLoopSignalFunc $ \_ _ -> runRequest disp (Request r) $> 0
- src <- Sys.wlEventLoopAddSignal loop sig func nullPtr
- srcRef <- newIORef src
- -- TODO: Free func
- pure $ EventSource func srcRef
-
-requestTermination :: DisplayM s ()
-requestTermination = do
- disp <- getDisplay
- liftIO $ Sys.wlDisplayTerminate (display disp)
-
-wrapInterface :: InterfaceDesc -> DisplayM s (Ptr Sys.WLInterface)
-wrapInterface i = do
- let mkArgumentType :: ArgumentDesc -> DisplayM s (Ptr Sys.WLInterface)
- mkArgumentType (ArgumentObject _ a) = wrapInterface a
- mkArgumentType (ArgumentNewID a) = wrapInterface a
- mkArgumentType _ = pure nullPtr
- let mkArgument :: ArgumentDesc -> String
- mkArgument ArgumentInt = "i"
- mkArgument ArgumentUInt = "u"
- mkArgument ArgumentFixed = "f"
- mkArgument (ArgumentString NotNull) = "s"
- mkArgument (ArgumentString AllowNull) = "?s"
- mkArgument (ArgumentObject NotNull _) = "o"
- mkArgument (ArgumentObject AllowNull _) = "?o"
- mkArgument (ArgumentNewID _) = "n"
- mkArgument ArgumentArray = "a"
- mkArgument ArgumentFD = "h"
- let mkMessage :: MessageDesc -> DisplayM s Sys.WLMessage
- mkMessage m = do
- name <- liftIO $ newCString (msgName m)
- signature <- liftIO $ newCString (show (msgSince m) ++ (msgSignature m >>= mkArgument))
- types <- mapM mkArgumentType (msgSignature m) >>= liftIO . newArray
- pure $ Sys.WLMessage { Sys.msgName = name
- , Sys.msgSignature = signature
- , Sys.msgTypes = types
- }
- let mkInterface :: DisplayM s (Ptr Sys.WLInterface)
- mkInterface = do
- name <- liftIO $ newCString (ifName i)
- methods <- mapM mkMessage (ifRequests i) >>= liftIO . newArray
- events <- mapM mkMessage (ifEvents i) >>= liftIO . newArray
- liftIO $ new $ Sys.WLInterface { Sys.ifName = name
- , Sys.ifVersion = fromIntegral $ ifVersion i
- , Sys.ifMethodCount = fromIntegral $ length (ifRequests i)
- , Sys.ifMethods = methods
- , Sys.ifEventCount = fromIntegral $ length (ifEvents i)
- , Sys.ifEvents = events
- }
- existing <- liftDisplayState $ use nativeInterfaces
- case M.lookup i existing of
- Just ptr -> pure ptr
- Nothing -> do ptr <- mkInterface
- -- TODO: Free ptr
- liftDisplayState $ nativeInterfaces %= M.insert i ptr
- pure ptr
-
-getInterface :: ResourceClass r => Proxy r -> DisplayM s (Ptr Sys.WLInterface)
-getInterface p = do
- existing <- liftDisplayState $ use typeInterfaces
- case M.lookup (typeRep p) existing of
- Just ptr -> pure ptr
- Nothing -> do ptr <- wrapInterface (interface p)
- liftDisplayState $ typeInterfaces %= M.insert (typeRep p) ptr
- pure ptr
-
-data ArgumentIteratorState
- = ArgumentIteratorState { _argNext :: !(Ptr Sys.WLArgument)
- , _argRemaining :: !Int
- }
- deriving (Show)
-
-makeLenses ''ArgumentIteratorState
-
-newtype ArgumentIterator r a = ArgumentIterator { runArgumentIterator :: StateT ArgumentIteratorState (ContT r IO) a }
- deriving (Functor, Applicative, Monad, MonadFail, MonadState ArgumentIteratorState, MonadIO)
-
-advanceIterator :: ArgumentIterator r (Ptr Sys.WLArgument)
-advanceIterator = do
- next <- argNext <<%= \p -> plusPtr p (sizeOf (undefined :: Sys.WLArgument))
- remaining <- argRemaining <<%= (+(-1))
- when (remaining == 0) (fail "Out of range for argument")
- pure next
-
-getObjectForHandle :: ResourceClass r => r -> IO Sys.WLResource
-getObjectForHandle r = fetch (objectID r)
- where fetch (ObjectID _ handle) = readIORef handle >>= check
- check res@(Sys.WLResource ptr) | ptr == nullPtr = fail "Invalid object handle."
- | otherwise = pure res
-
-liftCont :: ((a -> IO r) -> IO r) -> ArgumentIterator r a
-liftCont = ArgumentIterator . lift . ContT
-
-instance ArgumentMarshaller (ArgumentIterator r) where
- marshalInt v = do
- arg <- advanceIterator
- liftIO $ Sys.wlArgumentPokeInt arg (fromIntegral v)
- marshalUInt v = do
- arg <- advanceIterator
- liftIO $ Sys.wlArgumentPokeUInt arg (fromIntegral v)
- marshalFixed v = do
- arg <- advanceIterator
- liftIO $ Sys.wlArgumentPokeFixed arg (Sys.WLFixed (round (v * 256)))
- marshalFD v = do
- arg <- advanceIterator
- liftIO $ Sys.wlArgumentPokeFD arg (fromIntegral v)
- marshalString v = do
- arg <- advanceIterator
- str <- liftCont $ withCString v
- liftIO $ Sys.wlArgumentPokeString arg str
- marshalObject v = do
- arg <- advanceIterator
- obj <- liftIO $ getObjectForHandle v
- liftIO $ Sys.wlArgumentPokeObject arg obj
- marshalNewID v = do
- arg <- advanceIterator
- obj <- liftIO $ getObjectForHandle v
- liftIO $ Sys.wlArgumentPokeObject arg obj
- marshalArray v = do
- arg <- advanceIterator
- let len = fromIntegral $ length v * sizeOf (head v)
- buf <- liftCont $ withArray v
- arr <- liftCont $ with $ Sys.WLArray { Sys.arrSize = len
- , Sys.arrAlloc = len
- , Sys.arrData = castPtr buf
- }
- liftIO $ Sys.wlArgumentPokeArray arg arr
-
-sendEvent :: forall r s. ResourceClass r => r -> ResourceEvent r -> DisplayM s ()
-sendEvent res e = liftIO $ do
- let params = msgSignature (eventSignature (Proxy :: Proxy r) e)
- ptr <- getObjectForHandle res
- allocaArray0 (length params) $ \buffer ->
- let s = ArgumentIteratorState { _argNext = buffer
- , _argRemaining = length params
- }
- handler (opcode, _) = Sys.wlResourcePostEventArray ptr (fromIntegral opcode) buffer
- in runContT (runStateT (runArgumentIterator (marshalEvent (Proxy :: Proxy r) e)) s) handler
-
-flushEvents :: DisplayM s ()
-flushEvents = do
- disp <- getDisplay
- liftIO $ Sys.wlDisplayFlushClients (display disp)
-
-destroyResource :: ResourceClass r => r -> DisplayM s ()
-destroyResource r = liftIO $ do
- ptr <- getObjectForHandle r
- Sys.wlResourceDestroy ptr
+import Wayland.Server.Interface
+import Wayland.Server.Object
+import Wayland.Server.Display