{-# LANGUAGE ScopedTypeVariables #-}
module Control.Debounce.Internal (
DebounceSettings (..),
DebounceEdge (..),
leadingEdge,
leadingMuteEdge,
trailingEdge,
trailingDelayEdge,
mkDebounceInternal,
) where
import Control.Concurrent (forkIO)
import Control.Concurrent.MVar (
MVar,
newEmptyMVar,
putMVar,
tryPutMVar,
tryTakeMVar,
)
import Control.Exception (SomeException, handle, mask_)
import Control.Monad (void, when)
import GHC.Clock (getMonotonicTimeNSec)
import GHC.Conc (atomically, newTVarIO, readTVar, readTVarIO, writeTVar)
import GHC.Conc.Sync (labelThread)
data DebounceSettings = DebounceSettings
{ DebounceSettings -> Int
debounceFreq :: Int
, DebounceSettings -> IO ()
debounceAction :: IO ()
, DebounceSettings -> DebounceEdge
debounceEdge :: DebounceEdge
, DebounceSettings -> String
debounceThreadName :: String
}
data DebounceEdge
=
Leading
|
LeadingMute
|
Trailing
|
TrailingDelay
deriving (Int -> DebounceEdge -> ShowS
[DebounceEdge] -> ShowS
DebounceEdge -> String
(Int -> DebounceEdge -> ShowS)
-> (DebounceEdge -> String)
-> ([DebounceEdge] -> ShowS)
-> Show DebounceEdge
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DebounceEdge -> ShowS
showsPrec :: Int -> DebounceEdge -> ShowS
$cshow :: DebounceEdge -> String
show :: DebounceEdge -> String
$cshowList :: [DebounceEdge] -> ShowS
showList :: [DebounceEdge] -> ShowS
Show, DebounceEdge -> DebounceEdge -> Bool
(DebounceEdge -> DebounceEdge -> Bool)
-> (DebounceEdge -> DebounceEdge -> Bool) -> Eq DebounceEdge
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: DebounceEdge -> DebounceEdge -> Bool
== :: DebounceEdge -> DebounceEdge -> Bool
$c/= :: DebounceEdge -> DebounceEdge -> Bool
/= :: DebounceEdge -> DebounceEdge -> Bool
Eq)
leadingEdge :: DebounceEdge
leadingEdge :: DebounceEdge
leadingEdge = DebounceEdge
Leading
leadingMuteEdge :: DebounceEdge
leadingMuteEdge :: DebounceEdge
leadingMuteEdge = DebounceEdge
LeadingMute
trailingEdge :: DebounceEdge
trailingEdge :: DebounceEdge
trailingEdge = DebounceEdge
Trailing
trailingDelayEdge :: DebounceEdge
trailingDelayEdge :: DebounceEdge
trailingDelayEdge = DebounceEdge
TrailingDelay
mkDebounceInternal
:: MVar () -> (Int -> IO ()) -> DebounceSettings -> IO (IO ())
mkDebounceInternal :: MVar () -> (Int -> IO ()) -> DebounceSettings -> IO (IO ())
mkDebounceInternal MVar ()
baton Int -> IO ()
delayFn (DebounceSettings Int
freq IO ()
action DebounceEdge
edge String
name) =
case DebounceEdge
edge of
DebounceEdge
Leading -> MVar () -> IO ()
leadingDebounce (MVar () -> IO ()) -> IO (MVar ()) -> IO (IO ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO (MVar ())
forall a. IO (MVar a)
newEmptyMVar
DebounceEdge
LeadingMute -> IO () -> IO (IO ())
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure IO ()
leadingMuteDebounce
DebounceEdge
Trailing -> IO () -> IO (IO ())
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure IO ()
trailingDebounce
DebounceEdge
TrailingDelay -> TVar Word64 -> IO ()
trailingDelayDebounce (TVar Word64 -> IO ()) -> IO (TVar Word64) -> IO (IO ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Word64 -> IO (TVar Word64)
forall a. a -> IO (TVar a)
newTVarIO Word64
forall a. Bounded a => a
minBound
where
leadingDebounce :: MVar () -> IO ()
leadingDebounce MVar ()
trigger = do
Maybe ()
success <- MVar () -> IO (Maybe ())
forall a. MVar a -> IO (Maybe a)
tryTakeMVar MVar ()
baton
case Maybe ()
success of
Maybe ()
Nothing -> IO Bool -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO Bool -> IO ()) -> IO Bool -> IO ()
forall a b. (a -> b) -> a -> b
$ MVar () -> () -> IO Bool
forall a. MVar a -> a -> IO Bool
tryPutMVar MVar ()
trigger ()
Just () -> do
IO (Maybe ()) -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO (Maybe ()) -> IO ()) -> IO (Maybe ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ MVar () -> IO (Maybe ())
forall a. MVar a -> IO (Maybe a)
tryTakeMVar MVar ()
trigger
IO () -> IO ()
forkAndLabel IO ()
loop
where
loop :: IO ()
loop = do
IO () -> IO ()
ignoreExc IO ()
action
Int -> IO ()
delayFn Int
freq
Maybe ()
isTriggered <- MVar () -> IO (Maybe ())
forall a. MVar a -> IO (Maybe a)
tryTakeMVar MVar ()
trigger
case Maybe ()
isTriggered of
Maybe ()
Nothing -> MVar () -> () -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar ()
baton ()
Just () -> IO ()
loop
leadingMuteDebounce :: IO ()
leadingMuteDebounce = do
Maybe ()
success <- MVar () -> IO (Maybe ())
forall a. MVar a -> IO (Maybe a)
tryTakeMVar MVar ()
baton
case Maybe ()
success of
Maybe ()
Nothing -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Just () ->
IO () -> IO ()
forkAndLabel (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
IO () -> IO ()
ignoreExc IO ()
action
Int -> IO ()
delayFn Int
freq
MVar () -> () -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar ()
baton ()
trailingDebounce :: IO ()
trailingDebounce = do
Maybe ()
success <- MVar () -> IO (Maybe ())
forall a. MVar a -> IO (Maybe a)
tryTakeMVar MVar ()
baton
case Maybe ()
success of
Maybe ()
Nothing -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Just () ->
IO () -> IO ()
forkAndLabel (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
Int -> IO ()
delayFn Int
freq
IO () -> IO ()
ignoreExc IO ()
action
MVar () -> () -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar ()
baton ()
trailingDelayDebounce :: TVar Word64 -> IO ()
trailingDelayDebounce TVar Word64
timeTVar = do
Word64
now <- IO Word64
getMonotonicTimeNSec
Maybe ()
success <- MVar () -> IO (Maybe ())
forall a. MVar a -> IO (Maybe a)
tryTakeMVar MVar ()
baton
case Maybe ()
success of
Maybe ()
Nothing -> STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
Word64
oldTime <- TVar Word64 -> STM Word64
forall a. TVar a -> STM a
readTVar TVar Word64
timeTVar
Bool -> STM () -> STM ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Word64
oldTime Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
< Word64
now) (STM () -> STM ()) -> STM () -> STM ()
forall a b. (a -> b) -> a -> b
$ TVar Word64 -> Word64 -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar Word64
timeTVar Word64
now
Just () -> do
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar Word64 -> Word64 -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar Word64
timeTVar Word64
now
IO () -> IO ()
forkAndLabel (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ Int -> IO ()
loop Int
freq
where
loop :: Int -> IO ()
loop Int
delay = do
Int -> IO ()
delayFn Int
delay
Word64
lastTrigger <- TVar Word64 -> IO Word64
forall a. TVar a -> IO a
readTVarIO TVar Word64
timeTVar
Word64
now <- IO Word64
getMonotonicTimeNSec
let diff :: Int
diff = Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word64
now Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
lastTrigger) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
1000
shouldWait :: Bool
shouldWait = Int
diff Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
freq
if Bool
shouldWait
then Int -> IO ()
loop (Int -> IO ()) -> Int -> IO ()
forall a b. (a -> b) -> a -> b
$ Int
freq Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
diff
else do
IO () -> IO ()
ignoreExc IO ()
action
Word64
timeAfterAction <- TVar Word64 -> IO Word64
forall a. TVar a -> IO a
readTVarIO TVar Word64
timeTVar
let wasTriggered :: Bool
wasTriggered = Word64
timeAfterAction Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
> Word64
now
if Bool
wasTriggered
then do
Word64
updatedNow <- IO Word64
getMonotonicTimeNSec
let newDiff :: Int
newDiff = Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word64
updatedNow Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
timeAfterAction) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
1000
Int -> IO ()
loop (Int -> IO ()) -> Int -> IO ()
forall a b. (a -> b) -> a -> b
$ Int
freq Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
newDiff
else MVar () -> () -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar ()
baton ()
forkAndLabel :: IO () -> IO ()
forkAndLabel IO ()
act = do
ThreadId
tid <- IO ThreadId -> IO ThreadId
forall a. IO a -> IO a
mask_ (IO ThreadId -> IO ThreadId) -> IO ThreadId -> IO ThreadId
forall a b. (a -> b) -> a -> b
$ IO () -> IO ThreadId
forkIO IO ()
act
ThreadId -> String -> IO ()
labelThread ThreadId
tid String
name
ignoreExc :: IO () -> IO ()
ignoreExc :: IO () -> IO ()
ignoreExc = (SomeException -> IO ()) -> IO () -> IO ()
forall e a. Exception e => (e -> IO a) -> IO a -> IO a
handle ((SomeException -> IO ()) -> IO () -> IO ())
-> (SomeException -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ \(SomeException
_ :: SomeException) -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()