ABOUT
Originally, this was my old archlinux DTOS configuration, but now i am on fedora (update: i am back home). I have tested on my fedora and it works fine. For other distributions it should be the same, just complete the necessary packages. I'll try to give a little guidance.
Note
Before you go and copy my config, read the section below for the
dependencies and short tutorial to get started. if I didn't miss
anything, this should be enough to get your linux to use this xmonad
configuration.
Credit: https://xmonad.org, https://gitlab.com/dtos
Documentation
https://xmonad.org/documentation.html What I will tell you next, all come from the documentation. Documentation is the most important thing when you learn something, whether it's a window manager, programming language, etc. Here are some links to the xmonad documentation:
- https://xmonad.org/documentation.html Documentation home page.
- https://xmonad.org/INSTALL.html On how to install xmonad.
- Hackage Documentation, It contains further documentation, like what libraries you can use, how to use them, more like how to use haskell for xmonad.
Installing xmonad
In general, the process is the same, the difference is which method you use. Either use your distro's package manager, or build from source with cabal or stack. This time I will use the package manager, because I find it easier and use less space. Of course if you want to play with haskell more deeply, using stack or cabal is great.
-
Installing dependencies
The dependencies are the same, but the names may differ in different package managers. I will give an example for Fedora, for other distros, you can open the install xmonad documentation link that I provided before. Run in terminal
sudo dnf update && sudo dnf install git libX11-devel libXft-devel libXinerama-devel libXrandr-devel libXScrnSaver-devel
-
Make xmonad config directory
This is where you store all configurations. The new xmonad saves its configuration in ~/.config/xmonad, while the older version is in ~/.xmonad. In my case, the fedora package manager provided xmonad which seemed to be older, so I saved the configuration in ~/.xmonad. If you are using a distro like arch linux you will probably use ~/.config/xmonad instead. Just test which one works later. To make configuration directory:
# for latest version: mkdir ~/.config/xmonad # or for older version: mkdir ~/.xmonad
-
Installing xmonad
Three things you need to install, xmonad itself, xmonad-contrib (Community-maintained tiling algorithms and extension modules for xmonad), and the bar (unless you don't want bar 😁). The bar that is commonly used in conjunction with xmonad is xmobar and distrotube has a great tutorial on this. The other bar, which I'll be using in this configuration is polybar, so we'll be installing that as well. On fedora:
sudo dnf install xmonad ghc-xmonad-contrib polybar
Remember, the package name may be different in each package manager that your distro uses. Use package search to help you, for example, on arch linux you can use
pacman -Ss pkg-name-you-are-looking-for
, or on ubuntu:~apt search pkg-name-you-are-looking-for
. if i remember correctly, on arch linux the names are xmonad, xmonad-contrib (without ghc-), and polybar. So on an arch based distro you can do:sudo pacman -S xmonad xmonad-contrib polybar
-
Another thing to install
-
Dmenu
Xmonad uses dmenu by default, you can copy my patched dmenu (see my dmenu build) and install it or install the plain version (https://tools.suckless.org/dmenu) and patch it yourself. Of course you can use rofi or another launcher, just set it in keybinding later.
-
Dbus
To easily display XMonad status in polybar we need DBus monitoring and i use xintron/xmonad-log for that. Xmonad-log allows us to send status over DBus using XMonad's DynamicLog to any application that support executing custom scripts such as polybar.
- First we need to install dbus. Again, i'm using fedora here:
sudo dnf install ghc-dbus ghc-dbus-devel
-
Next, we need to install golang to build xmonad-log:
sudo dnf install golang
-
Then we clone the xmonad-log repository. On its github it says we need to clone it to
$GOPATH/src/github.com/xintron/xmonad-log
.$GOPATH
is a variable used by Go to reference the location where all project folders are stored. However, Setup Go project using$GOPATH
is not recommended for the latest version of Go, instead we use Go Modules. So just clone to our home directory, or any other directory you want.# clone to home directory git clone https://github.com/xintron/xmonad-log.git ~/xmonad-log # or clone to another directory git clone https://github.com/xintron/xmonad-log.git ~/path/to/another/dir/xmonad-log
-
Now go to xmonad-log directory which we have clone, by ordering:
cd xmonad-log
-
Once we're on xmonad-log directory, then we initilize new Go project by:
go mod init xmonad-log
-
Then add dependencies to current module and install them, by:
go get xmonad-log
-
Now we build it
go build
This will give you an xmonad-log binary in the directory. Move this to an appropriate directory in your $PATH.
-
IMPORTS
These are Haskell modules that we must import so that we can use their
functions later in the config. We use import
stetement for to import
modules. Qualified import makes the imported module only available in
qualified form, eg. it force an obligatory namespace qualifier to
imported identifiers. A qualified import allows us to use functions with
the same name from several modules. Because there is a namespace,
haskell doesn't get agitated.
-- Base
import XMonad
import System.Directory
import System.IO (hClose, hPutStr, hPutStrLn)
import System.Exit (exitSuccess)
import qualified XMonad.StackSet as W
-- Actions
import XMonad.Actions.CopyWindow (kill1)
import XMonad.Actions.CycleWS (Direction1D(..), moveTo, shiftTo, toggleWS, WSType(..), nextScreen, prevScreen)
import XMonad.Actions.MouseResize
import XMonad.Actions.Promote
import XMonad.Actions.RotSlaves (rotSlavesDown, rotAllDown)
import XMonad.Actions.WindowGo (runOrRaise)
import XMonad.Actions.WithAll (sinkAll, killAll)
import qualified XMonad.Actions.Search as S
-- Data
import Data.Char (isSpace, toUpper)
import Data.Maybe (fromJust, isJust)
import Data.Monoid
import Data.Tree
import qualified Data.Map as M
-- Dbus
import qualified DBus as D
import qualified DBus.Client as D
import qualified Codec.Binary.UTF8.String as UTF8
-- Hooks
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.ManageDocks (avoidStruts, docks, manageDocks, ToggleStruts(..))
import XMonad.Hooks.ManageHelpers (isFullscreen, doFullFloat, doCenterFloat)
import XMonad.Hooks.ServerMode
import XMonad.Hooks.SetWMName
import XMonad.Hooks.StatusBar
import XMonad.Hooks.StatusBar.PP
import XMonad.Hooks.WindowSwallowing
import XMonad.Hooks.WorkspaceHistory
-- Layouts
import XMonad.Layout.Accordion
import XMonad.Layout.GridVariants (Grid(Grid))
import XMonad.Layout.SimplestFloat
import XMonad.Layout.Spiral
import XMonad.Layout.ResizableTile
import XMonad.Layout.Tabbed
import XMonad.Layout.ThreeColumns
-- Layouts modifiers
import XMonad.Layout.LayoutModifier
import XMonad.Layout.LimitWindows (limitWindows, increaseLimit, decreaseLimit)
import XMonad.Layout.MultiToggle (mkToggle, single, EOT(EOT), (??))
import XMonad.Layout.MultiToggle.Instances (StdTransformers(NBFULL, MIRROR, NOBORDERS))
import XMonad.Layout.NoBorders
import XMonad.Layout.Renamed
import XMonad.Layout.ShowWName
import XMonad.Layout.Simplest
import XMonad.Layout.Spacing
import XMonad.Layout.SubLayouts
import XMonad.Layout.WindowArranger (windowArrange, WindowArrangerMsg(..))
import XMonad.Layout.WindowNavigation
import qualified XMonad.Layout.ToggleLayouts as T (toggleLayouts, ToggleLayout(Toggle))
import qualified XMonad.Layout.MultiToggle as MT (Toggle(..))
-- Utilities
import XMonad.Util.Dmenu
import XMonad.Util.EZConfig (additionalKeysP, mkNamedKeymap)
import XMonad.Util.Hacks (windowedFullscreenFixEventHook, javaHack, trayerAboveXmobarEventHook, trayAbovePanelEventHook, trayerPaddingXmobarEventHook, trayPaddingXmobarEventHook, trayPaddingEventHook)
import XMonad.Util.NamedActions
import XMonad.Util.NamedScratchpad
import XMonad.Util.NamedWindows (getName)
import XMonad.Util.Run (runProcessWithInput, safeSpawn, spawnPipe)
import XMonad.Util.SpawnOnce
-- Colorscheme Module, made using pywal template, and link it to ~/.xmonaad/lib/Colors directory using ln -s
import Colors.XmonadPywal
VARIABLES
It's nice to assign values to stuff that you will use more than once in the config. Setting values for things like font, terminal and editor means you only have to change the value here to make changes globally.
-- Variables
myFont :: String
myFont = "xft:JetBrainsMono Nerd Font:regular:size=9:antialias=true:hinting=true"
myModMask :: KeyMask
myModMask = mod4Mask -- Sets modkey to super/windows key
myTerminal :: String
myTerminal = "alacritty" -- Sets default terminal
myBrowser :: String
myBrowser = "firefox " -- Sets qutebrowser as browser
myEmacs :: String
myEmacs = "emacsclient -c -a 'emacs' " -- Makes emacs keybindings easier to type
myEditor :: String
myEditor = "code" -- Sets emacs as editor
myFileManager :: String
myFileManager = "nemo" -- Sets emacs as editor
myBorderWidth :: Dimension
myBorderWidth = 2 -- Sets border width for windows
myNormColor :: String -- Border color of normal windows
myNormColor = background -- This variable is imported from Colors.THEME
myFocusColor :: String -- Border color of focused windows
myFocusColor = color4 -- This variable is imported from Colors.THEME
mySoundPlayer :: String
mySoundPlayer = "ffplay -nodisp -autoexit " -- The program that will play system sounds
-- just like his name, to count window
windowCount :: X (Maybe String)
windowCount = gets $ Just . show . length . W.integrate' . W.stack . W.workspace . W.current . windowset
AUTOSTARTS
This is a program that automatically runs on startup or restarting
xmonad. We need to store it in startupHook
on main method
(this similar to main
in other languages, such as c
). For now we
store it in myStartupHook
first then put it later on startupHook
.
myStartupHook :: X ()
myStartupHook = do
spawnOnce (mySoundPlayer ++ startupSound)
spawnOnce "picom"
spawnOnce "nm-applet"
spawnOnce "pasystray"
spawnOnce "xset dpms 0 0 10800 & xss-lock -- slock &"
spawn "/usr/bin/emacs --daemon" -- emacs daemon for the emacsclient
spawn "polybar mainbar-xmonad"
spawnOnce "wal -R &"
setWMName "LG3D"
LAYOUTS
Defining the layouts that I want to have available. This require us to
import modules XMonad.Layout
.
{- | Makes setting the spacingRaw simpler to write. The spacingRaw module adds
a configurable amount of space around windows.
-}
mySpacing :: Integer -> l a -> XMonad.Layout.LayoutModifier.ModifiedLayout Spacing l a
mySpacing i = spacingRaw False (Border i i i i) True (Border i i i i) True
{- | Below is a variation of the above except no borders are applied if fewer
than two windows. So a single window has no gaps.
-}
mySpacing' :: Integer -> l a -> XMonad.Layout.LayoutModifier.ModifiedLayout Spacing l a
mySpacing' i = spacingRaw True (Border i i i i) True (Border i i i i) True
{- Defining a bunch of layouts, many that I don't use. limitWindows n sets
maximum number of windows displayed for layout. mySpacing n sets the gap size
around the windows.
-}
tall = renamed [Replace "tall"]
$ limitWindows 5
$ addTabs shrinkText myTabTheme
$ subLayout [] (smartBorders Simplest)
$ mySpacing 4
$ ResizableTall 1 (3/100) (1/2) []
monocle = renamed [Replace "monocle"]
$ addTabs shrinkText myTabTheme
$ subLayout [] (smartBorders Simplest)
Full
floats = renamed [Replace "floats"]
simplestFloat
grid = renamed [Replace "grid"]
$ limitWindows 9
$ addTabs shrinkText myTabTheme
$ subLayout [] (smartBorders Simplest)
$ mySpacing 4
$ mkToggle (single MIRROR)
$ Grid (16/10)
spirals = renamed [Replace "spirals"]
$ limitWindows 9
$ addTabs shrinkText myTabTheme
$ subLayout [] (smartBorders Simplest)
$ mySpacing' 4
$ spiral (6/7)
threeCol = renamed [Replace "threeCol"]
$ limitWindows 7
$ addTabs shrinkText myTabTheme
$ subLayout [] (smartBorders Simplest)
$ ThreeCol 1 (3/100) (1/2)
{- | Mirror takes a layout and rotates it by 90 degrees.
So we are applying Mirror to the ThreeCol layout.
-}
threeRow = renamed [Replace "threeRow"]
$ limitWindows 7
$ addTabs shrinkText myTabTheme
$ subLayout [] (smartBorders Simplest)
$ Mirror
$ ThreeCol 1 (3/100) (1/2)
{- | I cannot add spacing to this layout because it will
add spacing between window and tabs which looks bad.
-}
tabs = renamed [Replace "tabs"] $ tabbed shrinkText myTabTheme
tallAccordion = renamed [Replace "tallAccordion"] Accordion
wideAccordion = renamed [Replace "wideAccordion"] $ Mirror Accordion
-- setting colors for tabs layout and tabs sublayout.
myTabTheme = def
{ fontName = myFont
, activeColor = color15
, inactiveColor = color8
, activeBorderColor = color15
, inactiveBorderColor = background
, activeTextColor = background
, inactiveTextColor = color0
}
-- The layout hook
myLayoutHook = avoidStruts
$ mouseResize
$ windowArrange
$ T.toggleLayouts floats
$ mkToggle (NBFULL ?? NOBORDERS ?? EOT) myDefaultLayout
where
myDefaultLayout = smartBorders
$ withBorder myBorderWidth
$ configurableNavigation noNavigateBorders tall
||| noBorders monocle
||| floats
||| noBorders tabs
||| grid
||| spirals
||| threeCol
||| threeRow
||| tallAccordion
||| wideAccordion
WORKSPACES
Thisi worspace configuration contains workspace name, how make custom xmonad polybar-module clickable, and theme for workspace name on change popup.
-- Workspace name
myWorkspaces :: [String]
myWorkspaces = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
{- | another worspace name that you can use:
myWorkspaces =
[ "dev"
, "www"
, "sys"
, "doc"
, "vbx"
, "cht"
, "mus"
, "vid"
, "trm"
, "gfx"
]
-}
-- Make custom xmonad polybar module clickable
myWorkspaceIndices = M.fromList $ zipWith (,) myWorkspaces [1..]
{- | Basically we use xdotool (don't forget to install it) to press our
keybinding, when we do click to polybar xmonad workspace.
-}
clickable ws = "%{A:xdotool key super+" ++ show i ++ " &:}" ++ ws ++ "%{A}"
where i = fromJust $ M.lookup ws myWorkspaceIndices
{- | Theme for showWName which prints current workspace when you change
workspaces. In order to make it work, on main method you need to have:
, layoutHook = showWName' myShowWNameTheme $ myLayoutHook
instead of just:
, layoutHook = myLayoutHook
-}
myShowWNameTheme :: SWNConfig
myShowWNameTheme = def
{ swn_font = "xft:JetBrainsMono Nerd Font:bold:size=50"
, swn_fade = 1.0
, swn_bgcolor = "#1c1f24"
, swn_color = "#ffffff"
}
MANAGEHOOK
Sets some rules for certain programs. Examples include forcing certain programs to always float, or to always appear on a certain workspace. Forcing programs to a certain workspace with a doShift requires xdotool if you are using clickable workspaces. You need the className or title of the program. Use xprop to get this info.
{- | 'doFloat' forces a window to float. Useful for dialog boxes and such.
using 'doShift ( myWorkspaces !! 7)' sends program to workspace 8! I'm doing it
this way because otherwise I would have to write out the full name of my
workspaces and the names would be very long if using clickable workspaces.
-}
myManageHook :: XMonad.Query (Data.Monoid.Endo WindowSet)
myManageHook = composeAll
[ className =? "confirm" --> doFloat
, className =? "file_progress" --> doFloat
, className =? "dialog" --> doFloat
, className =? "download" --> doFloat
, className =? "error" --> doFloat
, className =? "Gimp" --> doFloat
, className =? "notification" --> doFloat
, className =? "pinentry-gtk-2" --> doFloat
, className =? "splash" --> doFloat
, className =? "toolbar" --> doFloat
, className =? "Yad" --> doCenterFloat
, title =? "Oracle VM VirtualBox Manager" --> doFloat
, title =? "Order Chain - Market Snapshots" --> doFloat
, title =? "Mozilla Firefox" --> doShift ( myWorkspaces !! 1 )
, className =? "Brave-browser" --> doShift ( myWorkspaces !! 1 )
, className =? "mpv" --> doShift ( myWorkspaces !! 7 )
, className =? "Gimp" --> doShift ( myWorkspaces !! 8 )
, className =? "VirtualBox Manager" --> doShift ( myWorkspaces !! 4 )
, (className =? "firefox" <&&> resource =? "Dialog") --> doFloat
, isFullscreen --> doFullFloat
] <+> namedScratchpadManageHook myScratchPads
SYSTEM SOUNDS
Available sounds that are part of the default dtos-sounds package include:
- menu-01.mp3
- menu-02.mp3
- menu-03.mp3
- shutdown-01.mp3
- shutdown-02.mp3
- shutdown-03.mp3
- startup-01.mp3
- startup-02.mp3
- startup-03.mp3
soundDir = "/home/fqs/.config/dtos-sounds/"
startupSound = soundDir ++ "startup-01.mp3"
shutdownSound = soundDir ++ "shutdown-01.mp3"
NAMED ACTIONS
NamedActions
is a wrapper for keybinding configuration that can list
the available keybindings. The following custom functions are used to
add NamedActions
to our keybindings in the format that I desired.
subTitle'
allows me to format the subtitle (subKeys
) so that I can
prepend and/or append text to them. showKeybindings
is a function that
pipes the output of our NamedActions
into a GUI display program, such
as 'yad' or 'zenity'.
subtitle' :: String -> ((KeyMask, KeySym), NamedAction)
subtitle' x =
( (0,0)
, NamedAction $ map toUpper $ sep ++ "\n-- " ++ x ++ " --\n" ++ sep
) where
sep = replicate (6 + length x) '-'
showKeybindings :: [((KeyMask, KeySym), NamedAction)] -> NamedAction
showKeybindings x = addName "Show Keybindings" $ io $ do
h <- spawnPipe "yad --text-info --fontname=\"JetBrainsMono Nerd Font 11\" --fore=#46d9ff back=#282c36 --center --geometry=1200x700 --title \"XMonad keybindings\""
--hPutStr h (unlines $ showKm x) -- showKM adds ">>" before subtitles
hPutStr h (unlines $ showKmSimple x) -- showKmSimple doesn't add ">>" to subtitles
hClose h
return ()
KEYBINDINGS
This config use the
Xmonad.Util.EZConfig
module, that make keybindings to be written in simpler form. The
Super/Windows key is 'M' (the modkey), the ALT key is 'M1'. Shift is 'S'
and CTRL is 'C'. If you dont know what is the name of key actually is,
always refer to EZConfig
documentation
(for example, the backspace key is written as <Backspace>, note it is
enclosed in <…> and the initial letter is capitalized). Each group of
keybindings must have a subKeys
heading, and each individual
keybinding must use addName
to add a description. These headings and
descriptions are needed for the keybindings list that can be launched
with 'M-F1'.
Note
will complete the table later, the key should be
self-explanatory. I will give you small example: "M-C-r" means you need
to press Modkey+Ctrl+r "M-x r" means you need to press Modkey+x, release
it, then press r
A FEW KEYBINDINGS | ASSOCIATED ACTION |
---|---|
MODKEY + RETURN | opens terminal |
myKeys :: XConfig l0 -> [((KeyMask, KeySym), NamedAction)]
myKeys c =
--(subtitle "Custom Keys":) $ mkNamedKeymap c $
let subKeys str ks = subtitle' str : mkNamedKeymap c ks in
subKeys "Xmonad Essentials"
[ ("M-C-r", addName "Recompile XMonad" $ spawn "xmonad --recompile")
, ("M-S-<F5>", addName "Restart XMonad" $ spawn "killall emacs; killall polybar; xmonad --restart")
, ("M-S-q", addName "Quit XMonad" $ sequence_ [spawn (mySoundPlayer ++ shutdownSound), io exitSuccess])
, ("M-x", addName "Quit XMonad" $ spawn "dm-logout")
, ("M-q", addName "Kill focused window" $ kill1)
, ("M-S-q", addName "Kill all windows on WS"$ killAll)
, ("M-d", addName "Run prompt" $ spawn "dm_run")
, ("M-S-b", addName "Toggle bar show/hide" $ sendMessage ToggleStruts)
]
^++^ subKeys "Switch to workspace"
[ ("M-1", addName "Switch to workspace 1" $ windows $ W.greedyView $ myWorkspaces !! 0)
, ("M-2", addName "Switch to workspace 2" $ windows $ W.greedyView $ myWorkspaces !! 1)
, ("M-3", addName "Switch to workspace 3" $ windows $ W.greedyView $ myWorkspaces !! 2)
, ("M-4", addName "Switch to workspace 4" $ windows $ W.greedyView $ myWorkspaces !! 3)
, ("M-5", addName "Switch to workspace 5" $ windows $ W.greedyView $ myWorkspaces !! 4)
, ("M-6", addName "Switch to workspace 6" $ windows $ W.greedyView $ myWorkspaces !! 5)
, ("M-7", addName "Switch to workspace 7" $ windows $ W.greedyView $ myWorkspaces !! 6)
, ("M-8", addName "Switch to workspace 8" $ windows $ W.greedyView $ myWorkspaces !! 7)
, ("M-9", addName "Switch to workspace 9" $ windows $ W.greedyView $ myWorkspaces !! 8)
, ("M-0", addName "Switch to workspace 10" $ windows $ W.greedyView $ myWorkspaces !! 9)
]
^++^ subKeys "Send window to workspace"
[ ("M-S-1", addName "Send to workspace 1" $ windows $ W.shift $ myWorkspaces !! 0)
, ("M-S-2", addName "Send to workspace 2" $ windows $ W.shift $ myWorkspaces !! 1)
, ("M-S-3", addName "Send to workspace 3" $ windows $ W.shift $ myWorkspaces !! 2)
, ("M-S-4", addName "Send to workspace 4" $ windows $ W.shift $ myWorkspaces !! 3)
, ("M-S-5", addName "Send to workspace 5" $ windows $ W.shift $ myWorkspaces !! 4)
, ("M-S-6", addName "Send to workspace 6" $ windows $ W.shift $ myWorkspaces !! 5)
, ("M-S-7", addName "Send to workspace 7" $ windows $ W.shift $ myWorkspaces !! 6)
, ("M-S-8", addName "Send to workspace 8" $ windows $ W.shift $ myWorkspaces !! 7)
, ("M-S-9", addName "Send to workspace 9" $ windows $ W.shift $ myWorkspaces !! 8)
, ("M-S-0", addName "Send to workspace 10" $ windows $ W.shift $ myWorkspaces !! 9)
]
^++^ subKeys "Move window to WS and go there"
[ ("M-S-<Page_Up>", addName "Move window to next WS" $ shiftTo Next nonNSP >> moveTo Next nonNSP)
, ("M-S-<Page_Down>", addName "Move window to prev WS" $ shiftTo Prev nonNSP >> moveTo Prev nonNSP)
]
^++^ subKeys "Window navigation"
[ ("M-j", addName "Move focus to next window" $ windows W.focusDown)
, ("M-k", addName "Move focus to prev window" $ windows W.focusUp)
, ("M-m", addName "Move focus to master window" $ windows W.focusMaster)
, ("M-S-j", addName "Swap focused window with next window" $ windows W.swapDown)
, ("M-S-k", addName "Swap focused window with prev window" $ windows W.swapUp)
, ("M-S-m", addName "Swap focused window with master window"$ windows W.swapMaster)
, ("M-<Backspace>", addName "Move focused window to master" $ promote)
, ("M-S-,", addName "Rotate all windows except master" $ rotSlavesDown)
, ("M-S-.", addName "Rotate all windows current stack" $ rotAllDown)
]
{- | Dmenu scripts (dmscripts)
In Xmonad and many tiling window managers, M-p is the default keybinding to
launch dmenu_run, so I've decided to use M-p plus KEY for these dmenu scripts.
-}
^++^ subKeys "Dmenu scripts"
[ ("M-p h", addName "List all dmscripts" $ spawn "dm-hub")
, ("M-p b", addName "Set background" $ spawn "dm-setbg")
, ("M-p c", addName "Edit config files" $ spawn "dm-confedit")
, ("M-p m", addName "View manpages" $ spawn "dm-man")
, ("M-p o", addName "Store and copy notes" $ spawn "dm-note")
, ("M-p x", addName "Logout Menu" $ spawn "dm-logout")
, ("M-p r", addName "Listen to online radio"$ spawn "dm-radio")
, ("M-p s", addName "Record Screen" $ spawn "dm-record")
, ("M-p w", addName "Search various engines"$ spawn "dm-websearch")
, ("M-p n", addName "Connect Wifi" $ spawn "dm-wifi")
]
^++^ subKeys "Favorite programs"
[ ("M-<Return>", addName "Launch terminal" $ spawn myTerminal)
, ("M-b", addName "Launch web browser" $ spawn myBrowser)
, ("M-e", addName "Launch file manager" $ spawn myFileManager)
, ("M-s", addName "Launch file manager" $ spawn "flameshot gui")
, ("M-M1-h", addName "Launch htop" $ spawn (myTerminal ++ " -e htop"))
]
^++^ subKeys "Monitors"
[ ("M-.", addName "Switch focus to next monitor" $ nextScreen)
, ("M-,", addName "Switch focus to prev monitor" $ prevScreen)
]
-- Switch layouts
^++^ subKeys "Switch layouts"
[ ("M-<Tab>", addName "Switch to next layout" $ sendMessage NextLayout)
, ("M-f", addName "Toggle noborders/full" $ sendMessage (MT.Toggle NBFULL) >> sendMessage ToggleStruts)
]
-- Window resizing
^++^ subKeys "Window resizing"
[ ("M-h", addName "Shrink window" $ sendMessage Shrink)
, ("M-l", addName "Expand window" $ sendMessage Expand)
, ("M-M1-j", addName "Shrink window vertically" $ sendMessage MirrorShrink)
, ("M-M1-k", addName "Expand window vertically" $ sendMessage MirrorExpand)
]
-- Floating windows
^++^ subKeys "Floating windows"
[ ("M-S-<Space>", addName "Toggle float layout" $ sendMessage (T.Toggle "floats"))
, ("M-t", addName "Sink a floating window" $ withFocused $ windows . W.sink)
, ("M-S-t", addName "Sink all floated windows" $ sinkAll)
]
-- Increase/decrease spacing (gaps)
^++^ subKeys "Window spacing (gaps)"
[ ("M-g j", addName "Decrease window spacing" $ decWindowSpacing 4)
, ("M-g k", addName "Increase window spacing" $ incWindowSpacing 4)
, ("M-g h", addName "Decrease screen spacing" $ decScreenSpacing 4)
, ("M-g l", addName "Increase screen spacing" $ incScreenSpacing 4)
]
-- Increase/decrease windows in the master pane or the stack
^++^ subKeys "Increase/decrease windows in master pane or the stack"
[ ("M-S-<Up>", addName "Increase clients in master pane" $ sendMessage (IncMasterN 1))
, ("M-S-<Down>", addName "Decrease clients in master pane" $ sendMessage (IncMasterN (-1)))
, ("M-=", addName "Increase max # of windows for layout" $ increaseLimit)
, ("M--", addName "Decrease max # of windows for layout" $ decreaseLimit)
]
{- | Sublayouts
This is used to push windows to tabbed sublayouts, or pull them out of it.
-}
^++^ subKeys "Sublayouts"
[ ("M-C-h", addName "pullGroup L" $ sendMessage $ pullGroup L)
, ("M-C-l", addName "pullGroup R" $ sendMessage $ pullGroup R)
, ("M-C-k", addName "pullGroup U" $ sendMessage $ pullGroup U)
, ("M-C-j", addName "pullGroup D" $ sendMessage $ pullGroup D)
, ("M-C-m", addName "MergeAll" $ withFocused (sendMessage . MergeAll))
, ("M-C-u", addName "UnMerge" $ withFocused (sendMessage . UnMerge))
, ("M-C-/", addName "UnMergeAll" $ withFocused (sendMessage . UnMergeAll))
, ("M-C-.", addName "Switch focus next tab" $ onGroup W.focusUp')
, ("M-C-,", addName "Switch focus prev tab" $ onGroup W.focusDown')
]
{- | Scratchpads
Toggle show/hide these programs. They run on a hidden workspace.
When you toggle them to show, it brings them to current workspace.
Toggle them to hide and it sends them back to hidden workspace (NSP).
-}
^++^ subKeys "Scratchpads"
[ ("M-` 1", addName "Toggle scratchpad terminal" $ namedScratchpadAction myScratchPads "terminal")
, ("M-` 2", addName "Toggle scratchpad mocp" $ namedScratchpadAction myScratchPads "mocp")
, ("M-` 3", addName "Toggle scratchpad calculator" $ namedScratchpadAction myScratchPads "calculator")
]
-- Controls for mocp music player (SUPER-u followed by a key)
^++^ subKeys "Mocp music player"
[ ("M-u p", addName "mocp play" $ spawn "mocp --play")
, ("M-u l", addName "mocp next" $ spawn "mocp --next")
, ("M-u h", addName "mocp prev" $ spawn "mocp --previous")
, ("M-u <Space>", addName "mocp toggle pause" $ spawn "mocp --toggle-pause")
]
-- Emacs (SUPER-e followed by a key)
^++^ subKeys "Code Editor"
[ ("M-c c", addName "VS Code" $ spawn myEditor)
, ("M-c e", addName "Emacsclient" $ spawn myEmacs)
, ("M-c a", addName "Emacsclient EMMS (music)" $ spawn (myEmacs ++ "--eval '(emms)' --eval '(emms-play-directory-tree \"~/Music/\")'"))
, ("M-c b", addName "Emacsclient Ibuffer" $ spawn (myEmacs ++ "--eval '(ibuffer)'"))
, ("M-c d", addName "Emacsclient Dired" $ spawn (myEmacs ++ "--eval '(dired nil)'"))
, ("M-c m", addName "Mousepad" $ spawn "mousepad")
]
-- Multimedia Keys
^++^ subKeys "Multimedia keys"
[ ("<XF86AudioMute>", addName "Toggle audio mute" $ spawn "volumecontrol.sh -o m")
, ("<XF86AudioLowerVolume>", addName "Lower volume" $ spawn "volumecontrol.sh -o d")
, ("<XF86AudioRaiseVolume>", addName "Raise volume" $ spawn "volumecontrol.sh -o i")
, ("<XF86MonBrightnessDown>", addName "Raise vol" $ spawn "brightnesscontrol.sh d")
, ("<XF86MonBrightnessUp>", addName "Raise vol" $ spawn "brightnesscontrol.sh i")
, ("<Print>", addName "Take screenshot" $ spawn "flameshotgui")
]
-- Workspace
^++^ subKeys "Workspace"
[ ("M1-<Tab>", addName "Cycle with last WS" $ toggleWS) ]
-- The following lines are needed for named scratchpads.
where
nonNSP = WSIs (return (\ws -> W.tag ws /= "NSP"))
nonEmptyNonNSP = WSIs (return (\ws -> isJust (W.stack ws) && W.tag ws /= "NSP"))
MAKE XMONAD AND POLYBAR TALK TO EACH OTHER
As i have explained before, we need to integrate xmonad to visualize the current window title and workspaces in polybar. To make xmonad and polybar talk we only need to add [module/xmonad] in our polybar config and polybar ready to talk. Now, for xmonad to talk, we need to configure it to send log events information via dbus using XMonad's DynamicLog with the help of xmonad-log. The pretty printing (PP) options, allows us to customize the formatting of status information.
bg1 = "#3c3836"
bg2 = "#504945"
red = "#fb4934"
myLogHook :: D.Client -> PP
myLogHook dbus = def
{ ppOutput = dbusOutput dbus
, ppWsSep = ""
, ppSep = " | "
, ppLayout = wrap "%{A1:xdotool key super+Tab &:}" "%{A}"
, ppTitle = shorten 50
, ppExtras = [windowCount]
, ppOrder = \(_:l:t:ex) -> [l]++ex++[t]
}
-- Emit a DBus signal on log updates
dbusOutput :: D.Client -> String -> IO ()
dbusOutput dbus str = do
let signal = (D.signal objectPath interfaceName memberName) {
D.signalBody = [D.toVariant $ UTF8.decodeString str]
}
D.emit dbus signal
where
objectPath = D.objectPath_ "/org/xmonad/Log"
interfaceName = D.interfaceName_ "org.xmonad.Log"
memberName = D.memberName_ "Update"
THE MAIN
This is the "main" of XMonad. This where everything in our configs comes together and works.
main :: IO ()
main = do
dbus <- D.connectSession
-- Request access to the DBus name
D.requestName dbus (D.busName_ "org.xmonad.Log")
[D.nameAllowReplacement, D.nameReplaceExisting, D.nameDoNotQueue]
xmonad
$ addDescrKeys' ((mod4Mask, xK_F1), showKeybindings) myKeys
$ docks . ewmh
$ def
{ manageHook = myManageHook <+> manageDocks
, handleEventHook = windowedFullscreenFixEventHook <> swallowEventHook (className =? "Alacritty" <||> className =? "st-256color" <||> className =? "XTerm") (return True) <> trayerPaddingXmobarEventHook
, modMask = myModMask
, terminal = myTerminal
, startupHook = myStartupHook
, layoutHook = myLayoutHook
, workspaces = myWorkspaces
, borderWidth = myBorderWidth
, normalBorderColor = myNormColor
, focusedBorderColor = myFocusColor
, logHook = dynamicLogWithPP (myLogHook dbus)
}