2021-11-06 10:59:45 -04:00
|
|
|
{-# LANGUAGE ViewPatterns #-}
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | Random IO-ish functions used throughtout xmonad
|
|
|
|
--
|
|
|
|
-- Most (probably all) of these functions are intended to work with sysfs where
|
|
|
|
-- some safe assumptions can be made about file contents.
|
|
|
|
|
|
|
|
module XMonad.Internal.IO
|
|
|
|
( readInt
|
|
|
|
, readBool
|
|
|
|
, readPercent
|
|
|
|
, writeInt
|
|
|
|
, writeBool
|
|
|
|
, writePercent
|
|
|
|
, writePercentMin
|
|
|
|
, writePercentMax
|
|
|
|
, decPercent
|
|
|
|
, incPercent
|
2021-11-07 18:41:25 -05:00
|
|
|
-- , isReadable
|
|
|
|
-- , isWritable
|
2021-11-06 10:59:45 -04:00
|
|
|
, PermResult(..)
|
2021-11-07 18:41:25 -05:00
|
|
|
, getPermissionsSafe
|
2022-12-29 00:06:55 -05:00
|
|
|
, waitUntilExit
|
2021-11-06 10:59:45 -04:00
|
|
|
) where
|
|
|
|
|
|
|
|
import Data.Char
|
2022-12-30 10:56:09 -05:00
|
|
|
import Data.Text (pack, unpack)
|
|
|
|
import Data.Text.IO as T (readFile, writeFile)
|
2022-12-29 00:06:55 -05:00
|
|
|
|
|
|
|
import RIO
|
|
|
|
import RIO.Directory
|
|
|
|
import RIO.FilePath
|
2021-11-06 10:59:45 -04:00
|
|
|
|
|
|
|
import System.IO.Error
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | read
|
|
|
|
|
|
|
|
readInt :: (Read a, Integral a) => FilePath -> IO a
|
2022-12-30 10:56:09 -05:00
|
|
|
readInt = fmap (read . takeWhile isDigit . unpack) . T.readFile
|
2021-11-06 10:59:45 -04:00
|
|
|
|
|
|
|
readBool :: FilePath -> IO Bool
|
|
|
|
readBool = fmap (==(1 :: Int)) . readInt
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | write
|
|
|
|
|
|
|
|
writeInt :: (Show a, Integral a) => FilePath -> a -> IO ()
|
2022-12-30 10:56:09 -05:00
|
|
|
writeInt f = T.writeFile f . pack . show
|
2021-11-06 10:59:45 -04:00
|
|
|
|
|
|
|
writeBool :: FilePath -> Bool -> IO ()
|
|
|
|
writeBool f b = writeInt f ((if b then 1 else 0) :: Int)
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | percent-based read/write
|
|
|
|
--
|
|
|
|
-- "Raw" values are whatever is stored in sysfs and "percent" is the user-facing
|
|
|
|
-- value. Assume that the file being read has a min of 0 and an unchanging max
|
|
|
|
-- given by a runtime argument, which is scaled linearly to the range 0-100
|
|
|
|
-- (percent).
|
|
|
|
|
2021-11-21 00:42:40 -05:00
|
|
|
rawToPercent :: (Integral a, Integral b, Read b, RealFrac c) => (a, a) -> b -> c
|
|
|
|
rawToPercent (lower, upper) raw =
|
|
|
|
100 * (fromIntegral raw - fromIntegral lower) / fromIntegral (upper - lower)
|
|
|
|
-- rawToPercent upper raw = 100 * fromIntegral raw / fromIntegral upper
|
2021-11-06 10:59:45 -04:00
|
|
|
|
2021-11-21 00:42:40 -05:00
|
|
|
readPercent :: (Integral a, RealFrac b) => (a, a) -> FilePath -> IO b
|
|
|
|
readPercent bounds path = do
|
2021-11-06 10:59:45 -04:00
|
|
|
i <- readInt path
|
2021-11-21 00:42:40 -05:00
|
|
|
return $ rawToPercent bounds (i :: Integer)
|
2021-11-06 10:59:45 -04:00
|
|
|
|
2021-11-21 00:42:40 -05:00
|
|
|
percentToRaw :: (Integral a, RealFrac b, Integral c) => (a, a) -> b -> c
|
|
|
|
percentToRaw (lower, upper) perc = round $
|
|
|
|
fromIntegral lower + perc / 100.0 * (fromIntegral upper - fromIntegral lower)
|
2021-11-06 10:59:45 -04:00
|
|
|
|
2021-11-21 00:42:40 -05:00
|
|
|
writePercent :: (Integral a, RealFrac b) => (a, a) -> FilePath -> b -> IO b
|
|
|
|
writePercent bounds path perc = do
|
2021-11-06 10:59:45 -04:00
|
|
|
let t | perc > 100 = 100
|
|
|
|
| perc < 0 = 0
|
|
|
|
| otherwise = perc
|
2021-11-21 00:42:40 -05:00
|
|
|
writeInt path (percentToRaw bounds t :: Int)
|
2021-11-06 10:59:45 -04:00
|
|
|
return t
|
|
|
|
|
2021-11-21 00:42:40 -05:00
|
|
|
writePercentMin :: (Integral a, RealFrac b) => (a, a) -> FilePath -> IO b
|
|
|
|
writePercentMin bounds path = writePercent bounds path 0
|
2021-11-06 10:59:45 -04:00
|
|
|
|
2021-11-21 00:42:40 -05:00
|
|
|
writePercentMax :: (Integral a, RealFrac b) => (a, a) -> FilePath -> IO b
|
|
|
|
writePercentMax bounds path = writePercent bounds path 100
|
2021-11-06 10:59:45 -04:00
|
|
|
|
|
|
|
shiftPercent :: (Integral a, RealFrac b) => (b -> b -> b) -> Int -> FilePath
|
2021-11-21 00:42:40 -05:00
|
|
|
-> (a, a) -> IO b
|
|
|
|
shiftPercent f steps path bounds = writePercent bounds path . f stepsize
|
|
|
|
=<< readPercent bounds path
|
2021-11-06 10:59:45 -04:00
|
|
|
where
|
|
|
|
stepsize = 100 / fromIntegral steps
|
|
|
|
|
2021-11-21 00:42:40 -05:00
|
|
|
incPercent :: (Integral a, RealFrac b) => Int -> FilePath -> (a, a) -> IO b
|
2021-11-06 10:59:45 -04:00
|
|
|
incPercent = shiftPercent (+)
|
|
|
|
|
2021-11-21 00:42:40 -05:00
|
|
|
decPercent :: (Integral a, RealFrac b) => Int -> FilePath -> (a, a) -> IO b
|
2021-11-06 10:59:45 -04:00
|
|
|
decPercent = shiftPercent subtract -- silly (-) operator thingy error
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | permission query
|
|
|
|
|
|
|
|
data PermResult a = PermResult a | NotFoundError | PermError
|
|
|
|
deriving (Show, Eq)
|
|
|
|
|
2021-11-07 18:41:25 -05:00
|
|
|
-- instance Functor PermResult where
|
|
|
|
-- fmap f (PermResult r) = PermResult $ f r
|
|
|
|
-- fmap _ NotFoundError = NotFoundError
|
|
|
|
-- fmap _ PermError = PermError
|
2021-11-06 10:59:45 -04:00
|
|
|
|
|
|
|
getPermissionsSafe :: FilePath -> IO (PermResult Permissions)
|
|
|
|
getPermissionsSafe f = do
|
|
|
|
r <- tryIOError $ getPermissions f
|
|
|
|
return $ case r of
|
|
|
|
Right z -> PermResult z
|
|
|
|
Left (isPermissionError -> True) -> PermError
|
|
|
|
Left (isDoesNotExistError -> True) -> NotFoundError
|
|
|
|
-- the above error should be the only ones thrown by getPermission,
|
|
|
|
-- so the catchall case should never happen
|
|
|
|
_ -> error "Unknown permission error"
|
|
|
|
|
2021-11-07 18:41:25 -05:00
|
|
|
-- isReadable :: FilePath -> IO (PermResult Bool)
|
|
|
|
-- isReadable = fmap (fmap readable) . getPermissionsSafe
|
2021-11-06 10:59:45 -04:00
|
|
|
|
2021-11-07 18:41:25 -05:00
|
|
|
-- isWritable :: FilePath -> IO (PermResult Bool)
|
|
|
|
-- isWritable = fmap (fmap writable) . getPermissionsSafe
|
2022-12-29 00:06:55 -05:00
|
|
|
|
|
|
|
-- | Block until a PID has exited.
|
|
|
|
-- Use this to control flow based on a process that was not explicitly started
|
|
|
|
-- by the Haskell runtime itself, and thus has no data structures to query.
|
|
|
|
waitUntilExit :: (Show t, Num t) => t -> IO ()
|
|
|
|
waitUntilExit pid = do
|
|
|
|
res <- doesDirectoryExist $ "/proc" </> show pid
|
|
|
|
when res $ do
|
|
|
|
threadDelay 100000
|
|
|
|
waitUntilExit pid
|