dots-of-war/files/.xmonad/lib/Config.hs

626 lines
28 KiB
Haskell
Raw Normal View History

2020-06-10 11:17:55 +00:00
{-# LANGUAGE NamedFieldPuns #-}
2020-05-25 13:07:50 +00:00
{-# LANGUAGE MultiParamTypeClasses, DeriveDataTypeable, TypeSynonymInstances, FlexibleInstances, FlexibleContexts, ScopedTypeVariables, LambdaCase #-}
2020-03-23 19:23:31 +00:00
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-unused-binds #-}
-- Imports -------------------------------------------------------- {{{
2020-04-01 10:02:59 +00:00
2020-03-22 18:40:50 +00:00
module Config (main) where
2020-05-26 17:20:09 +00:00
import qualified Data.Map.Strict as M
2020-05-09 13:24:23 +00:00
import Control.Concurrent
2020-05-18 10:32:35 +00:00
import Control.Exception ( catch , SomeException)
2020-06-09 20:52:14 +00:00
import Control.Monad ( filterM
, when
, guard
)
import Control.Arrow ( (>>>) )
2020-06-09 20:52:14 +00:00
import Data.List ( isPrefixOf
, isSuffixOf
, isInfixOf
2020-06-10 10:58:56 +00:00
, intercalate
2020-06-09 20:52:14 +00:00
)
import qualified Foreign.C.Types
2020-03-25 10:05:44 +00:00
import System.Exit (exitSuccess)
2020-06-09 20:52:14 +00:00
import qualified XMonad.Util.ExtensibleState as XS
2020-04-01 10:08:40 +00:00
import qualified Rofi
2020-05-18 10:08:22 +00:00
import qualified DescribedSubmap
import qualified TiledDragging
2020-06-20 12:31:37 +00:00
--import qualified WindowSwallowing
import XMonad.Hooks.WindowSwallowing as WindowSwallowing
2020-04-01 10:08:40 +00:00
2020-03-22 18:06:44 +00:00
2020-05-18 10:32:35 +00:00
import Data.Foldable ( for_ )
2020-05-30 15:16:18 +00:00
2020-05-18 10:32:35 +00:00
import Data.Function ((&))
2020-04-02 07:31:20 +00:00
2020-03-22 18:06:44 +00:00
import XMonad hiding ((|||))
import XMonad.Actions.CopyWindow
2020-05-18 10:32:35 +00:00
import XMonad.Actions.PhysicalScreens ( horizontalScreenOrderer )
import XMonad.Actions.SpawnOn
2020-03-22 18:06:44 +00:00
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.ManageDocks
import XMonad.Hooks.SetWMName (setWMName)
2020-04-01 10:08:40 +00:00
import XMonad.Layout.BinarySpacePartition
import XMonad.Layout.BorderResize
2020-03-22 18:06:44 +00:00
import XMonad.Layout.Gaps
2020-05-18 10:32:35 +00:00
import XMonad.Layout.IndependentScreens
2020-03-22 18:06:44 +00:00
import XMonad.Layout.LayoutCombinators ((|||))
2020-04-01 10:08:40 +00:00
import XMonad.Layout.LayoutHints
import XMonad.Layout.MouseResizableTile
2020-04-01 10:02:59 +00:00
import XMonad.Layout.NoBorders
2020-04-01 10:08:40 +00:00
import XMonad.Layout.Renamed (renamed, Rename(Replace))
2020-05-21 12:11:23 +00:00
--import qualified XMonad.Layout.MultiColumns as MultiCol
2020-04-01 10:02:59 +00:00
import XMonad.Layout.ResizableTile
2020-05-18 10:32:35 +00:00
import XMonad.Layout.Simplest
2020-05-21 13:05:39 +00:00
import XMonad.Layout.Reflect
2020-06-05 13:23:19 +00:00
import XMonad.Layout.Spacing (spacingRaw, Border(..), toggleWindowSpacingEnabled, incScreenWindowSpacing, decScreenWindowSpacing)
2020-05-18 10:32:35 +00:00
import XMonad.Layout.SubLayouts
import XMonad.Layout.Tabbed
import XMonad.Layout.WindowNavigation ( windowNavigation )
2020-03-23 19:23:31 +00:00
import XMonad.Layout.ZoomRow
2020-05-18 12:23:16 +00:00
import XMonad.Layout.ThreeColumns
2020-06-05 13:23:19 +00:00
import XMonad.Layout.ResizableThreeColumns
2020-05-25 13:07:50 +00:00
import XMonad.Layout.WindowSwitcherDecoration
import XMonad.Layout.DraggingVisualizer
2020-06-20 12:31:37 +00:00
--import XMonad.Layout.Hidden as Hidden
2020-05-25 13:07:50 +00:00
2020-05-18 12:23:16 +00:00
import XMonad.Util.EZConfig ( additionalKeysP
, removeKeysP
, checkKeymap
)
2020-05-29 15:57:38 +00:00
2020-06-05 13:23:19 +00:00
import XMonad.Layout.LayoutModifier
2020-03-22 18:06:44 +00:00
import XMonad.Util.NamedScratchpad
2020-03-23 19:23:31 +00:00
import XMonad.Util.Run
2020-03-22 18:06:44 +00:00
import XMonad.Util.SpawnOnce (spawnOnce)
2020-06-11 13:19:11 +00:00
import qualified XMonad.Hooks.UrgencyHook as Urgency
2020-05-18 10:32:35 +00:00
import XMonad.Util.WorkspaceCompare ( getSortByXineramaPhysicalRule , getSortByIndex)
import qualified Data.Monoid
2020-06-09 20:52:14 +00:00
import Data.Monoid ( Endo )
2020-05-29 09:00:20 +00:00
import Data.Traversable ( for )
2020-06-09 20:52:14 +00:00
import Data.Semigroup ( All(..) )
2020-05-18 12:23:16 +00:00
import qualified System.IO as SysIO
import qualified XMonad.Actions.Navigation2D as Nav2d
import qualified XMonad.Config.Desktop as Desktop
import qualified XMonad.Hooks.EwmhDesktops as Ewmh
2020-06-07 20:01:57 +00:00
import XMonad.Hooks.FadeInactive
2020-05-18 12:23:16 +00:00
import qualified XMonad.Hooks.ManageHelpers as ManageHelpers
2020-05-30 15:16:18 +00:00
import XMonad.Hooks.DebugStack ( debugStackString
, debugStackFullString
)
2020-05-18 12:23:16 +00:00
import qualified XMonad.Layout.BoringWindows as BoringWindows
import qualified XMonad.Layout.MultiToggle as MTog
2020-05-18 10:32:35 +00:00
import qualified XMonad.Layout.MultiToggle.Instances as MTog
2020-05-18 12:23:16 +00:00
import qualified XMonad.Layout.ToggleLayouts as ToggleLayouts
import qualified XMonad.StackSet as W
import qualified XMonad.Util.XSelection as XSel
2020-06-09 20:52:14 +00:00
import XMonad.Util.WindowProperties
2020-05-21 13:05:39 +00:00
import qualified XMonad.Layout.PerScreen as PerScreen
2020-04-01 10:02:59 +00:00
{-# ANN module "HLint: ignore Redundant $" #-}
{-# ANN module "HLint: ignore Redundant bracket" #-}
{-# ANN module "HLint: ignore Move brackets to avoid $" #-}
2020-04-01 10:08:40 +00:00
{-# ANN module "HLint: ignore Unused LANGUAGE pragma" #-}
-- }}}
2020-04-01 10:02:59 +00:00
2020-03-22 18:06:44 +00:00
-- Values -------------------- {{{
myModMask = mod4Mask
2020-03-29 09:36:09 +00:00
myLauncher = Rofi.asCommand (def { Rofi.theme = Rofi.bigTheme }) ["-show run"]
2020-06-07 20:01:57 +00:00
--myTerminal = "termite --name törminell"
myTerminal = "alacritty"
2020-03-29 17:39:21 +00:00
myBrowser = "qutebrowser"
useSharedWorkspaces = False
2020-03-29 17:39:21 +00:00
--myBrowser = "google-chrome-stable"
2020-03-22 18:06:44 +00:00
2020-03-25 10:05:44 +00:00
{-| adds the scripts-directory path to the filename of a script |-}
scriptFile :: String -> String
scriptFile script = "/home/leon/scripts/" ++ script
2020-05-18 10:08:22 +00:00
2020-03-22 18:06:44 +00:00
scratchpads :: [NamedScratchpad]
2020-03-23 19:23:31 +00:00
scratchpads =
2020-05-21 13:14:21 +00:00
[ NS "terminal" "termite --class sp_term" (className =? "sp_term") (customFloating $ W.RationalRect 0.66 0.7 0.34 0.3)
, NS "spotify" "spotify" (appName =? "spotify") defaultFloating
2020-05-24 20:58:23 +00:00
, NS "whatsapp" launchWhatsapp (("WhatsApp" `isSuffixOf`) <$> title) defaultFloating
2020-05-21 13:14:21 +00:00
, NS "slack" "slack" (("Slack | " `isPrefixOf`) <$> title) defaultFloating
2020-06-02 10:51:43 +00:00
, NS "discord" launchDiscord (appName =? "discord") defaultFloating
2020-03-22 18:06:44 +00:00
]
2020-06-02 14:47:42 +00:00
where
--launchWhatsapp = "gtk-launch chrome-hnpfjngllnobngcgfapefoaidbinmjnm-Default.desktop"
launchWhatsapp = "google-chrome-stable --start-fullscreen -kiosk --app='https://web.whatsapp.com'"
2020-06-05 13:23:19 +00:00
launchDiscord = "discord"
--launchDiscord = "beautifuldiscord --css /home/leon/.config/beautifuldiscord/custom_discord.css"
2020-06-02 14:47:42 +00:00
2020-03-22 18:06:44 +00:00
-- Colors ------ {{{
fg = "#ebdbb2"
bg = "#282828"
gray = "#888974"
2020-03-22 18:06:44 +00:00
bg1 = "#3c3836"
bg2 = "#504945"
bg3 = "#665c54"
bg4 = "#7c6f64"
green = "#b8bb26"
darkgreen = "#98971a"
red = "#fb4934"
darkred = "#cc241d"
yellow = "#fabd2f"
blue = "#83a598"
purple = "#d3869b"
aqua = "#8ec07c"
-- }}}
-- }}}
-- Layout ---------------------------------------- {{{
2020-05-12 08:29:58 +00:00
myTabTheme = def -- defaultThemeWithButtons
2020-05-28 16:34:56 +00:00
{ -- activeColor = "#8ec07c"
activeColor = "#1d2021"
2020-05-28 16:34:56 +00:00
--activeColor = "#1d2021"
--activeColor = "#504945"
--, inactiveColor = "#282828"
2020-06-08 14:05:32 +00:00
, inactiveColor = "#282828"
, activeBorderColor = "#1d2021"
2020-05-18 12:23:16 +00:00
, inactiveBorderColor = "#282828"
, activeTextColor = "#fbf1c7"
, inactiveTextColor = "#fbf1c7"
2020-05-28 16:34:56 +00:00
, decoHeight = 15
2020-05-25 13:07:50 +00:00
, fontName = "-misc-cozettevector-*-*-*-*-10-*-*-*-*-*-*-*"
2020-05-28 16:34:56 +00:00
--, fontName = "-misc-scientifica-*-*-*-*-10-*-*-*-*-*-*-*"
2020-05-12 08:29:58 +00:00
}
2020-05-12 09:42:08 +00:00
data EmptyShrinker = EmptyShrinker deriving (Show, Read)
2020-05-26 17:20:09 +00:00
instance Shrinker EmptyShrinker where
shrinkIt _ _ = [] :: [String]
2020-05-18 12:23:16 +00:00
myLayout = avoidStruts
2020-05-21 13:05:39 +00:00
$ smartBorders
2020-05-13 08:47:11 +00:00
$ MTog.mkToggle1 MTog.FULL
2020-05-21 13:05:39 +00:00
$ ToggleLayouts.toggleLayouts (rename "Tabbed" . makeTabbed . spacingAndGaps $ ResizableTall 1 (3/100) (1/2) [])
2020-05-25 13:07:50 +00:00
$ MTog.mkToggle1 WINDOWDECORATION
$ draggingVisualizer
2020-06-20 12:31:37 +00:00
-- $ Hidden.hiddenWindows
2020-05-13 08:47:11 +00:00
$ layoutHintsToCenter
$ layouts
2020-03-23 19:23:31 +00:00
where
-- | if the screen is wider than 1900px it's horizontal, so use horizontal layouts.
2020-05-21 13:05:39 +00:00
-- if it's not, it's vertical, so use layouts for vertical screens.
layouts = PerScreen.ifWider 1900 horizScreenLayouts vertScreenLayouts
2020-03-23 19:23:31 +00:00
2020-05-25 13:07:50 +00:00
horizScreenLayouts =
2020-06-08 14:21:55 +00:00
((rename "Tall" $ spacingAndGaps $ mouseResizableTile {draggerType = BordersDragger})
||| (rename "Horizon" $ spacingAndGaps $ mouseResizableTileMirrored {draggerType = BordersDragger})
||| (rename "BSP" $ spacingAndGaps $ borderResize $ emptyBSP)
2020-06-17 21:36:03 +00:00
||| (rename "ThreeCol" $ makeTabbed $ spacingAndGaps $ ResizableThreeColMid 1 (3/100) (1/2) [])
2020-06-09 20:52:14 +00:00
||| (rename "TabbedRow" $ makeTabbed $ spacingAndGaps $ zoomRow))
2020-03-23 19:23:31 +00:00
2020-05-25 13:07:50 +00:00
vertScreenLayouts =
2020-05-21 13:05:39 +00:00
((rename "ThreeCol" $ makeTabbed $ spacingAndGaps $ Mirror $ reflectHoriz $ ThreeColMid 1 (3/100) (1/2))
2020-06-08 14:21:55 +00:00
||| (rename "Horizon" $ spacingAndGaps $ mouseResizableTileMirrored {draggerType = BordersDragger}))
2020-05-12 11:02:54 +00:00
2020-05-21 13:05:39 +00:00
rename n = renamed [Replace n]
2020-06-08 14:21:55 +00:00
spacingAndGaps = let intGap = 10 :: Integer
border = Border intGap intGap intGap intGap
2020-05-12 09:42:08 +00:00
in spacingRaw False border True border True
2020-05-12 11:02:54 +00:00
2020-05-21 13:05:39 +00:00
-- | transform a layout into supporting tabs
2020-05-18 09:17:04 +00:00
makeTabbed layout = BoringWindows.boringWindows . windowNavigation . addTabs shrinkText myTabTheme $ subLayout [] Simplest $ layout
2020-05-18 12:23:16 +00:00
2020-05-26 17:20:09 +00:00
-- | window decoration layout modifier. this needs you to add `dragginVisualizer` yourself
2020-05-25 13:07:50 +00:00
data WINDOWDECORATION = WINDOWDECORATION deriving (Read, Show, Eq, Typeable)
instance MTog.Transformer WINDOWDECORATION Window where
transform WINDOWDECORATION x k = k
2020-05-26 17:20:09 +00:00
(windowSwitcherDecoration shrinkText (myTabTheme { activeBorderColor = "#1d2021" }) $ x)
2020-05-25 13:07:50 +00:00
(const x)
2020-03-22 18:06:44 +00:00
-- }}}
-- Startuphook ----------------------------- {{{
2020-03-22 18:40:50 +00:00
myStartupHook :: X ()
2020-03-22 18:06:44 +00:00
myStartupHook = do
setWMName "LG3D" -- Java stuff hack
2020-06-17 21:36:03 +00:00
--spawnOnce "nm-applet &"
--spawnOnce "udiskie -s &" -- Mount USB sticks automatically. -s is smart systray mode: systray icon if something is mounted
spawnOnce "xfce4-clipman &"
spawnOnce "redshift -P -O 5000 &"
spawn "xset r rate 300 50 &" -- make key repeat quicker
2020-05-28 16:34:56 +00:00
spawn "/home/leon/.screenlayout/dualscreen-stacked.sh"
2020-05-16 15:30:38 +00:00
io $ threadDelay $ 1000 * 100
2020-06-11 13:19:11 +00:00
spawnOnce "/home/leon/Downloads/picom --config /home/leon/.config/picom.conf --experimental-backends --backend xrender" --no-fading-openclose"
2020-05-18 09:17:04 +00:00
spawn "/home/leon/.config/polybar/launch.sh"
2020-05-16 19:44:11 +00:00
spawnOnce "nitrogen --restore"
2020-05-24 20:58:23 +00:00
spawnOnce "mailnag"
2020-06-11 13:19:11 +00:00
--spawnOnce "flashfocus"
2020-05-24 20:58:23 +00:00
for_ ["led1", "led2"] $ \led -> safeSpawn "sudo" ["liquidctl", "set", led, "color", "fixed", "00ffff"]
2020-03-22 18:06:44 +00:00
-- }}}
-- Keymap --------------------------------------- {{{
myMouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ())
myMouseBindings (XConfig {XMonad.modMask = modMask'}) = M.fromList
[((modMask' .|. shiftMask, button1), TiledDragging.tiledDrag)]
2020-05-07 14:05:16 +00:00
multiMonitorOperation :: (WorkspaceId -> WindowSet -> WindowSet) -> ScreenId -> X ()
multiMonitorOperation operation n = do
monitor <- screenWorkspace n
2020-05-09 13:24:23 +00:00
case monitor of
2020-05-18 09:17:04 +00:00
Just mon -> windows $ operation mon
2020-05-07 14:05:16 +00:00
Nothing -> return ()
2020-05-21 13:05:39 +00:00
-- Default mappings that need to be removed
removedKeys :: [String]
removedKeys = ["M-<Tab>", "M-S-c", "M-S-q", "M-h", "M-l", "M-j", "M-k", "M-S-<Return>"]
2020-05-29 09:00:20 +00:00
++ if useSharedWorkspaces then [] else [key ++ show n | key <- ["M-", "M-S-", "M-C-"], n <- [1..9 :: Int]]
2020-05-07 14:05:16 +00:00
2020-06-05 13:23:19 +00:00
2020-05-12 09:42:08 +00:00
myKeys :: [(String, X ())]
2020-05-29 09:00:20 +00:00
myKeys = concat [ zoomRowBindings, tabbedBindings, multiMonitorBindings, programLaunchBindings, miscBindings, windowControlBindings, workspaceBindings ]
2020-05-30 15:16:18 +00:00
where
2020-05-29 09:00:20 +00:00
keyDirPairs = [("h", L), ("j", D), ("k", U), ("l", R)]
zoomRowBindings :: [(String, X ())]
2020-05-30 15:16:18 +00:00
zoomRowBindings =
2020-05-29 09:00:20 +00:00
[ ("M-+", sendMessage zoomIn)
, ("M--", sendMessage zoomOut)
, ("M-#", sendMessage zoomReset)
]
tabbedBindings :: [(String, X ())]
tabbedBindings =
[ ("M-j", ifLayoutName ("Tabbed" `isPrefixOf`) (BoringWindows.focusDown) (windows W.focusDown))
, ("M-k", ifLayoutName ("Tabbed" `isPrefixOf`) (BoringWindows.focusUp) (windows W.focusUp))
, ("M-C-S-h", sendMessage $ pullGroup L)
, ("M-C-S-j", sendMessage $ pullGroup D)
, ("M-C-S-k", sendMessage $ pullGroup U)
, ("M-C-S-l", sendMessage $ pullGroup R)
, ("M-S-C-m", withFocused (sendMessage . MergeAll))
, ("M-S-C-<Backspace>", withFocused (sendMessage . UnMerge))
, ("M-<Tab>", onGroup W.focusDown')
, ("M-C-<Tab>", onGroup W.focusUp')
, ("M-S-t", toggleTabbedLayout)
-- In tabbed mode, while focussing master pane, cycle tabs on the first slave
, ("M-S-<Tab>", do windows W.focusMaster
BoringWindows.focusDown
onGroup W.focusDown'
windows W.focusMaster)
]
multiMonitorBindings :: [(String, X ())]
2020-05-30 15:16:18 +00:00
multiMonitorBindings =
2020-05-29 09:00:20 +00:00
[ ("M-s", multiMonitorOperation W.view 1)
, ("M-d", multiMonitorOperation W.view 0)
, ("M-S-s", (multiMonitorOperation W.shift 1) >> multiMonitorOperation W.view 1)
, ("M-S-d", (multiMonitorOperation W.shift 0) >> multiMonitorOperation W.view 0)
2020-05-29 15:57:38 +00:00
, ("M-C-s", windows swapScreenContents)
2020-05-29 09:00:20 +00:00
]
programLaunchBindings :: [(String, X ())]
programLaunchBindings =
[ ("M-p", spawn myLauncher)
, ("M-S-p", Rofi.showCombi (def { Rofi.theme = Rofi.bigTheme }) [ "drun", "window", "ssh" ])
, ("M-S-e", Rofi.showNormal (def { Rofi.theme = Rofi.bigTheme, Rofi.fuzzy = False }) "emoji")
--, ("M-s", spawn $ scriptFile "rofi-search.sh")
, ("M-S-o", spawn $ scriptFile "rofi-open.sh")
, ("M-n", scratchpadSubmap)
, ("M-e", Rofi.promptRunCommand def specialCommands)
, ("M-o", Rofi.promptRunCommand def withSelectionCommands)
2020-05-30 15:16:18 +00:00
, ("M-S-C-g", spawn "killall -INT -g giph") -- stop gif recording
2020-05-29 09:00:20 +00:00
--, ("M-b", launchWithBackgroundInstance (className =? "qutebrowser") "bwrap --bind / / --dev-bind /dev /dev --tmpfs /tmp --tmpfs /run qutebrowser")
2020-05-31 17:36:54 +00:00
--, ("M-b", safeSpawnProg "qutebrowser")
, ("M-b", safeSpawnProg "firefox")
2020-06-10 10:58:56 +00:00
, ("M-S-<Return>", spawn "alacritty")
--, ("M-S-<Return>", launchWithBackgroundInstance (className =? "Alacritty") "alacritty")
2020-05-29 09:00:20 +00:00
]
miscBindings :: [(String, X ())]
miscBindings =
2020-05-30 15:16:18 +00:00
[ ("M-f", do withFocused (windows . W.sink)
sendMessage (MTog.Toggle MTog.FULL)
2020-05-29 09:00:20 +00:00
sendMessage ToggleStruts)
, ("M-C-S-w", sendMessage $ MTog.Toggle WINDOWDECORATION)
, ("M-S-C-c", kill1)
, ("M-S-C-q", io exitSuccess)
-- Binary space partitioning
2020-06-07 20:01:57 +00:00
, ("M-<Delete>", sendMessage Swap)
, ("M-M1-<Delete>", sendMessage Rotate)
2020-05-29 09:00:20 +00:00
-- Media
, ("<XF86AudioRaiseVolume>", spawn "amixer sset Master 5%+")
, ("<XF86AudioLowerVolume>", spawn "amixer sset Master 5%-")
, ("M-S-C-,", (notify "hi" (show $ map (\(a, _) -> show a) workspaceBindings)) >> (notify "ho" (show removedKeys)))
2020-06-07 20:01:57 +00:00
, ("M-<Backspace>", spawn "flash_window")
2020-06-05 13:23:19 +00:00
, ("M-g", incScreenWindowSpacing 5)
, ("M-S-g", decScreenWindowSpacing 5)
2020-05-29 09:00:20 +00:00
]
workspaceBindings :: [(String, X ())]
2020-05-30 15:16:18 +00:00
workspaceBindings =
if useSharedWorkspaces
then []
else concat $
2020-05-29 09:00:20 +00:00
[ [ ("M-" ++ show wspNum, runActionOnWorkspace W.view wspNum)
, ("M-S-" ++ show wspNum, runActionOnWorkspace W.shift wspNum)
2020-05-30 15:16:18 +00:00
, ("M-C-" ++ show wspNum, runActionOnWorkspace copy wspNum)
2020-05-29 09:00:20 +00:00
]
| wspNum <- [1..9 :: Int]
]
where
runActionOnWorkspace action wspNum = do
wsps <- workspaces' <$> asks config
windows $ onCurrentScreen action (wsps !! (wspNum - 1))
windowControlBindings :: [(String, X ())]
windowControlBindings = windowGoMappings ++ windowSwapMappings ++ resizeMappings
where
windowGoMappings = [ ("M-M1-" ++ key, Nav2d.windowGo dir False) | (key, dir) <- keyDirPairs ]
windowSwapMappings = [ ("M-S-M1-" ++ key, Nav2d.windowSwap dir False) | (key, dir) <- keyDirPairs ]
resizeMappings =
[ ("M-C-h", ifLayoutIs "BSP" (sendMessage $ ExpandTowards L) (ifLayoutIs "Horizon" (sendMessage ShrinkSlave) (sendMessage Shrink)))
, ("M-C-j", ifLayoutIs "BSP" (sendMessage $ ExpandTowards D) (ifLayoutIs "Horizon" (sendMessage Expand) (sendMessage MirrorShrink >> sendMessage ExpandSlave)))
, ("M-C-k", ifLayoutIs "BSP" (sendMessage $ ExpandTowards U) (ifLayoutIs "Horizon" (sendMessage Shrink) (sendMessage MirrorExpand >> sendMessage ShrinkSlave)))
, ("M-C-l", ifLayoutIs "BSP" (sendMessage $ ExpandTowards R) (ifLayoutIs "Horizon" (sendMessage ExpandSlave) (sendMessage Expand)))
2020-05-18 10:08:22 +00:00
]
2020-05-29 09:00:20 +00:00
-- | toggle tabbed Tall layout, merging all non-master windows
-- into a single tab group when initializing the tabbed layout.
toggleTabbedLayout :: X ()
toggleTabbedLayout = do
sendMessage $ ToggleLayouts.Toggle "Tabbed"
ifLayoutIs "Tabbed" (do BoringWindows.focusMaster
withFocused (sendMessage . MergeAll)
withFocused (sendMessage . UnMerge)
-- refresh the tabs, so they draw correctly
windows W.focusUp
windows W.focusDown)
(return ())
-- | launch a program by starting an instance in a hidden workspace,
-- and just raising an already running instance. This allows for super quick "startup" time.
-- For this to work, the window needs to have the `_NET_WM_PID` set and unique!
launchWithBackgroundInstance :: (Query Bool) -> String -> X ()
launchWithBackgroundInstance windowQuery commandToRun = withWindowSet $ \winSet -> do
fittingHiddenWindows <- (W.allWindows winSet) |> filter (\win -> Just "NSP" == W.findTag win winSet)
|> filterM (runQuery windowQuery)
case fittingHiddenWindows of
[] -> do spawnHere commandToRun
spawnOn "NSP" commandToRun
[winId] -> do windows $ W.shiftWin (W.currentTag winSet) winId
spawnOn "NSP" commandToRun
(winId:_) -> windows $ W.shiftWin (W.currentTag winSet) winId
2020-05-29 15:57:38 +00:00
swapScreenContents :: W.StackSet i l a sid sd -> W.StackSet i l a sid sd
2020-05-30 15:16:18 +00:00
swapScreenContents ws = if null (W.visible ws) then ws else
let
2020-05-29 15:57:38 +00:00
otherScreen = head $ W.visible ws
otherWsp = W.workspace otherScreen
currentScreen = W.current ws
currentWsp = W.workspace currentScreen
2020-05-30 15:16:18 +00:00
in
ws
2020-05-29 15:57:38 +00:00
{ W.current = currentScreen { W.workspace = otherWsp { W.tag = W.tag currentWsp } }
, W.visible = (otherScreen { W.workspace = currentWsp { W.tag = W.tag otherWsp } } : (tail $ W.visible ws))
}
2020-05-29 09:00:20 +00:00
withSelectionCommands :: [(String, X ())]
withSelectionCommands =
[ ("Google", XSel.transformPromptSelection ("https://google.com/search?q=" ++) "qutebrowser")
, ("Hoogle", XSel.transformPromptSelection ("https://hoogle.haskell.org/?hoogle=" ++) "qutebrowser")
2020-06-01 12:10:09 +00:00
, ("Translate", XSel.getSelection >>= translateMenu)
2020-05-29 09:00:20 +00:00
]
2020-06-01 12:10:09 +00:00
translateMenu :: String -> X ()
translateMenu input = do
selectedLanguage <- Rofi.promptSimple def ["de", "en", "fr"]
translated <- runProcessWithInput "trans" [":" ++ selectedLanguage, input, "--no-ansi"] ""
notify "Translation" translated
2020-05-29 09:00:20 +00:00
specialCommands :: [(String, X ())]
specialCommands =
[ ("screenshot", spawn $ scriptFile "screenshot.sh")
, ("screenshot to file", spawn $ scriptFile "screenshot.sh --tofile")
, ("screenshot full to file", spawn $ scriptFile "screenshot.sh --tofile --fullscreen")
, ("screengif to file", spawn (scriptFile "screengif.sh") >> notify "gif" "stop gif-recording with M-S-C-g")
, ("toggleOptimal", sendMessage ToggleGaps >> toggleWindowSpacingEnabled)
, ("toggleSpacing", toggleWindowSpacingEnabled)
, ("toggleGaps", sendMessage ToggleGaps)
, ("Copy to all workspaces", windows copyToAll)
, ("Kill all other copies", killAllOtherCopies)
, ("toggle polybar", sendMessage ToggleStruts >> safeSpawn "polybar-msg" ["cmd", "toggle"])
2020-05-30 15:16:18 +00:00
, ("get debug data", debugStackFullString >>= (\str -> safeSpawn "xmessage" [str]))
2020-05-29 09:00:20 +00:00
]
scratchpadSubmap :: X ()
scratchpadSubmap = DescribedSubmap.describedSubmap "Scratchpads"
[ ("M-n", "terminal", namedScratchpadAction scratchpads "terminal")
, ("M-w", "whatsapp", namedScratchpadAction scratchpads "whatsapp")
, ("M-s", "slack", namedScratchpadAction scratchpads "slack")
, ("M-m", "spotify", namedScratchpadAction scratchpads "spotify")
, ("M-d", "discord", namedScratchpadAction scratchpads "discord")
]
2020-05-18 10:08:22 +00:00
2020-03-31 16:38:27 +00:00
2020-03-22 18:06:44 +00:00
-- }}}
-- ManageHook -------------------------------{{{
myManageHook :: Query (Data.Monoid.Endo WindowSet)
myManageHook = composeAll
[ resource =? "Dialog" --> ManageHelpers.doCenterFloat
, appName =? "pavucontrol" --> ManageHelpers.doCenterFloat
, className =? "mpv" --> ManageHelpers.doRectFloat (W.RationalRect 0.9 0.9 0.1 0.1)
, title =? "Something" --> doFloat
, className =? "termite_floating" --> ManageHelpers.doRectFloat(W.RationalRect 0.2 0.2 0.6 0.6)
2020-05-13 22:50:42 +00:00
, className =? "bar_system_status_indicator" --> ManageHelpers.doRectFloat (W.RationalRect 0.7 0.05 0.29 0.26)
2020-03-23 19:23:31 +00:00
, manageDocks
, namedScratchpadManageHook scratchpads
2020-03-22 18:40:50 +00:00
]
2020-03-22 18:06:44 +00:00
-- }}}
-- Main ------------------------------------ {{{
2020-03-22 18:40:50 +00:00
main :: IO ()
2020-03-22 18:06:44 +00:00
main = do
2020-05-09 13:24:23 +00:00
currentScreenCount :: Int <- countScreens
let monitorIndices = [0..currentScreenCount - 1]
-- create a fifo named pipe for every monitor (called /tmp/xmonad-state-bar0, etc)
for_ monitorIndices (\idx -> safeSpawn "mkfifo" ["/tmp/xmonad-state-bar" ++ show idx])
-- create polybarLogHooks for every monitor and combine them using the <+> monoid instance
2020-05-16 15:30:38 +00:00
let polybarLogHooks = composeAll $ map polybarLogHook monitorIndices
2020-05-09 13:24:23 +00:00
2020-05-18 12:23:16 +00:00
let myConfig = flip additionalKeysP myKeys
2020-05-29 09:00:20 +00:00
$ flip removeKeysP removedKeys
2020-05-18 12:23:16 +00:00
$ Desktop.desktopConfig
2020-05-09 13:24:23 +00:00
{ terminal = myTerminal
2020-05-18 09:17:04 +00:00
, workspaces = if useSharedWorkspaces
then (map show [1..9 :: Int]) ++ ["NSP"]
else (withScreens (fromIntegral currentScreenCount) (map show [1..6 :: Int])) ++ ["NSP"]
2020-05-09 13:24:23 +00:00
, modMask = myModMask
2020-06-07 20:01:57 +00:00
, borderWidth = 1
2020-05-09 13:24:23 +00:00
, layoutHook = myLayout
2020-06-11 13:19:11 +00:00
, logHook = mconcat [ polybarLogHooks, Ewmh.ewmhDesktopsLogHook, logHook Desktop.desktopConfig, logHook def]
, startupHook = mconcat [ myStartupHook, Ewmh.ewmhDesktopsStartup, return () >> checkKeymap myConfig myKeys]
, manageHook = mconcat [ manageSpawn, myManageHook, manageHook def]
2020-06-13 11:11:50 +00:00
--, focusedBorderColor = aqua
, focusedBorderColor = "#427b58"
2020-05-09 13:24:23 +00:00
, normalBorderColor = "#282828"
2020-06-11 13:19:11 +00:00
, handleEventHook = mconcat [ mySwallowEventHook
, activateWindowEventHook
, handleEventHook Desktop.desktopConfig
, Ewmh.ewmhDesktopsEventHook
]
2020-05-09 13:24:23 +00:00
--, handleEventHook = minimizeEventHook <+> handleEventHook def <+> hintsEventHook -- <+> Ewmh.fullscreenEventHook
2020-05-26 17:20:09 +00:00
, mouseBindings = myMouseBindings <+> mouseBindings def
2020-05-18 12:23:16 +00:00
}
2020-05-09 13:24:23 +00:00
2020-03-27 08:06:13 +00:00
xmonad
2020-03-25 10:05:44 +00:00
$ Nav2d.withNavigation2DConfig def { Nav2d.defaultTiledNavigation = Nav2d.sideNavigation }
2020-05-18 10:32:35 +00:00
$ docks
2020-05-09 13:24:23 +00:00
$ myConfig
2020-03-22 18:06:44 +00:00
-- }}}
2020-06-09 20:52:14 +00:00
2020-06-20 12:31:37 +00:00
mySwallowEventHook = WindowSwallowing.swallowEventHook [className =? "Alacritty", className =? "Termite", className =? "Thunar"] [return True]
2020-06-10 11:17:55 +00:00
2020-06-09 20:52:14 +00:00
2020-06-11 13:19:11 +00:00
activateWindowEventHook :: Event -> X All
activateWindowEventHook (ClientMessageEvent { ev_message_type = messageType, ev_window = window }) = withWindowSet $ \ws -> do
activateWindowAtom <- getAtom "_NET_ACTIVE_WINDOW"
when (messageType == activateWindowAtom) $
if window `elem` (concatMap (W.integrate' . W.stack . W.workspace) (W.current ws : W.visible ws))
then windows (W.focusWindow window)
else windows (W.shiftWin (W.tag $ W.workspace $ W.current ws) window)
return $ All True
activateWindowEventHook _ = return $ All True
2020-03-22 18:06:44 +00:00
-- POLYBAR Kram -------------------------------------- {{{
2020-05-09 13:24:23 +00:00
-- | Loghook for polybar on a given monitor.
-- Will write the polybar formatted string to /tmp/xmonad-state-bar${monitor}
polybarLogHook :: Int -> X ()
polybarLogHook monitor = do
2020-05-18 09:17:04 +00:00
barOut <- dynamicLogString $ polybarPP $ fromIntegral monitor
2020-05-09 13:24:23 +00:00
io $ SysIO.appendFile ("/tmp/xmonad-state-bar" ++ show monitor) (barOut ++ "\n")
2020-03-22 18:06:44 +00:00
2020-05-09 13:24:23 +00:00
-- swapping namedScratchpadFilterOutWorkspacePP and marshallPP will throw "Prelude.read no Parse" errors..... wtf
-- | create a polybar Pretty printer, marshalled for given monitor.
2020-05-18 09:17:04 +00:00
polybarPP :: ScreenId -> PP
polybarPP monitor = namedScratchpadFilterOutWorkspacePP . (if useSharedWorkspaces then id else marshallPP $ fromIntegral monitor) $ def
{ ppCurrent = withFG aqua . withMargin . withFont 5 . const "__active__"
, ppVisible = withFG aqua . withMargin . withFont 5 . const "__active__"
, ppUrgent = withFG red . withMargin . withFont 5 . const "__urgent__"
, ppHidden = withFG gray . withMargin . withFont 5 . (`wrapClickableWorkspace` "__hidden__")
, ppHiddenNoWindows = withFG gray . withMargin . withFont 5 . (`wrapClickableWorkspace` "__empty__")
, ppWsSep = ""
, ppSep = ""
, ppLayout = removeWords ["DraggingVisualizer", "WindowSwitcherDeco", "Minimize", "Hinted", "Spacing", "Tall"]
>>> \l -> if l == "Tall" || l == "Horizon" || l == "" then ""
else (withFG gray " | ") ++ (withFG purple $ withMargin l)
, ppExtras = []
, ppTitle = const "" -- withFG aqua . (shorten 40)
2020-05-18 09:17:04 +00:00
, ppSort = if useSharedWorkspaces then getSortByXineramaPhysicalRule horizontalScreenOrderer
else onlyRelevantWspsSorter
2020-03-22 18:06:44 +00:00
}
where
withMargin = wrap " " " "
removeWord substr = unwords . filter (/= substr) . words
2020-05-18 09:17:04 +00:00
removeWords wrds = unwords . filter (`notElem` wrds). words
withFont fNum = wrap ("%{T" ++ show (fNum :: Int) ++ "}") "%{T}"
withBG col = wrap ("%{B" ++ col ++ "}") "%{B-}"
withFG col = wrap ("%{F" ++ col ++ "}") "%{F-}"
wrapOnClickCmd command = wrap ("%{A1:" ++ command ++ ":}") "%{A}"
wrapClickableWorkspace wsp = wrapOnClickCmd ("xdotool key super+" ++ wsp)
2020-05-18 09:17:04 +00:00
onlyRelevantWspsSorter = do
sortByIndex <- getSortByIndex
visibleWorkspaceTags <- getVisibleWorkspacesTagsOnMonitor monitor
let isEmptyAndNotOpened wsp = (null $ W.stack wsp) && (W.tag wsp) `notElem` visibleWorkspaceTags
return $ dropEndWhile isEmptyAndNotOpened . sortByIndex
2020-03-22 18:06:44 +00:00
-- }}}
2020-03-23 19:23:31 +00:00
2020-03-25 10:05:44 +00:00
-- Utilities --------------------------------------------------- {{{
2020-05-09 13:24:23 +00:00
2020-05-16 15:30:38 +00:00
(|>) :: a -> (a -> b) -> b
(|>) = (&)
infixl 1 |>
2020-05-09 13:24:23 +00:00
2020-05-18 09:17:04 +00:00
dropEndWhile :: (a -> Bool) -> [a] -> [a]
dropEndWhile _ [] = []
dropEndWhile test xs = if test $ last xs then dropEndWhile test (init xs) else xs
2020-05-09 13:24:23 +00:00
catchAndNotifyAny :: IO () -> IO ()
2020-05-18 10:08:22 +00:00
catchAndNotifyAny ioAction = catch ioAction (\(e :: SomeException) -> notify "Xmonad exception" (show e))
2020-05-09 13:24:23 +00:00
2020-05-18 09:17:04 +00:00
getVisibleWorkspacesTagsOnMonitor :: ScreenId -> X [VirtualWorkspace]
getVisibleWorkspacesTagsOnMonitor monitor = do
ws <- gets windowset
return $ W.current ws : W.visible ws
|> map (W.tag . W.workspace)
|> filter (\tag -> monitor == fromIntegral (unmarshallS tag))
|> map unmarshallW
2020-05-18 10:08:22 +00:00
notify :: MonadIO m => String -> String -> m ()
notify notificationTitle notificationMsg = safeSpawn "notify-send" [notificationTitle, notificationMsg]
2020-03-25 10:05:44 +00:00
2020-04-01 12:37:33 +00:00
ifLayoutIs :: String -> X a -> X a -> X a
2020-05-12 11:02:54 +00:00
ifLayoutIs layoutAName = ifLayoutName (== layoutAName)
ifLayoutName :: (String -> Bool) -> X a -> X a -> X a
ifLayoutName check onLayoutA onLayoutB = do
2020-05-13 08:47:11 +00:00
layout <- getActiveLayoutDescription
2020-05-12 11:02:54 +00:00
if (check layout) then onLayoutA else onLayoutB
2020-04-01 12:37:33 +00:00
-- Get the name of the active layout.
getActiveLayoutDescription :: X String
getActiveLayoutDescription = (description . W.layout . W.workspace . W.current) <$> gets windowset
2020-03-25 10:05:44 +00:00
-- }}}