-- {{{ Imports import Control.Monad (liftM2) import Data.Map qualified as M import XMonad import XMonad.Actions.CopyWindow (copyToAll, killAllOtherCopies) import XMonad.Actions.FloatSnap import XMonad.Actions.Submap 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.Hooks.WindowSwallowing (swallowEventHook) import XMonad.Layout.CenteredIfSingle import XMonad.Layout.IndependentScreens import XMonad.Layout.LayoutHints (hintsEventHook, layoutHints) import XMonad.Layout.PerScreen import XMonad.Layout.PerWorkspace import XMonad.Layout.Renamed import XMonad.Layout.Spacing import XMonad.Layout.Tabbed import XMonad.Layout.ThreeColumns import XMonad.StackSet qualified as W import XMonad.Util.EZConfig import XMonad.Util.Hacks qualified as Hacks import XMonad.Util.Loggers import XMonad.Util.Paste -- }}} -- 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) ] -- }}} -- dynamicManageHook {{{ myDynamicManageHook :: ManageHook myDynamicManageHook = composeAll [ title =? "Zettelkasten — Firefox Developer Edition" --> doShift "1_sh" ] -- }}} -- layoutHook {{{ myLayoutHook = avoidStruts $ smartSpacingWithEdge 4 $ layoutHints $ onWorkspace "1_sh" (Tall nmaster delta 0.8) $ ifWider smallWidth (tWide ||| c3mWide ||| f) t where smallWidth = 1920 -- Tall layouts tWide = centeredIfSingle 0.62 1 t t = named "[]+" $ Tall nmaster delta ratio -- Column layouts c3mWide = centeredIfSingle 0.62 1 c3m c3m = named "[|]" $ ThreeColMid nmaster delta ratio -- Fullscreen layouts f = named "[+]" Full -- Modifiers named n = renamed [Replace n] nmaster = 1 ratio = 0.62 delta = 4 / 100 -- }}} -- handleEventHook {{{ myHandleEventHook = handleEventHook def -- See window swallowing (https://hackage.haskell.org/package/xmonad-contrib-0.18.0/docs/XMonad-Hooks-WindowSwallowing.html) <> swallowEventHook (className =? "Alacritty") (return True) <> onXPropertyChange "WM_NAME" myDynamicManageHook <> Hacks.windowedFullscreenFixEventHook <> hintsEventHook -- }}} -- }}} -- Main config {{{ myWorkspaces = [ "sh", "www", "dev", "info", "etc" ] myWorkspaceKeys = [ "a", "s", "d", "f", "g" ] mySharedWorkspaces = [ "shared" ] mySharedWorkspaceKeys = [ "1" ] -- Use Win key instead of Alt myModMask = mod4Mask myConfig = def { terminal = "alacritty", modMask = myModMask, workspaces = withScreen 1 myWorkspaces ++ withScreen 2 mySharedWorkspaces, -- Styling focusedBorderColor = "#000", normalBorderColor = "#0000", borderWidth = 4, -- Hooks startupHook = myStartupHook, manageHook = myManageHook <+> manageHook def, layoutHook = myLayoutHook, handleEventHook = myHandleEventHook } `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), ("M-w", spawnWindowSwitcher), ("M-S-w", spawnWifiMenu), ("", 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" spawnWindowSwitcher = spawn "rofi -show window -show-icons" spawnWifiMenu = spawn "rofi -show wifi -modi \"wifi:iwdrofimenu\"" 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]] -- }}}