ENH make signal happen when method is called

This commit is contained in:
Nathan Dwarshuis 2020-03-20 23:47:02 -04:00
parent 6ae7ca5df1
commit c96bcc0bfa
3 changed files with 101 additions and 124 deletions

View File

@ -13,7 +13,7 @@ module DBus.IntelBacklight
, matchSignal
) where
import Control.Monad (forM_)
import Control.Monad (void)
import Data.Char
@ -25,6 +25,7 @@ import Data.Text.IO as T (readFile, writeFile)
import DBus
import DBus.Client
import DBus.Internal
--------------------------------------------------------------------------------
-- | Low level sysfs functions
@ -69,6 +70,7 @@ getRawBrightness = readFileInt curFile
setRawBrightness :: RawBrightness -> IO ()
setRawBrightness = T.writeFile curFile . pack . show
-- TODO this has rounding errors that make the steps uneven
rawToNorm :: RawBrightness -> RawBrightness -> Brightness
rawToNorm maxRaw curRaw = fromIntegral
$ (curRaw - 1) * maxNorm `div` (maxRaw - 1)
@ -105,69 +107,48 @@ setBrightness maxRaw newNorm = do
-- integer and emit a signal with the same brightness value. Additionally, there
-- is one method to get the current brightness.
brPath :: ObjectPath
brPath = "/intelbacklight"
path :: ObjectPath
path = "/intelbacklight"
brInterface :: InterfaceName
brInterface = "org.xmonad.Brightness"
interface :: InterfaceName
interface = "org.xmonad.Brightness"
brCurrentBrightness :: MemberName
brCurrentBrightness = "CurrentBrightness"
memCurrentBrightness :: MemberName
memCurrentBrightness = "CurrentBrightness"
brGetBrightness :: MemberName
brGetBrightness = "GetBrightness"
memGetBrightness :: MemberName
memGetBrightness = "GetBrightness"
brMaxBrightness :: MemberName
brMaxBrightness = "MaxBrightness"
memMaxBrightness :: MemberName
memMaxBrightness = "MaxBrightness"
brMinBrightness :: MemberName
brMinBrightness = "MinBrightness"
memMinnBrightness :: MemberName
memMinnBrightness = "MinBrightness"
brIncBrightness :: MemberName
brIncBrightness = "IncBrightness"
memIncBrightness :: MemberName
memIncBrightness = "IncBrightness"
brDecBrightness :: MemberName
brDecBrightness = "DecBrightness"
memDecBrightness :: MemberName
memDecBrightness = "DecBrightness"
brSignal :: Signal
brSignal = signal brPath brInterface brCurrentBrightness
brSignal = signal path interface memCurrentBrightness
-- { signalDestination = Just "org.xmonad" }
brMatcher :: MatchRule
brMatcher = matchAny
{ matchPath = Just brPath
, matchInterface = Just brInterface
, matchMember = Just brCurrentBrightness
{ matchPath = Just path
, matchInterface = Just interface
, matchMember = Just memCurrentBrightness
}
callBacklight :: Client -> MemberName -> IO (Maybe [Variant])
callBacklight client method = do
-- TODO this will throw a clienterror if it cannot connect at all
reply <- call client (methodCall brPath brInterface method)
{ methodCallDestination = Just "org.xmonad" }
return $ case reply of
Left _ -> Nothing
Right ret -> Just $ methodReturnBody ret
callBacklight' :: MemberName -> IO (Maybe Brightness)
callBacklight' method = do
client <- connectSession
body <- callBacklight client method
-- TODO this is a bit convoluted...I return the body in the reply of
-- the method call and feed that to the signal and then return the
-- body (the latter is not really necessary since the only things
-- that read the backlight status either use the signal or call
-- GetBrightness directly
forM_ body $ emitBrightness client
return $ body >>= signalBrightness
callBacklight :: MemberName -> IO ()
callBacklight method = void $ callMethod $ methodCall path interface method
emitBrightness :: Client -> [Variant] -> IO ()
emitBrightness client body =
emit client $ brSignal { signalBody = body }
signalBrightness :: [Variant] -> Maybe Brightness
signalBrightness [b] = fromVariant b :: Maybe Brightness
signalBrightness _ = Nothing
bodyGetBrightness :: [Variant] -> Maybe Brightness
bodyGetBrightness [b] = fromVariant b :: Maybe Brightness
bodyGetBrightness _ = Nothing
-- | Exported haskell API
@ -175,36 +156,39 @@ exportIntelBacklight :: Client -> IO ()
exportIntelBacklight client = do
maxval <- getMaxRawBrightness -- assume the max value will never change
let stepsize = maxBrightness `div` steps
export client brPath defaultInterface
{ interfaceName = brInterface
let emit' = emitBrightness client
export client path defaultInterface
{ interfaceName = interface
, interfaceMethods =
[ autoMethod brMaxBrightness (setBrightness maxval maxBrightness)
, autoMethod brMinBrightness (setBrightness maxval 0)
, autoMethod brIncBrightness (changeBrightness maxval stepsize)
, autoMethod brDecBrightness (changeBrightness maxval (-stepsize))
, autoMethod brGetBrightness (getBrightness maxval)
[ autoMethod memMaxBrightness $ emit' =<< setBrightness maxval maxBrightness
, autoMethod memMinnBrightness $ emit' =<< setBrightness maxval 0
, autoMethod memIncBrightness $ emit' =<< changeBrightness maxval stepsize
, autoMethod memDecBrightness $ emit' =<< changeBrightness maxval (-stepsize)
, autoMethod memGetBrightness $ getBrightness maxval
]
}
callMaxBrightness :: IO (Maybe Brightness)
callMaxBrightness = callBacklight' brMaxBrightness
emitBrightness :: Client -> Brightness -> IO ()
emitBrightness client cur = emit client $ brSignal { signalBody = [toVariant cur] }
callMinBrightness :: IO (Maybe Brightness)
callMinBrightness = callBacklight' brMinBrightness
callMaxBrightness :: IO ()
callMaxBrightness = callBacklight memMaxBrightness
callIncBrightness :: IO (Maybe Brightness)
callIncBrightness = callBacklight' brIncBrightness
callMinBrightness :: IO ()
callMinBrightness = callBacklight memMinnBrightness
callDecBrightness :: IO (Maybe Brightness)
callDecBrightness = callBacklight' brDecBrightness
callIncBrightness :: IO ()
callIncBrightness = callBacklight memIncBrightness
callDecBrightness :: IO ()
callDecBrightness = callBacklight memDecBrightness
callGetBrightness :: IO (Maybe Brightness)
callGetBrightness = do
client <- connectSession
body <- callBacklight client brGetBrightness
return $ body >>= signalBrightness
reply <- callMethod $ methodCall path interface memGetBrightness
return $ reply >>= bodyGetBrightness
matchSignal :: (Maybe Brightness -> IO ()) -> IO SignalHandler
matchSignal cb = do
client <- connectSession
addMatch client brMatcher $ cb . signalBrightness . signalBody
addMatch client brMatcher $ cb . bodyGetBrightness . signalBody

View File

@ -2,35 +2,19 @@
module DBus.Internal where
import Control.Monad (forM_)
import DBus
import DBus.Client
callMethod' :: Client -> MethodCall -> IO (Maybe [Variant])
callMethod' client mc = do
-- TODO not all methods warrent that we wait for a reply?
callMethod :: MethodCall -> IO (Maybe [Variant])
callMethod mc = do
client <- connectSession
-- TODO handle clienterrors here
reply <- call client mc { methodCallDestination = Just "org.xmonad" }
return $ case reply of
Left _ -> Nothing
Right ret -> Just $ methodReturnBody ret
callMethod :: MethodCall -> ([Variant] -> Maybe a) -> IO (Maybe a)
callMethod mc procBody = do
client <- connectSession
body <- callMethod' client mc
return $ body >>= procBody
callMethodEmit :: MethodCall
-> ([Variant] -> Maybe a)
-> ([Variant] -> Signal)
-> IO (Maybe a)
callMethodEmit mc procBody bodyToSignal = do
client <- connectSession
body <- callMethod' client mc
forM_ body $ emit client . bodyToSignal
return $ body >>= procBody
addMatchCallback :: MatchRule -> ([Variant] -> IO ()) -> IO SignalHandler
addMatchCallback rule cb = do
client <- connectSession

View File

@ -3,7 +3,14 @@
--------------------------------------------------------------------------------
-- | DBus module for X11 screensave/DPMS control
module DBus.Screensaver where
module DBus.Screensaver
( exportScreensaver
, callToggle
, callQuery
, matchSignal
) where
import Control.Monad (void)
import DBus
import DBus.Client
@ -46,59 +53,61 @@ query = do
--------------------------------------------------------------------------------
-- | DBus Interface
--
-- Define two methods to enable/disable the screensaver. These methods will
-- emit signals with the state when called. Define another method to get the
-- current state.
-- Define a methods to toggle the screensaver. This methods will emit signal
-- with the new state when called. Define another method to get the current
-- state.
ssPath :: ObjectPath
ssPath = "/screensaver"
path :: ObjectPath
path = "/screensaver"
ssInterface :: InterfaceName
ssInterface = "org.xmonad.Screensaver"
interface :: InterfaceName
interface = "org.xmonad.Screensaver"
ssState :: MemberName
ssState = "State"
memState :: MemberName
memState = "State"
ssToggle :: MemberName
ssToggle = "Toggle"
memToggle :: MemberName
memToggle = "Toggle"
ssQuery :: MemberName
ssQuery = "Query"
memQuery :: MemberName
memQuery = "Query"
ssSignal :: Signal
ssSignal = signal ssPath ssInterface ssState
sigCurrentState :: Signal
sigCurrentState = signal path interface memState
ssMatcher :: MatchRule
ssMatcher = matchAny
{ matchPath = Just ssPath
, matchInterface = Just ssInterface
, matchMember = Just ssState
ruleCurrentState :: MatchRule
ruleCurrentState = matchAny
{ matchPath = Just path
, matchInterface = Just interface
, matchMember = Just memState
}
emitState :: Client -> SSState -> IO ()
emitState client sss = emit client $ sigCurrentState { signalBody = [toVariant sss] }
bodyGetCurrentState :: [Variant] -> Maybe SSState
bodyGetCurrentState [b] = fromVariant b :: Maybe SSState
bodyGetCurrentState _ = Nothing
-- | Exported haskell API
exportScreensaver :: Client -> IO ()
exportScreensaver client =
export client ssPath defaultInterface
{ interfaceName = ssInterface
export client path defaultInterface
{ interfaceName = interface
, interfaceMethods =
[ autoMethod ssToggle toggle
, autoMethod ssQuery query
[ autoMethod memToggle $ emitState client =<< toggle
, autoMethod memQuery query
]
}
callToggle :: IO (Maybe SSState)
callToggle = callMethodEmit mc bodyState sig
where
mc = methodCall ssPath ssInterface ssToggle
sig b = ssSignal { signalBody = b }
bodyState :: [Variant] -> Maybe SSState
bodyState [b] = fromVariant b :: Maybe SSState
bodyState _ = Nothing
callToggle :: IO ()
callToggle = void $ callMethod $ methodCall path interface memToggle
callQuery :: IO (Maybe SSState)
callQuery = callMethod mc bodyState
where
mc = methodCall ssPath ssInterface ssQuery
callQuery = do
reply <- callMethod $ methodCall path interface memQuery
return $ reply >>= bodyGetCurrentState
matchSignal :: (Maybe SSState -> IO ()) -> IO SignalHandler
matchSignal cb = addMatchCallback ssMatcher $ cb . bodyState
matchSignal cb = addMatchCallback ruleCurrentState $ cb . bodyGetCurrentState