{-|
Module      : KMonad.App.BEnv
Description : Implementation details behind 'Button'
Copyright   : (c) David Janssen, 2019
License     : MIT
Maintainer  : janssen.dhj@gmail.com
Stability   : experimental
Portability : portable

When running KMonad, we need to keep track the last switchstate of a 'Button',
because we only allowing switching, (we have to filter out repeated 'Press' or
'Release' events). Additionally, we keep track of what 'Keycode' a button is
bound to, to provide the 'myBinding' functionality from 'MonadK'.

-}

module KMonad.App.BEnv
  ( BEnv(..)
  , HasBEnv(..)
  , initBEnv
  , runBEnv
  )
where

import KMonad.Prelude

import KMonad.Action
import KMonad.Button
import KMonad.Keyboard

--------------------------------------------------------------------------------
-- $benv
--
-- When running KMonad, a button also keeps track of what keycode it's bound to,
-- and what its last switch was. This is used to provide the 'myBinding' feature
-- of MonadK, and the invariant that switches always alternate (there is no
-- press-press or release-release).

-- | The configuration of a 'Button' with some additional state to keep track of
-- the last 'Switch'
data BEnv = BEnv
  { BEnv -> Button
_beButton   :: !Button        -- ^ The configuration for this button
  , BEnv -> Keycode
_binding    :: !Keycode       -- ^ The 'Keycode' to which this button is bound
  , BEnv -> MVar Switch
_lastSwitch :: !(MVar Switch) -- ^ State to keep track of last manipulation
  }
makeClassy ''BEnv

instance HasButton BEnv where button :: (Button -> f Button) -> BEnv -> f BEnv
button = (Button -> f Button) -> BEnv -> f BEnv
forall c. HasBEnv c => Lens' c Button
beButton

-- | Initialize a 'BEnv', note that a key is always initialized in an unpressed
-- state.
initBEnv :: MonadIO m => Button -> Keycode -> m BEnv
initBEnv :: Button -> Keycode -> m BEnv
initBEnv b :: Button
b c :: Keycode
c = Button -> Keycode -> MVar Switch -> BEnv
BEnv Button
b Keycode
c (MVar Switch -> BEnv) -> m (MVar Switch) -> m BEnv
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Switch -> m (MVar Switch)
forall (m :: * -> *) a. MonadIO m => a -> m (MVar a)
newMVar Switch
Release

-- | Try to switch a 'BEnv'. This only does something if the 'Switch' is
-- different from the 'lastSwitch' field. I.e. pressing a pressed button or
-- releasing a released button does nothing.
runBEnv :: MonadUnliftIO m => BEnv -> Switch -> m (Maybe Action)
runBEnv :: BEnv -> Switch -> m (Maybe Action)
runBEnv b :: BEnv
b a :: Switch
a =
  MVar Switch
-> (Switch -> m (Switch, Maybe Action)) -> m (Maybe Action)
forall (m :: * -> *) a b.
MonadUnliftIO m =>
MVar a -> (a -> m (a, b)) -> m b
modifyMVar (BEnv
bBEnv -> Getting (MVar Switch) BEnv (MVar Switch) -> MVar Switch
forall s a. s -> Getting a s a -> a
^.Getting (MVar Switch) BEnv (MVar Switch)
forall c. HasBEnv c => Lens' c (MVar Switch)
lastSwitch) ((Switch -> m (Switch, Maybe Action)) -> m (Maybe Action))
-> (Switch -> m (Switch, Maybe Action)) -> m (Maybe Action)
forall a b. (a -> b) -> a -> b
$ \l :: Switch
l -> (Switch, Maybe Action) -> m (Switch, Maybe Action)
forall (f :: * -> *) a. Applicative f => a -> f a
pure ((Switch, Maybe Action) -> m (Switch, Maybe Action))
-> (Switch, Maybe Action) -> m (Switch, Maybe Action)
forall a b. (a -> b) -> a -> b
$ case (Switch
a, Switch
l) of
    (Press, Release) -> (Switch
Press,   Action -> Maybe Action
forall a. a -> Maybe a
Just (Action -> Maybe Action) -> Action -> Maybe Action
forall a b. (a -> b) -> a -> b
$ BEnv
bBEnv -> Getting Action BEnv Action -> Action
forall s a. s -> Getting a s a -> a
^.Getting Action BEnv Action
forall c. HasButton c => Lens' c Action
pressAction)
    (Release, Press) -> (Switch
Release, Action -> Maybe Action
forall a. a -> Maybe a
Just (Action -> Maybe Action) -> Action -> Maybe Action
forall a b. (a -> b) -> a -> b
$ BEnv
bBEnv -> Getting Action BEnv Action -> Action
forall s a. s -> Getting a s a -> a
^.Getting Action BEnv Action
forall c. HasButton c => Lens' c Action
releaseAction)
    _                -> (Switch
a,       Maybe Action
forall a. Maybe a
Nothing)