-- Base import XMonad import System.Exit (exitSuccess) import System.IO (hPutStrLn, Handle) import qualified XMonad.StackSet as W -- Actions import XMonad.Actions.CopyWindow (kill1) import XMonad.Actions.CycleWS (Direction1D(..), moveTo, shiftTo, WSType(..), nextScreen, prevScreen) import XMonad.Actions.MouseResize import XMonad.Actions.Promote import XMonad.Actions.RotSlaves (rotSlavesDown, rotAllDown) import XMonad.Actions.WithAll (sinkAll, killAll) -- Data import Data.Maybe (isJust, fromJust) import Data.Monoid import qualified Data.Map as M -- Hooks import XMonad.Hooks.DynamicLog (dynamicLogWithPP, wrap, xmobarPP, xmobarColor, shorten, PP(..)) import XMonad.Hooks.ManageDocks (avoidStruts, docksEventHook, manageDocks, ToggleStruts(..)) import XMonad.Hooks.EwmhDesktops import XMonad.Hooks.ManageHelpers (isFullscreen, doFullFloat) -- Layouts import XMonad.Layout.GridVariants (Grid(Grid)) import XMonad.Layout.SimplestFloat import XMonad.Layout.Spiral import XMonad.Layout.Tabbed import XMonad.Layout.ResizableTile -- Layouts modifiers import XMonad.Layout.LayoutModifier import XMonad.Layout.LimitWindows (limitWindows, increaseLimit, decreaseLimit) import XMonad.Layout.Magnifier import XMonad.Layout.MultiToggle (mkToggle, single, EOT(EOT), (??)) import XMonad.Layout.MultiToggle.Instances (StdTransformers(NBFULL, MIRROR, NOBORDERS)) import XMonad.Layout.NoBorders import XMonad.Layout.Renamed import XMonad.Layout.ShowWName import XMonad.Layout.Simplest import XMonad.Layout.Spacing import XMonad.Layout.SubLayouts import XMonad.Layout.WindowArranger (windowArrange) import XMonad.Layout.WindowNavigation import qualified XMonad.Layout.ToggleLayouts as T (toggleLayouts, ToggleLayout(Toggle)) import qualified XMonad.Layout.MultiToggle as MT (Toggle(..)) -- Utilities import XMonad.Util.Dmenu import XMonad.Util.EZConfig (additionalKeysP) import XMonad.Util.Run (spawnPipe) import XMonad.Util.SpawnOnce ----------------------------------------------------------------------------- -- Basic settings: -- Set the modkey -- mod1Mask: left alt, mod4Mask: super key. myModMask :: KeyMask myModMask = mod4Mask -- Preferred programs myTerminal = "alacritty" myBrowser = "firefox" -- Preferred font myFont :: String myFont = "xft:SauceCodePro Nerd Font Mono:regular:size=9:antialias=true:hinting=true" -- Width of the window border in pixels myBorderWidth :: Dimension myBorderWidth = 2 -- Border color of normal windows myNormalBorderColor :: String myNormalBorderColor = "#3b4252" -- Border color of focused windows myFocusedBorderColor :: String myFocusedBorderColor = "#bc96da" -- Default workspaces. Number of workspaces is determined by the list length. myWorkspaces = ["dev", "www", "sys", "chat", "mus", "vid", "doc", "virt", "etc"] myWorkspaceIndices = M.fromList $ zipWith (,) myWorkspaces [1..] -- (,) == \x y -> (x,y) -- Make the workspaces clickable clickable ws = ""++ws++"" where i = fromJust $ M.lookup ws myWorkspaceIndices -- Keep track of the number of windows in current workspace windowCount :: X (Maybe String) windowCount = gets $ Just . show . length . W.integrate' . W.stack . W.workspace . W.current . windowset ------------------------------------------------------------------------------- -- Key bindings with EZConfig: -- C: Ctrl, S: Shift, M: Mod, M1: Alt myKeys :: [(String, X ())] myKeys = -- XMonad [ ("M-S-r", spawn "xmonad --recompile; xmonad --restart") -- Recompiles xmonad , ("M-S-q", io exitSuccess) -- Quits xmonad -- Programs , ("M-b", spawn (myBrowser)) , ("M-", spawn (myTerminal)) , ("M-M1-h", spawn (myTerminal ++ " -e htop")) , ("M-M1-b", spawn (myTerminal ++ " -e bpytop")) , ("M-M1-p", spawn (myTerminal ++ " -e ipython")) -- Dmenu , ("M-S-", spawn "dmenu_run -i -p \"Run: \"") -- Screenshots , ("", spawn "flameshot gui") , ("M-", spawn "flameshot screen -p ~/Pictures/Screenshots") , ("M-S-", spawn "flameshot screen -c") , ("C-", spawn "flameshot full -p ~/Pictures/Screenshots") , ("C-S-", spawn "flameshot full -c") -- Script shortcuts , ("M-S-p", spawn "setbg ~/Pictures/Wallpapers/Active") -- Set random background , ("M-S-d", spawn "displayselect") -- Kill windows , ("M-w", kill1) -- Kill the currently focused client , ("M-S-w", killAll) -- Kill all windows on current workspace -- Compositor , ("M-C-x", spawn "picom -b") -- Run picom compositor , ("M-S-x", spawn "killall picom") -- Kill picom compositor -- Workspaces , ("M-.", nextScreen) -- Switch focus to next monitor , ("M-,", prevScreen) -- Switch focus to prev monitor , ("M-S-", shiftTo Next nonNSP >> moveTo Next nonNSP) -- Shifts focused window to next ws , ("M-S-", shiftTo Prev nonNSP >> moveTo Prev nonNSP) -- Shifts focused window to prev ws -- Floating windows , ("M-f", sendMessage (T.Toggle "floats")) -- Toggles 'floats' layout , ("M-t", withFocused $ windows . W.sink) -- Push floating window back to tile , ("M-S-t", sinkAll) -- Push all floating windows to tile -- Increase/decrease spacing (gaps) , ("C-M1-j", decWindowSpacing 4) -- Decrease window spacing , ("C-M1-k", incWindowSpacing 4) -- Increase window spacing , ("C-M1-h", decScreenSpacing 4) -- Decrease screen spacing , ("C-M1-l", incScreenSpacing 4) -- Increase screen spacing -- Windows navigation , ("M-m", windows W.focusMaster) -- Move focus to the master window , ("M-j", windows W.focusDown) -- Move focus to the next window , ("M-k", windows W.focusUp) -- Move focus to the prev window , ("M-S-m", windows W.swapMaster) -- Swap the focused window and the master window , ("M-S-j", windows W.swapDown) -- Swap focused window with next window , ("M-S-k", windows W.swapUp) -- Swap focused window with prev window , ("M-S-", rotSlavesDown) -- Rotate all windows except master and keep focus in place , ("M-C-", rotAllDown) -- Rotate all windows in the current stack , ("M-", promote) -- Moves focused window to master, others maintain order -- Layouts , ("M-", sendMessage NextLayout) -- Switch to next layout , ("M-", sendMessage (MT.Toggle NBFULL) >> sendMessage ToggleStruts) -- Toggles noborder/full -- Increase/decrease windows in the master pane or the stack , ("M-S-", sendMessage (IncMasterN 1)) -- Increase # of clients master pane , ("M-S-", sendMessage (IncMasterN (-1))) -- Decrease # of clients master pane , ("M-C-", increaseLimit) -- Increase # of windows , ("M-C-", decreaseLimit) -- Decrease # of windows -- Window resizing , ("M-h", sendMessage Shrink) -- Shrink horiz window width , ("M-l", sendMessage Expand) -- Expand horiz window width , ("M-M1-j", sendMessage MirrorShrink) -- Shrink vert window width , ("M-M1-k", sendMessage MirrorExpand) -- Expand vert window width -- Multimedia keys , ("", spawn "amixer set Master 5%- unmute") , ("", spawn "amixer set Master 5%+ unmute") , ("", spawn "amixer set Master toggle") , ("", spawn "brightness + 10 %") , ("", spawn "brightness - 10 %") ] where nonNSP = WSIs (return (\ws -> W.tag ws /= "NSP")) nonEmptyNonNSP = WSIs (return (\ws -> isJust (W.stack ws) && W.tag ws /= "NSP")) ------------------------------------------------------------------------------- -- Layout vars: --Makes setting the spacingRaw simpler to write. --The spacingRaw module adds a configurable amount of space around windows. mySpacing :: Integer -> l a -> XMonad.Layout.LayoutModifier.ModifiedLayout Spacing l a mySpacing i = spacingRaw False (Border i i i i) True (Border i i i i) True -- Below is a variation of the above except no borders are applied -- if fewer than two windows. So a single window has no gaps. mySpacing' :: Integer -> l a -> XMonad.Layout.LayoutModifier.ModifiedLayout Spacing l a mySpacing' i = spacingRaw True (Border i i i i) True (Border i i i i) True -- Colors for tabs layout and tabs sublayout. myTabTheme = def { fontName = myFont , activeColor = "#46d9ff" , inactiveColor = "#313846" , activeBorderColor = "#46d9ff" , inactiveBorderColor = "#282c34" , activeTextColor = "#282c34" , inactiveTextColor = "#d0d0d0" } -- Theme for showWName which prints current workspace when you change workspaces. myShowWNameTheme :: SWNConfig myShowWNameTheme = def { swn_font = "xft:Ubuntu:bold:size=60" , swn_fade = 1.0 , swn_bgcolor = "#1c1f24" , swn_color = "#ffffff" } ------------------------------------------------------------------------------- -- Layouts: -- All of these layouts have to be defined in myLayoutHook, otherwise -- type errors will occur tall = renamed [Replace "tall"] $ smartBorders $ windowNavigation $ addTabs shrinkText myTabTheme $ subLayout [] (smartBorders Simplest) $ limitWindows 12 $ mySpacing 8 $ ResizableTall 1 (3/100) (1/2) [] magnify = renamed [Replace "magnify"] $ smartBorders $ windowNavigation $ addTabs shrinkText myTabTheme $ subLayout [] (smartBorders Simplest) $ magnifier $ limitWindows 12 $ mySpacing 8 $ ResizableTall 1 (3/100) (1/2) [] floats = renamed [Replace "floats"] $ smartBorders $ limitWindows 20 simplestFloat grid = renamed [Replace "grid"] $ smartBorders $ windowNavigation $ addTabs shrinkText myTabTheme $ subLayout [] (smartBorders Simplest) $ limitWindows 12 $ mySpacing 8 $ mkToggle (single MIRROR) $ Grid (16/10) spirals = renamed [Replace "spirals"] $ smartBorders $ windowNavigation $ addTabs shrinkText myTabTheme $ subLayout [] (smartBorders Simplest) $ mySpacing' 8 $ spiral (6/7) tabs = renamed [Replace "tabs"] -- I cannot add spacing to this layout because it will -- add spacing between window and tabs which looks bad. $ tabbed shrinkText myTabTheme myLayoutHook = avoidStruts $ mouseResize $ windowArrange $ T.toggleLayouts floats $ mkToggle (NBFULL ?? NOBORDERS ?? EOT) myDefaultLayout where myDefaultLayout = withBorder myBorderWidth tall ||| magnify ||| floats ||| grid ||| spirals ||| tabs ------------------------------------------------------------------------------- -- Window rules: -- Execute arbitrary actions and WindowSet manipulations when managing -- a new window. You can use this to, for example, always float a -- particular program, or have a client always appear on a particular -- workspace. -- -- To find the property name associated with a program, use -- > xprop | grep WM_CLASS -- and click on the client you're interested in. -- -- To match on the WM_NAME, you can use 'title' in the same way that -- 'className' and 'resource' are used below. -- -- `doFloat` forces a window to float, useful for dialog boxes and such. -- `doShift (myWorkspaces !! 7)` sends program to workspace 8 myManageHook :: XMonad.Query (Data.Monoid.Endo WindowSet) myManageHook = composeAll -- Make dialog boxes floating, don't tile them [ className =? "notification" --> doFloat , className =? "confirm" --> doFloat , className =? "dialog" --> doFloat , className =? "error" --> doFloat , className =? "download" --> doFloat , className =? "file_progress" --> doFloat , className =? "splash" --> doFloat , className =? "toolbar" --> doFloat , className =? "Qalculate-gtk" --> doFloat , isFullscreen --> doFullFloat -- auto-shift applications to their respecitve workspaces , className =? "discord" --> doShift ( myWorkspaces !! 3 ) , className =? "Code" --> doShift ( myWorkspaces !! 0 ) , title =? "Mozilla Firefox" --> doShift ( myWorkspaces !! 1 ) ] ------------------------------------------------------------------------ -- Startup hook -- Perform an arbitrary action each time xmonad starts or is restarted -- with mod-q. Used by, e.g., XMonad.Layout.PerWorkspace to initialize -- per-workspace layout choices. -- -- I don't really use this because I define these applications -- in ~/.config/x11/xprofile instead, that way it will apply for -- all WMs, not just for XMonad myStartupHook :: X () myStartupHook = do -- Automatically run autostart.sh script which will start -- .desktop applications defined in ~/.config/autostart spawnOnce "$HOME/.config/xmonad/scripts/autostart.sh &" ------------------------------------------------------------------------------- -- Log hook: this sends info to xmobar process(es) myLogHook :: Handle -> Handle -> X () myLogHook xmproc0 xmproc1 = dynamicLogWithPP $ xmobarPP { ppOutput = \x -> hPutStrLn xmproc0 x -- xmobar on monitor 1 >> hPutStrLn xmproc1 x -- xmobar on monitor 2 , ppCurrent = xmobarColor "#98be65" "" . wrap " [" "] " -- Current workspace , ppVisible = xmobarColor "#98be65" "" . wrap " " " " . clickable -- Visible but not current workspace , ppHidden = xmobarColor "#82AAFF" "" . wrap " *" " " . clickable -- Hidden workspaces , ppHiddenNoWindows = xmobarColor "#c792ea" "" . wrap " " " " . clickable -- Hidden workspaces (no windows) , ppTitle = xmobarColor "#b3afc2" "" . shorten 60 -- Title of active window , ppSep = " | " -- Separator character , ppUrgent = xmobarColor "#C45500" "" . wrap " !" "! " -- Urgent workspace , ppExtras = [windowCount] -- # of windows current workspace , ppOrder = \(ws:l:t:ex) -> [ws,l]++ex++[t] -- order of things in xmobar } ------------------------------------------------------------------------------- -- Run xmonad with all the defaults we set up. main :: IO () main = do -- Launching 2 instances of xmobar on their respective monitors. xmproc0 <- spawnPipe "xmobar -x 0 $HOME/.config/xmobar/xmobarrc0" xmproc1 <- spawnPipe "xmobar -x 1 $HOME/.config/xmobar/xmobarrc1" -- Xmonad config definitions xmonad $ ewmh def { modMask = myModMask , terminal = myTerminal , workspaces = myWorkspaces , startupHook = myStartupHook , manageHook = myManageHook <+> manageDocks , handleEventHook = docksEventHook , layoutHook = showWName' myShowWNameTheme $ myLayoutHook , borderWidth = myBorderWidth , normalBorderColor = myNormalBorderColor , focusedBorderColor = myFocusedBorderColor , logHook = myLogHook xmproc0 xmproc1 } `additionalKeysP` myKeys