From 8456cd1c8de9a204e329a2ddd79a017790a61ae4 Mon Sep 17 00:00:00 2001 From: Jens Nolte <git@queezle.net> Date: Wed, 17 Aug 2022 00:30:18 +0200 Subject: [PATCH] Add wl_shm server implementation on top of new buffer api --- examples/Client.hs | 16 +++-- examples/Server.hs | 9 ++- quasar-wayland.cabal | 2 + src/Quasar/Wayland/Client/Buffer.hs | 2 +- src/Quasar/Wayland/Protocol.hs | 1 + src/Quasar/Wayland/Protocol/Core.hs | 5 +- src/Quasar/Wayland/Server/Shm.hs | 46 ++++++++++++++ src/Quasar/Wayland/Shm.hs | 98 +++++++++++++++++++++++++++++ 8 files changed, 169 insertions(+), 10 deletions(-) create mode 100644 src/Quasar/Wayland/Server/Shm.hs create mode 100644 src/Quasar/Wayland/Shm.hs diff --git a/examples/Client.hs b/examples/Client.hs index 2bdb1d1..a809077 100644 --- a/examples/Client.hs +++ b/examples/Client.hs @@ -126,6 +126,7 @@ main = do } wlrLayerSurface.set_size 512 512 + -- Commit role wlSurface.commit -- Should await first `configure` event @@ -137,12 +138,17 @@ main = do wlSurface.attach (Just buffer) 0 0 wlSurface.commit - -- buffer2 <- liftIO $ toImageBuffer shm (wallpaperImage wallpaper) + buffer2 <- liftIO $ toImageBuffer shm (mkImage solidColor) - --liftIO $ atomically do - -- wlSurface.attach (Nothing) 0 0 - -- --wlSurface.damage 0 0 100 100 - -- wlSurface.commit + liftIO $ atomically do + wlSurface.attach (Just buffer2) 0 0 + wlSurface.damage 0 0 42 42 + wlSurface.commit + + liftIO $ atomically do + wlSurface.attach (Nothing) 0 0 + wlSurface.damage 100 100 42 42 + wlSurface.commit traceIO . ("shm buffer formats: " <>) . mconcat . intersperse ", " . fmap show . toList =<< await shm.formats diff --git a/examples/Server.hs b/examples/Server.hs index 4044cf9..2b6bfb4 100644 --- a/examples/Server.hs +++ b/examples/Server.hs @@ -2,16 +2,16 @@ module Main (main) where import Quasar import Quasar.Prelude -import Quasar.Wayland.Surface import Quasar.Wayland.Server import Quasar.Wayland.Server.Registry +import Quasar.Wayland.Server.Shm +import Quasar.Wayland.Shm import Quasar.Wayland.Protocol import Quasar.Wayland.Protocol.Generated main :: IO () main = runQuasarAndExit (stderrLogger LogLevelWarning) do let - shmGlobal = createGlobal @Interface_wl_shm maxVersion (\_ -> traceM "wl_shm not implemented") layerShellGlobal = createGlobal @Interface_zwlr_layer_shell_v1 maxVersion (\x -> setRequestHandler x layerShellHandler) registry <- newRegistry [compositorGlobal @ShmBufferBackend, shmGlobal, layerShellGlobal] server <- newWaylandServer registry @@ -21,7 +21,10 @@ main = runQuasarAndExit (stderrLogger LogLevelWarning) do layerShellHandler :: RequestHandler_zwlr_layer_shell_v1 layerShellHandler = RequestHandler_zwlr_layer_shell_v1 { - get_layer_surface = \wlLayerSurface _ _ _ _ -> setRequestHandler wlLayerSurface layerSurfaceHandler, + get_layer_surface = \wlLayerSurface _ _ _ _ -> do + setRequestHandler wlLayerSurface layerSurfaceHandler + -- Just send a "correct" configure event for the demo client to get things rolling + wlLayerSurface.configure 0 512 512, destroy = pure () } diff --git a/quasar-wayland.cabal b/quasar-wayland.cabal index cf63fae..e57de7a 100644 --- a/quasar-wayland.cabal +++ b/quasar-wayland.cabal @@ -87,8 +87,10 @@ library Quasar.Wayland.Region Quasar.Wayland.Server Quasar.Wayland.Server.Registry + Quasar.Wayland.Server.Shm Quasar.Wayland.Server.Socket Quasar.Wayland.Server.Surface + Quasar.Wayland.Shm Quasar.Wayland.Surface other-modules: Quasar.Wayland.Protocol.Core diff --git a/src/Quasar/Wayland/Client/Buffer.hs b/src/Quasar/Wayland/Client/Buffer.hs index 3041ea3..ae3c9f3 100644 --- a/src/Quasar/Wayland/Client/Buffer.hs +++ b/src/Quasar/Wayland/Client/Buffer.hs @@ -71,7 +71,7 @@ newShmBuffer shm width height = do wlBuffer <- wlShmPool.create_buffer offset width height stride pixelFormat setEventHandler wlBuffer EventHandler_wl_buffer { -- TODO - release = pure () + release = wlBuffer.destroy } pure wlBuffer diff --git a/src/Quasar/Wayland/Protocol.hs b/src/Quasar/Wayland/Protocol.hs index 11a880b..644010d 100644 --- a/src/Quasar/Wayland/Protocol.hs +++ b/src/Quasar/Wayland/Protocol.hs @@ -11,6 +11,7 @@ module Quasar.Wayland.Protocol ( getMessageHandler, setInterfaceData, getInterfaceData, + isDestroyed, -- ** Wayland types WlFixed(..), diff --git a/src/Quasar/Wayland/Protocol/Core.hs b/src/Quasar/Wayland/Protocol/Core.hs index 6332cc5..d97afa7 100644 --- a/src/Quasar/Wayland/Protocol/Core.hs +++ b/src/Quasar/Wayland/Protocol/Core.hs @@ -26,6 +26,7 @@ module Quasar.Wayland.Protocol.Core ( getMessageHandler, setInterfaceData, getInterfaceData, + isDestroyed, NewObject, IsObject, IsMessage(..), @@ -325,7 +326,6 @@ data Object s i = IsInterfaceSide s i => Object { destroyed :: TVar Bool } - getMessageHandler :: IsInterfaceSide s i => Object s i -> STM (MessageHandler s i) getMessageHandler object = maybe (throwM (InternalError ("No message handler attached to " <> showObject object))) pure =<< readTVar object.messageHandler @@ -346,6 +346,9 @@ setInterfaceData object value = writeTVar object.interfaceData (toDyn value) getInterfaceData :: Typeable a => Object s i -> STM (Maybe a) getInterfaceData object = fromDynamic <$> readTVar object.interfaceData +isDestroyed :: Object s i -> STM Bool +isDestroyed object = readTVar object.destroyed + -- | Type alias to indicate an object is created with a message. type NewObject s i = Object s i diff --git a/src/Quasar/Wayland/Server/Shm.hs b/src/Quasar/Wayland/Server/Shm.hs new file mode 100644 index 0000000..504c346 --- /dev/null +++ b/src/Quasar/Wayland/Server/Shm.hs @@ -0,0 +1,46 @@ +module Quasar.Wayland.Server.Shm ( + shmGlobal, +) where + +import Quasar.Prelude +import Quasar.Wayland.Protocol +import Quasar.Wayland.Protocol.Generated +import Quasar.Wayland.Server.Registry +import Quasar.Wayland.Shm +import Quasar.Wayland.Server.Surface +import System.Posix (Fd) + + +shmGlobal :: Global +shmGlobal = createGlobal @Interface_wl_shm maxVersion initializeWlShm + +shmRequestHandler :: RequestHandler_wl_shm +shmRequestHandler = RequestHandler_wl_shm { + create_pool = initializeWlShmPool +} + +initializeWlShm :: NewObject 'Server Interface_wl_shm -> STM () +initializeWlShm wlShm = do + setRequestHandler wlShm shmRequestHandler + -- argb8888 (0) and xrgb8888 (1) are required by the spec + -- TODO add more formats later (i.e. 10bit formats are missing right now) + wlShm.format 0 + wlShm.format 1 + +initializeWlShmPool :: NewObject 'Server Interface_wl_shm_pool -> Fd -> Int32 -> STM () +initializeWlShmPool wlShmPool fd size = do + pool <- newShmPool fd size + setRequestHandler wlShmPool RequestHandler_wl_shm_pool { + create_buffer = initializeWlShmBuffer pool, + destroy = destroyShmPool pool, + resize = resizeShmPool pool + } + +initializeWlShmBuffer :: ShmPool -> NewObject 'Server Interface_wl_buffer -> Int32 -> Int32 -> Int32 -> Int32 -> Word32 -> STM () +initializeWlShmBuffer pool wlBuffer offset width height stride format = do + shmBuffer <- newShmBuffer pool offset width height stride format releaseFn + initializeWlBuffer @ShmBufferBackend wlBuffer shmBuffer + where + releaseFn :: STM () + -- TODO handle other exceptions (e.g. disconnected) + releaseFn = unlessM (isDestroyed wlBuffer) wlBuffer.release diff --git a/src/Quasar/Wayland/Shm.hs b/src/Quasar/Wayland/Shm.hs new file mode 100644 index 0000000..f45bef1 --- /dev/null +++ b/src/Quasar/Wayland/Shm.hs @@ -0,0 +1,98 @@ +module Quasar.Wayland.Shm ( + ShmBufferBackend, + ShmPool, + newShmPool, + resizeShmPool, + destroyShmPool, + newShmBuffer, +) where + +import Control.Monad.Catch +import Quasar.Prelude +import Quasar.Wayland.Protocol +import Quasar.Wayland.Surface +import System.Posix (Fd) + +data ShmBufferBackend + +instance BufferBackend ShmBufferBackend where + type BufferContent ShmBufferBackend = ShmBuffer + releaseBuffer buffer = buffer.releaseFn + releaseBufferStorage buffer = do + modifyTVar buffer.pool.bufferCount pred + traceM "Finalized ShmBuffer" + tryFinalizeShmPool buffer.pool + + +-- | Wrapper for an externally managed shm pool +data ShmPool = ShmPool { + fd :: TVar (Maybe Fd), + size :: TVar Int32, + bufferCount :: TVar Word32, + destroyed :: TVar Bool, + downstreams :: TVar [DownstreamShmPool] +} + +data ShmBuffer = ShmBuffer { + pool :: ShmPool, + offset :: Int32, + width :: Int32, + height :: Int32, + stride :: Int32, + format :: Word32, + releaseFn :: STM () +} + +-- | Create an `ShmPool` for externally managed memory. Takes ownership of the passed file descriptor. +newShmPool :: Fd -> Int32 -> STM ShmPool +newShmPool fd size = do + fdVar <- newTVar (Just fd) + sizeVar <- newTVar size + bufferCount <- newTVar 0 + destroyed <- newTVar False + downstreams <- newTVar mempty + pure ShmPool { + fd = fdVar, + size = sizeVar, + bufferCount, + destroyed, + downstreams + } + +-- | Resize an externally managed shm pool. +resizeShmPool :: ShmPool -> Int32 -> STM () +resizeShmPool pool size = do + oldSize <- readTVar pool.size + when (oldSize > size) $ throwM $ ProtocolUsageError (mconcat ["wl_shm: Invalid resize from ", show oldSize, " to ", show size]) + writeTVar pool.size size + +-- | Destroy an externally managed shm pool. Memory shared to this pool will be deallocated after the last buffer is released. +destroyShmPool :: ShmPool -> STM () +destroyShmPool pool = do + alreadyDestroyed <- swapTVar pool.destroyed True + unless alreadyDestroyed do + tryFinalizeShmPool pool + +tryFinalizeShmPool :: ShmPool -> STM () +tryFinalizeShmPool pool = do + destroyed <- readTVar pool.destroyed + bufferCount <- readTVar pool.bufferCount + when (destroyed && bufferCount == 0) do + fd <- swapTVar pool.fd Nothing + traceM "Finalized ShmPool" + -- TODO close fd + traceM $ "leaking fd " <> show fd <> " (closing fd is not implemented yet)" + + +-- | Create a new buffer for an externally managed pool +newShmBuffer :: ShmPool -> Int32 -> Int32 -> Int32 -> Int32 -> Word32 -> STM () -> STM (Buffer ShmBufferBackend) +newShmBuffer pool offset width height stride format releaseFn = do + -- TODO check arguments + modifyTVar pool.bufferCount succ + let shmBuffer = ShmBuffer pool offset width height stride format releaseFn + newBuffer @ShmBufferBackend shmBuffer + +data DownstreamShmPool = DownstreamShmPool + +connectDownstreamShmPool :: ShmPool -> DownstreamShmPool -> STM () +connectDownstreamShmPool pool downstream = undefined -- GitLab