packages feed

xnobar-1.0.0.0: lib/Scroller/XNobar/Scroller.hs

{-# LANGUAGE RecordWildCards #-}
module XNobar.Scroller (scroller, Config(..)) where

import Control.Concurrent.Async (withAsync)
import Control.Exception (finally)
import Control.Monad (forever, when)
import Control.Monad.State.Strict (evalStateT, get, liftIO, modify')
import Data.IORef (atomicModifyIORef', newIORef, readIORef)
import Data.Maybe (fromJust, isJust, maybe)
import Flow ((.>))
import GHC.IO.Handle.FD (withFileBlocking)
import System.Directory (removeFile)
import System.IO (IOMode(ReadMode), hGetContents')
import System.Process (readProcessWithExitCode)

import XNobar.Internal.Notification (makeId)
import XNobar.Internal.Scroller (onlyIf, remove, merge, scroll, showNotifs, Config(..))
import XNobar.Server (NotificationsRef, fetch)
import Control.Concurrent (threadDelay)

scroller :: Config -> (String -> IO ()) -> NotificationsRef -> IO ()
scroller config@(Config{..}) callback notifs = do
  clicked <- newIORef Nothing
  (_, n, _) <- readProcessWithExitCode "uuidgen" [] ""
  let pipe = "/tmp/xnobar-" ++ removeLinebreak n where removeLinebreak = init
  (_, _, _) <- readProcessWithExitCode "mkfifo" [pipe] ""
  withAsync (forever $ do out <- withFileBlocking pipe ReadMode hGetContents'
                          atomicModifyIORef' clicked (const (Just out, ())))
            (const $ evalStateT (forever (update clicked pipe)) Nothing)
    `finally` removeFile pipe
    where
      update clicked pipe = do

        newNotifs <- liftIO $ fetch notifs

        clicked' <- liftIO $ readIORef clicked

        when (isJust clicked') $ liftIO $ clear clicked

        modify' $ onlyIf (isJust clicked')
                         (remove (getId $ fromJust clicked'))
               .> onlyIf (not $ null newNotifs)
                         (merge newNotifs)
               .> scroll

        get >>= maybe (noNewsPrefix ++ idleText) (showNotifs config pipe)
             .> (liftIO . callback)

        liftIO $ tenthSeconds scrollPeriod
        where
          clear = (`atomicModifyIORef'` const (Nothing, ()))
          getId = makeId . read
          tenthSeconds = threadDelay . (100000*)