https://github.com/harryprayiv/nix-config/blob/intelTower/home/programs/xmonad/config.hs
import Control.Monad ( replicateM_
, filterM
, liftM
, join
, unless
)
import Data.IORef
import Data.Char
import Data.List
import Data.Foldable ( traverse_ )
import Data.Monoid
import Graphics.X11.ExtraTypes.XF86
import System.Exit
import System.Directory ( doesFileExist )
import System.IO ( hPutStr
, hClose
, writeFile
)
import XMonad
import XMonad.Actions.CycleWS ( Direction1D(..)
, WSType(..)
, anyWS
, findWorkspace
)
import XMonad.Actions.DynamicProjects ( Project(..)
, dynamicProjects
, switchProjectPrompt
)
import XMonad.Actions.DynamicWorkspaces ( removeWorkspace )
import XMonad.Actions.FloatKeys ( keysAbsResizeWindow
, keysResizeWindow
)
import XMonad.Actions.RotSlaves ( rotSlavesUp )
import XMonad.Actions.SpawnOn ( manageSpawn
, spawnOn
)
import XMonad.Actions.WithAll ( killAll )
import XMonad.Actions.CopyWindow ( killAllOtherCopies
, copy
, copyToAll
, kill1
)
import XMonad.Hooks.EwmhDesktops ( ewmh
, ewmhFullscreen
)
import XMonad.Hooks.FadeInactive
import XMonad.Hooks.InsertPosition ( Focus(..)
, Position(..)
--, Position(Above)
, insertPosition
)
import XMonad.Hooks.ManageDocks ( Direction2D(..)
, ToggleStruts(..)
, avoidStruts
, docks
)
import XMonad.Hooks.ManageHelpers ( (-?>)
, composeOne
, doCenterFloat
, doFullFloat
, isDialog
, isFullscreen
, isInProperty
)
import XMonad.Hooks.UrgencyHook ( UrgencyHook(..)
, withUrgencyHook
)
import XMonad.Layout.Gaps ( GapSpec(..)
, GapMessage (IncGap, DecGap)
, weakModifyGaps
, gaps
, setGaps
)
import XMonad.Layout.MultiToggle ( Toggle(..)
, mkToggle
, single
)
import XMonad.Layout.MultiToggle.Instances ( StdTransformers(NBFULL) )
import XMonad.Layout.NoBorders ( smartBorders )
import XMonad.Layout.PerWorkspace ( onWorkspace )
import XMonad.Layout.Spacing ( SpacingModifier(..)
, spacing
, incScreenWindowSpacing
, decScreenWindowSpacing
)
import XMonad.Layout.HintedGrid
import XMonad.Layout.ThreeColumns ( ThreeCol(..) )
import XMonad.Layout.Spiral
import XMonad.Layout.Fullscreen
import XMonad.Layout.OneBig
import XMonad.Layout.Tabbed
import XMonad.Layout.Reflect ( reflectHoriz )
import XMonad.Layout.ResizableTile
import XMonad.Prompt ( XPConfig(..)
, amberXPConfig
, XPPosition(CenteredAt)
)
import XMonad.Util.EZConfig ( mkNamedKeymap
, additionalKeys
, removeKeys
)
import XMonad.Util.NamedActions ( (^++^)
, NamedAction (..)
, addDescrKeys'
, addName
, showKm
, subtitle
)
import XMonad.Util.NamedScratchpad ( NamedScratchpad(..)
, customFloating
, defaultFloating
, namedScratchpadAction
, namedScratchpadManageHook
)
import XMonad.Util.Run ( safeSpawn
, spawnPipe
)
import XMonad.Util.SpawnOnce ( spawnOnce )
import XMonad.Util.WorkspaceCompare ( getSortByIndex )
import XMonad.Util.XSelection ( safePromptSelection )
import qualified Control.Exception as E
import qualified Data.Map as M
import qualified XMonad.StackSet as W
import qualified XMonad.Util.NamedWindows as W
import qualified XMonad.Util.ExtensibleState as XS -- Custom State
-- Imports for Polybar --
import qualified Codec.Binary.UTF8.String as UTF8
import qualified Data.Set as S
import qualified DBus as D
import qualified DBus.Client as D
import XMonad.Hooks.DynamicLog
import HueLighting
main :: IO ()
main = mkDbusClient >>= main'
main' :: D.Client -> IO ()
main' dbus = xmonad . docks . ewmh . ewmhFullscreen . dynProjects . keybindings . urgencyHook $ def
{ terminal = myTerminal
, focusFollowsMouse = False
, clickJustFocuses = False
, borderWidth = 2
, modMask = myModMask
, workspaces = myWS
, normalBorderColor = "#32343d" -- #neutral gray
, focusedBorderColor = "#6C99B8" -- nice blue
, mouseBindings = myMouseBindings
, layoutHook = myLayout
, manageHook = myManageHook
, logHook = myPolybarLogHook dbus
, startupHook = myStartupHook
}
where
myModMask = mod4Mask -- super as the mod key
dynProjects = dynamicProjects projects
keybindings = addDescrKeys' ((myModMask, xK_F1), showKeybindings) myKeys
urgencyHook = withUrgencyHook LibNotifyUrgencyHook
-- Perform an arbitrary action each time xmonad starts or is restarted
-- with mod-q. Used by, e.g., XMonad.Layout.PerWorkspace to initialize
-- per-workspace layout choices.
myStartupHook = startupHook def
-- original idea: https://pbrisbin.com/posts/using_notify_osd_for_xmonad_notifications/
data LibNotifyUrgencyHook = LibNotifyUrgencyHook deriving (Read, Show)
instance UrgencyHook LibNotifyUrgencyHook where
urgencyHook LibNotifyUrgencyHook w = do
name <- W.getName w
maybeIdx <- W.findTag w <$> gets windowset
traverse_ (\i -> safeSpawn "notify-send" [show name, "workspace " ++ i]) maybeIdx
------------------------------------------------------------------------
-- Polybar settings (needs DBus client).
--
mkDbusClient :: IO D.Client
mkDbusClient = do
dbus <- D.connectSession
D.requestName dbus (D.busName_ "org.xmonad.log") opts
return dbus
where
opts = [D.nameAllowReplacement, D.nameReplaceExisting, D.nameDoNotQueue]
-- Emit a DBus signal on log updates
dbusOutput :: D.Client -> String -> IO ()
dbusOutput dbus str =
let opath = D.objectPath_ "/org/xmonad/Log"
iname = D.interfaceName_ "org.xmonad.Log"
mname = D.memberName_ "Update"
signal = D.signal opath iname mname
body = [D.toVariant $ UTF8.decodeString str]
in D.emit dbus $ signal { D.signalBody = body }
polybarHook :: D.Client -> PP
polybarHook dbus =
let wrapper c s | s /= "NSP" = wrap ("%{F" <> c <> "} ") " %{F-}" s
| otherwise = mempty
blue = "#619DEC"
gray = "#8D868F"
white = "#FFFFFF"
orange = "#CF6A4C"
yellow = "#F9F4CD"
green = "#8F9D6A"
darkgray = "#5F5A60"
in def { ppOutput = dbusOutput dbus
, ppCurrent = wrapper blue
, ppVisible = wrapper white
, ppUrgent = wrapper orange
, ppHidden = wrapper gray
, ppHiddenNoWindows = wrapper darkgray
, ppTitle = wrapper yellow . shorten 120
}
myPolybarLogHook dbus = myLogHook <+> dynamicLogWithPP (polybarHook dbus)
***
Dynamic Gaps implementation
-- Gaps between windows
gapSize = 10
newtype GapState = GapIndex Int deriving Show
instance ExtensionClass GapState where
initialValue = GapIndex 0
myGaps :: [GapSpec]
myGaps = [ [(R,10),(L,10),(U,10),(D,10)] -- you do have to specify all directions
, [(R,5),(L,5),(U,5),(D,5)]
, [(R,2),(L,2),(U,2),(D,2)]
, [(R,20),(L,20),(U,20),(D,20)]
, [(R,25),(L,25),(U,25),(D,25)]
, [(R,30),(L,30),(U,30),(D,30)]
, [(R,40),(L,40),(U,40),(D,40)]
, [(R,0),(L,0),(U,100),(D,100)]
, [(R,500),(L,0),(U,0),(D,0)]
, [(R,0),(L,500),(U,0),(D,0)]
, [(R,0),(L,0),(U,400),(D,0)]
, [(R,0),(L,0),(U,0),(D,400)]
, [(R,0),(L,0),(U,0),(D,0)]
]
cycleGaps :: X()
cycleGaps = do
(GapIndex idx) <- XS.gets $ \(GapIndex i) ->
let n = if i >= length myGaps - 1 then 0 else i+1 in GapIndex n
sendMessage (setGaps $ myGaps !! idx)
XS.put $ GapIndex idx
myTerminal = "alacritty"
terminalWithCommand :: String -> String
terminalWithCommand cmd = "alacritty -o shell.program=fish -o shell.args=['-C " <> cmd <> "']"
myBashTerminal = "alacritty --hold -e bash"
myZshTerminal = "alacritty --hold -e zsh"
delayTerminal = "sleep 2s && alacritty"
myGuildView = "alacritty --hold -e ./guild-operators/scripts/cnode-helper-scripts/gLiveView.sh"
cnodeStatus = "alacritty --hold -o font.size=5 -e systemctl status cardano-node"
myCardanoCli = "sleep 20m && alacritty --hold -e node_check"
appLauncher = "rofi -modi drun,ssh,window -show drun -show-icons"
playerctl c = "playerctl --player=spotify,%any " <> c
calcLauncher = "rofi -show calc -modi calc -no-show-match -no-sort"
emojiPicker = "rofi -modi emoji -show emoji -emoji-mode copy"
--spotlight = "rofi -modi spotlight -show spotlight -spotlight-mode copy"
-- Screen Lock
screenLocker = "betterlockscreen -l dim"
------------------------------------------------------------------------
-- Key bindings. Add, modify or remove key bindings here.
showKeybindings :: [((KeyMask, KeySym), NamedAction)] -> NamedAction
showKeybindings xs =
let
filename = "/home/bismuth/.xmonad/keybindings"
command f = "alacritty -e dialog --title 'XMonad Key Bindings' --colors --hline \"$(date)\" --textbox " ++ f ++ " 50 100"
in addName "Show Keybindings" $ do
b <- liftIO $ doesFileExist filename
unless b $ liftIO (writeFile filename (unlines $ showKm xs))
spawnOn webWs $ command filename -- show dialog on webWs
windows $ W.greedyView webWs -- switch to webWs
-- use "xev" to figure out exactly what key you are pressing
myKeys conf@XConfig {XMonad.modMask = modm} =
keySet "Applications"
[ key "Mastodon" (modm .|. controlMask, xK_t ) $ spawnOn comWs "brave --app=https://mastodon.social/"
, key "Youtube" (modm .|. controlMask, xK_y ) $ spawnOn webWs "brave --app=https://youtube.com/"
, key "Private Browser" (modm .|. controlMask, xK_p ) $ spawnOn webWs "brave --incognito"
-- , key "Home Page w/App" (modm .|. controlMask, xK_a ) $ spawnOn webWs "brave --app=https://prettycoffee.github.io/fluidity/"
] ^++^
keySet "Lighting Cues"
[ key "DarkWarm" (0, xF86XK_MonBrightnessDown ) (runLightCue 0)
, key "BrighterWarm" (0, xF86XK_MonBrightnessUp ) (runLightCue 2)
, key "FullWarm" (controlMask, xF86XK_MonBrightnessUp ) (runLightCue 3)
, key "Prev Lighting Cue" (modm, xF86XK_MonBrightnessDown ) prevLightCue
, key "Next Lighting Cue" (modm, xF86XK_MonBrightnessUp ) nextLightCue
, key "FullCold" (modm .|. controlMask, xF86XK_MonBrightnessUp ) (runLightCue 6)
] ^++^
keySet "Audio"
[ key "Mute" (0, xF86XK_AudioMute ) $ spawn "amixer -q set Master toggle"
, key "Lower volume" (0, xF86XK_AudioLowerVolume ) $ spawn "amixer -q set Master 3%-"
, key "Raise volume" (0, xF86XK_AudioRaiseVolume ) $ spawn "amixer -q set Master 3%+"
, key "Lower S volume" (modm, xF86XK_AudioLowerVolume ) $ spawn $ playerctl "volume 0.05-"
, key "Raise S volume" (modm, xF86XK_AudioRaiseVolume ) $ spawn $ playerctl "volume 0.05+"
, key "Mute S volume" (modm, xF86XK_AudioMute ) $ spawn $ playerctl "volume 0.0"
, key "100% S volume" (modm .|. shiftMask , xF86XK_AudioMute ) $ spawn $ playerctl "volume 1.0"
, key "Play / Pause" (0, xF86XK_AudioPlay ) $ spawn $ playerctl "play-pause"
, key "Stop" (0, xF86XK_AudioStop ) $ spawn $ playerctl "stop"
, key "Previous" (0, xF86XK_AudioPrev ) $ spawn $ playerctl "previous"
, key "Next" (0, xF86XK_AudioNext ) $ spawn $ playerctl "next"
] ^++^
keySet "Launchers"
[ key "Terminal" (modm .|. shiftMask , xK_Return ) $ spawn (XMonad.terminal conf)
, key "Bash Terminal" (modm .|. controlMask, xK_b ) $ spawn myBashTerminal
, key "Zsh Terminal" (modm .|. controlMask, xK_z ) $ spawn myZshTerminal
, key "Apps (Rofi)" (0, xF86XK_LaunchA ) $ spawn appLauncher
, key "Calc (Rofi)" (modm .|. shiftMask , xK_c ) $ spawn calcLauncher
, key "Emojis (Rofi)" (modm .|. shiftMask , xK_m ) $ spawn emojiPicker
, key "Lock screen" (modm .|. controlMask, xK_l ) $ spawn screenLocker >> runLightCue 8
] ^++^
keySet "Layouts"
[ key "Next" (modm , xK_space ) $ sendMessage NextLayout
, key "SpaceInc" (modm , xK_g ) $ incScreenWindowSpacing 2
, key "SpaceDec" (modm .|. shiftMask , xK_g ) $ decScreenWindowSpacing 2
, key "BorderSwitch" (modm , xK_b ) cycleGaps
, key "Reset" (modm .|. shiftMask , xK_space ) $ setLayout (XMonad.layoutHook conf)
, key "Fullscreen" (modm , xK_f ) $ sendMessage (Toggle NBFULL)
] ^++^
keySet "Polybar"
[ key "Toggle" (modm , xK_equal ) togglePolybar
] ^++^
keySet "Projects"
[ key "Switch prompt" (0, xF86XK_KbdBrightnessDown ) $ switchProjectPrompt projectsTheme
] ^++^
keySet "Scratchpads"
[ key "bottom" (0, xF86XK_LaunchB ) $ runScratchpadApp btm
, key "GuildView" (modm .|. controlMask, xK_g ) $ spawnOn spoWs myGuildView
, key "Files" (modm .|. controlMask, xK_f ) $ runScratchpadApp nautilus
, key "Screen recorder" (modm .|. controlMask, xK_r ) $ runScratchpadApp scr
-- , key "Spotify" (modm .|. controlMask, xK_s ) $ runScratchpadApp spotify
, key "Mpv" (modm .|. controlMask, xK_m ) $ safePromptSelection "mpv"
, key "Gimp" (modm .|. controlMask, xK_i ) $ runScratchpadApp gimp
] ^++^
keySet "Screens" switchScreen ^++^
keySet "System"
[ key "Toggle status bar gap" (modm .|. shiftMask, xK_b ) toggleStruts
, key "Logout (quit XMonad)" (modm .|. shiftMask, xK_q ) $ io exitSuccess
, key "Restart XMonad" (modm , xK_q ) $ spawn "xmonad --recompile; xmonad --restart"
, key "Capture entire screen" (modm , xK_Print ) $ spawn "flameshot full -p ~/Pictures/flameshot/"
, key "Switch keyboard layout" (modm , xK_F8 ) $ spawn "kls"
, key "Disable CapsLock" (modm , xK_F9 ) $ spawn "setxkbmap -option ctrl:nocaps"
] ^++^
keySet "Windows"
[ key "Close focused" (modm , xK_BackSpace ) kill1
, key "Close all in ws" (modm .|. shiftMask, xK_BackSpace ) killAll
, key "Refresh size" (modm , xK_n ) refresh
, key "Focus next" (modm , xK_j ) $ windows W.focusDown
, key "Focus previous" (modm , xK_k ) $ windows W.focusUp
, key "Focus master" (modm , xK_m ) $ windows W.focusMaster
, key "Swap master" (modm , xK_Return ) $ windows W.swapMaster
, key "Swap next" (modm .|. shiftMask, xK_j ) $ windows W.swapDown
, key "Swap previous" (modm .|. shiftMask, xK_k ) $ windows W.swapUp
, key "Shrink master" (modm , xK_h ) $ sendMessage Shrink
, key "Expand master" (modm , xK_l ) $ sendMessage Expand
, key "Mirror shrink" (modm , xK_a ) $ sendMessage MirrorShrink
, key "Mirror grow" (modm , xK_z ) $ sendMessage MirrorExpand
, key "Switch to tile" (modm , xK_t ) $ withFocused (windows . W.sink)
, key "Rotate slaves" (modm .|. shiftMask, xK_Tab ) rotSlavesUp
, key "Decrease size" (modm , xK_d ) $ withFocused (keysResizeWindow (-10,-10) (1,1))
, key "Increase size" (modm , xK_s ) $ withFocused (keysResizeWindow (10,10) (1,1))
, key "Decr abs size" (modm .|. shiftMask, xK_d ) $ withFocused (keysAbsResizeWindow (-10,-10) (1024,752))
, key "Incr abs size" (modm .|. shiftMask, xK_s ) $ withFocused (keysAbsResizeWindow (10,10) (1024,752))
, key "Always Visible" (modm , xK_v ) $ windows copyToAll
, key "Kill Copies" (modm .|. shiftMask, xK_v ) $ killAllOtherCopies
] ^++^
keySet "Workspaces"
[ key "Next" (modm , xK_period ) nextWS'
, key "Previous" (modm , xK_comma ) prevWS'
, key "Remove" (modm , xF86XK_Eject ) removeWorkspace
] ++ switchWsById
where
togglePolybar = spawn "polybar-msg cmd toggle &"
toggleStruts = togglePolybar >> sendMessage ToggleStruts
keySet s ks = subtitle s : ks
key n k a = (k, addName n a)
action m = if m == shiftMask then "Move to " else "Switch to "
-- mod-[1..9]: Switch to workspace N |
-- mod-shift-[1..9]: Move client to workspace N
switchWsById =
[ key (action m <> show i) (m .|. modm, k) (windows $ f i)
| (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask), (copy, shiftMask .|. controlMask)]]
-- mod-{w,e,r}, Switch to physical/Xinerama screens 1, 2, or 3
-- mod-shift-{w,e,r}, Move client to screen 1, 2, or 3
switchScreen =
[ key (action m <> show sc) (m .|. modm, k) (screenWorkspace sc >>= flip whenJust (windows . f))
| (k, sc) <- zip [xK_w, xK_e, xK_r] [0..]
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
----------- Cycle through workspaces one by one but filtering out NSP (scratchpads) -----------
nextWS' = switchWS Next
prevWS' = switchWS Prev
switchWS dir =
findWorkspace filterOutNSP dir anyWS 1 >>= windows . W.view
filterOutNSP =
let g f xs = filter (\(W.Workspace t _ _) -> t /= "NSP") (f xs)
in g <$> getSortByIndex
------------------------------------------------------------------------
-- 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-button2, Raise the window to the top of the stack
, ((modm, button2), \w -> focus 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:
-- You can specify and transform your layouts by modifying these values.
-- If you change layout bindings be sure to use 'mod-shift-space' after
-- restarting (with 'mod-q') to reset your layout state to the new
-- defaults, as xmonad preserves your old layout settings by default.
--
-- The available layouts. Note that each layout is separated by |||,
-- which denotes layout choice.
--
myLayout =
avoidStruts
. smartBorders
. fullScreenToggle
. comLayout
. vscLayout
. musLayout
. webLayout
. mscLayout
. devLayout
. scdLayout
. spoLayout
. vmsLayout
. secLayout $ (tiled ||| Mirror tiled ||| column3 ||| full)
where
-- default tiling algorithm partitions the screen into two panes
grid = spacing gapSize . gaps (head myGaps) $ Grid False
grid_strict_portrait = spacing gapSize . gaps (head myGaps) $ GridRatio grid_portrait False
grid_strict_landscape = spacing gapSize . gaps (head myGaps) $ GridRatio grid_landscape False
tiled = spacing gapSize . gaps (head myGaps) $ Tall nmaster delta golden_ratio
doubletiled = spacing gapSize . gaps (head myGaps) $ Tall nmasterTwo delta golden_ratio
tiled_nogap = spacing 0 . gaps (myGaps !! 12) $ Tall nmaster delta golden_ratio
tiled_spaced = spacing 0 . gaps (myGaps !! 3) $ Tall nmaster delta ratio
column3_og = spacing gapSize . gaps (head myGaps) $ ThreeColMid 1 (3/100) (1/2)
video_tile = spacing gapSize . gaps (head myGaps) $ Mirror (Tall 1 (1/50) (3/5))
full = Full
fuller = spacing 0 . gaps (myGaps !! 12) $ Full
column3 = spacing 2 . gaps (myGaps !! 2) $ ThreeColMid 1 (33/100) (1/2)
goldenSpiral = spacing gapSize . gaps (head myGaps) $ spiral golden_ratio
silverSpiral = spacing gapSize . gaps (head myGaps) $ spiralWithDir East CCW ratio
dynamicGaps = spacing gapSize . gaps (head myGaps) $ spiralWithDir East CCW ratio
oneBig = spacing 2 . gaps (myGaps !! 3) $ OneBig (3/4) (3/4)
tabbed = spacing 2 . gaps (myGaps !! 3) $ simpleTabbed
resizeTall = spacing gapSize . gaps (head myGaps) $ ResizableTall nmaster (delta) ratio []
resize2Master = spacing gapSize . gaps (head myGaps) $ ResizableTall nmasterTwo (delta) ratio []
-- The default number of windows in the master pane
nmaster = 1
nmasterTwo = 2
-- Default proportions of screen occupied by master pane
ratio = 1/2
golden_ratio = 1/1.618033988749894e0
grid_portrait = 3/4
grid_landscape = 4/3
-- Percent of screen to increment by when resizing panes
delta = 2/100
-- Per workspace layout
webLayout = onWorkspace webWs (resizeTall ||| tiled_nogap ||| oneBig ||| reflectHoriz tiled_nogap ||| fuller ||| goldenSpiral ||| reflectHoriz goldenSpiral ||| tiled_spaced ||| reflectHoriz tiled_spaced ||| full ||| grid ||| grid_strict_landscape)
mscLayout = onWorkspace mscWs (resize2Master ||| resizeTall ||| tabbed ||| resize2Master ||| dynamicGaps ||| doubletiled ||| Mirror grid_strict_landscape ||| grid_strict_landscape ||| Mirror grid_strict_portrait ||| grid_strict_portrait ||| column3_og ||| tiled_spaced ||| grid ||| fuller ||| Mirror tiled_nogap ||| Mirror tiled ||| tiled_nogap ||| tiled ||| video_tile ||| full ||| column3 ||| goldenSpiral ||| silverSpiral)
musLayout = onWorkspace musWs (fuller ||| oneBig ||| tiled ||| tiled_spaced ||| reflectHoriz tiled_spaced)
vscLayout = onWorkspace vscWs (resize2Master ||| oneBig ||| Mirror tiled_nogap ||| reflectHoriz tiled ||| fuller ||| tiled_nogap ||| goldenSpiral ||| full ||| Mirror tiled ||| column3_og )
comLayout = onWorkspace comWs (tiled ||| full ||| column3 ||| goldenSpiral)
spoLayout = onWorkspace spoWs (tabbed ||| oneBig ||| goldenSpiral ||| column3 ||| Mirror tiled_nogap ||| fuller ||| full ||| tiled)
scdLayout = onWorkspace scdWs (oneBig ||| dynamicGaps ||| doubletiled ||| Mirror grid_strict_landscape ||| grid_strict_landscape ||| Mirror grid_strict_portrait ||| grid_strict_portrait ||| column3_og ||| tiled_spaced ||| grid ||| fuller ||| Mirror tiled_nogap ||| Mirror tiled ||| tiled_nogap ||| tiled ||| video_tile ||| full ||| column3 ||| goldenSpiral ||| silverSpiral)
devLayout = onWorkspace devWs (oneBig ||| reflectHoriz tiled ||| goldenSpiral ||| full ||| tiled ||| Mirror tiled ||| column3)
secLayout = onWorkspace secWs (tiled ||| fuller ||| column3)
vmsLayout = onWorkspace vmsWs (full ||| tiled ||| fuller ||| column3)
-- Fullscreen
fullScreenToggle = mkToggle (single NBFULL)
-- Defining Rectangles using absolute points (https://gist.github.com/tkf/1343015)
doFloatAbsRect :: Rational -> Rational -> Rational -> Rational -> ManageHook
doFloatAbsRect x y width height = do
win <- ask -- get Window
q <- liftX (floatLocation win) -- get (ScreenId, W.RationalRect)
let sid = fst q :: ScreenId
oirgRect = snd q :: W.RationalRect
ss2ss ss = -- :: StackSet ... -> StackSet ...
W.float win newRect ss where
mapping = map (\s -> (W.screen s, W.screenDetail s)) (c:v) where
c = W.current ss
v = W.visible ss
maybeSD = lookup sid mapping
scRect = fmap screenRect maybeSD
newRect = case scRect of
Nothing -> oirgRect
Just (Rectangle x0 y0 w0 h0) ->
W.RationalRect x' y' w' h' where
W.RationalRect x1 y1 w1 h1 = oirgRect
x' = if x0 == 0 then x1 else x / (fromIntegral x0)
y' = if y0 == 0 then y1 else y / (fromIntegral y0)
w' = if w0 == 0 then w1 else width / (fromIntegral w0)
h' = if h0 == 0 then h1 else height / (fromIntegral h0)
doF ss2ss
type AppName = String
type AppTitle = String
type AppClassName = String
type AppCommand = String
data App
= ClassApp AppClassName AppCommand
| TitleApp AppTitle AppCommand
| NameApp AppName AppCommand
deriving Show
audacious = ClassApp "Audacious" "audacious"
btm = TitleApp "btm" "alacritty -t btm -e btm --color gruvbox --default_widget_type proc"
virtbox = ClassApp "VirtualBox Machine" "VBoxManage startvm 'plutusVM_bismuth'"
calendar = ClassApp "Orage" "orage"
cmatrix = TitleApp "cmatrix" "alacritty cmatrix"
eog = NameApp "eog" "eog"
evince = ClassApp "Evince" "evince"
gimp = ClassApp "Gimp" "gimp"
keepass = ClassApp "KeePassXC" "keepassxc"
-- mastodon = TitleApp "Mastodon" "tokodon"
nautilus = ClassApp "Org.Gnome.Nautilus" "nautilus"
office = ClassApp "libreoffice-draw" "libreoffice-draw"
pavuctrl = ClassApp "Pavucontrol" "pavucontrol"
scr = ClassApp "SimpleScreenRecorder" "simplescreenrecorder"
-- spotify = ClassApp "Spotify" "spotify"
vlc = ClassApp "Vlc" "vlc --qt-minimal-view"
mpv = ClassApp "Mpv" "mpv"
kodi = ClassApp "Kodi" "kodi"
vscodium = ClassApp "VSCodium" "vscodium"
yad = ClassApp "Yad" "yad --text-info --text 'XMonad'"
myManageHook = manageApps <+> manageSpawn <+> manageScratchpads
where
isBrowserDialog = isDialog <&&> className =? "Brave-browser"
isFileChooserDialog = isRole =? "GtkFileChooserDialog"
isPopup = isRole =? "pop-up"
isSplash = isInProperty "_NET_WM_WINDOW_TYPE" "_NET_WM_WINDOW_TYPE_SPLASH"
isRole = stringProperty "WM_WINDOW_ROLE"
tileBelow = insertPosition Below Newer
tileAbove = insertPosition Above Newer
doVideoFloat = doFloatAbsRect 240 720 600 300
doCalendarFloat = customFloating (W.RationalRect (11 / 15) (1 / 48) (1 / 4) (1 / 8))
manageScratchpads = namedScratchpadManageHook scratchpads
anyOf :: [Query Bool] -> Query Bool
anyOf = foldl (<||>) (pure False)
match :: [App] -> Query Bool
match = anyOf . fmap isInstance
manageApps = composeOne
[ isInstance calendar -?> doCalendarFloat
, match [ virtbox
] -?> tileAbove
, match [ keepass
, mpv
] -?> tileAbove
, match [ audacious
, eog
, nautilus
, office
, pavuctrl
, scr
] -?> doCenterFloat
, match [ btm
, evince
, gimp
] -?> doFullFloat
, resource =? "desktop_window" -?> doIgnore
, resource =? "kdesktop" -?> doIgnore
, anyOf [ isPopup
] -?> tileBelow
, anyOf [ isFileChooserDialog
, isDialog
, isSplash
, isBrowserDialog
] -?> doCenterFloat
, isFullscreen -?> doFullFloat
, pure True -?> tileBelow
]
isInstance (ClassApp c _) = className =? c
isInstance (TitleApp t _) = title =? t
isInstance (NameApp n _) = appName =? n
getNameCommand (ClassApp n c) = (n, c)
getNameCommand (TitleApp n c) = (n, c)
getNameCommand (NameApp n c) = (n, c)
getAppName = fst . getNameCommand
getAppCommand = snd . getNameCommand
scratchpadApp :: App -> NamedScratchpad
scratchpadApp app = NS (getAppName app) (getAppCommand app) (isInstance app) defaultFloating
runScratchpadApp = namedScratchpadAction scratchpads . getAppName
scratchpads = scratchpadApp <$> [ audacious, btm, nautilus, scr, gimp, mpv, virtbox ]
------------------------------------------------------------------------
-- Workspaces
--
webWs = "web"
mscWs = "msc"
musWs = "mus"
vscWs = "vsc"
comWs = "com"
spoWs = "spo"
scdWs = "scd"
devWs = "dev"
secWs = "sec"
vmsWs = "vms"
myWS :: [WorkspaceId]
myWS = [webWs, mscWs, musWs, vscWs, comWs, spoWs, devWs, scdWs, secWs, vmsWs]
------------------------------------------------------------------------
-- Dynamic Projects
--
projects :: [Project]
projects =
[ Project { projectName = webWs
, projectDirectory = "~/plutus/workspace/webWs/"
, projectStartHook = Just $ do spawn "brave"
spawn "brave --app=https://youtube.com/"
}
, Project { projectName = mscWs
, projectDirectory = "~/plutus/workspace/mscWs/"
, projectStartHook = Just $ do spawn (terminalWithCommand "cowsay 'how are you today?'")
}
, Project { projectName = musWs
, projectDirectory = "~/plutus/workspace/musWs/"
, projectStartHook = Just $ do spawn "brave --app=https://music.youtube.com/"
}
, Project { projectName = vscWs
, projectDirectory = "~/plutus/workspace/vscWs/nix-config.git/intelTower/"
, projectStartHook = Just $ do spawn "codium -n ."
spawn delayTerminal
}
, Project { projectName = comWs
, projectDirectory = "~/plutus/workspace/comWs/"
, projectStartHook = Just $ do spawn "brave --app=https://mastodon.social/"
spawn "discord"
spawn "telegram-desktop"
spawn "signal-desktop"
-- spawn "element-desktop"
-- spawn "slack"
}
, Project { projectName = spoWs
, projectDirectory = "~/plutus/workspace/spoWs/"
, projectStartHook = Just $ do spawn (terminalWithCommand "systemctl status cardano-node")
}
, Project { projectName = devWs
, projectDirectory = "~/plutus/workspace/devWs/"
, projectStartHook = Just $ do spawn "codium -n ."
spawn delayTerminal
}
, Project { projectName = scdWs
, projectDirectory = "~/Cardano/git/"
, projectStartHook = Just $ do spawn "codium -n ."
spawn delayTerminal
}
, Project { projectName = secWs
, projectDirectory = "~/plutus/workspace/secWs/"
, projectStartHook = Just $ do spawn "keepassxc"
}
, Project { projectName = vmsWs
, projectDirectory = "~/plutus/workspace/vmsWs/"
, projectStartHook = Just $ runScratchpadApp virtbox
}
]
projectsTheme :: XPConfig
projectsTheme = amberXPConfig
{ bgHLight = "#002b36"
, font = "xft:Bitstream Vera Sans Mono:size=8:antialias=true"
, height = 60
, position = CenteredAt 0.5 0.5
}
------------------------------------------------------------------------
-- Event handling
-- * EwmhDesktops users should change this to ewmhDesktopsEventHook
--
-- Defines a custom handler function for X Events. The function should
-- return (All True) if the default handler is to be run afterwards. To
-- combine event hooks use mappend or mconcat from Data.Monoid.
--
-- NOTE: the (docks . ewmh . ewmhFullscreen) defined in main already overrides handleEventHook
--
-- myEventHook = docksEventHook <+> ewmhDesktopsEventHook <+> fullscreenEventHook
------------------------------------------------------------------------
-- Status bars and logging
-- Perform an arbitrary action on each internal state change or X event.
-- See the 'XMonad.Hooks.DynamicLog' extension for examples.
--TODO: Add transparency on a per app basis
--TODO: Add figure out how to make one window persistent using a keystroke
myLogHook = fadeInactiveLogHook 1.0
and the custom Hue stuff:
module HueLighting
( nextLightCue
, prevLightCue
, cycleLightCue
, runLightCue
) where
import XMonad
import XMonad.Util.Run
import qualified XMonad.Util.ExtensibleState as XS
type LightCommand = String
data HueCommand = HueCommand { lights :: [Int], commands :: [LightCommand] }
type LightZone = [Int]
{- Smart Lighting Hotkeys
Lights:
1. Window 1 (rest area)
2. Corner (rest area)
3. Closet (work area)
5. TV candle 2 (rest area)
6. Bed China Ball (rest area)
7. Chandelier 1 (Su)
8. Dresser Candle 1 (boundary)
9. Shelf Box 1 (rest area)
10. Chandelier 2 (Su)
12. Desk Light (Su)
14. Overhead Computers (work area)
16. Overhead Middle (boundary)
17. Guitars (work area)
18. Overhead Bed (rest area)
19. TV Candle 1 (rest area)
20. Window 2 (rest area)
21. TV Candle 3 (rest area)
-}
-- A function to generate hue strings
buildLightCommand :: HueCommand -> String
buildLightCommand (HueCommand ls cmds) = concatMap buildCommands cmds
where
buildCommands cmd = unwords $ concatMap (\light -> ["hue light", show light, cmd, ";"]) ls
-- Chain multiple HueCommands
chainCommands :: [HueCommand] -> String
chainCommands = unwords . map buildLightCommand
-- Zones
wholeRoom, restArea, boundary, workArea, suRoom:: LightZone
wholeRoom = [1, 2, 3, 5, 6, 8, 9, 14, 16, 17, 18, 19, 20, 21]
restArea = [1, 2, 5, 6, 9, 18, 19, 20, 21]
boundary = [8, 16]
workArea = [3, 14, 17]
suRoom = [7, 10, 12]
-- Hue Lighting Cues
blackOut :: String
blackOut = chainCommands [ HueCommand wholeRoom ["off"]]
darkWarm = chainCommands [ HueCommand restArea ["off"]
, HueCommand boundary ["off"]
, HueCommand [17] [ "on", "brightness 28%", "color 2141" ]
, HueCommand [3] [ "on", "color 2000", "brightness 100%" ]
, HueCommand [14] [ "on", "color 2141", "brightness 10%" ]
]
brightWarm = chainCommands [ HueCommand restArea ["relax", "brightness 50%" ]
, HueCommand boundary [ "relax", "brightness 22%" ]
, HueCommand [17] [ "on", "color 2141", "brightness 69%" ]
, HueCommand [3] [ "on", "color 2141", "brightness 100%" ]
, HueCommand [14] [ "on", "color 2141", "brightness 80%" ]
]
basque = chainCommands [ HueCommand [1] [ "on", "brightness 155", "color 0.4247, 0.3421" ]
, HueCommand [2] [ "on", "brightness 132", "color 0.4834, 0.3606" ]
, HueCommand [3] [ "on", "brightness 155", "color 0.4129, 0.3416" ]
, HueCommand [6] [ "on", "brightness 134", "color 0.414, 0.3905" ]
, HueCommand [8] [ "on", "brightness 125", "color 0.4129, 0.3416" ]
, HueCommand [5, 9, 14, 16, 18, 19] [ "on", "brightness 125", "color 0.5106, 0.3739" ]
, HueCommand [17] [ "on", "brightness 156", "color 0.4834, 0.3606" ]
, HueCommand [20] [ "on", "brightness 155", "color 0.413, 0.3415" ]
, HueCommand [21] [ "on", "brightness 156", "color 0.4831, 0.3606" ]
]
fullWarm = chainCommands [ HueCommand wholeRoom ["relax"]]
darkCold = chainCommands [ HueCommand restArea ["off"]
, HueCommand boundary ["off"]
, HueCommand [17] ["on", "color 6200", "brightness 28%"]
, HueCommand [3] ["on", "blue", "brightness 100%"]
, HueCommand [14] ["on", "blue", "brightness 10%"]
]
brightCold = chainCommands [ HueCommand restArea [ "on", "color 6500", "brightness 50%" ]
, HueCommand boundary [ "on", "color 6500", "brightness 22%" ]
, HueCommand [17] [ "on", "color 6500", "brightness 69%" ]
, HueCommand [3] ["concentrate"]
, HueCommand [14] [ "on", "color 6500", "brightness 80%" ]
]
fullCold = chainCommands [ HueCommand wholeRoom ["concentrate"]]
freakOut = chainCommands [ HueCommand restArea ["on", "blue", "brightness 80%" ]
, HueCommand workArea ["on", "red", "brightness 80%" ]
, HueCommand boundary ["on", "green", "brightness 80%", "blink" ]
, HueCommand [2, 17] [ "on", "brightness 28%" ]
, HueCommand [3] [ "on", "brightness 100%", "blink" ]
, HueCommand [14] [ "on", "brightness 45%" ]
]
deskOff = chainCommands [ HueCommand [17] ["off"]]
data LightingCue = DarkWarm | BrightWarm | Basque | FullWarm | DarkCold | BrightCold | FullCold | FreakOut | DeskOff | BlackOut deriving (Enum, Bounded, Show)
lightingCues :: [X ()]
lightingCues =
[ spawn darkWarm
, spawn brightWarm
, spawn basque
, spawn fullWarm
, spawn darkCold
, spawn brightCold
, spawn fullCold
, spawn freakOut
, spawn deskOff
, spawn blackOut
]
nextLightCue, prevLightCue :: X ()
nextLightCue = cycleLightCue 1
prevLightCue = cycleLightCue (-1)
cycleLightCue :: Int -> X ()
cycleLightCue d = do
LightCueIndex idx <- XS.get
let n = length lightingCues
newIndex = (idx + d) `mod` n
(lightingCues !! newIndex)
XS.put $ LightCueIndex newIndex
runLightCue :: Int -> X ()
runLightCue idx = do
let n = length lightingCues
newIndex = idx `mod` n
(lightingCues !! newIndex)
XS.put $ LightCueIndex newIndex
newtype LightCueState = LightCueIndex Int deriving Show
instance ExtensionClass LightCueState where
initialValue = LightCueIndex 0