2020-03-20 18:14:54 -04:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | DBus module for Intel Backlight control
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-04-01 20:17:47 -04:00
|
|
|
module XMonad.Internal.DBus.IntelBacklight
|
2020-03-20 15:41:13 -04:00
|
|
|
( callDecBrightness
|
|
|
|
, callGetBrightness
|
|
|
|
, callIncBrightness
|
|
|
|
, callMaxBrightness
|
|
|
|
, callMinBrightness
|
|
|
|
, exportIntelBacklight
|
|
|
|
, matchSignal
|
2021-06-20 20:54:23 -04:00
|
|
|
, hasBacklight
|
2021-06-21 23:41:57 -04:00
|
|
|
, blPath
|
2021-06-20 20:54:23 -04:00
|
|
|
, BacklightControls(..)
|
2020-03-20 15:41:13 -04:00
|
|
|
) where
|
|
|
|
|
2020-04-01 20:17:47 -04:00
|
|
|
import Control.Monad (void)
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-04-01 20:17:47 -04:00
|
|
|
import Data.Char
|
2021-06-20 20:54:23 -04:00
|
|
|
import Data.Either
|
2020-04-01 20:17:47 -04:00
|
|
|
import Data.Int (Int16, Int32)
|
|
|
|
import Data.Text (pack, unpack)
|
|
|
|
import Data.Text.IO as T (readFile, writeFile)
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-04-01 20:17:47 -04:00
|
|
|
import DBus
|
|
|
|
import DBus.Client
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2021-06-20 20:54:23 -04:00
|
|
|
import System.Directory
|
|
|
|
import System.FilePath.Posix
|
|
|
|
import System.IO.Error
|
|
|
|
|
2020-04-01 20:17:47 -04:00
|
|
|
import XMonad.Internal.DBus.Common
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-03-20 18:14:54 -04:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | Low level sysfs functions
|
|
|
|
--
|
|
|
|
-- Distinguish between "raw" brightness "normalized" brightness with two type
|
|
|
|
-- synonyms. The former is the value read directly in sysfs and generally goes
|
|
|
|
-- from 1 (min brightness) to some multiple 1000's number (note that raw values
|
|
|
|
-- of 0 turn the monitor off). The latter is the raw brightness scaled from 0 to
|
|
|
|
-- 10000 (which can easily be converted to a percent).
|
|
|
|
|
2020-04-01 20:17:47 -04:00
|
|
|
-- use strict IO here, the data in these files is literally 1-10 bytes
|
|
|
|
|
2020-03-20 18:14:54 -04:00
|
|
|
-- TODO this is hacky but not sure if there is a cleaner way to enforce type
|
|
|
|
-- checking between these without making two new types and adding Integral
|
|
|
|
-- instances to both of them
|
|
|
|
type Brightness = Int16
|
|
|
|
|
|
|
|
type RawBrightness = Int32
|
|
|
|
|
|
|
|
maxBrightness :: Brightness
|
|
|
|
maxBrightness = 10000
|
|
|
|
|
|
|
|
steps :: Brightness
|
|
|
|
steps = 16
|
|
|
|
|
2020-03-20 15:41:13 -04:00
|
|
|
backlightDir :: FilePath
|
|
|
|
backlightDir = "/sys/class/backlight/intel_backlight/"
|
|
|
|
|
2021-06-20 20:54:23 -04:00
|
|
|
maxFile :: FilePath
|
|
|
|
maxFile = backlightDir </> "max_brightness"
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2021-06-20 20:54:23 -04:00
|
|
|
curFile :: FilePath
|
|
|
|
curFile = backlightDir </> "brightness"
|
2020-03-20 15:41:13 -04:00
|
|
|
|
|
|
|
readFileInt :: FilePath -> IO RawBrightness
|
2020-03-20 18:14:54 -04:00
|
|
|
readFileInt file = read . takeWhile isDigit . unpack <$> T.readFile file
|
2020-03-20 15:41:13 -04:00
|
|
|
|
|
|
|
getMaxRawBrightness :: IO RawBrightness
|
|
|
|
getMaxRawBrightness = readFileInt maxFile
|
|
|
|
|
|
|
|
getRawBrightness :: IO RawBrightness
|
|
|
|
getRawBrightness = readFileInt curFile
|
|
|
|
|
|
|
|
setRawBrightness :: RawBrightness -> IO ()
|
|
|
|
setRawBrightness = T.writeFile curFile . pack . show
|
|
|
|
|
2020-03-20 23:47:02 -04:00
|
|
|
-- TODO this has rounding errors that make the steps uneven
|
2020-03-20 15:41:13 -04:00
|
|
|
rawToNorm :: RawBrightness -> RawBrightness -> Brightness
|
|
|
|
rawToNorm maxRaw curRaw = fromIntegral
|
|
|
|
$ (curRaw - 1) * maxNorm `div` (maxRaw - 1)
|
|
|
|
where
|
|
|
|
maxNorm = fromIntegral maxBrightness :: Int32
|
|
|
|
|
|
|
|
normToRaw :: RawBrightness -> Brightness -> RawBrightness
|
2020-03-20 18:14:54 -04:00
|
|
|
normToRaw maxRaw curNorm = curNorm' * (maxRaw - 1) `div` maxNorm + 1
|
2020-03-20 15:41:13 -04:00
|
|
|
where
|
2020-03-20 18:14:54 -04:00
|
|
|
curNorm' = fromIntegral curNorm :: Int32
|
2020-03-20 15:41:13 -04:00
|
|
|
maxNorm = fromIntegral maxBrightness :: Int32
|
|
|
|
|
|
|
|
truncateNorm :: Brightness -> Brightness
|
|
|
|
truncateNorm = min maxBrightness . max 0
|
|
|
|
|
|
|
|
getBrightness :: RawBrightness -> IO Brightness
|
|
|
|
getBrightness maxRaw = rawToNorm maxRaw <$> getRawBrightness
|
|
|
|
|
|
|
|
changeBrightness :: RawBrightness -> Brightness -> IO Brightness
|
|
|
|
changeBrightness maxRaw delta = setBrightness maxRaw
|
2021-06-20 20:54:23 -04:00
|
|
|
. (+ delta) =<< getBrightness maxRaw
|
2020-03-20 15:41:13 -04:00
|
|
|
|
|
|
|
setBrightness :: RawBrightness -> Brightness -> IO Brightness
|
|
|
|
setBrightness maxRaw newNorm = do
|
|
|
|
let newNorm' = truncateNorm newNorm
|
|
|
|
setRawBrightness $ normToRaw maxRaw newNorm'
|
|
|
|
return newNorm'
|
|
|
|
|
2021-06-20 20:54:23 -04:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | Access checks
|
|
|
|
|
|
|
|
-- | determine if backlight is accessible/present
|
|
|
|
-- Right True -> backlight accessible and present
|
|
|
|
-- Right False -> backlight not present
|
|
|
|
-- Left x -> backlight present but could not access (x explaining why)
|
|
|
|
hasBacklight' :: IO (Either String Bool)
|
|
|
|
hasBacklight' = do
|
|
|
|
mx <- doesFileExist maxFile
|
|
|
|
cx <- doesFileExist curFile
|
|
|
|
if not $ mx || cx
|
|
|
|
then return $ Right False
|
|
|
|
else do
|
|
|
|
mp <- tryIOError $ readable <$> getPermissions maxFile
|
|
|
|
cp <- tryIOError $ (\p -> writable p && readable p) <$> getPermissions curFile
|
|
|
|
return $ case (mp, cp) of
|
|
|
|
(Right True, Right True) -> Right True
|
|
|
|
(Right _, Right _) -> Left "Insufficient permissions for backlight files"
|
|
|
|
_ -> Left "Could not determine backlight file permissions"
|
|
|
|
|
|
|
|
msg :: Either String Bool -> IO ()
|
|
|
|
msg (Right True) = return ()
|
|
|
|
msg (Right False) = print ("No backlight detected. Controls disabled" :: String)
|
|
|
|
msg (Left m) = print ("WARNING: " ++ m)
|
|
|
|
|
|
|
|
hasBacklightMsg :: IO Bool
|
|
|
|
hasBacklightMsg = do
|
|
|
|
b <- hasBacklight'
|
|
|
|
msg b
|
|
|
|
return $ fromRight False b
|
|
|
|
|
|
|
|
hasBacklight :: IO Bool
|
|
|
|
hasBacklight = fromRight False <$> hasBacklight'
|
|
|
|
|
2020-03-20 18:14:54 -04:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | DBus interface
|
|
|
|
--
|
|
|
|
-- Define four methods to increase, decrease, maximize, or minimize the
|
|
|
|
-- brightness. These methods will all return the current brightness as a 32-bit
|
|
|
|
-- integer and emit a signal with the same brightness value. Additionally, there
|
|
|
|
-- is one method to get the current brightness.
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2021-06-21 23:41:57 -04:00
|
|
|
blPath :: ObjectPath
|
|
|
|
blPath = objectPath_ "/intelbacklight"
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-03-20 23:47:02 -04:00
|
|
|
interface :: InterfaceName
|
2021-06-21 23:41:57 -04:00
|
|
|
interface = interfaceName_ "org.xmonad.Brightness"
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-03-20 23:47:02 -04:00
|
|
|
memCurrentBrightness :: MemberName
|
2021-06-21 23:41:57 -04:00
|
|
|
memCurrentBrightness = memberName_ "CurrentBrightness"
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-03-20 23:47:02 -04:00
|
|
|
memGetBrightness :: MemberName
|
2021-06-21 23:41:57 -04:00
|
|
|
memGetBrightness = memberName_ "GetBrightness"
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-03-20 23:47:02 -04:00
|
|
|
memMaxBrightness :: MemberName
|
2021-06-21 23:41:57 -04:00
|
|
|
memMaxBrightness = memberName_ "MaxBrightness"
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2021-06-20 20:54:23 -04:00
|
|
|
memMinBrightness :: MemberName
|
2021-06-21 23:41:57 -04:00
|
|
|
memMinBrightness = memberName_ "MinBrightness"
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-03-20 23:47:02 -04:00
|
|
|
memIncBrightness :: MemberName
|
2021-06-21 23:41:57 -04:00
|
|
|
memIncBrightness = memberName_ "IncBrightness"
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-03-20 23:47:02 -04:00
|
|
|
memDecBrightness :: MemberName
|
2021-06-21 23:41:57 -04:00
|
|
|
memDecBrightness = memberName_ "DecBrightness"
|
2020-03-20 15:41:13 -04:00
|
|
|
|
|
|
|
brSignal :: Signal
|
2021-06-21 23:41:57 -04:00
|
|
|
brSignal = signal blPath interface memCurrentBrightness
|
2020-03-20 15:41:13 -04:00
|
|
|
-- { signalDestination = Just "org.xmonad" }
|
|
|
|
|
|
|
|
brMatcher :: MatchRule
|
|
|
|
brMatcher = matchAny
|
2021-06-21 23:41:57 -04:00
|
|
|
{ matchPath = Just blPath
|
2020-03-20 23:47:02 -04:00
|
|
|
, matchInterface = Just interface
|
|
|
|
, matchMember = Just memCurrentBrightness
|
2020-03-20 15:41:13 -04:00
|
|
|
}
|
|
|
|
|
2020-03-20 23:47:02 -04:00
|
|
|
callBacklight :: MemberName -> IO ()
|
2021-06-21 23:41:57 -04:00
|
|
|
callBacklight method = void $ callMethod $ methodCall blPath interface method
|
2020-03-20 23:47:02 -04:00
|
|
|
|
|
|
|
bodyGetBrightness :: [Variant] -> Maybe Brightness
|
|
|
|
bodyGetBrightness [b] = fromVariant b :: Maybe Brightness
|
|
|
|
bodyGetBrightness _ = Nothing
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-04-01 22:06:00 -04:00
|
|
|
--------------------------------------------------------------------------------
|
2020-03-20 18:14:54 -04:00
|
|
|
-- | Exported haskell API
|
|
|
|
|
2021-06-20 20:54:23 -04:00
|
|
|
data BacklightControls = BacklightControls
|
|
|
|
{ backlightMax :: IO ()
|
|
|
|
, backlightMin :: IO ()
|
|
|
|
, backlightUp :: IO ()
|
|
|
|
, backlightDown :: IO ()
|
|
|
|
}
|
|
|
|
|
|
|
|
exportIntelBacklight :: Client -> IO (Maybe BacklightControls)
|
2020-03-20 18:14:54 -04:00
|
|
|
exportIntelBacklight client = do
|
2021-06-20 20:54:23 -04:00
|
|
|
b <- hasBacklightMsg
|
|
|
|
if b then Just <$> exportIntelBacklight' client else return Nothing
|
|
|
|
|
|
|
|
exportIntelBacklight' :: Client -> IO BacklightControls
|
|
|
|
exportIntelBacklight' client = do
|
2020-03-20 18:14:54 -04:00
|
|
|
maxval <- getMaxRawBrightness -- assume the max value will never change
|
|
|
|
let stepsize = maxBrightness `div` steps
|
2020-03-20 23:47:02 -04:00
|
|
|
let emit' = emitBrightness client
|
2021-06-21 23:41:57 -04:00
|
|
|
export client blPath defaultInterface
|
2020-03-20 23:47:02 -04:00
|
|
|
{ interfaceName = interface
|
2020-03-20 18:14:54 -04:00
|
|
|
, interfaceMethods =
|
2020-03-20 23:47:02 -04:00
|
|
|
[ autoMethod memMaxBrightness $ emit' =<< setBrightness maxval maxBrightness
|
2021-06-20 20:54:23 -04:00
|
|
|
, autoMethod memMinBrightness $ emit' =<< setBrightness maxval 0
|
2020-03-20 23:47:02 -04:00
|
|
|
, autoMethod memIncBrightness $ emit' =<< changeBrightness maxval stepsize
|
|
|
|
, autoMethod memDecBrightness $ emit' =<< changeBrightness maxval (-stepsize)
|
|
|
|
, autoMethod memGetBrightness $ getBrightness maxval
|
2020-03-20 18:14:54 -04:00
|
|
|
]
|
|
|
|
}
|
2021-06-20 20:54:23 -04:00
|
|
|
return $ BacklightControls
|
|
|
|
{ backlightMax = callMaxBrightness
|
|
|
|
, backlightMin = callMinBrightness
|
|
|
|
, backlightUp = callIncBrightness
|
|
|
|
, backlightDown = callDecBrightness
|
|
|
|
}
|
2020-03-20 18:14:54 -04:00
|
|
|
|
2020-03-20 23:47:02 -04:00
|
|
|
emitBrightness :: Client -> Brightness -> IO ()
|
|
|
|
emitBrightness client cur = emit client $ brSignal { signalBody = [toVariant cur] }
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-03-20 23:47:02 -04:00
|
|
|
callMaxBrightness :: IO ()
|
|
|
|
callMaxBrightness = callBacklight memMaxBrightness
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-03-20 23:47:02 -04:00
|
|
|
callMinBrightness :: IO ()
|
2021-06-20 20:54:23 -04:00
|
|
|
callMinBrightness = callBacklight memMinBrightness
|
2020-03-20 15:41:13 -04:00
|
|
|
|
2020-03-20 23:47:02 -04:00
|
|
|
callIncBrightness :: IO ()
|
|
|
|
callIncBrightness = callBacklight memIncBrightness
|
|
|
|
|
|
|
|
callDecBrightness :: IO ()
|
|
|
|
callDecBrightness = callBacklight memDecBrightness
|
2020-03-20 15:41:13 -04:00
|
|
|
|
|
|
|
callGetBrightness :: IO (Maybe Brightness)
|
|
|
|
callGetBrightness = do
|
2021-06-21 23:41:57 -04:00
|
|
|
reply <- callMethod $ methodCall blPath interface memGetBrightness
|
2020-03-20 23:47:02 -04:00
|
|
|
return $ reply >>= bodyGetBrightness
|
2020-03-20 15:41:13 -04:00
|
|
|
|
|
|
|
matchSignal :: (Maybe Brightness -> IO ()) -> IO SignalHandler
|
|
|
|
matchSignal cb = do
|
|
|
|
client <- connectSession
|
2020-03-20 23:47:02 -04:00
|
|
|
addMatch client brMatcher $ cb . bodyGetBrightness . signalBody
|