summaryrefslogtreecommitdiffhomepage
path: root/src/Xmobar/Plugins/Monitors/MultiCpu.hs
blob: 70ae132a403e020ecf13f531a56af7828d16f5c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
-----------------------------------------------------------------------------
-- |
-- Module      :  Plugins.Monitors.MultiCpu
-- Copyright   :  (c) Jose A Ortega Ruiz
-- License     :  BSD-style (see LICENSE)
--
-- Maintainer  :  Jose A Ortega <jao@gnu.org>
-- Stability   :  unstable
-- Portability :  unportable
--
-- A multi-cpu monitor for Xmobar
--
-----------------------------------------------------------------------------

module Xmobar.Plugins.Monitors.MultiCpu (startMultiCpu) where

import Xmobar.Plugins.Monitors.Common
import Control.Applicative ((<$>))
import qualified Data.ByteString.Lazy.Char8 as B
import Data.List (isPrefixOf, transpose, unfoldr)
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import System.Console.GetOpt

data MultiCpuOpts = MultiCpuOpts
  { loadIconPatterns :: [IconPattern]
  , loadIconPattern :: Maybe IconPattern
  , fallbackIconPattern :: Maybe IconPattern
  , contiguous :: Bool
  }

defaultOpts :: MultiCpuOpts
defaultOpts = MultiCpuOpts
  { loadIconPatterns = []
  , loadIconPattern = Nothing
  , fallbackIconPattern = Nothing
  , contiguous = False
  }

options :: [OptDescr (MultiCpuOpts -> MultiCpuOpts)]
options =
  [ Option "" ["load-icon-pattern"] (ReqArg (\x o ->
     o { loadIconPattern = Just $ parseIconPattern x }) "") ""
  , Option "" ["load-icon-patterns"] (ReqArg (\x o ->
     o { loadIconPatterns = parseIconPattern x : loadIconPatterns o }) "") ""
  , Option "" ["fallback-icon-pattern"] (ReqArg (\x o ->
     o { fallbackIconPattern = Just $ parseIconPattern x }) "") ""
  , Option "" ["contiguous-icons"] (NoArg (\o -> o {contiguous = True})) ""
  ]

parseOpts :: [String] -> IO MultiCpuOpts
parseOpts argv =
  case getOpt Permute options argv of
    (o, _, []) -> return $ foldr id defaultOpts o
    (_, _, errs) -> ioError . userError $ concat errs

variables :: [String]
variables = ["bar", "vbar","ipat","total","user","nice","system","idle"]
vNum :: Int
vNum = length variables

multiCpuConfig :: IO MConfig
multiCpuConfig =
  mkMConfig "Cpu: <total>%" $
            ["auto" ++ k | k <- variables] ++
            [ k ++ n     | n <- "" : map show [0 :: Int ..]
                         , k <- variables]

type CpuDataRef = IORef [[Int]]

cpuData :: IO [[Int]]
cpuData = parse `fmap` B.readFile "/proc/stat"
  where parse = map parseList . cpuLists
        cpuLists = takeWhile isCpu . map B.words . B.lines
        isCpu (w:_) = "cpu" `isPrefixOf` B.unpack w
        isCpu _ = False
        parseList = map (parseInt . B.unpack) . tail

parseCpuData :: CpuDataRef -> IO [[Float]]
parseCpuData cref =
  do as <- readIORef cref
     bs <- cpuData
     writeIORef cref bs
     let p0 = zipWith percent bs as
     return p0

percent :: [Int] -> [Int] -> [Float]
percent b a = if tot > 0 then map (/ tot) $ take 4 dif else [0, 0, 0, 0]
  where dif = map fromIntegral $ zipWith (-) b a
        tot = sum dif

formatMultiCpus :: MultiCpuOpts -> [[Float]] -> Monitor [String]
formatMultiCpus _ [] = return []
formatMultiCpus opts xs =
  concat <$> mapM (\(i, x) -> formatCpu opts i x) (zip [0..] xs)

formatCpu :: MultiCpuOpts -> Int -> [Float] -> Monitor [String]
formatCpu opts i xs
  | length xs < 4 = showPercentsWithColors $ replicate vNum 0.0
  | otherwise = let t = sum $ take 3 xs
                in do b <- showPercentBar (100 * t) t
                      h <- showVerticalBar (100 * t) t
                      d <- showIconPattern tryString t
                      ps <- showPercentsWithColors (t:xs)
                      return (b:h:d:ps)
  where tryString
          | i == 0 = loadIconPattern opts
          | i <= length (loadIconPatterns opts) =
              Just $ loadIconPatterns opts !! (i - 1)
          | otherwise = fallbackIconPattern opts

splitEvery :: Int -> [a] -> [[a]]
splitEvery n = unfoldr (\x -> if null x then Nothing else Just $ splitAt n x)

groupData :: [String] -> [[String]]
groupData = transpose . tail . splitEvery vNum

formatAutoCpus :: MultiCpuOpts -> [String] -> Monitor [String]
formatAutoCpus _ [] = return $ replicate vNum ""
formatAutoCpus opts xs =
  return $ map (if (contiguous opts) then concat else unwords) (groupData xs)

runMultiCpu :: CpuDataRef -> [String] -> Monitor String
runMultiCpu cref argv =
  do c <- io $ parseCpuData cref
     opts <- io $ parseOpts argv
     l <- formatMultiCpus opts c
     a <- formatAutoCpus opts l
     parseTemplate $ a ++ l

startMultiCpu :: [String] -> Int -> (String -> IO ()) -> IO ()
startMultiCpu a r cb = do
  cref <- newIORef [[]]
  _ <- parseCpuData cref
  runM a multiCpuConfig (runMultiCpu cref) r cb