{-# LANGUAGE DeriveDataTypeable, TypeSynonymInstances, MultiParamTypeClasses #-} -------------------------------------------------------------------------- {{{ -- | -- Module : xmonad -- Copyright : (c) Mads N Noe 2010 -- Maintainer : mail (@) madsnoe.dk -- License : as-is -- -- Modular xmonad config. -- -- Highlights: -- * pager with icons for DynamicLog -- * per application configuration -- * minimize windows -- -- Requires xmonad 0.9. Note that this work is not finished. -- There are still lot of things I want to behave differently, -- and I need to do some cleanup here and there. -- -- Still, I hope you can get inspired by some of my ideas. Enjoy :-) -- -------------------------------------------------------------------------- }}} -- IMPORTS {{{ -- Haskell modules import Control.Monad (when, liftM) import Data.IORef (IORef) import Data.List import Data.Maybe (isJust) import qualified Data.Map as M import System.IO (Handle) -- XMonad modules import XMonad hiding ( (|||) ) import XMonad.Actions.CycleSelectedLayouts import XMonad.Actions.CycleWS import XMonad.Actions.FloatKeys import XMonad.Actions.FloatSnap import XMonad.Actions.SwapWorkspaces (swapWithCurrent) import XMonad.Hooks.DynamicLog import XMonad.Hooks.EwmhDesktops import XMonad.Hooks.ManageDocks import XMonad.Hooks.ManageHelpers (doCenterFloat) import XMonad.Hooks.Place import XMonad.Hooks.RestoreMinimized import XMonad.Hooks.ServerMode import XMonad.Hooks.UrgencyHook import XMonad.Layout.LayoutCombinators import XMonad.Layout.Named import XMonad.Layout.NoBorders import XMonad.Layout.Reflect import XMonad.Layout.ResizableTile import qualified XMonad.StackSet as W import XMonad.Util.Run (hPutStrLn) import XMonad.Util.WorkspaceCompare (getSortByTag) -- Custom modules import App import BorderColors import Commands import DMenu import Panel import Config import IM import Layout import MyApps import Pager import Utils import Workspace -- }}} -- MAIN {{{ main :: IO () main = do host <- getHost pipes <- spawnPanels xmonad $ withUrgencyHook NoUrgencyHook $ ewmh $ myXConfig host pipes -- }}} -- SETTINGS {{{ -- | Layout to show initially, and when issuing the according keybinding. My -- desktop is widescreen, but not my laptop. defaultLayout Laptop = "Tall" defaultLayout Netbook = "Wide" cycledLayouts Laptop = ["Mirror", defaultLayout Laptop] cycledLayouts Netbook = ["Accordion", "Tall", defaultLayout Netbook] myWorkspaces = map show [1..8] ++ [hiddenWorkspaceTag, summonWorkspaceTag] -- Colors myNormalBorderColor = defaultBG myFocusedBorderColor = "#3939ff" masterBorderColor = "#ff1010" floatBorderColor = "#10c010" myPlacement = withGaps (22, 0, 0, 0) $ smart (0.5,0.5) myXConfig host pipes = XConfig { terminal = "xterm" -- unused , focusFollowsMouse = True , borderWidth = 3 , modMask = mod5Mask -- unused , numlockMask = mod2Mask , workspaces = myWorkspaces , normalBorderColor = myNormalBorderColor , focusedBorderColor = myFocusedBorderColor , keys = myKeys host , mouseBindings = myMouseBindings , handleEventHook = myHandleEventHook , layoutHook = myLayoutHook , manageHook = myManageHook host , logHook = myLogHook host pipes , startupHook = myStartupHook host } -- }}} -- KEYS/MOUSE {{{ -- | The keybindings are optimized for the Colemak () -- keyboard layout. The keys are placed in the right side of the keyboard, -- using right alt as the modifier. myKeys host _ = M.fromList $ makeKeys apps ++ [ ((i , xK_comma), runCommand) , ((i , xK_slash), dmenuRun) , ((u , xK_h), hideSummonWindows apps) -- See https://addons.mozilla.org/en-US/firefox/addon/61262. , ((is, xK_f), spawn "firefox -unfocus") -- Enhance clipboard functionality in xterm (otherwise, xterm easily -- "forgets" the selection). Also, xclip will remember the selection -- even if the host app exits. , ((i , xK_z), spawn "xclip -selection primary -o | xclip -selection clipboard -i") -- FLOATING WINDOWS , ((u , xK_p), placeFocused $ myPlacement) , ((u , xK_b), withFocused $ windows . W.sink) -- WINDOW HANDLING , ((i , xK_j), windows W.focusDown >> warpToWindow') , ((i , xK_k), windows W.focusUp >> warpToWindow') , ((is, xK_j), windows W.swapMaster) , ((i , xK_h), swapOrRaise) , ((is, xK_h), swapOrLower) , ((i , xK_s), windows $ hideFocused) , ((i , xK_r), windows $ restoreLast) , ((is, xK_n), kill) , ((mod1Mask, xK_F4), kill) -- LAYOUT MESSAGES , ((i , xK_space), cycleThroughLayouts $ cycledLayouts host) , ((is, xK_space), sendMessage $ JumpToLayout $ defaultLayout host) , ((u , xK_n), sendMessage $ JumpToLayout "NoBorders") , ((u , xK_u), sendMessage $ ToggleStruts) , ((im, xK_Right), sendMessage Shrink) , ((im, xK_Left), sendMessage Expand) , ((im, xK_Down), sendMessage MirrorShrink) , ((im, xK_Up), sendMessage MirrorExpand) , ((i , xK_Left), withFocused $ keysMoveWindow (-300, 0)) , ((i , xK_Right), withFocused $ keysMoveWindow ( 300, 0)) , ((i , xK_Up), withFocused $ keysMoveWindow ( 0, -200)) , ((i , xK_Down), withFocused $ keysMoveWindow ( 0, 200)) , ((is, xK_Left), withFocused $ snapMove L Nothing) , ((is, xK_Right), withFocused $ snapMove R Nothing) , ((is, xK_Up), withFocused $ snapMove U Nothing) , ((is, xK_Down), withFocused $ snapMove D Nothing) -- SESSION , ((i , xK_Delete), spawn "gnome-session-save --shutdown-dialog") , ((is, xK_BackSpace), spawn "gnome-session-save --logout") , ((i , xK_BackSpace), killPanels >> restart "xmonad" True) -- WORKSPACES -- Note that I have swapped Y and J in my modified Colemak keyboard layout. , ((i , xK_y), doWithWS W.greedyView Prev EmptyWS) , ((is, xK_y), doWithWS shiftView Prev EmptyWS) , ((im, xK_y), doWithWS swapWithCurrent Prev EmptyWS) , ((i , xK_u), doWithWS W.greedyView Prev NonEmptyWS) , ((is, xK_u), doWithWS shiftView Prev NonEmptyWS) , ((im, xK_u), doWithWS swapWithCurrent Prev NonEmptyWS) , ((i , xK_i), doWithWS W.greedyView Next NonEmptyWS) , ((is, xK_i), doWithWS shiftView Next NonEmptyWS) , ((im, xK_I), doWithWS swapWithCurrent Next NonEmptyWS) , ((i , xK_o), doWithWS W.greedyView Next EmptyWS) , ((is, xK_o), doWithWS shiftView Next EmptyWS) , ((im, xK_o), doWithWS swapWithCurrent Next EmptyWS) , ((i , xK_l), doWithWS shiftView Next EmptyWS) , ((is, xK_l), doWithWS W.shift Next EmptyWS) , ((i , xK_7), swapNextScreen') , ((i , xK_8), toggleWS) , ((i , xK_9), screenWorkspace 0 >>= flip whenJust (windows . W.view) >> warpToWindow') , ((is, xK_9), screenWorkspace 0 >>= flip whenJust (windows . shiftViewUngreedy) >> warpToWindow') , ((i , xK_0), screenWorkspace 1 >>= flip whenJust (windows . W.view) >> warpToWindow') , ((is, xK_0), screenWorkspace 1 >>= flip whenJust (windows . shiftViewUngreedy) >> warpToWindow') ] -- MOUSE myMouseBindings _ = M.fromList $ [ ((mod5Mask, button1), focusAnd mouseMoveWindow $ snapMagicMove (Just 50) (Just 50)) , ((mod5Mask .|. shiftMask, button1), focusAnd mouseMoveWindow $ snapMagicResize [L,R,U,D] (Just 50) (Just 50)) , ((mod5Mask, button3), focusAnd mouseResizeWindow $ snapMagicResize [R,D] (Just 50) (Just 50)) ] where -- | Focus and raise the window before performing a mouse operation. focusAnd job1 job2 w = focus w >> windows W.swapMaster >> job1 w >> job2 w -- }}} -- LAYOUTHOOK {{{ myLayoutHook = avoidStruts $ smartBorders $ withIM (1/5) (Role "gimp-toolbox") ( (named "Wide" $ Mirror $ ResizableTall 1 (3/40) (2/3) []) ||| (named "Tall" $ reflectHoriz $ ResizableTall 1 (3/40) (4/7) []) ||| (named "Mirror" $ ResizableTall 1 (3/40) (4/7) []) ||| (twoAccordion) ||| (named "NoBorders" $ noBorders Full) ) -- }}} -- MANAGEHOOK {{{ myManageHook xs = composeAll [ floats --> doCenterFloat , className =? "MPlayer" --> doFloat , ignores --> doIgnore , appManageHook apps , manageDocks ] where floats = foldr1 (<||>) [ checkDialog , title =? "." <&&> ( className =? "" <||> appName =? "." ) , title =? "VLC media player" , className =? "Nautilus" <&&> fmap (not . isSuffixOf " - File Browser") title , className =? "Firefox" <&&> fmap (/="Navigator") appName , flip fmap className $ flip elem [ "Gnome_swallow" , "Gdmsetup" , "Xmessage" , "Zenity" ] ] ignores = foldr1 (<||>) [ className =? "Gnome-typing-monitor" ] -- }}} -- HANDLEEVENTHOOK {{{ myHandleEventHook = do restoreMinimizedEventHook serverModeEventHook' smCommands -- }}} -- STARTUP HOOK {{{ myStartupHook :: Host -> X () myStartupHook host = do broadcastMessage $ JumpToLayout $ defaultLayout $ host refresh -- }}} -- LOGHOOK {{{ myLogHook :: Host -> [Handle] -> X () myLogHook host pipes = do -- I found it least confusing when coloring the master window only. This -- makes it easy to tell which window has focus, without moving your eyes -- to the border of the screen, as the coloring is based on the window -- position. colorWhen isMaster masterBorderColor -- Make it easy to distinguish between floating and non-floating windows. -- Sometimes I accidently makes a window floating without moving it out of -- its position. colorWhen isFloat floatBorderColor mapM_ (\pipe -> dynamicLogString (myPP host) >>= io . hPutStrLn pipe) pipes -- TODO: refactor myPP host = defaultPP { ppCurrent = highlight , ppVisible = pad 2 -- ppHidden overwrites colors of ppUrgent , ppHidden = pad 6 , ppHiddenNoWindows = pad 2 , ppUrgent = pad 6 . ((dzenColor "#01ce02" "#fcfb03") (adjust " ! ")++) -- temporary solution , ppTitle = pad 2 , ppLayout = ifNonDefault host (highlight . adjust) , ppWsSep = "" , ppSep = " " , ppSort = getSortByTag , ppOrder = order , ppExtras = [ labeledPager $ myPP host ] } where -- Ignore the original workspace list and use labeledPager instead. order (_:l:t:ws:[]) = (" " ++ ws):l:adjust t:[] order xs = ["Error in order list: " ++ show xs] -- Hide the layout label when default layout is used. ifNonDefault host f s | s == defaultLayout host = "" | otherwise = f s highlight x = leftIcon ++ dzenColor hilightFG hilightBG x ++ rightIcon -- Called every time a text string is shown, making the font appear vertically -- aligned with the icons. adjust x = "^p(;+2)" ++ x ++ "^p()" pad w x = concat ["^p(", show w, ")", x, "^p(", show w, ")"] -- }}} -- vim: set ft=haskell fdm=marker fdl=1 fdc=4: