diff options
| author | Alexander Polakov <plhk@sdf.org> | 2013-02-07 16:08:56 +0400 | 
|---|---|---|
| committer | Jose Antonio Ortega Ruiz <jao@gnu.org> | 2013-03-13 21:11:46 +0100 | 
| commit | fcdc939572cfece0a8ce99f9164aa85f217ef369 (patch) | |
| tree | 68841ad65e3e7e33ca247bd9c1ef1cb02f1428a5 | |
| parent | 8dffc6e722a58924ea65b50dc0e1471b3dd3976b (diff) | |
| download | xmobar-fcdc939572cfece0a8ce99f9164aa85f217ef369.tar.gz xmobar-fcdc939572cfece0a8ce99f9164aa85f217ef369.tar.bz2 | |
Introduce Actions
 Actions are event re-actions.
 Currently only ButtonPress event is handled by Actions and only
 one action is defined, which is called Spawn (run external command).
 Type (and parser) can be extended to EWMH actions (switch to desktop,
 close window, whatever).
| -rw-r--r-- | src/Actions.hs | 23 | ||||
| -rw-r--r-- | src/Bitmap.hs | 9 | ||||
| -rw-r--r-- | src/Parsers.hs | 48 | ||||
| -rw-r--r-- | src/Signal.hs | 2 | ||||
| -rw-r--r-- | src/Xmobar.hs | 68 | ||||
| -rw-r--r-- | xmobar.cabal | 2 | 
6 files changed, 110 insertions, 42 deletions
| diff --git a/src/Actions.hs b/src/Actions.hs new file mode 100644 index 0000000..156dc4e --- /dev/null +++ b/src/Actions.hs @@ -0,0 +1,23 @@ +----------------------------------------------------------------------------- +-- | +-- Module      :  Xmobar.Actions +-- Copyright   :  (c) Alexander Polakov +-- License     :  BSD-style (see LICENSE) +-- +-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org> +-- Stability   :  unstable +-- Portability :  unportable +-- +----------------------------------------------------------------------------- + +module Actions where + +import System.Process (system) +import Control.Monad (void) + +data Action = Spawn String +                deriving (Eq) + +runAction :: Action -> IO () +runAction (Spawn s) = void $ system (s ++ "&") +runAction _ = return ()  diff --git a/src/Bitmap.hs b/src/Bitmap.hs index b073c9b..2045e1a 100644 --- a/src/Bitmap.hs +++ b/src/Bitmap.hs @@ -22,17 +22,18 @@ import System.Directory (doesFileExist)  import System.Mem.Weak ( addFinalizer )  import ColorCache  import Parsers (Widget(..)) +import Actions (Action)  data Bitmap = Bitmap { width  :: Dimension                       , height :: Dimension                       , pixmap :: Pixmap                       } -updateCache :: Display -> Window -> Map FilePath Bitmap -> [[(Widget, String)]] -               -> IO (Map FilePath Bitmap) +updateCache :: Display -> Window -> Map FilePath Bitmap -> +               [[(Widget, String, Maybe Action)]] -> IO (Map FilePath Bitmap)  updateCache dpy win cache ps = do -  let paths = map (\(Icon p, _) -> p) . concatMap (filter icons) $ ps -      icons (Icon _, _) = True +  let paths = map (\(Icon p, _, _) -> p) . concatMap (filter icons) $ ps +      icons (Icon _, _, _) = True        icons _ = False        go m path = if member path m                       then return m diff --git a/src/Parsers.hs b/src/Parsers.hs index 62345fe..f3b0a2e 100644 --- a/src/Parsers.hs +++ b/src/Parsers.hs @@ -23,6 +23,7 @@ module Parsers  import Config  import Runnable  import Commands +import Actions  import qualified Data.Map as Map  import Text.ParserCombinators.Parsec @@ -33,24 +34,26 @@ data Widget = Icon String | Text String  type ColorString = String  -- | Runs the string parser -parseString :: Config -> String -> IO [(Widget, ColorString)] +parseString :: Config -> String -> IO [(Widget, ColorString, Maybe Action)]  parseString c s = -    case parse (stringParser (fgColor c)) "" s of -      Left  _ -> return [(Text $ "Could not parse string: " ++ s, fgColor c)] +    case parse (stringParser (fgColor c) Nothing) "" s of +      Left  _ -> return [(Text $ "Could not parse string: " ++ s, fgColor c, Nothing)]        Right x -> return (concat x)  -- | Gets the string and combines the needed parsers -stringParser :: String -> Parser [[(Widget, ColorString)]] -stringParser c = manyTill (textParser c <|> try (iconParser c) <|> colorParser) eof +stringParser :: String -> Maybe Action -> Parser [[(Widget, ColorString, Maybe Action)]] +stringParser c a = manyTill (textParser c a <|> try (iconParser c a) <|> try (actionParser c) <|> colorParser a) eof  -- | Parses a maximal string without color markup. -textParser :: String -> Parser [(Widget, ColorString)] -textParser c = do s <- many1 $ -                    noneOf "<" <|> -                    (try $ notFollowedBy' (char '<') -                                          (string "fc="  <|> -                                          string "icon=" <|> string "/fc>")) -                  return [(Text s, c)] +textParser :: String -> Maybe Action -> Parser [(Widget, ColorString, Maybe Action)] +textParser c a = do s <- many1 $ +                        noneOf "<" <|> +                        (try $ notFollowedBy' (char '<') +                                               (try (string "fc=")  <|> +                                               try (string "action=") <|> +                                               try (string "/action>") <|> +                                               try (string "icon=") <|> string "/fc>")) +                    return [(Text s, c, a)]  -- | Wrapper for notFollowedBy that returns the result of the first parser. @@ -61,17 +64,24 @@ notFollowedBy' p e = do x <- p                          notFollowedBy $ try (e >> return '*')                          return x -iconParser :: String -> Parser [(Widget, ColorString)] -iconParser c = do +iconParser :: String -> Maybe Action -> Parser [(Widget, ColorString, Maybe Action)] +iconParser c a = do    string "<icon="    i <- manyTill (noneOf ">") (try (string "/>")) -  return [(Icon i, c)] -   +  return [(Icon i, c, a)] + +actionParser :: String -> Parser [(Widget, ColorString, Maybe Action)] +actionParser c = do +  a <- between (string "<action=") (string ">") (many1 (noneOf ">")) +  let a' = Just (Spawn a) +  s <- manyTill (try (textParser c a') <|> try (iconParser c a') <|> try (colorParser a') <|> actionParser c) (try $ string "</action>") +  return (concat s) +   -- | Parsers a string wrapped in a color specification. -colorParser :: Parser [(Widget, ColorString)] -colorParser = do +colorParser :: Maybe Action -> Parser [(Widget, ColorString, Maybe Action)] +colorParser a = do    c <- between (string "<fc=") (string ">") colors -  s <- manyTill (try (textParser c <|> iconParser c) <|> colorParser) (try $ string "</fc>") +  s <- manyTill (try (textParser c a) <|> try (iconParser c a) <|> try (colorParser a) <|> actionParser c) (try $ string "</fc>")    return (concat s)  -- | Parses a color specification (hex or named) diff --git a/src/Signal.hs b/src/Signal.hs index 8b3b325..34d8cd7 100644 --- a/src/Signal.hs +++ b/src/Signal.hs @@ -22,6 +22,7 @@ import Data.Typeable (Typeable)  import Control.Concurrent.STM  import Control.Exception hiding (handle)  import System.Posix.Signals +import Graphics.X11.Xlib.Types (Position)  #ifdef DBUS  import DBus (IsVariant(..)) @@ -40,6 +41,7 @@ data SignalType = Wakeup                  | Reveal Int                  | Toggle Int                  | TogglePersistent +                | Action Position      deriving (Read, Show)  #ifdef DBUS diff --git a/src/Xmobar.hs b/src/Xmobar.hs index 67badaa..25d8fab 100644 --- a/src/Xmobar.hs +++ b/src/Xmobar.hs @@ -42,11 +42,13 @@ import Control.Concurrent.STM  import Control.Exception (handle, SomeException(..))  import Data.Bits  import Data.Map hiding (foldr, map, filter) +import Data.Maybe (fromJust)  import Bitmap  import Config  import Parsers  import Commands +import Actions  import Runnable  import Signal  import Window @@ -98,7 +100,7 @@ startLoop xcfg@(XConf _ _ w _ _ _) sig vs = do  #ifdef DBUS      runIPC sig  #endif -    eventLoop tv xcfg sig +    eventLoop tv xcfg [] sig    where      handler thing (SomeException _) =        void $ putStrLn ("Thread " ++ thing ++ " failed") @@ -107,7 +109,7 @@ startLoop xcfg@(XConf _ _ w _ _ _) sig vs = do        allocaXEvent $ \e -> do          dpy <- openDisplay ""          xrrSelectInput    dpy (defaultRootWindow dpy) rrScreenChangeNotifyMask -        selectInput       dpy w (exposureMask .|. structureNotifyMask) +        selectInput       dpy w (exposureMask .|. structureNotifyMask .|. buttonPressMask)          forever $ do  #ifdef THREADED_RUNTIME @@ -120,6 +122,7 @@ startLoop xcfg@(XConf _ _ w _ _ _) sig vs = do              ConfigureEvent {} -> atomically $ putTMVar signal Reposition              ExposeEvent {} -> atomically $ putTMVar signal Wakeup              RRScreenChangeNotifyEvent {} -> atomically $ putTMVar signal Reposition +            ButtonEvent {} -> atomically $ putTMVar signal (Action (fi $ ev_x ev))              _ -> return ()  -- | Send signal to eventLoop every time a var is updated @@ -141,15 +144,16 @@ checker tvar ov vs signal = do  -- | Continuously wait for a signal from a thread or a interrupt handler -eventLoop :: TVar [String] -> XConf -> TMVar SignalType -> IO () -eventLoop tv xc@(XConf d r w fs is cfg) signal = do +eventLoop :: TVar [String] -> XConf -> [(Action, Position, Position)] -> TMVar SignalType -> IO () +eventLoop tv xc@(XConf d r w fs is cfg) as signal = do        typ <- atomically $ takeTMVar signal        case typ of           Wakeup -> do              str <- updateString cfg tv              xc' <- updateCache d w is str >>= \c -> return xc { iconS = c } +            as' <- updateActions xc r str              runX xc' $ drawInWin r str -            eventLoop tv xc' signal +            eventLoop tv xc' as' signal           Reposition ->              reposWindow cfg @@ -163,36 +167,38 @@ eventLoop tv xc@(XConf d r w fs is cfg) signal = do           Toggle t -> toggle t           TogglePersistent -> eventLoop -            tv xc { config = cfg { persistent = not $ persistent cfg } } signal +            tv xc { config = cfg { persistent = not $ persistent cfg } } as signal + +         Action x -> action x      where          isPersistent = not $ persistent cfg          hide t              | t == 0 = -                when isPersistent (hideWindow d w) >> eventLoop tv xc signal +                when isPersistent (hideWindow d w) >> eventLoop tv xc as signal              | otherwise = do                  void $ forkIO                       $ threadDelay t >> atomically (putTMVar signal $ Hide 0) -                eventLoop tv xc signal +                eventLoop tv xc as signal          reveal t              | t == 0 = do                  when isPersistent (showWindow r cfg d w) -                eventLoop tv xc signal +                eventLoop tv xc as signal              | otherwise = do                  void $ forkIO                       $ threadDelay t >> atomically (putTMVar signal $ Reveal 0) -                eventLoop tv xc signal +                eventLoop tv xc as signal          toggle t = do              ismapped <- isMapped d w              atomically (putTMVar signal $ if ismapped then Hide t else Reveal t) -            eventLoop tv xc signal +            eventLoop tv xc as signal          reposWindow rcfg = do            r' <- repositionWin d w fs rcfg -          eventLoop tv (XConf d r' w fs is rcfg) signal +          eventLoop tv (XConf d r' w fs is rcfg) as signal          updateConfigPosition ocfg =            case position ocfg of @@ -205,6 +211,9 @@ eventLoop tv xc@(XConf d r w fs is cfg) signal = do              o ->                return (ocfg {position = OnScreen 1 o}) +        action x = do mapM_ (\(a,_,_) -> runAction a) $ filter (\(_, from, to) -> x >= from && x <= to) as +                      eventLoop tv xc as signal +  -- $command  -- | Runs a command as an independent thread and returns its thread id @@ -224,24 +233,47 @@ startCommand sig (com,s,ss)                             return (Just h,var)      where is = s ++ "Updating..." ++ ss -updateString :: Config -> TVar [String] -> IO [[(Widget, String)]] +updateString :: Config -> TVar [String] -> IO [[(Widget, String, Maybe Action)]]  updateString conf v = do    s <- atomically $ readTVar v    let l:c:r:_ = s ++ repeat ""    io $ mapM (parseString conf) [l, c, r] +updateActions :: XConf -> Rectangle -> [[(Widget, String, Maybe Action)]] -> +                 IO [(Action, Position, Position)] +updateActions conf (Rectangle _ _ wid _) ~[left,center,right] = do +  let (d,fs) = (display &&& fontS) conf +      strLn  = io . mapM getCoords +      iconW i = maybe 0 Bitmap.width (lookup i $ iconS conf) +      getCoords (Text s,_,a) = textWidth d fs s >>= \tw -> return (a,0,fi tw) +      getCoords (Icon s,_,a) = return (a,0,fi $ iconW s) +      partCoord off xs = map (\(a, x, x') -> (fromJust a, x, x')) $ +                         filter (\(a, _,_) -> a /= Nothing) $ +                         scanl (\(_,_,x') (a,_,w') -> (a, x', x' + w')) (Nothing, 0, off) xs + +      totSLen              = (\(_,_,len) -> fi len) . last +      remWidth xs          = fi wid - totSLen xs +      offs                 = 1 +      offset a xs          = case a of +                               C -> (remWidth xs + offs) `div` 2 +                               R -> remWidth xs +                               L -> offs + +  fmap concat $ mapM (\(a,xs) -> strLn xs >>= \xs' -> return $ partCoord (offset a xs') xs') $ +                zip [L,C,R] [left,center,right] +  -- $print  -- | Draws in and updates the window -drawInWin :: Rectangle -> [[(Widget, String)]] -> X () +drawInWin :: Rectangle -> [[(Widget, String, Maybe Action)]] -> X ()  drawInWin (Rectangle _ _ wid ht) ~[left,center,right] = do    r <- ask    let (c,d ) = (config &&& display) r        (w,fs) = (window &&& fontS  ) r        strLn  = io . mapM getWidth        iconW i = maybe 0 Bitmap.width (lookup i $ iconS r) -      getWidth (Text s,cl) = textWidth d fs s >>= \tw -> return (Text s,cl,fi tw) -      getWidth (Icon s,cl) = return (Icon s,cl,fi $ iconW s) +      getWidth (Text s,cl,_) = textWidth d fs s >>= \tw -> return (Text s,cl,fi tw) +      getWidth (Icon s,cl,_) = return (Icon s,cl,fi $ iconW s)    withColors d [bgColor c, borderColor c] $ \[bgcolor, bdcolor] -> do      gc <- io $ createGC  d w @@ -272,8 +304,8 @@ printStrings _ _ _ _ _ [] = return ()  printStrings dr gc fontst offs a sl@((s,c,l):xs) = do    r <- ask    (as,ds) <- case s of -                  Text t -> io $ textExtents fontst t -                  Icon _ -> return (0, 0) +               Text t -> io $ textExtents fontst t +               Icon _ -> return (0, 0)    let (conf,d)             = (config &&& display) r        Rectangle _ _ wid ht = rect r        totSLen              = foldr (\(_,_,len) -> (+) len) 0 sl diff --git a/xmobar.cabal b/xmobar.cabal index 49ad9ff..2cd247f 100644 --- a/xmobar.cabal +++ b/xmobar.cabal @@ -74,7 +74,7 @@ executable xmobar      hs-source-dirs:     src      main-is:            Main.hs      other-modules: -      Xmobar, Bitmap, Config, Parsers, Commands, Localize, +      Xmobar, Actions, Bitmap, Config, Parsers, Commands, Localize,        XUtil, StatFS, Runnable, ColorCache, Window, Signal,        Plugins, Plugins.BufferedPipeReader,        Plugins.CommandReader, Plugins.Date, Plugins.EWMH, | 
