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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
|
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
module Wayland.Server
( Request(..)
, ObjectID
, Global
, ArgumentMarshaller(..)
, ArgumentDemarshaller(..)
, DisplayM(..)
, ResourceClass(..)
, EventClass
, 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, newStablePtr, castStablePtrToPtr, castPtrToStablePtr, deRefStablePtr, freeStablePtr, Storable (..), Int32, castPtr, free, mallocArray0)
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)
import Data.Word (Word32)
import System.Mem.StableName (StableName, makeStableName)
import Data.Hashable (Hashable(..))
import Foreign.Ptr (plusPtr)
data Global = Global
-- IORef set to nullPtr once destroyed.
data ObjectID = ObjectID (StableName (IORef Sys.WLResource)) (IORef Sys.WLResource)
deriving (Eq)
instance Ord ObjectID where
(ObjectID nameA _) <= (ObjectID nameB _) = hash nameA <= hash nameB
instance Hashable ObjectID where
hashWithSalt s (ObjectID name _) = hashWithSalt s name
instance Show ObjectID where
show (ObjectID name _) = "ObjectID " ++ show (hash name)
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
class (Typeable e, Show e) => EventClass e 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, RequestClass (ResourceRequest r), EventClass (ResourceEvent r)) => ResourceClass r where
type ResourceRequest r :: Type
type ResourceEvent r :: Type
objectID :: r -> ObjectID
fromID :: ObjectID -> r
interface :: Proxy r -> InterfaceDesc
marshalEvent :: ArgumentMarshaller m => Proxy r -> ResourceEvent r -> m Int
demarshalRequest :: ArgumentDemarshaller m => r -> Int -> m (ResourceRequest r)
eventSignature :: Proxy r -> ResourceEvent r -> MessageDesc
requestSignature :: Proxy r -> ResourceRequest r -> MessageDesc
class (Monad m, MonadFail m) => ArgumentMarshaller m where
marshalInt :: Int -> m ()
marshalUInt :: Word -> m ()
marshalFixed :: Float -> m ()
marshalString :: String -> m ()
marshalObject :: ResourceClass r => r -> m ()
marshalNewID :: ResourceClass r => r -> m ()
marshalArray :: Storable a => [a] -> m ()
marshalFD :: Int32 -> m ()
class (Monad m, MonadFail m) => ArgumentDemarshaller m where
demarshalInt :: m Int
demarshalUInt :: m Word
demarshalFixed :: m Float
demarshalString :: m String
demarshalObject :: ResourceClass r => m r
demarshalNewID :: ResourceClass r => m r
demarshalArray :: Storable a => m [a]
demarshalFD :: m Int32
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
registerGlobal :: forall r bindReq unbindReq s. ResourceClass r => RequestClass bindReq => RequestClass unbindReq => (r -> bindReq) -> (r -> unbindReq) -> Int -> DisplayM s Global
registerGlobal mkBindReq mkUnbindReq version = do
disp <- getDisplay
iface <- getInterface (Proxy :: Proxy r)
let dispatcher :: Ptr () -> Sys.WLResource -> Word32 -> Ptr Sys.WLMessage -> Ptr Sys.WLArgument -> IO CInt
dispatcher _ res opcode _ _ = do
handlePtr <- castPtrToStablePtr <$> Sys.wlResourceGetUserData res
objID@(ObjectID _ _) <- deRefStablePtr handlePtr
putStrLn $ "Handling request " ++ show opcode ++ " for " ++ show objID
-- TODO: Handle request
pure 0
let destroy :: Sys.WLResource -> IO ()
destroy res = do
handlePtr <- castPtrToStablePtr <$> Sys.wlResourceGetUserData res
objID@(ObjectID _ handle) <- deRefStablePtr handlePtr
writeIORef handle (Sys.WLResource nullPtr)
freeStablePtr handlePtr
let req = mkUnbindReq (fromID objID)
runRequest disp (Request req)
pure ()
let createObject :: Sys.WLClient -> Ptr () -> Word32 -> Word32 -> IO ()
createObject client _ ver protID = do
res <- Sys.wlResourceCreate client iface (fromIntegral ver) protID
handle <- newIORef res
name <- makeStableName handle
let objID = ObjectID name handle
handlePtr <- newStablePtr objID
dis <- Sys.wrapWLDispatcherFunc dispatcher
des <- Sys.wrapWLResourceDestroyFunc destroy
-- TODO: Free functions
Sys.wlResourceSetDispatcher res dis nullPtr (castStablePtrToPtr handlePtr) des
let req = mkBindReq (fromID objID)
runRequest disp (Request req)
pure ()
liftIO $ do
func <- Sys.wrapWLGlobalBindFunc createObject
_ <- Sys.wlGlobalCreate (display disp) iface (fromIntegral version) nullPtr func
-- TODO: Free func
-- TODO: Return proper Global
pure ()
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
-- TODO: Return event source handle
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
data ArgumentIteratorState
= ArgumentIteratorState { _argNext :: !(Ptr Sys.WLArgument)
, _argRemaining :: !Int
, _allocatedBuffers :: ![Ptr ()]
}
deriving (Show)
makeLenses ''ArgumentIteratorState
newtype ArgumentIterator a = ArgumentIterator { runArgumentIterator :: StateT ArgumentIteratorState IO a }
deriving (Functor, Applicative, Monad, MonadFail, MonadState ArgumentIteratorState, MonadIO)
advanceIterator :: ArgumentIterator (Ptr Sys.WLArgument)
advanceIterator = do
next <- argNext <<%= \p -> plusPtr p (sizeOf (undefined :: Sys.WLArgument))
remaining <- argRemaining <<%= (+(-1))
when (remaining == 0) (fail "Out of range for argument")
pure next
getObjectForHandle :: ResourceClass r => r -> IO (Ptr a)
getObjectForHandle r = fetch (objectID r)
where fetch (ObjectID _ handle) = readIORef handle >>= check
check (Sys.WLResource ptr) | ptr == nullPtr = fail "Invalid object handle."
| otherwise = pure (castPtr ptr)
instance ArgumentMarshaller ArgumentIterator where
marshalInt v = advanceIterator >>= (liftIO . Sys.wlArgumentSetInt (fromIntegral v))
marshalUInt v = advanceIterator >>= (liftIO . Sys.wlArgumentSetUInt (fromIntegral v))
marshalFixed v = advanceIterator >>= (liftIO . Sys.wlArgumentSetFixed (Sys.WLFixed (round (v * 256))))
marshalFD v = advanceIterator >>= (liftIO . Sys.wlArgumentSetFD (fromIntegral v))
marshalString v = do
arg <- advanceIterator
str <- liftIO $ newCString v
allocatedBuffers %= (castPtr str:)
liftIO $ Sys.wlArgumentSetString str arg
marshalObject v = do
arg <- advanceIterator
obj <- liftIO $ getObjectForHandle v
liftIO $ Sys.wlArgumentSetObject obj arg
marshalNewID v = do
arg <- advanceIterator
obj <- liftIO $ getObjectForHandle v
liftIO $ Sys.wlArgumentSetObject obj arg
marshalArray v = do
arg <- advanceIterator
let len = fromIntegral $ length v * sizeOf (head v)
buf <- liftIO $ newArray v
arr <- liftIO $ new $ Sys.WLArray { Sys.arrSize = len
, Sys.arrAlloc = len
, Sys.arrData = castPtr buf
}
allocatedBuffers %= \p -> castPtr buf:castPtr arr:p
liftIO $ Sys.wlArgumentSetArray arr arg
sendEvent :: forall r s. ResourceClass r => r -> ResourceEvent r -> DisplayM s ()
sendEvent res e = liftIO $ do
let params = msgSignature (eventSignature (Proxy :: Proxy r) e)
ptr <- getObjectForHandle res
buffer <- mallocArray0 (length params)
let s = ArgumentIteratorState { _argNext = buffer
, _argRemaining = length params
, _allocatedBuffers = [castPtr buffer]
}
(opcode, s') <- runStateT (runArgumentIterator (marshalEvent (Proxy :: Proxy r) e)) s
Sys.wlResourcePostEventArray (Sys.WLResource ptr) (fromIntegral opcode) buffer
mapM_ free (_allocatedBuffers s')
flushEvents :: DisplayM s ()
flushEvents = do
disp <- getDisplay
liftIO $ Sys.wlDisplayFlushClients (display disp)
destroyResource :: ResourceClass r => r -> DisplayM s ()
destroyResource r = liftIO $ do
ptr <- getObjectForHandle r
Sys.wlResourceDestroy (Sys.WLResource ptr)
|