musicScroll-0.3.3: src/MusicScroll/DBusSignals.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module MusicScroll.DBusSignals
( mediaPropChangeRule,
waitForChange,
changeMusicClient,
)
where
import Control.Concurrent.STM (atomically)
import Control.Concurrent.STM.TMVar (TMVar, newEmptyTMVar, putTMVar, takeTMVar)
import Control.Monad.State.Class (MonadState (..))
import DBus
import DBus.Client
import Data.Foldable (find)
import Data.Maybe (fromJust)
import MusicScroll.ConnState
import MusicScroll.DBusNames
import Pipes
mediaPropChangeRule, busNameAddedRule :: MatchRule
mediaPropChangeRule =
matchAny
{ matchPath = pure mediaObject,
matchInterface = pure "org.freedesktop.DBus.Properties",
matchMember = pure "PropertiesChanged"
}
busNameAddedRule =
matchAny
{ matchSender = pure dbusBus, -- unique name
matchPath = pure "/org/freedesktop/DBus",
matchInterface = pure "org.freedesktop.DBus",
matchMember = pure "NameOwnerChanged"
}
waitForChange :: (MonadState ConnState m, MonadIO m) => MatchRule -> m ()
waitForChange rule = do
(ConnState client _) <- get
liftIO $ do
trigger <- atomically newEmptyTMVar
disarmHandler <- armSignal client trigger rule
_ <- atomically $ takeTMVar trigger
removeMatch client disarmHandler
armSignal :: Client -> TMVar () -> MatchRule -> IO SignalHandler
armSignal client trigger rule =
addMatch client rule (\_ -> atomically (putTMVar trigger ()))
changeMusicClient :: (MonadState ConnState m, MonadIO m) => m ()
changeMusicClient =
do
(ConnState client _) <- get
availableStatus <- liftIO $ traverse (checkName client) allBuses
let taggedBuses = zip allBuses availableStatus
case fst <$> find snd taggedBuses of
Just newBus -> put (ConnState client newBus)
Nothing -> do
waitForChange busNameAddedRule
changeMusicClient
checkName :: Client -> BusName -> IO Bool
checkName client name = do
returnCall <-
call_
client
(methodCall "/org/freedesktop/DBus" "org.freedesktop.DBus" "NameHasOwner")
{ methodCallDestination = Just dbusBus,
methodCallBody = [toVariant name]
}
-- We assume this call is correct, as it's done to the master dbus
-- object. So fromJust/head are safe.
return . fromJust . fromVariant . head . methodReturnBody $ returnCall