module Montis.Standard.Bindings.MagicKey where import Control.Monad (unless, when) import Data.Data (Typeable) import Data.Word (Word32) import Montis.Core import Montis.Standard.Bindings (forwardKeyToSeat) import Montis.Standard.Bindings.Dsl data MagicKeyConfig where MagicKeyConfig :: { magicKeyCodepoint :: Char } -> MagicKeyConfig deriving (Typeable) data MagicKeyState where MagicKeyState :: { magicActive :: Bool, magicPressedAt :: Maybe Word32, magicPressEvent :: Maybe KeyEvent, magicInterrupted :: Bool } -> MagicKeyState deriving (Typeable) data Magic = Magic instance StateExtension MagicKeyState where initialValue = MagicKeyState False Nothing Nothing False marshalExtension _ = Nothing demarshalExtension _ = Nothing instance InputMatch Magic where matches _ _ = do MagicKeyState {magicActive} <- xStateGet return magicActive markInterrupted :: MagicKeyState -> MagicKeyState markInterrupted st@MagicKeyState {magicActive} | magicActive = st {magicInterrupted = True} | otherwise = st emitTap :: KeyEvent -> KeyEvent -> Montis () emitTap pressEv releaseEv = do forwardKeyToSeat pressEv forwardKeyToSeat releaseEv handleMagicKey :: Char -> KeyEvent -> Montis Bool handleMagicKey magicKey ev | keyEvent_codepoint ev == magicKey = case keyEvent_state ev of KeyPressed -> do xStatePut $ MagicKeyState True (Just (keyEvent_timeMs ev)) (Just ev) False return True KeyReleased -> do MagicKeyState _ pressAt pressEv interrupted <- xStateGet xStatePut (MagicKeyState False Nothing Nothing False) case (pressAt, pressEv) of (Just t0, Just pev) -> do let t1 = keyEvent_timeMs ev if not interrupted && t1 >= t0 && t1 - t0 <= 200 then emitTap pev ev >> return True else return True _ -> return True | otherwise = do when (keyEvent_state ev == KeyPressed) $ xStateModify markInterrupted return False instance ConfigModule Montis MagicKeyConfig where alterConfig cfg c = let okh = keyHook (startingHooks c) in c { startingHooks = (startingHooks c) { keyHook = \ev -> do handled <- handleMagicKey (magicKeyCodepoint cfg) ev unless handled (okh ev) } }