1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
|
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TemplateHaskell #-}
module Wayland.Server
( Request(..)
, ObjectID
, Global
, DisplayM(..)
, ResourceClass(..)
, RequestClass
, InterfaceDesc(..)
, MessageDesc(..)
, ArgumentDesc(..)
, Nullability(..)
, fromRequest
, withDisplay
, bindSocket
, processRequests
, flushEvents
, sendEvent
, destroyResource
, registerGlobal
, unregisterGlobal
, registerSignalEvent
, requestTermination
) where
import Data.Typeable (Typeable, Proxy, cast, TypeRep, typeRep)
import Data.Kind (Type)
import Control.Monad.State.Strict (StateT, MonadState(..), MonadIO(..), runStateT)
import Lens.Micro.Mtl.Internal (Zoom(..), Zoomed)
import Lens.Micro.TH (makeLenses)
import Lens.Micro.Mtl ((%=), use)
import qualified Wayland.Server.System as Sys
import Control.Monad.Trans (MonadTrans(..))
import Foreign.C (peekCString, newCString)
import Foreign.C.Types (CInt)
import Foreign (Ptr, nullPtr, new)
import Data.IORef (IORef, newIORef, writeIORef, readIORef)
import Control.Monad.Reader (ReaderT, MonadReader (..), runReaderT)
import Control.Exception (bracket)
import Data.Functor (($>))
import Control.Monad (when)
import Data.Maybe (isJust)
import qualified Data.Map as M
import Foreign.Marshal (newArray)
data Global = Global
newtype ObjectID = ObjectID String
deriving (Show, Eq, Ord)
data Nullability
= AllowNull
| NotNull
deriving (Show, Eq, Ord)
data ArgumentDesc
= ArgumentInt
| ArgumentUInt
| ArgumentFixed
| ArgumentString !Nullability
| ArgumentObject !Nullability !InterfaceDesc
| ArgumentNewID !InterfaceDesc
| ArgumentArray
| ArgumentFD
deriving (Show, Eq, Ord)
data MessageDesc
= MessageDesc { msgName :: !String
, msgSignature :: ![ArgumentDesc]
, msgSince :: !Int
, msgIsDestructor :: !Bool
}
deriving (Show, Eq, Ord)
data InterfaceDesc
= InterfaceDesc { ifName :: !String
, ifVersion :: !Int
, ifRequests :: ![MessageDesc]
, ifEvents :: ![MessageDesc]
}
deriving (Show, Eq, Ord)
class (Typeable r, Show r) => RequestClass r where
data Request = forall a. RequestClass a => Request a
newtype DisplayM s a = DisplayM { runDisplayM :: StateT s (StateT DisplayState (ReaderT Display IO)) a }
deriving (Functor, Applicative, Monad, MonadIO, MonadState s, MonadFail)
type RequestHandler s = Request -> DisplayM s ()
newtype EventLoop = EventLoop { handleEvent :: Request -> IO () }
data Display
= Display { display :: !Sys.WLDisplay
, activeEventLoop :: !(IORef (Maybe EventLoop))
}
data DisplayState
= DisplayState { _typeInterfaces :: !(M.Map TypeRep (Ptr Sys.WLInterface))
, _nativeInterfaces :: !(M.Map InterfaceDesc (Ptr Sys.WLInterface))
}
makeLenses ''DisplayState
class Typeable r => ResourceClass r where
type ResourceRequest r :: Type
type ResourceEvent r :: Type
objectID :: r -> ObjectID
interface :: Proxy r -> InterfaceDesc
instance Show Request where
show (Request r) = show r
type instance Zoomed (DisplayM s) = Zoomed (StateT s (StateT DisplayState (ReaderT Display IO)))
instance Zoom (DisplayM s) (DisplayM t) s t where
zoom l (DisplayM m) = DisplayM (zoom l m)
getDisplay :: DisplayM s Display
getDisplay = DisplayM $ lift $ lift ask
liftDisplayState :: StateT DisplayState (ReaderT Display IO) a -> DisplayM s a
liftDisplayState m = DisplayM $ lift m
fromRequest :: Typeable a => Request -> Maybe a
fromRequest (Request x) = cast x
withDisplay :: s -> DisplayM s a -> IO (a, s)
withDisplay initialState f = bracket Sys.wlDisplayCreate Sys.wlDisplayDestroy $ \sysDisp -> do
eventLoop <- newIORef Nothing
let disp = Display { display = sysDisp
, activeEventLoop = eventLoop
}
dispState = DisplayState { _typeInterfaces = M.empty
, _nativeInterfaces = M.empty
}
(res, _) <- runReaderT (runStateT (runStateT (runDisplayM f) initialState) dispState) disp
pure res
bindSocket :: DisplayM s String
bindSocket = do
disp <- getDisplay
name <- liftIO $ Sys.wlDisplayAddSocketAuto (display disp)
liftIO $ peekCString name
processRequests :: RequestHandler s -> DisplayM s ()
processRequests h = do
disp <- getDisplay
stateBefore <- get
displayStateBefore <- liftDisplayState get
stateRef <- liftIO $ newIORef (stateBefore, displayStateBefore)
let handler :: Request -> IO ()
handler r = do
(s, ds) <- readIORef stateRef
((_, s'), ds') <- runReaderT (runStateT (runStateT (runDisplayM (h r)) s) ds) disp
writeIORef stateRef (s', ds')
loop <- liftIO $ readIORef (activeEventLoop disp)
when (isJust loop) (fail "Cannot start an event loop from within an event handler.")
liftIO $ writeIORef (activeEventLoop disp) (Just (EventLoop handler))
liftIO $ Sys.wlDisplayRun (display disp)
(stateAfter, displayStateAfter) <- liftIO $ readIORef stateRef
put stateAfter
liftDisplayState $ put displayStateAfter
sendEvent :: ResourceClass r => r -> ResourceEvent r -> DisplayM s ()
sendEvent _ _ = undefined
flushEvents :: DisplayM s ()
flushEvents = undefined
destroyResource :: ResourceClass r => r -> DisplayM s ()
destroyResource _ = undefined
registerGlobal :: ResourceClass r => RequestClass req => (r -> req) -> DisplayM s Global
registerGlobal _ = pure Global
unregisterGlobal :: Global -> DisplayM s ()
unregisterGlobal _ = pure ()
runRequest :: Display -> Request -> IO ()
runRequest disp r = do
loop <- readIORef (activeEventLoop disp)
case loop of
Just handler -> handleEvent handler r
Nothing -> putStrLn $ "Lost request because no event loop is active: " ++ show r
registerSignalEvent :: RequestClass req => req -> CInt -> DisplayM s ()
registerSignalEvent r sig = do
disp <- getDisplay
liftIO $ do
loop <- Sys.wlDisplayGetEventLoop (display disp)
func <- Sys.wrapWLEventLoopSignalFunc $ \_ _ -> runRequest disp (Request r) $> 0
_ <- Sys.wlEventLoopAddSignal loop sig func nullPtr
-- TODO: Free func
pure ()
requestTermination :: DisplayM s ()
requestTermination = do
disp <- getDisplay
liftIO $ Sys.wlDisplayTerminate (display disp)
wrapInterface :: InterfaceDesc -> DisplayM s (Ptr Sys.WLInterface)
wrapInterface i = do
let mkArgumentType :: ArgumentDesc -> DisplayM s (Ptr Sys.WLInterface)
mkArgumentType (ArgumentObject _ a) = wrapInterface a
mkArgumentType (ArgumentNewID a) = wrapInterface a
mkArgumentType _ = pure nullPtr
let mkArgument :: ArgumentDesc -> String
mkArgument ArgumentInt = "i"
mkArgument ArgumentUInt = "u"
mkArgument ArgumentFixed = "f"
mkArgument (ArgumentString NotNull) = "s"
mkArgument (ArgumentString AllowNull) = "?s"
mkArgument (ArgumentObject NotNull _) = "o"
mkArgument (ArgumentObject AllowNull _) = "?o"
mkArgument (ArgumentNewID _) = "n"
mkArgument ArgumentArray = "a"
mkArgument ArgumentFD = "h"
let mkMessage :: MessageDesc -> DisplayM s Sys.WLMessage
mkMessage m = do
name <- liftIO $ newCString (msgName m)
signature <- liftIO $ newCString (show (msgSince m) ++ (msgSignature m >>= mkArgument))
types <- mapM mkArgumentType (msgSignature m) >>= liftIO . newArray
pure $ Sys.WLMessage { Sys.msgName = name
, Sys.msgSignature = signature
, Sys.msgTypes = types
}
let mkInterface :: DisplayM s (Ptr Sys.WLInterface)
mkInterface = do
name <- liftIO $ newCString (ifName i)
methods <- mapM mkMessage (ifRequests i) >>= liftIO . newArray
events <- mapM mkMessage (ifEvents i) >>= liftIO . newArray
liftIO $ new $ Sys.WLInterface { Sys.ifName = name
, Sys.ifVersion = fromIntegral $ ifVersion i
, Sys.ifMethodCount = fromIntegral $ length (ifRequests i)
, Sys.ifMethods = methods
, Sys.ifEventCount = fromIntegral $ length (ifEvents i)
, Sys.ifEvents = events
}
existing <- liftDisplayState $ use nativeInterfaces
case M.lookup i existing of
Just ptr -> pure ptr
Nothing -> do ptr <- mkInterface
-- TODO: Free ptr
liftDisplayState $ nativeInterfaces %= M.insert i ptr
pure ptr
getInterface :: ResourceClass r => Proxy r -> DisplayM s (Ptr Sys.WLInterface)
getInterface p = do
existing <- liftDisplayState $ use typeInterfaces
case M.lookup (typeRep p) existing of
Just ptr -> pure ptr
Nothing -> do ptr <- wrapInterface (interface p)
liftDisplayState $ typeInterfaces %= M.insert (typeRep p) ptr
pure ptr
|