Make xmonad more flexible in regard to multi monitor handling

This commit is contained in:
Leon Kowarschick 2020-05-15 14:39:35 +02:00
parent 05ec12bd99
commit bdb2d28832
7 changed files with 93 additions and 79 deletions

View file

@ -307,8 +307,8 @@ colors: *gruvbox
background_opacity: 1 background_opacity: 1
font: font:
size: 14 size: 12
normal: # >>= >= normal:
#family: JetBrainsMono Nerd Font #family: JetBrainsMono Nerd Font
#family: Iosevka #family: Iosevka
#family: cozette #family: cozette

View file

@ -1,4 +1,4 @@
{ {
"optOut": false, "optOut": false,
"lastUpdateCheck": 1589407237422 "lastUpdateCheck": 1589536718785
} }

View file

@ -103,7 +103,7 @@ font-0 = "cherry:size=12;2"
font-1 = "Symbola:size=8;1" font-1 = "Symbola:size=8;1"
font-2 = "FontAwesome5Free:style=Solid:size=8;2" font-2 = "FontAwesome5Free:style=Solid:size=8;2"
font-3 = "Iosevka Nerd Font:size=10;2" font-3 = "Iosevka Nerd Font:size=10;2"
;font-4 = "Iosevka Nerd Font:size=17;4" font-4 = "Symbola:size=9;2"
;font-1 = "FontAwesome:fontformat=truetype:size=12;1" ;font-1 = "FontAwesome:fontformat=truetype:size=12;1"

View file

@ -16,3 +16,4 @@ https://wiki.haskell.org/GHC_optimisations#Fusion GHC optimisations - HaskellWik
https://github.com/ViktorNova/architect ViktorNova/architect: Automatically builds native packages for any Linux distribution using the source code repositories from Arch Linux and the Arch User Repository (AUR) https://github.com/ViktorNova/architect ViktorNova/architect: Automatically builds native packages for any Linux distribution using the source code repositories from Arch Linux and the Arch User Repository (AUR)
https://stackoverflow.com/questions/38252123/piping-tail-f-to-cut-to-sed-produces-no-output linux - Piping tail -f to cut to sed produces no output - Stack Overflow https://stackoverflow.com/questions/38252123/piping-tail-f-to-cut-to-sed-produces-no-output linux - Piping tail -f to cut to sed produces no output - Stack Overflow
https://start.duckduckgo.com/ DuckDuckGo — Privacy, simplified. https://start.duckduckgo.com/ DuckDuckGo — Privacy, simplified.
http://hackage.haskell.org/package/xmonad-eval xmonad-eval: Module for evaluation Haskell expressions in the running xmonad instance

View file

@ -1,6 +1,7 @@
[options] [options]
font = Iosevka Nerd Font Medium 12 ;font = Iosevka Nerd Font Medium 12
;font = Cozette 10 ;font = Cozette 10
font = Terminus (TTF) 12
;font = scientifica Regular 12 ;font = scientifica Regular 12
;font = Victor mono 13 ;font = Victor mono 13
;font = jetbrains mono Nerd Font 12 ;font = jetbrains mono Nerd Font 12

View file

