aboutsummaryrefslogtreecommitdiff
path: root/src/Wetterhorn/Foreign/ForeignInterface.hs
blob: 471e3a99960730380f89d4ad815ae704885b841d (plain) (blame)
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
module Wetterhorn.Foreign.ForeignInterface
  ( getForeignInterface,
    ForeignInterface (..),
    ForeignDemarshal (..),
    runForeignDemarshal,
    demarshal,
    doShellExec,
  )
where

import Control.Monad.State (MonadState (get, put), MonadTrans (lift), StateT, evalStateT)
import Data.Void (Void)
import Foreign (Ptr, Storable (peek, sizeOf), castPtr, plusPtr)
import Foreign.C (CChar, CInt (..))
import Foreign.C.String
import GHC.Exts (FunPtr)
import Wetterhorn.Foreign.WlRoots

newtype ForeignDemarshal a = ForeignDemarshal (StateT (Ptr ()) IO a)
  deriving (Functor, Monad, Applicative, MonadState (Ptr ()))

runForeignDemarshal :: Ptr b -> ForeignDemarshal a -> IO a
runForeignDemarshal p (ForeignDemarshal dm) = evalStateT dm (castPtr p)

demarshal :: (Storable a) => ForeignDemarshal a
demarshal = do
  ptr <- get
  val <- ForeignDemarshal $ lift $ peek $ castPtr ptr
  put (plusPtr ptr (sizeOf val))
  return val

type CtxT = Ptr Void

type ForeignCallGetPtr = CtxT -> IO (Ptr ())

type ForeignCall = CtxT -> IO ()

type ForeignCallStr = CtxT -> CString -> IO ()

type ForeignCallInt = CtxT -> CInt -> IO ()

foreign import ccall "get_foreign_interface" foreignInterfacePtr :: IO (Ptr ())

foreign import ccall "dynamic" toForeignCall :: FunPtr ForeignCall -> ForeignCall

foreign import ccall "dynamic" toForeignCallStr :: FunPtr ForeignCallStr -> ForeignCallStr

foreign import ccall "dynamic" toForeignCallInt :: FunPtr ForeignCallInt -> ForeignCallInt

foreign import ccall "dynamic" toForeignCallGetPtr :: FunPtr ForeignCallGetPtr -> ForeignCallGetPtr

foreign import ccall "shell_exec" shellExec :: Ptr CChar -> IO ()

data ForeignInterface = ForeignInterface
  { requestHotReload :: IO (),
    requestLog :: String -> IO (),
    requestExit :: Int -> IO (),
    getSeat :: IO (Ptr WlrSeat)
  }

doShellExec :: String -> IO ()
doShellExec str = withCString str shellExec

getForeignInterface :: IO ForeignInterface
getForeignInterface = do
  ptr <- foreignInterfacePtr
  runForeignDemarshal ptr $ do
    ctx <- demarshal
    requestHotReloadFn <- demarshal
    doLogFn <- demarshal
    doRequestExit <- demarshal
    getSeatFn <- demarshal

    return $
      ForeignInterface
        { requestHotReload = toForeignCall requestHotReloadFn ctx,
          requestLog = \str ->
            withCString str $ \cs -> toForeignCallStr doLogFn ctx cs,
          requestExit = toForeignCallInt doRequestExit ctx . fromIntegral,
          getSeat = castPtr <$> toForeignCallGetPtr getSeatFn ctx
        }