From 7f32e7007082dac41fcf98cb365620a191057e28 Mon Sep 17 00:00:00 2001 From: Jens Nolte <jens@nightmarestudio.de> Date: Fri, 13 Mar 2020 03:26:23 +0100 Subject: [PATCH] Fix block update reason behaviour for SignalBlock and PollBlock --- src/QBar/BlockHelper.hs | 156 ++++++++++++++++++++-------------------- 1 file changed, 79 insertions(+), 77 deletions(-) diff --git a/src/QBar/BlockHelper.hs b/src/QBar/BlockHelper.hs index c69ad73..605ed8e 100644 --- a/src/QBar/BlockHelper.hs +++ b/src/QBar/BlockHelper.hs @@ -1,5 +1,4 @@ {-# LANGUAGE RankNTypes #-} -{-# LANGUAGE TupleSections #-} module QBar.BlockHelper where @@ -19,33 +18,27 @@ import Pipes.Core data Signal a = RegularSignal | UserSignal a | EventSignal BlockEvent deriving (Show, Eq) -type SignalBlock a = (Signal a -> Server (Signal a) BlockUpdate BarIO ExitBlock) +type SignalBlock a = (Signal a -> Server (Signal a) (Maybe BlockOutput) BarIO ExitBlock) -- |Block that 'respond's with an update whenever it receives a 'PollSignal'. -type PollBlock' = Server PollSignal BlockUpdate BarIO -type PollBlock = Server PollSignal BlockUpdate BarIO ExitBlock +type PollBlock = Server PollSignal (Maybe BlockOutput) BarIO ExitBlock +type PollBlock' = Server PollSignal (Maybe BlockOutput) BarIO data PollSignal = PollSignal -respondBlockUpdate :: BlockOutput -> Server' (Signal s) BlockUpdate BarIO (Signal s) -respondBlockUpdate blockOutput = respond (Just (blockOutput, Nothing), DefaultUpdate) - -respondBlockUpdate' :: BlockEventHandler -> BlockOutput -> Server' (Signal s) BlockUpdate BarIO (Signal s) -respondBlockUpdate' blockEventHandler blockOutput = respond (Just (blockOutput, Just blockEventHandler), PollUpdate) +respondBlockUpdate :: BlockOutput -> Server' (Signal s) (Maybe BlockOutput) BarIO (Signal s) +respondBlockUpdate blockOutput = respond $ Just blockOutput -- |Update a block by removing the current output -respondEmptyBlockUpdate :: Server' (Signal s) BlockUpdate BarIO (Signal s) -respondEmptyBlockUpdate = respond (Nothing, PollUpdate) - +respondEmptyBlockUpdate :: Server' (Signal s) (Maybe BlockOutput) BarIO (Signal s) +respondEmptyBlockUpdate = respond Nothing -yieldBlockUpdate :: BlockOutput -> Server' PollSignal BlockUpdate BarIO () -yieldBlockUpdate blockOutput = void . respond $ (Just (blockOutput, Nothing), PollUpdate) -yieldBlockUpdate' :: BlockEventHandler -> BlockOutput -> Server' PollSignal BlockUpdate BarIO () -yieldBlockUpdate' blockEventHandler blockOutput = void . respond $ (Just (blockOutput, Just blockEventHandler), PollUpdate) +yieldBlockUpdate :: BlockOutput -> Server' PollSignal (Maybe BlockOutput) BarIO () +yieldBlockUpdate blockOutput = void . respond $ Just blockOutput -- |Update a block by removing the current output -yieldEmptyBlockUpdate :: Server' PollSignal BlockUpdate BarIO () -yieldEmptyBlockUpdate = void . respond $ (Nothing, PollUpdate) +yieldEmptyBlockUpdate :: Server' PollSignal (Maybe BlockOutput) BarIO () +yieldEmptyBlockUpdate = void . respond $ Nothing runSignalBlock :: forall a. Maybe Interval -> Maybe ((a -> IO ()) -> BarIO ()) -> SignalBlock a -> Block @@ -58,18 +51,18 @@ runSignalBlock maybeInterval maybeSignalSourceThread signalBlock' = runSignalBlo } -runSignalBlockFn :: forall a. Maybe Interval -> ((a -> IO ()) -> BarIO ()) -> ((a, Maybe BlockEvent) -> BarIO BlockState) -> Block +runSignalBlockFn :: forall a. Maybe Interval -> ((a -> IO ()) -> BarIO ()) -> ((a, Maybe BlockEvent) -> BarIO (Maybe BlockOutput)) -> Block runSignalBlockFn maybeInterval signalSourceThread renderFn = runSignalBlock maybeInterval (Just signalSourceThread) signalBlock where - signalBlock :: (Signal a -> Server (Signal a) BlockUpdate BarIO ExitBlock) + signalBlock :: SignalBlock a signalBlock (UserSignal value) = signalBlock' value (UserSignal value) signalBlock _ = signalBlock =<< respondEmptyBlockUpdate - signalBlock' :: a -> (Signal a -> Server (Signal a) BlockUpdate BarIO ExitBlock) - signalBlock' state RegularSignal = signalBlock' state =<< respond =<< (, PollUpdate) <$> lift (renderFn (state, Nothing)) - signalBlock' _ (UserSignal value) = signalBlock' value =<< respond =<< (, DefaultUpdate) <$> lift (renderFn (value, Nothing)) - signalBlock' state (EventSignal event) = signalBlock' state =<< respond =<< (, DefaultUpdate) <$> lift (renderFn (state, Just event)) + signalBlock' :: a -> SignalBlock a + signalBlock' state RegularSignal = signalBlock' state =<< respond =<< lift (renderFn (state, Nothing)) + signalBlock' _ (UserSignal value) = signalBlock' value =<< respond =<< lift (renderFn (value, Nothing)) + signalBlock' state (EventSignal event) = signalBlock' state =<< respond =<< lift (renderFn (state, Just event)) -runSignalBlockFn' :: Maybe Interval -> (Maybe BlockEvent -> BarIO BlockState) -> Block +runSignalBlockFn' :: Maybe Interval -> (Maybe BlockEvent -> BarIO (Maybe BlockOutput)) -> Block runSignalBlockFn' maybeInterval renderFn = runSignalBlockConfiguration $ SignalBlockConfiguration { initialize = const $ return (), signalThread = Nothing, @@ -78,9 +71,9 @@ runSignalBlockFn' maybeInterval renderFn = runSignalBlockConfiguration $ SignalB finalize = return } where - eventBlock :: (Signal a -> Server (Signal a) BlockUpdate BarIO ExitBlock) - eventBlock (EventSignal event) = eventBlock =<< respond =<< (, DefaultUpdate) <$> lift (renderFn (Just event)) - eventBlock _ = eventBlock =<< respond =<< (, PollUpdate) <$> lift (renderFn Nothing) + eventBlock :: SignalBlock a + eventBlock (EventSignal event) = eventBlock =<< respond =<< lift (renderFn (Just event)) + eventBlock _ = eventBlock =<< respond =<< lift (renderFn Nothing) @@ -111,7 +104,7 @@ runSignalBlockConfiguration SignalBlockConfiguration{initialize, signalThread, s intervalTask <- liftBarIO $ barAsync intervalTimer -- Run block - void (signalBlock context +>> signalPipe >-> attachEventHandlerP) + void (signalBlock context +>> signalPipe) -- Cancel threads when the block terminates -- TODO: use bracketP? @@ -129,12 +122,18 @@ runSignalBlockConfiguration SignalBlockConfiguration{initialize, signalThread, s liftIO . atomically $ writeTChan signalChan $ UserSignal value Event.set signalEvent - signalPipe :: Proxy (Signal p) BlockUpdate () BlockUpdate BarIO ExitBlock + signalPipe :: Proxy (Signal p) (Maybe BlockOutput) () BlockUpdate BarIO ExitBlock signalPipe = do initial <- request RegularSignal - yield initial - evalStateT stateSignalPipe initial - stateSignalPipe :: StateT BlockUpdate (Proxy (Signal p) BlockUpdate () BlockUpdate BarIO) ExitBlock + let initialUpdate = (mkBlockStateWithHandler initial, PollUpdate) + yield initialUpdate + evalStateT stateSignalPipe initialUpdate + + mkBlockStateWithHandler :: Maybe BlockOutput -> BlockState + mkBlockStateWithHandler Nothing = Nothing + mkBlockStateWithHandler (Just output) = Just (output, Just signalEventHandler) + + stateSignalPipe :: StateT BlockUpdate (Proxy (Signal p) (Maybe BlockOutput) () BlockUpdate BarIO) ExitBlock stateSignalPipe = forever $ do -- Handle all queued events eventHandled <- sendQueuedEvents @@ -147,7 +146,7 @@ runSignalBlockConfiguration SignalBlockConfiguration{initialize, signalThread, s liftIO $ Event.clear signalEvent where - sendQueuedEvents :: StateT BlockUpdate (Proxy (Signal p) BlockUpdate () BlockUpdate BarIO) Bool + sendQueuedEvents :: StateT BlockUpdate (Proxy (Signal p) (Maybe BlockOutput) () BlockUpdate BarIO) Bool sendQueuedEvents = do maybeSignal <- liftIO . atomically $ tryReadTChan signalChan case maybeSignal of @@ -162,11 +161,17 @@ runSignalBlockConfiguration SignalBlockConfiguration{initialize, signalThread, s return True Nothing -> return False - outputAndStore :: Signal p -> StateT BlockUpdate (Proxy (Signal p) BlockUpdate () BlockUpdate BarIO) () + outputAndStore :: Signal p -> StateT BlockUpdate (Proxy (Signal p) (Maybe BlockOutput) () BlockUpdate BarIO) () outputAndStore signal = do - value <- lift $ request signal - put value - lift $ yield value + maybeOutput <- lift $ request signal + let update = (mkBlockStateWithHandler maybeOutput, signalToReason signal) + put update + lift $ yield update + + signalToReason :: Signal a -> BlockUpdateReason + signalToReason (UserSignal _) = DefaultUpdate + signalToReason (EventSignal _) = UserUpdate + signalToReason RegularSignal = PollUpdate intervalTimer :: BarIO () @@ -178,20 +183,10 @@ runSignalBlockConfiguration SignalBlockConfiguration{initialize, signalThread, s liftIO $ Event.set signalEvent Nothing -> return () - attachEventHandlerP :: Pipe BlockUpdate BlockUpdate BarIO ExitBlock - attachEventHandlerP = forever $ do - (state, reason) <- await - let state' = if hasEventHandler state - -- If state already has an event handler, we do not attach another one - then state - -- Attach a click handler that will trigger a block update - else updateEventHandler signalEventHandler state - yield (state', reason) - where - signalEventHandler :: BlockEventHandler - signalEventHandler event = do - liftIO . atomically $ writeTChan signalChan $ EventSignal event - liftIO $ Event.set signalEvent + signalEventHandler :: BlockEventHandler + signalEventHandler event = do + liftIO . atomically $ writeTChan signalChan $ EventSignal event + liftIO $ Event.set signalEvent -- |Converts a 'PollBlock' to a 'Block' by running it whenever the 'defaultInterval' is triggered. @@ -200,34 +195,41 @@ runPollBlock = runPollBlock' defaultInterval -- |Converts a 'PollBlock' to a 'Block' by running it whenever the provided 'Interval' is triggered. runPollBlock' :: Interval -> PollBlock -> Block -runPollBlock' interval pb = pb >>~ addPollSignal >-> sleepToNextInterval +runPollBlock' interval pb = do + event <- liftIO Event.new + pb >>~ addPollSignal >-> sleepToNextInterval event where - addPollSignal :: BlockUpdate -> Proxy PollSignal BlockUpdate () BlockUpdate BarIO ExitBlock + addPollSignal :: a -> Proxy PollSignal a () a BarIO ExitBlock addPollSignal = respond >=> const (request PollSignal) >=> addPollSignal - sleepToNextInterval :: Pipe BlockUpdate BlockUpdate BarIO ExitBlock - sleepToNextInterval = do - event <- liftIO Event.new - forever $ do - (state, reason) <- await - if hasEventHandler state - then do - -- If state already has an event handler, we do not attach another one - yield (state, reason) - sleepUntilInterval interval - else do - -- Attach a click handler that will trigger a block update - yield (updateEventHandler (triggerOnClick event) state, reason) - - scheduler <- askSleepScheduler - result <- liftIO $ do - timerTask <- async $ sleepUntilInterval' scheduler defaultInterval - eventTask <- async $ Event.wait event - waitEitherCancel timerTask eventTask - - when (isRight result) $ do - liftIO $ Event.clear event - yield (invalidateBlockState state, UserUpdate) + sleepToNextInterval :: Event.Event -> Pipe (Maybe BlockOutput) BlockUpdate BarIO ExitBlock + sleepToNextInterval event = sleepToNextInterval' False + where + sleepToNextInterval' :: Bool -> Pipe (Maybe BlockOutput) BlockUpdate BarIO ExitBlock + sleepToNextInterval' isEvent = do + maybeOutput <- await + -- Attach a click handler that will trigger a block update + let state = mkBlockStateWithHandler (triggerOnClick event) maybeOutput + yield (state, if isEvent then UserUpdate else PollUpdate) + + scheduler <- askSleepScheduler + result <- liftIO $ do + timerTask <- async $ sleepUntilInterval' scheduler interval + eventTask <- async $ Event.wait event + waitEitherCancel timerTask eventTask + + let isEventNew = isRight result + + when isEventNew $ do + liftIO $ Event.clear event + yield (invalidateBlockState state, UserUpdate) + + sleepToNextInterval' isEventNew + + + mkBlockStateWithHandler :: BlockEventHandler -> Maybe BlockOutput -> BlockState + mkBlockStateWithHandler _ Nothing = Nothing + mkBlockStateWithHandler handler (Just output) = Just (output, Just handler) triggerOnClick :: Event.Event -> BlockEvent -> BarIO () triggerOnClick event _ = liftIO $ Event.set event -- GitLab