-- Module for the KeyFeed monad. -- -- The KeyFeed Monad abstracts control flow over a stream of key presses in RDE. module Rahm.Desktop.Keys.KeyFeed where import Control.Monad (void, when) import Control.Monad.State (MonadTrans (lift), StateT (..), evalStateT, modify') import Control.Monad.Trans.Maybe (MaybeT (MaybeT, runMaybeT)) import Data.List.Safe (head, last) import Linear.V (reifyDim) import Rahm.Desktop.Common (Xish (..)) import Rahm.Desktop.Submap (mapNextStringWithKeysym) import Rahm.Desktop.XMobarLog.PendingBuffer (addStringToPendingBuffer, pushAddPendingBuffer, pushPendingBuffer) import XMonad import Prelude hiding (head, last) -- A key is a mask and a keysym. The string is the string returned from -- XLookupString. type Key = (KeyMask, KeySym, String) -- A list of keys type KeyString = [Key] -- List of actions which return a key. type KeyStream = [MaybeT X Key] newtype KeyFeed a = KeyFeed (StateT KeyStream (MaybeT X) a) deriving (Functor, Applicative, Monad) instance Xish KeyFeed where liftFromX = KeyFeed . lift . lift finallyX (KeyFeed mt) (KeyFeed err) = KeyFeed $ do r <- recover mt case r of Nothing -> do err let (KeyFeed r) = feedFail in r Just a -> err >> pure a where recover fn = StateT $ \s -> MaybeT $ do r <- runMaybeT (runStateT fn s) pure $ Just $ case r of Nothing -> (Nothing, s) Just (a, s') -> (Just a, s') -- Executes a KeyFeed, returning a MaybeT of the result runKeyFeed :: KeyFeed a -> MaybeT X a runKeyFeed = runKeyFeedWithStartingKeys [] -- Executes a KeyFeed, evaluating down to an X (Maybe a) runKeyFeedX :: KeyFeed a -> X (Maybe a) runKeyFeedX = runMaybeT . runKeyFeed -- Exceutes a KeyFeed. Does not evaluate the results. execKeyFeed :: KeyFeed a -> X () execKeyFeed = void . runMaybeT . runKeyFeed -- Executes a KeyFeed, evaluating the keystring first, then evaluating actual -- key presses. runKeyFeedWithStartingKeys :: KeyString -> KeyFeed a -> MaybeT X a runKeyFeedWithStartingKeys st (KeyFeed r) = evalStateT r $ (map return st ++) $ repeat $ do mapNextStringWithKeysym $ \m s st -> return (m, s, st) -- Executes a KeyFeed only on the given key presses. runKeyFeedWithKeys :: KeyString -> KeyFeed a -> MaybeT X a runKeyFeedWithKeys st (KeyFeed r) = evalStateT r (toKeyStream st) -- Executes a function on the next key read and returns the result. readNextKey :: ((KeyMask, KeySym, String) -> KeyFeed a) -> KeyFeed a readNextKey fn = KeyFeed $ do keyList <- get nextKeyFn <- upMaybe $ head keyList nextKey@(_, sym, str) <- lift nextKeyFn -- escape always ends a key feed. when (sym == xK_Escape) $ do let (KeyFeed r) = feedFail in r modify' tail let (KeyFeed r) = liftFromX (addStringToPendingBuffer str) >> fn nextKey in r where upMaybe :: Maybe a -> StateT KeyStream (MaybeT X) a upMaybe m = lift $ MaybeT (return m) skipKey :: KeyFeed () skipKey = KeyFeed $ modify' tail -- Lifts a Maybe int o a KeyFeed. hoistMaybe :: Maybe a -> KeyFeed a hoistMaybe = KeyFeed . lift . MaybeT . return -- Lifts a Maybe int o a KeyFeed. hoistMaybeT :: MaybeT X a -> KeyFeed a hoistMaybeT = KeyFeed . lift -- Fails a KeyFeed action. feedFail :: KeyFeed a feedFail = KeyFeed $ lift (MaybeT $ return Nothing) -- Lifts an X action into a KeyFeed action. liftXToFeed :: X a -> KeyFeed a liftXToFeed = KeyFeed . lift . lift -- Lifts an X action into a KeyFeed action. liftXMaybe :: X (Maybe a) -> KeyFeed a liftXMaybe = KeyFeed . lift . MaybeT -- Removes a maybe and pushes it into the KeyFeed monad. If the maybe is -- Nothing, the KeyFeed fails. absorbMaybe :: KeyFeed (Maybe a) -> KeyFeed a absorbMaybe fn = hoistMaybe =<< fn -- Inserts keys to the beginnig of the KeyFeed buffer. pushKeys :: KeyString -> KeyFeed () pushKeys ks = KeyFeed $ modify' (map return ks ++) -- Inserts a single key to the beginning of the KeyFeed buffer. pushKey :: (KeyMask, KeySym, String) -> KeyFeed () pushKey = pushKeys . (: []) -- Reads the next key, but does not consume it from the read buffer. peekKey :: ((KeyMask, KeySym, String) -> KeyFeed a) -> KeyFeed a peekKey fn = KeyFeed $ do keyList <- get nextKeyFn <- lift . MaybeT . return $ head keyList nextKey@(_, sym, str) <- lift nextKeyFn modify' ((return nextKey :) . tail) let (KeyFeed r) = fn nextKey in r -- reifiedKey <- lift nextKeyFn -- modify' (\l -> reifiedKey : tail l) -- return reifiedKey -- Converts a string of keys to a stream of keys. toKeyStream :: KeyString -> KeyStream toKeyStream = map return