2020-04-01 20:17:47 -04:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | Commands for controlling power
|
|
|
|
|
|
|
|
module XMonad.Internal.Command.Power
|
2020-03-28 18:38:38 -04:00
|
|
|
( runHibernate
|
|
|
|
, runOptimusPrompt
|
|
|
|
, runPowerOff
|
|
|
|
, runPowerPrompt
|
|
|
|
, runReboot
|
|
|
|
, runScreenLock
|
|
|
|
, runSuspend
|
|
|
|
, runSuspendPrompt
|
|
|
|
, runQuitPrompt
|
2021-06-23 20:47:41 -04:00
|
|
|
, hasBattery
|
2020-04-01 20:17:47 -04:00
|
|
|
) where
|
2020-03-28 18:38:38 -04:00
|
|
|
|
|
|
|
import Control.Arrow (first)
|
|
|
|
|
2021-06-23 20:47:41 -04:00
|
|
|
import Data.Either
|
2020-03-28 18:38:38 -04:00
|
|
|
import qualified Data.Map as M
|
|
|
|
|
|
|
|
import Graphics.X11.Types
|
|
|
|
|
2020-04-01 20:17:47 -04:00
|
|
|
import System.Directory
|
|
|
|
import System.Exit
|
2021-06-23 20:47:41 -04:00
|
|
|
import System.FilePath.Posix
|
|
|
|
import System.IO.Error
|
2021-06-20 01:01:36 -04:00
|
|
|
import System.Process
|
2020-04-01 20:17:47 -04:00
|
|
|
|
2020-03-28 18:38:38 -04:00
|
|
|
import XMonad.Core
|
2021-11-07 13:35:08 -05:00
|
|
|
import XMonad.Internal.Dependency
|
2021-06-20 01:01:36 -04:00
|
|
|
import XMonad.Internal.Process (readCreateProcessWithExitCode')
|
2020-04-01 20:17:47 -04:00
|
|
|
import qualified XMonad.Internal.Theme as T
|
2020-03-28 18:38:38 -04:00
|
|
|
import XMonad.Prompt
|
|
|
|
import XMonad.Prompt.ConfirmPrompt
|
|
|
|
|
2021-06-20 01:01:36 -04:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | Executables
|
|
|
|
|
|
|
|
myScreenlock :: String
|
|
|
|
myScreenlock = "screenlock"
|
|
|
|
|
|
|
|
myOptimusManager :: String
|
|
|
|
myOptimusManager = "optimus-manager"
|
|
|
|
|
2020-03-28 18:38:38 -04:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | Core commands
|
|
|
|
|
2021-06-20 22:26:58 -04:00
|
|
|
runScreenLock :: IO MaybeX
|
2021-06-20 01:01:36 -04:00
|
|
|
runScreenLock = spawnIfInstalled myScreenlock
|
2020-03-28 18:38:38 -04:00
|
|
|
|
|
|
|
runPowerOff :: X ()
|
|
|
|
runPowerOff = spawn "systemctl poweroff"
|
|
|
|
|
|
|
|
runSuspend :: X ()
|
|
|
|
runSuspend = spawn "systemctl suspend"
|
|
|
|
|
|
|
|
runHibernate :: X ()
|
|
|
|
runHibernate = spawn "systemctl hibernate"
|
|
|
|
|
|
|
|
runReboot :: X ()
|
|
|
|
runReboot = spawn "systemctl reboot"
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | Confirm prompt wrappers
|
|
|
|
|
|
|
|
runSuspendPrompt :: X ()
|
|
|
|
runSuspendPrompt = confirmPrompt T.promptTheme "suspend?" runSuspend
|
|
|
|
|
|
|
|
runQuitPrompt :: X ()
|
|
|
|
runQuitPrompt = confirmPrompt T.promptTheme "quit?" $ io exitSuccess
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | Nvidia Optimus
|
|
|
|
|
|
|
|
-- TODO for some reason the screen never wakes up after suspend when
|
|
|
|
-- the nvidia card is up, so block suspend if nvidia card is running
|
|
|
|
-- and warn user
|
|
|
|
isUsingNvidia :: IO Bool
|
|
|
|
isUsingNvidia = doesDirectoryExist "/sys/module/nvidia"
|
|
|
|
|
2021-06-20 01:01:36 -04:00
|
|
|
withShellOutput :: Show a => String -> (String -> a) -> IO (Maybe a)
|
|
|
|
withShellOutput cmd f = do
|
|
|
|
(rc, out, _) <- readCreateProcessWithExitCode' (shell cmd) ""
|
|
|
|
return $ case rc of
|
|
|
|
ExitSuccess -> Just $ f out
|
|
|
|
_ -> Nothing
|
|
|
|
|
|
|
|
-- TODO this will work for most of my use cases but won't work in general
|
|
|
|
-- because it assumes "Intel" means "integrated graphics" ...sorry AMD
|
|
|
|
-- TODO this is hacky AF, I really only need the lspci command and the rest
|
|
|
|
-- can be parsed with some simple string matching if I use the -vmm option
|
|
|
|
hasSwitchableGPU :: IO (Maybe Bool)
|
|
|
|
hasSwitchableGPU = withShellOutput cmd hasIntelAndOther
|
|
|
|
where
|
|
|
|
cmd = fmtCmd "lspci" ["-mm"]
|
|
|
|
#!| fmtCmd "grep" ["VGA"]
|
|
|
|
#!| fmtCmd "sed" ["'s/ \"\\([^\"]*\\)\"*/|\\1/g'"]
|
|
|
|
#!| fmtCmd "cut" ["-f3", "-d'|'"]
|
|
|
|
hasIntelAndOther out =
|
|
|
|
let vendors = lines out
|
|
|
|
ivendors = filter (== "Intel Corporation") vendors in
|
|
|
|
length vendors > length ivendors && not (null ivendors)
|
|
|
|
|
2021-06-23 20:47:41 -04:00
|
|
|
hasBattery :: IO Bool
|
|
|
|
hasBattery = do
|
|
|
|
ps <- fromRight [] <$> tryIOError (listDirectory syspath)
|
|
|
|
ts <- mapM readType ps
|
|
|
|
return $ "Battery\n" `elem` ts
|
|
|
|
where
|
|
|
|
readType p = fromRight [] <$> tryIOError (readFile $ syspath </> p </> "type")
|
|
|
|
syspath = "/sys/class/power_supply"
|
2021-06-20 01:01:36 -04:00
|
|
|
|
|
|
|
requireOptimus :: IO Bool
|
|
|
|
requireOptimus = do
|
|
|
|
s <- hasSwitchableGPU
|
|
|
|
b <- hasBattery
|
|
|
|
case (s, b) of
|
2021-06-23 20:47:41 -04:00
|
|
|
(Just True, True) -> return True
|
|
|
|
_ -> warn >> return False
|
2021-06-20 01:01:36 -04:00
|
|
|
where
|
|
|
|
warn = putStrLn
|
|
|
|
"WARNING: could not determine if switchable GPU present. Assuming not"
|
|
|
|
|
|
|
|
runOptimusPrompt' :: X ()
|
|
|
|
runOptimusPrompt' = do
|
2020-03-28 18:38:38 -04:00
|
|
|
nvidiaOn <- io isUsingNvidia
|
2021-06-20 01:01:36 -04:00
|
|
|
switch $ if nvidiaOn then "integrated" else "nvidia"
|
2020-03-28 18:38:38 -04:00
|
|
|
where
|
|
|
|
switch mode = confirmPrompt T.promptTheme (prompt mode) (cmd mode)
|
|
|
|
prompt mode = "gpu switch to " ++ mode ++ "?"
|
2020-07-03 22:16:15 -04:00
|
|
|
cmd mode = spawn $
|
2021-06-20 01:01:36 -04:00
|
|
|
"prime-offload"
|
|
|
|
#!&& unwords [myOptimusManager, "--switch", mode, "--no-confirm"]
|
2020-07-03 22:16:15 -04:00
|
|
|
#!&& "killall xmonad"
|
2020-03-28 18:38:38 -04:00
|
|
|
|
2021-06-20 01:01:36 -04:00
|
|
|
runOptimusPrompt :: IO MaybeX
|
|
|
|
runOptimusPrompt = do
|
|
|
|
g <- requireOptimus
|
2021-06-20 17:17:30 -04:00
|
|
|
if g then runIfInstalled [exe myOptimusManager] runOptimusPrompt'
|
2021-06-20 01:01:36 -04:00
|
|
|
else return Ignore
|
|
|
|
|
2020-03-28 18:38:38 -04:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | Universal power prompt
|
|
|
|
|
|
|
|
data PowerAction = Poweroff
|
|
|
|
| Shutdown
|
|
|
|
| Hibernate
|
|
|
|
| Reboot
|
|
|
|
deriving (Eq)
|
|
|
|
|
|
|
|
instance Enum PowerAction where
|
|
|
|
toEnum 0 = Poweroff
|
|
|
|
toEnum 1 = Shutdown
|
|
|
|
toEnum 2 = Hibernate
|
|
|
|
toEnum 3 = Reboot
|
|
|
|
toEnum _ = errorWithoutStackTrace "Main.Enum.PowerAction.toEnum: bad argument"
|
|
|
|
|
|
|
|
fromEnum Poweroff = 0
|
|
|
|
fromEnum Shutdown = 1
|
|
|
|
fromEnum Hibernate = 2
|
|
|
|
fromEnum Reboot = 3
|
|
|
|
|
|
|
|
data PowerPrompt = PowerPrompt
|
|
|
|
|
|
|
|
instance XPrompt PowerPrompt where
|
|
|
|
showXPrompt PowerPrompt = "(P)oweroff (S)uspend (H)ibernate (R)eboot:"
|
|
|
|
|
|
|
|
runPowerPrompt :: X ()
|
|
|
|
runPowerPrompt = mkXPrompt PowerPrompt theme comp executeAction
|
|
|
|
where
|
|
|
|
comp = mkComplFunFromList []
|
|
|
|
theme = T.promptTheme { promptKeymap = keymap }
|
|
|
|
keymap = M.fromList
|
|
|
|
$ ((controlMask, xK_g), quit) :
|
|
|
|
map (first $ (,) 0)
|
|
|
|
[ (xK_p, sendAction Poweroff)
|
|
|
|
, (xK_s, sendAction Shutdown)
|
|
|
|
, (xK_h, sendAction Hibernate)
|
|
|
|
, (xK_r, sendAction Reboot)
|
|
|
|
, (xK_Return, quit)
|
|
|
|
, (xK_Escape, quit)
|
|
|
|
]
|
|
|
|
sendAction a = setInput (show $ fromEnum a) >> setSuccess True >> setDone True
|
|
|
|
executeAction a = case toEnum $ read a of
|
|
|
|
Poweroff -> runPowerOff
|
2021-06-19 00:17:47 -04:00
|
|
|
Shutdown -> (io runScreenLock >>= whenInstalled) >> runSuspend
|
|
|
|
Hibernate -> (io runScreenLock >>= whenInstalled) >> runHibernate
|
2020-03-28 18:38:38 -04:00
|
|
|
Reboot -> runReboot
|