2012-06-15 19 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線程運行,直到它返回。

相關問題