import Control.Monad (liftM2) import Data.List (isInfixOf) import qualified Data.Monoid import Data.Ratio import XMonad ( ChangeLayout (NextLayout), Default (def), Full (Full), Mirror (Mirror), Query, Tall (Tall), WindowSet, WindowSpace, X, XConfig ( borderWidth, focusedBorderColor, layoutHook, logHook, manageHook, modMask, normalBorderColor, startupHook, terminal, workspaces ), className, composeAll, doF, doFloat, doIgnore, doShift, handleEventHook, mod4Mask, resource, sendMessage, spawn, title, withWindowSet, xmonad, (-->), (<&&>), (<+>), (=?), (|||), ) import XMonad.Actions.WindowGo (raise, runOrRaise) import XMonad.Hooks.DynamicLog ( PP ( ppCurrent, ppExtras, ppHidden, ppHiddenNoWindows, ppOrder, ppSep, ppSort, ppTitleSanitize, ppUrgent ), ppVisible, shorten, wrap, xmobarBorder, xmobarColor, xmobarRaw, xmobarStrip, ) import XMonad.Hooks.EwmhDesktops ( ewmh, ewmhFullscreen, ) import XMonad.Hooks.InsertPosition (Focus (Newer), Position (End), insertPosition) import XMonad.Hooks.ManageHelpers (doRectFloat) import XMonad.Hooks.Place (placeHook, simpleSmart) import XMonad.Hooks.StatusBar ( defToggleStrutsKey, statusBarProp, withEasySB, ) import XMonad.Layout (Resize (Expand, Shrink)) import XMonad.Layout.Fullscreen (fullscreenEventHook) import qualified XMonad.Layout.LayoutModifier import XMonad.Layout.Named (named) import XMonad.Layout.NoBorders import XMonad.Layout.Spacing ( Border (Border), Spacing, spacingRaw, ) import XMonad.Layout.WindowNavigation ( Direction2D (L, R), Navigate (Go), windowNavigation, ) import XMonad.StackSet (RationalRect (RationalRect), Workspace (tag), currentTag, greedyView, integrate', shift, stack) import XMonad.Util.EZConfig (additionalKeysP) import XMonad.Util.Loggers (logTitles) import XMonad.Util.SpawnOnce (spawnOnce) import XMonad.Util.Ungrab (unGrab) import XMonad.Util.WorkspaceCompare (getSortByIndex) import XMonad.Hooks.SetWMName (setWMName) main :: IO () main = do colors <- getWalColors xmonad . ewmhFullscreen . ewmh . withEasySB (statusBarProp "xmobar ~/.config/xmobar/xmobarrc" (pure $ myXmobarPP colors)) defToggleStrutsKey $ def { modMask = mod4Mask, terminal = "st", startupHook = myStartupHook, borderWidth = 2, normalBorderColor = "#000000", focusedBorderColor = colors !! 7, workspaces = myWorkspaces, manageHook = myManageHook, layoutHook = myLayout, handleEventHook = handleEventHook def <+> fullscreenEventHook } `additionalKeysP` [ ("M-S-z", spawn "xscreensaver-command -lock"), ("M-S-s", unGrab *> spawn "cast_screenshot"), ("M-f", runOrRaise "brave" (className =? "Brave-browser")), ("M-i", raise (className =? "jetbrains-idea")), ("M-", spawn "rofi -show drun"), ("M-p", spawn "dmen"), -- audio settings ("", spawn "amixer set Master 5%- unmute"), ("", spawn "amixer set Master 5%+ unmute"), ("", spawn "amixer set Master toggle"), ("", spawn "playerctl play-pause"), ("", spawn "playerctl previous"), ("", spawn "playerctl next"), -- window navigation ("M-l", sendMessage $ Go R), ("M-h", sendMessage $ Go L), ("M-S-h", sendMessage Shrink), -- Shrink horiz window width ("M-S-l", sendMessage Expand), -- Expand horiz window width -- layout switching ("M-", sendMessage NextLayout), -- dmenu stuff ("M1-", spawn "powerbutton"), -- lockscreen ("M-", spawn "betterlockscreen -l") ] myXmobarPP :: [String] -> PP myXmobarPP colors = def { ppSep = walXmobarColor 1 " \63617 ", ppTitleSanitize = xmobarStrip, ppCurrent = wrap " " "" . xmobarBorder "Top" (colors !! 6) 2, ppHidden = white . wrap " " "", ppHiddenNoWindows = lowWhite . wrap " " "", ppUrgent = red . wrap (yellow "!") (yellow "!"), ppVisible = wrap " " "" . xmobarBorder "Bottom" (colors !! 6) 2, ppOrder = \[ws, l, _, wins] -> [ws, l, wins], ppExtras = [logTitles formatFocused formatUnfocused], ppSort = hideWorkspaces } where formatFocused = wrap (walXmobarColor 7 "\58279") (walXmobarColor 7 "\58254") . walXmobarColor 7 . ppWindow formatUnfocused = wrap (walXmobarColor 4 "\58279") (walXmobarColor 4 "\58254") . walXmobarColor 4 . ppWindow walXmobarColor index = xmobarColor (colors !! index) "" ppWindow :: String -> String ppWindow = xmobarRaw . (\w -> if null w then "untitled" else w) . shorten 30 lowWhite, red, white, yellow :: String -> String white = xmobarColor "#f8f8f2" "" yellow = xmobarColor "#f1fa8c" "" red = xmobarColor "#ff5555" "" lowWhite = xmobarColor "#bbbbbb" "" hideWorkspaces = do sortByIndex <- getSortByIndex wsfilter <- thefilter return (wsfilter . sortByIndex) where thefilter :: X ([WindowSpace] -> [WindowSpace]) --thefilter = filter ( not . isEmptyWs) . sortworkspaces thefilter = do currentWS <- withWindowSet (pure . currentTag) return $ reverse . dropWhile (liftM2 (&&) ((currentWS /=) . tag) isEmptyWs) . reverse isEmptyWs workspace = null $ integrate' $ stack workspace myStartupHook :: X () myStartupHook = do setWMName "LG3D" spawnOnce "picom &" spawnOnce "sh $HOME/.fehbg" spawnOnce "setxkbmap -option caps:escape" getWalColors :: IO [String] getWalColors = do let file = "/home/qv/.cache/wal/colors" contents <- readFile file let colors = lines contents return (colors ++ replicate (16 - length colors) "#000000") mySpacing' :: Integer -> l a -> XMonad.Layout.LayoutModifier.ModifiedLayout Spacing l a mySpacing' i = spacingRaw True (Border i 0 i i) True (Border i 0 i i) True --myWorkspaces = [" dev ", " www ", " sys ", " doc ", " vbox ", " chat ", " mus ", " vid ", " gfx "] myWorkspaces :: [String] myWorkspaces = show <$> ([1 .. 9] :: [Integer]) myLayout = smartBorders $ windowNavigation tiled ||| smartBorders Full ||| Mirror tiled where tiled = named "Tall" $ mySpacing' 8 $ Tall nmaster delta ratio nmaster = 1 -- Default number of windows in the master pane ratio = 1 / 2 -- Default proportion of screen occupied by master pane delta = 3 / 100 -- Percent of screen to increment by when resizing panes myManageHook :: XMonad.Query (Data.Monoid.Endo WindowSet) myManageHook = composeAll -- 'doFloat' forces a window to float. Useful for dialog boxes and such. -- using 'doShift ( myWorkspaces !! 7)' sends program to workspace 8! -- I'm doing it this way because otherwise I would have to write out the full -- name of my workspaces and the names would be very long if using clickable workspaces. [ className =? "confirm" --> doFloat, className =? "file_progress" --> doFloat, className =? "dialog" --> doFloat, className =? "download" --> doFloat, className =? "error" --> doFloat, className =? "Gimp" --> doFloat, className =? "notification" --> doFloat, className =? "pinentry-gtk-2" --> doFloat, className =? "splash" --> doFloat, className =? "toolbar" --> doFloat, title =? "Oracle VM VirtualBox Manager" --> doFloat, title =? "Mozilla Firefox" --> viewShift (myWorkspaces !! 1), title =? "Signal" --> viewShift (myWorkspaces !! 2), className =? "TelegramDesktop" --> viewShift (myWorkspaces !! 2), className =? "Element" --> viewShift (myWorkspaces !! 2), className =? "Thunderbird" --> viewShift (myWorkspaces !! 2), className =? "Gimp" --> doShift (myWorkspaces !! 8), (className =? "firefox" <&&> resource =? "Dialog") --> doFloat, -- Float Firefox Dialog className =? "alfaview" --> viewShift (myWorkspaces !! 3), className =? "jetbrains-idea" --> viewShift (myWorkspaces !! 4), className =? "jetbrains-code-with-me-guest" --> viewShift (myWorkspaces !! 4), (className =? "jetbrains-studio") <&&> (title ~=? "win") --> doIgnore, (className =? "TelegramDesktop") <&&> (title ~=? "Media viewer") --> doFloat ] <+> insertPosition End Newer where viewShift = doF . liftM2 (.) greedyView shift -- IntelliJ fix (~=?) :: Eq a => Query [a] -> [a] -> Query Bool q ~=? x = fmap (isInfixOf x) q