aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <rahm@google.com>2024-03-21 16:34:22 -0600
committerJosh Rahm <rahm@google.com>2024-03-21 16:34:22 -0600
commit211fce2128121f3c9374442a13c5d756182a7cb1 (patch)
tree3db70b03f270d9ac9a4cac700e4bf21be33d8af1
parent15a7989977afaebd63e0d87a6eb1aeb735feddde (diff)
downloadwetterhorn-211fce2128121f3c9374442a13c5d756182a7cb1.tar.gz
wetterhorn-211fce2128121f3c9374442a13c5d756182a7cb1.tar.bz2
wetterhorn-211fce2128121f3c9374442a13c5d756182a7cb1.zip
Implement more stuff. Add DSL for binding to the new input handler.
-rw-r--r--package.yaml1
-rw-r--r--src/Config.hs53
-rw-r--r--src/Wetterhorn/Dsl/Bind.hs129
-rw-r--r--src/Wetterhorn/Dsl/Buttons.hsc229
-rw-r--r--src/Wetterhorn/Dsl/Input.hs (renamed from src/Wetterhorn/Core/Input.hs)64
-rw-r--r--src/Wetterhorn/Foreign/WlRoots.hs17
6 files changed, 442 insertions, 51 deletions
diff --git a/package.yaml b/package.yaml
index 48f839a..cd8023b 100644
--- a/package.yaml
+++ b/package.yaml
@@ -58,6 +58,7 @@ ghc-options:
- -XViewPatterns
- -XDerivingVia
- -XDisambiguateRecordFields
+- -XLambdaCase
- -fPIC
executables:
diff --git a/src/Config.hs b/src/Config.hs
index 5f09cbe..dab514b 100644
--- a/src/Config.hs
+++ b/src/Config.hs
@@ -1,13 +1,14 @@
module Config (config) where
-import Text.Printf
import Control.Monad.IO.Class
import Control.Monad.Loops
-import Wetterhorn.Core.KeyEvent qualified as KeyEvent
-import Wetterhorn.Core.Keys
+import Text.Printf
import Wetterhorn.Core.W
+import Wetterhorn.Dsl.Bind
+import Wetterhorn.Dsl.Input
import Wetterhorn.Keys.Macros
import Wetterhorn.Layout.Full
+import Control.Monad (unless)
config :: Config WindowLayout
config =
@@ -19,42 +20,28 @@ config =
},
layout = WindowLayout Full,
resetHook = do
- useKeysWithContinuation recordMacroContinuation $ do
- ev <- nextButtonOrKeyPress
-
- case ev of
- Right kp -> do
- bind kp (Mod1 .+ 'q') macroKeyBind
-
- bind kp (weak $ Mod1 .+ '@') replayMacroKeybind
+ useInputHandler $ do
+ ev <- nextInputEvent
- bind kp (Mod1 .+ 'r') (shellExec "wofi --show run")
+ bind ev (released btnLeft) $ do
+ wio $ putStrLn "Left Button Released!!"
- bind kp (Shift .+ Mod1 .+ 'R') requestHotReload
+ unless (isPressEvent ev) $ do
+ forwardEvent ev
+ continue
- bind kp (Mod1 .+ 't') (shellExec "alacritty")
+ bind ev (Shift .+ Mod1 .+ 'R') requestHotReload
- bind kp (Mod1 .+ 'n') (return () :: W ())
+ bind ev (Mod1 .+ 't') (shellExec "alacritty")
- bind kp (weak $ Mod1 .+ '∫') (shellExec "gxmessage hi")
+ bind ev (Mod1 .+ 'p') $ do
+ ev2 <- nextInputPressEvent
- bind kp (Mod1 .+ 'p') $ do
- str <-
- unfoldM
- ( do
- ke <- nextKeyPress
- return $
- if KeyEvent.codepoint ke == '\r'
- then Nothing
- else Just (KeyEvent.codepoint ke)
- )
- liftIO $ putStrLn $ "You input: " ++ str
- bind kp (str == "hello") $ do
- wio $ putStrLn "You Win! *\\o/*"
- liftIO $ putStrLn "You lose :("
+ bind ev2 (Mod1 .+ 'p') $ do
+ wio $ putStrLn "Test"
- forwardEvent kp
+ bind ev (Mod1 .+ btnLeft) $ do
+ wio $ putStrLn "Left Button Press!!"
- Left but ->
- liftIO $ putStrLn $ "ButtonEvent! " ++ (show but)
+ forwardEvent ev
}
diff --git a/src/Wetterhorn/Dsl/Bind.hs b/src/Wetterhorn/Dsl/Bind.hs
new file mode 100644
index 0000000..7bdb9de
--- /dev/null
+++ b/src/Wetterhorn/Dsl/Bind.hs
@@ -0,0 +1,129 @@
+-- | eDSL for the 'bind' function. The 'bind' function provides an easy way to
+-- bind certain actions to other actions.
+module Wetterhorn.Dsl.Bind
+ ( bind,
+ (.+),
+ MatchEvent (..),
+ Modifier (..),
+ released,
+ weak,
+ spy,
+ module X,
+ )
+where
+
+import Control.Monad
+import Control.Monad.Trans
+import Data.Bits
+import Data.Word
+import qualified Wetterhorn.Core.ButtonEvent as ButtonEvent
+import qualified Wetterhorn.Core.KeyEvent as KeyEvent
+import Wetterhorn.Core.W
+import Wetterhorn.Dsl.Buttons as X
+import Wetterhorn.Dsl.Input
+
+class MatchEvent m where
+ matches :: m -> InputEvent -> InputM Bool
+
+instance MatchEvent (InputEvent -> InputM Bool) where
+ matches = ($)
+
+instance MatchEvent Char where
+ matches ch (InputKeyEvent ke) = return $ KeyEvent.codepoint ke == ch
+ matches _ _ = return False
+
+instance MatchEvent Button where
+ matches (Button b) (InputButtonEvent be) =
+ return $ ButtonEvent.button be == b
+ matches _ _ = return False
+
+class InputAction m where
+ doAction :: m -> InputM ()
+
+instance (a ~ ()) => InputAction (W a) where
+ doAction = liftW
+
+instance (a ~ ()) => InputAction (InputM a) where
+ doAction = id
+
+-- | Enumeration of possible modifiers
+data Modifier = Shift | Lock | Control | Mod1 | Mod2 | Mod3 | Mod4 | Mod5
+ deriving (Eq, Ord, Show, Read, Enum, Bounded)
+
+-- | Converts a modifier to its associated mask.
+modifierToMask :: Modifier -> Word32
+modifierToMask m =
+ 1
+ `shiftL` case m of
+ Shift -> 0
+ Lock -> 1
+ Control -> 2
+ Mod1 -> 3
+ Mod2 -> 4
+ Mod3 -> 5
+ Mod4 -> 6
+ Mod5 -> 7
+
+released :: (MatchEvent m) => m -> InputEvent -> InputM Bool
+released me ev | not (isPressEvent ev) = matches me ev
+released _ _ = return False
+
+data MatchModifiers = MatchModifiers
+ { weakModifierMatch :: Bool,
+ modifierMask :: Word32,
+ baseMatch :: InputEvent -> InputM Bool
+ }
+
+instance MatchEvent MatchModifiers where
+ matches (MatchModifiers weak bits base) ev@(InputKeyEvent ke) = do
+ b <- base ev
+
+ return $
+ b
+ && ( (not weak && KeyEvent.modifiers ke == bits)
+ || (weak && (bits .&. KeyEvent.modifiers ke == bits))
+ )
+ matches (MatchModifiers weak bits base) ev = do
+ mods <- liftW getModifierState
+ b <- base ev
+
+ return $
+ b
+ && ( (not weak && mods == bits)
+ || (weak && (bits .&. mods == bits))
+ )
+
+class LiftMatchModifiers a where
+ toModifiers :: a -> MatchModifiers
+ default toModifiers :: (MatchEvent a) => a -> MatchModifiers
+ toModifiers = MatchModifiers False 0 . matches
+
+instance LiftMatchModifiers MatchModifiers where
+ toModifiers = id
+
+instance LiftMatchModifiers Char
+
+instance LiftMatchModifiers Button
+
+-- toModifiers ch = MatchModifiers False 0 (matches ch)
+
+(.+) :: (LiftMatchModifiers mods) => Modifier -> mods -> MatchModifiers
+(.+) modifier (toModifiers -> (MatchModifiers b mask base)) =
+ MatchModifiers b (mask .|. modifierToMask modifier) base
+
+infixr 9 .+
+
+-- | Like 'bind', but does not restart consume the event and start from the top,
+-- rather it continues the operation as normal.
+spy :: (MatchEvent match, InputAction action) => InputEvent -> match -> action -> InputM ()
+spy ev match action = do
+ matches' <- matches match ev
+ when matches' (doAction action)
+
+bind :: (MatchEvent match, InputAction action) => InputEvent -> match -> action -> InputM ()
+bind ev match action = do
+ matches' <- matches match ev
+ when matches' (doAction action >> continue)
+
+weak :: MatchModifiers -> MatchModifiers
+weak m = m {weakModifierMatch = True}
diff --git a/src/Wetterhorn/Dsl/Buttons.hsc b/src/Wetterhorn/Dsl/Buttons.hsc
new file mode 100644
index 0000000..c3e049c
--- /dev/null
+++ b/src/Wetterhorn/Dsl/Buttons.hsc
@@ -0,0 +1,229 @@
+module Wetterhorn.Dsl.Buttons where
+
+import Data.Word
+
+#include </usr/include/linux/input-event-codes.h>
+
+data Button = Button Word32
+
+btnMisc :: Button
+btnMisc = Button #const BTN_MISC
+
+btn0 :: Button
+btn0 = Button #const BTN_0
+
+btn1 :: Button
+btn1 = Button #const BTN_1
+
+btn2 :: Button
+btn2 = Button #const BTN_2
+
+btn3 :: Button
+btn3 = Button #const BTN_3
+
+btn4 :: Button
+btn4 = Button #const BTN_4
+
+btn5 :: Button
+btn5 = Button #const BTN_5
+
+btn6 :: Button
+btn6 = Button #const BTN_6
+
+btn7 :: Button
+btn7 = Button #const BTN_7
+
+btn8 :: Button
+btn8 = Button #const BTN_8
+
+btn9 :: Button
+btn9 = Button #const BTN_9
+
+btnMouse :: Button
+btnMouse = Button #const BTN_MOUSE
+
+btnLeft :: Button
+btnLeft = Button #const BTN_LEFT
+
+btnRight :: Button
+btnRight = Button #const BTN_RIGHT
+
+btnMiddle :: Button
+btnMiddle = Button #const BTN_MIDDLE
+
+btnSide :: Button
+btnSide = Button #const BTN_SIDE
+
+btnExtra :: Button
+btnExtra = Button #const BTN_EXTRA
+
+btnForward :: Button
+btnForward = Button #const BTN_FORWARD
+
+btnBack :: Button
+btnBack = Button #const BTN_BACK
+
+btnTask :: Button
+btnTask = Button #const BTN_TASK
+
+btnJoystick :: Button
+btnJoystick = Button #const BTN_JOYSTICK
+
+btnTrigger :: Button
+btnTrigger = Button #const BTN_TRIGGER
+
+btnThumb :: Button
+btnThumb = Button #const BTN_THUMB
+
+btnThumb2 :: Button
+btnThumb2 = Button #const BTN_THUMB2
+
+btnTop :: Button
+btnTop = Button #const BTN_TOP
+
+btnTop2 :: Button
+btnTop2 = Button #const BTN_TOP2
+
+btnPinkie :: Button
+btnPinkie = Button #const BTN_PINKIE
+
+btnBase :: Button
+btnBase = Button #const BTN_BASE
+
+btnBase2 :: Button
+btnBase2 = Button #const BTN_BASE2
+
+btnBase3 :: Button
+btnBase3 = Button #const BTN_BASE3
+
+btnBase4 :: Button
+btnBase4 = Button #const BTN_BASE4
+
+btnBase5 :: Button
+btnBase5 = Button #const BTN_BASE5
+
+btnBase6 :: Button
+btnBase6 = Button #const BTN_BASE6
+
+btnDead :: Button
+btnDead = Button #const BTN_DEAD
+
+btnGamepad :: Button
+btnGamepad = Button #const BTN_GAMEPAD
+
+btnSouth :: Button
+btnSouth = Button #const BTN_SOUTH
+
+btnA :: Button
+btnA = Button #const BTN_A
+
+btnEast :: Button
+btnEast = Button #const BTN_EAST
+
+btnB :: Button
+btnB = Button #const BTN_B
+
+btnC :: Button
+btnC = Button #const BTN_C
+
+btnNorth :: Button
+btnNorth = Button #const BTN_NORTH
+
+btnX :: Button
+btnX = Button #const BTN_X
+
+btnWest :: Button
+btnWest = Button #const BTN_WEST
+
+btnY :: Button
+btnY = Button #const BTN_Y
+
+btnZ :: Button
+btnZ = Button #const BTN_Z
+
+btnTl :: Button
+btnTl = Button #const BTN_TL
+
+btnTr :: Button
+btnTr = Button #const BTN_TR
+
+btnTl2 :: Button
+btnTl2 = Button #const BTN_TL2
+
+btnTr2 :: Button
+btnTr2 = Button #const BTN_TR2
+
+btnSelect :: Button
+btnSelect = Button #const BTN_SELECT
+
+btnStart :: Button
+btnStart = Button #const BTN_START
+
+btnMode :: Button
+btnMode = Button #const BTN_MODE
+
+btnThumbl :: Button
+btnThumbl = Button #const BTN_THUMBL
+
+btnThumbr :: Button
+btnThumbr = Button #const BTN_THUMBR
+
+btnDigi :: Button
+btnDigi = Button #const BTN_DIGI
+
+btnToolPen :: Button
+btnToolPen = Button #const BTN_TOOL_PEN
+
+btnToolRubber :: Button
+btnToolRubber = Button #const BTN_TOOL_RUBBER
+
+btnToolBrush :: Button
+btnToolBrush = Button #const BTN_TOOL_BRUSH
+
+btnToolPencil :: Button
+btnToolPencil = Button #const BTN_TOOL_PENCIL
+
+btnToolAirbrush :: Button
+btnToolAirbrush = Button #const BTN_TOOL_AIRBRUSH
+
+btnToolFinger :: Button
+btnToolFinger = Button #const BTN_TOOL_FINGER
+
+btnToolMouse :: Button
+btnToolMouse = Button #const BTN_TOOL_MOUSE
+
+btnToolLens :: Button
+btnToolLens = Button #const BTN_TOOL_LENS
+
+btnToolQuinttap :: Button
+btnToolQuinttap = Button #const BTN_TOOL_QUINTTAP
+
+btnStylus3 :: Button
+btnStylus3 = Button #const BTN_STYLUS3
+
+btnTouch :: Button
+btnTouch = Button #const BTN_TOUCH
+
+btnStylus :: Button
+btnStylus = Button #const BTN_STYLUS
+
+btnStylus2 :: Button
+btnStylus2 = Button #const BTN_STYLUS2
+
+btnToolDoubletap :: Button
+btnToolDoubletap = Button #const BTN_TOOL_DOUBLETAP
+
+btnToolTripletap :: Button
+btnToolTripletap = Button #const BTN_TOOL_TRIPLETAP
+
+btnToolQuadtap :: Button
+btnToolQuadtap = Button #const BTN_TOOL_QUADTAP
+
+btnWheel :: Button
+btnWheel = Button #const BTN_WHEEL
+
+btnGearDown :: Button
+btnGearDown = Button #const BTN_GEAR_DOWN
+
+btnGearUp :: Button
+btnGearUp = Button #const BTN_GEAR_UP
diff --git a/src/Wetterhorn/Core/Input.hs b/src/Wetterhorn/Dsl/Input.hs
index d3fbb0c..55f1a5b 100644
--- a/src/Wetterhorn/Core/Input.hs
+++ b/src/Wetterhorn/Dsl/Input.hs
@@ -1,26 +1,33 @@
-module Wetterhorn.Core.Input
- ( forwardEvent,
+module Wetterhorn.Dsl.Input
+ ( InputM,
+ InputEvent (..),
+ forwardEvent,
forwardKey,
whenKeyEvent,
whenButtonEvent,
- InputM,
- InputEvent,
+ useInputHandler,
+ unwrap,
+ filterEvent,
+ isPressEvent,
+ nextInputEventThat,
nextInputPressEvent,
continue,
- unwrap,
nextInputEvent,
+ getModifierState,
)
where
-import Control.Monad (void)
+import Control.Monad (forever, void)
import Control.Monad.Cont (MonadCont)
import Control.Monad.RWS (MonadIO, MonadReader (ask), MonadState (get), MonadTrans (lift), RWST, execRWST, modify)
import Control.Monad.Trans.Cont
+import Data.Maybe (fromMaybe)
+import Data.Word (Word32)
import qualified Wetterhorn.Core.ButtonEvent as ButtonEvent
import qualified Wetterhorn.Core.KeyEvent as KeyEvent
import Wetterhorn.Core.W (W (..))
import qualified Wetterhorn.Core.W as W
-import Wetterhorn.Foreign.WlRoots (wlrSeatKeyboardNotifyKey, wlrSeatSetKeyboard)
+import Wetterhorn.Foreign.WlRoots (guardNull, wlrKeyboardGetModifiers, wlrSeatGetKeyboard, wlrSeatKeyboardNotifyKey, wlrSeatSetKeyboard)
-- | Union of event types.
data InputEvent
@@ -90,20 +97,45 @@ unwrap :: Maybe a -> InputM a
unwrap (Just val) = return val
unwrap Nothing = continue
+-- | Call in the reset handler with the InputM handler you wolud like to use.
+useInputHandler :: InputM () -> W ()
+useInputHandler (forever -> top@(InputM ctop)) = do
+ void $ execRWST (runContT ctop return) (InputContext top) ()
+
-- | Returns the next input event that's either a kep press or a button press.
nextInputPressEvent :: InputM InputEvent
-nextInputPressEvent = do
+nextInputPressEvent = nextInputEventThat isPressEvent
+
+nextInputEventThat :: (InputEvent -> Bool) -> InputM InputEvent
+nextInputEventThat fn =
nextInputEvent
- >>= ( \ie -> case ie of
- InputButtonEvent be
- | ButtonEvent.state be == ButtonEvent.ButtonPressed ->
- return ie
- InputKeyEvent ke
- | KeyEvent.state ke == KeyEvent.KeyPressed ->
- return ie
- _ -> nextInputEvent
+ >>= ( \ie ->
+ if fn ie
+ then return ie
+ else forwardEvent ie >> nextInputEventThat fn
)
+isPressEvent :: InputEvent -> Bool
+isPressEvent (InputButtonEvent be)
+ | ButtonEvent.state be == ButtonEvent.ButtonPressed =
+ True
+isPressEvent (InputKeyEvent ke)
+ | KeyEvent.state ke == KeyEvent.KeyPressed =
+ True
+isPressEvent _ = False
+
+-- | Returns the event only if it matches the filter. If it does not match the
+-- filter, execution resets to the top.
+filterEvent :: (InputEvent -> Bool) -> InputEvent -> InputM InputEvent
+filterEvent fn ev | fn ev = return ev
+filterEvent _ _ = continue
+
+getModifierState :: W Word32
+getModifierState = do
+ seat <- W.getSeat
+ keyboard <- W.wio $ wlrSeatGetKeyboard seat
+ maybe (return 0) (W.wio . wlrKeyboardGetModifiers) (guardNull keyboard)
+
-- | Gets the next input event.
nextInputEvent :: InputM InputEvent
nextInputEvent =
diff --git a/src/Wetterhorn/Foreign/WlRoots.hs b/src/Wetterhorn/Foreign/WlRoots.hs
index 05ed3d6..0581b77 100644
--- a/src/Wetterhorn/Foreign/WlRoots.hs
+++ b/src/Wetterhorn/Foreign/WlRoots.hs
@@ -1,8 +1,10 @@
module Wetterhorn.Foreign.WlRoots where
-import Foreign (IntPtr, Ptr, Word32, intPtrToPtr, ptrToIntPtr)
+import Foreign (IntPtr, Ptr, Word32, intPtrToPtr, ptrToIntPtr, nullPtr)
import Text.Read
+data WlrKeyboard
+
data WlrPointer
data WlrPointerButtonEvent
@@ -47,7 +49,18 @@ instance ForeignSurface WlrXdgSurface where
instance ForeignSurface WlrXWaylandSurface where
toSurface = XWaylandSurface
-foreign import ccall "wlr_seat_set_keyboard" wlrSeatSetKeyboard :: Ptr WlrSeat -> Ptr WlrInputDevice -> IO ()
+guardNull :: Ptr a -> Maybe (Ptr a)
+guardNull p | p == nullPtr = Nothing
+guardNull p = Just p
+
+foreign import ccall "wlr_seat_set_keyboard" wlrSeatSetKeyboard ::
+ Ptr WlrSeat -> Ptr WlrInputDevice -> IO ()
+
+foreign import ccall "wlr_seat_get_keyboard" wlrSeatGetKeyboard ::
+ Ptr WlrSeat -> IO (Ptr WlrKeyboard)
+
+foreign import ccall "wlr_keyboard_get_modifiers" wlrKeyboardGetModifiers ::
+ Ptr WlrKeyboard -> IO Word32
foreign import ccall "wlr_seat_keyboard_notify_key"
wlrSeatKeyboardNotifyKey ::