-- Import import Data.Default import Data.Monoid import Graphics.X11.ExtraTypes.XF86 import Control.Monad (liftM2) import XMonad import XMonad.Actions.PhysicalScreens import XMonad.Hooks.DynamicLog import XMonad.Hooks.EwmhDesktops import XMonad.Hooks.FloatConfigureReq import XMonad.Hooks.InsertPosition import XMonad.Hooks.ManageDocks import XMonad.Hooks.ManageHelpers import XMonad.Hooks.Rescreen import XMonad.Hooks.StatusBar import XMonad.Hooks.StatusBar.PP import XMonad.Layout.Grid import XMonad.Layout.LayoutCombinators import XMonad.Layout.Magnifier import XMonad.Layout.NoBorders import XMonad.Layout.PerWorkspace import XMonad.Layout.Renamed import XMonad.Layout.Spacing import XMonad.Layout.Spiral import XMonad.Layout.Tabbed import XMonad.Layout.ThreeColumns import qualified XMonad.StackSet as W import XMonad.Util.EZConfig import qualified XMonad.Util.Hacks as Hacks import XMonad.Util.Loggers import XMonad.Util.NamedScratchpad import XMonad.Util.SpawnOnce -- Main main :: IO () main = xmonad . ewmhFullscreen . ewmh . dynamicEasySBs myStatusBars . addRandrChangeHook (spawn myScreenHotplug) $ myConfig -- Variables myModMask :: KeyMask myModMask = mod4Mask myTerminal :: String myTerminal = "kitty" myBrowser :: String myBrowser = "zen-browser" myNormBorderColor :: String myNormBorderColor = "#1f2335" myFocusBorderColor :: String myFocusBorderColor = "#41a6b5" myKeyboardLayoutIT :: String myKeyboardLayoutIT = " -layout it" myKeyboardLayoutUS = " -layout us" myKeyboardLayoutSK = " -layout sk -variant qwerty" myScreenshotDir :: String myScreenshotDir = "/home/fuxino/Pictures/Screenshots/" myScreenshotName :: String myScreenshotName = "Screenshot-%Y-%m-%d-%H%M%S.png" -- Sets up the outputs and the wallpaper, and leaves the tray's monitor in $tray myScreenLayout :: String myScreenLayout = "if xrandr --query | grep -q '^HDMI-1-0 connected'; then" ++ " xrandr --output HDMI-1-0 --preferred --primary --output eDP-1 --right-of HDMI-1-0 --preferred; tray=1;" ++ " else xrandr --output eDP-1 --preferred --primary --output HDMI-1-0 --off; tray=0; fi" ++ "; ~/.fehbg" -- Replaces any running trayer, so there's never more than one myTrayer :: String myTrayer = "pkill -x trayer; while pgrep -x trayer > /dev/null; do sleep 0.1; done; \ \trayer -l --edge top --align right --SetDockType true \ \--SetPartialStrut true --expand true --widthtype request \ \--tint 0x232634 --height 18 --monitor $tray" -- Safe to re-run (e.g. spawnOnce after this string changes and xmonad restarts) myScreenSetup :: String myScreenSetup = "sleep 1; " ++ myScreenLayout ++ "; pgrep -x picom > /dev/null || picom -b -c; " ++ myTrayer -- Run when a monitor is (dis)connected; restarts trayer so it moves to the right screen myScreenHotplug :: String myScreenHotplug = myScreenLayout ++ "; " ++ myTrayer myScratchpads :: [NamedScratchpad] myScratchpads = [ NS "Riseup-vpn" "riseup-vpn" (className =? "RiseupVPN") (customFloating $ W.RationalRect 0.15 0.4 0.15 0.4), NS "Terminal" spawnTerminal (className =? "scratch-terminal") (customFloating $ W.RationalRect 0.2 0.2 0.6 0.6), NS "Music" spawnMusic (className =? "scratch-music") (customFloating $ W.RationalRect 0.2 0.2 0.6 0.6), NS "Mail" spawnMail (className =? "scratch-mail") (customFloating $ W.RationalRect 0.2 0.2 0.6 0.6), NS "Cal" spawnCal (className =? "scratch-cal") (customFloating $ W.RationalRect 0.2 0.2 0.6 0.6), NS "Btop" spawnBtop (className =? "scratch-btop") (customFloating $ W.RationalRect 0.2 0.2 0.6 0.6) ] where spawnTerminal = myTerminal ++ " --class scratch-terminal -T kitty-float" spawnMusic = myTerminal ++ " --class scratch-music -T ncmpcpp ncmpcpp" spawnMail = myTerminal ++ " --class scratch-mail -T mutt bash -c 'pushd ~/Downloads > /dev/null; mutt; popd > /dev/null'" spawnCal = myTerminal ++ " --class scratch-cal -T calcurse calcurse" spawnBtop = myTerminal ++ " --class scratch-btop -T btop btop" -- Config myConfig = def { modMask = myModMask, terminal = myTerminal, workspaces = myWorkspaces, focusedBorderColor = myFocusBorderColor, normalBorderColor = myNormBorderColor, layoutHook = myLayoutHook, startupHook = myStartupHook, manageHook = myManageHook, handleEventHook = myHandleEventHook } `additionalKeysP` [ ("M-b", sendMessage ToggleStruts), ("M-S-l", spawn "slock"), ("", unGrab *> spawn ("scrot " ++ myScreenshotDir ++ myScreenshotName)), ("S-", unGrab *> spawn ("scrot -s " ++ myScreenshotDir ++ myScreenshotName)), ("M-S-", unGrab *> spawn ("scrot -u " ++ myScreenshotDir ++ myScreenshotName)), ("M-d", spawn "dmenu_run"), ("M-p", spawn "passmenu -i"), ("M-f", sendMessage $ JumpToLayout "Tabbed"), ("M-i", spawn $ "setxkbmap" ++ myKeyboardLayoutIT), ("M-u", spawn $ "setxkbmap" ++ myKeyboardLayoutUS), ("M-s", spawn $ "setxkbmap" ++ myKeyboardLayoutSK), ("M-S-v", namedScratchpadAction myScratchpads "Riseup-vpn"), ("M-S-s", namedScratchpadAction myScratchpads "Terminal"), ("M-S-a", namedScratchpadAction myScratchpads "Cal"), ("M-o", namedScratchpadAction myScratchpads "Btop"), ("M-x", spawn "bluetoothctl connect F4:9D:8A:B9:8C:EE"), ("M-S-x", spawn "bluetoothctl disconnect F4:9D:8A:B9:8C:EE") ] `additionalKeys` [ ((0, xF86XK_AudioMute), spawn "pactl set-sink-mute $(pactl get-default-sink) toggle"), ((0, xF86XK_AudioLowerVolume), spawn "pactl set-sink-volume $(pactl get-default-sink) -10%"), ((0, xF86XK_AudioRaiseVolume), spawn "pactl set-sink-volume $(pactl get-default-sink) +10%"), ((0, xF86XK_Calculator), spawn "qalculate-gtk"), ((0, xF86XK_HomePage), spawn myBrowser), ((0, xF86XK_Mail), namedScratchpadAction myScratchpads "Mail"), ((0, xF86XK_Tools), namedScratchpadAction myScratchpads "Music") ] ++ [ ((myModMask .|. mask, key), f sc) | (key, sc) <- zip [xK_w, xK_e] [0 ..], (f, mask) <- [(viewScreen def, 0), (sendToScreen def, shiftMask)] ] -- Workspaces myWorkspaces :: [String] myWorkspaces = ["1:\xf489 ", "2:\xe743 ", "3:\xf1b6 ", "4:\xf10b ", "5:\xead9 ", "6:\xeb69 ", "7:\xea7a "] ++ map show [8, 9] -- Layout myLayoutHook = onWorkspace "2:\xe743 " myWebLayout $ onWorkspace "3:\xf1b6 " myGamesLayout $ onWorkspace "5:\xead9 " myVideoLayout myDefaultLayout where myWebLayout = avoidStruts $ renamed [CutWordsLeft 1] $ spacingWithEdge 3 $ myTabbed ||| tiled ||| Mirror tiled ||| threeCol ||| Grid ||| spiral 0.856 myGamesLayout = avoidStruts $ smartBorders Full myVideoLayout = avoidStruts $ smartBorders Full myDefaultLayout = avoidStruts $ renamed [CutWordsLeft 1] $ spacingWithEdge 3 $ tiled ||| Mirror tiled ||| myTabbed ||| threeCol ||| Grid ||| spiral 0.856 threeCol = renamed [Replace "ThreeCol"] $ magnifiercz' 1.3 $ ThreeColMid nmaster delta ratio myTabbed = renamed [Replace "Tabbed"] $ tabbed shrinkText myTabConfig myTabConfig = def { activeColor = "#24283b", inactiveColor = "#737aa2" } tiled = Tall nmaster delta ratio nmaster = 1 ratio = 1 / 2 delta = 3 / 100 -- Xmobar -- One bar per connected screen. The laptop config leaves room for trayer, so it -- goes on the laptop screen, which is screen 1 with HDMI and screen 0 without. myStatusBars :: ScreenId -> X StatusBarConfig myStatusBars (S i) = do nScreens <- gets (length . W.screens . windowset) let rc = if nScreens > 1 && i /= 1 then "xmobarrc_hdmi" else "xmobarrc_laptop" pure $ statusBarProp ("xmobar -x " ++ show i ++ " ~/.config/xmobar/" ++ rc) (pure myXmobarPP) myXmobarPP :: PP myXmobarPP = def { ppSep = pink " • ", ppTitleSanitize = xmobarStrip, ppCurrent = wrap " " "" . xmobarBorder "Top" "#ff79c6" 2, ppHidden = cultured . wrap " " "", ppHiddenNoWindows = outerspace . wrap " " "", ppUrgent = sunsetorange . wrap (keylime "!") (keylime "!"), ppOrder = \[ws, l, _, wins] -> [ws, l, wins], ppExtras = [logTitles formatFocused formatUnfocused] } where formatFocused = wrap (cultured "[") (cultured "]") . pink . ppWindow formatUnfocused = wrap (outerspace "[") (outerspace "]") . cultured . ppWindow ppWindow :: String -> String ppWindow = xmobarRaw . (\w -> if null w then "untitled" else w) . shorten 20 outerspace, pink, sunsetorange, cultured, keylime :: String -> String pink = xmobarColor "#ff79c6" "" cultured = xmobarColor "#f8f8f2" "" keylime = xmobarColor "#f1fa8c" "" sunsetorange = xmobarColor "#ff5555" "" outerspace = xmobarColor "#44475a" "" -- Autostart myStartupHook :: X () myStartupHook = do spawnOnce myScreenSetup spawnOnce "xsetroot -cursor_name left_ptr" spawnOnce "xautolock -time 10 -locker slock -detectsleep" spawnOnce "redshift-gtk" spawnOnce "udiskie" spawnOnce "nm-applet" spawnOnce "arch-audit-gtk" spawnOnce "calcurse --daemon" spawnOnce "dunst" spawnOnce "lxpolkit" spawnOnce "numlockx" spawnOnce "blueman-applet" spawnOnce "mousemove" spawnOnce "adb start-server" spawnOnce "/home/fuxino/.local/bin/discover-overlay-wait" -- Manage hook myManageHook :: ManageHook myManageHook = composeAll [ className =? "Gpodder" --> doShift "5:\xead9 ", className =? "An Anime Game Launcher" --> doShift "3:\xf1b6 ", className =? "Qalculate-gtk" --> doFloat, title =? "Genshin Impact" --> doShift "3:\xf1b6 ", className =? "Signal" --> doShift "4:\xf10b ", className =? "Spotify" --> doShift "5:\xead9 ", className =? "Transmission-gtk" --> doShift "2:\xe743 ", className =? "Viewnior" --> doFloat, className =? "Vivaldi-stable" --> doShift "2:\xe743 ", className =? "Xmessage" --> doFloat, className =? "Xreader" --> doShift "6:\xeb69 ", className =? "calibre" --> doShift "6:\xeb69 ", className =? "discord" --> doShift "4:\xf10b ", className =? "explorer.exe" --> doShift "3:\xf1b6 ", className =? "feh" --> doFloat, title =? "glxgears" --> doFloat, className =? "library_manager" --> doShift "6:\xeb69 ", className =? "libreoffice-calc" --> doShift "6:\xeb69 ", className =? "libreoffice-writer" --> doShift "6:\xeb69 ", className =? "mpv" --> doFullFloat, className =? "mpv" --> viewShift "5:\xead9 ", className =? "freetube" --> doShift "5:\xead9 ", className =? myTerminal --> doShift "1:\xf489 ", className =? "scrcpy" --> doShift "5:\xead9 ", className =? "steam" --> doShift "3:\xf1b6 ", className =? "steam_app_109600" --> doFloat, className =? "steam_app_109600" --> doShift "3:\xf1b6 ", className =? "steam_app_289070" --> doFloat, className =? "steam_app_289070" --> doShift "3:\xf1b6 ", className =? "transmission-gtk" --> doShift "2:\xe743 ", className =? "VirtualBox Machine" --> doShift "7:\xea7a ", className =? "VirtualBox Machine" --> doFullFloat, className =? "VirtualBox Manager" --> doShift "7:\xea7a ", className =? "zen" --> doShift "2:\xe743 ", isDialog --> doFloat, isFullscreen --> doFullFloat ] <+> namedScratchpadManageHook myScratchpads where viewShift = doF . liftM2 (.) W.greedyView W.shift -- Handle event hook myHandleEventHook :: Event -> X All myHandleEventHook = handleEventHook def <> Hacks.trayerAboveXmobarEventHook <> Hacks.trayerPaddingXmobarEventHook <> Hacks.fixSteamFlicker