From 842d60b134c0fd60f734adf68829a24e22c9cb3f Mon Sep 17 00:00:00 2001
From: Jens Nolte <git@queezle.net>
Date: Tue, 14 Dec 2021 18:04:57 +0100
Subject: [PATCH] Move wl_display and wl_registry implementation to client
 module

This simplifies cyclic dependencies for now (the registry needs to call
wl_display.sync).
---
 quasar-wayland.cabal                |   3 +-
 src/Quasar/Wayland/Client.hs        | 107 +++++++++++++++++++---------
 src/Quasar/Wayland/Client/Socket.hs |  43 +++++++++++
 src/Quasar/Wayland/Display.hs       |  36 ----------
 src/Quasar/Wayland/Registry.hs      |  45 ------------
 5 files changed, 118 insertions(+), 116 deletions(-)
 create mode 100644 src/Quasar/Wayland/Client/Socket.hs
 delete mode 100644 src/Quasar/Wayland/Display.hs
 delete mode 100644 src/Quasar/Wayland/Registry.hs

diff --git a/quasar-wayland.cabal b/quasar-wayland.cabal
index 1c5bdc3..ebb8ae0 100644
--- a/quasar-wayland.cabal
+++ b/quasar-wayland.cabal
@@ -75,13 +75,12 @@ library
   import: shared-properties
   exposed-modules:
     Quasar.Wayland.Client
+    Quasar.Wayland.Client.Socket
     Quasar.Wayland.Connection
-    Quasar.Wayland.Display
     Quasar.Wayland.Protocol
     Quasar.Wayland.Protocol.Display
     Quasar.Wayland.Protocol.Generated
     Quasar.Wayland.Protocol.TH
-    Quasar.Wayland.Registry
   other-modules:
     Quasar.Wayland.Protocol.Core
   build-depends:
diff --git a/src/Quasar/Wayland/Client.hs b/src/Quasar/Wayland/Client.hs
index 9544447..c9bcf13 100644
--- a/src/Quasar/Wayland/Client.hs
+++ b/src/Quasar/Wayland/Client.hs
@@ -3,19 +3,29 @@ module Quasar.Wayland.Client (
   connectWaylandClient,
   newWaylandClient,
   connectWaylandSocket,
+
+  -- * wl_display
+  ClientDisplay,
+  newClientDisplay,
+
+  -- * wl_registry
+  ClientRegistry,
+  createClientRegistry,
 ) 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 Network.Socket qualified as Socket
 import Quasar
 import Quasar.Prelude
+import Quasar.Wayland.Client.Socket
 import Quasar.Wayland.Connection
-import Quasar.Wayland.Display
 import Quasar.Wayland.Protocol
