2012-06-15 17 views
5

我使用dynamicLogWithPPXMonad.Hooks.DynamicLog连同dzen2作为xmonad下的状态栏。我想在栏中显示的其中一件事是当前正在播放的曲目中大胆的时间(如果有的话)。得到这个信息很简单:xmonad的logHook可以以设定的时间间隔运行,而不是仅仅响应布局事件?

audStatus :: Player -> X (Maybe String) 
audStatus p = do 
    info <- liftIO $ tryS $ withPlayer p $ do 
       ispaused <- paused 
       md <- getMetadataString 
       timeleftmillis <- (-) <$> (getCurrentTrack >>= songFrames) <*> time 
       let artist = md ! "artist" 
        title = md ! "title" 
        timeleft = timeleftmillis `quot` 1000 
        (minutes, seconds) = timeleft `quotRem` 60 
        disp = artist ++ " - " ++ title ++ " (-"++(show minutes)++":"++(show seconds)++")" -- will be wrong if seconds < 10 
        audcolor False = dzenColor base0 base03 
        audcolor True = dzenColor base1 base02 
       return $ wrap "^ca(1, pms p)" "^ca()" (audcolor ispaused disp) 
    return $ either (const Nothing) Just info 

所以我能够坚持,在ppExtras,它工作正常— 除了它只有当logHook获取运行总是会自动运行,并在合适的事件归结派克只会发生。因此,显示屏可能会长时间处于静态状态,直到我(例如)切换工作区。

看起来好像有些人只是跑两个酒吧,其中一个从shell脚本获取输出。这是定期更新的唯一方法吗?或者,这可以从xmonad内完成(不要太疯狂/哈克)?

ETA:我想这一点,这似乎如果它应该更好地工作比它:

  1. 从XMonad更新创建TChan,另一个用于从函数轮询大胆更新;
  2. 设置ppOutput字段在PP结构中从DynamicLog写到第一个TChan;
  3. fork大胆轮询功能,并写入第二个TChan;
  4. 从两个TChans中分别读取一个函数(首先检查它们是否为空),然后组合输出。

更新从XMonad从通道读取,并及时处理,但大胆的更新,在所有—几乎不登记每五秒左右最好。尽管如此,似乎沿着这些方向的一些方法应该起作用。

+0

我已经更新了我的答案,并解释了为什么您提出的'TChan'解决方案以及其他基于多线程的解决方案无法正常工作。 –

+0

感谢您的更新。 –

回答

6

我知道这是一个古老的问题,但我前几天在这里寻找答案,我想我会分享我解决它的方式。你实际上可以完全从xmonad完成。这是一个小黑客,但我认为它比我遇到的任何替代品都要好得多。

基本上,我使用了XMonad.Util.Timer库,它会在指定的时间段(在本例中为一秒)后发送X事件。然后我为它写了一个事件钩子,它再次启动计时器,然后手动运行日志钩子。

我也不得不使用XMonad.Util.ExtensibleState库,因为Timer使用一个id变量来确保它响应正确的事件,所以我必须在事件之间存储该变量。

这里是我的代码:

{-# LANGUAGE DeriveDataTypeable #-} 

import qualified XMonad.Util.ExtensibleState as XS 
import XMonad.Util.Timer 

... 

-- wrapper for the Timer id, so it can be stored as custom mutable state 
data TidState = TID TimerId deriving Typeable 

instance ExtensionClass TidState where 
    initialValue = TID 0 

... 

-- put this in your startupHook 
-- start the initial timer, store its id 
clockStartupHook = startTimer 1 >>= XS.put . TID 

-- put this in your handleEventHook 
clockEventHook e = do    -- e is the event we've hooked 
    (TID t) <- XS.get     -- get the recent Timer id 
    handleTimer t e $ do    -- run the following if e matches the id 
    startTimer 1 >>= XS.put . TID -- restart the timer, store the new id 
    ask >>= logHook.config   -- get the loghook and run it 
    return Nothing     -- return required type 
    return $ All True     -- return required type 

非常简单。我希望这对某人有帮助。

+0

对于使用此代码的其他人,您还必须'导入Data.Monoid' – user316146

+0

这打破了为我重新启动xmonad的能力:我假设重新启动的进程无法处理来自旧进程的事件(不确定这些活动如何生存该过程重新启动)。我必须延长它停止发射关机已启动并等待,直到关机过程中处理了最后一个事件。 – akosch

+0

@akosch哦哇,我忘了这个问题。是的,它也使我无法重新启动,并且自从发布此答案以来我已更改我的个人设置。现在我有一个Conky实例每秒发送一次X事件给xmonad一次。该事件包含硬件信息,我也在dzen中显示,但重点是我正在使用外部程序来触发更新。但是如果你在重启时使用它,也许应该用你的代码更新答案? – DarthFennec

2

它不能从xmonad内完成; xmonad目前的线程模型有点欠缺(dzen也是如此)。但是,您可以启动一个单独的进程,定期轮询您的音乐播放器,然后使用其中一个dzen多路复用器(例如dmplex)合并两个进程的输出。

您可能还想看看xmobartaffybar,它们的线程故事比dzen更好。

关于为什么您建议的TChan解决方案无法正常工作,您可能需要阅读my crash course on the FFI and gtk上的“约定”,“外部导入”和“无线运行时”部分,请记住xmonad目前使用GHC的非线程运行时。简短的回答是,xmonad的主循环对Xlib进行FFI调用,等待X事件;此调用阻止所有其他Haskell线程运行,直到它返回。

相关问题