diff --git a/.xmonad/xmonad.hs b/.xmonad/xmonad.hs index a141c4d..5e9558b 100644 --- a/.xmonad/xmonad.hs +++ b/.xmonad/xmonad.hs @@ -1,18 +1,25 @@ --- The xmonad configuration of Derek Taylor (DistroTube) +-- Xmonad is a dynamically tiling X11 window manager that is written and +-- configured in Haskell. Official documentation: https://xmonad.org + +-- This is the xmonad configuration of Derek Taylor (DistroTube) -- My YouTube: http://www.youtube.com/c/DistroTube -- My GitLab: http://www.gitlab.com/dwt1/ --- For more information on Xmonad, visit: https://xmonad.org + +-- This config is massively long. It is purposely bloated with a ton of +-- examples of what you can do with xmonad. It is written more as a +-- study guide rather than a config that you should download and use. ------------------------------------------------------------------------ -- IMPORTS ------------------------------------------------------------------------ -- Base import XMonad -import System.IO (hPutStrLn) +import System.IO (hPutStr, hPutStrLn) import System.Exit (exitSuccess) import qualified XMonad.StackSet as W -- Actions +import XMonad.Actions.Commands (defaultCommands, screenCommands, workspaceCommands) import XMonad.Actions.CopyWindow (kill1, killAllOtherCopies) import XMonad.Actions.CycleWS (moveTo, shiftTo, WSType(..), nextScreen, prevScreen) import XMonad.Actions.GridSelect @@ -30,15 +37,18 @@ import Data.List import Data.Monoid import Data.Maybe (isJust) import Data.Tree +import qualified Data.Tuple.Extra as TE import qualified Data.Map as M -- Hooks import XMonad.Hooks.DynamicLog (dynamicLogWithPP, wrap, xmobarPP, xmobarColor, shorten, PP(..)) import XMonad.Hooks.EwmhDesktops -- for some fullscreen events, also for xcomposite in obs. +import XMonad.Hooks.FadeInactive import XMonad.Hooks.ManageDocks (avoidStruts, docksEventHook, manageDocks, ToggleStruts(..)) import XMonad.Hooks.ManageHelpers (isFullscreen, doFullFloat) import XMonad.Hooks.ServerMode import XMonad.Hooks.SetWMName +import XMonad.Hooks.WorkspaceHistory -- Layouts import XMonad.Layout.GridVariants (Grid(Grid)) @@ -56,6 +66,7 @@ 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 (renamed, Rename(Replace)) +import XMonad.Layout.ShowWName import XMonad.Layout.Spacing import XMonad.Layout.WindowArranger (windowArrange, WindowArrangerMsg(..)) import qualified XMonad.Layout.ToggleLayouts as T (toggleLayouts, ToggleLayout(Toggle)) @@ -64,6 +75,7 @@ import qualified XMonad.Layout.MultiToggle as MT (Toggle(..)) -- Prompt import XMonad.Prompt import XMonad.Prompt.Input +import XMonad.Prompt.FuzzyMatch import XMonad.Prompt.Man import XMonad.Prompt.Pass import XMonad.Prompt.Shell (shellPrompt) @@ -80,8 +92,11 @@ import XMonad.Util.SpawnOnce ------------------------------------------------------------------------ -- 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. myFont :: String -myFont = "xft:Mononoki Nerd Font:bold:pixelsize=13" +myFont = "xft:Mononoki Nerd Font:bold:size=9" myModMask :: KeyMask myModMask = mod4Mask -- Sets modkey to super/windows key @@ -89,6 +104,12 @@ myModMask = mod4Mask -- Sets modkey to super/windows key myTerminal :: String myTerminal = "alacritty" -- Sets default terminal +myBrowser :: String +myBrowser = myTerminal ++ " -e lynx " -- Sets browser for tree select + +myEditor :: String +myEditor = "emacsclient -c -a emacs " -- Sets editor for tree select + myBorderWidth :: Dimension myBorderWidth = 2 -- Sets border width for windows @@ -109,7 +130,7 @@ windowCount = gets $ Just . show . length . W.integrate' . W.stack . W.workspace ------------------------------------------------------------------------ myStartupHook :: X () myStartupHook = do - spawnOnce "nitrogen --restore &" + spawnOnce "nitrogen --restore &" spawnOnce "picom &" spawnOnce "nm-applet &" spawnOnce "volumeicon &" @@ -121,6 +142,8 @@ myStartupHook = do ------------------------------------------------------------------------ -- GRID SELECT ------------------------------------------------------------------------ +-- GridSelect displays items (programs, open windows, etc.) in a 2D grid +-- and lets the user select from it with the cursor/hjkl keys or the mouse. myColorizer :: Window -> Bool -> X (String, String) myColorizer = colorRangeFromClassName (0x29,0x2d,0x3e) -- lowest inactive bg @@ -128,13 +151,13 @@ myColorizer = colorRangeFromClassName (0xc7,0x92,0xea) -- active bg (0xc0,0xa7,0x9a) -- inactive fg (0x29,0x2d,0x3e) -- active fg - + -- gridSelect menu layout mygridConfig :: p -> GSConfig Window mygridConfig colorizer = (buildDefaultGSConfig myColorizer) { gs_cellheight = 40 - , gs_cellwidth = 250 - , gs_cellpadding = 8 + , gs_cellwidth = 200 + , gs_cellpadding = 6 , gs_originFractX = 0.5 , gs_originFractY = 0.5 , gs_font = myFont @@ -143,33 +166,171 @@ mygridConfig colorizer = (buildDefaultGSConfig myColorizer) spawnSelected' :: [(String, String)] -> X () spawnSelected' lst = gridselect conf lst >>= flip whenJust spawn where conf = def + { gs_cellheight = 40 + , gs_cellwidth = 200 + , gs_cellpadding = 6 + , gs_originFractX = 0.5 + , gs_originFractY = 0.5 + , gs_font = myFont + } --- Set favorite apps for the spawnSelected' -myAppGrid :: [(String, String)] -myAppGrid = [ ("Audacity", "audacity") - , ("Deadbeef", "deadbeef") - , ("Emacs", "emacs") - , ("Firefox", "firefox") - , ("Geany", "geany") - , ("Geary", "geary") - , ("Gimp", "gimp") - , ("Kdenlive", "kdenlive") - , ("LibreOffice Impress", "loimpress") - , ("LibreOffice Writer", "lowriter") - , ("OBS", "obs") - , ("PCManFM", "pcmanfm") - , ("Simple Terminal", "st") - , ("Steam", "steam") - , ("Surf Browser", "surf suckless.org") - , ("Xonotic", "xonotic-glx") +-- The lists below are actually 3-tuples for use with gridSelect and treeSelect. +-- TreeSelect uses all three values in the 3-tuples but GridSelect only needs first +-- two values in each list (see myAppGrid, myBookmarkGrid and myConfigGrid below). +myApplications :: [([Char], [Char], [Char])] +myApplications = [ ("Audacity", "audacity", "Graphical cross-platform audio eidtor") + , ("Deadbeef", "deadbeef", "Lightweight GUI audio player") + , ("Emacs", "emacs", "Much more than a text editor") + , ("Firefox", "firefox", "The famous open source web browser") + , ("Geany", "geany", "A nice text editor") + , ("Geary", "geary", "Email client that is attractive") + , ("Gimp", "gimp", "Open source alternative to Photoshop") + , ("Kdenlive", "kdenlive", "A great open source video editor") + , ("LibreOffice Impress", "loimpress", "For making presentations") + , ("LibreOffice Writer", "lowriter", "A fully featured word processor") + , ("OBS", "obs", "Open broadcaster software") + , ("PCManFM", "pcmanfm", "Lightweight graphical file manager") + , ("Simple Terminal", "st", "Suckless simple terminal") + , ("Steam", "steam", "Proprietary gaming platform") + , ("Surf Browser", "surf suckless.org", "Suckless surf web browser") + , ("Xonotic", "xonotic-glx", "A fast-paced first person shooter") ] +myBookmarks :: [([Char], [Char], [Char])] +myBookmarks = [ ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + , ("Site Name", myBrowser ++ "https://www.distrotube.com", "Official website for DistroTube") + ] + +myConfigs :: [([Char], [Char], [Char])] +myConfigs = [ ("bashrc", myEditor ++ "/home/dt/.bashrc", "the bourne again shell") + , ("doom emacs config.el", myEditor ++ "/home/dt/.doom.d/config.el", "doom emacs config") + , ("doom emacs init.el", myEditor ++ "/home/dt/.doom.d/init.el", "doom emacs init") + , ("dwm", myEditor ++ "/home/dt/dwm-distrotube/config.h", "dwm config file") + , ("qtile", myEditor ++ "/home/dt/.config/qtile/config.py", "qtile config") + , ("xmonad.hs", myEditor ++ "/home/dt/.xmonad/xmonad.hs", "xmonad config") + , ("zshrc", myEditor ++ "/home/dt/.zshrc", "config for the z shell") + ] + +-- Three functions for pulling the first, second and third items +-- from each list in the above 3-tuple named myApplications. +-- getFst, getSnd, getThd ::[[Char]] +-- getFst = [TE.fst3 $ xs !! n | n <- [0..((length xs)-1)]] +-- getSnd = [TE.snd3 $ myApplications !! n | n <- [0..((length myApplications)-1)]] +-- getThd = [TE.thd3 $ myApplications !! n | n <- [0..((length myApplications)-1)]] + +-- Creating two lists and then zipping them together in a 2-tuple so that +-- GridSelect can use them. myAppGrid is the same as myApplications above, +-- minus the third set of values (the app descriptions). +myAppGrid :: [([Char], [Char])] +myAppGrid = zip + [TE.fst3 $ xs !! n | n <- [0..(length xs - 1)]] + [TE.snd3 $ xs !! n | n <- [0..(length xs - 1)]] + where xs = myApplications + +myBookmarkGrid :: [([Char], [Char])] +myBookmarkGrid = zip + [TE.fst3 $ xs !! n | n <- [0..(length xs - 1)]] + [TE.snd3 $ xs !! n | n <- [0..(length xs - 1)]] + where xs = myBookmarks + +myConfigGrid :: [([Char], [Char])] +myConfigGrid = zip + [TE.fst3 $ xs !! n | n <- [0..(length xs - 1)]] + [TE.snd3 $ xs !! n | n <- [0..(length xs - 1)]] + where xs = myConfigs + +------------------------------------------------------------------------ +-- TREE SELECT +------------------------------------------------------------------------ +-- TreeSelect displays your workspaces or actions in a Tree-like format. +-- You can select desired workspace/action with the cursor or hjkl keys. + +treeselectAction :: TS.TSConfig (X ()) -> X () +treeselectAction a = TS.treeselectAction a + [ Node (TS.TSNode "applications" "a list of programs I use often" (return ())) + [Node (TS.TSNode (TE.fst3 $ myApplications !! n) + (TE.thd3 $ myApplications !! n) + (spawn $ TE.snd3 $ myApplications !! n) + ) [] | n <- [0..(length myApplications - 1)] + ] + , Node (TS.TSNode "bookmarks" "a list of web bookmarks" (return ())) + [Node (TS.TSNode(TE.fst3 $ myBookmarks !! n) + (TE.thd3 $ myBookmarks !! n) + (spawn $ TE.snd3 $ myBookmarks !! n) + ) [] | n <- [0..(length myBookmarks - 1)] + ] + , Node (TS.TSNode "config files" "config files that edit often" (return ())) + [Node (TS.TSNode (TE.fst3 $ myConfigs !! n) + (TE.thd3 $ myConfigs !! n) + (spawn $ TE.snd3 $ myConfigs !! n) + ) [] | n <- [0..(length myConfigs - 1)] + ] + ] + +tsDefaultConfig :: TS.TSConfig a +tsDefaultConfig = TS.TSConfig { TS.ts_hidechildren = True + , TS.ts_background = 0xdd292d3e + , TS.ts_font = myFont + , TS.ts_node = (0xffd0d0d0, 0xff202331) + , TS.ts_nodealt = (0xffd0d0d0, 0xff292d3e) + , TS.ts_highlight = (0xffffffff, 0xff755999) + , TS.ts_extra = 0xffd0d0d0 + , TS.ts_node_width = 200 + , TS.ts_node_height = 20 + , TS.ts_originX = 0 + , TS.ts_originY = 0 + , TS.ts_indent = 80 + , TS.ts_navigate = myTreeNavigation + } + +myTreeNavigation = M.fromList + [ ((0, xK_Escape), TS.cancel) + , ((0, xK_Return), TS.select) + , ((0, xK_space), TS.select) + , ((0, xK_Up), TS.movePrev) + , ((0, xK_Down), TS.moveNext) + , ((0, xK_Left), TS.moveParent) + , ((0, xK_Right), TS.moveChild) + , ((0, xK_k), TS.movePrev) + , ((0, xK_j), TS.moveNext) + , ((0, xK_h), TS.moveParent) + , ((0, xK_l), TS.moveChild) + , ((0, xK_o), TS.moveHistBack) + , ((0, xK_i), TS.moveHistForward) + , ((0, xK_b), TS.moveTo ["web", "browser"]) + , ((0, xK_d), TS.moveTo ["dev"]) + , ((0, xK_e), TS.moveTo ["dev", "emacs"]) + , ((0, xK_f), TS.moveTo ["dev", "files"]) + , ((0, xK_t), TS.moveTo ["dev", "terminal"]) + , ((0, xK_g), TS.moveTo ["graphics"]) + , ((0, xK_m), TS.moveTo ["music"]) + , ((0, xK_p), TS.moveTo ["dev", "programming", "haskell"]) + , ((0, xK_v), TS.moveTo ["video"]) + , ((0, xK_1), TS.moveTo ["video", "video editor"]) + , ((0, xK_w), TS.moveTo ["web"]) + ] + ------------------------------------------------------------------------ -- XPROMPT SETTINGS ------------------------------------------------------------------------ dtXPConfig :: XPConfig dtXPConfig = def - { font = "xft:Mononoki Nerd Font:size=9" + { font = myFont , bgColor = "#292d3e" , fgColor = "#d0d0d0" , bgHLight = "#c792ea" @@ -185,7 +346,8 @@ dtXPConfig = def , defaultText = [] , autoComplete = Just 100000 -- set Just 100000 for .1 sec , showCompletionOnTab = False - , searchPredicate = isPrefixOf + -- , searchPredicate = isPrefixOf + , searchPredicate = fuzzyMatch , alwaysHighlight = True , maxComplRows = Nothing -- set to Just 5 for 5 rows } @@ -194,10 +356,11 @@ dtXPConfig = def -- certain Xprompts, like the search engine prompts. dtXPConfig' :: XPConfig dtXPConfig' = dtXPConfig - { autoComplete = Nothing + { autoComplete = Nothing } --- A list of all of the standard Xmonad prompts +-- A list of all of the standard Xmonad prompts and a key press assigned to them. +-- These are used in conjunction with keybinding I set later in the config. promptList :: [(String, XPConfig -> X ())] promptList = [ ("m", manPrompt) -- manpages prompt , ("p", passPrompt) -- get passwords (requires 'pass') @@ -207,20 +370,21 @@ promptList = [ ("m", manPrompt) -- manpages prompt , ("x", xmonadPrompt) -- xmonad prompt ] --- A list of my custom prompts +-- Same as the above list except this is for my custom prompts. promptList' :: [(String, XPConfig -> String -> X (), String)] promptList' = [ ("c", calcPrompt, "qalc") -- requires qalculate-gtk ] + ------------------------------------------------------------------------ -- CUSTOM PROMPTS ------------------------------------------------------------------------ -- calcPrompt requires a cli calculator called qalcualte-gtk. --- You could use this as a template for other custom prompts that +-- You could use this as a template for other custom prompts that -- use command line programs that return a single line of output. -calcPrompt :: XPConfig -> String -> X () +calcPrompt :: XPConfig -> String -> X () calcPrompt c ans = - inputPrompt c (trim ans) ?+ \input -> - liftIO(runProcessWithInput "qalc" [input] "") >>= calcPrompt c + inputPrompt c (trim ans) ?+ \input -> + liftIO(runProcessWithInput "qalc" [input] "") >>= calcPrompt c where trim = f . f where f = reverse . dropWhile isSpace @@ -232,7 +396,7 @@ dtXPKeymap :: M.Map (KeyMask,KeySym) (XP ()) dtXPKeymap = M.fromList $ map (first $ (,) controlMask) -- control + [ (xK_z, killBefore) -- kill line backwards - , (xK_k, killAfter) -- kill line fowards + , (xK_k, killAfter) -- kill line forwards , (xK_a, startOfLine) -- move to the beginning of the line , (xK_e, endOfLine) -- move to the end of the line , (xK_m, deleteString Next) -- delete a character foward @@ -302,256 +466,103 @@ searchList = [ ("a", archwiki) , ("z", S.amazon) ] ------------------------------------------------------------------------- --- TREE SELECT ------------------------------------------------------------------------- -treeselectAction :: TS.TSConfig (X ()) -> X () -treeselectAction a = TS.treeselectAction a - [ Node (TS.TSNode "hello" "displays hello" (spawn "xmessage hello!")) [] - , Node (TS.TSNode "shutdown" "poweroff the system" (spawn "shutdown")) [] - , Node (TS.TSNode "xmonad" "working with xmonad" (return ())) - [ Node (TS.TSNode "edit xmonad" "edit xmonad" (spawn (myTerminal ++ " -e vim ~/.xmonad/xmonad.hs"))) [] - , Node (TS.TSNode "recompile xmonad" "recompile xmonad" (spawn "xmonad --recompile")) [] - , Node (TS.TSNode "restart xmonad" "restart xmonad" (spawn "xmonad --restart")) [] - ] - , Node (TS.TSNode "brightness" "Sets screen brightness using xbacklight" (return ())) - [ Node (TS.TSNode "bright" "full power" (spawn "xbacklight -set 100")) [] - , Node (TS.TSNode "normal" "normal brightness (50%)" (spawn "xbacklight -set 50")) [] - , Node (TS.TSNode "dim" "quite dark" (spawn "xbacklight -set 10")) [] - ] - , Node (TS.TSNode "system monitors" "system monitoring applications" (return ())) - [ Node (TS.TSNode "htop" "a much better top" (spawn (myTerminal ++ " -e htop"))) [] - , Node (TS.TSNode "glances" "an eye on your system" (spawn (myTerminal ++ " -e glances"))) [] - , Node (TS.TSNode "gtop" "a more graphical top" (spawn (myTerminal ++ " -e gtop"))) [] - , Node (TS.TSNode "nmon" "network monitor" (spawn (myTerminal ++ " -e nmon"))) [] - , Node (TS.TSNode "s-tui" "stress your system" (spawn (myTerminal ++ " -e s-tui"))) [] - ] - ] -tsDefaultConfig :: TS.TSConfig a -tsDefaultConfig = TS.TSConfig { TS.ts_hidechildren = True - , TS.ts_background = 0xdd292d3e - , TS.ts_font = "xft:Mononoki Nerd Font:bold:pixelsize=13" - , TS.ts_node = (0xffd0d0d0, 0xff202331) - , TS.ts_nodealt = (0xffd0d0d0, 0xff292d3e) - , TS.ts_highlight = (0xffffffff, 0xff755999) - , TS.ts_extra = 0xffd0d0d0 - , TS.ts_node_width = 200 - , TS.ts_node_height = 20 - , TS.ts_originX = 0 - , TS.ts_originY = 0 - , TS.ts_indent = 80 - , TS.ts_navigate = myTreeNavigation - } - -myTreeNavigation = M.fromList - [ ((0, xK_Escape), TS.cancel) - , ((0, xK_Return), TS.select) - , ((0, xK_space), TS.select) - , ((0, xK_Up), TS.movePrev) - , ((0, xK_Down), TS.moveNext) - , ((0, xK_Left), TS.moveParent) - , ((0, xK_Right), TS.moveChild) - , ((0, xK_k), TS.movePrev) - , ((0, xK_j), TS.moveNext) - , ((0, xK_h), TS.moveParent) - , ((0, xK_l), TS.moveChild) - , ((0, xK_o), TS.moveHistBack) - , ((0, xK_i), TS.moveHistForward) - ] - ------------------------------------------------------------------------- --- KEYBINDINGS ------------------------------------------------------------------------- --- I am using the Xmonad.Util.EZConfig module which allows keybindings --- to be written in simpler, emacs-like format. -myKeys :: [(String, X ())] -myKeys = - -- Xmonad - [ ("M-C-r", spawn "xmonad --recompile") -- Recompiles xmonad - , ("M-S-r", spawn "xmonad --restart") -- Restarts xmonad - , ("M-S-q", io exitSuccess) -- Quits xmonad - - -- Open my preferred terminal - , ("M-", spawn (myTerminal)) - - -- Run Prompt - , ("M-S-", shellPrompt dtXPConfig) -- Shell Prompt - - -- Windows - , ("M-S-c", kill1) -- Kill the currently focused client - , ("M-S-a", killAll) -- Kill all windows on current workspace - - -- Floating windows - , ("M-f", sendMessage (T.Toggle "floats")) -- Toggles my 'floats' layout - , ("M-", withFocused $ windows . W.sink) -- Push floating window back to tile - , ("M-S-", sinkAll) -- Push ALL floating windows to tile - - -- Grid Select - , ("C-g g", spawnSelected' myAppGrid) -- grid select favorite apps - , ("C-g t", goToSelected $ mygridConfig myColorizer) -- goto selected - , ("C-g b", bringSelected $ mygridConfig myColorizer) -- bring selected - - -- Tree Select - , ("M-S-t", treeselectAction tsDefaultConfig) -- tree select actions menu - , ("C-t t", TS.treeselectWorkspace tsDefaultConfig myWorkspaces W.greedyView) - , ("C-t g", TS.treeselectWorkspace tsDefaultConfig myWorkspaces W.shift) - - -- Windows navigation - , ("M-m", windows W.focusMaster) -- Move focus to the master window - , ("M-j", windows W.focusDown) -- Move focus to the next window - , ("M-k", windows W.focusUp) -- Move focus to the prev window - --, ("M-S-m", windows W.swapMaster) -- Swap the focused window and the master window - , ("M-S-j", windows W.swapDown) -- Swap focused window with next window - , ("M-S-k", windows W.swapUp) -- Swap focused window with prev window - , ("M-", promote) -- Moves focused window to master, others maintain order - , ("M1-S-", rotSlavesDown) -- Rotate all windows except master and keep focus in place - , ("M1-C-", rotAllDown) -- Rotate all the windows in the current stack - --, ("M-S-s", windows copyToAll) - , ("M-C-s", killAllOtherCopies) - - -- Layouts - , ("M-", sendMessage NextLayout) -- Switch to next layout - , ("M-C-M1-", sendMessage Arrange) - , ("M-C-M1-", sendMessage DeArrange) - , ("M-", sendMessage (MT.Toggle NBFULL) >> sendMessage ToggleStruts) -- Toggles noborder/full - , ("M-S-", sendMessage ToggleStruts) -- Toggles struts - , ("M-S-n", sendMessage $ MT.Toggle NOBORDERS) -- Toggles noborder - , ("M-", sendMessage (IncMasterN 1)) -- Increase number of clients in master pane - , ("M-", sendMessage (IncMasterN (-1))) -- Decrease number of clients in master pane - , ("M-S-", increaseLimit) -- Increase number of windows - , ("M-S-", decreaseLimit) -- Decrease number of windows - - , ("M-h", sendMessage Shrink) -- Shrink horiz window width - , ("M-l", sendMessage Expand) -- Expand horiz window width - , ("M-C-j", sendMessage MirrorShrink) -- Shrink vert window width - , ("M-C-k", sendMessage MirrorExpand) -- Exoand vert window width - - -- Workspaces - , ("M-.", nextScreen) -- Switch focus to next monitor - , ("M-,", prevScreen) -- Switch focus to prev monitor - , ("M-S-", shiftTo Next nonNSP >> moveTo Next nonNSP) -- Shifts focused window to next ws - , ("M-S-", shiftTo Prev nonNSP >> moveTo Prev nonNSP) -- Shifts focused window to prev ws - - -- Scratchpads - , ("M-C-", namedScratchpadAction myScratchPads "terminal") - , ("M-C-c", namedScratchpadAction myScratchPads "mocp") - - -- Controls for mocp music player. - , ("M-u p", spawn "mocp --play") - , ("M-u l", spawn "mocp --next") - , ("M-u h", spawn "mocp --previous") - , ("M-u ", spawn "mocp --toggle-pause") - - -- Emacs - , ("C-e e", spawn "emacsclient -c -a ''") -- start emacs - , ("C-e a", spawn "emacsclient -c -a '' --eval '(emms)'") -- emms emacs audio player - , ("C-e b", spawn "emacsclient -c -a '' --eval '(ibuffer)'") -- list emacs buffers - , ("C-e d", spawn "emacsclient -c -a '' --eval '(dired nil)'") -- dired emacs file manager - , ("C-e m", spawn "emacsclient -c -a '' --eval '(mu4e)'") -- mu4e emacs email client - , ("C-e n", spawn "emacsclient -c -a '' --eval '(elfeed)'") -- elfeed emacs rss client - , ("C-e s", spawn "emacsclient -c -a '' --eval '(eshell)'") -- eshell within emacs - , ("C-e t", spawn "emacsclient -c -a '' --eval '(+vterm/here nil)'") -- eshell within emacs - - --- My Applications (Super+Alt+Key) - , ("M-M1-a", spawn (myTerminal ++ " -e ncpamixer")) - , ("M-M1-b", spawn "surf www.youtube.com/c/DistroTube/") - --, ("M-M1-e", spawn (myTerminal ++ " -e neomutt")) - , ("M-M1-e", spawn "emacsclient -c -a '' --eval '(mu4e)'") - , ("M-M1-f", spawn (myTerminal ++ " -e sh ./.config/vifm/scripts/vifmrun")) - , ("M-M1-i", spawn (myTerminal ++ " -e irssi")) - , ("M-M1-j", spawn (myTerminal ++ " -e joplin")) - , ("M-M1-l", spawn (myTerminal ++ " -e lynx -cfg=~/.lynx/lynx.cfg -lss=~/.lynx/lynx.lss gopher://distro.tube")) - , ("M-M1-m", spawn (myTerminal ++ " -e mocp")) - , ("M-M1-n", spawn "emacsclient -c -a '' --eval '(elfeed)'") - , ("M-M1-p", spawn (myTerminal ++ " -e pianobar")) - , ("M-M1-r", spawn (myTerminal ++ " -e rtv")) - , ("M-M1-t", spawn (myTerminal ++ " -e toot curses")) - , ("M-M1-w", spawn (myTerminal ++ " -e wopr report.xml")) - , ("M-M1-y", spawn (myTerminal ++ " -e youtube-viewer")) - - -- Multimedia Keys - , ("", spawn "cmus toggle") - , ("", spawn "cmus prev") - , ("", spawn "cmus next") - -- , ("", spawn "amixer set Master toggle") -- Bug prevents it from toggling correctly in 12.04. - , ("", spawn "amixer set Master 5%- unmute") - , ("", spawn "amixer set Master 5%+ unmute") - , ("", spawn "firefox") - , ("", safeSpawn "firefox" ["https://www.google.com/"]) - , ("", runOrRaise "geary" (resource =? "thunderbird")) - , ("", runOrRaise "gcalctool" (resource =? "gcalctool")) - , ("", spawn "toggleeject") - , ("", spawn "scrotd 0") - ] - -- Appending search engines to keybindings list - ++ [("M-s " ++ k, S.promptSearch dtXPConfig' f) | (k,f) <- searchList ] - ++ [("M-S-s " ++ k, S.selectSearch f) | (k,f) <- searchList ] - ++ [("M-p " ++ k, f dtXPConfig') | (k,f) <- promptList ] - ++ [("M-p " ++ k, f dtXPConfig' g) | (k,f,g) <- promptList' ] - -- Appending named scratchpads to keybindings list - where nonNSP = WSIs (return (\ws -> W.tag ws /= "nsp")) - nonEmptyNonNSP = WSIs (return (\ws -> isJust (W.stack ws) && W.tag ws /= "nsp")) - ------------------------------------------------------------------------ -- WORKSPACES ------------------------------------------------------------------------ -- My workspaces are clickable meaning that the mouse can be used to switch --- workspaces. This requires xdotool. +-- workspaces. This requires xdotool. You need to use UnsafeStdInReader instead +-- of simply StdInReader in xmobar config so you can pass actions to it. xmobarEscape :: String -> String xmobarEscape = concatMap doubleLts where doubleLts '<' = "<<" doubleLts x = [x] - --- myWorkspaces :: [String] --- myWorkspaces = clickable . map xmobarEscape - -- $ ["dev", "www", "sys", "doc", "vbox", "chat", "mus", "vid", "gfx"] - -- where - -- clickable l = [ "" ++ ws ++ "" | - -- (i,ws) <- zip [1..9] l, - -- let n = i ] +{- Commented out clickable xmobar workspaces to use TreeSelect workspaces. + +myWorkspaces :: [String] +myWorkspaces = clickable . map xmobarEscape + $ ["dev", "www", "sys", "doc", "vbox", "chat", "mus", "vid", "gfx"] + where + clickable l = [ "" ++ ws ++ "" | + (i,ws) <- zip [1..9] l, + let n = i ] + +End of comment -} + +-- TreeSelect workspaces myWorkspaces :: Forest String -myWorkspaces = [ Node "Browser" [] -- a workspace for your browser - , Node "Home" -- for everyday activity's - [ Node "1" [] -- with 4 extra sub-workspaces, for even more activity's - , Node "2" [] - , Node "3" [] - , Node "4" [] +myWorkspaces = [ Node "dev" + [ Node "terminal" [] + , Node "emacs" [] + , Node "docs" [] + , Node "files" [] + , Node "programming" + [ Node "haskell" [] + , Node "python" [] + , Node "shell" [] + ] + , Node "virtualization" [] ] - , Node "Programming" -- for all your programming needs - [ Node "Haskell" [] - , Node "Docs" [] -- documentation + , Node "web" + [ Node "browser" [] + , Node "chat" [] + , Node "email" [] + , Node "rss" [] + , Node "video conferencing" [] + ] + , Node "graphics" + [ Node "gimp" [] + , Node "image viewer" [] + ] + , Node "music" + [ Node "audio editor" [] + , Node "music player" [] + ] + , Node "video" + [ Node "obs" [] + , Node "movie player" [] + , Node "video editor" [] ] ] + ------------------------------------------------------------------------ -- 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 +-- if you are using clickable workspaces. You need the className or title -- of the program. Use xprop to get this info. myManageHook :: XMonad.Query (Data.Monoid.Endo WindowSet) myManageHook = composeAll -- using 'doShift ( myWorkspaces !! 7)' sends program to workspace 8! - -- I'm doing it this way because otherwise I would have to write out + -- I'm doing it this way because otherwise I would have to write out -- the full name of my clickable workspaces, which would look like: -- doShift "gfx" - [ className =? "obs" --> doShift ( "Browser") - , title =? "firefox" --> doShift ( "Browser") - , title =? "qutebrowser" --> doShift ( "Browser") - , className =? "vlc" --> doShift ( "Browser") - , className =? "Gimp" --> doShift ( "Browser") + [ className =? "obs" --> doShift ( "video.obs" ) + , title =? "firefox" --> doShift ( "web.browser" ) + , title =? "qutebrowser" --> doShift ( "web.browser" ) + , className =? "mpv" --> doShift ( "video.movie player" ) + , className =? "vlc" --> doShift ( "video.movie player" ) + , className =? "Gimp" --> doShift ( "graphics.gimp") , className =? "Gimp" --> doFloat , title =? "Oracle VM VirtualBox Manager" --> doFloat - , className =? "Oracle VM VirtualBox Manager" --> doShift ( "Browser") + , className =? "VirtualBox Manager" --> doShift ( "dev.virtualization" ) , (className =? "firefox" <&&> resource =? "Dialog") --> doFloat -- Float Firefox Dialog ] <+> namedScratchpadManageHook myScratchPads +------------------------------------------------------------------------ +-- LOGHOOK +------------------------------------------------------------------------ +-- sets opacity for inactive (unfocused) windows. +myLogHook :: X () +myLogHook = fadeInactiveLogHook fadeAmount + where fadeAmount = 0.9 + ------------------------------------------------------------------------ -- LAYOUTS ------------------------------------------------------------------------ @@ -611,7 +622,16 @@ tabs = renamed [Replace "tabs"] , activeTextColor = "#ffffff" , inactiveTextColor = "#d0d0d0" } - + +-- Theme for showWName which prints current workspace when you change workspaces. +myShowWNameTheme :: SWNConfig +myShowWNameTheme = def + { swn_font = "xft:Sans:bold:size=60" + , swn_fade = 1.0 + , swn_bgcolor = "#000000" + , swn_color = "#FFFFFF" + } + -- The layout hook myLayoutHook = avoidStruts $ mouseResize $ windowArrange $ T.toggleLayouts floats $ mkToggle (NBFULL ?? NOBORDERS ?? EOT) myDefaultLayout @@ -630,6 +650,8 @@ myLayoutHook = avoidStruts $ mouseResize $ windowArrange $ T.toggleLayouts float ------------------------------------------------------------------------ -- SCRATCHPADS ------------------------------------------------------------------------ +-- Allows to have several floating scratchpads running different applications. +-- Import Util.NamedScratchpad. Bind a key to namedScratchpadSpawnAction. myScratchPads :: [NamedScratchpad] myScratchPads = [ NS "terminal" spawnTerm findTerm manageTerm , NS "mocp" spawnMocp findMocp manageMocp @@ -652,6 +674,147 @@ myScratchPads = [ NS "terminal" spawnTerm findTerm manageTerm t = 0.95 -h l = 0.95 -w +------------------------------------------------------------------------ +-- KEYBINDINGS +------------------------------------------------------------------------ +-- I am using the Xmonad.Util.EZConfig module which allows keybindings +-- to be written in simpler, emacs-like format. +myKeys :: [(String, X ())] +myKeys = + -- Xmonad + [ ("M-C-r", spawn "xmonad --recompile") -- Recompiles xmonad + , ("M-S-r", spawn "xmonad --restart") -- Restarts xmonad + , ("M-S-q", io exitSuccess) -- Quits xmonad + + -- Open my preferred terminal + , ("M-", spawn myTerminal) + + -- Run Prompt + , ("M-S-", shellPrompt dtXPConfig) -- Shell Prompt + + -- Windows + , ("M-S-c", kill1) -- Kill the currently focused client + , ("M-S-a", killAll) -- Kill all windows on current workspace + + -- Floating windows + , ("M-f", sendMessage (T.Toggle "floats")) -- Toggles my 'floats' layout + , ("M-", withFocused $ windows . W.sink) -- Push floating window back to tile + , ("M-S-", sinkAll) -- Push ALL floating windows to tile + + -- Grid Select (CTRL-g followed by a key) + , ("C-g g", spawnSelected' myAppGrid) -- grid select favorite apps + , ("C-g m", spawnSelected' myBookmarkGrid) -- grid select some bookmarks + , ("C-g c", spawnSelected' myConfigGrid) -- grid select useful config files + , ("C-g t", goToSelected $ mygridConfig myColorizer) -- goto selected window + , ("C-g b", bringSelected $ mygridConfig myColorizer) -- bring selected window + + -- Tree Select/ + -- tree select actions menu + , ("C-t a", treeselectAction tsDefaultConfig) + -- tree select workspaces menu + , ("C-t t", TS.treeselectWorkspace tsDefaultConfig myWorkspaces W.greedyView) + -- tree select choose workspace to send window + , ("C-t g", TS.treeselectWorkspace tsDefaultConfig myWorkspaces W.shift) + + -- Windows navigation + , ("M-m", windows W.focusMaster) -- Move focus to the master window + , ("M-j", windows W.focusDown) -- Move focus to the next window + , ("M-k", windows W.focusUp) -- Move focus to the prev window + --, ("M-S-m", windows W.swapMaster) -- Swap the focused window and the master window + , ("M-S-j", windows W.swapDown) -- Swap focused window with next window + , ("M-S-k", windows W.swapUp) -- Swap focused window with prev window + , ("M-", promote) -- Moves focused window to master, others maintain order + , ("M1-S-", rotSlavesDown) -- Rotate all windows except master and keep focus in place + , ("M1-C-", rotAllDown) -- Rotate all the windows in the current stack + --, ("M-S-s", windows copyToAll) + , ("M-C-s", killAllOtherCopies) + + -- Layouts + , ("M-", sendMessage NextLayout) -- Switch to next layout + , ("M-C-M1-", sendMessage Arrange) + , ("M-C-M1-", sendMessage DeArrange) + , ("M-", sendMessage (MT.Toggle NBFULL) >> sendMessage ToggleStruts) -- Toggles noborder/full + , ("M-S-", sendMessage ToggleStruts) -- Toggles struts + , ("M-S-n", sendMessage $ MT.Toggle NOBORDERS) -- Toggles noborder + , ("M-", sendMessage (IncMasterN 1)) -- Increase number of clients in master pane + , ("M-", sendMessage (IncMasterN (-1))) -- Decrease number of clients in master pane + , ("M-S-", increaseLimit) -- Increase number of windows + , ("M-S-", decreaseLimit) -- Decrease number of windows + + , ("M-h", sendMessage Shrink) -- Shrink horiz window width + , ("M-l", sendMessage Expand) -- Expand horiz window width + , ("M-C-j", sendMessage MirrorShrink) -- Shrink vert window width + , ("M-C-k", sendMessage MirrorExpand) -- Exoand vert window width + + -- Workspaces + , ("M-.", nextScreen) -- Switch focus to next monitor + , ("M-,", prevScreen) -- Switch focus to prev monitor + , ("M-S-", shiftTo Next nonNSP >> moveTo Next nonNSP) -- Shifts focused window to next ws + , ("M-S-", shiftTo Prev nonNSP >> moveTo Prev nonNSP) -- Shifts focused window to prev ws + + -- Scratchpads + , ("M-C-", namedScratchpadAction myScratchPads "terminal") + , ("M-C-c", namedScratchpadAction myScratchPads "mocp") + + -- Controls for mocp music player. + , ("M-u p", spawn "mocp --play") + , ("M-u l", spawn "mocp --next") + , ("M-u h", spawn "mocp --previous") + , ("M-u ", spawn "mocp --toggle-pause") + + -- Emacs (CTRL-e followed by a key) + , ("C-e e", spawn "emacsclient -c -a ''") -- start emacs + , ("C-e a", spawn "emacsclient -c -a '' --eval '(emms)'") -- emms emacs audio player + , ("C-e b", spawn "emacsclient -c -a '' --eval '(ibuffer)'") -- list emacs buffers + , ("C-e d", spawn "emacsclient -c -a '' --eval '(dired nil)'") -- dired emacs file manager + , ("C-e m", spawn "emacsclient -c -a '' --eval '(mu4e)'") -- mu4e emacs email client + , ("C-e n", spawn "emacsclient -c -a '' --eval '(elfeed)'") -- elfeed emacs rss client + , ("C-e s", spawn "emacsclient -c -a '' --eval '(eshell)'") -- eshell within emacs + , ("C-e t", spawn "emacsclient -c -a '' --eval '(+vterm/here nil)'") -- eshell within emacs + + --- My Applications (Super+Alt+Key) + , ("M-M1-a", spawn (myTerminal ++ " -e ncpamixer")) + , ("M-M1-b", spawn "surf www.youtube.com/c/DistroTube/") + --, ("M-M1-e", spawn (myTerminal ++ " -e neomutt")) + , ("M-M1-e", spawn "emacsclient -c -a '' --eval '(mu4e)'") + , ("M-M1-f", spawn (myTerminal ++ " -e sh ./.config/vifm/scripts/vifmrun")) + , ("M-M1-i", spawn (myTerminal ++ " -e irssi")) + , ("M-M1-j", spawn (myTerminal ++ " -e joplin")) + , ("M-M1-l", spawn (myTerminal ++ " -e lynx -cfg=~/.lynx/lynx.cfg -lss=~/.lynx/lynx.lss gopher://distro.tube")) + , ("M-M1-m", spawn (myTerminal ++ " -e mocp")) + , ("M-M1-n", spawn "emacsclient -c -a '' --eval '(elfeed)'") + , ("M-M1-p", spawn (myTerminal ++ " -e pianobar")) + , ("M-M1-r", spawn (myTerminal ++ " -e rtv")) + , ("M-M1-t", spawn (myTerminal ++ " -e toot curses")) + , ("M-M1-w", spawn (myTerminal ++ " -e wopr report.xml")) + , ("M-M1-y", spawn (myTerminal ++ " -e youtube-viewer")) + + -- Multimedia Keys + , ("", spawn "cmus toggle") + , ("", spawn "cmus prev") + , ("", spawn "cmus next") + -- , ("", spawn "amixer set Master toggle") -- Bug prevents it from toggling correctly in 12.04. + , ("", spawn "amixer set Master 5%- unmute") + , ("", spawn "amixer set Master 5%+ unmute") + , ("", spawn "firefox") + , ("", safeSpawn "firefox" ["https://www.google.com/"]) + , ("", runOrRaise "geary" (resource =? "thunderbird")) + , ("", runOrRaise "gcalctool" (resource =? "gcalctool")) + , ("", spawn "toggleeject") + , ("", spawn "scrotd 0") + ] + -- Appending search engine prompts to keybindings list. + -- Look at "search engines" section of this config for values for "k". + ++ [("M-s " ++ k, S.promptSearch dtXPConfig' f) | (k,f) <- searchList ] + ++ [("M-S-s " ++ k, S.selectSearch f) | (k,f) <- searchList ] + -- Appending some extra xprompts to keybindings list. + -- Look at "xprompt settings" section this of config for values for "k". + ++ [("M-p " ++ k, f dtXPConfig') | (k,f) <- promptList ] + ++ [("M-p " ++ k, f dtXPConfig' g) | (k,f,g) <- promptList' ] + -- 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")) + ------------------------------------------------------------------------ -- MAIN ------------------------------------------------------------------------ @@ -666,31 +829,31 @@ main = do { manageHook = ( isFullscreen --> doFullFloat ) <+> myManageHook <+> manageDocks -- Run xmonad commands from command line with "xmonadctl command". Commands include: -- shrink, expand, next-layout, default-layout, restart-wm, xterm, kill, refresh, run, - -- focus-up, focus-down, swap-up, swap-down, swap-master, sink, quit-wm. You can run + -- focus-up, focus-down, swap-up, swap-down, swap-master, sink, quit-wm. You can run -- "xmonadctl 0" to generate full list of commands written to ~/.xsession-errors. - , handleEventHook = serverModeEventHookCmd - <+> serverModeEventHook + , handleEventHook = serverModeEventHookCmd + <+> serverModeEventHook <+> serverModeEventHookF "XMONAD_PRINT" (io . putStrLn) <+> docksEventHook , modMask = myModMask , terminal = myTerminal , startupHook = myStartupHook - , layoutHook = myLayoutHook + , layoutHook = showWName' myShowWNameTheme myLayoutHook , workspaces = TS.toWorkspaces myWorkspaces , borderWidth = myBorderWidth , normalBorderColor = myNormColor , focusedBorderColor = myFocusColor - , logHook = dynamicLogWithPP $ xmobarPP - { ppOutput = \x -> hPutStrLn xmproc0 x >> hPutStrLn xmproc1 x >> hPutStrLn xmproc2 x - , ppCurrent = xmobarColor "#c3e88d" "" . wrap "[" "]" -- Current workspace in xmobar - , ppVisible = xmobarColor "#c3e88d" "" -- Visible but not current workspace - , ppHidden = xmobarColor "#82AAFF" "" . wrap "*" "" -- Hidden workspaces in xmobar - , ppHiddenNoWindows = xmobarColor "#F07178" "" -- Hidden workspaces (no windows) - , ppTitle = xmobarColor "#d0d0d0" "" . shorten 60 -- Title of active window in xmobar - , ppSep = " | " -- Separators in xmobar - , ppUrgent = xmobarColor "#C45500" "" . wrap "!" "!" -- Urgent workspace - , ppExtras = [windowCount] -- # of windows current workspace - , ppOrder = \(ws:l:t:ex) -> [ws,l]++ex++[t] - } - } `additionalKeysP` myKeys - + , logHook = workspaceHistoryHook <+> myLogHook <+> dynamicLogWithPP xmobarPP + { ppOutput = \x -> hPutStrLn xmproc0 x >> hPutStrLn xmproc1 x >> hPutStrLn xmproc2 x + , ppCurrent = xmobarColor "#c3e88d" "" . wrap "[" "]" -- Current workspace in xmobar + , ppVisible = xmobarColor "#c3e88d" "" -- Visible but not current workspace + , ppHidden = xmobarColor "#82AAFF" "" . wrap "*" "" -- Hidden workspaces in xmobar + -- , ppHiddenNoWindows = xmobarColor "#F07178" "" -- Hidden workspaces (no windows) + , ppHiddenNoWindows= \( _ ) -> "" -- Only shows visible workspaces. Useful for TreeSelect. + , ppTitle = xmobarColor "#d0d0d0" "" . shorten 60 -- Title of active window in xmobar + , ppSep = " | " -- Separators in xmobar + , ppUrgent = xmobarColor "#C45500" "" . wrap "!" "!" -- Urgent workspace + , ppExtras = [windowCount] -- # of windows current workspace + , ppOrder = \(ws:l:t:ex) -> [ws,l]++ex++[t] + } + } `additionalKeysP` myKeys