45 KiB
Xmonad Config
- XMonad Config
- XMobar Config
- Nix Integration
XMonad Config
The main configuration file for XMonad is ~/.xmonad/xmonad.hs.
Imports
First I import a bunch of libraries:
-- IMPORTS
import qualified Data.Map as M
import Control.Monad as C
import Data.List
import Data.Monoid
import Data.Maybe (fromJust)
import Graphics.X11.ExtraTypes.XF86
import System.Exit
import System.IO
import XMonad
import XMonad.Actions.Navigation2D
import XMonad.Actions.SpawnOn
import XMonad.Actions.TiledWindowDragging
import XMonad.Actions.Warp
import XMonad.Actions.WindowNavigation
import XMonad.Actions.WithAll
import XMonad.Hooks.DynamicLog
import qualified XMonad.Hooks.EwmhDesktops as EWMHD
import XMonad.Hooks.FadeWindows
import XMonad.Hooks.ManageDocks
import XMonad.Hooks.RefocusLast
import XMonad.Hooks.ServerMode
import XMonad.Hooks.StatusBar
import XMonad.Hooks.StatusBar.PP
import XMonad.Layout.DraggingVisualizer
import XMonad.Layout.Dwindle
import XMonad.Layout.Fullscreen
import XMonad.Layout.Gaps
import XMonad.Layout.LayoutHints
import XMonad.Layout.LimitWindows
import XMonad.Layout.MouseResizableTile
import XMonad.Layout.Spacing
import XMonad.ManageHook
import qualified XMonad.StackSet as W
--import qualified DBus as D
--import qualified DBus.Client as D
import XMonad.Util.NamedScratchpad
import XMonad.Util.Run
import XMonad.Util.SpawnOnce
Theme Setup
Color Variable Declarations
Next, I declare some variables necessary to setup 16-bit color schemes.
-- CUSTOM COLORS
colorSchemeList, colorSchemePrettyList :: [String]
colorBgNormalList, colorFgNormalList :: [String]
color01NormalList, color01BrightList, color02NormalList, color02BrightList :: [String]
color03NormalList, color03BrightList, color04NormalList, color04BrightList :: [String]
color05NormalList, color05BrightList, color06NormalList, color06BrightList :: [String]
color07NormalList, color07BrightList, color08NormalList, color08BrightList :: [String]
colorFocusList, colorSecondaryList :: [String]
colorScheme, colorSchemePretty :: String
colorBgNormal, colorFgNormal :: [Char]
color01Normal, color01Bright, color02Normal, color02Bright :: String
color03Normal, color03Bright, color04Normal, color04Bright :: String
color05Normal, color05Bright, color06Normal, color06Bright :: String
color07Normal, color07Bright, color08Normal, color08Bright :: String
colorFocus, colorSecondary :: String
gtkTheme :: String
alacrittyTheme :: String
doomEmacsTheme :: String
Color Scheme Enumerations
Next, I enumerate each color scheme as an integer.
gruvboxIndex, solarizedIndex, draculaIndex, tomorrowNightIndex, tokyoNightIndex, oceanicNextIndex, ubuntuIndex :: Int
gruvboxIndex = 0
solarizedIndex = 1
draculaIndex = 2
tomorrowNightIndex = 3
tokyoNightIndex = 4
oceanicNextIndex = 5
oldHopeIndex = 6
ubuntuIndex = 7
Color Scheme Arrays
Each enumerated color scheme from before corresponds to the index of the following arrays. These arrays are then used as the basis to select a color scheme.
-- color scheme arrays
colorSchemeList = ["gruvbox", "solarized", "dracula", "tomorrow-night", "tokyo-night", "oceanic-next", "old-hope"]
colorSchemePrettyList = ["Gruvbox Dark", "Solarized Dark", "Dracula", "Tomorrow Night", "Tokyo Night", "Oceanic Next", "Old Hope"]
gtkThemeList = ["MyGruvbox", "", "OfficialDracula", "MyGraphite", "", "MyOceanicNext", "SweetDark"] -- names of corresponding gtk themes
alacrittyThemeList = ["gruvbox_dark", "solarized_dark", "dracula", "tomorrow_night", "tokyo_night", "oceanic_next", "old_hope"]
doomEmacsThemeList = ["doom-gruvbox", "doom-solarized-dark", "doom-dracula", "doom-tomorrow-night", "doom-tokyo-night", "doom-oceanic-next", "doom-old-hope"]
colorBgNormalList = ["#282828", "#002b36", "#282a36", "#1d1f21", "#1a1b26", "#1b2b34", "#1c1d21"] -- normal bg
colorBgBrightList = ["#3b3838", "#113b3f", "#36343f", "#3d3f41", "#2a2b36", "#2b3b41", "#3c3d41"] -- lighter bg
trayerBgNormalList = ["0x00282828", "0x00002b36", "0x00282a36", "0x1d1f21", "0x1a1b26", "0x1b2b34", "0x1c1d21"] -- trayer tint
colorFgNormalList = ["#ebdbb2", "#839496", "#f8f8f2", "#c5c8c6", "#a9b1d6", "#d8dee9", "#cbcdd2"] -- normal fg
color01NormalList = ["#343428", "#073642", "#000000", "#1d1f21", "#32344a", "#29414f", "#45474f"] -- black
color01BrightList = ["#928374", "#002b36", "#555555", "#666666", "#444b6a", "#405860", "#65676f"] -- bright black
color02NormalList = ["#cc241d", "#dc3ddf", "#ff5555", "#cc6666", "#f7768e", "#ec5f67", "#eb3d54"] -- red
color02BrightList = ["#fb4934", "#cb4b16", "#ff1010", "#ff3334", "#ff7a93", "#ff3130", "#eb3d54"] -- bright red
color03NormalList = ["#98971a", "#859900", "#50fa7b", "#b5bd68", "#9ece6a", "#99c794", "#78bd65"] -- green
color03BrightList = ["#b8bb26", "#586e75", "#02fe03", "#9ec400", "#b9f27c", "#66fa56", "#78bd65"] -- bright green
color04NormalList = ["#d79921", "#b58900", "#f1fa8c", "#e6c547", "#e0af68", "#fac863", "#e5cd52"] -- yellow
color04BrightList = ["#fabd2f", "#657b83", "#ffff02", "#f0c674", "#ff9e64", "#ffca4f", "#e5cd52"] -- bright yellow
color05NormalList = ["#458588", "#268bd2", "#bd93f9", "#81a2be", "#7aa2f7", "#6699cc", "#4fb4d8"] -- blue
color05BrightList = ["#83a598", "#839496", "#4d31fd", "#81a2be", "#7da6ff", "#4477ee", "#4fb4d8"] -- bright blue
color06NormalList = ["#b16286", "#d33682", "#ff79c6", "#b29fbb", "#ad8ee6", "#c594c5", "#ef7c2a"] -- magenta
color06BrightList = ["#d3869b", "#6c71c4", "#ff20d8", "#b77ee0", "#bb9af7", "#d864d8", "#ef7c2a"] -- bright magenta
color07NormalList = ["#689d6a", "#2aa198", "#8be9fd", "#70c0ba", "#449dab", "#5fb3b3", "#4fb4d8"] -- cyan
color07BrightList = ["#8ec07c", "#93a1a1", "#03feff", "#54ced6", "#0db9d7", "#30d2d0", "#4fb4d8"] -- bright cyan
color08NormalList = ["#a89984", "#eee8d5", "#bbbbbb", "#676b71", "#787c99", "#65737e", "#cbcdd2"] -- white
color08BrightList = ["#ebdbb2", "#fdf6e3", "#ffffff", "#787a7e", "#acb0d0", "#d8dee9", "#cbcdd2"] -- bright white
colorFocusList = ["#458588", "#859900", "#ff79c6", "#e6c547", "#ff9e64", "#c594c5", "#eb3d54"] -- focus and run launcher color
colorSecondaryList = ["#d79921", "#dc3ddf", "#bbbbbb", "#70c0ba", "#0db9d7", "#fac863", "#4fb4d8"] -- secondary color
Choose Theme
A theme is selected by setting the myColorScheme
variable to one of the color schemes enumerated before.
-- choose a color scheme
myColorScheme = oldHopeIndex
Setup Color Variables
-- setup color variables
colorScheme = colorSchemeList !! myColorScheme
colorSchemePretty = colorSchemePrettyList !! myColorScheme
gtkTheme = gtkThemeList !! myColorScheme
alacrittyTheme = alacrittyThemeList !! myColorScheme
doomEmacsTheme = doomEmacsThemeList !! myColorScheme
colorBgNormal = colorBgNormalList !! myColorScheme -- normal bg
colorBgBright = colorBgBrightList !! myColorScheme -- lighter bg
trayerBgNormal = trayerBgNormalList !! myColorScheme -- trayer tint
colorFgNormal = colorFgNormalList !! myColorScheme -- normal fg
color01Normal = color01NormalList !! myColorScheme -- black
color01Bright = color01BrightList !! myColorScheme -- bright black
color02Normal = color02NormalList !! myColorScheme -- red
color02Bright = color02BrightList !! myColorScheme -- bright red
color03Normal = color03NormalList !! myColorScheme -- green
color03Bright = color03BrightList !! myColorScheme -- bright green
color04Normal = color04NormalList !! myColorScheme -- yellow
color04Bright = color04BrightList !! myColorScheme -- bright yellow
color05Normal = color05NormalList !! myColorScheme -- blue
color05Bright = color05BrightList !! myColorScheme -- bright blue
color06Normal = color06NormalList !! myColorScheme -- magenta
color06Bright = color06BrightList !! myColorScheme -- bright magenta
color07Normal = color07NormalList !! myColorScheme -- cyan
color07Bright = color07BrightList !! myColorScheme -- bright cyan
color08Normal = color08NormalList !! myColorScheme -- white
color08Bright = color08BrightList !! myColorScheme -- bright white
colorFocus = colorFocusList !! myColorScheme -- focus and run launcher color
colorSecondary = colorSecondaryList !! myColorScheme
Settings
Border Color
-- Border colors for unfocused and focused windows, respectively.
myNormalBorderColor, myFocusedBorderColor :: String
myNormalBorderColor = colorBgNormal
myFocusedBorderColor = colorFocus
Default Apps
-- Default apps
myTerminal, myBrowser :: String
myTerminal = "alacritty -o font.size=20"
myBrowser = "librewolf"
Mouse Focus
-- Whether focus follows the mouse pointer.
myFocusFollowsMouse :: Bool
myFocusFollowsMouse = False
-- Whether clicking on a window to focus also passes the click to the window
myClickJustFocuses :: Bool
myClickJustFocuses = False
Border Width
-- Width of the window border in pixels.
myBorderWidth :: Dimension
myBorderWidth = 3
Select Modkey
The default modkey is mod1Mask
which is bound to left alt. mod3Mask
can be used for right alt, but most people (including myself) simply use mod4Mask
which is bound to the super key.
-- Modmask
myModMask :: KeyMask
myModMask = mod4Mask
Workspaces
By default, workspaces are simply numeric strings ("1", "2", "3", etc..), but any strings can be used (i.e. "web", "irc", "code", etc..). I set workspace names with <fn=1>\x____</fn> where the blank spaces represent a nerd font symbol code. This works nicely because I have a Nerd Font as fn=1 in my xmobar, which renders the nerd font glyphs in xmobar.
myWorkspaces :: [String]
myWorkspaces =
[ "<fn=1>\xf15c¹</fn>", -- document icon for writing
"<fn=1>\xeb01 ²</fn>", -- globe icon for browsing
"<fn=1>\xf121³</fn>", -- dev icon for programming
"<fn=1>\xf0cb9 ⁴</fn>", -- music file icon for composition
"<fn=1>\xf1fc⁵</fn>", -- paint icon for art
"<fn=1>\xf0bdc ⁶</fn>", -- video icon for recording/editing
"<fn=1>\xf0d6⁷</fn>", -- money icon for finances
"<fn=1>\xf19d⁸</fn>", -- cap icon for teaching
"<fn=1>\xf11b⁹</fn>" -- gamepad icon for gaming
]
myWorkspaceIndices = M.fromList $ zipWith (,) myWorkspaces [1..] -- (,) == \x y -> (x,y)
clickable ws = "<action=xdotool key super+"++show i++">"++ws++"</action>"
where i = fromJust $ M.lookup ws myWorkspaceIndices
Scratchpads
Scratchpads are single applications that are normally not visible (in a workspace called "NSP"), but can be brought into the current workspace with a quick keybind. I find that this works really well for applications I use frequently for quick tasks, such as my terminal, password manager, email, and music player.
-- Scratchpads
myScratchPads :: [NamedScratchpad]
myScratchPads =
[ NS "terminal" spawnTerm findTerm manageTerm,
NS "ranger" spawnRanger findRanger manageRanger,
NS "octave" spawnOctave findOctave manageOctave,
NS "btm" spawnBtm findBtm manageBtm,
NS "geary" spawnGeary findGeary manageGeary,
NS "helpmenu" spawnHelp findHelp manageHelp,
NS "cmus" spawnCmus findCmus manageCmus,
NS "cal" spawnCal findCal manageCal,
NS "pavucontrol" spawnPavucontrol findPavucontrol managePavucontrol,
NS "discord" spawnDiscord findDiscord manageDiscord
]
where
spawnTerm = myTerminal ++ " --title scratchpad"
findTerm = title =? "scratchpad"
manageTerm = customFloating $ W.RationalRect l t w h
where
h = 0.9
w = 0.9
t = 0.95 - h
l = 0.95 - w
--spawnRanger = myTerminal ++ " --title ranger-scratchpad -e ranger"
spawnRanger = "kitty --title ranger-scratchpad -e ranger"
findRanger = title =? "ranger-scratchpad"
manageRanger = customFloating $ W.RationalRect l t w h
where
h = 0.9
w = 0.9
t = 0.95 - h
l = 0.95 - w
spawnOctave = myTerminal ++ " --title octave-scratchpad -e octave"
findOctave = title =? "octave-scratchpad"
manageOctave = customFloating $ W.RationalRect l t w h
where
h = 0.5
w = 0.4
t = 0.75 - h
l = 0.70 - w
spawnBtm = myTerminal ++ " -o font.size=12 --title btm-scratchpad -e btm"
findBtm = title =? "btm-scratchpad"
manageBtm = customFloating $ W.RationalRect l t w h
where
h = 0.5
w = 0.4
t = 0.75 - h
l = 0.70 - w
spawnDiscord = "flatpak run com.discordapp.Discord"
findDiscord = className =? "discord"
manageDiscord = customFloating $ W.RationalRect l t w h
where
h = 0.5
w = 0.4
t = 0.75 - h
l = 0.70 - w
spawnGeary = "geary"
findGeary = className =? "Geary"
manageGeary = customFloating $ W.RationalRect l t w h
where
h = 0.5
w = 0.4
t = 0.75 - h
l = 0.70 - w
spawnHelp = myTerminal ++ " --title xmonad_helpmenu -e w3m ~/.xmonad/helpmenu.txt"
findHelp = title =? "xmonad_helpmenu"
manageHelp = customFloating $ W.RationalRect l t w h
where
h = 0.9
w = 0.9
t = 0.95 - h
l = 0.95 - w
spawnCmus = myTerminal ++ " -o font.size=28 --title cmus-scratchpad -e cmus && cmus-remote -R && cmus-remote -S"
findCmus = title =? "cmus-scratchpad"
manageCmus = customFloating $ W.RationalRect l t w h
where
h = 0.9
w = 0.9
t = 0.95 - h
l = 0.95 - w
spawnCal = "alacritty -o font.size=18 --title cal-scratchpad -e calcurse"
findCal = title =? "cal-scratchpad"
manageCal = customFloating $ W.RationalRect l t w h
where
h = 0.6
w = 0.6
t = 0.65 - h
l = 1 - w
spawnPavucontrol = "pavucontrol"
findPavucontrol = className =? "Pavucontrol"
managePavucontrol = customFloating $ W.RationalRect l t w h
where
h = 0.5
w = 0.3
t = 0.9 - h
l = 0.65 - w
Keybindings
Keybinds can be set with an array of values like: (keybind, action)
. The array is declared like so:
myKeys conf@(XConfig {XMonad.modMask = modm}) =
M.fromList $
[
-- insert keybinds with array values of ((keybind, action))
Then, keybindings are setup line by line as in the following sections:
Quick App Keybindings
The following binds the following:
Keybinding | Action |
---|---|
S-Return | New terminal |
S-a | New emacs frame |
S-s | New browser window |
PrintScreen | Snip a screenshot |
C-PrintScreen | Snip a screenshot (to clipboard) |
Shift-PrintScreen | Screen capture current monitor |
Shift-C-PrintScreen | Screen capture current monitor (to clipboard) |
-- launch a terminal
((modm, xK_Return), spawn $ XMonad.terminal conf),
-- launch emacsclient
((modm, xK_a), spawn "emacsclient -c -a 'emacs'"),
-- launch browser
((modm, xK_s), spawn myBrowser),
-- take screenshots
((0, xK_Print), spawn "flameshot gui"), -- snip screenshot and save
((controlMask, xK_Print), spawn "flameshot gui --clipboard"), -- snip screenshot to clipboard
((shiftMask, xK_Print), spawn "flameshot screen"), -- screen capture current monitor and save
((controlMask .|. shiftMask, xK_Print), spawn "flameshot screen -c"), -- screen capture current monitor to clipboard
-- launch game manager in gaming workspace
((modm, xK_g), spawn "xdotool key Super+9 && gamehub"),
Generic Keybindings
These setup standard bindings for brightness and audio control from the keyboard.
-- control brightness from kbd
((0, xF86XK_MonBrightnessUp), spawn "brightnessctl set +15"),
((0, xF86XK_MonBrightnessDown), spawn "brightnessctl set 15-"),
-- control kbd brightness from kbd
((0, xF86XK_KbdBrightnessUp), spawn "brightnessctl --device='asus::kbd_backlight' set +1 & xset r rate 350 100"),
((0, xF86XK_KbdBrightnessDown), spawn "brightnessctl --device='asus::kbd_backlight' set 1- & xset r rate 350 100"),
((shiftMask, xF86XK_MonBrightnessUp), spawn "brightnessctl --device='asus::kbd_backlight' set +1 & xset r rate 350 100"),
((shiftMask, xF86XK_MonBrightnessDown), spawn "brightnessctl --device='asus::kbd_backlight' set 1- & xset r rate 350 100"),
-- control volume from kbd
((0, xF86XK_AudioLowerVolume), spawn "pamixer -d 10"),
((0, xF86XK_AudioRaiseVolume), spawn "pamixer -i 10"),
((0, xF86XK_AudioMute), spawn "pamixer -t"),
-- control music from kbd
((0, xF86XK_AudioPlay), spawn "cmus-remote -u"),
((0, xF86XK_AudioStop), spawn "cmus-remote -s"),
((0, xF86XK_AudioNext), spawn "cmus-remote -n && ~/.local/bin/cmus-current-song-notify.sh"),
((0, xF86XK_AudioPrev), spawn "cmus-remote -r && ~/.local/bin/cmus-current-song-notify.sh"),
-- manage multiple monitors with kbd
-- ((0, xF86XK_Explorer), spawn "/home/librephoenix/.local/bin/setup_external_monitor.sh"),
-- ((0, xK_F8), spawn "/home/librephoenix/.local/bin/setup_external_monitor.sh"),
Launcher Keybinds
I have rofi
bound to S-;
for quick app access.
-- launch dmenu
--((modm, xK_semicolon), spawn ("dmenu_run -nb '" ++ colorBgNormal ++ "' -nf '" ++ color08Bright ++ "' -sb '" ++ colorFocus ++ "' -sf '" ++ color08Bright ++ "' -fn 'UbuntuMono-R:regular:pixelsize=28' -l 4 -p '➤'")),
((modm, xK_semicolon), spawn ("rofi -show drun -show-icons")),
((modm, xK_p), spawn ("keepmenu")),
Window Management Keybinds
All of the following keybinds pertain to window management and layouts:
Keybinding | Action |
---|---|
S-q | Kill window |
S-Shift-c | Kill all windows on current workspace |
S-Shift-q | Exit xmonad |
S-Shift-Escape | Lock xmonad |
S-Shift-s | Lock xmonad and suspend |
S-Shift-Escape | Lock xmonad and suspend |
S-Space | Switch to next layout |
S-Shift-Space | Reset layout on current workspace |
S-r | Resize windows to correct size |
S-{←,↓,↑,→} | Switch to screen visually {left,down,up,right} (requires a Navigation2Dconfig) |
S-{h,j,k,l} | Switch to window visually {left,down,up,right} (requires a Navigation2Dconfig) |
S-Shift-{h,j,k,l} | Swap window visually {left,down,up,right} on current workspace (requires a Navigation2Dconfig) |
S-C-{h,l} | Resize master window area |
S-m | Move current window into master window area |
S-t | Toggle floating status of a window (this is a function defined here) |
S-, | Increase number of windows in the master window area |
S-. | Decrease number of windows in the master window area |
These keybindings are then set via:
-- close focused window
((modm, xK_q), kill),
-- close all windows on current workspace
((modm .|. shiftMask, xK_c), killAll),
-- exit xmonad
((modm .|. shiftMask, xK_q), spawn "killall xmonad-x86_64-linux"),
-- Lock with dm-tool
((modm, xK_Escape), spawn "dm-tool switch-to-greeter"),
-- Lock with dm-tool and suspend
((modm .|. shiftMask, xK_s), spawn "dm-tool switch-to-greeter & systemctl suspend"),
((modm .|. shiftMask, xK_Escape), spawn "dm-tool switch-to-greeter & systemctl suspend"),
-- Rotate through the available layout algorithms
((modm, xK_space), sendMessage NextLayout),
-- Reset the layouts on the current workspace to default
((modm .|. shiftMask, xK_space), setLayout $ XMonad.layoutHook conf),
-- Resize viewed windows to the correct size
((modm, xK_r), refresh),
-- Move focus to window below
((modm, xK_j), C.sequence_ [windowGo D True, switchLayer, warpToWindow 0.5 0.5]),
-- Move focus to window above
((modm, xK_k), C.sequence_ [windowGo U True, switchLayer, warpToWindow 0.5 0.5]),
-- Move focus to window left
((modm, xK_h), C.sequence_ [windowGo L True, switchLayer, warpToWindow 0.5 0.5]),
-- Move focus to window right
((modm, xK_l), C.sequence_ [windowGo R True, switchLayer, warpToWindow 0.5 0.5]),
-- Move focus to screen below
((modm, xK_Down), C.sequence_ [screenGo D True, warpToCurrentScreen 0.5 0.5]),
-- Move focus to screen up
((modm, xK_Up), C.sequence_ [screenGo U True, warpToCurrentScreen 0.5 0.5]),
-- Move focus to screen left
((modm, xK_Left), C.sequence_ [screenGo L True, warpToCurrentScreen 0.5 0.5]),
-- Move focus to screen right
((modm, xK_Right), C.sequence_ [screenGo R True, warpToCurrentScreen 0.5 0.5]),
-- Swap with window below
((modm .|. shiftMask, xK_j), C.sequence_ [windowSwap D True, windowGo U True, switchLayer]),
-- Swap with window above
((modm .|. shiftMask, xK_k), C.sequence_ [windowSwap U True, windowGo D True, switchLayer]),
-- Swap with window left
((modm .|. shiftMask, xK_h), C.sequence_ [windowSwap L True, windowGo R True, switchLayer]),
-- Swap with window right
((modm .|. shiftMask, xK_l), C.sequence_ [windowSwap R True, windowGo L True, switchLayer]),
-- Shrink the master area
((modm .|. controlMask, xK_h), sendMessage Shrink),
-- Expand the master area
((modm .|. controlMask, xK_l), sendMessage Expand),
-- Swap the focused window and the master window
((modm, xK_m), windows W.swapMaster),
-- Toggle tiling/floating status of window
((modm, xK_t), withFocused toggleFloat),
-- Increment the number of windows in the master area
((modm, xK_comma), sendMessage (IncMasterN 1)),
-- Deincrement the number of windows in the master area
((modm, xK_period), sendMessage (IncMasterN (-1))),
Scratchpad Keybinds
I have each scratchpad bound to a keybinding for quick access:
Keybinding | Associated Scratchpad |
---|---|
S-f | Ranger file manager |
S-x | KeePassXC password manager |
S-z | Terminal |
S-b | Bottom control panel |
S-d | Discord |
S-o | Octave (calculator) |
S-e | mu4e (email) |
S-n | Music player |
S-c | cfw (calendar) |
S-y | Pavucontrol (audio mixer) |
S-/ | Keybinding help menu |
These are then bound:
-- scratchpad keybindings
((modm, xK_f), namedScratchpadAction myScratchPads "ranger"),
--((modm, xK_x), namedScratchpadAction myScratchPads "keepassxc"),
((modm, xK_z), namedScratchpadAction myScratchPads "terminal"),
((modm, xK_b), namedScratchpadAction myScratchPads "btm"),
((modm, xK_d), namedScratchpadAction myScratchPads "discord"),
((modm, xK_o), namedScratchpadAction myScratchPads "octave"),
((modm, xK_e), namedScratchpadAction myScratchPads "geary"),
((modm, xK_n), namedScratchpadAction myScratchPads "cmus"),
((modm, xK_c), namedScratchpadAction myScratchPads "cal"),
((modm, xK_y), namedScratchpadAction myScratchPads "pavucontrol"),
((modm, xK_slash), namedScratchpadAction myScratchPads "helpmenu")
End of Standard Keybinds
To finish the section of standard keybinds, we simply close the array started above.
]
Workspace Management Keybinds
Workspaces are generically managed via mod-[1..9]
to shift to a workspace, and mod-shift-[1..9]
to send a window to another workspace. To generate this effect, the following code is added to the keybindings definition:
++
-- mod-[1..9], Switch to workspace N
-- mod-shift-[1..9], Move client to workspace N
[ ((m .|. modm, k), windows $ f i)
| (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9],
(f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]
]
Custom Function Definitions
To have toggleFloat
and warpToCurrentScreen
, I must define them after setting up the keybinds like so:
where
-- toggle float/tiling status of current window
toggleFloat w =
windows
( \s ->
if M.member w (W.floating s)
then W.sink w s
else (W.float w (W.RationalRect (1 / 8) (1 / 8) (3 / 4) (3 / 4)) s)
)
-- warp cursor to (x, y) coordinate of current screen
warpToCurrentScreen x y = do
sid <- withWindowSet $ return . W.screen . W.current
warpToScreen sid x y
-- TODO goto and warp (coords x, y) to window in DIRECTION, or goto and warp (coords x, y) to screen in DIRECTION if no window is available
windowOrScreenGoAndWarp direction x y =
do windowGo direction True
Mouse Bindings
The following code sets up some convenient mouse bindings:
Mouse Binding | Action |
---|---|
S-Left click | Make window floating and drag to move window |
S-Right click | Make window floating and resize window |
-- Mouse bindings: default actions bound to mouse events
myMouseBindings (XConfig {XMonad.modMask = modm}) =
M.fromList $
-- -- mod-button1, Set the window to floating mode and move by dragging
[ ( (modm, button1),
( \w ->
focus w
>> mouseMoveWindow w
>> windows W.shiftMaster
)
),
-- mod-button3, Set the window to floating mode and resize by dragging
( (modm, button3),
( \w ->
focus w
>> mouseResizeWindow w
>> windows W.shiftMaster
)
)
-- you may also bind events to the mouse scroll wheel (button4 and button5)
]
Layouts
By default, I utilize three layouts:
mouseResizable
which is a master/stack layout I have set up to have dwindling sizesmouseResizableMirrored
, same as above except mirroredFull
where only one window takes up the entire space of the screen
I embellish these layouts with a few modifiers:
fullscreenFocus
for fullscreen support (also requires a fullscreen manage hook)draggingVisualizer
so that I can drag tiling windows about via my mouse bindingsavoidStruts
since I use xmobarspacingRaw
to put a few pixels of space between windows since it looks nice
This is all applied in the following code to set the myLayout
variable, which gets used later in the main function:
-- Layouts:
spcPx = 5
mySpacing = spacingRaw False (Border spcPx spcPx spcPx spcPx) True (Border spcPx spcPx spcPx spcPx) True
myLayout = fullscreenFocus $ draggingVisualizer $ avoidStruts $ layoutHintsToCenter $ (mySpacing $ (Full ||| mouseResizable ||| mouseResizableMirrored))
where
-- default tiling algorithm partitions the screen into two panes
tiled = Tall 1 (5 / 100) (1 / 2)
dwindled = Dwindle R CW 1.1 1.1
mouseResizable =
mouseResizableTile
{ masterFrac = 0.51,
slaveFrac = 0.51,
draggerType = BordersDragger
}
mouseResizableMirrored =
mouseResizableTile
{ masterFrac = 0.51,
slaveFrac = 0.51,
draggerType = BordersDragger,
isMirrored = True
}
Window Rules and Hooks
Window rules apply actions when a new window matching a specific query is apprehended by xmonad. I mainly use these to control my scratchpads (to make them all floating) and for some apps that don't behave nicely inside of a tiling window manager.
The easiest way to do a query is by either className
or title
which can both be found using xprop
.
The list of window rules must be made into a manage hook, which gets used in the main function when starting xmonad.
-- Window rules:
myManageHook =
composeAll
[ title =? "Myuzi" --> (customFloating $ W.RationalRect 0.05 0.05 0.9 0.9),
title =? "octave-scratchpad" --> (customFloating $ W.RationalRect 0.1 0.1 0.8 0.8),
title =? "scratchpad" --> (customFloating $ W.RationalRect 0.1 0.1 0.8 0.8),
className =? "discord" --> (customFloating $ W.RationalRect 0.1 0.1 0.8 0.8),
title =? "ranger-scratchpad" --> (customFloating $ W.RationalRect 0.05 0.05 0.9 0.9),
title =? "btm-scratchpad" --> (customFloating $ W.RationalRect 0.1 0.1 0.8 0.8),
className =? "Geary" --> (customFloating $ W.RationalRect 0.05 0.05 0.9 0.9),
title =? "scratch_cfw" --> (customFloating $ W.RationalRect 0.58 0.04 0.42 0.7),
title =? "xmonad_helpmenu" --> (customFloating $ W.RationalRect 0.05 0.05 0.9 0.9),
className =? "Pavucontrol" --> (customFloating $ W.RationalRect 0.05 0.04 0.5 0.35),
className =? "Syncthing GTK" --> (customFloating $ W.RationalRect 0.53 0.50 0.46 0.45),
className =? "Proton Mail Bridge" --> (customFloating $ W.RationalRect 0.59 0.66 0.40 0.30),
className =? "Zenity" --> (customFloating $ W.RationalRect 0.45 0.4 0.1 0.2),
resource =? "desktop_window" --> doIgnore,
-- this gimp snippet is from Kathryn Anderson (https://xmonad.haskell.narkive.com/bV34Aiw3/layout-for-gimp-how-to)
(className =? "Gimp" <&&> fmap ("color-selector" `isSuffixOf`) role) --> doFloat,
(className =? "Gimp" <&&> fmap ("layer-new" `isSuffixOf`) role) --> doFloat,
(className =? "Gimp" <&&> fmap ("-dialog" `isSuffixOf`) role) --> doFloat,
(className =? "Gimp" <&&> fmap ("-tool" `isSuffixOf`) role) --> doFloat,
-- end snippet
resource =? "kdesktop" --> doIgnore,
manageDocks
]
where role = stringProperty "WM_WINDOW_ROLE"
I also must set my fullscreen manage hook and fullscreen event hook here to fully enable fullscreen support mentioned earlier:
-- Apply fullscreen manage and event hooks
myFullscreenManageHook = fullscreenManageHook
myFullscreenEventHook = fullscreenEventHook
Next, I set up my event hook to put xmonad into server mode, which allows me to use xmonadctl from xmonad-contrib, which enables control of xmonad actions from the shell/scripts.
-- Server mode event hook
myEventHook = serverModeEventHook
Next I set up a navigation2DConfig
for use with visual window movement:
-- navigation 2d config required for visual window movement
myNavigation2DConfig = def {layoutNavigation = [("Tall", hybridOf sideNavigation $ hybridOf centerNavigation lineNavigation), ("Full", hybridOf sideNavigation centerNavigation)]
, floatNavigation = hybridOf lineNavigation centerNavigation
, screenNavigation = hybridOf lineNavigation centerNavigation}
Startup Script
I have a startup script at ~/.xmonad/startup.sh
which starts various apps and sets up a few things. The script starts by taking some passed color values as strings from xmonad, which are set in the beginning of the config.
#!/bin/sh
trayertint=$1
nbColor=$2
nfColor=$3
sbColor=$4
sfColor=$5
themeGTKName=$6
themeAlacrittyName=$7
themeDoomEmacsName=$8
colorBgNormal=$2
colorBgBright=${27}
colorFgNormal=$3
color01Normal=$9
color01Bright=${10}
color02Normal=${11}
color02Bright=${12}
color03Normal=${13}
color03Bright=${14}
color04Normal=${15}
color04Bright=${16}
color05Normal=${17}
color05Bright=${18}
color06Normal=${19}
color06Bright=${20}
color07Normal=${21}
color07Bright=${22}
color08Normal=${23}
color08Bright=${24}
colorFocus=${25}
colorSecondary=${26}
In my xmonad config, it is then autostarted by setting a startupHook
. Inside my startup hook, I pass the colors of my currently selected theme to the script:
-- Startup hook
myStartupHook = do
spawnOnce ("~/.config/xmonad/startup.sh '" ++ trayerBgNormal ++ "' '" ++ colorBgNormal ++ "' '" ++ color08Bright ++ "' '" ++ colorFocus ++ "' '" ++ color08Bright ++ "' '" ++ gtkTheme ++ "' '" ++ alacrittyTheme ++ "' '" ++ doomEmacsTheme ++ "' '" ++ color01Normal ++ "' '" ++ color01Bright ++ "' '" ++ color02Normal ++ "' '" ++ color02Bright ++ "' '" ++ color03Normal ++ "' '" ++ color03Bright ++ "' '" ++ color04Normal ++ "' '" ++ color04Bright ++ "' '" ++ color05Normal ++ "' '" ++ color05Bright ++ "' '" ++ color06Normal ++ "' '" ++ color06Bright ++ "' '" ++ color07Normal ++ "' '" ++ color07Bright ++ "' '" ++ color08Normal ++ "' '" ++ color08Bright ++ "' '" ++ colorFocus ++ "' '" ++ colorSecondary ++ "' '" ++ colorBgBright ++ "'")
The autostart script kills all applications I am autostarting, which prevents multiple instances of background applications when I restart xmonad:
# Startup shell script called by xmonad to start necessary programs
#
## Kill previous instances of applications (Prevents multiple instances of the following if XMonad is restarted durin the X session)
killall xmobar
killall twmnd
killall trayer
killall nm-applet
killall nextcloud
killall xautolock
killall caffeine
killall syncthing-gtk
killall discord
killall qjoypad
Then, a few things are set up before starting any applications, including the dpi, compositor, keyboard, and environment variables.
# pre-launch configurations
# dbus-update-activation-environment --all &
# ~/.local/bin/setup-external-monitor.sh &
# picom --experimental-backends &
picom --animations --animation-window-mass 1 --animation-for-open-window zoom --animation-stiffness 200 --experimental-backends && # requires picom-pijulius
xset r rate 350 50 &
setxkbmap -option caps:escape &
# betterdiscordctl --d-install flatpak install &
# setup necessary environment variables
# export QT_QPA_PLATFORMTHEME="qt5ct" &
# export GTK_THEME=$themeGTKName
Next, the color themes for various applications are set in configuration files using sed.
sed -i 's/background_color=.*/background_color='$nbcolor'/' ~/.config/twmn/twmn.conf &
sed -i 's/foreground_color=.*/foreground_color='$sbcolor'/' ~/.config/twmn/twmn.conf &
sed -i 's/colors: .*/colors: *'$themeAlacrittyName'/' ~/.config/alacritty/alacritty.yml &
sed -i 's/colors: .*/colors: *'$themeAlacrittyName'/' ~/.config/alacritty/alacritty.org &
sed -i "s/(setq doom-theme .*/(setq doom-theme '"$themeDoomEmacsName")/" ~/.doom.d/config.el &
sed -i "s/(setq doom-theme .*/(setq doom-theme '"$themeDoomEmacsName")/" ~/.doom.d/doom.org &
sed -i "s/(setq doom-theme .*/(setq doom-theme '"$themeDoomEmacsName")/" ~/.doom.d/doom-pub.org &
cp -f ~/.config/xmobar/base-xmobarrc ~/.config/xmobar/xmobarrc &&
sed -i "s/colorBgNormal/"$colorBgNormal"/g" ~/.config/xmobar/xmobarrc # normal background
sed -i "s/colorBgBright/"$colorBgBright"/g" ~/.config/xmobar/xmobarrc # bright background
sed -i "s/colorFgNormal/"$colorFgNormal"/g" ~/.config/xmobar/xmobarrc # normal foreground
sed -i "s/color01Normal/"$color01Normal"/g" ~/.config/xmobar/xmobarrc # normal black
sed -i "s/color01Bright/"$color01Bright"/g" ~/.config/xmobar/xmobarrc # bright black
sed -i "s/color02Normal/"$color02Normal"/g" ~/.config/xmobar/xmobarrc # normal red
sed -i "s/color02Bright/"$color02Bright"/g" ~/.config/xmobar/xmobarrc # bright red
sed -i "s/color03Normal/"$color03Normal"/g" ~/.config/xmobar/xmobarrc # normal green
sed -i "s/color03Bright/"$color03Bright"/g" ~/.config/xmobar/xmobarrc # bright green
sed -i "s/color04Normal/"$color04Normal"/g" ~/.config/xmobar/xmobarrc # normal yellow
sed -i "s/color04Bright/"$color04Bright"/g" ~/.config/xmobar/xmobarrc # bright yellow
sed -i "s/color05Normal/"$color05Normal"/g" ~/.config/xmobar/xmobarrc # normal blue
sed -i "s/color05Bright/"$color05Bright"/g" ~/.config/xmobar/xmobarrc # bright blue
sed -i "s/color06Normal/"$color06Normal"/g" ~/.config/xmobar/xmobarrc # normal magenta
sed -i "s/color06Bright/"$color06Bright"/g" ~/.config/xmobar/xmobarrc # bright magenta
sed -i "s/color07Normal/"$color07Normal"/g" ~/.config/xmobar/xmobarrc # normal cyan
sed -i "s/color07Bright/"$color07Bright"/g" ~/.config/xmobar/xmobarrc # bright cyan
sed -i "s/color08Normal/"$color08Normal"/g" ~/.config/xmobar/xmobarrc # normal white
sed -i "s/color08Bright/"$color08Bright"/g" ~/.config/xmobar/xmobarrc # bright white
sed -i "s/colorFocus/"$colorFocus"/g" ~/.config/xmobar/xmobarrc # wm focus color
sed -i "s/colorSecondary/"$colorSecondary"/g" ~/.config/xmobar/xmobarrc & # xmobar highlight color
Lastly, desktop applications are started in the background.
# Launch necessary desktop applications
# emacs --daemon &
# xautolock -time 10 -locker "dm-tool switch-to-greeter & systemctl suspend" &
twmnd &
alttab -w 1 -t 240x160 -i 64x64 -sc 1 -bg $colorBgNormal -fg $colorFgNormal -frame $colorSecondary -inact $colorFgNormal &
autokey-gtk &
##/usr/bin/trayer --edge top --align right --SetDockType true --SetPartialStrut true --expand true --widthtype request --transparent true --alpha 0 --height 28 --tint $trayertint --monitor "primary" &
nm-applet &
GOMAXPROCS=1 syncthing --no-browser &
rclone mount adantium-nextcloud:/ ~/Nextcloud &
syncthing-gtk -m &
# flatpak run com.discordapp.Discord --start-minimized &
protonmail-bridge --no-window
~/.local/bin/setup-external-monitor.sh &
rm -rf ~/org &
gnome-keyring-daemon --daemonize --login &
gnome-keyring-daemon --start --components=secrets &
#back4.sh 0.04 ~/Media/Backgrounds/steampunk-city.gif &
##sleep 2 && xwinwrap -b -s -fs -st -sp -nf -ov -fdt -- mpv -wid WID --really-quiet --framedrop=vo --no-audio --panscan="1.0" --loop-file=inf --osc=no ~/Downloads/gruvbox-town-mod.gif --scale="bilinear"
New Xmobar Setup
--myPP = def { ppCurrent = xmobarColor colorFocus "" }
myPP = xmobarPP { ppTitle = xmobarColor colorFocus "",
ppCurrent = xmobarStripTags ["NSP"] . xmobarColor colorFocus "",
ppVisible = xmobarStripTags ["NSP"] . xmobarColor colorSecondary "",
ppHidden = xmobarStripTags ["NSP"] . xmobarColor colorFgNormal "",
ppHiddenNoWindows = xmobarStripTags ["NSP"] . xmobarColor colorBgBright "",
ppOrder = \(ws : _) -> [ws],
ppSep = " "
}
mySB = statusBarProp "xmobar" (pure myPP)
Main
Lastly, xmonad is started with all of the settings set up as variables. First xmobar is setup with spawnPipe
so that it has access to the workspaces from xmonad. Then xmonad is executed with the settings.
-- Now run xmonad with all the defaults we set up.
main = do
spawn ("xmobar -x 0")
spawn ("xmobar -x 1")
spawn ("xmobar -x 2")
xmonad . withSB mySB $
withNavigation2DConfig myNavigation2DConfig $
fullscreenSupportBorder $
docks $
EWMHD.ewmh
def
{ -- simple stuff
terminal = myTerminal,
focusFollowsMouse = myFocusFollowsMouse,
clickJustFocuses = myClickJustFocuses,
borderWidth = myBorderWidth,
modMask = myModMask,
workspaces = myWorkspaces,
normalBorderColor = myNormalBorderColor,
focusedBorderColor = myFocusedBorderColor,
-- key bindings
keys = myKeys,
mouseBindings = myMouseBindings,
-- hooks, layouts
layoutHook = myLayout,
manageHook = myManageHook <+> myFullscreenManageHook <+> namedScratchpadManageHook myScratchPads,
handleEventHook = myEventHook <+> myFullscreenEventHook <+> fadeWindowsEventHook,
logHook = (refocusLastLogHook >> nsHideOnFocusLoss myScratchPads),
startupHook = myStartupHook
}
XMobar Config
I utilize xmobar as a status bar on my monitors. To manage my xmobar configs, three main files are used:
- base-xmobarrc which contains the structure of my xmobar config
- startup.sh which has a section which generates a copy of my xmobarrcs for the current color scheme I set in xmonad.hs
Base xmobarrc
This is my base xmobarrc. This is a full xmobar config with placeholders for the colors (i.e. colorFgNormal
, colorBgNormal
, color01Normal
, color01Bright
, etc…). startup.sh copies this into xmobarrc
with my current base16 color scheme. This also depends on Inconsolata
and Symbols Nerd Font
.
Config { font = "Inconsolata 18"
, additionalFonts = ["Symbols Nerd Font 14"]
, border = NoBorder
, bgColor = "colorBgNormal"
, alpha = 200
, fgColor = "colorFgNormal"
, position = TopSize C 100 28
, textOffset = -1
, iconOffset = -1
, lowerOnStart = True
, pickBroadest = False
, persistent = False
, hideOnStart = False
, iconRoot = "."
, allDesktops = True
, overrideRedirect = True
, commands = [
Run XMonadLog
, Run Date "<fc=color06Normal> <fn=1>\xf073</fn> %a %-m/%-d/%y %-I:%M:%S%P</fc>" "date" 10
, Run BatteryP ["BAT0"]
["-t", "<acstatus>",
"-L", "10", "-H", "80", "-p", "3", "--",
"-O","<fc=colorFgNormal><fn=1>\xf313</fn></fc> <fc=color03Normal> <fn=1>\xf17e3</fn><left>% </fc>",
"-i","<fc=colorFgNormal><fn=1>\xf313</fn></fc> <fc=color03Normal> <fn=1>\xf17e7</fn><left>% </fc>",
"-o","<fc=colorFgNormal><fn=1>\xf313</fn></fc> <fc=color02Normal> <fn=1>\xf17e4</fn><left>% </fc>",
"-L", "-15", "-H", "-5",
"-l", "color02Normal", "-m", "color05Normal", "-h", "color03Normal"] 10
, Run Brightness
[ "-t", "<fc=color04Normal><fn=1>\xf0eb</fn> <percent>% </fc>", "--",
"-D", "amdgpu_bl1"
] 2
, Run Volume "default" "Master"
[ "-t", "<status>", "--"
, "--on", "<fc=color07Normal> <fn=1>\xf028</fn> <volume>% </fc>"
, "--onc", "color07Normal"
, "--off", "<fc=color06Normal> <fn=1>\xf026</fn> Mute </fc>"
, "--offc", "color06Normal"
] 1
]
, sepChar = "%"
, alignSep = "}{"
, template = " %battery% %bright%<action=`xdotool key Super_L+y`>%default:Master%</action>}<box color=colorBgBright width=0>%XMonadLog%</box>{<action=`xdotool key Super_L+c`>%date%</action> "
}
}
Nix Integration
In order to have Nix put my xmonad/xmobar configuration in the proper places, I have xmonad.nix, which I source in the imports
block of my home.nix.
{ config, pkgs, ... }:
{
imports = [ ../picom/picom.nix ];
home.file.".config/xmonad/xmonad.hs".source = ./xmonad.hs;
home.file.".config/xmobar/base-xmobarrc".source = ./base-xmobarrc;
home.file.".config/xmonad/startup.sh".source = ./startup.sh;
}