{-# LANGUAGE RecordWildCards#-} {-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} module Main (main) where import XMonad import qualified XMonad.StackSet as W import XMonad.Hooks.EwmhDesktops import XMonad.Hooks.ManageDocks import XMonad.Hooks.Modal import XMonad.Hooks.RefocusLast import XMonad.Hooks.StatusBar import XMonad.Util.EZConfig import XMonad.Util.NamedScratchpad import Data.Foldable (traverse_) import Data.Maybe (fromMaybe) import XMonad.Actions.TagWindows import XMonad.Util.Loggers.NamedScratchpad import Control.Monad (void) main :: IO () main = do xmonad . ewmhFullscreen . ewmh . withSB mySB . docks . modal myModes $ myConfig myConfig = def { modMask = mod4Mask -- Rebind Mod to the Super key , terminal = myTerminal , keys = flip mkKeymap baseKeymap -- if changing mouse keybind take care to have those keybinds somwhere , layoutHook = myLayout , logHook = myLogHook , manageHook = myManageHook , startupHook = myStartupHook myConfig } -- | Set kitty as my terminal. character Overlap is too big of a problem to use alacritty myTerminal :: String myTerminal = "kitty" myLayout = avoidStruts $ myTall ||| Mirror myTall ||| Full myTall = Tall 1 (3/100) (1/2) myEventHook :: Event -> X () myEventHook ev = void $ nspTrackHook myScratchpads ev myLogHook :: X () myLogHook = refocusLastLogHook myManageHook :: ManageHook myManageHook = namedScratchpadManageHook myScratchpads myStartupHook :: XConfig a -> X () myStartupHook cfg = checkKeybinds cfg >> nspTrackStartup myScratchpads myScratchpads :: NamedScratchpads myScratchpads = [ plansNSP ] plansNSP :: NamedScratchpad plansNSP = NS { name = "plans" , cmd = "kitty -d /home/daily/plans" -- TODO: this is hardcoded and should be set from nix , query = hasTagQuery "plansSP" , hook = floatAndSetTagManageHook "plansSP" } hasTagQuery :: String -> Query Bool hasTagQuery tg = do window <- ask liftX $ hasTag tg window floatAndSetTagManageHook :: String -> ManageHook floatAndSetTagManageHook tg = do window <- ask liftX (addTag tg window) doFloat -- | Sends current mode to statusbar via x window properties mySB :: StatusBarConfig mySB = def { sbLogHook = mySBLogHook -- xmonad doesn't reliably start taffybar on launch so taffybar is launched -- elsewhere in the nix config } mySBLogHook :: X () mySBLogHook = do md <- logMode xmonadPropLog' "_XMONAD_LOG_CURRENT_MODE" (fromMaybe "No Mode" md) -- | checks keybinds with no mode and all keybinds for each mode checkKeybinds :: forall l. XConfig l -> X () checkKeybinds cfg = do checkKeymap cfg baseKeymap traverse_ (checkKeymap cfg) myModeKeys -- | keymap used when no mode is on baseKeymap :: forall a. [(String, X ())] baseKeymap = -- restart xmonad without recompiling (nix's jobs) [ ("M-q", restart "xmonad" True) --- kill , ("M-c", kill) -- common Spawn operations , ("M-", spawn myTerminal) -- resize keybindings , ("M-h", sendMessage Shrink) , ("M-l", sendMessage Expand) -- focus keybindings , ("M-j", windows W.focusDown) -- focus (j){down} , ("M-k", windows W.focusUp) -- focus (k){up} , ("M-m", windows W.focusMaster) -- focus (m)aster , ("M-S-j", windows W.swapDown) -- focus (S)wap (j){down} , ("M-S-k", windows W.swapUp) -- focus (S)wap (k){up} ] ++ appendModKey nwsKeybinds ++ addKeyPrefix "M-S-m " modesKeymap ++ addKeyPrefix "M-s " spawnsKeymap modesKeymap :: [(String, X ())] modesKeymap = [ ("l",setModeConfig layoutModeConfig) -- (m)ode switch (l)ayout , ("s",setModeConfig spawnModeConfig) -- (m)ode switch (s)pawn ] spawnsKeymap :: [(String, X ())] spawnsKeymap = [ ("c", spawn "rofi -show run") -- (s)pawn (c)ommand , ("d", spawn "rofi -show drun") -- (s)pawn (d)esktop , ("f", spawn "rofi -show filebrowser") -- (s)pawn (f)ilebrowser , ("p", namedScratchpadAction myScratchpads "plans") , ("s", unGrab *> spawn "scrot -s") -- (s)crot (s)creenshot , ("w", spawn "rofi -show window") -- (s)witch (w)indow ] -- | the 'ModeConfig' Type exists mainly due to the 'Mode' Type not supporting -- inspection operations like getModeKeys or getModeLabel data ModeConfig = MkModeConfig { label :: String , modeKeys :: [(String,X ())] , isOverlay :: Bool } modeConfigToMode :: ModeConfig -> Mode modeConfigToMode (MkModeConfig {..}) = let overlayFun = if isOverlay then overlay else const id in overlayFun label . mode label $ flip mkKeymap modeKeys myModeConfigs :: [ModeConfig] myModeConfigs = [layoutModeConfig, spawnModeConfig] myModes :: [Mode] myModes = fmap modeConfigToMode myModeConfigs myModeKeys :: [[(String, X ())]] myModeKeys = fmap modeKeys myModeConfigs setModeConfig :: ModeConfig -> X () setModeConfig (MkModeConfig {label}) = setMode label spawnModeConfig :: ModeConfig spawnModeConfig = MkModeConfig { label = "Spawn" , modeKeys = [ ("", spawn myTerminal) -- on base keymap , ("c",kill) -- on base keymap -- rofi , ("f", spawn "rofi -show filebrowser") , ("p", spawn "rofi -show drun") -- on base keymap , ("S-p", spawn "rofi -show run") -- on base keymap , ("s", spawn "rofi -show window") -- neovim , ("n", spawn "neovide") -- do i even want this anymore kitty is a better solution -- scrot , ("C-s",unGrab *> spawn "scrot -s") ] , isOverlay = True } layoutModeConfig :: ModeConfig layoutModeConfig = MkModeConfig { label = "Layout" , modeKeys = -- resize keybindings [ ("h", sendMessage Shrink) , ("l", sendMessage Expand) -- focus keybindings , ("j", windows W.focusDown) , ("k", windows W.focusUp) , ("m", windows W.focusMaster) -- change layout , ("", sendMessage NextLayout) , ("S-", setLayout $ Layout myTall) -- swap windows , ("",windows W.swapMaster) , ("S-j", windows W.swapDown) , ("S-k", windows W.swapUp) -- tile floating window , ("t", withFocused $ windows . W.sink) -- change the number of master windows , (",", sendMessage $ IncMasterN 1) , (".", sendMessage $ IncMasterN (-1)) ] , isOverlay = True } -- numbered workspaces wsNum :: [Int] wsNum = [1 .. 9] viewNWS, shiftToNWS, nwsKeybinds :: [(String, X ())] viewNWS = [(i, windows $ W.greedyView i) | i <- fmap show wsNum] shiftToNWS = [("S-" <> i, windows $ W.shift i) | i <- fmap show wsNum] nwsKeybinds = viewNWS <> shiftToNWS -- keybind utilities appendModKey :: [(String, X ())] -> [(String, X ())] appendModKey = fmap appendModKeySingle where appendModKeySingle :: (String, X ()) -> (String, X ()) appendModKeySingle (kb, act) = ("M-" <> kb, act) -- same key but also needs mod addKeyPrefix :: String -> [(String, X ())] -> [(String, X ())] addKeyPrefix prefix = fmap (addKeyPrefixSingle prefix) where addKeyPrefixSingle :: String -> (String, X ()) -> (String, X ()) addKeyPrefixSingle prefix (kb, act) = (prefix <> kb, act)