-import System.Environment (getEnv, lookupEnv)
-import System.FilePath ((</>), isRelative)
-import Text.Read (readEither)
+import Quasar.Wayland.Protocol.Display
+import Quasar.Wayland.Protocol.Generated
 
 
 data WaylandClient = WaylandClient {
@@ -42,33 +52,64 @@ connectWaylandClient = mask_ do
   socket <- liftIO connectWaylandSocket
   newWaylandClient socket
 
-connectWaylandSocket :: IO Socket
-connectWaylandSocket = do
-  lookupEnv "WAYLAND_SOCKET" >>= \case
-    -- Parent process already established connection
-    Just waylandSocketEnv -> do
-      case readEither waylandSocketEnv of
-        Left err -> fail $ "Failed to parse WAYLAND_SOCKET: " <> err
-        Right fd -> Socket.mkSocket fd
-    Nothing -> do
-      path <- getWaylandSocketPath
-      newUnixSocket path
 
+
+-- * 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
+    var <- newAsyncVarSTM
+    wlCallback <- display.wlDisplay.sync
+    setEventHandler wlCallback EventHandler_wl_callback {
+      done = const $ 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
-    getWaylandSocketPath :: IO FilePath
-    getWaylandSocketPath = do
-      waylandDisplayEnv <- lookupEnv "WAYLAND_DISPLAY"
-      let waylandDisplay = fromMaybe "wayland-0" waylandDisplayEnv
-      if isRelative waylandDisplay
-        then do
-          xdgRuntimeDir <- getEnv "XDG_RUNTIME_DIR"
-          pure (xdgRuntimeDir </> waylandDisplay)
-        else
-          pure waylandDisplay
-
-    newUnixSocket :: FilePath -> IO Socket
-    newUnixSocket socketPath =
-      bracketOnError (Socket.socket Socket.AF_UNIX Socket.Stream Socket.defaultProtocol) Socket.close $ \sock -> do
-        Socket.withFdSocket sock Socket.setCloseOnExecIfNeeded
-        Socket.connect sock $ Socket.SockAddrUnix socketPath
-        pure sock
+    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/Socket.hs b/src/Quasar/Wayland/Client/Socket.hs
new file mode 100644
index 0000000..106ce3f
--- /dev/null
+++ b/src/Quasar/Wayland/Client/Socket.hs
@@ -0,0 +1,43 @@
+module Quasar.Wayland.Client.Socket (
+  connectWaylandSocket
+) where
+
+import Control.Monad.Catch
+import Network.Socket (Socket)
+import Network.Socket qualified as Socket
+import Quasar.Prelude
+import System.Environment (getEnv, lookupEnv)
+import System.FilePath ((</>), isRelative)
+import Text.Read (readEither)
+
+
+connectWaylandSocket :: IO Socket
+connectWaylandSocket = do
+  lookupEnv "WAYLAND_SOCKET" >>= \case
+    -- Parent process already established connection
+    Just waylandSocketEnv -> do
+      case readEither waylandSocketEnv of
+        Left err -> fail $ "Failed to parse WAYLAND_SOCKET: " <> err
+        Right fd -> Socket.mkSocket fd
+    Nothing -> do
+      path <- getWaylandSocketPath
+      newUnixSocket path
+
+  where
+    getWaylandSocketPath :: IO FilePath
+    getWaylandSocketPath = do
+      waylandDisplayEnv <- lookupEnv "WAYLAND_DISPLAY"
+      let waylandDisplay = fromMaybe "wayland-0" waylandDisplayEnv
+      if isRelative waylandDisplay
+        then do
+          xdgRuntimeDir <- getEnv "XDG_RUNTIME_DIR"
+          pure (xdgRuntimeDir </> waylandDisplay)
+        else
+          pure waylandDisplay
+
+    newUnixSocket :: FilePath -> IO Socket
+    newUnixSocket socketPath =
+      bracketOnError (Socket.socket Socket.AF_UNIX Socket.Stream Socket.defaultProtocol) Socket.close $ \sock -> do
+        Socket.withFdSocket sock Socket.setCloseOnExecIfNeeded
+        Socket.connect sock $ Socket.SockAddrUnix socketPath
+        pure sock
diff --git a/src/Quasar/Wayland/Display.hs b/src/Quasar/Wayland/Display.hs
deleted file mode 100644
index 8350525..0000000
--- a/src/Quasar/Wayland/Display.hs
+++ /dev/null
@@ -1,36 +0,0 @@
-module Quasar.Wayland.Display (
-  ClientDisplay,
-  newClientDisplay,
-) where
-
-import Control.Concurrent.STM
-import GHC.Records
-import Quasar.Awaitable
-import Quasar.Prelude
-import Quasar.Wayland.Protocol
-import Quasar.Wayland.Protocol.Display
-import Quasar.Wayland.Protocol.Generated
-import Quasar.Wayland.Registry
-
-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
-    var <- newAsyncVarSTM
-    wlCallback <- display.wlDisplay.sync
-    setEventHandler wlCallback EventHandler_wl_callback {
-      done = const $ putAsyncVarSTM_ var ()
-    }
-    pure $ toAwaitable var
diff --git a/src/Quasar/Wayland/Registry.hs b/src/Quasar/Wayland/Registry.hs
deleted file mode 100644
index 9b8b798..0000000
--- a/src/Quasar/Wayland/Registry.hs
+++ /dev/null
@@ -1,45 +0,0 @@
-module Quasar.Wayland.Registry (
-  ClientRegistry,
-  createClientRegistry,
-) where
-
-import Control.Concurrent.STM
-import Control.Monad.Fix (mfix)
-import Control.Monad.Reader (lift)
-import Data.ByteString qualified as BS
-import Data.HashMap.Strict qualified as HM
-import Data.Tuple (swap)
-import Quasar.Prelude
-import Quasar.Wayland.Protocol
-import Quasar.Wayland.Protocol.Generated
-
-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 ()
-- 
GitLab