1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
|
import System.IO
import System.Exit
import Data.Semigroup(All)
import qualified Data.List as L
import qualified Data.Map as M
import XMonad
import qualified XMonad.StackSet as W
import XMonad.Hooks.ManageDocks
import XMonad.Hooks.ServerMode
import XMonad.Hooks.StatusBar
import XMonad.Hooks.StatusBar.PP
import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.UrgencyHook
import XMonad.Layout.Gaps
import XMonad.Layout.NoBorders
import XMonad.Layout.Spacing
import XMonad.Layout.MultiToggle
import XMonad.Layout.MultiToggle.Instances
import XMonad.Layout.LayoutModifier
import XMonad.Actions.CycleWS
import XMonad.Actions.NoBorders
import XMonad.Util.ClickableWorkspaces
------------------------------------------------------------------------
-- Workspaces
-- The default number of workspaces (virtual screens) and their names.
--
myWorkspaces :: [String]
myWorkspaces = map show [1..9]
------------------------------------------------------------------------
-- Window rules
-- Execute arbitrary actions and WindowSet manipulations when managing
-- a new window. You can use this to, for example, always float a
-- particular program, or have a client always appear on a particular
-- workspace.
--
-- To find the property name associated with a program, use
-- > xprop | grep WM_CLASS
-- and click on the client you're interested in.
--
-- To match on the WM_NAME, you can use 'title' in the same way that
-- 'className' and 'resource' are used below.
--
myManageHook :: ManageHook
myManageHook = composeAll
[
]
------------------------------------------------------------------------
-- 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.
gap :: Int
gap = 7
myGaps :: l a -> ModifiedLayout Gaps l a
myGaps = gaps [(U, gap), (R, gap), (L, gap), (D, gap)]
addSpace :: l a -> ModifiedLayout Spacing l a
addSpace = spacing gap
tiledLayout :: Tall Window
tiledLayout = tiled
where
-- default tiling algorithm partitions the screen into two panes
tiled = Tall nmaster delta ratio
-- The default number of windows in the master pane
nmaster = 1
-- Default proportion of screen occupied by master pane
ratio = 1/2
-- Percent of screen to increment by when resizing panes
delta = 3/100
layouts :: Tall Window
layouts = tiledLayout
{-
Set up layouts.
1. Remove borders on floating windows.
2. Create NBFULL (No borders fullscreen toggle)
3. Configure layouts to avoid struts
-}
myLayout = lessBorders OnlyLayoutFloat
$ mkToggle (NBFULL ?? EOT)
$ avoidStruts $ myGaps $ addSpace
$ layouts
------------------------------------------------------------------------
-- Colors and borders
myBorderWidth :: Dimension
myBorderWidth = 2
myNormalBorderColor :: String
myNormalBorderColor = "#444444"
backgroundColor :: String
backgroundColor = "#005577"
foregroundColor :: String
foregroundColor = "#bbbbbb"
urgentColor :: String
urgentColor = "#ff0000"
------------------------------------------------------------------------
{-
This sets the "_NET_WM_STATE_FULLSCREEN" window property, helping some programs such as firefox to adjust acoordingly to fullscreen mode
In a perfect world we shouldnt need to do this manually but it seems like ewmhFullscreen/others dont implement this functionality
-}
setFullscreenProp :: Bool -> Window -> X ()
setFullscreenProp b win = withDisplay $ \dpy -> do
state <- getAtom "_NET_WM_STATE"
fullsc <- getAtom "_NET_WM_STATE_FULLSCREEN"
let replaceWMStateProperty = changeProperty32 dpy win state 4 propModeReplace
if b
then io $ replaceWMStateProperty [fromIntegral fullsc]
else io $ replaceWMStateProperty []
{-
Fullscreen: Hide xmobar -> Hide borders -> Set fullscreen -> Set fullscreenprops
Unfullscreen: ^^ but reverse
-}
toggleFullScreen :: X ()
toggleFullScreen = do
mIsFullScreen <- withWindowSet (isToggleActive NBFULL . W.workspace . W.current)
case mIsFullScreen of
Just isFullScreen -> if isFullScreen
then withFocused (setFullscreenProp False) >> sendMessage (Toggle NBFULL) >> sendMessage ToggleStruts
else sendMessage ToggleStruts >> sendMessage (Toggle NBFULL) >> withFocused (setFullscreenProp True)
Nothing -> return ()
toggleFloat :: Window -> X ()
toggleFloat w = windows (\s -> if M.member w (W.floating s)
then W.sink w s
else (W.float w (W.RationalRect 0.125 0.125 0.75 0.75) s))
------------------------------------------------------------------------
-- External commands
myCommands :: [(String, X ())]
myCommands =
[ ("decrease-master-size" , sendMessage Shrink )
, ("increase-master-size" , sendMessage Expand )
, ("decrease-master-count" , sendMessage $ IncMasterN (-1) )
, ("increase-master-count" , sendMessage $ IncMasterN ( 1) )
, ("focus-prev" , windows W.focusUp )
, ("focus-next" , windows W.focusDown )
, ("focus-master" , windows W.focusMaster )
, ("swap-with-prev" , windows W.swapUp )
, ("swap-with-next" , windows W.swapDown )
, ("swap-with-master" , windows W.swapMaster )
, ("togglefullscreen" , toggleFullScreen )
, ("togglefloating" , withFocused toggleFloat )
, ("next-layout" , sendMessage NextLayout )
, ("cycle-workspace" , toggleWS )
, ("kill-window" , kill )
, ("quit" , io $ exitWith ExitSuccess )
, ("restart" , spawn "xmonad --recompile; xmonad --restart" )
]
-----------------------------------------------------------------------
-- Custom server mode
myServerModeEventHook :: Event -> X All
myServerModeEventHook = serverModeEventHookCmd' $ return myCommands'
myCommands' :: [(String, X ())]
myCommands' = ("list-commands", listMyServerCmds) : myCommands ++ wscs ++ sccs
where
wscs = [((m ++ s), windows $f s) | s <- myWorkspaces
, (f, m) <- [(W.view, "focus-workspace-"), (W.shift, "send-to-workspace-")] ]
sccs = [((m ++ show sc), screenWorkspace (fromIntegral sc) >>= flip whenJust (windows . f))
| sc <- [0..10], (f, m) <- [(W.view, "focus-screen-"), (W.shift, "send-to-screen-")]]
listMyServerCmds :: X ()
listMyServerCmds = spawn ("echo '" ++ asmc ++ "' | xmessage -file -")
where asmc = concat $ "Available commands:" : map (\(x, _)-> " " ++ x) myCommands'
------------------------------------------------------------------------
-- Mouse bindings
myFocusFollowsMouse :: Bool
myFocusFollowsMouse = False
myMouseBindings :: XConfig l -> M.Map (KeyMask, Button) (Window -> X ())
myMouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList $
[
-- mod-button1, Set the window to floating mode and move by dragging
((modMask, button1),
(\w -> focus w >> mouseMoveWindow w))
-- mod-button2, Toggle float on the window
, ((modMask, button2),
(\w -> focus w >> toggleFloat w))
-- mod-button3, Set the window to floating mode and resize by dragging
, ((modMask, button3),
(\w -> focus w >> mouseResizeWindow w))
-- you may also bind events to the mouse scroll wheel (button4 and button5)
]
------------------------------------------------------------------------
-- Status bar
{-
Configure xmobar in a dwm style with statusBarPP
1. Get the active, inactive, urgent and full rectangles above workspace numbers.
2. Make workspaces clickable.
3. Set a 2 space separation between workspaces.
-}
myStatusBar :: StatusBarConfig
myStatusBar = statusBarProp "xmobar" (do
numWindows <- getNumberOfWindowsInWorkpace
return $ def {
ppCurrent = if numWindows > 0
then xmobarBorder "Top" foregroundColor 4 . xmobarColor foregroundColor backgroundColor . wrap " " " "
else xmobarColor foregroundColor backgroundColor . wrap " " " "
, ppTitle = (\xs -> case length xs > 70 of
True -> take 70 xs ++ "..."
False -> xs) . xmobarStrip
, ppSep = " | "
, ppWsSep = ""
, ppLayout = (\_ -> "")
, ppHidden = (\s -> clickableWrap ((read s::Int) - 1) (createDwmBox foregroundColor (" " ++ s ++ " "))) --better way to clickablewrap .
, ppHiddenNoWindows = (\s -> clickableWrap ((read s::Int) - 1) (" " ++ s ++ " "))
, ppUrgent = (\s -> clickableWrap ((read s::Int) - 1) (xmobarBorder "Top" urgentColor 4 (" " ++ s ++ " ")))
}
)
where
getNumberOfWindowsInWorkpace = withWindowSet (pure . length . W.index)
createDwmBox color prefix = "<box type=HBoth offset=L20 color="++color++"><box type=Top mt=3 color="++color++"><box type=Top color="++color++">" ++ prefix ++ "</box></box></box>"
------------------------------------------------------------------------
-- Launch xmonad with the aforementioned settings
{-
Main function (Launch xmonad)
1. Setup statusbar (with docks).
2. Enable fullscreen hook.
3. Enable urgency hook.
4. Setup xmonad defaults.
-}
main :: IO ()
main = do
xmonad
. withSB myStatusBar
. docks
. ewmhFullscreen
. ewmh
$ withUrgencyHookC BorderUrgencyHook { urgencyBorderColor = urgentColor } urgencyConfig { suppressWhen = XMonad.Hooks.UrgencyHook.Never }
$ def {
focusFollowsMouse = myFocusFollowsMouse,
borderWidth = myBorderWidth,
workspaces = myWorkspaces,
normalBorderColor = myNormalBorderColor,
focusedBorderColor = backgroundColor,
mouseBindings = myMouseBindings,
layoutHook = myLayout,
handleEventHook = myServerModeEventHook,
manageHook = myManageHook
}
|