diff options
Diffstat (limited to 'src/Xmobar/App')
| -rw-r--r-- | src/Xmobar/App/EventLoop.hs | 252 | 
1 files changed, 252 insertions, 0 deletions
| diff --git a/src/Xmobar/App/EventLoop.hs b/src/Xmobar/App/EventLoop.hs new file mode 100644 index 0000000..8f0b2dc --- /dev/null +++ b/src/Xmobar/App/EventLoop.hs @@ -0,0 +1,252 @@ +{-# LANGUAGE CPP #-} + +------------------------------------------------------------------------------ +-- | +-- Module: Xmobar.X11.EventLoop +-- Copyright: (c) 2018 Jose Antonio Ortega Ruiz +-- License: BSD3-style (see LICENSE) +-- +-- Maintainer: jao@gnu.org +-- Stability: unstable +-- Portability: portable +-- Created: Sat Nov 24, 2018 19:40 +-- +-- +-- Event loop +-- +------------------------------------------------------------------------------ + + +module Xmobar.App.EventLoop (startLoop, startCommand) where + +import Prelude hiding (lookup) +import Graphics.X11.Xlib hiding (textExtents, textWidth) +import Graphics.X11.Xlib.Extras +import Graphics.X11.Xinerama +import Graphics.X11.Xrandr + +import Control.Arrow ((&&&)) +import Control.Applicative ((<$>)) +import Control.Monad.Reader +import Control.Concurrent +import Control.Concurrent.Async (Async, async) +import Control.Concurrent.STM +import Control.Exception (handle, SomeException(..)) +import Data.Bits +import Data.Map hiding (foldr, map, filter) +import Data.Maybe (fromJust, isJust) + +import Xmobar.Config +import Xmobar.Actions +import Xmobar.Utils +import Xmobar.System.Signal +import Xmobar.Run.Commands +import Xmobar.Run.Runnable +import Xmobar.X11.Parsers +import Xmobar.X11.Window +import Xmobar.X11.XUtil +import Xmobar.X11.Draw +import Xmobar.X11.Bitmap as Bitmap +import Xmobar.X11.Types + +#ifdef XFT +import Graphics.X11.Xft +#endif + +#ifdef DBUS +import Xmobar.System.DBus +#endif + +runX :: XConf -> X () -> IO () +runX xc f = runReaderT f xc + +-- | Starts the main event loop and threads +startLoop :: XConf -> TMVar SignalType -> [[([Async ()], TVar String)]] +             -> IO () +startLoop xcfg@(XConf _ _ w _ _ _ _) sig vs = do +#ifdef XFT +    xftInitFtLibrary +#endif +    tv <- atomically $ newTVar [] +    _ <- forkIO (handle (handler "checker") (checker tv [] vs sig)) +#ifdef THREADED_RUNTIME +    _ <- forkOS (handle (handler "eventer") (eventer sig)) +#else +    _ <- forkIO (handle (handler "eventer") (eventer sig)) +#endif +#ifdef DBUS +    runIPC sig +#endif +    eventLoop tv xcfg [] sig +  where +    handler thing (SomeException e) = +      void $ putStrLn ("Thread " ++ thing ++ " failed: " ++ show e) +    -- Reacts on events from X +    eventer signal = +      allocaXEvent $ \e -> do +        dpy <- openDisplay "" +        xrrSelectInput dpy (defaultRootWindow dpy) rrScreenChangeNotifyMask +        selectInput dpy w (exposureMask .|. structureNotifyMask .|. buttonPressMask) + +        forever $ do +#ifdef THREADED_RUNTIME +          nextEvent dpy e +#else +          nextEvent' dpy e +#endif +          ev <- getEvent e +          case ev of +            ConfigureEvent {} -> atomically $ putTMVar signal Reposition +            ExposeEvent {} -> atomically $ putTMVar signal Wakeup +            RRScreenChangeNotifyEvent {} -> atomically $ putTMVar signal Reposition +            ButtonEvent {} -> atomically $ putTMVar signal (Action (ev_button ev) (fi $ ev_x ev)) +            _ -> return () + +-- | Send signal to eventLoop every time a var is updated +checker :: TVar [String] +           -> [String] +           -> [[([Async ()], TVar String)]] +           -> TMVar SignalType +           -> IO () +checker tvar ov vs signal = do +      nval <- atomically $ do +              nv <- mapM concatV vs +              guard (nv /= ov) +              writeTVar tvar nv +              return nv +      atomically $ putTMVar signal Wakeup +      checker tvar nval vs signal +    where +      concatV = fmap concat . mapM (readTVar . snd) + + +-- | Continuously wait for a signal from a thread or a interrupt handler +eventLoop :: TVar [String] +             -> XConf +             -> [([Action], Position, Position)] +             -> TMVar SignalType +             -> IO () +eventLoop tv xc@(XConf d r w fs vos is cfg) as signal = do +      typ <- atomically $ takeTMVar signal +      case typ of +         Wakeup -> do +            str <- updateString cfg tv +            xc' <- updateCache d w is (iconRoot cfg) str >>= +                     \c -> return xc { iconS = c } +            as' <- updateActions xc r str +            runX xc' $ drawInWin r str +            eventLoop tv xc' as' signal + +         Reposition -> +            reposWindow cfg + +         ChangeScreen -> do +            ncfg <- updateConfigPosition cfg +            reposWindow ncfg + +         Hide   t -> hide   (t*100*1000) +         Reveal t -> reveal (t*100*1000) +         Toggle t -> toggle t + +         TogglePersistent -> eventLoop +            tv xc { config = cfg { persistent = not $ persistent cfg } } as signal + +         Action but x -> action but x + +    where +        isPersistent = not $ persistent cfg + +        hide t +            | t == 0 = +                when isPersistent (hideWindow d w) >> eventLoop tv xc as signal +            | otherwise = do +                void $ forkIO +                     $ threadDelay t >> atomically (putTMVar signal $ Hide 0) +                eventLoop tv xc as signal + +        reveal t +            | t == 0 = do +                when isPersistent (showWindow r cfg d w) +                eventLoop tv xc as signal +            | otherwise = do +                void $ forkIO +                     $ threadDelay t >> atomically (putTMVar signal $ Reveal 0) +                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 as signal + +        reposWindow rcfg = do +          r' <- repositionWin d w (head fs) rcfg +          eventLoop tv (XConf d r' w fs vos is rcfg) as signal + +        updateConfigPosition ocfg = +          case position ocfg of +            OnScreen n o -> do +              srs <- getScreenInfo d +              return (if n == length srs +                       then +                        (ocfg {position = OnScreen 1 o}) +                       else +                        (ocfg {position = OnScreen (n+1) o})) +            o -> return (ocfg {position = OnScreen 1 o}) + +        action button x = do +          mapM_ runAction $ +            filter (\(Spawn b _) -> button `elem` b) $ +            concatMap (\(a,_,_) -> 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 Async handles +-- and the TVar the command will be writing to. +startCommand :: TMVar SignalType +             -> (Runnable,String,String) +             -> IO ([Async ()], TVar String) +startCommand sig (com,s,ss) +    | alias com == "" = do var <- atomically $ newTVar is +                           atomically $ writeTVar var (s ++ ss) +                           return ([], var) +    | otherwise = do var <- atomically $ newTVar is +                     let cb str = atomically $ writeTVar var (s ++ str ++ ss) +                     a1 <- async $ start com cb +                     a2 <- async $ trigger com $ maybe (return ()) +                                                 (atomically . putTMVar sig) +                     return ([a1, a2], var) +    where is = s ++ "Updating..." ++ ss + +updateString :: Config -> TVar [String] +                -> IO [[(Widget, String, Int, Maybe [Action])]] +updateString conf v = do +  s <- readTVarIO v +  let l:c:r:_ = s ++ repeat "" +  liftIO $ mapM (parseString conf) [l, c, r] + +updateActions :: XConf -> Rectangle -> [[(Widget, String, Int, Maybe [Action])]] +                 -> IO [([Action], Position, Position)] +updateActions conf (Rectangle _ _ wid _) ~[left,center,right] = do +  let (d,fs) = (display &&& fontListS) conf +      strLn :: [(Widget, String, Int, Maybe [Action])] -> IO [(Maybe [Action], Position, Position)] +      strLn  = liftIO . mapM getCoords +      iconW i = maybe 0 Bitmap.width (lookup i $ iconS conf) +      getCoords (Text s,_,i,a) = textWidth d (fs!!i) 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, _,_) -> isJust a) $ +                         scanl (\(_,_,x') (a,_,w') -> (a, x', x' + w')) +                               (Nothing, 0, off) +                               xs +      totSLen = foldr (\(_,_,len) -> (+) len) 0 +      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) -> +                       (\xs' -> partCoord (offset a xs') xs') <$> strLn xs) $ +                     zip [L,C,R] [left,center,right] | 
