From 9714aaac55fdb6a616e38576ccdd99701a844fea Mon Sep 17 00:00:00 2001
From: Jens Nolte <git@queezle.net>
Date: Wed, 15 Dec 2021 07:28:23 +0100
Subject: [PATCH] Re-introduce Registry module

---
 quasar-wayland.cabal                   |   1 +
 src/Quasar/Wayland/Client.hs           | 100 ++++++-------------------
 src/Quasar/Wayland/Client/Registry.hs  |  53 +++++++++++++
 src/Quasar/Wayland/Protocol/Display.hs |  10 +++
 4 files changed, 88 insertions(+), 76 deletions(-)
 create mode 100644 src/Quasar/Wayland/Client/Registry.hs

diff --git a/quasar-wayland.cabal b/quasar-wayland.cabal
index ebb8ae0..0131719 100644
--- a/quasar-wayland.cabal
+++ b/quasar-wayland.cabal
@@ -75,6 +75,7 @@ library
   import: shared-properties
   exposed-modules:
     Quasar.Wayland.Client
+    Quasar.Wayland.Client.Registry
     Quasar.Wayland.Client.Socket
     Quasar.Wayland.Connection
     Quasar.Wayland.Protocol
diff --git a/src/Quasar/Wayland/Client.hs b/src/Quasar/Wayland/Client.hs
index c9bcf13..ee2b9f0 100644
--- a/src/Quasar/Wayland/Client.hs
+++ b/src/Quasar/Wayland/Client.hs
@@ -1,26 +1,19 @@
 module Quasar.Wayland.Client (
-  WaylandClient(display),
+  WaylandClient(registry),
   connectWaylandClient,
   newWaylandClient,
-  connectWaylandSocket,
-
-  -- * wl_display
-  ClientDisplay,
-  newClientDisplay,
 
   -- * wl_registry
-  ClientRegistry,
-  createClientRegistry,
+  Registry,
 ) where
 
 import Control.Concurrent.STM
 import Control.Monad.Catch
-import Data.HashMap.Strict qualified as HM
-import Data.Tuple (swap)
 import GHC.Records
 import Network.Socket (Socket)
 import Quasar
 import Quasar.Prelude
+import Quasar.Wayland.Client.Registry
 import Quasar.Wayland.Client.Socket
 import Quasar.Wayland.Connection
 import Quasar.Wayland.Protocol
@@ -30,86 +23,41 @@ import Quasar.Wayland.Protocol.Generated
 
 data WaylandClient = WaylandClient {
   connection :: WaylandConnection 'Client,
-  display :: ClientDisplay
+  wlDisplay :: Object 'Client Interface_wl_display,
+  registry :: Registry
 }
 
 instance IsResourceManager WaylandClient where
-  toResourceManager (WaylandClient connection _) = toResourceManager connection
+  toResourceManager client = toResourceManager client.connection
 
 instance IsDisposable WaylandClient where
-  toDisposable (WaylandClient connection _) = toDisposable connection
-
-newWaylandClient :: MonadResourceManager m => Socket -> m WaylandClient
-newWaylandClient socket = do
-  (display, connection) <- newWaylandConnection newClientDisplay socket
-  pure WaylandClient {
-    connection,
-    display
-  }
+  toDisposable client = toDisposable client.connection
 
 connectWaylandClient :: MonadResourceManager m => m WaylandClient
 connectWaylandClient = mask_ do
   socket <- liftIO connectWaylandSocket
   newWaylandClient socket
 
