tianbar-0.3.4.0: src/System/Tianbar/DBus.hs
{-# Language OverloadedStrings #-}
module System.Tianbar.DBus where
import Control.Concurrent.MVar
import Control.Monad
import Data.Aeson hiding (Array)
import Data.Int
import Data.List
import Data.List.Split
import qualified Data.Map as M
import qualified Data.Text.Lazy as T
import qualified Data.Text.Lazy.Encoding as E
import Data.Word
import qualified Graphics.UI.Gtk as Gtk
import Graphics.UI.Gtk.WebKit.WebView
import DBus
import DBus.Client
import Network.URI
import System.Tianbar.UriOverride
-- DBus callback override
type DBusClient = MVar Client
data DBusState = DBusState { dbusSession :: DBusClient
, dbusSystem :: DBusClient
}
initDBusState :: IO DBusState
initDBusState = do
session <- connectSession >>= newMVar
system <- connectSystem >>= newMVar
return $ DBusState session system
reloadDBusState :: DBusState -> IO ()
reloadDBusState dbus = do
modifyMVar_ (dbusSession dbus) $ \client -> do
disconnect client
connectSession
modifyMVar_ (dbusSystem dbus) $ \client -> do
disconnect client
connectSystem
instance ToJSON Variant where
toJSON v = case variantType v of
TypeBoolean -> let Just b = fromVariant v :: Maybe Bool in toJSON b
TypeWord8 -> let Just i = fromVariant v :: Maybe Word8 in toJSON i
TypeWord16 -> let Just i = fromVariant v :: Maybe Word16 in toJSON i
TypeWord32 -> let Just i = fromVariant v :: Maybe Word32 in toJSON i
TypeWord64 -> let Just i = fromVariant v :: Maybe Word64 in toJSON i
TypeInt16 -> let Just i = fromVariant v :: Maybe Int16 in toJSON i
TypeInt32 -> let Just i = fromVariant v :: Maybe Int32 in toJSON i
TypeInt64 -> let Just i = fromVariant v :: Maybe Int64 in toJSON i
TypeDouble -> let Just i = fromVariant v :: Maybe Double in toJSON i
TypeString -> let Just s = fromVariant v :: Maybe String in toJSON s
TypeSignature -> let Just s = fromVariant v :: Maybe Signature in
toJSON $ formatSignature s
TypeObjectPath -> let Just p = fromVariant v :: Maybe ObjectPath in
toJSON $ formatObjectPath p
TypeVariant -> let Just n = fromVariant v :: Maybe Variant in toJSON n
TypeArray _ -> let Just a = fromVariant v :: Maybe Array in
toJSON $ arrayItems a
TypeDictionary _ _ -> let Just d = fromVariant v :: Maybe Dictionary in
toJSON $ M.fromList $ map variantStringKey $ dictionaryItems d
TypeStructure _ -> let Just a = fromVariant v :: Maybe Structure in
toJSON $ structureItems a
variantString :: Variant -> String
variantString v = s
where Just s = case variantType v of
TypeString -> fromVariant v
TypeVariant -> Just $ variantString v'
where Just v' = fromVariant v
_ -> Just $ show v
variantStringKey :: (Variant, Variant) -> (String, Variant)
variantStringKey (k, v) = (variantString k, v)
instance ToJSON ObjectPath where
toJSON = toJSON . formatObjectPath
instance ToJSON InterfaceName where
toJSON = toJSON . formatInterfaceName
instance ToJSON MemberName where
toJSON = toJSON . formatMemberName
instance ToJSON Signal where
toJSON s = object [ "path" .= toJSON (signalPath s)
, "iface" .= toJSON (signalInterface s)
, "member" .= toJSON (signalMember s)
]
instance ToJSON ErrorName where
toJSON = toJSON . formatErrorName
instance ToJSON Serial where
toJSON = toJSON . serialValue
instance ToJSON BusName where
toJSON = toJSON . formatBusName
instance ToJSON MethodError where
toJSON e = object [ "name" .= toJSON (methodErrorName e)
, "serial" .= toJSON (methodErrorSerial e)
, "sender" .= toJSON (methodErrorSender e)
, "destination" .= toJSON (methodErrorDestination e)
, "body" .= toJSON (methodErrorBody e)
, "message" .= toJSON (methodErrorMessage e)
]
instance ToJSON MethodReturn where
toJSON r = object [ "serial" .= toJSON (methodReturnSerial r)
, "sender" .= toJSON (methodReturnSender r)
, "destination" .= toJSON (methodReturnDestination r)
, "body" .= toJSON (methodReturnBody r)
]
callback :: WebView -> Int -> Signal -> IO ()
callback wk index sig =
Gtk.postGUIAsync $ webViewExecuteScript wk $ dbusCallback index sig
dbusCallback :: Int -> Signal -> String
dbusCallback index sig =
"window.dbusCallbacks && "
++ "window.dbusCallbacks[" ++ indexStr ++ "](" ++ paramsStr ++ ")"
where indexStr = show index
paramsStr = intercalate "," $ map (T.unpack . E.decodeUtf8) [signalStr, bodyStr]
signalStr = encode $ toJSON sig
bodyStr = encode $ toJSON $ signalBody sig
returnJSON :: (ToJSON a) => a -> IO String
returnJSON = returnContent . T.unpack . E.decodeUtf8 . encode . toJSON
dbusOverride :: WebView -> DBusState -> UriOverride
dbusOverride wk dbus = withScheme "dbus:" $ \uri -> do
let [busName, busCall] = splitOn "/" (uriPath uri)
let bus = uriBus busName dbus
withMVar bus $ \client -> case busCall of
"listen" -> do
dbusListen wk client uri
returnContent ""
"call" -> do
result <- dbusCall wk client uri
returnJSON result
_ -> returnContent ""
dbusListen :: WebView -> Client -> URI -> IO ()
dbusListen wk client uri = do
let params = parseQuery uri
let matcher = matchRuleUri uri
let (Just index) = liftM read $ lookupQueryParam "index" params
_ <- addMatch client matcher $ callback wk index
return ()
dbusCall :: WebView -> Client -> URI -> IO (Either MethodError MethodReturn)
dbusCall _ client uri = do
let mcall = methodCallUri uri
call client mcall
uriBus :: String -> DBusState -> MVar Client
uriBus "session" = dbusSession
uriBus "system" = dbusSystem
uriBus _ = error "Unknown bus"
matchRuleUri :: URI -> MatchRule
matchRuleUri uri = matchAny { matchSender = Nothing
, matchDestination = Nothing
, matchPath = lookupQueryParam "path" params >>= parseObjectPath
, matchInterface = lookupQueryParam "iface" params >>= parseInterfaceName
, matchMember = lookupQueryParam "member" params >>= parseMemberName
}
where params = parseQuery uri
variantFromString :: String -> Variant
variantFromString param = case splitOn ":" param of
["string", str] -> toVariant str
_ -> error "Invalid variant string"
methodCallUri :: URI -> MethodCall
methodCallUri uri = (methodCall callPath iface member) { methodCallBody = body
, methodCallDestination = dest
}
where params = parseQuery uri
(Just callPath) = parseObjectPath $ getQueryParam "path" params
(Just iface) = parseInterfaceName $ getQueryParam "iface" params
(Just member) = parseMemberName $ getQueryParam "member" params
body = map variantFromString $ getQueryParams "body[]" params
dest = lookupQueryParam "destination" params >>= parseBusName