From 6948fd25a82a1bb671bc26303eb589bfd95c7680 Mon Sep 17 00:00:00 2001
From: Jens Nolte <git@queezle.net>
Date: Sun, 24 Jul 2022 20:43:52 +0200
Subject: [PATCH] Handle destructor messages

---
 src/Quasar/Wayland/Protocol/Core.hs | 25 +++++++++-------
 src/Quasar/Wayland/Protocol/TH.hs   | 44 +++++++++++++++++++++--------
 2 files changed, 47 insertions(+), 22 deletions(-)

diff --git a/src/Quasar/Wayland/Protocol/Core.hs b/src/Quasar/Wayland/Protocol/Core.hs
index 21edd76..5d8f742 100644
--- a/src/Quasar/Wayland/Protocol/Core.hs
+++ b/src/Quasar/Wayland/Protocol/Core.hs
@@ -17,7 +17,6 @@ module Quasar.Wayland.Protocol.Core (
   Version,
   interfaceVersion,
   IsInterfaceSide(..),
-  IsInterfaceHandler(..),
   Object(objectProtocol),
   setEventHandler,
   setRequestHandler,
@@ -41,6 +40,7 @@ module Quasar.Wayland.Protocol.Core (
   -- * Low-level protocol interaction
   objectWireArgument,
   nullableObjectWireArgument,
+  handleDestructor,
   checkObject,
   sendMessage,
   newObject,
@@ -260,7 +260,7 @@ class (
     IsMessage (WireDown s i)
   )
   => IsInterfaceSide (s :: Side) i where
-  handleMessage :: MessageHandler s i -> WireDown s i -> ProtocolM s ()
+  handleMessage :: Object s i -> MessageHandler s i -> WireDown s i -> ProtocolM s ()
 
 
 getWireDown :: forall s i. IsInterfaceSide s i => Object s i -> Opcode -> Get (ProtocolM s (WireDown s i))
@@ -270,10 +270,6 @@ putWireUp :: forall s i. IsInterfaceSide s i => Object s i -> WireUp s i -> Eith
 putWireUp _ = putMessage @(WireUp s i)
 
 
-class IsInterfaceSide s i => IsInterfaceHandler s i a where
-  handlerHandleMessage :: a -> Object s i -> WireDown s i -> ProtocolM s ()
-
-
 -- | Data kind
 data Side = Client | Server
   deriving stock (Eq, Show)
@@ -283,7 +279,8 @@ data Side = Client | Server
 data Object s i = IsInterfaceSide s i => Object {
   objectProtocol :: (ProtocolHandle s),
   objectId :: ObjectId (InterfaceName i),
-  messageHandler :: TVar (Maybe (MessageHandler s i))
+  messageHandler :: TVar (Maybe (MessageHandler s i)),
+  destroyed :: TVar Bool
 }
 
 
@@ -488,7 +485,8 @@ initializeProtocol wlDisplayMessageHandler initializationAction = do
   writeTVar stateVar (Right state)
 
   messageHandlerVar <- newTVar (Just (wlDisplayMessageHandler protocol))
-  let wlDisplay = Object protocol wlDisplayId messageHandlerVar
+  destroyed <- newTVar False
+  let wlDisplay = Object protocol wlDisplayId messageHandlerVar destroyed
   modifyTVar' objectsVar (HM.insert (toGenericObjectId wlDisplayId) (SomeObject wlDisplay))
 
   result <- initializationAction wlDisplay
@@ -591,8 +589,9 @@ newObjectFromId
 newObjectFromId messageHandler (NewId oId) = do
   protocol <- askProtocol
   messageHandlerVar <- lift $ newTVar messageHandler
+  destroyed <- lift $ newTVar False
   let
-    object = Object protocol oId messageHandlerVar
+    object = Object protocol oId messageHandlerVar destroyed
     someObject = SomeObject object
   modifyProtocolVar (.objectsVar) (HM.insert (genericObjectId object) someObject)
   pure object
@@ -665,9 +664,15 @@ handleWlDisplayError _protocol oId code message = throwM $ ServerError code (toS
 -- to be called from the client module.
 handleWlDisplayDeleteId :: ProtocolHandle 'Client -> Word32 -> STM ()
 handleWlDisplayDeleteId protocol oId = runProtocolM protocol do
+  -- TODO call destructor
   modifyProtocolVar (.objectsVar) $ HM.delete (GenericObjectId oId)
 
 
+handleDestructor :: IsInterfaceSide s i => Object s i -> ProtocolM s ()
+handleDestructor object = do
+  traceM $ "Handling destructor for " <> showObject object
+  lift $ writeTVar object.destroyed True
+
 
 checkObject :: IsInterface i => Object s i -> ProtocolM s (Either String ())
 checkObject object = do
@@ -748,7 +753,7 @@ handleRawMessage (oId, opcode, body) = do
         message <- verifyMessage
         traceM $ "<- " <> showObjectMessage object message
         messageHandler <- lift $ getMessageHandler object
-        handleMessage @s @i messageHandler message
+        handleMessage @s @i object messageHandler message
 
 type RawMessage = (GenericObjectId, Opcode, BSL.ByteString)
 
diff --git a/src/Quasar/Wayland/Protocol/TH.hs b/src/Quasar/Wayland/Protocol/TH.hs
index 9a53e0e..489df04 100644
--- a/src/Quasar/Wayland/Protocol/TH.hs
+++ b/src/Quasar/Wayland/Protocol/TH.hs
@@ -15,7 +15,7 @@ import Language.Haskell.TH
 import Language.Haskell.TH.Syntax (addDependentFile)
 import Quasar.Prelude
 import Quasar.Wayland.Protocol.Core
-import System.Posix.Types (Fd(Fd))
+import System.Posix.Types (Fd)
 import Text.Read (readEither)
 import Text.XML.Light
 
@@ -150,27 +150,27 @@ interfaceDecs interface = do
       tySynInstD (tySynEqn Nothing (appT (conT ''InterfaceName) iT) (litT (strTyLit interface.name))),
       tySynInstD (tySynEqn Nothing (appT (conT ''InterfaceVersion) iT) (litT (numTyLit interface.version)))
       ]
-    -- | IsInterfaceSide instance
+    -- IsInterfaceSide instance
     tellQs interfaceSideInstanceDs
 
     when (length interface.requests > 0) do
-      -- | Requests record
+      -- Requests record
       tellQ requestCallbackRecordD
-      -- | Request proxies
+      -- Request proxies
       tellQs requestProxyInstanceDecs
 
     when (length interface.events > 0) do
-      -- | Events record
+      -- Events record
       tellQ eventCallbackRecordD
-      -- | Event proxies
+      -- Event proxies
       tellQs eventProxyInstanceDecs
 
   internals <- execWriterT do
-    -- | Request wire type
+    -- Request wire type
     when (length interface.requests > 0) do
       tellQs $ messageTypeDecs rTypeName wireRequestContexts
 
-    -- | Event wire type
+    -- Event wire type
     when (length interface.events > 0) do
       tellQs $ messageTypeDecs eTypeName wireEventContexts
 
@@ -222,6 +222,12 @@ interfaceDecs interface = do
     eventProxyInstanceDecs :: Q [Dec]
     eventProxyInstanceDecs = messageProxyInstanceDecs Server wireEventContexts
 
+    objectName = mkName "object"
+    objectP :: Q Pat
+    objectP = varP objectName
+    objectE :: Q Exp
+    objectE = varE objectName
+
     handlerName = mkName "handler"
     handlerP :: Q Pat
     handlerP = varP handlerName
@@ -238,18 +244,24 @@ interfaceDecs interface = do
     handleMessageD Server = funD 'handleMessage (handleMessageClauses wireRequestContexts)
 
     handleMessageClauses :: [MessageContext] -> [Q Clause]
-    handleMessageClauses [] = [clause [wildP] (normalB [|absurd|]) []]
+    handleMessageClauses [] = [clause [wildP, wildP] (normalB [|absurd|]) []]
     handleMessageClauses messageContexts = handleMessageClause <$> messageContexts
 
     handleMessageClause :: MessageContext -> Q Clause
-    handleMessageClause msg = clause [handlerP, msgConP msg] (normalB bodyE) []
+    handleMessageClause msg = clause [objectIfRequiredP, handlerP, msgConP msg] (normalB bodyE) []
       where
+        objectIfRequiredP :: Q Pat
+        objectIfRequiredP = if msg.msgSpec.isDestructor then objectP else wildP
         fieldNameLitT :: Q Type
         fieldNameLitT = litT (strTyLit (messageFieldNameString msg))
         msgHandlerE :: Q Exp
         msgHandlerE = [|$(appTypeE [|getField|] fieldNameLitT) $handlerE|]
         bodyE :: Q Exp
-        bodyE = [|lift =<< $(applyMsgArgs msgHandlerE)|]
+        bodyE
+          | msg.msgSpec.isDestructor = [|handleDestructor $objectE >> $msgE|]
+          | otherwise = msgE
+        msgE :: Q Exp
+        msgE = [|$(applyMsgArgs msgHandlerE) >>= lift|]
 
         applyMsgArgs :: Q Exp -> Q Exp
         applyMsgArgs base = applyA base (argE <$> msg.msgSpec.arguments)
@@ -287,7 +299,10 @@ messageProxyInstanceDecs side messageContexts = mapM messageProxyInstanceD messa
         args = proxyArguments msg.msgSpec
 
         actionE :: Q Exp
-        actionE = if msg.msgSpec.isConstructor then ctorE else normalE
+        actionE
+          | msg.msgSpec.isConstructor = ctorE
+          | msg.msgSpec.isDestructor = dtorE
+          | otherwise = normalE
 
         -- Constructor: the first argument becomes the return value
         ctorE :: Q Exp
@@ -296,6 +311,9 @@ messageProxyInstanceDecs side messageContexts = mapM messageProxyInstanceD messa
             msgE :: Q Exp -> Q Exp
             msgE idArgE = mkWireMsgE (idArgE : (wireArgE <$> args))
 
+        dtorE :: Q Exp
+        dtorE = [|handleDestructor object >> $normalE|]
+
         -- Body for a normal (i.e. non-constructor) proxy
         normalE :: Q Exp
         normalE = [|sendMessage object =<< $(msgE)|]
@@ -603,6 +621,8 @@ parseMessage isRequest interface (opcode, element) = do
       Just "destructor" -> pure True
       Just messageType -> fail $ "Unknown message type: " <> messageType
 
+  when (isDestructor && not (null arguments)) $ fail $ "Destructor must not have arguments: " <> loc
+
   forM_ arguments \arg -> do
     when
       do arg.argType == GenericNewIdArgument && (interface /= "wl_registry" || name /= "bind")
-- 
GitLab