Xmonad/Config archive/adamvo's xmonad.hs
From HaskellWiki
(Difference between revisions)
m (formatting) |
(updates) |
||
| Line 5: | Line 5: | ||
<haskell> | <haskell> | ||
| - | -- current darcs as of | + | -- current darcs as of 2010-10-16 |
| - | {-# OPTIONS_GHC -W -fno-warn-missing-signatures #-} | + | {-# LANGUAGE |
| - | + | DeriveDataTypeable, | |
| - | + | FlexibleContexts, | |
| - | + | FlexibleInstances, | |
| - | + | MultiParamTypeClasses, | |
| - | + | NoMonomorphismRestriction, | |
| + | PatternGuards, | ||
| + | ScopedTypeVariables, | ||
| + | TypeSynonymInstances, | ||
| + | UndecidableInstances | ||
| + | #-} | ||
| + | {-# OPTIONS_GHC -W -fwarn-unused-imports -fno-warn-missing-signatures #-} | ||
| + | import Control.Applicative | ||
| + | import Control.Exception as E | ||
| + | import Control.Monad | ||
import Control.Monad.Instances () | import Control.Monad.Instances () | ||
| - | + | import Control.Monad.Writer | |
| - | + | import Data.Function | |
| - | + | ||
| - | + | ||
| - | import Control.Monad | + | |
| - | import Data.Function | + | |
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
import Data.List | import Data.List | ||
| - | import | + | import Data.Maybe |
| - | + | import Data.Monoid | |
| + | import Data.Traversable(traverse) | ||
| + | import Debug.Trace | ||
| + | import Graphics.X11.Xinerama | ||
| + | import qualified Data.Map as M | ||
| + | import qualified Data.Set as S | ||
| + | import qualified Data.Set as Set | ||
| + | import qualified XMonad.Layout.Groups.Examples as G | ||
| + | import qualified XMonad.StackSet as W | ||
| + | import qualified XMonad.Util.ExtensibleState as XS | ||
| + | import System.Exit | ||
| + | import System.IO | ||
| + | import System.Process | ||
| + | import XMonad | ||
| + | import XMonad.Actions.DwmPromote | ||
| + | import XMonad.Actions.FloatSnap | ||
| + | import XMonad.Actions.GridSelect | ||
| + | import XMonad.Actions.Search | ||
| + | import XMonad.Actions.SpawnOn | ||
| + | import XMonad.Actions.Submap | ||
| + | import XMonad.Actions.TopicSpace | ||
| + | import XMonad.Actions.UpdatePointer | ||
| + | import XMonad.Actions.Warp | ||
| + | import XMonad.Hooks.DynamicLog | ||
| + | import XMonad.Hooks.EwmhDesktops | ||
| + | import XMonad.Hooks.ManageDocks | ||
import XMonad.Hooks.ManageHelpers | import XMonad.Hooks.ManageHelpers | ||
| + | import XMonad.Hooks.UrgencyHook | ||
| + | import XMonad.Layout.Accordion | ||
| + | import XMonad.Layout.BoringWindows | ||
| + | import XMonad.Layout.Drawer | ||
| + | import XMonad.Layout.Grid | ||
| + | import XMonad.Layout.IM | ||
| + | import XMonad.Layout.LayoutHints | ||
| + | import XMonad.Layout.LayoutModifier | ||
import XMonad.Layout.Magnifier | import XMonad.Layout.Magnifier | ||
| - | import | + | import XMonad.Layout.Master |
| - | + | import XMonad.Layout.Mosaic | |
| + | import XMonad.Layout.MosaicAlt | ||
| + | import XMonad.Layout.MouseResizableTile | ||
| + | import XMonad.Layout.Named | ||
| + | import XMonad.Layout.NoBorders | ||
| + | import XMonad.Layout.PerWorkspace | ||
| + | import XMonad.Layout.Simplest | ||
import XMonad.Layout.SimplestFloat | import XMonad.Layout.SimplestFloat | ||
| - | import XMonad.Layout. | + | import XMonad.Layout.SubLayouts |
| - | + | import XMonad.Layout.SubLayouts | |
| - | import XMonad.Layout. | + | import XMonad.Layout.Tabbed |
| - | + | import XMonad.Layout.TrackFloating | |
| - | import | + | import XMonad.Layout.WindowNavigation |
| - | import | + | import XMonad.Prompt |
| - | + | import XMonad.Prompt.RunOrRaise | |
| - | import | + | import XMonad.Prompt.Ssh |
| - | import | + | import XMonad.Prompt.Window |
| - | + | import XMonad.Prompt.XMonad | |
| + | import XMonad.Util.EZConfig | ||
import XMonad.Util.Paste | import XMonad.Util.Paste | ||
| - | import XMonad. | + | import XMonad.Util.Replace |
| + | import XMonad.Util.Run | ||
| - | |||
main :: IO () | main :: IO () | ||
| Line 98: | Line 92: | ||
replace | replace | ||
checkTopicConfig myTopics myTopicConfig | checkTopicConfig myTopics myTopicConfig | ||
| - | xmonad . ewmh . | + | let urgency |
| - | + | | True = withUrgencyHook FocusHook | |
| - | + | | True = withUrgencyHook NoUrgencyHook | |
| - | + | xmonad . ewmh . urgency . myConfig | |
| - | + | =<< mapM xmobarScreen =<< getScreens | |
| - | + | ||
| - | + | sofficeToolbox = className =? "OpenOffice.org 3.1" | |
| - | + | <&&> isInProperty "WM_PROTOCOLS" "WM_TAKE_FOCUS" | |
myConfig hs = let c = defaultConfig { | myConfig hs = let c = defaultConfig { | ||
layoutHook = myLayout | layoutHook = myLayout | ||
, focusFollowsMouse = False | , focusFollowsMouse = False | ||
| - | , focusedBorderColor = " | + | , focusedBorderColor = "red" |
, startupHook = do | , startupHook = do | ||
| - | return () | + | return () -- supposedly to avoid inf. loops with checkKeymap |
checkKeymap (myConfig []) (myKeys c) | checkKeymap (myConfig []) (myKeys c) | ||
, terminal = "urxvt" | , terminal = "urxvt" | ||
| Line 119: | Line 112: | ||
, logHook = do | , logHook = do | ||
multiPP' | multiPP' | ||
| - | (mergePPOutputs [pprWindowSet myTopicConfig,dynamicLogString . onlyTitle]) | + | (mergePPOutputs [XMonad.Actions.TopicSpace.pprWindowSet myTopicConfig, |
| + | dynamicLogString . onlyTitle]) | ||
myPP | myPP | ||
| - | myPP { ppTitle = const "" } | + | myPP{ ppTitle = const "" } |
hs | hs | ||
updatePointer (TowardsCentre 0.2 0.2) | updatePointer (TowardsCentre 0.2 0.2) | ||
| - | , handleEventHook = focusFollow | + | , handleEventHook = ewmhDesktopsEventHook <+> fullscreenEventHook <+> focusFollow <+> |
| + | (\e -> case e of | ||
| + | PropertyEvent{ ev_window = w } -> do | ||
| + | isURXVT <- runQuery (className =? "URxvt") w | ||
| + | if not isURXVT then hintsEventHook e else return (All True) | ||
| + | _ -> return (All True)) | ||
| + | -- <+> logEvents <+> clickNoFocus | ||
, workspaces = myTopics | , workspaces = myTopics | ||
| - | , manageHook = | + | , manageHook = mconcat |
[manageSpawn | [manageSpawn | ||
,isFullscreen --> doFullFloat | ,isFullscreen --> doFullFloat | ||
| + | -- ,className =? "MPlayer" --> doFullFloat | ||
| + | ,className =? "XTerm" --> queryMerge (className =? "XTerm") | ||
,manageDocks | ,manageDocks | ||
| - | |||
] | ] | ||
} in additionalKeysP c (myKeys c) | } in additionalKeysP c (myKeys c) | ||
| Line 137: | Line 138: | ||
myXPConfig = greenXPConfig { font = "xft:Profont:pixelsize=15:autohint=true" } | myXPConfig = greenXPConfig { font = "xft:Profont:pixelsize=15:autohint=true" } | ||
| - | gsConfig = defaultGSConfig { gs_navigate = neiu | + | gsConfig = defaultGSConfig { gs_navigate = neiu <+> |
| - | where neiu = M.insert (0,xK_space) (const (0,0)) $ M.map (\(x,y) (a,b) -> (x+a,y+b)) $ M.fromList | + | gs_navigate (defaultGSConfig`asTypeOf`gsConfig) } |
| - | + | where neiu = M.insert (0,xK_space) (const (0,0)) | |
| - | + | $ M.map (\(x,y) (a,b) -> (x+a,y+b)) | |
| - | + | $ M.fromList | |
| - | + | [((0,xK_n),(-1,0)) | |
| + | ,((0,xK_e),(0,1)) | ||
| + | ,((0,xK_i),(1,0)) | ||
| + | ,((0,xK_u),(0,-1))] | ||
| + | |||
| + | |||
| + | data ExpandEdges a = ExpandEdges Int deriving (Read,Show) | ||
| + | |||
| + | instance LayoutModifier ExpandEdges Window where | ||
| + | modifyLayout (ExpandEdges n) ws (Rectangle x y w h) = let | ||
| + | bigRect = Rectangle (x - fromIntegral n) (y - fromIntegral n) | ||
| + | (w + 2*fromIntegral n) (h + 2*fromIntegral n) | ||
| + | in | ||
| + | runLayout ws bigRect | ||
| + | |||
| + | -- | push edges off-screen | ||
| + | expandEdges n layout = ModifiedLayout (ExpandEdges n) layout | ||
| + | |||
| + | |||
-------------------- Layout ---------------------------------- | -------------------- Layout ---------------------------------- | ||
| - | myLayout = | + | myLayout = |
| - | . | + | trackFloating . smartBorders |
| - | . onWorkspace " | + | . onWorkspace "movie" (magnifier m ||| layoutHints Full) |
| - | . onWorkspace " | + | . avoidStruts |
| - | . onWorkspace "gimp" (named "G" | + | . onWorkspace "test" (multimastered 2 (1/100) (1/2) Grid) |
| - | . onWorkspace " | + | . onWorkspace "gimp" (named "G" gimp) |
| + | . onWorkspace "xm-conf" ((nav $ ModifiedLayout (ExpandEdges 1) (Tall 1 0.3 0.5)) ||| Full) | ||
$ m ||| named "F" (noBorders Full) | $ m ||| named "F" (noBorders Full) | ||
where nav = configurableNavigation (navigateColor "#ffff00") | where nav = configurableNavigation (navigateColor "#ffff00") | ||
| Line 161: | Line 181: | ||
. subLayout [] (Simplest ||| simplestFloat) | . subLayout [] (Simplest ||| simplestFloat) | ||
$ mosaic 1.5 [7,5,2] | $ mosaic 1.5 [7,5,2] | ||
| - | + | gimp = nav | |
| - | + | . onLeft (simpleDrawer 0.01 0.3 $ Role "gimp-toolbox") | |
. withIM 0.15 (Role "gimp-dock") | . withIM 0.15 (Role "gimp-dock") | ||
. addTabs shrinkText defaultTheme | . addTabs shrinkText defaultTheme | ||
| Line 193: | Line 213: | ||
,("M-S-b", restart "/home/aavogt/bin/obtoxmd" True) | ,("M-S-b", restart "/home/aavogt/bin/obtoxmd" True) | ||
,("M-S-d", restart "urxvt -e xmonad" False) | ,("M-S-d", restart "urxvt -e xmonad" False) | ||
| - | |||
| - | |||
,("M-S-o" , withFocused $ sendMessage . UnMerge ) | ,("M-S-o" , withFocused $ sendMessage . UnMerge ) | ||
| Line 205: | Line 223: | ||
,("M-x", submap $ M.fromList subMaps) | ,("M-x", submap $ M.fromList subMaps) | ||
,("M-g", submap $ defaultSublMap c ) | ,("M-g", submap $ defaultSublMap c ) | ||
| + | ,("M-z", submap $ M.fromList wmiiCmds) | ||
,("M-S-.", focusDown) | ,("M-S-.", focusDown) | ||
| Line 211: | Line 230: | ||
,("M-S-a", currentTopicAction myTopicConfig) | ,("M-S-a", currentTopicAction myTopicConfig) | ||
,("M-a", warpToCentre >> goToSelected gsConfig) | ,("M-a", warpToCentre >> goToSelected gsConfig) | ||
| - | -- | + | -- workaround |
,("M-<Tab>", switchNthLastFocused myTopicConfig . succ . length . W.visible . windowset =<< get ) | ,("M-<Tab>", switchNthLastFocused myTopicConfig . succ . length . W.visible . windowset =<< get ) | ||
| Line 221: | Line 240: | ||
,("M-S-<Return>", spawnShell) | ,("M-S-<Return>", spawnShell) | ||
-- don't force a recompile, if nothing has changed (xmonad --recompile runs XMonad.recompile True) | -- don't force a recompile, if nothing has changed (xmonad --recompile runs XMonad.recompile True) | ||
| - | ,("M-q", spawn | + | ,("M-q", spawn "ghc -e ':m +XMonad Control.Monad System.Exit' -e 'flip unless exitFailure =<< recompile False' && xmonad --restart") |
| - | + | ||
,("M-S-q", spawn "~/wip/x11-wm/xmonad/rebuild.sh") | ,("M-S-q", spawn "~/wip/x11-wm/xmonad/rebuild.sh") | ||
,("<Print>", spawn "scrot") | ,("<Print>", spawn "scrot") | ||
| Line 264: | Line 282: | ||
] | ] | ||
| - | mediaKeys = [("<XF86AudioPlay>", mpcAct "toggle"), | + | wmiiCmds = |
| + | [((0, xK_i), G.zoomGroupIn) | ||
| + | ,((0, xK_o), G.zoomGroupOut) | ||
| + | ,((0, xK_space), G.zoomGroupReset) | ||
| + | ,((0, xK_t), G.toggleGroupFull) | ||
| + | ,((0, xK_n), G.groupToNextLayout) | ||
| + | ,((0, xK_f), G.groupToFullLayout) | ||
| + | ,((0, xK_a), G.groupToTabbedLayout) | ||
| + | ,((0, xK_s), G.groupToVerticalLayout) | ||
| + | ,((0, xK_b), G.splitGroup) | ||
| + | ] | ||
| + | |||
| + | amarok = False | ||
| + | |||
| + | mediaKeys = [("<XF86AudioPlay>", do mpcAct "toggle"; when amarok $ spawn "amarok -t"), | ||
("<XF86AudioStop>", promptHost), | ("<XF86AudioStop>", promptHost), | ||
| - | ("<XF86AudioNext>", mpcAct "next"), | + | ("<XF86AudioNext>", do mpcAct "next"; when amarok $ spawn "amarok -f"), |
| - | ("<XF86AudioPrev>", mpcAct "prev"), | + | ("<XF86AudioPrev>", do mpcAct "prev"; when amarok $ spawn "amarok -r"), |
("<XF86AudioMute>", spawn "ossmix vmix0-outvol 0"), | ("<XF86AudioMute>", spawn "ossmix vmix0-outvol 0"), | ||
| - | |||
("<XF86AudioLowerVolume>", spawn "ossmix vmix0-outvol -- -1"), | ("<XF86AudioLowerVolume>", spawn "ossmix vmix0-outvol -- -1"), | ||
("S-<XF86AudioLowerVolume>", spawn "ossmix vmix0-outvol -- -0.1"), | ("S-<XF86AudioLowerVolume>", spawn "ossmix vmix0-outvol -- -0.1"), | ||
("<XF86AudioRaiseVolume>", spawn "ossmix vmix0-outvol +1"), | ("<XF86AudioRaiseVolume>", spawn "ossmix vmix0-outvol +1"), | ||
| - | ("S-<XF86AudioRaiseVolume>", spawn "ossmix vmix0-outvol +0.1") | + | ("S-<XF86AudioRaiseVolume>", spawn "ossmix vmix0-outvol +0.1") |
| - | ("<XF86Sleep>", spawn "sudo pm-suspend")] | + | -- ,("<XF86Sleep>", spawn "sudo pm-suspend") |
| + | ] | ||
where mpcAct c = do | where mpcAct c = do | ||
h <- XS.gets hostPrompt | h <- XS.gets hostPrompt | ||
| Line 282: | Line 314: | ||
newtype HostPrompt = HostPrompt { hostPrompt :: String } deriving (Read,Show,Typeable) | newtype HostPrompt = HostPrompt { hostPrompt :: String } deriving (Read,Show,Typeable) | ||
instance ExtensionClass HostPrompt where | instance ExtensionClass HostPrompt where | ||
| - | initialValue = HostPrompt " | + | initialValue = HostPrompt "127.0.0.1" |
extensionType = PersistentExtension | extensionType = PersistentExtension | ||
instance XPrompt HostPrompt where showXPrompt _ = "Pick MPD Host: " | instance XPrompt HostPrompt where showXPrompt _ = "Pick MPD Host: " | ||
promptHost = mkXPrompt (HostPrompt "") myXPConfig (return . compl) (XS.put . HostPrompt) | promptHost = mkXPrompt (HostPrompt "") myXPConfig (return . compl) (XS.put . HostPrompt) | ||
| - | where compl s = nub $ filter (s `isPrefixOf`) [" | + | where compl s = nub $ filter (s `isPrefixOf`) ["127.0.0.1","dell"] |
-------------------------------------------------------------- | -------------------------------------------------------------- | ||
| Line 315: | Line 347: | ||
when isFoc $ get >>= tell . Last . Just | when isFoc $ get >>= tell . Last . Just | ||
return out | return out | ||
| - | + | traverse put . getLast | |
=<< execWriterT . (io . zipWithM_ hPutStrLn handles <=< mapM pickPP) . catMaybes | =<< execWriterT . (io . zipWithM_ hPutStrLn handles <=< mapM pickPP) . catMaybes | ||
=<< mapM screenWorkspace (zipWith const [0..] handles) | =<< mapM screenWorkspace (zipWith const [0..] handles) | ||
| + | return () | ||
mergePPOutputs :: [PP -> X String] -> PP -> X String | mergePPOutputs :: [PP -> X String] -> PP -> X String | ||
| Line 335: | Line 368: | ||
myPP :: PP | myPP :: PP | ||
| - | myPP = sjanssenPP { ppLayout = xmobarColor "orange" "" } | + | myPP = sjanssenPP { ppLayout = xmobarColor "orange" "", ppUrgent = xmobarColor "red" "" . ('^':) } |
-------------------------------------------------------------- | -------------------------------------------------------------- | ||
| Line 341: | Line 374: | ||
myTopics :: [Topic] | myTopics :: [Topic] | ||
myTopics = | myTopics = | ||
| - | [ " | + | [ "a" |
, "web" | , "web" | ||
, "haskell" | , "haskell" | ||
| Line 353: | Line 386: | ||
, "music" | , "music" | ||
, "pdf" | , "pdf" | ||
| - | , " | + | , "xm-conf" |
| - | , " | + | , "xmc" |
| - | , " | + | , "xme" |
| - | , " | + | , "xm" |
, "xmobar" | , "xmobar" | ||
, "wip" | , "wip" | ||
| - | |||
| - | |||
| - | |||
, "test" | , "test" | ||
] | ] | ||
| Line 368: | Line 398: | ||
myTopicConfig = TopicConfig | myTopicConfig = TopicConfig | ||
{ topicDirs = M.fromList $ | { topicDirs = M.fromList $ | ||
| - | [ (" | + | [ ("a", "./") |
, ("haskell", "haskell") | , ("haskell", "haskell") | ||
| - | , (" | + | , ("xm-conf", ".xmonad") |
| - | , (" | + | , ("xme", "wip/x11-wm/xmonad/extras/xmonad-extras/XMonad") |
| - | , (" | + | , ("xm", "wip/x11-wm/xmonad/core/xmonad") |
| - | , (" | + | , ("xmc", "wip/x11-wm/xmonad/contrib/XMonadContrib/XMonad") |
, ("xmobar", "wip/x11-wm/xmobar") | , ("xmobar", "wip/x11-wm/xmobar") | ||
, ("movie", "media/movie") | , ("movie", "media/movie") | ||
| Line 384: | Line 414: | ||
] | ] | ||
, defaultTopicAction = const $ spawnShell >*> 2 | , defaultTopicAction = const $ spawnShell >*> 2 | ||
| - | , defaultTopic = " | + | , defaultTopic = "a" |
, maxTopicHistory = 10 | , maxTopicHistory = 10 | ||
, topicActions = M.fromList $ | , topicActions = M.fromList $ | ||
| - | [ (" | + | [ ("xm-conf", spawnShellIn ".xmonad/lib/XMonad/Layout" >> |
spawn "urxvt -e vim ~/.xmonad/xmonad.hs") | spawn "urxvt -e vim ~/.xmonad/xmonad.hs") | ||
| - | + | , ("xmc" , spawnShell >*> 2) | |
| - | + | , ("xmobar" , spawnShellIn "wip/x11-wm/xmobar/Plugins" >*> 2) | |
| - | + | , ("music" , spawn "urxvt -e ncmpc" >> spawn "export MPD_HOST=192.168.1.2; mpc && urxvt -e ncmpc -h 192.168.1.2") | |
| - | + | , ("mail" , spawnOn "mail" "urxvt -e mutt") | |
| - | + | , ("irc" , spawnOn "irc" "urxvt --title irc -e ssh engage") | |
| - | + | , ("web" , spawnOn "web" "firefox") | |
| - | + | , ("pdf" , spawnOn "pdf" "okular") | |
| - | + | , ("gimp" , spawnHere "gimp") | |
] | ] | ||
} | } | ||
| Line 430: | Line 460: | ||
toggleFF = XS.modify $ FocusFollow . not . getFocusFollow | toggleFF = XS.modify $ FocusFollow . not . getFocusFollow | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
| + | |||
| + | {- | Sometimes this picks the wrong element to merge into (that is, not the | ||
| + | 'focused' element of the group), and SubLayouts breaks up the whole group | ||
| + | -} | ||
| + | queryMerge pGrp = do | ||
| + | w <- ask | ||
| + | aws <- liftX $ filterM (runQuery pGrp) =<< gets | ||
| + | (W.integrate' . W.stack . W.workspace . W.current . windowset) | ||
| + | |||
| + | let addRem = False -- run the query with window removed?? | ||
| + | when addRem | ||
| + | (liftX $ modify (\ws -> ws { windowset = W.insertUp w (windowset ws) })) | ||
| + | liftX $ windows (W.insertUp w) | ||
| + | |||
| + | mapM_ (liftX . sendMessage . XMonad.Layout.SubLayouts.Merge w) aws | ||
| + | |||
| + | when addRem | ||
| + | (liftX $ modify (\ws -> ws { windowset = W.delete' w (windowset ws) })) | ||
| + | |||
| + | idHook | ||
| + | |||
| + | logEvents ev = do | ||
| + | io $ appendFile "/tmp/xmevents" ('\n' : show ev) | ||
| + | return (All True) | ||
| + | |||
| + | |||
| + | clickNoFocus ButtonEvent{} = do | ||
| + | return (All False) | ||
| + | clickNoFocus _ = mempty | ||
</haskell> | </haskell> | ||
Revision as of 00:57, 17 October 2010
Xmonad/Config archive/adamvo's xmobarrc (0.9.2)
Xmonad/Config archive/obtoxmd -- script called to temporarily run another wm
-- current darcs as of 2010-10-16 {-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, NoMonomorphismRestriction, PatternGuards, ScopedTypeVariables, TypeSynonymInstances, UndecidableInstances #-} {-# OPTIONS_GHC -W -fwarn-unused-imports -fno-warn-missing-signatures #-} import Control.Applicative import Control.Exception as E import Control.Monad import Control.Monad.Instances () import Control.Monad.Writer import Data.Function import Data.List import Data.Maybe import Data.Monoid import Data.Traversable(traverse) import Debug.Trace import Graphics.X11.Xinerama import qualified Data.Map as M import qualified Data.Set as S import qualified Data.Set as Set import qualified XMonad.Layout.Groups.Examples as G import qualified XMonad.StackSet as W import qualified XMonad.Util.ExtensibleState as XS import System.Exit import System.IO import System.Process import XMonad import XMonad.Actions.DwmPromote import XMonad.Actions.FloatSnap import XMonad.Actions.GridSelect import XMonad.Actions.Search import XMonad.Actions.SpawnOn import XMonad.Actions.Submap import XMonad.Actions.TopicSpace import XMonad.Actions.UpdatePointer import XMonad.Actions.Warp import XMonad.Hooks.DynamicLog import XMonad.Hooks.EwmhDesktops import XMonad.Hooks.ManageDocks import XMonad.Hooks.ManageHelpers import XMonad.Hooks.UrgencyHook import XMonad.Layout.Accordion import XMonad.Layout.BoringWindows import XMonad.Layout.Drawer import XMonad.Layout.Grid import XMonad.Layout.IM import XMonad.Layout.LayoutHints import XMonad.Layout.LayoutModifier import XMonad.Layout.Magnifier import XMonad.Layout.Master import XMonad.Layout.Mosaic import XMonad.Layout.MosaicAlt import XMonad.Layout.MouseResizableTile import XMonad.Layout.Named import XMonad.Layout.NoBorders import XMonad.Layout.PerWorkspace import XMonad.Layout.Simplest import XMonad.Layout.SimplestFloat import XMonad.Layout.SubLayouts import XMonad.Layout.SubLayouts import XMonad.Layout.Tabbed import XMonad.Layout.TrackFloating import XMonad.Layout.WindowNavigation import XMonad.Prompt import XMonad.Prompt.RunOrRaise import XMonad.Prompt.Ssh import XMonad.Prompt.Window import XMonad.Prompt.XMonad import XMonad.Util.EZConfig import XMonad.Util.Paste import XMonad.Util.Replace import XMonad.Util.Run main :: IO () main = do replace checkTopicConfig myTopics myTopicConfig let urgency | True = withUrgencyHook FocusHook | True = withUrgencyHook NoUrgencyHook xmonad . ewmh . urgency . myConfig =<< mapM xmobarScreen =<< getScreens sofficeToolbox = className =? "OpenOffice.org 3.1" <&&> isInProperty "WM_PROTOCOLS" "WM_TAKE_FOCUS" myConfig hs = let c = defaultConfig { layoutHook = myLayout , focusFollowsMouse = False , focusedBorderColor = "red" , startupHook = do return () -- supposedly to avoid inf. loops with checkKeymap checkKeymap (myConfig []) (myKeys c) , terminal = "urxvt" , modMask = mod4Mask , logHook = do multiPP' (mergePPOutputs [XMonad.Actions.TopicSpace.pprWindowSet myTopicConfig, dynamicLogString . onlyTitle]) myPP myPP{ ppTitle = const "" } hs updatePointer (TowardsCentre 0.2 0.2) , handleEventHook = ewmhDesktopsEventHook <+> fullscreenEventHook <+> focusFollow <+> (\e -> case e of PropertyEvent{ ev_window = w } -> do isURXVT <- runQuery (className =? "URxvt") w if not isURXVT then hintsEventHook e else return (All True) _ -> return (All True)) -- <+> logEvents <+> clickNoFocus , workspaces = myTopics , manageHook = mconcat [manageSpawn ,isFullscreen --> doFullFloat -- ,className =? "MPlayer" --> doFullFloat ,className =? "XTerm" --> queryMerge (className =? "XTerm") ,manageDocks ] } in additionalKeysP c (myKeys c) myXPConfig :: XPConfig myXPConfig = greenXPConfig { font = "xft:Profont:pixelsize=15:autohint=true" } gsConfig = defaultGSConfig { gs_navigate = neiu <+> gs_navigate (defaultGSConfig`asTypeOf`gsConfig) } where neiu = M.insert (0,xK_space) (const (0,0)) $ M.map (\(x,y) (a,b) -> (x+a,y+b)) $ M.fromList [((0,xK_n),(-1,0)) ,((0,xK_e),(0,1)) ,((0,xK_i),(1,0)) ,((0,xK_u),(0,-1))] data ExpandEdges a = ExpandEdges Int deriving (Read,Show) instance LayoutModifier ExpandEdges Window where modifyLayout (ExpandEdges n) ws (Rectangle x y w h) = let bigRect = Rectangle (x - fromIntegral n) (y - fromIntegral n) (w + 2*fromIntegral n) (h + 2*fromIntegral n) in runLayout ws bigRect -- | push edges off-screen expandEdges n layout = ModifiedLayout (ExpandEdges n) layout -------------------- Layout ---------------------------------- myLayout = trackFloating . smartBorders . onWorkspace "movie" (magnifier m ||| layoutHints Full) . avoidStruts . onWorkspace "test" (multimastered 2 (1/100) (1/2) Grid) . onWorkspace "gimp" (named "G" gimp) . onWorkspace "xm-conf" ((nav $ ModifiedLayout (ExpandEdges 1) (Tall 1 0.3 0.5)) ||| Full) $ m ||| named "F" (noBorders Full) where nav = configurableNavigation (navigateColor "#ffff00") m = named "M" . lessBorders Screen . layoutHintsToCenter . addTabs shrinkText defaultTheme . nav . boringAuto . subLayout [] (Simplest ||| simplestFloat) $ mosaic 1.5 [7,5,2] gimp = nav . onLeft (simpleDrawer 0.01 0.3 $ Role "gimp-toolbox") . withIM 0.15 (Role "gimp-dock") . addTabs shrinkText defaultTheme . nav . boringAuto . subLayout [] Simplest $ mouseResizableTile ||| Full -------------------------------------------------------------- -------------------- Keys ------------------------------------ myKeys c = [("M-<Left>" , withFocused $ snapMove L Nothing ) ,("M-<Right>" , withFocused $ snapMove R Nothing ) ,("M-<Up>" , withFocused $ snapMove U Nothing ) ,("M-<Down>" , withFocused $ snapMove D Nothing ) ,("M-S-<Left>" , withFocused $ snapShrink R Nothing) ,("M-S-<Right>", withFocused $ snapGrow R Nothing) ,("M-S-<Up>" , withFocused $ snapShrink D Nothing) ,("M-S-<Down>" , withFocused $ snapGrow D Nothing) , ("M-l", withFocused (sendMessage . expandWindowAlt) >> sendMessage Expand) , ("M-h", withFocused (sendMessage . shrinkWindowAlt) >> sendMessage Shrink) ,("M-;", withFocused (sendMessage . tallWindowAlt) >> sendMessage Taller) ,("M-o", withFocused (sendMessage . wideWindowAlt) >> sendMessage Wider ) ,("M-v", toggleFF) -- ,("M-d", layoutScreens 2 $ TwoPane 0.5 0.5) -- ,("S-M-d", rescreen) ,("M-S-b", restart "/home/aavogt/bin/obtoxmd" True) ,("M-S-d", restart "urxvt -e xmonad" False) ,("M-S-o" , withFocused $ sendMessage . UnMerge ) ,("M-S-C-o", withFocused $ sendMessage . UnMergeAll) ,("M-C-m" , withFocused $ sendMessage . MergeAll ) ,("M-C-." , onGroup W.focusDown') ,("M-C-," , onGroup W.focusUp' ) ,("M-p", shellPromptHere myXPConfig) ,("M-x", submap $ M.fromList subMaps) ,("M-g", submap $ defaultSublMap c ) ,("M-z", submap $ M.fromList wmiiCmds) ,("M-S-.", focusDown) ,("M-S-,", focusUp ) ,("M-S-a", currentTopicAction myTopicConfig) ,("M-a", warpToCentre >> goToSelected gsConfig) -- workaround ,("M-<Tab>", switchNthLastFocused myTopicConfig . succ . length . W.visible . windowset =<< get ) ,("M-s" , warpToCentre >> promptedGoto ) ,("M-S-s", warpToCentre >> promptedShift) ,("M-b", sendMessage ToggleStruts) ,("M-<Return>", dwmpromote) ,("M-S-<Return>", spawnShell) -- don't force a recompile, if nothing has changed (xmonad --recompile runs XMonad.recompile True) ,("M-q", spawn "ghc -e ':m +XMonad Control.Monad System.Exit' -e 'flip unless exitFailure =<< recompile False' && xmonad --restart") ,("M-S-q", spawn "~/wip/x11-wm/xmonad/rebuild.sh") ,("<Print>", spawn "scrot") ] ++ concatMap (\(m,f) -> lrud ("M-"++m) f) [("S-" , sendMessage . Swap) ,("C-" , sendMessage . pullGroup) ,("S-C-", sendMessage . pushWindow) ,("" , sendMessage . Go)] ++ mediaKeys ++ [("M-"++m++[key], screenWorkspace sc >>= flip whenJust (windows . f)) | (f, m) <- [(W.view, ""), (W.shift, "S-")] , (key, sc) <- zip "wf" [0 .. ]] ++ [ ("M-"++m++[k], a i) | (a, m) <- [(switchNthLastFocused myTopicConfig,""),(shiftNthLastFocused, "S-")] , (i, k) <- zip [1..] "123456789"] -- helper for windowNavigation keys -- note: with colemak neiu are placed where jkli are with qwerty layout lrud :: String -> (Direction2D -> b) -> [(String, b)] lrud m cmd = zip ks cmds where ks = map (\x -> m ++ [x]) "niue" cmds = zipWith ($) (repeat cmd) [L,R,U,D] subMaps = [((0, xK_o), runOrRaisePrompt myXPConfig), ((0, xK_p), shellPromptHere myXPConfig), ((0, xK_x), xmonadPrompt myXPConfig), ((0, xK_z), sshPrompt myXPConfig), ((shiftMask, xK_w), windowPromptGoto myXPConfig), ((0, xK_w), promptSearch myXPConfig wikipedia), ((0, xK_s), promptSearch myXPConfig multi), ((0, xK_m), promptSearch myXPConfig mathworld), ((0, xK_b), sendMessage ToggleStruts), ((0, xK_f), withFocused $ windows . W.sink), ((0, xK_v), refresh), ((0, xK_c), asks config >>= spawnHere . terminal), ((0, xK_k), kill) ] wmiiCmds = [((0, xK_i), G.zoomGroupIn) ,((0, xK_o), G.zoomGroupOut) ,((0, xK_space), G.zoomGroupReset) ,((0, xK_t), G.toggleGroupFull) ,((0, xK_n), G.groupToNextLayout) ,((0, xK_f), G.groupToFullLayout) ,((0, xK_a), G.groupToTabbedLayout) ,((0, xK_s), G.groupToVerticalLayout) ,((0, xK_b), G.splitGroup) ] amarok = False mediaKeys = [("<XF86AudioPlay>", do mpcAct "toggle"; when amarok $ spawn "amarok -t"), ("<XF86AudioStop>", promptHost), ("<XF86AudioNext>", do mpcAct "next"; when amarok $ spawn "amarok -f"), ("<XF86AudioPrev>", do mpcAct "prev"; when amarok $ spawn "amarok -r"), ("<XF86AudioMute>", spawn "ossmix vmix0-outvol 0"), ("<XF86AudioLowerVolume>", spawn "ossmix vmix0-outvol -- -1"), ("S-<XF86AudioLowerVolume>", spawn "ossmix vmix0-outvol -- -0.1"), ("<XF86AudioRaiseVolume>", spawn "ossmix vmix0-outvol +1"), ("S-<XF86AudioRaiseVolume>", spawn "ossmix vmix0-outvol +0.1") -- ,("<XF86Sleep>", spawn "sudo pm-suspend") ] where mpcAct c = do h <- XS.gets hostPrompt spawn $ unwords ["export MPD_HOST="++h,";","mpc",c] -- Prompt for mpd host newtype HostPrompt = HostPrompt { hostPrompt :: String } deriving (Read,Show,Typeable) instance ExtensionClass HostPrompt where initialValue = HostPrompt "127.0.0.1" extensionType = PersistentExtension instance XPrompt HostPrompt where showXPrompt _ = "Pick MPD Host: " promptHost = mkXPrompt (HostPrompt "") myXPConfig (return . compl) (XS.put . HostPrompt) where compl s = nub $ filter (s `isPrefixOf`) ["127.0.0.1","dell"] -------------------------------------------------------------- warpToCentre = gets (W.screen . W.current . windowset) >>= \x -> warpToScreen x 0.5 0.5 -------------------- Support for per-screen xmobars --------- -- Some parts of this should be merged into contrib sometime getScreens :: IO [Int] getScreens = openDisplay "" >>= liftA2 (<*) f closeDisplay where f = fmap (zipWith const [0..]) . getScreenInfo multiPP :: PP -- ^ The PP to use if the screen is focused -> PP -- ^ The PP to use otherwise -> [Handle] -- ^ Handles for the status bars, in order of increasing X -- screen number -> X () multiPP = multiPP' dynamicLogString multiPP' :: (PP -> X String) -> PP -> PP -> [Handle] -> X () multiPP' dynlStr focusPP unfocusPP handles = do state <- get let pickPP :: WorkspaceId -> WriterT (Last XState) X String pickPP ws = do let isFoc = (ws ==) . W.tag . W.workspace . W.current $ windowset state put state{ windowset = W.view ws $ windowset state } out <- lift $ dynlStr $ if isFoc then focusPP else unfocusPP when isFoc $ get >>= tell . Last . Just return out traverse put . getLast =<< execWriterT . (io . zipWithM_ hPutStrLn handles <=< mapM pickPP) . catMaybes =<< mapM screenWorkspace (zipWith const [0..] handles) return () mergePPOutputs :: [PP -> X String] -> PP -> X String mergePPOutputs x pp = fmap (intercalate (ppSep pp)) . sequence . sequence x $ pp onlyTitle :: PP -> PP onlyTitle pp = defaultPP { ppCurrent = const "" , ppHidden = const "" , ppVisible = const "" , ppLayout = ppLayout pp , ppTitle = ppTitle pp } -- | Requires a recent addition to xmobar (>0.9.2), otherwise you have to use -- multiple configuration files, which gets messy xmobarScreen :: Int -> IO Handle xmobarScreen = spawnPipe . ("~/.cabal/bin/xmobar -x " ++) . show myPP :: PP myPP = sjanssenPP { ppLayout = xmobarColor "orange" "", ppUrgent = xmobarColor "red" "" . ('^':) } -------------------------------------------------------------- -------------------- X.Actions.TopicSpace -------------------- myTopics :: [Topic] myTopics = [ "a" , "web" , "haskell" , "irc" , "admin" , "documents" , "gimp" , "gitit" , "mail" , "movie" , "music" , "pdf" , "xm-conf" , "xmc" , "xme" , "xm" , "xmobar" , "wip" , "test" ] myTopicConfig = TopicConfig { topicDirs = M.fromList $ [ ("a", "./") , ("haskell", "haskell") , ("xm-conf", ".xmonad") , ("xme", "wip/x11-wm/xmonad/extras/xmonad-extras/XMonad") , ("xm", "wip/x11-wm/xmonad/core/xmonad") , ("xmc", "wip/x11-wm/xmonad/contrib/XMonadContrib/XMonad") , ("xmobar", "wip/x11-wm/xmobar") , ("movie", "media/movie") , ("music", "media/music") , ("documents", "doc") , ("pdf", "ref") , ("gitit", "wip/gitit") , ("gimp", "./") , ("wip", "wip") ] , defaultTopicAction = const $ spawnShell >*> 2 , defaultTopic = "a" , maxTopicHistory = 10 , topicActions = M.fromList $ [ ("xm-conf", spawnShellIn ".xmonad/lib/XMonad/Layout" >> spawn "urxvt -e vim ~/.xmonad/xmonad.hs") , ("xmc" , spawnShell >*> 2) , ("xmobar" , spawnShellIn "wip/x11-wm/xmobar/Plugins" >*> 2) , ("music" , spawn "urxvt -e ncmpc" >> spawn "export MPD_HOST=192.168.1.2; mpc && urxvt -e ncmpc -h 192.168.1.2") , ("mail" , spawnOn "mail" "urxvt -e mutt") , ("irc" , spawnOn "irc" "urxvt --title irc -e ssh engage") , ("web" , spawnOn "web" "firefox") , ("pdf" , spawnOn "pdf" "okular") , ("gimp" , spawnHere "gimp") ] } -- From the sample config in TopicSpace, these should probably be exported from that module spawnShell = currentTopicDir myTopicConfig >>= spawnShellIn spawnShellIn dir = do -- color <- randomBg' (HSV 255 255) t <- asks (terminal . config) spawnHere $ "cd " ++ dir ++ " && " ++ t -- ++ " -bg " ++ color wsgrid = gridselect gsConfig <=< asks $ map (\x -> (x,x)) . workspaces . config promptedGoto = wsgrid >>= flip whenJust (switchTopic myTopicConfig) promptedShift = wsgrid >>= \x -> whenJust x $ \y -> windows (W.greedyView y . W.shift y) -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- A nice little example of extensiblestate newtype FocusFollow = FocusFollow {getFocusFollow :: Bool } deriving (Typeable,Read,Show) instance ExtensionClass FocusFollow where initialValue = FocusFollow True extensionType = PersistentExtension -- this eventHook is the same as from xmonad for handling crossing events focusFollow e@(CrossingEvent {ev_window=w, ev_event_type=t}) | t == enterNotify, ev_mode e == notifyNormal = whenX (XS.gets getFocusFollow) (focus w) >> return (All True) focusFollow _ = return (All True) toggleFF = XS.modify $ FocusFollow . not . getFocusFollow -------------------------------------------------------------------------------- {- | Sometimes this picks the wrong element to merge into (that is, not the 'focused' element of the group), and SubLayouts breaks up the whole group -} queryMerge pGrp = do w <- ask aws <- liftX $ filterM (runQuery pGrp) =<< gets (W.integrate' . W.stack . W.workspace . W.current . windowset) let addRem = False -- run the query with window removed?? when addRem (liftX $ modify (\ws -> ws { windowset = W.insertUp w (windowset ws) })) liftX $ windows (W.insertUp w) mapM_ (liftX . sendMessage . XMonad.Layout.SubLayouts.Merge w) aws when addRem (liftX $ modify (\ws -> ws { windowset = W.delete' w (windowset ws) })) idHook logEvents ev = do io $ appendFile "/tmp/xmevents" ('\n' : show ev) return (All True) clickNoFocus ButtonEvent{} = do return (All False) clickNoFocus _ = mempty
