diff options
Diffstat (limited to 'src/Plugins/Monitors')
-rw-r--r-- | src/Plugins/Monitors/Batt.hs | 10 | ||||
-rw-r--r-- | src/Plugins/Monitors/Bright.hs | 118 | ||||
-rw-r--r-- | src/Plugins/Monitors/CoreCommon.hs | 151 | ||||
-rw-r--r-- | src/Plugins/Monitors/CoreTemp.hs | 12 | ||||
-rw-r--r-- | src/Plugins/Monitors/CpuFreq.hs | 18 | ||||
-rw-r--r-- | src/Plugins/Monitors/MPD.hs | 13 | ||||
-rw-r--r-- | src/Plugins/Monitors/ThermalZone.hs | 43 | ||||
-rw-r--r-- | src/Plugins/Monitors/Top.hs | 1 |
8 files changed, 295 insertions, 71 deletions
diff --git a/src/Plugins/Monitors/Batt.hs b/src/Plugins/Monitors/Batt.hs index 7d4242a..c276e6b 100644 --- a/src/Plugins/Monitors/Batt.hs +++ b/src/Plugins/Monitors/Batt.hs @@ -123,18 +123,20 @@ readBattery files = return $ Battery (3600 * a / 1000000) -- wattseconds (3600 * b / 1000000) -- wattseconds (c / 1000000) -- volts - (d / c) -- amperes - where grab = fmap (read . B.unpack) . B.readFile + (if c > 0 then (d / c) else -1) -- amperes + where grab f = catch (fmap (read . B.unpack) $ B.readFile f) (\_ -> return 0) readBatteries :: BattOpts -> [Files] -> IO Result readBatteries opts bfs = do bats <- mapM readBattery (take 3 bfs) ac <- haveAc (onlineFile opts) let sign = if ac then 1 else -1 - left = sum (map now bats) / sum (map full bats) + ft = sum (map full bats) + left = if ft > 0 then sum (map now bats) / ft else 0 watts = sign * sum (map voltage bats) * sum (map current bats) time = if watts == 0 then 0 else sum $ map time' bats - time' b = (if ac then full b - now b else now b) / (sign * watts) + mwatts = if watts == 0 then 1 else sign * watts + time' b = (if ac then full b - now b else now b) / mwatts acstr = if ac then onString opts else offString opts return $ if isNaN left then NA else Result left watts time acstr diff --git a/src/Plugins/Monitors/Bright.hs b/src/Plugins/Monitors/Bright.hs new file mode 100644 index 0000000..499e5bc --- /dev/null +++ b/src/Plugins/Monitors/Bright.hs @@ -0,0 +1,118 @@ +----------------------------------------------------------------------------- +---- | +---- Module : Plugins.Monitors.Birght +---- Copyright : (c) Martin Perner +---- License : BSD-style (see LICENSE) +---- +---- Maintainer : Martin Perner <martin@perner.cc> +---- Stability : unstable +---- Portability : unportable +---- +---- A screen brightness monitor for Xmobar +---- +------------------------------------------------------------------------------- + +module Plugins.Monitors.Bright (brightConfig, runBright) where + +import Plugins.Monitors.Common +import qualified Data.ByteString.Lazy.Char8 as B +import Data.Char +import System.FilePath ((</>)) +import System.Posix.Files (fileExist) +import System.Console.GetOpt + +data BrightOpts = BrightOpts + { subDir :: String + , currBright :: String + , maxBright :: String + } + +defaultOpts :: BrightOpts +defaultOpts = BrightOpts + { subDir = "acpi_video0" + , currBright = "actual_brightness" + , maxBright = "max_brightness" + } + +options :: [OptDescr (BrightOpts -> BrightOpts)] +options = + [ Option "D" ["device"] (ReqArg (\x o -> o { subDir = x }) "") "" + , Option "C" ["curr"] (ReqArg (\x o -> o { currBright = x }) "") "" + , Option "M" ["max"] (ReqArg (\x o -> o { maxBright = x }) "") "" + ] + +-- from Batt.hs +parseOpts :: [String] -> IO BrightOpts +parseOpts argv = + case getOpt Permute options argv of + (o, _, []) -> return $ foldr id defaultOpts o + (_, _, errs) -> ioError . userError $ concat errs + +sysDir :: FilePath +sysDir = "/sys/class/backlight/" + +brightConfig :: IO MConfig +brightConfig = mkMConfig + "<percent>" -- template + ["hbar", "percent", "bar"] -- replacements + +data Files = Files + { fCurr :: String + , fMax :: String + } | NoFiles + +brightFiles :: BrightOpts -> IO Files +brightFiles opts = + do + is_curr <- fileExist $ (fCurr files) + is_max <- fileExist $ (fCurr files) + if is_curr && is_max + then return files + else return NoFiles + where + prefix = sysDir </> (subDir opts) + files = Files { fCurr = prefix </> (currBright opts) + , fMax = prefix </> (maxBright opts) + } + + +runBright :: [String] -> Monitor String +runBright args = do + opts <- io $ parseOpts args + f <- io $ brightFiles opts + c <- io $ readBright f + case f of + NoFiles -> return "hurz" + _ -> do x <- fmtPercent c + parseTemplate (x) + where + fmtPercent :: Float -> Monitor [String] + fmtPercent c = do + r <- showHorizontalBar (100 * c) + s <- showPercentWithColors c + t <- showPercentBar (100 * c) c + return [r,s,t] + +readBright :: Files -> IO Float +readBright NoFiles = return 0 +readBright files = + do + currVal<- grab $ (fCurr files) + maxVal <- grab $ (fMax files) + return $ (currVal / maxVal) + where + grab f = catch (fmap (read . B.unpack) $ B.readFile f)(\_ -> return 0) + + +showHorizontalBar :: Float -> Monitor String +showHorizontalBar x = do + return $ [convert x] + where + convert :: Float -> Char + convert val + | t <= 9600 = ' ' + | t > 9608 = chr 9608 + | otherwise = chr t + where + -- we scale from 0 to 100, we have 8 slots (9 elements), 100/8 = 12 + t = 9600 + ((round val) `div` 12) diff --git a/src/Plugins/Monitors/CoreCommon.hs b/src/Plugins/Monitors/CoreCommon.hs index 80e7700..e508f7d 100644 --- a/src/Plugins/Monitors/CoreCommon.hs +++ b/src/Plugins/Monitors/CoreCommon.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE PatternGuards #-} + ----------------------------------------------------------------------------- -- | -- Module : Plugins.Monitors.CoreCommon @@ -14,46 +16,119 @@ module Plugins.Monitors.CoreCommon where +import Control.Applicative +import Control.Monad +import Data.Char hiding (Space) +import Data.Function +import Data.List +import Data.Maybe import Plugins.Monitors.Common -import System.Posix.Files (fileExist) -import System.IO (withFile, IOMode(ReadMode), hGetLine) import System.Directory -import Data.Char (isDigit) -import Data.List (isPrefixOf) --- | --- Function checks the existence of first file specified by pattern and if the --- file doesn't exists failure message is shown, otherwise the data retrieval --- is performed. -checkedDataRetrieval :: (Num a, Ord a, Show a) => - String -> String -> String -> String -> (Double -> a) - -> (a -> String) -> Monitor String -checkedDataRetrieval failureMessage dir file pattern trans fmt = do - exists <- io $ fileExist $ concat [dir, "/", pattern, "0/", file] - case exists of - False -> return failureMessage - True -> retrieveData dir file pattern trans fmt +checkedDataRetrieval :: (Ord a, Num a) + => String -> [String] -> Maybe (String, String -> Int) + -> (Double -> a) -> (a -> String) -> Monitor String +checkedDataRetrieval msg path lbl trans fmt = liftM (maybe msg id) $ + retrieveData path lbl trans fmt --- | --- Function retrieves data from files in directory dir specified by --- pattern. String values are converted to double and 'trans' applied --- to each one. Final array is processed by template parser function --- and returned as monitor string. -retrieveData :: (Num a, Ord a, Show a) => - String -> String -> String -> (Double -> a) -> (a -> String) -> - Monitor String -retrieveData dir file pattern trans fmt = do - count <- io $ dirCount dir pattern - contents <- io $ mapM getGuts $ files count - values <- mapM (showWithColors fmt) $ map conversion contents - parseTemplate values - where - getGuts f = withFile f ReadMode hGetLine - dirCount path str = getDirectoryContents path - >>= return . length - . filter (\s -> str `isPrefixOf` s - && isDigit (last s)) - files count = map (\i -> concat [dir, "/", pattern, show i, "/", file]) - [0 .. count - 1] - conversion = trans . (read :: String -> Double) +retrieveData :: (Ord a, Num a) + => [String] -> Maybe (String, String -> Int) + -> (Double -> a) -> (a -> String) -> Monitor (Maybe String) +retrieveData path lbl trans fmt = do + pairs <- map snd . sortBy (compare `on` fst) <$> (mapM readFiles =<< findFiles path lbl) + if null pairs + then return Nothing + else Just <$> ( parseTemplate + =<< mapM (showWithColors fmt . trans . read) pairs + ) + +-- | Represents the different types of path components +data Comp = Fix String + | Var [String] + deriving Show + +-- | Used to represent parts of file names separated by slashes and spaces +data CompOrSep = Slash + | Space + | Comp String + deriving (Eq, Show) + +-- | Function to turn a list of of strings into a list of path components +pathComponents :: [String] -> [Comp] +pathComponents = joinComps . drop 2 . concat . intersperse [Space] . map splitParts + where + splitParts p | (l, _:r) <- break (== '/') p = (Comp l):Slash:splitParts r + | otherwise = [Comp p] + + joinComps = uncurry joinComps' . partition isComp + + isComp (Comp _) = True + isComp _ = False + + fromComp (Comp s) = s + fromComp _ = error "fromComp applied to value other than (Comp _)" + + joinComps' cs [] = [Fix $ fromComp $ head cs] -- cs should have only one element here, + -- but this keeps the pattern matching + -- exhaustive + joinComps' cs (p:ps) = let (ss, ps') = span (== p) ps + ct = if null ps' || (p == Space) then length ss + 1 + else length ss + (ls, rs) = splitAt (ct+1) cs + c = case p of + Space -> Var $ map fromComp ls + Slash -> Fix $ intercalate "/" $ map fromComp ls + _ -> error "Should not happen" + in if null ps' then [c] + else c:joinComps' rs (drop ct ps) + +-- | Function to find all files matching the given path and possible label file. +-- The path must be absolute (start with a leading slash). +findFiles :: [String] -> Maybe (String, String -> Int) + -> Monitor [(String, Either Int (String, String -> Int))] +findFiles path lbl = catMaybes + <$> ( mapM addLabel . zip [0..] . sort + =<< recFindFiles (pathComponents path) "/" + ) + where + addLabel (i, f) = maybe (return $ Just (f, Left i)) + (\(s, t) -> justIfExists f s t) + lbl + + justIfExists f s t = let f' = take (length f - length s) f ++ s + in ifthen (Just (f, Right (f', t))) Nothing <$> (io $ doesFileExist f') + + recFindFiles [] d = ifthen [d] [] + <$> (io $ if null d then return False else doesFileExist d) + recFindFiles ps d = ifthen (recFindFiles' ps d) (return []) + =<< (io $ if null d then return True else doesDirectoryExist d) + + recFindFiles' [] _ = error "Should not happen" + recFindFiles' (Fix p:ps) d = recFindFiles ps (d ++ "/" ++ p) + recFindFiles' (Var p:ps) d = concat + <$> ( mapM (recFindFiles ps) + . map (\f -> d ++ "/" ++ f) + . filter (matchesVar p) + =<< (io $ getDirectoryContents d) + ) + + matchesVar [] _ = False + matchesVar [v] f = v == f + matchesVar (v:vs) f = let f' = drop (length v) f + f'' = dropWhile isDigit f' + in and [ v `isPrefixOf` f + , not (null f') + , isDigit (head f') + , matchesVar vs f'' + ] + +-- | Function to read the contents of the given file(s) +readFiles :: (String, Either Int (String, String -> Int)) + -> Monitor (Int, String) +readFiles (fval, flbl) = (,) <$> either return (\(f, ex) -> liftM ex + $ io $ readFile f) flbl + <*> (io $ readFile fval) +-- | Function that captures if-then-else +ifthen :: a -> a -> Bool -> a +ifthen thn els cnd = if cnd then thn else els
\ No newline at end of file diff --git a/src/Plugins/Monitors/CoreTemp.hs b/src/Plugins/Monitors/CoreTemp.hs index a24b284..f7b5c95 100644 --- a/src/Plugins/Monitors/CoreTemp.hs +++ b/src/Plugins/Monitors/CoreTemp.hs @@ -31,11 +31,9 @@ coreTempConfig = mkMConfig -- Function retrieves monitor string holding the core temperature -- (or temperatures) runCoreTemp :: [String] -> Monitor String -runCoreTemp _ = do - let dir = "/sys/bus/platform/devices" - file = "temp1_input" - pattern = "coretemp." - divisor = 1e3 :: Double - failureMessage = "CoreTemp: N/A" - checkedDataRetrieval failureMessage dir file pattern (/divisor) show +runCoreTemp _ = let path = ["/sys/bus/platform/devices/coretemp.", "/temp", "_input"] + lbl = Just ("_label", read . drop 5) + divisor = 1e3 :: Double + failureMessage = "CoreTemp: N/A" + in checkedDataRetrieval failureMessage path lbl (/divisor) show diff --git a/src/Plugins/Monitors/CpuFreq.hs b/src/Plugins/Monitors/CpuFreq.hs index 4f01922..dcf75e5 100644 --- a/src/Plugins/Monitors/CpuFreq.hs +++ b/src/Plugins/Monitors/CpuFreq.hs @@ -28,16 +28,12 @@ cpuFreqConfig = mkMConfig -- replacements -- | --- Function retrieves monitor string holding the cpu frequency (or --- frequencies) +-- Function retrieves monitor string holding the cpu frequency (or frequencies) runCpuFreq :: [String] -> Monitor String -runCpuFreq _ = do - let dir = "/sys/devices/system/cpu" - file = "cpufreq/scaling_cur_freq" - pattern = "cpu" - divisor = 1e6 :: Double - failureMessage = "CpuFreq: N/A" - fmt x | x < 1 = show (round (x * 1000) :: Integer) ++ "MHz" - | otherwise = showDigits 1 x ++ "GHz" - checkedDataRetrieval failureMessage dir file pattern (/divisor) fmt +runCpuFreq _ = let path = ["/sys/devices/system/cpu/cpu", "/cpufreq/scaling_cur_freq"] + divisor = 1e6 :: Double + failureMessage = "CpuFreq: N/A" + fmt x | x < 1 = (show (round (x * 1000) :: Integer)) ++ "MHz" + | otherwise = (show x) ++ "GHz" + in checkedDataRetrieval failureMessage path Nothing (/divisor) fmt diff --git a/src/Plugins/Monitors/MPD.hs b/src/Plugins/Monitors/MPD.hs index daf0ed4..60c3e48 100644 --- a/src/Plugins/Monitors/MPD.hs +++ b/src/Plugins/Monitors/MPD.hs @@ -30,9 +30,6 @@ data MOpts = MOpts { mPlaying :: String , mStopped :: String , mPaused :: String - , mHost :: String - , mPort :: Integer - , mPassword :: String } defaultOpts :: MOpts @@ -40,9 +37,6 @@ defaultOpts = MOpts { mPlaying = ">>" , mStopped = "><" , mPaused = "||" - , mHost = "127.0.0.1" - , mPort = 6600 - , mPassword = "" } options :: [OptDescr (MOpts -> MOpts)] @@ -50,15 +44,12 @@ options = [ Option "P" ["playing"] (ReqArg (\x o -> o { mPlaying = x }) "") "" , Option "S" ["stopped"] (ReqArg (\x o -> o { mStopped = x }) "") "" , Option "Z" ["paused"] (ReqArg (\x o -> o { mPaused = x }) "") "" - , Option "h" ["host"] (ReqArg (\x o -> o { mHost = x }) "") "" - , Option "p" ["port"] (ReqArg (\x o -> o { mPort = read x }) "") "" - , Option "x" ["password"] (ReqArg (\x o -> o { mPassword = x }) "") "" ] runMPD :: [String] -> Monitor String runMPD args = do opts <- io $ mopts args - let mpd = M.withMPDEx (mHost opts) (mPort opts) (mPassword opts) + let mpd = M.withMPD status <- io $ mpd M.status song <- io $ mpd M.currentSong s <- parseMPD status song opts @@ -100,7 +91,7 @@ parseSong (Right Nothing) = return $ repeat "" parseSong (Right (Just s)) = let join [] = "" join (x:xs) = foldl (\a o -> a ++ ", " ++ o) x xs - str sel = maybe "" join (M.sgGet sel s) + str sel = maybe "" join (M.sgGetTag sel s) sels = [ M.Name, M.Artist, M.Composer, M.Performer , M.Album, M.Title, M.Track, M.Genre ] fields = M.sgFilePath s : map str sels diff --git a/src/Plugins/Monitors/ThermalZone.hs b/src/Plugins/Monitors/ThermalZone.hs new file mode 100644 index 0000000..55fb2ca --- /dev/null +++ b/src/Plugins/Monitors/ThermalZone.hs @@ -0,0 +1,43 @@ +------------------------------------------------------------------------------ +-- | +-- Module : Plugins.Monitors.ThermalZone +-- Copyright : (c) 2011 Jose Antonio Ortega Ruiz +-- License : BSD3-style (see LICENSE) +-- +-- Maintainer : jao@gnu.org +-- Stability : unstable +-- Portability : portable +-- Created : Fri Feb 25, 2011 03:18 +-- +-- +-- A thermal zone plugin based on the sysfs linux interface. +-- See http://kernel.org/doc/Documentation/thermal/sysfs-api.txt +-- +------------------------------------------------------------------------------ + +module Plugins.Monitors.ThermalZone (thermalZoneConfig, runThermalZone) where + +import Plugins.Monitors.Common + +import System.Posix.Files (fileExist) +import qualified Data.ByteString.Char8 as B + +-- | Default thermal configuration. +thermalZoneConfig :: IO MConfig +thermalZoneConfig = mkMConfig "<temp>C" ["temp"] + +-- | Retrieves thermal information. Argument is name of thermal +-- directory in \/sys\/clas\/thermal. Returns the monitor string +-- parsed according to template (either default or user specified). +runThermalZone :: [String] -> Monitor String +runThermalZone args = do + let zone = head args + file = "/sys/class/thermal/thermal_zone" ++ zone ++ "/temp" + parse = return . (read :: String -> Int) . B.unpack + exists <- io $ fileExist file + if exists + then do mdegrees <- io $ B.readFile file >>= parse + temp <- showWithColors show (mdegrees `quot` 1000) + parseTemplate [ temp ] + else return "N/A" + diff --git a/src/Plugins/Monitors/Top.hs b/src/Plugins/Monitors/Top.hs index e45210c..6001164 100644 --- a/src/Plugins/Monitors/Top.hs +++ b/src/Plugins/Monitors/Top.hs @@ -13,6 +13,7 @@ ----------------------------------------------------------------------------- {-# LANGUAGE ForeignFunctionInterface #-} +{-# LANGUAGE BangPatterns #-} module Plugins.Monitors.Top (startTop, topMemConfig, runTopMem) where |