--- /dev/null
+{-# LANGUAGE
+ FlexibleContexts
+ , FlexibleInstances
+ , KindSignatures
+ , MultiParamTypeClasses
+ , UnicodeSyntax
+ , ScopedTypeVariables
+ #-}
+-- | EsounD controlling handles.
+module Sound.EsounD.Controller
+ ( Controller
+ , openController
+
+ , lock
+ , unlock
+ )
+ where
+import Bindings.EsounD
+import Control.Exception.Peel
+import Control.Monad.IO.Class
+import Control.Monad.IO.Peel
+import Control.Monad.Trans.Region
+import Control.Monad.Trans.Region.OnExit
+import Control.Monad.Unicode
+import Foreign.C.Types
+import Network
+import Sound.EsounD.Internals
+import System.IO.SaferFileHandles.Unsafe
+import System.Posix.Types
+import Text.Printf
+
+-- ^ An opaque ESD handle for controlling ESD.
+data Controller (r ∷ ★ → ★)
+ = Controller {
+ coSocket ∷ !Fd
+ , coCloseH ∷ !(FinalizerHandle r)
+ }
+
+instance Dup Controller where
+ dup co = do ch' ← dup (coCloseH co)
+ return co { coCloseH = ch' }
+
+-- | Open an ESD handle for controlling ESD.
+openController ∷ MonadPeelIO pr
+ ⇒ Maybe HostName -- ^ host to connect to.
+ → RegionT s pr (Controller (RegionT s pr))
+openController host
+ = block $
+ do s ← liftIO openSocket
+ ch ← onExit $ sanitizeIOError $ closeSocket' s
+ return Controller {
+ coSocket = s
+ , coCloseH = ch
+ }
+ where
+ openSocket ∷ IO Fd
+ openSocket = withCStrOrNull host $ \hostPtr →
+ c'esd_open_sound hostPtr
+ ≫= wrapSocket'
+
+ wrapSocket' ∷ Monad m ⇒ CInt → m Fd
+ wrapSocket' (-1) = fail ( printf "esd_open_sound(%s) returned an error"
+ (show host)
+ )
+ wrapSocket' fd = return $ Fd fd
+
+ closeSocket' ∷ Fd → IO ()
+ closeSocket' fd
+ = do _ ← c'esd_close $ fdToCInt fd
+ return ()
+
+fdToCInt ∷ Fd → CInt
+fdToCInt (Fd fd) = fromIntegral fd
+
+-- | Lock the ESD so that it won't accept connections from remote
+-- hosts.
+lock ∷ ( AncestorRegion pr cr
+ , MonadIO cr
+ )
+ ⇒ Controller pr
+ → cr ()
+lock co
+ = liftIO $
+ sanitizeIOError $
+ c'esd_lock (fdToCInt $ coSocket co)
+ ≫= failOnError "esd_lock(fd) returned an error"
+
+-- | Unlock the ESD so that it will accept connections from remote
+-- hosts.
+unlock ∷ ( AncestorRegion pr cr
+ , MonadIO cr
+ )
+ ⇒ Controller pr
+ → cr ()
+unlock co
+ = liftIO $
+ sanitizeIOError $
+ c'esd_unlock (fdToCInt $ coSocket co)
+ ≫= failOnError "esd_unlock(fd) returned an error"