{-# LANGUAGE OverloadedStrings #-} module XMonad.Config.MasseR where import XMonad.Password import XMonad.CustomPrompt import XMonad.TopicSpace import qualified Data.List as List import XMonad import XMonad.Actions.CycleWS (swapNextScreen) import XMonad.Actions.Search import XMonad.Hooks.EwmhDesktops (ewmh, ewmhDesktopsStartup) import XMonad.Hooks.SetWMName (setWMName) import XMonad.Hooks.UrgencyHook (args, dzenUrgencyHook, withUrgencyHook) import XMonad.Layout.Accordion import XMonad.Layout.BinarySpacePartition (emptyBSP) import XMonad.Layout.Decoration (Decoration, DefaultShrinker) import XMonad.Layout.DwmStyle import XMonad.Layout.LayoutModifier (ModifiedLayout) import XMonad.Layout.Master import XMonad.Layout.NoBorders (smartBorders) import XMonad.Layout.PerWorkspace (onWorkspace) import XMonad.Layout.Renamed import XMonad.Layout.Simplest (Simplest) import XMonad.Layout.Spiral import XMonad.Layout.Tabbed (TabbedDecoration, Theme (..), shrinkText, tabbed) import XMonad.Layout.ToggleLayouts (ToggleLayout (..)) import XMonad.Prompt.RunOrRaise (runOrRaisePrompt) import qualified XMonad.StackSet as W import XMonad.Util.EZConfig import XMonad.XMobar (zenburnPP) import Data.Monoid (Endo, (<>)) import XMonad.Util.NamedScratchpad import XMonad.Util.SpawnOnce import System.IO (hClose, hPutStr) import XMonad.Actions.Navigation2D import XMonad.Util.NamedActions import XMonad.Util.Run (spawnPipe) import XMonad.Config.MasseR.ExtraConfig -- import Customizations import qualified Data.Text as T import XMonad.Hooks.DynamicLog (statusBar) import qualified Data.Set as S spotify :: XConfig l -> NamedAction spotify conf = submapName . mkNamedKeymap conf $ [ ("M-p", addName "Play" $ spawn "sp play") ] scratchpads :: [NamedScratchpad] scratchpads = [ NS "notes" "vim -g --role notes -c 'e ~/wikidata/index.md'" (wmRole =? "notes") nonFloating ] where wmRole = stringProperty "WM_WINDOW_ROLE" scratchSubmaps :: XConfig l -> NamedAction scratchSubmaps conf = submapName . mkNamedKeymap conf $ [ ("M-n", addName "Open notes" $ namedScratchpadAction scratchpads "notes") ] -- Search engines inside submaps searchSubmaps :: XConfig l -> NamedAction searchSubmaps conf = let mkBrowser = promptSearchBrowser def "qutebrowser" _googleP = addName "Search google" $ mkBrowser google ddgP = addName "Search duckduckgo" $ mkBrowser (searchEngine "duckduckgo" "http://duckduckgo.com/?q=") in submapName . mkNamedKeymap conf $ [ ("d", ddgP) -- Training to use ddg again , ("g", ddgP) -- training to use ddg again ] myNav2d :: Navigation2DConfig myNav2d = def { defaultTiledNavigation = lineNavigation } showKeybindings :: [((KeyMask, KeySym), NamedAction)] -> NamedAction showKeybindings x = addName "Show keybindings" $ io $ do h <- spawnPipe "zenity --text-info --font=terminus" hPutStr h (unlines $ showKm x) hClose h -- Layout myTabConfig :: Theme myTabConfig = def { activeBorderColor = "#DCDCCC" , activeTextColor = "#DCDCCC" , activeColor = "#3F3F3F" , fontName = "xft:Inconsolata-9" , inactiveBorderColor = "#262626" , inactiveTextColor = "#9FAFAF" , inactiveColor = "#262626" } (=~?) :: XMonad.Query String -> String -> XMonad.Query Bool q =~? x = fmap (x `List.isInfixOf`) q -- Manage hooks -- Move programs to their workspaces myManageHook :: XMonad.Query (Endo WindowSet) myManageHook = composeAll $ concat [ dynamicsHook , webHooks , pdfHooks , documentHooks , floatHooks , debuggerHooks , ideHooks , flowHook ] where classHook y = map (\x -> className =? x --> y) ideHooks = classHook (doShift "eclipse") [ "Anypoint Studio" ] webHooks = classHook (doShift "web") [ "Firefox" , "qutebrowser" , "Midori" , "chromium-browser" , "Uzbl-tabbed" , "Uzbl-core" ] pdfHooks = classHook (doShift "pdf") [ "Evince" , "Okular" , "Kpdf" , "Xdvi" , ".zathura-wrapped_" ] documentHooks = classHook (doShift "documents") [ "libreoffice" , "libreoffice-calc" , "Assistant" , "Bouml" ] floatHooks = classHook doFloat [ "SMplayer" , "Gimp" , "MPlayer" , "Kaffeine" , "Xmessage" , "Wfica_Seamless" , "mpv" ] debuggerHooks = classHook (doShift "debugger") [ "JSwat Debugger", "DBeaver" ] dynamicsHook = [title =~? "Dynamics" --> doShift "dynamics"] flowHook = [title =~? "www.flowdock.com" --> doShift "flowdock"] myKeys :: ExtraConfig -> XConfig l -> [((KeyMask, KeySym), NamedAction)] myKeys extraConfig conf = let subKeys str ks = subtitle str : mkNamedKeymap conf ks in subKeys "Actions" [ ("M-S-r", addName "foobar" (recompile True >> spawn "xmonad --restart")) , ("M-C-l", addName "Lock screen" $ spawn locker)] ^++^ subKeys "System" [ ("", addName "Suspend machine" $ spawn "sudo pm-suspend") , ("", addName "Increase volume" $ spawn "amixer set Master 2%+") , ("", addName "Decrease volume" $ spawn "amixer set Master 2%-") , ("M-", addName "Increase volume" $ spawn "amixer set Master 2+") , ("M-", addName "Decrease volume" $ spawn "amixer set Master 2-") -- , ("", addName "Play/pause spotify" $ spawn "/home/masse/.local/bin/sp play") , ("", addName "Play/pause mopidy" $ spawn "mpc toggle") , ("M-m", spotify conf) , ("M-S-", addName "Swap screens" swapNextScreen) , ("M-", addName "Kill window" kill) -- scrot requires `unGrab` , ("M-", addName "Take screenshot" $ spawn (screenshot . applications $ extraConfig))] ^++^ subKeys "Launchers" [ ("M-S-y", addName "Open youtube" $ spawn "mpv $(clip -o)") , ("M-S-", addName "Open terminal" $ spawn $ XMonad.terminal conf) , ("M-n", scratchSubmaps conf) , ("M-s", searchSubmaps conf) , ("M-p", addName "Retrieve password" $ passPrompt def) , ("M-S-e", addName "Open with app" xdgOpen) , ("M-e", addName "Run app" $ runOrRaisePrompt def)] ^++^ subKeys "Windows" [ ("M-j", addName "Go down" $ windowGo D False) , ("M-k", addName "Go up" $ windowGo U False) , ("M-h", addName "Go left" $ windowGo L False) , ("M-l", addName "Go right" $ windowGo R False) , ("M-S-j", addName "Shift window down" $ windowSwap D False) , ("M-S-k", addName "Shift window up" $ windowSwap U False) , ("M-S-h", addName "Shift window left" $ windowSwap L False) , ("M-S-l", addName "Shift window right" $ windowSwap R False) , ("M-.", addName "Go to previous window" $ windows W.focusDown) , ("M-,", addName "Go to next window" $ windows W.focusUp) , ("M-S-m", addName "Swap master" $ windows W.swapMaster) ] ^++^ subKeys "Projects & Workspaces" (topicKeys' extraConfig conf) ^++^ subKeys "Layout management" [ ("M-C-", addName "Toggle layout" $ sendMessage ToggleLayout) , ("M-", addName "Next layout" $ sendMessage NextLayout)] ^++^ subKeys "Resize" [] where locker = "sh ~/scripts/lock.sh" myStartupHook :: X () myStartupHook = spawnOnce "$HOME/wminit" masser :: ExtraConfig -> IO () masser extraConfig = xmonad =<< statusBar (bar extraConfig) zenburnPP toggleStrutsKey myConfig where toggleStrutsKey XConfig{modMask=modm} = (modm, xK_b) bar = prompt . applications myConfig = withUrgencyHook dzenUrgencyHook { args = ["-bg", "darkgreen", "-xs", "1"]} $ withNavigation2DConfig myNav2d $ ewmh $ addDescrKeys' ((mod4Mask, xK_F1), showKeybindings) (myKeys extraConfig) $ def { modMask = mod4Mask -- Hyper , terminal = urxvt . applications $ extraConfig , keys = const mempty , workspaces = let defaults = ["irc", "web", "mail"] external = map (T.unpack . topicName) . topics $ extraConfig in S.toList (S.fromList defaults <> S.fromList external) , layoutHook = smartBorders myLayout , clickJustFocuses = False , startupHook = myStartupHook >> ewmhDesktopsStartup >> setWMName "LG3D" , borderWidth = 2 , normalBorderColor = "#262626" , focusedBorderColor = "#7F9F7F" , manageHook = myManageHook , focusFollowsMouse = False } myLayout = onWorkspace "web" webLayout $ onWorkspace "dynamics" webLayout $ onWorkspace "pdf" pdfLayout $ onWorkspace "documents" documentLayout $ onWorkspace "mail" mailLayout defLayout where -- Default layout defLayout = tiled ||| tabLayout ||| readLayout ||| bspLayout ||| vimLayout ||| spiral (6/7) ||| Full -- Pdfs are restricted to tabs vimLayout = Mirror (mastered (1/100) (4/5) Accordion) pdfLayout = readLayout ||| tiled ||| tabLayout readLayout = renamed [Replace "2/3"] (dwmStyle shrinkText myTabConfig (mastered (1/100) (2/3) Accordion)) bspLayout = renamed [Replace "master bsp"] (dwmStyle shrinkText myTabConfig (mastered (1/100) (2/3) (Mirror emptyBSP))) -- Documents are by default tabs, but have looser restrictions documentLayout = tabLayout ||| Full ||| tiled ||| Mirror tiled -- Web is either tabbed, full, or tiled webLayout = readLayout ||| tabLayout ||| Full ||| tiled tiled = Tall nmaster delta ratio -- I need to restrict the type or type inferencer can't deduce type classes tabLayout :: ModifiedLayout (Decoration TabbedDecoration DefaultShrinker) Simplest Window tabLayout = tabbed shrinkText myTabConfig mailLayout = readLayout ||| tabLayout delta = 3/100 ratio = 1/2 nmaster = 1