-----------------------------------------------------------------------------
-- |
-- Module : Main.hs
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : sjanssen@cse.unl.edu
-- Stability : unstable
-- Portability : not portable, uses mtl, X11, posix
--
-----------------------------------------------------------------------------
--
-- thunk, a minimal window manager for X11
--
import qualified Data.Map as Map
import Data.Map (Map)
import Data.Sequence as Seq
import qualified Data.Foldable as Fold
import Data.Bits
import System.IO
import System.Process (runCommand)
import System.Exit
import Graphics.X11.Xlib
import Graphics.X11.Xlib.Extras
import Control.Monad.State
import W
------------------------------------------------------------------------
--
-- let's get underway
--
main :: IO ()
main = do
dpy <- openDisplay ""
runW realMain $ WState
{ display = dpy
, screenWidth = displayWidth dpy (defaultScreen dpy)
, screenHeight = displayHeight dpy (defaultScreen dpy)
, windows = Seq.empty
}
return ()
--
-- Grab the display and input, and jump into the input loop
--
realMain :: W ()
realMain = do
dpy <- getDisplay
let screen = defaultScreen dpy
io $ do root <- rootWindow dpy screen
selectInput dpy root (substructureRedirectMask .|. substructureNotifyMask)
sync dpy False
grabkeys
loop
--
-- The main event handling loop
--
loop :: W ()
loop = do
dpy <- getDisplay
forever $ do
e <- io $ allocaXEvent $ \ev -> nextEvent dpy ev >> getEvent ev
handler e
--
-- The event handler
--
handler :: Event -> W ()
handler (MapRequestEvent {window = w}) = manage w
handler (DestroyWindowEvent {window = w}) = do
modifyWindows (Seq.fromList . filter (/= w) . Fold.toList)
refresh
handler (KeyEvent {event_type = t, state = mod, keycode = code})
| t == keyPress = do
dpy <- getDisplay
sym <- io $ keycodeToKeysym dpy code 0
case filter (\(mod', sym', _) -> mod == mod' && sym == sym') keys of
[] -> return ()
((_, _, act):_) -> act
handler _ = return ()
--
-- switch focus to next window in list.
--
switch :: W ()
switch = do
ws' <- getWindows
case viewl ws' of
EmptyL -> return ()
(w :< ws) -> do
setWindows (ws |> w)
refresh
--
-- | spawn. Launch an external application
--
spawn :: String -> W ()
spawn = io_ . runCommand
--
-- | Keys we understand.
--
keys :: [(KeyMask, KeySym, W ())]
keys =
[ (mod1Mask .|. shiftMask, xK_Return, spawn "xterm")
, (mod1Mask, xK_p, spawn "exec=`dmenu_path | dmenu` && exec $exe")
, (controlMask, xK_space, spawn "gmrun")
, (mod1Mask, xK_Tab, switch)
, (mod1Mask .|. shiftMask, xK_q, io $ exitWith ExitSuccess)
]
--
-- | grabkeys. Register key commands
--
grabkeys :: W ()
grabkeys = do
dpy <- getDisplay
root <- io $ rootWindow dpy (defaultScreen dpy)
forM_ keys $ \(mod, sym, _) -> do
code <- io $ keysymToKeycode dpy sym
io $ grabKey dpy code mod root True grabModeAsync grabModeAsync
--
--
--
manage :: Window -> W ()
manage w = do
trace "manage"
d <- getDisplay
ws <- getWindows
when (Fold.notElem w ws) $ do
trace "modifying"
modifyWindows (w <|)
io $ mapWindow d w
refresh
--
-- refresh the windows
--
refresh :: W ()
refresh = do
v <- getWindows
case viewl v of
EmptyL -> return ()
(w :< _) -> do
d <- getDisplay
sw <- getScreenWidth
sh <- getScreenHeight
io $ do moveResizeWindow d w 0 0 (fromIntegral sw) (fromIntegral sh)
raiseWindow d w