@ -10,7 +10,11 @@ import Control.Exception ( catch
, SomeException , SomeException
) )
import Data.Char (isDigit) import Data.Char (isDigit)
import Data.List ( isSuffixOf , isPrefixOf) import Data.List ( isSuffixOf
, isPrefixOf
, sort
, sortBy
)
import System.Exit (exitSuccess) import System.Exit (exitSuccess)
import qualified Rofi import qualified Rofi
@ -19,6 +23,7 @@ import qualified Rofi
import qualified Data.Map as M import qualified Data.Map as M
import qualified Data.Monoid import qualified Data.Monoid
import Data.Foldable ( for_ ) import Data.Foldable ( for_ )
import Data.Ord ( comparing )
import qualified System.IO as SysIO import qualified System.IO as SysIO
import XMonad.Layout.HintedGrid import XMonad.Layout.HintedGrid
@ -57,6 +62,7 @@ import XMonad.Util.Run
import XMonad.Util.SpawnOnce (spawnOnce) import XMonad.Util.SpawnOnce (spawnOnce)
import XMonad.Layout.Tabbed import XMonad.Layout.Tabbed
import qualified XMonad.Actions.Navigation2D as Nav2d import qualified XMonad.Actions.Navigation2D as Nav2d
import XMonad.Actions.PhysicalScreens ( horizontalScreenOrderer )
import qualified XMonad.Hooks.EwmhDesktops as Ewmh import qualified XMonad.Hooks.EwmhDesktops as Ewmh
import qualified XMonad.Hooks.ManageHelpers as ManageHelpers import qualified XMonad.Hooks.ManageHelpers as ManageHelpers
import qualified XMonad.Layout.BoringWindows as BoringWindows import qualified XMonad.Layout.BoringWindows as BoringWindows
@ -64,6 +70,10 @@ import XMonad.Layout.IndependentScreens
import XMonad.Layout.SubLayouts import XMonad.Layout.SubLayouts
import qualified XMonad.StackSet as W import qualified XMonad.StackSet as W
import qualified XMonad.Util.XSelection as XSel import qualified XMonad.Util.XSelection as XSel
import XMonad.Util.WorkspaceCompare ( getSortByXineramaRule
, getSortByXineramaPhysicalRule
, getSortByIndex
)
import XMonad.Layout.WindowNavigation ( windowNavigation ) import XMonad.Layout.WindowNavigation ( windowNavigation )
import GHC.IO.Encoding ( setLocaleEncoding import GHC.IO.Encoding ( setLocaleEncoding
, utf8 , utf8
@ -83,6 +93,7 @@ myModMask = mod4Mask
myLauncher = Rofi.asCommand (def { Rofi.theme = Rofi.bigTheme }) ["-show run"] myLauncher = Rofi.asCommand (def { Rofi.theme = Rofi.bigTheme }) ["-show run"]
myTerminal = "alacritty" myTerminal = "alacritty"
myBrowser = "qutebrowser" myBrowser = "qutebrowser"
useSharedWorkspaces = False
--myBrowser = "google-chrome-stable" --myBrowser = "google-chrome-stable"
{-| adds the scripts-directory path to the filename of a script |-} {-| adds the scripts-directory path to the filename of a script |-}
@ -165,33 +176,30 @@ myLayout = avoidStruts
onlySpacing = gaps [ (dir, (gap*2)) | dir <- [L, R, D, U] ] -- gaps are included in mouseResizableTile onlySpacing = gaps [ (dir, (gap*2)) | dir <- [L, R, D, U] ] -- gaps are included in mouseResizableTile
dragger = let x = fromIntegral gap * 2 dragger = let x = fromIntegral gap * 2
in FixedDragger x x in FixedDragger x x
spacingAndGaps = let intGap = fromIntegral gap spacingAndGaps = let intGap = fromIntegral gap
border = Border (intGap) (intGap) (intGap) (intGap) border = Border (intGap) (intGap) (intGap) (intGap)
in spacingRaw False border True border True in spacingRaw False border True border True
-- transform a layout into supporting tabs -- transform a layout into supporting tabs
makeTabbed layout = windowNavigation $ addTabs shrinkText myTabTheme $ subLayout [] Simplest $ layout makeTabbed layout = windowNavigation $ addTabs shrinkText myTabTheme $ subLayout [] Simplest $ layout
-- }}} -- }}}
-- Startuphook ----------------------------- {{{ -- Startuphook ----------------------------- {{{
myStartupHook :: X () myStartupHook :: X ()
myStartupHook = do myStartupHook = do
spawnOnce "picom --config ~/.config/picom.conf --experimental-backends" --no-fading-openclose"
--spawnOnce "pasystray" -- just open the UI by right-clicking on polybar's pulseaudio module
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 "mailspring --background"
spawnOnce "redshift -P -O 5000"
spawn "xset r rate 300 50" -- make key repeat quicker
spawn "/home/leon/.screenlayout/dualscreen-landscape.sh"
_ <- liftIO $ Control.Concurrent.threadDelay (1000 * 10)
spawn "/home/leon/.config/polybar/launch.sh"
--spawn "feh --bg-fill /home/leon/Bilder/wallpapers/mountains_with_clounds.jpg"
spawn "nitrogen --restore"
setWMName "LG3D" -- Java stuff hack setWMName "LG3D" -- Java stuff hack
spawnOnce "picom --config ~/.config/picom.conf &" --no-fading-openclose"
--spawnOnce "pasystray" -- just open the UI by right-clicking on polybar's pulseaudio module
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 "mailspring --background &"
spawnOnce "redshift -P -O 5000 &"
spawn "xset r rate 300 50 &" -- make key repeat quicker
-- polybar and nitrogen need the screen layout to be restored fully before starting
spawn "/home/leon/.screenlayout/dualscreen-landscape.sh && /home/leon/.config/polybar/launch.sh && nitrogen --restore"
-- }}} -- }}}
@ -199,7 +207,8 @@ myStartupHook = do
-- Default mappings that need to be removed -- Default mappings that need to be removed
removedKeys :: [String] removedKeys :: [String]
removedKeys = ["M-<Tab>", "M-S-c", "M-S-q", "M-h", "M-l", "M-j", "M-k"] ++ ["M-" ++ show n | n <- [1..9 :: Int]] removedKeys = ["M-<Tab>", "M-S-c", "M-S-q", "M-h", "M-l", "M-j", "M-k"]
++ if useSharedWorkspaces then [key ++ show n | key <- ["M-", "M-S-", "M-C-"], n <- [1..9 :: Int]] else []
multiMonitorOperation :: (WorkspaceId -> WindowSet -> WindowSet) -> ScreenId -> X () multiMonitorOperation :: (WorkspaceId -> WindowSet -> WindowSet) -> ScreenId -> X ()
multiMonitorOperation operation n = do multiMonitorOperation operation n = do
@ -211,6 +220,7 @@ multiMonitorOperation operation n = do
myKeys :: [(String, X ())] myKeys :: [(String, X ())]
myKeys = myKeys =
-- ZoomRow
[ ("M-+", sendMessage zoomIn) [ ("M-+", sendMessage zoomIn)
, ("M--", sendMessage zoomOut) , ("M--", sendMessage zoomOut)
, ("M-#", sendMessage zoomReset) , ("M-#", sendMessage zoomReset)
@ -229,14 +239,14 @@ myKeys =
, ("M-C-<Tab>", onGroup W.focusUp') , ("M-C-<Tab>", onGroup W.focusUp')
, ("M-S-t", toggleTabbedLayout) , ("M-S-t", toggleTabbedLayout)
-- In tabbed mode, while focussing master pane, cycle tabs on the first slave
, ("M-S-<Tab>", do windows W.focusMaster , ("M-S-<Tab>", do windows W.focusMaster
BoringWindows.focusDown BoringWindows.focusDown
onGroup W.focusDown' onGroup W.focusDown'
windows W.focusMaster) windows W.focusMaster)
--, ("M-f", toggleFullscreen) , ("M-f", toggleFullscreen)
, ("M-f", sendMessage $ MTog.Toggle MTog.FULL)
, ("M-S-C-c", kill1) , ("M-S-C-c", kill1)
@ -259,13 +269,11 @@ myKeys =
-- programs -- programs
, ("M-p", spawn myLauncher) , ("M-p", spawn myLauncher)
, ("M-b", spawn myBrowser) , ("M-b", spawn myBrowser)
, ("M-C-p", spawn (myTerminal ++ " --class termite_floating -e fff"))
, ("M-S-p", Rofi.showCombi (def { Rofi.theme = Rofi.bigTheme }) [ "drun", "window", "ssh" ]) , ("M-S-p", Rofi.showCombi (def { Rofi.theme = Rofi.bigTheme }) [ "drun", "window", "ssh" ])
, ("M-S-e", Rofi.showNormal (def { Rofi.theme = Rofi.bigTheme }) "emoji" ) , ("M-S-e", Rofi.showNormal (def { Rofi.theme = Rofi.bigTheme }) "emoji" )
--, ("M-s", spawn $ scriptFile "rofi-search.sh") --, ("M-s", spawn $ scriptFile "rofi-search.sh")
, ("M-S-o", spawn $ scriptFile "rofi-open.sh") , ("M-S-o", spawn $ scriptFile "rofi-open.sh")
, ("M-n", scratchpadSubmap ) , ("M-n", scratchpadSubmap )
, ("M-m", mediaSubmap )
, ("M-e", Rofi.promptRunCommand def specialCommands) , ("M-e", Rofi.promptRunCommand def specialCommands)
, ("M-C-e", Rofi.promptRunCommand def =<< defaultCommands ) , ("M-C-e", Rofi.promptRunCommand def =<< defaultCommands )
, ("M-o", Rofi.promptRunCommand def withSelectionCommands) , ("M-o", Rofi.promptRunCommand def withSelectionCommands)
@ -276,15 +284,16 @@ myKeys =
generatedMappings = windowGoMappings ++ windowSwapMappings ++ resizeMappings ++ workspaceMappings generatedMappings = windowGoMappings ++ windowSwapMappings ++ resizeMappings ++ workspaceMappings
where where
workspaceMappings = workspaceMappings =
[ (mappingPrefix ++ show wspNum, if useSharedWorkspaces then [] else
do [ (mappingPrefix ++ show wspNum,
-- get all workspaces from the config by running an X action to query the config do
wsps <- workspaces' <$> asks config -- get all workspaces from the config by running an X action to query the config
windows $ onCurrentScreen action (wsps !! (wspNum - 1)) wsps <- workspaces' <$> asks config
) windows $ onCurrentScreen action (wsps !! (wspNum - 1))
| (wspNum) <- [1..9 :: Int] )
, (mappingPrefix, action) <- [("M-", W.greedyView), ("M-S-", W.shift), ("M-C-", copy)] | (wspNum) <- [1..9 :: Int]
] , (mappingPrefix, action) <- [("M-", W.greedyView), ("M-S-", W.shift), ("M-C-", copy)]
]
keyDirPairs = [("h", L), ("j", D), ("k", U), ("l", R)] keyDirPairs = [("h", L), ("j", D), ("k", U), ("l", R)]
@ -312,10 +321,8 @@ myKeys =
toggleFullscreen :: X () toggleFullscreen :: X ()
toggleFullscreen = do toggleFullscreen = do
--sendMessage ToggleLayout -- toggle fullscreen layout sendMessage $ MTog.Toggle MTog.FULL
sendMessage $ ToggleLayouts.Toggle "Full" sendMessage ToggleStruts
sendMessage ToggleStruts -- bar is hidden -> no need to make place for it
--safeSpawn "polybar-msg" ["cmd", "toggle"] -- toggle polybar visibility
scratchpadSubmap :: X () scratchpadSubmap :: X ()
@ -328,20 +335,11 @@ myKeys =
, ((myModMask, xK_d), "<M-m> discord", namedScratchpadAction scratchpads "discord") , ((myModMask, xK_d), "<M-m> discord", namedScratchpadAction scratchpads "discord")
] ]
mediaSubmap :: X ()
mediaSubmap = describedSubmap "Media"
[ ((myModMask, xK_m), "<M-m> play/pause", spawn "playerctl play-pause")
, ((myModMask, xK_l), "<M-l> next", spawn "playerctl next")
, ((myModMask, xK_l), "<M-h> previous", spawn "playerctl previous")
, ((myModMask, xK_k), "<M-k> increase volume", spawn "amixer sset Master 5%+")
, ((myModMask, xK_j), "<M-j> decrease volume", spawn "amixer sset Master 5%-")
]
withSelectionCommands :: [(String, X ())] withSelectionCommands :: [(String, X ())]
withSelectionCommands = withSelectionCommands =
[ ("Google", XSel.transformPromptSelection ("https://google.com/search?q=" ++) "qutebrowser") [ ("Google", XSel.transformPromptSelection ("https://google.com/search?q=" ++) "qutebrowser")
, ("Hoogle", XSel.transformPromptSelection ("https://hoogle.haskell.org/?hoogle=" ++) "qutebrowser") , ("Hoogle", XSel.transformPromptSelection ("https://hoogle.haskell.org/?hoogle=" ++) "qutebrowser")
, ("Translate", XSel.transformPromptSelection ("https://translate.google.com/#view=home&op=translate&sl=auto&tl=en&text=" ++) "qutebrowser") , ("Translate", XSel.transformPromptSelection ("https://translate.google.com/#view=home&op=translate&sl=auto&tl=en&text=" ++) "qutebrowser")
] ]
@ -373,13 +371,12 @@ myKeys =
myManageHook :: Query (Data.Monoid.Endo WindowSet) myManageHook :: Query (Data.Monoid.Endo WindowSet)
myManageHook = composeAll myManageHook = composeAll
[ resource =? "Dialog" --> ManageHelpers.doCenterFloat [ resource =? "Dialog" --> ManageHelpers.doCenterFloat
, appName =? "pavucontrol" --> ManageHelpers.doCenterFloat , appName =? "pavucontrol" --> ManageHelpers.doCenterFloat
, className =? "mpv" --> ManageHelpers.doRectFloat (W.RationalRect 0.9 0.9 0.1 0.1) , className =? "mpv" --> ManageHelpers.doRectFloat (W.RationalRect 0.9 0.9 0.1 0.1)
, title =? "Something" --> doFloat , title =? "Something" --> doFloat
, className =? "termite_floating" --> ManageHelpers.doRectFloat(W.RationalRect 0.2 0.2 0.6 0.6) , className =? "termite_floating" --> ManageHelpers.doRectFloat(W.RationalRect 0.2 0.2 0.6 0.6)
, className =? "bar_system_status_indicator" --> ManageHelpers.doRectFloat (W.RationalRect 0.7 0.05 0.29 0.26) , className =? "bar_system_status_indicator" --> ManageHelpers.doRectFloat (W.RationalRect 0.7 0.05 0.29 0.26)
-- , isFullscreen --> doF W.focusDown <+> doFullFloat
, manageDocks , manageDocks
, namedScratchpadManageHook scratchpads , namedScratchpadManageHook scratchpads
] ]
@ -402,7 +399,8 @@ main = do
let myConfig = desktopConfig let myConfig = desktopConfig
{ terminal = myTerminal { terminal = myTerminal
, workspaces = withScreens (fromIntegral currentScreenCount) (map show [1..6 :: Int]) , workspaces = if useSharedWorkspaces then map show [1..9 :: Int]
else withScreens (fromIntegral currentScreenCount) (map show [1..6 :: Int])
, modMask = myModMask , modMask = myModMask
, borderWidth = 2 , borderWidth = 2
, layoutHook = myLayout , layoutHook = myLayout
@ -417,6 +415,7 @@ main = do
xmonad xmonad
$ docks
$ Ewmh.ewmh $ Ewmh.ewmh
$ Nav2d.withNavigation2DConfig def { Nav2d.defaultTiledNavigation = Nav2d.sideNavigation } $ Nav2d.withNavigation2DConfig def { Nav2d.defaultTiledNavigation = Nav2d.sideNavigation }
$ myConfig $ myConfig
@ -437,28 +436,41 @@ polybarLogHook monitor = do
-- | create a polybar Pretty printer, marshalled for given monitor. -- | create a polybar Pretty printer, marshalled for given monitor.
polybarPP :: Int -> PP polybarPP :: Int -> PP
polybarPP monitor = namedScratchpadFilterOutWorkspacePP $ marshallPP (fromIntegral monitor) $ def polybarPP monitor = namedScratchpadFilterOutWorkspacePP . (if useSharedWorkspaces then id else marshallPP $ fromIntegral monitor) $ def
{ ppCurrent = withFG aqua . withMargin . const "__active__" { ppCurrent = withFG aqua . withMargin . withFont 5 . const "__active__"
, ppVisible = withFG aqua . withMargin . const "__active__" , ppVisible = withFG aqua . withMargin . withFont 5 . const "__active__"
, ppUrgent = withFG red . withMargin . const "__urgent__" , ppUrgent = withFG red . withMargin . withFont 5 . const "__urgent__"
, ppHidden = withFG gray . (\wsp -> wrapOnClickCmd ("xdotool key super+" ++ wsp) $ withMargin "__hidden__") , ppHidden = withFG gray . withMargin . withFont 5 . (`wrapClickableWorkspace` "__hidden__")
, ppHiddenNoWindows = withFG gray . (\wsp -> wrapOnClickCmd ("xdotool key super+" ++ wsp) $ withMargin "__empty__") , ppHiddenNoWindows = withFG gray . withMargin . withFont 5 . (`wrapClickableWorkspace` "__empty__")
, ppWsSep = "" , ppWsSep = ""
, ppSep = "" , ppSep = ""
, ppLayout = \l -> if l == "Tall" || l == "Horizon" , ppLayout = \l -> if l == "Tall" || l == "Horizon"
then "" then ""
else (withFG gray " | ") ++ else (withFG gray " | ") ++
(removeWords ["Minimize", "Hinted", "Spacing", "Tall"] . withFG purple . withMargin $ l) (removeWords ["Minimize", "Hinted", "Spacing", "Tall"] . withFG purple . withMargin $ l)
, ppExtras = [] , ppExtras = []
, ppTitle = const "" -- withFG aqua . (shorten 40) , ppTitle = const "" -- withFG aqua . (shorten 40)
, ppSort = if useSharedWorkspaces
then getSortByXineramaPhysicalRule horizontalScreenOrderer
else do
ws <- gets windowset
sorter <- getSortByIndex
let visibleWorkspaceTags = map (unmarshallW . W.tag . W.workspace) $ W.current ws : W.visible ws
let shouldDrop wsp = (null $ W.stack wsp) && (W.tag wsp) `notElem` visibleWorkspaceTags
return $ reverse . dropWhile shouldDrop . reverse . sorter
} }
where where
withMargin = wrap " " " " withMargin = wrap " " " "
removeWord substr = unwords . filter (/= substr) . words removeWord substr = unwords . filter (/= substr) . words
removeWords wrds str = foldr removeWord str wrds removeWords wrds str = foldr removeWord str wrds
withBG col = wrap ("%{B" ++ col ++ "}") "%{B-}" withFont fNum = wrap ("%{T" ++ show (fNum :: Int) ++ "}") "%{T}"
withFG col = wrap ("%{F" ++ col ++ "}") "%{F-}" withBG col = wrap ("%{B" ++ col ++ "}") "%{B-}"
wrapOnClickCmd command = wrap ("%{A1:" ++ command ++ ":}") "%{A}" withFG col = wrap ("%{F" ++ col ++ "}") "%{F-}"
wrapOnClickCmd command = wrap ("%{A1:" ++ command ++ ":}") "%{A}"
wrapClickableWorkspace wsp = wrapOnClickCmd ("xdotool key super+" ++ wsp)
correctlyOrderedXineramaSort = do xineramaSort <- getSortByXineramaRule
return (\wsps -> let (x:xs) = xineramaSort wsps
in [head xs, x] ++ tail xs)
-- }}} -- }}}