aboutsummaryrefslogtreecommitdiff
path: root/montis/src/Montis/Standard/Bindings/Dsl.hs
blob: 73c26c892f5ab6f9a7cb519dc604b9321477fcd6 (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
{-# LANGUAGE ImpredicativeTypes #-}

-- | Small DSL for defining key bindings and nested sub-maps.
module Montis.Standard.Bindings.Dsl where

import Control.Monad.Loops (anyM)
import Data.Bits (shiftL, (.&.))
import Data.Word (Word32)
import Montis.Core (ButtonEvent (buttonEvent_button, buttonEvent_modifiers, buttonEvent_state), ButtonState (ButtonPressed), Config, KeyEvent (keyEvent_codepoint, keyEvent_modifiers), Montis (..), install)
import Montis.Standard.Bindings
import Montis.Standard.Bindings.Button

-- | A predicate over a key event.
class InputMatch a where
  matches :: a -> InputEvent -> Montis Bool

instance InputMatch Char where
  matches c (InputEvent (Left k)) = return $ keyEvent_codepoint k == c
  matches _ _ = return False

-- | Modifier matches using wlroots bit positions.
data Modifier = Mod1 | Mod2 | Mod3 | Mod4 | Mod5 | Any | None

instance InputMatch Modifier where
  matches Mod1 ev = return $ inputModifiers ev .&. (1 `shiftL` 3) /= 0
  matches Mod2 ev = return $ inputModifiers ev .&. (1 `shiftL` 4) /= 0
  matches Mod3 ev = return $ inputModifiers ev .&. (1 `shiftL` 5) /= 0
  matches Mod4 ev = return $ inputModifiers ev .&. (1 `shiftL` 6) /= 0
  matches Mod5 ev = return $ inputModifiers ev .&. (1 `shiftL` 7) /= 0
  matches Any _ = return True
  matches None ev = return $ inputModifiers ev == 0

instance InputMatch Button where
  matches (Button b) (InputEvent (Right ev)) =
    return $ buttonEvent_button ev == b && buttonEvent_state ev == ButtonPressed
  matches _ _ = return False

inputModifiers :: InputEvent -> Word32
inputModifiers (InputEvent (Left k)) = keyEvent_modifiers k
inputModifiers (InputEvent (Right b)) = buttonEvent_modifiers b

-- | Actions run when a binding matches. Each action reports handled status.
class Action b where
  run :: b -> Montis Bool

instance Action (Montis ()) where
  run a = a >> return True

instance Action (InputEvent -> Montis ()) where
  run f = subkeys (\e -> f e >> return True)

instance Action (KeyEvent -> Montis ()) where
  run f =
    subkeys
      ( \case
          (InputEvent (Left k)) -> f k >> return True
          _ -> return True
      )

instance Action (ButtonEvent -> Montis ()) where
  run f =
    subkeys
      ( \case
          (InputEvent (Right b)) -> f b >> return True
          _ -> return True
      )

-- | Submap to the first binding that matches.
instance Action [Binding] where
  run bs = subkeys $ \ev ->
    anyM
      ( \(Bind k a) -> do
          m <- matches k ev
          if m then run a >> return True else return False
      )
      bs

instance Action (Montis [Binding]) where
  run mbs = do
    bs <- mbs
    run bs

-- | A single binding from a matcher to an action.
data Binding where
  Bind :: forall k b. (InputMatch k, Action b) => k -> b -> Binding

-- | A matcher for key + modifier chords.
data ChordMatch where
  ChordMatch :: forall k2 k1. (InputMatch k2, InputMatch k1) => k2 -> k1 -> ChordMatch

(.+) :: (InputMatch k1, InputMatch k2) => k1 -> k2 -> ChordMatch
(.+) = ChordMatch

instance InputMatch ChordMatch where
  matches (ChordMatch a b) ev = do
    ma <- matches a ev
    mb <- matches b ev
    return (ma && mb)

-- | Installs the bindings into a config as the starting key hook.
withBindings :: [Binding] -> Config Montis -> Config Montis
withBindings bs =
  install
    ( InputsConfig
        ( \ev ->
            anyM
              ( \(Bind k a) -> do
                  m <- matches k ev
                  if m then run a >> return True else return False
              )
              bs
        )
    )