+newWaylandClient :: MonadResourceManager m => Socket -> m WaylandClient
+newWaylandClient socket = do
+  ((wlDisplay, registry), connection) <- newWaylandConnection newClientDisplay socket
+  pure WaylandClient {
+    connection,
+    wlDisplay,
+    registry
+  }
+  where
+    newClientDisplay :: STM ((Object 'Client Interface_wl_display, Registry), ProtocolHandle 'Client)
+    newClientDisplay =
+      initializeProtocol wlDisplayEventHandler \wlDisplay -> do
+        registry <- createRegistry wlDisplay
+        pure (wlDisplay, registry)
 
 
--- * wl_display
-
-data ClientDisplay = ClientDisplay {
-  wlDisplay :: Object 'Client Interface_wl_display,
-  registry :: ClientRegistry
-}
-
-newClientDisplay :: STM (ClientDisplay, ProtocolHandle 'Client)
-newClientDisplay =
-  initializeProtocol wlDisplayEventHandler \wlDisplay -> do
-    registry <- createClientRegistry wlDisplay
-    pure ClientDisplay {
-      wlDisplay,
-      registry
-    }
 
-instance HasField "sync" ClientDisplay (STM (Awaitable ())) where
-  getField display = do
+instance HasField "sync" WaylandClient (STM (Awaitable ())) where
+  getField client = do
     var <- newAsyncVarSTM
-    wlCallback <- display.wlDisplay.sync
-    setEventHandler wlCallback EventHandler_wl_callback {
-      done = const $ putAsyncVarSTM_ var ()
-    }
+    lowLevelSync client.wlDisplay \_ -> putAsyncVarSTM_ var ()
     pure $ toAwaitable var
 
-
-
--- * wl_registry
-
-data ClientRegistry = ClientRegistry {
-  wlRegistry :: Object 'Client Interface_wl_registry,
-  globalsVar :: TVar (HM.HashMap Word32 (WlString, Word32))
-}
-
-createClientRegistry :: Object 'Client Interface_wl_display -> STM ClientRegistry
-createClientRegistry wlDisplay = mfix \clientRegistry -> do
-  globalsVar <- newTVar HM.empty
-
-  wlRegistry <- wlDisplay.get_registry
-  setMessageHandler wlRegistry (messageHandler clientRegistry)
-
-  pure ClientRegistry {
-    wlRegistry,
-    globalsVar
-  }
-  where
-    messageHandler :: ClientRegistry -> EventHandler_wl_registry
-    messageHandler clientRegistry = EventHandler_wl_registry { global, global_remove }
-      where
-        global :: Word32 -> WlString -> Word32 -> STM ()
-        global name interface version = do
-          modifyTVar clientRegistry.globalsVar (HM.insert name (interface, version))
-
-        global_remove :: Word32 -> STM ()
-        global_remove name = do
-          result <- stateTVar clientRegistry.globalsVar (swap . lookupDelete name)
-          case result of
-            Nothing -> traceM $ "Invalid global removed by server: " <> show name
-            Just (interface, version) -> pure ()
diff --git a/src/Quasar/Wayland/Client/Registry.hs b/src/Quasar/Wayland/Client/Registry.hs
new file mode 100644
index 0000000..26ee1e6
--- /dev/null
+++ b/src/Quasar/Wayland/Client/Registry.hs
@@ -0,0 +1,53 @@
+module Quasar.Wayland.Client.Registry (
+  Registry,
+  createRegistry
+) where
+
+import Control.Concurrent.STM
+import Data.HashMap.Strict qualified as HM
+import Data.Tuple (swap)
+import Quasar
+import Quasar.Prelude
+import Quasar.Wayland.Protocol
+import Quasar.Wayland.Protocol.Display
+import Quasar.Wayland.Protocol.Generated
+
+-- * wl_registry
+
+data Registry = Registry {
+  wlRegistry :: Object 'Client Interface_wl_registry,
+  globalsVar :: TVar (HM.HashMap Word32 (WlString, Word32)),
+  initialSyncComplete :: Awaitable ()
+}
+
+createRegistry :: Object 'Client Interface_wl_display -> STM Registry
+createRegistry wlDisplay = mfix \clientRegistry -> do
+  globalsVar <- newTVar HM.empty
+
+  wlRegistry <- wlDisplay.get_registry
+  setMessageHandler wlRegistry (messageHandler clientRegistry)
+
+  -- Manual sync (without high-level wrapper) to prevent a dependency loop to the client
+  var <- newAsyncVarSTM
+  lowLevelSync wlDisplay \_ -> putAsyncVarSTM_ var ()
+  let initialSyncComplete = toAwaitable var
+
+  pure Registry {
+    wlRegistry,
+    globalsVar,
+    initialSyncComplete
+  }
+  where
+    messageHandler :: Registry -> EventHandler_wl_registry
+    messageHandler clientRegistry = EventHandler_wl_registry { global, global_remove }
+      where
+        global :: Word32 -> WlString -> Word32 -> STM ()
+        global name interface version = do
+          modifyTVar clientRegistry.globalsVar (HM.insert name (interface, version))
+
+        global_remove :: Word32 -> STM ()
+        global_remove name = do
+          result <- stateTVar clientRegistry.globalsVar (swap . lookupDelete name)
+          case result of
+            Nothing -> traceM $ "Invalid global removed by server: " <> show name
+            Just (interface, version) -> pure ()
diff --git a/src/Quasar/Wayland/Protocol/Display.hs b/src/Quasar/Wayland/Protocol/Display.hs
index 6f9bb63..c37e3e1 100644
--- a/src/Quasar/Wayland/Protocol/Display.hs
+++ b/src/Quasar/Wayland/Protocol/Display.hs
@@ -1,8 +1,10 @@
 module Quasar.Wayland.Protocol.Display (
+  lowLevelSync,
   wlDisplayEventHandler,
 ) where
 
 import Control.Monad.Catch
+import Control.Monad.STM
 import Quasar.Prelude
 import Quasar.Wayland.Protocol.Core
 import Quasar.Wayland.Protocol.Generated
@@ -18,3 +20,11 @@ wlDisplayEventHandler = EventHandler_wl_display { error = waylandError, delete_i
   where
     waylandError oId code message = throwM $ ServerError code (toString message)
     delete_id deletedId = pure () -- TODO confirm delete
+
+
+lowLevelSync :: Object 'Client Interface_wl_display -> (Word32 -> STM ()) -> STM ()
+lowLevelSync wlDisplay callback = do
+  wlCallback <- wlDisplay.sync
+  setEventHandler wlCallback EventHandler_wl_callback {
+    done = callback
+  }
-- 
GitLab