diff --git a/src/QBar/Host.hs b/src/QBar/Host.hs index 5cc2fe82de50918a5ad58eb714679dd36c5e3fa7..637a8dd9a09ee5e190ffd72ef78d548520a53d08 100644 --- a/src/QBar/Host.hs +++ b/src/QBar/Host.hs @@ -34,13 +34,12 @@ installSignalHandlers bar = void $ installHandler sigCONT (Catch sigContAction) hPutStrLn stderr "SIGCONT received" updateBar' bar -eventDispatcher :: IORef [(T.Text, BlockEventHandler)] -> Consumer BlockEvent BarIO () -eventDispatcher eventHandlerListIORef = eventDispatcher' +eventDispatcher :: Bar -> IORef [(T.Text, BlockEventHandler)] -> Consumer BlockEvent IO () +eventDispatcher bar eventHandlerListIORef = eventDispatcher' where - eventDispatcher' :: Consumer BlockEvent BarIO () + eventDispatcher' :: Consumer BlockEvent IO () eventDispatcher' = do blockEvent <- await - bar <- askBar eventHandlerList <- liftIO $ readIORef eventHandlerListIORef let maybeEventHandler = getEventHandler eventHandlerList blockEvent case maybeEventHandler of @@ -51,10 +50,10 @@ eventDispatcher eventHandlerListIORef = eventDispatcher' getEventHandler eventHandlerList blockEvent = lookup (name blockEvent) eventHandlerList -runBlocks :: HostHandle -> Producer [BlockOutput] BarIO () -runBlocks HostHandle{barUpdateEvent, newBlockChan, eventHandlerListIORef} = runBlocks' [] +runBlocks :: Bar -> HostHandle -> Producer [BlockOutput] IO () +runBlocks bar HostHandle{barUpdateEvent, newBlockChan, eventHandlerListIORef} = runBlocks' [] where - runBlocks' :: [CachedBlock] -> Producer [BlockOutput] BarIO () + runBlocks' :: [CachedBlock] -> Producer [BlockOutput] IO () runBlocks' blocks = do liftIO $ do -- Wait for an update request @@ -64,9 +63,9 @@ runBlocks HostHandle{barUpdateEvent, newBlockChan, eventHandlerListIORef} = runB threadDelay 10000 Event.clear barUpdateEvent - blocks' <- lift $ addNewBlocks blocks + blocks' <- liftIO $ runBarIO bar $ addNewBlocks blocks - (blockStates, blocks'') <- lift $ getBlockStates blocks' + (blockStates, blocks'') <- liftIO $ runBarIO bar $ getBlockStates blocks' -- Pass blocks to output yield $ map fst $ catMaybes blockStates @@ -125,10 +124,11 @@ filterDuplicates = do filterDuplicates' value -runBarHost :: Consumer [BlockOutput] BarIO () - -> Producer BlockEvent BarIO () +runBarHost :: Consumer [BlockOutput] IO () + -> Producer BlockEvent IO () + -> BarIO () -> IO () -runBarHost host barEventProducer = do +runBarHost host barEventProducer loadBlocks = do -- Create an event used to signal bar updates barUpdateEvent <- Event.newSet let requestBarUpdate = Event.set barUpdateEvent @@ -150,10 +150,12 @@ runBarHost host barEventProducer = do eventHandlerListIORef } - let handleStdin = runEffect $ barEventProducer >-> eventDispatcher eventHandlerListIORef + runBarIO bar loadBlocks + + let handleStdin = liftIO $ runEffect $ barEventProducer >-> eventDispatcher bar eventHandlerListIORef -- Fork stdin handler void $ forkFinally (runBarIO bar handleStdin) (\result -> hPutStrLn stderr $ "handleStdin failed: " <> show result) -- Run bar host - runBarIO bar $ runEffect $ runBlocks hostHandle >-> filterDuplicates >-> host + runEffect $ runBlocks bar hostHandle >-> filterDuplicates >-> host diff --git a/src/QBar/Server.hs b/src/QBar/Server.hs index ec09b0d67f0051f0648c46df7570fe3aad1e7d5f..4e4150396360affa91eee9c41cfbb79c9724f65c 100644 --- a/src/QBar/Server.hs +++ b/src/QBar/Server.hs @@ -52,10 +52,10 @@ instance ToJSON PangoBlock where -- |A producer that reads swaybar/i3bar-input events from stdin and emits them as 'BlockEvent's. -swayBarInput :: MainOptions -> Producer BlockEvent BarIO () +swayBarInput :: MainOptions -> Producer BlockEvent IO () swayBarInput MainOptions{verbose} = swayBarInput' where - swayBarInput' :: Producer BlockEvent BarIO () + swayBarInput' :: Producer BlockEvent IO () swayBarInput' = do line <- liftIO $ BSSC8.hGetLine stdin @@ -77,20 +77,32 @@ swayBarInput MainOptions{verbose} = swayBarInput' | otherwise = line -outputLine :: MainOptions -> [ThemedBlockOutput] -> IO () -outputLine MainOptions{verbose} themedBlocks = do - let encodedOutput = encodeOutput themedBlocks - +swayBarOutput :: MainOptions -> Consumer [ThemedBlockOutput] IO () +swayBarOutput options = do + -- Print header liftIO $ do - hPut stdout encodedOutput - putStrLn "," - hFlush stdout - -- Echo output to stderr when verbose flag is set - when verbose $ do - hPut stderr encodedOutput - hPut stderr "\n" - hFlush stderr + putStrLn "{\"version\":1,\"click_events\":true}" + putStrLn "[" + + swayBarOutput' where + swayBarOutput' :: Consumer [ThemedBlockOutput] IO () + swayBarOutput' = do + await >>= (liftIO . outputLine options) + swayBarOutput' + outputLine :: MainOptions -> [ThemedBlockOutput] -> IO () + outputLine MainOptions{verbose} themedBlocks = do + let encodedOutput = encodeOutput themedBlocks + + liftIO $ do + hPut stdout encodedOutput + putStrLn "," + hFlush stdout + -- Echo output to stderr when verbose flag is set + when verbose $ do + hPut stderr encodedOutput + hPut stderr "\n" + hFlush stderr encodeOutput :: [ThemedBlockOutput] -> BS.ByteString encodeOutput blocks = encode $ map renderPangoBlock $ blocks renderPangoBlock :: ThemedBlockOutput -> PangoBlock @@ -101,16 +113,16 @@ outputLine MainOptions{verbose} themedBlocks = do } runBarServer :: BarIO () -> MainOptions -> IO () -runBarServer defaultBarConfig options = runBarHost barServer (swayBarInput options) +runBarServer defaultBarConfig options = runBarHost barServer (swayBarInput options) loadBlocks where - barServer :: Consumer [BlockOutput] BarIO () - barServer = do + loadBlocks :: BarIO () + loadBlocks = do -- Load blocks - lift $ do - when (indicator options) $ addBlock renderIndicator - defaultBarConfig - + when (indicator options) $ addBlock renderIndicator + defaultBarConfig + barServer :: Consumer [BlockOutput] IO () + barServer = do -- Event to render the bar (fired when block output or theme is changed) renderEvent <- liftIO Event.new @@ -118,7 +130,7 @@ runBarServer defaultBarConfig options = runBarHost barServer (swayBarInput optio (output, input) <- liftIO $ spawn $ latest [] -- MVar that holds the current theme, linked to the input from the above mailbox - (themedBlockProducerMVar :: MVar (Producer [ThemedBlockOutput] IO (), Bool)) <- liftIO $ newMVar $ (return (), False) + (themedBlockProducerMVar :: MVar (Producer [ThemedBlockOutput] IO (), Bool)) <- liftIO $ newMVar $ throw $ userError "Unexpected behavior: Default theme not set" -- Create control socket @@ -143,9 +155,6 @@ runBarServer defaultBarConfig options = runBarHost barServer (swayBarInput optio -- Set default theme setTheme input themedBlockProducerMVar defaultTheme - -- Print header - putStrLn "{\"version\":1,\"click_events\":true}" - putStrLn "[" -- Run render loop liftIO $ link =<< async (renderLoop renderEvent themedBlockProducerMVar) @@ -153,21 +162,29 @@ runBarServer defaultBarConfig options = runBarHost barServer (swayBarInput optio signalPipe renderEvent >-> toOutput output renderLoop :: Event.Event -> MVar (Producer [ThemedBlockOutput] IO (), Bool) -> IO () - renderLoop renderEvent themedBlockProducerMVar = forever $ do - (themedBlocks, isAnimated'') <- modifyMVar themedBlockProducerMVar (\(themedBlockProducer, isAnimated') -> do - result <- next themedBlockProducer - case result of - -- TODO: fix type safety on this somehow? - Left _ -> throw $ userError "Unexpected behavior: themes and mailboxes should never return" - Right (themedBlocks, nextThemedBlockProducer) -> - return ((nextThemedBlockProducer, isAnimated'), (themedBlocks, isAnimated')) - ) - outputLine options themedBlocks - if isAnimated'' - -- Limit to 10 FPS because swaybar rendering is surprisingly expensive - -- TODO: make FPS configurable - then void $ Event.waitTimeout renderEvent 100000 - else Event.wait renderEvent + renderLoop renderEvent themedBlockProducerMVar = runEffect $ + themeAnimator renderEvent themedBlockProducerMVar >-> filterDuplicates >-> swayBarOutput options + + themeAnimator :: Event.Event -> MVar (Producer [ThemedBlockOutput] IO (), Bool) -> Producer [ThemedBlockOutput] IO () + themeAnimator renderEvent themedBlockProducerMVar = themeAnimator' + where + themeAnimator' :: Producer [ThemedBlockOutput] IO () + themeAnimator' = do + (themedBlocks, isAnimated'') <- liftIO $ modifyMVar themedBlockProducerMVar (\(themedBlockProducer, isAnimated') -> do + result <- next themedBlockProducer + case result of + -- TODO: fix type safety on this somehow? + Left _ -> throw $ userError "Unexpected behavior: Themes and output cache mailbox should never return" + Right (themedBlocks, nextThemedBlockProducer) -> + return ((nextThemedBlockProducer, isAnimated'), (themedBlocks, isAnimated')) + ) + yield themedBlocks + liftIO $ if isAnimated'' + -- Limit to 10 FPS because swaybar rendering is surprisingly expensive + -- TODO: make FPS configurable + then void $ Event.waitTimeout renderEvent 100000 + else Event.wait renderEvent + themeAnimator' setTheme :: Input [BlockOutput] -> MVar (Producer [ThemedBlockOutput] IO (), Bool) -> Theme -> IO () setTheme blockOutputInput themedBlockProducerMVar (StaticTheme theme) =