2020-05-28 23:17:17 -04:00
|
|
|
--------------------------------------------------------------------------------
|
2022-12-30 14:58:23 -05:00
|
|
|
-- Device plugin
|
2020-05-28 23:17:17 -04:00
|
|
|
--
|
|
|
|
-- Display different text depending on whether or not the interface has
|
|
|
|
-- connectivity
|
|
|
|
|
2022-07-09 17:08:10 -04:00
|
|
|
module Xmobar.Plugins.Device
|
2022-12-30 14:58:23 -05:00
|
|
|
( Device (..)
|
2022-07-09 17:08:10 -04:00
|
|
|
, devDep
|
2022-12-30 14:58:23 -05:00
|
|
|
)
|
|
|
|
where
|
|
|
|
|
|
|
|
import DBus
|
|
|
|
import Data.Internal.DBus
|
2023-01-01 18:33:02 -05:00
|
|
|
import Data.Internal.XIO
|
2022-12-30 16:59:50 -05:00
|
|
|
import RIO
|
2023-09-30 18:51:07 -04:00
|
|
|
import qualified RIO.Map as M
|
|
|
|
import qualified RIO.NonEmpty as NE
|
2022-12-30 14:58:23 -05:00
|
|
|
import qualified RIO.Text as T
|
|
|
|
import XMonad.Internal.Command.Desktop
|
|
|
|
import XMonad.Internal.DBus.Common
|
|
|
|
import Xmobar
|
|
|
|
import Xmobar.Plugins.Common
|
2021-11-25 00:12:00 -05:00
|
|
|
|
2023-09-30 18:51:07 -04:00
|
|
|
newtype Device = Device (NE.NonEmpty T.Text, T.Text, Colors) deriving (Read, Show)
|
2021-11-25 00:12:00 -05:00
|
|
|
|
|
|
|
nmPath :: ObjectPath
|
|
|
|
nmPath = objectPath_ "/org/freedesktop/NetworkManager"
|
2020-05-28 23:17:17 -04:00
|
|
|
|
2021-11-25 00:12:00 -05:00
|
|
|
nmInterface :: InterfaceName
|
|
|
|
nmInterface = interfaceName_ "org.freedesktop.NetworkManager"
|
2021-06-21 23:41:57 -04:00
|
|
|
|
2023-09-30 18:51:07 -04:00
|
|
|
nmActiveInterface :: InterfaceName
|
|
|
|
nmActiveInterface =
|
|
|
|
interfaceName_ "org.freedesktop.NetworkManager.Connection.Active"
|
|
|
|
|
|
|
|
stateChanged :: MemberName
|
|
|
|
stateChanged = "StateChanged"
|
2020-05-28 23:17:17 -04:00
|
|
|
|
2021-11-25 00:12:00 -05:00
|
|
|
getByIP :: MemberName
|
|
|
|
getByIP = memberName_ "GetDeviceByIpIface"
|
2021-11-09 00:59:17 -05:00
|
|
|
|
2022-07-09 17:08:10 -04:00
|
|
|
devDep :: DBusDependency_ SysClient
|
2022-12-30 14:58:23 -05:00
|
|
|
devDep =
|
|
|
|
Endpoint networkManagerPkgs networkManagerBus nmPath nmInterface $
|
|
|
|
Method_ getByIP
|
2021-11-09 00:59:17 -05:00
|
|
|
|
2023-09-30 18:51:07 -04:00
|
|
|
-- -- TODO not DRY, make a NM specific section somewhere for this call
|
|
|
|
ethBus :: BusName
|
|
|
|
ethBus = busName_ "org.freedesktop.NetworkManager"
|
|
|
|
|
|
|
|
data EthEnv c = EthEnv
|
|
|
|
{ ethClient :: !c
|
|
|
|
, ethState :: !(MVar EthState)
|
|
|
|
, ethDisplay :: !(EthIO ())
|
|
|
|
, ethEnv :: !SimpleApp
|
|
|
|
}
|
|
|
|
|
|
|
|
instance HasLogFunc (EthEnv c) where
|
|
|
|
logFuncL = lens ethEnv (\x y -> x {ethEnv = y}) . logFuncL
|
|
|
|
|
|
|
|
instance HasClient EthEnv where
|
|
|
|
clientL = lens ethClient (\x y -> x {ethClient = y})
|
|
|
|
|
|
|
|
type EthIO = RIO (EthEnv SysClient)
|
|
|
|
|
|
|
|
type EthState = M.Map ObjectPath T.Text
|
|
|
|
|
|
|
|
getConnectionProp :: MemberName -> ObjectPath -> EthIO [Variant]
|
|
|
|
getConnectionProp prop path = callPropertyGet ethBus path nmActiveInterface prop
|
|
|
|
|
|
|
|
getConnectionId :: ObjectPath -> EthIO (Maybe T.Text)
|
|
|
|
getConnectionId = fmap fromSingletonVariant . getConnectionProp "Id"
|
|
|
|
|
|
|
|
getConnectionType :: ObjectPath -> EthIO (Maybe T.Text)
|
|
|
|
getConnectionType = fmap fromSingletonVariant . getConnectionProp "Type"
|
|
|
|
|
|
|
|
updateConnected :: NE.NonEmpty T.Text -> ObjectPath -> EthIO ()
|
|
|
|
updateConnected contypes path = do
|
|
|
|
typeRes <- getConnectionType path
|
|
|
|
case typeRes of
|
|
|
|
Nothing -> logError "could not get type"
|
|
|
|
Just contype -> do
|
|
|
|
when (contype `elem` contypes) $ do
|
|
|
|
idRes <- getConnectionId path
|
|
|
|
case idRes of
|
|
|
|
Nothing -> logError "could not get ID"
|
|
|
|
Just i -> do
|
|
|
|
s <- asks ethState
|
|
|
|
modifyMVar_ s $ return . M.insert path i
|
|
|
|
|
|
|
|
updateDisconnected :: ObjectPath -> EthIO ()
|
|
|
|
updateDisconnected path = do
|
|
|
|
s <- asks ethState
|
|
|
|
modifyMVar_ s $ return . M.delete path
|
|
|
|
|
|
|
|
testActiveType :: NE.NonEmpty T.Text -> Signal -> EthIO ()
|
|
|
|
testActiveType contypes sig = do
|
|
|
|
dpy <- asks ethDisplay
|
|
|
|
case signalBody sig of
|
|
|
|
[state, _] -> case fromVariant state of
|
|
|
|
Just (2 :: Word32) -> updateConnected contypes path >> dpy
|
|
|
|
Just 4 -> updateDisconnected path >> dpy
|
|
|
|
_ -> return ()
|
|
|
|
_ -> return ()
|
2021-11-25 00:12:00 -05:00
|
|
|
where
|
2023-09-30 18:51:07 -04:00
|
|
|
path = signalPath sig
|
2021-11-25 00:12:00 -05:00
|
|
|
|
2023-09-30 18:51:07 -04:00
|
|
|
initialState
|
2023-01-03 22:18:55 -05:00
|
|
|
:: ( SafeClient c
|
2023-09-30 18:51:07 -04:00
|
|
|
, MonadUnliftIO m
|
2023-01-03 22:18:55 -05:00
|
|
|
, MonadReader (env c) m
|
2023-09-30 18:51:07 -04:00
|
|
|
, HasClient env
|
2023-01-03 22:18:55 -05:00
|
|
|
, HasLogFunc (env c)
|
|
|
|
)
|
2023-09-30 18:51:07 -04:00
|
|
|
=> NE.NonEmpty T.Text
|
|
|
|
-> m EthState
|
|
|
|
initialState contypes =
|
|
|
|
M.mapMaybe go <$> callGetManagedObjects ethBus "/org/freedesktop"
|
|
|
|
where
|
|
|
|
go = getId <=< M.lookup nmActiveInterface
|
|
|
|
getId m =
|
|
|
|
fromVariant
|
|
|
|
=<< (\t -> if t `elem` contypes then M.lookup "Id" m else Nothing)
|
|
|
|
=<< fromVariant
|
|
|
|
=<< M.lookup "Type" m
|
2021-11-25 00:12:00 -05:00
|
|
|
|
2023-09-30 18:51:07 -04:00
|
|
|
readState :: EthIO [T.Text]
|
|
|
|
readState = M.elems <$> (readMVar =<< asks ethState)
|
2020-05-28 23:17:17 -04:00
|
|
|
|
|
|
|
instance Exec Device where
|
2023-09-30 18:51:07 -04:00
|
|
|
alias (Device (_, _, _)) = "connection"
|
|
|
|
start (Device (contypes, text, colors)) cb =
|
|
|
|
withDBusClientConnection cb (Just "ethernet.log") $ \c -> do
|
|
|
|
let dpy = displayMaybe cb formatter . Just =<< readState
|
|
|
|
i <- withDIO c $ initialState contypes
|
|
|
|
s <- newMVar i
|
|
|
|
mapRIO (EthEnv c s dpy) $ do
|
|
|
|
addListener
|
|
|
|
dpy
|
2020-05-28 23:17:17 -04:00
|
|
|
where
|
2023-09-30 18:51:07 -04:00
|
|
|
formatter names = return $ case names of
|
|
|
|
[] -> colorText colors False text
|
|
|
|
xs -> T.unwords [T.intercalate "|" xs, colorText colors True text]
|
|
|
|
addListener = do
|
|
|
|
res <- matchSignalFull ethBus Nothing (Just nmActiveInterface) (Just stateChanged)
|
2023-01-01 22:40:28 -05:00
|
|
|
case res of
|
2023-09-30 18:51:07 -04:00
|
|
|
Just rule -> void $ addMatchCallbackSignal rule (testActiveType contypes)
|
2023-01-01 22:40:28 -05:00
|
|
|
Nothing -> logError "could not start listener"
|