aboutsummaryrefslogtreecommitdiff
path: root/src/Wetterhorn/Core/W.hs
blob: 89ebf4bfce7781230227d284a7ac10c9a1edf194 (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
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
module Wetterhorn.Core.W where

import Control.Arrow (Arrow (first))
import Control.Monad.RWS (MonadIO (liftIO), MonadReader, MonadState)
import Control.Monad.Reader (ReaderT (runReaderT))
import Control.Monad.State (StateT (runStateT))
import Data.Data (Typeable, cast)
import Data.Kind (Constraint, Type)
import Data.Set (Set)
import Foreign (StablePtr)
import Text.Read
import Wetterhorn.Core.KeyEvent
import Wetterhorn.Core.SurfaceEvent
import Wetterhorn.Foreign
import Wetterhorn.Foreign.ForeignInterface (ForeignInterface)
import Wetterhorn.StackSet hiding (layout)

data RationalRect = RationalRect Rational Rational Rational Rational

-- | Wrapper for a message. Messages are sent to layout and layouts are supposed
-- to handle them. This hides a typeable parameter.
data Message where
  Message :: (Typeable a) => a -> Message

-- | casts a message to a type.
fromMessage :: (Typeable a) => Message -> Maybe a
fromMessage (Message t) = cast t

-- | Wraps a type in a message.
toMessage :: (Typeable a) => a -> Message
toMessage = Message

-- | Types of this class "lay out" windows by assigning rectangles and handle
-- messages.
class (Typeable l) => LayoutClass l where
  -- | Constraints on the type to lay out. Sometimes a layout requires the 'a'
  -- type to be "Ord", other times "Eq", this is the mechanism by which this
  -- constraint is expressed.
  type C l :: Type -> Constraint

  -- | Executes the layout on some windows in a pure way. Returns a list of
  -- windows to their assigned rectangle.
  pureLayout :: (C l a) => [a] -> l -> [(a, RationalRect)]
  pureLayout as _ = map (,RationalRect 0 0 0 0) as

  -- | Runs the layout in an impure way returning a modified layout and the list
  -- of windows to their rectangles under a monad.
  runLayout :: (C l a) => [a] -> l -> W (l, [(a, RationalRect)])
  runLayout as l = return (l, pureLayout as l)

  -- | Handles a message in a pure way. Returns the new layout after handling
  -- the message.
  pureMessage :: Message -> l -> l
  pureMessage _ = id

  -- | Handles a message in an impure way.
  handleMessage :: Message -> l -> W l
  handleMessage m = return . pureMessage m

  readLayout :: String -> Maybe l
  default readLayout :: (Read l) => String -> Maybe l
  readLayout = readMaybe

  serializeLayout :: l -> String
  default serializeLayout :: (Show l) => l -> String
  serializeLayout = show

  description :: l -> String
  default description :: (Show l) => l -> String
  description = show

-- A Layout which hides the layout parameter under an existential type and
-- asserts the layout hidden can work with Window types.
data WindowLayout
  = forall l a. (LayoutClass l, C l a, a ~ Window) => WindowLayout l

runWindowLayout :: [Window] -> WindowLayout -> W (WindowLayout, [(Window, RationalRect)])
runWindowLayout as (WindowLayout l) = first WindowLayout <$> runLayout as l

handleWindowMessage :: Message -> WindowLayout -> W WindowLayout
handleWindowMessage m (WindowLayout l) = WindowLayout <$> handleMessage m l

-- | Using the 'Layout' as a witness, parse existentially wrapped windows
-- from a 'String'.
readWindowLayout :: WindowLayout -> String -> WindowLayout
readWindowLayout (WindowLayout l) s
  | (Just x) <- readLayout s =
      WindowLayout (asTypeOf x l)
readWindowLayout l _ = l

serializeWindowLayout :: WindowLayout -> String
serializeWindowLayout (WindowLayout l) = serializeLayout l

type ScreenId = ()

type ScreenDetail = ()

type Tag = String

newtype Window = Window (TypedIntPtr ())
  deriving (Eq, Ord, Show, Read)

type Wetterhorn = StablePtr (Context, State)

data Context = Context
  { ctxForeignInterface :: ForeignInterface,
    ctxConfig :: Config WindowLayout
  }

defaultConfig :: Config ()
defaultConfig =
  Config
    { keyHook = \_ -> return (),
      surfaceHook = \_ -> return (),
      layout = ()
    }

data Config l = Config
  { keyHook :: KeyEvent -> W (),
    surfaceHook :: SurfaceEvent -> W (),
    layout :: l
  }

data State = State
  { mapped :: StackSet ScreenId ScreenDetail Tag WindowLayout Window,
    allWindows :: Set Window
  }

initColdState :: WindowLayout -> IO State
initColdState l = return $ State (StackSet (Screen () () (Workspace "0" l (Stack [] []))) [] []) mempty

marshalState :: State -> String
marshalState (State mapped allWindows) =
  show
    ( mapLayout serializeWindowLayout mapped,
      allWindows
    )

demarshalState :: WindowLayout -> String -> State
demarshalState witness str = State mapped allWindows
  where
    (mapLayout (readWindowLayout witness) -> mapped, allWindows) = read str

newtype W a = W (ReaderT Context (StateT State IO) a)
  deriving (Functor, Applicative, Monad, MonadState State, MonadReader Context, MonadIO)

runW :: W a -> (Context, State) -> IO (a, State)
runW (W fn) (ctx, st) = runStateT (runReaderT fn ctx) st

wio :: IO a -> W a
wio = liftIO