packages feed

tianbar-0.3.6.0: src/System/Tianbar/DBus.hs

{-# Language OverloadedStrings #-}
module System.Tianbar.DBus where

-- DBus connectivity

import Control.Concurrent.MVar
import Control.Monad

import Data.Aeson hiding (Array)
import Data.List
import Data.List.Split
import qualified Data.Text.Lazy as T
import qualified Data.Text.Lazy.Encoding as E

import qualified Graphics.UI.Gtk as Gtk
import Graphics.UI.Gtk.WebKit.WebView

import DBus
import DBus.Client

import Network.URI

import System.Tianbar.DBus.JSON ()
import System.Tianbar.UriOverride

type DBusState = MVar DBusConn

data DBusConn = DBusConn { dbusSession :: Client
                         , dbusSystem :: Client
                         }

connectState :: IO DBusConn
connectState = do
    session <- connectSession
    system <- connectSystem
    return $ DBusConn session system

initDBusState :: IO DBusState
initDBusState = connectState >>= newMVar

reloadDBusState :: DBusState -> IO ()
reloadDBusState state = modifyMVar_ state $ \dbus -> do
    mapM_ disconnect [dbusSession dbus, dbusSystem dbus]
    connectState

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 state = withScheme "dbus:" $ \uri -> do
    let busCall = splitOn "/" (uriPath uri)
    let params = parseQuery uri
    case busCall of
        [bus, "listen"] -> withMVar state $ \dbus -> do
            let client = uriBus bus dbus
            dbusListen wk client params
            returnContent ""
        [bus, "call"] -> withMVar state $ \dbus -> do
            let client = uriBus bus dbus
            result <- dbusCall client params
            returnJSON result
        _ -> returnContent ""

dbusListen :: WebView -> Client -> URIParams -> IO ()
dbusListen wk client params = do
    let matcher = matchRuleUri params
    let (Just index) = liftM read $ lookupQueryParam "index" params
    _ <- addMatch client matcher $ callback wk index
    return ()

dbusCall :: Client -> URIParams -> IO (Either MethodError MethodReturn)
dbusCall client params = do
    let mcall = methodCallUri params
    call client mcall

uriBus :: String -> DBusConn -> Client
uriBus "session" = dbusSession
uriBus "system" = dbusSystem
uriBus _ = error "Unknown bus"

matchRuleUri :: URIParams -> MatchRule
matchRuleUri params = matchAny { matchSender = Nothing
                               , matchDestination = Nothing
                               , matchPath = lookupQueryParam "path" params >>= parseObjectPath
                               , matchInterface = lookupQueryParam "iface" params >>= parseInterfaceName
                               , matchMember = lookupQueryParam "member" params >>= parseMemberName
                               }

variantFromString :: String -> Variant
variantFromString param = case splitOn ":" param of
    ["string", str] -> toVariant str
    _ -> error "Invalid variant string"

methodCallUri :: URIParams -> MethodCall
methodCallUri params = (methodCall callPath iface member) { methodCallBody = body
                                                          , methodCallDestination = dest
                                                          }
    where (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