73 lines
1.9 KiB
Haskell
73 lines
1.9 KiB
Haskell
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
module ACPI
|
|
( ACPIEvent(..)
|
|
, acpiMagic
|
|
, isDischarging
|
|
, runPowermon
|
|
) where
|
|
|
|
import SendXMsg
|
|
|
|
import Control.Exception
|
|
import Control.Monad
|
|
|
|
import Data.ByteString hiding (readFile)
|
|
import Data.ByteString.Char8 as C hiding (readFile)
|
|
import Data.Connection
|
|
|
|
import System.IO.Streams.Internal as S (read)
|
|
import System.IO.Streams.UnixSocket
|
|
|
|
data ACPIEvent = Power
|
|
| Sleep
|
|
| LidClose
|
|
deriving (Eq)
|
|
|
|
instance Enum ACPIEvent where
|
|
toEnum 0 = Power
|
|
toEnum 1 = Sleep
|
|
toEnum 2 = LidClose
|
|
toEnum _ = errorWithoutStackTrace "ACPI.Enum.ACPIEvent.toEnum: bad argument"
|
|
|
|
fromEnum Power = 0
|
|
fromEnum Sleep = 1
|
|
fromEnum LidClose = 2
|
|
|
|
sendACPIEvent :: ACPIEvent -> IO ()
|
|
sendACPIEvent = sendXMsg acpiMagic . show . fromEnum
|
|
|
|
parseLine :: ByteString -> Maybe ACPIEvent
|
|
parseLine line =
|
|
case splitLine line of
|
|
(_:"PBTN":_) -> Just Power
|
|
(_:"PWRF":_) -> Just Power
|
|
(_:"SLPB":_) -> Just Sleep
|
|
(_:"SBTN":_) -> Just Sleep
|
|
(_:"LID":"close":_) -> Just LidClose
|
|
_ -> Nothing
|
|
where
|
|
splitLine = C.words . C.reverse . C.dropWhile (== '\n') . C.reverse
|
|
|
|
isDischarging :: IO (Maybe Bool)
|
|
isDischarging = do
|
|
status <- try $ readFile "/sys/class/power_supply/BAT0/status"
|
|
:: IO (Either IOException String)
|
|
case status of
|
|
Left _ -> return Nothing
|
|
Right s -> return $ Just (s == "Discharging")
|
|
|
|
-- TODO use a data type that enforces strings of max length 5
|
|
acpiMagic :: String
|
|
acpiMagic = "%acpi"
|
|
|
|
runPowermon :: IO ()
|
|
runPowermon = do
|
|
-- TODO barf when the socket doesn't exist
|
|
Connection { source = s } <- connect "/var/run/acpid.socket"
|
|
forever $ readStream s
|
|
where
|
|
readStream s = do
|
|
out <- S.read s
|
|
mapM_ sendACPIEvent $ parseLine =<< out
|