taffybar-4.1.2: src/System/Taffybar/Widget/NetworkManager.hs
{-# LANGUAGE OverloadedStrings #-}
-----------------------------------------------------------------------------
-- |
-- Module : System.Taffybar.Widget.NetworkManager
-- Copyright : (c) Ivan A. Malison
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : Ivan A. Malison
-- Stability : unstable
-- Portability : unportable
--
-- Network widgets backed by NetworkManager's DBus API.
-----------------------------------------------------------------------------
module System.Taffybar.Widget.NetworkManager
( WifiWidgetConfig(..)
, defaultWifiWidgetConfig
, networkManagerWifiLabelNew
, networkManagerWifiLabelNewWith
, NetworkManagerWifiIconConfig(..)
, defaultNetworkManagerWifiIconConfig
, networkManagerWifiIconNew
, networkManagerWifiIconNewWith
, networkManagerWifiNew
, networkManagerWifiNewWith
-- Wifi text icon (nerd font label)
, networkManagerWifiTextIconNew
, networkManagerWifiTextIconNewWith
-- Wifi combined icon-label
, networkManagerWifiIconLabelNew
, networkManagerWifiIconLabelNewWith
, NetworkWidgetConfig(..)
, defaultNetworkWidgetConfig
, networkManagerNetworkLabelNew
, networkManagerNetworkLabelNewWith
, NetworkManagerNetworkIconConfig(..)
, defaultNetworkManagerNetworkIconConfig
, networkManagerNetworkIconNew
, networkManagerNetworkIconNewWith
, networkManagerNetworkNew
, networkManagerNetworkNewWith
-- Network text icon (nerd font label)
, networkManagerNetworkTextIconNew
, networkManagerNetworkTextIconNewWith
-- Network combined icon-label
, networkManagerNetworkIconLabelNew
, networkManagerNetworkIconLabelNewWith
) where
import Control.Applicative ((<|>))
import Control.Concurrent.MVar
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Trans.Reader
import Data.Default (Default(..))
import Data.Int (Int32)
import Data.IORef (newIORef, readIORef, writeIORef)
import Data.Maybe (fromMaybe)
import qualified Data.Text as T
import qualified GI.GLib as G
import GI.Gtk
import System.Taffybar.Context
import System.Taffybar.Information.NetworkManager
import System.Taffybar.Util
import System.Taffybar.Widget.Generic.ChannelWidget
import System.Taffybar.Widget.Generic.ScalingImage (scalingImage)
import System.Taffybar.Widget.Util (buildIconLabelBox)
import Text.StringTemplate
data WifiWidgetConfig = WifiWidgetConfig
{ wifiConnectedFormat :: String
, wifiDisconnectedFormat :: String
, wifiDisabledFormat :: String
, wifiUnknownFormat :: String
, wifiTooltipFormat :: Maybe String
}
defaultWifiWidgetConfig :: WifiWidgetConfig
defaultWifiWidgetConfig =
WifiWidgetConfig
{ wifiConnectedFormat = "$ssid$ $strength$%"
, wifiDisconnectedFormat = "disconnected"
, wifiDisabledFormat = "off"
, wifiUnknownFormat = "unknown"
, wifiTooltipFormat =
Just "SSID: $ssid$\nStrength: $strength$%\nConnection: $connection$\nState: $state$"
}
instance Default WifiWidgetConfig where
def = defaultWifiWidgetConfig
networkManagerWifiLabelNew :: TaffyIO Widget
networkManagerWifiLabelNew = networkManagerWifiLabelNewWith defaultWifiWidgetConfig
networkManagerWifiLabelNewWith :: WifiWidgetConfig -> TaffyIO Widget
networkManagerWifiLabelNewWith config = do
chan <- getWifiInfoChan
ctx <- ask
liftIO $ do
label <- labelNew Nothing
let updateWidget info = do
(labelText, tooltipText) <- formatWifiWidget config info
postGUIASync $ do
labelSetMarkup label labelText
widgetSetTooltipMarkup label tooltipText
void $ onWidgetRealize label $
runReaderT getWifiInfoState ctx >>= updateWidget
toWidget =<< channelWidgetNew label chan updateWidget
data NetworkManagerWifiIconConfig = NetworkManagerWifiIconConfig
{ wifiIconNone :: String
, wifiIconWeak :: String
, wifiIconOk :: String
, wifiIconGood :: String
, wifiIconExcellent :: String
, wifiIconDisconnected :: String
, wifiIconDisabled :: String
, wifiIconUnknown :: String
, wifiIconTooltipFormat :: Maybe String
}
defaultNetworkManagerWifiIconConfig :: NetworkManagerWifiIconConfig
defaultNetworkManagerWifiIconConfig =
NetworkManagerWifiIconConfig
{ wifiIconNone = "network-wireless-signal-none-symbolic"
, wifiIconWeak = "network-wireless-signal-weak-symbolic"
, wifiIconOk = "network-wireless-signal-ok-symbolic"
, wifiIconGood = "network-wireless-signal-good-symbolic"
, wifiIconExcellent = "network-wireless-signal-excellent-symbolic"
, wifiIconDisconnected = "network-wireless-offline-symbolic"
, wifiIconDisabled = "network-wireless-disabled-symbolic"
, wifiIconUnknown = "network-wireless-symbolic"
, wifiIconTooltipFormat =
Just "SSID: $ssid$\nStrength: $strength$%\nConnection: $connection$\nState: $state$"
}
themeLoadFlags :: [IconLookupFlags]
themeLoadFlags = [IconLookupFlagsGenericFallback, IconLookupFlagsUseBuiltin]
networkManagerWifiIconNew :: TaffyIO Widget
networkManagerWifiIconNew = networkManagerWifiIconNewWith defaultNetworkManagerWifiIconConfig
networkManagerWifiIconNewWith :: NetworkManagerWifiIconConfig -> TaffyIO Widget
networkManagerWifiIconNewWith config = do
chan <- getWifiInfoChan
ctx <- ask
defaultTheme <- liftIO iconThemeGetDefault
initialInfo <- liftIO $ runReaderT getWifiInfoState ctx
infoVar <- liftIO $ newMVar initialInfo
imageWidgetRef <- liftIO $ newIORef (error "imageWidget not initialised")
let
setIconForSize size = do
iw <- readIORef imageWidgetRef
styleCtx <- widgetGetStyleContext iw
info <- readMVar infoVar
let iconNames = wifiIconCandidates config info
iconInfo <- lookupFirstIcon defaultTheme size iconNames
traverse (extractPixbuf styleCtx) iconInfo
extractPixbuf styleCtx iconInfo =
fst <$> iconInfoLoadSymbolicForContext iconInfo styleCtx
(imageWidget, updateImage) <- scalingImage setIconForSize OrientationHorizontal
liftIO $ do
writeIORef imageWidgetRef imageWidget
let
updateWidget info = do
_ <- swapMVar infoVar info
(_, tooltipText) <- formatWifiWidget (iconTooltipAsLabelConfig config) info
postGUIASync $ do
widgetSetTooltipMarkup imageWidget tooltipText
updateImage
void $ onWidgetRealize imageWidget $ updateWidget initialInfo
toWidget =<< channelWidgetNew imageWidget chan updateWidget
iconTooltipAsLabelConfig :: NetworkManagerWifiIconConfig -> WifiWidgetConfig
iconTooltipAsLabelConfig cfg =
defaultWifiWidgetConfig { wifiTooltipFormat = wifiIconTooltipFormat cfg }
lookupFirstIcon :: IconTheme -> Int32 -> [String] -> IO (Maybe IconInfo)
lookupFirstIcon _ _ [] = return Nothing
lookupFirstIcon theme size (name:names) = do
info <- iconThemeLookupIcon theme (T.pack name) size themeLoadFlags
case info of
Just _ -> return info
Nothing -> lookupFirstIcon theme size names
wifiIconCandidates :: NetworkManagerWifiIconConfig -> WifiInfo -> [String]
wifiIconCandidates cfg info =
case wifiState info of
WifiDisabled ->
[ wifiIconDisabled cfg
, wifiIconDisconnected cfg
, wifiIconNone cfg
]
WifiDisconnected ->
[ wifiIconDisconnected cfg
, wifiIconNone cfg
]
WifiUnknown ->
[ wifiIconUnknown cfg
, wifiIconNone cfg
]
WifiConnected ->
strengthIconName cfg (wifiStrength info) :
[ wifiIconGood cfg
, wifiIconOk cfg
, wifiIconWeak cfg
, wifiIconNone cfg
]
strengthIconName :: NetworkManagerWifiIconConfig -> Maybe Int -> String
strengthIconName cfg strength =
case strength of
Nothing -> wifiIconNone cfg
Just s
| s < 20 -> wifiIconWeak cfg
| s < 40 -> wifiIconOk cfg
| s < 70 -> wifiIconGood cfg
| otherwise -> wifiIconExcellent cfg
networkManagerWifiNew :: TaffyIO Widget
networkManagerWifiNew = networkManagerWifiNewWith defaultWifiWidgetConfig defaultNetworkManagerWifiIconConfig
networkManagerWifiNewWith
:: WifiWidgetConfig
-> NetworkManagerWifiIconConfig
-> TaffyIO Widget
networkManagerWifiNewWith labelCfg iconCfg = do
iconWidget <- networkManagerWifiIconNewWith iconCfg
labelWidget <- networkManagerWifiLabelNewWith labelCfg
liftIO $ do
box <- boxNew OrientationHorizontal 0
containerAdd box iconWidget
containerAdd box labelWidget
widgetShowAll box
toWidget box
formatWifiWidget
:: WifiWidgetConfig
-> WifiInfo
-> IO (T.Text, Maybe T.Text)
formatWifiWidget config info = do
attrs <- buildAttrs info
let labelTemplate = case wifiState info of
WifiConnected -> wifiConnectedFormat config
WifiDisconnected -> wifiDisconnectedFormat config
WifiDisabled -> wifiDisabledFormat config
WifiUnknown -> wifiUnknownFormat config
labelText = renderTemplate labelTemplate attrs
tooltipText = fmap (`renderTemplate` attrs) (wifiTooltipFormat config)
return (T.pack labelText, T.pack <$> tooltipText)
renderTemplate :: String -> [(String, String)] -> String
renderTemplate template attrs = render $ setManyAttrib attrs (newSTMP template)
buildAttrs :: WifiInfo -> IO [(String, String)]
buildAttrs info = do
let
displayName =
fromMaybe "" (wifiSsid info <|> wifiConnectionId info)
ssidText = if T.null displayName then "unknown" else displayName
strengthText = maybe "?" show (wifiStrength info)
stateText = wifiStateText (wifiState info)
connectionText = fromMaybe "" (wifiConnectionId info)
ssid <- escapeText ssidText
strength <- escapeText (T.pack strengthText)
state <- escapeText (T.pack stateText)
connection <- escapeText connectionText
return
[ ("ssid", ssid)
, ("strength", strength)
, ("state", state)
, ("connection", connection)
]
escapeText :: T.Text -> IO String
escapeText input = T.unpack <$> G.markupEscapeText input (-1)
wifiStateText :: WifiState -> String
wifiStateText WifiDisabled = "disabled"
wifiStateText WifiDisconnected = "disconnected"
wifiStateText WifiConnected = "connected"
wifiStateText WifiUnknown = "unknown"
-- | A text "icon" intended for label widgets. Uses Font Awesome-ish codepoints.
wifiTextIcon :: WifiInfo -> T.Text
wifiTextIcon info =
case wifiState info of
WifiConnected ->
T.pack "\xF1EB" --
WifiDisconnected -> T.pack "\xF05E" --
WifiDisabled -> T.pack "\xF05E" --
WifiUnknown -> T.pack "\xF059" --
-- Network (WiFi + wired + vpn + disconnected)
data NetworkWidgetConfig = NetworkWidgetConfig
{ networkWifiFormat :: String
, networkWiredFormat :: String
, networkVpnFormat :: String
, networkOtherFormat :: String
, networkDisconnectedFormat :: String
, networkWifiDisabledFormat :: String
, networkUnknownFormat :: String
, networkTooltipFormat :: Maybe String
}
defaultNetworkWidgetConfig :: NetworkWidgetConfig
defaultNetworkWidgetConfig =
NetworkWidgetConfig
{ networkWifiFormat = "$ssid$ $strength$%"
, networkWiredFormat = "$connection$"
, networkVpnFormat = "$connection$"
, networkOtherFormat = "$type$ $connection$"
, networkDisconnectedFormat = "disconnected"
, networkWifiDisabledFormat = "disconnected (wifi off)"
, networkUnknownFormat = "unknown"
, networkTooltipFormat =
Just "Type: $type$\nConnection: $connection$\nSSID: $ssid$\nStrength: $strength$%\nState: $state$"
}
instance Default NetworkWidgetConfig where
def = defaultNetworkWidgetConfig
networkManagerNetworkLabelNew :: TaffyIO Widget
networkManagerNetworkLabelNew =
networkManagerNetworkLabelNewWith defaultNetworkWidgetConfig
networkManagerNetworkLabelNewWith :: NetworkWidgetConfig -> TaffyIO Widget
networkManagerNetworkLabelNewWith config = do
chan <- getNetworkInfoChan
ctx <- ask
liftIO $ do
label <- labelNew Nothing
let updateWidget info = do
(labelText, tooltipText) <- formatNetworkWidget config info
postGUIASync $ do
labelSetMarkup label labelText
widgetSetTooltipMarkup label tooltipText
void $ onWidgetRealize label $
runReaderT getNetworkInfoState ctx >>= updateWidget
toWidget =<< channelWidgetNew label chan updateWidget
data NetworkManagerNetworkIconConfig = NetworkManagerNetworkIconConfig
{ netIconWired :: String
, netIconVpn :: String
, netIconOther :: String
, netIconDisconnected :: String
, netIconUnknown :: String
, netIconWifiDisabled :: String
, netWifiIconNone :: String
, netWifiIconWeak :: String
, netWifiIconOk :: String
, netWifiIconGood :: String
, netWifiIconExcellent :: String
, netIconTooltipFormat :: Maybe String
}
defaultNetworkManagerNetworkIconConfig :: NetworkManagerNetworkIconConfig
defaultNetworkManagerNetworkIconConfig =
NetworkManagerNetworkIconConfig
{ netIconWired = "network-wired-symbolic"
, netIconVpn = "network-vpn-symbolic"
, netIconOther = "network-transmit-receive-symbolic"
, netIconDisconnected = "network-offline-symbolic"
, netIconUnknown = "network-error-symbolic"
, netIconWifiDisabled = "network-wireless-disabled-symbolic"
, netWifiIconNone = "network-wireless-signal-none-symbolic"
, netWifiIconWeak = "network-wireless-signal-weak-symbolic"
, netWifiIconOk = "network-wireless-signal-ok-symbolic"
, netWifiIconGood = "network-wireless-signal-good-symbolic"
, netWifiIconExcellent = "network-wireless-signal-excellent-symbolic"
, netIconTooltipFormat =
Just "Type: $type$\nConnection: $connection$\nSSID: $ssid$\nStrength: $strength$%\nState: $state$"
}
networkManagerNetworkIconNew :: TaffyIO Widget
networkManagerNetworkIconNew =
networkManagerNetworkIconNewWith defaultNetworkManagerNetworkIconConfig
networkManagerNetworkIconNewWith :: NetworkManagerNetworkIconConfig -> TaffyIO Widget
networkManagerNetworkIconNewWith config = do
chan <- getNetworkInfoChan
ctx <- ask
defaultTheme <- liftIO iconThemeGetDefault
initialInfo <- liftIO $ runReaderT getNetworkInfoState ctx
infoVar <- liftIO $ newMVar initialInfo
imageWidgetRef <- liftIO $ newIORef (error "imageWidget not initialised")
let
setIconForSize size = do
iw <- readIORef imageWidgetRef
styleCtx <- widgetGetStyleContext iw
info <- readMVar infoVar
let iconNames = networkIconCandidates config info
iconInfo <- lookupFirstIcon defaultTheme size iconNames
traverse (extractPixbuf styleCtx) iconInfo
extractPixbuf styleCtx iconInfo =
fst <$> iconInfoLoadSymbolicForContext iconInfo styleCtx
(imageWidget, updateImage) <- scalingImage setIconForSize OrientationHorizontal
liftIO $ do
writeIORef imageWidgetRef imageWidget
let
updateWidget info = do
_ <- swapMVar infoVar info
(_, tooltipText) <- formatNetworkWidget (iconTooltipAsNetworkLabelConfig config) info
postGUIASync $ do
widgetSetTooltipMarkup imageWidget tooltipText
updateImage
void $ onWidgetRealize imageWidget $ updateWidget initialInfo
toWidget =<< channelWidgetNew imageWidget chan updateWidget
iconTooltipAsNetworkLabelConfig :: NetworkManagerNetworkIconConfig -> NetworkWidgetConfig
iconTooltipAsNetworkLabelConfig cfg =
defaultNetworkWidgetConfig { networkTooltipFormat = netIconTooltipFormat cfg }
networkIconCandidates :: NetworkManagerNetworkIconConfig -> NetworkInfo -> [String]
networkIconCandidates cfg info =
case networkState info of
NetworkUnknown ->
[ netIconUnknown cfg
, netIconOther cfg
, netIconDisconnected cfg
]
NetworkDisconnected ->
case networkWirelessEnabled info of
Just False ->
[ netIconWifiDisabled cfg
, netIconDisconnected cfg
]
_ ->
[ netIconDisconnected cfg
, netIconOther cfg
]
NetworkConnected ->
case networkType info of
Just NetworkWired ->
[ netIconWired cfg
, netIconOther cfg
]
Just NetworkVpn ->
[ netIconVpn cfg
, netIconOther cfg
]
Just NetworkWifi ->
strengthToWifiIcon cfg (networkStrength info) :
[ netWifiIconGood cfg
, netWifiIconOk cfg
, netWifiIconWeak cfg
, netWifiIconNone cfg
]
Just (NetworkOther _) ->
[ netIconOther cfg
, netIconWired cfg
]
Nothing ->
[ netIconOther cfg
, netIconWired cfg
, netIconDisconnected cfg
]
strengthToWifiIcon :: NetworkManagerNetworkIconConfig -> Maybe Int -> String
strengthToWifiIcon cfg strength =
case strength of
Nothing -> netWifiIconNone cfg
Just s
| s < 20 -> netWifiIconWeak cfg
| s < 40 -> netWifiIconOk cfg
| s < 70 -> netWifiIconGood cfg
| otherwise -> netWifiIconExcellent cfg
networkManagerNetworkNew :: TaffyIO Widget
networkManagerNetworkNew =
networkManagerNetworkNewWith defaultNetworkWidgetConfig defaultNetworkManagerNetworkIconConfig
networkManagerNetworkNewWith
:: NetworkWidgetConfig
-> NetworkManagerNetworkIconConfig
-> TaffyIO Widget
networkManagerNetworkNewWith labelCfg iconCfg = do
iconWidget <- networkManagerNetworkIconNewWith iconCfg
labelWidget <- networkManagerNetworkLabelNewWith labelCfg
liftIO $ do
box <- boxNew OrientationHorizontal 0
containerAdd box iconWidget
containerAdd box labelWidget
widgetShowAll box
toWidget box
formatNetworkWidget
:: NetworkWidgetConfig
-> NetworkInfo
-> IO (T.Text, Maybe T.Text)
formatNetworkWidget config info = do
attrs <- buildNetworkAttrs info
let labelTemplate =
case networkState info of
NetworkConnected ->
case networkType info of
Just NetworkWifi -> networkWifiFormat config
Just NetworkWired -> networkWiredFormat config
Just NetworkVpn -> networkVpnFormat config
Just (NetworkOther _) -> networkOtherFormat config
Nothing -> networkOtherFormat config
NetworkDisconnected ->
case networkWirelessEnabled info of
Just False -> networkWifiDisabledFormat config
_ -> networkDisconnectedFormat config
NetworkUnknown -> networkUnknownFormat config
labelText = renderTemplate labelTemplate attrs
tooltipText = fmap (`renderTemplate` attrs) (networkTooltipFormat config)
return (T.pack labelText, T.pack <$> tooltipText)
buildNetworkAttrs :: NetworkInfo -> IO [(String, String)]
buildNetworkAttrs info = do
let
rawConnText = fromMaybe "" (networkConnectionId info)
connText = if T.null rawConnText then "unknown" else rawConnText
typeText = networkTypeText (networkType info)
displaySsid = fromMaybe "" (networkSsid info)
ssidText = if T.null displaySsid then "unknown" else displaySsid
strengthText = maybe "?" show (networkStrength info)
stateText = networkStateText (networkState info)
conn <- escapeText connText
typ <- escapeText typeText
ssid <- escapeText ssidText
strength <- escapeText (T.pack strengthText)
state <- escapeText (T.pack stateText)
return
[ ("connection", conn)
, ("type", typ)
, ("ssid", ssid)
, ("strength", strength)
, ("state", state)
]
networkTypeText :: Maybe NetworkType -> T.Text
networkTypeText Nothing = "unknown"
networkTypeText (Just NetworkWifi) = "wifi"
networkTypeText (Just NetworkWired) = "wired"
networkTypeText (Just NetworkVpn) = "vpn"
networkTypeText (Just (NetworkOther t)) = t
networkStateText :: NetworkState -> String
networkStateText NetworkConnected = "connected"
networkStateText NetworkDisconnected = "disconnected"
networkStateText NetworkUnknown = "unknown"
networkTextIcon :: NetworkInfo -> T.Text
networkTextIcon info =
case networkState info of
NetworkUnknown -> T.pack "\xF059" --
NetworkDisconnected ->
case networkWirelessEnabled info of
Just False -> T.pack "\xF05E\xF1EB" -- (wifi off-ish)
_ -> T.pack "\xF05E" --
NetworkConnected ->
case networkType info of
Just NetworkWifi ->
T.pack "\xF1EB" --
Just NetworkWired -> T.pack "\xF1E6" --
Just NetworkVpn -> T.pack "\xF023" --
Just (NetworkOther _) -> T.pack "\xF0AC" --
Nothing -> T.pack "\xF0AC" --
-- Wifi text icon
networkManagerWifiTextIconNew :: TaffyIO Widget
networkManagerWifiTextIconNew =
networkManagerWifiTextIconNewWith defaultWifiWidgetConfig
networkManagerWifiTextIconNewWith :: WifiWidgetConfig -> TaffyIO Widget
networkManagerWifiTextIconNewWith _config = do
chan <- getWifiInfoChan
ctx <- ask
liftIO $ do
label <- labelNew Nothing
let updateIcon info = do
let iconText = wifiTextIcon info
postGUIASync $ labelSetText label iconText
void $ onWidgetRealize label $
runReaderT getWifiInfoState ctx >>= updateIcon
toWidget =<< channelWidgetNew label chan updateIcon
-- Wifi icon-label
networkManagerWifiIconLabelNew :: TaffyIO Widget
networkManagerWifiIconLabelNew =
networkManagerWifiIconLabelNewWith defaultWifiWidgetConfig
networkManagerWifiIconLabelNewWith :: WifiWidgetConfig -> TaffyIO Widget
networkManagerWifiIconLabelNewWith config = do
iconWidget <- networkManagerWifiTextIconNewWith config
labelWidget <- networkManagerWifiLabelNewWith config
liftIO $ buildIconLabelBox iconWidget labelWidget
-- Network text icon
networkManagerNetworkTextIconNew :: TaffyIO Widget
networkManagerNetworkTextIconNew =
networkManagerNetworkTextIconNewWith defaultNetworkWidgetConfig
networkManagerNetworkTextIconNewWith :: NetworkWidgetConfig -> TaffyIO Widget
networkManagerNetworkTextIconNewWith _config = do
chan <- getNetworkInfoChan
ctx <- ask
liftIO $ do
label <- labelNew Nothing
let updateIcon info = do
let iconText = networkTextIcon info
postGUIASync $ labelSetText label iconText
void $ onWidgetRealize label $
runReaderT getNetworkInfoState ctx >>= updateIcon
toWidget =<< channelWidgetNew label chan updateIcon
-- Network icon-label
networkManagerNetworkIconLabelNew :: TaffyIO Widget
networkManagerNetworkIconLabelNew =
networkManagerNetworkIconLabelNewWith defaultNetworkWidgetConfig
networkManagerNetworkIconLabelNewWith :: NetworkWidgetConfig -> TaffyIO Widget
networkManagerNetworkIconLabelNewWith config = do
iconWidget <- networkManagerNetworkTextIconNewWith config
labelWidget <- networkManagerNetworkLabelNewWith config
liftIO $ buildIconLabelBox iconWidget labelWidget