-- {{{ Imports import Control.Monad (liftM2) import XMonad import XMonad.Actions.CopyWindow (copyToAll, killAllOtherCopies) import XMonad.Actions.FloatSnap import XMonad.Hooks.EwmhDesktops import XMonad.Hooks.ManageDocks import XMonad.Hooks.ManageHelpers import XMonad.Hooks.OnPropertyChange (onXPropertyChange) import XMonad.Hooks.StatusBar import XMonad.Hooks.StatusBar.PP import XMonad.Layout.CenteredIfSingle import XMonad.Layout.IndependentScreens import XMonad.Layout.PerScreen import XMonad.Layout.Renamed import XMonad.Layout.ShowWName import XMonad.Layout.ThreeColumns import XMonad.Util.EZConfig import XMonad.Util.Loggers import XMonad.Util.Paste import qualified XMonad.StackSet as W import qualified XMonad.Util.Hacks as Hacks -- }}} -- Statusbar {{{ pp' :: ScreenId -> PP -> PP pp' s pp = (marshallPP s pp) { ppSort = ppSort pp } pp :: PP pp = def { ppSep = tertiaryColor " ", ppCurrent = brackitify, ppHidden = secondaryColor, ppHiddenNoWindows = tertiaryColor, ppUrgent = red . wrap (yellow "!") (yellow "!"), ppLayout = id, ppTitle = shorten 80, ppTitleSanitize = xmobarStrip, ppOrder = \[workspaces, layout, windows, _] -> [workspaces, layout, windows], ppExtras = [logTitles formatFocused formatUnfocused] } where brackitify = wrap "〈" "〉" formatFocused = secondaryColor . ppWindow formatUnfocused = tertiaryColor . ppWindow ppWindow = xmobarRaw . (\w -> if null w then "Untitled" else w) . shorten 16 primaryColor = xmobarColor "#000000" "" secondaryColor = xmobarColor "#333333" "" tertiaryColor = xmobarColor "#555555" "" yellow = xmobarColor "#ff0" "" red = xmobarColor "#ff5555" "" -- }}} -- Workspaces & screens {{{ -- Shift to workspace and view workspace shiftAndView = doF . liftM2 (.) W.greedyView W.shift -- }}} -- Hooks {{{ -- startupHook myStartupHook = do spawn "killall polybar && polybar -r" -- manageHook myManageHook :: ManageHook myManageHook = composeAll [ isDialog --> doCenterFloat, className =? "Zathura" --> doShift "1_info", className =? "firefox" --> shiftAndView "1_www", className =? "firefoxdeveloperedition" --> shiftAndView "1_www", className =? "Anki" --> shiftAndView "1_etc", className =? "Obsidian" --> shiftAndView "1_etc", className =? "Launcher" --> doRectFloat (W.RationalRect 0.05 0.4 0.9 0.5), className =? "Zettelkasten" --> doRectFloat (W.RationalRect 0.05 0.4 0.9 0.5), className =? "Calculator" --> doCenterFloat, className =? "feh" --> doCenterFloat, -- Center matplotlib and prevent focus stealing -- className =? "matplotlib" --> doRectFloat (W.RationalRect 0.5 0.5 0.5 0.5), className =? "matplotlib" --> doCenterFloat, className =? "Matplotlib" --> doCenterFloat, className =? "Xournalpp" --> doRectFloat (W.RationalRect 0.5 0.5 0.5 0.5), className =? "KeePassXC" --> doRectFloat (W.RationalRect 0.1 0.1 0.8 0.8), className =? "flameshot" --> doRectFloat (W.RationalRect 0.1 0.1 0.8 0.8) ] myDynamicManageHook :: ManageHook myDynamicManageHook = composeAll [ title =? "Zettelkasten — Firefox Developer Edition" --> doShift "1_sh" ] -- TODO: Replace showWName by dunst notification myShowWNameConfig :: SWNConfig myShowWNameConfig = def { swn_font = "xft:Iosevka Term SS08:size=16", swn_fade = 0.3, swn_color = "#111111", swn_bgcolor = "#cccccc" } -- layoutHook myLayoutHook = showWName' myShowWNameConfig $ ifWider smallWidth ( t ||| -- Tiled layouts c3 ||| -- Column layouts c3m ||| -- f -- Monocle layouts ) ( t ||| -- Tiled layouts f -- Monocle layouts ) where smallWidth = 1920 t = renamed [Replace "[]+"] $ ifWider smallWidth (centeredIfSingle 0.8 0.9 $ Tall nmaster delta ratio) (Tall nmaster delta ratio) c3 = renamed [Replace "|||"] $ ThreeCol nmaster delta ratio c3m = renamed [Replace "[|]"] $ ThreeColMid nmaster delta ratio f = renamed [Replace "[+]"] Full nmaster = 1 ratio = 1 / 2 delta = 4 / 100 -- }}} -- Main config {{{ myWorkspaces = [ "sh", "www", "dev", "info", "etc" ] myWorkspaceKeys = [ "a", "s", "d", "f", "g" ] mySharedWorkspaces = [ "shared" ] mySharedWorkspaceKeys = [ "1" ] myConfig = def { terminal = "alacritty", -- Use Win key instead of Alt modMask = mod4Mask, workspaces = withScreen 1 myWorkspaces ++ withScreen 2 mySharedWorkspaces, -- Styling focusedBorderColor = "#000", normalBorderColor = "#0000", borderWidth = 4, -- Hooks startupHook = myStartupHook, manageHook = myManageHook <+> manageHook def, layoutHook = avoidStruts myLayoutHook, handleEventHook = onXPropertyChange "WM_NAME" myDynamicManageHook <> handleEventHook def <> Hacks.windowedFullscreenFixEventHook } `removeKeysP` myRemoveKeys `additionalKeysP` myKeys `additionalMouseBindings` myMouseBindings -- }}} -- Keybindings {{{ -- Keybindings to be added/overridden myKeys :: [(String, X ())] myKeys = [ ("M- s", unfloatFocusedW), ("M- ", nextLayout), -- Cycle through layouts ("M- S-", defaultLayout), -- ("M- M-", nextLayout), -- ..fat finger ("M- M-S-", defaultLayout), -- ("", spawnKeepassXC), ("M-z", spawnZettelkasten), ("M-p", spawnLauncher), ("", pasteSelection), ("", printScreen), ("", raiseVol), -- Audio volume & playback ("", lowerVol), -- ("", mute), -- ("M-", nextTrack), -- ("M-", prevTrack), -- ("M-", play), -- ("M-", pause), -- ("", brighten), -- Brightness & hue controls ("", dim), -- ("S-", warm), -- ("S-", cool), -- ("M-S-", resetTemp), -- ("M-S-", resetTemp), -- ("M-S-b", fullscreenBrowser), ("", spawnCalculator), ("", spawn "systemctl suspend"), --TODO: Only enable this on laptop ("M-c", windows copyToAll), ("M-S-c", killAllOtherCopies), ("M-S-", kill) ] ++ [ (m ++ k, windows $ f w) | (m, f) <- zip ["M-", "M-S-"] [W.greedyView, W.shift], (k, w) <- zip myWorkspaceKeys (withScreen 1 myWorkspaces) ++ zip mySharedWorkspaceKeys (withScreen 2 mySharedWorkspaces) ] zipKeyPrefixes :: [String] -> [String] -> [String] zipKeyPrefixes prefixes keys = [prefix ++ key | prefix <- prefixes, key <- keys] -- Keybindings to be removed myRemoveKeys :: [String] myRemoveKeys = "M-S-q" : zipKeyPrefixes ["M-", "M-S-"] (map show [ 1..5 ]) myMouseBindings = [ ((mod4Mask, button1), (\w -> focus w >> mouseMoveWindow w >> afterDrag (snapMagicMove (Just 50) (Just 50) w))) , ((mod4Mask .|. shiftMask, button1), (\w -> focus w >> mouseMoveWindow w >> afterDrag (snapMagicResize [L,R,U,D] (Just 50) (Just 50) w))) , ((mod4Mask, button3), (\w -> focus w >> mouseResizeWindow w >> afterDrag (snapMagicResize [R,D] (Just 50) (Just 50) w))) ] unfloatFocusedW :: X () unfloatFocusedW = withFocused $ windows . W.sink myStartupHook :: X () nextLayout = sendMessage NextLayout defaultLayout :: X () defaultLayout = setLayout $ Layout myLayoutHook spawnZettelkasten :: X () spawnZettelkasten = spawn "alacritty --class Zettelkasten,Zettelkasten -e nvim $(cat ~/.zk/current-zettel.txt)" spawnKeepassXC :: X () spawnKeepassXC = spawn "keepassxc" fullscreenBrowser :: X () fullscreenBrowser = spawn "firefox --fullscreen" spawnLauncher, spawnClipManager, spawnCalculator :: X () spawnLauncher = spawn "rofi -show drun -show-icons" spawnClipManager = spawn "rofi -modi 'clipboard:greenclip print' -show clipboard -run-command '{cmd}'" spawnCalculator = spawn "alacritty --class 'Calculator' -e ipython -i /home/h/.bin/calc.py" printScreen :: X () printScreen = spawn "flameshot gui" raiseVol, lowerVol, mute :: X () raiseVol = spawn "pactl set-sink-volume @DEFAULT_SINK@ +5%" lowerVol = spawn "pactl set-sink-volume @DEFAULT_SINK@ -5%" mute = spawn "pactl set-sink-mute @DEFAULT_SINK@ toggle" nextTrack, prevTrack, play, pause :: X () nextTrack = spawn "playerctl next" prevTrack = spawn "playerctl previous" play = spawn "playerctl play" pause = spawn "playerctl pause" brighten, dim, warm, cool, resetTemp :: X () brighten = spawn "brightnessctl set 20+" dim = spawn "brightnessctl set 20-" warm = spawn "screen-temperature +50" cool = spawn "screen-temperature -50" resetTemp = spawn "screen-temperature 3000" -- }}} -- Main {{{ main :: IO () main = do { xmonad } $ ewmh $ withEasySB (sb1 <> sb2) defToggleStrutsKey myConfig where [sb1, sb2] = [statusBarProp "polybar" $ pure (pp' (S i) pp) | i <- [0 .. 1]] -- }}}