diff options
| -rw-r--r-- | README | 24 | ||||
| -rw-r--r-- | src/Plugins/Monitors.hs | 15 | ||||
| -rw-r--r-- | src/Plugins/Monitors/Mpris.hs | 129 | ||||
| -rw-r--r-- | xmobar.cabal | 9 | 
4 files changed, 177 insertions, 0 deletions
| @@ -115,6 +115,10 @@ Otherwise, you'll need to install them yourself.  `with_mpd`  :    Enables support for the [MPD] daemon. Requires the [libmpd] package. +`with_mpris` +:    Enables support for MPRIS v1/v2 protocol.  +     Requires the [dbus-core] and [text] packages. +  `with_inotify`  :    Support for inotify in modern linux kernels. This option is needed       for the MBox and Mail plugins to work. Requires the [hinotify] @@ -631,6 +635,24 @@ Monitors have default aliases.                    "<composer> <title> (<album>) <track>/<plength> <statei> ",                    "--", "-P", ">>", "-Z", "|", "-S", "><"] 10 +`Mpris1 PlayerName Args RefreshRate` +- Aliases to `mpris1` +- Requires [dbus-core] and [text] packages.  +  To activate, pass `--flags="with_mpris"` during compilation. +- PlayerName: player supporting MPRIS v1 protocol, in lowercase. +- Args: default monitor arguments. +- Variables that can be used with the `-t`/`--template` argument: +            `album`, `artist`, `arturl`, `length`, `title`, `tracknumber` +- Default template: `<artist> - <title>` +- Example: + +         Run Mpris1 "clementine" ["-t", +                                  "<artist> - [<tracknumber>] <title>"] 10 + +`Mpris2 PlayerName Args RefreshRate` +- Just like Mpris1.  +  Supposed to be used with mediaplayers which support MPRIS v2. +  `Mail Args Alias`  - Args: list of maildirs in form @@ -1093,6 +1115,8 @@ Copyright © 2010-2011 Jose Antonio Ortega Ruiz  [iwlib]: http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html  [hinotify]: http://hackage.haskell.org/package/hinotify/  [libmpd]: http://hackage.haskell.org/package/libmpd/ +[dbus-core]: http://hackage.haskell.org/package/dbus-core +[text]: http://hackage.haskell.org/package/text  [sawfish]: http://sawfish.wikia.com/  [utf8-string]: http://hackage.haskell.org/package/utf8-string/  [alsa-core]: http://hackage.haskell.org/package/alsa-core diff --git a/src/Plugins/Monitors.hs b/src/Plugins/Monitors.hs index 5e1cb62..9f17699 100644 --- a/src/Plugins/Monitors.hs +++ b/src/Plugins/Monitors.hs @@ -44,6 +44,9 @@ import Plugins.Monitors.MPD  #ifdef ALSA  import Plugins.Monitors.Volume  #endif +#ifdef MPRIS +import Plugins.Monitors.Mpris +#endif  data Monitors = Weather      Station    Args Rate                | Network      Interface  Args Rate @@ -72,6 +75,10 @@ data Monitors = Weather      Station    Args Rate  #ifdef ALSA                | Volume   String     String Args Rate  #endif +#ifdef MPRIS +              | Mpris1   String     Args Rate +              | Mpris2   String     Args Rate +#endif                  deriving (Show,Read,Eq)  type Args      = [String] @@ -112,6 +119,10 @@ instance Exec Monitors where  #ifdef ALSA      alias (Volume m c _ _) = m ++ ":" ++ c  #endif +#ifdef MPRIS +    alias (Mpris1 _ _ _) = "mpris1" +    alias (Mpris2 _ _ _) = "mpris2" +#endif      start (Network  i a r) = startNet i a r      start (Cpu a r) = startCpu a r      start (MultiCpu a r) = startMultiCpu a r @@ -140,3 +151,7 @@ instance Exec Monitors where  #ifdef ALSA      start (Volume m c a r) = runM a volumeConfig (runVolume m c) r  #endif +#ifdef MPRIS +    start (Mpris1 s a r) = runM a mprisConfig (runMPRIS1 s) r +    start (Mpris2 s a r) = runM a mprisConfig (runMPRIS2 s) r +#endif diff --git a/src/Plugins/Monitors/Mpris.hs b/src/Plugins/Monitors/Mpris.hs new file mode 100644 index 0000000..1bac05b --- /dev/null +++ b/src/Plugins/Monitors/Mpris.hs @@ -0,0 +1,129 @@ +{-# LANGUAGE OverloadedStrings #-} + +---------------------------------------------------------------------------- +-- | +-- Module      :  Plugins.Monitors.Mpris +-- Copyright   :  (c) Artem Tarasov +-- License     :  BSD-style (see LICENSE) +-- +-- Maintainer  :  Artem Tarasov <lomereiter@gmail.com> +-- Stability   :  unstable +-- Portability :  unportable +-- +--   MPRIS song info +-- +---------------------------------------------------------------------------- + +module Plugins.Monitors.Mpris ( mprisConfig, runMPRIS1, runMPRIS2 ) where + +-- TODO: listen to signals + +import Plugins.Monitors.Common + +import Text.Printf (printf) +import qualified DBus.Client.Simple as C +import DBus.Types +import DBus.Connection ( ConnectionError ) +import Data.Maybe ( fromJust ) +import Data.Int ( Int32, Int64 ) +import System.IO.Unsafe (unsafePerformIO) +import qualified Data.Text as T + +import Control.Exception (try, evaluate) + +class MprisVersion a where +    getProxy :: a -> C.Client -> String -> IO C.Proxy +    getMetadataReply :: a -> C.Client -> String -> IO [Variant] +    fieldsList :: a -> [String] + +data MprisVersion1 = MprisVersion1 +instance MprisVersion MprisVersion1 where +    getProxy MprisVersion1 c p = do +        let playerBusName = T.concat ["org.mpris.", T.pack p] +        C.proxy c (C.busName_ playerBusName) "/Player" +    getMetadataReply MprisVersion1 c p = do +        player <- getProxy MprisVersion1 c p +        C.call player "org.freedesktop.MediaPlayer" "GetMetadata" [] +    fieldsList MprisVersion1 = [ "album", "artist", "arturl", "mtime", "title", "tracknumber" ] + +data MprisVersion2 = MprisVersion2 +instance MprisVersion MprisVersion2 where +    getProxy MprisVersion2 c p = do +        let playerBusName = T.concat ["org.mpris.MediaPlayer2.", T.pack p] +        C.proxy c (C.busName_ playerBusName) "/org/mpris/MediaPlayer2" +    getMetadataReply MprisVersion2 c p = do +        player <- getProxy MprisVersion2 c p +        C.call player "org.freedesktop.DBus.Properties"  +                      "Get"  +                      (map (toVariant::String -> Variant)  +                           ["org.mpris.MediaPlayer2.Player", "Metadata"] +                      ) +    fieldsList MprisVersion2 = [ "xesam:album", "xesam:artist", "mpris:artUrl" +                               , "mpris:length", "xesam:title", "xesam:trackNumber" +                               ] + +mprisConfig :: IO MConfig +mprisConfig = mkMConfig "<artist> - <title>" +                [ "album", "artist", "arturl", "length" , "title", "tracknumber"  +                ] + +dbusClient :: C.Client +dbusClient = unsafePerformIO C.connectSession + +runMPRIS :: (MprisVersion a) => a -> String -> [String] -> Monitor String +runMPRIS version playerName _ = do +    metadata <- io $ getMetadata version dbusClient playerName +    parseTemplate $ makeList version metadata + +runMPRIS1 :: String -> [String] -> Monitor String +runMPRIS1 = runMPRIS MprisVersion1 + +runMPRIS2 :: String -> [String] -> Monitor String +runMPRIS2 = runMPRIS MprisVersion2 + +--------------------------------------------------------------------------- + +fromVar :: (IsVariant a) => Variant -> a +fromVar = fromJust . fromVariant + +unpackMetadata :: [Variant] -> [(String, Variant)] +unpackMetadata [] = [] +unpackMetadata xs = ((map (\(k, v) -> (fromVar k, fromVar v))) . unpack . head) xs where  +                      unpack v = case variantType v of +                            TypeDictionary _ _ -> dictionaryItems $ fromVar v +                            TypeVariant -> unpack $ fromVar v +                            TypeStructure _ -> unpack $ head $ structureItems $ fromVar v +                            _ -> [] + +getMetadata :: (MprisVersion a) => a -> C.Client -> String -> IO [(String, Variant)] +getMetadata version client player = do +    reply <- try (getMetadataReply version client player) :: +                            IO (Either ConnectionError [Variant]) +    return $ case reply of  +                  Right metadata -> unpackMetadata metadata;  +                  Left _ -> [] + +makeList :: (MprisVersion a) => a -> [(String, Variant)] -> [String] +makeList version md = map getStr (fieldsList version) where  +            formatTime n = (if hh == 0 then printf "%02d:%02d" +                                       else printf "%d:%02d:%02d" hh) mm ss +                           where hh = (n `div` 60) `div` 60 +                                 mm = (n `div` 60) `mod` 60 +                                 ss = n `mod` 60 +            getStr str = case lookup str md of +                Nothing -> "" +                Just v -> case variantType v of +                            TypeString -> fromVar v +                            TypeInt32 -> let num = fromVar v in +                                          case str of +                                           "mtime" -> formatTime (num `div` 1000) +                                           "tracknumber" -> printf "%02d" num +                                           "mpris:length" -> formatTime (num `div` 1000000) +                                           "xesam:trackNumber" -> printf "%02d" num +                                           _ -> (show::Int32 -> String) num +                            TypeInt64 -> let num = fromVar v in +                                          case str of +                                           "mpris:length" -> formatTime (num `div` 1000000) +                                           _ -> (show::Int64 -> String) num +                            TypeArray TypeString -> fromVar $ head $ arrayItems $ fromVar v +                            _ -> "" diff --git a/xmobar.cabal b/xmobar.cabal index 1f7b4db..ffd2cd7 100644 --- a/xmobar.cabal +++ b/xmobar.cabal @@ -61,6 +61,10 @@ flag with_datezone    description: Enables localized date support    default: False +flag with_mpris +  description: MPRIS v1, v2 support +  default: False +  executable xmobar      hs-source-dirs:     src      main-is:            Main.hs @@ -150,3 +154,8 @@ executable xmobar         build-depends: timezone-olson == 0.1.*, timezone-series == 0.1.*         other-modules: Plugins.DateZone         cpp-options: -DDATEZONE + +    if flag(with_mpris) || flag(all_extensions) +       build-depends: dbus-core >= 0.9.2.1, text >= 0.11.1.5 && < 0.12 +       other-modules: Plugins.Monitors.Mpris +       cpp-options: -DMPRIS | 
