xmonad-config/lib/XMonad/Internal/Concurrent/ACPIEvent.hs

122 lines
3.6 KiB
Haskell
Raw Normal View History

2022-12-30 14:58:23 -05:00
{-# LANGUAGE LambdaCase #-}
2020-03-22 23:20:10 -04:00
{-# LANGUAGE OverloadedStrings #-}
2020-04-01 22:06:00 -04:00
--------------------------------------------------------------------------------
2022-12-30 14:58:23 -05:00
-- Concurrent module to handle events from acpid
2020-04-01 22:06:00 -04:00
2020-04-01 20:17:47 -04:00
module XMonad.Internal.Concurrent.ACPIEvent
2020-04-01 22:06:00 -04:00
( runPowermon
, runHandleACPI
2022-12-30 14:58:23 -05:00
)
where
2023-01-01 18:33:02 -05:00
import Data.Internal.XIO
2022-12-30 14:58:23 -05:00
import Network.Socket
import Network.Socket.ByteString
import RIO
import qualified RIO.ByteString as B
import XMonad.Core
import XMonad.Internal.Command.Power
import XMonad.Internal.Concurrent.ClientMessage
import XMonad.Internal.Shell
import XMonad.Internal.Theme (FontBuilder)
2020-03-28 18:38:38 -04:00
2020-04-01 22:06:00 -04:00
--------------------------------------------------------------------------------
2022-12-30 14:58:23 -05:00
-- Data structure to hold the ACPI events I care about
2020-04-01 22:06:00 -04:00
--
-- Enumerate so these can be converted to strings and back when sent in a
-- ClientMessage event to X
2022-12-30 14:58:23 -05:00
data ACPIEvent
= Power
| Sleep
| LidClose
deriving (Eq)
2020-03-22 23:20:10 -04:00
instance Enum ACPIEvent where
toEnum 0 = Power
toEnum 1 = Sleep
toEnum 2 = LidClose
2020-03-22 23:46:56 -04:00
toEnum _ = errorWithoutStackTrace "ACPI.Enum.ACPIEvent.toEnum: bad argument"
2020-03-22 23:20:10 -04:00
2022-12-30 14:58:23 -05:00
fromEnum Power = 0
fromEnum Sleep = 1
2020-03-22 23:20:10 -04:00
fromEnum LidClose = 2
2020-04-01 22:06:00 -04:00
--------------------------------------------------------------------------------
2022-12-30 14:58:23 -05:00
-- Internal functions
2020-03-22 23:20:10 -04:00
2020-04-01 22:06:00 -04:00
-- | Convert a string to an ACPI event (this string is assumed to come from
-- the acpid socket)
2020-03-22 23:20:10 -04:00
parseLine :: ByteString -> Maybe ACPIEvent
parseLine line =
case splitLine line of
2022-12-30 14:58:23 -05:00
(_ : "PBTN" : _) -> Just Power
(_ : "PWRF" : _) -> Just Power
(_ : "SLPB" : _) -> Just Sleep
(_ : "SBTN" : _) -> Just Sleep
(_ : "LID" : "close" : _) -> Just LidClose
_ -> Nothing
2020-03-22 23:20:10 -04:00
where
2022-12-28 20:11:06 -05:00
splitLine = B.split space . B.reverse . B.dropWhile (== newline) . B.reverse
newline = 10
space = 32
2020-03-13 20:50:13 -04:00
2020-04-01 22:06:00 -04:00
-- | Send an ACPIEvent to the X server as a ClientMessage
sendACPIEvent :: ACPIEvent -> IO ()
sendACPIEvent = sendXMsg ACPI . show . fromEnum
2020-03-13 20:50:13 -04:00
isDischarging :: IO (Maybe Bool)
isDischarging = do
2022-12-28 20:11:06 -05:00
status <- tryIO $ B.readFile "/sys/class/power_supply/BAT0/status"
2020-03-13 20:50:13 -04:00
case status of
2022-12-30 14:58:23 -05:00
Left _ -> return Nothing
2020-03-13 20:50:13 -04:00
Right s -> return $ Just (s == "Discharging")
2020-03-22 23:20:10 -04:00
2021-06-22 00:46:29 -04:00
listenACPI :: IO ()
listenACPI = do
2022-12-28 20:11:06 -05:00
sock <- socket AF_UNIX Stream defaultProtocol
connect sock $ SockAddrUnix acpiPath
forever $ do
out <- recv sock 1024
mapM_ sendACPIEvent $ parseLine out
2021-06-22 00:46:29 -04:00
acpiPath :: FilePath
acpiPath = "/var/run/acpid.socket"
2022-07-02 17:09:21 -04:00
socketDep :: IOTree_
socketDep = Only_ $ pathR acpiPath [Package Official "acpid"]
2020-04-01 22:06:00 -04:00
-- | Handle ClientMessage event containing and ACPI event (to be used in
-- Xmonad's event hook)
2022-07-02 17:09:21 -04:00
handleACPI :: FontBuilder -> X () -> String -> X ()
handleACPI fb lock tag = do
2020-03-28 18:38:38 -04:00
let acpiTag = toEnum <$> readMaybe tag :: Maybe ACPIEvent
forM_ acpiTag $ \case
2022-07-02 17:09:21 -04:00
Power -> powerPrompt lock fb
Sleep -> suspendPrompt fb
2020-03-28 18:38:38 -04:00
LidClose -> do
status <- io isDischarging
2021-06-22 00:46:29 -04:00
-- only run suspend if battery exists and is discharging
forM_ status $ flip when runSuspend
2021-11-19 22:42:19 -05:00
lock
2021-06-22 00:46:29 -04:00
2022-06-26 20:20:49 -04:00
--------------------------------------------------------------------------------
2022-12-30 14:58:23 -05:00
-- Exported API
2022-06-26 20:20:49 -04:00
-- | Spawn a new thread that will listen for ACPI events on the acpid socket
-- and send ClientMessage events when it receives them
runPowermon :: SometimesIO
runPowermon = sometimesIO_ "ACPI event monitor" "acpid" socketDep $ io listenACPI
2022-06-26 20:20:49 -04:00
runHandleACPI :: Always (String -> X ())
2022-07-02 17:09:21 -04:00
runHandleACPI = Always "ACPI event handler" $ Option sf fallback
2022-06-26 20:20:49 -04:00
where
sf = Subfeature withLock "acpid prompt"
2022-12-30 14:58:23 -05:00
withLock =
IORoot (uncurry handleACPI) $
And12 (,) promptFontDep $
Only $
IOSometimes runScreenLock id
2022-07-03 18:23:32 -04:00
fallback = Always_ $ FallbackAlone $ const skip