summaryrefslogtreecommitdiffhomepage
path: root/src/Xmobar.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Xmobar.hs')
-rw-r--r--src/Xmobar.hs143
1 files changed, 97 insertions, 46 deletions
diff --git a/src/Xmobar.hs b/src/Xmobar.hs
index 97d6990..e41c2b7 100644
--- a/src/Xmobar.hs
+++ b/src/Xmobar.hs
@@ -18,12 +18,13 @@ module Xmobar
-- $main
X , XConf (..), runX
, eventLoop
+ , setupSignalHandler
-- * Program Execution
-- $command
, startCommand
-- * Window Management
-- $window
- , createWin, updateWin, enableXRandrEventListen
+ , createWin, updateWin
-- * Printing
-- $print
, drawInWin, printStrings
@@ -42,7 +43,9 @@ import Control.Exception hiding (handle)
import Data.Bits
import Data.Maybe(fromMaybe)
import Data.Typeable (Typeable)
+import Foreign
import System.Posix.Process (getProcessID)
+import System.Posix.Signals
import Config
import Parsers
@@ -60,7 +63,6 @@ type X = ReaderT XConf IO
-- | The ReaderT inner component
data XConf =
XConf { display :: Display
- , xrrDspy :: Display -- display used for XRandr events
, rect :: Rectangle
, window :: Window
, fontS :: XFont
@@ -74,52 +76,97 @@ runX xc f = runReaderT f xc
data WakeUp = WakeUp deriving (Show,Typeable)
instance Exception WakeUp
+data SignalType = Wakeup | Reposition | ChangeScreen
+
-- | The event loop
-eventLoop :: XConf -> [[(Maybe ThreadId, TVar String)]] -> IO ()
-eventLoop xc@(XConf d xrrD _ w fs c) vs = block $ do
+eventLoop :: XConf -> [[(Maybe ThreadId, TVar String)]] -> MVar SignalType -> [Rectangle] -> IO ()
+eventLoop xcfg@(XConf d _ w fs _) vs signal screeninfo = do
tv <- atomically $ newTVar []
- t <- myThreadId
- ct <- forkIO (checker t tv [] `catch` \(SomeException _) -> return ())
- go tv ct
- where
+ tsrs <- atomically $ newTVar screeninfo
+ _ <- forkIO (checker tv [] `catch` \(SomeException _) -> putStrLn "Oh Noez checker" >> return ())
+ _ <- forkOS (eventer tsrs `catch` \(SomeException _) -> putStrLn "Oh Noez eventer" >>return ())
+ go tv xcfg tsrs
+ where
-- interrupt the drawing thread every time a var is updated
- checker t tvar ov = do
+ checker tvar ov = do
nval <- atomically $ do
nv <- mapM concatV vs
guard (nv /= ov)
writeTVar tvar nv
return nv
- throwTo t WakeUp
- checker t tvar nval
+ putMVar signal Wakeup
+ checker tvar nval
concatV = fmap concat . mapM (readTVar . snd)
+ eventer tsrs =
+ alloca $ \ptrEventBase ->
+ alloca $ \ptrErrorBase ->
+ allocaXEvent $ \e -> do
+ _ <- xrrQueryExtension d ptrEventBase ptrErrorBase
+ xrrEventBase <- peek ptrEventBase
+ forever $ do
+ nextEvent d e
+ ev <- getEvent e
+ case ev of
+ ConfigureEvent {} -> sendRepos
+ ExposeEvent {} -> putMVar signal Wakeup
+ _ ->
+ -- keyPressMask is the same value as RRScreenChangeNotify
+ when ( (fromIntegral (ev_event_type ev) - xrrEventBase) == fromIntegral keyPressMask) sendRepos
+ where
+ sendRepos = do
+ srs <- getScreenInfo d
+ atomically $ writeTVar tsrs srs
+ putMVar signal Reposition
+
+
-- Continuously wait for a timer interrupt or an expose event
- go tv ct = do
- catch (unblock $ allocaXEvent $ \e ->
- handle tv ct =<< (nextEvent' d e >> getEvent e))
- (\WakeUp -> runX xc (updateWin tv) >> return ())
- go tv ct
-
- -- event hanlder
- handle _ ct (ConfigureEvent {}) = recreateWindow ct
-
- handle tvar ct (ExposeEvent {}) = block $ do
- -- check if there are XRandr events pending
- num <- pending xrrD
- if num == 0 then
- -- if no pending events, make a update
- runX xc (updateWin tvar)
- else
- recreateWindow ct
-
- handle _ _ _ = return ()
-
- recreateWindow ct = do
- killThread ct
- destroyWindow d w
- (r',w') <- createWin d fs c
- eventLoop (XConf d xrrD r' w' fs c) vs
+ go tv xc@(XConf _ _ _ _ cfg) tsrs = do
+ typ <- takeMVar signal
+ case typ of
+ Wakeup -> do
+ runX xc (updateWin tv)
+ go tv xc tsrs
+ Reposition -> do
+ ncfg <- reposWindow cfg
+ go tv ncfg tsrs
+ ChangeScreen ->
+ case position cfg of
+ OnScreen n o -> do
+ srs <- readTVarIO tsrs
+ if n == length srs then do
+ ncfg <- reposWindow (cfg {position = OnScreen 1 o})
+ go tv ncfg tsrs
+ else do
+ ncfg <- reposWindow (cfg {position = OnScreen (n+1) o})
+ go tv ncfg tsrs
+ o -> do
+ ncfg <- reposWindow (cfg {position = OnScreen 1 o})
+ go tv ncfg tsrs
+ where
+ reposWindow rcfg = do
+ srs <- readTVarIO tsrs
+ r' <- repositionWin d w fs rcfg srs
+ return (XConf d r' w fs rcfg)
+
+-- | Signal handling
+setupSignalHandler :: IO (MVar SignalType)
+setupSignalHandler = do
+ tid <- newEmptyMVar
+ installHandler sigUSR1 (Catch $ updatePosHandler tid) Nothing
+ installHandler sigHUP (Catch $ changeScreenHandler tid) Nothing
+ return tid
+
+updatePosHandler :: MVar SignalType -> IO ()
+updatePosHandler sig = do
+ putMVar sig Reposition
+ return ()
+
+changeScreenHandler :: MVar SignalType -> IO ()
+changeScreenHandler sig = do
+ putMVar sig ChangeScreen
+ return ()
-- $command
@@ -138,16 +185,8 @@ startCommand (com,s,ss)
-- $window
--- | The function to enable notifications from XRandr
-enableXRandrEventListen :: Display -> IO ()
-enableXRandrEventListen d = do
- let dflt = defaultScreen d
- rootw <- rootWindow d dflt
- -- RRScreenChangeNotifyMask has the same value as keyPressMask
- xrrSelectInput d rootw keyPressMask
-
-- | The function to create the initial window
-createWin :: Display -> XFont -> Config -> IO (Rectangle,Window)
+createWin :: Display -> XFont -> Config -> IO (Rectangle,Window,[Rectangle])
createWin d fs c = do
let dflt = defaultScreen d
srs <- getScreenInfo d
@@ -156,11 +195,23 @@ createWin d fs c = do
let ht = as + ds + 4
(r,o) = setPosition (position c) srs (fi ht)
win <- newWindow d (defaultScreenOfDisplay d) rootw r o
+ -- keyPressMask is the same value as RRScreenChangeNotify
+ xrrSelectInput d rootw keyPressMask
selectInput d win (exposureMask .|. structureNotifyMask)
setProperties r c d win srs
when (lowerOnStart c) (lowerWindow d win)
mapWindow d win
- return (r,win)
+ return (r,win, srs)
+
+-- | Updates the size and position of the window
+repositionWin :: Display -> Window -> XFont -> Config -> [Rectangle] -> IO (Rectangle)
+repositionWin d win fs c srs = do
+ (as,ds) <- textExtents fs "0"
+ let ht = as + ds + 4
+ (r,_) = setPosition (position c) srs (fi ht)
+ moveResizeWindow d win (rect_x r) (rect_y r) (rect_width r) (rect_height r)
+ setProperties r c d win srs
+ return r
setPosition :: XPosition -> [Rectangle] -> Dimension -> (Rectangle,Bool)
setPosition p rs ht =